@stephen-netu/design-system 0.2.2 → 0.5.1

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 (229) hide show
  1. package/dist/d0-data-viz/BarChart.test.js +53 -0
  2. package/dist/d0-data-viz/CheckpointBar.test.js +83 -0
  3. package/dist/d0-data-viz/CheckpointPill.svelte +1 -1
  4. package/dist/d0-data-viz/CheckpointPill.test.js +78 -0
  5. package/dist/d0-data-viz/ConnectionHealth.test.js +70 -0
  6. package/dist/d0-data-viz/MetricCard.test.js +57 -0
  7. package/dist/d0-data-viz/SparkLine.test.js +47 -0
  8. package/dist/d0-data-viz/StorageMap.test.js +63 -0
  9. package/dist/d0-data-viz/Timeline.test.js +62 -0
  10. package/dist/d0-data-viz/WorldLineTimeline.test.js +99 -0
  11. package/dist/d0-data-viz/checkpoint-types.js +1 -1
  12. package/dist/d0-data-viz/storage-map-types.d.ts.map +1 -1
  13. package/dist/d0-data-viz/storage-map-types.js +6 -3
  14. package/dist/d0-data-viz/theme-integration.js +1 -1
  15. package/dist/demos/AmbientPrimitivesDemo.svelte +103 -0
  16. package/dist/demos/AmbientPrimitivesDemo.svelte.d.ts +5 -0
  17. package/dist/demos/AmbientPrimitivesDemo.svelte.d.ts.map +1 -0
  18. package/dist/demos/NodepadBlocksShowcase.svelte +2 -1
  19. package/dist/demos/NodepadBlocksShowcase.svelte.d.ts.map +1 -1
  20. package/dist/demos/index.d.ts +1 -0
  21. package/dist/demos/index.d.ts.map +1 -1
  22. package/dist/demos/index.js +1 -0
  23. package/dist/editor/CodeEditor.svelte +83 -26
  24. package/dist/editor/CodeEditor.svelte.d.ts +0 -1
  25. package/dist/editor/CodeEditor.svelte.d.ts.map +1 -1
  26. package/dist/editor/ExternalChangeDialog.svelte +117 -0
  27. package/dist/editor/ExternalChangeDialog.svelte.d.ts +13 -0
  28. package/dist/editor/ExternalChangeDialog.svelte.d.ts.map +1 -0
  29. package/dist/editor/MarkdownEditor.svelte +104 -617
  30. package/dist/editor/MarkdownEditor.svelte.d.ts +1 -1
  31. package/dist/editor/MarkdownEditor.svelte.d.ts.map +1 -1
  32. package/dist/editor/codemirror-config.d.ts +36 -0
  33. package/dist/editor/codemirror-config.d.ts.map +1 -0
  34. package/dist/editor/codemirror-config.js +87 -0
  35. package/dist/editor/components/CorrectionTooltip.svelte +180 -159
  36. package/dist/editor/components/CorrectionTooltip.svelte.d.ts +7 -23
  37. package/dist/editor/components/CorrectionTooltip.svelte.d.ts.map +1 -1
  38. package/dist/editor/extensions/autocomplete.d.ts +2 -2
  39. package/dist/editor/extensions/autocomplete.d.ts.map +1 -1
  40. package/dist/editor/extensions/autocomplete.test.js +121 -0
  41. package/dist/editor/extensions/spellcheck.d.ts +21 -3
  42. package/dist/editor/extensions/spellcheck.d.ts.map +1 -1
  43. package/dist/editor/extensions/spellcheck.js +51 -9
  44. package/dist/editor/extensions/spellcheck.test.js +168 -0
  45. package/dist/editor/extensions/types.d.ts +2 -1
  46. package/dist/editor/extensions/types.d.ts.map +1 -1
  47. package/dist/editor/index.d.ts +2 -1
  48. package/dist/editor/index.d.ts.map +1 -1
  49. package/dist/editor/index.js +1 -0
  50. package/dist/editor/markdown-preview.d.ts +9 -0
  51. package/dist/editor/markdown-preview.d.ts.map +1 -0
  52. package/dist/editor/markdown-preview.js +19 -0
  53. package/dist/editor/markdown-preview.test.js +38 -0
  54. package/dist/editor/useCodeMirror.d.ts +23 -0
  55. package/dist/editor/useCodeMirror.d.ts.map +1 -0
  56. package/dist/editor/useCodeMirror.js +40 -0
  57. package/dist/editor/useFilePersistence.d.ts +32 -0
  58. package/dist/editor/useFilePersistence.d.ts.map +1 -0
  59. package/dist/editor/useFilePersistence.js +117 -0
  60. package/dist/fabric/ai/ChatPanel.svelte +61 -62
  61. package/dist/fabric/ai/ChatPanel.svelte.d.ts.map +1 -1
  62. package/dist/fabric/ai/ResearchPanel.svelte +31 -24
  63. package/dist/fabric/data/FileTree.svelte +195 -0
  64. package/dist/fabric/data/FileTree.svelte.d.ts +19 -0
  65. package/dist/fabric/data/FileTree.svelte.d.ts.map +1 -0
  66. package/dist/fabric/data/FileTree.test.js +127 -0
  67. package/dist/fabric/data/index.d.ts +5 -9
  68. package/dist/fabric/data/index.d.ts.map +1 -1
  69. package/dist/fabric/data/index.js +4 -1
  70. package/dist/fabric/index.d.ts +1 -0
  71. package/dist/fabric/index.d.ts.map +1 -1
  72. package/dist/fabric/index.js +2 -0
  73. package/dist/fabric/input/KeyboardShortcuts.svelte +23 -21
  74. package/dist/fabric/layout/CollapsiblePanel.svelte +8 -10
  75. package/dist/fabric/navigation/ActivityRail.svelte +3 -5
  76. package/dist/fabric/navigation/ActivityRail.svelte.d.ts.map +1 -1
  77. package/dist/g0-generative/components/Text.svelte +38 -0
  78. package/dist/g0-generative/components/Text.svelte.d.ts +9 -0
  79. package/dist/g0-generative/components/Text.svelte.d.ts.map +1 -0
  80. package/dist/g0-generative/index.d.ts +1 -0
  81. package/dist/g0-generative/index.d.ts.map +1 -1
  82. package/dist/g0-generative/index.js +1 -0
  83. package/dist/g0-generative/library/ComponentLibrary.d.ts +4 -0
  84. package/dist/g0-generative/library/ComponentLibrary.d.ts.map +1 -1
  85. package/dist/g0-generative/library/ComponentLibrary.js +10 -0
  86. package/dist/g0-generative/library/ComponentLibrary.test.js +135 -0
  87. package/dist/g0-generative/renderer/OpenUIRenderer.svelte +16 -56
  88. package/dist/g0-generative/renderer/OpenUIRenderer.svelte.d.ts.map +1 -1
  89. package/dist/g0-generative/renderer/OpenUIRenderer.test.js +143 -0
  90. package/dist/g0-geo/SovereignMap.svelte +13 -13
  91. package/dist/g0-geo/SovereignMap.svelte.d.ts.map +1 -1
  92. package/dist/index.d.ts +0 -1
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +3 -1
  95. package/dist/l0-layout/block-writer/BlockContent.svelte +23 -22
  96. package/dist/l0-layout/block-writer/BlockContent.svelte.d.ts.map +1 -1
  97. package/dist/l0-layout/block-writer/BlockContent.test.js +47 -0
  98. package/dist/l0-layout/block-writer/markdown.js +11 -11
  99. package/dist/l0-layout/block-writer/markdown.test.js +81 -0
  100. package/dist/l0-layout/card/StackedCard.svelte +87 -0
  101. package/dist/l0-layout/card/StackedCard.svelte.d.ts +9 -0
  102. package/dist/l0-layout/card/StackedCard.svelte.d.ts.map +1 -0
  103. package/dist/l0-layout/card/StackedCard.test.js +29 -0
  104. package/dist/l0-layout/index.d.ts +1 -0
  105. package/dist/l0-layout/index.d.ts.map +1 -1
  106. package/dist/l0-layout/index.js +1 -0
  107. package/dist/l0-layout/kanban/KanbanBoard.svelte +1 -1
  108. package/dist/l0-layout/kanban/KanbanColumn.svelte +1 -1
  109. package/dist/n1-flow-canvas/FlowCanvas.svelte +50 -6
  110. package/dist/n1-flow-canvas/FlowCanvas.svelte.d.ts.map +1 -1
  111. package/dist/n1-flow-canvas/index.d.ts +2 -0
  112. package/dist/n1-flow-canvas/index.d.ts.map +1 -1
  113. package/dist/n1-flow-canvas/index.js +1 -0
  114. package/dist/n1-flow-canvas/pipeline-adapter.d.ts +37 -0
  115. package/dist/n1-flow-canvas/pipeline-adapter.d.ts.map +1 -0
  116. package/dist/n1-flow-canvas/pipeline-adapter.js +160 -0
  117. package/dist/n2-manifold-disk/ManifoldDisk.svelte +62 -35
  118. package/dist/n2-manifold-disk/ManifoldDisk.svelte.d.ts.map +1 -1
  119. package/dist/n4-adaptive-manifold/AdaptiveManifold.svelte +23 -19
  120. package/dist/n4-adaptive-manifold/AdaptiveManifold.svelte.d.ts.map +1 -1
  121. package/dist/n4-adaptive-manifold/types.d.ts +3 -0
  122. package/dist/n4-adaptive-manifold/types.d.ts.map +1 -1
  123. package/dist/p0-primitives/ThemeProvider.svelte +28 -7
  124. package/dist/p0-primitives/ThemeProvider.svelte.d.ts +5 -0
  125. package/dist/p0-primitives/ThemeProvider.svelte.d.ts.map +1 -1
  126. package/dist/p0-primitives/canvas-theme.d.ts +21 -1
  127. package/dist/p0-primitives/canvas-theme.d.ts.map +1 -1
  128. package/dist/p0-primitives/canvas-theme.js +78 -23
  129. package/dist/p0-primitives/debug.d.ts +6 -0
  130. package/dist/p0-primitives/debug.d.ts.map +1 -0
  131. package/dist/p0-primitives/debug.js +21 -0
  132. package/dist/p0-primitives/theme.d.ts.map +1 -1
  133. package/dist/p0-primitives/theme.js +11 -31
  134. package/dist/p0-primitives/tokens/ambient.css +76 -0
  135. package/dist/p0-primitives/tokens/tokens.css +124 -29
  136. package/dist/p0-primitives/utils/index.d.ts +2 -0
  137. package/dist/p0-primitives/utils/index.d.ts.map +1 -1
  138. package/dist/p0-primitives/utils/index.js +1 -0
  139. package/dist/p0-primitives/utils/reveal.d.ts +15 -0
  140. package/dist/p0-primitives/utils/reveal.d.ts.map +1 -0
  141. package/dist/p0-primitives/utils/reveal.js +29 -0
  142. package/dist/p0-primitives/utils/reveal.test.js +95 -0
  143. package/dist/s0-state/SyncStatus.svelte +1 -1
  144. package/dist/s0-state/index.d.ts +2 -1
  145. package/dist/s0-state/index.d.ts.map +1 -1
  146. package/dist/s0-state/index.js +2 -0
  147. package/dist/s0-state/modal-stack-store.svelte.d.ts +3 -3
  148. package/dist/s0-state/modal-stack-store.svelte.js +2 -2
  149. package/dist/spatial/ForceSimulation.svelte.js +1 -1
  150. package/dist/spatial/ParticleAssembleMark.svelte +285 -0
  151. package/dist/spatial/ParticleAssembleMark.svelte.d.ts +24 -0
  152. package/dist/spatial/ParticleAssembleMark.svelte.d.ts.map +1 -0
  153. package/dist/spatial/ZoneTiler.test.js +90 -0
  154. package/dist/spatial/index.js +1 -0
  155. package/dist/spatial/particle-assemble-math.d.ts +22 -0
  156. package/dist/spatial/particle-assemble-math.d.ts.map +1 -0
  157. package/dist/spatial/particle-assemble-math.js +26 -0
  158. package/dist/spatial/particle-assemble-math.test.js +56 -0
  159. package/dist/t0-transport/substrate.svelte.d.ts.map +1 -1
  160. package/dist/t0-transport/substrate.svelte.js +10 -4
  161. package/dist/u0-primitives/LockIndicator.svelte +16 -14
  162. package/dist/u0-primitives/VerticalToolbar.svelte +24 -21
  163. package/dist/u0-primitives/accordion/Accordion.svelte +8 -7
  164. package/dist/u0-primitives/animated-icon/AnimatedIcon.svelte +3 -3
  165. package/dist/u0-primitives/animated-icon/AnimatedIcon.test.js +82 -0
  166. package/dist/u0-primitives/avatar/Avatar.svelte +14 -14
  167. package/dist/u0-primitives/avatar/Avatar.test.js +81 -0
  168. package/dist/u0-primitives/badge/Badge.svelte +6 -4
  169. package/dist/u0-primitives/badge/ValidityBadge.svelte +4 -27
  170. package/dist/u0-primitives/button/Button.svelte +15 -12
  171. package/dist/u0-primitives/card/Card.svelte +37 -15
  172. package/dist/u0-primitives/card/Card.test.js +86 -0
  173. package/dist/u0-primitives/command-palette/CommandPalette.svelte +48 -43
  174. package/dist/u0-primitives/command-palette/CommandPalette.test.js +202 -0
  175. package/dist/u0-primitives/dropdown/Dropdown.svelte +20 -25
  176. package/dist/u0-primitives/grant-badge/GrantStatusBadge.svelte +2 -2
  177. package/dist/u0-primitives/index.d.ts +6 -0
  178. package/dist/u0-primitives/index.d.ts.map +1 -1
  179. package/dist/u0-primitives/index.js +8 -0
  180. package/dist/u0-primitives/input/Input.svelte +77 -24
  181. package/dist/u0-primitives/input/Input.svelte.d.ts +2 -10
  182. package/dist/u0-primitives/input/Input.svelte.d.ts.map +1 -1
  183. package/dist/u0-primitives/input/Input.test.js +81 -1
  184. package/dist/u0-primitives/live-dot/LiveDot.svelte +56 -0
  185. package/dist/u0-primitives/live-dot/LiveDot.svelte.d.ts +11 -0
  186. package/dist/u0-primitives/live-dot/LiveDot.svelte.d.ts.map +1 -0
  187. package/dist/u0-primitives/live-dot/LiveDot.test.js +41 -0
  188. package/dist/u0-primitives/live-dot/index.d.ts +3 -0
  189. package/dist/u0-primitives/live-dot/index.d.ts.map +1 -0
  190. package/dist/u0-primitives/live-dot/index.js +1 -0
  191. package/dist/u0-primitives/modal/Modal.svelte +22 -19
  192. package/dist/u0-primitives/modal/Modal.test.js +1 -1
  193. package/dist/u0-primitives/spinner/Spinner.svelte +11 -79
  194. package/dist/u0-primitives/spinner/Spinner.svelte.d.ts.map +1 -1
  195. package/dist/u0-primitives/steps/Steps.svelte +18 -19
  196. package/dist/u0-primitives/surface-state/SurfaceState.svelte +7 -7
  197. package/dist/u0-primitives/tabs/Tabs.svelte +13 -11
  198. package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.svelte +94 -0
  199. package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.svelte.d.ts +16 -0
  200. package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.svelte.d.ts.map +1 -0
  201. package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.test.js +48 -0
  202. package/dist/u0-primitives/theme-swatch-group/index.d.ts +3 -0
  203. package/dist/u0-primitives/theme-swatch-group/index.d.ts.map +1 -0
  204. package/dist/u0-primitives/theme-swatch-group/index.js +1 -0
  205. package/dist/u0-primitives/toggle/Toggle.svelte +10 -11
  206. package/dist/u0-primitives/tooltip/Tooltip.svelte +9 -9
  207. package/dist/u0-primitives/web-components/button-component.d.ts +5 -1
  208. package/dist/u0-primitives/web-components/button-component.d.ts.map +1 -1
  209. package/dist/u0-primitives/web-components/button-component.js +72 -33
  210. package/dist/u0-primitives/web-components/index.d.ts +3 -0
  211. package/dist/u0-primitives/web-components/index.d.ts.map +1 -0
  212. package/dist/u0-primitives/web-components/index.js +2 -0
  213. package/dist/u0-primitives/web-components/input-component.d.ts +5 -1
  214. package/dist/u0-primitives/web-components/input-component.d.ts.map +1 -1
  215. package/dist/u0-primitives/web-components/input-component.js +56 -39
  216. package/dist/u0-primitives/web-components/web-components.test.js +96 -0
  217. package/dist/u0-primitives/window-frame/WindowFrame.svelte +87 -0
  218. package/dist/u0-primitives/window-frame/WindowFrame.svelte.d.ts +10 -0
  219. package/dist/u0-primitives/window-frame/WindowFrame.svelte.d.ts.map +1 -0
  220. package/dist/u0-primitives/window-frame/WindowFrame.test.js +31 -0
  221. package/dist/u0-primitives/window-frame/index.d.ts +3 -0
  222. package/dist/u0-primitives/window-frame/index.d.ts.map +1 -0
  223. package/dist/u0-primitives/window-frame/index.js +1 -0
  224. package/dist/x0-enchanted-blocks/components/NodepadBlock.svelte +11 -2
  225. package/dist/x0-enchanted-blocks/components/NodepadBlock.svelte.d.ts.map +1 -1
  226. package/package.json +44 -28
  227. package/src/p0-primitives/tokens/ambient.css +76 -0
  228. package/src/p0-primitives/tokens/tokens.css +124 -29
  229. package/src/p0-primitives/tokens/tokens.json +42 -24
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { fitScale, easeOutCubic, lerp, particleFrame } from './particle-assemble-math';
3
+ describe('fitScale', () => {
4
+ it('centers a shape narrower than the canvas', () => {
5
+ const { scale, dx, dy } = fitScale(100, 100, 200, 200, 1);
6
+ expect(scale).toBe(2);
7
+ expect(dx).toBe(0);
8
+ expect(dy).toBe(0);
9
+ });
10
+ it('applies padRatio to shrink the fitted shape', () => {
11
+ const { scale, dx } = fitScale(100, 100, 200, 200, 0.5);
12
+ expect(scale).toBe(1);
13
+ expect(dx).toBe(50);
14
+ });
15
+ it('fits by the constraining dimension for non-square aspect ratios', () => {
16
+ const { scale, dx, dy } = fitScale(270, 305, 1000, 1000, 1);
17
+ // height is the tighter constraint (305 > 270), so scale is bound by height
18
+ expect(scale).toBeCloseTo(1000 / 305, 5);
19
+ expect(dx).toBeGreaterThan(0);
20
+ expect(dy).toBe(0);
21
+ });
22
+ });
23
+ describe('easeOutCubic', () => {
24
+ it('maps 0 to 0 and 1 to 1', () => {
25
+ expect(easeOutCubic(0)).toBe(0);
26
+ expect(easeOutCubic(1)).toBe(1);
27
+ });
28
+ it('clamps progress outside [0, 1]', () => {
29
+ expect(easeOutCubic(-0.5)).toBe(0);
30
+ expect(easeOutCubic(1.5)).toBe(1);
31
+ });
32
+ it('decelerates — front-loads more progress than a linear ramp', () => {
33
+ expect(easeOutCubic(0.5)).toBeGreaterThan(0.5);
34
+ });
35
+ });
36
+ describe('lerp', () => {
37
+ it('interpolates linearly', () => {
38
+ expect(lerp(0, 10, 0)).toBe(0);
39
+ expect(lerp(0, 10, 1)).toBe(10);
40
+ expect(lerp(0, 10, 0.5)).toBe(5);
41
+ });
42
+ });
43
+ describe('particleFrame', () => {
44
+ it('starts at the origin position with alpha 0.3 when eased is 0', () => {
45
+ const frame = particleFrame({ x: 10, y: 20, tx: 100, ty: 200 }, 0);
46
+ expect(frame.x).toBe(10);
47
+ expect(frame.y).toBe(20);
48
+ expect(frame.alpha).toBeCloseTo(0.3);
49
+ });
50
+ it('ends at the target position with alpha 1 when eased is 1', () => {
51
+ const frame = particleFrame({ x: 10, y: 20, tx: 100, ty: 200 }, 1);
52
+ expect(frame.x).toBe(100);
53
+ expect(frame.y).toBe(200);
54
+ expect(frame.alpha).toBeCloseTo(1);
55
+ });
56
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"substrate.svelte.d.ts","sourceRoot":"","sources":["../../src/t0-transport/substrate.svelte.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,GACtB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gCAAgC;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkDD,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,mBAAmB,CAErB"}
1
+ {"version":3,"file":"substrate.svelte.d.ts","sourceRoot":"","sources":["../../src/t0-transport/substrate.svelte.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,GACtB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gCAAgC;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAwDD,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,mBAAmB,CAErB"}
@@ -20,15 +20,21 @@ class SubstrateConnectionImpl {
20
20
  console.warn('[SubstrateConnection] Tauri bridge not available — skipping listeners');
21
21
  return;
22
22
  }
23
+ // Register listener FIRST: prevents missing a Connected transition that fires
24
+ // while invoke() is in-flight (the race that caused the persistent OFFLINE overlay).
25
+ this.unlisten = await listen(this.options.stateEvent ?? 'substrate:state', (event) => {
26
+ this.state = event.payload;
27
+ });
23
28
  try {
24
29
  this.state = await invoke(this.options.getStateCommand ?? 'substrate_get_state');
25
30
  }
26
31
  catch (error) {
27
- this.state = { status: 'failed', reason: error instanceof Error ? error.message : 'Unable to read substrate state' };
32
+ // A failed *read* of supervisor state is not a connection failure don't
33
+ // paint the degraded overlay on it. Stay non-degraded (`starting`) and let
34
+ // the `substrate:state` listener deliver the real state.
35
+ console.warn('[SubstrateConnection] substrate_get_state read failed; awaiting events', error);
36
+ this.state = { status: 'starting' };
28
37
  }
29
- this.unlisten = await listen(this.options.stateEvent ?? 'substrate:state', (event) => {
30
- this.state = event.payload;
31
- });
32
38
  }
33
39
  async retry() {
34
40
  this.state = { status: 'reconnecting', attempt: 0 };
@@ -122,7 +122,6 @@
122
122
  .lock-indicator {
123
123
  position: relative;
124
124
  background: var(--color-bg-panel, #181c20);
125
- border-radius: var(--radius-md, 4px);
126
125
  overflow: hidden;
127
126
  }
128
127
 
@@ -151,7 +150,7 @@
151
150
  .lock-avatar {
152
151
  width: 28px;
153
152
  height: 28px;
154
- border-radius: var(--radius-full, 9999px);
153
+ border-radius: var(--radius-full); /* avatar — intentionally round */
155
154
  flex-shrink: 0;
156
155
  }
157
156
 
@@ -186,20 +185,23 @@
186
185
  }
187
186
 
188
187
  .lock-request-btn {
189
- padding: var(--space-1, 0.25rem) var(--space-2, 0.5rem);
190
- background: var(--color-accent-subtle, var(--color-accent-muted));
191
- color: var(--color-accent, var(--color-accent));
192
- border: 1px solid var(--color-accent, var(--color-accent));
193
- border-radius: var(--radius-sm, 2px);
194
- font-size: var(--text-xs, 0.75rem);
188
+ padding: 3px 8px;
189
+ background: var(--color-accent-subtle);
190
+ color: var(--color-accent);
191
+ border: 1px solid var(--color-accent);
192
+ font-family: var(--font-mono);
193
+ font-size: var(--ui-label, 10px);
194
+ letter-spacing: 0.06em;
195
+ text-transform: uppercase;
195
196
  cursor: pointer;
196
- transition: all 0.15s ease;
197
+ transition: background var(--transition-fast) linear, color var(--transition-fast) linear;
197
198
  flex-shrink: 0;
198
199
  }
199
200
 
201
+ /* Hover fill justified: confirms destructive intent (same logic as toggle thumb) */
200
202
  .lock-request-btn:hover {
201
203
  background: var(--color-accent);
202
- color: var(--color-text-inverse);
204
+ color: var(--color-bg-app);
203
205
  }
204
206
 
205
207
  .lock-bar {
@@ -214,10 +216,10 @@
214
216
  .lock-spinner {
215
217
  width: 16px;
216
218
  height: 16px;
217
- border: 2px solid var(--color-accent, var(--color-accent));
218
- border-top-color: transparent;
219
- border-radius: var(--radius-full, 9999px);
220
- animation: spin 1s linear infinite;
219
+ border: 1.5px solid var(--border-default);
220
+ border-top-color: var(--color-accent);
221
+ border-right-color: var(--color-accent);
222
+ animation: spin 0.7s linear infinite;
221
223
  }
222
224
 
223
225
  @keyframes spin {
@@ -112,14 +112,14 @@
112
112
  align-items: center;
113
113
  justify-content: center;
114
114
  width: 36px;
115
- height: 36px;
115
+ height: var(--ui-control-h, 36px);
116
116
  padding: 0;
117
117
  background: transparent;
118
118
  border: 1px solid transparent;
119
- border-radius: var(--radius-md, 4px);
119
+ border-radius: 0;
120
120
  color: var(--color-text-muted, #6b6558);
121
121
  cursor: pointer;
122
- transition: all 0.15s ease;
122
+ transition: color var(--transition-fast) linear, background var(--transition-fast) linear;
123
123
  }
124
124
 
125
125
  .toolbar-item:hover:not(:disabled) {
@@ -138,16 +138,15 @@
138
138
  border-color: var(--color-accent-muted, var(--color-accent-muted));
139
139
  }
140
140
 
141
+ /* Active live-edge on the left — accent as functional state indicator */
141
142
  .toolbar-item.active::before {
142
143
  content: '';
143
144
  position: absolute;
144
145
  left: 0;
145
- top: 50%;
146
- transform: translateY(-50%);
147
- width: 3px;
148
- height: 20px;
149
- background: var(--color-accent, var(--color-accent));
150
- border-radius: 0 2px 2px 0;
146
+ top: 0;
147
+ bottom: 0;
148
+ width: 2px;
149
+ background: var(--color-accent);
151
150
  }
152
151
 
153
152
  .toolbar-item:disabled {
@@ -168,34 +167,38 @@
168
167
  height: 100%;
169
168
  }
170
169
 
170
+ /* Badge fill justified: count is a functional readout (unread count), not decoration */
171
171
  .toolbar-badge {
172
172
  position: absolute;
173
173
  top: 2px;
174
174
  right: 2px;
175
175
  min-width: 14px;
176
176
  height: 14px;
177
- padding: 0 4px;
178
- background: var(--color-accent, var(--color-accent));
179
- color: var(--color-bg-app, #141414);
180
- font-size: 10px;
181
- font-weight: 600;
182
- border-radius: var(--radius-full, 9999px);
177
+ padding: 0 3px;
178
+ background: var(--color-accent);
179
+ color: var(--color-bg-app, #0a0b0c);
180
+ font-family: var(--font-mono);
181
+ font-size: 9px;
182
+ font-weight: 700;
183
+ border-radius: 0;
183
184
  display: flex;
184
185
  align-items: center;
185
186
  justify-content: center;
187
+ font-variant-numeric: tabular-nums;
186
188
  }
187
189
 
188
190
  .toolbar-tooltip {
189
191
  position: absolute;
190
- padding: var(--space-1, 0.25rem) var(--space-2, 0.5rem);
191
- background: var(--color-bg-panel-elevated, #2a2a2a);
192
- color: var(--color-text-primary, #f2efe9);
193
- font-size: var(--text-xs, 0.75rem);
192
+ padding: 3px 7px;
193
+ background: var(--color-bg-panel-elevated);
194
+ color: var(--color-text-primary);
195
+ font-family: var(--font-mono);
196
+ font-size: var(--ui-label, 10px);
197
+ letter-spacing: 0.05em;
194
198
  white-space: nowrap;
195
- border-radius: var(--radius-md, 4px);
199
+ border: 1px solid var(--border-default);
196
200
  pointer-events: none;
197
201
  z-index: 1000;
198
- box-shadow: var(--shadow-md, 0 4px 6px rgba(0,0,0,0.3));
199
202
  }
200
203
 
201
204
  .toolbar-tooltip.tooltip-right {
@@ -126,8 +126,6 @@
126
126
 
127
127
  <style>
128
128
  .accordion {
129
- border-radius: var(--radius-md);
130
- overflow: hidden;
131
129
  border: var(--border-width-thin) solid var(--color-border);
132
130
  background: var(--color-bg-app);
133
131
  }
@@ -149,10 +147,11 @@
149
147
  display: flex;
150
148
  align-items: center;
151
149
  justify-content: space-between;
152
- padding: var(--space-4);
150
+ padding: var(--ui-pad-y) var(--ui-pad-x);
151
+ min-height: var(--ui-control-h);
153
152
  cursor: pointer;
154
153
  user-select: none;
155
- transition: background-color var(--duration-normal) ease-in-out;
154
+ transition: background-color var(--transition-fast) linear;
156
155
  }
157
156
 
158
157
  .accordion-header:hover:not(.disabled) {
@@ -163,7 +162,8 @@
163
162
 
164
163
  /* Focus styles for keyboard navigation */
165
164
  .accordion-header:focus-visible {
166
- box-shadow: 0 0 0 var(--border-width-default) var(--color-accent);
165
+ outline: var(--border-width-default) solid var(--color-accent);
166
+ outline-offset: -2px;
167
167
  }
168
168
 
169
169
  /* Disabled states */
@@ -180,7 +180,8 @@
180
180
  }
181
181
 
182
182
  .accordion-content {
183
- padding: var(--space-4, 1rem);
184
- padding-top: 0;
183
+ padding: var(--ui-pad-x);
184
+ padding-top: var(--ui-pad-y);
185
+ font-size: var(--ui-font);
185
186
  }
186
187
  </style>
@@ -116,7 +116,7 @@
116
116
  {:else if name === 'power-plant'}
117
117
  <!-- Power plant silhouette with cooling tower -->
118
118
  <defs>
119
- <linearGradient id="brassGrad-{size}" x1="0%" y1="0%" x2="100%" y2="100%">
119
+ <linearGradient id="accentGrad-{size}" x1="0%" y1="0%" x2="100%" y2="100%">
120
120
  <stop offset="0%" stop-color="var(--color-accent-hover)"/>
121
121
  <stop offset="50%" stop-color="var(--color-accent)"/>
122
122
  <stop offset="100%" stop-color="var(--color-accent-active)"/>
@@ -125,7 +125,7 @@
125
125
 
126
126
  <!-- Cooling tower -->
127
127
  <path d="M8 35 Q4 25 12 15 L14 5 Q20 2 26 5 L28 15 Q36 25 32 35 Z"
128
- fill="url(#brassGrad-{size})" opacity="0.8"/>
128
+ fill="url(#accentGrad-{size})" opacity="0.8"/>
129
129
 
130
130
  <!-- Inner detail -->
131
131
  <path d="M12 32 Q8 24 15 15 L17 8 Q20 6 23 8 L25 15 Q32 24 28 32"
@@ -200,7 +200,7 @@
200
200
  {:else if name === 'steam-tower'}
201
201
  <!-- Minimal steam tower with rising particles -->
202
202
  <path d="M12 38 L16 12 L24 12 L28 38 Z" fill="currentColor" opacity="0.3"/>
203
- <path d="M14 38 L17 14 L23 14 L26 38 Z" fill="url(#brassGrad-{size})" opacity="0.7"/>
203
+ <path d="M14 38 L17 14 L23 14 L26 38 Z" fill="url(#accentGrad-{size})" opacity="0.7"/>
204
204
 
205
205
  {#if animation === 'steam'}
206
206
  <!-- Rising particles -->
@@ -0,0 +1,82 @@
1
+ import { afterEach, describe, it, expect } from 'vitest';
2
+ import { cleanup, render } from '@testing-library/svelte';
3
+ import AnimatedIcon from './AnimatedIcon.svelte';
4
+ describe('AnimatedIcon', () => {
5
+ afterEach(() => cleanup());
6
+ it('renders an SVG element', () => {
7
+ const { container } = render(AnimatedIcon, { name: 'power-node' });
8
+ expect(container.querySelector('svg')).not.toBeNull();
9
+ });
10
+ it('applies the default size (40px)', () => {
11
+ const { container } = render(AnimatedIcon, { name: 'power-node' });
12
+ const svg = container.querySelector('svg');
13
+ expect(svg.getAttribute('style')).toContain('width: 40px');
14
+ expect(svg.getAttribute('style')).toContain('height: 40px');
15
+ });
16
+ it('applies a custom size', () => {
17
+ const { container } = render(AnimatedIcon, { name: 'power-node', size: 64 });
18
+ const svg = container.querySelector('svg');
19
+ expect(svg.getAttribute('style')).toContain('width: 64px');
20
+ expect(svg.getAttribute('style')).toContain('height: 64px');
21
+ });
22
+ it('applies a custom color', () => {
23
+ const { container } = render(AnimatedIcon, { name: 'power-node', color: 'red' });
24
+ expect(container.querySelector('svg').getAttribute('style')).toContain('color: red');
25
+ });
26
+ it('renders power-node icon paths (core circle)', () => {
27
+ const { container } = render(AnimatedIcon, { name: 'power-node' });
28
+ const svg = container.querySelector('svg');
29
+ expect(svg.querySelectorAll('circle').length).toBeGreaterThanOrEqual(3);
30
+ });
31
+ it('renders activity icon paths (rect bars)', () => {
32
+ const { container } = render(AnimatedIcon, { name: 'activity' });
33
+ const svg = container.querySelector('svg');
34
+ expect(svg.querySelectorAll('rect').length).toBe(4);
35
+ });
36
+ it('renders connection icon paths (two circles and a line)', () => {
37
+ const { container } = render(AnimatedIcon, { name: 'connection' });
38
+ const svg = container.querySelector('svg');
39
+ expect(svg.querySelectorAll('circle').length).toBeGreaterThanOrEqual(3);
40
+ expect(svg.querySelectorAll('line').length).toBe(1);
41
+ });
42
+ it('contains animate elements for animated icons', () => {
43
+ const { container } = render(AnimatedIcon, { name: 'activity' });
44
+ const svg = container.querySelector('svg');
45
+ expect(svg.querySelectorAll('animate').length).toBeGreaterThan(0);
46
+ });
47
+ it('contains animateTransform for orbit animation on power-node', () => {
48
+ const { container } = render(AnimatedIcon, { name: 'power-node', animation: 'orbit' });
49
+ const svg = container.querySelector('svg');
50
+ expect(svg.querySelectorAll('animateTransform').length).toBe(1);
51
+ });
52
+ it('render pulse animation markup on power-node', () => {
53
+ const { container } = render(AnimatedIcon, { name: 'power-node', animation: 'pulse' });
54
+ const svg = container.querySelector('svg');
55
+ expect(svg.querySelectorAll('animate').length).toBeGreaterThanOrEqual(2);
56
+ });
57
+ it('doubles speed halves the dur attribute', () => {
58
+ const { container: c1 } = render(AnimatedIcon, { name: 'power-node', animation: 'pulse', speed: 1 });
59
+ const animates1 = c1.querySelectorAll('animate');
60
+ const dur1 = animates1[0].getAttribute('dur');
61
+ cleanup();
62
+ const { container: c2 } = render(AnimatedIcon, { name: 'power-node', animation: 'pulse', speed: 2 });
63
+ const animates2 = c2.querySelectorAll('animate');
64
+ const dur2 = animates2[0].getAttribute('dur');
65
+ expect(dur1).toBe('2000ms');
66
+ expect(dur2).toBe('1000ms');
67
+ });
68
+ it('has aria-hidden attribute on SVG', () => {
69
+ const { container } = render(AnimatedIcon, { name: 'power-node' });
70
+ expect(container.querySelector('svg').getAttribute('aria-hidden')).toBe('true');
71
+ });
72
+ it('renders energy-pulse icon correctly', () => {
73
+ const { container } = render(AnimatedIcon, { name: 'energy-pulse' });
74
+ const svg = container.querySelector('svg');
75
+ expect(svg.querySelectorAll('circle').length).toBeGreaterThanOrEqual(4);
76
+ });
77
+ it('renders status-ring icon correctly', () => {
78
+ const { container } = render(AnimatedIcon, { name: 'status-ring' });
79
+ const svg = container.querySelector('svg');
80
+ expect(svg.querySelectorAll('path').length).toBe(2);
81
+ });
82
+ });
@@ -74,11 +74,10 @@
74
74
  position: relative;
75
75
  width: 100%;
76
76
  height: 100%;
77
- border-radius: var(--radius-full);
77
+ border-radius: var(--radius-full); /* circle — intentional, avatars are always round */
78
78
  overflow: hidden;
79
79
  background: var(--color-bg-panel-elevated);
80
80
  border: 1px solid var(--border-subtle);
81
- box-shadow: var(--shadow-sm);
82
81
  }
83
82
 
84
83
  .ds-avatar-image {
@@ -93,7 +92,7 @@
93
92
  display: flex;
94
93
  align-items: center;
95
94
  justify-content: center;
96
- background: linear-gradient(135deg, var(--color-bg-panel-elevated), var(--color-bg-panel));
95
+ background: var(--color-bg-panel-elevated);
97
96
  color: var(--color-text-secondary);
98
97
  }
99
98
 
@@ -142,36 +141,37 @@
142
141
  z-index: 10;
143
142
  }
144
143
 
145
- .status-online { background-color: var(--color-online); box-shadow: 0 0 6px var(--color-cyan-glow); }
144
+ .status-online { background-color: var(--color-online); }
146
145
  .status-away { background-color: var(--color-away); }
147
146
  .status-busy { background-color: var(--color-busy); }
148
147
  .status-offline { background-color: var(--color-offline); }
149
148
 
150
- .status-typing {
149
+ /* Typing: blinking dots — mechanical, no bounce, no glow */
150
+ .status-typing {
151
151
  background-color: var(--color-typing);
152
152
  display: flex;
153
153
  align-items: center;
154
154
  justify-content: space-evenly;
155
155
  width: auto;
156
- padding: 0 4px;
156
+ padding: 0 3px;
157
157
  min-width: 16px;
158
158
  border-radius: var(--radius-full);
159
- box-shadow: var(--shadow-glow);
160
159
  }
161
160
 
162
161
  .typing-dot {
163
162
  width: 3px;
164
163
  height: 3px;
165
- background-color: var(--color-text-inverse);
164
+ background-color: var(--color-text-inverse);
166
165
  border-radius: var(--radius-full);
167
- animation: typing-bounce 1.4s infinite ease-in-out both;
166
+ animation: typing-blink 1.2s step-end infinite;
168
167
  }
169
168
 
170
- .typing-dot:nth-child(1) { animation-delay: -0.32s; }
171
- .typing-dot:nth-child(2) { animation-delay: -0.16s; }
169
+ .typing-dot:nth-child(1) { animation-delay: 0s; }
170
+ .typing-dot:nth-child(2) { animation-delay: 0.4s; }
171
+ .typing-dot:nth-child(3) { animation-delay: 0.8s; }
172
172
 
173
- @keyframes typing-bounce {
174
- 0%, 80%, 100% { transform: scale(0); }
175
- 40% { transform: scale(1); }
173
+ @keyframes typing-blink {
174
+ 0%, 100% { opacity: 0.2; }
175
+ 50% { opacity: 1; }
176
176
  }
177
177
  </style>
@@ -0,0 +1,81 @@
1
+ import { afterEach, describe, it, expect } from 'vitest';
2
+ import { cleanup, render, fireEvent } from '@testing-library/svelte';
3
+ import Avatar from './Avatar.svelte';
4
+ describe('Avatar', () => {
5
+ afterEach(() => cleanup());
6
+ it('renders the ds-avatar-wrapper root element', () => {
7
+ const { container } = render(Avatar);
8
+ expect(container.querySelector('.ds-avatar-wrapper')).not.toBeNull();
9
+ });
10
+ it('applies the size-sm class', () => {
11
+ const { container } = render(Avatar, { size: 'sm' });
12
+ expect(container.querySelector('.size-sm')).not.toBeNull();
13
+ });
14
+ it('applies the size-md class by default', () => {
15
+ const { container } = render(Avatar);
16
+ expect(container.querySelector('.size-md')).not.toBeNull();
17
+ });
18
+ it('applies the size-lg class', () => {
19
+ const { container } = render(Avatar, { size: 'lg' });
20
+ expect(container.querySelector('.size-lg')).not.toBeNull();
21
+ });
22
+ it('applies the size-xl class', () => {
23
+ const { container } = render(Avatar, { size: 'xl' });
24
+ expect(container.querySelector('.size-xl')).not.toBeNull();
25
+ });
26
+ it('renders an image when src is provided', () => {
27
+ const { container } = render(Avatar, { src: 'https://example.com/photo.png', name: 'Jane' });
28
+ const img = container.querySelector('.ds-avatar-image');
29
+ expect(img).not.toBeNull();
30
+ expect(img.getAttribute('src')).toBe('https://example.com/photo.png');
31
+ });
32
+ it('renders initials from a two-word name', () => {
33
+ const { container } = render(Avatar, { name: 'Jane Doe' });
34
+ expect(container.querySelector('.ds-avatar-initials').textContent).toBe('JD');
35
+ });
36
+ it('renders initials from a single-word name', () => {
37
+ const { container } = render(Avatar, { name: 'Alice' });
38
+ expect(container.querySelector('.ds-avatar-initials').textContent).toBe('A');
39
+ });
40
+ it('renders a question mark when name is empty', () => {
41
+ const { container } = render(Avatar);
42
+ expect(container.querySelector('.ds-avatar-initials').textContent).toBe('?');
43
+ });
44
+ it('does not render a status dot when status is null', () => {
45
+ const { container } = render(Avatar);
46
+ expect(container.querySelector('.ds-avatar-status')).toBeNull();
47
+ });
48
+ it('renders a status dot with the online class', () => {
49
+ const { container } = render(Avatar, { status: 'online' });
50
+ const status = container.querySelector('.ds-avatar-status');
51
+ expect(status).not.toBeNull();
52
+ expect(status.classList.contains('status-online')).toBe(true);
53
+ });
54
+ it('renders a status dot with the away class', () => {
55
+ const { container } = render(Avatar, { status: 'away' });
56
+ expect(container.querySelector('.status-away')).not.toBeNull();
57
+ });
58
+ it('renders a status dot with the busy class', () => {
59
+ const { container } = render(Avatar, { status: 'busy' });
60
+ expect(container.querySelector('.status-busy')).not.toBeNull();
61
+ });
62
+ it('renders a status dot with the offline class', () => {
63
+ const { container } = render(Avatar, { status: 'offline' });
64
+ expect(container.querySelector('.status-offline')).not.toBeNull();
65
+ });
66
+ it('renders typing dots when status is typing', () => {
67
+ const { container } = render(Avatar, { status: 'typing' });
68
+ expect(container.querySelectorAll('.typing-dot').length).toBe(3);
69
+ });
70
+ it('switches to initials fallback on image error', async () => {
71
+ const { container } = render(Avatar, {
72
+ src: 'https://example.com/broken.png',
73
+ name: 'Test User',
74
+ });
75
+ const img = container.querySelector('.ds-avatar-image');
76
+ expect(img).not.toBeNull();
77
+ await fireEvent.error(img);
78
+ expect(container.querySelector('.ds-avatar-initials')).not.toBeNull();
79
+ expect(container.querySelector('.ds-avatar-initials').textContent).toBe('TU');
80
+ });
81
+ });
@@ -36,11 +36,13 @@
36
36
  display: inline-flex;
37
37
  align-items: center;
38
38
  justify-content: center;
39
- font-family: var(--font-sans);
39
+ font-family: var(--font-mono);
40
40
  font-weight: 600;
41
+ letter-spacing: 0.06em;
42
+ text-transform: uppercase;
41
43
  white-space: nowrap;
42
- border-radius: var(--radius-full);
43
- transition: all var(--transition-fast);
44
+ border-radius: 0;
45
+ transition: border-color var(--transition-fast), background-color var(--transition-fast), color var(--transition-fast);
44
46
  }
45
47
 
46
48
  /* --- Variants --- */
@@ -88,7 +90,7 @@
88
90
 
89
91
  /* Count (Solid background, white/dark text) */
90
92
  .variant-count {
91
- box-shadow: var(--shadow-sm);
93
+ font-variant-numeric: tabular-nums;
92
94
  }
93
95
 
94
96
  .variant-count.color-accent { background: var(--color-accent); color: var(--color-text-inverse); }
@@ -53,9 +53,10 @@
53
53
  font-family: var(--font-mono);
54
54
  font-weight: 700;
55
55
  white-space: nowrap;
56
- border-radius: var(--radius-full);
57
- transition: all var(--transition-fast);
56
+ border-radius: 0;
57
+ transition: border-color var(--transition-fast) linear, background var(--transition-fast) linear;
58
58
  gap: 0.125rem;
59
+ font-variant-numeric: tabular-nums;
59
60
  }
60
61
 
61
62
  /* High Validity (900-1000) */
@@ -125,29 +126,5 @@
125
126
  margin-left: 0.25rem;
126
127
  }
127
128
 
128
- /* Hover effect */
129
- .ds-validity-badge:hover {
130
- transform: translateY(-1px);
131
- box-shadow: var(--shadow-sm);
132
- }
133
-
134
- .validity-color-high:hover {
135
- background: var(--validity-high);
136
- color: #fff;
137
- }
138
-
139
- .validity-color-medium:hover {
140
- background: var(--validity-medium);
141
- color: #fff;
142
- }
143
-
144
- .validity-color-low:hover {
145
- background: var(--validity-low);
146
- color: #fff;
147
- }
148
-
149
- .validity-color-critical:hover {
150
- background: var(--validity-critical);
151
- color: #fff;
152
- }
129
+ /* No lift/shadow hover — reads as data, not interactive decoration */
153
130
  </style>