my-you-eye 0.3.0 → 0.3.2

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.
package/COMPONENTS.md CHANGED
@@ -16,9 +16,9 @@ import "my-you-eye/styles.css";
16
16
  | Component | Variants | Demos |
17
17
  |---|---|---|
18
18
  | `Canvas` | — | Empty grid |
19
- | `ConnectionLine` | variant: bezier / stepped / straight<br>state: connected | Path variants, Edge states |
19
+ | `ConnectionLine` | variant: bezier / stepped / straight<br>state: connected / highlighted / pending | Path variants, Edge states, Decorations (arrowheads + labels) |
20
20
  | `Graph` | type: button | Pipeline editor (drag nodes, connect ports, delete selected) |
21
- | `GraphNode` | variant: muted / selected | Variants |
21
+ | `GraphNode` | variant: muted / selected / simple | Variants, Simple variant, Free-form body |
22
22
  | `Port` | state: connected / default / highlighted | States |
23
23
 
24
24
  ## data
@@ -38,7 +38,7 @@ import "my-you-eye/styles.css";
38
38
  | `Avatar` | size: lg / md / sm | Sizes, Fallback variants, With image, With ring, With status dot |
39
39
  | `Badge` | variant: danger / neutral / primary / success / warning<br>style: soft | Variants (solid), Variants (soft) |
40
40
  | `Card` | variant: danger / default / elevated / ghost / outlined<br>size: sm | Variants, With footer actions |
41
- | `CodeBlock` | variant: elevated | Bare (no header, no language), Language-only (badge overlay, no header bar), With header + language, Elevated, Line numbers, No wrap (horizontal scroll), Syntax highlighting (TS), Syntax highlighting (CSS / HTML / SQL / YAML / Python) |
41
+ | `CodeBlock` | variant: elevated | Bare (no header, no language), Language-only (badge overlay, no header bar), With header + language, Elevated, Line numbers, No wrap (horizontal scroll), Syntax highlighting (TS), Line highlights, Line highlights (implicit gutter), Multi-color highlights, Substring highlights, Syntax highlighting (CSS / HTML / SQL / YAML / Python) |
42
42
  | `EmptyState` | variant: secondary | Default, With icon and action |
43
43
  | `Image` | — | Fit modes, Border radius, Aspect ratio, Styles, With caption |
44
44
  | `Kbd` | — | Default, Combinations |
package/components.json CHANGED
@@ -23,12 +23,15 @@
23
23
  "straight"
24
24
  ],
25
25
  "state": [
26
- "connected"
26
+ "connected",
27
+ "highlighted",
28
+ "pending"
27
29
  ]
28
30
  },
29
31
  "demos": [
30
32
  "Path variants",
31
- "Edge states"
33
+ "Edge states",
34
+ "Decorations (arrowheads + labels)"
32
35
  ]
33
36
  },
34
37
  {
@@ -51,11 +54,14 @@
51
54
  "props": {
52
55
  "variant": [
53
56
  "muted",
54
- "selected"
57
+ "selected",
58
+ "simple"
55
59
  ]
56
60
  },
57
61
  "demos": [
58
- "Variants"
62
+ "Variants",
63
+ "Simple variant",
64
+ "Free-form body"
59
65
  ]
60
66
  },
61
67
  {
@@ -248,6 +254,10 @@
248
254
  "Line numbers",
249
255
  "No wrap (horizontal scroll)",
250
256
  "Syntax highlighting (TS)",
257
+ "Line highlights",
258
+ "Line highlights (implicit gutter)",
259
+ "Multi-color highlights",
260
+ "Substring highlights",
251
261
  "Syntax highlighting (CSS / HTML / SQL / YAML / Python)"
252
262
  ]
253
263
  },
package/dist/index.d.ts CHANGED
@@ -404,7 +404,7 @@ interface CanvasProps extends HTMLAttributes<HTMLDivElement> {
404
404
  declare const Canvas: react.ForwardRefExoticComponent<CanvasProps & react.RefAttributes<HTMLDivElement>>;
405
405
 
406
406
  declare const graphNodeVariants: (props?: ({
407
- variant?: "default" | "selected" | "muted" | null | undefined;
407
+ variant?: "default" | "selected" | "muted" | "simple" | null | undefined;
408
408
  } & class_variance_authority_types.ClassProp) | undefined) => string;
409
409
  interface PortDef {
410
410
  side: "left" | "right";
@@ -472,6 +472,16 @@ declare function TreeView({ data, variant, indent, defaultExpandedDepth, expande
472
472
  declare const codeBlockVariants: (props?: ({
473
473
  variant?: "default" | "elevated" | null | undefined;
474
474
  } & class_variance_authority_types.ClassProp) | undefined) => string;
475
+ interface CodeBlockHighlightGroup {
476
+ lines: number[];
477
+ color?: "primary" | "warning" | "success" | "danger";
478
+ }
479
+ interface HighlightRangeDef {
480
+ line: number;
481
+ start: number;
482
+ end: number;
483
+ color?: "primary" | "warning" | "success" | "danger";
484
+ }
475
485
  interface CodeBlockProps extends HTMLAttributes<HTMLPreElement>, VariantProps<typeof codeBlockVariants> {
476
486
  code: string;
477
487
  language?: string;
@@ -480,6 +490,14 @@ interface CodeBlockProps extends HTMLAttributes<HTMLPreElement>, VariantProps<ty
480
490
  showLineNumbers?: boolean;
481
491
  /** Enable syntax highlighting for supported languages (js, ts, tsx, json, bash). */
482
492
  highlight?: boolean;
493
+ /** 1-indexed line numbers to highlight. Implicitly enables line numbers. */
494
+ highlightLines?: number[];
495
+ /** Color for highlightLines (default "primary"). */
496
+ highlightColor?: CodeBlockHighlightGroup["color"];
497
+ /** Multi-color highlight groups. When provided, takes precedence over highlightLines. */
498
+ highlightGroups?: CodeBlockHighlightGroup[];
499
+ /** Substring-level highlight ranges (0-indexed char positions within each line). */
500
+ highlightRanges?: HighlightRangeDef[];
483
501
  }
484
502
  declare const CodeBlock: react.ForwardRefExoticComponent<CodeBlockProps & react.RefAttributes<HTMLPreElement>>;
485
503
 
@@ -624,6 +642,8 @@ interface ConnectionLineProps extends VariantProps<typeof lineVariants> {
624
642
  x: number;
625
643
  y: number;
626
644
  };
645
+ arrowhead?: boolean;
646
+ label?: string;
627
647
  className?: string;
628
648
  }
629
649
  declare const ConnectionLine: react.ForwardRefExoticComponent<ConnectionLineProps & react.RefAttributes<SVGSVGElement>>;
package/dist/index.js CHANGED
@@ -2355,12 +2355,37 @@ function tokenize(code, language) {
2355
2355
  if (!lang || !TOKENIZERS[lang]) return null;
2356
2356
  return TOKENIZERS[lang](code);
2357
2357
  }
2358
- function renderHighlighted(tokens) {
2358
+ function splitTokensByLine(tokens) {
2359
+ const result = [];
2360
+ let current = [];
2361
+ for (const t of tokens) {
2362
+ if (t.text === "\n") {
2363
+ result.push(current);
2364
+ current = [];
2365
+ continue;
2366
+ }
2367
+ if (t.text.includes("\n")) {
2368
+ const parts = t.text.split("\n");
2369
+ for (let i = 0; i < parts.length; i++) {
2370
+ if (i > 0) {
2371
+ result.push(current);
2372
+ current = [];
2373
+ }
2374
+ if (parts[i]) current.push({ text: parts[i], className: t.className });
2375
+ }
2376
+ } else {
2377
+ current.push(t);
2378
+ }
2379
+ }
2380
+ if (current.length > 0) result.push(current);
2381
+ return result;
2382
+ }
2383
+ function renderHighlightedLine(tokens) {
2359
2384
  return /* @__PURE__ */ jsx45(Fragment, { children: tokens.map((t, i) => /* @__PURE__ */ jsx45("span", { className: t.className === "whitespace" ? void 0 : `hl-${t.className}`, children: t.text }, i)) });
2360
2385
  }
2361
2386
 
2362
2387
  // src/ui/code-block/CodeBlock.tsx
2363
- import { jsx as jsx46, jsxs as jsxs19 } from "react/jsx-runtime";
2388
+ import { Fragment as Fragment2, jsx as jsx46, jsxs as jsxs19 } from "react/jsx-runtime";
2364
2389
  var codeBlockVariants = cva25(
2365
2390
  "group relative overflow-clip rounded-ui border border-border bg-code-bg text-sm flex flex-col",
2366
2391
  {
@@ -2406,7 +2431,7 @@ function CopyButton({ copied, onCopy }) {
2406
2431
  );
2407
2432
  }
2408
2433
  var CodeBlock = forwardRef42(
2409
- ({ className, variant, code, language, header, wrap = true, showLineNumbers = false, highlight = false, ...props }, ref) => {
2434
+ ({ className, variant, code, language, header, wrap = true, showLineNumbers = false, highlight = false, highlightLines, highlightColor = "primary", highlightGroups, highlightRanges, ...props }, ref) => {
2410
2435
  const [copied, setCopied] = useState2(false);
2411
2436
  const copy = useCallback3(() => {
2412
2437
  navigator.clipboard.writeText(code).then(() => {
@@ -2416,14 +2441,72 @@ var CodeBlock = forwardRef42(
2416
2441
  }, [code]);
2417
2442
  const lines = useMemo(() => code.split("\n"), [code]);
2418
2443
  const hasHeader = Boolean(header || language);
2444
+ const showGutter = showLineNumbers || highlightLines != null && highlightLines.length > 0 || highlightGroups != null && highlightGroups.length > 0 || highlightRanges != null && highlightRanges.length > 0;
2419
2445
  const highlighted = useMemo(() => {
2420
2446
  if (!highlight) return null;
2421
2447
  return tokenize(code, language);
2422
2448
  }, [code, language, highlight]);
2423
- const renderCode = () => {
2424
- if (highlighted) return renderHighlighted(highlighted);
2425
- return code;
2449
+ const HIGHLIGHT_BG = {
2450
+ primary: "bg-primary/10",
2451
+ warning: "bg-warning/15",
2452
+ success: "bg-success/10",
2453
+ danger: "bg-danger/10"
2454
+ };
2455
+ const lineColor = useMemo(() => {
2456
+ const map = /* @__PURE__ */ new Map();
2457
+ if (highlightGroups) {
2458
+ for (const g of highlightGroups) {
2459
+ const cls = HIGHLIGHT_BG[g.color ?? "primary"] ?? HIGHLIGHT_BG.primary;
2460
+ for (const ln of g.lines) map.set(ln, cls);
2461
+ }
2462
+ } else if (highlightLines) {
2463
+ const cls = HIGHLIGHT_BG[highlightColor ?? "primary"] ?? HIGHLIGHT_BG.primary;
2464
+ for (const ln of highlightLines) map.set(ln, cls);
2465
+ }
2466
+ return map;
2467
+ }, [highlightLines, highlightColor, highlightGroups]);
2468
+ const perLineTokens = useMemo(() => {
2469
+ if (!highlighted) return null;
2470
+ return splitTokensByLine(highlighted);
2471
+ }, [highlighted]);
2472
+ const SUBSTR_BG = {
2473
+ primary: "bg-primary/15",
2474
+ warning: "bg-warning/20",
2475
+ success: "bg-success/15",
2476
+ danger: "bg-danger/15"
2426
2477
  };
2478
+ const lineRanges = useMemo(() => {
2479
+ const map = /* @__PURE__ */ new Map();
2480
+ if (!highlightRanges) return map;
2481
+ for (const r of highlightRanges) {
2482
+ const line = r.line;
2483
+ if (!map.has(line)) map.set(line, []);
2484
+ map.get(line).push({ start: r.start, end: r.end, color: r.color ?? "primary" });
2485
+ }
2486
+ return map;
2487
+ }, [highlightRanges]);
2488
+ function segmentedLine(line, ranges) {
2489
+ const sorted = [...ranges].sort((a, b) => a.start - b.start);
2490
+ const parts = [];
2491
+ let pos = 0;
2492
+ for (let i = 0; i < sorted.length; i++) {
2493
+ const r = sorted[i];
2494
+ const s = Math.max(r.start, 0);
2495
+ const e = Math.min(r.end, line.length);
2496
+ if (s > pos) parts.push(/* @__PURE__ */ jsx46("span", { children: line.slice(pos, s) }, `t${i}`));
2497
+ if (e > s) {
2498
+ const bg = SUBSTR_BG[r.color] ?? SUBSTR_BG.primary;
2499
+ parts.push(
2500
+ /* @__PURE__ */ jsx46("span", { className: cn(bg, "rounded-sm px-0.5 -mx-0.5"), children: line.slice(s, e) }, `h${i}`)
2501
+ );
2502
+ pos = e;
2503
+ }
2504
+ while (i + 1 < sorted.length && sorted[i + 1].start < pos) i++;
2505
+ }
2506
+ if (pos < line.length) parts.push(/* @__PURE__ */ jsx46("span", { children: line.slice(pos) }, "end"));
2507
+ if (parts.length === 0) parts.push(/* @__PURE__ */ jsx46("span", { children: "\xA0" }, "e"));
2508
+ return /* @__PURE__ */ jsx46(Fragment2, { children: parts });
2509
+ }
2427
2510
  return /* @__PURE__ */ jsxs19("div", { className: cn(codeBlockVariants({ variant }), className), children: [
2428
2511
  hasHeader && /* @__PURE__ */ jsxs19("div", { className: "flex items-center justify-between gap-2 h-9 pl-panel pr-1.5 border-b border-border", children: [
2429
2512
  /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2 min-w-0", children: [
@@ -2437,25 +2520,42 @@ var CodeBlock = forwardRef42(
2437
2520
  language && /* @__PURE__ */ jsx46("span", { className: "rounded-ui-sm bg-code-bg/80 px-1.5 py-0.5 font-mono text-xs uppercase tracking-wide text-code-muted border border-border/50 pointer-events-none", children: language }),
2438
2521
  /* @__PURE__ */ jsx46(CopyButton, { copied, onCopy: copy })
2439
2522
  ] }) }),
2440
- /* @__PURE__ */ jsxs19("div", { className: "flex flex-1 min-h-0", children: [
2441
- showLineNumbers && /* @__PURE__ */ jsx46(
2523
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-1 min-h-0 overflow-x-auto", children: [
2524
+ showGutter && /* @__PURE__ */ jsx46(
2442
2525
  "div",
2443
2526
  {
2444
2527
  "aria-hidden": true,
2445
- className: "select-none shrink-0 py-panel pl-compact-x pr-compact-x text-right font-mono text-xs leading-relaxed text-code-muted border-r border-border",
2446
- children: lines.map((_, i) => /* @__PURE__ */ jsx46("div", { children: i + 1 }, i))
2528
+ className: "select-none shrink-0 py-panel text-right font-mono text-xs leading-relaxed text-code-muted border-r border-border",
2529
+ children: lines.map((_, i) => /* @__PURE__ */ jsx46("div", { className: cn("pl-compact-x pr-compact-x", lineColor.get(i + 1)), children: i + 1 }, i))
2447
2530
  }
2448
2531
  ),
2449
- /* @__PURE__ */ jsx46(
2532
+ perLineTokens ? /* @__PURE__ */ jsx46(
2533
+ "pre",
2534
+ {
2535
+ ref,
2536
+ className: cn(
2537
+ "flex-1 min-w-0 py-panel font-mono text-xs leading-relaxed text-code-fg",
2538
+ wrap && "whitespace-pre-wrap break-words"
2539
+ ),
2540
+ ...props,
2541
+ children: /* @__PURE__ */ jsx46("code", { children: perLineTokens.map((lineTokens, i) => {
2542
+ const r = lineRanges.get(i + 1);
2543
+ return /* @__PURE__ */ jsx46("div", { className: cn("px-panel", lineColor.get(i + 1)), children: r ? segmentedLine(lines[i], r) : lineTokens.length > 0 ? renderHighlightedLine(lineTokens) : " " }, i);
2544
+ }) })
2545
+ }
2546
+ ) : /* @__PURE__ */ jsx46(
2450
2547
  "pre",
2451
2548
  {
2452
2549
  ref,
2453
2550
  className: cn(
2454
- "flex-1 min-w-0 p-panel font-mono text-xs leading-relaxed overflow-x-auto text-code-fg",
2551
+ "flex-1 min-w-0 py-panel font-mono text-xs leading-relaxed text-code-fg",
2455
2552
  wrap && "whitespace-pre-wrap break-words"
2456
2553
  ),
2457
2554
  ...props,
2458
- children: /* @__PURE__ */ jsx46("code", { children: renderCode() })
2555
+ children: /* @__PURE__ */ jsx46("code", { children: lines.map((line, i) => {
2556
+ const r = lineRanges.get(i + 1);
2557
+ return /* @__PURE__ */ jsx46("div", { className: cn("px-panel", lineColor.get(i + 1)), children: r ? segmentedLine(line, r) : line || " " }, i);
2558
+ }) })
2459
2559
  }
2460
2560
  )
2461
2561
  ] })
@@ -2734,7 +2834,7 @@ function TreeView({
2734
2834
  }
2735
2835
 
2736
2836
  // src/ui/cell-type/CellType.complex-displays.tsx
2737
- import { Fragment as Fragment2, jsx as jsx50, jsxs as jsxs21 } from "react/jsx-runtime";
2837
+ import { Fragment as Fragment3, jsx as jsx50, jsxs as jsxs21 } from "react/jsx-runtime";
2738
2838
  function safeStringify(value) {
2739
2839
  const seen = /* @__PURE__ */ new WeakSet();
2740
2840
  return JSON.stringify(value, (_, val) => {
@@ -2807,7 +2907,7 @@ function JsonDisplay({ value }) {
2807
2907
  const preview = jsonPreview(value);
2808
2908
  if (!preview) return /* @__PURE__ */ jsx50("span", { className: "text-muted", children: "\u2014" });
2809
2909
  return /* @__PURE__ */ jsxs21(Root9, { children: [
2810
- /* @__PURE__ */ jsx50(Trigger5, { className: "font-mono text-xs cursor-pointer hover:text-primary transition-colors flex w-full max-w-full min-w-0 items-center gap-1.5", children: count === 0 ? /* @__PURE__ */ jsx50("span", { className: "text-muted italic", children: "empty" }) : /* @__PURE__ */ jsxs21(Fragment2, { children: [
2910
+ /* @__PURE__ */ jsx50(Trigger5, { className: "font-mono text-xs cursor-pointer hover:text-primary transition-colors flex w-full max-w-full min-w-0 items-center gap-1.5", children: count === 0 ? /* @__PURE__ */ jsx50("span", { className: "text-muted italic", children: "empty" }) : /* @__PURE__ */ jsxs21(Fragment3, { children: [
2811
2911
  /* @__PURE__ */ jsxs21(Badge, { variant: "neutral", style: "soft", className: "text-xs px-1 py-0 leading-none shrink-0", children: [
2812
2912
  count,
2813
2913
  " ",
@@ -2872,7 +2972,7 @@ function TreeDisplay({ value, replacements }) {
2872
2972
  return () => ro.disconnect();
2873
2973
  }, [value]);
2874
2974
  return /* @__PURE__ */ jsxs21(Root9, { children: [
2875
- /* @__PURE__ */ jsx50(Trigger5, { className: "font-mono text-xs cursor-pointer hover:text-primary transition-colors flex w-full max-w-full min-w-0 items-center gap-1.5", children: count === 0 ? /* @__PURE__ */ jsx50("span", { className: "text-muted italic", children: "empty" }) : /* @__PURE__ */ jsxs21(Fragment2, { children: [
2975
+ /* @__PURE__ */ jsx50(Trigger5, { className: "font-mono text-xs cursor-pointer hover:text-primary transition-colors flex w-full max-w-full min-w-0 items-center gap-1.5", children: count === 0 ? /* @__PURE__ */ jsx50("span", { className: "text-muted italic", children: "empty" }) : /* @__PURE__ */ jsxs21(Fragment3, { children: [
2876
2976
  /* @__PURE__ */ jsxs21(Badge, { variant: "neutral", style: "soft", className: "text-xs px-1 py-0 leading-none shrink-0", children: [
2877
2977
  count,
2878
2978
  " ",
@@ -2912,7 +3012,7 @@ function ArrayDisplay({ value }) {
2912
3012
  return () => ro.disconnect();
2913
3013
  }, [value]);
2914
3014
  return /* @__PURE__ */ jsxs21(Root9, { children: [
2915
- /* @__PURE__ */ jsx50(Trigger5, { className: "font-mono text-xs cursor-pointer hover:text-primary transition-colors flex w-full max-w-full min-w-0 items-center gap-1.5", children: count === 0 ? /* @__PURE__ */ jsx50("span", { className: "text-muted italic", children: "empty" }) : /* @__PURE__ */ jsxs21(Fragment2, { children: [
3015
+ /* @__PURE__ */ jsx50(Trigger5, { className: "font-mono text-xs cursor-pointer hover:text-primary transition-colors flex w-full max-w-full min-w-0 items-center gap-1.5", children: count === 0 ? /* @__PURE__ */ jsx50("span", { className: "text-muted italic", children: "empty" }) : /* @__PURE__ */ jsxs21(Fragment3, { children: [
2916
3016
  /* @__PURE__ */ jsxs21(Badge, { variant: "neutral", style: "soft", className: "text-xs px-1 py-0 leading-none shrink-0", children: [
2917
3017
  count,
2918
3018
  " items"
@@ -2942,7 +3042,7 @@ function ArrayDisplay({ value }) {
2942
3042
  }
2943
3043
 
2944
3044
  // src/ui/cell-type/CellType.tsx
2945
- import { Fragment as Fragment3, jsx as jsx51, jsxs as jsxs22 } from "react/jsx-runtime";
3045
+ import { Fragment as Fragment4, jsx as jsx51, jsxs as jsxs22 } from "react/jsx-runtime";
2946
3046
  function BooleanDisplay({ value }) {
2947
3047
  const t = Boolean(value);
2948
3048
  return /* @__PURE__ */ jsx51("span", { className: cn("inline-flex items-center", t ? "text-success" : "text-muted"), children: /* @__PURE__ */ jsx51("svg", { viewBox: "0 0 12 12", className: "size-icon fill-current", children: t ? /* @__PURE__ */ jsx51("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }) : /* @__PURE__ */ jsx51("path", { d: "M2 2l8 8M10 2L2 10", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }) }) });
@@ -2956,7 +3056,7 @@ function applyReplacements(str, replacements) {
2956
3056
  function ImageDisplay({ value }) {
2957
3057
  const src = String(value);
2958
3058
  const [open, setOpen] = useState5(false);
2959
- return /* @__PURE__ */ jsxs22(Fragment3, { children: [
3059
+ return /* @__PURE__ */ jsxs22(Fragment4, { children: [
2960
3060
  /* @__PURE__ */ jsx51("img", { src, alt: "", className: "size-8 rounded-ui-sm object-cover border border-border cursor-pointer hover:opacity-80 transition-opacity", onClick: () => setOpen(true) }),
2961
3061
  /* @__PURE__ */ jsx51(Root6, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs22(DialogContent, { className: "p-0 overflow-hidden max-w-[90vw] w-auto", children: [
2962
3062
  /* @__PURE__ */ jsx51(DialogTitle, { className: "sr-only", children: "Image preview" }),
@@ -3148,7 +3248,7 @@ function snap(v) {
3148
3248
  }
3149
3249
 
3150
3250
  // src/ui/canvas/Canvas.tsx
3151
- import { Fragment as Fragment4, jsx as jsx52, jsxs as jsxs23 } from "react/jsx-runtime";
3251
+ import { Fragment as Fragment5, jsx as jsx52, jsxs as jsxs23 } from "react/jsx-runtime";
3152
3252
  var btn = "inline-flex items-center justify-center size-7 rounded-ui-sm border border-border bg-bg text-xs text-fg hover:bg-secondary cursor-pointer";
3153
3253
  var Canvas = forwardRef45(
3154
3254
  ({ className, gridSize = GRID, initialZoom = 1, minZoom = 0.25, maxZoom = 3, zoomStep = 0.1, controls, children, style, onBackgroundClick, offset: controlledOffset, zoom: controlledZoom, onOffsetChange, onZoomChange, ...props }, ref) => {
@@ -3301,7 +3401,7 @@ var Canvas = forwardRef45(
3301
3401
  "%"
3302
3402
  ] }),
3303
3403
  /* @__PURE__ */ jsx52("button", { type: "button", className: btn, onClick: zoomIn, title: "Zoom in", children: "+" }),
3304
- controls && /* @__PURE__ */ jsxs23(Fragment4, { children: [
3404
+ controls && /* @__PURE__ */ jsxs23(Fragment5, { children: [
3305
3405
  /* @__PURE__ */ jsx52("div", { className: "w-px h-4 bg-border mx-0.5" }),
3306
3406
  controls
3307
3407
  ] })
@@ -3320,7 +3420,7 @@ import { cva as cva28 } from "class-variance-authority";
3320
3420
  // src/ui/connection-line/ConnectionLine.tsx
3321
3421
  import { forwardRef as forwardRef46, useMemo as useMemo3 } from "react";
3322
3422
  import { cva as cva27 } from "class-variance-authority";
3323
- import { jsx as jsx53 } from "react/jsx-runtime";
3423
+ import { jsx as jsx53, jsxs as jsxs24 } from "react/jsx-runtime";
3324
3424
  var lineVariants = cva27("fill-none", {
3325
3425
  variants: {
3326
3426
  variant: {
@@ -3355,10 +3455,31 @@ function generatePath(from, to, variant) {
3355
3455
  }
3356
3456
  }
3357
3457
  }
3458
+ function getArrowAngle(from, to, variant) {
3459
+ switch (variant) {
3460
+ case "stepped": {
3461
+ const midX = (from.x + to.x) / 2;
3462
+ return to.x >= midX ? 0 : 180;
3463
+ }
3464
+ case "straight":
3465
+ return Math.atan2(to.y - from.y, to.x - from.x) * (180 / Math.PI);
3466
+ default:
3467
+ return 0;
3468
+ }
3469
+ }
3470
+ var ARROWHEAD_COLOR = {
3471
+ connected: "text-primary",
3472
+ highlighted: "text-primary",
3473
+ default: "text-muted",
3474
+ pending: "text-muted"
3475
+ };
3358
3476
  var ConnectionLine = forwardRef46(
3359
- function ConnectionLine2({ className, variant = "bezier", state = "connected", from, to }, ref) {
3477
+ function ConnectionLine2({ className, variant = "bezier", state = "connected", from, to, arrowhead, label }, ref) {
3360
3478
  const d = useMemo3(() => generatePath(from, to, variant ?? "bezier"), [from, to, variant]);
3361
- return /* @__PURE__ */ jsx53(
3479
+ const arrowAngle = useMemo3(() => getArrowAngle(from, to, variant ?? "bezier"), [from, to, variant]);
3480
+ const midX = (from.x + to.x) / 2;
3481
+ const midY = (from.y + to.y) / 2;
3482
+ return /* @__PURE__ */ jsxs24(
3362
3483
  "svg",
3363
3484
  {
3364
3485
  ref,
@@ -3367,7 +3488,36 @@ var ConnectionLine = forwardRef46(
3367
3488
  "absolute inset-0 w-full h-full pointer-events-none overflow-visible",
3368
3489
  className
3369
3490
  ),
3370
- children: /* @__PURE__ */ jsx53("path", { d })
3491
+ children: [
3492
+ /* @__PURE__ */ jsx53("path", { d }),
3493
+ arrowhead && /* @__PURE__ */ jsx53(
3494
+ "polygon",
3495
+ {
3496
+ points: "0,-4 8,0 0,4",
3497
+ fill: "currentColor",
3498
+ className: ARROWHEAD_COLOR[state ?? "connected"],
3499
+ transform: `translate(${to.x},${to.y}) rotate(${arrowAngle})`
3500
+ }
3501
+ ),
3502
+ label && /* @__PURE__ */ jsx53(
3503
+ "foreignObject",
3504
+ {
3505
+ x: midX - label.length * 3.5 - 6,
3506
+ y: midY - 10,
3507
+ width: label.length * 7 + 12,
3508
+ height: 20,
3509
+ className: "overflow-visible pointer-events-none",
3510
+ children: /* @__PURE__ */ jsx53(
3511
+ "span",
3512
+ {
3513
+ className: "inline-flex items-center rounded-sm px-1.5 py-0.5 text-xs text-fg font-mono bg-canvas-surface border border-border/60 shadow-sm whitespace-nowrap leading-none",
3514
+ style: { backdropFilter: "blur(2px)" },
3515
+ children: label
3516
+ }
3517
+ )
3518
+ }
3519
+ )
3520
+ ]
3371
3521
  }
3372
3522
  );
3373
3523
  }
@@ -3375,7 +3525,7 @@ var ConnectionLine = forwardRef46(
3375
3525
  ConnectionLine.displayName = "ConnectionLine";
3376
3526
 
3377
3527
  // src/ui/canvas/Edge.tsx
3378
- import { jsx as jsx54, jsxs as jsxs24 } from "react/jsx-runtime";
3528
+ import { jsx as jsx54, jsxs as jsxs25 } from "react/jsx-runtime";
3379
3529
  var edgeVariants = cva28("fill-none", {
3380
3530
  variants: {
3381
3531
  state: {
@@ -3392,7 +3542,7 @@ var edgeVariants = cva28("fill-none", {
3392
3542
  var HIT_STROKE = 12;
3393
3543
  function Edge({ from, to, state, className, hitStrokeWidth = HIT_STROKE, onClick, onContextMenu, ...props }) {
3394
3544
  const d = useMemo4(() => generatePath(from, to, "bezier"), [from, to]);
3395
- return /* @__PURE__ */ jsxs24("g", { onMouseDown: (e) => e.stopPropagation(), children: [
3545
+ return /* @__PURE__ */ jsxs25("g", { onMouseDown: (e) => e.stopPropagation(), children: [
3396
3546
  onClick || onContextMenu ? /* @__PURE__ */ jsx54(
3397
3547
  "path",
3398
3548
  {
@@ -3416,7 +3566,7 @@ import { cva as cva30 } from "class-variance-authority";
3416
3566
  // src/ui/port/Port.tsx
3417
3567
  import { forwardRef as forwardRef47 } from "react";
3418
3568
  import { cva as cva29 } from "class-variance-authority";
3419
- import { jsx as jsx55, jsxs as jsxs25 } from "react/jsx-runtime";
3569
+ import { jsx as jsx55, jsxs as jsxs26 } from "react/jsx-runtime";
3420
3570
  var portVariants = cva29(
3421
3571
  "size-3 rounded-full border-2 bg-bg transition-colors",
3422
3572
  {
@@ -3438,7 +3588,7 @@ var portVariants = cva29(
3438
3588
  }
3439
3589
  );
3440
3590
  var Port = forwardRef47(
3441
- ({ className, side, state, label, ...props }, ref) => /* @__PURE__ */ jsxs25("div", { ref, className: cn("relative flex items-center gap-2", side === "in" ? "flex-row" : "flex-row-reverse"), children: [
3591
+ ({ className, side, state, label, ...props }, ref) => /* @__PURE__ */ jsxs26("div", { ref, className: cn("relative flex items-center gap-2", side === "in" ? "flex-row" : "flex-row-reverse"), children: [
3442
3592
  /* @__PURE__ */ jsx55("div", { className: cn(portVariants({ side, state }), className), ...props }),
3443
3593
  label && /* @__PURE__ */ jsx55("span", { className: "text-xs text-muted", children: label })
3444
3594
  ] })
@@ -3446,7 +3596,7 @@ var Port = forwardRef47(
3446
3596
  Port.displayName = "Port";
3447
3597
 
3448
3598
  // src/ui/graph-node/GraphNode.tsx
3449
- import { jsx as jsx56, jsxs as jsxs26 } from "react/jsx-runtime";
3599
+ import { jsx as jsx56, jsxs as jsxs27 } from "react/jsx-runtime";
3450
3600
  var ROW_PORT_Y_OFFSET = HEADER * GRID;
3451
3601
  var graphNodeVariants = cva30(
3452
3602
  // `bg-canvas-surface` (never `bg-surface`): nodes render an opaque,
@@ -3460,7 +3610,8 @@ var graphNodeVariants = cva30(
3460
3610
  variant: {
3461
3611
  default: "border-border",
3462
3612
  selected: "border-primary ring-2 ring-primary/20",
3463
- muted: "border-border opacity-dim"
3613
+ muted: "border-border opacity-dim",
3614
+ simple: "border-border"
3464
3615
  }
3465
3616
  },
3466
3617
  defaultVariants: {
@@ -3470,9 +3621,10 @@ var graphNodeVariants = cva30(
3470
3621
  );
3471
3622
  var GraphNode = forwardRef48(
3472
3623
  ({ className, variant, x, y, header, accent, ports, footer, rows, children, style, ...props }, ref) => {
3624
+ const isSimple = variant === "simple";
3473
3625
  const hasRows = rows && rows.length > 0;
3474
- const height = hasRows ? nodeHeightPx(rows.length, !!footer) : void 0;
3475
- return /* @__PURE__ */ jsxs26(
3626
+ const height = hasRows ? nodeHeightPx(rows.length, !!footer && !isSimple) : void 0;
3627
+ return /* @__PURE__ */ jsxs27(
3476
3628
  "div",
3477
3629
  {
3478
3630
  ref,
@@ -3480,16 +3632,16 @@ var GraphNode = forwardRef48(
3480
3632
  style: { left: x, top: y, height, ...style },
3481
3633
  ...props,
3482
3634
  children: [
3483
- header && /* @__PURE__ */ jsx56("div", { className: cn("flex flex-col shrink-0", accent && "border-t-2 border-primary"), style: { height: HEADER * GRID }, children: /* @__PURE__ */ jsx56("div", { className: "flex items-center px-3 border-b border-border flex-1 min-h-0", children: /* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-inline flex-1 min-w-0", children: [
3484
- /* @__PURE__ */ jsxs26("div", { className: "flex gap-0.5", children: [
3635
+ header && /* @__PURE__ */ jsx56("div", { className: cn("flex flex-col shrink-0", accent && !isSimple && "border-t-2 border-primary"), style: { height: HEADER * GRID }, children: /* @__PURE__ */ jsx56("div", { className: "flex items-center px-3 border-b border-border flex-1 min-h-0", children: /* @__PURE__ */ jsxs27("div", { className: cn("flex items-center gap-inline flex-1 min-w-0", isSimple && "justify-center"), children: [
3636
+ !isSimple && /* @__PURE__ */ jsxs27("div", { className: "flex gap-0.5", children: [
3485
3637
  /* @__PURE__ */ jsx56("span", { className: "size-1.5 rounded-full bg-danger" }),
3486
3638
  /* @__PURE__ */ jsx56("span", { className: "size-1.5 rounded-full bg-warning" }),
3487
3639
  /* @__PURE__ */ jsx56("span", { className: "size-1.5 rounded-full bg-success" })
3488
3640
  ] }),
3489
3641
  /* @__PURE__ */ jsx56("span", { className: "text-xs font-semibold truncate", children: header })
3490
3642
  ] }) }) }),
3491
- hasRows ? /* @__PURE__ */ jsxs26("div", { className: "flex-1 overflow-hidden relative", children: [
3492
- rows.map((row, i) => /* @__PURE__ */ jsxs26(
3643
+ hasRows ? /* @__PURE__ */ jsxs27("div", { className: "flex-1 overflow-hidden relative", children: [
3644
+ rows.map((row, i) => /* @__PURE__ */ jsxs27(
3493
3645
  "div",
3494
3646
  {
3495
3647
  className: "grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 px-3 text-xs items-center border-b border-border/50 last:border-b-0 hover:bg-muted/10 transition-colors",
@@ -3501,7 +3653,7 @@ var GraphNode = forwardRef48(
3501
3653
  },
3502
3654
  i
3503
3655
  )),
3504
- /* @__PURE__ */ jsx56("div", { className: "absolute inset-0 pointer-events-none", children: rows.map((row, i) => /* @__PURE__ */ jsxs26("div", { children: [
3656
+ !isSimple && /* @__PURE__ */ jsx56("div", { className: "absolute inset-0 pointer-events-none", children: rows.map((row, i) => /* @__PURE__ */ jsxs27("div", { children: [
3505
3657
  row.portLeft && /* @__PURE__ */ jsx56(
3506
3658
  "div",
3507
3659
  {
@@ -3520,14 +3672,14 @@ var GraphNode = forwardRef48(
3520
3672
  )
3521
3673
  ] }, `ports-${i}`)) })
3522
3674
  ] }) : children && /* @__PURE__ */ jsx56("div", { className: "px-3 py-2 text-xs flex-1", children }),
3523
- ports && ports.length > 0 && !hasRows && /* @__PURE__ */ jsx56("div", { className: "absolute inset-0 pointer-events-none", children: ports.map((p, i) => {
3675
+ ports && ports.length > 0 && !hasRows && !isSimple && /* @__PURE__ */ jsx56("div", { className: "absolute inset-0 pointer-events-none", children: ports.map((p, i) => {
3524
3676
  const leftPorts = ports.filter((x2) => x2.side === "left");
3525
3677
  const rightPorts = ports.filter((x2) => x2.side === "right");
3526
3678
  const isLeft = p.side === "left";
3527
3679
  const idx = isLeft ? leftPorts.indexOf(p) : rightPorts.indexOf(p);
3528
3680
  const total = isLeft ? leftPorts.length : rightPorts.length;
3529
3681
  const yPos = (HEADER * GRID + GRID) / (total + 1) * (idx + 1);
3530
- return /* @__PURE__ */ jsxs26(
3682
+ return /* @__PURE__ */ jsxs27(
3531
3683
  "div",
3532
3684
  {
3533
3685
  className: cn(
@@ -3543,7 +3695,7 @@ var GraphNode = forwardRef48(
3543
3695
  i
3544
3696
  );
3545
3697
  }) }),
3546
- footer && /* @__PURE__ */ jsx56(
3698
+ footer && !isSimple && /* @__PURE__ */ jsx56(
3547
3699
  "div",
3548
3700
  {
3549
3701
  className: "px-3 border-t border-border text-xs text-muted flex items-center gap-1.5 shrink-0 bg-muted/5",
@@ -3568,7 +3720,7 @@ Typography.displayName = "Typography";
3568
3720
 
3569
3721
  // src/ui/combobox/Combobox.tsx
3570
3722
  import { forwardRef as forwardRef50, useState as useState7, useMemo as useMemo5, useRef as useRef6 } from "react";
3571
- import { jsx as jsx58, jsxs as jsxs27 } from "react/jsx-runtime";
3723
+ import { jsx as jsx58, jsxs as jsxs28 } from "react/jsx-runtime";
3572
3724
  var Combobox = forwardRef50(
3573
3725
  function Combobox2({ options, value, onChange, placeholder = "Search...", emptyText = "No results found", className, disabled }, ref) {
3574
3726
  const [open, setOpen] = useState7(false);
@@ -3579,11 +3731,11 @@ var Combobox = forwardRef50(
3579
3731
  [options, query]
3580
3732
  );
3581
3733
  const selectedLabel = options.find((o) => o.value === value)?.label ?? "";
3582
- return /* @__PURE__ */ jsxs27(Root9, { open, onOpenChange: (v) => {
3734
+ return /* @__PURE__ */ jsxs28(Root9, { open, onOpenChange: (v) => {
3583
3735
  setOpen(v);
3584
3736
  if (!v) setQuery("");
3585
3737
  }, children: [
3586
- /* @__PURE__ */ jsx58(Trigger5, { asChild: true, children: /* @__PURE__ */ jsxs27(
3738
+ /* @__PURE__ */ jsx58(Trigger5, { asChild: true, children: /* @__PURE__ */ jsxs28(
3587
3739
  "button",
3588
3740
  {
3589
3741
  ref,
@@ -3607,7 +3759,7 @@ var Combobox = forwardRef50(
3607
3759
  ]
3608
3760
  }
3609
3761
  ) }),
3610
- /* @__PURE__ */ jsxs27(
3762
+ /* @__PURE__ */ jsxs28(
3611
3763
  PopoverContent,
3612
3764
  {
3613
3765
  side: "bottom",
@@ -3652,7 +3804,7 @@ Combobox.displayName = "Combobox";
3652
3804
 
3653
3805
  // src/ui/multi-select/MultiSelect.tsx
3654
3806
  import { forwardRef as forwardRef51, useState as useState8, useMemo as useMemo6, useRef as useRef7 } from "react";
3655
- import { Fragment as Fragment5, jsx as jsx59, jsxs as jsxs28 } from "react/jsx-runtime";
3807
+ import { Fragment as Fragment6, jsx as jsx59, jsxs as jsxs29 } from "react/jsx-runtime";
3656
3808
  var MultiSelect = forwardRef51(
3657
3809
  function MultiSelect2({ options, value = [], onChange, placeholder = "Select...", emptyText = "No results found", className, disabled }, ref) {
3658
3810
  const [open, setOpen] = useState8(false);
@@ -3667,7 +3819,7 @@ var MultiSelect = forwardRef51(
3667
3819
  onChange?.(next);
3668
3820
  };
3669
3821
  const selectedLabels = options.filter((o) => value.includes(o.value));
3670
- return /* @__PURE__ */ jsxs28(Root9, { open, onOpenChange: (v) => {
3822
+ return /* @__PURE__ */ jsxs29(Root9, { open, onOpenChange: (v) => {
3671
3823
  setOpen(v);
3672
3824
  if (!v) setQuery("");
3673
3825
  }, children: [
@@ -3684,16 +3836,16 @@ var MultiSelect = forwardRef51(
3684
3836
  selectedLabels.length === 0 && "text-muted",
3685
3837
  className
3686
3838
  ),
3687
- children: selectedLabels.length === 0 ? placeholder : selectedLabels.length <= 3 ? selectedLabels.map((opt) => /* @__PURE__ */ jsx59(Badge, { variant: "neutral", style: "soft", children: opt.label }, opt.value)) : /* @__PURE__ */ jsxs28(Fragment5, { children: [
3839
+ children: selectedLabels.length === 0 ? placeholder : selectedLabels.length <= 3 ? selectedLabels.map((opt) => /* @__PURE__ */ jsx59(Badge, { variant: "neutral", style: "soft", children: opt.label }, opt.value)) : /* @__PURE__ */ jsxs29(Fragment6, { children: [
3688
3840
  selectedLabels.slice(0, 2).map((opt) => /* @__PURE__ */ jsx59(Badge, { variant: "neutral", style: "soft", children: opt.label }, opt.value)),
3689
- /* @__PURE__ */ jsxs28(Badge, { variant: "neutral", style: "soft", children: [
3841
+ /* @__PURE__ */ jsxs29(Badge, { variant: "neutral", style: "soft", children: [
3690
3842
  "+",
3691
3843
  selectedLabels.length - 2
3692
3844
  ] })
3693
3845
  ] })
3694
3846
  }
3695
3847
  ) }),
3696
- /* @__PURE__ */ jsxs28(
3848
+ /* @__PURE__ */ jsxs29(
3697
3849
  PopoverContent,
3698
3850
  {
3699
3851
  side: "bottom",
@@ -3711,7 +3863,7 @@ var MultiSelect = forwardRef51(
3711
3863
  className: "border-0 rounded-none ring-0 focus-visible:ring-0"
3712
3864
  }
3713
3865
  ) }),
3714
- /* @__PURE__ */ jsx59(ScrollArea, { className: "max-h-60", children: /* @__PURE__ */ jsx59("div", { className: "p-1", children: filtered.length === 0 ? /* @__PURE__ */ jsx59("p", { className: "px-2 py-4 text-sm text-muted text-center", children: emptyText }) : filtered.map((option) => /* @__PURE__ */ jsxs28(
3866
+ /* @__PURE__ */ jsx59(ScrollArea, { className: "max-h-60", children: /* @__PURE__ */ jsx59("div", { className: "p-1", children: filtered.length === 0 ? /* @__PURE__ */ jsx59("p", { className: "px-2 py-4 text-sm text-muted text-center", children: emptyText }) : filtered.map((option) => /* @__PURE__ */ jsxs29(
3715
3867
  "label",
3716
3868
  {
3717
3869
  className: cn(
@@ -3740,7 +3892,7 @@ MultiSelect.displayName = "MultiSelect";
3740
3892
 
3741
3893
  // src/ui/command-palette/CommandPalette.tsx
3742
3894
  import { forwardRef as forwardRef52, useState as useState9, useMemo as useMemo7, useRef as useRef8, useEffect as useEffect3, useCallback as useCallback8 } from "react";
3743
- import { jsx as jsx60, jsxs as jsxs29 } from "react/jsx-runtime";
3895
+ import { jsx as jsx60, jsxs as jsxs30 } from "react/jsx-runtime";
3744
3896
  var CommandPalette = forwardRef52(
3745
3897
  function CommandPalette2({ open, onOpenChange, actions, onSelect, placeholder = "Search commands...", emptyText = "No results found", groups }, ref) {
3746
3898
  const [query, setQuery] = useState9("");
@@ -3787,7 +3939,7 @@ var CommandPalette = forwardRef52(
3787
3939
  [filtered, activeIdx, onSelect, onOpenChange]
3788
3940
  );
3789
3941
  const flatItems = groupedFiltered ? groupedFiltered.flatMap((g) => g.actions) : filtered;
3790
- return /* @__PURE__ */ jsx60(Root6, { open, onOpenChange, children: /* @__PURE__ */ jsx60("div", { ref, children: /* @__PURE__ */ jsxs29(DialogContent, { size: "lg", className: "p-0 overflow-hidden gap-0", children: [
3942
+ return /* @__PURE__ */ jsx60(Root6, { open, onOpenChange, children: /* @__PURE__ */ jsx60("div", { ref, children: /* @__PURE__ */ jsxs30(DialogContent, { size: "lg", className: "p-0 overflow-hidden gap-0", children: [
3791
3943
  /* @__PURE__ */ jsx60("div", { className: "border-b border-border", children: /* @__PURE__ */ jsx60(
3792
3944
  "input",
3793
3945
  {
@@ -3802,7 +3954,7 @@ var CommandPalette = forwardRef52(
3802
3954
  className: "w-full bg-transparent px-4 py-3 text-sm outline-none placeholder:text-muted"
3803
3955
  }
3804
3956
  ) }),
3805
- /* @__PURE__ */ jsx60(ScrollArea, { className: "max-h-80", children: /* @__PURE__ */ jsx60("div", { className: "p-2", onKeyDown: handleKeyDown, children: flatItems.length === 0 ? /* @__PURE__ */ jsx60("p", { className: "px-2 py-8 text-sm text-muted text-center", children: emptyText }) : groupedFiltered ? groupedFiltered.map((group) => /* @__PURE__ */ jsxs29("div", { children: [
3957
+ /* @__PURE__ */ jsx60(ScrollArea, { className: "max-h-80", children: /* @__PURE__ */ jsx60("div", { className: "p-2", onKeyDown: handleKeyDown, children: flatItems.length === 0 ? /* @__PURE__ */ jsx60("p", { className: "px-2 py-8 text-sm text-muted text-center", children: emptyText }) : groupedFiltered ? groupedFiltered.map((group) => /* @__PURE__ */ jsxs30("div", { children: [
3806
3958
  /* @__PURE__ */ jsx60("p", { className: "px-2 py-1 text-xs font-semibold text-muted uppercase tracking-wider", children: group.label }),
3807
3959
  group.actions.map((action) => {
3808
3960
  const idx = flatItems.indexOf(action);
@@ -3843,7 +3995,7 @@ function CommandItem({
3843
3995
  onSelect,
3844
3996
  onMouseEnter
3845
3997
  }) {
3846
- return /* @__PURE__ */ jsxs29(
3998
+ return /* @__PURE__ */ jsxs30(
3847
3999
  "button",
3848
4000
  {
3849
4001
  type: "button",
@@ -3866,7 +4018,7 @@ function CommandItem({
3866
4018
  import { forwardRef as forwardRef53 } from "react";
3867
4019
  import { Root as Root13, Trigger as Trigger7, Portal as Portal6, Overlay as Overlay2, Content as Content7, Title as Title3, Description as Description3, Close as Close4 } from "@radix-ui/react-dialog";
3868
4020
  import { cva as cva31 } from "class-variance-authority";
3869
- import { jsx as jsx61, jsxs as jsxs30 } from "react/jsx-runtime";
4021
+ import { jsx as jsx61, jsxs as jsxs31 } from "react/jsx-runtime";
3870
4022
  var drawerOverlay = "fixed inset-0 z-[var(--z-overlay)] bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out";
3871
4023
  var drawerContentVariants = cva31(
3872
4024
  "fixed z-[var(--z-overlay)] flex flex-col bg-surface-elevated shadow-lg transition-transform duration-[var(--duration-slow)] ease-[var(--ease-standard)]",
@@ -3889,9 +4041,9 @@ var drawerContentVariants = cva31(
3889
4041
  }
3890
4042
  );
3891
4043
  var DrawerContent = forwardRef53(
3892
- ({ className, side, size, children, ...props }, ref) => /* @__PURE__ */ jsxs30(Portal6, { children: [
4044
+ ({ className, side, size, children, ...props }, ref) => /* @__PURE__ */ jsxs31(Portal6, { children: [
3893
4045
  /* @__PURE__ */ jsx61(Overlay2, { className: drawerOverlay }),
3894
- /* @__PURE__ */ jsxs30(Content7, { ref, className: cn(drawerContentVariants({ side, size }), className), style: { backdropFilter: "blur(var(--backdrop-blur))" }, ...props, children: [
4046
+ /* @__PURE__ */ jsxs31(Content7, { ref, className: cn(drawerContentVariants({ side, size }), className), style: { backdropFilter: "blur(var(--backdrop-blur))" }, ...props, children: [
3895
4047
  children,
3896
4048
  /* @__PURE__ */ jsx61(Close4, { className: "absolute right-panel top-panel rounded-ui-sm opacity-70 hover:opacity-100", children: /* @__PURE__ */ jsx61("svg", { viewBox: "0 0 15 15", className: "size-4 fill-current", children: /* @__PURE__ */ jsx61("path", { d: "M2 2l11 11M13 2L2 13", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }) }) })
3897
4049
  ] })
@@ -3922,7 +4074,7 @@ DrawerFooter.displayName = "DrawerFooter";
3922
4074
  // src/ui/data-list/DataList.tsx
3923
4075
  import { forwardRef as forwardRef54 } from "react";
3924
4076
  import { cva as cva32 } from "class-variance-authority";
3925
- import { jsx as jsx62, jsxs as jsxs31 } from "react/jsx-runtime";
4077
+ import { jsx as jsx62, jsxs as jsxs32 } from "react/jsx-runtime";
3926
4078
  var dataListVariants = cva32("overflow-hidden divide-y divide-border", {
3927
4079
  variants: {
3928
4080
  variant: {
@@ -3941,7 +4093,7 @@ var DataList = forwardRef54(
3941
4093
  ref,
3942
4094
  className: cn(dataListVariants({ variant }), className),
3943
4095
  ...props,
3944
- children: items.map((item, i) => /* @__PURE__ */ jsxs31(
4096
+ children: items.map((item, i) => /* @__PURE__ */ jsxs32(
3945
4097
  "div",
3946
4098
  {
3947
4099
  className: cn(
@@ -3949,7 +4101,7 @@ var DataList = forwardRef54(
3949
4101
  variant === "compact" ? "py-1 px-2" : "py-2 px-3"
3950
4102
  ),
3951
4103
  children: [
3952
- /* @__PURE__ */ jsxs31("dt", { className: "flex w-36 shrink-0 items-center gap-2 text-sm text-muted", children: [
4104
+ /* @__PURE__ */ jsxs32("dt", { className: "flex w-36 shrink-0 items-center gap-2 text-sm text-muted", children: [
3953
4105
  item.icon && /* @__PURE__ */ jsx62("span", { className: "shrink-0", children: item.icon }),
3954
4106
  item.label
3955
4107
  ] }),
@@ -3977,7 +4129,7 @@ DataList.displayName = "DataList";
3977
4129
  // src/ui/patterns/data-table/DataTable.tsx
3978
4130
  import { forwardRef as forwardRef55 } from "react";
3979
4131
  import { cva as cva33 } from "class-variance-authority";
3980
- import { jsx as jsx63, jsxs as jsxs32 } from "react/jsx-runtime";
4132
+ import { jsx as jsx63, jsxs as jsxs33 } from "react/jsx-runtime";
3981
4133
  var COLUMN_WIDTH_SCALE = {
3982
4134
  xs: "8%",
3983
4135
  sm: "12%",
@@ -4002,7 +4154,7 @@ var dataTableVariants = cva33("", {
4002
4154
  }
4003
4155
  });
4004
4156
  var DataTable = forwardRef55(
4005
- ({ className, columns, rows, variant, density, stickyHeader, replacements, layout = "fixed", ...props }, ref) => /* @__PURE__ */ jsx63(ScrollArea, { ref, className: cn("w-full", className), ...props, children: /* @__PURE__ */ jsxs32(
4157
+ ({ className, columns, rows, variant, density, stickyHeader, replacements, layout = "fixed", ...props }, ref) => /* @__PURE__ */ jsx63(ScrollArea, { ref, className: cn("w-full", className), ...props, children: /* @__PURE__ */ jsxs33(
4006
4158
  Table,
4007
4159
  {
4008
4160
  variant,
@@ -4031,10 +4183,10 @@ DataTable.displayName = "DataTable";
4031
4183
 
4032
4184
  // src/ui/slider/Slider.tsx
4033
4185
  import { forwardRef as forwardRef56 } from "react";
4034
- import { jsx as jsx64, jsxs as jsxs33 } from "react/jsx-runtime";
4186
+ import { jsx as jsx64, jsxs as jsxs34 } from "react/jsx-runtime";
4035
4187
  var Slider = forwardRef56(
4036
- ({ className, label, showValue, value, min = 0, max = 100, step = 1, ...props }, ref) => /* @__PURE__ */ jsxs33("div", { className: cn("flex flex-col gap-1", className), children: [
4037
- (label || showValue) && /* @__PURE__ */ jsxs33("div", { className: "flex items-center justify-between", children: [
4188
+ ({ className, label, showValue, value, min = 0, max = 100, step = 1, ...props }, ref) => /* @__PURE__ */ jsxs34("div", { className: cn("flex flex-col gap-1", className), children: [
4189
+ (label || showValue) && /* @__PURE__ */ jsxs34("div", { className: "flex items-center justify-between", children: [
4038
4190
  label && /* @__PURE__ */ jsx64("span", { className: "text-sm text-fg", children: label }),
4039
4191
  showValue && /* @__PURE__ */ jsx64("span", { className: "text-sm text-muted font-mono", children: String(value ?? 0) })
4040
4192
  ] }),
@@ -4057,7 +4209,7 @@ Slider.displayName = "Slider";
4057
4209
 
4058
4210
  // src/ui/markdown/Markdown.tsx
4059
4211
  import { useMemo as useMemo8 } from "react";
4060
- import { Fragment as Fragment6, jsx as jsx65, jsxs as jsxs34 } from "react/jsx-runtime";
4212
+ import { Fragment as Fragment7, jsx as jsx65, jsxs as jsxs35 } from "react/jsx-runtime";
4061
4213
  function escapeHtml(text) {
4062
4214
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4063
4215
  }
@@ -4084,7 +4236,7 @@ function renderInline(text) {
4084
4236
  if (lastIndex < text.length) {
4085
4237
  parts.push(escapeHtml(text.slice(lastIndex)));
4086
4238
  }
4087
- return parts.length === 1 ? parts[0] : parts.length > 1 ? /* @__PURE__ */ jsx65(Fragment6, { children: parts }) : "";
4239
+ return parts.length === 1 ? parts[0] : parts.length > 1 ? /* @__PURE__ */ jsx65(Fragment7, { children: parts }) : "";
4088
4240
  }
4089
4241
  function parseBlocks(content) {
4090
4242
  const lines = content.split("\n");
@@ -4169,7 +4321,7 @@ function Markdown({ content, className, ...props }) {
4169
4321
  case "code":
4170
4322
  return /* @__PURE__ */ jsx65(CodeBlock, { code: block.code, language: block.language }, i);
4171
4323
  case "table":
4172
- return /* @__PURE__ */ jsx65("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs34("table", { className: "w-full text-sm border-collapse", children: [
4324
+ return /* @__PURE__ */ jsx65("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs35("table", { className: "w-full text-sm border-collapse", children: [
4173
4325
  block.headers.length > 0 && /* @__PURE__ */ jsx65("thead", { children: /* @__PURE__ */ jsx65("tr", { children: block.headers.map((h, j) => /* @__PURE__ */ jsx65("th", { className: "border border-border px-3 py-2 text-left font-semibold text-fg text-xs uppercase tracking-wider", children: renderInline(h) }, j)) }) }),
4174
4326
  /* @__PURE__ */ jsx65("tbody", { children: block.rows.map((row, j) => /* @__PURE__ */ jsx65("tr", { children: row.map((c, k) => /* @__PURE__ */ jsx65("td", { className: "border border-border px-3 py-1.5 text-fg", children: renderInline(c) }, k)) }, j)) })
4175
4327
  ] }) }, i);
@@ -4182,7 +4334,7 @@ function Markdown({ content, className, ...props }) {
4182
4334
  // src/ui/file-drop/FileDrop.tsx
4183
4335
  import { forwardRef as forwardRef57, useState as useState10, useRef as useRef9, useCallback as useCallback9 } from "react";
4184
4336
  import { cva as cva34 } from "class-variance-authority";
4185
- import { Fragment as Fragment7, jsx as jsx66, jsxs as jsxs35 } from "react/jsx-runtime";
4337
+ import { Fragment as Fragment8, jsx as jsx66, jsxs as jsxs36 } from "react/jsx-runtime";
4186
4338
  var fileDropVariants = cva34(
4187
4339
  "relative flex flex-col items-center justify-center gap-2 rounded-ui border-2 border-dashed p-8 text-center transition-colors cursor-pointer",
4188
4340
  {
@@ -4254,7 +4406,7 @@ var FileDrop = forwardRef57(
4254
4406
  if (e.target.files) validateAndDrop(e.target.files);
4255
4407
  }, [validateAndDrop]);
4256
4408
  const state = disabled ? "default" : error ? "error" : success ? "success" : dragOver ? "dragging" : "default";
4257
- return /* @__PURE__ */ jsxs35(
4409
+ return /* @__PURE__ */ jsxs36(
4258
4410
  "div",
4259
4411
  {
4260
4412
  ref,
@@ -4266,15 +4418,15 @@ var FileDrop = forwardRef57(
4266
4418
  ...props,
4267
4419
  children: [
4268
4420
  /* @__PURE__ */ jsx66("input", { ref: inputRef, type: "file", accept, multiple, className: "hidden", onChange: handleFileChange }),
4269
- dragOver ? /* @__PURE__ */ jsxs35(Fragment7, { children: [
4421
+ dragOver ? /* @__PURE__ */ jsxs36(Fragment8, { children: [
4270
4422
  /* @__PURE__ */ jsx66("svg", { viewBox: "0 0 24 24", className: "size-8 text-primary fill-none stroke-current stroke-[1.5]", children: /* @__PURE__ */ jsx66("path", { d: "M12 4v12m0 0l-3-3m3 3l3-3M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2" }) }),
4271
4423
  /* @__PURE__ */ jsx66("p", { className: "font-medium text-primary", children: "Drop files here" })
4272
- ] }) : /* @__PURE__ */ jsxs35(Fragment7, { children: [
4424
+ ] }) : /* @__PURE__ */ jsxs36(Fragment8, { children: [
4273
4425
  /* @__PURE__ */ jsx66("svg", { viewBox: "0 0 24 24", className: "size-8 text-muted fill-none stroke-current stroke-[1.5]", children: /* @__PURE__ */ jsx66("path", { d: "M7 16a4 4 0 010-8 5 5 0 0110 0 4 4 0 010 8h-1M12 4v12m0 0l-3-3m3 3l3-3" }) }),
4274
4426
  /* @__PURE__ */ jsx66("p", { className: "font-medium text-fg", children: error || success ? "" : "Drop files here or click to browse" }),
4275
4427
  error && /* @__PURE__ */ jsx66("p", { className: "text-xs text-danger", children: error }),
4276
4428
  success && /* @__PURE__ */ jsx66("p", { className: "text-xs text-success", children: "Files added" }),
4277
- !error && !success && /* @__PURE__ */ jsxs35("p", { className: "text-xs text-muted", children: [
4429
+ !error && !success && /* @__PURE__ */ jsxs36("p", { className: "text-xs text-muted", children: [
4278
4430
  accept ? `Accepts: ${accept}` : "Any file type",
4279
4431
  maxSize ? ` \xB7 Max ${(maxSize / 1024 / 1024).toFixed(0)}MB` : "",
4280
4432
  multiple ? " \xB7 Multiple" : ""
@@ -4290,7 +4442,7 @@ FileDrop.displayName = "FileDrop";
4290
4442
  // src/ui/image/Image.tsx
4291
4443
  import { forwardRef as forwardRef58 } from "react";
4292
4444
  import { cva as cva35 } from "class-variance-authority";
4293
- import { jsx as jsx67, jsxs as jsxs36 } from "react/jsx-runtime";
4445
+ import { jsx as jsx67, jsxs as jsxs37 } from "react/jsx-runtime";
4294
4446
  var imageVariants = cva35("inline-block object-cover bg-secondary", {
4295
4447
  variants: {
4296
4448
  fit: {
@@ -4339,7 +4491,7 @@ var Image2 = forwardRef58(
4339
4491
  }
4340
4492
  );
4341
4493
  if (caption) {
4342
- return /* @__PURE__ */ jsxs36("figure", { className: "inline-flex flex-col gap-1.5", children: [
4494
+ return /* @__PURE__ */ jsxs37("figure", { className: "inline-flex flex-col gap-1.5", children: [
4343
4495
  img,
4344
4496
  /* @__PURE__ */ jsx67("figcaption", { className: "text-xs text-muted text-center", children: caption })
4345
4497
  ] });
@@ -4409,7 +4561,7 @@ function PortHitZone({
4409
4561
  }
4410
4562
 
4411
4563
  // src/ui/patterns/graph/GraphNode.tsx
4412
- import { jsx as jsx69, jsxs as jsxs37 } from "react/jsx-runtime";
4564
+ import { jsx as jsx69, jsxs as jsxs38 } from "react/jsx-runtime";
4413
4565
  function GraphNodeRenderer({
4414
4566
  node,
4415
4567
  isSelected,
@@ -4422,7 +4574,7 @@ function GraphNodeRenderer({
4422
4574
  onSelect(node.id);
4423
4575
  onDragStart(node.id, e.clientX, e.clientY, node.x, node.y);
4424
4576
  }, [node.id, node.x, node.y, onSelect, onDragStart]);
4425
- return /* @__PURE__ */ jsxs37("div", { className: "absolute", style: { left: node.x, top: node.y, width: NODE_WIDTH }, onMouseDown: onDown, children: [
4577
+ return /* @__PURE__ */ jsxs38("div", { className: "absolute", style: { left: node.x, top: node.y, width: NODE_WIDTH }, onMouseDown: onDown, children: [
4426
4578
  /* @__PURE__ */ jsx69(
4427
4579
  GraphNode,
4428
4580
  {
@@ -4443,7 +4595,7 @@ function GraphNodeRenderer({
4443
4595
  }
4444
4596
 
4445
4597
  // src/ui/patterns/graph/Graph.tsx
4446
- import { jsx as jsx70, jsxs as jsxs38 } from "react/jsx-runtime";
4598
+ import { jsx as jsx70, jsxs as jsxs39 } from "react/jsx-runtime";
4447
4599
  function graphReducer(state, action) {
4448
4600
  switch (action.type) {
4449
4601
  case "MOVE_NODE":
@@ -4582,7 +4734,7 @@ var Graph = ({
4582
4734
  dispatch({ type: "SELECT_EDGE", id: edgeId });
4583
4735
  }, []);
4584
4736
  const { nodes, edges, selectedNodeIds, selectedEdgeIds, connecting, offset: offset2, zoom } = state;
4585
- return /* @__PURE__ */ jsxs38(
4737
+ return /* @__PURE__ */ jsxs39(
4586
4738
  Canvas,
4587
4739
  {
4588
4740
  ref: containerRef,
@@ -4594,7 +4746,7 @@ var Graph = ({
4594
4746
  onBackgroundClick: () => dispatch({ type: "DESELECT_ALL" }),
4595
4747
  controls,
4596
4748
  children: [
4597
- /* @__PURE__ */ jsxs38("svg", { className: "absolute inset-0 overflow-visible w-full h-full pointer-events-none", children: [
4749
+ /* @__PURE__ */ jsxs39("svg", { className: "absolute inset-0 overflow-visible w-full h-full pointer-events-none", children: [
4598
4750
  edges.map((edge) => {
4599
4751
  const fn = nodes.find((n) => n.id === edge.from.nodeId);
4600
4752
  const tn = nodes.find((n) => n.id === edge.to.nodeId);
@@ -4789,7 +4941,7 @@ var LAYER_SVGS = {
4789
4941
  };
4790
4942
 
4791
4943
  // src/ui/patterns/textured-surface/TexturedSurface.tsx
4792
- import { jsx as jsx71, jsxs as jsxs39 } from "react/jsx-runtime";
4944
+ import { jsx as jsx71, jsxs as jsxs40 } from "react/jsx-runtime";
4793
4945
  var TEXTURE_STRENGTHS = {
4794
4946
  "paper-grain": { subtle: 0.3, medium: 0.5, strong: 0.75 },
4795
4947
  "frosted-glass": { subtle: 0.22, medium: 0.35, strong: 0.55 },
@@ -4882,7 +5034,7 @@ var TexturedSurface = forwardRef59(
4882
5034
  if (conf) {
4883
5035
  const tileLayers = conf.layers.filter((l) => l.tileSize);
4884
5036
  const coverLayers = conf.layers.filter((l) => !l.tileSize);
4885
- return /* @__PURE__ */ jsxs39(
5037
+ return /* @__PURE__ */ jsxs40(
4886
5038
  "div",
4887
5039
  {
4888
5040
  ref,
@@ -4948,8 +5100,8 @@ var TexturedSurface = forwardRef59(
4948
5100
  TexturedSurface.displayName = "TexturedSurface";
4949
5101
 
4950
5102
  // src/ui/patterns/textured-surface/ParamTable.tsx
4951
- import { Fragment as Fragment8 } from "react";
4952
- import { jsx as jsx72, jsxs as jsxs40 } from "react/jsx-runtime";
5103
+ import { Fragment as Fragment9 } from "react";
5104
+ import { jsx as jsx72, jsxs as jsxs41 } from "react/jsx-runtime";
4953
5105
  var LAYERS = ["page", "surface", "foreground"];
4954
5106
  var STRENGTHS = ["subtle", "medium", "strong"];
4955
5107
  var LAYER_LABELS = {
@@ -4958,10 +5110,10 @@ var LAYER_LABELS = {
4958
5110
  foreground: "Foreground"
4959
5111
  };
4960
5112
  function ParamTable({ texture = "paper-grain" }) {
4961
- return /* @__PURE__ */ jsxs40("div", { className: "w-full grid grid-cols-[4rem_repeat(3,1fr)] gap-1 items-center", children: [
5113
+ return /* @__PURE__ */ jsxs41("div", { className: "w-full grid grid-cols-[4rem_repeat(3,1fr)] gap-1 items-center", children: [
4962
5114
  /* @__PURE__ */ jsx72("div", {}),
4963
5115
  STRENGTHS.map((s) => /* @__PURE__ */ jsx72("div", { className: "text-center text-xs font-medium text-muted", children: s }, s)),
4964
- LAYERS.map((layer) => /* @__PURE__ */ jsxs40(Fragment8, { children: [
5116
+ LAYERS.map((layer) => /* @__PURE__ */ jsxs41(Fragment9, { children: [
4965
5117
  /* @__PURE__ */ jsx72("div", { className: "text-xs font-medium text-muted self-center", children: LAYER_LABELS[layer] }),
4966
5118
  STRENGTHS.map((strength) => /* @__PURE__ */ jsx72(
4967
5119
  TexturedSurface,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-you-eye",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sadigaxund/my-you-eye"