Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
70188e9
button to add new marker
ldbach Dec 8, 2025
442be70
success to adjust with the time
ldbach Dec 8, 2025
7469db2
timestamp for mock graphs
ldbach Dec 9, 2025
a294945
time stamp for winder
ldbach Dec 9, 2025
9ce105d
time stamp for extruder
ldbach Dec 9, 2025
8e4cc67
first approach for time stamp in excel
ldbach Dec 9, 2025
6a2cda5
refactor code
ldbach Dec 11, 2025
61a2470
clean up redundant code and comments
ldbach Dec 11, 2025
8a59d69
Fix: Applied Prettier formatting fixes
ldbach Dec 11, 2025
923bacf
Merge branch 'master' into add-timestamp
ldbach Dec 11, 2025
af839c4
Merge branch 'master' into add-timestamp
EinsPommes Jan 17, 2026
3341f16
Fix chart y-axis scaling for visible data; improve container selection
EinsPommes Jan 17, 2026
5747596
Merge branch 'master' into add-timestamp
EinsPommes Jan 25, 2026
7ef2f55
feat(graph): persist markers and render marker points
EinsPommes Jan 25, 2026
e1b5517
feat(graph): add marker functionality and improve marker rendering
EinsPommes Jan 25, 2026
149447c
Merge branch 'master' into add-timestamp
EinsPommes Feb 1, 2026
8144209
Refactor graph components to improve marker handling and UI consistency
EinsPommes Feb 1, 2026
fa67bef
feat(graph): add duplicate marker validation and long-press delete
EinsPommes Feb 1, 2026
52dea0a
refactor(graph): streamline marker handling and enhance component str…
EinsPommes Feb 1, 2026
678fd73
fix(graph): ensure proper null handling for chart destruction and cor…
EinsPommes Feb 1, 2026
a472816
Merge branch 'master' into add-timestamp
EinsPommes Feb 2, 2026
b4d0609
Merge branch 'master' into add-timestamp
EinsPommes Feb 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions electron/src/components/graph/AddMarkerDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import React, { useState, useEffect } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
} from "@/components/ui/dialog";
import { TouchButton } from "@/components/touch/TouchButton";
import { TimeInput } from "@/components/time/TimeInput";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { Icon } from "@/components/Icon";

type AddMarkerDialogProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
onAddMarker: (name: string, timestamp: number, color?: string) => void;
currentTimestamp: number | null;
defaultName?: string;
// Existing marker names to prevent duplicates (case-insensitive)
existingNames?: string[];
};

export function AddMarkerDialog({
open,
onOpenChange,
onAddMarker,
currentTimestamp,
defaultName = "",
existingNames = [],
}: AddMarkerDialogProps) {
const [name, setName] = useState(defaultName);
const [selectedTimestamp, setSelectedTimestamp] = useState<number | null>(
null,
);
const [color, setColor] = useState("#000000");
// Shown when user tries to add a marker whose name already exists
const [duplicateNameError, setDuplicateNameError] = useState(false);

// Reset form only when dialog opens or closes; do not reset when currentTimestamp
// updates while open (e.g. from graph) or we overwrite the user's time input
useEffect(() => {
if (open) {
setName(defaultName);
setSelectedTimestamp(currentTimestamp ?? Date.now());
setDuplicateNameError(false);
} else {
setName("");
setSelectedTimestamp(null);
setDuplicateNameError(false);
}
}, [open]);

const handleAdd = () => {
if (!name.trim()) return;

// Reject duplicate names (compare trimmed, case-insensitive)
const trimmedName = name.trim();
const isDuplicate = existingNames.some(
(existing) => existing.trim().toLowerCase() === trimmedName.toLowerCase(),
);
if (isDuplicate) {
setDuplicateNameError(true);
return;
}

setDuplicateNameError(false);

// Use selected time if set, else context/graph time, else now
const timestamp = selectedTimestamp ?? currentTimestamp ?? Date.now();
if (!timestamp) return;

onAddMarker(trimmedName, timestamp, color);
onOpenChange(false);
};

const handleCancel = () => {
onOpenChange(false);
};

return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="flex flex-row items-center gap-2">
<Icon name="lu:Bookmark" />
Add Marker
</DialogTitle>
<DialogDescription>
Create a marker for all graphs of this machine at the current time.
</DialogDescription>
</DialogHeader>
<Separator />

<div className="flex flex-col gap-4">
{/* Name Input */}
<div className="flex flex-col gap-2">
<Label htmlFor="marker-name">Marker Name</Label>
<Input
id="marker-name"
type="text"
value={name}
onChange={(e) => {
setName(e.target.value);
setDuplicateNameError(false);
}}
placeholder="Enter marker name"
autoFocus
onKeyDown={(e) => {
if (e.key === "Enter") {
handleAdd();
}
}}
/>
{duplicateNameError && (
<p className="text-destructive text-sm">
A marker with this name already exists.
</p>
)}
</div>

{/* Time Input (optional) */}
<div className="flex flex-col gap-2">
<Label>Time (optional)</Label>
<TimeInput
timestamp={selectedTimestamp}
onTimeChange={setSelectedTimestamp}
onClear={() =>
setSelectedTimestamp(currentTimestamp ?? Date.now())
}
/>
<p className="text-muted-foreground text-xs">
Leave empty to use current time
</p>
</div>

{/* Color Input */}
<div className="flex flex-col gap-2">
<Label htmlFor="marker-color">Color</Label>
<div className="flex items-center gap-3">
<input
id="marker-color"
type="color"
value={color}
onChange={(e) => setColor(e.target.value)}
className="border-input h-9 w-20 cursor-pointer rounded-md border"
/>
<Input
type="text"
value={color}
onChange={(e) => setColor(e.target.value)}
placeholder="#000000"
/>
</div>
</div>
</div>

<Separator />
<div className="flex flex-row gap-4">
<TouchButton
variant="outline"
icon="lu:X"
className="h-21 flex-1"
onClick={handleCancel}
>
Abort
</TouchButton>
<TouchButton
className="h-21 flex-1 flex-shrink-0"
onClick={handleAdd}
icon="lu:Bookmark"
disabled={!name.trim()}
>
Add Marker
</TouchButton>
</div>
</DialogContent>
</Dialog>
);
}
10 changes: 6 additions & 4 deletions electron/src/components/graph/BigGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export function BigGraph({
config,
graphId,
syncGraph,
uplotRefOut,
onRegisterForExport,
onUnregisterFromExport,
}: BigGraphProps & {
Expand Down Expand Up @@ -456,6 +457,7 @@ export function BigGraph({
useBigGraphEffects({
containerRef,
uplotRef,
uplotRefOut,
startTimeRef,
manualScaleRef,
lastProcessedCountRef,
Expand Down Expand Up @@ -494,8 +496,8 @@ export function BigGraph({
return (
<div className="h-[50vh] w-full">
<div className="flex h-full w-full flex-col overflow-hidden rounded-3xl border border-gray-200 bg-white shadow">
<div className="flex items-center justify-between pt-4 pr-5 pb-6 pl-6">
<div className="mt-1 flex items-center gap-4">
<div className="flex shrink-0 items-center justify-between gap-4 pt-4 pr-5 pb-6 pl-6">
<div className="mt-1 flex min-w-0 flex-1 items-center gap-4">
<Icon
name={unit ? getUnitIcon(unit) : "lu:TrendingUp"}
className="size-6 text-gray-600"
Expand All @@ -506,9 +508,9 @@ export function BigGraph({
</h2>
</div>

<div className="flex items-center gap-4">
<div className="flex shrink-0 items-center gap-4">
{normalizedSeries.length === 1 && (
<ControlCard className="rounded-md px-4 py-3">
<ControlCard className="rounded-lg border-gray-200/80 bg-gray-50/80 px-4 py-3 shadow-sm">
<div className="flex items-center gap-2 text-base text-gray-600">
<span className="font-mono leading-none font-bold text-gray-900">
{formatDisplayValue(displayValue, renderValue)}
Expand Down
42 changes: 32 additions & 10 deletions electron/src/components/graph/GraphControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function GraphControls({
onSwitchToLive,
onSwitchToHistorical,
onExport,
onAddMarker,
timeWindowOptions = DEFAULT_TIME_WINDOW_OPTIONS,
showFromTimestamp,
onShowFromChange,
Expand Down Expand Up @@ -102,16 +103,27 @@ export function GraphControls({
Live
</TouchButton>

{onExport && (
{(onExport || onAddMarker) && (
<>
<div className="mx-2 h-8 w-px bg-gray-200"></div>
<TouchButton
onClick={onExport}
variant="outline"
className="h-auto bg-green-600 px-3 py-3 text-base font-medium text-white hover:bg-green-700"
>
Export
</TouchButton>
{onAddMarker && (
<TouchButton
onClick={onAddMarker}
variant="outline"
className="h-auto bg-blue-600 px-3 py-3 text-base font-medium text-white hover:bg-blue-700"
>
Add Marker
</TouchButton>
)}
{onExport && (
<TouchButton
onClick={onExport}
variant="outline"
className="h-auto bg-green-600 px-3 py-3 text-base font-medium text-white hover:bg-green-700"
>
Export
</TouchButton>
)}
</>
)}
</div>
Expand All @@ -127,6 +139,7 @@ export function FloatingControlPanel({
onSwitchToLive,
onSwitchToHistorical,
onExport,
onAddMarker,
timeWindowOptions = DEFAULT_TIME_WINDOW_OPTIONS,
showFromTimestamp,
onShowFromChange,
Expand Down Expand Up @@ -218,10 +231,19 @@ export function FloatingControlPanel({
>
Live
</TouchButton>
{isExpanded && onExport && (
{isExpanded && (onExport || onAddMarker) && (
<div className="h-8 w-px bg-gray-200"></div>
)}
{onExport && (
{isExpanded && onAddMarker && (
<TouchButton
onClick={onAddMarker}
variant="outline"
className="h-auto bg-blue-600 px-3 py-3 text-base font-medium text-white hover:bg-blue-700"
>
Add Marker
</TouchButton>
)}
{isExpanded && onExport && (
<TouchButton
onClick={onExport}
variant="outline"
Expand Down
Loading
Loading