@caddis/cli 0.4.45 → 0.4.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/bundles/antigravity-plugin/plugin.json +1 -1
  2. package/bundles/antigravity-plugin/skills/flow-studio/README.md +46 -7
  3. package/bundles/antigravity-plugin/skills/flow-studio/SKILL.md +80 -81
  4. package/bundles/antigravity-plugin/skills/flow-studio/examples/README.md +8 -0
  5. package/bundles/antigravity-plugin/skills/flow-studio/examples/agent-journey.json +103 -0
  6. package/bundles/antigravity-plugin/skills/flow-studio/examples/approval-flow.json +134 -0
  7. package/bundles/antigravity-plugin/skills/flow-studio/examples/ci-cd-pipeline.json +132 -0
  8. package/bundles/antigravity-plugin/skills/flow-studio/examples/incident-timeline.json +109 -0
  9. package/bundles/antigravity-plugin/skills/flow-studio/examples/order-desk.json +99 -0
  10. package/bundles/antigravity-plugin/skills/flow-studio/examples/service-map.json +133 -0
  11. package/bundles/antigravity-plugin/skills/flow-studio/examples/state-machine.json +106 -0
  12. package/bundles/antigravity-plugin/skills/flow-studio/examples/ticket-lifecycle.json +107 -0
  13. package/bundles/antigravity-plugin/skills/flow-studio/model.schema.json +27 -8
  14. package/bundles/antigravity-plugin/skills/flow-studio/react/FlowStudioView.test.tsx +82 -0
  15. package/bundles/antigravity-plugin/skills/flow-studio/react/FlowStudioView.tsx +67 -0
  16. package/bundles/antigravity-plugin/skills/flow-studio/react/README.md +37 -0
  17. package/bundles/antigravity-plugin/skills/flow-studio/react/index.ts +3 -0
  18. package/bundles/antigravity-plugin/skills/flow-studio/react/native/FlowCanvas.tsx +58 -0
  19. package/bundles/antigravity-plugin/skills/flow-studio/react/native/Inspector.tsx +27 -0
  20. package/bundles/antigravity-plugin/skills/flow-studio/react/native/index.ts +6 -0
  21. package/bundles/antigravity-plugin/skills/flow-studio/react/native/native.css +18 -0
  22. package/bundles/antigravity-plugin/skills/flow-studio/react/native/native.test.tsx +161 -0
  23. package/bundles/antigravity-plugin/skills/flow-studio/react/native/useFlowGeometry.ts +22 -0
  24. package/bundles/antigravity-plugin/skills/flow-studio/react/native/useTrace.ts +29 -0
  25. package/bundles/antigravity-plugin/skills/flow-studio/react/native/useViewport.ts +75 -0
  26. package/bundles/antigravity-plugin/skills/flow-studio/react/states.tsx +13 -0
  27. package/bundles/antigravity-plugin/skills/flow-studio/references/design-language.md +169 -0
  28. package/bundles/antigravity-plugin/skills/flow-studio/references/implement-in-app.md +213 -0
  29. package/bundles/antigravity-plugin/skills/flow-studio/references/model-schema.md +254 -0
  30. package/bundles/antigravity-plugin/skills/flow-studio/references/notes-and-spec.md +74 -0
  31. package/bundles/antigravity-plugin/skills/flow-studio/references/patterns.md +385 -49
  32. package/bundles/antigravity-plugin/skills/flow-studio/references/ux-rules.md +91 -0
  33. package/bundles/antigravity-plugin/skills/flow-studio/references/verify.md +124 -0
  34. package/bundles/antigravity-plugin/skills/flow-studio/scripts/build-engine.mjs +54 -14
  35. package/bundles/antigravity-plugin/skills/flow-studio/scripts/build.mjs +21 -4
  36. package/bundles/antigravity-plugin/skills/flow-studio/scripts/check.mjs +320 -28
  37. package/bundles/antigravity-plugin/skills/flow-studio/scripts/default-notes.json +67 -0
  38. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/auto-entry-guard.mjs +5 -0
  39. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/auto-entry-guard.test.mjs +14 -0
  40. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/auto-entry.test.mjs +31 -0
  41. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/build-engine.test.mjs +30 -2
  42. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/check-args.mjs +18 -0
  43. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/check-args.test.mjs +15 -0
  44. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/check-selftest.test.mjs +64 -4
  45. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/cli.test.mjs +115 -0
  46. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/demo-pack.mjs +23 -0
  47. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/demo-pack.test.mjs +34 -0
  48. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/docs.test.mjs +128 -0
  49. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/library.test.mjs +26 -0
  50. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/react-proof-cleanup.mjs +12 -0
  51. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/react-proof-cleanup.test.mjs +45 -0
  52. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/react-proof-page.mjs +3 -0
  53. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/react-proof-page.test.mjs +9 -0
  54. package/bundles/antigravity-plugin/skills/flow-studio/scripts/react-proof.mjs +92 -0
  55. package/bundles/antigravity-plugin/skills/flow-studio/scripts/validate.mjs +5 -1
  56. package/bundles/antigravity-plugin/skills/flow-studio/src/autolayout.test.ts +78 -0
  57. package/bundles/antigravity-plugin/skills/flow-studio/src/autolayout.ts +132 -0
  58. package/bundles/antigravity-plugin/skills/flow-studio/src/chrome.ts +11 -7
  59. package/bundles/antigravity-plugin/skills/flow-studio/src/controls.ts +8 -0
  60. package/bundles/antigravity-plugin/skills/flow-studio/src/ctx.ts +16 -0
  61. package/bundles/antigravity-plugin/skills/flow-studio/src/diff.test.ts +31 -0
  62. package/bundles/antigravity-plugin/skills/flow-studio/src/diff.ts +34 -0
  63. package/bundles/antigravity-plugin/skills/flow-studio/src/engine.ts +83 -32
  64. package/bundles/antigravity-plugin/skills/flow-studio/src/inspector.ts +23 -5
  65. package/bundles/antigravity-plugin/skills/flow-studio/src/interact.ts +21 -8
  66. package/bundles/antigravity-plugin/skills/flow-studio/src/layout.test.ts +75 -0
  67. package/bundles/antigravity-plugin/skills/flow-studio/src/layout.ts +49 -11
  68. package/bundles/antigravity-plugin/skills/flow-studio/src/lib.ts +20 -0
  69. package/bundles/antigravity-plugin/skills/flow-studio/src/metrics.test.ts +57 -0
  70. package/bundles/antigravity-plugin/skills/flow-studio/src/metrics.ts +29 -0
  71. package/bundles/antigravity-plugin/skills/flow-studio/src/model.test.ts +62 -2
  72. package/bundles/antigravity-plugin/skills/flow-studio/src/model.ts +101 -16
  73. package/bundles/antigravity-plugin/skills/flow-studio/src/notes.ts +8 -0
  74. package/bundles/antigravity-plugin/skills/flow-studio/src/page.ts +21 -0
  75. package/bundles/antigravity-plugin/skills/flow-studio/src/playback.test.ts +104 -0
  76. package/bundles/antigravity-plugin/skills/flow-studio/src/playback.ts +106 -0
  77. package/bundles/antigravity-plugin/skills/flow-studio/src/playbackui.ts +193 -0
  78. package/bundles/antigravity-plugin/skills/flow-studio/src/render.ts +66 -18
  79. package/bundles/antigravity-plugin/skills/flow-studio/src/routing.test.ts +21 -1
  80. package/bundles/antigravity-plugin/skills/flow-studio/src/routing.ts +32 -0
  81. package/bundles/antigravity-plugin/skills/flow-studio/src/rules.test.ts +48 -1
  82. package/bundles/antigravity-plugin/skills/flow-studio/src/rules.ts +52 -1
  83. package/bundles/antigravity-plugin/skills/flow-studio/src/selection.test.ts +12 -0
  84. package/bundles/antigravity-plugin/skills/flow-studio/src/selection.ts +4 -0
  85. package/bundles/antigravity-plugin/skills/flow-studio/src/stateCss.test.ts +7 -0
  86. package/bundles/antigravity-plugin/skills/flow-studio/src/stateCss.ts +1 -0
  87. package/bundles/antigravity-plugin/skills/flow-studio/src/styles.css +35 -2
  88. package/bundles/antigravity-plugin/skills/flow-studio/src/trace.test.ts +21 -1
  89. package/bundles/antigravity-plugin/skills/flow-studio/src/trace.ts +18 -0
  90. package/bundles/antigravity-plugin/skills/flow-studio/templates/engine.html +986 -74
  91. package/bundles/antigravity-plugin/skills/flow-studio/templates/engine.shell.html +1 -39
  92. package/bundles/antigravity-plugin/skills/flow-studio/templates/flow-studio.css +190 -0
  93. package/bundles/antigravity-plugin/skills/flow-studio/templates/flow-studio.lib.d.mts +1 -0
  94. package/bundles/antigravity-plugin/skills/flow-studio/templates/flow-studio.lib.mjs +2491 -0
  95. package/bundles/antigravity-plugin/skills/flow-studio/templates/shell-body.html +50 -0
  96. package/bundles/codex/.codex/skills/frontend/flow-studio/README.md +46 -7
  97. package/bundles/codex/.codex/skills/frontend/flow-studio/SKILL.md +80 -81
  98. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/README.md +8 -0
  99. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/agent-journey.json +103 -0
  100. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/approval-flow.json +134 -0
  101. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/ci-cd-pipeline.json +132 -0
  102. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/incident-timeline.json +109 -0
  103. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/order-desk.json +99 -0
  104. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/service-map.json +133 -0
  105. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/state-machine.json +106 -0
  106. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/ticket-lifecycle.json +107 -0
  107. package/bundles/codex/.codex/skills/frontend/flow-studio/model.schema.json +27 -8
  108. package/bundles/codex/.codex/skills/frontend/flow-studio/react/FlowStudioView.test.tsx +82 -0
  109. package/bundles/codex/.codex/skills/frontend/flow-studio/react/FlowStudioView.tsx +67 -0
  110. package/bundles/codex/.codex/skills/frontend/flow-studio/react/README.md +37 -0
  111. package/bundles/codex/.codex/skills/frontend/flow-studio/react/index.ts +3 -0
  112. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/FlowCanvas.tsx +58 -0
  113. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/Inspector.tsx +27 -0
  114. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/index.ts +6 -0
  115. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/native.css +18 -0
  116. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/native.test.tsx +161 -0
  117. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/useFlowGeometry.ts +22 -0
  118. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/useTrace.ts +29 -0
  119. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/useViewport.ts +75 -0
  120. package/bundles/codex/.codex/skills/frontend/flow-studio/react/states.tsx +13 -0
  121. package/bundles/codex/.codex/skills/frontend/flow-studio/references/design-language.md +169 -0
  122. package/bundles/codex/.codex/skills/frontend/flow-studio/references/implement-in-app.md +213 -0
  123. package/bundles/codex/.codex/skills/frontend/flow-studio/references/model-schema.md +254 -0
  124. package/bundles/codex/.codex/skills/frontend/flow-studio/references/notes-and-spec.md +74 -0
  125. package/bundles/codex/.codex/skills/frontend/flow-studio/references/patterns.md +385 -49
  126. package/bundles/codex/.codex/skills/frontend/flow-studio/references/ux-rules.md +91 -0
  127. package/bundles/codex/.codex/skills/frontend/flow-studio/references/verify.md +124 -0
  128. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/build-engine.mjs +54 -14
  129. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/build.mjs +21 -4
  130. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/check.mjs +320 -28
  131. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/default-notes.json +67 -0
  132. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/auto-entry-guard.mjs +5 -0
  133. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/auto-entry-guard.test.mjs +14 -0
  134. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/auto-entry.test.mjs +31 -0
  135. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/build-engine.test.mjs +30 -2
  136. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/check-args.mjs +18 -0
  137. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/check-args.test.mjs +15 -0
  138. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/check-selftest.test.mjs +64 -4
  139. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/cli.test.mjs +115 -0
  140. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/demo-pack.mjs +23 -0
  141. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/demo-pack.test.mjs +34 -0
  142. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/docs.test.mjs +128 -0
  143. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/library.test.mjs +26 -0
  144. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/react-proof-cleanup.mjs +12 -0
  145. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/react-proof-cleanup.test.mjs +45 -0
  146. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/react-proof-page.mjs +3 -0
  147. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/react-proof-page.test.mjs +9 -0
  148. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/react-proof.mjs +92 -0
  149. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/validate.mjs +5 -1
  150. package/bundles/codex/.codex/skills/frontend/flow-studio/src/autolayout.test.ts +78 -0
  151. package/bundles/codex/.codex/skills/frontend/flow-studio/src/autolayout.ts +132 -0
  152. package/bundles/codex/.codex/skills/frontend/flow-studio/src/chrome.ts +11 -7
  153. package/bundles/codex/.codex/skills/frontend/flow-studio/src/controls.ts +8 -0
  154. package/bundles/codex/.codex/skills/frontend/flow-studio/src/ctx.ts +16 -0
  155. package/bundles/codex/.codex/skills/frontend/flow-studio/src/diff.test.ts +31 -0
  156. package/bundles/codex/.codex/skills/frontend/flow-studio/src/diff.ts +34 -0
  157. package/bundles/codex/.codex/skills/frontend/flow-studio/src/engine.ts +83 -32
  158. package/bundles/codex/.codex/skills/frontend/flow-studio/src/inspector.ts +23 -5
  159. package/bundles/codex/.codex/skills/frontend/flow-studio/src/interact.ts +21 -8
  160. package/bundles/codex/.codex/skills/frontend/flow-studio/src/layout.test.ts +75 -0
  161. package/bundles/codex/.codex/skills/frontend/flow-studio/src/layout.ts +49 -11
  162. package/bundles/codex/.codex/skills/frontend/flow-studio/src/lib.ts +20 -0
  163. package/bundles/codex/.codex/skills/frontend/flow-studio/src/metrics.test.ts +57 -0
  164. package/bundles/codex/.codex/skills/frontend/flow-studio/src/metrics.ts +29 -0
  165. package/bundles/codex/.codex/skills/frontend/flow-studio/src/model.test.ts +62 -2
  166. package/bundles/codex/.codex/skills/frontend/flow-studio/src/model.ts +101 -16
  167. package/bundles/codex/.codex/skills/frontend/flow-studio/src/notes.ts +8 -0
  168. package/bundles/codex/.codex/skills/frontend/flow-studio/src/page.ts +21 -0
  169. package/bundles/codex/.codex/skills/frontend/flow-studio/src/playback.test.ts +104 -0
  170. package/bundles/codex/.codex/skills/frontend/flow-studio/src/playback.ts +106 -0
  171. package/bundles/codex/.codex/skills/frontend/flow-studio/src/playbackui.ts +193 -0
  172. package/bundles/codex/.codex/skills/frontend/flow-studio/src/render.ts +66 -18
  173. package/bundles/codex/.codex/skills/frontend/flow-studio/src/routing.test.ts +21 -1
  174. package/bundles/codex/.codex/skills/frontend/flow-studio/src/routing.ts +32 -0
  175. package/bundles/codex/.codex/skills/frontend/flow-studio/src/rules.test.ts +48 -1
  176. package/bundles/codex/.codex/skills/frontend/flow-studio/src/rules.ts +52 -1
  177. package/bundles/codex/.codex/skills/frontend/flow-studio/src/selection.test.ts +12 -0
  178. package/bundles/codex/.codex/skills/frontend/flow-studio/src/selection.ts +4 -0
  179. package/bundles/codex/.codex/skills/frontend/flow-studio/src/stateCss.test.ts +7 -0
  180. package/bundles/codex/.codex/skills/frontend/flow-studio/src/stateCss.ts +1 -0
  181. package/bundles/codex/.codex/skills/frontend/flow-studio/src/styles.css +35 -2
  182. package/bundles/codex/.codex/skills/frontend/flow-studio/src/trace.test.ts +21 -1
  183. package/bundles/codex/.codex/skills/frontend/flow-studio/src/trace.ts +18 -0
  184. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/engine.html +986 -74
  185. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/engine.shell.html +1 -39
  186. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/flow-studio.css +190 -0
  187. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/flow-studio.lib.d.mts +1 -0
  188. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/flow-studio.lib.mjs +2491 -0
  189. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/shell-body.html +50 -0
  190. package/bundles/manifest.json +2 -2
  191. package/package.json +1 -1
@@ -70,15 +70,21 @@
70
70
  .bandlabel b { font-size:13px; } .bandlabel .bd { font-size:10.5px; font-weight:700; padding:0 6px; border:1px solid currentColor; color:var(--muted); letter-spacing:.03em; text-transform:uppercase; }
71
71
  .bandlabel .bd.t-ok { color:var(--ok); } .bandlabel .bd.t-warn { color:var(--warn); } .bandlabel .bn { font-size:11.5px; color:var(--muted); }
72
72
  .node { position:absolute; background:var(--panel); border:1px solid var(--edge); border-radius:8px; padding:5px 10px 7px; box-shadow:var(--shadow); transition:opacity .35s, transform .35s, box-shadow .2s, border-color .2s; cursor:pointer; z-index:2; }
73
+ .node.gateway { border:0; background:transparent; box-shadow:none; padding:0; border-radius:0; display:flex; align-items:center; justify-content:center; }
74
+ .node.gateway svg.gw { position:absolute; inset:0; width:100%; height:100%; overflow:visible; }
75
+ .node.gateway polygon { stroke-width:2; vector-effect:non-scaling-stroke; }
76
+ .node.gateway .gw-title { position:relative; width:58%; text-align:center; font-size:11px; font-weight:600; line-height:1.2; color:var(--fg); overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow-wrap:anywhere; }
77
+ .node.gateway:hover, .node.gateway.lit, .node.gateway.sel { filter:drop-shadow(0 0 7px var(--accent)); }
73
78
  .node.wide { overflow:hidden; }
74
79
  .node:hover { border-color:var(--accent); box-shadow:0 0 0 3px var(--glow), var(--shadow); }
75
80
  .node .t { display:flex; gap:4px; align-items:flex-start; font-weight:600; line-height:1.25; font-size:13px; }
76
81
  .node .t .name { overflow-wrap:anywhere; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
82
+ .node .fold { flex:none; color:var(--accent); font-size:11px; }
77
83
  .node .s { color:var(--muted); font-size:12px; line-height:1.3; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
78
84
  .ev { font-size:10.5px; font-weight:700; letter-spacing:.03em; }
79
85
  .node.wide .t { font-weight:500; } .node.wide .s { white-space:normal; }
80
- .node.ref { padding:4px 9px; border-style:dashed; box-shadow:none; background:color-mix(in srgb, var(--panel) 70%, transparent); } .node.ref .t { font-size:12px; } .node.ref .s { font-size:11px; }
81
- .node.sel { outline:2px solid var(--accent); outline-offset:2px; } .node.dim { opacity:.28; } .node.off { opacity:0; transform:translateY(8px); pointer-events:none; }
86
+ .node.ref { padding:4px 9px; border-style:dashed; box-shadow:none; background:color-mix(in srgb, var(--panel) 70%, transparent); } .node.ref .t { font-size:12px; } .node.ref .t .name { display:block; min-width:0; white-space:nowrap; text-overflow:ellipsis; } .node.ref .s { font-size:11px; }
87
+ .node.sel { outline:2px solid var(--accent); outline-offset:2px; } .node.dim { opacity:.28; } .node.off { opacity:0; visibility:hidden; transform:translateY(8px); pointer-events:none; }
82
88
  .node .bar { position:absolute; left:0; right:0; bottom:0; display:flex; height:4px; overflow:hidden; border-radius:0 0 7px 7px; background:var(--panel2); } .node .bar i { display:block; height:100%; } .bar .good { background:var(--ok); } .bar .neutral { background:var(--muted); } .bar .gap { background:var(--warn); }
83
89
  .newdot { display:none; width:7px; height:7px; border-radius:50%; background:var(--accent); margin:5px 4px 0 0; flex:none; } body.alt .node.isnew .newdot { display:inline-block; }
84
90
  body.alt .node.isnew.pulse { animation:newring 1.8s ease-out 1; }
@@ -155,3 +161,30 @@
155
161
  @keyframes beat { 0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--ok) 60%, transparent);} 70%,100%{box-shadow:0 0 0 9px transparent;} }
156
162
  @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation:none !important; transition:none !important; } path.edge.hot { stroke-dasharray:none; } }
157
163
  @media (max-width: 1100px) { .summary { grid-template-columns:1fr; } .workspace { height:auto; } .workspace .canvaswrap { height:75vh; } .insp { width:min(340px, 92%); } #drawer { width:100%; } .layerstrip { grid-template-columns:repeat(3,1fr) !important; } }
164
+ #banner:not([hidden]) { margin:0 14px 10px; padding:8px 12px; color:var(--crit); background:var(--crit-bg); border:1px solid var(--crit); font-size:12px; }
165
+ #playbar:not([hidden]) { display:flex; flex-wrap:wrap; align-items:center; gap:7px; padding:7px 12px; border-top:1px solid var(--rule); background:var(--panel); font-size:12px; }
166
+ #playbar select, #playbar button { font-size:11px; }
167
+ #pbScrub { flex:1 1 140px; min-width:90px; }
168
+ #pbCaption { flex:1 1 100%; color:var(--fg); }
169
+ #pbCounter { font:11px var(--mono); color:var(--fg); }
170
+ #pbCounter.warn { color:var(--warn); } #pbCounter.breach { color:var(--crit); font-weight:700; }
171
+ #pbChoices { display:flex; gap:5px; flex-wrap:wrap; } #pbChoices:empty { display:none; }
172
+ #pbChoices .suggested { border-color:var(--accent); box-shadow:inset 0 -2px 0 var(--accent); }
173
+ circle.token { fill:var(--accent); stroke:var(--panel); stroke-width:2; filter:drop-shadow(0 0 5px var(--accent)); pointer-events:none; }
174
+ path.edge.playing { stroke:var(--accent); stroke-width:3; filter:drop-shadow(0 0 5px var(--accent)); opacity:1; }
175
+ path.edge.visited { stroke:var(--accent); opacity:.7; }
176
+ .node.playing { outline:2px solid var(--accent); outline-offset:2px; }
177
+ /* The pulse runs only while the player really plays; a paused step keeps the plain outline. */
178
+ body.playing .node.playing { animation:selpulse 1.8s ease-out infinite; }
179
+ .node.visited::after { content:"✓"; position:absolute; right:3px; bottom:1px; color:var(--accent); font-size:11px; }
180
+ .node.updated { animation:newring 1.2s ease-out 1; }
181
+ .node.added { animation:rise 1.2s ease-out 1; }
182
+ .node .t .name { min-width:0; flex:1 1 auto; }
183
+ .node .metric-badge { flex:none; max-width:72px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; position:relative; padding-bottom:3px; text-align:right; font:11px var(--mono); color:var(--accent); }
184
+ .node .metric-badge i { display:block; height:2px; position:absolute; bottom:0; left:0; background:var(--accent); }
185
+ .node .s.has-spark { padding-right:60px; }
186
+ .node .spark { position:absolute; right:6px; bottom:4px; overflow:visible; }
187
+ .node.gateway .metric-badge { position:absolute; right:9px; top:18px; }
188
+ .node.gateway:has(.metric-badge) .gw-title, .node.gateway.has-metrics .gw-title { width:45%; margin-right:42px; }
189
+ .node .spark polyline, .metrics polyline { fill:none; stroke:var(--accent); stroke-width:1.5; }
190
+ .metrics svg { max-width:100%; margin-top:8px; } .metric-range { display:flex; justify-content:space-between; width:300px; max-width:100%; font:10px var(--mono); color:var(--muted); }
@@ -1,6 +1,6 @@
1
1
  import { test } from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { relatives, connected } from "./trace.ts";
3
+ import { relatives, connected, withinHops } from "./trace.ts";
4
4
 
5
5
  const edges = [{ from: "a", to: "b" }, { from: "b", to: "c" }, { from: "x", to: "c" }, { from: "c", to: "d" }];
6
6
  const id = (s: string) => s;
@@ -17,4 +17,24 @@ test("a cycle terminates", () => {
17
17
  const cyc = [{ from: "a", to: "b" }, { from: "b", to: "a" }];
18
18
  assert.deepEqual([...relatives(cyc, id, "a", "down")].sort(), ["a", "b"]);
19
19
  });
20
+ test("a loop-back does not break relatives", () => {
21
+ const cycle = [{ from: "a", to: "b" }, { from: "b", to: "c" }, { from: "c", to: "a" }];
22
+ assert.deepEqual([...relatives(cycle, id, "a", "down")].sort(), ["a", "b", "c"]);
23
+ assert.deepEqual([...relatives(cycle, id, "c", "up")].sort(), ["a", "b", "c"]);
24
+ });
20
25
  test("an unknown start returns only itself", () => assert.deepEqual([...relatives(edges, id, "nope", "down")], ["nope"]));
26
+
27
+ test("withinHops includes starts and walks links in either direction", () => {
28
+ assert.deepEqual([...withinHops(edges, id, ["b"], 0)].sort(), ["b"]);
29
+ assert.deepEqual([...withinHops(edges, id, ["b"], 1)].sort(), ["a", "b", "c"]);
30
+ assert.deepEqual([...withinHops(edges, id, ["b"], 2)].sort(), ["a", "b", "c", "d", "x"]);
31
+ });
32
+ test("withinHops canonicalizes aliases and joins multiple starts", () => {
33
+ const canon = (s: string) => s === "b2" ? "b" : s;
34
+ assert.deepEqual([...withinHops(edges, canon, ["b2", "x"], 1)].sort(), ["a", "b", "c", "x"]);
35
+ });
36
+ test("withinHops terminates on cycles and treats a negative radius as zero", () => {
37
+ const cycle = [{ from: "a", to: "b" }, { from: "b", to: "a" }];
38
+ assert.deepEqual([...withinHops(cycle, id, ["a"], 9)].sort(), ["a", "b"]);
39
+ assert.deepEqual([...withinHops(cycle, id, ["a"], -1)], ["a"]);
40
+ });
@@ -20,3 +20,21 @@ export function relatives(edges: Edge[], canonOf: (id: string) => string, start:
20
20
  export function connected(edges: Edge[], canonOf: (id: string) => string, start: string): Set<string> {
21
21
  return new Set([...relatives(edges, canonOf, start, "up"), ...relatives(edges, canonOf, start, "down")]);
22
22
  }
23
+
24
+ /** Undirected breadth-first radius around one or more nodes. */
25
+ export function withinHops(edges: Edge[], canonOf: (id: string) => string, starts: string[], n: number): Set<string> {
26
+ const found = new Set(starts.map(canonOf));
27
+ const radius = Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 0;
28
+ let frontier = [...found];
29
+ for (let hop = 0; hop < radius && frontier.length; hop++) {
30
+ const next: string[] = [];
31
+ const current = new Set(frontier);
32
+ for (const edge of edges) {
33
+ const a = canonOf(edge.from), b = canonOf(edge.to);
34
+ if (current.has(a) && !found.has(b)) { found.add(b); next.push(b); }
35
+ if (current.has(b) && !found.has(a)) { found.add(a); next.push(a); }
36
+ }
37
+ frontier = next;
38
+ }
39
+ return found;
40
+ }