@simten/ui 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,8 @@ export interface ClockControlsProps {
26
26
  floating?: boolean;
27
27
  /** Drop the inner border + background when the parent already provides them */
28
28
  chromeless?: boolean;
29
+ /** Pulse the Run button to hint "press this to simulate" (editor first-run only). */
30
+ pulseRun?: boolean;
29
31
  }
30
- export declare function ClockControls({ cycle, historyLength, historyIndex, isRunning, isViewingPast, onStep, onRun, onPause, onReset, onStepBack, onStepForward, onSeek, onSpeedChange, speed, maxSpeed, showScrubber, floating, chromeless, }: ClockControlsProps): import("react/jsx-runtime").JSX.Element;
32
+ export declare function ClockControls({ cycle, historyLength, historyIndex, isRunning, isViewingPast, onStep, onRun, onPause, onReset, onStepBack, onStepForward, onSeek, onSpeedChange, speed, maxSpeed, showScrubber, floating, chromeless, pulseRun, }: ClockControlsProps): import("react/jsx-runtime").JSX.Element;
31
33
  //# sourceMappingURL=ClockControls.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ClockControls.d.ts","sourceRoot":"","sources":["../../src/canvas/ClockControls.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsDH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mGAAmG;IACnG,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,aAAa,EACb,YAAY,EACZ,SAAS,EACT,aAAa,EACb,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,UAAU,EACV,aAAa,EACb,MAAM,EACN,aAAa,EACb,KAAS,EACT,QAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,GACX,EAAE,kBAAkB,2CAsFpB"}
1
+ {"version":3,"file":"ClockControls.d.ts","sourceRoot":"","sources":["../../src/canvas/ClockControls.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA6DH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mGAAmG;IACnG,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,aAAa,EACb,YAAY,EACZ,SAAS,EACT,aAAa,EACb,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,UAAU,EACV,aAAa,EACb,MAAM,EACN,aAAa,EACb,KAAS,EACT,QAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,QAAQ,GACT,EAAE,kBAAkB,2CAsFpB"}
@@ -16,10 +16,12 @@ import { Button } from "../primitives/button";
16
16
  * and bakes in the Tooltip so each call site is a one-liner. The `label`
17
17
  * doubles as `aria-label` and visible tooltip content.
18
18
  */
19
- function IconBtn({ label, icon: Icon, onClick, disabled, }) {
20
- return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", onClick: onClick, disabled: disabled, "aria-label": label, className: "h-7 w-7 text-muted-foreground disabled:opacity-40 [&_svg]:size-3.5", children: _jsx(Icon, {}) }) }), _jsx(TooltipContent, { side: "bottom", children: label })] }));
19
+ function IconBtn({ label, icon: Icon, onClick, disabled, pulse, }) {
20
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", onClick: onClick, disabled: disabled, "aria-label": label, className: `h-7 w-7 text-muted-foreground disabled:opacity-40 [&_svg]:size-3.5${pulse
21
+ ? " animate-pulse text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-950/40 ring-2 ring-blue-500/40"
22
+ : ""}`, children: _jsx(Icon, {}) }) }), _jsx(TooltipContent, { side: "bottom", children: label })] }));
21
23
  }
22
- export function ClockControls({ cycle, historyLength, historyIndex, isRunning, isViewingPast, onStep, onRun, onPause, onReset, onStepBack, onStepForward, onSeek, onSpeedChange, speed = 1, maxSpeed = 100, showScrubber, floating, chromeless, }) {
24
+ export function ClockControls({ cycle, historyLength, historyIndex, isRunning, isViewingPast, onStep, onRun, onPause, onReset, onStepBack, onStepForward, onSeek, onSpeedChange, speed = 1, maxSpeed = 100, showScrubber, floating, chromeless, pulseRun, }) {
23
25
  const wrapper = floating
24
26
  ? "absolute top-3 left-1/2 -translate-x-1/2 z-10"
25
27
  : "";
@@ -28,6 +30,6 @@ export function ClockControls({ cycle, historyLength, historyIndex, isRunning, i
28
30
  : "flex items-center gap-1.5 border-t border-border bg-card/95 px-3 py-1.5";
29
31
  return (_jsx(TooltipProvider, { children: _jsx("div", { className: wrapper, children: _jsxs("div", { className: inner, children: [_jsx(IconBtn, { label: "Tick", icon: SkipForward, onClick: onStep, disabled: isRunning || isViewingPast }), isRunning
30
32
  ? _jsx(IconBtn, { label: "Pause", icon: Pause, onClick: onPause })
31
- : _jsx(IconBtn, { label: "Run", icon: Play, onClick: onRun, disabled: isViewingPast }), _jsx(IconBtn, { label: "Reset", icon: RotateCcw, onClick: onReset }), onSpeedChange && (_jsxs("div", { className: "flex items-center gap-2 border-l border-border pl-2 ml-0.5", children: [_jsx(Gauge, { className: "h-3.5 w-3.5 text-muted-foreground/70", "aria-hidden": true }), _jsx("input", { type: "range", min: "1", max: maxSpeed, value: speed, onChange: (e) => onSpeedChange(Number(e.target.value)), className: "w-20 h-1 rounded-lg appearance-none cursor-pointer accent-blue-600", disabled: isViewingPast, "aria-label": "Simulation speed" }), _jsxs("span", { className: "min-w-[55px] text-[10px] text-muted-foreground font-mono tabular-nums", children: [speed, " tick/s"] })] })), _jsx("div", { className: "border-l border-border pl-1.5 ml-0.5", children: _jsxs("span", { className: "text-[11px] text-muted-foreground", children: ["Cycle ", _jsx("span", { className: "font-mono font-semibold text-foreground", children: cycle })] }) }), historyLength > 1 && (_jsxs("div", { className: "flex items-center gap-0.5 border-l border-border pl-1.5 ml-0.5", children: [_jsx(IconBtn, { label: "Step back", icon: ChevronLeft, onClick: onStepBack, disabled: historyIndex <= 0 || isRunning }), _jsx("span", { className: "min-w-[40px] text-center text-[11px] text-muted-foreground", children: isViewingPast ? (_jsxs("span", { className: "font-mono text-amber-600 dark:text-amber-400", children: [historyIndex + 1, "/", historyLength] })) : (_jsxs("span", { className: "font-mono", children: [historyLength, "/", historyLength] })) }), _jsx(IconBtn, { label: "Step forward", icon: ChevronRight, onClick: onStepForward, disabled: !isViewingPast || isRunning })] })), showScrubber && onSeek && historyLength > 1 && (_jsxs("div", { className: "flex items-center gap-2 border-l border-border pl-2 ml-0.5", children: [_jsx(History, { className: "h-3.5 w-3.5 text-muted-foreground/70", "aria-hidden": true }), _jsx("input", { type: "range", min: "0", max: historyLength - 1, value: historyIndex, onChange: (e) => onSeek(Number(e.target.value)), className: "w-24 h-1 rounded-lg appearance-none cursor-pointer accent-blue-600", disabled: isRunning, "aria-label": "Cycle scrubber" })] }))] }) }) }));
33
+ : _jsx(IconBtn, { label: "Run", icon: Play, onClick: onRun, disabled: isViewingPast, pulse: pulseRun && !isViewingPast }), _jsx(IconBtn, { label: "Reset", icon: RotateCcw, onClick: onReset }), onSpeedChange && (_jsxs("div", { className: "flex items-center gap-2 border-l border-border pl-2 ml-0.5", children: [_jsx(Gauge, { className: "h-3.5 w-3.5 text-muted-foreground/70", "aria-hidden": true }), _jsx("input", { type: "range", min: "1", max: maxSpeed, value: speed, onChange: (e) => onSpeedChange(Number(e.target.value)), className: "w-20 h-1 rounded-lg appearance-none cursor-pointer accent-blue-600", disabled: isViewingPast, "aria-label": "Simulation speed" }), _jsxs("span", { className: "min-w-[55px] text-[10px] text-muted-foreground font-mono tabular-nums", children: [speed, " tick/s"] })] })), _jsx("div", { className: "border-l border-border pl-1.5 ml-0.5", children: _jsxs("span", { className: "text-[11px] text-muted-foreground", children: ["Cycle ", _jsx("span", { className: "font-mono font-semibold text-foreground", children: cycle })] }) }), historyLength > 1 && (_jsxs("div", { className: "flex items-center gap-0.5 border-l border-border pl-1.5 ml-0.5", children: [_jsx(IconBtn, { label: "Step back", icon: ChevronLeft, onClick: onStepBack, disabled: historyIndex <= 0 || isRunning }), _jsx("span", { className: "min-w-[40px] text-center text-[11px] text-muted-foreground", children: isViewingPast ? (_jsxs("span", { className: "font-mono text-amber-600 dark:text-amber-400", children: [historyIndex + 1, "/", historyLength] })) : (_jsxs("span", { className: "font-mono", children: [historyLength, "/", historyLength] })) }), _jsx(IconBtn, { label: "Step forward", icon: ChevronRight, onClick: onStepForward, disabled: !isViewingPast || isRunning })] })), showScrubber && onSeek && historyLength > 1 && (_jsxs("div", { className: "flex items-center gap-2 border-l border-border pl-2 ml-0.5", children: [_jsx(History, { className: "h-3.5 w-3.5 text-muted-foreground/70", "aria-hidden": true }), _jsx("input", { type: "range", min: "0", max: historyLength - 1, value: historyIndex, onChange: (e) => onSeek(Number(e.target.value)), className: "w-24 h-1 rounded-lg appearance-none cursor-pointer accent-blue-600", disabled: isRunning, "aria-label": "Cycle scrubber" })] }))] }) }) }));
32
34
  }
33
35
  //# sourceMappingURL=ClockControls.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ClockControls.js","sourceRoot":"","sources":["../../src/canvas/ClockControls.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unified ClockControls — one design, used everywhere.\n *\n * Icon buttons (Step, Play/Pause, Reset) + cycle counter + time-travel.\n * Optional: speed slider, scrubber.\n * Floating style — position via parent container.\n */\n\n\"use client\";\n\nimport type { ComponentType, SVGProps } from \"react\";\nimport {\n SkipForward,\n Play,\n Pause,\n RotateCcw,\n ChevronLeft,\n ChevronRight,\n Gauge,\n History,\n} from \"lucide-react\";\nimport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from \"../primitives/tooltip\";\nimport { Button } from \"../primitives/button\";\n\n/**\n * Dense icon button used throughout this toolbar. Wraps shadcn `Button`\n * with `ghost` + `icon` variants but tightens the size for the embed bar,\n * and bakes in the Tooltip so each call site is a one-liner. The `label`\n * doubles as `aria-label` and visible tooltip content.\n */\nfunction IconBtn({\n label,\n icon: Icon,\n onClick,\n disabled,\n}: {\n label: string;\n icon: ComponentType<SVGProps<SVGSVGElement>>;\n onClick: () => void;\n disabled?: boolean;\n}) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={onClick}\n disabled={disabled}\n aria-label={label}\n className=\"h-7 w-7 text-muted-foreground disabled:opacity-40 [&_svg]:size-3.5\"\n >\n <Icon />\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\">{label}</TooltipContent>\n </Tooltip>\n );\n}\n\nexport interface ClockControlsProps {\n cycle: number;\n historyLength: number;\n historyIndex: number;\n isRunning: boolean;\n isViewingPast: boolean;\n onStep: () => void;\n onRun: () => void;\n onPause: () => void;\n onReset: () => void;\n onStepBack: () => void;\n onStepForward: () => void;\n onSeek?: (index: number) => void;\n onSpeedChange?: (speed: number) => void;\n speed?: number;\n maxSpeed?: number;\n showScrubber?: boolean;\n /** Render as a floating pill (inspector/canvas overlay) vs inline bar (editor/embed bottom bar) */\n floating?: boolean;\n /** Drop the inner border + background when the parent already provides them */\n chromeless?: boolean;\n}\n\nexport function ClockControls({\n cycle,\n historyLength,\n historyIndex,\n isRunning,\n isViewingPast,\n onStep,\n onRun,\n onPause,\n onReset,\n onStepBack,\n onStepForward,\n onSeek,\n onSpeedChange,\n speed = 1,\n maxSpeed = 100,\n showScrubber,\n floating,\n chromeless,\n}: ClockControlsProps) {\n const wrapper = floating\n ? \"absolute top-3 left-1/2 -translate-x-1/2 z-10\"\n : \"\";\n const inner = chromeless\n ? \"flex items-center gap-1.5\"\n : \"flex items-center gap-1.5 border-t border-border bg-card/95 px-3 py-1.5\";\n\n return (\n <TooltipProvider>\n <div className={wrapper}>\n <div className={inner}>\n <IconBtn label=\"Tick\" icon={SkipForward} onClick={onStep} disabled={isRunning || isViewingPast} />\n {isRunning\n ? <IconBtn label=\"Pause\" icon={Pause} onClick={onPause} />\n : <IconBtn label=\"Run\" icon={Play} onClick={onRun} disabled={isViewingPast} />}\n <IconBtn label=\"Reset\" icon={RotateCcw} onClick={onReset} />\n\n {/* Speed slider */}\n {onSpeedChange && (\n <div className=\"flex items-center gap-2 border-l border-border pl-2 ml-0.5\">\n <Gauge className=\"h-3.5 w-3.5 text-muted-foreground/70\" aria-hidden />\n <input\n type=\"range\"\n min=\"1\"\n max={maxSpeed}\n value={speed}\n onChange={(e) => onSpeedChange(Number(e.target.value))}\n className=\"w-20 h-1 rounded-lg appearance-none cursor-pointer accent-blue-600\"\n disabled={isViewingPast}\n aria-label=\"Simulation speed\"\n />\n <span className=\"min-w-[55px] text-[10px] text-muted-foreground font-mono tabular-nums\">\n {speed} tick/s\n </span>\n </div>\n )}\n\n {/* Cycle counter */}\n <div className=\"border-l border-border pl-1.5 ml-0.5\">\n <span className=\"text-[11px] text-muted-foreground\">\n Cycle <span className=\"font-mono font-semibold text-foreground\">{cycle}</span>\n </span>\n </div>\n\n {/* Time-travel */}\n {historyLength > 1 && (\n <div className=\"flex items-center gap-0.5 border-l border-border pl-1.5 ml-0.5\">\n <IconBtn label=\"Step back\" icon={ChevronLeft} onClick={onStepBack} disabled={historyIndex <= 0 || isRunning} />\n\n <span className=\"min-w-[40px] text-center text-[11px] text-muted-foreground\">\n {isViewingPast ? (\n <span className=\"font-mono text-amber-600 dark:text-amber-400\">\n {historyIndex + 1}/{historyLength}\n </span>\n ) : (\n <span className=\"font-mono\">\n {historyLength}/{historyLength}\n </span>\n )}\n </span>\n\n <IconBtn label=\"Step forward\" icon={ChevronRight} onClick={onStepForward} disabled={!isViewingPast || isRunning} />\n </div>\n )}\n\n {/* Scrubber */}\n {showScrubber && onSeek && historyLength > 1 && (\n <div className=\"flex items-center gap-2 border-l border-border pl-2 ml-0.5\">\n <History className=\"h-3.5 w-3.5 text-muted-foreground/70\" aria-hidden />\n <input\n type=\"range\"\n min=\"0\"\n max={historyLength - 1}\n value={historyIndex}\n onChange={(e) => onSeek(Number(e.target.value))}\n className=\"w-24 h-1 rounded-lg appearance-none cursor-pointer accent-blue-600\"\n disabled={isRunning}\n aria-label=\"Cycle scrubber\"\n />\n </div>\n )}\n </div>\n </div>\n </TooltipProvider>\n );\n}\n"]}
1
+ {"version":3,"file":"ClockControls.js","sourceRoot":"","sources":["../../src/canvas/ClockControls.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unified ClockControls — one design, used everywhere.\n *\n * Icon buttons (Step, Play/Pause, Reset) + cycle counter + time-travel.\n * Optional: speed slider, scrubber.\n * Floating style — position via parent container.\n */\n\n\"use client\";\n\nimport type { ComponentType, SVGProps } from \"react\";\nimport {\n SkipForward,\n Play,\n Pause,\n RotateCcw,\n ChevronLeft,\n ChevronRight,\n Gauge,\n History,\n} from \"lucide-react\";\nimport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from \"../primitives/tooltip\";\nimport { Button } from \"../primitives/button\";\n\n/**\n * Dense icon button used throughout this toolbar. Wraps shadcn `Button`\n * with `ghost` + `icon` variants but tightens the size for the embed bar,\n * and bakes in the Tooltip so each call site is a one-liner. The `label`\n * doubles as `aria-label` and visible tooltip content.\n */\nfunction IconBtn({\n label,\n icon: Icon,\n onClick,\n disabled,\n pulse,\n}: {\n label: string;\n icon: ComponentType<SVGProps<SVGSVGElement>>;\n onClick: () => void;\n disabled?: boolean;\n /** Draw attention to this control (first-run \"press Run\" hint). */\n pulse?: boolean;\n}) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={onClick}\n disabled={disabled}\n aria-label={label}\n className={`h-7 w-7 text-muted-foreground disabled:opacity-40 [&_svg]:size-3.5${\n pulse\n ? \" animate-pulse text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-950/40 ring-2 ring-blue-500/40\"\n : \"\"\n }`}\n >\n <Icon />\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\">{label}</TooltipContent>\n </Tooltip>\n );\n}\n\nexport interface ClockControlsProps {\n cycle: number;\n historyLength: number;\n historyIndex: number;\n isRunning: boolean;\n isViewingPast: boolean;\n onStep: () => void;\n onRun: () => void;\n onPause: () => void;\n onReset: () => void;\n onStepBack: () => void;\n onStepForward: () => void;\n onSeek?: (index: number) => void;\n onSpeedChange?: (speed: number) => void;\n speed?: number;\n maxSpeed?: number;\n showScrubber?: boolean;\n /** Render as a floating pill (inspector/canvas overlay) vs inline bar (editor/embed bottom bar) */\n floating?: boolean;\n /** Drop the inner border + background when the parent already provides them */\n chromeless?: boolean;\n /** Pulse the Run button to hint \"press this to simulate\" (editor first-run only). */\n pulseRun?: boolean;\n}\n\nexport function ClockControls({\n cycle,\n historyLength,\n historyIndex,\n isRunning,\n isViewingPast,\n onStep,\n onRun,\n onPause,\n onReset,\n onStepBack,\n onStepForward,\n onSeek,\n onSpeedChange,\n speed = 1,\n maxSpeed = 100,\n showScrubber,\n floating,\n chromeless,\n pulseRun,\n}: ClockControlsProps) {\n const wrapper = floating\n ? \"absolute top-3 left-1/2 -translate-x-1/2 z-10\"\n : \"\";\n const inner = chromeless\n ? \"flex items-center gap-1.5\"\n : \"flex items-center gap-1.5 border-t border-border bg-card/95 px-3 py-1.5\";\n\n return (\n <TooltipProvider>\n <div className={wrapper}>\n <div className={inner}>\n <IconBtn label=\"Tick\" icon={SkipForward} onClick={onStep} disabled={isRunning || isViewingPast} />\n {isRunning\n ? <IconBtn label=\"Pause\" icon={Pause} onClick={onPause} />\n : <IconBtn label=\"Run\" icon={Play} onClick={onRun} disabled={isViewingPast} pulse={pulseRun && !isViewingPast} />}\n <IconBtn label=\"Reset\" icon={RotateCcw} onClick={onReset} />\n\n {/* Speed slider */}\n {onSpeedChange && (\n <div className=\"flex items-center gap-2 border-l border-border pl-2 ml-0.5\">\n <Gauge className=\"h-3.5 w-3.5 text-muted-foreground/70\" aria-hidden />\n <input\n type=\"range\"\n min=\"1\"\n max={maxSpeed}\n value={speed}\n onChange={(e) => onSpeedChange(Number(e.target.value))}\n className=\"w-20 h-1 rounded-lg appearance-none cursor-pointer accent-blue-600\"\n disabled={isViewingPast}\n aria-label=\"Simulation speed\"\n />\n <span className=\"min-w-[55px] text-[10px] text-muted-foreground font-mono tabular-nums\">\n {speed} tick/s\n </span>\n </div>\n )}\n\n {/* Cycle counter */}\n <div className=\"border-l border-border pl-1.5 ml-0.5\">\n <span className=\"text-[11px] text-muted-foreground\">\n Cycle <span className=\"font-mono font-semibold text-foreground\">{cycle}</span>\n </span>\n </div>\n\n {/* Time-travel */}\n {historyLength > 1 && (\n <div className=\"flex items-center gap-0.5 border-l border-border pl-1.5 ml-0.5\">\n <IconBtn label=\"Step back\" icon={ChevronLeft} onClick={onStepBack} disabled={historyIndex <= 0 || isRunning} />\n\n <span className=\"min-w-[40px] text-center text-[11px] text-muted-foreground\">\n {isViewingPast ? (\n <span className=\"font-mono text-amber-600 dark:text-amber-400\">\n {historyIndex + 1}/{historyLength}\n </span>\n ) : (\n <span className=\"font-mono\">\n {historyLength}/{historyLength}\n </span>\n )}\n </span>\n\n <IconBtn label=\"Step forward\" icon={ChevronRight} onClick={onStepForward} disabled={!isViewingPast || isRunning} />\n </div>\n )}\n\n {/* Scrubber */}\n {showScrubber && onSeek && historyLength > 1 && (\n <div className=\"flex items-center gap-2 border-l border-border pl-2 ml-0.5\">\n <History className=\"h-3.5 w-3.5 text-muted-foreground/70\" aria-hidden />\n <input\n type=\"range\"\n min=\"0\"\n max={historyLength - 1}\n value={historyIndex}\n onChange={(e) => onSeek(Number(e.target.value))}\n className=\"w-24 h-1 rounded-lg appearance-none cursor-pointer accent-blue-600\"\n disabled={isRunning}\n aria-label=\"Cycle scrubber\"\n />\n </div>\n )}\n </div>\n </div>\n </TooltipProvider>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simten/ui",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Canvas, editor, and waveform React components for Simten. Built on React Flow with dagre layout and Zustand stores.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Charles Harris",
@@ -88,7 +88,7 @@
88
88
  "radix-ui": "^1.4.3",
89
89
  "tailwind-merge": "^3.4.0",
90
90
  "zustand": "^5.0.10",
91
- "@simten/core": "0.5.0"
91
+ "@simten/core": "0.5.1"
92
92
  },
93
93
  "peerDependencies": {
94
94
  "react": "^18.0.0 || ^19.0.0",