@hlang-tech/editor-ui 0.3.0 → 0.4.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.
package/lib/index.d.mts CHANGED
@@ -1,5 +1,5 @@
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, RefObject } from 'react';
1
+ import { NodeKind, EditorNode, PortDescriptor, EditorCore, NodeId, Diagnostic, ParamValue, NodeDescriptor, ParamBag, Endpoint, IConnectionValidator, Point as Point$1, IExecutionHandle, INodeDeliveryEvent, INodeErrorEvent, INodeEndedEvent, INodeFinishedEvent, INodeDroppedEvent, EdgeId, EditorEdge, EdgeBounds } from '@hlang-tech/editor-core';
2
+ import { CSSProperties, ReactNode, ReactElement, RefObject, UIEvent } from 'react';
3
3
  import { NodeProps, EdgeProps, NodeTypes, EdgeTypes, OnConnectStartParams } from '@xyflow/react';
4
4
  import { HlangRFNode, HlangRFEdge } from '@hlang-tech/editor-react';
5
5
 
@@ -159,7 +159,7 @@ declare function NodeBodySlot({ children }: NodeBodySlotProps): ReactElement | n
159
159
 
160
160
  /**
161
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) —
162
+ * conduit (or, from a node back into itself, a loop routed around the node) plus a silver overlay that marches from output (O) to input (I) —
163
163
  * exactly what an hlang RxJS port does; a cold spark lights it while flowing.
164
164
  *
165
165
  * When the SOURCE node carries a semantic `role` param (e.g. Character,
@@ -170,6 +170,48 @@ declare function NodeBodySlot({ children }: NodeBodySlotProps): ReactElement | n
170
170
 
171
171
  declare function FlowEdge(props: EdgeProps<HlangRFEdge>): ReactElement;
172
172
 
173
+ /**
174
+ * The route of an edge from a node back into the same node: a Transform fed
175
+ * by its own output (iteration). A curve between two handles on opposite
176
+ * sides of one node would cut across it; instead the edge leaves the output
177
+ * to the right, runs under the node and comes up on the left into the input,
178
+ * with rounded corners, as node editors route an edge that goes backwards
179
+ * (n8n's back edges). Pure geometry, so the edge component only draws it.
180
+ */
181
+ interface Point {
182
+ readonly x: number;
183
+ readonly y: number;
184
+ }
185
+ interface Rect {
186
+ readonly x: number;
187
+ readonly y: number;
188
+ readonly width: number;
189
+ readonly height: number;
190
+ }
191
+ interface SelfLoopInput {
192
+ /** The output handle (on the node's right side) and the input handle (on its left). */
193
+ readonly source: Point;
194
+ readonly target: Point;
195
+ /** The node, in the same coordinates. */
196
+ readonly node: Rect;
197
+ /** How far the route keeps from the node. Default `SELF_LOOP_CLEARANCE`. */
198
+ readonly clearance?: number;
199
+ /** The corner radius, reduced where a segment is too short. Default `SELF_LOOP_RADIUS`. */
200
+ readonly radius?: number;
201
+ }
202
+ interface SelfLoop {
203
+ /** SVG path data. */
204
+ readonly path: string;
205
+ /** Where a label sits: the middle of the run under the node. */
206
+ readonly labelX: number;
207
+ readonly labelY: number;
208
+ }
209
+ /** The design system's distance between a self-loop and its node, in flow units. */
210
+ declare const SELF_LOOP_CLEARANCE = 28;
211
+ /** The design system's corner radius for a self-loop, in flow units. */
212
+ declare const SELF_LOOP_RADIUS = 16;
213
+ declare function selfLoopPath(input: SelfLoopInput): SelfLoop;
214
+
173
215
  /**
174
216
  * One-line React Flow wiring: `createHlangFlowTypes()` returns the `nodeTypes`
175
217
  * and `edgeTypes` maps to spread onto `<ReactFlow>`, binding hlang's R/T/W node
@@ -188,11 +230,84 @@ interface HlangFlowTypes {
188
230
  /** Build the React Flow node/edge type maps for the hlang editor. */
189
231
  declare function createHlangFlowTypes(): HlangFlowTypes;
190
232
 
233
+ /**
234
+ * The size rules of the editor's resizable footer (its console): how tall it
235
+ * may be, and how the keyboard and a double click change it. Pure, so the
236
+ * pointer and keyboard handling in `useFooterResize` stays a thin layer.
237
+ */
238
+ /** How far one arrow key press moves the footer edge, in pixels; Shift moves four times as far. */
239
+ declare const FOOTER_KEY_STEP = 16;
240
+ interface FooterBounds {
241
+ readonly min: number;
242
+ readonly max: number;
243
+ }
244
+ /**
245
+ * The heights the footer may take in an editor with `available` pixels below
246
+ * its toolbar: at least `minHeight` (its header alone, collapsed), and at most
247
+ * what leaves the canvas `minCanvasHeight`.
248
+ */
249
+ declare function footerBounds(options: {
250
+ readonly available: number;
251
+ readonly minHeight: number;
252
+ readonly minCanvasHeight: number;
253
+ }): FooterBounds;
254
+ /** `height` held within `bounds`, in whole pixels. */
255
+ declare function clampFooter(height: number, bounds: FooterBounds): number;
256
+ /** The height after pressing `key` on the footer edge, or `undefined` for a key that does nothing. */
257
+ declare function resizeByKey(height: number, key: string, shift: boolean, bounds: FooterBounds): number | undefined;
258
+ /** The footer's height and the height to return to when it is opened again. */
259
+ interface FooterSize {
260
+ readonly height: number;
261
+ readonly restore: number;
262
+ }
263
+ /** Collapses an open footer (remembering its height), or opens a collapsed one to that height. */
264
+ declare function toggleCollapsed(size: FooterSize, bounds: FooterBounds): FooterSize;
265
+
266
+ /**
267
+ * `useFooterResize` — the state of a footer whose top edge the user drags, or
268
+ * moves with the keyboard, to make it taller or shorter. It takes only what it
269
+ * reads (a pointer's `clientY`, a key), so `FooterResizer` adapts the browser's
270
+ * events to it and the behavior is testable without a browser.
271
+ *
272
+ * The height the user asked for is kept apart from the height shown: when the
273
+ * editor gets shorter the footer shrinks to fit, and when it grows back the
274
+ * footer returns to the asked-for height.
275
+ */
276
+
277
+ /** How a resizable footer behaves. */
278
+ interface FooterResizeOptions {
279
+ /** Its height when the editor opens, in pixels. */
280
+ readonly defaultHeight: number;
281
+ /** Its height when collapsed: its own header, so it can always be grabbed and opened again. */
282
+ readonly minHeight: number;
283
+ /** The height the canvas always keeps. Default `DEFAULT_MIN_CANVAS_HEIGHT`. */
284
+ readonly minCanvasHeight?: number;
285
+ }
286
+ /** The canvas height a footer never takes away, unless a host sets another. */
287
+ declare const DEFAULT_MIN_CANVAS_HEIGHT = 160;
288
+ interface FooterResize {
289
+ /** The height to render. */
290
+ readonly height: number;
291
+ readonly bounds: FooterBounds;
292
+ readonly dragging: boolean;
293
+ /** Tells the hook how many pixels the toolbar leaves for the canvas and the footer. */
294
+ readonly measure: (available: number) => void;
295
+ readonly startDrag: (clientY: number) => void;
296
+ readonly drag: (clientY: number) => void;
297
+ readonly endDrag: () => void;
298
+ /** Applies a key pressed on the edge; returns whether the key resized the footer. */
299
+ readonly keyDown: (key: string, shift: boolean) => boolean;
300
+ /** Collapses the footer, or opens it again to the height it had. */
301
+ readonly toggle: () => void;
302
+ }
303
+ declare function useFooterResize(options: FooterResizeOptions): FooterResize;
304
+
191
305
  /**
192
306
  * `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).
307
+ * center canvas, right inspector, footer). It holds NO graph logic; a host
308
+ * slots children into named regions. Left/right panels are collapsible via
309
+ * UI-only props, and the footer can be made resizable (presentation state,
310
+ * never part of the graph).
196
311
  */
197
312
 
198
313
  interface EditorShellProps {
@@ -206,10 +321,36 @@ interface EditorShellProps {
206
321
  readonly canvas: ReactNode;
207
322
  /** Optional footer region (e.g. an output/console strip). */
208
323
  readonly footer?: ReactNode;
324
+ /**
325
+ * Makes the footer resizable: its top edge can be dragged, moved with the
326
+ * keyboard, and double-clicked to collapse it. Without it the footer takes
327
+ * its content's height.
328
+ */
329
+ readonly footerResize?: FooterResizeOptions & {
330
+ /** The accessible name of the footer's resize handle. Default "Resize the panel". */
331
+ readonly label?: string;
332
+ };
209
333
  readonly leftCollapsed?: boolean;
210
334
  readonly rightCollapsed?: boolean;
211
335
  }
212
- declare function EditorShell({ toolbar, left, right, canvas, footer, leftCollapsed, rightCollapsed, }: EditorShellProps): ReactElement;
336
+ declare function EditorShell({ toolbar, left, right, canvas, footer, footerResize, leftCollapsed, rightCollapsed, }: EditorShellProps): ReactElement;
337
+
338
+ /**
339
+ * `FooterResizer` — the grab handle on a resizable footer's top edge. Drag it
340
+ * with a mouse, a finger or a pen; with focus, the arrow keys move it (Shift
341
+ * for larger steps), Home and End go to the smallest and largest heights, and
342
+ * Enter or a double click collapses the footer and opens it again. It is a
343
+ * focusable ARIA `separator` that reports the footer's height.
344
+ */
345
+
346
+ interface FooterResizerProps {
347
+ readonly resize: FooterResize;
348
+ /** The id of the footer the handle resizes. */
349
+ readonly controls: string;
350
+ /** Its accessible name. */
351
+ readonly label: string;
352
+ }
353
+ declare function FooterResizer({ resize, controls, label }: FooterResizerProps): ReactElement;
213
354
 
214
355
  /**
215
356
  * The top toolbar: a thin glass bar with a brand wordmark on the left and an
@@ -339,6 +480,7 @@ declare function EmptyState({ title, hint, }: EmptyStateProps): ReactElement;
339
480
  * `InspectorPanel` — the right-hand property inspector. It follows selection:
340
481
  * with exactly one node selected it shows that node's identity plus a
341
482
  * schema-driven parameter form (edits commit undoable UpdateParamsCommands);
483
+ * with exactly one connection selected, the connection and its back-pressure;
342
484
  * otherwise it shows an empty/summary state. This is the unified control center
343
485
  * (nover.studio's model) over hlang's headless core.
344
486
  */
@@ -641,9 +783,9 @@ declare function connectableCandidates(query: string, descriptors: readonly Node
641
783
  /** Where and how the palette is currently open. */
642
784
  interface NodeSearchState {
643
785
  /** Screen-space anchor for rendering the floating panel. */
644
- readonly screen: Point;
786
+ readonly screen: Point$1;
645
787
  /** Flow-space position where the created node will be placed. */
646
- readonly flow: Point;
788
+ readonly flow: Point$1;
647
789
  /** Set when opened by releasing a dangling connection. */
648
790
  readonly pending?: PendingConnection;
649
791
  }
@@ -652,9 +794,9 @@ interface NodeSearchController {
652
794
  /** Current open state, or `undefined` when closed. */
653
795
  readonly state: NodeSearchState | undefined;
654
796
  /** Open the palette to create a free-standing node. */
655
- openBlank(screen: Point, flow: Point): void;
797
+ openBlank(screen: Point$1, flow: Point$1): void;
656
798
  /** Open the palette to complete a released connection. */
657
- openFromConnection(screen: Point, flow: Point, pending: PendingConnection): void;
799
+ openFromConnection(screen: Point$1, flow: Point$1, pending: PendingConnection): void;
658
800
  /** Close the palette without creating anything. */
659
801
  close(): void;
660
802
  /**
@@ -779,14 +921,14 @@ declare function useEditorHotkeys(core: EditorCore, options?: EditorHotkeysOptio
779
921
  /** Where the menu is open and which node it targets. */
780
922
  interface NodeContextMenuState {
781
923
  /** Screen-space anchor (the click point). */
782
- readonly screen: Point;
924
+ readonly screen: Point$1;
783
925
  /** The node the menu acts on. */
784
926
  readonly nodeId: NodeId;
785
927
  }
786
928
  interface NodeContextMenuController {
787
929
  readonly state: NodeContextMenuState | undefined;
788
930
  /** Open the menu for a node at a screen point. */
789
- openForNode(screen: Point, nodeId: NodeId): void;
931
+ openForNode(screen: Point$1, nodeId: NodeId): void;
790
932
  close(): void;
791
933
  }
792
934
  declare function useNodeContextMenu(): NodeContextMenuController;
@@ -897,27 +1039,43 @@ declare function useToast(): ToastApi;
897
1039
  * frame however many signals arrived in it.
898
1040
  */
899
1041
  /** Live state of a node during execution. */
900
- type NodeLiveState = 'idle' | 'active' | 'error';
901
- type Listener = () => void;
902
- /** Minimal edge shape the store needs to map a source-port pulse to edges. */
1042
+ type NodeLiveState = 'idle' | 'active' | 'error' | 'finished';
1043
+ type Listener$1 = () => void;
1044
+ /**
1045
+ * Minimal edge shape the store needs to map a signal to edges. With its
1046
+ * target, a delivery lights exactly the edge that carried it.
1047
+ */
903
1048
  interface EdgeIndexEntry {
904
1049
  readonly id: string;
905
1050
  readonly sourceNodeId: string;
906
1051
  readonly sourcePortId: string;
1052
+ readonly targetNodeId?: string;
1053
+ readonly targetPortId?: string;
907
1054
  readonly isError: boolean;
908
1055
  }
1056
+ /** A message delivered on one connection: its source ran and sent it, its target ran to handle it. */
1057
+ interface DeliverySignal {
1058
+ readonly sourceNodeId: string;
1059
+ readonly sourcePortId: string;
1060
+ readonly targetNodeId: string;
1061
+ readonly targetPortId: string;
1062
+ }
909
1063
  /** Injected clock + scheduler so the store is deterministic and testable. */
910
1064
  interface LiveSignalStoreDeps {
911
1065
  now(): number;
912
1066
  schedule(cb: () => void): number;
913
1067
  cancel(handle: number): void;
914
1068
  }
1069
+ /** The browser's clock and frame scheduler, with fallbacks outside a browser. */
1070
+ declare const defaultLiveDeps: LiveSignalStoreDeps;
915
1071
  declare class LiveSignalStore {
916
1072
  private readonly deps;
917
1073
  /** nodeId -> last-active timestamp. */
918
1074
  private readonly nodeActiveAt;
919
1075
  /** nodeId set that errored during this run. */
920
1076
  private readonly erroredNodes;
1077
+ /** nodeId set that finished during this run (RFC 0001). */
1078
+ private readonly finishedNodes;
921
1079
  /** edgeId -> last-flowing timestamp. */
922
1080
  private readonly edgeFlowAt;
923
1081
  /** Per-id listeners. */
@@ -928,21 +1086,42 @@ declare class LiveSignalStore {
928
1086
  private readonly lastEdgeFlowing;
929
1087
  /** Edges sourced by a given (nodeId@portId), for pulse -> edge mapping. */
930
1088
  private edgesBySourcePort;
1089
+ /** Edges by the connection they carry (source port -> target port). */
1090
+ private edgesByConnection;
1091
+ /** How many messages each node has handled or sent, and each edge carried, this run. */
1092
+ private readonly nodePulse;
1093
+ private readonly edgePulse;
1094
+ /** The pulse counts last notified, so a new message notifies even while the state stays active. */
1095
+ private readonly lastNodePulse;
1096
+ private readonly lastEdgePulse;
931
1097
  private rafHandle;
932
1098
  private running;
933
1099
  constructor(deps?: LiveSignalStoreDeps);
934
1100
  /** Provide the current edge index (call whenever edges change). */
935
1101
  setEdgeIndex(edges: readonly EdgeIndexEntry[]): void;
1102
+ /**
1103
+ * Record a message delivered on a connection, when it happens: its source
1104
+ * and its target each pulse once (each ran), and so does the edge that
1105
+ * carried it, and only that edge. Subscribers are notified on the next
1106
+ * frame, once however many messages arrived in it.
1107
+ */
1108
+ markDelivery(signal: DeliverySignal): void;
1109
+ /** How many messages a node has handled or sent this run; it changes each time the node runs. */
1110
+ getNodePulse(nodeId: string): number;
1111
+ /** How many messages an edge has carried this run. */
1112
+ getEdgePulse(edgeId: string): number;
936
1113
  /** Record a node emitting on an output port (drives node + edge activity). */
937
1114
  markActivity(nodeId: string, portId: string): void;
1115
+ /** Record a node finishing (RFC 0001): shown once its last flash has faded. */
1116
+ markFinished(nodeId: string): void;
938
1117
  /** Record a node erroring (sticky for the run). */
939
1118
  markError(nodeId: string): void;
940
1119
  /** Clear all live state (on stop/teardown) and notify everyone. */
941
1120
  reset(): void;
942
1121
  /** Subscribe to a single node's live state. */
943
- subscribeNode(nodeId: string, listener: Listener): () => void;
1122
+ subscribeNode(nodeId: string, listener: Listener$1): () => void;
944
1123
  /** Subscribe to a single edge's flowing state. */
945
- subscribeEdge(edgeId: string, listener: Listener): () => void;
1124
+ subscribeEdge(edgeId: string, listener: Listener$1): () => void;
946
1125
  /** Current live state of a node. */
947
1126
  getNodeState(nodeId: string): NodeLiveState;
948
1127
  /** Whether an edge is currently flowing. */
@@ -1000,8 +1179,288 @@ declare function LiveSignalProvider({ store, children }: LiveSignalProviderProps
1000
1179
  declare function useLiveSignalStore(): LiveSignalStore | null;
1001
1180
  /** Subscribe to a single node's live state. Idle when no store is present. */
1002
1181
  declare function useNodeLiveState(nodeId: string): NodeLiveState;
1182
+ /** How many messages a node has handled or sent this run; it changes each time the node runs. */
1183
+ declare function useNodePulse(nodeId: string): number;
1184
+ /** How many messages an edge has carried this run. */
1185
+ declare function useEdgePulse(edgeId: string): number;
1003
1186
  /** Subscribe to a single edge's flowing state. False when no store is present. */
1004
1187
  declare function useEdgeFlowing(edgeId: string): boolean;
1005
1188
 
1006
- export { AssetGrid, AssetNodeChrome, CanvasChrome, CanvasDock, Cheatsheet, DiagnosticBadge, EditorShell, EditorUIThemeProvider, EmptyState, FlowEdge, HlangNode, InspectorPanel, KIND_ROLE, KIND_TOKEN, LiveSignalProvider, LiveSignalStore, NodeBodySlot, NodeChrome, NodeContextMenu, NodeInteractionProvider, NodeSearch, OutlineTree, ParamForm, PillNodeChrome, PortRow, PositionPanel, PreviewPanel, RailTabs, RunButton, SignalBar, TOKEN_NAMES, ToastProvider, ToolCluster, Toolbar, ToolbarGhostButton, ToolbarStat, UndoRedo, connectableCandidates, createHlangFlowTypes, createTheme, deriveParamSchema, descriptorCandidates, fuzzyRank, fuzzyScore, isEditableTarget, searchNodes, tokenVar, useEdgeFlowing, useEditorHotkeys, useLiveSignalStore, useLiveSignals, useNodeContextMenu, useNodeInteraction, useNodeLiveState, useNodeParams, useNodeSearch, useNodeSearchTriggers, useToast };
1007
- export type { AssetGridProps, AssetItem, AssetNodeChromeProps, CanvasChromeProps, CanvasDockProps, CanvasMode, CheatsheetProps, ConnectableResult, ConnectionEnd, ContextMenuItem, DiagnosticBadgeProps, EdgeIndexEntry, EditorHotkeysOptions, EditorShellProps, EditorUIThemeProviderProps, EmptyStateProps, FuzzyCandidate, FuzzyMatch, FuzzyResult, HlangFlowTypes, InspectorPanelProps, LiveSignalProviderProps, LiveSignalStoreDeps, NodeBodySlotProps, NodeCandidate, NodeChromeProps, NodeContextMenuController, NodeContextMenuProps, NodeContextMenuState, NodeInteraction, NodeInteractionProviderProps, NodeLiveState, NodeParamsHandle, NodeSearchController, NodeSearchProps, NodeSearchResult, NodeSearchResultView, NodeSearchState, NodeSearchTriggerOptions, NodeSearchTriggers, OutlineTreeProps, ParamField, ParamFieldBase, ParamFormProps, PendingConnection, PillNodeChromeProps, PointerPosition, PortRowProps, PositionPanelProps, PreviewPanelProps, RailTab, RailTabsProps, RunButtonProps, ShortcutGroup, ShortcutRow, ThemeOverride, ToastApi, ToastProviderProps, TokenName, ToolClusterProps, ToolItem, ToolbarProps, TouchPositions, UndoRedoProps };
1189
+ /**
1190
+ * `RunTrace` — what a running graph did: a bounded log of every delivery and
1191
+ * error, and per-node statistics (messages in and out, errors, time spent,
1192
+ * the latest values). It is fed from an execution handle's `onDelivery` and
1193
+ * `onError` (see `useRunTrace`) and read by the run log and the inspector.
1194
+ *
1195
+ * Like `LiveSignalStore`, it records synchronously and notifies at most once
1196
+ * per frame, so a flow delivering thousands of messages a second costs one
1197
+ * render per frame. The log is a ring buffer: memory stays bounded however
1198
+ * long the run, while the statistics keep counting everything.
1199
+ */
1200
+
1201
+ /** How many log entries a trace keeps unless told otherwise. */
1202
+ declare const RUN_LOG_CAPACITY = 500;
1203
+ /** A message delivered on a connection. `at` is milliseconds since the run started. */
1204
+ interface RunDeliveryEntry {
1205
+ readonly kind: 'delivery';
1206
+ readonly seq: number;
1207
+ readonly at: number;
1208
+ readonly sourceNodeId: string;
1209
+ readonly sourcePortId: string;
1210
+ readonly targetNodeId: string;
1211
+ readonly targetPortId: string;
1212
+ readonly message: unknown;
1213
+ /** How long the receiving node's handlers took, in milliseconds. */
1214
+ readonly duration: number;
1215
+ }
1216
+ /** A node error. `at` is milliseconds since the run started. */
1217
+ interface RunErrorEntry {
1218
+ readonly kind: 'error';
1219
+ readonly seq: number;
1220
+ readonly at: number;
1221
+ readonly nodeId: string;
1222
+ readonly error: unknown;
1223
+ }
1224
+ /** An input that ended (RFC 0001): every connection into it closed and its messages done. */
1225
+ interface RunEndedEntry {
1226
+ readonly kind: 'ended';
1227
+ readonly seq: number;
1228
+ readonly at: number;
1229
+ readonly nodeId: string;
1230
+ readonly portId: string;
1231
+ }
1232
+ /** A node that finished (RFC 0001): its inputs ended and its outputs closed. */
1233
+ interface RunFinishedEntry {
1234
+ readonly kind: 'finished';
1235
+ readonly seq: number;
1236
+ readonly at: number;
1237
+ readonly nodeId: string;
1238
+ }
1239
+ /** A message a full `drop-oldest` connection dropped (RFC 0001). */
1240
+ interface RunDroppedEntry {
1241
+ readonly kind: 'dropped';
1242
+ readonly seq: number;
1243
+ readonly at: number;
1244
+ readonly sourceNodeId: string;
1245
+ readonly sourcePortId: string;
1246
+ readonly targetNodeId: string;
1247
+ readonly targetPortId: string;
1248
+ readonly message: unknown;
1249
+ }
1250
+ type RunLogEntry = RunDeliveryEntry | RunErrorEntry | RunEndedEntry | RunFinishedEntry | RunDroppedEntry;
1251
+ /** One node's activity during the run. */
1252
+ interface NodeRunSummary {
1253
+ readonly received: number;
1254
+ readonly sent: number;
1255
+ readonly errors: number;
1256
+ /** Time spent handling what it received, in milliseconds: in total and the longest once. */
1257
+ readonly totalMs: number;
1258
+ readonly maxMs: number;
1259
+ readonly lastReceived: unknown;
1260
+ readonly lastSent: unknown;
1261
+ /** Messages a full `drop-oldest` connection into this node dropped. */
1262
+ readonly dropped: number;
1263
+ /** Whether the node has finished: its inputs ended and its outputs closed. */
1264
+ readonly finished: boolean;
1265
+ }
1266
+ interface RunTraceOptions {
1267
+ /** How many log entries to keep. Default `RUN_LOG_CAPACITY`. */
1268
+ readonly capacity?: number;
1269
+ /** Clock and frame scheduler; injected in tests. */
1270
+ readonly deps?: LiveSignalStoreDeps;
1271
+ }
1272
+ type Listener = () => void;
1273
+ declare class RunTrace {
1274
+ private readonly capacity;
1275
+ private readonly deps;
1276
+ /** The log as a ring: `ring[(head + i) % capacity]` is the i-th oldest entry. */
1277
+ private readonly ring;
1278
+ private head;
1279
+ private droppedCount;
1280
+ private seq;
1281
+ private startedAt;
1282
+ private readonly stats;
1283
+ private readonly listeners;
1284
+ /** Bumped by every change; the entries cache follows it. */
1285
+ private revision;
1286
+ /** Bumped once per notified frame; what subscribers compare. */
1287
+ private version;
1288
+ private frame;
1289
+ /** The entries as an array, rebuilt only after a change. */
1290
+ private entriesCache;
1291
+ private entriesRevision;
1292
+ constructor(options?: RunTraceOptions);
1293
+ /** Begins a run: clears everything and times entries from now. */
1294
+ start(): void;
1295
+ /** Forgets the log and the statistics. */
1296
+ clear(): void;
1297
+ recordDelivery(event: INodeDeliveryEvent): void;
1298
+ recordError(event: INodeErrorEvent): void;
1299
+ recordEnded(event: INodeEndedEvent): void;
1300
+ recordFinished(event: INodeFinishedEvent): void;
1301
+ recordDropped(event: INodeDroppedEvent): void;
1302
+ /** The log, oldest first. The same array is returned until something changes. */
1303
+ entries(): readonly RunLogEntry[];
1304
+ /** A node's statistics, or `undefined` when it has done nothing this run. */
1305
+ statsOf(nodeId: string): NodeRunSummary | undefined;
1306
+ /** How many entries the log has let go of to stay within its capacity. */
1307
+ get dropped(): number;
1308
+ /** Changes once per notified frame; a snapshot for `useSyncExternalStore`. */
1309
+ getVersion(): number;
1310
+ subscribe(listener: Listener): () => void;
1311
+ private append;
1312
+ private nextSeq;
1313
+ private elapsed;
1314
+ private statsFor;
1315
+ /** Schedules one notification for the next frame, however many changes come before it. */
1316
+ private changed;
1317
+ }
1318
+ /** Whether `entry` concerns `nodeId`: either end of a delivery, or the node of an error. */
1319
+ declare function involves(entry: RunLogEntry, nodeId: string): boolean;
1320
+
1321
+ /**
1322
+ * React glue for `RunTrace`. A host provides one trace for the editor; the run
1323
+ * log and the inspector read it through `useRunTraceVersion`, which changes at
1324
+ * most once per frame, so a busy flow costs one render per frame.
1325
+ */
1326
+
1327
+ interface RunTraceProviderProps {
1328
+ readonly trace: RunTrace;
1329
+ readonly children: ReactNode;
1330
+ }
1331
+ declare function RunTraceProvider({ trace, children }: RunTraceProviderProps): ReactElement;
1332
+ /** The trace, or `null` when there is no provider (run views stay hidden). */
1333
+ declare function useRunTraceStore(): RunTrace | null;
1334
+ /** Re-renders the caller when the trace changes; returns its version (0 without a trace). */
1335
+ declare function useRunTraceVersion(trace: RunTrace | null): number;
1336
+
1337
+ /**
1338
+ * `useRunTrace` — feeds a run's deliveries and errors into a `RunTrace`. Each
1339
+ * new handle starts a new run in the trace; when the handle goes away (the
1340
+ * run stopped) the trace keeps what it recorded, so the log can still be read.
1341
+ * A handle that cannot trace (no `onDelivery`) still feeds its errors.
1342
+ */
1343
+
1344
+ declare function useRunTrace(trace: RunTrace, handle: IExecutionHandle | undefined): void;
1345
+
1346
+ /**
1347
+ * `useFollowScroll` — a scrolling list that stays on its newest entry, as a
1348
+ * terminal does. While following, every render scrolls it to the bottom. When
1349
+ * the reader scrolls back to read, following stops so the view holds still;
1350
+ * scrolling back to the bottom (or `setFollow(true)`) resumes it.
1351
+ */
1352
+
1353
+ /** Within this many pixels of the bottom, the list counts as at the bottom. */
1354
+ declare const FOLLOW_SLACK_PX = 24;
1355
+ interface FollowScroll<T extends HTMLElement> {
1356
+ /** Attach to the scrolling element. */
1357
+ readonly ref: RefObject<T | null>;
1358
+ /** Attach as the scrolling element's `onScroll`. */
1359
+ readonly onScroll: (event: UIEvent<T>) => void;
1360
+ readonly follow: boolean;
1361
+ readonly setFollow: (follow: boolean) => void;
1362
+ }
1363
+ declare function useFollowScroll<T extends HTMLElement>(): FollowScroll<T>;
1364
+
1365
+ /**
1366
+ * `RunLog` — the editor's live console. Every message the running graph
1367
+ * delivers, and every node error, as it happens: when (since the run
1368
+ * started), from which node and port to which, a preview of the value, and
1369
+ * how long the receiving node took. Choosing a row selects its node, and the
1370
+ * log can follow the selection. It reads the `RunTrace` from
1371
+ * `<RunTraceProvider>` and renders nothing without one.
1372
+ */
1373
+
1374
+ interface RunLogProps {
1375
+ readonly core: EditorCore;
1376
+ /** Also called when a row is chosen, after its node is selected (e.g. to bring it into view). */
1377
+ readonly onSelectNode?: (nodeId: string) => void;
1378
+ /** The log's name, shown at the start of its header and used as its accessible name. */
1379
+ readonly title?: string;
1380
+ /** Shown at the start of the header in place of the title, e.g. the host's tabs. */
1381
+ readonly leading?: ReactNode;
1382
+ }
1383
+ declare function RunLog({ core, onSelectNode, title, leading }: RunLogProps): ReactElement | null;
1384
+
1385
+ /**
1386
+ * `NodeRunStats` — what one node did in the current run: messages in and out,
1387
+ * errors, the average and longest time it spent on a message, and the latest
1388
+ * values. Shown in the inspector when the host provides a `RunTrace`.
1389
+ */
1390
+
1391
+ declare function NodeRunStats({ nodeId }: {
1392
+ readonly nodeId: string;
1393
+ }): ReactElement | null;
1394
+
1395
+ /**
1396
+ * `ConnectionInspector` — the inspector for one selected connection: where it
1397
+ * runs from and to, and its back-pressure (RFC 0001): whether it is bounded,
1398
+ * its capacity, and whether a full connection drops its oldest message.
1399
+ * Changes are undoable commands.
1400
+ */
1401
+
1402
+ declare function ConnectionInspector({ core, edgeId }: {
1403
+ readonly core: EditorCore;
1404
+ readonly edgeId: EdgeId;
1405
+ }): ReactElement | null;
1406
+
1407
+ /**
1408
+ * `useEdgeBounds` — one connection and its bounds (RFC 0001), kept current
1409
+ * as commands change it, with a setter that commits an undoable
1410
+ * `SetEdgeBoundsCommand`.
1411
+ */
1412
+
1413
+ interface EdgeBoundsHandle {
1414
+ /** The edge, or undefined if it no longer exists. */
1415
+ readonly edge: EditorEdge | undefined;
1416
+ /** Bound the connection, or unbound it, as one undoable step. */
1417
+ readonly setBounds: (bounds: EdgeBounds) => void;
1418
+ }
1419
+ declare function useEdgeBounds(core: EditorCore, edgeId: EdgeId | undefined): EdgeBoundsHandle;
1420
+
1421
+ /**
1422
+ * A connection's bounds (RFC 0001) as the editor shows and edits them: the
1423
+ * fields of the inspector's Connection section, the bounds a change to one of
1424
+ * them makes, and the label a bounded edge carries.
1425
+ */
1426
+
1427
+ /** The capacity a connection gets when it is first bounded: JavaFBP's production default. */
1428
+ declare const DEFAULT_EDGE_CAPACITY = 10;
1429
+ /** The inspector's fields for a connection with `bounds`. */
1430
+ declare function boundsFields(bounds: EdgeBounds): ParamField[];
1431
+ /** The bounds after the inspector's field `key` is set to `value`. */
1432
+ declare function nextBounds(bounds: EdgeBounds, key: string, value: ParamValue): EdgeBounds;
1433
+ /** A bounded edge's label, in FBP notation: `[4]`; `undefined` when unbounded. */
1434
+ declare function capacityLabel(bounds: EdgeBounds): string | undefined;
1435
+
1436
+ /**
1437
+ * `previewValue` — a short, single-line text for any value a node sends, for
1438
+ * the run log and the inspector. Messages are `unknown`: they can be huge,
1439
+ * cyclic, class instances, or objects whose getters throw, so the preview
1440
+ * never throws, never prints a function's source, and is cut to a limit.
1441
+ */
1442
+ /** The longest preview, in characters, unless a caller asks for another. */
1443
+ declare const PREVIEW_LENGTH = 120;
1444
+ /** A single-line preview of `value`, at most `maxLength` characters. */
1445
+ declare function previewValue(value: unknown, maxLength?: number): string;
1446
+
1447
+ /** Short, aligned text for the times the run views show. */
1448
+ /** How long a delivery took: `<0.01 ms`, `0.12 ms`, `4.6 ms`, `42 ms`, `1.52 s`. */
1449
+ declare function formatDuration(ms: number): string;
1450
+ /** When something happened, in seconds since the run started: `0.013s`. */
1451
+ declare function formatElapsed(ms: number): string;
1452
+
1453
+ /**
1454
+ * `LivePulse` — one flash on a node each time it runs during a live run. The
1455
+ * pulse count from the live store keys the element, so every new count
1456
+ * mounts a fresh element and its CSS animation plays once: the node flashes
1457
+ * exactly as often as it actually ran (per frame at most). Place it as the
1458
+ * first child of a node's rounded, positioned root.
1459
+ */
1460
+
1461
+ declare function LivePulse({ nodeId }: {
1462
+ readonly nodeId: string;
1463
+ }): ReactElement | null;
1464
+
1465
+ export { AssetGrid, AssetNodeChrome, CanvasChrome, CanvasDock, Cheatsheet, ConnectionInspector, DEFAULT_EDGE_CAPACITY, DEFAULT_MIN_CANVAS_HEIGHT, DiagnosticBadge, EditorShell, EditorUIThemeProvider, EmptyState, FOLLOW_SLACK_PX, FOOTER_KEY_STEP, FlowEdge, FooterResizer, HlangNode, InspectorPanel, KIND_ROLE, KIND_TOKEN, LivePulse, LiveSignalProvider, LiveSignalStore, NodeBodySlot, NodeChrome, NodeContextMenu, NodeInteractionProvider, NodeRunStats, NodeSearch, OutlineTree, PREVIEW_LENGTH, ParamForm, PillNodeChrome, PortRow, PositionPanel, PreviewPanel, RUN_LOG_CAPACITY, RailTabs, RunButton, RunLog, RunTrace, RunTraceProvider, SELF_LOOP_CLEARANCE, SELF_LOOP_RADIUS, SignalBar, TOKEN_NAMES, ToastProvider, ToolCluster, Toolbar, ToolbarGhostButton, ToolbarStat, UndoRedo, boundsFields, capacityLabel, clampFooter, connectableCandidates, createHlangFlowTypes, createTheme, defaultLiveDeps, deriveParamSchema, descriptorCandidates, footerBounds, formatDuration, formatElapsed, fuzzyRank, fuzzyScore, involves, isEditableTarget, nextBounds, previewValue, resizeByKey, searchNodes, selfLoopPath, toggleCollapsed, tokenVar, useEdgeBounds, useEdgeFlowing, useEdgePulse, useEditorHotkeys, useFollowScroll, useFooterResize, useLiveSignalStore, useLiveSignals, useNodeContextMenu, useNodeInteraction, useNodeLiveState, useNodeParams, useNodePulse, useNodeSearch, useNodeSearchTriggers, useRunTrace, useRunTraceStore, useRunTraceVersion, useToast };
1466
+ export type { AssetGridProps, AssetItem, AssetNodeChromeProps, CanvasChromeProps, CanvasDockProps, CanvasMode, CheatsheetProps, ConnectableResult, ConnectionEnd, ContextMenuItem, DeliverySignal, DiagnosticBadgeProps, EdgeBoundsHandle, EdgeIndexEntry, EditorHotkeysOptions, EditorShellProps, EditorUIThemeProviderProps, EmptyStateProps, FollowScroll, FooterBounds, FooterResize, FooterResizeOptions, FooterResizerProps, FooterSize, FuzzyCandidate, FuzzyMatch, FuzzyResult, HlangFlowTypes, InspectorPanelProps, LiveSignalProviderProps, LiveSignalStoreDeps, NodeBodySlotProps, NodeCandidate, NodeChromeProps, NodeContextMenuController, NodeContextMenuProps, NodeContextMenuState, NodeInteraction, NodeInteractionProviderProps, NodeLiveState, NodeParamsHandle, NodeRunSummary, NodeSearchController, NodeSearchProps, NodeSearchResult, NodeSearchResultView, NodeSearchState, NodeSearchTriggerOptions, NodeSearchTriggers, OutlineTreeProps, ParamField, ParamFieldBase, ParamFormProps, PendingConnection, PillNodeChromeProps, PointerPosition, PortRowProps, PositionPanelProps, PreviewPanelProps, RailTab, RailTabsProps, RunButtonProps, RunDeliveryEntry, RunErrorEntry, RunLogEntry, RunLogProps, RunTraceOptions, RunTraceProviderProps, SelfLoop, SelfLoopInput, ShortcutGroup, ShortcutRow, ThemeOverride, ToastApi, ToastProviderProps, TokenName, ToolClusterProps, ToolItem, ToolbarProps, TouchPositions, UndoRedoProps };