@typeonce/effect-machine-devtools 0.25.0 → 0.26.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/README.md +2 -2
- package/dist/client/assets/index-BsorPSDS.css +1 -0
- package/dist/client/assets/index-eiptehRd.js +37 -0
- package/dist/client/index.html +2 -2
- package/package.json +6 -6
- package/src/internal/browser/chart-layout-policy.ts +206 -0
- package/src/internal/browser/chart-layout.ts +334 -81
- package/src/internal/browser/chart-model.ts +10 -0
- package/src/internal/browser/chart-renderer.ts +127 -14
- package/src/internal/browser/styles.css +188 -10
- package/src/internal/browser/transition-semantics-example.ts +10 -1
- package/src/internal/browser/visualizer-analysis.ts +52 -0
- package/src/internal/browser/visualizer-app.ts +110 -10
- package/dist/client/assets/index-BTOAhezX.js +0 -37
- package/dist/client/assets/index-Cg7xzSkz.css +0 -1
|
@@ -168,7 +168,7 @@ const stateContent = (layout: LaidOutChartNode, stateStatus: HTMLElement): Docum
|
|
|
168
168
|
const activities = element("div", "chart-state-section")
|
|
169
169
|
activities.append(element("div", "chart-section-label", "invokes"))
|
|
170
170
|
layout.node.activities.slice(0, maxVisibleActivities).forEach((activity) => {
|
|
171
|
-
const row = element("div",
|
|
171
|
+
const row = element("div", `chart-activity-row chart-activity-${activity.kind}`)
|
|
172
172
|
row.append(
|
|
173
173
|
element("span", "chart-activity-kind", activity.kind),
|
|
174
174
|
element("span", "chart-activity-name", activity.label)
|
|
@@ -184,10 +184,83 @@ const stateContent = (layout: LaidOutChartNode, stateStatus: HTMLElement): Docum
|
|
|
184
184
|
return fragment
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
const
|
|
187
|
+
const pointAlong = (start: ChartPoint, end: ChartPoint, distance: number): ChartPoint => {
|
|
188
|
+
const length = Math.abs(end.x - start.x) + Math.abs(end.y - start.y)
|
|
189
|
+
if (length === 0) return start
|
|
190
|
+
const ratio = distance / length
|
|
191
|
+
return {
|
|
192
|
+
x: start.x + (end.x - start.x) * ratio,
|
|
193
|
+
y: start.y + (end.y - start.y) * ratio
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const chartEdgePathData = (points: ReadonlyArray<ChartPoint>, radius = 9): string => {
|
|
188
198
|
const first = points[0]
|
|
189
199
|
if (first === undefined) return ""
|
|
190
|
-
|
|
200
|
+
if (points.length === 1) return `M ${first.x} ${first.y}`
|
|
201
|
+
let path = `M ${first.x} ${first.y}`
|
|
202
|
+
for (let index = 1; index < points.length - 1; index++) {
|
|
203
|
+
const previous = points[index - 1]!
|
|
204
|
+
const corner = points[index]!
|
|
205
|
+
const next = points[index + 1]!
|
|
206
|
+
const incoming = Math.abs(corner.x - previous.x) + Math.abs(corner.y - previous.y)
|
|
207
|
+
const outgoing = Math.abs(next.x - corner.x) + Math.abs(next.y - corner.y)
|
|
208
|
+
const cornerRadius = Math.min(radius, incoming / 2, outgoing / 2)
|
|
209
|
+
const before = pointAlong(corner, previous, cornerRadius)
|
|
210
|
+
const after = pointAlong(corner, next, cornerRadius)
|
|
211
|
+
path += ` L ${before.x} ${before.y} Q ${corner.x} ${corner.y} ${after.x} ${after.y}`
|
|
212
|
+
}
|
|
213
|
+
const last = points.at(-1)!
|
|
214
|
+
return `${path} L ${last.x} ${last.y}`
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface ChartDirectionCue {
|
|
218
|
+
readonly start: ChartPoint
|
|
219
|
+
readonly end: ChartPoint
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const chartDirectionCue = (
|
|
223
|
+
points: ReadonlyArray<ChartPoint>,
|
|
224
|
+
minimumRouteLength = 280
|
|
225
|
+
): ChartDirectionCue | null => {
|
|
226
|
+
const segments = points.slice(1).map((end, index) => {
|
|
227
|
+
const start = points[index]!
|
|
228
|
+
return {
|
|
229
|
+
start,
|
|
230
|
+
end,
|
|
231
|
+
length: Math.abs(end.x - start.x) + Math.abs(end.y - start.y)
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
const total = segments.reduce((sum, segment) => sum + segment.length, 0)
|
|
235
|
+
if (total < minimumRouteLength) return null
|
|
236
|
+
let remaining = total * 0.32
|
|
237
|
+
let selected: { readonly start: ChartPoint; readonly end: ChartPoint; readonly distance: number } | undefined
|
|
238
|
+
for (const segment of segments) {
|
|
239
|
+
if (segment.length >= 24 && remaining <= segment.length) {
|
|
240
|
+
selected = {
|
|
241
|
+
start: segment.start,
|
|
242
|
+
end: segment.end,
|
|
243
|
+
distance: Math.min(segment.length - 12, Math.max(12, remaining))
|
|
244
|
+
}
|
|
245
|
+
break
|
|
246
|
+
}
|
|
247
|
+
remaining -= segment.length
|
|
248
|
+
}
|
|
249
|
+
if (selected === undefined) {
|
|
250
|
+
const segment = [...segments].sort((left, right) => right.length - left.length)[0]
|
|
251
|
+
if (segment === undefined || segment.length < 24) return null
|
|
252
|
+
selected = { start: segment.start, end: segment.end, distance: segment.length / 2 }
|
|
253
|
+
}
|
|
254
|
+
const center = pointAlong(selected.start, selected.end, selected.distance)
|
|
255
|
+
const deltaX = selected.end.x - selected.start.x
|
|
256
|
+
const deltaY = selected.end.y - selected.start.y
|
|
257
|
+
const horizontal = Math.abs(deltaX) >= Math.abs(deltaY)
|
|
258
|
+
const directionX = horizontal ? Math.sign(deltaX) : 0
|
|
259
|
+
const directionY = horizontal ? 0 : Math.sign(deltaY)
|
|
260
|
+
return {
|
|
261
|
+
start: { x: center.x - directionX * 5, y: center.y - directionY * 5 },
|
|
262
|
+
end: { x: center.x + directionX * 5, y: center.y + directionY * 5 }
|
|
263
|
+
}
|
|
191
264
|
}
|
|
192
265
|
|
|
193
266
|
const setStateClass = (
|
|
@@ -219,16 +292,29 @@ const render = (
|
|
|
219
292
|
const definitions = svgElement("defs")
|
|
220
293
|
const marker = svgElement("marker")
|
|
221
294
|
marker.id = "chart-arrow"
|
|
222
|
-
marker.setAttribute("viewBox", "0 0
|
|
223
|
-
marker.setAttribute("refX", "
|
|
224
|
-
marker.setAttribute("refY", "
|
|
225
|
-
marker.setAttribute("markerWidth", "
|
|
226
|
-
marker.setAttribute("markerHeight", "
|
|
295
|
+
marker.setAttribute("viewBox", "0 0 12 12")
|
|
296
|
+
marker.setAttribute("refX", "11")
|
|
297
|
+
marker.setAttribute("refY", "6")
|
|
298
|
+
marker.setAttribute("markerWidth", "12")
|
|
299
|
+
marker.setAttribute("markerHeight", "12")
|
|
300
|
+
marker.setAttribute("markerUnits", "userSpaceOnUse")
|
|
227
301
|
marker.setAttribute("orient", "auto-start-reverse")
|
|
228
302
|
const arrow = svgElement("path")
|
|
229
|
-
arrow.setAttribute("d", "M
|
|
303
|
+
arrow.setAttribute("d", "M 1 1 L 11 6 L 1 11 z")
|
|
230
304
|
marker.append(arrow)
|
|
231
|
-
|
|
305
|
+
const directionMarker = svgElement("marker")
|
|
306
|
+
directionMarker.id = "chart-direction"
|
|
307
|
+
directionMarker.setAttribute("viewBox", "0 0 8 8")
|
|
308
|
+
directionMarker.setAttribute("refX", "7")
|
|
309
|
+
directionMarker.setAttribute("refY", "4")
|
|
310
|
+
directionMarker.setAttribute("markerWidth", "8")
|
|
311
|
+
directionMarker.setAttribute("markerHeight", "8")
|
|
312
|
+
directionMarker.setAttribute("markerUnits", "userSpaceOnUse")
|
|
313
|
+
directionMarker.setAttribute("orient", "auto")
|
|
314
|
+
const directionArrow = svgElement("path")
|
|
315
|
+
directionArrow.setAttribute("d", "M 1 1 L 7 4 L 1 7 z")
|
|
316
|
+
directionMarker.append(directionArrow)
|
|
317
|
+
definitions.append(marker, directionMarker)
|
|
232
318
|
svg.append(definitions)
|
|
233
319
|
const nodesLayer = element("div", "chart-nodes")
|
|
234
320
|
const labelsLayer = element("div", "chart-labels")
|
|
@@ -274,6 +360,15 @@ const render = (
|
|
|
274
360
|
edgeElements.set(id, registered)
|
|
275
361
|
}
|
|
276
362
|
|
|
363
|
+
for (const regionLayout of layout.regions) {
|
|
364
|
+
const region = element("div", "chart-unconnected-region")
|
|
365
|
+
region.title = "No statically known transition reaches these states. Runtime-resolved targets may still reach them."
|
|
366
|
+
position(region, regionLayout)
|
|
367
|
+
const label = element("div", "chart-unconnected-region-label", "No static path from initial")
|
|
368
|
+
region.append(label)
|
|
369
|
+
regions.append(region)
|
|
370
|
+
}
|
|
371
|
+
|
|
277
372
|
for (const laidOut of layout.nodes) {
|
|
278
373
|
if (laidOut.node.children.length > 0) {
|
|
279
374
|
const region = element("div", `chart-compound chart-compound-${laidOut.node.type}`)
|
|
@@ -337,16 +432,32 @@ const render = (
|
|
|
337
432
|
const group = svgElement(
|
|
338
433
|
"g",
|
|
339
434
|
`chart-edge-group chart-edge-${laidOut.kind}${
|
|
340
|
-
laidOut.kind === "transition"
|
|
435
|
+
laidOut.kind === "transition"
|
|
436
|
+
? ` chart-transition-${laidOut.edge.kind} chart-edge-trigger-${laidOut.edge.trigger.type}${
|
|
437
|
+
laidOut.edge.activityKind === null ? "" : ` chart-edge-activity-${laidOut.edge.activityKind}`
|
|
438
|
+
}`
|
|
439
|
+
: ""
|
|
341
440
|
}`
|
|
342
441
|
)
|
|
442
|
+
const casing = svgElement("path", "chart-edge-casing")
|
|
343
443
|
const visible = svgElement("path", "chart-edge-line")
|
|
344
|
-
const route =
|
|
444
|
+
const route = chartEdgePathData(laidOut.points)
|
|
445
|
+
casing.setAttribute("d", route)
|
|
345
446
|
visible.setAttribute("d", route)
|
|
346
447
|
visible.setAttribute("marker-end", "url(#chart-arrow)")
|
|
347
448
|
const hit = svgElement("path", "chart-edge-hit")
|
|
348
449
|
hit.setAttribute("d", route)
|
|
349
|
-
group.append(
|
|
450
|
+
group.append(casing, visible)
|
|
451
|
+
if (laidOut.kind === "transition") {
|
|
452
|
+
const cue = chartDirectionCue(laidOut.points)
|
|
453
|
+
if (cue !== null) {
|
|
454
|
+
const direction = svgElement("path", "chart-edge-direction")
|
|
455
|
+
direction.setAttribute("d", `M ${cue.start.x} ${cue.start.y} L ${cue.end.x} ${cue.end.y}`)
|
|
456
|
+
direction.setAttribute("marker-end", "url(#chart-direction)")
|
|
457
|
+
group.append(direction)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
group.append(hit)
|
|
350
461
|
svg.append(group)
|
|
351
462
|
if (laidOut.kind === "initial") continue
|
|
352
463
|
|
|
@@ -354,7 +465,9 @@ const render = (
|
|
|
354
465
|
registerEdgeElement(laidOut.edge.id, group)
|
|
355
466
|
const label = element(
|
|
356
467
|
"button",
|
|
357
|
-
`chart-edge-label chart-edge-label-${laidOut.edge.trigger.type}
|
|
468
|
+
`chart-edge-label chart-edge-label-${laidOut.edge.trigger.type}${
|
|
469
|
+
laidOut.edge.activityKind === null ? "" : ` chart-edge-activity-${laidOut.edge.activityKind}`
|
|
470
|
+
}`,
|
|
358
471
|
laidOut.edge.label
|
|
359
472
|
)
|
|
360
473
|
label.type = "button"
|
|
@@ -308,6 +308,31 @@ button {
|
|
|
308
308
|
cursor: default;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
.toolbar-button[aria-pressed="true"] {
|
|
312
|
+
color: #dce8ff;
|
|
313
|
+
background: rgb(65 103 165 / 28%);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.analysis-button {
|
|
317
|
+
display: inline-flex;
|
|
318
|
+
align-items: center;
|
|
319
|
+
gap: 6px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.analysis-button[hidden] {
|
|
323
|
+
display: none;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.analysis-button-count {
|
|
327
|
+
min-width: 17px;
|
|
328
|
+
padding: 2px 5px;
|
|
329
|
+
border-radius: 8px;
|
|
330
|
+
color: #9da6b3;
|
|
331
|
+
background: #20242a;
|
|
332
|
+
font: 10px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
333
|
+
text-align: center;
|
|
334
|
+
}
|
|
335
|
+
|
|
311
336
|
.topology-chart {
|
|
312
337
|
display: flex;
|
|
313
338
|
flex: 1;
|
|
@@ -411,6 +436,23 @@ button {
|
|
|
411
436
|
background: rgb(20 24 30 / 74%);
|
|
412
437
|
}
|
|
413
438
|
|
|
439
|
+
.chart-unconnected-region {
|
|
440
|
+
position: absolute;
|
|
441
|
+
border: 1px dashed #353b44;
|
|
442
|
+
border-radius: 5px;
|
|
443
|
+
background: rgb(14 16 19 / 58%);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.chart-unconnected-region-label {
|
|
447
|
+
position: absolute;
|
|
448
|
+
top: 17px;
|
|
449
|
+
left: 24px;
|
|
450
|
+
color: #727984;
|
|
451
|
+
font: 9px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
452
|
+
letter-spacing: 0.05em;
|
|
453
|
+
text-transform: uppercase;
|
|
454
|
+
}
|
|
455
|
+
|
|
414
456
|
.chart-state {
|
|
415
457
|
position: absolute;
|
|
416
458
|
display: block;
|
|
@@ -558,11 +600,51 @@ button {
|
|
|
558
600
|
|
|
559
601
|
.chart-activity-kind {
|
|
560
602
|
min-width: max-content;
|
|
561
|
-
color: #77a990;
|
|
603
|
+
color: var(--chart-activity-color, #77a990);
|
|
562
604
|
overflow: visible;
|
|
563
605
|
text-overflow: clip;
|
|
564
606
|
}
|
|
565
607
|
|
|
608
|
+
.chart-activity-process,
|
|
609
|
+
.chart-edge-activity-process,
|
|
610
|
+
.badge-activity-process {
|
|
611
|
+
--chart-activity-color: #a98978;
|
|
612
|
+
--chart-activity-border: #51433c;
|
|
613
|
+
--chart-activity-background: #191513;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.chart-activity-effect,
|
|
617
|
+
.chart-edge-activity-effect,
|
|
618
|
+
.badge-activity-effect {
|
|
619
|
+
--chart-activity-color: #7f9f8c;
|
|
620
|
+
--chart-activity-border: #42564b;
|
|
621
|
+
--chart-activity-background: #131815;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.chart-activity-timer,
|
|
625
|
+
.chart-edge-activity-timer,
|
|
626
|
+
.badge-activity-timer {
|
|
627
|
+
--chart-activity-color: #aaa07c;
|
|
628
|
+
--chart-activity-border: #57503c;
|
|
629
|
+
--chart-activity-background: #191812;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.chart-activity-stream,
|
|
633
|
+
.chart-edge-activity-stream,
|
|
634
|
+
.badge-activity-stream {
|
|
635
|
+
--chart-activity-color: #7898a2;
|
|
636
|
+
--chart-activity-border: #405259;
|
|
637
|
+
--chart-activity-background: #121719;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.chart-activity-machine,
|
|
641
|
+
.chart-edge-activity-machine,
|
|
642
|
+
.badge-activity-machine {
|
|
643
|
+
--chart-activity-color: #958aa8;
|
|
644
|
+
--chart-activity-border: #4d4658;
|
|
645
|
+
--chart-activity-background: #17151a;
|
|
646
|
+
}
|
|
647
|
+
|
|
566
648
|
.chart-more {
|
|
567
649
|
min-height: 18px;
|
|
568
650
|
padding-top: 3px;
|
|
@@ -591,17 +673,30 @@ button {
|
|
|
591
673
|
user-select: none;
|
|
592
674
|
}
|
|
593
675
|
|
|
676
|
+
.chart-edge-casing,
|
|
594
677
|
.chart-edge-line,
|
|
678
|
+
.chart-edge-direction,
|
|
595
679
|
.chart-edge-hit {
|
|
596
680
|
fill: none;
|
|
597
681
|
}
|
|
598
682
|
|
|
599
|
-
.chart-edge-
|
|
683
|
+
.chart-edge-casing {
|
|
684
|
+
stroke: #0b0c0e;
|
|
685
|
+
stroke-width: 5.5;
|
|
686
|
+
vector-effect: non-scaling-stroke;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.chart-edge-line,
|
|
690
|
+
.chart-edge-direction {
|
|
600
691
|
stroke: #626b77;
|
|
601
692
|
stroke-width: 1.4;
|
|
602
693
|
vector-effect: non-scaling-stroke;
|
|
603
694
|
}
|
|
604
695
|
|
|
696
|
+
.chart-edge-direction {
|
|
697
|
+
pointer-events: none;
|
|
698
|
+
}
|
|
699
|
+
|
|
605
700
|
.chart-edge-initial .chart-edge-line {
|
|
606
701
|
stroke: #89929e;
|
|
607
702
|
}
|
|
@@ -613,7 +708,8 @@ button {
|
|
|
613
708
|
pointer-events: stroke;
|
|
614
709
|
}
|
|
615
710
|
|
|
616
|
-
#chart-arrow path
|
|
711
|
+
#chart-arrow path,
|
|
712
|
+
#chart-direction path {
|
|
617
713
|
fill: context-stroke;
|
|
618
714
|
}
|
|
619
715
|
|
|
@@ -647,7 +743,8 @@ button {
|
|
|
647
743
|
color: #d4b4e9;
|
|
648
744
|
}
|
|
649
745
|
|
|
650
|
-
.chart-edge-group.is-hovered .chart-edge-line
|
|
746
|
+
.chart-edge-group.is-hovered .chart-edge-line,
|
|
747
|
+
.chart-edge-group.is-hovered .chart-edge-direction {
|
|
651
748
|
stroke: #7c9ed2;
|
|
652
749
|
stroke-width: 1.8;
|
|
653
750
|
}
|
|
@@ -661,22 +758,56 @@ button {
|
|
|
661
758
|
stroke-dasharray: 5 5;
|
|
662
759
|
}
|
|
663
760
|
|
|
664
|
-
.chart-edge-group.
|
|
761
|
+
.chart-edge-group.chart-edge-trigger-always .chart-edge-line,
|
|
762
|
+
.chart-edge-group.chart-edge-trigger-choice .chart-edge-line,
|
|
763
|
+
.chart-edge-group.chart-edge-trigger-done .chart-edge-line,
|
|
764
|
+
.chart-edge-group.chart-edge-trigger-invoke .chart-edge-line {
|
|
765
|
+
stroke-dasharray: 7 5;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
.chart-edge-group.chart-edge-activity-process .chart-edge-line,
|
|
769
|
+
.chart-edge-group.chart-edge-activity-effect .chart-edge-line,
|
|
770
|
+
.chart-edge-group.chart-edge-activity-timer .chart-edge-line,
|
|
771
|
+
.chart-edge-group.chart-edge-activity-stream .chart-edge-line,
|
|
772
|
+
.chart-edge-group.chart-edge-activity-machine .chart-edge-line,
|
|
773
|
+
.chart-edge-group.chart-edge-activity-process .chart-edge-direction,
|
|
774
|
+
.chart-edge-group.chart-edge-activity-effect .chart-edge-direction,
|
|
775
|
+
.chart-edge-group.chart-edge-activity-timer .chart-edge-direction,
|
|
776
|
+
.chart-edge-group.chart-edge-activity-stream .chart-edge-direction,
|
|
777
|
+
.chart-edge-group.chart-edge-activity-machine .chart-edge-direction {
|
|
778
|
+
stroke: var(--chart-activity-color);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.chart-edge-label.chart-edge-activity-process,
|
|
782
|
+
.chart-edge-label.chart-edge-activity-effect,
|
|
783
|
+
.chart-edge-label.chart-edge-activity-timer,
|
|
784
|
+
.chart-edge-label.chart-edge-activity-stream,
|
|
785
|
+
.chart-edge-label.chart-edge-activity-machine {
|
|
786
|
+
border-color: var(--chart-activity-border);
|
|
787
|
+
color: var(--chart-activity-color);
|
|
788
|
+
background: var(--chart-activity-background);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
.chart-edge-group.is-incoming .chart-edge-line,
|
|
792
|
+
.chart-edge-group.is-incoming .chart-edge-direction {
|
|
665
793
|
stroke: #ed6a70;
|
|
666
794
|
stroke-width: 2;
|
|
667
795
|
}
|
|
668
796
|
|
|
669
|
-
.chart-edge-group.is-outgoing .chart-edge-line
|
|
797
|
+
.chart-edge-group.is-outgoing .chart-edge-line,
|
|
798
|
+
.chart-edge-group.is-outgoing .chart-edge-direction {
|
|
670
799
|
stroke: #6eb6dc;
|
|
671
800
|
stroke-width: 2;
|
|
672
801
|
}
|
|
673
802
|
|
|
674
|
-
.chart-edge-group.is-selected .chart-edge-line
|
|
803
|
+
.chart-edge-group.is-selected .chart-edge-line,
|
|
804
|
+
.chart-edge-group.is-selected .chart-edge-direction {
|
|
675
805
|
stroke: #8ab5ff;
|
|
676
806
|
stroke-width: 2.5;
|
|
677
807
|
}
|
|
678
808
|
|
|
679
|
-
.chart-edge-group.is-walkthrough-available .chart-edge-line
|
|
809
|
+
.chart-edge-group.is-walkthrough-available .chart-edge-line,
|
|
810
|
+
.chart-edge-group.is-walkthrough-available .chart-edge-direction {
|
|
680
811
|
stroke: #d5aa57;
|
|
681
812
|
stroke-width: 2;
|
|
682
813
|
}
|
|
@@ -827,6 +958,15 @@ button {
|
|
|
827
958
|
background: rgb(78 181 131 / 14%);
|
|
828
959
|
}
|
|
829
960
|
|
|
961
|
+
.badge-activity-process,
|
|
962
|
+
.badge-activity-effect,
|
|
963
|
+
.badge-activity-timer,
|
|
964
|
+
.badge-activity-stream,
|
|
965
|
+
.badge-activity-machine {
|
|
966
|
+
color: var(--chart-activity-color);
|
|
967
|
+
background: var(--chart-activity-background);
|
|
968
|
+
}
|
|
969
|
+
|
|
830
970
|
.badge-count {
|
|
831
971
|
color: #858c96;
|
|
832
972
|
background: transparent;
|
|
@@ -835,7 +975,7 @@ button {
|
|
|
835
975
|
.inspector {
|
|
836
976
|
position: absolute;
|
|
837
977
|
z-index: 10;
|
|
838
|
-
top:
|
|
978
|
+
top: 60px;
|
|
839
979
|
right: 14px;
|
|
840
980
|
bottom: 14px;
|
|
841
981
|
width: min(460px, calc(100% - 28px));
|
|
@@ -973,6 +1113,13 @@ button {
|
|
|
973
1113
|
border-bottom: 1px solid var(--line);
|
|
974
1114
|
}
|
|
975
1115
|
|
|
1116
|
+
.analysis-summary {
|
|
1117
|
+
margin: 12px 0 0;
|
|
1118
|
+
color: #7f8690;
|
|
1119
|
+
font-size: 12px;
|
|
1120
|
+
line-height: 1.6;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
976
1123
|
.state-annotations {
|
|
977
1124
|
margin-top: 18px;
|
|
978
1125
|
}
|
|
@@ -1071,6 +1218,33 @@ button {
|
|
|
1071
1218
|
background: var(--surface-raised);
|
|
1072
1219
|
}
|
|
1073
1220
|
|
|
1221
|
+
.analysis-card {
|
|
1222
|
+
padding-bottom: 12px;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.analysis-card .state-link {
|
|
1226
|
+
color: #e2e4e7;
|
|
1227
|
+
font-size: 12px;
|
|
1228
|
+
font-weight: 600;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
.analysis-message,
|
|
1232
|
+
.analysis-qualification {
|
|
1233
|
+
margin: 0;
|
|
1234
|
+
padding: 0 12px;
|
|
1235
|
+
font-size: 11px;
|
|
1236
|
+
line-height: 1.6;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
.analysis-message {
|
|
1240
|
+
color: #b9bec6;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
.analysis-qualification {
|
|
1244
|
+
margin-top: 5px;
|
|
1245
|
+
color: #747c87;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1074
1248
|
.card-header {
|
|
1075
1249
|
display: flex;
|
|
1076
1250
|
min-height: 42px;
|
|
@@ -1546,7 +1720,7 @@ button {
|
|
|
1546
1720
|
}
|
|
1547
1721
|
|
|
1548
1722
|
.inspector {
|
|
1549
|
-
top:
|
|
1723
|
+
top: 54px;
|
|
1550
1724
|
right: 8px;
|
|
1551
1725
|
bottom: 8px;
|
|
1552
1726
|
width: min(440px, calc(100% - 16px));
|
|
@@ -1572,6 +1746,10 @@ button {
|
|
|
1572
1746
|
padding: 48px 14px 18px;
|
|
1573
1747
|
}
|
|
1574
1748
|
|
|
1749
|
+
.inspector {
|
|
1750
|
+
top: 84px;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1575
1753
|
.zoom-controls {
|
|
1576
1754
|
bottom: 8px;
|
|
1577
1755
|
left: 8px;
|
|
@@ -33,6 +33,9 @@ class Paused extends Schema.TaggedClass<Paused>("TransitionPaused")("Paused", {
|
|
|
33
33
|
class Published extends Schema.TaggedClass<Published>("TransitionPublished")("Published", {
|
|
34
34
|
result: Schema.String
|
|
35
35
|
}) {}
|
|
36
|
+
class Disabled extends Schema.TaggedClass<Disabled>("TransitionDisabled")("Disabled", {
|
|
37
|
+
reason: Schema.String
|
|
38
|
+
}) {}
|
|
36
39
|
|
|
37
40
|
class Create extends Schema.TaggedClass<Create>("TransitionCreate")("Create", {
|
|
38
41
|
text: Schema.String,
|
|
@@ -67,6 +70,9 @@ class BumpWorkspace extends Schema.TaggedClass<BumpWorkspace>("TransitionBumpWor
|
|
|
67
70
|
"BumpWorkspace",
|
|
68
71
|
{}
|
|
69
72
|
) {}
|
|
73
|
+
class Archive extends Schema.TaggedClass<Archive>("TransitionArchive")("Archive", {
|
|
74
|
+
reason: Schema.String
|
|
75
|
+
}) {}
|
|
70
76
|
|
|
71
77
|
const TransitionStates = Machine.states({
|
|
72
78
|
Workspace: {
|
|
@@ -91,6 +97,7 @@ const TransitionStates = Machine.states({
|
|
|
91
97
|
}
|
|
92
98
|
},
|
|
93
99
|
Paused,
|
|
100
|
+
Disabled,
|
|
94
101
|
Published: { schema: Published, type: "final", output: Schema.String }
|
|
95
102
|
})
|
|
96
103
|
|
|
@@ -121,7 +128,8 @@ export const transitionSemanticsMachine = Machine.make({
|
|
|
121
128
|
Refresh,
|
|
122
129
|
Ignore,
|
|
123
130
|
MaybeHandle,
|
|
124
|
-
BumpWorkspace
|
|
131
|
+
BumpWorkspace,
|
|
132
|
+
Archive
|
|
125
133
|
),
|
|
126
134
|
initial: (to) => to.Paused().resolve(({ target }) => target.decoded(new Paused({ reason: "not started" })))
|
|
127
135
|
}).handle({
|
|
@@ -230,6 +238,7 @@ export const transitionSemanticsMachine = Machine.make({
|
|
|
230
238
|
)
|
|
231
239
|
}
|
|
232
240
|
},
|
|
241
|
+
Disabled: {},
|
|
233
242
|
Published: {
|
|
234
243
|
output: ({ state }) => state.result
|
|
235
244
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { MachineDocument as VisualizationDocument } from "../../MachineDocument.js"
|
|
2
|
+
import { makeChartLayoutPolicy } from "./chart-layout-policy.js"
|
|
3
|
+
import { makeChartModel } from "./chart-model.js"
|
|
4
|
+
|
|
5
|
+
export interface UnhandledPublicEventWarning {
|
|
6
|
+
readonly _tag: "UnhandledPublicEvent"
|
|
7
|
+
readonly event: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface NoStaticPathNote {
|
|
11
|
+
readonly _tag: "NoStaticPathFromInitial"
|
|
12
|
+
readonly path: string
|
|
13
|
+
readonly label: string
|
|
14
|
+
readonly type: "atomic" | "compound" | "parallel" | "final" | "history" | "choice"
|
|
15
|
+
readonly descendantCount: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface VisualizerAnalysis {
|
|
19
|
+
readonly warnings: ReadonlyArray<UnhandledPublicEventWarning>
|
|
20
|
+
readonly topologyNotes: ReadonlyArray<NoStaticPathNote>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Static findings derived without executing machine callbacks. */
|
|
24
|
+
export const analyzeVisualization = (document: VisualizationDocument): VisualizerAnalysis => {
|
|
25
|
+
const handledEvents = new Set(
|
|
26
|
+
document.transitions.flatMap((transition): ReadonlyArray<string> =>
|
|
27
|
+
transition.trigger.type === "event" ? [transition.trigger.event] : []
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
const declaredEvents = new Set(document.inputs.events.map(({ event }) => event))
|
|
31
|
+
const warnings = [...declaredEvents].flatMap((event): ReadonlyArray<UnhandledPublicEventWarning> =>
|
|
32
|
+
handledEvents.has(event) ? [] : [{ _tag: "UnhandledPublicEvent", event }]
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const chart = makeChartModel(document)
|
|
36
|
+
const policy = makeChartLayoutPolicy(chart)
|
|
37
|
+
const topologyNotes = chart.nodes.flatMap((node): ReadonlyArray<NoStaticPathNote> => {
|
|
38
|
+
const hasStaticPath = policy.node(node.path).staticPath
|
|
39
|
+
const parentHasStaticPath = node.parent === null || policy.node(node.parent).staticPath
|
|
40
|
+
if (hasStaticPath || !parentHasStaticPath) return []
|
|
41
|
+
const descendantCount = chart.nodes.filter(({ path }) => path.startsWith(`${node.path}.`)).length
|
|
42
|
+
return [{
|
|
43
|
+
_tag: "NoStaticPathFromInitial",
|
|
44
|
+
path: node.path,
|
|
45
|
+
label: node.label,
|
|
46
|
+
type: node.type,
|
|
47
|
+
descendantCount
|
|
48
|
+
}]
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return { warnings, topologyNotes }
|
|
52
|
+
}
|