@stephen-netu/design-system 0.2.1 → 0.2.2
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/b0-app-shell/clarification-overlay/ClarificationOverlay.svelte +10 -2
- package/dist/b0-app-shell/clarification-overlay/ClarificationOverlay.svelte.d.ts.map +1 -1
- package/dist/b0-app-shell/{command-bar/CommandBar.svelte → global-command-bar/GlobalCommandBar.svelte} +75 -75
- package/dist/b0-app-shell/{command-bar/CommandBar.svelte.d.ts → global-command-bar/GlobalCommandBar.svelte.d.ts} +6 -6
- package/dist/b0-app-shell/global-command-bar/GlobalCommandBar.svelte.d.ts.map +1 -0
- package/dist/b0-app-shell/index.d.ts +1 -1
- package/dist/b0-app-shell/index.d.ts.map +1 -1
- package/dist/b0-app-shell/index.js +1 -1
- package/dist/d0-data-viz/CheckpointPill.svelte +7 -2
- package/dist/d0-data-viz/CheckpointPill.svelte.d.ts.map +1 -1
- package/dist/d0-data-viz/theme-integration.d.ts +3 -3
- package/dist/d0-data-viz/theme-integration.js +3 -3
- package/dist/d0-data-viz/theme-integration.test.js +41 -0
- package/dist/demos/BspSizingInfographic.svelte +3 -32
- package/dist/editor/CodeEditor.test.js +38 -0
- package/dist/editor/FindReplaceDialog.svelte +32 -32
- package/dist/editor/MarkdownEditor.svelte +71 -58
- package/dist/editor/MarkdownEditor.svelte.d.ts.map +1 -1
- package/dist/editor/MarkdownEditor.test.js +38 -0
- package/dist/editor/components/CorrectionTooltip.svelte +0 -1
- package/dist/entity-graph-view/WorldView.svelte +1 -1
- package/dist/fabric/ai/ChatPanel.svelte +31 -31
- package/dist/fabric/ai/ResearchPanel.svelte +6 -3
- package/dist/fabric/ai/ResearchPanel.svelte.d.ts.map +1 -1
- package/dist/fabric/index.d.ts +4 -5
- package/dist/fabric/index.d.ts.map +1 -1
- package/dist/fabric/index.js +3 -3
- package/dist/fabric/input/{CommandBar.svelte → AppHeaderBar.svelte} +13 -13
- package/dist/fabric/input/{CommandBar.svelte.d.ts → AppHeaderBar.svelte.d.ts} +5 -5
- package/dist/fabric/input/AppHeaderBar.svelte.d.ts.map +1 -0
- package/dist/fabric/input/{CommandBar.test.js → AppHeaderBar.test.js} +9 -9
- package/dist/fabric/input/index.d.ts +2 -2
- package/dist/fabric/input/index.d.ts.map +1 -1
- package/dist/fabric/input/index.js +1 -1
- package/dist/fabric/layout/KanbanBoard.svelte +25 -155
- package/dist/fabric/layout/KanbanBoard.svelte.d.ts +2 -26
- package/dist/fabric/layout/KanbanBoard.svelte.d.ts.map +1 -1
- package/dist/fabric/layout/KanbanBoard.test.js +125 -10
- package/dist/fabric/layout/KanbanCard.svelte +27 -223
- package/dist/fabric/layout/KanbanCard.svelte.d.ts +2 -19
- package/dist/fabric/layout/KanbanCard.svelte.d.ts.map +1 -1
- package/dist/fabric/layout/KanbanColumn.svelte +41 -275
- package/dist/fabric/layout/KanbanColumn.svelte.d.ts +2 -22
- package/dist/fabric/layout/KanbanColumn.svelte.d.ts.map +1 -1
- package/dist/fabric/layout/index.d.ts +2 -5
- package/dist/fabric/layout/index.d.ts.map +1 -1
- package/dist/fabric/layout/index.js +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/l0-layout/SectionSplitter.svelte +5 -5
- package/dist/l0-layout/block-writer/BlockItem.svelte +6 -4
- package/dist/l0-layout/card/CompactCard.svelte +4 -3
- package/dist/l0-layout/grant/GrantCard.svelte +15 -5
- package/dist/l0-layout/grant/GrantCard.svelte.d.ts.map +1 -1
- package/dist/l0-layout/index.d.ts +2 -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 +246 -29
- package/dist/l0-layout/kanban/KanbanBoard.svelte.d.ts +28 -2
- package/dist/l0-layout/kanban/KanbanBoard.svelte.d.ts.map +1 -1
- package/dist/l0-layout/kanban/KanbanCard.svelte +175 -150
- package/dist/l0-layout/kanban/KanbanCard.svelte.d.ts +16 -18
- package/dist/l0-layout/kanban/KanbanCard.svelte.d.ts.map +1 -1
- package/dist/l0-layout/kanban/KanbanColumn.svelte +152 -78
- package/dist/l0-layout/kanban/KanbanColumn.svelte.d.ts +28 -7
- package/dist/l0-layout/kanban/KanbanColumn.svelte.d.ts.map +1 -1
- package/dist/l0-layout/kanban/index.d.ts +6 -0
- package/dist/l0-layout/kanban/index.d.ts.map +1 -0
- package/dist/l0-layout/kanban/index.js +3 -0
- package/dist/n0-node-graph/Node.svelte +2 -2
- package/dist/n0-node-graph/NodePalette.svelte +2 -2
- package/dist/n1-flow-canvas/graph-state.test.js +69 -0
- package/dist/n1-flow-canvas/utils.test.js +116 -0
- package/dist/n3-force-graph/GraphTooltip.svelte +2 -2
- package/dist/n4-adaptive-manifold/AdaptiveManifold.svelte +12 -11
- package/dist/n4-adaptive-manifold/AdaptiveManifold.svelte.d.ts.map +1 -1
- package/dist/n4-adaptive-manifold/classifier.test.js +104 -0
- package/dist/n4-adaptive-manifold/n4-tokens.css +2 -2
- package/dist/p0-primitives/ThemeProvider.svelte +10 -11
- package/dist/p0-primitives/ThemeProvider.svelte.d.ts +1 -1
- package/dist/p0-primitives/ThemeProvider.svelte.d.ts.map +1 -1
- package/dist/p0-primitives/canvas-theme.d.ts +12 -0
- package/dist/p0-primitives/canvas-theme.d.ts.map +1 -1
- package/dist/p0-primitives/canvas-theme.js +10 -0
- package/dist/p0-primitives/index.d.ts +2 -1
- package/dist/p0-primitives/index.d.ts.map +1 -1
- package/dist/p0-primitives/index.js +1 -0
- package/dist/p0-primitives/theme-context.d.ts +9 -0
- package/dist/p0-primitives/theme-context.d.ts.map +1 -0
- package/dist/p0-primitives/theme-context.js +8 -0
- package/dist/p0-primitives/tokens/tokens.css +22 -11
- package/dist/p0-primitives/utils/index.d.ts +2 -2
- package/dist/p0-primitives/utils/index.d.ts.map +1 -1
- package/dist/s0-state/SyncStatus.svelte +1 -1
- package/dist/s0-state/ThemeStore.svelte.d.ts +3 -2
- package/dist/s0-state/ThemeStore.svelte.d.ts.map +1 -1
- package/dist/s0-state/ThemeStore.svelte.js +20 -1
- package/dist/s0-state/index.d.ts +0 -1
- package/dist/s0-state/index.d.ts.map +1 -1
- package/dist/s0-state/index.js +1 -0
- package/dist/s0-state/modal-stack-store.svelte.d.ts +25 -0
- package/dist/s0-state/modal-stack-store.svelte.d.ts.map +1 -1
- package/dist/s0-state/modal-stack-store.svelte.js +19 -0
- package/dist/showcase/Showcase.svelte +43 -34
- package/dist/showcase/Showcase.svelte.d.ts.map +1 -1
- package/dist/spatial/BspTilingCanvas.svelte +5 -5
- package/dist/spatial/BspTilingSimulation.test.js +88 -0
- package/dist/spatial/CameraController.test.js +120 -0
- package/dist/spatial/ForceCanvas.svelte +18 -13
- package/dist/spatial/ForceCanvas.svelte.d.ts.map +1 -1
- package/dist/spatial/ForceSimulation.svelte.d.ts.map +1 -1
- package/dist/spatial/ForceSimulation.svelte.js +32 -21
- package/dist/spatial/LodRenderer.svelte +14 -1
- package/dist/spatial/LodRenderer.svelte.d.ts.map +1 -1
- package/dist/spatial/SnapZoomCamera.svelte +23 -13
- package/dist/spatial/SnapZoomCamera.svelte.d.ts.map +1 -1
- package/dist/spatial/ZoneTiler.svelte +3 -2
- package/dist/spatial/d3-types.d.ts +31 -0
- package/dist/spatial/d3-types.d.ts.map +1 -0
- package/dist/spatial/d3-types.js +19 -0
- package/dist/ssr-import-clean.test.js +37 -0
- package/dist/t0-transport/fs.d.ts +12 -0
- package/dist/t0-transport/fs.d.ts.map +1 -0
- package/dist/t0-transport/fs.js +55 -0
- package/dist/t0-transport/index.d.ts +1 -0
- package/dist/t0-transport/index.d.ts.map +1 -1
- package/dist/t0-transport/index.js +1 -0
- package/dist/t0-transport/invoke.d.ts.map +1 -1
- package/dist/t0-transport/invoke.js +5 -2
- package/dist/t0-transport/quality.d.ts +12 -3
- package/dist/t0-transport/quality.d.ts.map +1 -1
- package/dist/t0-transport/quality.js +17 -4
- package/dist/t0-transport/substrate.svelte.d.ts +27 -0
- package/dist/t0-transport/substrate.svelte.d.ts.map +1 -0
- package/dist/t0-transport/substrate.svelte.js +49 -0
- package/dist/u0-primitives/LockIndicator.svelte +1 -1
- package/dist/u0-primitives/VerticalToolbar.svelte +1 -1
- package/dist/u0-primitives/accordion/Accordion.svelte +10 -9
- package/dist/u0-primitives/accordion/Accordion.svelte.d.ts +0 -7
- package/dist/u0-primitives/accordion/Accordion.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/accordion/Accordion.test.js +5 -5
- package/dist/u0-primitives/badge/Badge.test.js +10 -10
- package/dist/u0-primitives/badge/ValidityBadge.test.js +9 -9
- package/dist/u0-primitives/button/Button.svelte +3 -1
- package/dist/u0-primitives/button/Button.svelte.d.ts +1 -0
- package/dist/u0-primitives/button/Button.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/button/Button.test.js +11 -11
- package/dist/u0-primitives/card/Card.svelte +26 -20
- package/dist/u0-primitives/card/Card.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/command-palette/CommandPalette.svelte +27 -0
- package/dist/u0-primitives/command-palette/CommandPalette.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/command-palette/commandStore.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/command-palette/commandStore.svelte.js +2 -1
- package/dist/u0-primitives/dropdown/Dropdown.test.js +1 -1
- package/dist/u0-primitives/index.d.ts +2 -0
- package/dist/u0-primitives/index.d.ts.map +1 -1
- package/dist/u0-primitives/index.js +1 -0
- package/dist/u0-primitives/input/Input.svelte +9 -0
- package/dist/u0-primitives/input/Input.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/input/Input.test.js +1 -1
- package/dist/u0-primitives/modal/Modal.svelte +46 -23
- package/dist/u0-primitives/modal/Modal.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/modal/Modal.test.js +63 -15
- package/dist/u0-primitives/rsvp/RsvpReader.svelte +8 -8
- package/dist/u0-primitives/rsvp/RsvpReader.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/steps/Steps.svelte +2 -2
- package/dist/u0-primitives/surface-state/SurfaceState.svelte +190 -0
- package/dist/u0-primitives/surface-state/SurfaceState.svelte.d.ts +15 -0
- package/dist/u0-primitives/surface-state/SurfaceState.svelte.d.ts.map +1 -0
- package/dist/u0-primitives/toggle/Toggle.svelte +12 -2
- package/dist/u0-primitives/tooltip/Tooltip.test.js +12 -12
- package/dist/v0-diagrams/MarkmapDiagram.test.js +49 -0
- package/dist/v0-diagrams/MermaidDiagram.svelte +21 -9
- package/dist/v0-diagrams/MermaidDiagram.svelte.d.ts.map +1 -1
- package/dist/v0-diagrams/MermaidDiagram.test.js +40 -0
- package/package.json +50 -24
- package/src/n4-adaptive-manifold/n4-tokens.css +2 -2
- package/src/p0-primitives/tokens/tokens.css +22 -11
- package/src/p0-primitives/tokens/tokens.json +13 -2
- package/dist/b0-app-shell/command-bar/CommandBar.svelte.d.ts.map +0 -1
- package/dist/fabric/input/CommandBar.svelte.d.ts.map +0 -1
- package/dist/stores/vaultStore.svelte +0 -14
- package/dist/stores/vaultStore.svelte.d.ts +0 -24
- package/dist/stores/vaultStore.svelte.d.ts.map +0 -1
- package/dist/tauri/index.d.ts +0 -3
- package/dist/tauri/index.d.ts.map +0 -1
- package/dist/tauri/index.js +0 -11
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
|
|
90
90
|
<div class="component-section">
|
|
91
91
|
<h3>Inputs & Forms</h3>
|
|
92
|
-
<div class="demo-row" style="max-width:
|
|
92
|
+
<div class="demo-row" style="max-width: 25rem; display: flex; flex-direction: column;">
|
|
93
93
|
<Input placeholder="Standard Input" bind:value={inputValue} />
|
|
94
94
|
<Input placeholder="With leading icon">
|
|
95
95
|
{#snippet iconLeading()} <Search size={16}/> {/snippet}
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
|
|
183
183
|
<div class="component-section">
|
|
184
184
|
<h3>Form Field & Search</h3>
|
|
185
|
-
<div class="demo-row" style="flex-direction: column; max-width:
|
|
185
|
+
<div class="demo-row" style="flex-direction: column; max-width: 25rem;">
|
|
186
186
|
<FormField id="demo-field" label="Email Address" helper="We'll never share your email.">
|
|
187
187
|
<Input id="demo-field" type="email" placeholder="agent@kos.network" />
|
|
188
188
|
</FormField>
|
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
<!-- TODO: Re-add when Chat components exist
|
|
199
199
|
<div class="component-section">
|
|
200
200
|
<h3>List Items</h3>
|
|
201
|
-
|
|
201
|
+
<div class="demo-row" style="flex-direction: column; max-width: 20rem;">
|
|
202
202
|
<ChannelListItem id="c1" name="general-chat" time="2m" unreadCount={5} preview="Hey, did we merge the PR?" />
|
|
203
203
|
<ChannelListItem id="c2" name="architecture-req" isActive preview="Let's discuss the capability model." />
|
|
204
204
|
<ChannelListItem id="c3" name="alerts-noisy" isMuted unreadCount={12} preview="[CRITICAL] Pod failure" />
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
|
|
208
208
|
<div class="component-section">
|
|
209
209
|
<h3>Message Bubbles</h3>
|
|
210
|
-
<div class="demo-row" style="flex-direction: column; max-width:
|
|
210
|
+
<div class="demo-row" style="flex-direction: column; max-width: 31.25rem">
|
|
211
211
|
<MessageBubble id="m1" senderName="Alice" timestamp={Date.now() - 60000} content="..." />
|
|
212
212
|
</div>
|
|
213
213
|
</div>
|
|
@@ -216,12 +216,11 @@
|
|
|
216
216
|
<div class="component-section">
|
|
217
217
|
<h3>Kanban Card</h3>
|
|
218
218
|
<div class="demo-row">
|
|
219
|
-
<div style="width:
|
|
220
|
-
<KanbanCard
|
|
219
|
+
<div style="width: 17.5rem">
|
|
220
|
+
<KanbanCard card={{
|
|
221
221
|
id: '1', title: 'Implement Design Tokens', priority: 'high',
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
assignee: { name: 'Netu' }
|
|
222
|
+
description: 'Add full css variable support to all atomic components.',
|
|
223
|
+
tags: ['Frontend'],
|
|
225
224
|
}} />
|
|
226
225
|
</div>
|
|
227
226
|
</div>
|
|
@@ -235,13 +234,13 @@
|
|
|
235
234
|
<!-- TODO: Re-add when ContextPanel component exists
|
|
236
235
|
<div class="component-section">
|
|
237
236
|
<h3>Context Panel</h3>
|
|
238
|
-
<div style="height:
|
|
239
|
-
<div style="padding:
|
|
237
|
+
<div style="height: 18.75rem; position: relative; border: var(--border-width-thin) solid var(--border-default); overflow: hidden; border-radius: var(--radius-lg);">
|
|
238
|
+
<div style="padding: var(--space-5);">
|
|
240
239
|
<Button onclick={() => isContextPanelOpen = true}>Open Panel</Button>
|
|
241
240
|
</div>
|
|
242
241
|
<ContextPanel bind:isOpen={isContextPanelOpen} title="Task Details" tabs={[]}>
|
|
243
242
|
<div class="ds-stack">
|
|
244
|
-
<KanbanCard
|
|
243
|
+
<KanbanCard card={{id:'x', title:'Sample Task', priority:'urgent'}} />
|
|
245
244
|
</div>
|
|
246
245
|
</ContextPanel>
|
|
247
246
|
</div>
|
|
@@ -250,26 +249,35 @@
|
|
|
250
249
|
|
|
251
250
|
<div class="component-section">
|
|
252
251
|
<h3>Kanban Board</h3>
|
|
253
|
-
<div style="height:
|
|
254
|
-
<KanbanBoard
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
252
|
+
<div style="height: 25rem; border: var(--border-width-thin) solid var(--border-default); border-radius: var(--radius-lg);">
|
|
253
|
+
<KanbanBoard
|
|
254
|
+
columns={[
|
|
255
|
+
{
|
|
256
|
+
column: { id: 'todo', title: 'To Do' },
|
|
257
|
+
cards: [
|
|
258
|
+
{ id: 't1', title: 'Setup repo', priority: 'high' },
|
|
259
|
+
{ id: 't2', title: 'Config vite', priority: 'medium' },
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
column: { id: 'inprog', title: 'In Progress' },
|
|
264
|
+
cards: [
|
|
265
|
+
{ id: 't3', title: 'Build components', priority: 'high' },
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
column: { id: 'done', title: 'Done' },
|
|
270
|
+
cards: [],
|
|
271
|
+
},
|
|
272
|
+
]}
|
|
273
|
+
/>
|
|
266
274
|
</div>
|
|
267
275
|
</div>
|
|
268
276
|
|
|
269
277
|
<!-- TODO: Re-add when ChatPanel and CanvasToolbar components exist
|
|
270
278
|
<div class="component-section">
|
|
271
279
|
<h3>Chat Panel (Organism)</h3>
|
|
272
|
-
<div style="height:
|
|
280
|
+
<div style="height: 31.25rem; border: var(--border-width-thin) solid var(--border-default); border-radius: var(--radius-lg);">
|
|
273
281
|
<ChatPanel title="general-chat" memberCount={42}>
|
|
274
282
|
{#snippet headerActions()}<Button variant="ghost" size="sm"><Search size={16}/></Button>{/snippet}
|
|
275
283
|
{#snippet messages()}<p>Messages here</p>{/snippet}
|
|
@@ -280,7 +288,7 @@
|
|
|
280
288
|
|
|
281
289
|
<div class="component-section">
|
|
282
290
|
<h3>Canvas Toolbar</h3>
|
|
283
|
-
<div style="height:
|
|
291
|
+
<div style="height: 12.5rem; position: relative; background: var(--color-bg-canvas);">
|
|
284
292
|
<CanvasToolbar position="bottom">
|
|
285
293
|
<Button variant="ghost"><Settings size={16}/></Button>
|
|
286
294
|
</CanvasToolbar>
|
|
@@ -298,7 +306,7 @@
|
|
|
298
306
|
<p style="color: var(--color-text-secondary); margin-bottom: 1rem;">
|
|
299
307
|
ADR: ui-timeline-checkpoint-bar-001 — Scrollable checkpoint navigation with color-coded components.
|
|
300
308
|
</p>
|
|
301
|
-
<div style="border:
|
|
309
|
+
<div style="border: var(--border-width-thin) solid var(--border-default); border-radius: var(--radius-lg); overflow: hidden;">
|
|
302
310
|
<CheckpointBar
|
|
303
311
|
checkpoints={demoCheckpoints}
|
|
304
312
|
currentSeqno={demoCurrentSeqno}
|
|
@@ -323,9 +331,9 @@
|
|
|
323
331
|
<h3>Component Color Coding</h3>
|
|
324
332
|
<div class="demo-row">
|
|
325
333
|
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
|
326
|
-
{#each Object.entries({ 'rust-crate': 'var(--color-accent)', 'ui-bundle': '
|
|
334
|
+
{#each Object.entries({ 'rust-crate': 'var(--color-accent)', 'ui-bundle': 'var(--color-info)', 'config': 'var(--color-warning)', 'faculty-state': 'var(--expert-sovereign)', 'substrate-binary': 'var(--color-info)', 'system-snapshot': 'var(--color-offline)' }) as [type, color]}
|
|
327
335
|
<div style="display: flex; align-items: center; gap: 0.75rem;">
|
|
328
|
-
<span style="color: {color}; font-size:
|
|
336
|
+
<span style="color: {color}; font-size: var(--text-base);">◉</span>
|
|
329
337
|
<span style="font-size: 0.85rem; color: var(--color-text-secondary); font-family: var(--font-mono);">{type}</span>
|
|
330
338
|
<span style="font-size: 0.75rem; color: var(--color-text-muted);">{color}</span>
|
|
331
339
|
</div>
|
|
@@ -342,7 +350,7 @@
|
|
|
342
350
|
<style>
|
|
343
351
|
.showcase {
|
|
344
352
|
padding: var(--space-8);
|
|
345
|
-
max-width:
|
|
353
|
+
max-width: 75rem;
|
|
346
354
|
margin: 0 auto;
|
|
347
355
|
font-family: var(--font-sans);
|
|
348
356
|
color: var(--color-text-primary);
|
|
@@ -358,8 +366,9 @@
|
|
|
358
366
|
h1 {
|
|
359
367
|
margin: 0;
|
|
360
368
|
font-size: var(--text-2xl);
|
|
361
|
-
background: linear-gradient(135deg, var(--color-accent) 0%,
|
|
369
|
+
background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-secondary) 100%);
|
|
362
370
|
-webkit-background-clip: text;
|
|
371
|
+
background-clip: text;
|
|
363
372
|
-webkit-text-fill-color: transparent;
|
|
364
373
|
}
|
|
365
374
|
|
|
@@ -367,7 +376,7 @@
|
|
|
367
376
|
margin: 0 0 var(--space-4) 0;
|
|
368
377
|
font-weight: 500;
|
|
369
378
|
color: var(--color-text-secondary);
|
|
370
|
-
border-bottom:
|
|
379
|
+
border-bottom: var(--border-width-thin) solid var(--border-subtle);
|
|
371
380
|
padding-bottom: var(--space-2);
|
|
372
381
|
}
|
|
373
382
|
|
|
@@ -395,7 +404,7 @@
|
|
|
395
404
|
|
|
396
405
|
.glass-bg {
|
|
397
406
|
padding: var(--space-4);
|
|
398
|
-
background: linear-gradient(135deg,
|
|
407
|
+
background: linear-gradient(135deg, var(--color-accent), var(--color-info));
|
|
399
408
|
border-radius: var(--radius-lg);
|
|
400
409
|
}
|
|
401
410
|
</style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Showcase.svelte.d.ts","sourceRoot":"","sources":["../../src/showcase/Showcase.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Showcase.svelte.d.ts","sourceRoot":"","sources":["../../src/showcase/Showcase.svelte.ts"],"names":[],"mappings":"AA+SA,QAAA,MAAM,QAAQ,2DAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
// ── Simulation (reactive) ────────────────────────────────────────────────
|
|
36
36
|
|
|
37
|
-
let sim = $state(
|
|
37
|
+
let sim: BspTilingSimulation | null = $state(null);
|
|
38
38
|
|
|
39
39
|
$effect(() => {
|
|
40
40
|
const c = cards;
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
function runLayout(): void {
|
|
83
|
+
if (!sim) return;
|
|
83
84
|
const cc = makeConstraintContext();
|
|
84
85
|
const lc = makeLayoutContext();
|
|
85
86
|
sim.constrain(cc);
|
|
@@ -111,7 +112,7 @@
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
function paintCanvas(): void {
|
|
114
|
-
if (!canvasCtx) return;
|
|
115
|
+
if (!canvasCtx || !sim) return;
|
|
115
116
|
sim.render(canvasCtx);
|
|
116
117
|
canvasCtx.flush();
|
|
117
118
|
}
|
|
@@ -119,6 +120,7 @@
|
|
|
119
120
|
// ── Event handlers ────────────────────────────────────────────────────────
|
|
120
121
|
|
|
121
122
|
function handlePointerDown(e: PointerEvent): void {
|
|
123
|
+
if (!sim) return;
|
|
122
124
|
const target = e.currentTarget as HTMLElement;
|
|
123
125
|
const ctx = makeInteractionContext(e, target);
|
|
124
126
|
sim.interact(ctx);
|
|
@@ -136,8 +138,6 @@
|
|
|
136
138
|
ctx2d.scale(dpr, dpr);
|
|
137
139
|
}
|
|
138
140
|
canvasCtx = new CanvasRenderContext(canvasEl);
|
|
139
|
-
runLayout();
|
|
140
|
-
paintCanvas();
|
|
141
141
|
});
|
|
142
142
|
</script>
|
|
143
143
|
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
aria-hidden="true"
|
|
150
150
|
></canvas>
|
|
151
151
|
|
|
152
|
-
{#each sim
|
|
152
|
+
{#each (sim?.positions ?? []) as zone (zone.cardId)}
|
|
153
153
|
<div
|
|
154
154
|
class="tiling-slot"
|
|
155
155
|
style:left="{zone.x}px"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { BspTilingSimulation } from './BspTilingSimulation.svelte';
|
|
3
|
+
describe('BspTilingSimulation', () => {
|
|
4
|
+
it('returns empty positions for empty card list', () => {
|
|
5
|
+
const sim = new BspTilingSimulation([], 800, 600);
|
|
6
|
+
sim.layout({ bounds: { x: 0, y: 0, width: 800, height: 600 } });
|
|
7
|
+
expect(sim.positions).toEqual([]);
|
|
8
|
+
});
|
|
9
|
+
it('produces one zone for a single card', () => {
|
|
10
|
+
const sim = new BspTilingSimulation([{ id: 'card-1', minWidth: 200, minHeight: 150 }], 800, 600);
|
|
11
|
+
sim.layout({ bounds: { x: 0, y: 0, width: 800, height: 600 } });
|
|
12
|
+
expect(sim.positions.length).toBe(1);
|
|
13
|
+
expect(sim.positions[0].cardId).toBe('card-1');
|
|
14
|
+
});
|
|
15
|
+
it('produces N zones for N cards', () => {
|
|
16
|
+
const cards = [
|
|
17
|
+
{ id: 'c1', minWidth: 100, minHeight: 100 },
|
|
18
|
+
{ id: 'c2', minWidth: 100, minHeight: 100 },
|
|
19
|
+
{ id: 'c3', minWidth: 100, minHeight: 100 },
|
|
20
|
+
];
|
|
21
|
+
const sim = new BspTilingSimulation(cards, 1200, 800);
|
|
22
|
+
sim.layout({ bounds: { x: 0, y: 0, width: 1200, height: 800 } });
|
|
23
|
+
expect(sim.positions.length).toBe(3);
|
|
24
|
+
const cardIds = sim.positions.map((p) => p.cardId);
|
|
25
|
+
expect(cardIds).toContain('c1');
|
|
26
|
+
expect(cardIds).toContain('c2');
|
|
27
|
+
expect(cardIds).toContain('c3');
|
|
28
|
+
});
|
|
29
|
+
it('zones do not overlap', () => {
|
|
30
|
+
const cards = [
|
|
31
|
+
{ id: 'c1', minWidth: 100, minHeight: 100 },
|
|
32
|
+
{ id: 'c2', minWidth: 100, minHeight: 100 },
|
|
33
|
+
{ id: 'c3', minWidth: 100, minHeight: 100 },
|
|
34
|
+
{ id: 'c4', minWidth: 100, minHeight: 100 },
|
|
35
|
+
];
|
|
36
|
+
const sim = new BspTilingSimulation(cards, 1200, 800);
|
|
37
|
+
sim.layout({ bounds: { x: 0, y: 0, width: 1200, height: 800 } });
|
|
38
|
+
for (let i = 0; i < sim.positions.length; i++) {
|
|
39
|
+
for (let j = i + 1; j < sim.positions.length; j++) {
|
|
40
|
+
const a = sim.positions[i];
|
|
41
|
+
const b = sim.positions[j];
|
|
42
|
+
const overlapX = a.x < b.x + b.width && a.x + a.width > b.x;
|
|
43
|
+
const overlapY = a.y < b.y + b.height && a.y + a.height > b.y;
|
|
44
|
+
expect(overlapX && overlapY).toBe(false);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
it('produces dividers when zones are adjacent', () => {
|
|
49
|
+
// Use a wide container so BSP produces side-by-side zones
|
|
50
|
+
const cards = [
|
|
51
|
+
{ id: 'c1', minWidth: 100, minHeight: 100 },
|
|
52
|
+
{ id: 'c2', minWidth: 100, minHeight: 100 },
|
|
53
|
+
];
|
|
54
|
+
const sim = new BspTilingSimulation(cards, 2000, 400);
|
|
55
|
+
sim.layout({ bounds: { x: 0, y: 0, width: 2000, height: 400 } });
|
|
56
|
+
// Dividers depend on adjacency — with a wide container the BSP
|
|
57
|
+
// should split vertically, producing adjacent zones
|
|
58
|
+
expect(sim.dividers.length).toBeGreaterThanOrEqual(0);
|
|
59
|
+
});
|
|
60
|
+
it('hit test returns consumed for position inside a zone', () => {
|
|
61
|
+
const sim = new BspTilingSimulation([{ id: 'c1', minWidth: 200, minHeight: 200 }], 800, 600);
|
|
62
|
+
sim.layout({ bounds: { x: 0, y: 0, width: 800, height: 600 } });
|
|
63
|
+
const zone = sim.positions[0];
|
|
64
|
+
const result = sim.interact({
|
|
65
|
+
event: { type: 'pointerdown', position: { x: zone.x + 10, y: zone.y + 10 } },
|
|
66
|
+
});
|
|
67
|
+
expect(result).toBe('consumed');
|
|
68
|
+
});
|
|
69
|
+
it('hit test returns propagated for position far outside all zones', () => {
|
|
70
|
+
const sim = new BspTilingSimulation([{ id: 'c1', minWidth: 100, minHeight: 100 }], 200, 200);
|
|
71
|
+
sim.layout({ bounds: { x: 0, y: 0, width: 200, height: 200 } });
|
|
72
|
+
// Zone fills the entire 200x200, so test a position outside bounds
|
|
73
|
+
const result = sim.interact({
|
|
74
|
+
event: { type: 'pointerdown', position: { x: 9999, y: 9999 } },
|
|
75
|
+
});
|
|
76
|
+
expect(result).toBe('propagated');
|
|
77
|
+
});
|
|
78
|
+
it('constrain returns minimum size based on card areas', () => {
|
|
79
|
+
const cards = [
|
|
80
|
+
{ id: 'c1', minWidth: 200, minHeight: 200 },
|
|
81
|
+
{ id: 'c2', minWidth: 200, minHeight: 200 },
|
|
82
|
+
];
|
|
83
|
+
const sim = new BspTilingSimulation(cards, 800, 600);
|
|
84
|
+
const constraints = sim.constrain({});
|
|
85
|
+
expect(constraints.width.min).toBeGreaterThan(0);
|
|
86
|
+
expect(constraints.height.min).toBeGreaterThan(0);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { CameraController } from './CameraController.svelte';
|
|
3
|
+
beforeEach(() => {
|
|
4
|
+
vi.useFakeTimers();
|
|
5
|
+
// Mock requestAnimationFrame to actually invoke the callback
|
|
6
|
+
let rafId = 0;
|
|
7
|
+
const callbacks = new Map();
|
|
8
|
+
vi.stubGlobal('requestAnimationFrame', (cb) => {
|
|
9
|
+
const id = ++rafId;
|
|
10
|
+
callbacks.set(id, cb);
|
|
11
|
+
return id;
|
|
12
|
+
});
|
|
13
|
+
vi.stubGlobal('cancelAnimationFrame', (id) => {
|
|
14
|
+
callbacks.delete(id);
|
|
15
|
+
});
|
|
16
|
+
// Helper to advance rAF by one frame
|
|
17
|
+
globalThis.__flushRaf = () => {
|
|
18
|
+
const cbs = [...callbacks.values()];
|
|
19
|
+
callbacks.clear();
|
|
20
|
+
cbs.forEach((cb) => cb(performance.now()));
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
vi.useRealTimers();
|
|
25
|
+
vi.unstubAllGlobals();
|
|
26
|
+
});
|
|
27
|
+
describe('CameraController', () => {
|
|
28
|
+
it('initializes with default zoom and pan', () => {
|
|
29
|
+
const cam = new CameraController(800, 600);
|
|
30
|
+
expect(cam.zoom).toBe(1.0);
|
|
31
|
+
expect(cam.panX).toBe(0);
|
|
32
|
+
expect(cam.panY).toBe(0);
|
|
33
|
+
});
|
|
34
|
+
it('returns camera state via getCameraState', () => {
|
|
35
|
+
const cam = new CameraController(800, 600);
|
|
36
|
+
const state = cam.getCameraState();
|
|
37
|
+
expect(state.zoom).toBe(1.0);
|
|
38
|
+
expect(state.panX).toBe(0);
|
|
39
|
+
expect(state.panY).toBe(0);
|
|
40
|
+
});
|
|
41
|
+
it('clamps zoom to min/max bounds', () => {
|
|
42
|
+
const cam = new CameraController(800, 600, { minZoom: 0.5, maxZoom: 3.0 });
|
|
43
|
+
cam.layout({ viewportSize: { width: 800, height: 600 } });
|
|
44
|
+
for (let i = 0; i < 100; i++) {
|
|
45
|
+
cam.interact({
|
|
46
|
+
event: {
|
|
47
|
+
type: 'wheel',
|
|
48
|
+
position: { x: 400, y: 300 },
|
|
49
|
+
nativeEvent: { deltaY: -50, preventDefault: () => { } },
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
expect(cam.zoom).toBeLessThanOrEqual(3.0);
|
|
54
|
+
for (let i = 0; i < 200; i++) {
|
|
55
|
+
cam.interact({
|
|
56
|
+
event: {
|
|
57
|
+
type: 'wheel',
|
|
58
|
+
position: { x: 400, y: 300 },
|
|
59
|
+
nativeEvent: { deltaY: 50, preventDefault: () => { } },
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
expect(cam.zoom).toBeGreaterThanOrEqual(0.5);
|
|
64
|
+
});
|
|
65
|
+
it('pan moves camera position', () => {
|
|
66
|
+
const cam = new CameraController(800, 600);
|
|
67
|
+
cam.layout({ viewportSize: { width: 800, height: 600 } });
|
|
68
|
+
cam.interact({
|
|
69
|
+
event: {
|
|
70
|
+
type: 'pointerdown',
|
|
71
|
+
position: { x: 100, y: 100 },
|
|
72
|
+
nativeEvent: { button: 1, preventDefault: () => { } },
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
cam.interact({
|
|
76
|
+
event: {
|
|
77
|
+
type: 'pointermove',
|
|
78
|
+
position: { x: 200, y: 150 },
|
|
79
|
+
nativeEvent: { clientX: 200, clientY: 150 },
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
expect(cam.panX).not.toBe(0);
|
|
83
|
+
expect(cam.panY).not.toBe(0);
|
|
84
|
+
});
|
|
85
|
+
it('resetCamera eventually snaps to origin', () => {
|
|
86
|
+
const cam = new CameraController(800, 600);
|
|
87
|
+
cam.layout({ viewportSize: { width: 800, height: 600 } });
|
|
88
|
+
cam.snapTo({ x: 100, y: 100, zoom: 2.0 });
|
|
89
|
+
// Flush all pending rAF callbacks
|
|
90
|
+
for (let i = 0; i < 100; i++)
|
|
91
|
+
globalThis.__flushRaf();
|
|
92
|
+
cam.resetCamera();
|
|
93
|
+
for (let i = 0; i < 100; i++)
|
|
94
|
+
globalThis.__flushRaf();
|
|
95
|
+
// resetCamera() calls snapTo({x:0, y:0, zoom:1.0})
|
|
96
|
+
// targetPanX = viewportW/2 - x*zoom = 800/2 - 0 = 400
|
|
97
|
+
// targetPanY = viewportH/2 - y*zoom = 600/2 - 0 = 300
|
|
98
|
+
expect(cam.zoom).toBeCloseTo(1.0, 0);
|
|
99
|
+
expect(cam.panX).toBeCloseTo(400, 0);
|
|
100
|
+
expect(cam.panY).toBeCloseTo(300, 0);
|
|
101
|
+
});
|
|
102
|
+
it('stop cancels active animation', () => {
|
|
103
|
+
const cam = new CameraController(800, 600);
|
|
104
|
+
cam.layout({ viewportSize: { width: 800, height: 600 } });
|
|
105
|
+
cam.snapTo({ x: 50, y: 50, zoom: 2.0 });
|
|
106
|
+
cam.stop();
|
|
107
|
+
const zoomAfterStop = cam.zoom;
|
|
108
|
+
for (let i = 0; i < 100; i++)
|
|
109
|
+
globalThis.__flushRaf();
|
|
110
|
+
expect(cam.zoom).toBe(zoomAfterStop);
|
|
111
|
+
});
|
|
112
|
+
it('constrain returns fixed dimensions matching constructor size', () => {
|
|
113
|
+
const cam = new CameraController(1024, 768);
|
|
114
|
+
const c = cam.constrain({});
|
|
115
|
+
expect(c.width.min).toBe(1024);
|
|
116
|
+
expect(c.width.preferred).toBe(1024);
|
|
117
|
+
expect(c.height.min).toBe(768);
|
|
118
|
+
expect(c.height.preferred).toBe(768);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -41,19 +41,15 @@
|
|
|
41
41
|
}: Props = $props();
|
|
42
42
|
|
|
43
43
|
let canvas: HTMLCanvasElement | undefined = $state();
|
|
44
|
-
let sim = $state(
|
|
44
|
+
let sim: ForceSimulation | null = $state(null);
|
|
45
45
|
let renderCtx: CanvasRenderContext | null = null;
|
|
46
46
|
let rafId: number | null = null;
|
|
47
47
|
|
|
48
|
-
// ──
|
|
49
|
-
// We read+write `sim` inside this effect; without untrack(), reassigning
|
|
50
|
-
// `sim` would invalidate the effect's own dependency and loop forever
|
|
51
|
-
// (effect_update_depth_exceeded). Touch cells/config to register them as
|
|
52
|
-
// deps, then mutate sim outside tracking.
|
|
48
|
+
// ── Initialize + sync cells/config changes → simulation ──────────────────
|
|
53
49
|
$effect(() => {
|
|
54
50
|
cells; config;
|
|
55
51
|
untrack(() => {
|
|
56
|
-
sim
|
|
52
|
+
sim?.stop();
|
|
57
53
|
sim = new ForceSimulation(cells, config);
|
|
58
54
|
if (canvas) runLayout();
|
|
59
55
|
});
|
|
@@ -68,7 +64,7 @@
|
|
|
68
64
|
// ── Notify LodRenderer of position changes (backward compat) ────────────
|
|
69
65
|
|
|
70
66
|
$effect(() => {
|
|
71
|
-
|
|
67
|
+
if (sim && sim.positions.length > 0) {
|
|
72
68
|
onPositionsUpdate?.(sim.positions);
|
|
73
69
|
}
|
|
74
70
|
});
|
|
@@ -76,7 +72,7 @@
|
|
|
76
72
|
// ── Lifecycle phases ─────────────────────────────────────────────────────
|
|
77
73
|
|
|
78
74
|
function runLayout() {
|
|
79
|
-
if (!canvas || !active) return;
|
|
75
|
+
if (!canvas || !active || !sim) return;
|
|
80
76
|
|
|
81
77
|
// Phase 1: constrain (advisory only — canvas is given explicit width/height)
|
|
82
78
|
const constraintCtx: ConstraintContext = {
|
|
@@ -111,18 +107,27 @@
|
|
|
111
107
|
function startRaf() {
|
|
112
108
|
if (rafId !== null) return;
|
|
113
109
|
function frame() {
|
|
114
|
-
if (!renderCtx) return;
|
|
115
|
-
|
|
116
|
-
sim.render(renderCtx);
|
|
110
|
+
if (!renderCtx || !sim) return;
|
|
111
|
+
sim?.render(renderCtx);
|
|
117
112
|
renderCtx.flush();
|
|
118
113
|
rafId = requestAnimationFrame(frame);
|
|
119
114
|
}
|
|
115
|
+
const reduceMotion = typeof window !== 'undefined' &&
|
|
116
|
+
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
117
|
+
if (reduceMotion) {
|
|
118
|
+
if (renderCtx && sim) {
|
|
119
|
+
sim.render(renderCtx);
|
|
120
|
+
renderCtx.flush();
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
120
124
|
rafId = requestAnimationFrame(frame);
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
// ── Click → interact phase ───────────────────────────────────────────────
|
|
124
128
|
|
|
125
129
|
function handleClick(e: MouseEvent) {
|
|
130
|
+
if (!sim) return;
|
|
126
131
|
for (const pos of sim.positions) {
|
|
127
132
|
const r = pos.radius + 4;
|
|
128
133
|
const dx = e.offsetX - pos.x;
|
|
@@ -137,7 +142,7 @@
|
|
|
137
142
|
onMount(() => { if (active) runLayout(); });
|
|
138
143
|
onDestroy(() => {
|
|
139
144
|
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
140
|
-
sim
|
|
145
|
+
sim?.stop();
|
|
141
146
|
});
|
|
142
147
|
</script>
|
|
143
148
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ForceCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/spatial/ForceCanvas.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAOxF,UAAU,KAAK;IACb,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IACxD,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"ForceCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/spatial/ForceCanvas.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAOxF,UAAU,KAAK;IACb,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IACxD,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,KAAK,CAAC;AA8IvC,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ForceSimulation.svelte.d.ts","sourceRoot":"","sources":["../../src/spatial/ForceSimulation.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"ForceSimulation.svelte.d.ts","sourceRoot":"","sources":["../../src/spatial/ForceSimulation.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,iBAAiB,EAAa,MAAM,sBAAsB,CAAC;AAOlG,qBAAa,eAAgB,YAAW,SAAS;IAE/C,SAAS,EAAE,YAAY,EAAE,CAAc;IAEvC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAK;gBAEX,KAAK,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,EAAE,iBAAiB;IAO1D,SAAS,CAAC,IAAI,EAAE,iBAAiB,GAAG,eAAe;IAWnD,MAAM,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAQhC,MAAM,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;YAYlB,cAAc;IAkE5B,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,SAAS;IAiBjB,IAAI,IAAI,IAAI;CACb"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// S-02: No Math.random — initial positions seeded by node index.
|
|
6
6
|
// S-05: alphaDecay bounds simulation convergence; RAF cancelled on destroy.
|
|
7
|
+
import { toD3Node, toD3Link } from './d3-types';
|
|
7
8
|
import { weightToRadius, cosineSimilarity, semanticSpringLength } from './force-canvas-types';
|
|
8
9
|
const MIN_ZONE_PAD = 40;
|
|
9
10
|
export class ForceSimulation {
|
|
@@ -51,9 +52,9 @@ export class ForceSimulation {
|
|
|
51
52
|
this.positions = [];
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
|
-
const
|
|
55
|
+
const d3Mod = await import('d3-force');
|
|
55
56
|
if (gen !== this.generation)
|
|
56
|
-
return;
|
|
57
|
+
return;
|
|
57
58
|
const cfg = this.config;
|
|
58
59
|
const w = this.boundsWidth;
|
|
59
60
|
const h = this.boundsHeight;
|
|
@@ -66,33 +67,43 @@ export class ForceSimulation {
|
|
|
66
67
|
const velocityDecay = cfg?.velocityDecay ?? 0.4;
|
|
67
68
|
const cx = w / 2;
|
|
68
69
|
const cy = h / 2;
|
|
69
|
-
|
|
70
|
+
const nodes = this.cells.map((cell, i) => {
|
|
70
71
|
if (cell.x === undefined) {
|
|
71
|
-
|
|
72
|
-
cell.
|
|
73
|
-
cell.y = cy + (cell.index ?? 0) * 2;
|
|
72
|
+
cell.x = cx + (cell.index ?? i) * 3;
|
|
73
|
+
cell.y = cy + (cell.index ?? i) * 2;
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
return toD3Node(cell);
|
|
76
|
+
});
|
|
77
|
+
const links = this.buildLinks().map(toD3Link);
|
|
78
|
+
const sim = d3Mod.forceSimulation(nodes)
|
|
79
79
|
.alphaDecay(0.02)
|
|
80
80
|
.velocityDecay(velocityDecay)
|
|
81
|
-
.force('charge',
|
|
82
|
-
.force('collision',
|
|
83
|
-
|
|
84
|
-
.
|
|
81
|
+
.force('charge', d3Mod.forceManyBody().strength(chargeStrength))
|
|
82
|
+
.force('collision', d3Mod.forceCollide().radius((d) => {
|
|
83
|
+
const cell = this.cells.find((c) => c.id === d.id);
|
|
84
|
+
return cell ? weightToRadius(cell.weight) + collisionPadding : collisionPadding + 8;
|
|
85
|
+
}))
|
|
86
|
+
.force('link', d3Mod.forceLink(links).id((d) => d.id)
|
|
87
|
+
.distance((l) => semanticSpringLength(1 - (l.distance ?? 0.5), minSpring, maxSpring))
|
|
85
88
|
.strength(0.5))
|
|
86
|
-
.force('center',
|
|
89
|
+
.force('center', d3Mod.forceCenter(cx, cy).strength(0.03))
|
|
87
90
|
.force('wall', this.wallForce(w, h, wallMargin, wallStrength))
|
|
88
91
|
.on('tick', () => {
|
|
89
|
-
this.positions =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
this.positions = nodes.map((n) => {
|
|
93
|
+
const cell = this.cells.find((c) => c.id === n.id);
|
|
94
|
+
return {
|
|
95
|
+
x: n.x ?? cx,
|
|
96
|
+
y: n.y ?? cy,
|
|
97
|
+
radius: weightToRadius(cell.weight),
|
|
98
|
+
cell,
|
|
99
|
+
};
|
|
100
|
+
});
|
|
95
101
|
});
|
|
102
|
+
if (gen !== this.generation) {
|
|
103
|
+
sim.stop();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.simulation = sim;
|
|
96
107
|
}
|
|
97
108
|
buildLinks() {
|
|
98
109
|
const links = [];
|
|
@@ -209,12 +209,25 @@
|
|
|
209
209
|
{@const state = getLodState(pos.cell.id)}
|
|
210
210
|
{@const isTransitioning = state?.transitionProgress !== null && state?.transitionProgress !== undefined}
|
|
211
211
|
|
|
212
|
+
{@const cellAttrs = onCellClick ? {
|
|
213
|
+
role: 'button',
|
|
214
|
+
tabindex: 0,
|
|
215
|
+
'aria-label': 'Open cell at current level of detail',
|
|
216
|
+
onclick: () => onCellClick?.(pos.cell.id, currentLod),
|
|
217
|
+
onkeydown: (e: KeyboardEvent) => {
|
|
218
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
219
|
+
e.preventDefault();
|
|
220
|
+
onCellClick?.(pos.cell.id, currentLod);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} : {}}
|
|
224
|
+
|
|
212
225
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
213
226
|
<div
|
|
214
227
|
class="lod-cell"
|
|
215
228
|
style:left="{pos.x - pos.radius}px"
|
|
216
229
|
style:top="{pos.y - pos.radius}px"
|
|
217
|
-
|
|
230
|
+
{...cellAttrs}
|
|
218
231
|
data-lod={currentLod}
|
|
219
232
|
data-cell-id={pos.cell.id}
|
|
220
233
|
>
|