@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,95 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, it, expect } from 'vitest';
|
|
2
|
+
import { reveal } from './reveal';
|
|
3
|
+
// Scoped to this file only (installed in beforeEach, removed in afterEach) —
|
|
4
|
+
// a global IntersectionObserver polyfill in vitest-setup.ts changes which
|
|
5
|
+
// code path unrelated components (e.g. MarkmapDiagram's d3-zoom setup) take,
|
|
6
|
+
// crashing on jsdom's incomplete SVG support. Keep this local.
|
|
7
|
+
class IntersectionObserverMock {
|
|
8
|
+
static instances = [];
|
|
9
|
+
callback;
|
|
10
|
+
observedTargets = [];
|
|
11
|
+
constructor(callback) {
|
|
12
|
+
this.callback = callback;
|
|
13
|
+
IntersectionObserverMock.instances.push(this);
|
|
14
|
+
}
|
|
15
|
+
observe(target) { this.observedTargets.push(target); }
|
|
16
|
+
unobserve(target) { this.observedTargets = this.observedTargets.filter((t) => t !== target); }
|
|
17
|
+
disconnect() { this.observedTargets = []; }
|
|
18
|
+
takeRecords() { return []; }
|
|
19
|
+
}
|
|
20
|
+
function lastObserver() {
|
|
21
|
+
return IntersectionObserverMock.instances[IntersectionObserverMock.instances.length - 1];
|
|
22
|
+
}
|
|
23
|
+
function makeEntry(target, isIntersecting) {
|
|
24
|
+
return { target, isIntersecting };
|
|
25
|
+
}
|
|
26
|
+
describe('reveal', () => {
|
|
27
|
+
let node;
|
|
28
|
+
let originalIntersectionObserver;
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
originalIntersectionObserver = globalThis.IntersectionObserver;
|
|
31
|
+
IntersectionObserverMock.instances = [];
|
|
32
|
+
globalThis.IntersectionObserver = IntersectionObserverMock;
|
|
33
|
+
});
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
node?.remove();
|
|
36
|
+
globalThis.IntersectionObserver = originalIntersectionObserver;
|
|
37
|
+
});
|
|
38
|
+
it('adds the ds-reveal class on mount', () => {
|
|
39
|
+
node = document.createElement('div');
|
|
40
|
+
reveal(node);
|
|
41
|
+
expect(node.classList.contains('ds-reveal')).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
it('adds ds-reveal-in once the node intersects', () => {
|
|
44
|
+
node = document.createElement('div');
|
|
45
|
+
reveal(node);
|
|
46
|
+
const observer = lastObserver();
|
|
47
|
+
observer.callback([makeEntry(node, true)], observer);
|
|
48
|
+
expect(node.classList.contains('ds-reveal-in')).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
it('does not add ds-reveal-in before intersecting', () => {
|
|
51
|
+
node = document.createElement('div');
|
|
52
|
+
reveal(node);
|
|
53
|
+
expect(node.classList.contains('ds-reveal-in')).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it('sets --ds-reveal-delay when delayMs is passed', () => {
|
|
56
|
+
node = document.createElement('div');
|
|
57
|
+
reveal(node, { delayMs: 240 });
|
|
58
|
+
expect(node.style.getPropertyValue('--ds-reveal-delay')).toBe('240ms');
|
|
59
|
+
});
|
|
60
|
+
it('removes ds-reveal-in on exit when once is false', () => {
|
|
61
|
+
node = document.createElement('div');
|
|
62
|
+
reveal(node, { once: false });
|
|
63
|
+
const observer = lastObserver();
|
|
64
|
+
observer.callback([makeEntry(node, true)], observer);
|
|
65
|
+
expect(node.classList.contains('ds-reveal-in')).toBe(true);
|
|
66
|
+
observer.callback([makeEntry(node, false)], observer);
|
|
67
|
+
expect(node.classList.contains('ds-reveal-in')).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
it('disconnects the observer on destroy', () => {
|
|
70
|
+
node = document.createElement('div');
|
|
71
|
+
const action = reveal(node);
|
|
72
|
+
const observer = lastObserver();
|
|
73
|
+
expect(observer.observedTargets).toContain(node);
|
|
74
|
+
action?.destroy?.();
|
|
75
|
+
expect(observer.observedTargets).not.toContain(node);
|
|
76
|
+
});
|
|
77
|
+
it('reveals immediately with no observer when prefers-reduced-motion is set', () => {
|
|
78
|
+
const originalMatchMedia = window.matchMedia;
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
+
window.matchMedia = (query) => ({
|
|
81
|
+
matches: query.includes('reduce'),
|
|
82
|
+
media: query,
|
|
83
|
+
addListener: () => { },
|
|
84
|
+
removeListener: () => { },
|
|
85
|
+
addEventListener: () => { },
|
|
86
|
+
removeEventListener: () => { },
|
|
87
|
+
dispatchEvent: () => false
|
|
88
|
+
});
|
|
89
|
+
node = document.createElement('div');
|
|
90
|
+
reveal(node);
|
|
91
|
+
expect(node.classList.contains('ds-reveal-in')).toBe(true);
|
|
92
|
+
expect(node.classList.contains('ds-reveal')).toBe(false);
|
|
93
|
+
window.matchMedia = originalMatchMedia;
|
|
94
|
+
});
|
|
95
|
+
});
|
package/dist/s0-state/index.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export type { DerivedOptions } from './derived';
|
|
|
5
5
|
export { createVirtualList, createVirtualGrid, createPreloader, calculateGridLayout, type VirtualListConfig, type VirtualItem, type VirtualList, type PreloaderConfig, type PreloaderState, } from './virtual';
|
|
6
6
|
export { default as SyncStatus } from './SyncStatus.svelte';
|
|
7
7
|
export type { SyncStatusState, SyncStatusAction } from './SyncStatus.svelte';
|
|
8
|
-
export
|
|
8
|
+
export { modalStackStore } from './modal-stack-store.svelte';
|
|
9
|
+
export type { ModalData, VisibleModal } from './modal-stack-store.svelte';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/s0-state/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG7E,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/s0-state/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG7E,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/s0-state/index.js
CHANGED
|
@@ -5,4 +5,6 @@ export { createDerived } from './derived';
|
|
|
5
5
|
export { createVirtualList, createVirtualGrid, createPreloader, calculateGridLayout, } from './virtual';
|
|
6
6
|
// New from NNJAS port - Sync status component
|
|
7
7
|
export { default as SyncStatus } from './SyncStatus.svelte';
|
|
8
|
+
// Modal stack management — subpath only (module-scope singleton)
|
|
9
|
+
export { modalStackStore } from './modal-stack-store.svelte';
|
|
8
10
|
// Theme management — subpath only (module-scope singleton)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Modal Stack Store — Stack-based modal management for @
|
|
2
|
+
* Modal Stack Store — Stack-based modal management for @stephen-netu/design-system.
|
|
3
3
|
*
|
|
4
4
|
* Manages a stack of modals (not just one). Opening a modal pushes onto the
|
|
5
5
|
* stack; closing pops. Enables "back" navigation when opening modals from
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Adopted from Chronicler's modalStack.ts pattern (2026-05-25 investigation).
|
|
9
9
|
*
|
|
10
10
|
* Usage:
|
|
11
|
-
* import { modalStackStore } from '@
|
|
11
|
+
* import { modalStackStore } from '@stephen-netu/design-system/s0-state';
|
|
12
12
|
*
|
|
13
13
|
* // Push a modal
|
|
14
14
|
* modalStackStore.push({ id: 'confirm-delete', props: { itemId: 'abc' } });
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* @module s0-state/modal-stack-store
|
|
34
34
|
* @example
|
|
35
35
|
* ```ts
|
|
36
|
-
* import { modalStackStore } from '@
|
|
36
|
+
* import { modalStackStore } from '@stephen-netu/design-system/s0-state';
|
|
37
37
|
* console.log(modalStackStore.depth);
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Modal Stack Store — Stack-based modal management for @
|
|
2
|
+
* Modal Stack Store — Stack-based modal management for @stephen-netu/design-system.
|
|
3
3
|
*
|
|
4
4
|
* Manages a stack of modals (not just one). Opening a modal pushes onto the
|
|
5
5
|
* stack; closing pops. Enables "back" navigation when opening modals from
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Adopted from Chronicler's modalStack.ts pattern (2026-05-25 investigation).
|
|
9
9
|
*
|
|
10
10
|
* Usage:
|
|
11
|
-
* import { modalStackStore } from '@
|
|
11
|
+
* import { modalStackStore } from '@stephen-netu/design-system/s0-state';
|
|
12
12
|
*
|
|
13
13
|
* // Push a modal
|
|
14
14
|
* modalStackStore.push({ id: 'confirm-delete', props: { itemId: 'abc' } });
|
|
@@ -41,7 +41,7 @@ export class ForceSimulation {
|
|
|
41
41
|
render(ctx) {
|
|
42
42
|
for (const pos of this.positions) {
|
|
43
43
|
const r = pos.radius;
|
|
44
|
-
ctx.drawRect({ x: pos.x - r, y: pos.y - r, width: r * 2, height: r * 2 }, '#
|
|
44
|
+
ctx.drawRect({ x: pos.x - r, y: pos.y - r, width: r * 2, height: r * 2 }, 'var(--color-accent, #888)');
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
// ── Internal ─────────────────────────────────────────────────────────────
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// ParticleAssembleMark — animates a canvas particle field converging into
|
|
3
|
+
// an arbitrary set of SVG-authored polygons (a logomark, glyph, or any
|
|
4
|
+
// flat-fill shape). Generalizes the "form the mark from particles" hero
|
|
5
|
+
// moment pattern.
|
|
6
|
+
//
|
|
7
|
+
// NOTE: Heavy stateful component (canvas + RAF loop). Use {#if} to fully
|
|
8
|
+
// mount/unmount — visibility:hidden would keep the rAF loop running.
|
|
9
|
+
//
|
|
10
|
+
// S-05: the assembly RAF loop is bounded — it terminates once particles
|
|
11
|
+
// settle and switches to a single static draw; it does not run forever.
|
|
12
|
+
// Per-particle start edge/position/delay are cosmetically randomized for
|
|
13
|
+
// visual variety only (no business logic depends on this), so
|
|
14
|
+
// Math.random() here does not carry the S-02 determinism concern that
|
|
15
|
+
// applies to kernel/backend computation.
|
|
16
|
+
|
|
17
|
+
import { onMount, onDestroy } from 'svelte';
|
|
18
|
+
import { fitScale, easeOutCubic, particleFrame } from './particle-assemble-math.js';
|
|
19
|
+
|
|
20
|
+
export interface ParticleShape {
|
|
21
|
+
/** Polygon points in the coordinate space of viewBoxWidth/viewBoxHeight. */
|
|
22
|
+
points: [number, number][];
|
|
23
|
+
/** 'primary' (gradient fill) or 'secondary' (solid accent fill). Defaults to 'primary'. */
|
|
24
|
+
tone?: 'primary' | 'secondary';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Props {
|
|
28
|
+
shapes: ParticleShape[];
|
|
29
|
+
viewBoxWidth: number;
|
|
30
|
+
viewBoxHeight: number;
|
|
31
|
+
width?: number;
|
|
32
|
+
height?: number;
|
|
33
|
+
primaryColor?: string;
|
|
34
|
+
primaryColor2?: string;
|
|
35
|
+
secondaryColor?: string;
|
|
36
|
+
durationMs?: number;
|
|
37
|
+
active?: boolean;
|
|
38
|
+
class?: string;
|
|
39
|
+
'aria-label'?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let {
|
|
43
|
+
shapes,
|
|
44
|
+
viewBoxWidth,
|
|
45
|
+
viewBoxHeight,
|
|
46
|
+
width = 200,
|
|
47
|
+
height = 200,
|
|
48
|
+
primaryColor = 'currentColor',
|
|
49
|
+
primaryColor2,
|
|
50
|
+
secondaryColor = 'currentColor',
|
|
51
|
+
durationMs = 900,
|
|
52
|
+
active = true,
|
|
53
|
+
class: className = '',
|
|
54
|
+
'aria-label': ariaLabel
|
|
55
|
+
}: Props = $props();
|
|
56
|
+
|
|
57
|
+
interface Particle {
|
|
58
|
+
x: number;
|
|
59
|
+
y: number;
|
|
60
|
+
tx: number;
|
|
61
|
+
ty: number;
|
|
62
|
+
color: string;
|
|
63
|
+
r: number;
|
|
64
|
+
delay: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let canvas: HTMLCanvasElement | undefined = $state();
|
|
68
|
+
let settled = $state(false);
|
|
69
|
+
let rafId: number | null = null;
|
|
70
|
+
let particles: Particle[] = [];
|
|
71
|
+
let observer: IntersectionObserver | null = null;
|
|
72
|
+
let lastDx = 0;
|
|
73
|
+
let lastDy = 0;
|
|
74
|
+
let lastScale = 1;
|
|
75
|
+
const dpr = typeof window !== 'undefined' ? Math.min(window.devicePixelRatio || 1, 3) : 1;
|
|
76
|
+
|
|
77
|
+
// Canvas fillStyle cannot parse CSS custom properties or `currentColor` —
|
|
78
|
+
// it isn't part of the CSSOM cascade. Resolve those to a concrete color
|
|
79
|
+
// via the canvas element's computed style before handing them to canvas.
|
|
80
|
+
function resolveColor(value: string): string {
|
|
81
|
+
if (!canvas) return value;
|
|
82
|
+
const trimmed = value.trim();
|
|
83
|
+
const varMatch = trimmed.match(/^var\((--[\w-]+)(?:\s*,\s*(.+))?\)$/);
|
|
84
|
+
if (varMatch) {
|
|
85
|
+
const resolved = getComputedStyle(canvas).getPropertyValue(varMatch[1]).trim();
|
|
86
|
+
return resolved || varMatch[2] || trimmed;
|
|
87
|
+
}
|
|
88
|
+
if (trimmed === 'currentColor') {
|
|
89
|
+
return getComputedStyle(canvas).color;
|
|
90
|
+
}
|
|
91
|
+
return trimmed;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function drawShapes(
|
|
95
|
+
ctx: CanvasRenderingContext2D,
|
|
96
|
+
dx: number,
|
|
97
|
+
dy: number,
|
|
98
|
+
scale: number
|
|
99
|
+
) {
|
|
100
|
+
const resolvedPrimary = resolveColor(primaryColor);
|
|
101
|
+
const resolvedPrimary2 = resolveColor(primaryColor2 || primaryColor);
|
|
102
|
+
const resolvedSecondary = resolveColor(secondaryColor);
|
|
103
|
+
|
|
104
|
+
const gradient = ctx.createLinearGradient(dx, dy, dx + viewBoxWidth * scale, dy + viewBoxHeight * scale);
|
|
105
|
+
gradient.addColorStop(0, resolvedPrimary);
|
|
106
|
+
gradient.addColorStop(1, resolvedPrimary2);
|
|
107
|
+
|
|
108
|
+
for (const shape of shapes) {
|
|
109
|
+
ctx.fillStyle = shape.tone === 'secondary' ? resolvedSecondary : gradient;
|
|
110
|
+
ctx.beginPath();
|
|
111
|
+
shape.points.forEach(([px, py], i) => {
|
|
112
|
+
const x = dx + px * scale;
|
|
113
|
+
const y = dy + py * scale;
|
|
114
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
115
|
+
else ctx.lineTo(x, y);
|
|
116
|
+
});
|
|
117
|
+
ctx.closePath();
|
|
118
|
+
ctx.fill();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function buildTargets(): { x: number; y: number; r: number; g: number; b: number }[] {
|
|
123
|
+
if (!canvas) return [];
|
|
124
|
+
const cssW = canvas.clientWidth || width;
|
|
125
|
+
const cssH = canvas.clientHeight || height;
|
|
126
|
+
canvas.width = cssW * dpr;
|
|
127
|
+
canvas.height = cssH * dpr;
|
|
128
|
+
|
|
129
|
+
const off = document.createElement('canvas');
|
|
130
|
+
off.width = canvas.width;
|
|
131
|
+
off.height = canvas.height;
|
|
132
|
+
const offCtx = off.getContext('2d');
|
|
133
|
+
if (!offCtx) return [];
|
|
134
|
+
|
|
135
|
+
const { scale, dx, dy } = fitScale(viewBoxWidth, viewBoxHeight, canvas.width, canvas.height);
|
|
136
|
+
|
|
137
|
+
drawShapes(offCtx, dx, dy, scale);
|
|
138
|
+
lastDx = dx;
|
|
139
|
+
lastDy = dy;
|
|
140
|
+
lastScale = scale;
|
|
141
|
+
|
|
142
|
+
const data = offCtx.getImageData(0, 0, off.width, off.height).data;
|
|
143
|
+
const step = Math.max(1, Math.floor(dpr * 1.15));
|
|
144
|
+
const pts: { x: number; y: number; r: number; g: number; b: number }[] = [];
|
|
145
|
+
for (let y = 0; y < off.height; y += step) {
|
|
146
|
+
for (let x = 0; x < off.width; x += step) {
|
|
147
|
+
const idx = (y * off.width + x) * 4;
|
|
148
|
+
const alpha = data[idx + 3];
|
|
149
|
+
if (alpha > 100) {
|
|
150
|
+
pts.push({ x, y, r: data[idx], g: data[idx + 1], b: data[idx + 2] });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return pts;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function initParticles() {
|
|
158
|
+
const pts = buildTargets();
|
|
159
|
+
if (!canvas) return;
|
|
160
|
+
const cw = canvas.width;
|
|
161
|
+
const ch = canvas.height;
|
|
162
|
+
particles = pts.map((p) => {
|
|
163
|
+
const edge = Math.floor(Math.random() * 4);
|
|
164
|
+
let sx: number;
|
|
165
|
+
let sy: number;
|
|
166
|
+
if (edge === 0) { sx = -40; sy = Math.random() * ch; }
|
|
167
|
+
else if (edge === 1) { sx = cw + 40; sy = Math.random() * ch; }
|
|
168
|
+
else if (edge === 2) { sx = Math.random() * cw; sy = -40; }
|
|
169
|
+
else { sx = Math.random() * cw; sy = ch + 40; }
|
|
170
|
+
return {
|
|
171
|
+
x: sx,
|
|
172
|
+
y: sy,
|
|
173
|
+
tx: p.x,
|
|
174
|
+
ty: p.y,
|
|
175
|
+
color: `rgb(${p.r},${p.g},${p.b})`,
|
|
176
|
+
r: (Math.random() * 1.0 + 1.3) * dpr,
|
|
177
|
+
delay: Math.random() * 300
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function renderFinalMark() {
|
|
183
|
+
const ctx = canvas?.getContext('2d');
|
|
184
|
+
if (!ctx || !canvas) return;
|
|
185
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
186
|
+
drawShapes(ctx, lastDx, lastDy, lastScale);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function animate(now: number, startTime: number) {
|
|
190
|
+
const ctx = canvas?.getContext('2d');
|
|
191
|
+
if (!ctx || !canvas) return;
|
|
192
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
193
|
+
let allDone = true;
|
|
194
|
+
for (const p of particles) {
|
|
195
|
+
const t = now - startTime - p.delay;
|
|
196
|
+
if (t < 0) { allDone = false; continue; }
|
|
197
|
+
const progress = t / durationMs;
|
|
198
|
+
const eased = easeOutCubic(progress);
|
|
199
|
+
if (progress < 1) allDone = false;
|
|
200
|
+
const frame = particleFrame(p, eased);
|
|
201
|
+
ctx.globalAlpha = frame.alpha;
|
|
202
|
+
ctx.fillStyle = p.color;
|
|
203
|
+
ctx.beginPath();
|
|
204
|
+
ctx.arc(frame.x, frame.y, p.r, 0, Math.PI * 2);
|
|
205
|
+
ctx.fill();
|
|
206
|
+
}
|
|
207
|
+
ctx.globalAlpha = 1;
|
|
208
|
+
if (!allDone) {
|
|
209
|
+
rafId = requestAnimationFrame((n) => animate(n, startTime));
|
|
210
|
+
} else {
|
|
211
|
+
renderFinalMark();
|
|
212
|
+
settled = true;
|
|
213
|
+
rafId = null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function start() {
|
|
218
|
+
if (!canvas) return;
|
|
219
|
+
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
220
|
+
initParticles();
|
|
221
|
+
if (reduceMotion) {
|
|
222
|
+
renderFinalMark();
|
|
223
|
+
settled = true;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
settled = false;
|
|
227
|
+
const now = performance.now();
|
|
228
|
+
rafId = requestAnimationFrame((n) => animate(n, n));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function replay() {
|
|
232
|
+
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
233
|
+
rafId = null;
|
|
234
|
+
start();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Re-run assembly when colors change (e.g. an accent-seed picker swap),
|
|
238
|
+
// mirroring the settled-mark retheme behavior — only after first settle,
|
|
239
|
+
// so this never fires during the initial mount's own assembly.
|
|
240
|
+
$effect(() => {
|
|
241
|
+
primaryColor; primaryColor2; secondaryColor;
|
|
242
|
+
if (settled) replay();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
onMount(() => {
|
|
246
|
+
if (!active || !canvas) return;
|
|
247
|
+
if ('IntersectionObserver' in window) {
|
|
248
|
+
observer = new IntersectionObserver(
|
|
249
|
+
(entries) => {
|
|
250
|
+
for (const entry of entries) {
|
|
251
|
+
if (entry.isIntersecting) {
|
|
252
|
+
start();
|
|
253
|
+
observer?.disconnect();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
{ threshold: 0.3 }
|
|
258
|
+
);
|
|
259
|
+
observer.observe(canvas);
|
|
260
|
+
} else {
|
|
261
|
+
start();
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
onDestroy(() => {
|
|
266
|
+
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
267
|
+
observer?.disconnect();
|
|
268
|
+
});
|
|
269
|
+
</script>
|
|
270
|
+
|
|
271
|
+
<canvas
|
|
272
|
+
bind:this={canvas}
|
|
273
|
+
class="ds-particle-assemble-mark {className}"
|
|
274
|
+
style:width="{width}px"
|
|
275
|
+
style:height="{height}px"
|
|
276
|
+
aria-hidden={ariaLabel ? undefined : 'true'}
|
|
277
|
+
aria-label={ariaLabel}
|
|
278
|
+
role={ariaLabel ? 'img' : undefined}
|
|
279
|
+
></canvas>
|
|
280
|
+
|
|
281
|
+
<style>
|
|
282
|
+
.ds-particle-assemble-mark {
|
|
283
|
+
display: block;
|
|
284
|
+
}
|
|
285
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ParticleShape {
|
|
2
|
+
/** Polygon points in the coordinate space of viewBoxWidth/viewBoxHeight. */
|
|
3
|
+
points: [number, number][];
|
|
4
|
+
/** 'primary' (gradient fill) or 'secondary' (solid accent fill). Defaults to 'primary'. */
|
|
5
|
+
tone?: 'primary' | 'secondary';
|
|
6
|
+
}
|
|
7
|
+
export interface Props {
|
|
8
|
+
shapes: ParticleShape[];
|
|
9
|
+
viewBoxWidth: number;
|
|
10
|
+
viewBoxHeight: number;
|
|
11
|
+
width?: number;
|
|
12
|
+
height?: number;
|
|
13
|
+
primaryColor?: string;
|
|
14
|
+
primaryColor2?: string;
|
|
15
|
+
secondaryColor?: string;
|
|
16
|
+
durationMs?: number;
|
|
17
|
+
active?: boolean;
|
|
18
|
+
class?: string;
|
|
19
|
+
'aria-label'?: string;
|
|
20
|
+
}
|
|
21
|
+
declare const ParticleAssembleMark: import("svelte").Component<Props, {}, "">;
|
|
22
|
+
type ParticleAssembleMark = ReturnType<typeof ParticleAssembleMark>;
|
|
23
|
+
export default ParticleAssembleMark;
|
|
24
|
+
//# sourceMappingURL=ParticleAssembleMark.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParticleAssembleMark.svelte.d.ts","sourceRoot":"","sources":["../../src/spatial/ParticleAssembleMark.svelte.ts"],"names":[],"mappings":"AAqBE,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC3B,2FAA2F;IAC3F,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;CAChC;AAED,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkQH,QAAA,MAAM,oBAAoB,2CAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createDefaultBsp, splitZone, flattenBsp, findZone, serializeBsp, deserializeBsp, } from './zone-tiler-types';
|
|
3
|
+
const dt = 6;
|
|
4
|
+
describe('zone-tiler-types', () => {
|
|
5
|
+
it('createDefaultBsp produces a single leaf node', () => {
|
|
6
|
+
const tree = createDefaultBsp('main', 'Main Zone');
|
|
7
|
+
expect(tree.kind).toBe('leaf');
|
|
8
|
+
expect(tree.id).toBe('main');
|
|
9
|
+
expect(tree.label).toBe('Main Zone');
|
|
10
|
+
expect(tree.privacyState).toBe('ouvert');
|
|
11
|
+
});
|
|
12
|
+
it('splitZone replaces a leaf with a split node', () => {
|
|
13
|
+
const tree = createDefaultBsp('z1', 'Zone 1');
|
|
14
|
+
const newZone = { kind: 'leaf', id: 'z2', label: 'Zone 2', privacyState: 'ouvert' };
|
|
15
|
+
const result = splitZone(tree, 'z1', 'horizontal', newZone, 0.5);
|
|
16
|
+
expect(result.kind).toBe('split');
|
|
17
|
+
if (result.kind === 'split') {
|
|
18
|
+
expect(result.direction).toBe('horizontal');
|
|
19
|
+
expect(result.ratio).toBe(0.5);
|
|
20
|
+
expect(result.first.id).toBe('z1');
|
|
21
|
+
expect(result.second.id).toBe('z2');
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
it('flattenBsp returns one zone for a single leaf', () => {
|
|
25
|
+
const tree = createDefaultBsp('leaf1', 'Leaf');
|
|
26
|
+
const result = flattenBsp(tree, 0, 0, 800, 600, dt);
|
|
27
|
+
expect(result.zones.length).toBe(1);
|
|
28
|
+
expect(result.zones[0].id).toBe('leaf1');
|
|
29
|
+
expect(result.zones[0].x).toBe(0);
|
|
30
|
+
expect(result.zones[0].y).toBe(0);
|
|
31
|
+
expect(result.zones[0].width).toBe(800);
|
|
32
|
+
expect(result.zones[0].height).toBe(600);
|
|
33
|
+
expect(result.dividers.length).toBe(0);
|
|
34
|
+
});
|
|
35
|
+
it('flattenBsp produces two zones and one divider for a horizontal split', () => {
|
|
36
|
+
const tree = createDefaultBsp('z1', 'A');
|
|
37
|
+
const newZone = { kind: 'leaf', id: 'z2', label: 'B', privacyState: 'ouvert' };
|
|
38
|
+
const split = splitZone(tree, 'z1', 'horizontal', newZone, 0.5);
|
|
39
|
+
const result = flattenBsp(split, 0, 0, 800, 600, dt);
|
|
40
|
+
expect(result.zones.length).toBe(2);
|
|
41
|
+
expect(result.dividers.length).toBe(1);
|
|
42
|
+
expect(result.dividers[0].direction).toBe('horizontal');
|
|
43
|
+
});
|
|
44
|
+
it('flattenBsp zones do not overlap', () => {
|
|
45
|
+
const tree = createDefaultBsp('z1', 'A');
|
|
46
|
+
const z2 = { kind: 'leaf', id: 'z2', label: 'B', privacyState: 'ouvert' };
|
|
47
|
+
const split = splitZone(tree, 'z1', 'vertical', z2, 0.5);
|
|
48
|
+
const result = flattenBsp(split, 0, 0, 800, 600, dt);
|
|
49
|
+
const a = result.zones[0];
|
|
50
|
+
const b = result.zones[1];
|
|
51
|
+
const overlapX = a.x < b.x + b.width && a.x + a.width > b.x;
|
|
52
|
+
const overlapY = a.y < b.y + b.height && a.y + a.height > b.y;
|
|
53
|
+
expect(overlapX && overlapY).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it('findZone locates a leaf by id', () => {
|
|
56
|
+
const tree = createDefaultBsp('target', 'Target');
|
|
57
|
+
const found = findZone(tree, 'target');
|
|
58
|
+
expect(found).not.toBeNull();
|
|
59
|
+
expect(found.id).toBe('target');
|
|
60
|
+
});
|
|
61
|
+
it('findZone returns null for nonexistent id', () => {
|
|
62
|
+
const tree = createDefaultBsp('only', 'Only');
|
|
63
|
+
expect(findZone(tree, 'missing')).toBeNull();
|
|
64
|
+
});
|
|
65
|
+
it('findZone searches nested splits', () => {
|
|
66
|
+
const tree = createDefaultBsp('root', 'Root');
|
|
67
|
+
const child = { kind: 'leaf', id: 'child', label: 'Child', privacyState: 'ouvert' };
|
|
68
|
+
const split = splitZone(tree, 'root', 'horizontal', child);
|
|
69
|
+
const found = findZone(split, 'child');
|
|
70
|
+
expect(found).not.toBeNull();
|
|
71
|
+
expect(found.id).toBe('child');
|
|
72
|
+
});
|
|
73
|
+
it('serializeBsp and deserializeBsp round-trip', () => {
|
|
74
|
+
const tree = createDefaultBsp('rt', 'RoundTrip');
|
|
75
|
+
const child = { kind: 'leaf', id: 'c', label: 'C', privacyState: 'ouvert' };
|
|
76
|
+
const split = splitZone(tree, 'rt', 'vertical', child, 0.7);
|
|
77
|
+
const json = serializeBsp(split);
|
|
78
|
+
const restored = deserializeBsp(json);
|
|
79
|
+
expect(restored.kind).toBe('split');
|
|
80
|
+
if (restored.kind === 'split') {
|
|
81
|
+
expect(restored.ratio).toBe(0.7);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
it('flattenBsp with nested splits produces correct zone count', () => {
|
|
85
|
+
let tree = createDefaultBsp('z1', 'A');
|
|
86
|
+
tree = splitZone(tree, 'z1', 'horizontal', { kind: 'leaf', id: 'z2', label: 'B', privacyState: 'ouvert' });
|
|
87
|
+
const result = flattenBsp(tree, 0, 0, 800, 600, dt);
|
|
88
|
+
expect(result.zones.length).toBe(2);
|
|
89
|
+
});
|
|
90
|
+
});
|
package/dist/spatial/index.js
CHANGED
|
@@ -15,3 +15,4 @@ export { CameraController } from './CameraController.svelte.js';
|
|
|
15
15
|
// import LodRenderer from '@kos/design-system/spatial/LodRenderer.svelte';
|
|
16
16
|
// import SnapZoomCamera from '@kos/design-system/spatial/SnapZoomCamera.svelte';
|
|
17
17
|
// import BspTilingCanvas from '@kos/design-system/spatial/BspTilingCanvas.svelte';
|
|
18
|
+
// import ParticleAssembleMark from '@kos/design-system/spatial/ParticleAssembleMark.svelte';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface Fit {
|
|
2
|
+
scale: number;
|
|
3
|
+
dx: number;
|
|
4
|
+
dy: number;
|
|
5
|
+
}
|
|
6
|
+
/** Fit a viewBoxWidth x viewBoxHeight shape, centered, into canvasWidth x canvasHeight. */
|
|
7
|
+
export declare function fitScale(viewBoxWidth: number, viewBoxHeight: number, canvasWidth: number, canvasHeight: number, padRatio?: number): Fit;
|
|
8
|
+
export declare function easeOutCubic(progress: number): number;
|
|
9
|
+
export declare function lerp(a: number, b: number, t: number): number;
|
|
10
|
+
export interface ParticleFrame {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
alpha: number;
|
|
14
|
+
}
|
|
15
|
+
/** Position + alpha for a particle at a given eased progress (0..1). */
|
|
16
|
+
export declare function particleFrame(particle: {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
tx: number;
|
|
20
|
+
ty: number;
|
|
21
|
+
}, eased: number): ParticleFrame;
|
|
22
|
+
//# sourceMappingURL=particle-assemble-math.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"particle-assemble-math.d.ts","sourceRoot":"","sources":["../../src/spatial/particle-assemble-math.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,2FAA2F;AAC3F,wBAAgB,QAAQ,CACtB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,QAAQ,SAAO,GACd,GAAG,CAKL;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAC3B,QAAQ,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAC1D,KAAK,EAAE,MAAM,GACZ,aAAa,CAMf"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Pure math for ParticleAssembleMark — kept separate from the .svelte
|
|
2
|
+
// RAF/canvas runner so it's unit-testable without a real canvas 2D context
|
|
3
|
+
// (mirrors this dir's split between *-types.ts/simulation logic and the
|
|
4
|
+
// thin canvas-runner .svelte components, e.g. ForceSimulation vs ForceCanvas).
|
|
5
|
+
/** Fit a viewBoxWidth x viewBoxHeight shape, centered, into canvasWidth x canvasHeight. */
|
|
6
|
+
export function fitScale(viewBoxWidth, viewBoxHeight, canvasWidth, canvasHeight, padRatio = 0.92) {
|
|
7
|
+
const scale = Math.min(canvasWidth / viewBoxWidth, canvasHeight / viewBoxHeight) * padRatio;
|
|
8
|
+
const dx = (canvasWidth - viewBoxWidth * scale) / 2;
|
|
9
|
+
const dy = (canvasHeight - viewBoxHeight * scale) / 2;
|
|
10
|
+
return { scale, dx, dy };
|
|
11
|
+
}
|
|
12
|
+
export function easeOutCubic(progress) {
|
|
13
|
+
const clamped = Math.min(1, Math.max(0, progress));
|
|
14
|
+
return 1 - Math.pow(1 - clamped, 3);
|
|
15
|
+
}
|
|
16
|
+
export function lerp(a, b, t) {
|
|
17
|
+
return a + (b - a) * t;
|
|
18
|
+
}
|
|
19
|
+
/** Position + alpha for a particle at a given eased progress (0..1). */
|
|
20
|
+
export function particleFrame(particle, eased) {
|
|
21
|
+
return {
|
|
22
|
+
x: lerp(particle.x, particle.tx, eased),
|
|
23
|
+
y: lerp(particle.y, particle.ty, eased),
|
|
24
|
+
alpha: 0.3 + eased * 0.7
|
|
25
|
+
};
|
|
26
|
+
}
|