@joewinke/jatui 0.25.0 → 0.27.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 (74) hide show
  1. package/CLAUDE.md +115 -2
  2. package/dist/actions/keyboardScope.d.ts +58 -0
  3. package/dist/actions/keyboardScope.js +110 -0
  4. package/dist/actions/listNav.d.ts +48 -3
  5. package/dist/actions/listNav.js +267 -77
  6. package/dist/actions/longpress.d.ts +37 -0
  7. package/dist/actions/longpress.js +105 -0
  8. package/dist/actions/stickToBottom.d.ts +66 -0
  9. package/dist/actions/stickToBottom.js +155 -0
  10. package/dist/actions/swipe.d.ts +64 -0
  11. package/dist/actions/swipe.js +179 -0
  12. package/dist/components/BottomSheet.svelte +280 -0
  13. package/dist/components/BottomSheet.svelte.d.ts +27 -0
  14. package/dist/components/ConfirmDialog.svelte +21 -0
  15. package/dist/components/ConfirmDialog.svelte.d.ts +11 -1
  16. package/dist/components/ConfirmModal.svelte +15 -0
  17. package/dist/components/ConfirmModal.svelte.d.ts +5 -0
  18. package/dist/components/Drawer.svelte +18 -1
  19. package/dist/components/Drawer.svelte.d.ts +9 -1
  20. package/dist/components/InputDialog.svelte +21 -0
  21. package/dist/components/InputDialog.svelte.d.ts +11 -1
  22. package/dist/components/KeyboardShortcutsOverlay.svelte +5 -0
  23. package/dist/components/Modal.svelte +50 -1
  24. package/dist/components/Modal.svelte.d.ts +8 -0
  25. package/dist/components/basic/Empty.svelte +173 -9
  26. package/dist/components/basic/Empty.svelte.d.ts +50 -1
  27. package/dist/components/basic/ErrorState.svelte +128 -0
  28. package/dist/components/basic/ErrorState.svelte.d.ts +46 -0
  29. package/dist/components/basic/JumpToLatest.svelte +60 -0
  30. package/dist/components/basic/JumpToLatest.svelte.d.ts +9 -0
  31. package/dist/components/basic/PageHeader.svelte +216 -0
  32. package/dist/components/basic/PageHeader.svelte.d.ts +52 -0
  33. package/dist/components/basic/ProgressButton.svelte +221 -0
  34. package/dist/components/basic/ProgressButton.svelte.d.ts +63 -0
  35. package/dist/components/graph/GraphApiBridge.svelte +36 -0
  36. package/dist/components/graph/GraphApiBridge.svelte.d.ts +9 -0
  37. package/dist/components/graph/GraphCanvas.svelte +212 -27
  38. package/dist/components/graph/GraphCanvas.svelte.d.ts +113 -19
  39. package/dist/components/graph/api.d.ts +52 -0
  40. package/dist/components/graph/api.js +66 -0
  41. package/dist/components/graph/index.d.ts +2 -0
  42. package/dist/components/graph/index.js +1 -0
  43. package/dist/components/graph/theme.js +13 -0
  44. package/dist/components/messaging/MessageItem.svelte +5 -0
  45. package/dist/components/messaging/MessageItem.svelte.d.ts +7 -0
  46. package/dist/components/table/DataTable.svelte +607 -72
  47. package/dist/components/table/DataTable.svelte.d.ts +23 -2
  48. package/dist/components/table/ManageColumnsDropdown.svelte +466 -0
  49. package/dist/components/table/ManageColumnsDropdown.svelte.d.ts +26 -0
  50. package/dist/components/table/columnStorage.d.ts +29 -0
  51. package/dist/components/table/columnStorage.js +61 -0
  52. package/dist/components/table/groupRows.d.ts +72 -0
  53. package/dist/components/table/groupRows.js +118 -0
  54. package/dist/components/table/index.d.ts +7 -1
  55. package/dist/components/table/index.js +3 -0
  56. package/dist/components/table/types.d.ts +207 -3
  57. package/dist/index.d.ts +25 -2
  58. package/dist/index.js +32 -1
  59. package/dist/utils/ansiToHtml.d.ts +83 -0
  60. package/dist/utils/ansiToHtml.js +543 -0
  61. package/dist/utils/keyboardShortcutRegistry.d.ts +332 -11
  62. package/dist/utils/keyboardShortcutRegistry.js +1070 -76
  63. package/dist/utils/pollBackoff.d.ts +83 -0
  64. package/dist/utils/pollBackoff.js +151 -0
  65. package/dist/utils/snapFlick.d.ts +59 -0
  66. package/dist/utils/snapFlick.js +66 -0
  67. package/dist/video/advisors/qr.d.ts +1 -1
  68. package/dist/virtual/createRowWindow.d.ts +75 -0
  69. package/dist/virtual/createRowWindow.js +129 -0
  70. package/dist/virtual/index.d.ts +4 -0
  71. package/dist/virtual/index.js +5 -0
  72. package/dist/virtual/rowWindowCore.d.ts +202 -0
  73. package/dist/virtual/rowWindowCore.js +510 -0
  74. package/package.json +11 -1
@@ -1,27 +1,117 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="N extends Node = Node, E extends Edge = Edge">
2
2
  import '@xyflow/svelte/dist/style.css'
3
3
 
4
- import { SvelteFlow, Background, Controls, MiniMap, type Node, type Edge } from '@xyflow/svelte'
4
+ import {
5
+ SvelteFlow,
6
+ Background,
7
+ Controls,
8
+ MiniMap,
9
+ type Node,
10
+ type Edge,
11
+ type NodeTypes,
12
+ type EdgeTypes,
13
+ type DefaultEdgeOptions,
14
+ type Viewport,
15
+ type FitViewOptions,
16
+ type SnapGrid,
17
+ type ConnectionMode,
18
+ type MiniMapProps,
19
+ type BackgroundProps,
20
+ type NodeEventWithPointer,
21
+ type NodeEvents,
22
+ type EdgeEvents,
23
+ type PaneEvents,
24
+ type IsValidConnection,
25
+ type OnConnect,
26
+ type OnDelete,
27
+ type OnBeforeDelete,
28
+ type OnSelectionChange,
29
+ type OnMoveEnd,
30
+ type KeyDefinition,
31
+ } from '@xyflow/svelte'
32
+ import type { Snippet } from 'svelte'
5
33
  import JatuiNode from './JatuiNode.svelte'
34
+ import GraphApiBridge from './GraphApiBridge.svelte'
6
35
  import { watchAndApplyTheme } from './theme.js'
36
+ import {
37
+ createGraphCanvasApi,
38
+ mergeNodeTypes,
39
+ resolveBackgroundProps,
40
+ resolveMinimapProps,
41
+ viewportsEqual,
42
+ type GraphFlowHelpers,
43
+ } from './api.js'
7
44
 
8
- import type { NodeEventWithPointer } from '@xyflow/svelte'
45
+ // Named (exported) xyflow types only — indexed SvelteFlowProps[...] lookups
46
+ // make svelte-package drop the .d.ts as "not portable".
47
+ type Keys = KeyDefinition | KeyDefinition[] | null
9
48
 
10
49
  interface Props {
11
- /** Flow nodes — bindable for interactive graphs. */
12
- nodes: Node[]
13
- /** Flow edges — bindable for interactive graphs. */
14
- edges: Edge[]
15
- /** When true, disables pan/zoom/selection interactions. */
50
+ // ── data ──────────────────────────────────────────────
51
+ /** Flow nodes — bindable; drags and selection write back. */
52
+ nodes: N[]
53
+ /** Flow edges — bindable; connects and deletes write back. */
54
+ edges: E[]
55
+ /** When true, disables drag/connect/select/pan/zoom interactions. */
16
56
  readonly?: boolean
17
57
  /** Fit the graph to the viewport on first render. */
18
58
  fitView?: boolean
19
- /** Show the minimap panel. */
20
- minimap?: boolean
21
- /** Fired when the user clicks a node. */
22
- onNodeClick?: NodeEventWithPointer<MouseEvent | TouchEvent>
59
+ /** Show the minimap. Object form forwards MiniMap props (nodeColor, pannable, zoomable, …). */
60
+ minimap?: boolean | MiniMapProps
61
+ /** Fired when the user clicks a node (camelCase alias of `onnodeclick`; both fire). */
62
+ onNodeClick?: NodeEventWithPointer<MouseEvent | TouchEvent, N>
23
63
  /** Height of the canvas container (CSS value). Defaults to 100%. */
24
64
  height?: string
65
+
66
+ // ── types ─────────────────────────────────────────────
67
+ /** Custom node components, merged OVER `{ jatuiNode: JatuiNode }`. Keep the object stable. */
68
+ nodeTypes?: NodeTypes
69
+ /** Custom edge components. Keep the object stable. */
70
+ edgeTypes?: EdgeTypes
71
+ defaultEdgeOptions?: DefaultEdgeOptions
72
+
73
+ // ── viewport ──────────────────────────────────────────
74
+ /** Live viewport `{ x, y, zoom }` — bindable; updates on every pan/zoom. */
75
+ viewport?: Viewport
76
+ /** Default `{ padding: 0.2 }`. */
77
+ fitViewOptions?: FitViewOptions<N>
78
+ minZoom?: number
79
+ maxZoom?: number
80
+
81
+ // ── editing ───────────────────────────────────────────
82
+ snapToGrid?: boolean
83
+ snapGrid?: SnapGrid
84
+ connectionMode?: ConnectionMode
85
+ isValidConnection?: IsValidConnection<E>
86
+ onconnect?: OnConnect
87
+ onbeforedelete?: OnBeforeDelete<N, E>
88
+ ondelete?: OnDelete<N, E>
89
+ deleteKey?: Keys
90
+ panActivationKey?: Keys
91
+ multiSelectionKey?: Keys
92
+ selectionKey?: Keys
93
+ /** Defaults to `!readonly`. */
94
+ panOnDrag?: boolean | number[]
95
+ selectionOnDrag?: boolean
96
+ zoomOnDoubleClick?: boolean
97
+
98
+ // ── events (forwarded verbatim) ───────────────────────
99
+ onnodeclick?: NodeEvents<N>['onnodeclick']
100
+ onnodecontextmenu?: NodeEvents<N>['onnodecontextmenu']
101
+ onnodedragstart?: NodeEvents<N>['onnodedragstart']
102
+ onnodedragstop?: NodeEvents<N>['onnodedragstop']
103
+ onedgeclick?: EdgeEvents<E>['onedgeclick']
104
+ onpaneclick?: PaneEvents['onpaneclick']
105
+ onselectionchange?: OnSelectionChange<N, E>
106
+ onmoveend?: OnMoveEnd
107
+
108
+ // ── chrome ────────────────────────────────────────────
109
+ /** Show zoom controls. Default true. */
110
+ controls?: boolean
111
+ /** `false` hides the dot grid; an object overrides `{ gap: 16, size: 1 }`. */
112
+ background?: false | BackgroundProps
113
+ /** Rendered INSIDE <SvelteFlow> — Panels, overlays, anything that calls useSvelteFlow(). */
114
+ children?: Snippet
25
115
  }
26
116
 
27
117
  let {
@@ -32,9 +122,69 @@
32
122
  minimap = false,
33
123
  onNodeClick,
34
124
  height = '100%',
125
+ nodeTypes,
126
+ edgeTypes,
127
+ defaultEdgeOptions,
128
+ viewport = $bindable(),
129
+ fitViewOptions = { padding: 0.2 },
130
+ minZoom,
131
+ maxZoom,
132
+ snapToGrid,
133
+ snapGrid,
134
+ connectionMode,
135
+ isValidConnection,
136
+ onconnect,
137
+ onbeforedelete,
138
+ ondelete,
139
+ deleteKey,
140
+ panActivationKey,
141
+ multiSelectionKey,
142
+ selectionKey,
143
+ panOnDrag,
144
+ selectionOnDrag,
145
+ zoomOnDoubleClick,
146
+ onnodeclick,
147
+ onnodecontextmenu,
148
+ onnodedragstart,
149
+ onnodedragstop,
150
+ onedgeclick,
151
+ onpaneclick,
152
+ onselectionchange,
153
+ onmoveend,
154
+ controls = true,
155
+ background,
156
+ children,
35
157
  }: Props = $props()
36
158
 
37
- const nodeTypes = { jatuiNode: JatuiNode }
159
+ // Stable identity unless the consumer swaps nodeTypes — xyflow re-mounts
160
+ // every node when this object changes.
161
+ const resolvedNodeTypes = $derived(mergeNodeTypes({ jatuiNode: JatuiNode }, nodeTypes) as NodeTypes)
162
+ const minimapProps = $derived(resolveMinimapProps(minimap))
163
+ const backgroundProps = $derived(resolveBackgroundProps(background))
164
+
165
+ const handleNodeClick: NodeEventWithPointer<MouseEvent | TouchEvent, N> = (e) => {
166
+ onnodeclick?.(e)
167
+ onNodeClick?.(e)
168
+ }
169
+
170
+ let helpers: GraphFlowHelpers | null = null
171
+
172
+ /**
173
+ * Imperative handle (`bind:this={canvas}` → `canvas.api.fitView()`). Every
174
+ * method is a safe no-op until the flow initialises. `fitView` lives only on
175
+ * `api` because the `fitView` prop owns the bare name; the rest are also
176
+ * exported directly.
177
+ */
178
+ export const api = createGraphCanvasApi(() => helpers)
179
+ export const zoomTo = api.zoomTo
180
+ export const getZoom = api.getZoom
181
+ export const setCenter = api.setCenter
182
+ export const screenToFlowPosition = api.screenToFlowPosition
183
+ export const getViewport = api.getViewport
184
+
185
+ function syncViewport(v: Viewport) {
186
+ if (!viewportsEqual(viewport, v)) viewport = v
187
+ }
38
188
 
39
189
  let flowEl: HTMLDivElement | null = $state(null)
40
190
  let stopWatch: (() => void) | null = null
@@ -52,24 +202,54 @@
52
202
 
53
203
  <div class="graph-canvas" style:height bind:this={flowEl}>
54
204
  <SvelteFlow
55
- {nodes}
56
- {edges}
57
- {nodeTypes}
205
+ bind:nodes
206
+ bind:edges
207
+ bind:viewport
208
+ nodeTypes={resolvedNodeTypes}
209
+ {edgeTypes}
210
+ {defaultEdgeOptions}
58
211
  {fitView}
212
+ {fitViewOptions}
213
+ {minZoom}
214
+ {maxZoom}
215
+ {connectionMode}
216
+ {isValidConnection}
217
+ {onconnect}
218
+ {onbeforedelete}
219
+ {ondelete}
220
+ {deleteKey}
221
+ {panActivationKey}
222
+ {multiSelectionKey}
223
+ {selectionKey}
224
+ {selectionOnDrag}
225
+ {zoomOnDoubleClick}
226
+ snapGrid={snapToGrid ? (snapGrid ?? [16, 16]) : snapGrid}
59
227
  nodesDraggable={!readonly}
60
228
  nodesConnectable={!readonly}
61
229
  elementsSelectable={!readonly}
62
- panOnDrag={!readonly}
230
+ panOnDrag={panOnDrag ?? !readonly}
63
231
  zoomOnScroll={!readonly}
64
- onnodeclick={onNodeClick as NodeEventWithPointer<MouseEvent | TouchEvent> | undefined}
65
- fitViewOptions={{ padding: 0.2 }}
232
+ onnodeclick={handleNodeClick}
233
+ {onnodecontextmenu}
234
+ {onnodedragstart}
235
+ {onnodedragstop}
236
+ {onedgeclick}
237
+ {onpaneclick}
238
+ {onselectionchange}
239
+ {onmoveend}
66
240
  attributionPosition="bottom-right"
67
241
  >
68
- <Background gap={16} size={1} />
69
- <Controls showLock={false} />
70
- {#if minimap}
71
- <MiniMap />
242
+ <GraphApiBridge onready={(h) => (helpers = h)} onviewport={syncViewport} />
243
+ {#if backgroundProps}
244
+ <Background {...backgroundProps} />
245
+ {/if}
246
+ {#if controls}
247
+ <Controls showLock={false} />
248
+ {/if}
249
+ {#if minimapProps}
250
+ <MiniMap {...minimapProps} />
72
251
  {/if}
252
+ {@render children?.()}
73
253
  </SvelteFlow>
74
254
  </div>
75
255
 
@@ -84,19 +264,24 @@
84
264
  background: var(--color-base-100, oklch(0.98 0 0));
85
265
  }
86
266
 
87
- /* Strip xyflow's built-in node box-shadow (house rule: no at-rest shadows) */
88
- :global(.svelte-flow .svelte-flow__node) {
267
+ /*
268
+ * House rule (no at-rest shadows): strip xyflow's built-in shadow on the
269
+ * node WRAPPER — scoped to this canvas so other flows in the app are left
270
+ * alone. A custom node that draws a status glow on an INNER element is
271
+ * unaffected; put glows there, not on the wrapper.
272
+ */
273
+ .graph-canvas :global(.svelte-flow__node) {
89
274
  box-shadow: none !important;
90
275
  filter: none !important;
91
276
  }
92
277
 
93
278
  /* Edge animation uses opacity+stroke transitions, not layout — allowed */
94
- :global(.svelte-flow .svelte-flow__edge path) {
279
+ .graph-canvas :global(.svelte-flow__edge path) {
95
280
  transition: stroke var(--motion-fast) var(--motion-glide);
96
281
  }
97
282
 
98
283
  @media (prefers-reduced-motion: reduce) {
99
- :global(.svelte-flow .svelte-flow__edge path) {
284
+ .graph-canvas :global(.svelte-flow__edge path) {
100
285
  transition: none;
101
286
  }
102
287
  }
@@ -1,22 +1,116 @@
1
1
  import '@xyflow/svelte/dist/style.css';
2
- import { type Node, type Edge } from '@xyflow/svelte';
3
- import type { NodeEventWithPointer } from '@xyflow/svelte';
4
- interface Props {
5
- /** Flow nodes — bindable for interactive graphs. */
6
- nodes: Node[];
7
- /** Flow edges — bindable for interactive graphs. */
8
- edges: Edge[];
9
- /** When true, disables pan/zoom/selection interactions. */
10
- readonly?: boolean;
11
- /** Fit the graph to the viewport on first render. */
12
- fitView?: boolean;
13
- /** Show the minimap panel. */
14
- minimap?: boolean;
15
- /** Fired when the user clicks a node. */
16
- onNodeClick?: NodeEventWithPointer<MouseEvent | TouchEvent>;
17
- /** Height of the canvas container (CSS value). Defaults to 100%. */
18
- height?: string;
2
+ import { type Node, type Edge, type NodeTypes, type EdgeTypes, type DefaultEdgeOptions, type Viewport, type FitViewOptions, type SnapGrid, type ConnectionMode, type MiniMapProps, type BackgroundProps, type NodeEventWithPointer, type NodeEvents, type EdgeEvents, type PaneEvents, type IsValidConnection, type OnConnect, type OnDelete, type OnBeforeDelete, type OnSelectionChange, type OnMoveEnd, type KeyDefinition } from '@xyflow/svelte';
3
+ import type { Snippet } from 'svelte';
4
+ declare function $$render<N extends Node = Node, E extends Edge = Edge>(): {
5
+ props: {
6
+ /** Flow nodes — bindable; drags and selection write back. */
7
+ nodes: N[];
8
+ /** Flow edges — bindable; connects and deletes write back. */
9
+ edges: E[];
10
+ /** When true, disables drag/connect/select/pan/zoom interactions. */
11
+ readonly?: boolean;
12
+ /** Fit the graph to the viewport on first render. */
13
+ fitView?: boolean;
14
+ /** Show the minimap. Object form forwards MiniMap props (nodeColor, pannable, zoomable, …). */
15
+ minimap?: boolean | MiniMapProps;
16
+ /** Fired when the user clicks a node (camelCase alias of `onnodeclick`; both fire). */
17
+ onNodeClick?: NodeEventWithPointer<MouseEvent | TouchEvent, N>;
18
+ /** Height of the canvas container (CSS value). Defaults to 100%. */
19
+ height?: string;
20
+ /** Custom node components, merged OVER `{ jatuiNode: JatuiNode }`. Keep the object stable. */
21
+ nodeTypes?: NodeTypes;
22
+ /** Custom edge components. Keep the object stable. */
23
+ edgeTypes?: EdgeTypes;
24
+ defaultEdgeOptions?: DefaultEdgeOptions;
25
+ /** Live viewport `{ x, y, zoom }` — bindable; updates on every pan/zoom. */
26
+ viewport?: Viewport;
27
+ /** Default `{ padding: 0.2 }`. */
28
+ fitViewOptions?: FitViewOptions<N>;
29
+ minZoom?: number;
30
+ maxZoom?: number;
31
+ snapToGrid?: boolean;
32
+ snapGrid?: SnapGrid;
33
+ connectionMode?: ConnectionMode;
34
+ isValidConnection?: IsValidConnection<E>;
35
+ onconnect?: OnConnect;
36
+ onbeforedelete?: OnBeforeDelete<N, E>;
37
+ ondelete?: OnDelete<N, E>;
38
+ deleteKey?: KeyDefinition | KeyDefinition[] | null;
39
+ panActivationKey?: KeyDefinition | KeyDefinition[] | null;
40
+ multiSelectionKey?: KeyDefinition | KeyDefinition[] | null;
41
+ selectionKey?: KeyDefinition | KeyDefinition[] | null;
42
+ /** Defaults to `!readonly`. */
43
+ panOnDrag?: boolean | number[];
44
+ selectionOnDrag?: boolean;
45
+ zoomOnDoubleClick?: boolean;
46
+ onnodeclick?: NodeEvents<N>["onnodeclick"];
47
+ onnodecontextmenu?: NodeEvents<N>["onnodecontextmenu"];
48
+ onnodedragstart?: NodeEvents<N>["onnodedragstart"];
49
+ onnodedragstop?: NodeEvents<N>["onnodedragstop"];
50
+ onedgeclick?: EdgeEvents<E>["onedgeclick"];
51
+ onpaneclick?: PaneEvents["onpaneclick"];
52
+ onselectionchange?: OnSelectionChange<N, E>;
53
+ onmoveend?: OnMoveEnd;
54
+ /** Show zoom controls. Default true. */
55
+ controls?: boolean;
56
+ /** `false` hides the dot grid; an object overrides `{ gap: 16, size: 1 }`. */
57
+ background?: false | BackgroundProps;
58
+ /** Rendered INSIDE <SvelteFlow> — Panels, overlays, anything that calls useSvelteFlow(). */
59
+ children?: Snippet;
60
+ };
61
+ exports: {
62
+ /**
63
+ * Imperative handle (`bind:this={canvas}` → `canvas.api.fitView()`). Every
64
+ * method is a safe no-op until the flow initialises. `fitView` lives only on
65
+ * `api` because the `fitView` prop owns the bare name; the rest are also
66
+ * exported directly.
67
+ */ api: import("./api.js").GraphCanvasApi;
68
+ zoomTo: (zoom: number, options?: {
69
+ duration?: number;
70
+ }) => Promise<boolean>;
71
+ getZoom: () => number;
72
+ setCenter: (x: number, y: number, options?: {
73
+ zoom?: number;
74
+ duration?: number;
75
+ }) => Promise<boolean>;
76
+ screenToFlowPosition: (p: import("@xyflow/system").XYPosition) => import("@xyflow/system").XYPosition;
77
+ getViewport: () => Viewport;
78
+ };
79
+ bindings: "viewport" | "nodes" | "edges";
80
+ slots: {};
81
+ events: {};
82
+ };
83
+ declare class __sveltets_Render<N extends Node = Node, E extends Edge = Edge> {
84
+ props(): ReturnType<typeof $$render<N, E>>['props'];
85
+ events(): ReturnType<typeof $$render<N, E>>['events'];
86
+ slots(): ReturnType<typeof $$render<N, E>>['slots'];
87
+ bindings(): "viewport" | "nodes" | "edges";
88
+ exports(): {
89
+ /**
90
+ * Imperative handle (`bind:this={canvas}` → `canvas.api.fitView()`). Every
91
+ * method is a safe no-op until the flow initialises. `fitView` lives only on
92
+ * `api` because the `fitView` prop owns the bare name; the rest are also
93
+ * exported directly.
94
+ */ api: import("./api.js").GraphCanvasApi;
95
+ zoomTo: (zoom: number, options?: {
96
+ duration?: number;
97
+ }) => Promise<boolean>;
98
+ getZoom: () => number;
99
+ setCenter: (x: number, y: number, options?: {
100
+ zoom?: number;
101
+ duration?: number;
102
+ }) => Promise<boolean>;
103
+ screenToFlowPosition: (p: import("@xyflow/system").XYPosition) => import("@xyflow/system").XYPosition;
104
+ getViewport: () => Viewport;
105
+ };
19
106
  }
20
- declare const GraphCanvas: import("svelte").Component<Props, {}, "nodes" | "edges">;
21
- type GraphCanvas = ReturnType<typeof GraphCanvas>;
107
+ interface $$IsomorphicComponent {
108
+ new <N extends Node = Node, E extends Edge = Edge>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<N, E>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<N, E>['props']>, ReturnType<__sveltets_Render<N, E>['events']>, ReturnType<__sveltets_Render<N, E>['slots']>> & {
109
+ $$bindings?: ReturnType<__sveltets_Render<N, E>['bindings']>;
110
+ } & ReturnType<__sveltets_Render<N, E>['exports']>;
111
+ <N extends Node = Node, E extends Edge = Edge>(internal: unknown, props: ReturnType<__sveltets_Render<N, E>['props']> & {}): ReturnType<__sveltets_Render<N, E>['exports']>;
112
+ z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
113
+ }
114
+ declare const GraphCanvas: $$IsomorphicComponent;
115
+ type GraphCanvas<N extends Node = Node, E extends Edge = Edge> = InstanceType<typeof GraphCanvas<N, E>>;
22
116
  export default GraphCanvas;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * GraphCanvas imperative API + pure prop helpers.
3
+ *
4
+ * `useSvelteFlow()` only works inside a CHILD of <SvelteFlow>, so a consumer
5
+ * holding `bind:this` on <GraphCanvas> can't reach it. GraphCanvas mounts a
6
+ * tiny internal child (GraphApiBridge) that calls the hook and hands the
7
+ * helpers up; `createGraphCanvasApi` wraps them so every method is a safe
8
+ * no-op until the flow has initialised.
9
+ *
10
+ * Kept free of Svelte/xyflow runtime imports so it is unit-testable in node.
11
+ */
12
+ import type { BackgroundProps, FitViewOptions, MiniMapProps, Viewport, XYPosition } from '@xyflow/svelte';
13
+ /** Imperative handle exposed on a GraphCanvas instance (`bind:this`). */
14
+ export interface GraphCanvasApi {
15
+ fitView(options?: FitViewOptions): Promise<boolean>;
16
+ zoomTo(zoom: number, options?: {
17
+ duration?: number;
18
+ }): Promise<boolean>;
19
+ getZoom(): number;
20
+ setCenter(x: number, y: number, options?: {
21
+ zoom?: number;
22
+ duration?: number;
23
+ }): Promise<boolean>;
24
+ screenToFlowPosition(p: XYPosition): XYPosition;
25
+ getViewport(): Viewport;
26
+ }
27
+ /** The subset of `useSvelteFlow()` the bridge hands up. */
28
+ export type GraphFlowHelpers = GraphCanvasApi;
29
+ export declare const DEFAULT_VIEWPORT: Viewport;
30
+ /**
31
+ * Build a GraphCanvasApi over a lazily-resolved helper set. Before the bridge
32
+ * reports in, methods resolve `false` / return identity values instead of
33
+ * throwing.
34
+ */
35
+ export declare function createGraphCanvasApi(getHelpers: () => GraphFlowHelpers | null): GraphCanvasApi;
36
+ export declare function viewportsEqual(a: Viewport | undefined, b: Viewport | undefined): boolean;
37
+ /** `minimap` prop → MiniMap props, or null when hidden. */
38
+ export declare function resolveMinimapProps(minimap: boolean | MiniMapProps | undefined): MiniMapProps | null;
39
+ export declare const DEFAULT_BACKGROUND: BackgroundProps;
40
+ /** `background` prop → Background props, or null when disabled. */
41
+ export declare function resolveBackgroundProps(background: false | BackgroundProps | undefined): BackgroundProps | null;
42
+ /**
43
+ * Merge consumer nodeTypes OVER the built-ins. Callers must memoise the
44
+ * result — xyflow re-mounts every node when the nodeTypes identity changes.
45
+ */
46
+ export declare function mergeNodeTypes<T extends Record<string, unknown>>(builtins: T, custom: Record<string, unknown> | undefined): Record<string, unknown>;
47
+ /**
48
+ * Viewport for zooming to `zoom` while keeping the point at the centre of a
49
+ * `width`×`height` pane fixed (xyflow 1.x has no zoomTo helper; setViewport
50
+ * alone would zoom around the top-left corner).
51
+ */
52
+ export declare function zoomViewportAround(current: Viewport, zoom: number, width: number, height: number): Viewport;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * GraphCanvas imperative API + pure prop helpers.
3
+ *
4
+ * `useSvelteFlow()` only works inside a CHILD of <SvelteFlow>, so a consumer
5
+ * holding `bind:this` on <GraphCanvas> can't reach it. GraphCanvas mounts a
6
+ * tiny internal child (GraphApiBridge) that calls the hook and hands the
7
+ * helpers up; `createGraphCanvasApi` wraps them so every method is a safe
8
+ * no-op until the flow has initialised.
9
+ *
10
+ * Kept free of Svelte/xyflow runtime imports so it is unit-testable in node.
11
+ */
12
+ export const DEFAULT_VIEWPORT = { x: 0, y: 0, zoom: 1 };
13
+ /**
14
+ * Build a GraphCanvasApi over a lazily-resolved helper set. Before the bridge
15
+ * reports in, methods resolve `false` / return identity values instead of
16
+ * throwing.
17
+ */
18
+ export function createGraphCanvasApi(getHelpers) {
19
+ return {
20
+ fitView: (options) => getHelpers()?.fitView(options) ?? Promise.resolve(false),
21
+ zoomTo: (zoom, options) => getHelpers()?.zoomTo(zoom, options) ?? Promise.resolve(false),
22
+ getZoom: () => getHelpers()?.getZoom() ?? DEFAULT_VIEWPORT.zoom,
23
+ setCenter: (x, y, options) => getHelpers()?.setCenter(x, y, options) ?? Promise.resolve(false),
24
+ screenToFlowPosition: (p) => getHelpers()?.screenToFlowPosition(p) ?? { x: p.x, y: p.y },
25
+ getViewport: () => getHelpers()?.getViewport() ?? { ...DEFAULT_VIEWPORT },
26
+ };
27
+ }
28
+ export function viewportsEqual(a, b) {
29
+ if (a === b)
30
+ return true;
31
+ if (!a || !b)
32
+ return false;
33
+ return a.x === b.x && a.y === b.y && a.zoom === b.zoom;
34
+ }
35
+ /** `minimap` prop → MiniMap props, or null when hidden. */
36
+ export function resolveMinimapProps(minimap) {
37
+ if (!minimap)
38
+ return null;
39
+ return minimap === true ? {} : minimap;
40
+ }
41
+ export const DEFAULT_BACKGROUND = { gap: 16, size: 1 };
42
+ /** `background` prop → Background props, or null when disabled. */
43
+ export function resolveBackgroundProps(background) {
44
+ if (background === false)
45
+ return null;
46
+ return { ...DEFAULT_BACKGROUND, ...(background ?? {}) };
47
+ }
48
+ /**
49
+ * Merge consumer nodeTypes OVER the built-ins. Callers must memoise the
50
+ * result — xyflow re-mounts every node when the nodeTypes identity changes.
51
+ */
52
+ export function mergeNodeTypes(builtins, custom) {
53
+ return custom ? { ...builtins, ...custom } : builtins;
54
+ }
55
+ /**
56
+ * Viewport for zooming to `zoom` while keeping the point at the centre of a
57
+ * `width`×`height` pane fixed (xyflow 1.x has no zoomTo helper; setViewport
58
+ * alone would zoom around the top-left corner).
59
+ */
60
+ export function zoomViewportAround(current, zoom, width, height) {
61
+ const cx = width / 2;
62
+ const cy = height / 2;
63
+ const fx = (cx - current.x) / current.zoom;
64
+ const fy = (cy - current.y) / current.zoom;
65
+ return { x: cx - fx * zoom, y: cy - fy * zoom, zoom };
66
+ }
@@ -2,3 +2,5 @@ export { default as GraphCanvas } from './GraphCanvas.svelte';
2
2
  export { default as JatuiNode } from './JatuiNode.svelte';
3
3
  export { getStatusColor, applyXyflowTheme, watchAndApplyTheme } from './theme';
4
4
  export type { NodeStatus } from './theme';
5
+ export { createGraphCanvasApi, DEFAULT_VIEWPORT, DEFAULT_BACKGROUND } from './api';
6
+ export type { GraphCanvasApi } from './api';
@@ -2,3 +2,4 @@
2
2
  export { default as GraphCanvas } from './GraphCanvas.svelte';
3
3
  export { default as JatuiNode } from './JatuiNode.svelte';
4
4
  export { getStatusColor, applyXyflowTheme, watchAndApplyTheme } from './theme';
5
+ export { createGraphCanvasApi, DEFAULT_VIEWPORT, DEFAULT_BACKGROUND } from './api';
@@ -32,6 +32,13 @@ const THEME_MAP = [
32
32
  // Selection box
33
33
  ['--xy-selection-background-color', 'color-mix(in oklch, var(--color-primary, #6366f1) 10%, transparent)'],
34
34
  ['--xy-selection-border', '1px dotted color-mix(in oklch, var(--color-primary, #6366f1) 50%, transparent)'],
35
+ // Controls (zoom buttons) — otherwise stay light-on-dark in dark themes
36
+ ['--xy-controls-button-background-color', 'var(--color-base-100, #fefefe)'],
37
+ ['--xy-controls-button-background-color-hover', 'var(--color-base-200, #f4f4f4)'],
38
+ ['--xy-controls-button-color', 'var(--color-base-content, inherit)'],
39
+ ['--xy-controls-button-color-hover', 'var(--color-base-content, inherit)'],
40
+ ['--xy-controls-button-border-color', 'var(--color-base-300, #eee)'],
41
+ ['--xy-controls-box-shadow', 'none'],
35
42
  // Attribution
36
43
  ['--xy-attribution-background-color', 'color-mix(in oklch, var(--color-base-100, #fff) 80%, transparent)'],
37
44
  ];
@@ -112,6 +119,12 @@ function resolveThemeValue(prop) {
112
119
  '--xy-minimap-node-background-color': () => getDaisyUIColor('base-200'),
113
120
  '--xy-selection-background-color': () => `color-mix(in oklch, ${getDaisyUIColor('primary')} 10%, transparent)`,
114
121
  '--xy-selection-border': () => `1px dotted color-mix(in oklch, ${getDaisyUIColor('primary')} 50%, transparent)`,
122
+ '--xy-controls-button-background-color': () => getDaisyUIColor('base-100'),
123
+ '--xy-controls-button-background-color-hover': () => getDaisyUIColor('base-200'),
124
+ '--xy-controls-button-color': () => getDaisyUIColor('base-content'),
125
+ '--xy-controls-button-color-hover': () => getDaisyUIColor('base-content'),
126
+ '--xy-controls-button-border-color': () => getDaisyUIColor('base-300'),
127
+ '--xy-controls-box-shadow': () => 'none',
115
128
  '--xy-attribution-background-color': () => `color-mix(in oklch, ${getDaisyUIColor('base-100')} 80%, transparent)`,
116
129
  };
117
130
  return propToColor[prop]?.() ?? '';
@@ -16,6 +16,7 @@
16
16
  AttachmentCallbacks,
17
17
  } from '../../types/messaging'
18
18
  import { renderMentionsAsHTML, extractPlainText } from '../../utils/mentionParser'
19
+ import { longpress } from '../../actions/longpress'
19
20
  import Avatar from './Avatar.svelte'
20
21
  import QuotedMessageDisplay from './QuotedMessageDisplay.svelte'
21
22
  import MessageAttachment from './MessageAttachment.svelte'
@@ -49,6 +50,8 @@
49
50
  swipeActions?: boolean
50
51
  /** Snippet rendered in the revealed action zone (right side, behind the item). */
51
52
  swipeContent?: Snippet
53
+ /** Long-press (touch) / right-click (mouse) affordance — e.g. open a context sheet. Fires with the message id and gesture coordinates. */
54
+ onlongpress?: (detail: { messageId: string; x: number; y: number; source: 'touch' | 'mouse' }) => void
52
55
  }
53
56
 
54
57
  let {
@@ -72,6 +75,7 @@
72
75
  onreactionchange,
73
76
  swipeActions = false,
74
77
  swipeContent,
78
+ onlongpress,
75
79
  }: Props = $props()
76
80
 
77
81
  let isEditing = $state(false)
@@ -276,6 +280,7 @@
276
280
  class:pt-2={!isConsecutive}
277
281
  data-message-id={message.id}
278
282
  onclick={handleMessageClick}
283
+ use:longpress={{ enabled: !!onlongpress, onLongPress: (d) => onlongpress?.({ messageId: message.id, x: d.x, y: d.y, source: d.source }) }}
279
284
  role="button"
280
285
  tabindex="0"
281
286
  onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleMessageClick(e as any) } }}
@@ -40,6 +40,13 @@ interface Props {
40
40
  swipeActions?: boolean;
41
41
  /** Snippet rendered in the revealed action zone (right side, behind the item). */
42
42
  swipeContent?: Snippet;
43
+ /** Long-press (touch) / right-click (mouse) affordance — e.g. open a context sheet. Fires with the message id and gesture coordinates. */
44
+ onlongpress?: (detail: {
45
+ messageId: string;
46
+ x: number;
47
+ y: number;
48
+ source: 'touch' | 'mouse';
49
+ }) => void;
43
50
  }
44
51
  declare const MessageItem: import("svelte").Component<Props, {}, "">;
45
52
  type MessageItem = ReturnType<typeof MessageItem>;