@signalsafe/tree-spec-editor-react 0.1.2 → 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.
- package/README.md +118 -42
- package/README.standalone.md +14 -0
- package/dist/TreeSpecGraphEditor.js +16 -16
- package/dist/canvas/edgeBuilders.js +2 -2
- package/dist/canvas/edgeStyle.js +1 -1
- package/dist/canvas/typeGuards.js +1 -1
- package/dist/contextMenu/GraphCanvasContextMenu.d.ts +1 -1
- package/dist/contextMenu/GraphCanvasContextMenu.js +2 -2
- package/dist/hooks/useCanvasContextMenu.d.ts +1 -1
- package/dist/hooks/useCanvasGraphState.js +2 -2
- package/dist/hooks/useEditorAdapter.d.ts +1 -1
- package/dist/hooks/useEditorAutosave.d.ts +1 -1
- package/dist/hooks/useEditorSelection.d.ts.map +1 -1
- package/dist/hooks/useEditorSelection.js +1 -2
- package/dist/hooks/useGraphConnect.js +1 -1
- package/dist/hooks/useTreeSpecEditor.d.ts +1 -1
- package/dist/hooks/useTreeSpecEditor.js +5 -5
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/nodes/ChoiceCanvasRow.js +3 -3
- package/dist/nodes/EndNode.js +2 -2
- package/dist/nodes/PromptNode.d.ts +1 -1
- package/dist/nodes/PromptNode.js +7 -7
- package/dist/nodes/PromptNodeChoicesList.d.ts +1 -1
- package/dist/nodes/PromptNodeChoicesList.js +3 -3
- package/dist/nodes/PromptNodeHeader.d.ts +1 -1
- package/dist/nodes/PromptNodeHeader.js +2 -2
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,64 +1,140 @@
|
|
|
1
1
|
# @signalsafe/tree-spec-editor-react
|
|
2
2
|
|
|
3
|
-
Headless React layer for the SignalSafe TreeSpec graph editor
|
|
4
|
-
React Flow canvas, owns React state plumbing, and exposes the editor as a
|
|
5
|
-
single React component — without depending on any specific UI library.
|
|
3
|
+
Headless **React + React Flow** layer for the SignalSafe TreeSpec graph editor: canvas component, orchestration hook, and wiring to `@signalsafe/tree-spec-editor-core`.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
| | |
|
|
6
|
+
|---|---|
|
|
7
|
+
| **npm** | `@signalsafe/tree-spec-editor-react` |
|
|
8
|
+
| **GitHub** | [SignalSafeSoftware/tree-spec-editor-react](https://github.com/SignalSafeSoftware/tree-spec-editor-react) |
|
|
9
|
+
| **Peer deps** | `react`, `react-dom`, `reactflow` (^18 / ^11) |
|
|
8
10
|
|
|
9
|
-
## What this package
|
|
11
|
+
## What this package does
|
|
10
12
|
|
|
11
|
-
- **`TreeSpecGraphEditor`**
|
|
12
|
-
- **`useTreeSpecEditor`**
|
|
13
|
-
-
|
|
13
|
+
- Renders **`TreeSpecGraphEditor`** (React Flow canvas, custom nodes/edges, selection).
|
|
14
|
+
- Exposes **`useTreeSpecEditor`** for load/validate/autosave/publish orchestration (host injects adapter callbacks).
|
|
15
|
+
- Re-exports core editor types used by the hook.
|
|
14
16
|
|
|
15
|
-
##
|
|
17
|
+
## What this package does not do
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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 |
|
|
19
|
+
- Sidebar panels, modals, toolbars, or Bootstrap chrome — use `@signalsafe/tree-spec-editor` or your own UI shell.
|
|
20
|
+
- Routing, HTTP, authentication, or persistence — host app provides adapter implementations.
|
|
21
|
+
- Wire compile/publish to a backend without your adapter code.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
## Install
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
```bash
|
|
26
|
+
npm install @signalsafe/tree-spec-editor-react @signalsafe/tree-spec-editor-core @signalsafe/tree-spec react react-dom reactflow
|
|
27
|
+
```
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|--------|---------|
|
|
29
|
-
| Editor model, tree operations, layout, autosave/keyboard helpers, choice edge hints | `@signalsafe/tree-spec-editor-core` |
|
|
30
|
-
| Sidebar panels, inspector, modals, toolbar (Bootstrap-styled) | `@signalsafe/tree-spec-editor` |
|
|
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` |
|
|
29
|
+
### React Flow CSS (required)
|
|
34
30
|
|
|
35
|
-
|
|
31
|
+
This package imports `reactflow/dist/style.css` from source. Bundlers treat it as a side effect (`sideEffects: ["**/*.css"]` in `package.json`).
|
|
36
32
|
|
|
37
|
-
|
|
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).
|
|
33
|
+
Ensure your app loads React Flow styles, for example:
|
|
40
34
|
|
|
41
|
-
|
|
35
|
+
```ts
|
|
36
|
+
import "reactflow/dist/style.css";
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If you use `@signalsafe/tree-spec-editor` (Bootstrap shell), the canvas still comes from this package — consumers may need the CSS import in the app entry when tree-shaking.
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { useState } from "react";
|
|
45
|
+
import TreeSpecGraphEditor from "@signalsafe/tree-spec-editor-react";
|
|
46
|
+
import {
|
|
47
|
+
END_NODE_ID,
|
|
48
|
+
type EditorTree,
|
|
49
|
+
} from "@signalsafe/tree-spec-editor-core";
|
|
50
|
+
|
|
51
|
+
const initialTree: EditorTree = {
|
|
52
|
+
start_node: "start",
|
|
53
|
+
nodes: {
|
|
54
|
+
start: {
|
|
55
|
+
id: "start",
|
|
56
|
+
type: "prompt",
|
|
57
|
+
prompt: "Example prompt",
|
|
58
|
+
choices: [{ id: "done", label: "Finish" }],
|
|
59
|
+
position: { x: 40, y: 120 },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
transitions: [
|
|
63
|
+
{
|
|
64
|
+
id: "t1",
|
|
65
|
+
fromNodeId: "start",
|
|
66
|
+
fromChoiceId: "done",
|
|
67
|
+
toNodeId: END_NODE_ID,
|
|
68
|
+
outcome: "safe",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export function ExampleCanvas() {
|
|
74
|
+
const [tree, setTree] = useState(initialTree);
|
|
75
|
+
return (
|
|
76
|
+
<TreeSpecGraphEditor
|
|
77
|
+
tree={tree}
|
|
78
|
+
onChange={setTree}
|
|
79
|
+
className="h-[60vh] border rounded"
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
For full authoring flows (load/save/validate), compose **`useTreeSpecEditor`** with your adapter — see tests and `@signalsafe/tree-spec-editor` for a reference shell.
|
|
86
|
+
|
|
87
|
+
## Public exports
|
|
88
|
+
|
|
89
|
+
| Export | Purpose |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `default` / `TreeSpecGraphEditor` | React Flow canvas |
|
|
92
|
+
| `TreeSpecGraphEditorProps` | Canvas props |
|
|
93
|
+
| `useTreeSpecEditor` | Stateful editor orchestration |
|
|
94
|
+
| `TreeSpecEditorAdapter`, `UseTreeSpecEditorResult`, … | Adapter and hook types |
|
|
95
|
+
|
|
96
|
+
Import from `@signalsafe/tree-spec-editor-react` only (no subpath exports).
|
|
97
|
+
|
|
98
|
+
## Package boundaries
|
|
99
|
+
|
|
100
|
+
| Layer | Package |
|
|
101
|
+
|---|---|
|
|
102
|
+
| Wire | `@signalsafe/tree-spec` |
|
|
103
|
+
| Editor model | `@signalsafe/tree-spec-editor-core` |
|
|
104
|
+
| **React canvas (this package)** | `@signalsafe/tree-spec-editor-react` |
|
|
105
|
+
| Bootstrap shell | `@signalsafe/tree-spec-editor` |
|
|
106
|
+
|
|
107
|
+
## Canvas selection behavior
|
|
108
|
+
|
|
109
|
+
| Selection | Inspector context | Contextual zoom (default on) |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| Node | selected node | fits node in viewport |
|
|
112
|
+
| Edge | source node + focused choice | viewport unchanged |
|
|
113
|
+
| Choice | `focusChoiceId` set | fits parent node when selected |
|
|
114
|
+
|
|
115
|
+
Pass `contextualZoom={false}` to disable automatic viewport fitting.
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
`yarn build` uses `tsconfig.build.json` and resolves `@signalsafe/*` from `node_modules`. Ecosystem sibling `paths` in `tsconfig.json` apply to local typecheck/tests only.
|
|
42
120
|
|
|
43
121
|
```bash
|
|
44
|
-
|
|
122
|
+
yarn install
|
|
123
|
+
yarn build
|
|
124
|
+
yarn test
|
|
125
|
+
yarn typecheck
|
|
45
126
|
```
|
|
46
127
|
|
|
47
|
-
|
|
48
|
-
e.g. `import 'reactflow/dist/style.css';`) in the consuming app. The
|
|
49
|
-
package itself imports the CSS file from its source, so bundlers that
|
|
50
|
-
resolve module references will pick it up automatically.
|
|
128
|
+
## Security
|
|
51
129
|
|
|
52
|
-
|
|
130
|
+
See [SECURITY.md](./SECURITY.md). Host applications must authenticate users, authorize edits, and validate TreeSpec JSON server-side before publish.
|
|
53
131
|
|
|
54
|
-
|
|
55
|
-
to ship a Material-styled editor only need to publish their own UI shell
|
|
56
|
-
(panels, modals, toolbar) — they reuse the canvas and the editor model
|
|
57
|
-
unchanged. This also keeps `@signalsafe/tree-spec-editor` (the
|
|
58
|
-
Bootstrap variant) from being the sole React entry point.
|
|
132
|
+
## Changelog and releases
|
|
59
133
|
|
|
60
|
-
|
|
134
|
+
- [CHANGELOG.md](./CHANGELOG.md)
|
|
135
|
+
- [RELEASING.md](./RELEASING.md)
|
|
61
136
|
|
|
62
|
-
|
|
137
|
+
## Related packages
|
|
63
138
|
|
|
64
|
-
|
|
139
|
+
- [`@signalsafe/tree-spec-editor-core`](https://github.com/SignalSafeSoftware/tree-spec-editor-core) — framework-agnostic editor helpers.
|
|
140
|
+
- [`@signalsafe/tree-spec-editor`](https://github.com/SignalSafeSoftware/tree-spec-editor) — full Bootstrap UI shell.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Standalone export
|
|
2
|
+
|
|
3
|
+
This tree was generated by `scripts/export-standalone-npm-package.sh tree-spec-editor-react` from the DeliveryPlus monorepo.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git init
|
|
7
|
+
git branch -M main
|
|
8
|
+
git add .
|
|
9
|
+
git commit -m "Sync tree-spec-editor-react from DeliveryPlus"
|
|
10
|
+
git remote add origin git@github.com:SignalSafeSoftware/tree-spec-editor-react.git
|
|
11
|
+
git push -u origin main
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
See `RELEASING.md` for npm publish steps.
|
|
@@ -3,22 +3,22 @@ import { useCallback, useMemo, useRef, useState } from 'react';
|
|
|
3
3
|
import ReactFlow, { Background, ConnectionMode, Controls, MiniMap, ReactFlowProvider, useReactFlow, } from 'reactflow';
|
|
4
4
|
import 'reactflow/dist/style.css';
|
|
5
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';
|
|
6
|
+
import { GraphEditorCanvasContext, } from './GraphEditorCanvasContext.js';
|
|
7
|
+
import { buildEdgeMarker, getIssueEdgeStyle, resolveSelectedEdgeStroke } from './canvas/edgeStyle.js';
|
|
8
|
+
import { CANVAS_CLASS } from './canvas/constants.js';
|
|
9
|
+
import { resolveCanvasFocusChoiceId } from './canvas/focusChoice.js';
|
|
10
|
+
import { isChoiceRowClickTarget } from './canvas/typeGuards.js';
|
|
11
|
+
import { GraphCanvasContextMenu } from './contextMenu/GraphCanvasContextMenu.js';
|
|
12
|
+
import { EndNode } from './nodes/EndNode.js';
|
|
13
|
+
import { PromptNode } from './nodes/PromptNode.js';
|
|
14
|
+
import { joinClasses } from './utils/joinClasses.js';
|
|
15
|
+
import { useCanvasContextMenu } from './hooks/useCanvasContextMenu.js';
|
|
16
|
+
import { useCanvasGraphState } from './hooks/useCanvasGraphState.js';
|
|
17
|
+
import { useCanvasIssueIndex } from './hooks/useCanvasIssueIndex.js';
|
|
18
|
+
import { useCanvasNodeResize } from './hooks/useCanvasNodeResize.js';
|
|
19
|
+
import { useCanvasViewport } from './hooks/useCanvasViewport.js';
|
|
20
|
+
import { useChoiceDragDrop } from './hooks/useChoiceDragDrop.js';
|
|
21
|
+
import { useGraphConnect } from './hooks/useGraphConnect.js';
|
|
22
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', }) {
|
|
23
23
|
const rf = useReactFlow();
|
|
24
24
|
const treeRef = useRef(tree);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TERMINAL_OUTCOME } from '@signalsafe/tree-spec';
|
|
2
2
|
import { END_NODE_ID, choiceIdFromHandle, resolveDefaultEdgeType, resolveEffectiveEdgeType, resolveEdgeStrokeColor, shouldShowEdgeLabel, } from '@signalsafe/tree-spec-editor-core';
|
|
3
|
-
import { CHOICE_HANDLE_PREFIX, TARGET_HANDLE_ID, } from './constants';
|
|
4
|
-
import { buildEdgeMarker, buildEdgeStyle, resolveEdgePathStroke } from './edgeStyle';
|
|
3
|
+
import { CHOICE_HANDLE_PREFIX, TARGET_HANDLE_ID, } from './constants.js';
|
|
4
|
+
import { buildEdgeMarker, buildEdgeStyle, resolveEdgePathStroke } from './edgeStyle.js';
|
|
5
5
|
export function edgeLabelForTransition(tree, t) {
|
|
6
6
|
const node = tree.nodes[t.fromNodeId];
|
|
7
7
|
const choice = node?.choices?.find((c) => c.id === t.fromChoiceId);
|
package/dist/canvas/edgeStyle.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_CANVAS_EDGE_STROKE } from '@signalsafe/tree-spec-editor-core';
|
|
2
|
-
import { BORDER_DANGER_CLASS, BORDER_WARNING_CLASS, EDGE_ARROW_MARKER, SELECTED_EDGE_STROKE } from './constants';
|
|
2
|
+
import { BORDER_DANGER_CLASS, BORDER_WARNING_CLASS, EDGE_ARROW_MARKER, SELECTED_EDGE_STROKE } from './constants.js';
|
|
3
3
|
export function getPromptNodeBorderClass(hasErrors, warningCount) {
|
|
4
4
|
if (hasErrors) {
|
|
5
5
|
return BORDER_DANGER_CLASS;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CanvasContextMenuState } from './types';
|
|
1
|
+
import type { CanvasContextMenuState } from './types.js';
|
|
2
2
|
export declare function GraphCanvasContextMenu({ menu, readOnly, onClose, onDuplicateNode, onDeleteNode, onAutoLayout, }: Readonly<{
|
|
3
3
|
menu: CanvasContextMenuState | null;
|
|
4
4
|
readOnly: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { END_NODE_ID } from '@signalsafe/tree-spec-editor-core';
|
|
3
|
-
import { CONTEXT_MENU_CLASS } from '../canvas/constants';
|
|
4
|
-
import { joinClasses } from '../utils/joinClasses';
|
|
3
|
+
import { CONTEXT_MENU_CLASS } from '../canvas/constants.js';
|
|
4
|
+
import { joinClasses } from '../utils/joinClasses.js';
|
|
5
5
|
export function GraphCanvasContextMenu({ menu, readOnly, onClose, onDuplicateNode, onDeleteNode, onAutoLayout, }) {
|
|
6
6
|
if (!menu || readOnly)
|
|
7
7
|
return null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type MouseEvent } from 'react';
|
|
2
2
|
import type { Node } from 'reactflow';
|
|
3
|
-
import type { CanvasContextMenuState } from '../contextMenu/types';
|
|
3
|
+
import type { CanvasContextMenuState } from '../contextMenu/types.js';
|
|
4
4
|
export type UseCanvasContextMenuOptions = {
|
|
5
5
|
readOnly: boolean;
|
|
6
6
|
onAutoLayout?: () => void;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { useEdgesState, useNodesState, } from 'reactflow';
|
|
3
3
|
import { END_NODE_ID, getEditorHints, isNodeLocked, patchGraphEditorMeta, resolveCanvasNodeWidth, resolveEndNodePosition, snapPosition, } from '@signalsafe/tree-spec-editor-core';
|
|
4
|
-
import { buildEdgesFromTransitions, buildTransitionsFromEdges } from '../canvas/edgeBuilders';
|
|
5
|
-
import { NODE_DRAG_HANDLE_SELECTOR } from '../canvas/constants';
|
|
4
|
+
import { buildEdgesFromTransitions, buildTransitionsFromEdges } from '../canvas/edgeBuilders.js';
|
|
5
|
+
import { NODE_DRAG_HANDLE_SELECTOR } from '../canvas/constants.js';
|
|
6
6
|
export function useCanvasGraphState(options) {
|
|
7
7
|
const { tree, onChange, issues, issuesByNode, readOnly, resizeHeightByNodeId, isResizingRef, suppressViewportSaveRef, } = options;
|
|
8
8
|
const isDraggingRef = useRef(false);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type MutableRefObject, type RefObject } from 'react';
|
|
2
2
|
import { type TreeSpecIssue, type TreeSpecWire } from '@signalsafe/tree-spec';
|
|
3
3
|
import { type AutosaveStatus, type EditorTree, type TreeSpecAuditEventItem, type TreeSpecSnapshotItem } from '@signalsafe/tree-spec-editor-core';
|
|
4
|
-
import type { GraphEditorVersionInfo, UseTreeSpecEditorActions, UseTreeSpecEditorOptions } from './types';
|
|
4
|
+
import type { GraphEditorVersionInfo, UseTreeSpecEditorActions, UseTreeSpecEditorOptions } from './types.js';
|
|
5
5
|
export type UseEditorAdapterOptions = {
|
|
6
6
|
options: UseTreeSpecEditorOptions;
|
|
7
7
|
tree: EditorTree | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type MutableRefObject, type RefObject } from 'react';
|
|
2
2
|
import { type AutosaveStatus, type EditorTree } from '@signalsafe/tree-spec-editor-core';
|
|
3
|
-
import type { UseTreeSpecEditorActions } from './types';
|
|
3
|
+
import type { UseTreeSpecEditorActions } from './types.js';
|
|
4
4
|
export type UseEditorAutosaveOptions = {
|
|
5
5
|
enableAutosave: boolean;
|
|
6
6
|
autosaveDebounceMs: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEditorSelection.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorSelection.ts"],"names":[],"mappings":"AAEA,OAAO,EAIH,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,cAAc,EACtB,MAAM,mCAAmC,CAAC;AAE3C,MAAM,MAAM,wBAAwB,GAAG;IACnC,SAAS,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtC,aAAa,EAAE,UAAU,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,cAAc,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvE,YAAY,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,wBAAwB,
|
|
1
|
+
{"version":3,"file":"useEditorSelection.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorSelection.ts"],"names":[],"mappings":"AAEA,OAAO,EAIH,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,cAAc,EACtB,MAAM,mCAAmC,CAAC;AAE3C,MAAM,MAAM,wBAAwB,GAAG;IACnC,SAAS,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtC,aAAa,EAAE,UAAU,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,cAAc,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvE,YAAY,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,wBAAwB,CA4EpF"}
|
|
@@ -13,8 +13,7 @@ export function useEditorSelection(tree) {
|
|
|
13
13
|
const applySelection = useCallback((next) => {
|
|
14
14
|
setSelection(next);
|
|
15
15
|
if (next.kind === GRAPH_SELECTION_KIND.EDGE && next.id && tree) {
|
|
16
|
-
|
|
17
|
-
if (!edge)
|
|
16
|
+
if (!tree.transitions.some((t) => t.id === next.id))
|
|
18
17
|
return;
|
|
19
18
|
}
|
|
20
19
|
const { focusNodeId: nextFocusNodeId, focusChoiceId: nextFocusChoiceId } = resolveGraphSelectionFocus(next, tree);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useRef } from 'react';
|
|
2
2
|
import { useReactFlow } from 'reactflow';
|
|
3
3
|
import { GRAPH_SELECTION_KIND, applyEditorConnect, applyEditorConnectOnDrop, applyEditorReconnect, isValidEditorConnection, } from '@signalsafe/tree-spec-editor-core';
|
|
4
|
-
import { isReactFlowPaneTarget } from '../canvas/typeGuards';
|
|
4
|
+
import { isReactFlowPaneTarget } from '../canvas/typeGuards.js';
|
|
5
5
|
export function useGraphConnect(options) {
|
|
6
6
|
const { treeRef, onChange, onSelect, readOnly } = options;
|
|
7
7
|
const rf = useReactFlow();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UseTreeSpecEditorOptions, UseTreeSpecEditorResult } from './types';
|
|
1
|
+
import type { UseTreeSpecEditorOptions, UseTreeSpecEditorResult } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Headless React hook that owns the full stateful behavior of the SignalSafe
|
|
4
4
|
* TreeSpec graph editor — loading, autosave, validation, publish, snapshots,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { compileTreeSpec, decompileTreeSpec, TERMINAL_OUTCOME, TREE_SPEC_ISSUE_SEVERITY, } from '@signalsafe/tree-spec';
|
|
3
3
|
import { applyTreeTemplate, autoLayoutTree, AUTOSAVE_STATUS, deleteNode, deleteTransitionsForChoice, END_NODE_ID, getNextSpawnPosition, getTransition, GRAPH_SELECTION_KIND, moveChoiceInTree, moveNodeChoice, patchChoiceEdgeHints, patchGraphEditorMeta, renameNodeChoiceId, safeUUID, upsertTransition, } from '@signalsafe/tree-spec-editor-core';
|
|
4
|
-
import { useEditorAdapter } from './useEditorAdapter';
|
|
5
|
-
import { useEditorAutosave } from './useEditorAutosave';
|
|
6
|
-
import { useEditorHistory } from './useEditorHistory';
|
|
7
|
-
import { useEditorSelection } from './useEditorSelection';
|
|
8
|
-
import { dispatchEditorKeyboardShortcut, resolveEditorKeyboardShortcutAction } from './keyboardShortcutDispatch';
|
|
4
|
+
import { useEditorAdapter } from './useEditorAdapter.js';
|
|
5
|
+
import { useEditorAutosave } from './useEditorAutosave.js';
|
|
6
|
+
import { useEditorHistory } from './useEditorHistory.js';
|
|
7
|
+
import { useEditorSelection } from './useEditorSelection.js';
|
|
8
|
+
import { dispatchEditorKeyboardShortcut, resolveEditorKeyboardShortcutAction } from './keyboardShortcutDispatch.js';
|
|
9
9
|
const DEFAULT_AUTOSAVE_DEBOUNCE_MS = 2500;
|
|
10
10
|
function isTextFieldTarget(target) {
|
|
11
11
|
const tag = target instanceof HTMLElement ? target.tagName.toLowerCase() : '';
|
package/dist/index.d.ts
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* UI shells layer on top of this package; routing is host-injected via hook
|
|
11
11
|
* callbacks.
|
|
12
12
|
*/
|
|
13
|
-
export { default } from './TreeSpecGraphEditor';
|
|
14
|
-
export type { TreeSpecGraphEditorProps } from './TreeSpecGraphEditor';
|
|
15
|
-
export { useTreeSpecEditor } from './hooks/useTreeSpecEditor';
|
|
16
|
-
export type { AdapterValidationIssue, GraphEditorVersionInfo, TreeSpecEditorAdapter, UseTreeSpecEditorActions, UseTreeSpecEditorOptions, UseTreeSpecEditorResult, UseTreeSpecEditorState, } from './hooks/types';
|
|
13
|
+
export { default } from './TreeSpecGraphEditor.js';
|
|
14
|
+
export type { TreeSpecGraphEditorProps } from './TreeSpecGraphEditor.js';
|
|
15
|
+
export { useTreeSpecEditor } from './hooks/useTreeSpecEditor.js';
|
|
16
|
+
export type { AdapterValidationIssue, GraphEditorVersionInfo, TreeSpecEditorAdapter, UseTreeSpecEditorActions, UseTreeSpecEditorOptions, UseTreeSpecEditorResult, UseTreeSpecEditorState, } from './hooks/types.js';
|
|
17
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
* UI shells layer on top of this package; routing is host-injected via hook
|
|
11
11
|
* callbacks.
|
|
12
12
|
*/
|
|
13
|
-
export { default } from './TreeSpecGraphEditor';
|
|
14
|
-
export { useTreeSpecEditor } from './hooks/useTreeSpecEditor';
|
|
13
|
+
export { default } from './TreeSpecGraphEditor.js';
|
|
14
|
+
export { useTreeSpecEditor } from './hooks/useTreeSpecEditor.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Handle, Position } from 'reactflow';
|
|
3
|
-
import { CANVAS_NODE_SELECTED_CLASS, CANVAS_NODE_SELECTED_TEXT_CLASS, CANVAS_CHOICE_SELECTED_CLASS, CHOICE_DRAG_HANDLE_CLASS, CHOICE_DRAG_HANDLE_SELECTOR, CHOICE_DROP_TARGET_CLASS, CHOICE_HANDLE_CLASS, CHOICE_ROW_CLASS, CHOICE_ROW_SELECT_CLASS, CHOICE_ROW_SELECTABLE_CLASS, CHOICE_HANDLE_PREFIX, } from '../canvas/constants';
|
|
4
|
-
import { useGraphEditorCanvas } from '../GraphEditorCanvasContext';
|
|
5
|
-
import { joinClasses } from '../utils/joinClasses';
|
|
3
|
+
import { CANVAS_NODE_SELECTED_CLASS, CANVAS_NODE_SELECTED_TEXT_CLASS, CANVAS_CHOICE_SELECTED_CLASS, CHOICE_DRAG_HANDLE_CLASS, CHOICE_DRAG_HANDLE_SELECTOR, CHOICE_DROP_TARGET_CLASS, CHOICE_HANDLE_CLASS, CHOICE_ROW_CLASS, CHOICE_ROW_SELECT_CLASS, CHOICE_ROW_SELECTABLE_CLASS, CHOICE_HANDLE_PREFIX, } from '../canvas/constants.js';
|
|
4
|
+
import { useGraphEditorCanvas } from '../GraphEditorCanvasContext.js';
|
|
5
|
+
import { joinClasses } from '../utils/joinClasses.js';
|
|
6
6
|
export function ChoiceCanvasRow({ nodeId, choice, choiceIndex, focusChoiceId, choiceTextClass, readOnly, }) {
|
|
7
7
|
const { choiceDrag, choiceDropTarget, onSelectChoice, onChoiceDragStart, onChoiceDragEnd, onChoiceDragOver, onChoiceDrop, } = useGraphEditorCanvas();
|
|
8
8
|
const isFocused = focusChoiceId === choice.id;
|
package/dist/nodes/EndNode.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Handle, Position } from 'reactflow';
|
|
3
|
-
import { BORDER_DANGER_CLASS, CANVAS_NODE_CLASS, CANVAS_NODE_SELECTED_CLASS, CANVAS_NODE_SELECTED_TEXT_CLASS, END_NODE_WIDTH_CLASS, TARGET_HANDLE_CLASS_DANGER, TARGET_HANDLE_ID, } from '../canvas/constants';
|
|
4
|
-
import { joinClasses } from '../utils/joinClasses';
|
|
3
|
+
import { BORDER_DANGER_CLASS, CANVAS_NODE_CLASS, CANVAS_NODE_SELECTED_CLASS, CANVAS_NODE_SELECTED_TEXT_CLASS, END_NODE_WIDTH_CLASS, TARGET_HANDLE_CLASS_DANGER, TARGET_HANDLE_ID, } from '../canvas/constants.js';
|
|
4
|
+
import { joinClasses } from '../utils/joinClasses.js';
|
|
5
5
|
export function EndNode({ selected }) {
|
|
6
6
|
return (_jsxs("div", { className: joinClasses('card', 'rounded', CANVAS_NODE_CLASS, BORDER_DANGER_CLASS, END_NODE_WIDTH_CLASS, selected && CANVAS_NODE_SELECTED_CLASS, selected && CANVAS_NODE_SELECTED_TEXT_CLASS), children: [_jsx(Handle, { type: "target", position: Position.Left, id: TARGET_HANDLE_ID, className: TARGET_HANDLE_CLASS_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" })] })] }));
|
|
7
7
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type NodeProps } from 'reactflow';
|
|
2
|
-
import type { PromptNodeData } from './types';
|
|
2
|
+
import type { PromptNodeData } from './types.js';
|
|
3
3
|
type PromptNodeProps = Readonly<NodeProps<PromptNodeData>>;
|
|
4
4
|
export declare function PromptNode({ data, selected, id }: PromptNodeProps): import("react").JSX.Element;
|
|
5
5
|
export {};
|
package/dist/nodes/PromptNode.js
CHANGED
|
@@ -2,13 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useCallback, useEffect } from 'react';
|
|
3
3
|
import { Handle, NodeResizer, Position, useUpdateNodeInternals } from 'reactflow';
|
|
4
4
|
import { editorHintsToStyle, getEditorHints, nodeTextWrapClassName, resolveNodeTextWrap, } from '@signalsafe/tree-spec-editor-core';
|
|
5
|
-
import { CANVAS_NODE_BODY_CLASS, CANVAS_NODE_CLASS, CANVAS_NODE_SELECTED_CLASS, CANVAS_NODE_SELECTED_TEXT_CLASS, MIN_NODE_HEIGHT, MAX_NODE_HEIGHT, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TARGET_HANDLE_CLASS_DEFAULT, TARGET_HANDLE_ID, } from '../canvas/constants';
|
|
6
|
-
import { getPromptNodeBorderClass } from '../canvas/edgeStyle';
|
|
7
|
-
import { useGraphEditorCanvas } from '../GraphEditorCanvasContext';
|
|
8
|
-
import { joinClasses } from '../utils/joinClasses';
|
|
9
|
-
import { PromptNodeChoicesList } from './PromptNodeChoicesList';
|
|
10
|
-
import { PromptNodeHeader } from './PromptNodeHeader';
|
|
11
|
-
import { PromptNodeToolbar } from './PromptNodeToolbar';
|
|
5
|
+
import { CANVAS_NODE_BODY_CLASS, CANVAS_NODE_CLASS, CANVAS_NODE_SELECTED_CLASS, CANVAS_NODE_SELECTED_TEXT_CLASS, MIN_NODE_HEIGHT, MAX_NODE_HEIGHT, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TARGET_HANDLE_CLASS_DEFAULT, TARGET_HANDLE_ID, } from '../canvas/constants.js';
|
|
6
|
+
import { getPromptNodeBorderClass } from '../canvas/edgeStyle.js';
|
|
7
|
+
import { useGraphEditorCanvas } from '../GraphEditorCanvasContext.js';
|
|
8
|
+
import { joinClasses } from '../utils/joinClasses.js';
|
|
9
|
+
import { PromptNodeChoicesList } from './PromptNodeChoicesList.js';
|
|
10
|
+
import { PromptNodeHeader } from './PromptNodeHeader.js';
|
|
11
|
+
import { PromptNodeToolbar } from './PromptNodeToolbar.js';
|
|
12
12
|
export function PromptNode({ data, selected, id }) {
|
|
13
13
|
const n = data.node;
|
|
14
14
|
const choices = n.choices ?? [];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type EditorChoice } from '@signalsafe/tree-spec-editor-core';
|
|
2
|
-
import { type GraphEditorCanvasContextValue } from '../GraphEditorCanvasContext';
|
|
2
|
+
import { type GraphEditorCanvasContextValue } from '../GraphEditorCanvasContext.js';
|
|
3
3
|
export declare function PromptNodeChoicesList({ nodeId, choices, focusChoiceId, choiceTextClass, readOnly, choiceDrag, choiceDropTarget, onChoiceDragOver, onChoiceDrop, }: Readonly<{
|
|
4
4
|
nodeId: string;
|
|
5
5
|
choices: EditorChoice[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { CHOICE_DROP_APPEND_CLASS, CHOICE_DROP_TARGET_CLASS, } from '../canvas/constants';
|
|
3
|
-
import { joinClasses } from '../utils/joinClasses';
|
|
4
|
-
import { ChoiceCanvasRow } from './ChoiceCanvasRow';
|
|
2
|
+
import { CHOICE_DROP_APPEND_CLASS, CHOICE_DROP_TARGET_CLASS, } from '../canvas/constants.js';
|
|
3
|
+
import { joinClasses } from '../utils/joinClasses.js';
|
|
4
|
+
import { ChoiceCanvasRow } from './ChoiceCanvasRow.js';
|
|
5
5
|
export function PromptNodeChoicesList({ nodeId, choices, focusChoiceId, choiceTextClass, readOnly, choiceDrag, choiceDropTarget, onChoiceDragOver, onChoiceDrop, }) {
|
|
6
6
|
const handleListDragOver = (event) => {
|
|
7
7
|
if (readOnly || !choiceDrag)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getEditorHints, type EditorNode } from '@signalsafe/tree-spec-editor-core';
|
|
2
|
-
import type { PromptNodeData } from './types';
|
|
2
|
+
import type { PromptNodeData } from './types.js';
|
|
3
3
|
export declare function PromptNodeHeader({ node, data, editor, locked, readOnly, }: Readonly<{
|
|
4
4
|
node: EditorNode;
|
|
5
5
|
data: PromptNodeData;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { NODE_DRAG_HANDLE_CLASS } from '../canvas/constants';
|
|
3
|
-
import { PromptNodeIssueBadges } from './PromptNodeIssueBadges';
|
|
2
|
+
import { NODE_DRAG_HANDLE_CLASS } from '../canvas/constants.js';
|
|
3
|
+
import { PromptNodeIssueBadges } from './PromptNodeIssueBadges.js';
|
|
4
4
|
export function PromptNodeHeader({ node, data, editor, locked, readOnly, }) {
|
|
5
5
|
return (_jsx("div", { className: "card-header bg-body-secondary py-2 px-2 min-w-0 flex-shrink-0", children: _jsxs("div", { className: "d-flex justify-content-between align-items-start gap-2 min-w-0", children: [_jsxs("div", { className: "d-flex align-items-start gap-1 min-w-0 flex-grow-1 overflow-hidden", children: [locked || readOnly ? null : (_jsx("span", { className: `${NODE_DRAG_HANDLE_CLASS} flex-shrink-0`, title: "Drag node", "aria-label": "Drag node", children: _jsx("i", { className: "bi bi-grip-vertical", "aria-hidden": true }) })), _jsx("div", { className: "min-w-0 flex-grow-1 overflow-hidden", children: _jsxs("div", { className: "fw-bold font-size-13", children: [data.isStart ? '▶ ' : '', node.type, editor.locked ? (_jsx("i", { className: "bi bi-lock-fill ms-1 text-secondary", title: "Locked", "aria-hidden": true })) : null, _jsx(PromptNodeIssueBadges, { issuesTotal: data.issuesTotal, issuesErrors: data.issuesErrors, issuesWarnings: data.issuesWarnings, issuesInfo: data.issuesInfo })] }) })] }), _jsx("div", { className: "text-muted font-size-11 flex-shrink-0", children: node.id.slice(0, 8) })] }) }));
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signalsafe/tree-spec-editor-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Headless React canvas for the SignalSafe TreeSpec graph editor (React Flow shell, no UI library).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
"typescript",
|
|
24
24
|
"signalsafe"
|
|
25
25
|
],
|
|
26
|
-
"sideEffects":
|
|
26
|
+
"sideEffects": [
|
|
27
|
+
"**/*.css"
|
|
28
|
+
],
|
|
27
29
|
"main": "./dist/index.js",
|
|
28
30
|
"types": "./dist/index.d.ts",
|
|
29
31
|
"exports": {
|
|
@@ -41,6 +43,7 @@
|
|
|
41
43
|
"engines": {
|
|
42
44
|
"node": ">=18"
|
|
43
45
|
},
|
|
46
|
+
"packageManager": "yarn@1.22.22",
|
|
44
47
|
"publishConfig": {
|
|
45
48
|
"access": "public"
|
|
46
49
|
},
|
|
@@ -48,14 +51,15 @@
|
|
|
48
51
|
"build": "tsc -p tsconfig.build.json",
|
|
49
52
|
"typecheck": "tsc --noEmit",
|
|
50
53
|
"pack:local": "npm pack --pack-destination ../../dist/reusable-npm",
|
|
54
|
+
"smoke:package": "node scripts/smoke-package.mjs",
|
|
51
55
|
"prepublishOnly": "npm run build",
|
|
52
56
|
"prepare": "npm run build",
|
|
53
57
|
"test": "vitest run",
|
|
54
58
|
"test:monorepo": "cd ../../frontend && yarn vitest run --config vitest.tree-spec-editor-react.config.ts"
|
|
55
59
|
},
|
|
56
60
|
"dependencies": {
|
|
57
|
-
"@signalsafe/tree-spec": "^0.3.
|
|
58
|
-
"@signalsafe/tree-spec-editor-core": "^0.1.
|
|
61
|
+
"@signalsafe/tree-spec": "^0.3.2",
|
|
62
|
+
"@signalsafe/tree-spec-editor-core": "^0.1.3"
|
|
59
63
|
},
|
|
60
64
|
"peerDependencies": {
|
|
61
65
|
"react": "^18.0.0",
|
|
@@ -71,6 +75,7 @@
|
|
|
71
75
|
"react-test-renderer": "^18.3.1",
|
|
72
76
|
"reactflow": "^11.11.4",
|
|
73
77
|
"typescript": "^5.4.2",
|
|
78
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
74
79
|
"vitest": "^3.2.4"
|
|
75
80
|
}
|
|
76
81
|
}
|