@zenwave360/vistas-eventflow 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FLOWVIEWMODEL_EXAMPLES.md +896 -0
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/EventFlowVisualizer.d.ts +25 -0
- package/dist/EventFlowVisualizer.d.ts.map +1 -0
- package/dist/ServiceMapVisualizer.d.ts +14 -0
- package/dist/ServiceMapVisualizer.d.ts.map +1 -0
- package/dist/components/CausationEdge.d.ts +8 -0
- package/dist/components/CausationEdge.d.ts.map +1 -0
- package/dist/components/CommandNode.d.ts +10 -0
- package/dist/components/CommandNode.d.ts.map +1 -0
- package/dist/components/EndNode.d.ts +10 -0
- package/dist/components/EndNode.d.ts.map +1 -0
- package/dist/components/EventNode.d.ts +10 -0
- package/dist/components/EventNode.d.ts.map +1 -0
- package/dist/components/OutcomeNode.d.ts +7 -0
- package/dist/components/OutcomeNode.d.ts.map +1 -0
- package/dist/components/PolicyNode.d.ts +16 -0
- package/dist/components/PolicyNode.d.ts.map +1 -0
- package/dist/components/ServiceGroupNode.d.ts +10 -0
- package/dist/components/ServiceGroupNode.d.ts.map +1 -0
- package/dist/components/ServiceMapNode.d.ts +8 -0
- package/dist/components/ServiceMapNode.d.ts.map +1 -0
- package/dist/components/StartNode.d.ts +10 -0
- package/dist/components/StartNode.d.ts.map +1 -0
- package/dist/components/SystemGroupNode.d.ts +7 -0
- package/dist/components/SystemGroupNode.d.ts.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +645 -0
- package/dist/index.js.map +1 -0
- package/dist/labelFormatting.d.ts +6 -0
- package/dist/labelFormatting.d.ts.map +1 -0
- package/dist/styles.css +1 -0
- package/dist/types/FlowViewModel-updated.d.ts +114 -0
- package/dist/types/FlowViewModel-updated.d.ts.map +1 -0
- package/dist/types/FlowViewModel.d.ts +124 -0
- package/dist/types/FlowViewModel.d.ts.map +1 -0
- package/dist/types/ServiceViewModel.d.ts +64 -0
- package/dist/types/ServiceViewModel.d.ts.map +1 -0
- package/event-flow-architecture-and-contracts.md +1067 -0
- package/package.json +54 -0
|
@@ -0,0 +1,1067 @@
|
|
|
1
|
+
# ZFL Event Flow Renderer - Architecture and Contracts
|
|
2
|
+
|
|
3
|
+
## 1. Purpose
|
|
4
|
+
|
|
5
|
+
The ZFL Event Flow renderer is the visualization layer for event-flow diagrams in the ZFL ecosystem.
|
|
6
|
+
|
|
7
|
+
Its job is to take a pre-built `FlowViewModel` JSON document and render it as an interactive graph in a browser or embedded webview. It is intentionally downstream from parsing and semantic analysis.
|
|
8
|
+
|
|
9
|
+
In practical terms, the renderer exists to let a host such as the IntelliJ plugin:
|
|
10
|
+
|
|
11
|
+
- display event-flow topology
|
|
12
|
+
- let users inspect nodes and edges
|
|
13
|
+
- let users click graph elements and map them back to source locations
|
|
14
|
+
- optionally let users drag nodes and persist updated layout metadata
|
|
15
|
+
|
|
16
|
+
The renderer is not the source of truth for ZFL semantics. The source of truth remains the parser/language side that produces the graph model.
|
|
17
|
+
|
|
18
|
+
## 2. Scope of responsibility
|
|
19
|
+
|
|
20
|
+
### What the renderer owns
|
|
21
|
+
|
|
22
|
+
- Rendering a `FlowViewModel` into a node/edge graph
|
|
23
|
+
- Visual mapping of ZFL event-flow concepts to graph shapes and colors
|
|
24
|
+
- Local interaction inside the canvas
|
|
25
|
+
- Emitting UI-level interaction callbacks
|
|
26
|
+
- Returning updated node positions when drag-based editing is enabled
|
|
27
|
+
- Tolerating partially laid-out models by skipping nodes without layout coordinates
|
|
28
|
+
|
|
29
|
+
### What the renderer does not own
|
|
30
|
+
|
|
31
|
+
- Parsing `.zfl` source text
|
|
32
|
+
- Resolving imports, models, or semantic correctness
|
|
33
|
+
- Building the event-flow graph from source code
|
|
34
|
+
- Computing graph layout
|
|
35
|
+
- Source navigation policy
|
|
36
|
+
- Host-side message transport
|
|
37
|
+
- Persistence of layout or selection state
|
|
38
|
+
|
|
39
|
+
### Boundary with parser
|
|
40
|
+
|
|
41
|
+
The parser owns source-to-model transformation. It decides what nodes and edges exist, their semantic type, labels, IDs, and source references.
|
|
42
|
+
|
|
43
|
+
The renderer assumes that parsing is already complete and consumes JSON only.
|
|
44
|
+
|
|
45
|
+
### Boundary with language server
|
|
46
|
+
|
|
47
|
+
The language server owns project awareness, file resolution, diagnostics, symbol/source navigation, and any server-side generation of `FlowViewModel`.
|
|
48
|
+
|
|
49
|
+
The renderer does not call the language server directly. A host must fetch the model from the language server and pass it into the renderer.
|
|
50
|
+
|
|
51
|
+
### Boundary with IntelliJ plugin
|
|
52
|
+
|
|
53
|
+
The IntelliJ plugin owns embedding, message transport, model loading, host commands, selection synchronization with other IntelliJ UI, error presentation outside the canvas, and persistence.
|
|
54
|
+
|
|
55
|
+
The renderer owns only the in-webview graph UI.
|
|
56
|
+
|
|
57
|
+
## 3. Technology stack
|
|
58
|
+
|
|
59
|
+
### Core stack
|
|
60
|
+
|
|
61
|
+
- React 18
|
|
62
|
+
- TypeScript 5
|
|
63
|
+
- React Flow 11
|
|
64
|
+
- Vite 7 for dev/build
|
|
65
|
+
|
|
66
|
+
### Rendering model
|
|
67
|
+
|
|
68
|
+
The renderer is a React component named `EventFlowVisualizer`.
|
|
69
|
+
|
|
70
|
+
It receives a full `FlowViewModel` object as props and transforms it into React Flow nodes and edges. Rendering is declarative: whenever the host replaces `flowViewModel`, the graph is re-derived from that new model.
|
|
71
|
+
|
|
72
|
+
There is no internal automatic layout engine. The renderer expects absolute node positions and dimensions to already be present when the host wants a visible graph.
|
|
73
|
+
|
|
74
|
+
### Embedding assumptions
|
|
75
|
+
|
|
76
|
+
The current implementation is a React component library, not a standalone protocol-driven web app.
|
|
77
|
+
|
|
78
|
+
That means:
|
|
79
|
+
|
|
80
|
+
- the implemented public contract today is the component prop/callback API
|
|
81
|
+
- a host-facing message bridge must be supplied by the embedding layer
|
|
82
|
+
- the IntelliJ plugin should treat the renderer as a UI component wrapped by a small adapter
|
|
83
|
+
|
|
84
|
+
### Browser/webview assumptions
|
|
85
|
+
|
|
86
|
+
- Runs in a DOM environment
|
|
87
|
+
- Requires standard browser APIs used by React, React DOM, and React Flow
|
|
88
|
+
- Assumes pointer/mouse interaction is available
|
|
89
|
+
- Assumes the host gives the root container a real width and height
|
|
90
|
+
- Assumes modern JavaScript support roughly aligned with `ES2020`
|
|
91
|
+
|
|
92
|
+
For JCEF/IntelliJ, the host should provide a container that fills the available panel space and avoid mounting the renderer into a zero-height root.
|
|
93
|
+
|
|
94
|
+
## 4. Input contract
|
|
95
|
+
|
|
96
|
+
### Canonical top-level shape
|
|
97
|
+
|
|
98
|
+
The renderer consumes a `FlowViewModel` object with this shape:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"schema": "zfl.eventflow.view@1",
|
|
103
|
+
"nodes": [],
|
|
104
|
+
"edges": [],
|
|
105
|
+
"layout": null,
|
|
106
|
+
"bounds": null,
|
|
107
|
+
"systemGroups": null
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Top-level fields
|
|
112
|
+
|
|
113
|
+
- `schema: string`
|
|
114
|
+
Must currently be `zfl.eventflow.view@1`.
|
|
115
|
+
- `nodes: FlowNode[]`
|
|
116
|
+
Required. May be empty.
|
|
117
|
+
- `edges: FlowEdge[]`
|
|
118
|
+
Required. May be empty.
|
|
119
|
+
- `layout: LayoutMetadata | null`
|
|
120
|
+
Optional in practice, but expected for a fully laid-out graph.
|
|
121
|
+
- `bounds: FlowBounds | null`
|
|
122
|
+
Optional in practice. Used as metadata only.
|
|
123
|
+
- `systemGroups: FlowSystemGroupView[] | null`
|
|
124
|
+
Present in the schema but ignored by the current renderer.
|
|
125
|
+
|
|
126
|
+
### Node fields
|
|
127
|
+
|
|
128
|
+
Each node has this shape:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"id": "command:renewSubscription",
|
|
133
|
+
"type": "COMMAND",
|
|
134
|
+
"label": "renewSubscription",
|
|
135
|
+
"system": "Subscription",
|
|
136
|
+
"service": "SubscriptionService",
|
|
137
|
+
"sourceRef": {
|
|
138
|
+
"file": "subscription-flow.zfl",
|
|
139
|
+
"line": 3,
|
|
140
|
+
"column": 1
|
|
141
|
+
},
|
|
142
|
+
"position": {
|
|
143
|
+
"x": 220,
|
|
144
|
+
"y": 160
|
|
145
|
+
},
|
|
146
|
+
"dimensions": {
|
|
147
|
+
"width": 180,
|
|
148
|
+
"height": 64
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Required node fields:
|
|
154
|
+
|
|
155
|
+
- `id: string`
|
|
156
|
+
Unique in the graph. Edges reference this ID.
|
|
157
|
+
- `type: "START" | "COMMAND" | "EVENT" | "POLICY" | "END"`
|
|
158
|
+
- `label: string`
|
|
159
|
+
- `system: string | null`
|
|
160
|
+
- `service: string | null`
|
|
161
|
+
- `sourceRef: SourceRef`
|
|
162
|
+
- `position: { x: number, y: number } | null`
|
|
163
|
+
- `dimensions: { width: number, height: number } | null`
|
|
164
|
+
|
|
165
|
+
### Edge fields
|
|
166
|
+
|
|
167
|
+
Each edge has this shape:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"id": "e4",
|
|
172
|
+
"source": "policy:SubscriptionRenewed:chargePayment",
|
|
173
|
+
"target": "command:chargePayment",
|
|
174
|
+
"type": "TRIGGER",
|
|
175
|
+
"label": null,
|
|
176
|
+
"sourceRef": null
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Required edge fields:
|
|
181
|
+
|
|
182
|
+
- `id: string`
|
|
183
|
+
- `source: string`
|
|
184
|
+
Must match a node ID.
|
|
185
|
+
- `target: string`
|
|
186
|
+
Must match a node ID.
|
|
187
|
+
- `type: "CAUSATION" | "TRIGGER" | "CONDITIONAL" | "ERROR"`
|
|
188
|
+
- `label: string | null`
|
|
189
|
+
- `sourceRef: SourceRef | null`
|
|
190
|
+
|
|
191
|
+
### Node type/category expectations
|
|
192
|
+
|
|
193
|
+
The current visual mapping is fixed:
|
|
194
|
+
|
|
195
|
+
- `START`: start node styling
|
|
196
|
+
- `COMMAND`: command styling
|
|
197
|
+
- `EVENT`: event styling
|
|
198
|
+
- `POLICY`: policy styling
|
|
199
|
+
- `END`: end-state styling
|
|
200
|
+
|
|
201
|
+
Unknown node types are not part of the contract. Hosts should not send them.
|
|
202
|
+
|
|
203
|
+
### Styling/category expectations
|
|
204
|
+
|
|
205
|
+
The renderer derives styling from semantic types, not from ad hoc style fields in the JSON.
|
|
206
|
+
|
|
207
|
+
Current expectations:
|
|
208
|
+
|
|
209
|
+
- node shape and color come from `node.type`
|
|
210
|
+
- edge color and dash pattern come from `edge.type`
|
|
211
|
+
- `edge.label` is rendered if non-null
|
|
212
|
+
- `system` and `service` are used as tooltip/title metadata, not swimlanes
|
|
213
|
+
- policy labels may be visually split into `when ... do ...` segments if they match that text pattern
|
|
214
|
+
- labels may be humanized for display only; this does not change the underlying JSON
|
|
215
|
+
|
|
216
|
+
There is currently no contract for per-node custom colors, icons, CSS classes, or arbitrary metadata blobs.
|
|
217
|
+
|
|
218
|
+
### Required source metadata
|
|
219
|
+
|
|
220
|
+
`sourceRef` is required on every node and should be treated as required for integration.
|
|
221
|
+
|
|
222
|
+
Shape:
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"file": "subscription-flow.zfl",
|
|
227
|
+
"line": 7,
|
|
228
|
+
"column": 1
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Semantics:
|
|
233
|
+
|
|
234
|
+
- `file` is a host-resolvable path or file identifier
|
|
235
|
+
- `line` is 1-based
|
|
236
|
+
- `column` is 1-based
|
|
237
|
+
|
|
238
|
+
Edge `sourceRef` may be `null`.
|
|
239
|
+
|
|
240
|
+
### Optional layout metadata
|
|
241
|
+
|
|
242
|
+
Top-level `layout`:
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"engine": "elk-layered",
|
|
247
|
+
"direction": "LR",
|
|
248
|
+
"rankSpacing": 180,
|
|
249
|
+
"nodeSpacing": 80
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Top-level `bounds`:
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"x": 0,
|
|
258
|
+
"y": 0,
|
|
259
|
+
"width": 2200,
|
|
260
|
+
"height": 420
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Current behavior:
|
|
265
|
+
|
|
266
|
+
- if a node is missing `position` or `dimensions`, that node is skipped
|
|
267
|
+
- edges whose source or target node is skipped are also skipped
|
|
268
|
+
- `systemGroups` is ignored
|
|
269
|
+
|
|
270
|
+
### Example input JSON
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"schema": "zfl.eventflow.view@1",
|
|
275
|
+
"nodes": [
|
|
276
|
+
{
|
|
277
|
+
"id": "event:CustomerRequestsSubscriptionRenewal",
|
|
278
|
+
"type": "START",
|
|
279
|
+
"label": "CustomerRequestsSubscriptionRenewal",
|
|
280
|
+
"system": null,
|
|
281
|
+
"service": null,
|
|
282
|
+
"sourceRef": {
|
|
283
|
+
"file": "subscription-flow.zfl",
|
|
284
|
+
"line": 1,
|
|
285
|
+
"column": 1
|
|
286
|
+
},
|
|
287
|
+
"position": {
|
|
288
|
+
"x": 40,
|
|
289
|
+
"y": 140
|
|
290
|
+
},
|
|
291
|
+
"dimensions": {
|
|
292
|
+
"width": 100,
|
|
293
|
+
"height": 100
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"id": "command:renewSubscription",
|
|
298
|
+
"type": "COMMAND",
|
|
299
|
+
"label": "renewSubscription",
|
|
300
|
+
"system": "Subscription",
|
|
301
|
+
"service": "SubscriptionService",
|
|
302
|
+
"sourceRef": {
|
|
303
|
+
"file": "subscription-flow.zfl",
|
|
304
|
+
"line": 3,
|
|
305
|
+
"column": 1
|
|
306
|
+
},
|
|
307
|
+
"position": {
|
|
308
|
+
"x": 220,
|
|
309
|
+
"y": 160
|
|
310
|
+
},
|
|
311
|
+
"dimensions": {
|
|
312
|
+
"width": 180,
|
|
313
|
+
"height": 64
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
],
|
|
317
|
+
"edges": [
|
|
318
|
+
{
|
|
319
|
+
"id": "e1",
|
|
320
|
+
"source": "event:CustomerRequestsSubscriptionRenewal",
|
|
321
|
+
"target": "command:renewSubscription",
|
|
322
|
+
"type": "TRIGGER",
|
|
323
|
+
"label": null,
|
|
324
|
+
"sourceRef": null
|
|
325
|
+
}
|
|
326
|
+
],
|
|
327
|
+
"layout": {
|
|
328
|
+
"engine": "elk-layered",
|
|
329
|
+
"direction": "LR",
|
|
330
|
+
"rankSpacing": 180,
|
|
331
|
+
"nodeSpacing": 80
|
|
332
|
+
},
|
|
333
|
+
"bounds": {
|
|
334
|
+
"x": 0,
|
|
335
|
+
"y": 0,
|
|
336
|
+
"width": 1200,
|
|
337
|
+
"height": 400
|
|
338
|
+
},
|
|
339
|
+
"systemGroups": null
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## 5. Event/output contract
|
|
344
|
+
|
|
345
|
+
### Important distinction
|
|
346
|
+
|
|
347
|
+
Current implementation:
|
|
348
|
+
|
|
349
|
+
- implemented callbacks are `onNodeClick`, `onSelectionChange`, and `onFlowViewModelChange`
|
|
350
|
+
- there is no built-in `window.postMessage` bridge
|
|
351
|
+
- there is no implemented `webviewReady`, `viewportChanged`, `contextMenuRequested`, or `errorReported` event
|
|
352
|
+
|
|
353
|
+
Recommended host contract:
|
|
354
|
+
|
|
355
|
+
For IntelliJ integration, use a thin wrapper around `EventFlowVisualizer` and normalize its callbacks into explicit host events. The event definitions below are the recommended bridge contract for the embedding host even where the base renderer does not emit them yet.
|
|
356
|
+
|
|
357
|
+
### Event envelope
|
|
358
|
+
|
|
359
|
+
Renderer-to-host events should use a stable envelope:
|
|
360
|
+
|
|
361
|
+
```json
|
|
362
|
+
{
|
|
363
|
+
"channel": "zfl.eventflow",
|
|
364
|
+
"version": 1,
|
|
365
|
+
"type": "nodeSelected",
|
|
366
|
+
"requestId": null,
|
|
367
|
+
"timestamp": "2026-03-14T12:00:00.000Z",
|
|
368
|
+
"payload": {}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Required envelope fields:
|
|
373
|
+
|
|
374
|
+
- `channel: "zfl.eventflow"`
|
|
375
|
+
- `version: 1`
|
|
376
|
+
- `type: string`
|
|
377
|
+
- `payload: object`
|
|
378
|
+
|
|
379
|
+
Optional envelope fields:
|
|
380
|
+
|
|
381
|
+
- `requestId: string | null`
|
|
382
|
+
- `timestamp: string`
|
|
383
|
+
|
|
384
|
+
### `webviewReady`
|
|
385
|
+
|
|
386
|
+
Status: recommended bridge event, not implemented by the base component.
|
|
387
|
+
|
|
388
|
+
Trigger:
|
|
389
|
+
|
|
390
|
+
- emitted once after the wrapper has mounted and is ready to receive host messages
|
|
391
|
+
|
|
392
|
+
Payload required fields:
|
|
393
|
+
|
|
394
|
+
- `rendererVersion: string`
|
|
395
|
+
- `supportedIncomingMessages: string[]`
|
|
396
|
+
- `supportedOutgoingEvents: string[]`
|
|
397
|
+
|
|
398
|
+
Payload optional fields:
|
|
399
|
+
|
|
400
|
+
- `capabilities: object`
|
|
401
|
+
|
|
402
|
+
Example:
|
|
403
|
+
|
|
404
|
+
```json
|
|
405
|
+
{
|
|
406
|
+
"channel": "zfl.eventflow",
|
|
407
|
+
"version": 1,
|
|
408
|
+
"type": "webviewReady",
|
|
409
|
+
"payload": {
|
|
410
|
+
"rendererVersion": "1.0.0",
|
|
411
|
+
"supportedIncomingMessages": [
|
|
412
|
+
"loadModel",
|
|
413
|
+
"refreshModel",
|
|
414
|
+
"selectNodes",
|
|
415
|
+
"highlightNodes",
|
|
416
|
+
"fitToNodes",
|
|
417
|
+
"clearSelection",
|
|
418
|
+
"setTheme"
|
|
419
|
+
],
|
|
420
|
+
"supportedOutgoingEvents": [
|
|
421
|
+
"webviewReady",
|
|
422
|
+
"nodeSelected",
|
|
423
|
+
"selectionChanged",
|
|
424
|
+
"layoutChanged",
|
|
425
|
+
"errorReported"
|
|
426
|
+
],
|
|
427
|
+
"capabilities": {
|
|
428
|
+
"dragLayoutEditing": true,
|
|
429
|
+
"viewportCommands": false,
|
|
430
|
+
"contextMenu": false
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### `nodeSelected`
|
|
437
|
+
|
|
438
|
+
Status: can be produced today by adapting `onNodeClick`.
|
|
439
|
+
|
|
440
|
+
Trigger:
|
|
441
|
+
|
|
442
|
+
- user clicks a node
|
|
443
|
+
|
|
444
|
+
Payload required fields:
|
|
445
|
+
|
|
446
|
+
- `nodeId: string`
|
|
447
|
+
- `nodeType: string`
|
|
448
|
+
- `sourceRef: SourceRef`
|
|
449
|
+
|
|
450
|
+
Payload optional fields:
|
|
451
|
+
|
|
452
|
+
- `label: string`
|
|
453
|
+
- `system: string | null`
|
|
454
|
+
- `service: string | null`
|
|
455
|
+
|
|
456
|
+
Example:
|
|
457
|
+
|
|
458
|
+
```json
|
|
459
|
+
{
|
|
460
|
+
"channel": "zfl.eventflow",
|
|
461
|
+
"version": 1,
|
|
462
|
+
"type": "nodeSelected",
|
|
463
|
+
"payload": {
|
|
464
|
+
"nodeId": "command:renewSubscription",
|
|
465
|
+
"nodeType": "COMMAND",
|
|
466
|
+
"label": "renewSubscription",
|
|
467
|
+
"system": "Subscription",
|
|
468
|
+
"service": "SubscriptionService",
|
|
469
|
+
"sourceRef": {
|
|
470
|
+
"file": "subscription-flow.zfl",
|
|
471
|
+
"line": 3,
|
|
472
|
+
"column": 1
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### `selectionChanged`
|
|
479
|
+
|
|
480
|
+
Status: can be produced today by adapting `onSelectionChange`.
|
|
481
|
+
|
|
482
|
+
Trigger:
|
|
483
|
+
|
|
484
|
+
- user changes selected nodes or edges in the canvas
|
|
485
|
+
|
|
486
|
+
Payload required fields:
|
|
487
|
+
|
|
488
|
+
- `selectedNodeIds: string[]`
|
|
489
|
+
- `selectedEdgeIds: string[]`
|
|
490
|
+
|
|
491
|
+
Payload optional fields:
|
|
492
|
+
|
|
493
|
+
- `primaryNodeId: string | null`
|
|
494
|
+
|
|
495
|
+
Example:
|
|
496
|
+
|
|
497
|
+
```json
|
|
498
|
+
{
|
|
499
|
+
"channel": "zfl.eventflow",
|
|
500
|
+
"version": 1,
|
|
501
|
+
"type": "selectionChanged",
|
|
502
|
+
"payload": {
|
|
503
|
+
"selectedNodeIds": [
|
|
504
|
+
"command:renewSubscription"
|
|
505
|
+
],
|
|
506
|
+
"selectedEdgeIds": [],
|
|
507
|
+
"primaryNodeId": "command:renewSubscription"
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### `viewportChanged`
|
|
513
|
+
|
|
514
|
+
Status: not implemented by the base component.
|
|
515
|
+
|
|
516
|
+
Trigger:
|
|
517
|
+
|
|
518
|
+
- recommended for future use when the wrapper starts listening to React Flow viewport changes
|
|
519
|
+
|
|
520
|
+
Payload required fields:
|
|
521
|
+
|
|
522
|
+
- `x: number`
|
|
523
|
+
- `y: number`
|
|
524
|
+
- `zoom: number`
|
|
525
|
+
|
|
526
|
+
Payload optional fields:
|
|
527
|
+
|
|
528
|
+
- `reason: "user" | "programmatic"`
|
|
529
|
+
|
|
530
|
+
Example:
|
|
531
|
+
|
|
532
|
+
```json
|
|
533
|
+
{
|
|
534
|
+
"channel": "zfl.eventflow",
|
|
535
|
+
"version": 1,
|
|
536
|
+
"type": "viewportChanged",
|
|
537
|
+
"payload": {
|
|
538
|
+
"x": 120,
|
|
539
|
+
"y": 40,
|
|
540
|
+
"zoom": 0.9,
|
|
541
|
+
"reason": "user"
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### `contextMenuRequested`
|
|
547
|
+
|
|
548
|
+
Status: not implemented by the base component.
|
|
549
|
+
|
|
550
|
+
Trigger:
|
|
551
|
+
|
|
552
|
+
- recommended future event on right-click or equivalent context gesture
|
|
553
|
+
|
|
554
|
+
Payload required fields:
|
|
555
|
+
|
|
556
|
+
- `targetType: "node" | "edge" | "canvas"`
|
|
557
|
+
- `clientX: number`
|
|
558
|
+
- `clientY: number`
|
|
559
|
+
|
|
560
|
+
Payload optional fields:
|
|
561
|
+
|
|
562
|
+
- `nodeId: string`
|
|
563
|
+
- `edgeId: string`
|
|
564
|
+
- `sourceRef: SourceRef`
|
|
565
|
+
|
|
566
|
+
Example:
|
|
567
|
+
|
|
568
|
+
```json
|
|
569
|
+
{
|
|
570
|
+
"channel": "zfl.eventflow",
|
|
571
|
+
"version": 1,
|
|
572
|
+
"type": "contextMenuRequested",
|
|
573
|
+
"payload": {
|
|
574
|
+
"targetType": "node",
|
|
575
|
+
"clientX": 522,
|
|
576
|
+
"clientY": 311,
|
|
577
|
+
"nodeId": "event:SubscriptionRenewed",
|
|
578
|
+
"sourceRef": {
|
|
579
|
+
"file": "subscription-flow.zfl",
|
|
580
|
+
"line": 5,
|
|
581
|
+
"column": 1
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### `layoutChanged`
|
|
588
|
+
|
|
589
|
+
Status: can be produced today by adapting `onFlowViewModelChange`.
|
|
590
|
+
|
|
591
|
+
Trigger:
|
|
592
|
+
|
|
593
|
+
- user drags one or more nodes and the renderer emits an updated `FlowViewModel`
|
|
594
|
+
|
|
595
|
+
Payload required fields:
|
|
596
|
+
|
|
597
|
+
- `flowViewModel: FlowViewModel`
|
|
598
|
+
|
|
599
|
+
Payload optional fields:
|
|
600
|
+
|
|
601
|
+
- `changedNodeIds: string[]`
|
|
602
|
+
|
|
603
|
+
Example:
|
|
604
|
+
|
|
605
|
+
```json
|
|
606
|
+
{
|
|
607
|
+
"channel": "zfl.eventflow",
|
|
608
|
+
"version": 1,
|
|
609
|
+
"type": "layoutChanged",
|
|
610
|
+
"payload": {
|
|
611
|
+
"changedNodeIds": [
|
|
612
|
+
"command:renewSubscription"
|
|
613
|
+
],
|
|
614
|
+
"flowViewModel": {
|
|
615
|
+
"schema": "zfl.eventflow.view@1",
|
|
616
|
+
"nodes": [],
|
|
617
|
+
"edges": [],
|
|
618
|
+
"layout": {
|
|
619
|
+
"engine": "elk-layered",
|
|
620
|
+
"direction": "LR",
|
|
621
|
+
"rankSpacing": 180,
|
|
622
|
+
"nodeSpacing": 80
|
|
623
|
+
},
|
|
624
|
+
"bounds": {
|
|
625
|
+
"x": 0,
|
|
626
|
+
"y": 0,
|
|
627
|
+
"width": 1200,
|
|
628
|
+
"height": 400
|
|
629
|
+
},
|
|
630
|
+
"systemGroups": null
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
### `errorReported`
|
|
637
|
+
|
|
638
|
+
Status: recommended bridge event, not implemented by the base component.
|
|
639
|
+
|
|
640
|
+
Trigger:
|
|
641
|
+
|
|
642
|
+
- wrapper cannot parse host message
|
|
643
|
+
- host sends unsupported command
|
|
644
|
+
- wrapper detects invalid model
|
|
645
|
+
|
|
646
|
+
Payload required fields:
|
|
647
|
+
|
|
648
|
+
- `code: string`
|
|
649
|
+
- `message: string`
|
|
650
|
+
|
|
651
|
+
Payload optional fields:
|
|
652
|
+
|
|
653
|
+
- `details: object`
|
|
654
|
+
- `recoverable: boolean`
|
|
655
|
+
|
|
656
|
+
Example:
|
|
657
|
+
|
|
658
|
+
```json
|
|
659
|
+
{
|
|
660
|
+
"channel": "zfl.eventflow",
|
|
661
|
+
"version": 1,
|
|
662
|
+
"type": "errorReported",
|
|
663
|
+
"payload": {
|
|
664
|
+
"code": "INVALID_GRAPH",
|
|
665
|
+
"message": "Graph payload is missing required top-level field 'nodes'.",
|
|
666
|
+
"recoverable": true,
|
|
667
|
+
"details": {
|
|
668
|
+
"field": "nodes"
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
## 6. Host integration model
|
|
675
|
+
|
|
676
|
+
The IntelliJ plugin should wrap the renderer with a very small adapter component and treat it as a controlled view.
|
|
677
|
+
|
|
678
|
+
Recommended interaction model:
|
|
679
|
+
|
|
680
|
+
1. Host creates the webview and waits for `webviewReady`.
|
|
681
|
+
2. Host sends `loadModel` with a full `FlowViewModel`.
|
|
682
|
+
3. Renderer displays the graph from that model.
|
|
683
|
+
4. User interactions inside the graph are emitted back to the host.
|
|
684
|
+
5. If the underlying source changes, the host sends `refreshModel` with a new full `FlowViewModel`.
|
|
685
|
+
6. If the host wants selection/highlight synchronization, it sends explicit commands such as `selectNodes` or `highlightNodes`.
|
|
686
|
+
7. If drag layout editing is enabled and the user moves nodes, the renderer emits `layoutChanged`; the host decides whether to persist or ignore it.
|
|
687
|
+
|
|
688
|
+
Important: the current base component already supports steps 2, 4, and 7 through React props/callbacks. Steps involving message passing require a wrapper.
|
|
689
|
+
|
|
690
|
+
## 7. Message bridge assumptions
|
|
691
|
+
|
|
692
|
+
### Recommended protocol
|
|
693
|
+
|
|
694
|
+
Use a single JSON message envelope in both directions:
|
|
695
|
+
|
|
696
|
+
```json
|
|
697
|
+
{
|
|
698
|
+
"channel": "zfl.eventflow",
|
|
699
|
+
"version": 1,
|
|
700
|
+
"type": "loadModel",
|
|
701
|
+
"requestId": "req-1",
|
|
702
|
+
"payload": {}
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### Host -> renderer messages
|
|
707
|
+
|
|
708
|
+
#### `loadModel`
|
|
709
|
+
|
|
710
|
+
Use when the webview is first populated.
|
|
711
|
+
|
|
712
|
+
```json
|
|
713
|
+
{
|
|
714
|
+
"channel": "zfl.eventflow",
|
|
715
|
+
"version": 1,
|
|
716
|
+
"type": "loadModel",
|
|
717
|
+
"requestId": "req-1",
|
|
718
|
+
"payload": {
|
|
719
|
+
"flowViewModel": {
|
|
720
|
+
"schema": "zfl.eventflow.view@1",
|
|
721
|
+
"nodes": [],
|
|
722
|
+
"edges": [],
|
|
723
|
+
"layout": null,
|
|
724
|
+
"bounds": null,
|
|
725
|
+
"systemGroups": null
|
|
726
|
+
},
|
|
727
|
+
"theme": "light",
|
|
728
|
+
"editable": true,
|
|
729
|
+
"displaySettings": {
|
|
730
|
+
"humanizeLabels": true
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
#### `refreshModel`
|
|
737
|
+
|
|
738
|
+
Use when the host has a replacement graph after reparse/reanalysis.
|
|
739
|
+
|
|
740
|
+
```json
|
|
741
|
+
{
|
|
742
|
+
"channel": "zfl.eventflow",
|
|
743
|
+
"version": 1,
|
|
744
|
+
"type": "refreshModel",
|
|
745
|
+
"payload": {
|
|
746
|
+
"flowViewModel": {
|
|
747
|
+
"schema": "zfl.eventflow.view@1",
|
|
748
|
+
"nodes": [],
|
|
749
|
+
"edges": [],
|
|
750
|
+
"layout": null,
|
|
751
|
+
"bounds": null,
|
|
752
|
+
"systemGroups": null
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
#### `selectNodes`
|
|
759
|
+
|
|
760
|
+
Recommended command for controlled selection.
|
|
761
|
+
|
|
762
|
+
```json
|
|
763
|
+
{
|
|
764
|
+
"channel": "zfl.eventflow",
|
|
765
|
+
"version": 1,
|
|
766
|
+
"type": "selectNodes",
|
|
767
|
+
"payload": {
|
|
768
|
+
"nodeIds": [
|
|
769
|
+
"command:renewSubscription"
|
|
770
|
+
],
|
|
771
|
+
"replace": true,
|
|
772
|
+
"fitView": false
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
#### `highlightNodes`
|
|
778
|
+
|
|
779
|
+
Recommended command for transient emphasis distinct from selection.
|
|
780
|
+
|
|
781
|
+
```json
|
|
782
|
+
{
|
|
783
|
+
"channel": "zfl.eventflow",
|
|
784
|
+
"version": 1,
|
|
785
|
+
"type": "highlightNodes",
|
|
786
|
+
"payload": {
|
|
787
|
+
"nodeIds": [
|
|
788
|
+
"event:SubscriptionRenewed",
|
|
789
|
+
"policy:SubscriptionRenewed:chargePayment"
|
|
790
|
+
],
|
|
791
|
+
"mode": "transient"
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
#### `fitToNodes`
|
|
797
|
+
|
|
798
|
+
Recommended future command.
|
|
799
|
+
|
|
800
|
+
```json
|
|
801
|
+
{
|
|
802
|
+
"channel": "zfl.eventflow",
|
|
803
|
+
"version": 1,
|
|
804
|
+
"type": "fitToNodes",
|
|
805
|
+
"payload": {
|
|
806
|
+
"nodeIds": [
|
|
807
|
+
"command:renewSubscription"
|
|
808
|
+
],
|
|
809
|
+
"padding": 0.2,
|
|
810
|
+
"maxZoom": 1.2
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
#### `clearSelection`
|
|
816
|
+
|
|
817
|
+
```json
|
|
818
|
+
{
|
|
819
|
+
"channel": "zfl.eventflow",
|
|
820
|
+
"version": 1,
|
|
821
|
+
"type": "clearSelection",
|
|
822
|
+
"payload": {}
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
#### `setTheme`
|
|
827
|
+
|
|
828
|
+
```json
|
|
829
|
+
{
|
|
830
|
+
"channel": "zfl.eventflow",
|
|
831
|
+
"version": 1,
|
|
832
|
+
"type": "setTheme",
|
|
833
|
+
"payload": {
|
|
834
|
+
"theme": "dark"
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
### Renderer -> host messages
|
|
840
|
+
|
|
841
|
+
Recommended outgoing messages:
|
|
842
|
+
|
|
843
|
+
- `webviewReady`
|
|
844
|
+
- `nodeSelected`
|
|
845
|
+
- `selectionChanged`
|
|
846
|
+
- `viewportChanged`
|
|
847
|
+
- `layoutChanged`
|
|
848
|
+
- `contextMenuRequested`
|
|
849
|
+
- `errorReported`
|
|
850
|
+
|
|
851
|
+
Example:
|
|
852
|
+
|
|
853
|
+
```json
|
|
854
|
+
{
|
|
855
|
+
"channel": "zfl.eventflow",
|
|
856
|
+
"version": 1,
|
|
857
|
+
"type": "selectionChanged",
|
|
858
|
+
"payload": {
|
|
859
|
+
"selectedNodeIds": [
|
|
860
|
+
"event:SubscriptionRenewed"
|
|
861
|
+
],
|
|
862
|
+
"selectedEdgeIds": []
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
## 8. Selection and highlighting model
|
|
868
|
+
|
|
869
|
+
### Current behavior
|
|
870
|
+
|
|
871
|
+
The base component supports:
|
|
872
|
+
|
|
873
|
+
- user click on a node
|
|
874
|
+
- user-driven multi-selection inside React Flow
|
|
875
|
+
- callback emission when selection changes
|
|
876
|
+
|
|
877
|
+
The base component does not currently expose imperative APIs for:
|
|
878
|
+
|
|
879
|
+
- programmatic node selection
|
|
880
|
+
- programmatic highlighting
|
|
881
|
+
- programmatic fit-to-selection
|
|
882
|
+
- external tree-to-graph synchronization
|
|
883
|
+
|
|
884
|
+
### Recommended host model
|
|
885
|
+
|
|
886
|
+
Use two distinct concepts:
|
|
887
|
+
|
|
888
|
+
- `selection`
|
|
889
|
+
Persistent, host-owned state representing the item currently selected in graph or tree UI.
|
|
890
|
+
- `highlight`
|
|
891
|
+
Transient emphasis for hover, search result preview, or cross-panel focus.
|
|
892
|
+
|
|
893
|
+
Recommended rules:
|
|
894
|
+
|
|
895
|
+
- selecting a node in the tree sends `selectNodes`
|
|
896
|
+
- selecting a node in the graph emits `nodeSelected` and `selectionChanged`
|
|
897
|
+
- the host updates the rest of the IntelliJ UI from those events
|
|
898
|
+
- highlighting should not replace selection unless explicitly requested
|
|
899
|
+
- `fitToNodes` should be host-triggered, not automatic on every selection change
|
|
900
|
+
|
|
901
|
+
Recommended command semantics:
|
|
902
|
+
|
|
903
|
+
- `highlightNodes`
|
|
904
|
+
Visually emphasize nodes without changing selection
|
|
905
|
+
- `selectNodes`
|
|
906
|
+
Replace or extend current selection
|
|
907
|
+
- `fitToNodes`
|
|
908
|
+
Center/zoom to a node set
|
|
909
|
+
- `clearSelection`
|
|
910
|
+
Remove current graph selection
|
|
911
|
+
|
|
912
|
+
Because these commands are not implemented in the base component yet, the IntelliJ integration should treat them as wrapper-level features.
|
|
913
|
+
|
|
914
|
+
## 9. Error and empty-state handling
|
|
915
|
+
|
|
916
|
+
### Graph is missing
|
|
917
|
+
|
|
918
|
+
If no graph model is available, the host should render or instruct the wrapper to render an empty placeholder state such as:
|
|
919
|
+
|
|
920
|
+
- "No event flow available"
|
|
921
|
+
- "Open a ZFL flow file to visualize"
|
|
922
|
+
|
|
923
|
+
The base component expects a `flowViewModel` prop and does not itself own the "missing model" state. The demo page handles this outside the component.
|
|
924
|
+
|
|
925
|
+
### Graph is invalid
|
|
926
|
+
|
|
927
|
+
The host or wrapper should validate basic structure before rendering:
|
|
928
|
+
|
|
929
|
+
- `schema` present
|
|
930
|
+
- `nodes` array present
|
|
931
|
+
- `edges` array present
|
|
932
|
+
- node IDs unique
|
|
933
|
+
- edge endpoints reference existing nodes
|
|
934
|
+
|
|
935
|
+
Current renderer behavior on invalid-but-parseable data:
|
|
936
|
+
|
|
937
|
+
- nodes without `position` or `dimensions` are skipped
|
|
938
|
+
- edges with missing visible endpoints are skipped
|
|
939
|
+
- some classes of invalid input may still fail earlier in the host if required fields are absent
|
|
940
|
+
|
|
941
|
+
Recommended behavior:
|
|
942
|
+
|
|
943
|
+
- emit `errorReported`
|
|
944
|
+
- keep the previous valid graph if possible
|
|
945
|
+
- show a visible invalid-state message rather than a blank pane with no explanation
|
|
946
|
+
|
|
947
|
+
### Graph is empty
|
|
948
|
+
|
|
949
|
+
An empty but valid graph is:
|
|
950
|
+
|
|
951
|
+
```json
|
|
952
|
+
{
|
|
953
|
+
"schema": "zfl.eventflow.view@1",
|
|
954
|
+
"nodes": [],
|
|
955
|
+
"edges": [],
|
|
956
|
+
"layout": null,
|
|
957
|
+
"bounds": null,
|
|
958
|
+
"systemGroups": null
|
|
959
|
+
}
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
Recommended host behavior:
|
|
963
|
+
|
|
964
|
+
- render an explicit "empty flow" message
|
|
965
|
+
- do not treat empty as an error
|
|
966
|
+
|
|
967
|
+
## 10. Webview embedding guidance for IntelliJ
|
|
968
|
+
|
|
969
|
+
For IntelliJ JCEF embedding:
|
|
970
|
+
|
|
971
|
+
- mount the renderer into a root element with explicit width and height
|
|
972
|
+
- avoid reloading the whole webview for every selection change; send messages instead
|
|
973
|
+
- prefer full-model replacement on refresh over incremental graph patching
|
|
974
|
+
- keep message payloads pure JSON
|
|
975
|
+
- do not rely on filesystem access from inside the webview
|
|
976
|
+
- keep source navigation in the IntelliJ host, not inside the renderer
|
|
977
|
+
|
|
978
|
+
Practical constraints:
|
|
979
|
+
|
|
980
|
+
- React Flow needs measurable layout space
|
|
981
|
+
- webview theme changes should be forwarded explicitly
|
|
982
|
+
- focus handling may differ from a normal browser tab, so keyboard shortcuts should stay host-owned unless intentionally added
|
|
983
|
+
- large graphs should be sent after model generation is complete, not streamed node-by-node
|
|
984
|
+
|
|
985
|
+
Recommended IntelliJ-side split:
|
|
986
|
+
|
|
987
|
+
- plugin: fetch model, validate, persist, navigate source, own selection state
|
|
988
|
+
- webview wrapper: translate plugin messages to React props
|
|
989
|
+
- renderer: draw graph and report UI interactions
|
|
990
|
+
|
|
991
|
+
## 11. Current capabilities vs planned capabilities
|
|
992
|
+
|
|
993
|
+
### Current behavior in this repository
|
|
994
|
+
|
|
995
|
+
- Renders `FlowViewModel` using React Flow
|
|
996
|
+
- Supports node types `START`, `COMMAND`, `EVENT`, `POLICY`, `END`
|
|
997
|
+
- Supports edge types `CAUSATION`, `TRIGGER`, `CONDITIONAL`, `ERROR`
|
|
998
|
+
- Supports light/dark theme prop
|
|
999
|
+
- Supports display-only label humanization
|
|
1000
|
+
- Emits node click callbacks
|
|
1001
|
+
- Emits selection-change callbacks
|
|
1002
|
+
- Emits updated `FlowViewModel` after drag stop when editing is enabled
|
|
1003
|
+
- Skips nodes lacking `position` or `dimensions`
|
|
1004
|
+
- Skips dangling edges
|
|
1005
|
+
- Ignores `systemGroups`
|
|
1006
|
+
- Has no built-in message bridge
|
|
1007
|
+
- Has no built-in programmatic selection/highlight API
|
|
1008
|
+
- Has no built-in viewport event API
|
|
1009
|
+
|
|
1010
|
+
### Planned or recommended for host integration
|
|
1011
|
+
|
|
1012
|
+
- Standard host/webview JSON message protocol
|
|
1013
|
+
- `webviewReady` handshake
|
|
1014
|
+
- Programmatic selection commands
|
|
1015
|
+
- Programmatic highlight commands
|
|
1016
|
+
- Fit-to-selection commands
|
|
1017
|
+
- Viewport synchronization events
|
|
1018
|
+
- Structured error reporting
|
|
1019
|
+
- Context menu events
|
|
1020
|
+
- Optional richer empty/invalid states inside the wrapper
|
|
1021
|
+
|
|
1022
|
+
## 12. Versioning and compatibility notes
|
|
1023
|
+
|
|
1024
|
+
### Model versioning
|
|
1025
|
+
|
|
1026
|
+
The graph payload already contains a schema marker:
|
|
1027
|
+
|
|
1028
|
+
- `zfl.eventflow.view@1`
|
|
1029
|
+
|
|
1030
|
+
Hosts should branch on schema version before assuming field compatibility.
|
|
1031
|
+
|
|
1032
|
+
### Bridge versioning
|
|
1033
|
+
|
|
1034
|
+
The recommended message bridge should include:
|
|
1035
|
+
|
|
1036
|
+
- `channel`
|
|
1037
|
+
- `version`
|
|
1038
|
+
- `type`
|
|
1039
|
+
|
|
1040
|
+
Use `version: 1` for the first stable IntelliJ bridge contract.
|
|
1041
|
+
|
|
1042
|
+
### Safe evolution rules
|
|
1043
|
+
|
|
1044
|
+
- add new message types without changing existing ones
|
|
1045
|
+
- add optional payload fields freely
|
|
1046
|
+
- do not rename existing fields in place
|
|
1047
|
+
- do not change selection or event semantics without bumping bridge version
|
|
1048
|
+
- if a new graph schema is introduced, support old schema in the host adapter during a migration period
|
|
1049
|
+
|
|
1050
|
+
Recommended compatibility stance:
|
|
1051
|
+
|
|
1052
|
+
- renderer should ignore unknown incoming optional fields
|
|
1053
|
+
- host should ignore unknown outgoing event fields
|
|
1054
|
+
- unsupported incoming message types should yield `errorReported`, not silent failure
|
|
1055
|
+
|
|
1056
|
+
## 13. Open questions / ambiguities
|
|
1057
|
+
|
|
1058
|
+
- Should the IntelliJ integration standardize on full-model replacement only, or also support incremental updates later?
|
|
1059
|
+
- Should `sourceRef.file` be an absolute path, workspace-relative path, URI, or logical document ID?
|
|
1060
|
+
- Should graph selection be single-select only from IntelliJ tree interactions, even though React Flow supports multi-selection?
|
|
1061
|
+
- Should host-triggered highlighting be a separate visual state from selection, or can selection styling serve both?
|
|
1062
|
+
- Should drag-based layout editing be enabled in IntelliJ by default, or remain an opt-in editing mode?
|
|
1063
|
+
- Should `layoutChanged` be emitted on every drag move, or only on drag stop? Current base behavior is drag stop only.
|
|
1064
|
+
- Should the wrapper preserve the previous valid graph when a refresh payload is invalid?
|
|
1065
|
+
- Is `systemGroups` intentionally deferred, or should swimlane/group rendering become part of the supported contract?
|
|
1066
|
+
- Does the IntelliJ plugin need edge-click events in addition to node-click events?
|
|
1067
|
+
- Should viewport persistence be part of the first integration version, or deferred until selection and navigation are stable?
|