@pixerate/editor-svelte 0.20.0 → 0.22.0

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.
Files changed (63) hide show
  1. package/README.md +4 -3
  2. package/dist/canvas/CanvasGuidesLayer.svelte +32 -2
  3. package/dist/canvas/CanvasGuidesLayer.svelte.d.ts +2 -0
  4. package/dist/canvas/CanvasGuidesLayer.svelte.d.ts.map +1 -1
  5. package/dist/canvas/ShapeNode.svelte +57 -0
  6. package/dist/canvas/ShapeNode.svelte.d.ts +21 -0
  7. package/dist/canvas/ShapeNode.svelte.d.ts.map +1 -0
  8. package/dist/canvas/createCanvasArrange.svelte.d.ts +23 -1
  9. package/dist/canvas/createCanvasArrange.svelte.d.ts.map +1 -1
  10. package/dist/canvas/createCanvasArrange.svelte.js +19 -2
  11. package/dist/canvas/createCanvasMeasure.svelte.d.ts +53 -0
  12. package/dist/canvas/createCanvasMeasure.svelte.d.ts.map +1 -0
  13. package/dist/canvas/createCanvasMeasure.svelte.js +80 -0
  14. package/dist/canvas/index.d.ts +4 -0
  15. package/dist/canvas/index.d.ts.map +1 -1
  16. package/dist/canvas/index.js +6 -0
  17. package/dist/commands/CommandPalette.svelte +129 -0
  18. package/dist/commands/CommandPalette.svelte.d.ts +13 -0
  19. package/dist/commands/CommandPalette.svelte.d.ts.map +1 -0
  20. package/dist/commands/commandShortcuts.svelte.d.ts +30 -0
  21. package/dist/commands/commandShortcuts.svelte.d.ts.map +1 -0
  22. package/dist/commands/commandShortcuts.svelte.js +37 -0
  23. package/dist/commands/index.d.ts +7 -0
  24. package/dist/commands/index.d.ts.map +1 -0
  25. package/dist/commands/index.js +3 -0
  26. package/dist/commands/palette.svelte.d.ts +34 -0
  27. package/dist/commands/palette.svelte.d.ts.map +1 -0
  28. package/dist/commands/palette.svelte.js +61 -0
  29. package/dist/history/createHistoryShortcuts.d.ts +1 -1
  30. package/dist/history/createHistoryShortcuts.d.ts.map +1 -1
  31. package/dist/history/createHistoryShortcuts.js +10 -32
  32. package/dist/history/history.svelte.d.ts +13 -4
  33. package/dist/history/history.svelte.d.ts.map +1 -1
  34. package/dist/history/history.svelte.js +10 -0
  35. package/dist/history/historyDirty.svelte.d.ts +31 -0
  36. package/dist/history/historyDirty.svelte.d.ts.map +1 -0
  37. package/dist/history/historyDirty.svelte.js +30 -0
  38. package/dist/history/index.d.ts +1 -0
  39. package/dist/history/index.d.ts.map +1 -1
  40. package/dist/history/index.js +1 -0
  41. package/dist/image-editor/ImageEditor.svelte +39 -0
  42. package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -1
  43. package/dist/image-editor/imageEditorState.svelte.d.ts +2 -0
  44. package/dist/image-editor/imageEditorState.svelte.d.ts.map +1 -1
  45. package/dist/image-editor/imageEditorState.svelte.js +4 -0
  46. package/dist/index.d.ts +3 -2
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +2 -1
  49. package/dist/review/ReviewToolbar.svelte +15 -0
  50. package/dist/review/ReviewToolbar.svelte.d.ts +4 -0
  51. package/dist/review/ReviewToolbar.svelte.d.ts.map +1 -1
  52. package/dist/spreadsheet/FormulaBar.svelte +12 -1
  53. package/dist/spreadsheet/FormulaBar.svelte.d.ts.map +1 -1
  54. package/dist/spreadsheet/SpreadsheetContextMenu.svelte +21 -2
  55. package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts +1 -1
  56. package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts.map +1 -1
  57. package/dist/spreadsheet/SpreadsheetEditor.svelte +48 -6
  58. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -1
  59. package/dist/spreadsheet/SpreadsheetGroupRow.svelte +1 -1
  60. package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -1
  61. package/dist/spreadsheet/tableInteractions.d.ts.map +1 -1
  62. package/dist/spreadsheet/tableInteractions.js +2 -1
  63. package/package.json +10 -2
package/README.md CHANGED
@@ -37,7 +37,7 @@ Requires **Svelte 5** (the components use runes). Components ship as `.svelte` s
37
37
 
38
38
  `content` sets the initial document. Read later changes from the bound `editor` (for example in `onUpdate`), as above. The editor is preloaded with `createRichTextPreset()`. Pass `markdown`, `image`, `uploadImage`, `extensions` or `richTextOptions` to configure it.
39
39
 
40
- `fileImport` imports dropped/pasted `.md`, `.html`, `.txt` and `.rtf` files at the drop position, and `richClipboard` copies lossless slices between editors (both opt-in). `createFileImport(() => editor)` returns reactive `isImporting` / `error` plus `openFilePicker()` and `importFiles()` for an import button.
40
+ `fileImport` imports dropped/pasted `.md`, `.html`, `.txt`, `.rtf` and `.docx` files (plus any adapter you register, e.g. PDF or zipped exports) at the drop position, and `richClipboard` copies lossless slices between editors (both opt-in). `createFileImport(() => editor)` returns reactive `isImporting` / `error` plus `openFilePicker()` and `importFiles()` for an import button.
41
41
 
42
42
  `useAgentToolProvider(registry, () => editor && createRichTextToolProvider(editor, { id: "doc" }))` registers an agent tool provider (see `ToolRegistry` in `@pixerate/editor/agent`) for the component's lifetime. Call it during component initialisation.
43
43
 
@@ -67,15 +67,16 @@ Requires **Svelte 5** (the components use runes). Components ship as `.svelte` s
67
67
  <CommentsPanel {editor} />
68
68
  ```
69
69
 
70
- Put `<CommentButton {editor} />` inside a `BubbleMenu` to comment on the selection. `createComments(editorOrStore)` and `createTrackChanges(editor)` are rune (and Svelte store) adapters for custom UIs.
70
+ `ReviewToolbar` steps through insertions, deletions, formatting and block changes and shows the current one's author and description (e.g. "Ada: Formatted: Bold"); hide it with `showDescription={false}` or restyle it with `descriptionClass`. Put `<CommentButton {editor} />` inside a `BubbleMenu` to comment on the selection. `createComments(editorOrStore)` and `createTrackChanges(editor)` are rune (and Svelte store) adapters for custom UIs.
71
71
 
72
72
  ## More exports
73
73
 
74
74
  | Import | Exports |
75
75
  | :--- | :--- |
76
- | `@pixerate/editor-svelte` | `EditableTextNodeEditor`, `BubbleMenu`, `TemplateRenderer`, `initiateEditor`, `createReactiveEditor`, `createHistory`, `createHistoryShortcuts`, `navigationGuard` (action), `createNavigationGuard`, `SpreadsheetEditor`, `FormulaBar`, `FormulaHints`, `createReactiveSpreadsheet`, `DirtyTracker` |
76
+ | `@pixerate/editor-svelte` | `EditableTextNodeEditor`, `BubbleMenu`, `TemplateRenderer`, `initiateEditor`, `createReactiveEditor`, `createHistory`, `createHistoryDirty`, `createHistoryShortcuts`, `navigationGuard` (action), `createNavigationGuard`, `SpreadsheetEditor`, `FormulaBar`, `FormulaHints`, `createReactiveSpreadsheet`, `DirtyTracker` |
77
77
  | `@pixerate/editor-svelte/spreadsheet` | `SpreadsheetEditor`, `FormulaBar`, `FormulaHints`, `SpreadsheetContextMenu`, `SpreadsheetGroupRow`, `createReactiveSpreadsheet` (also re-exported from the root entry) |
78
78
  | `@pixerate/editor-svelte/image-editor` | `ImageEditor`, `createReactiveImageEditor`: crop, rotate/flip, adjustments, annotations, depth masks, and `onInpaint` / `onRemoveBackground` AI hooks |
79
+ | `@pixerate/editor-svelte/commands` | `CommandPalette`, `createCommandPalette` (`hotkey: "Mod+K"`; call `destroy()` when done), `createCommandShortcuts`, `useRegisterCommands` over a `CommandRegistry` from `@pixerate/editor/commands` (also re-exported from the root entry) |
79
80
  | `@pixerate/editor-svelte/canvas` | Runes for `@xyflow/svelte`: `createCanvasGraph`, `createCanvasClipboard`, `createCanvasDocking`, `createCanvasShortcuts`, `createCanvasInteractions`, `createCanvasExplosion`, `createCanvasNodeSync`, `createCanvasMultiDrag`, `createStreamingCanvas`, `AgentPresenceLayer`, `FloatingHorizontalScrollbar`, plus layout helpers |
80
81
 
81
82
  Prop types are exported alongside each component, for example `EditableTextNodeEditorProps`, `BubbleMenuProps`, `ImageEditorProps` and `SpreadsheetEditorProps`.
@@ -10,6 +10,8 @@
10
10
  color?: string;
11
11
  /** Colour of equal-spacing guides. */
12
12
  spacingColor?: string;
13
+ /** Colour of measuring lines, their labels and extension lines. */
14
+ measureColor?: string;
13
15
  class?: string;
14
16
  }
15
17
 
@@ -18,6 +20,7 @@
18
20
  viewport = { x: 0, y: 0, zoom: 1 },
19
21
  color = '#f59e0b',
20
22
  spacingColor = '#ec4899',
23
+ measureColor = '#ef4444',
21
24
  class: className = ''
22
25
  }: Props = $props();
23
26
 
@@ -42,11 +45,38 @@
42
45
  y1={a.y}
43
46
  x2={b.x}
44
47
  y2={b.y}
45
- stroke={guide.kind === 'spacing' ? spacingColor : color}
48
+ stroke={guide.kind === 'measure' || guide.kind === 'extension'
49
+ ? measureColor
50
+ : guide.kind === 'spacing'
51
+ ? spacingColor
52
+ : color}
46
53
  stroke-width="1"
47
- stroke-dasharray={guide.kind === 'centre' ? '4 3' : undefined}
54
+ stroke-dasharray={guide.kind === 'centre' ? '4 3' : guide.kind === 'extension' ? '3 3' : undefined}
48
55
  shape-rendering="crispEdges"
49
56
  />
50
57
  {/each}
58
+ {#each guides as guide, i (i)}
59
+ {#if guide.label}
60
+ {@const mid = toScreen((guide.x1 + guide.x2) / 2, (guide.y1 + guide.y2) / 2)}
61
+ {@const vertical = guide.orientation === 'vertical'}
62
+ {@const width = guide.label.length * 6.5 + 8}
63
+ <!-- Labels sit beside vertical lines and below horizontal ones. -->
64
+ <g
65
+ class="pixerate-canvas-guides__label"
66
+ data-label={guide.label}
67
+ transform="translate({vertical ? mid.x + 4 : mid.x - width / 2} {vertical ? mid.y - 8 : mid.y + 4})"
68
+ >
69
+ <rect {width} height="16" rx="3" fill={measureColor} />
70
+ <text
71
+ x={width / 2}
72
+ y="11.5"
73
+ text-anchor="middle"
74
+ font-size="11"
75
+ font-family="system-ui, sans-serif"
76
+ fill="#fff">{guide.label}</text
77
+ >
78
+ </g>
79
+ {/if}
80
+ {/each}
51
81
  </svg>
52
82
  {/if}
@@ -8,6 +8,8 @@ interface Props {
8
8
  color?: string;
9
9
  /** Colour of equal-spacing guides. */
10
10
  spacingColor?: string;
11
+ /** Colour of measuring lines, their labels and extension lines. */
12
+ measureColor?: string;
11
13
  class?: string;
12
14
  }
13
15
  declare const CanvasGuidesLayer: import("svelte").Component<Props, {}, "">;
@@ -1 +1 @@
1
- {"version":3,"file":"CanvasGuidesLayer.svelte.d.ts","sourceRoot":"","sources":["../../src/canvas/CanvasGuidesLayer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGxE,UAAU,KAAK;IACd,kEAAkE;IAClE,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AA+BF,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"CanvasGuidesLayer.svelte.d.ts","sourceRoot":"","sources":["../../src/canvas/CanvasGuidesLayer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGxE,UAAU,KAAK;IACd,kEAAkE;IAClE,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAgDF,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,57 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { getShapeRenderData, isShapeDescription, type ShapeDescription } from '@pixerate/editor/canvas';
4
+
5
+ interface Props {
6
+ /** Node data; `data.shape` holds the shape description. */
7
+ data: { shape?: ShapeDescription; [key: string]: any };
8
+ /** Node width / height (SvelteFlow passes them to custom nodes). */
9
+ width?: number;
10
+ height?: number;
11
+ selected?: boolean;
12
+ class?: string;
13
+ /** Rendered over the shape, e.g. SvelteFlow `<Handle>`s or a label. */
14
+ children?: Snippet;
15
+ [key: string]: any;
16
+ }
17
+
18
+ let { data, width, height, selected = false, class: className = '', children }: Props = $props();
19
+
20
+ const shape = $derived(isShapeDescription(data?.shape) ? data.shape : null);
21
+ const render = $derived(shape ? getShapeRenderData(shape, { width, height }) : null);
22
+ </script>
23
+
24
+ <!--
25
+ Headless shape node: draws `data.shape` as one SVG path that fills the node box.
26
+ Register it as a SvelteFlow node type (`nodeTypes={{ shape: ShapeNode }}`), or wrap it
27
+ to add handles. Strokes keep their width when the node is resized.
28
+ -->
29
+ {#if shape && render}
30
+ <div
31
+ class="pixerate-shape-node {className}"
32
+ data-shape-kind={shape.kind}
33
+ data-selected={selected ? 'true' : undefined}
34
+ style="position: relative; width: 100%; height: 100%;"
35
+ >
36
+ <svg
37
+ viewBox={render.viewBox}
38
+ preserveAspectRatio="none"
39
+ width="100%"
40
+ height="100%"
41
+ style="display: block; overflow: visible;"
42
+ aria-hidden="true"
43
+ >
44
+ <path
45
+ d={render.d}
46
+ transform={render.transform}
47
+ fill={render.fill}
48
+ fill-rule={render.fillRule}
49
+ stroke={render.stroke}
50
+ stroke-width={render.strokeWidth}
51
+ opacity={render.opacity}
52
+ vector-effect="non-scaling-stroke"
53
+ />
54
+ </svg>
55
+ {@render children?.()}
56
+ </div>
57
+ {/if}
@@ -0,0 +1,21 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type ShapeDescription } from '@pixerate/editor/canvas';
3
+ interface Props {
4
+ /** Node data; `data.shape` holds the shape description. */
5
+ data: {
6
+ shape?: ShapeDescription;
7
+ [key: string]: any;
8
+ };
9
+ /** Node width / height (SvelteFlow passes them to custom nodes). */
10
+ width?: number;
11
+ height?: number;
12
+ selected?: boolean;
13
+ class?: string;
14
+ /** Rendered over the shape, e.g. SvelteFlow `<Handle>`s or a label. */
15
+ children?: Snippet;
16
+ [key: string]: any;
17
+ }
18
+ declare const ShapeNode: import("svelte").Component<Props, {}, "">;
19
+ type ShapeNode = ReturnType<typeof ShapeNode>;
20
+ export default ShapeNode;
21
+ //# sourceMappingURL=ShapeNode.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ShapeNode.svelte.d.ts","sourceRoot":"","sources":["../../src/canvas/ShapeNode.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAA0C,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGvG,UAAU,KAAK;IACd,2DAA2D;IAC3D,IAAI,EAAE;QAAE,KAAK,CAAC,EAAE,gBAAgB,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IACvD,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACnB;AAwBF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -16,10 +16,21 @@ export interface CreateCanvasArrangeOptions<TNode extends CanvasNode = CanvasNod
16
16
  * Wire snapping to SvelteFlow with `onnodedragstart={() => arrange.beginDrag()}`,
17
17
  * `onnodedrag={({ nodes }) => arrange.drag(nodes.map((n) => n.id))}` and
18
18
  * `onnodedragstop={() => arrange.endDrag()}`, and render `arrange.guides` with
19
- * `<CanvasGuidesLayer>`.
19
+ * `<CanvasGuidesLayer>`. Snap resizes with `<NodeResizer {...arrange.resizerHandlers(id)} />`.
20
20
  */
21
21
  export declare function createCanvasArrange<TNode extends CanvasNode = CanvasNode>(options: CreateCanvasArrangeOptions<TNode>): {
22
22
  guides: GuideLine[];
23
+ /**
24
+ * Resize snapping for SvelteFlow's `<NodeResizer>` / `<NodeResizeControl>`:
25
+ * `<NodeResizer {...arrange.resizerHandlers(id)} />`. The resizer writes the raw size
26
+ * to the nodes after `onResize`, so snapping runs a microtask later and writes the
27
+ * snapped box back. Locked nodes snap back to their starting box.
28
+ */
29
+ resizerHandlers(id: string): {
30
+ onResizeStart: () => void;
31
+ onResize: () => void;
32
+ onResizeEnd: () => void;
33
+ };
23
34
  apply: (name: string, updates: import("@pixerate/editor/canvas").CanvasNodeUpdate[]) => import("@pixerate/editor/canvas").CanvasNodeUpdate[];
24
35
  align(edge: import("@pixerate/editor/canvas").AlignEdge, ids?: Iterable<string>, alignOptions?: import("@pixerate/editor/canvas").AlignNodesOptions): import("@pixerate/editor/canvas").CanvasNodeUpdate[];
25
36
  distribute(axis: import("@pixerate/editor/canvas").DistributeAxis, mode?: import("@pixerate/editor/canvas").DistributeMode, ids?: Iterable<string>, distributeOptions?: import("@pixerate/editor/canvas").DistributeNodesOptions): import("@pixerate/editor/canvas").CanvasNodeUpdate[];
@@ -33,10 +44,21 @@ export declare function createCanvasArrange<TNode extends CanvasNode = CanvasNod
33
44
  unlock: (ids?: Iterable<string>) => string[];
34
45
  toggleLock(ids?: Iterable<string>): string[];
35
46
  isLocked: (id: string) => boolean;
47
+ combineShapes(operation: import("@pixerate/editor/canvas").ShapeOperation, ids?: Iterable<string>, combineOptions?: import("@pixerate/editor/canvas").CombineShapeNodesOptions): import("@pixerate/editor/canvas").CombineShapeNodesResult<any>;
48
+ unite: (ids?: Iterable<string>, o?: import("@pixerate/editor/canvas").CombineShapeNodesOptions) => import("@pixerate/editor/canvas").CombineShapeNodesResult<any>;
49
+ intersect: (ids?: Iterable<string>, o?: import("@pixerate/editor/canvas").CombineShapeNodesOptions) => import("@pixerate/editor/canvas").CombineShapeNodesResult<any>;
50
+ subtract: (ids?: Iterable<string>, o?: import("@pixerate/editor/canvas").CombineShapeNodesOptions) => import("@pixerate/editor/canvas").CombineShapeNodesResult<any>;
51
+ exclude: (ids?: Iterable<string>, o?: import("@pixerate/editor/canvas").CombineShapeNodesOptions) => import("@pixerate/editor/canvas").CombineShapeNodesResult<any>;
52
+ divide: (ids?: Iterable<string>, o?: import("@pixerate/editor/canvas").CombineShapeNodesOptions) => import("@pixerate/editor/canvas").CombineShapeNodesResult<any>;
53
+ canCombineShapes: (ids?: Iterable<string>) => boolean;
36
54
  runAction(action: CanvasArrangeAction): boolean;
37
55
  beginDrag(ids?: Iterable<string>): void;
38
56
  drag(ids?: Iterable<string>): import("@pixerate/editor/canvas").SnapNodeDragResult | null;
39
57
  snapPositionChanges<TChange extends import("@pixerate/editor/canvas").PositionChangeLike>(changes: TChange[]): TChange[];
58
+ beginResize(id: string): void;
59
+ resize(id: string, handle?: import("@pixerate/editor/canvas").ResizeHandle): import("@pixerate/editor/canvas").SnapNodeResizeResult | null;
60
+ endResize(): void;
61
+ snapResizeChanges<TChange extends import("@pixerate/editor/canvas").PositionChangeLike>(changes: TChange[]): TChange[];
40
62
  endDrag(): void;
41
63
  };
42
64
  export type CanvasArrange<TNode extends CanvasNode = CanvasNode> = ReturnType<typeof createCanvasArrange<TNode>>;
@@ -1 +1 @@
1
- {"version":3,"file":"createCanvasArrange.svelte.d.ts","sourceRoot":"","sources":["../../src/canvas/createCanvasArrange.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,8BAA8B,EACnC,KAAK,SAAS,EACd,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,KAAK,WAAW,GAAG,8BAA8B,CAAC,GAAG,CAAC,CAAC;AAEvD,MAAM,WAAW,0BAA0B,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,CAChF,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,UAAU,GAAG,gBAAgB,CAAC;IACrE,QAAQ,EAAE,MAAM,KAAK,EAAE,CAAC;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;IACnC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;CAC/C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,EACxE,OAAO,EAAE,0BAA0B,CAAC,KAAK,CAAC;;;;;;;sBA2D65iC,CAAC;sBAAiE,CAAC;sBAAiE,CAAC;oBAA+D,CAAC;cAAyD,CAAC;gBAAiD,CAAC;;;;;;;;EAxCxvjC;AAED,MAAM,MAAM,aAAa,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,IAAI,UAAU,CAAC,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;AAEjH,MAAM,WAAW,6BAA6B;IAC7C,OAAO,EAAE;QAAE,SAAS,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,OAAO,CAAA;KAAE,CAAC;IACjE,uEAAuE;IACvE,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,OAAO,CAAA;KAAE,CAAC;IAC1D,aAAa,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,6BAA6B,GAAG,MAAM,IAAI,CAqB/F"}
1
+ {"version":3,"file":"createCanvasArrange.svelte.d.ts","sourceRoot":"","sources":["../../src/canvas/createCanvasArrange.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,8BAA8B,EACnC,KAAK,SAAS,EACd,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,KAAK,WAAW,GAAG,8BAA8B,CAAC,GAAG,CAAC,CAAC;AAEvD,MAAM,WAAW,0BAA0B,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,CAChF,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,UAAU,GAAG,gBAAgB,CAAC;IACrE,QAAQ,EAAE,MAAM,KAAK,EAAE,CAAC;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;IACnC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;CAC/C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,EACxE,OAAO,EAAE,0BAA0B,CAAC,KAAK,CAAC;;IAkBzC;;;;;OAKG;wBACiB,MAAM;;;;;;;;;;sBAoD+j/C,CAAC;sBAAiE,CAAC;sBAAiE,CAAC;oBAA+D,CAAC;cAAyD,CAAC;gBAAiD,CAAC;;;;;;;;;0BAA8tC,CAAC;;;;;;;;;;EAxC1miD;AAED,MAAM,MAAM,aAAa,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,IAAI,UAAU,CAAC,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;AAEjH,MAAM,WAAW,6BAA6B;IAC7C,OAAO,EAAE;QAAE,SAAS,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,OAAO,CAAA;KAAE,CAAC;IACjE,uEAAuE;IACvE,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,OAAO,CAAA;KAAE,CAAC;IAC1D,aAAa,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,6BAA6B,GAAG,MAAM,IAAI,CAqB/F"}
@@ -9,7 +9,7 @@ import { createCanvasArrangeController, matchArrangeShortcut } from '@pixerate/e
9
9
  * Wire snapping to SvelteFlow with `onnodedragstart={() => arrange.beginDrag()}`,
10
10
  * `onnodedrag={({ nodes }) => arrange.drag(nodes.map((n) => n.id))}` and
11
11
  * `onnodedragstop={() => arrange.endDrag()}`, and render `arrange.guides` with
12
- * `<CanvasGuidesLayer>`.
12
+ * `<CanvasGuidesLayer>`. Snap resizes with `<NodeResizer {...arrange.resizerHandlers(id)} />`.
13
13
  */
14
14
  export function createCanvasArrange(options) {
15
15
  let guides = $state.raw([]);
@@ -22,9 +22,26 @@ export function createCanvasArrange(options) {
22
22
  });
23
23
  return {
24
24
  ...controller,
25
- /** Guide segments (flow coordinates) for the active drag; empty otherwise. */
25
+ /** Guide segments (flow coordinates) for the active drag or resize; empty otherwise. */
26
26
  get guides() {
27
27
  return guides;
28
+ },
29
+ /**
30
+ * Resize snapping for SvelteFlow's `<NodeResizer>` / `<NodeResizeControl>`:
31
+ * `<NodeResizer {...arrange.resizerHandlers(id)} />`. The resizer writes the raw size
32
+ * to the nodes after `onResize`, so snapping runs a microtask later and writes the
33
+ * snapped box back. Locked nodes snap back to their starting box.
34
+ */
35
+ resizerHandlers(id) {
36
+ return {
37
+ onResizeStart: () => controller.beginResize(id),
38
+ onResize: () => {
39
+ queueMicrotask(() => controller.resize(id));
40
+ },
41
+ onResizeEnd: () => {
42
+ queueMicrotask(() => controller.endResize());
43
+ }
44
+ };
28
45
  }
29
46
  };
30
47
  }
@@ -0,0 +1,53 @@
1
+ import { type NodeRectOptions } from '@pixerate/editor/canvas';
2
+ import type { CanvasNode } from './types.js';
3
+ export interface CreateCanvasMeasureOptions<TNode extends CanvasNode = CanvasNode> {
4
+ getNodes: () => TNode[];
5
+ /** Ids measured from (default: nodes with `selected: true`). */
6
+ getSelectedIds?: () => string[];
7
+ /** Turns measuring off (default `true`). Read reactively. */
8
+ enabled?: boolean | (() => boolean);
9
+ /** Measure only while Alt / Option is held (default `true`); `false` always measures. */
10
+ requireModifier?: boolean;
11
+ rectOptions?: NodeRectOptions;
12
+ }
13
+ /**
14
+ * Headless Svelte 5 rune for Alt-hover distance measuring: hold Alt / Option and hover a
15
+ * node to see red distance lines with labels from the selection to it (or to the selection's
16
+ * parent when hovering the selection or empty space).
17
+ *
18
+ * Wire it with `$effect(() => measure.listen())`, `onnodepointerenter={measure.onnodepointerenter}`
19
+ * and `onnodepointerleave={measure.onnodepointerleave}` on `<SvelteFlow>`, and render
20
+ * `measure.guides` with `<CanvasGuidesLayer>` (concatenate with `arrange.guides` to share one).
21
+ */
22
+ export declare function createCanvasMeasure<TNode extends CanvasNode = CanvasNode>(options: CreateCanvasMeasureOptions<TNode>): {
23
+ /** Measuring lines (flow coordinates) for `<CanvasGuidesLayer>`. */
24
+ readonly guides: import("@pixerate/editor/canvas").MeasureGuide[];
25
+ /** The box measured to (outline it), or `null`. */
26
+ readonly target: import("@pixerate/editor/canvas").Rect | null;
27
+ /** Whether measuring is showing. */
28
+ readonly active: boolean;
29
+ readonly hoveredId: string | null;
30
+ setHovered(id: string | null): void;
31
+ /** Sets the modifier state by hand (e.g. from a toolbar toggle). */
32
+ setModifier(held: boolean): void;
33
+ onnodepointerenter: ({ node, event }: {
34
+ node: {
35
+ id: string;
36
+ };
37
+ event?: {
38
+ altKey?: boolean;
39
+ };
40
+ }) => void;
41
+ onnodepointerleave: ({ node }: {
42
+ node: {
43
+ id: string;
44
+ };
45
+ }) => void;
46
+ /**
47
+ * Tracks the Alt key on `target` (default `window`) and resets it when the window
48
+ * loses focus. Returns a cleanup function.
49
+ */
50
+ listen(target?: HTMLElement | Window | null): () => void;
51
+ };
52
+ export type CanvasMeasure = ReturnType<typeof createCanvasMeasure>;
53
+ //# sourceMappingURL=createCanvasMeasure.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createCanvasMeasure.svelte.d.ts","sourceRoot":"","sources":["../../src/canvas/createCanvasMeasure.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,KAAK,eAAe,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,0BAA0B,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU;IAChF,QAAQ,EAAE,MAAM,KAAK,EAAE,CAAC;IACxB,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;IAChC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IACpC,yFAAyF;IACzF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,eAAe,CAAC;CAC9B;AAID;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,EAAE,OAAO,EAAE,0BAA0B,CAAC,KAAK,CAAC;IAcnH,oEAAoE;;IAIpE,mDAAmD;;IAInD,oCAAoC;;;mBAOrB,MAAM,GAAG,IAAI;IAG5B,oEAAoE;sBAClD,OAAO;0CAGa;QAAE,IAAI,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,KAAK,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE;mCAI7D;QAAE,IAAI,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;IAGvD;;;OAGG;oBACY,WAAW,GAAG,MAAM,GAAG,IAAI,GAAmD,MAAM,IAAI;EAmBxG;AAED,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
@@ -0,0 +1,80 @@
1
+ import { isMeasureModifierHeld, measureSelection } from '@pixerate/editor/canvas';
2
+ const EMPTY = { guides: [], target: null, selection: null };
3
+ /**
4
+ * Headless Svelte 5 rune for Alt-hover distance measuring: hold Alt / Option and hover a
5
+ * node to see red distance lines with labels from the selection to it (or to the selection's
6
+ * parent when hovering the selection or empty space).
7
+ *
8
+ * Wire it with `$effect(() => measure.listen())`, `onnodepointerenter={measure.onnodepointerenter}`
9
+ * and `onnodepointerleave={measure.onnodepointerleave}` on `<SvelteFlow>`, and render
10
+ * `measure.guides` with `<CanvasGuidesLayer>` (concatenate with `arrange.guides` to share one).
11
+ */
12
+ export function createCanvasMeasure(options) {
13
+ let hoveredId = $state(null);
14
+ let modifier = $state(false);
15
+ const enabled = () => (typeof options.enabled === 'function' ? options.enabled() : options.enabled !== false);
16
+ const active = $derived(enabled() && (options.requireModifier === false || modifier));
17
+ const result = $derived.by(() => {
18
+ if (!active)
19
+ return EMPTY;
20
+ const nodes = options.getNodes();
21
+ const ids = options.getSelectedIds?.() ?? nodes.filter((n) => n.selected).map((n) => n.id);
22
+ return measureSelection(nodes, ids, hoveredId, options.rectOptions);
23
+ });
24
+ return {
25
+ /** Measuring lines (flow coordinates) for `<CanvasGuidesLayer>`. */
26
+ get guides() {
27
+ return result.guides;
28
+ },
29
+ /** The box measured to (outline it), or `null`. */
30
+ get target() {
31
+ return result.target;
32
+ },
33
+ /** Whether measuring is showing. */
34
+ get active() {
35
+ return active;
36
+ },
37
+ get hoveredId() {
38
+ return hoveredId;
39
+ },
40
+ setHovered(id) {
41
+ hoveredId = id;
42
+ },
43
+ /** Sets the modifier state by hand (e.g. from a toolbar toggle). */
44
+ setModifier(held) {
45
+ modifier = held;
46
+ },
47
+ onnodepointerenter: ({ node, event }) => {
48
+ hoveredId = node.id;
49
+ if (event && typeof event.altKey === 'boolean')
50
+ modifier = event.altKey;
51
+ },
52
+ onnodepointerleave: ({ node }) => {
53
+ if (hoveredId === node.id)
54
+ hoveredId = null;
55
+ },
56
+ /**
57
+ * Tracks the Alt key on `target` (default `window`) and resets it when the window
58
+ * loses focus. Returns a cleanup function.
59
+ */
60
+ listen(target = typeof window !== 'undefined' ? window : null) {
61
+ if (!target)
62
+ return () => { };
63
+ const onKey = (event) => {
64
+ modifier = isMeasureModifierHeld(event);
65
+ };
66
+ const onBlur = () => {
67
+ modifier = false;
68
+ };
69
+ target.addEventListener('keydown', onKey);
70
+ target.addEventListener('keyup', onKey);
71
+ const win = typeof window !== 'undefined' ? window : null;
72
+ win?.addEventListener('blur', onBlur);
73
+ return () => {
74
+ target.removeEventListener('keydown', onKey);
75
+ target.removeEventListener('keyup', onKey);
76
+ win?.removeEventListener('blur', onBlur);
77
+ };
78
+ }
79
+ };
80
+ }
@@ -17,6 +17,10 @@ export { createStreamingCanvas, type CreateStreamingCanvasOptions } from './crea
17
17
  export { createCanvasArrange, createCanvasArrangeShortcuts, type CanvasArrange, type CreateCanvasArrangeOptions, type CanvasArrangeShortcutsOptions } from './createCanvasArrange.svelte.js';
18
18
  export { createCanvasScenes, type CanvasScenes, type CreateCanvasScenesOptions } from './createCanvasScenes.svelte.js';
19
19
  export { default as CanvasGuidesLayer } from './CanvasGuidesLayer.svelte';
20
+ export { createCanvasMeasure, type CanvasMeasure, type CreateCanvasMeasureOptions } from './createCanvasMeasure.svelte.js';
21
+ export { measureBetween, measureToLine, measureSelection, measureLabel, isMeasureModifierHeld, snapToGrid, snapResize, snapNodeResize, inferResizeHandle, resizeHandleAxes, getResizingNodeRect, RESIZE_HANDLES, fitCubics, fitCubicRun, fitPathData, fitFreehandPath, cubicsToPathData, cubicPointAt, isStraightCubic, type MeasureGuide, type MeasureSelectionResult, type MeasureSelectionOptions, type ResizeHandle, type SnapResizeOptions, type SnapResizeResult, type SnapNodeResizeOptions, type SnapNodeResizeResult, type CubicSegment, type FitCubicsOptions, type FitPathDataOptions } from '@pixerate/editor/canvas';
20
22
  export { alignNodes, distributeNodes, matchSize, reorderNodes, applyNodeUpdates, invertNodeUpdates, lockNodes, unlockNodes, toggleLockNodes, isLocked, getNodeSize, getNodeRect, getAbsolutePosition, readStyleSize, withStyleSize, computeSnapGuides, createGuideIndex, createNodeGuideIndex, snapNodeDrag, findConnectorSnap, nearestHandlePositions, handlePoint, positionToSide, sideToPosition, oppositePosition, CONNECTION_LINE_TYPES, CanvasScenesStore, createCanvasScenesStore, parseCanvasScenes, fitViewportToRect, fitViewportToNodes, matchArrangeShortcut, DEFAULT_ARRANGE_SHORTCUTS, type AlignEdge, type DistributeAxis, type DistributeMode, type SizeDimension, type ZOrderOperation, type CanvasNodeUpdate, type GuideLine, type GuideIndex, type SnapGuidesOptions, type SnapResult, type ConnectorSnap, type ConnectorSnapTarget, type CanvasConnectionLineType, type CanvasScene, type CanvasScenesState, type CanvasViewport, type CanvasArrangeAction, type ArrangeShortcutBinding } from '@pixerate/editor/canvas';
23
+ export { default as ShapeNode } from './ShapeNode.svelte';
24
+ export { combineShapeNodes, canCombineShapeNodes, applyShapeCombination, revertShapeCombination, shapeNodeToPolygon, isShapeNode, getNodeShape, SHAPE_DATA_KEY, SHAPE_NODE_TYPE, SHAPE_OPERATIONS, SHAPE_KINDS, isShapeDescription, shapeToPolygon, getShapeSvg, getShapeRenderData, parseSvgPath, flattenSvgPath, polygonToSvgPath, simplifyRing, polygonBoolean, unionPolygons, intersectPolygons, subtractPolygons, xorPolygons, dividePolygons, polygonArea, multiPolygonArea, multiPolygonBounds, ringSignedArea, pointInRings, pointInMultiPolygon, type ShapeOperation, type ShapeDescription, type ShapeKind, type ShapePaint, type ShapeSize, type ShapeRenderData, type RectShape, type EllipseShape, type RegularPolygonShape, type StarShape, type PathShape, type PathCommand, type FlattenOptions, type PolygonToPathOptions, type CombineShapeNodesOptions, type CombineShapeNodesResult, type CombineShapeNodesRefusal, type ShapeNodePolygonOptions, type MultiPolygon, type PolygonWithHoles, type PolygonRing, type PolygonFillRule, type PolygonOperand, type PolygonInput, type PolygonPiece, type PolygonBooleanOperation, type PolygonBooleanOptions } from '@pixerate/editor/canvas';
21
25
  export { realClock, ManualClock, layoutBounds, canvasHeight, estimateCanvasHeight, blendLayout, connectedOnly, type Clock, type CanvasHeightOptions } from '@pixerate/editor/canvas';
22
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/canvas/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/canvas/index.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,12 @@ export { createStreamingCanvas } from './createStreamingCanvas.svelte.js';
34
34
  export { createCanvasArrange, createCanvasArrangeShortcuts } from './createCanvasArrange.svelte.js';
35
35
  export { createCanvasScenes } from './createCanvasScenes.svelte.js';
36
36
  export { default as CanvasGuidesLayer } from './CanvasGuidesLayer.svelte';
37
+ // Distance measuring (Alt-hover), resize / grid snapping and curve fitting
38
+ export { createCanvasMeasure } from './createCanvasMeasure.svelte.js';
39
+ export { measureBetween, measureToLine, measureSelection, measureLabel, isMeasureModifierHeld, snapToGrid, snapResize, snapNodeResize, inferResizeHandle, resizeHandleAxes, getResizingNodeRect, RESIZE_HANDLES, fitCubics, fitCubicRun, fitPathData, fitFreehandPath, cubicsToPathData, cubicPointAt, isStraightCubic } from '@pixerate/editor/canvas';
37
40
  export { alignNodes, distributeNodes, matchSize, reorderNodes, applyNodeUpdates, invertNodeUpdates, lockNodes, unlockNodes, toggleLockNodes, isLocked, getNodeSize, getNodeRect, getAbsolutePosition, readStyleSize, withStyleSize, computeSnapGuides, createGuideIndex, createNodeGuideIndex, snapNodeDrag, findConnectorSnap, nearestHandlePositions, handlePoint, positionToSide, sideToPosition, oppositePosition, CONNECTION_LINE_TYPES, CanvasScenesStore, createCanvasScenesStore, parseCanvasScenes, fitViewportToRect, fitViewportToNodes, matchArrangeShortcut, DEFAULT_ARRANGE_SHORTCUTS } from '@pixerate/editor/canvas';
41
+ export { default as ShapeNode } from './ShapeNode.svelte';
42
+ // Shape nodes and boolean operations (Unite / Intersect / Subtract / Exclude / Divide)
43
+ export { combineShapeNodes, canCombineShapeNodes, applyShapeCombination, revertShapeCombination, shapeNodeToPolygon, isShapeNode, getNodeShape, SHAPE_DATA_KEY, SHAPE_NODE_TYPE, SHAPE_OPERATIONS, SHAPE_KINDS, isShapeDescription, shapeToPolygon, getShapeSvg, getShapeRenderData, parseSvgPath, flattenSvgPath, polygonToSvgPath, simplifyRing, polygonBoolean, unionPolygons, intersectPolygons, subtractPolygons, xorPolygons, dividePolygons, polygonArea, multiPolygonArea, multiPolygonBounds, ringSignedArea, pointInRings, pointInMultiPolygon } from '@pixerate/editor/canvas';
38
44
  // Clock and view helpers from core
39
45
  export { realClock, ManualClock, layoutBounds, canvasHeight, estimateCanvasHeight, blendLayout, connectedOnly } from '@pixerate/editor/canvas';
@@ -0,0 +1,129 @@
1
+ <script lang="ts" module>
2
+ // Ids only render while the palette is open (client side), so a counter is enough
3
+ // (`$props.id()` needs Svelte 5.20, newer than our peer range).
4
+ let nextId = 0;
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import type { CommandPaletteItem } from '@pixerate/editor/commands';
9
+ import type { SvelteCommandPalette } from './palette.svelte.js';
10
+
11
+ interface Props {
12
+ /** State from `createCommandPalette`. */
13
+ palette: SvelteCommandPalette;
14
+ placeholder?: string;
15
+ emptyMessage?: string;
16
+ /** Extra classes for the dialog panel. */
17
+ class?: string;
18
+ }
19
+
20
+ let {
21
+ palette,
22
+ placeholder = 'Type a command…',
23
+ emptyMessage = 'No matching commands',
24
+ class: className = ''
25
+ }: Props = $props();
26
+
27
+ const baseId = `command-palette-${++nextId}`;
28
+ const listId = `${baseId}-list`;
29
+ const optionId = (index: number) => `${baseId}-option-${index}`;
30
+
31
+ let inputEl = $state<HTMLInputElement | null>(null);
32
+ let listEl = $state<HTMLUListElement | null>(null);
33
+
34
+ $effect(() => {
35
+ if (palette.open) inputEl?.focus();
36
+ });
37
+
38
+ $effect(() => {
39
+ void palette.activeIndex;
40
+ if (!palette.open) return;
41
+ const active = listEl?.querySelector<HTMLElement>('[aria-selected="true"]');
42
+ active?.scrollIntoView?.({ block: 'nearest' });
43
+ });
44
+
45
+ const segments = (item: CommandPaletteItem) => {
46
+ const marked = new Set(item.labelIndices);
47
+ return Array.from(item.label).map((ch, i) => ({ ch, match: marked.has(i) }));
48
+ };
49
+ </script>
50
+
51
+ {#if palette.open}
52
+ <!-- Headless-styled command palette: Arrow keys move, Enter runs, Escape or a backdrop press closes. -->
53
+ <div
54
+ class="fixed inset-0 z-50 flex items-start justify-center bg-black/30 px-4 pt-[15vh]"
55
+ data-command-palette-backdrop=""
56
+ role="presentation"
57
+ onmousedown={(e) => {
58
+ if (e.target === e.currentTarget) palette.setOpen(false);
59
+ }}
60
+ >
61
+ <div
62
+ role="dialog"
63
+ aria-modal="true"
64
+ aria-label="Command palette"
65
+ class="w-full max-w-lg overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-xl {className}"
66
+ style="background: var(--popover, var(--background, #fff));"
67
+ >
68
+ <input
69
+ bind:this={inputEl}
70
+ type="text"
71
+ role="combobox"
72
+ aria-expanded="true"
73
+ aria-controls={listId}
74
+ aria-autocomplete="list"
75
+ aria-activedescendant={palette.items.length ? optionId(palette.activeIndex) : undefined}
76
+ aria-label="Search commands"
77
+ {placeholder}
78
+ value={palette.query}
79
+ oninput={(e) => palette.setQuery(e.currentTarget.value)}
80
+ onkeydown={(e) => {
81
+ if (palette.handleKeyDown(e)) e.stopPropagation();
82
+ }}
83
+ class="w-full border-b border-border bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground"
84
+ />
85
+ {#if palette.items.length === 0}
86
+ <div class="px-3 py-6 text-center text-sm text-muted-foreground">{emptyMessage}</div>
87
+ {:else}
88
+ <ul bind:this={listEl} id={listId} role="listbox" aria-label="Commands" class="max-h-80 overflow-y-auto py-1">
89
+ {#each palette.items as item, index (item.id)}
90
+ <li
91
+ id={optionId(index)}
92
+ role="option"
93
+ aria-selected={index === palette.activeIndex}
94
+ aria-disabled={!item.enabled || undefined}
95
+ data-command-id={item.id}
96
+ data-recent={item.recent ? '' : undefined}
97
+ title={item.disabledReason}
98
+ onmousemove={() => index !== palette.activeIndex && palette.setActiveIndex(index)}
99
+ onmousedown={(e) => e.preventDefault()}
100
+ onclick={() => palette.run(item.id)}
101
+ onkeydown={() => {}}
102
+ class="flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm {index === palette.activeIndex
103
+ ? 'bg-muted'
104
+ : ''} {item.enabled ? '' : 'cursor-default opacity-50'}"
105
+ >
106
+ <span class="min-w-0 flex-1 truncate">
107
+ {#each segments(item) as segment, i (i)}{#if segment.match}<span
108
+ class="font-semibold text-foreground"
109
+ data-match="">{segment.ch}</span
110
+ >{:else}{segment.ch}{/if}{/each}
111
+ {#if item.disabledReason}
112
+ <span class="ml-2 text-xs text-muted-foreground" data-disabled-reason="">{item.disabledReason}</span>
113
+ {/if}
114
+ </span>
115
+ {#if item.group}
116
+ <span class="shrink-0 text-xs text-muted-foreground">{item.group}</span>
117
+ {/if}
118
+ {#if item.shortcut}
119
+ <kbd class="shrink-0 rounded border border-border px-1.5 font-mono text-[10px] text-muted-foreground"
120
+ >{item.shortcut}</kbd
121
+ >
122
+ {/if}
123
+ </li>
124
+ {/each}
125
+ </ul>
126
+ {/if}
127
+ </div>
128
+ </div>
129
+ {/if}
@@ -0,0 +1,13 @@
1
+ import type { SvelteCommandPalette } from './palette.svelte.js';
2
+ interface Props {
3
+ /** State from `createCommandPalette`. */
4
+ palette: SvelteCommandPalette;
5
+ placeholder?: string;
6
+ emptyMessage?: string;
7
+ /** Extra classes for the dialog panel. */
8
+ class?: string;
9
+ }
10
+ declare const CommandPalette: import("svelte").Component<Props, {}, "">;
11
+ type CommandPalette = ReturnType<typeof CommandPalette>;
12
+ export default CommandPalette;
13
+ //# sourceMappingURL=CommandPalette.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette.svelte.d.ts","sourceRoot":"","sources":["../../src/commands/CommandPalette.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG9D,UAAU,KAAK;IACb,yCAAyC;IACzC,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA8EH,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}