@xxrandr/conecta-diagram 0.0.0-stage → 0.1.4
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/LICENSE +21 -0
- package/README.md +319 -2
- package/dist/chunk-3QGSCPWL.cjs +663 -0
- package/dist/chunk-43VYPB4U.js +2136 -0
- package/dist/chunk-6ADTOXBT.cjs +626 -0
- package/dist/chunk-B2RLLFGC.js +0 -0
- package/dist/chunk-GXOBW2BK.js +290 -0
- package/dist/chunk-HZER5BPR.js +1122 -0
- package/dist/chunk-IT5H6WO2.js +626 -0
- package/dist/chunk-LKOWNTPR.cjs +1 -0
- package/dist/chunk-Q62JPMJH.js +663 -0
- package/dist/chunk-QI4NNFRQ.cjs +290 -0
- package/dist/chunk-VD32JNDO.cjs +2136 -0
- package/dist/chunk-ZMSQTBVW.cjs +1122 -0
- package/dist/core/index.cjs +56 -0
- package/dist/core/index.d.cts +65 -0
- package/dist/core/index.d.ts +65 -0
- package/dist/core/index.js +56 -0
- package/dist/d3/index.cjs +8 -0
- package/dist/d3/index.d.cts +46 -0
- package/dist/d3/index.d.ts +46 -0
- package/dist/d3/index.js +8 -0
- package/dist/index.cjs +71 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +71 -0
- package/dist/photon.css +339 -0
- package/dist/runner-display-BkBJpBY-.d.ts +151 -0
- package/dist/runner-display-DHHtavSX.d.cts +151 -0
- package/dist/store-CtOInpPu.d.cts +242 -0
- package/dist/store-CtOInpPu.d.ts +242 -0
- package/dist/svg/index.cjs +15 -0
- package/dist/svg/index.d.cts +172 -0
- package/dist/svg/index.d.ts +172 -0
- package/dist/svg/index.js +15 -0
- package/docs/devtools-refactor.md +93 -0
- package/docs/java-runtime-integration.md +268 -0
- package/docs/photon-template.html +547 -0
- package/docs/presentation-architecture.md +30 -0
- package/docs/runner-display.md +109 -0
- package/package.json +130 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jhosua Daga
|
|
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
|
@@ -1,3 +1,320 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Conecta Diagram
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A TypeScript library for **planning program flows**, editing diagrams, and simulating control flow. Framework-independent: React and Angular are hosts, not dependencies. The core has no D3 imports; presentation uses focused D3 modules for physics, keyed joins, easing, interpolation and frame scheduling.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @xxrandr/conecta-diagram
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The package ships ESM and CommonJS builds with TypeScript declarations and no runtime dependencies (the small D3 modules are bundled). Import the stylesheet once and instantiate the editor on an `<svg>` element — the adapter is imperative, so any framework can host it.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import "@xxrandr/conecta-diagram/photon.css";
|
|
15
|
+
import { FlowStore, SvgFlowEditor } from "@xxrandr/conecta-diagram";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Development
|
|
19
|
+
|
|
20
|
+
Use Node 22.18+ for the development tools. The built core supports Node 20+; the SVG adapter requires a browser when instantiated.
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm ci
|
|
24
|
+
npm run dev # Builds first, serves current demo, watches/reloads changes
|
|
25
|
+
npm run check # types, tests, dual-format build, package import checks
|
|
26
|
+
npm audit
|
|
27
|
+
npm pack --dry-run
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use `npm run dev` and open `http://127.0.0.1:4173/examples/index.html` for the runner-operation viewer and simulated result/signal replay. This builds before serving and avoids stale `dist` imports. Static serving after a fresh build also works. `examples/approval.ts` remains a typed async/parallel/human-approval control-flow example.
|
|
31
|
+
|
|
32
|
+
The npm package is `@xxrandr/conecta-diagram` (MIT). Nothing is published automatically. The publish allowlist includes only `dist/`, the README, the license and integration documentation, not the old prototype, tests, examples, or editor swap files.
|
|
33
|
+
|
|
34
|
+
To release: run `npm version patch|minor|major`, push with tags (`git push --follow-tags`), and the [Publish workflow](.github/workflows/publish.yml) builds, checks and publishes to npm using the `NPM_TOKEN` repository secret. Alternatively, run `npm publish` locally once `npm run check` passes.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
Load the shared visual system once in your browser entry: `import '@xxrandr/conecta-diagram/photon.css';`.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { FlowStore, SvgFlowEditor, type FlowDocument } from "@xxrandr/conecta-diagram";
|
|
42
|
+
|
|
43
|
+
const flow: FlowDocument = {
|
|
44
|
+
schemaVersion: 1,
|
|
45
|
+
id: "hello",
|
|
46
|
+
nodes: [
|
|
47
|
+
{
|
|
48
|
+
id: "start",
|
|
49
|
+
type: "start",
|
|
50
|
+
label: "Start",
|
|
51
|
+
position: { x: 100, y: 150 },
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "task",
|
|
55
|
+
type: "task",
|
|
56
|
+
mode: "async",
|
|
57
|
+
handler: "send-email",
|
|
58
|
+
label: "Send email",
|
|
59
|
+
position: { x: 350, y: 150 },
|
|
60
|
+
},
|
|
61
|
+
{ id: "end", type: "end", label: "Done", position: { x: 600, y: 150 } },
|
|
62
|
+
],
|
|
63
|
+
edges: [
|
|
64
|
+
{ id: "a", source: "start", target: "task" },
|
|
65
|
+
{ id: "b", source: "task", target: "end" },
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const store = new FlowStore(flow);
|
|
70
|
+
// <svg viewBox="0 0 1000 600" width="100%" height="600"></svg>
|
|
71
|
+
const editor = new SvgFlowEditor(svgElement, store, {
|
|
72
|
+
width: 1000,
|
|
73
|
+
height: 600,
|
|
74
|
+
onNodeActivate: (node) => openYourInspector(node),
|
|
75
|
+
onError: (error) => console.error(error),
|
|
76
|
+
});
|
|
77
|
+
editor.fitToView();
|
|
78
|
+
|
|
79
|
+
const unsubscribe = store.subscribe(({ document, reason, revision }) => {
|
|
80
|
+
// Persist document or synchronize with your host application's state.
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// On unmount:
|
|
84
|
+
unsubscribe();
|
|
85
|
+
editor.destroy();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Use `@xxrandr/conecta-diagram/core` for Node/server simulations and authoring without a renderer. Use `@xxrandr/conecta-diagram/svg` for the SVG adapter. ESM and CommonJS exports include TypeScript declarations. Even the root entry is safe to import during SSR; only instantiate the adapter after a browser mount.
|
|
89
|
+
|
|
90
|
+
## Flow model
|
|
91
|
+
|
|
92
|
+
Documents are versioned JSON. Nodes and edges have unique ids in their respective namespaces, and edges reference node **ids**, never mutable node objects. Coordinates represent node centers. Use `data` objects for application-specific JSON metadata.
|
|
93
|
+
|
|
94
|
+
| Node | Meaning |
|
|
95
|
+
| -------------------------------- | ------------------------------------------------------------- |
|
|
96
|
+
| `start` | One entry, one output; exactly one per executable document |
|
|
97
|
+
| `task`, `mode: 'sync'` (default) | A simulated operation that advances immediately |
|
|
98
|
+
| `task`, `mode: 'async'` | Wait for `complete(tokenId, patch)` from the host |
|
|
99
|
+
| `wait`, `signal` | Wait for `signal(tokenId, signalName, patch)` from the host |
|
|
100
|
+
| `decision` | Take one matching conditional branch or its required fallback |
|
|
101
|
+
| `fork`, `join: id` | Activate all output branches; names the matching join |
|
|
102
|
+
| `join`, `fork: id` | Wait for every branch of the corresponding fork activation |
|
|
103
|
+
| `end` | Finish this token; no outputs |
|
|
104
|
+
|
|
105
|
+
Normal nodes have exactly one output. Multiple inputs on a normal node **do not synchronize**: each incoming token activates it separately. Use an explicit join to synchronize. Parallel regions must be properly nested; branches cannot bypass their join or enter a join from outside its fork. Multiple end nodes and sequential loops are supported.
|
|
106
|
+
|
|
107
|
+
### Conditions
|
|
108
|
+
|
|
109
|
+
Conditions are a data AST, not JavaScript strings. No `eval`, expression compiler, or arbitrary property traversal. Paths use arrays, including nested properties and array indices as strings.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import type { Condition } from "@xxrandr/conecta-diagram/core";
|
|
113
|
+
|
|
114
|
+
const condition: Condition = {
|
|
115
|
+
op: "all",
|
|
116
|
+
conditions: [
|
|
117
|
+
{ op: "gte", path: ["order", "total"], value: 100 },
|
|
118
|
+
{ op: "eq", path: ["approval", "accepted"], value: true },
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
store.addEdge({
|
|
123
|
+
id: "accepted",
|
|
124
|
+
source: "decision",
|
|
125
|
+
target: "success",
|
|
126
|
+
label: "Approved",
|
|
127
|
+
condition: { op: "eq", path: ["approved"], value: true },
|
|
128
|
+
priority: 0,
|
|
129
|
+
});
|
|
130
|
+
store.addEdge({
|
|
131
|
+
id: "fallback",
|
|
132
|
+
source: "decision",
|
|
133
|
+
target: "rejected",
|
|
134
|
+
label: "Otherwise",
|
|
135
|
+
default: true,
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Supported operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `exists`, `all`, `any`, `not`. Equality is structural JSON equality, with no type coercion; numeric ordering requires numbers. Missing paths make comparisons false, including `neq`. `exists` is true for explicit `null`. Empty `all` is true, empty `any` is false. Only own properties are resolved.
|
|
140
|
+
|
|
141
|
+
Each decision needs exactly one unconditional default and conditions on all other outputs. If several conditions match, lower `priority` wins (default `0`), then lexicographic edge id. A fallback cannot also have a condition.
|
|
142
|
+
|
|
143
|
+
`visibleWhen` on nodes/edges is **presentation only**. `editor.setContext(context)` controls visibility; incident edges of hidden nodes are hidden. Execution always uses the full document. Changing a node's visibility must never silently change the program.
|
|
144
|
+
|
|
145
|
+
## Authoring APIs
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
store.addNode(node);
|
|
149
|
+
store.updateNode(id, (node) => ({ ...node, label: "New label" }));
|
|
150
|
+
store.moveNode(id, { x: 100, y: 200 });
|
|
151
|
+
store.removeNode(id); // Also removes incident edges
|
|
152
|
+
store.addEdge(edge);
|
|
153
|
+
store.updateEdge(id, (edge) => ({ ...edge, label: "Yes" }));
|
|
154
|
+
store.removeEdge(id);
|
|
155
|
+
store.replace(document);
|
|
156
|
+
store.undo();
|
|
157
|
+
store.redo();
|
|
158
|
+
store.canUndo;
|
|
159
|
+
store.canRedo;
|
|
160
|
+
|
|
161
|
+
store.transaction("insert-branch", (draft) => {
|
|
162
|
+
// Perform several related mutations on a detached draft.
|
|
163
|
+
draft.nodes.push(newNode);
|
|
164
|
+
draft.edges.push(newEdge);
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Transactions are synchronous and atomic. Invalid changes throw `FlowError` without changing history or publishing an event. Unchanged transactions do not notify. Snapshots are deeply frozen and stable until the next edit, suitable for external-store integration. Inputs are copied and undo history is bounded (default 100). Dragging is view-only and does not create a transaction. Async/nested transactions and edits from inside notification callbacks are rejected. Use `onListenerError` in store options to observe subscriber failures without corrupting a committed edit.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import {
|
|
172
|
+
validateFlow,
|
|
173
|
+
parseFlow,
|
|
174
|
+
serializeFlow,
|
|
175
|
+
layoutFlow,
|
|
176
|
+
} from "@xxrandr/conecta-diagram/core";
|
|
177
|
+
|
|
178
|
+
const draftIssues = validateFlow(value, { mode: "draft" });
|
|
179
|
+
const executionIssues = validateFlow(value); // Default: executable
|
|
180
|
+
const document = parseFlow(importedJson); // Draft validation; unknown versions rejected
|
|
181
|
+
const exportedJson = serializeFlow(document);
|
|
182
|
+
store.replace(layoutFlow(document, { direction: "right" }));
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Drafts may be incomplete. Draft validation still requires well-formed JSON, valid node/condition shapes, unique ids, and non-dangling edges. Executable validation also checks routing, entry/exit rules, structured parallel regions, reachability warnings, and routes to an end. Errors contain structured diagnostics (`code`, `path`, `severity`, `message`). Reachability/termination of arbitrary conditions cannot be proven statically; loops must be bounded at runtime.
|
|
186
|
+
|
|
187
|
+
## Simulations and external runtimes
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { FlowSimulator } from "@xxrandr/conecta-diagram/core";
|
|
191
|
+
|
|
192
|
+
const simulation = new FlowSimulator(store.getSnapshot(), { approved: false });
|
|
193
|
+
let result = simulation.run(1000);
|
|
194
|
+
editor.setExecution(result.snapshot);
|
|
195
|
+
|
|
196
|
+
// Async work is deliberately not executed by this library.
|
|
197
|
+
const pendingTask = result.snapshot.tokens.find(
|
|
198
|
+
(token) => token.status === "async",
|
|
199
|
+
);
|
|
200
|
+
if (pendingTask) simulation.complete(pendingTask.id, { result: "ok" });
|
|
201
|
+
|
|
202
|
+
result = simulation.run();
|
|
203
|
+
const pendingInput = result.snapshot.tokens.find(
|
|
204
|
+
(token) => token.status === "signal",
|
|
205
|
+
);
|
|
206
|
+
if (pendingInput) {
|
|
207
|
+
// Your application collects a response elsewhere, then delivers it here.
|
|
208
|
+
simulation.signal(pendingInput.id, "approval.received", { approved: true });
|
|
209
|
+
}
|
|
210
|
+
editor.setExecution(simulation.run().snapshot);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- `step()` processes one ready token. `run(maxSteps)` stops on a wait, completion, failure, cancellation, or step budget. A `step-limit` result is resumable, not a failure.
|
|
214
|
+
- Async completion and signals target **activation token ids**, not node ids. This prevents one response from completing unrelated loop/parallel activations. Wrong signals, wrong token states, and duplicate responses are rejected atomically.
|
|
215
|
+
- Fork instances have correlation frames; nested and repeated forks do not mix their join arrivals. Ready tokens run FIFO to avoid starving other branches.
|
|
216
|
+
- Context is one shared JSON object. Completion/signal patches are shallow top-level merges. Concurrent results are applied in the order the host submits them; use separate context keys for independent branches.
|
|
217
|
+
- `fail(tokenId, message)` fails the run; `cancel()` terminates it. There are no implicit retries, side effects, clocks, promises, or network requests.
|
|
218
|
+
- A simulator captures an immutable document revision. Later edits do not rewrite an active run. Create a new simulator for a new plan.
|
|
219
|
+
- Snapshots include tokens, completed node ids, traversed edge ids, bounded ordered events, context and step count. `traceLimit` defaults to 1000, `maxTokens` to 10000. Snapshots are observation data, not durable checkpoints to restore.
|
|
220
|
+
- Feed snapshots to `editor.setExecution(snapshot)` to visualize local simulations. For runner observations, use `RunnerDisplay` or supply an `ExecutionOverlay` directly. `setExecution(null)` clears the overlay. Rendering never mutates the plan.
|
|
221
|
+
|
|
222
|
+
## Task actions, decisions and runner observations
|
|
223
|
+
|
|
224
|
+
Task/decision nodes can carry validated `action` previews for API, database, file/transfer, process/Python, SSH, SFTP, mail, messaging, event, cancellation and generic extension families. `signals` describes displayed IN/OUT names. Decision `decision.expression` is a code preview, not executable logic. Photon cards show operation details and observed status; Assemble is the only node/global editing surface, and Trace owns execution detail.
|
|
225
|
+
|
|
226
|
+
See **[Runner display guide](docs/runner-display.md)** for the types, protobuf mapping boundary, advertised capabilities, uint64 result sequences, independent command/file-chunk finality, and safe result projection. No generated protocol client or real API integration is included. Code/SQL/requests are never executed by the diagram. The demo replays fake runner events/signals rather than asking the user to execute tasks manually.
|
|
227
|
+
|
|
228
|
+
The demo uses one dockable **DevTools** section with Assemble, Trace and Performance inspection tabs. The old fixed DETAILS and bottom-console shells are removed. DevTools docks bottom/right/left or opens in a real synchronized window; pane sizes and light/dark preferences persist. See **[Photon + DevTools refactor](docs/devtools-refactor.md)** for the complete architecture, unchanged replay call path, sync protocol and acceptance checks.
|
|
229
|
+
|
|
230
|
+
## SVG interaction
|
|
231
|
+
|
|
232
|
+
Drag nodes, drag empty space to pan, scroll to zoom, click nodes/edges to select, and double-click a node to open a host-provided inspector. Keyboard: Enter/Space selects, arrows move a selected node (Shift = larger step), Delete removes, Ctrl/Cmd+Z undoes, Ctrl/Cmd+Shift+Z or Ctrl/Cmd+Y redoes, C starts a connection, Escape cancels. Choose a target node to finish the connection. With an edge selected, R reconnects its target (Shift+R reconnects its source). `beginReconnection(edgeId, endpoint)` rewires in one undoable edit while preserving edge id, condition and metadata. Interactive edges are draft edges; configure their conditions/defaults in your inspector before executing.
|
|
233
|
+
|
|
234
|
+
The library does not impose a toolbar, modal, global side panel, or input form. Create nodes and edit properties through the store using your framework's components. `readOnly: true` disables planning edits but retains selection, view-only node movement, pan/zoom and overlays; it is a UI mode, not an authorization boundary for the store.
|
|
235
|
+
|
|
236
|
+
Adapter API: `select`, `getSelection`, `beginConnection`, `beginReconnection`, `cancelConnection`, `setContext`, `setExecution`, `getViewport`, `setViewport`, `animateViewport`, `zoomBy`, `fitToView`, `resize`, `exportSvg`, `destroy`, plus the placement APIs below. Selection callbacks receive `{ type: 'node' | 'edge' | 'global', id }` or `null`. Viewport changes do not enter document history. Coordinate units follow the host SVG's viewBox; update that viewBox alongside `resize(width, height)` if your host uses one.
|
|
237
|
+
|
|
238
|
+
Import `@xxrandr/conecta-diagram/photon.css` with the renderer. Its light/dark token blocks use the Photon palette from `docs/photon-template.html`. Set `html[data-theme="light" | "dark"]`; the demo persists `wf-theme`. Node cards use the template's real `.flow-node` structure inside SVG foreignObjects, including category stripe, signals, preview and footer. No extra theme tokens were added. Groups expose `data-node-id`/`data-state`, edges expose `data-edge-id`. All user-provided text is inserted safely through DOM APIs. `destroy()` removes only instance-owned content/listeners.
|
|
239
|
+
|
|
240
|
+
Diagram globals are optional `FlowDocument.globals` values with explicit `array`, `boolean`, `integer` or `json` types. `FlowStore.addGlobal/updateGlobal/removeGlobal` validate and preserve undo history. `orderedFlowItems()` pins globals before nodes/connections in recorded creation order. Globals are not new workflow nodes and have no implicit reference/execution behavior.
|
|
241
|
+
|
|
242
|
+
## D3 placement and motion
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
const editor = new SvgFlowEditor(svgElement, store, {
|
|
246
|
+
force: {
|
|
247
|
+
anchorStrength: 0.12,
|
|
248
|
+
maxDisplacement: 200,
|
|
249
|
+
fixedNodeIds: ["start"],
|
|
250
|
+
},
|
|
251
|
+
motion: "auto", // Honors prefers-reduced-motion; also 'full' or 'reduced'
|
|
252
|
+
animationDuration: 180, // Milliseconds; 0 disables authored-position tweens
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Physics starts automatically; no placement button or saving step is required.
|
|
256
|
+
editor.getNodePositions(); // Detached map of currently displayed coordinates
|
|
257
|
+
editor.moveNodeView("start", { x: 200, y: 150 }); // Local visual pin, not a plan edit
|
|
258
|
+
editor.resetPlacement(); // Clear local pins and restore the native initial placement
|
|
259
|
+
editor.fitToView(50, true); // Fit displayed positions with an animated camera
|
|
260
|
+
editor.animateViewport({ x: 20, y: 30, scale: 1.5 }, 250);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Force placement is **native/default**; an optional `force` object tunes it, and `force: false` is available for static/testing views. D3 operates on detached mutable physics nodes and cloned link endpoints. It cannot mutate frozen plans, stream runtime commands, or flood undo history. Initial-position springs, link springs, charge and quadtree-backed rectangular collisions gently arrange nodes rather than globally recentering the plan. Existing `position` values are initial seeds, not persisted drag coordinates. `maxDisplacement` bounds drift around each seed; dense/pinned graphs may still overlap. This is gentle placement, not a guaranteed nonoverlap/obstacle-routing solver.
|
|
264
|
+
|
|
265
|
+
Dragging/arrow keys pin the displayed node while neighbors relax. Everything remains local to the view: no position saving, plan mutation or history entries. `commitPlacement()` and the old placement controls have been removed. Optional programmatic `relax()`, `stopForce()` and `settlePlacement()` remain available for compatibility/controlled rendering, but normal users never need them. Read-only views can be arranged without rewiring edges. Geometry ticks update transforms, paths and labels without replacing group elements. Keyed D3 joins preserve group identity/focus; motion uses D3 timers, interpolation and easing.
|
|
266
|
+
|
|
267
|
+
With reduced motion, camera/position tweens are immediate and a requested force relaxation settles synchronously for 120 ticks rather than running an animated simulation. Automatic preference changes stop ongoing animations. Metadata, selection and execution-overlay updates do not reheat physics; topology/authored-coordinate edits do. `destroy()` stops force and animation timers and removes listeners, including the motion-preference listener.
|
|
268
|
+
|
|
269
|
+
For headless D3 placement use the dedicated entry (no DOM required):
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
import { ForcePlacement } from "@xxrandr/conecta-diagram/d3";
|
|
273
|
+
const placement = new ForcePlacement(document, { maxDisplacement: 150 });
|
|
274
|
+
const positions = placement.tick(120); // Constructor is stopped; same inputs/ticks are deterministic
|
|
275
|
+
placement.destroy();
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`ForcePlacement` also exposes `update`, `getPositions`, `subscribe`, `pin`, `restart`, `stop`, and idempotent `destroy`. Tick counts are bounded to 1–10000 per call. D3 modules are bundled into the presentation build so CommonJS and the static browser demo work without bare ESM imports; the `/core` build remains separate.
|
|
279
|
+
|
|
280
|
+
## Java execution API and signal-stream plan
|
|
281
|
+
|
|
282
|
+
See **[Java runtime integration plan](docs/java-runtime-integration.md)** for the proposed contracts and phased implementation. It covers task/decision signal bindings, immutable revision ownership, Java REST endpoints, snapshot + SSE projection, durable replay cursors, reconnect/resnapshot handling, command idempotency, authorization and concurrency tests. These endpoints and the production streaming client are **planned, not implemented**. The diagram remains the planner/viewer; Java remains the executor and the host website owns signal forms and network transport.
|
|
283
|
+
|
|
284
|
+
## React and Angular
|
|
285
|
+
|
|
286
|
+
React: keep the store outside the render cycle (or create it once with `useState`), mount in an effect, destroy in cleanup. The imperative adapter does not require React to reconcile SVG children.
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
function FlowCanvas({ store }: { store: FlowStore }) {
|
|
290
|
+
const ref = useRef<SVGSVGElement>(null);
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
const editor = new SvgFlowEditor(ref.current!, store, {
|
|
293
|
+
width: 1000,
|
|
294
|
+
height: 600,
|
|
295
|
+
});
|
|
296
|
+
editor.fitToView();
|
|
297
|
+
return () => editor.destroy();
|
|
298
|
+
}, [store]);
|
|
299
|
+
return <svg ref={ref} viewBox="0 0 1000 600" width="100%" height="600" />;
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
For inspector state use `useSyncExternalStore` with `callback => store.subscribe(() => callback())` and `() => store.getSnapshot()` (including an appropriate server snapshot). Angular: instantiate in `ngAfterViewInit` using `@ViewChild`, then call `destroy()` in `ngOnDestroy`; bridge subscriptions into your component's preferred signals/change-detection mechanism. No framework-specific wrapper is required.
|
|
304
|
+
|
|
305
|
+
## Migration from the prototype
|
|
306
|
+
|
|
307
|
+
The original root-level D3 files and their existing local changes are preserved. They are **not the package entry point**, are excluded from the new TypeScript build, and are not published. New development lives in `src/`; the old prototype is not a compatibility layer.
|
|
308
|
+
|
|
309
|
+
- `BaseNode` instances become plain `FlowNode` data: lowercase `type`, `{ x, y }` becomes `position`, `properties` becomes JSON `data`.
|
|
310
|
+
- `BaseLink.originParent/finalParent` become `source/target` ids.
|
|
311
|
+
- Replace generic `Gateway` nodes with explicit `decision`, `fork` or `join` nodes. Branch labels alone are not execution conditions. Set conditional expressions and one fallback explicitly.
|
|
312
|
+
- The prototype's implicit fan-out becomes an explicit fork/join pair. Do not infer these semantics automatically from geometry.
|
|
313
|
+
- Replace `D3FlowDiagram` with `FlowStore` + `SvgFlowEditor`. Subscribe to store changes instead of mutable node arrays; always destroy the adapter when unmounting.
|
|
314
|
+
- Document-global menus are intentionally not carried forward. D3 force placement is a native, isolated presentation controller. Coordinates in old documents are initial seeds; dragging does not write back to them.
|
|
315
|
+
|
|
316
|
+
## Current boundaries / release gate
|
|
317
|
+
|
|
318
|
+
This release provides a tested core and a D3-assisted SVG authoring adapter, not every feature of an established visual IDE. It does **not** yet include obstacle-avoiding edge routing, multi-selection, signal ports/bindings UI, groups/subflows, collaboration, persisted simulation checkpoints, cancellation scopes, retries/timeouts, inclusive/event gateways, BPMN import/export, or built-in React/Angular components. Static layout is deterministic breadth-first placement; force placement is a bounded, approximate visual aid. Multiple edges between the same pair currently share a route. Group elements use keyed joins and physics ticks update geometry only, but large-graph performance still needs benchmarking.
|
|
319
|
+
|
|
320
|
+
Before declaring production maturity: exercise framework integrations, cross-browser accessibility and visual regressions, add large-graph performance tests, and review the public API against real execution-system requirements. Automated coverage exercises core editing/simulation, action previews, runner-result correlation/finality/uint64 handling, signals, D3 placement/motion, SVG lifecycle, and package imports. Browser DOM integration and exhaustive visual/accessibility checks are not yet automated.
|