@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.
- package/dist/d0-data-viz/BarChart.test.js +53 -0
- package/dist/d0-data-viz/CheckpointBar.test.js +83 -0
- package/dist/d0-data-viz/CheckpointPill.svelte +1 -1
- package/dist/d0-data-viz/CheckpointPill.test.js +78 -0
- package/dist/d0-data-viz/ConnectionHealth.test.js +70 -0
- package/dist/d0-data-viz/MetricCard.test.js +57 -0
- package/dist/d0-data-viz/SparkLine.test.js +47 -0
- package/dist/d0-data-viz/StorageMap.test.js +63 -0
- package/dist/d0-data-viz/Timeline.test.js +62 -0
- package/dist/d0-data-viz/WorldLineTimeline.test.js +99 -0
- package/dist/d0-data-viz/checkpoint-types.js +1 -1
- package/dist/d0-data-viz/storage-map-types.d.ts.map +1 -1
- package/dist/d0-data-viz/storage-map-types.js +6 -3
- package/dist/d0-data-viz/theme-integration.js +1 -1
- package/dist/demos/AmbientPrimitivesDemo.svelte +103 -0
- package/dist/demos/AmbientPrimitivesDemo.svelte.d.ts +5 -0
- package/dist/demos/AmbientPrimitivesDemo.svelte.d.ts.map +1 -0
- package/dist/demos/NodepadBlocksShowcase.svelte +2 -1
- package/dist/demos/NodepadBlocksShowcase.svelte.d.ts.map +1 -1
- package/dist/demos/index.d.ts +1 -0
- package/dist/demos/index.d.ts.map +1 -1
- package/dist/demos/index.js +1 -0
- package/dist/editor/CodeEditor.svelte +83 -26
- package/dist/editor/CodeEditor.svelte.d.ts +0 -1
- package/dist/editor/CodeEditor.svelte.d.ts.map +1 -1
- package/dist/editor/ExternalChangeDialog.svelte +117 -0
- package/dist/editor/ExternalChangeDialog.svelte.d.ts +13 -0
- package/dist/editor/ExternalChangeDialog.svelte.d.ts.map +1 -0
- package/dist/editor/MarkdownEditor.svelte +104 -617
- package/dist/editor/MarkdownEditor.svelte.d.ts +1 -1
- package/dist/editor/MarkdownEditor.svelte.d.ts.map +1 -1
- package/dist/editor/codemirror-config.d.ts +36 -0
- package/dist/editor/codemirror-config.d.ts.map +1 -0
- package/dist/editor/codemirror-config.js +87 -0
- package/dist/editor/components/CorrectionTooltip.svelte +180 -159
- package/dist/editor/components/CorrectionTooltip.svelte.d.ts +7 -23
- package/dist/editor/components/CorrectionTooltip.svelte.d.ts.map +1 -1
- package/dist/editor/extensions/autocomplete.d.ts +2 -2
- package/dist/editor/extensions/autocomplete.d.ts.map +1 -1
- package/dist/editor/extensions/autocomplete.test.js +121 -0
- package/dist/editor/extensions/spellcheck.d.ts +21 -3
- package/dist/editor/extensions/spellcheck.d.ts.map +1 -1
- package/dist/editor/extensions/spellcheck.js +51 -9
- package/dist/editor/extensions/spellcheck.test.js +168 -0
- package/dist/editor/extensions/types.d.ts +2 -1
- package/dist/editor/extensions/types.d.ts.map +1 -1
- package/dist/editor/index.d.ts +2 -1
- package/dist/editor/index.d.ts.map +1 -1
- package/dist/editor/index.js +1 -0
- package/dist/editor/markdown-preview.d.ts +9 -0
- package/dist/editor/markdown-preview.d.ts.map +1 -0
- package/dist/editor/markdown-preview.js +19 -0
- package/dist/editor/markdown-preview.test.js +38 -0
- package/dist/editor/useCodeMirror.d.ts +23 -0
- package/dist/editor/useCodeMirror.d.ts.map +1 -0
- package/dist/editor/useCodeMirror.js +40 -0
- package/dist/editor/useFilePersistence.d.ts +32 -0
- package/dist/editor/useFilePersistence.d.ts.map +1 -0
- package/dist/editor/useFilePersistence.js +117 -0
- package/dist/fabric/ai/ChatPanel.svelte +61 -62
- package/dist/fabric/ai/ChatPanel.svelte.d.ts.map +1 -1
- package/dist/fabric/ai/ResearchPanel.svelte +31 -24
- package/dist/fabric/data/FileTree.svelte +195 -0
- package/dist/fabric/data/FileTree.svelte.d.ts +19 -0
- package/dist/fabric/data/FileTree.svelte.d.ts.map +1 -0
- package/dist/fabric/data/FileTree.test.js +127 -0
- package/dist/fabric/data/index.d.ts +5 -9
- package/dist/fabric/data/index.d.ts.map +1 -1
- package/dist/fabric/data/index.js +4 -1
- package/dist/fabric/index.d.ts +1 -0
- package/dist/fabric/index.d.ts.map +1 -1
- package/dist/fabric/index.js +2 -0
- package/dist/fabric/input/KeyboardShortcuts.svelte +23 -21
- package/dist/fabric/layout/CollapsiblePanel.svelte +8 -10
- package/dist/fabric/navigation/ActivityRail.svelte +3 -5
- package/dist/fabric/navigation/ActivityRail.svelte.d.ts.map +1 -1
- package/dist/g0-generative/components/Text.svelte +38 -0
- package/dist/g0-generative/components/Text.svelte.d.ts +9 -0
- package/dist/g0-generative/components/Text.svelte.d.ts.map +1 -0
- package/dist/g0-generative/index.d.ts +1 -0
- package/dist/g0-generative/index.d.ts.map +1 -1
- package/dist/g0-generative/index.js +1 -0
- package/dist/g0-generative/library/ComponentLibrary.d.ts +4 -0
- package/dist/g0-generative/library/ComponentLibrary.d.ts.map +1 -1
- package/dist/g0-generative/library/ComponentLibrary.js +10 -0
- package/dist/g0-generative/library/ComponentLibrary.test.js +135 -0
- package/dist/g0-generative/renderer/OpenUIRenderer.svelte +16 -56
- package/dist/g0-generative/renderer/OpenUIRenderer.svelte.d.ts.map +1 -1
- package/dist/g0-generative/renderer/OpenUIRenderer.test.js +143 -0
- package/dist/g0-geo/SovereignMap.svelte +13 -13
- package/dist/g0-geo/SovereignMap.svelte.d.ts.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/l0-layout/block-writer/BlockContent.svelte +23 -22
- package/dist/l0-layout/block-writer/BlockContent.svelte.d.ts.map +1 -1
- package/dist/l0-layout/block-writer/BlockContent.test.js +47 -0
- package/dist/l0-layout/block-writer/markdown.js +11 -11
- package/dist/l0-layout/block-writer/markdown.test.js +81 -0
- package/dist/l0-layout/card/StackedCard.svelte +87 -0
- package/dist/l0-layout/card/StackedCard.svelte.d.ts +9 -0
- package/dist/l0-layout/card/StackedCard.svelte.d.ts.map +1 -0
- package/dist/l0-layout/card/StackedCard.test.js +29 -0
- package/dist/l0-layout/index.d.ts +1 -0
- package/dist/l0-layout/index.d.ts.map +1 -1
- package/dist/l0-layout/index.js +1 -0
- package/dist/l0-layout/kanban/KanbanBoard.svelte +1 -1
- package/dist/l0-layout/kanban/KanbanColumn.svelte +1 -1
- package/dist/n1-flow-canvas/FlowCanvas.svelte +50 -6
- package/dist/n1-flow-canvas/FlowCanvas.svelte.d.ts.map +1 -1
- package/dist/n1-flow-canvas/index.d.ts +2 -0
- package/dist/n1-flow-canvas/index.d.ts.map +1 -1
- package/dist/n1-flow-canvas/index.js +1 -0
- package/dist/n1-flow-canvas/pipeline-adapter.d.ts +37 -0
- package/dist/n1-flow-canvas/pipeline-adapter.d.ts.map +1 -0
- package/dist/n1-flow-canvas/pipeline-adapter.js +160 -0
- package/dist/n2-manifold-disk/ManifoldDisk.svelte +62 -35
- package/dist/n2-manifold-disk/ManifoldDisk.svelte.d.ts.map +1 -1
- package/dist/n4-adaptive-manifold/AdaptiveManifold.svelte +23 -19
- package/dist/n4-adaptive-manifold/AdaptiveManifold.svelte.d.ts.map +1 -1
- package/dist/n4-adaptive-manifold/types.d.ts +3 -0
- package/dist/n4-adaptive-manifold/types.d.ts.map +1 -1
- package/dist/p0-primitives/ThemeProvider.svelte +28 -7
- package/dist/p0-primitives/ThemeProvider.svelte.d.ts +5 -0
- package/dist/p0-primitives/ThemeProvider.svelte.d.ts.map +1 -1
- package/dist/p0-primitives/canvas-theme.d.ts +21 -1
- package/dist/p0-primitives/canvas-theme.d.ts.map +1 -1
- package/dist/p0-primitives/canvas-theme.js +78 -23
- package/dist/p0-primitives/debug.d.ts +6 -0
- package/dist/p0-primitives/debug.d.ts.map +1 -0
- package/dist/p0-primitives/debug.js +21 -0
- package/dist/p0-primitives/theme.d.ts.map +1 -1
- package/dist/p0-primitives/theme.js +11 -31
- package/dist/p0-primitives/tokens/ambient.css +76 -0
- package/dist/p0-primitives/tokens/tokens.css +124 -29
- package/dist/p0-primitives/utils/index.d.ts +2 -0
- package/dist/p0-primitives/utils/index.d.ts.map +1 -1
- package/dist/p0-primitives/utils/index.js +1 -0
- package/dist/p0-primitives/utils/reveal.d.ts +15 -0
- package/dist/p0-primitives/utils/reveal.d.ts.map +1 -0
- package/dist/p0-primitives/utils/reveal.js +29 -0
- package/dist/p0-primitives/utils/reveal.test.js +95 -0
- package/dist/s0-state/SyncStatus.svelte +1 -1
- package/dist/s0-state/index.d.ts +2 -1
- package/dist/s0-state/index.d.ts.map +1 -1
- package/dist/s0-state/index.js +2 -0
- package/dist/s0-state/modal-stack-store.svelte.d.ts +3 -3
- package/dist/s0-state/modal-stack-store.svelte.js +2 -2
- package/dist/spatial/ForceSimulation.svelte.js +1 -1
- package/dist/spatial/ParticleAssembleMark.svelte +285 -0
- package/dist/spatial/ParticleAssembleMark.svelte.d.ts +24 -0
- package/dist/spatial/ParticleAssembleMark.svelte.d.ts.map +1 -0
- package/dist/spatial/ZoneTiler.test.js +90 -0
- package/dist/spatial/index.js +1 -0
- package/dist/spatial/particle-assemble-math.d.ts +22 -0
- package/dist/spatial/particle-assemble-math.d.ts.map +1 -0
- package/dist/spatial/particle-assemble-math.js +26 -0
- package/dist/spatial/particle-assemble-math.test.js +56 -0
- package/dist/t0-transport/substrate.svelte.d.ts.map +1 -1
- package/dist/t0-transport/substrate.svelte.js +10 -4
- package/dist/u0-primitives/LockIndicator.svelte +16 -14
- package/dist/u0-primitives/VerticalToolbar.svelte +24 -21
- package/dist/u0-primitives/accordion/Accordion.svelte +8 -7
- package/dist/u0-primitives/animated-icon/AnimatedIcon.svelte +3 -3
- package/dist/u0-primitives/animated-icon/AnimatedIcon.test.js +82 -0
- package/dist/u0-primitives/avatar/Avatar.svelte +14 -14
- package/dist/u0-primitives/avatar/Avatar.test.js +81 -0
- package/dist/u0-primitives/badge/Badge.svelte +6 -4
- package/dist/u0-primitives/badge/ValidityBadge.svelte +4 -27
- package/dist/u0-primitives/button/Button.svelte +15 -12
- package/dist/u0-primitives/card/Card.svelte +37 -15
- package/dist/u0-primitives/card/Card.test.js +86 -0
- package/dist/u0-primitives/command-palette/CommandPalette.svelte +48 -43
- package/dist/u0-primitives/command-palette/CommandPalette.test.js +202 -0
- package/dist/u0-primitives/dropdown/Dropdown.svelte +20 -25
- package/dist/u0-primitives/grant-badge/GrantStatusBadge.svelte +2 -2
- package/dist/u0-primitives/index.d.ts +6 -0
- package/dist/u0-primitives/index.d.ts.map +1 -1
- package/dist/u0-primitives/index.js +8 -0
- package/dist/u0-primitives/input/Input.svelte +77 -24
- package/dist/u0-primitives/input/Input.svelte.d.ts +2 -10
- package/dist/u0-primitives/input/Input.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/input/Input.test.js +81 -1
- package/dist/u0-primitives/live-dot/LiveDot.svelte +56 -0
- package/dist/u0-primitives/live-dot/LiveDot.svelte.d.ts +11 -0
- package/dist/u0-primitives/live-dot/LiveDot.svelte.d.ts.map +1 -0
- package/dist/u0-primitives/live-dot/LiveDot.test.js +41 -0
- package/dist/u0-primitives/live-dot/index.d.ts +3 -0
- package/dist/u0-primitives/live-dot/index.d.ts.map +1 -0
- package/dist/u0-primitives/live-dot/index.js +1 -0
- package/dist/u0-primitives/modal/Modal.svelte +22 -19
- package/dist/u0-primitives/modal/Modal.test.js +1 -1
- package/dist/u0-primitives/spinner/Spinner.svelte +11 -79
- package/dist/u0-primitives/spinner/Spinner.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/steps/Steps.svelte +18 -19
- package/dist/u0-primitives/surface-state/SurfaceState.svelte +7 -7
- package/dist/u0-primitives/tabs/Tabs.svelte +13 -11
- package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.svelte +94 -0
- package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.svelte.d.ts +16 -0
- package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.svelte.d.ts.map +1 -0
- package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.test.js +48 -0
- package/dist/u0-primitives/theme-swatch-group/index.d.ts +3 -0
- package/dist/u0-primitives/theme-swatch-group/index.d.ts.map +1 -0
- package/dist/u0-primitives/theme-swatch-group/index.js +1 -0
- package/dist/u0-primitives/toggle/Toggle.svelte +10 -11
- package/dist/u0-primitives/tooltip/Tooltip.svelte +9 -9
- package/dist/u0-primitives/web-components/button-component.d.ts +5 -1
- package/dist/u0-primitives/web-components/button-component.d.ts.map +1 -1
- package/dist/u0-primitives/web-components/button-component.js +72 -33
- package/dist/u0-primitives/web-components/index.d.ts +3 -0
- package/dist/u0-primitives/web-components/index.d.ts.map +1 -0
- package/dist/u0-primitives/web-components/index.js +2 -0
- package/dist/u0-primitives/web-components/input-component.d.ts +5 -1
- package/dist/u0-primitives/web-components/input-component.d.ts.map +1 -1
- package/dist/u0-primitives/web-components/input-component.js +56 -39
- package/dist/u0-primitives/web-components/web-components.test.js +96 -0
- package/dist/u0-primitives/window-frame/WindowFrame.svelte +87 -0
- package/dist/u0-primitives/window-frame/WindowFrame.svelte.d.ts +10 -0
- package/dist/u0-primitives/window-frame/WindowFrame.svelte.d.ts.map +1 -0
- package/dist/u0-primitives/window-frame/WindowFrame.test.js +31 -0
- package/dist/u0-primitives/window-frame/index.d.ts +3 -0
- package/dist/u0-primitives/window-frame/index.d.ts.map +1 -0
- package/dist/u0-primitives/window-frame/index.js +1 -0
- package/dist/x0-enchanted-blocks/components/NodepadBlock.svelte +11 -2
- package/dist/x0-enchanted-blocks/components/NodepadBlock.svelte.d.ts.map +1 -1
- package/package.json +44 -28
- package/src/p0-primitives/tokens/ambient.css +76 -0
- package/src/p0-primitives/tokens/tokens.css +124 -29
- 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;
|
|
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
|
-
|
|
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
|
|
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:
|
|
190
|
-
background: var(--color-accent-subtle
|
|
191
|
-
color: var(--color-accent
|
|
192
|
-
border: 1px solid var(--color-accent
|
|
193
|
-
|
|
194
|
-
font-size: var(--
|
|
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:
|
|
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-
|
|
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:
|
|
218
|
-
border-top-color:
|
|
219
|
-
border-
|
|
220
|
-
animation: spin
|
|
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:
|
|
119
|
+
border-radius: 0;
|
|
120
120
|
color: var(--color-text-muted, #6b6558);
|
|
121
121
|
cursor: pointer;
|
|
122
|
-
transition:
|
|
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:
|
|
146
|
-
|
|
147
|
-
width:
|
|
148
|
-
|
|
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
|
|
178
|
-
background: var(--color-accent
|
|
179
|
-
color: var(--color-bg-app, #
|
|
180
|
-
font-
|
|
181
|
-
font-
|
|
182
|
-
|
|
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:
|
|
191
|
-
background: var(--color-bg-panel-elevated
|
|
192
|
-
color: var(--color-text-primary
|
|
193
|
-
font-
|
|
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
|
|
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(--
|
|
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(--
|
|
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
|
-
|
|
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(--
|
|
184
|
-
padding-top:
|
|
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="
|
|
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(#
|
|
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(#
|
|
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:
|
|
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);
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
164
|
+
background-color: var(--color-text-inverse);
|
|
166
165
|
border-radius: var(--radius-full);
|
|
167
|
-
animation: typing-
|
|
166
|
+
animation: typing-blink 1.2s step-end infinite;
|
|
168
167
|
}
|
|
169
168
|
|
|
170
|
-
.typing-dot:nth-child(1) { animation-delay:
|
|
171
|
-
.typing-dot:nth-child(2) { animation-delay:
|
|
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-
|
|
174
|
-
0%,
|
|
175
|
-
|
|
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-
|
|
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:
|
|
43
|
-
transition:
|
|
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
|
-
|
|
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:
|
|
57
|
-
transition:
|
|
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
|
-
/*
|
|
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>
|