@hlang-tech/editor-ui 0.0.0-stage → 0.1.1

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.
@@ -0,0 +1,971 @@
1
+ import { NodeKind, EditorNode, PortDescriptor, EditorCore, NodeId, Diagnostic, ParamValue, NodeDescriptor, ParamBag, Endpoint, IConnectionValidator, Point, IExecutionHandle } from '@hlang-tech/editor-core';
2
+ import { CSSProperties, ReactNode, ReactElement, MouseEvent } from 'react';
3
+ import { NodeProps, EdgeProps, NodeTypes, EdgeTypes, OnConnectStart, OnConnectEnd } from '@xyflow/react';
4
+ import { HlangRFNode, HlangRFEdge } from '@hlang-tech/editor-react';
5
+
6
+ /**
7
+ * Typed accessors for the design tokens. This module only NAMES the CSS custom
8
+ * properties declared in `tokens.css` — it never duplicates their values, so
9
+ * there is a single source of truth. Components reference tokens through here
10
+ * (e.g. `KIND_TOKEN[node.kind]`) instead of hardcoding hex values.
11
+ */
12
+
13
+ /** The full set of token custom-property names (without the leading `--`). */
14
+ declare const TOKEN_NAMES: readonly ["hl-void", "hl-void-2", "hl-surface", "hl-surface-2", "hl-line", "hl-line-strong", "hl-silver", "hl-silver-hi", "hl-silver-lo", "hl-spark", "hl-r", "hl-t", "hl-w", "hl-error", "hl-ink", "hl-ink-2", "hl-ink-3", "hl-font-ui", "hl-font-mono", "hl-r-sm", "hl-r-md", "hl-r-lg", "hl-r-pill", "hl-shadow-node", "hl-shadow-glass", "hl-glow-silver", "hl-ease"];
15
+ /** A token name (without `--`), e.g. `'hl-silver'`. */
16
+ type TokenName = (typeof TOKEN_NAMES)[number];
17
+ /** `var(--<name>)` reference string for a token. */
18
+ declare function tokenVar(name: TokenName): string;
19
+ /** Maps a node kind to its color-temperature token reference. */
20
+ declare const KIND_TOKEN: Record<NodeKind, string>;
21
+ /** Human-readable role label per node kind (used in node headers). */
22
+ declare const KIND_ROLE: Record<NodeKind, string>;
23
+
24
+ /**
25
+ * Theming: a host overrides any subset of tokens by producing a CSS-variable
26
+ * style object with `createTheme(...)` and applying it (via
27
+ * `EditorUIThemeProvider` or directly on a wrapper element). Zero-runtime and
28
+ * SSR-safe — it is just inline custom properties.
29
+ */
30
+
31
+ /**
32
+ * A partial override map from token name to CSS value. Values may be
33
+ * `undefined` (treated as "not overridden") so callers can pass conditional
34
+ * overrides without pre-filtering.
35
+ */
36
+ type ThemeOverride = Partial<Record<TokenName, string | undefined>>;
37
+ /**
38
+ * Build a style object that overrides the given tokens as inline custom
39
+ * properties. Apply it to a wrapping element to re-theme everything beneath.
40
+ *
41
+ * ```ts
42
+ * <div style={createTheme({ 'hl-silver': '#d8dce4' })}> … </div>
43
+ * ```
44
+ */
45
+ declare function createTheme(overrides: ThemeOverride): CSSProperties;
46
+
47
+ /**
48
+ * `EditorUIThemeProvider` — applies a token override to its subtree by wrapping
49
+ * children in an element carrying the inline custom properties from
50
+ * `createTheme`. Import `@hlang-tech/editor-ui/tokens.css` once at the app root
51
+ * for the base tokens; use this provider only to override.
52
+ */
53
+
54
+ interface EditorUIThemeProviderProps {
55
+ readonly theme?: ThemeOverride;
56
+ readonly className?: string;
57
+ readonly style?: CSSProperties;
58
+ readonly children: ReactNode;
59
+ }
60
+ declare function EditorUIThemeProvider({ theme, className, style, children, }: EditorUIThemeProviderProps): ReactElement;
61
+
62
+ /**
63
+ * The R/T/W node component. A near-monochrome glass card whose personality
64
+ * comes from a thin kind-tinted signal bar and glowing ports; body content is
65
+ * an extension point (empty for generic nodes). Ports are laid out in two
66
+ * columns — inputs (I) left, outputs (O) right — each Handle anchored to its
67
+ * own row so the `@node/error` port never drifts. W nodes render no error port
68
+ * because their descriptor has none.
69
+ */
70
+
71
+ /** Presentational node chrome, decoupled from React Flow for easy testing. */
72
+ interface NodeChromeProps {
73
+ readonly node: EditorNode;
74
+ readonly selected?: boolean;
75
+ readonly body?: ReactNode;
76
+ }
77
+ declare function NodeChrome({ node, selected, body }: NodeChromeProps): ReactElement;
78
+ /**
79
+ * The React Flow node component. Reads everything from `data.editorNode`, then
80
+ * DISPATCHES to the right dressing — all in editor-ui, with no change to the
81
+ * runtime (every case is just a node with ports):
82
+ * - an image asset (params.imageUrl) → {@link AssetNodeChrome} (framed photo)
83
+ * - anything else → {@link PillNodeChrome} (nover capsule)
84
+ * The verbose port-board {@link NodeChrome} stays available as a future
85
+ * "expanded" view.
86
+ */
87
+ declare function HlangNode({ data, selected }: NodeProps<HlangRFNode>): ReactElement;
88
+
89
+ /**
90
+ * `AssetNode` — a source node dressed as a real image asset.
91
+ *
92
+ * Architecturally this is just an R node: to the runtime it has output ports and
93
+ * emits its image (its `params.imageUrl`). editor-ui is free to DRESS it as a
94
+ * framed photograph — a frosted-glass card whose hero is the image itself — with
95
+ * no change to the input/output contract. This is the layering: the bottom is an
96
+ * input→output DAG; this layer decides what the node looks like.
97
+ *
98
+ * Ports are reused verbatim (PortRow → React Flow Handle), so wiring behaves
99
+ * exactly like any other node; the output handle rides the image's right edge.
100
+ */
101
+
102
+ interface AssetNodeChromeProps {
103
+ readonly node: EditorNode;
104
+ readonly selected?: boolean;
105
+ }
106
+ declare function AssetNodeChrome({ node, selected }: AssetNodeChromeProps): ReactElement;
107
+
108
+ /**
109
+ * `PillNode` — the default dressing for a non-asset node: a nover-style capsule.
110
+ *
111
+ * To the runtime this is still an ordinary R/T/W node with ports; editor-ui just
112
+ * dresses it as a rounded pill — a kind-tinted domain dot, the name, and a
113
+ * status dot — with the ports HIDDEN at rest and revealed on hover/selection.
114
+ * The picture, not the plumbing. The verbose port-board (`NodeChrome`) remains
115
+ * available as an "expanded" view for later.
116
+ *
117
+ * Ports reuse `PortRow` (so wiring is identical); the pill styles the handles
118
+ * to ride its left/right edges as small silver dots.
119
+ */
120
+
121
+ interface PillNodeChromeProps {
122
+ readonly node: EditorNode;
123
+ readonly selected?: boolean;
124
+ }
125
+ declare function PillNodeChrome({ node, selected }: PillNodeChromeProps): ReactElement;
126
+
127
+ /**
128
+ * The thin, kind-tinted bar across the top of a node — the node's kind
129
+ * expressed with restraint (no large color fills). Reads `--hl-kind` set by the
130
+ * node's kind class.
131
+ */
132
+
133
+ declare function SignalBar(): ReactElement;
134
+
135
+ /**
136
+ * A single port row: a React Flow `Handle` anchored to this row (so it centers
137
+ * on its own label, never drifting) plus the port's mono label. Renders the
138
+ * live/value distinction (filled vs hollow handle) and the error hue.
139
+ */
140
+
141
+ interface PortRowProps {
142
+ readonly port: PortDescriptor;
143
+ }
144
+ declare function PortRow({ port }: PortRowProps): ReactElement;
145
+
146
+ /**
147
+ * The node-body extension point. In M-UI-1 this renders nothing (generic nodes
148
+ * stay clean R/T/W cards). M-UI-5 wires it to the NodeBodyRegistry so a host
149
+ * application can render custom content — image/video previews, inline
150
+ * widgets — per node `code`/`kind` without editor-ui knowing anything about it.
151
+ */
152
+
153
+ interface NodeBodySlotProps {
154
+ readonly node: EditorNode;
155
+ /** Custom body content, resolved by the host/registry. Empty renders nothing. */
156
+ readonly children?: ReactNode;
157
+ }
158
+ declare function NodeBodySlot({ children }: NodeBodySlotProps): ReactElement | null;
159
+
160
+ /**
161
+ * The signature edge: a connection rendered as a living stream. A base bezier
162
+ * conduit plus a silver overlay that marches from output (O) to input (I) —
163
+ * exactly what an hlang RxJS port does; a cold spark lights it while flowing.
164
+ *
165
+ * When the SOURCE node carries a semantic `role` param (e.g. Character,
166
+ * Background), a small glass label pill rides the edge midpoint — naming what
167
+ * the connection carries, the way nover labels its wires. The role is read from
168
+ * the source node's params (no change to editor-react's edge data).
169
+ */
170
+
171
+ declare function FlowEdge(props: EdgeProps<HlangRFEdge>): ReactElement;
172
+
173
+ /**
174
+ * One-line React Flow wiring: `createHlangFlowTypes()` returns the `nodeTypes`
175
+ * and `edgeTypes` maps to spread onto `<ReactFlow>`, binding hlang's R/T/W node
176
+ * kinds to `HlangNode` and the default edge to the flowing `FlowEdge`.
177
+ *
178
+ * ```tsx
179
+ * const { nodeTypes, edgeTypes } = createHlangFlowTypes();
180
+ * <ReactFlow nodeTypes={nodeTypes} edgeTypes={edgeTypes} {...useHlangFlow(core)} />
181
+ * ```
182
+ */
183
+
184
+ interface HlangFlowTypes {
185
+ readonly nodeTypes: NodeTypes;
186
+ readonly edgeTypes: EdgeTypes;
187
+ }
188
+ /** Build the React Flow node/edge type maps for the hlang editor. */
189
+ declare function createHlangFlowTypes(): HlangFlowTypes;
190
+
191
+ /**
192
+ * `EditorShell` — composes the editor's regions (top toolbar, left panel,
193
+ * center canvas, right inspector) as a CSS grid. It holds NO graph logic; a
194
+ * host slots children into named regions. Left/right panels are collapsible via
195
+ * UI-only props (collapse state is presentation, never part of the graph).
196
+ */
197
+
198
+ interface EditorShellProps {
199
+ /** Top toolbar region. */
200
+ readonly toolbar?: ReactNode;
201
+ /** Left region (node palette / outline). */
202
+ readonly left?: ReactNode;
203
+ /** Right region (inspector). */
204
+ readonly right?: ReactNode;
205
+ /** Center region — required; the host renders <ReactFlow> + <CanvasChrome>. */
206
+ readonly canvas: ReactNode;
207
+ /** Optional footer region (e.g. an output/console strip). */
208
+ readonly footer?: ReactNode;
209
+ readonly leftCollapsed?: boolean;
210
+ readonly rightCollapsed?: boolean;
211
+ }
212
+ declare function EditorShell({ toolbar, left, right, canvas, footer, leftCollapsed, rightCollapsed, }: EditorShellProps): ReactElement;
213
+
214
+ /**
215
+ * The top toolbar: a thin glass bar with a brand wordmark on the left and an
216
+ * action cluster on the right. `Toolbar` is the presentational container;
217
+ * `UndoRedo` and `RunButton` are the standard actions wired to editor-core via
218
+ * editor-react hooks. Hosts may pass extra actions through `actions`.
219
+ */
220
+
221
+ interface ToolbarProps {
222
+ /** Brand/title shown on the left. Defaults to a generic wordmark. */
223
+ readonly title?: ReactNode;
224
+ /** Centre slot — a tool cluster (add/frame/view), absolutely centred. */
225
+ readonly center?: ReactNode;
226
+ /** Action nodes rendered on the right (e.g. <UndoRedo/>, <RunButton/>). */
227
+ readonly actions?: ReactNode;
228
+ }
229
+ declare function Toolbar({ title, center, actions }: ToolbarProps): ReactElement;
230
+ interface UndoRedoProps {
231
+ readonly core: EditorCore;
232
+ }
233
+ declare function UndoRedo({ core }: UndoRedoProps): ReactElement;
234
+ interface RunButtonProps {
235
+ /**
236
+ * Invoked when clicked. This is a toggle: when `running` is true the button
237
+ * shows a Stop affordance, and the host is expected to stop the execution.
238
+ */
239
+ readonly onRun: () => void;
240
+ /** Whether an execution is currently in progress. */
241
+ readonly running?: boolean;
242
+ readonly runLabel?: string;
243
+ readonly stopLabel?: string;
244
+ }
245
+ declare function RunButton({ onRun, running, runLabel, stopLabel, }: RunButtonProps): ReactElement;
246
+ /** A compact read-only stat pill (e.g. a node/vertex count) for the top bar. */
247
+ declare function ToolbarStat({ label, value }: {
248
+ readonly label?: string;
249
+ readonly value: ReactNode;
250
+ }): ReactElement;
251
+ /** A secondary ghost button for the top bar (e.g. Invite). Presentational. */
252
+ declare function ToolbarGhostButton({ children, onClick, title, }: {
253
+ readonly children: ReactNode;
254
+ readonly onClick?: () => void;
255
+ readonly title?: string;
256
+ }): ReactElement;
257
+
258
+ /**
259
+ * `ToolCluster` — the centred group of canvas tools in the top bar (nover-style):
260
+ * a row of icon buttons with optional separators, in the black/silver glass
261
+ * language. Purely presentational: the host supplies the items and their
262
+ * actions. Kept generic (no business/media concepts) so it stays an editor-ui
263
+ * primitive — a "frame", "add", or "view" tool is just an item with an icon.
264
+ */
265
+
266
+ interface ToolItem {
267
+ readonly id: string;
268
+ readonly icon: ReactNode;
269
+ readonly title: string;
270
+ readonly onClick?: () => void;
271
+ readonly active?: boolean;
272
+ readonly disabled?: boolean;
273
+ /** Draw a thin divider before this item. */
274
+ readonly separatorBefore?: boolean;
275
+ }
276
+ interface ToolClusterProps {
277
+ readonly items: readonly ToolItem[];
278
+ }
279
+ declare function ToolCluster({ items }: ToolClusterProps): ReactElement;
280
+
281
+ /**
282
+ * `CanvasChrome` — themed React Flow canvas furniture. Now just the dotted
283
+ * background: React Flow's default MiniMap and Controls are intentionally NOT
284
+ * rendered — a creation studio replaces them with our own centred `CanvasDock`
285
+ * (pan/zoom/add) so the canvas isn't cluttered with a stock topology-editor
286
+ * toolbar. Rendered as children of <ReactFlow>.
287
+ */
288
+
289
+ interface CanvasChromeProps {
290
+ readonly background?: boolean;
291
+ }
292
+ declare function CanvasChrome({ background }: CanvasChromeProps): ReactElement;
293
+
294
+ /**
295
+ * `CanvasDock` — the centred, bottom-of-canvas floating toolbar of a creation
296
+ * studio: pointer/pan mode, add-node, and zoom controls, in the black/silver
297
+ * glass language. It replaces React Flow's default corner Controls + MiniMap
298
+ * with one calm, centred dock.
299
+ *
300
+ * Presentation only: it renders buttons and the current zoom %, and calls back
301
+ * to the host for every action. It holds no React Flow or graph state — the host
302
+ * wires the callbacks to `useReactFlow()` (zoom/fit) and the node search (add).
303
+ */
304
+
305
+ /** Interaction mode the canvas is in — left-drag selects, or left-drag pans. */
306
+ type CanvasMode = 'select' | 'pan';
307
+ interface CanvasDockProps {
308
+ /** Current zoom as a fraction (1 = 100%). */
309
+ readonly zoom: number;
310
+ readonly onZoomIn: () => void;
311
+ readonly onZoomOut: () => void;
312
+ readonly onFitView: () => void;
313
+ /** Open the node search to add a node. */
314
+ readonly onAdd: () => void;
315
+ /** Current interaction mode + setter (left-drag selects vs pans). */
316
+ readonly mode: CanvasMode;
317
+ readonly onModeChange: (mode: CanvasMode) => void;
318
+ /** Undo/redo — colocated with the canvas tools rather than the top bar. */
319
+ readonly onUndo: () => void;
320
+ readonly onRedo: () => void;
321
+ readonly canUndo: boolean;
322
+ readonly canRedo: boolean;
323
+ }
324
+ declare function CanvasDock({ zoom, onZoomIn, onZoomOut, onFitView, onAdd, mode, onModeChange, onUndo, onRedo, canUndo, canRedo, }: CanvasDockProps): ReactElement;
325
+
326
+ /**
327
+ * `EmptyState` — a quiet, centered hint shown over an empty canvas. Purely
328
+ * presentational; the host decides when to render it (e.g. when the graph has
329
+ * no nodes). Non-interactive so it never blocks canvas gestures.
330
+ */
331
+
332
+ interface EmptyStateProps {
333
+ readonly title?: ReactNode;
334
+ readonly hint?: ReactNode;
335
+ }
336
+ declare function EmptyState({ title, hint, }: EmptyStateProps): ReactElement;
337
+
338
+ /**
339
+ * `InspectorPanel` — the right-hand property inspector. It follows selection:
340
+ * with exactly one node selected it shows that node's identity plus a
341
+ * schema-driven parameter form (edits commit undoable UpdateParamsCommands);
342
+ * otherwise it shows an empty/summary state. This is the unified control center
343
+ * (nover.studio's model) over hlang's headless core.
344
+ */
345
+
346
+ interface InspectorPanelProps {
347
+ readonly core: EditorCore;
348
+ /** Shown when no single node is selected. */
349
+ readonly emptyState?: ReactNode;
350
+ }
351
+ declare function InspectorPanel({ core, emptyState }: InspectorPanelProps): ReactElement;
352
+
353
+ /**
354
+ * `OutlineTree` — the left-rail outline: a two-level GROUPING, not a tree.
355
+ *
356
+ * hlang's graph is a DAG (R/T/W nodes, O->I edges, T<->T feedback allowed), so
357
+ * there is no parent/child hierarchy to render. Instead we group by weakly-
358
+ * connected component (`GraphTopology.components()`): each component is one
359
+ * expandable "Flow N" container, and its member nodes are listed FLAT — they are
360
+ * peers. The real relationships between nodes (edges, fan-in/out) are shown on
361
+ * the canvas, never faked here as nesting. So: the outline expresses BELONGING
362
+ * (which Flow a node is in); the canvas expresses DATA FLOW.
363
+ *
364
+ * Rows carry per-type icons, a diagnostic marker, hover-revealed visible/lock
365
+ * controls, and a solid silver highlight when selected. Clicking a node selects
366
+ * and focuses it. Visibility/lock are per-node view state via `useViewState`.
367
+ */
368
+
369
+ interface OutlineTreeProps {
370
+ readonly core: EditorCore;
371
+ readonly onFocus?: (id: NodeId) => void;
372
+ }
373
+ declare function OutlineTree({ core, onFocus }: OutlineTreeProps): ReactElement;
374
+
375
+ /**
376
+ * `PreviewPanel` — a framed preview of a result image. Generic: the host passes
377
+ * an image URL + title (and optional status), and it renders a glass-framed
378
+ * hero card. In the creator flow this is the "output" of the graph — the render
379
+ * — completing the素材→intent→result narrative. editor-ui only draws it; what
380
+ * the image IS (a render, a still, a mock) is the host's business.
381
+ */
382
+
383
+ interface PreviewPanelProps {
384
+ readonly imageUrl?: string;
385
+ readonly title?: string;
386
+ readonly status?: string;
387
+ }
388
+ declare function PreviewPanel({ imageUrl, title, status }: PreviewPanelProps): ReactElement;
389
+
390
+ /**
391
+ * `RailTabs` — the segmented tab strip at the top of a side rail (Outline /
392
+ * Insert / Assets, nover-style), in the black/silver glass language. Purely
393
+ * presentational and generic: the host supplies the tab ids/labels and owns the
394
+ * active state, so this is an editor-ui primitive with no coupling to what the
395
+ * tabs contain.
396
+ */
397
+
398
+ interface RailTab {
399
+ readonly id: string;
400
+ readonly label: string;
401
+ }
402
+ interface RailTabsProps {
403
+ readonly tabs: readonly RailTab[];
404
+ readonly active: string;
405
+ readonly onChange: (id: string) => void;
406
+ }
407
+ declare function RailTabs({ tabs, active, onChange }: RailTabsProps): ReactElement;
408
+
409
+ /**
410
+ * `AssetGrid` — a thumbnail grid of render results / source images (the nover
411
+ * "Renders" / "Assets" panel). Purely presentational: the host supplies image
412
+ * URLs; clicking one calls back. editor-ui stays generic — an "asset" here is
413
+ * just an image with an id, no media/model business concept.
414
+ */
415
+
416
+ interface AssetItem {
417
+ readonly id: string;
418
+ readonly url: string;
419
+ readonly label?: string;
420
+ }
421
+ interface AssetGridProps {
422
+ readonly items: readonly AssetItem[];
423
+ readonly onSelect?: (id: string) => void;
424
+ /** Shown when there are no assets yet. */
425
+ readonly emptyHint?: string;
426
+ }
427
+ declare function AssetGrid({ items, onSelect, emptyHint }: AssetGridProps): ReactElement;
428
+
429
+ /**
430
+ * `PositionPanel` — the "Position" section of the inspector (nover-style): a
431
+ * 3×2 align cluster, the selected node's X/Y, and a Pin toggle. Generic geometry
432
+ * only — alignment/coordinates carry no media/model concept, so this belongs in
433
+ * editor-ui. All moves go through the command service (undoable) via `useRename`-
434
+ * style command execution; nothing mutates the model directly.
435
+ *
436
+ * With a single selected node it shows and edits that node's position. With
437
+ * multiple, the align actions distribute them against their common bounding box.
438
+ */
439
+
440
+ interface PositionPanelProps {
441
+ readonly core: EditorCore;
442
+ /**
443
+ * Optional LIVE position reader (canvas coords) for a node. A drag commits to
444
+ * the model only on drag-stop, so to show X/Y updating DURING a drag the host
445
+ * supplies the canvas's in-flight position (e.g. React Flow's node.position).
446
+ * When omitted, the committed model position is shown. Keeping this a callback
447
+ * means editor-ui never imports React Flow — the binding stays in the host.
448
+ */
449
+ readonly livePosition?: (id: string) => {
450
+ x: number;
451
+ y: number;
452
+ } | undefined;
453
+ }
454
+ declare function PositionPanel({ core, livePosition }: PositionPanelProps): ReactElement;
455
+
456
+ /**
457
+ * `DiagnosticBadge` — a single edit-time diagnostic marker (error/warning icon +
458
+ * tooltip), shared by the node chrome and the outline so the severity→icon and
459
+ * severity→class mapping lives in ONE place. `variant` only sets the wrapper
460
+ * class prefix so each host can size/position it via its own CSS.
461
+ */
462
+
463
+ interface DiagnosticBadgeProps {
464
+ readonly diagnostic: Diagnostic;
465
+ /** Class-prefix so hosts style it in context (e.g. 'hl-pill-diag'). */
466
+ readonly variant: string;
467
+ readonly size?: number;
468
+ }
469
+ declare function DiagnosticBadge({ diagnostic, variant, size }: DiagnosticBadgeProps): ReactElement;
470
+
471
+ /**
472
+ * Schema-driven property editing. A node's editable parameters are derived into
473
+ * a strongly-typed `ParamField[]` (zero `any`), which the inspector renders as
474
+ * form controls. Field types are inferred from each default value's runtime
475
+ * type — the only metadata a `NodeDescriptor` carries today (`defaultParams`).
476
+ */
477
+
478
+ /** Common fields on every derived form field. */
479
+ interface ParamFieldBase {
480
+ readonly key: string;
481
+ readonly label: string;
482
+ readonly disabled?: boolean;
483
+ }
484
+ /** A discriminated union of the built-in form field types (zero `any`). */
485
+ type ParamField = (ParamFieldBase & {
486
+ readonly type: 'text';
487
+ readonly value: string;
488
+ }) | (ParamFieldBase & {
489
+ readonly type: 'textarea';
490
+ readonly value: string;
491
+ }) | (ParamFieldBase & {
492
+ readonly type: 'number';
493
+ readonly value: number;
494
+ }) | (ParamFieldBase & {
495
+ readonly type: 'toggle';
496
+ readonly value: boolean;
497
+ }) | (ParamFieldBase & {
498
+ readonly type: 'custom';
499
+ readonly fieldType: string;
500
+ readonly value: ParamValue;
501
+ });
502
+ /**
503
+ * Derive the ordered form schema for a node. Keys come from the descriptor's
504
+ * `defaultParams` (the canonical param set); current values come from the live
505
+ * node, falling back to the default.
506
+ */
507
+ declare function deriveParamSchema(descriptor: NodeDescriptor, node: EditorNode): readonly ParamField[];
508
+
509
+ /**
510
+ * `ParamForm` — renders a derived `ParamField[]` as compact, pro-tool form
511
+ * controls. Text/number inputs commit on blur or Enter (not per keystroke, so
512
+ * one edit is one undo entry); toggles commit immediately. `custom` fields are
513
+ * a placeholder until the ParamFieldRegistry lands (M-UI-5).
514
+ */
515
+
516
+ interface ParamFormProps {
517
+ readonly fields: readonly ParamField[];
518
+ /** Commit a single field's new value (undoable, handled by the caller). */
519
+ readonly onCommit: (key: string, value: ParamValue) => void;
520
+ }
521
+ declare function ParamForm({ fields, onCommit }: ParamFormProps): ReactElement;
522
+
523
+ /**
524
+ * `useNodeParams` — reactively read a node's params and commit edits through the
525
+ * command service (so every change is undoable). Re-reads on the `nodeChanged`
526
+ * event via a version counter (the same pattern editor-react's selectors use).
527
+ */
528
+
529
+ interface NodeParamsHandle {
530
+ /** The node, or undefined if it no longer exists. */
531
+ readonly node: EditorNode | undefined;
532
+ /** Set a single param, committing an undoable UpdateParamsCommand. */
533
+ setParam(key: string, value: ParamValue): void;
534
+ /** Replace the whole param bag, committing an undoable UpdateParamsCommand. */
535
+ setParams(next: ParamBag): void;
536
+ }
537
+ declare function useNodeParams(core: EditorCore, nodeId: NodeId | undefined): NodeParamsHandle;
538
+
539
+ /**
540
+ * Small, dependency-free fuzzy matcher for the node search palette. Pure
541
+ * functions — the only logic in editor-ui that carries its own unit tests
542
+ * (behavior, not pixels). Ranks candidates by subsequence match quality:
543
+ * contiguous and word-boundary matches score higher, earlier matches score
544
+ * higher, shorter targets break ties.
545
+ */
546
+ /** A scored match with the matched character positions (for highlighting). */
547
+ interface FuzzyMatch {
548
+ readonly score: number;
549
+ readonly positions: readonly number[];
550
+ }
551
+ /**
552
+ * Score `query` against `target` as a case-insensitive subsequence match.
553
+ * Returns `null` when `target` does not contain `query` as a subsequence.
554
+ * An empty query matches everything with score 0 (stable, no reordering).
555
+ */
556
+ declare function fuzzyScore(query: string, target: string): FuzzyMatch | null;
557
+ /** A candidate to rank, carrying an opaque payload of type `T`. */
558
+ interface FuzzyCandidate<T> {
559
+ readonly text: string;
560
+ readonly value: T;
561
+ }
562
+ /** A ranked result: the candidate plus its match. */
563
+ interface FuzzyResult<T> {
564
+ readonly value: T;
565
+ readonly text: string;
566
+ readonly match: FuzzyMatch;
567
+ }
568
+ /**
569
+ * Rank candidates against a query, best first. Non-matching candidates are
570
+ * dropped. With an empty query, candidates are returned in their original order.
571
+ */
572
+ declare function fuzzyRank<T>(query: string, candidates: readonly FuzzyCandidate<T>[]): readonly FuzzyResult<T>[];
573
+
574
+ /**
575
+ * Pure model behind the node-search palette. No React, no DOM.
576
+ *
577
+ * Two responsibilities:
578
+ * 1. Turn the design-time registry into fuzzy-searchable candidates and rank
579
+ * them against a query (`descriptorCandidates` + `searchNodes`).
580
+ * 2. When the palette is opened by releasing a dangling connection into empty
581
+ * space, offer ONLY nodes that can legally complete that connection, and
582
+ * remember which port on each closes it (`connectableCandidates`). The
583
+ * legality decision is delegated to the core `IConnectionValidator` — this
584
+ * module never re-encodes hlang's connection rules.
585
+ */
586
+
587
+ /** A registry descriptor made searchable, keyed by its display name. */
588
+ type NodeCandidate = FuzzyCandidate<NodeDescriptor>;
589
+ /** A ranked search result carrying the matched descriptor. */
590
+ type NodeSearchResult = FuzzyResult<NodeDescriptor>;
591
+ /** Build fuzzy candidates from registry descriptors (display-name keyed). */
592
+ declare function descriptorCandidates(descriptors: readonly NodeDescriptor[]): readonly NodeCandidate[];
593
+ /** Rank descriptors against `query`, best first (empty query keeps order). */
594
+ declare function searchNodes(query: string, descriptors: readonly NodeDescriptor[]): readonly NodeSearchResult[];
595
+ /**
596
+ * A connection left dangling in empty space. `fromEndpoint` is the port the
597
+ * user dragged from and `fromPort` its resolved descriptor; `direction` says
598
+ * whether that endpoint is the connection's source (an output, needing a
599
+ * target) or its target (an input, needing a source). The palette will create a
600
+ * node and wire the missing side.
601
+ */
602
+ interface PendingConnection {
603
+ readonly fromEndpoint: Endpoint;
604
+ /** Descriptor of the port the drag started from (resolved from the model). */
605
+ readonly fromPort: PortDescriptor;
606
+ readonly direction: 'from-source' | 'from-target';
607
+ }
608
+ /**
609
+ * A search result augmented with the specific port on the candidate node that
610
+ * would complete a pending connection.
611
+ */
612
+ interface ConnectableResult {
613
+ readonly candidate: NodeSearchResult;
614
+ /** The port id on the (future) new node that closes the connection. */
615
+ readonly connectPortId: string;
616
+ }
617
+ /**
618
+ * Filter and rank descriptors to those that can legally complete `pending`,
619
+ * annotating each with the completing port. Legality is decided by the core
620
+ * validator's model-free `canLink` (direction + DTO): for a dangling source we
621
+ * ask canLink(danglingSource, candidatePort); for a dangling target we ask
622
+ * canLink(candidatePort, danglingTarget). No graph model is consulted, because
623
+ * the candidate node does not exist yet — legality here is purely a property of
624
+ * the two ports.
625
+ */
626
+ declare function connectableCandidates(query: string, descriptors: readonly NodeDescriptor[], pending: PendingConnection, validator: IConnectionValidator): readonly ConnectableResult[];
627
+
628
+ /**
629
+ * `useNodeSearch` — palette state + commit logic for creating nodes by search.
630
+ *
631
+ * The palette can open in two modes:
632
+ * - blank: create a free-standing node at a point (canvas double-click, `/`).
633
+ * - from a released connection: create a node AND wire the dangling link to a
634
+ * compatible port on it, as ONE undoable step.
635
+ *
636
+ * All mutation goes through the core command service; connection legality and
637
+ * edge ids come from the core (validator + id generator). This hook holds no
638
+ * graph rules of its own.
639
+ */
640
+
641
+ /** Where and how the palette is currently open. */
642
+ interface NodeSearchState {
643
+ /** Screen-space anchor for rendering the floating panel. */
644
+ readonly screen: Point;
645
+ /** Flow-space position where the created node will be placed. */
646
+ readonly flow: Point;
647
+ /** Set when opened by releasing a dangling connection. */
648
+ readonly pending?: PendingConnection;
649
+ }
650
+ /** The palette controller returned by the hook. */
651
+ interface NodeSearchController {
652
+ /** Current open state, or `undefined` when closed. */
653
+ readonly state: NodeSearchState | undefined;
654
+ /** Open the palette to create a free-standing node. */
655
+ openBlank(screen: Point, flow: Point): void;
656
+ /** Open the palette to complete a released connection. */
657
+ openFromConnection(screen: Point, flow: Point, pending: PendingConnection): void;
658
+ /** Close the palette without creating anything. */
659
+ close(): void;
660
+ /**
661
+ * Rank registry descriptors for the current query and open mode. In blank
662
+ * mode every node is offered; in connection mode only nodes that can legally
663
+ * complete the pending link are offered.
664
+ */
665
+ results(query: string): readonly NodeSearchResultView[];
666
+ /** Create the chosen node (and wire the pending link, if any), then close. */
667
+ commit(result: NodeSearchResultView): void;
668
+ }
669
+ /**
670
+ * A ranked result as the palette renders it: the fuzzy match plus, in
671
+ * connection mode, the port on the new node that completes the link.
672
+ */
673
+ interface NodeSearchResultView {
674
+ readonly result: NodeSearchResult;
675
+ /** Present only in connection mode. */
676
+ readonly connectPortId?: string;
677
+ }
678
+ declare function useNodeSearch(core: EditorCore): NodeSearchController;
679
+
680
+ /**
681
+ * `NodeSearch` — the floating command palette for creating nodes by fuzzy
682
+ * search. Rendered when the controller is open; anchored at the trigger point.
683
+ *
684
+ * Presentation only: it owns the query string, keyboard navigation, and match
685
+ * highlighting, and delegates ranking + creation to the `NodeSearchController`.
686
+ * Deep-space glass panel, electric-blue accent, kind-tinted result chips.
687
+ */
688
+
689
+ interface NodeSearchProps {
690
+ readonly controller: NodeSearchController;
691
+ }
692
+ declare function NodeSearch({ controller }: NodeSearchProps): ReactElement | null;
693
+
694
+ /**
695
+ * `useNodeSearchTriggers` — bridge React Flow gestures to the node-search
696
+ * palette. Returns handlers to spread onto `<ReactFlow>`:
697
+ *
698
+ * - `onConnectStart` / `onConnectEnd`: when a link is dragged from a port and
699
+ * released into empty space (not onto a handle), open the palette pre-filtered
700
+ * to nodes that can complete that connection.
701
+ * - `onPaneClick`: two clicks on the empty pane within `doubleClickMs` open a
702
+ * blank palette. We use React Flow's first-class `onPaneClick` (which fires
703
+ * only for the pane, never for nodes/edges/handles) rather than a raw DOM
704
+ * double-click, so this never reaches into React Flow's internal markup.
705
+ *
706
+ * Screen→flow coordinate conversion uses the React Flow instance, so created
707
+ * nodes land under the pointer regardless of pan/zoom. This hook must be used
708
+ * within a `ReactFlowProvider` (React Flow's own requirement for `useReactFlow`).
709
+ */
710
+
711
+ /** Options for the trigger hook (injectable clock keeps it deterministic). */
712
+ interface NodeSearchTriggerOptions {
713
+ /** Double-click detection window in ms. Defaults to 350. */
714
+ readonly doubleClickMs?: number;
715
+ /** Monotonic clock in ms. Defaults to `performance.now`. */
716
+ readonly now?: () => number;
717
+ }
718
+ /** Handlers to spread onto the `<ReactFlow>` element. */
719
+ interface NodeSearchTriggers {
720
+ readonly onConnectStart: OnConnectStart;
721
+ readonly onConnectEnd: OnConnectEnd;
722
+ /** Attach to `<ReactFlow onPaneClick>`; a rapid second click opens the palette. */
723
+ onPaneClick(event: MouseEvent): void;
724
+ }
725
+ declare function useNodeSearchTriggers(core: EditorCore, search: NodeSearchController, options?: NodeSearchTriggerOptions): NodeSearchTriggers;
726
+
727
+ /**
728
+ * `useEditorHotkeys` — global keyboard shortcuts for the editor canvas:
729
+ *
730
+ * ⌘/Ctrl+Z undo ⌘/Ctrl+Shift+Z, ⌘/Ctrl+Y redo
731
+ * ⌘/Ctrl+C copy ⌘/Ctrl+V paste ⌘/Ctrl+X cut ⌘/Ctrl+D duplicate
732
+ * Delete / Backspace delete
733
+ *
734
+ * Clipboard actions run through `useClipboard`; undo/redo drive the history
735
+ * service. Every action is a proper command, so it is itself undoable. The
736
+ * listener is on `window` but SUPPRESSED while a text input / textarea /
737
+ * contenteditable is focused, so typing never triggers a canvas action.
738
+ */
739
+
740
+ /** True when focus is in an editable field, where shortcuts must NOT fire. */
741
+ declare function isEditableTarget(target: EventTarget | null): boolean;
742
+ declare function useEditorHotkeys(core: EditorCore): void;
743
+
744
+ /**
745
+ * `useNodeContextMenu` — controller for the right-click node menu. Holds only
746
+ * where the menu is open and which node it targets; the menu ITEMS (labels,
747
+ * shortcuts, actions) are assembled by the consumer from clipboard/view-state
748
+ * actions, so this hook carries no graph rules.
749
+ */
750
+
751
+ /** Where the menu is open and which node it targets. */
752
+ interface NodeContextMenuState {
753
+ /** Screen-space anchor (the click point). */
754
+ readonly screen: Point;
755
+ /** The node the menu acts on. */
756
+ readonly nodeId: NodeId;
757
+ }
758
+ interface NodeContextMenuController {
759
+ readonly state: NodeContextMenuState | undefined;
760
+ /** Open the menu for a node at a screen point. */
761
+ openForNode(screen: Point, nodeId: NodeId): void;
762
+ close(): void;
763
+ }
764
+ declare function useNodeContextMenu(): NodeContextMenuController;
765
+
766
+ /**
767
+ * `NodeContextMenu` — the right-click menu for a node. A black/silver glass
768
+ * panel matching the command palette's material and dismissal, listing actions
769
+ * the consumer supplies (duplicate/copy/cut/paste/delete/hide/lock). Presentation
770
+ * only: each item carries its own `onSelect`; this component owns positioning,
771
+ * keyboard navigation, and dismissal.
772
+ */
773
+
774
+ /** One menu row. A separator is a bare `{ separator: true }`. */
775
+ interface ContextMenuItem {
776
+ readonly id: string;
777
+ readonly label: string;
778
+ /** Optional keyboard-shortcut hint shown right-aligned (e.g. "⌘D"). */
779
+ readonly shortcut?: string;
780
+ /** Optional leading glyph. */
781
+ readonly icon?: ReactNode;
782
+ /** Destructive styling (e.g. Delete). */
783
+ readonly danger?: boolean;
784
+ /** Disabled rows are shown dim and are not selectable. */
785
+ readonly disabled?: boolean;
786
+ readonly onSelect: () => void;
787
+ }
788
+ interface NodeContextMenuProps {
789
+ readonly controller: NodeContextMenuController;
790
+ /** Build the items for the targeted node id (recomputed on open). */
791
+ readonly items: (nodeId: string) => readonly ContextMenuItem[];
792
+ }
793
+ declare function NodeContextMenu({ controller, items }: NodeContextMenuProps): ReactElement | null;
794
+
795
+ /**
796
+ * `NodeInteractionContext` — optional per-node interaction wiring supplied by the
797
+ * host (diagnostics to badge, rename to commit). Node chrome reads it to render a
798
+ * diagnostic badge and to support inline rename, but degrades gracefully to a
799
+ * plain, read-only node when no provider is present — so editor-ui stays usable
800
+ * without the full editor wiring.
801
+ */
802
+
803
+ interface NodeInteraction {
804
+ /** The most severe diagnostic on a node, or undefined. */
805
+ diagnosticFor(id: NodeId): Diagnostic | undefined;
806
+ /** Commit a rename (host routes it through the command service). */
807
+ rename(id: NodeId, name: string): void;
808
+ }
809
+ interface NodeInteractionProviderProps {
810
+ readonly value: NodeInteraction;
811
+ readonly children: ReactNode;
812
+ }
813
+ declare function NodeInteractionProvider({ value, children, }: NodeInteractionProviderProps): ReactElement;
814
+ declare function useNodeInteraction(): NodeInteraction;
815
+
816
+ /**
817
+ * `Cheatsheet` — a keyboard-shortcut reference in the black/silver glass
818
+ * language. Toggled by the host (typically the `?` key). Presentation only: it
819
+ * takes an `open` flag and an `onClose`, and renders a grouped list of the
820
+ * editor's shortcuts so they are discoverable, not memorized.
821
+ */
822
+
823
+ interface ShortcutRow {
824
+ readonly keys: string;
825
+ readonly label: string;
826
+ }
827
+ interface ShortcutGroup {
828
+ readonly title: string;
829
+ readonly rows: readonly ShortcutRow[];
830
+ }
831
+ interface CheatsheetProps {
832
+ readonly open: boolean;
833
+ readonly onClose: () => void;
834
+ /** Groups to display; a sensible default set is used when omitted. */
835
+ readonly groups?: readonly ShortcutGroup[];
836
+ }
837
+ declare function Cheatsheet({ open, onClose, groups }: CheatsheetProps): ReactElement | null;
838
+
839
+ /**
840
+ * `ToastProvider` / `useToast` — lightweight transient notifications in the
841
+ * black/silver glass language. A host wraps the app once; any component calls
842
+ * `useToast().show('Copied 3 nodes')` for a brief confirmation. Presentation
843
+ * only: it holds a small queue that auto-dismisses.
844
+ */
845
+
846
+ interface ToastApi {
847
+ show(message: string): void;
848
+ }
849
+ interface ToastProviderProps {
850
+ readonly children: ReactNode;
851
+ /** Auto-dismiss delay in ms (default 2200). */
852
+ readonly duration?: number;
853
+ }
854
+ declare function ToastProvider({ children, duration }: ToastProviderProps): ReactElement;
855
+ declare function useToast(): ToastApi;
856
+
857
+ /**
858
+ * `LiveSignalStore` — the source of truth for a running graph's live visual
859
+ * state. It ingests activity/error signals, decays activity over a short
860
+ * window, and lets node/edge components subscribe to THEIR OWN id only.
861
+ *
862
+ * Design rationale (industrial-grade, no tricks):
863
+ * - No global DOM scanning and no dependency on React Flow's private CSS
864
+ * classes. Components read their state by id through `useSyncExternalStore`,
865
+ * so only the components whose state actually flips re-render.
866
+ * - Edge "flowing" state is computed from an injected edge index, not by
867
+ * reaching into another layer's model at paint time.
868
+ * - A single rAF loop advances decay and notifies only changed ids.
869
+ */
870
+ /** Live state of a node during execution. */
871
+ type NodeLiveState = 'idle' | 'active' | 'error';
872
+ type Listener = () => void;
873
+ /** Minimal edge shape the store needs to map a source-port pulse to edges. */
874
+ interface EdgeIndexEntry {
875
+ readonly id: string;
876
+ readonly sourceNodeId: string;
877
+ readonly sourcePortId: string;
878
+ readonly isError: boolean;
879
+ }
880
+ /** Injected clock + scheduler so the store is deterministic and testable. */
881
+ interface LiveSignalStoreDeps {
882
+ now(): number;
883
+ schedule(cb: () => void): number;
884
+ cancel(handle: number): void;
885
+ }
886
+ declare class LiveSignalStore {
887
+ private readonly deps;
888
+ /** nodeId -> last-active timestamp. */
889
+ private readonly nodeActiveAt;
890
+ /** nodeId set that errored during this run. */
891
+ private readonly erroredNodes;
892
+ /** edgeId -> last-flowing timestamp. */
893
+ private readonly edgeFlowAt;
894
+ /** Per-id listeners. */
895
+ private readonly nodeListeners;
896
+ private readonly edgeListeners;
897
+ /** Snapshot of last-emitted state per id, so we only notify on real change. */
898
+ private readonly lastNodeState;
899
+ private readonly lastEdgeFlowing;
900
+ /** Edges sourced by a given (nodeId@portId), for pulse -> edge mapping. */
901
+ private edgesBySourcePort;
902
+ private rafHandle;
903
+ private running;
904
+ constructor(deps?: LiveSignalStoreDeps);
905
+ /** Provide the current edge index (call whenever edges change). */
906
+ setEdgeIndex(edges: readonly EdgeIndexEntry[]): void;
907
+ /** Record a node emitting on an output port (drives node + edge activity). */
908
+ markActivity(nodeId: string, portId: string): void;
909
+ /** Record a node erroring (sticky for the run). */
910
+ markError(nodeId: string): void;
911
+ /** Clear all live state (on stop/teardown) and notify everyone. */
912
+ reset(): void;
913
+ /** Subscribe to a single node's live state. */
914
+ subscribeNode(nodeId: string, listener: Listener): () => void;
915
+ /** Subscribe to a single edge's flowing state. */
916
+ subscribeEdge(edgeId: string, listener: Listener): () => void;
917
+ /** Current live state of a node. */
918
+ getNodeState(nodeId: string): NodeLiveState;
919
+ /** Whether an edge is currently flowing. */
920
+ isEdgeFlowing(edgeId: string): boolean;
921
+ private subscribe;
922
+ private ensureLoop;
923
+ private stopLoop;
924
+ /**
925
+ * Recompute every subscribed id's state, notify only those that changed, and
926
+ * report whether anything is still within its active window (so the loop can
927
+ * stop when the graph goes quiet).
928
+ */
929
+ private flushNotifications;
930
+ }
931
+
932
+ /**
933
+ * `useLiveSignals` — feed a running flow's activity/error signals into the
934
+ * LiveSignalStore, which node/edge components subscribe to by id. No global DOM
935
+ * access and no dependency on React Flow's private structure: the store is the
936
+ * single source of truth and components read their own state (see
937
+ * `LiveSignalContext`). Must be used within a `<LiveSignalProvider>`.
938
+ */
939
+
940
+ /**
941
+ * Drive live visuals from an execution handle. Pass the handle returned by
942
+ * `core.execution.run(...)`; pass `undefined` to clear. Keeps the store's edge
943
+ * index in sync with the model so a source-port pulse lights the right edges.
944
+ *
945
+ * The store is taken from `<LiveSignalProvider>` context; pass `explicitStore`
946
+ * when the feeding component sits at or above the provider (so it and the
947
+ * subscribing components share one store).
948
+ */
949
+ declare function useLiveSignals(core: EditorCore, handle: IExecutionHandle | undefined, explicitStore?: LiveSignalStore): void;
950
+
951
+ /**
952
+ * React glue for the LiveSignalStore. A single store instance is provided via
953
+ * context; node/edge components subscribe to their own id through
954
+ * `useNodeLiveState` / `useEdgeFlowing` (each returns a primitive, so only the
955
+ * components whose state actually flips re-render — no global DOM scanning).
956
+ */
957
+
958
+ interface LiveSignalProviderProps {
959
+ /** Optional external store; a fresh one is created if omitted. */
960
+ readonly store?: LiveSignalStore;
961
+ readonly children: ReactNode;
962
+ }
963
+ declare function LiveSignalProvider({ store, children }: LiveSignalProviderProps): ReactElement;
964
+ /** Access the live store, or `null` when there is no provider (visuals off). */
965
+ declare function useLiveSignalStore(): LiveSignalStore | null;
966
+ /** Subscribe to a single node's live state. Idle when no store is present. */
967
+ declare function useNodeLiveState(nodeId: string): NodeLiveState;
968
+ /** Subscribe to a single edge's flowing state. False when no store is present. */
969
+ declare function useEdgeFlowing(edgeId: string): boolean;
970
+
971
+ export { AssetGrid, type AssetGridProps, type AssetItem, AssetNodeChrome, type AssetNodeChromeProps, CanvasChrome, type CanvasChromeProps, CanvasDock, type CanvasDockProps, type CanvasMode, Cheatsheet, type CheatsheetProps, type ConnectableResult, type ContextMenuItem, DiagnosticBadge, type DiagnosticBadgeProps, type EdgeIndexEntry, EditorShell, type EditorShellProps, EditorUIThemeProvider, type EditorUIThemeProviderProps, EmptyState, type EmptyStateProps, FlowEdge, type FuzzyCandidate, type FuzzyMatch, type FuzzyResult, type HlangFlowTypes, HlangNode, InspectorPanel, type InspectorPanelProps, KIND_ROLE, KIND_TOKEN, LiveSignalProvider, type LiveSignalProviderProps, LiveSignalStore, type LiveSignalStoreDeps, NodeBodySlot, type NodeBodySlotProps, type NodeCandidate, NodeChrome, type NodeChromeProps, NodeContextMenu, type NodeContextMenuController, type NodeContextMenuProps, type NodeContextMenuState, type NodeInteraction, NodeInteractionProvider, type NodeInteractionProviderProps, type NodeLiveState, type NodeParamsHandle, NodeSearch, type NodeSearchController, type NodeSearchProps, type NodeSearchResult, type NodeSearchResultView, type NodeSearchState, type NodeSearchTriggerOptions, type NodeSearchTriggers, OutlineTree, type OutlineTreeProps, type ParamField, type ParamFieldBase, ParamForm, type ParamFormProps, type PendingConnection, PillNodeChrome, type PillNodeChromeProps, PortRow, type PortRowProps, PositionPanel, type PositionPanelProps, PreviewPanel, type PreviewPanelProps, type RailTab, RailTabs, type RailTabsProps, RunButton, type RunButtonProps, type ShortcutGroup, type ShortcutRow, SignalBar, TOKEN_NAMES, type ThemeOverride, type ToastApi, ToastProvider, type ToastProviderProps, type TokenName, ToolCluster, type ToolClusterProps, type ToolItem, Toolbar, ToolbarGhostButton, type ToolbarProps, ToolbarStat, UndoRedo, type UndoRedoProps, connectableCandidates, createHlangFlowTypes, createTheme, deriveParamSchema, descriptorCandidates, fuzzyRank, fuzzyScore, isEditableTarget, searchNodes, tokenVar, useEdgeFlowing, useEditorHotkeys, useLiveSignalStore, useLiveSignals, useNodeContextMenu, useNodeInteraction, useNodeLiveState, useNodeParams, useNodeSearch, useNodeSearchTriggers, useToast };