@signalsafe/tree-spec-editor-react 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +28 -7
  3. package/dist/GraphEditorCanvasContext.d.ts +26 -0
  4. package/dist/GraphEditorCanvasContext.d.ts.map +1 -0
  5. package/dist/GraphEditorCanvasContext.js +20 -0
  6. package/dist/TreeSpecGraphEditor.d.ts +22 -1
  7. package/dist/TreeSpecGraphEditor.d.ts.map +1 -1
  8. package/dist/TreeSpecGraphEditor.js +133 -260
  9. package/dist/canvas/constants.d.ts +41 -0
  10. package/dist/canvas/constants.d.ts.map +1 -0
  11. package/dist/canvas/constants.js +40 -0
  12. package/dist/canvas/edgeBuilders.d.ts +6 -0
  13. package/dist/canvas/edgeBuilders.d.ts.map +1 -0
  14. package/dist/canvas/edgeBuilders.js +57 -0
  15. package/dist/canvas/edgeStyle.d.ts +16 -0
  16. package/dist/canvas/edgeStyle.d.ts.map +1 -0
  17. package/dist/canvas/edgeStyle.js +44 -0
  18. package/dist/canvas/focusChoice.d.ts +3 -0
  19. package/dist/canvas/focusChoice.d.ts.map +1 -0
  20. package/dist/canvas/focusChoice.js +12 -0
  21. package/dist/canvas/typeGuards.d.ts +3 -0
  22. package/dist/canvas/typeGuards.d.ts.map +1 -0
  23. package/dist/canvas/typeGuards.js +16 -0
  24. package/dist/contextMenu/GraphCanvasContextMenu.d.ts +10 -0
  25. package/dist/contextMenu/GraphCanvasContextMenu.d.ts.map +1 -0
  26. package/dist/contextMenu/GraphCanvasContextMenu.js +39 -0
  27. package/dist/contextMenu/types.d.ts +11 -0
  28. package/dist/contextMenu/types.d.ts.map +1 -0
  29. package/dist/contextMenu/types.js +1 -0
  30. package/dist/hooks/keyboardShortcutDispatch.d.ts +30 -0
  31. package/dist/hooks/keyboardShortcutDispatch.d.ts.map +1 -0
  32. package/dist/hooks/keyboardShortcutDispatch.js +88 -0
  33. package/dist/hooks/types.d.ts +32 -2
  34. package/dist/hooks/types.d.ts.map +1 -1
  35. package/dist/hooks/useCanvasContextMenu.d.ts +15 -0
  36. package/dist/hooks/useCanvasContextMenu.d.ts.map +1 -0
  37. package/dist/hooks/useCanvasContextMenu.js +50 -0
  38. package/dist/hooks/useCanvasGraphState.d.ts +29 -0
  39. package/dist/hooks/useCanvasGraphState.d.ts.map +1 -0
  40. package/dist/hooks/useCanvasGraphState.js +150 -0
  41. package/dist/hooks/useCanvasIssueIndex.d.ts +12 -0
  42. package/dist/hooks/useCanvasIssueIndex.d.ts.map +1 -0
  43. package/dist/hooks/useCanvasIssueIndex.js +32 -0
  44. package/dist/hooks/useCanvasNodeResize.d.ts +17 -0
  45. package/dist/hooks/useCanvasNodeResize.d.ts.map +1 -0
  46. package/dist/hooks/useCanvasNodeResize.js +53 -0
  47. package/dist/hooks/useCanvasViewport.d.ts +12 -0
  48. package/dist/hooks/useCanvasViewport.d.ts.map +1 -0
  49. package/dist/hooks/useCanvasViewport.js +84 -0
  50. package/dist/hooks/useChoiceDragDrop.d.ts +25 -0
  51. package/dist/hooks/useChoiceDragDrop.d.ts.map +1 -0
  52. package/dist/hooks/useChoiceDragDrop.js +40 -0
  53. package/dist/hooks/useEditorAdapter.d.ts +46 -0
  54. package/dist/hooks/useEditorAdapter.d.ts.map +1 -0
  55. package/dist/hooks/useEditorAdapter.js +281 -0
  56. package/dist/hooks/useEditorAutosave.d.ts +18 -0
  57. package/dist/hooks/useEditorAutosave.d.ts.map +1 -0
  58. package/dist/hooks/useEditorAutosave.js +37 -0
  59. package/dist/hooks/useEditorHistory.d.ts +16 -0
  60. package/dist/hooks/useEditorHistory.d.ts.map +1 -0
  61. package/dist/hooks/useEditorHistory.js +103 -0
  62. package/dist/hooks/useEditorSelection.d.ts +22 -0
  63. package/dist/hooks/useEditorSelection.d.ts.map +1 -0
  64. package/dist/hooks/useEditorSelection.js +76 -0
  65. package/dist/hooks/useGraphConnect.d.ts +22 -0
  66. package/dist/hooks/useGraphConnect.d.ts.map +1 -0
  67. package/dist/hooks/useGraphConnect.js +75 -0
  68. package/dist/hooks/useTreeSpecEditor.d.ts +0 -8
  69. package/dist/hooks/useTreeSpecEditor.d.ts.map +1 -1
  70. package/dist/hooks/useTreeSpecEditor.js +231 -462
  71. package/dist/nodes/ChoiceCanvasRow.d.ts +10 -0
  72. package/dist/nodes/ChoiceCanvasRow.d.ts.map +1 -0
  73. package/dist/nodes/ChoiceCanvasRow.js +55 -0
  74. package/dist/nodes/EndNode.d.ts +3 -0
  75. package/dist/nodes/EndNode.d.ts.map +1 -0
  76. package/dist/nodes/EndNode.js +7 -0
  77. package/dist/nodes/PromptNode.d.ts +6 -0
  78. package/dist/nodes/PromptNode.d.ts.map +1 -0
  79. package/dist/nodes/PromptNode.js +51 -0
  80. package/dist/nodes/PromptNodeChoicesList.d.ts +14 -0
  81. package/dist/nodes/PromptNodeChoicesList.d.ts.map +1 -0
  82. package/dist/nodes/PromptNodeChoicesList.js +24 -0
  83. package/dist/nodes/PromptNodeHeader.d.ts +10 -0
  84. package/dist/nodes/PromptNodeHeader.d.ts.map +1 -0
  85. package/dist/nodes/PromptNodeHeader.js +6 -0
  86. package/dist/nodes/PromptNodeIssueBadges.d.ts +7 -0
  87. package/dist/nodes/PromptNodeIssueBadges.d.ts.map +1 -0
  88. package/dist/nodes/PromptNodeIssueBadges.js +6 -0
  89. package/dist/nodes/PromptNodeToolbar.d.ts +6 -0
  90. package/dist/nodes/PromptNodeToolbar.d.ts.map +1 -0
  91. package/dist/nodes/PromptNodeToolbar.js +5 -0
  92. package/dist/nodes/types.d.ts +13 -0
  93. package/dist/nodes/types.d.ts.map +1 -0
  94. package/dist/nodes/types.js +1 -0
  95. package/dist/utils/joinClasses.d.ts +2 -0
  96. package/dist/utils/joinClasses.d.ts.map +1 -0
  97. package/dist/utils/joinClasses.js +3 -0
  98. package/package.json +30 -12
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SignalSafe Software
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -8,20 +8,35 @@ This is the React-specific sibling to **[`@signalsafe/tree-spec-editor-core`](..
8
8
 
9
9
  ## What this package owns
10
10
 
11
- - **`TreeSpecGraphEditor`** — the React Flow canvas (Background, Controls, MiniMap, custom node renderer, transition edges, selection wiring, focus/fit-view).
11
+ - **`TreeSpecGraphEditor`** — the React Flow canvas (Background, Controls, MiniMap, custom node renderer, transition edges, selection wiring, choice focus highlighting, focus/fit-view).
12
+ - **`useTreeSpecEditor`** — stateful orchestration hook (load, autosave, validate, publish, selection, undo/redo, choice/edge helpers). Exposes `inspectorNode` (selected node or edge source node), `focusChoiceId`, and `selectedEdge` for sidebar panels.
12
13
  - **`TreeSpecGraphEditorProps`** — props type.
13
- - Future: framework-shaped headless hooks (e.g. `useTreeSpecEditorState`,
14
- `useGraphSelection`) that wrap the framework-agnostic helpers from
15
- `tree-spec-editor-core`.
14
+
15
+ ## Canvas selection behavior
16
+
17
+ | Selection | Inspector context | Contextual zoom (`contextualZoom`, default `true`) |
18
+ |-----------|-------------------|-----------------------------------------------------|
19
+ | **Node** | `selectedNode` | Fits viewport to the node |
20
+ | **Edge** | `inspectorNode` = source node; `focusChoiceId` = source choice | **No pan/zoom** (viewport stays put) |
21
+ | **Choice** (canvas or inspector focus) | `focusChoiceId` set; nodes list highlights via `focusNodeId` | Fits when the parent node is selected |
22
+
23
+ Pass `onChoiceSelect` + `focusChoiceId` to keep canvas choice rows, inspector choice cards, and the nodes list in sync. Pass `contextualZoom={false}` to disable automatic viewport fitting on node selection.
16
24
 
17
25
  ## What lives elsewhere
18
26
 
19
27
  | Concern | Package |
20
28
  |--------|---------|
21
- | Editor model, tree operations, layout, autosave/keyboard helpers, constants | `@signalsafe/tree-spec-editor-core` |
29
+ | Editor model, tree operations, layout, autosave/keyboard helpers, choice edge hints | `@signalsafe/tree-spec-editor-core` |
22
30
  | Sidebar panels, inspector, modals, toolbar (Bootstrap-styled) | `@signalsafe/tree-spec-editor` |
23
- | Material-styled UI shells (planned) | `@signalsafe/tree-spec-editor-react-mui` |
24
- | Angular implementation (planned) | `@signalsafe/tree-spec-editor-angular` |
31
+ | Material-styled React shell (planned) | `@signalsafe/tree-spec-editor-react-mui` |
32
+ | Angular shell + canvas (planned) | `@signalsafe/tree-spec-editor-angular` |
33
+ | Vue shell + canvas (planned) | `@signalsafe/tree-spec-editor-vue` |
34
+
35
+ ## Maintainer notes
36
+
37
+ - **This package stays React + reactflow only.** Angular/Vue hosts will use `-core` with their own canvas packages, not `-react`.
38
+ - **Material (React)** hosts should add `-react-mui` (planned), reusing this canvas unchanged.
39
+ - Layer boundaries and refactor plan: [docs/ai/packages-editor-architecture.md](../../docs/ai/packages-editor-architecture.md).
25
40
 
26
41
  ## Install
27
42
 
@@ -41,3 +56,9 @@ to ship a Material-styled editor only need to publish their own UI shell
41
56
  (panels, modals, toolbar) — they reuse the canvas and the editor model
42
57
  unchanged. This also keeps `@signalsafe/tree-spec-editor` (the
43
58
  Bootstrap variant) from being the sole React entry point.
59
+
60
+ ## Repository
61
+
62
+ Standalone source and releases: [SignalSafeSoftware/tree-spec-editor-react](https://github.com/SignalSafeSoftware/tree-spec-editor-react).
63
+
64
+ Published as [`@signalsafe/tree-spec-editor-react`](https://www.npmjs.com/package/@signalsafe/tree-spec-editor-react) on npm.
@@ -0,0 +1,26 @@
1
+ export type ChoiceDropTarget = {
2
+ nodeId: string;
3
+ index: number;
4
+ };
5
+ export type ChoiceDragState = {
6
+ sourceNodeId: string;
7
+ choiceId: string;
8
+ };
9
+ export type GraphEditorCanvasContextValue = {
10
+ readOnly: boolean;
11
+ onDuplicateNode: (nodeId: string) => void;
12
+ onDeleteNode: (nodeId: string) => void;
13
+ onResizeNode: (nodeId: string, width: number, height: number) => void;
14
+ /** Lock the node box height when a resize drag begins (prevents wrap reflow jumps). */
15
+ onResizeNodeStart: (nodeId: string, width: number, height: number) => void;
16
+ onSelectChoice: (nodeId: string, choiceId: string) => void;
17
+ choiceDrag: ChoiceDragState | null;
18
+ choiceDropTarget: ChoiceDropTarget | null;
19
+ onChoiceDragStart: (nodeId: string, choiceId: string) => void;
20
+ onChoiceDragEnd: () => void;
21
+ onChoiceDragOver: (nodeId: string, index: number) => void;
22
+ onChoiceDrop: (nodeId: string, index: number) => void;
23
+ };
24
+ export declare const GraphEditorCanvasContext: import("react").Context<GraphEditorCanvasContextValue>;
25
+ export declare function useGraphEditorCanvas(): GraphEditorCanvasContextValue;
26
+ //# sourceMappingURL=GraphEditorCanvasContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GraphEditorCanvasContext.d.ts","sourceRoot":"","sources":["../src/GraphEditorCanvasContext.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IACxC,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtE,uFAAuF;IACvF,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3E,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9D,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzD,CAAC;AAmBF,eAAO,MAAM,wBAAwB,wDAA8B,CAAC;AAEpE,wBAAgB,oBAAoB,IAAI,6BAA6B,CAEpE"}
@@ -0,0 +1,20 @@
1
+ import { createContext, useContext } from 'react';
2
+ const noop = () => undefined;
3
+ const defaultValue = {
4
+ readOnly: true,
5
+ onDuplicateNode: noop,
6
+ onDeleteNode: noop,
7
+ onResizeNode: noop,
8
+ onResizeNodeStart: noop,
9
+ onSelectChoice: noop,
10
+ choiceDrag: null,
11
+ choiceDropTarget: null,
12
+ onChoiceDragStart: noop,
13
+ onChoiceDragEnd: noop,
14
+ onChoiceDragOver: noop,
15
+ onChoiceDrop: noop,
16
+ };
17
+ export const GraphEditorCanvasContext = createContext(defaultValue);
18
+ export function useGraphEditorCanvas() {
19
+ return useContext(GraphEditorCanvasContext);
20
+ }
@@ -7,10 +7,31 @@ export type TreeSpecGraphEditorProps = {
7
7
  showMiniMap?: boolean;
8
8
  selected?: GraphSelection;
9
9
  onSelect?: (sel: GraphSelection) => void;
10
+ /** Called when a choice row is clicked on the canvas (after `onSelect` for the parent node). */
11
+ onChoiceSelect?: (nodeId: string, choiceId: string) => void;
12
+ /** Called when a choice is dropped after drag (reorder or move to another node). */
13
+ onRepositionChoice?: (fromNodeId: string, choiceId: string, toNodeId: string, toIndex: number) => void;
10
14
  focusNodeId?: string | null;
15
+ /** Focused choice on the currently selected node (canvas + inspector sync). */
16
+ focusChoiceId?: string | null;
11
17
  fitViewNonce?: number;
12
18
  /** Optional class for the outer container (default includes h-70vh border rounded). */
13
19
  className?: string;
20
+ /** When true, disables canvas editing affordances (toolbar, resize, context menu). */
21
+ readOnly?: boolean;
22
+ /** Duplicate a prompt node from the canvas toolbar or context menu. */
23
+ onDuplicateNode?: (nodeId: string) => void;
24
+ /** Delete a prompt node from the canvas toolbar or context menu. */
25
+ onDeleteNode?: (nodeId: string) => void;
26
+ /** Run auto-layout from the pane context menu. */
27
+ onAutoLayout?: () => void;
28
+ /**
29
+ * When true (default), zooms the viewport to fit the current node/edge selection.
30
+ * Skipped when `focusNodeId` already targets the same node.
31
+ */
32
+ contextualZoom?: boolean;
33
+ /** Canvas chrome mode — host should pass Bootstrap `colorScheme` (`light` / `dark`). */
34
+ colorMode?: 'light' | 'dark';
14
35
  };
15
- export default function TreeSpecGraphEditor(props: Readonly<TreeSpecGraphEditorProps>): import("react/jsx-runtime").JSX.Element;
36
+ export default function TreeSpecGraphEditor(props: Readonly<TreeSpecGraphEditorProps>): import("react").JSX.Element;
16
37
  //# sourceMappingURL=TreeSpecGraphEditor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TreeSpecGraphEditor.d.ts","sourceRoot":"","sources":["../src/TreeSpecGraphEditor.tsx"],"names":[],"mappings":"AAkBA,OAAO,0BAA0B,CAAC;AAIlC,OAAO,EAKH,KAAK,UAAU,EAEf,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAGxB,MAAM,mCAAmC,CAAC;AAE3C,MAAM,MAAM,wBAAwB,GAAG;IACnC,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAsZF,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,wBAAwB,CAAC,2CAMpF"}
1
+ {"version":3,"file":"TreeSpecGraphEditor.d.ts","sourceRoot":"","sources":["../src/TreeSpecGraphEditor.tsx"],"names":[],"mappings":"AAYA,OAAO,0BAA0B,CAAC;AAElC,OAAO,EAKH,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACxB,MAAM,mCAAmC,CAAC;AAsB3C,MAAM,MAAM,wBAAwB,GAAG;IACnC,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,gGAAgG;IAChG,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,oFAAoF;IACpF,kBAAkB,CAAC,EAAE,CACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,KACd,IAAI,CAAC;IACV,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uEAAuE;IACvE,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAChC,CAAC;AA4PF,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,wBAAwB,CAAC,+BAMpF"}
@@ -1,276 +1,149 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useMemo, useRef } from 'react';
3
- import ReactFlow, { Background, Controls, Handle, MiniMap, Position, ReactFlowProvider, addEdge, useEdgesState, useNodesState, useReactFlow, } from 'reactflow';
2
+ import { useCallback, useMemo, useRef, useState } from 'react';
3
+ import ReactFlow, { Background, ConnectionMode, Controls, MiniMap, ReactFlowProvider, useReactFlow, } from 'reactflow';
4
4
  import 'reactflow/dist/style.css';
5
- import { TERMINAL_OUTCOME, TREE_SPEC_ISSUE_SEVERITY } from '@signalsafe/tree-spec';
6
- import { END_NODE_ID, GRAPH_SELECTION_KIND, safeUUID, } from '@signalsafe/tree-spec-editor-core';
7
- /** Background highlight when the node matches editor selection (sidebar / issues / canvas). */
8
- const CANVAS_NODE_SELECTED_CLASS = 'bg-primary-subtle';
9
- function joinClasses(...parts) {
10
- return parts.filter(Boolean).join(' ');
11
- }
12
- function getPromptNodeBorderClass(hasErrors, warningCount) {
13
- if (hasErrors) {
14
- return 'border-danger';
15
- }
16
- if (warningCount > 0) {
17
- return 'border-warning';
18
- }
19
- return '';
20
- }
21
- function getIssueEdgeStyle(style, hasIssue) {
22
- if (!hasIssue) {
23
- return style;
24
- }
25
- return style ? { ...style, strokeWidth: 2, strokeDasharray: '6 4' } : { strokeWidth: 2, strokeDasharray: '6 4' };
26
- }
27
- function PromptNode({ data, selected }) {
28
- const n = data.node;
29
- const choices = n.choices ?? [];
30
- const hasErrors = data.issuesErrors > 0;
31
- const borderClass = getPromptNodeBorderClass(hasErrors, data.issuesWarnings);
32
- return (_jsxs("div", { className: joinClasses('card', 'card-min-width-280', borderClass, selected && CANVAS_NODE_SELECTED_CLASS), children: [_jsx(Handle, { type: "target", position: Position.Left, id: "in", className: "handle-bg-default" }), _jsxs("div", { className: "card-body p-2", children: [_jsxs("div", { className: "d-flex justify-content-between align-items-center", children: [_jsxs("div", { children: [_jsxs("div", { className: "fw-bold font-size-13", children: [data.isStart ? '▶ ' : '', n.type, data.issuesTotal > 0 ? (_jsxs("span", { className: "ms-2", title: `${data.issuesErrors} errors, ${data.issuesWarnings} warnings, ${data.issuesInfo} info`, children: [data.issuesErrors > 0 ? _jsx("span", { className: "badge bg-danger me-1", children: data.issuesErrors }) : null, data.issuesWarnings > 0 ? (_jsx("span", { className: "badge bg-warning text-dark me-1", children: data.issuesWarnings })) : null, data.issuesInfo > 0 ? _jsx("span", { className: "badge bg-info text-dark", children: data.issuesInfo }) : null] })) : null] }), _jsx("div", { className: "font-size-12 opacity-90 text-truncate", children: n.prompt || _jsx("em", { className: "text-muted", children: "(empty prompt)" }) })] }), _jsx("div", { className: "text-muted font-size-11", children: n.id.slice(0, 8) })] }), _jsx("hr", { className: "my-2" }), _jsx("div", { className: "font-size-12", children: choices.length === 0 ? (_jsx("div", { className: "text-muted", children: _jsx("em", { children: "No choices" }) })) : (_jsx("ul", { className: "list-unstyled mb-0", children: choices.map((c) => (_jsxs("li", { className: "d-flex justify-content-between align-items-center position-relative pr-18", children: [_jsx("span", { className: "text-truncate max-w-210", children: c.label }), _jsx("span", { className: "badge bg-light text-dark", children: c.id }), _jsx(Handle, { type: "source", position: Position.Right, id: `choice:${c.id}`, className: "graph-editor-handle", style: {
33
- position: 'absolute',
34
- right: -6,
35
- top: '50%',
36
- transform: 'translateY(-50%)',
37
- } })] }, c.id))) })) })] })] }));
38
- }
39
- function EndNode({ selected }) {
40
- return (_jsxs("div", { className: joinClasses('card', 'border-danger', 'w-180', selected && CANVAS_NODE_SELECTED_CLASS), children: [_jsx(Handle, { type: "target", position: Position.Left, id: "in", className: "handle-bg-danger" }), _jsxs("div", { className: "card-body p-2 text-center", children: [_jsx("div", { className: "fw-bold text-danger", children: "END" }), _jsx("div", { className: "text-muted font-size-12", children: "Outcome required" })] })] }));
41
- }
42
- function choiceIdFromHandle(h) {
43
- if (!h)
44
- return '';
45
- if (h.startsWith('choice:'))
46
- return h.slice('choice:'.length);
47
- return h;
48
- }
49
- function edgeLabelForTransition(tree, t) {
50
- const node = tree.nodes[t.fromNodeId];
51
- const choice = node?.choices?.find((c) => c.id === t.fromChoiceId);
52
- const base = choice?.label || t.fromChoiceId;
53
- if (t.toNodeId === END_NODE_ID) {
54
- const oc = t.outcome ?? TERMINAL_OUTCOME.AT_RISK;
55
- return `${base} → END (${oc})`;
56
- }
57
- return base;
58
- }
59
- function buildEdgesFromTransitions(tree) {
60
- return tree.transitions.map((t) => ({
61
- id: t.id,
62
- source: t.fromNodeId,
63
- target: t.toNodeId,
64
- sourceHandle: `choice:${t.fromChoiceId}`,
65
- label: edgeLabelForTransition(tree, t),
66
- }));
67
- }
68
- function buildTransitionsFromEdges(edges, existing) {
69
- const existingById = new Map(existing.map((t) => [t.id, t]));
70
- return edges
71
- .filter((e) => e.source && e.target && e.source !== END_NODE_ID)
72
- .map((e) => {
73
- const fromChoiceId = choiceIdFromHandle(e.sourceHandle);
74
- const prior = existingById.get(String(e.id));
75
- return {
76
- id: String(e.id),
77
- fromNodeId: String(e.source),
78
- fromChoiceId: fromChoiceId,
79
- toNodeId: String(e.target),
80
- outcome: String(e.target) === END_NODE_ID ? (prior?.outcome ?? TERMINAL_OUTCOME.AT_RISK) : undefined,
81
- };
82
- })
83
- .filter((t) => t.fromChoiceId.length > 0);
84
- }
85
- function TreeSpecGraphInner({ tree, onChange, issues = [], selected, onSelect, focusNodeId, showMiniMap = true, fitViewNonce, className = 'h-70vh border rounded', }) {
5
+ import { GRAPH_SELECTION_KIND, choiceIdFromHandle, resolveGraphViewport, LAYOUT_SNAP_GRID, } from '@signalsafe/tree-spec-editor-core';
6
+ import { GraphEditorCanvasContext, } from './GraphEditorCanvasContext';
7
+ import { buildEdgeMarker, getIssueEdgeStyle, resolveSelectedEdgeStroke } from './canvas/edgeStyle';
8
+ import { CANVAS_CLASS } from './canvas/constants';
9
+ import { resolveCanvasFocusChoiceId } from './canvas/focusChoice';
10
+ import { isChoiceRowClickTarget } from './canvas/typeGuards';
11
+ import { GraphCanvasContextMenu } from './contextMenu/GraphCanvasContextMenu';
12
+ import { EndNode } from './nodes/EndNode';
13
+ import { PromptNode } from './nodes/PromptNode';
14
+ import { joinClasses } from './utils/joinClasses';
15
+ import { useCanvasContextMenu } from './hooks/useCanvasContextMenu';
16
+ import { useCanvasGraphState } from './hooks/useCanvasGraphState';
17
+ import { useCanvasIssueIndex } from './hooks/useCanvasIssueIndex';
18
+ import { useCanvasNodeResize } from './hooks/useCanvasNodeResize';
19
+ import { useCanvasViewport } from './hooks/useCanvasViewport';
20
+ import { useChoiceDragDrop } from './hooks/useChoiceDragDrop';
21
+ import { useGraphConnect } from './hooks/useGraphConnect';
22
+ function TreeSpecGraphInner({ tree, onChange, issues = [], selected, onSelect, onChoiceSelect, onRepositionChoice, focusNodeId, focusChoiceId = null, showMiniMap = true, fitViewNonce, className = 'h-70vh border rounded', readOnly = false, onDuplicateNode, onDeleteNode, onAutoLayout, contextualZoom = true, colorMode = 'light', }) {
86
23
  const rf = useReactFlow();
87
- const isDraggingRef = useRef(false);
88
- const nodesRef = useRef([]);
89
- const edgesRef = useRef([]);
90
- const issuesByNode = useMemo(() => {
91
- const m = new Map();
92
- for (const i of issues) {
93
- if (!i.node_id)
94
- continue;
95
- const cur = m.get(i.node_id) ?? { total: 0, errors: 0, warnings: 0, info: 0 };
96
- cur.total += 1;
97
- const sev = String(i.severity ?? '').toLowerCase();
98
- if (sev === TREE_SPEC_ISSUE_SEVERITY.WARNING)
99
- cur.warnings += 1;
100
- else if (sev === TREE_SPEC_ISSUE_SEVERITY.INFO)
101
- cur.info += 1;
102
- else
103
- cur.errors += 1;
104
- m.set(i.node_id, cur);
105
- }
106
- return m;
107
- }, [issues]);
108
- const issueKeySet = useMemo(() => {
109
- const s = new Set();
110
- for (const i of issues) {
111
- if (!i.node_id || !i.choice_id)
112
- continue;
113
- s.add(`${i.node_id}::${i.choice_id}`);
114
- }
115
- return s;
116
- }, [issues]);
117
- const initialNodes = useMemo(() => {
118
- const arr = [];
119
- for (const n of Object.values(tree.nodes)) {
120
- arr.push({
121
- id: n.id,
122
- type: 'promptNode',
123
- position: n.position ?? { x: 0, y: 0 },
124
- data: {
125
- node: n,
126
- isStart: tree.start_node === n.id,
127
- issuesTotal: issuesByNode.get(n.id)?.total ?? 0,
128
- issuesErrors: issuesByNode.get(n.id)?.errors ?? 0,
129
- issuesWarnings: issuesByNode.get(n.id)?.warnings ?? 0,
130
- issuesInfo: issuesByNode.get(n.id)?.info ?? 0,
131
- },
132
- });
133
- }
134
- arr.push({
135
- id: END_NODE_ID,
136
- type: 'endNode',
137
- position: { x: 700, y: 0 },
138
- data: {},
139
- selectable: true,
140
- draggable: true,
141
- });
142
- return arr;
143
- }, [tree.nodes, tree.start_node, issuesByNode]);
144
- const initialEdges = useMemo(() => buildEdgesFromTransitions(tree), [tree]);
145
- const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
146
- const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
147
- useEffect(() => {
148
- nodesRef.current = nodes;
149
- }, [nodes]);
150
- useEffect(() => {
151
- edgesRef.current = edges;
152
- }, [edges]);
153
- const lastTreeRef = useRef('');
154
- useEffect(() => {
155
- const nextKey = JSON.stringify({ tree, issues: issues.length });
156
- if (nextKey === lastTreeRef.current)
157
- return;
158
- lastTreeRef.current = nextKey;
159
- setNodes(initialNodes);
160
- setEdges(initialEdges);
161
- }, [tree, issues.length, initialNodes, initialEdges, setNodes, setEdges]);
162
- useEffect(() => {
163
- if (!focusNodeId)
164
- return;
165
- try {
166
- const n = rf.getNode(focusNodeId);
167
- if (!n)
168
- return;
169
- rf.setCenter(n.position.x + 150, n.position.y + 60, { zoom: 1, duration: 300 });
170
- }
171
- catch {
172
- // ignore
173
- }
174
- }, [focusNodeId, rf]);
175
- useEffect(() => {
176
- if (!fitViewNonce)
177
- return;
178
- const id = setTimeout(() => {
179
- try {
180
- rf.fitView({ padding: 0.2, duration: 300 });
181
- }
182
- catch {
183
- // ignore
184
- }
185
- }, 100);
186
- return () => clearTimeout(id);
187
- }, [fitViewNonce, rf]);
24
+ const treeRef = useRef(tree);
25
+ treeRef.current = tree;
26
+ const suppressViewportSaveRef = useRef(true);
27
+ const isResizingRef = useRef(false);
28
+ const { issuesByNode, issueKeySet } = useCanvasIssueIndex(issues);
29
+ const { choiceDrag, choiceDropTarget, handleChoiceDragStart, handleChoiceDragEnd, handleChoiceDragOver, handleChoiceDrop, } = useChoiceDragDrop({
30
+ readOnly,
31
+ onChoiceSelect,
32
+ onSelect,
33
+ onRepositionChoice,
34
+ });
35
+ const { contextMenu, closeContextMenu, onNodeContextMenu, onPaneContextMenu } = useCanvasContextMenu({
36
+ readOnly,
37
+ onAutoLayout,
38
+ });
39
+ const [resizeHeightByNodeId, setResizeHeightByNodeId] = useState({});
40
+ const graphState = useCanvasGraphState({
41
+ tree,
42
+ onChange,
43
+ issues,
44
+ issuesByNode,
45
+ readOnly,
46
+ resizeHeightByNodeId,
47
+ isResizingRef,
48
+ suppressViewportSaveRef,
49
+ });
50
+ const { nodes, edges, onNodesChangeWrapped, onEdgesChangeWrapped, onNodeDragStart, onNodeDragStop, onMoveEnd, setNodes, } = graphState;
51
+ const resizeHandlers = useCanvasNodeResize({
52
+ readOnly,
53
+ treeRef,
54
+ onChange,
55
+ setNodes,
56
+ isResizingRef,
57
+ resizeHeightByNodeId,
58
+ setResizeHeightByNodeId,
59
+ });
60
+ useCanvasViewport({
61
+ rf,
62
+ focusNodeId,
63
+ fitViewNonce,
64
+ contextualZoom,
65
+ selected,
66
+ suppressViewportSaveRef,
67
+ });
68
+ const { onConnect, onConnectStart, onConnectEnd, onReconnect, onEdgesDelete, isValidConnection, } = useGraphConnect({
69
+ treeRef,
70
+ onChange,
71
+ onSelect,
72
+ readOnly,
73
+ });
74
+ const savedViewport = useMemo(() => resolveGraphViewport(tree), [tree._meta]);
188
75
  const nodeTypes = useMemo(() => ({
189
76
  promptNode: PromptNode,
190
77
  endNode: EndNode,
191
78
  }), []);
192
- const commit = useCallback((nextNodes, nextEdges) => {
193
- if (isDraggingRef.current)
79
+ const onNodeClick = useCallback((evt, node) => {
80
+ if (isChoiceRowClickTarget(evt.target))
194
81
  return;
195
- const updatedNodes = { ...tree.nodes };
196
- for (const n of nextNodes) {
197
- if (n.id === END_NODE_ID)
198
- continue;
199
- const existing = updatedNodes[n.id];
200
- if (!existing)
201
- continue;
202
- updatedNodes[n.id] = {
203
- ...existing,
204
- position: { x: n.position.x, y: n.position.y },
205
- };
206
- }
207
- const transitions = buildTransitionsFromEdges(nextEdges, tree.transitions);
208
- onChange({
209
- ...tree,
210
- nodes: updatedNodes,
211
- transitions,
212
- });
213
- }, [tree, onChange]);
214
- const onConnect = useCallback((conn) => {
215
- const choiceId = choiceIdFromHandle(conn.sourceHandle);
216
- if (!conn.source || !conn.target || !choiceId)
217
- return;
218
- const newEdge = {
219
- id: safeUUID(),
220
- source: conn.source,
221
- target: conn.target,
222
- sourceHandle: conn.sourceHandle,
223
- label: choiceId,
224
- };
225
- setEdges((eds) => {
226
- const next = addEdge(newEdge, eds);
227
- commit(nodesRef.current, next);
228
- return next;
229
- });
230
- }, [setEdges, commit]);
231
- const onNodesChangeWrapped = useCallback((changes) => {
232
- onNodesChange(changes);
233
- const shouldCommit = changes.some((c) => {
234
- if (c?.type === 'select' || c?.type === 'dimensions')
235
- return false;
236
- if (c?.type === 'position' && c?.dragging)
237
- return false;
238
- return true;
239
- });
240
- if (shouldCommit)
241
- queueMicrotask(() => commit(nodesRef.current, edgesRef.current));
242
- }, [onNodesChange, commit]);
243
- const onEdgesChangeWrapped = useCallback((changes) => {
244
- onEdgesChange(changes);
245
- const shouldCommit = changes.some((c) => c?.type !== 'select');
246
- if (shouldCommit)
247
- queueMicrotask(() => commit(nodesRef.current, edgesRef.current));
248
- }, [onEdgesChange, commit]);
249
- const onNodeDragStart = useCallback(() => {
250
- isDraggingRef.current = true;
251
- }, []);
252
- const onNodeDragStop = useCallback(() => {
253
- isDraggingRef.current = false;
254
- commit(nodesRef.current, edgesRef.current);
255
- }, [commit]);
256
- const onNodeClick = useCallback((_evt, node) => {
257
82
  onSelect?.({ kind: GRAPH_SELECTION_KIND.NODE, id: node.id });
258
83
  }, [onSelect]);
259
84
  const onEdgeClick = useCallback((_evt, edge) => {
260
85
  onSelect?.({ kind: GRAPH_SELECTION_KIND.EDGE, id: edge.id });
261
86
  }, [onSelect]);
262
- return (_jsx("div", { className: className, children: _jsxs(ReactFlow, { nodes: nodes.map((n) => ({
263
- ...n,
264
- selected: selected?.kind === GRAPH_SELECTION_KIND.NODE && selected.id === n.id,
265
- })), edges: edges.map((e) => {
266
- const fromChoiceId = choiceIdFromHandle(e.sourceHandle);
267
- const hasIssue = fromChoiceId ? issueKeySet.has(`${e.source}::${fromChoiceId}`) : false;
268
- return {
269
- ...e,
270
- selected: selected?.kind === GRAPH_SELECTION_KIND.EDGE && selected.id === e.id,
271
- style: getIssueEdgeStyle(e.style, hasIssue),
272
- };
273
- }), onNodesChange: onNodesChangeWrapped, onEdgesChange: onEdgesChangeWrapped, onConnect: onConnect, onNodeDragStart: onNodeDragStart, onNodeDragStop: onNodeDragStop, onNodeClick: onNodeClick, onEdgeClick: onEdgeClick, nodeTypes: nodeTypes, children: [showMiniMap ? _jsx(MiniMap, {}) : null, _jsx(Controls, {}), _jsx(Background, {})] }) }));
87
+ const onPaneClick = useCallback(() => {
88
+ onSelect?.({ kind: null, id: null });
89
+ }, [onSelect]);
90
+ const canvasContextValue = useMemo(() => ({
91
+ readOnly,
92
+ onDuplicateNode: (nodeId) => onDuplicateNode?.(nodeId),
93
+ onDeleteNode: (nodeId) => onDeleteNode?.(nodeId),
94
+ onResizeNode: resizeHandlers.handleResizeNode,
95
+ onResizeNodeStart: resizeHandlers.handleResizeNodeStart,
96
+ onSelectChoice: (nodeId, choiceId) => {
97
+ if (onChoiceSelect) {
98
+ onChoiceSelect(nodeId, choiceId);
99
+ return;
100
+ }
101
+ onSelect?.({ kind: GRAPH_SELECTION_KIND.NODE, id: nodeId });
102
+ },
103
+ choiceDrag,
104
+ choiceDropTarget,
105
+ onChoiceDragStart: handleChoiceDragStart,
106
+ onChoiceDragEnd: handleChoiceDragEnd,
107
+ onChoiceDragOver: handleChoiceDragOver,
108
+ onChoiceDrop: handleChoiceDrop,
109
+ }), [
110
+ readOnly,
111
+ onDuplicateNode,
112
+ onDeleteNode,
113
+ resizeHandlers.handleResizeNode,
114
+ resizeHandlers.handleResizeNodeStart,
115
+ onSelect,
116
+ onChoiceSelect,
117
+ choiceDrag,
118
+ choiceDropTarget,
119
+ handleChoiceDragStart,
120
+ handleChoiceDragEnd,
121
+ handleChoiceDragOver,
122
+ handleChoiceDrop,
123
+ ]);
124
+ return (_jsx(GraphEditorCanvasContext.Provider, { value: canvasContextValue, children: _jsxs("div", { className: joinClasses(className, CANVAS_CLASS), "data-color-mode": colorMode, children: [_jsx(GraphCanvasContextMenu, { menu: contextMenu, readOnly: readOnly, onClose: closeContextMenu, onDuplicateNode: onDuplicateNode, onDeleteNode: onDeleteNode, onAutoLayout: onAutoLayout }), _jsxs(ReactFlow, { defaultViewport: savedViewport, elementsSelectable: false, selectNodesOnDrag: false, nodes: nodes.map((n) => ({
125
+ ...n,
126
+ selected: selected?.kind === GRAPH_SELECTION_KIND.NODE && selected.id === n.id,
127
+ data: n.type === 'promptNode'
128
+ ? {
129
+ ...n.data,
130
+ focusChoiceId: resolveCanvasFocusChoiceId(n.id, selected, focusNodeId, focusChoiceId),
131
+ }
132
+ : n.data,
133
+ })), edges: edges.map((e) => {
134
+ const fromChoiceId = choiceIdFromHandle(e.sourceHandle);
135
+ const hasIssue = fromChoiceId ? issueKeySet.has(`${e.source}::${fromChoiceId}`) : false;
136
+ const isEdgeSelected = selected?.kind === GRAPH_SELECTION_KIND.EDGE && selected.id === e.id;
137
+ let style = getIssueEdgeStyle(e.style, hasIssue);
138
+ const { style: resolvedStyle, stroke } = resolveSelectedEdgeStroke(style, isEdgeSelected);
139
+ style = resolvedStyle;
140
+ return {
141
+ ...e,
142
+ selected: isEdgeSelected,
143
+ style,
144
+ markerEnd: buildEdgeMarker(stroke),
145
+ };
146
+ }), onNodesChange: onNodesChangeWrapped, onEdgesChange: onEdgesChangeWrapped, onConnect: onConnect, onConnectStart: onConnectStart, onConnectEnd: onConnectEnd, onReconnect: onReconnect, onEdgesDelete: onEdgesDelete, isValidConnection: isValidConnection, connectionMode: ConnectionMode.Strict, snapToGrid: true, snapGrid: [LAYOUT_SNAP_GRID, LAYOUT_SNAP_GRID], onNodeDragStart: onNodeDragStart, onNodeDragStop: onNodeDragStop, onNodeClick: onNodeClick, onEdgeClick: onEdgeClick, onPaneClick: onPaneClick, onNodeContextMenu: onNodeContextMenu, onPaneContextMenu: onPaneContextMenu, onMoveEnd: onMoveEnd, nodeTypes: nodeTypes, children: [showMiniMap ? _jsx(MiniMap, { pannable: true, ariaLabel: "Canvas overview \u2014 drag to pan" }) : null, _jsx(Controls, {}), _jsx(Background, {})] })] }) }));
274
147
  }
275
148
  export default function TreeSpecGraphEditor(props) {
276
149
  return (_jsx(ReactFlowProvider, { children: _jsx(TreeSpecGraphInner, { ...props }) }));
@@ -0,0 +1,41 @@
1
+ import { MarkerType } from 'reactflow';
2
+ /** Background highlight when the node matches editor selection (sidebar / issues / canvas). */
3
+ export declare const CANVAS_NODE_SELECTED_CLASS = "bg-primary-subtle";
4
+ /** Dark readable text on selected canvas node cards. */
5
+ export declare const CANVAS_NODE_SELECTED_TEXT_CLASS = "graph-editor-canvas-selected";
6
+ export declare const CANVAS_CLASS = "graph-editor-canvas";
7
+ export declare const CANVAS_NODE_CLASS = "graph-editor-canvas-node";
8
+ export declare const CANVAS_NODE_BODY_CLASS = "graph-editor-canvas-node-body";
9
+ export declare const CANVAS_CHOICE_SELECTED_CLASS = "graph-editor-canvas-choice-selected";
10
+ export declare const CHOICE_ROW_CLASS = "graph-editor-choice-row";
11
+ export declare const CHOICE_ROW_SELECTOR = ".graph-editor-choice-row";
12
+ export declare const CHOICE_DRAG_HANDLE_CLASS = "graph-editor-choice-drag-handle";
13
+ export declare const CHOICE_DRAG_HANDLE_SELECTOR = ".graph-editor-choice-drag-handle";
14
+ export declare const CHOICE_DROP_TARGET_CLASS = "graph-editor-choice-drop-target";
15
+ export declare const CHOICE_DROP_APPEND_CLASS = "graph-editor-choice-drop-append";
16
+ export declare const CHOICE_ROW_SELECT_CLASS = "graph-editor-choice-row-select";
17
+ export declare const CHOICE_ROW_SELECTABLE_CLASS = "graph-editor-choice-row-selectable";
18
+ export declare const CHOICE_HANDLE_CLASS = "graph-editor-handle graph-editor-choice-handle";
19
+ export declare const NODE_DRAG_HANDLE_CLASS = "graph-editor-drag-handle";
20
+ export declare const NODE_DRAG_HANDLE_SELECTOR = ".graph-editor-drag-handle";
21
+ export declare const TARGET_HANDLE_CLASS_DEFAULT = "handle-bg-default graph-editor-target-handle";
22
+ export declare const TARGET_HANDLE_CLASS_DANGER = "handle-bg-danger graph-editor-target-handle";
23
+ export declare const CONTEXT_MENU_CLASS = "graph-editor-context-menu dropdown-menu show position-fixed shadow-sm";
24
+ export declare const REACT_FLOW_PANE_CLASS = "react-flow__pane";
25
+ export declare const BORDER_DANGER_CLASS = "border-danger";
26
+ export declare const BORDER_WARNING_CLASS = "border-warning";
27
+ export declare const END_NODE_WIDTH_CLASS = "w-180";
28
+ export declare const MIN_NODE_WIDTH = 180;
29
+ export declare const MAX_NODE_WIDTH = 560;
30
+ export declare const MIN_NODE_HEIGHT = 80;
31
+ export declare const MAX_NODE_HEIGHT = 480;
32
+ /** Matches React Flow selected edge stroke (`.react-flow__edge.selected .react-flow__edge-path`). */
33
+ export declare const SELECTED_EDGE_STROKE = "#555";
34
+ export declare const EDGE_ARROW_MARKER: {
35
+ type: MarkerType;
36
+ width: number;
37
+ height: number;
38
+ };
39
+ export declare const TARGET_HANDLE_ID = "in";
40
+ export declare const CHOICE_HANDLE_PREFIX = "choice:";
41
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/canvas/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,+FAA+F;AAC/F,eAAO,MAAM,0BAA0B,sBAAsB,CAAC;AAE9D,wDAAwD;AACxD,eAAO,MAAM,+BAA+B,iCAAiC,CAAC;AAE9E,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAClD,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAC5D,eAAO,MAAM,sBAAsB,kCAAkC,CAAC;AACtE,eAAO,MAAM,4BAA4B,wCAAwC,CAAC;AAElF,eAAO,MAAM,gBAAgB,4BAA4B,CAAC;AAC1D,eAAO,MAAM,mBAAmB,6BAAyB,CAAC;AAC1D,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAC1E,eAAO,MAAM,2BAA2B,qCAAiC,CAAC;AAC1E,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAC1E,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAC1E,eAAO,MAAM,uBAAuB,mCAAmC,CAAC;AACxE,eAAO,MAAM,2BAA2B,uCAAuC,CAAC;AAChF,eAAO,MAAM,mBAAmB,mDAAmD,CAAC;AAEpF,eAAO,MAAM,sBAAsB,6BAA6B,CAAC;AACjE,eAAO,MAAM,yBAAyB,8BAA+B,CAAC;AAEtE,eAAO,MAAM,2BAA2B,iDAAiD,CAAC;AAC1F,eAAO,MAAM,0BAA0B,gDAAgD,CAAC;AAExF,eAAO,MAAM,kBAAkB,0EAA0E,CAAC;AAE1G,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAErD,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,qGAAqG;AACrG,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAE3C,eAAO,MAAM,iBAAiB;;;;CAI7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,oBAAoB,YAAY,CAAC"}