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

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 (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -28
  3. package/README.standalone.md +14 -0
  4. package/dist/GraphEditorCanvasContext.d.ts +26 -0
  5. package/dist/GraphEditorCanvasContext.d.ts.map +1 -0
  6. package/dist/GraphEditorCanvasContext.js +20 -0
  7. package/dist/TreeSpecGraphEditor.d.ts +22 -1
  8. package/dist/TreeSpecGraphEditor.d.ts.map +1 -1
  9. package/dist/TreeSpecGraphEditor.js +133 -260
  10. package/dist/canvas/constants.d.ts +41 -0
  11. package/dist/canvas/constants.d.ts.map +1 -0
  12. package/dist/canvas/constants.js +40 -0
  13. package/dist/canvas/edgeBuilders.d.ts +6 -0
  14. package/dist/canvas/edgeBuilders.d.ts.map +1 -0
  15. package/dist/canvas/edgeBuilders.js +57 -0
  16. package/dist/canvas/edgeStyle.d.ts +16 -0
  17. package/dist/canvas/edgeStyle.d.ts.map +1 -0
  18. package/dist/canvas/edgeStyle.js +44 -0
  19. package/dist/canvas/focusChoice.d.ts +3 -0
  20. package/dist/canvas/focusChoice.d.ts.map +1 -0
  21. package/dist/canvas/focusChoice.js +12 -0
  22. package/dist/canvas/typeGuards.d.ts +3 -0
  23. package/dist/canvas/typeGuards.d.ts.map +1 -0
  24. package/dist/canvas/typeGuards.js +16 -0
  25. package/dist/contextMenu/GraphCanvasContextMenu.d.ts +10 -0
  26. package/dist/contextMenu/GraphCanvasContextMenu.d.ts.map +1 -0
  27. package/dist/contextMenu/GraphCanvasContextMenu.js +39 -0
  28. package/dist/contextMenu/types.d.ts +11 -0
  29. package/dist/contextMenu/types.d.ts.map +1 -0
  30. package/dist/contextMenu/types.js +1 -0
  31. package/dist/hooks/keyboardShortcutDispatch.d.ts +30 -0
  32. package/dist/hooks/keyboardShortcutDispatch.d.ts.map +1 -0
  33. package/dist/hooks/keyboardShortcutDispatch.js +88 -0
  34. package/dist/hooks/types.d.ts +32 -2
  35. package/dist/hooks/types.d.ts.map +1 -1
  36. package/dist/hooks/useCanvasContextMenu.d.ts +15 -0
  37. package/dist/hooks/useCanvasContextMenu.d.ts.map +1 -0
  38. package/dist/hooks/useCanvasContextMenu.js +50 -0
  39. package/dist/hooks/useCanvasGraphState.d.ts +29 -0
  40. package/dist/hooks/useCanvasGraphState.d.ts.map +1 -0
  41. package/dist/hooks/useCanvasGraphState.js +150 -0
  42. package/dist/hooks/useCanvasIssueIndex.d.ts +12 -0
  43. package/dist/hooks/useCanvasIssueIndex.d.ts.map +1 -0
  44. package/dist/hooks/useCanvasIssueIndex.js +32 -0
  45. package/dist/hooks/useCanvasNodeResize.d.ts +17 -0
  46. package/dist/hooks/useCanvasNodeResize.d.ts.map +1 -0
  47. package/dist/hooks/useCanvasNodeResize.js +53 -0
  48. package/dist/hooks/useCanvasViewport.d.ts +12 -0
  49. package/dist/hooks/useCanvasViewport.d.ts.map +1 -0
  50. package/dist/hooks/useCanvasViewport.js +84 -0
  51. package/dist/hooks/useChoiceDragDrop.d.ts +25 -0
  52. package/dist/hooks/useChoiceDragDrop.d.ts.map +1 -0
  53. package/dist/hooks/useChoiceDragDrop.js +40 -0
  54. package/dist/hooks/useEditorAdapter.d.ts +46 -0
  55. package/dist/hooks/useEditorAdapter.d.ts.map +1 -0
  56. package/dist/hooks/useEditorAdapter.js +281 -0
  57. package/dist/hooks/useEditorAutosave.d.ts +18 -0
  58. package/dist/hooks/useEditorAutosave.d.ts.map +1 -0
  59. package/dist/hooks/useEditorAutosave.js +37 -0
  60. package/dist/hooks/useEditorHistory.d.ts +16 -0
  61. package/dist/hooks/useEditorHistory.d.ts.map +1 -0
  62. package/dist/hooks/useEditorHistory.js +103 -0
  63. package/dist/hooks/useEditorSelection.d.ts +22 -0
  64. package/dist/hooks/useEditorSelection.d.ts.map +1 -0
  65. package/dist/hooks/useEditorSelection.js +75 -0
  66. package/dist/hooks/useGraphConnect.d.ts +22 -0
  67. package/dist/hooks/useGraphConnect.d.ts.map +1 -0
  68. package/dist/hooks/useGraphConnect.js +75 -0
  69. package/dist/hooks/useTreeSpecEditor.d.ts +1 -9
  70. package/dist/hooks/useTreeSpecEditor.d.ts.map +1 -1
  71. package/dist/hooks/useTreeSpecEditor.js +231 -462
  72. package/dist/index.d.ts +4 -4
  73. package/dist/index.js +2 -2
  74. package/dist/nodes/ChoiceCanvasRow.d.ts +10 -0
  75. package/dist/nodes/ChoiceCanvasRow.d.ts.map +1 -0
  76. package/dist/nodes/ChoiceCanvasRow.js +55 -0
  77. package/dist/nodes/EndNode.d.ts +3 -0
  78. package/dist/nodes/EndNode.d.ts.map +1 -0
  79. package/dist/nodes/EndNode.js +7 -0
  80. package/dist/nodes/PromptNode.d.ts +6 -0
  81. package/dist/nodes/PromptNode.d.ts.map +1 -0
  82. package/dist/nodes/PromptNode.js +51 -0
  83. package/dist/nodes/PromptNodeChoicesList.d.ts +14 -0
  84. package/dist/nodes/PromptNodeChoicesList.d.ts.map +1 -0
  85. package/dist/nodes/PromptNodeChoicesList.js +24 -0
  86. package/dist/nodes/PromptNodeHeader.d.ts +10 -0
  87. package/dist/nodes/PromptNodeHeader.d.ts.map +1 -0
  88. package/dist/nodes/PromptNodeHeader.js +6 -0
  89. package/dist/nodes/PromptNodeIssueBadges.d.ts +7 -0
  90. package/dist/nodes/PromptNodeIssueBadges.d.ts.map +1 -0
  91. package/dist/nodes/PromptNodeIssueBadges.js +6 -0
  92. package/dist/nodes/PromptNodeToolbar.d.ts +6 -0
  93. package/dist/nodes/PromptNodeToolbar.d.ts.map +1 -0
  94. package/dist/nodes/PromptNodeToolbar.js +5 -0
  95. package/dist/nodes/types.d.ts +13 -0
  96. package/dist/nodes/types.d.ts.map +1 -0
  97. package/dist/nodes/types.js +1 -0
  98. package/dist/utils/joinClasses.d.ts +2 -0
  99. package/dist/utils/joinClasses.d.ts.map +1 -0
  100. package/dist/utils/joinClasses.js +3 -0
  101. package/package.json +36 -13
@@ -0,0 +1,12 @@
1
+ import { GRAPH_SELECTION_KIND } from '@signalsafe/tree-spec-editor-core';
2
+ export function resolveCanvasFocusChoiceId(nodeId, selected, focusNodeId, focusChoiceId) {
3
+ if (!focusChoiceId)
4
+ return null;
5
+ if (selected?.kind === GRAPH_SELECTION_KIND.NODE && selected.id === nodeId) {
6
+ return focusChoiceId;
7
+ }
8
+ if (selected?.kind === GRAPH_SELECTION_KIND.EDGE && focusNodeId === nodeId) {
9
+ return focusChoiceId;
10
+ }
11
+ return null;
12
+ }
@@ -0,0 +1,3 @@
1
+ export declare function isChoiceRowClickTarget(target: EventTarget | null): boolean;
2
+ export declare function isReactFlowPaneTarget(target: EventTarget | null): boolean;
3
+ //# sourceMappingURL=typeGuards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeGuards.d.ts","sourceRoot":"","sources":["../../src/canvas/typeGuards.ts"],"names":[],"mappings":"AAEA,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAI1E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAMzE"}
@@ -0,0 +1,16 @@
1
+ import { CHOICE_ROW_SELECTOR, REACT_FLOW_PANE_CLASS } from './constants.js';
2
+ export function isChoiceRowClickTarget(target) {
3
+ if (target == null || typeof target !== 'object')
4
+ return false;
5
+ if (!('closest' in target) || typeof target.closest !== 'function')
6
+ return false;
7
+ return Boolean(target.closest(CHOICE_ROW_SELECTOR));
8
+ }
9
+ export function isReactFlowPaneTarget(target) {
10
+ if (target == null || typeof target !== 'object')
11
+ return false;
12
+ if (!('classList' in target) || typeof target.classList?.contains !== 'function') {
13
+ return false;
14
+ }
15
+ return target.classList.contains(REACT_FLOW_PANE_CLASS);
16
+ }
@@ -0,0 +1,10 @@
1
+ import type { CanvasContextMenuState } from './types.js';
2
+ export declare function GraphCanvasContextMenu({ menu, readOnly, onClose, onDuplicateNode, onDeleteNode, onAutoLayout, }: Readonly<{
3
+ menu: CanvasContextMenuState | null;
4
+ readOnly: boolean;
5
+ onClose: () => void;
6
+ onDuplicateNode?: (nodeId: string) => void;
7
+ onDeleteNode?: (nodeId: string) => void;
8
+ onAutoLayout?: () => void;
9
+ }>): import("react").JSX.Element | null;
10
+ //# sourceMappingURL=GraphCanvasContextMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GraphCanvasContextMenu.d.ts","sourceRoot":"","sources":["../../src/contextMenu/GraphCanvasContextMenu.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEtD,wBAAgB,sBAAsB,CAAC,EACnC,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,eAAe,EACf,YAAY,EACZ,YAAY,GACf,EAAE,QAAQ,CAAC;IACR,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACpC,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B,CAAC,sCAwDD"}
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { END_NODE_ID } from '@signalsafe/tree-spec-editor-core';
3
+ import { CONTEXT_MENU_CLASS } from '../canvas/constants.js';
4
+ import { joinClasses } from '../utils/joinClasses.js';
5
+ export function GraphCanvasContextMenu({ menu, readOnly, onClose, onDuplicateNode, onDeleteNode, onAutoLayout, }) {
6
+ if (!menu || readOnly)
7
+ return null;
8
+ const items = [];
9
+ if (menu.kind === 'node' && menu.nodeId !== END_NODE_ID) {
10
+ if (onDuplicateNode) {
11
+ items.push({
12
+ key: 'duplicate',
13
+ label: 'Duplicate node',
14
+ onClick: () => onDuplicateNode(menu.nodeId),
15
+ });
16
+ }
17
+ if (onDeleteNode) {
18
+ items.push({
19
+ key: 'delete',
20
+ label: 'Delete node',
21
+ danger: true,
22
+ onClick: () => onDeleteNode(menu.nodeId),
23
+ });
24
+ }
25
+ }
26
+ else if (menu.kind === 'pane' && onAutoLayout) {
27
+ items.push({
28
+ key: 'auto-layout',
29
+ label: 'Auto layout',
30
+ onClick: () => onAutoLayout(),
31
+ });
32
+ }
33
+ if (items.length === 0)
34
+ return null;
35
+ return (_jsx("div", { className: CONTEXT_MENU_CLASS, style: { left: menu.x, top: menu.y, zIndex: 1050 }, role: "menu", tabIndex: -1, onContextMenu: (event) => event.preventDefault(), onPointerDown: (event) => event.stopPropagation(), children: items.map((item) => (_jsx("button", { type: "button", className: joinClasses('dropdown-item', item.danger && 'text-danger'), role: "menuitem", onClick: () => {
36
+ item.onClick();
37
+ onClose();
38
+ }, children: item.label }, item.key))) }));
39
+ }
@@ -0,0 +1,11 @@
1
+ export type CanvasContextMenuState = {
2
+ kind: 'node';
3
+ nodeId: string;
4
+ x: number;
5
+ y: number;
6
+ } | {
7
+ kind: 'pane';
8
+ x: number;
9
+ y: number;
10
+ };
11
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/contextMenu/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,sBAAsB,GAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { type EditorTree, type GraphSelection, type KeyboardShortcutAction } from '@signalsafe/tree-spec-editor-core';
2
+ export type KeyboardShortcutEvent = Pick<KeyboardEvent, 'preventDefault'>;
3
+ export type EditorKeyboardShortcutHandlers = {
4
+ tree: EditorTree | null;
5
+ selection: GraphSelection;
6
+ isPublished: boolean;
7
+ onPreview?: () => void;
8
+ undo: () => boolean;
9
+ redo: () => boolean;
10
+ copySelectedNode: () => boolean;
11
+ pasteCopiedNode: () => boolean;
12
+ deleteSelectedNode: () => boolean;
13
+ saveDraft: () => void | Promise<void>;
14
+ validate: () => void | Promise<void>;
15
+ commitTree: (next: EditorTree) => void;
16
+ setSelection: (sel: GraphSelection) => void;
17
+ setFocusNodeId: (id: string | null) => void;
18
+ };
19
+ export type EditorKeyboardShortcutKeyEvent = Pick<KeyboardEvent, 'ctrlKey' | 'metaKey' | 'shiftKey' | 'key'>;
20
+ /** Map editor state + key event to a shortcut action (extracted for cognitive complexity). */
21
+ export declare function resolveEditorKeyboardShortcutAction(keyEvent: EditorKeyboardShortcutKeyEvent, state: {
22
+ tree: EditorTree | null;
23
+ selection: GraphSelection;
24
+ canUndo: boolean;
25
+ canRedo: boolean;
26
+ hasCopiedNode: boolean;
27
+ isPublished: boolean;
28
+ }): KeyboardShortcutAction | null;
29
+ export declare function dispatchEditorKeyboardShortcut(action: KeyboardShortcutAction | null, event: KeyboardShortcutEvent, handlers: EditorKeyboardShortcutHandlers): void;
30
+ //# sourceMappingURL=keyboardShortcutDispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboardShortcutDispatch.d.ts","sourceRoot":"","sources":["../../src/hooks/keyboardShortcutDispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC9B,MAAM,mCAAmC,CAAC;AAE3C,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,aAAa,EAAE,gBAAgB,CAAC,CAAC;AAE1E,MAAM,MAAM,8BAA8B,GAAG;IACzC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,OAAO,CAAC;IACpB,IAAI,EAAE,MAAM,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,OAAO,CAAC;IAChC,eAAe,EAAE,MAAM,OAAO,CAAC;IAC/B,kBAAkB,EAAE,MAAM,OAAO,CAAC;IAClC,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,UAAU,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IAC5C,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG,IAAI,CAC7C,aAAa,EACb,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAC7C,CAAC;AAEF,8FAA8F;AAC9F,wBAAgB,mCAAmC,CAC/C,QAAQ,EAAE,8BAA8B,EACxC,KAAK,EAAE;IACH,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,cAAc,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;CACxB,GACF,sBAAsB,GAAG,IAAI,CAgB/B;AA0CD,wBAAgB,8BAA8B,CAC1C,MAAM,EAAE,sBAAsB,GAAG,IAAI,EACrC,KAAK,EAAE,qBAAqB,EAC5B,QAAQ,EAAE,8BAA8B,GACzC,IAAI,CA6CN"}
@@ -0,0 +1,88 @@
1
+ import { duplicateNode, getKeyboardShortcutAction, GRAPH_SELECTION_KIND, KEYBOARD_SHORTCUT_ACTION, } from '@signalsafe/tree-spec-editor-core';
2
+ /** Map editor state + key event to a shortcut action (extracted for cognitive complexity). */
3
+ export function resolveEditorKeyboardShortcutAction(keyEvent, state) {
4
+ return getKeyboardShortcutAction({
5
+ ctrlKey: keyEvent.ctrlKey,
6
+ metaKey: keyEvent.metaKey,
7
+ shiftKey: keyEvent.shiftKey,
8
+ key: keyEvent.key,
9
+ hasSelectedNode: Boolean(state.tree && state.selection.kind === GRAPH_SELECTION_KIND.NODE && state.selection.id),
10
+ hasSelectedEdge: Boolean(state.tree && state.selection.kind === GRAPH_SELECTION_KIND.EDGE && state.selection.id),
11
+ canUndo: state.canUndo && !state.isPublished,
12
+ canRedo: state.canRedo && !state.isPublished,
13
+ hasCopiedNode: state.hasCopiedNode && !state.isPublished,
14
+ });
15
+ }
16
+ /** Apply a resolved keyboard shortcut action (pure dispatch — no listener wiring). */
17
+ function preventDefaultIfSucceeded(event, succeeded) {
18
+ if (succeeded)
19
+ event.preventDefault();
20
+ }
21
+ function dispatchDuplicateShortcut(event, handlers, tree, selectedNodeId) {
22
+ if (!tree || !selectedNodeId)
23
+ return;
24
+ event.preventDefault();
25
+ const duplicated = duplicateNode(tree, selectedNodeId);
26
+ if (!duplicated)
27
+ return;
28
+ handlers.commitTree(duplicated.nextTree);
29
+ handlers.setSelection({ kind: GRAPH_SELECTION_KIND.NODE, id: duplicated.nextNodeId });
30
+ handlers.setFocusNodeId(duplicated.nextNodeId);
31
+ }
32
+ function dispatchDeleteShortcut(event, handlers, tree, selectedNodeId, selectedEdgeId) {
33
+ if (tree && selectedEdgeId) {
34
+ handlers.commitTree({
35
+ ...tree,
36
+ transitions: tree.transitions.filter((transition) => transition.id !== selectedEdgeId),
37
+ });
38
+ handlers.setSelection({ kind: null, id: null });
39
+ event.preventDefault();
40
+ return;
41
+ }
42
+ if (!tree || !selectedNodeId)
43
+ return;
44
+ preventDefaultIfSucceeded(event, handlers.deleteSelectedNode());
45
+ }
46
+ export function dispatchEditorKeyboardShortcut(action, event, handlers) {
47
+ if (!action)
48
+ return;
49
+ const selectedNodeId = handlers.selection.kind === GRAPH_SELECTION_KIND.NODE ? handlers.selection.id : null;
50
+ const selectedEdgeId = handlers.selection.kind === GRAPH_SELECTION_KIND.EDGE ? handlers.selection.id : null;
51
+ const { tree } = handlers;
52
+ switch (action) {
53
+ case KEYBOARD_SHORTCUT_ACTION.UNDO:
54
+ preventDefaultIfSucceeded(event, handlers.undo());
55
+ return;
56
+ case KEYBOARD_SHORTCUT_ACTION.REDO:
57
+ preventDefaultIfSucceeded(event, handlers.redo());
58
+ return;
59
+ case KEYBOARD_SHORTCUT_ACTION.COPY:
60
+ preventDefaultIfSucceeded(event, handlers.copySelectedNode());
61
+ return;
62
+ case KEYBOARD_SHORTCUT_ACTION.PASTE:
63
+ preventDefaultIfSucceeded(event, handlers.pasteCopiedNode());
64
+ return;
65
+ case KEYBOARD_SHORTCUT_ACTION.SAVE:
66
+ event.preventDefault();
67
+ void handlers.saveDraft();
68
+ return;
69
+ case KEYBOARD_SHORTCUT_ACTION.VALIDATE:
70
+ event.preventDefault();
71
+ void handlers.validate();
72
+ return;
73
+ case KEYBOARD_SHORTCUT_ACTION.PREVIEW:
74
+ if (handlers.onPreview) {
75
+ event.preventDefault();
76
+ handlers.onPreview();
77
+ }
78
+ return;
79
+ case KEYBOARD_SHORTCUT_ACTION.DUPLICATE:
80
+ dispatchDuplicateShortcut(event, handlers, tree, selectedNodeId);
81
+ return;
82
+ case KEYBOARD_SHORTCUT_ACTION.DELETE:
83
+ dispatchDeleteShortcut(event, handlers, tree, selectedNodeId, selectedEdgeId);
84
+ return;
85
+ default:
86
+ return;
87
+ }
88
+ }
@@ -1,4 +1,4 @@
1
- import type { AutosaveStatus, EditorNode, EditorTransition, EditorTree, GraphSelection, KeyboardShortcutAction, TreeSpecAuditEventItem, TreeSpecIssue, TreeSpecSnapshotItem, TreeSpecWire, TreeTemplateSpec } from '@signalsafe/tree-spec-editor-core';
1
+ import type { AutosaveStatus, ChoiceEdgeHints, EditorNode, EditorTransition, EditorTree, GraphEditorEdgeType, GraphSelection, KeyboardShortcutAction, TreeSpecAuditEventItem, TreeSpecIssue, TreeSpecSnapshotItem, TreeSpecWire, TreeTemplateSpec } from '@signalsafe/tree-spec-editor-core';
2
2
  /**
3
3
  * Optional metadata returned with {@link TreeSpecEditorAdapter.getVersion} for
4
4
  * host info panels (e.g. scenario id, version label, timestamps).
@@ -211,6 +211,8 @@ export interface UseTreeSpecEditorState {
211
211
  selectedNode: EditorNode | null;
212
212
  /** Transition object for `selection` when it's an EDGE selection; `null` otherwise. */
213
213
  selectedEdge: EditorTransition | null;
214
+ /** Node shown in the inspector (selected node, or edge source node). */
215
+ inspectorNode: EditorNode | null;
214
216
  /** Search filter for the Nodes panel. */
215
217
  nodeSearch: string;
216
218
  /** Search filter for the Issues panel. */
@@ -231,13 +233,21 @@ export interface UseTreeSpecEditorState {
231
233
  showAudit: boolean;
232
234
  /** Publish-review modal visibility. */
233
235
  showPublishModal: boolean;
236
+ /** Whether undo (Ctrl/Cmd+Z) is available for the current draft. */
237
+ canUndo: boolean;
238
+ /** Whether redo (Ctrl/Cmd+Shift+Z or Ctrl/Cmd+Y) is available. */
239
+ canRedo: boolean;
240
+ /** Whether a node was copied and paste (Ctrl/Cmd+V) can run. */
241
+ hasCopiedNode: boolean;
234
242
  }
235
243
  /** Actions surfaced by {@link useTreeSpecEditor}. */
236
244
  export interface UseTreeSpecEditorActions {
237
245
  /** Replace the entire tree (e.g. after the canvas emits a change). */
238
246
  setTree: (next: EditorTree | null) => void;
239
- /** Replace the current selection. */
247
+ /** Replace the current selection. Clears choice focus when selecting a node or clearing selection. */
240
248
  setSelection: (next: GraphSelection) => void;
249
+ /** Select a choice on the canvas: keeps the parent node selected and focuses the choice. */
250
+ selectChoice: (nodeId: string, choiceId: string) => void;
241
251
  /** Replace the node-focus marker. */
242
252
  setFocusNodeId: (id: string | null) => void;
243
253
  /** Replace the choice-focus marker. */
@@ -308,12 +318,22 @@ export interface UseTreeSpecEditorActions {
308
318
  updateSelectedNode: (patch: Partial<EditorNode>) => void;
309
319
  /** Append a new (empty) choice to the currently-selected node. */
310
320
  addChoice: () => void;
321
+ /** Change a choice's stable type id (updates transitions). */
322
+ setChoiceType: (choiceId: string, typeId: string, defaultLabel?: string) => void;
311
323
  /** Remove a choice from the selected node (and its outgoing transitions). */
312
324
  deleteChoice: (choiceId: string) => void;
325
+ /** Move a choice up or down within the selected node's choice list. */
326
+ moveChoice: (choiceId: string, direction: 'up' | 'down') => void;
327
+ /** Drag-drop reposition: reorder within a node or move to another node. */
328
+ repositionChoice: (fromNodeId: string, choiceId: string, toNodeId: string, toIndex: number) => void;
313
329
  /** Re-target a choice's transition (creates the transition if missing). */
314
330
  setChoiceTarget: (choiceId: string, targetNodeId: string) => void;
315
331
  /** Update the terminal outcome of a choice targeting the END node. */
316
332
  setChoiceOutcome: (choiceId: string, outcome: string) => void;
333
+ /** Patch canvas-only edge appearance hints on any node's choice. */
334
+ updateChoiceEdgeHints: (nodeId: string, choiceId: string, patch: Partial<ChoiceEdgeHints>) => void;
335
+ /** Set the scenario-level default edge routing type. */
336
+ setDefaultEdgeType: (edgeType: GraphEditorEdgeType) => void;
317
337
  /**
318
338
  * Click an issue from the Issues panel: focuses the issue's node + choice,
319
339
  * selects the node, and bumps the fitView nonce.
@@ -322,6 +342,16 @@ export interface UseTreeSpecEditorActions {
322
342
  node_id?: string;
323
343
  choice_id?: string;
324
344
  }) => void;
345
+ /** Undo the most recent tree edit. Returns `true` when a step was restored. */
346
+ undo: () => boolean;
347
+ /** Redo the most recently undone edit. Returns `true` when a step was restored. */
348
+ redo: () => boolean;
349
+ /** Copy the selected node for paste. Returns `true` when a node was copied. */
350
+ copySelectedNode: () => boolean;
351
+ /** Paste the copied node as a duplicate. Returns `true` when paste succeeded. */
352
+ pasteCopiedNode: () => boolean;
353
+ /** Duplicate a node by id (same semantics as Ctrl/Cmd+D). Returns the new node id. */
354
+ duplicateNodeById: (nodeId: string) => string | undefined;
325
355
  }
326
356
  /** Return value of {@link useTreeSpecEditor}. */
327
357
  export interface UseTreeSpecEditorResult extends UseTreeSpecEditorState {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/hooks/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,gBAAgB,EACnB,MAAM,mCAAmC,CAAC;AAE3C;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,UAAU,EAAE,CACR,QAAQ,EAAE,MAAM,KACf,OAAO,CAAC;QACT,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,YAAY,EAAE,OAAO,CAAC;QACtB,IAAI,CAAC,EAAE,sBAAsB,CAAC;KACjC,GAAG,IAAI,CAAC,CAAC;IACV,2EAA2E;IAC3E,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpG;;;OAGG;IACH,QAAQ,CAAC,EAAE,CACP,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACjC,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,sBAAsB,EAAE,CAAA;KAAE,CAAC,CAAC;IACpE,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,4EAA4E;IAC5E,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACtE,iCAAiC;IACjC,eAAe,CAAC,EAAE,CACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,KACjB,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACrD,yCAAyC;IACzC,cAAc,CAAC,EAAE,CACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAC9D,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,iEAAiE;IACjE,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;CACvE;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACnC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACrC,uEAAuE;IACvE,OAAO,EAAE,qBAAqB,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4EAA4E;IAC5E,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,YAAY,GAAG,IAAI,CAAC;IACtD;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,aAAa,EAAE,CAAC;IAC9E;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,EAAE,GAAG,IAAI,CAAC;IACtE;;;;OAIG;IACH,4BAA4B,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,GAAG,SAAS,KAAK,OAAO,CAAC;CAChF;AAED,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACnC,wFAAwF;IACxF,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,MAAM,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,UAAU,EAAE,OAAO,CAAC;IACpB,4CAA4C;IAC5C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kDAAkD;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,uCAAuC;IACvC,cAAc,EAAE,cAAc,CAAC;IAC/B,8DAA8D;IAC9D,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B,+EAA+E;IAC/E,WAAW,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,qEAAqE;IACrE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB;;;OAGG;IACH,YAAY,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,kEAAkE;IAClE,gBAAgB,EAAE,YAAY,GAAG,IAAI,CAAC;IACtC,0DAA0D;IAC1D,WAAW,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,WAAW,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC3C,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IAEjB,2EAA2E;IAC3E,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,kEAAkE;IAClE,YAAY,EAAE,aAAa,EAAE,CAAC;IAC9B;;;OAGG;IACH,aAAa,EAAE,aAAa,EAAE,CAAC;IAC/B,2EAA2E;IAC3E,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,+DAA+D;IAC/D,UAAU,EAAE,OAAO,CAAC;IAEpB,qDAAqD;IACrD,SAAS,EAAE,cAAc,CAAC;IAC1B,yEAAyE;IACzE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,0DAA0D;IAC1D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,YAAY,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,uFAAuF;IACvF,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAEtC,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,WAAW,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,wDAAwD;IACxD,WAAW,EAAE,sBAAsB,EAAE,CAAC;IACtC,kCAAkC;IAClC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qCAAqC;IACrC,YAAY,EAAE,OAAO,CAAC;IACtB,sCAAsC;IACtC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,8BAA8B;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,gBAAgB,EAAE,OAAO,CAAC;CAC7B;AAED,qDAAqD;AACrD,MAAM,WAAW,wBAAwB;IACrC,sEAAsE;IACtE,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3C,qCAAqC;IACrC,YAAY,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,qCAAqC;IACrC,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,uCAAuC;IACvC,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,4CAA4C;IAC5C,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,6CAA6C;IAC7C,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,kCAAkC;IAClC,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,yCAAyC;IACzC,mBAAmB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C,iCAAiC;IACjC,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,0CAA0C;IAC1C,mBAAmB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7C;;;OAGG;IACH,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IACjF;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,OAAO,CAAC;IAClC;;;;OAIG;IACH,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,6DAA6D;IAC7D,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,oEAAoE;IACpE,cAAc,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAEjD;;;;;OAKG;IACH,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE,YAAY,KAClC,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,sBAAsB,EAAE,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAC/E,+EAA+E;IAC/E,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B;;;OAGG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7B,6DAA6D;IAC7D,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,0EAA0E;IAC1E,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD;;;OAGG;IACH,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAElC,yEAAyE;IACzE,kBAAkB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IACzD,kEAAkE;IAClE,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,6EAA6E;IAC7E,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,2EAA2E;IAC3E,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IAClE,sEAAsE;IACtE,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9D;;;OAGG;IACH,WAAW,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC1E;AAED,iDAAiD;AACjD,MAAM,WAAW,uBAAwB,SAAQ,sBAAsB;IACnE,OAAO,EAAE,wBAAwB,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAAG,sBAAsB,GAAG,IAAI,CAAC;AAErE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/hooks/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { type MouseEvent } from 'react';
2
+ import type { Node } from 'reactflow';
3
+ import type { CanvasContextMenuState } from '../contextMenu/types.js';
4
+ export type UseCanvasContextMenuOptions = {
5
+ readOnly: boolean;
6
+ onAutoLayout?: () => void;
7
+ };
8
+ export type UseCanvasContextMenuResult = {
9
+ contextMenu: CanvasContextMenuState | null;
10
+ closeContextMenu: () => void;
11
+ onNodeContextMenu: (event: MouseEvent, node: Node) => void;
12
+ onPaneContextMenu: (event: MouseEvent) => void;
13
+ };
14
+ export declare function useCanvasContextMenu(options: UseCanvasContextMenuOptions): UseCanvasContextMenuResult;
15
+ //# sourceMappingURL=useCanvasContextMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCanvasContextMenu.d.ts","sourceRoot":"","sources":["../../src/hooks/useCanvasContextMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAC1E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,MAAM,MAAM,2BAA2B,GAAG;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACrC,WAAW,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC3C,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,iBAAiB,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC3D,iBAAiB,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CAClD,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,0BAA0B,CAsDrG"}
@@ -0,0 +1,50 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ export function useCanvasContextMenu(options) {
3
+ const { readOnly, onAutoLayout } = options;
4
+ const [contextMenu, setContextMenu] = useState(null);
5
+ const closeContextMenu = useCallback(() => setContextMenu(null), []);
6
+ useEffect(() => {
7
+ if (!contextMenu)
8
+ return;
9
+ if (typeof document === 'undefined')
10
+ return;
11
+ const onPointerDown = () => closeContextMenu();
12
+ const onKeyDown = (event) => {
13
+ if (event.key === 'Escape')
14
+ closeContextMenu();
15
+ };
16
+ document.addEventListener('pointerdown', onPointerDown);
17
+ document.addEventListener('keydown', onKeyDown);
18
+ return () => {
19
+ document.removeEventListener('pointerdown', onPointerDown);
20
+ document.removeEventListener('keydown', onKeyDown);
21
+ };
22
+ }, [contextMenu, closeContextMenu]);
23
+ const onNodeContextMenu = useCallback((event, node) => {
24
+ if (readOnly)
25
+ return;
26
+ event.preventDefault();
27
+ setContextMenu({
28
+ kind: 'node',
29
+ nodeId: node.id,
30
+ x: event.clientX,
31
+ y: event.clientY,
32
+ });
33
+ }, [readOnly]);
34
+ const onPaneContextMenu = useCallback((event) => {
35
+ if (readOnly || !onAutoLayout)
36
+ return;
37
+ event.preventDefault();
38
+ setContextMenu({
39
+ kind: 'pane',
40
+ x: event.clientX,
41
+ y: event.clientY,
42
+ });
43
+ }, [readOnly, onAutoLayout]);
44
+ return {
45
+ contextMenu,
46
+ closeContextMenu,
47
+ onNodeContextMenu,
48
+ onPaneContextMenu,
49
+ };
50
+ }
@@ -0,0 +1,29 @@
1
+ import { type Edge, type Node, type OnEdgesChange, type OnNodesChange, type Viewport } from 'reactflow';
2
+ import { type EditorTree, type GraphEditorIssue } from '@signalsafe/tree-spec-editor-core';
3
+ export type UseCanvasGraphStateOptions = {
4
+ tree: EditorTree;
5
+ onChange: (next: EditorTree) => void;
6
+ issues: GraphEditorIssue[];
7
+ issuesByNode: Map<string, {
8
+ total: number;
9
+ errors: number;
10
+ warnings: number;
11
+ info: number;
12
+ }>;
13
+ readOnly: boolean;
14
+ resizeHeightByNodeId: Record<string, number>;
15
+ isResizingRef: React.MutableRefObject<boolean>;
16
+ suppressViewportSaveRef: React.MutableRefObject<boolean>;
17
+ };
18
+ export type UseCanvasGraphStateResult = {
19
+ nodes: Node[];
20
+ edges: Edge[];
21
+ onNodesChangeWrapped: OnNodesChange;
22
+ onEdgesChangeWrapped: OnEdgesChange;
23
+ onNodeDragStart: () => void;
24
+ onNodeDragStop: () => void;
25
+ onMoveEnd: (event: MouseEvent | TouchEvent, viewport: Viewport) => void;
26
+ setNodes: (value: Node[] | ((nodes: Node[]) => Node[])) => void;
27
+ };
28
+ export declare function useCanvasGraphState(options: UseCanvasGraphStateOptions): UseCanvasGraphStateResult;
29
+ //# sourceMappingURL=useCanvasGraphState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCanvasGraphState.d.ts","sourceRoot":"","sources":["../../src/hooks/useCanvasGraphState.ts"],"names":[],"mappings":"AACA,OAAO,EAGH,KAAK,IAAI,EACT,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,QAAQ,EAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EASH,KAAK,UAAU,EACf,KAAK,gBAAgB,EAGxB,MAAM,mCAAmC,CAAC;AAM3C,MAAM,MAAM,0BAA0B,GAAG;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7F,QAAQ,EAAE,OAAO,CAAC;IAClB,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,aAAa,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC/C,uBAAuB,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACpC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,oBAAoB,EAAE,aAAa,CAAC;IACpC,oBAAoB,EAAE,aAAa,CAAC;IACpC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,SAAS,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACxE,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;CACnE,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,yBAAyB,CA4KlG"}
@@ -0,0 +1,150 @@
1
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
2
+ import { useEdgesState, useNodesState, } from 'reactflow';
3
+ import { END_NODE_ID, getEditorHints, isNodeLocked, patchGraphEditorMeta, resolveCanvasNodeWidth, resolveEndNodePosition, snapPosition, } from '@signalsafe/tree-spec-editor-core';
4
+ import { buildEdgesFromTransitions, buildTransitionsFromEdges } from '../canvas/edgeBuilders.js';
5
+ import { NODE_DRAG_HANDLE_SELECTOR } from '../canvas/constants.js';
6
+ export function useCanvasGraphState(options) {
7
+ const { tree, onChange, issues, issuesByNode, readOnly, resizeHeightByNodeId, isResizingRef, suppressViewportSaveRef, } = options;
8
+ const isDraggingRef = useRef(false);
9
+ const nodesRef = useRef([]);
10
+ const edgesRef = useRef([]);
11
+ const treeRef = useRef(tree);
12
+ treeRef.current = tree;
13
+ const endNodePosition = useMemo(() => resolveEndNodePosition(tree), [tree._meta, tree.nodes]);
14
+ useEffect(() => {
15
+ suppressViewportSaveRef.current = true;
16
+ const id = globalThis.setTimeout(() => {
17
+ suppressViewportSaveRef.current = false;
18
+ }, 0);
19
+ return () => globalThis.clearTimeout(id);
20
+ }, [tree._meta, suppressViewportSaveRef]);
21
+ const initialNodes = useMemo(() => {
22
+ const arr = [];
23
+ for (const n of Object.values(tree.nodes)) {
24
+ const editor = getEditorHints(n);
25
+ const locked = isNodeLocked(n);
26
+ const nodeWidth = resolveCanvasNodeWidth(editor);
27
+ const nodeHeight = editor.height ?? resizeHeightByNodeId[n.id];
28
+ arr.push({
29
+ id: n.id,
30
+ type: 'promptNode',
31
+ position: n.position ?? { x: 0, y: 0 },
32
+ draggable: !locked,
33
+ dragHandle: locked || readOnly ? undefined : NODE_DRAG_HANDLE_SELECTOR,
34
+ style: {
35
+ width: nodeWidth,
36
+ ...(nodeHeight === undefined ? {} : { height: nodeHeight }),
37
+ },
38
+ data: {
39
+ node: n,
40
+ isStart: tree.start_node === n.id,
41
+ issuesTotal: issuesByNode.get(n.id)?.total ?? 0,
42
+ issuesErrors: issuesByNode.get(n.id)?.errors ?? 0,
43
+ issuesWarnings: issuesByNode.get(n.id)?.warnings ?? 0,
44
+ issuesInfo: issuesByNode.get(n.id)?.info ?? 0,
45
+ lockedResizeHeight: resizeHeightByNodeId[n.id],
46
+ },
47
+ });
48
+ }
49
+ arr.push({
50
+ id: END_NODE_ID,
51
+ type: 'endNode',
52
+ position: endNodePosition,
53
+ data: {},
54
+ selectable: true,
55
+ draggable: true,
56
+ });
57
+ return arr;
58
+ }, [tree.nodes, tree.start_node, endNodePosition, issuesByNode, readOnly, resizeHeightByNodeId]);
59
+ const initialEdges = useMemo(() => buildEdgesFromTransitions(tree), [tree]);
60
+ const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
61
+ const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
62
+ useEffect(() => {
63
+ nodesRef.current = nodes;
64
+ }, [nodes]);
65
+ useEffect(() => {
66
+ edgesRef.current = edges;
67
+ }, [edges]);
68
+ const lastTreeRef = useRef('');
69
+ useEffect(() => {
70
+ const nextKey = JSON.stringify({ tree, issues: issues.length });
71
+ if (nextKey === lastTreeRef.current)
72
+ return;
73
+ if (isResizingRef.current)
74
+ return;
75
+ lastTreeRef.current = nextKey;
76
+ setNodes(initialNodes);
77
+ setEdges(initialEdges);
78
+ }, [tree, issues.length, initialNodes, initialEdges, setNodes, setEdges, isResizingRef]);
79
+ const commit = useCallback((nextNodes, nextEdges) => {
80
+ if (isDraggingRef.current)
81
+ return;
82
+ const updatedNodes = { ...tree.nodes };
83
+ for (const n of nextNodes) {
84
+ if (n.id === END_NODE_ID)
85
+ continue;
86
+ const existing = updatedNodes[n.id];
87
+ if (!existing)
88
+ continue;
89
+ if (isNodeLocked(existing))
90
+ continue;
91
+ updatedNodes[n.id] = {
92
+ ...existing,
93
+ position: snapPosition({ x: n.position.x, y: n.position.y }),
94
+ };
95
+ }
96
+ const transitions = buildTransitionsFromEdges(nextEdges, tree.transitions);
97
+ const endNode = nextNodes.find((n) => n.id === END_NODE_ID);
98
+ let nextTree = {
99
+ ...tree,
100
+ nodes: updatedNodes,
101
+ transitions,
102
+ };
103
+ if (endNode) {
104
+ nextTree = patchGraphEditorMeta(nextTree, {
105
+ end_position: { x: endNode.position.x, y: endNode.position.y },
106
+ });
107
+ }
108
+ onChange(nextTree);
109
+ }, [tree, onChange]);
110
+ const onMoveEnd = useCallback((_event, viewport) => {
111
+ if (suppressViewportSaveRef.current)
112
+ return;
113
+ onChange(patchGraphEditorMeta(tree, { viewport }));
114
+ }, [tree, onChange, suppressViewportSaveRef]);
115
+ const onNodesChangeWrapped = useCallback((changes) => {
116
+ onNodesChange(changes);
117
+ const shouldCommit = changes.some((c) => {
118
+ if (c?.type === 'select' || c?.type === 'dimensions')
119
+ return false;
120
+ if (c?.type === 'position' && c?.dragging)
121
+ return false;
122
+ return true;
123
+ });
124
+ if (shouldCommit)
125
+ queueMicrotask(() => commit(nodesRef.current, edgesRef.current));
126
+ }, [onNodesChange, commit]);
127
+ const onEdgesChangeWrapped = useCallback((changes) => {
128
+ onEdgesChange(changes);
129
+ const shouldCommit = changes.some((c) => c?.type !== 'select');
130
+ if (shouldCommit)
131
+ queueMicrotask(() => commit(nodesRef.current, edgesRef.current));
132
+ }, [onEdgesChange, commit]);
133
+ const onNodeDragStart = useCallback(() => {
134
+ isDraggingRef.current = true;
135
+ }, []);
136
+ const onNodeDragStop = useCallback(() => {
137
+ isDraggingRef.current = false;
138
+ commit(nodesRef.current, edgesRef.current);
139
+ }, [commit]);
140
+ return {
141
+ nodes,
142
+ edges,
143
+ onNodesChangeWrapped,
144
+ onEdgesChangeWrapped,
145
+ onNodeDragStart,
146
+ onNodeDragStop,
147
+ onMoveEnd,
148
+ setNodes,
149
+ };
150
+ }
@@ -0,0 +1,12 @@
1
+ import type { GraphEditorIssue } from '@signalsafe/tree-spec-editor-core';
2
+ export type CanvasIssueIndex = {
3
+ issuesByNode: Map<string, {
4
+ total: number;
5
+ errors: number;
6
+ warnings: number;
7
+ info: number;
8
+ }>;
9
+ issueKeySet: Set<string>;
10
+ };
11
+ export declare function useCanvasIssueIndex(issues: GraphEditorIssue[]): CanvasIssueIndex;
12
+ //# sourceMappingURL=useCanvasIssueIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCanvasIssueIndex.d.ts","sourceRoot":"","sources":["../../src/hooks/useCanvasIssueIndex.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,MAAM,MAAM,gBAAgB,GAAG;IAC3B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7F,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,CA0BhF"}