@stephen-netu/design-system 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/README.md +66 -0
  2. package/package.json +130 -0
  3. package/src/b0-app-shell/clarification-overlay/ClarificationOverlay.svelte +221 -0
  4. package/src/b0-app-shell/clarification-overlay/index.ts +1 -0
  5. package/src/b0-app-shell/command-bar/CommandBar.svelte +565 -0
  6. package/src/b0-app-shell/index.ts +14 -0
  7. package/src/base.css +152 -0
  8. package/src/d0-data-viz/BarChart.svelte +236 -0
  9. package/src/d0-data-viz/CheckpointBar.svelte +574 -0
  10. package/src/d0-data-viz/CheckpointPill.svelte +297 -0
  11. package/src/d0-data-viz/MetricCard.svelte +159 -0
  12. package/src/d0-data-viz/SparkLine.svelte +66 -0
  13. package/src/d0-data-viz/StorageMap.svelte +182 -0
  14. package/src/d0-data-viz/Timeline.svelte +188 -0
  15. package/src/d0-data-viz/checkpoint-types.ts +162 -0
  16. package/src/d0-data-viz/d0-tokens.css +32 -0
  17. package/src/d0-data-viz/index.ts +58 -0
  18. package/src/d0-data-viz/storage-map-types.ts +142 -0
  19. package/src/d0-data-viz/theme-integration.ts +134 -0
  20. package/src/d0-data-viz/types.ts +53 -0
  21. package/src/demos/BspSizingInfographic.svelte +386 -0
  22. package/src/demos/BspZoneDemo.svelte +255 -0
  23. package/src/demos/ControlRoomDemo.svelte +570 -0
  24. package/src/demos/NodepadBlocksShowcase.svelte +189 -0
  25. package/src/demos/index.ts +7 -0
  26. package/src/editor/CodeEditor.svelte +188 -0
  27. package/src/editor/FindReplaceDialog.svelte +516 -0
  28. package/src/editor/MarkdownEditor.svelte +660 -0
  29. package/src/editor/index.ts +2 -0
  30. package/src/entity-graph-view/WorldView.svelte +171 -0
  31. package/src/entity-graph-view/index.ts +6 -0
  32. package/src/entity-graph-view/types.ts +364 -0
  33. package/src/epistemic/index.ts +2 -0
  34. package/src/epistemic/types.ts +134 -0
  35. package/src/epistemic/utils.ts +32 -0
  36. package/src/f0-forms/checkbox/Checkbox.svelte +158 -0
  37. package/src/f0-forms/field/FormField.svelte +91 -0
  38. package/src/f0-forms/index.ts +11 -0
  39. package/src/f0-forms/radio/RadioGroup.svelte +192 -0
  40. package/src/f0-forms/search/SearchBar.svelte +99 -0
  41. package/src/f0-forms/select/Select.svelte +198 -0
  42. package/src/f0-forms/textarea/TextArea.svelte +170 -0
  43. package/src/f0-forms/toggle/FormToggle.svelte +147 -0
  44. package/src/f0-forms/validation/validate.ts +47 -0
  45. package/src/fabric/ai/ChatPanel.svelte +648 -0
  46. package/src/fabric/ai/index.ts +11 -0
  47. package/src/fabric/data/index.ts +21 -0
  48. package/src/fabric/feedback/StatusBar.svelte +274 -0
  49. package/src/fabric/feedback/ToastContainer.svelte +122 -0
  50. package/src/fabric/feedback/index.ts +14 -0
  51. package/src/fabric/index.ts +37 -0
  52. package/src/fabric/input/CommandBar.svelte +79 -0
  53. package/src/fabric/input/KeyboardShortcuts.svelte +388 -0
  54. package/src/fabric/input/index.ts +12 -0
  55. package/src/fabric/layout/GlassBox.svelte +193 -0
  56. package/src/fabric/layout/KanbanBoard.svelte +158 -0
  57. package/src/fabric/layout/KanbanCard.svelte +226 -0
  58. package/src/fabric/layout/KanbanColumn.svelte +278 -0
  59. package/src/fabric/layout/index.ts +17 -0
  60. package/src/fabric/navigation/ActivityRail.svelte +271 -0
  61. package/src/fabric/navigation/index.ts +11 -0
  62. package/src/g0-geo/SovereignMap.svelte +149 -0
  63. package/src/g0-geo/index.ts +2 -0
  64. package/src/index.ts +68 -0
  65. package/src/l0-layout/block-writer/BlockConnectionLines.svelte +98 -0
  66. package/src/l0-layout/block-writer/BlockContent.svelte +213 -0
  67. package/src/l0-layout/block-writer/BlockHeader.svelte +230 -0
  68. package/src/l0-layout/block-writer/BlockItem.svelte +177 -0
  69. package/src/l0-layout/block-writer/BlockWriter.svelte +219 -0
  70. package/src/l0-layout/block-writer/block-writer-types.ts +63 -0
  71. package/src/l0-layout/block-writer/index.ts +24 -0
  72. package/src/l0-layout/block-writer/markdown.ts +110 -0
  73. package/src/l0-layout/chat-view/ChatViewLayout.svelte +61 -0
  74. package/src/l0-layout/epistemic/AllayDetailPanel.svelte +113 -0
  75. package/src/l0-layout/epistemic/AssayDetailPanel.svelte +164 -0
  76. package/src/l0-layout/epistemic/AvenueTrace.svelte +253 -0
  77. package/src/l0-layout/epistemic/KnowledgeDetailPanel.svelte +204 -0
  78. package/src/l0-layout/epistemic/ParadigmLens.svelte +170 -0
  79. package/src/l0-layout/epistemic/ProvenanceChain.svelte +164 -0
  80. package/src/l0-layout/epistemic/TrustView.svelte +195 -0
  81. package/src/l0-layout/epistemic/index.ts +7 -0
  82. package/src/l0-layout/grove/AccordCard.svelte +156 -0
  83. package/src/l0-layout/grove/GroveCard.svelte +197 -0
  84. package/src/l0-layout/grove/MemberBadge.svelte +96 -0
  85. package/src/l0-layout/grove/MemberList.svelte +59 -0
  86. package/src/l0-layout/grove/grove-types.ts +29 -0
  87. package/src/l0-layout/grove/index.ts +14 -0
  88. package/src/l0-layout/index.ts +45 -0
  89. package/src/l0-layout/kanban/KanbanBoard.svelte +71 -0
  90. package/src/l0-layout/kanban/KanbanCard.svelte +276 -0
  91. package/src/l0-layout/kanban/KanbanColumn.svelte +198 -0
  92. package/src/l0-layout/kanban/KanbanViewLayout.svelte +61 -0
  93. package/src/l0-layout/workspace/WorkspaceLayout.svelte +93 -0
  94. package/src/n0-node-graph/GraphEditor.svelte +342 -0
  95. package/src/n0-node-graph/GraphHistory.svelte +122 -0
  96. package/src/n0-node-graph/Node.svelte +269 -0
  97. package/src/n0-node-graph/NodeCanvas.svelte +187 -0
  98. package/src/n0-node-graph/NodeEdge.svelte +152 -0
  99. package/src/n0-node-graph/NodePalette.svelte +318 -0
  100. package/src/n0-node-graph/NodePort.svelte +128 -0
  101. package/src/n0-node-graph/index.ts +33 -0
  102. package/src/n0-node-graph/n0-tokens.css +29 -0
  103. package/src/n0-node-graph/node-port-types.ts +19 -0
  104. package/src/n0-node-graph/types.ts +96 -0
  105. package/src/n0-node-graph/validate.test.ts +722 -0
  106. package/src/n0-node-graph/validate.ts +371 -0
  107. package/src/n1-flow-canvas/FlowCanvas.svelte +202 -0
  108. package/src/n1-flow-canvas/graph-state.svelte.ts +95 -0
  109. package/src/n1-flow-canvas/index.ts +21 -0
  110. package/src/n1-flow-canvas/layout.ts +118 -0
  111. package/src/n1-flow-canvas/layout.worker.ts +128 -0
  112. package/src/n1-flow-canvas/types.ts +102 -0
  113. package/src/n1-flow-canvas/utils.ts +146 -0
  114. package/src/n2-manifold-disk/ManifoldDisk.svelte +314 -0
  115. package/src/n2-manifold-disk/index.ts +6 -0
  116. package/src/n2-manifold-disk/poincare.ts +184 -0
  117. package/src/n3-force-graph/ForceGraph.svelte +180 -0
  118. package/src/n3-force-graph/GraphTooltip.svelte +99 -0
  119. package/src/n3-force-graph/animated-edge.ts +107 -0
  120. package/src/n3-force-graph/health-node.ts +128 -0
  121. package/src/n3-force-graph/index.ts +31 -0
  122. package/src/n3-force-graph/types.ts +94 -0
  123. package/src/n4-adaptive-manifold/AdaptiveManifold.svelte +1165 -0
  124. package/src/n4-adaptive-manifold/DetailCard.svelte +212 -0
  125. package/src/n4-adaptive-manifold/SuggestionIndicator.svelte +91 -0
  126. package/src/n4-adaptive-manifold/classifier.ts +139 -0
  127. package/src/n4-adaptive-manifold/index.ts +45 -0
  128. package/src/n4-adaptive-manifold/n4-tokens.css +155 -0
  129. package/src/n4-adaptive-manifold/orchestrator.ts +301 -0
  130. package/src/n4-adaptive-manifold/phase-theme.ts +92 -0
  131. package/src/n4-adaptive-manifold/pinnacle-events.ts +135 -0
  132. package/src/n4-adaptive-manifold/providers/gravitational-provider.ts +191 -0
  133. package/src/n4-adaptive-manifold/providers/manifold-provider.ts +276 -0
  134. package/src/n4-adaptive-manifold/transition.ts +90 -0
  135. package/src/n4-adaptive-manifold/types.ts +313 -0
  136. package/src/p0-primitives/index.ts +6 -0
  137. package/src/p0-primitives/tokens/control-room.css +309 -0
  138. package/src/p0-primitives/tokens/index.ts +6 -0
  139. package/src/p0-primitives/tokens/tokens.css +140 -0
  140. package/src/p0-primitives/tokens/tokens.json +132 -0
  141. package/src/p0-primitives/types/index.ts +123 -0
  142. package/src/p0-primitives/types/plugin-types.ts +74 -0
  143. package/src/p0-primitives/utils/index.ts +39 -0
  144. package/src/platform/hud-protocol.ts +20 -0
  145. package/src/platform/index.ts +3 -0
  146. package/src/s0-state/create-store.ts +84 -0
  147. package/src/s0-state/derived.ts +41 -0
  148. package/src/s0-state/index.ts +6 -0
  149. package/src/showcase/Showcase.svelte +401 -0
  150. package/src/spatial/BspTilingCanvas.svelte +232 -0
  151. package/src/spatial/ForceCanvas.svelte +289 -0
  152. package/src/spatial/LodRenderer.svelte +334 -0
  153. package/src/spatial/SnapZoomCamera.svelte +185 -0
  154. package/src/spatial/ZoneTiler.svelte +311 -0
  155. package/src/spatial/camera-types.ts +45 -0
  156. package/src/spatial/content-proportional-bsp.ts +253 -0
  157. package/src/spatial/force-canvas-types.ts +102 -0
  158. package/src/spatial/index.ts +15 -0
  159. package/src/spatial/lod-types.ts +70 -0
  160. package/src/spatial/zone-tiler-types.ts +177 -0
  161. package/src/stores/vaultStore.svelte +14 -0
  162. package/src/t0-transport/connection.ts +34 -0
  163. package/src/t0-transport/error.ts +37 -0
  164. package/src/t0-transport/events.ts +50 -0
  165. package/src/t0-transport/index.ts +6 -0
  166. package/src/t0-transport/invoke.ts +30 -0
  167. package/src/tauri/index.ts +13 -0
  168. package/src/u0-primitives/allay-gauge/AllayGauge.svelte +90 -0
  169. package/src/u0-primitives/allay-gauge/index.ts +1 -0
  170. package/src/u0-primitives/animated-icon/AnimatedIcon.svelte +273 -0
  171. package/src/u0-primitives/animated-icon/README.md +53 -0
  172. package/src/u0-primitives/animated-icon/index.ts +31 -0
  173. package/src/u0-primitives/assay-badge/AssayBadge.svelte +121 -0
  174. package/src/u0-primitives/assay-badge/AssaySignalBar.svelte +83 -0
  175. package/src/u0-primitives/assay-badge/index.ts +2 -0
  176. package/src/u0-primitives/avatar/Avatar.svelte +177 -0
  177. package/src/u0-primitives/badge/Badge.svelte +149 -0
  178. package/src/u0-primitives/button/Button.svelte +304 -0
  179. package/src/u0-primitives/card/Card.svelte +182 -0
  180. package/src/u0-primitives/dropdown/Dropdown.svelte +305 -0
  181. package/src/u0-primitives/expert-badge/ExpertBadge.svelte +89 -0
  182. package/src/u0-primitives/expert-badge/index.ts +1 -0
  183. package/src/u0-primitives/index.ts +35 -0
  184. package/src/u0-primitives/input/Input.svelte +237 -0
  185. package/src/u0-primitives/modal/Modal.svelte +331 -0
  186. package/src/u0-primitives/modal/index.ts +1 -0
  187. package/src/u0-primitives/spinner/Spinner.svelte +129 -0
  188. package/src/u0-primitives/tabs/Tabs.svelte +225 -0
  189. package/src/u0-primitives/tabs/tabs-types.ts +11 -0
  190. package/src/u0-primitives/toggle/Toggle.svelte +161 -0
  191. package/src/u0-primitives/tooltip/Tooltip.svelte +234 -0
  192. package/src/v0-diagrams/DiagramPanel.svelte +68 -0
  193. package/src/v0-diagrams/MarkmapDiagram.svelte +56 -0
  194. package/src/v0-diagrams/MermaidDiagram.svelte +53 -0
  195. package/src/v0-diagrams/index.ts +4 -0
  196. package/src/v0-diagrams/types.ts +28 -0
  197. package/src/v0-diagrams/v0-tokens.css +6 -0
  198. package/src/vite-env.d.ts +7 -0
  199. package/src/x0-enchanted-blocks/ENCHANTED_BLOCKS_SPEC.md +457 -0
  200. package/src/x0-enchanted-blocks/components/EnchantedBlock.svelte +485 -0
  201. package/src/x0-enchanted-blocks/components/NodepadBlock.svelte +262 -0
  202. package/src/x0-enchanted-blocks/core/block-layout.ts +211 -0
  203. package/src/x0-enchanted-blocks/core/block-types.ts +227 -0
  204. package/src/x0-enchanted-blocks/index.ts +46 -0
@@ -0,0 +1,574 @@
1
+ <script lang="ts">
2
+ // CheckpointBar.svelte — Bottom-mounted timeline navigation
3
+ // ADR: ui-timeline-checkpoint-bar-001
4
+ // Svelte 5 runes pattern — no stores, pure props/events
5
+
6
+ import type {
7
+ Checkpoint,
8
+ CheckpointComponentType,
9
+ CheckpointStatus,
10
+ } from './checkpoint-types';
11
+ import {
12
+ CHECKPOINT_COLORS,
13
+ CHECKPOINT_STATUS_SYMBOLS,
14
+ CHECKPOINT_BAR_HEIGHT,
15
+ CHECKPOINT_NODE_SIZE,
16
+ CHECKPOINT_NODE_ACTIVE_SIZE,
17
+ getCheckpointColor,
18
+ } from './checkpoint-types';
19
+
20
+ interface Props {
21
+ checkpoints: Checkpoint[];
22
+ currentSeqno: number;
23
+ previewSeqno?: number | null;
24
+ onCheckpointClick?: (checkpoint: Checkpoint) => void;
25
+ onCheckpointDoubleClick?: (checkpoint: Checkpoint) => void;
26
+ onNowHandleDrag?: (seqno: number) => void;
27
+ onExitPreview?: () => void;
28
+ 'aria-label'?: string;
29
+ }
30
+
31
+ let {
32
+ checkpoints,
33
+ currentSeqno,
34
+ previewSeqno = null,
35
+ onCheckpointClick,
36
+ onCheckpointDoubleClick,
37
+ onNowHandleDrag,
38
+ onExitPreview,
39
+ 'aria-label': ariaLabel = 'System checkpoint timeline. Use arrow keys to navigate, Enter to preview, Escape to exit preview.',
40
+ }: Props = $props();
41
+
42
+ // Derived state
43
+ let focusedIndex = $state<number>(-1);
44
+
45
+ let maxSeqno = $derived(
46
+ checkpoints.length > 0
47
+ ? Math.max(...checkpoints.map((c) => c.seqno))
48
+ : 0
49
+ );
50
+
51
+ let minSeqno = $derived(
52
+ checkpoints.length > 0
53
+ ? Math.min(...checkpoints.map((c) => c.seqno))
54
+ : 0
55
+ );
56
+
57
+ let displaySeqno = $derived(previewSeqno ?? currentSeqno);
58
+
59
+ // Track scrolling for overflow indicators
60
+ let trackRef: HTMLDivElement | null = $state(null);
61
+ let canScrollLeft = $state(false);
62
+ let canScrollRight = $state(false);
63
+
64
+ function updateScrollIndicators() {
65
+ if (!trackRef) return;
66
+ canScrollLeft = trackRef.scrollLeft > 0;
67
+ canScrollRight =
68
+ trackRef.scrollLeft < trackRef.scrollWidth - trackRef.clientWidth;
69
+ }
70
+
71
+ function scrollToCheckpoint(seqno: number) {
72
+ if (!trackRef) return;
73
+ const index = checkpoints.findIndex((c) => c.seqno === seqno);
74
+ if (index === -1) return;
75
+
76
+ // Calculate approximate position
77
+ const nodeWidth = 40; // Approximate width per checkpoint node
78
+ const scrollPos = index * nodeWidth - trackRef.clientWidth / 2;
79
+ trackRef.scrollTo({ left: scrollPos, behavior: 'smooth' });
80
+ }
81
+
82
+ function handleCheckpointClick(checkpoint: Checkpoint, index: number) {
83
+ focusedIndex = index;
84
+ onCheckpointClick?.(checkpoint);
85
+ }
86
+
87
+ function handleCheckpointDoubleClick(checkpoint: Checkpoint) {
88
+ onCheckpointDoubleClick?.(checkpoint);
89
+ }
90
+
91
+ function handleKeyDown(event: KeyboardEvent) {
92
+ if (checkpoints.length === 0) return;
93
+
94
+ switch (event.key) {
95
+ case 'ArrowLeft':
96
+ event.preventDefault();
97
+ focusedIndex = Math.max(0, focusedIndex - 1);
98
+ scrollToCheckpoint(checkpoints[focusedIndex].seqno);
99
+ break;
100
+ case 'ArrowRight':
101
+ event.preventDefault();
102
+ focusedIndex = Math.min(checkpoints.length - 1, focusedIndex + 1);
103
+ scrollToCheckpoint(checkpoints[focusedIndex].seqno);
104
+ break;
105
+ case 'Enter':
106
+ event.preventDefault();
107
+ if (focusedIndex >= 0 && focusedIndex < checkpoints.length) {
108
+ if (event.shiftKey) {
109
+ onCheckpointDoubleClick?.(checkpoints[focusedIndex]);
110
+ } else {
111
+ onCheckpointClick?.(checkpoints[focusedIndex]);
112
+ }
113
+ }
114
+ break;
115
+ case 'Escape':
116
+ event.preventDefault();
117
+ if (previewSeqno !== null) {
118
+ onExitPreview?.();
119
+ }
120
+ break;
121
+ case 'Home':
122
+ event.preventDefault();
123
+ focusedIndex = 0;
124
+ scrollToCheckpoint(checkpoints[0].seqno);
125
+ break;
126
+ case 'End':
127
+ event.preventDefault();
128
+ focusedIndex = checkpoints.length - 1;
129
+ scrollToCheckpoint(checkpoints[checkpoints.length - 1].seqno);
130
+ break;
131
+ }
132
+ }
133
+
134
+ // Detect dark mode for fallback colors
135
+ let isDarkMode = $state(true);
136
+
137
+ $effect(() => {
138
+ if (typeof window !== 'undefined') {
139
+ isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
140
+ }
141
+ });
142
+
143
+ function getNodeColor(type: CheckpointComponentType): string {
144
+ // Return CSS var reference; browser will use themeStore-set value or default
145
+ return CHECKPOINT_COLORS[type] ?? CHECKPOINT_COLORS['system-snapshot'];
146
+ }
147
+
148
+ function getNodeColorFallback(type: CheckpointComponentType): string {
149
+ // SSR-safe fallback when CSS vars not yet applied
150
+ return getCheckpointColor(type, isDarkMode);
151
+ }
152
+
153
+ function getNodeSymbol(status: CheckpointStatus): string {
154
+ return CHECKPOINT_STATUS_SYMBOLS[status] ?? CHECKPOINT_STATUS_SYMBOLS.committed;
155
+ }
156
+
157
+ function isCheckpointActive(checkpoint: Checkpoint): boolean {
158
+ return checkpoint.seqno === currentSeqno;
159
+ }
160
+
161
+ function isCheckpointPreview(checkpoint: Checkpoint): boolean {
162
+ return previewSeqno !== null && checkpoint.seqno === previewSeqno;
163
+ }
164
+
165
+ // Scroll button handlers
166
+ function scrollLeft() {
167
+ if (!trackRef) return;
168
+ trackRef.scrollBy({ left: -200, behavior: 'smooth' });
169
+ }
170
+
171
+ function scrollRight() {
172
+ if (!trackRef) return;
173
+ trackRef.scrollBy({ left: 200, behavior: 'smooth' });
174
+ }
175
+
176
+ // Initialize focused index to current checkpoint
177
+ $effect(() => {
178
+ if (focusedIndex === -1 && checkpoints.length > 0) {
179
+ const currentIndex = checkpoints.findIndex((c) => c.seqno === currentSeqno);
180
+ focusedIndex = currentIndex >= 0 ? currentIndex : 0;
181
+ }
182
+ });
183
+ </script>
184
+
185
+ <div
186
+ class="checkpoint-bar"
187
+ role="slider"
188
+ aria-roledescription="checkpoint timeline"
189
+ aria-label={ariaLabel}
190
+ aria-valuemin={minSeqno}
191
+ aria-valuemax={maxSeqno}
192
+ aria-valuenow={displaySeqno}
193
+ aria-orientation="horizontal"
194
+ tabindex="0"
195
+ onkeydown={handleKeyDown}
196
+ style="--checkpoint-bar-height: {CHECKPOINT_BAR_HEIGHT}px;"
197
+ >
198
+ <!-- Scroll Left Indicator -->
199
+ <button
200
+ class="scroll-indicator scroll-left"
201
+ class:visible={canScrollLeft}
202
+ onclick={scrollLeft}
203
+ aria-label="View older checkpoints"
204
+ >
205
+
206
+ </button>
207
+
208
+ <!-- Timeline Track -->
209
+ <div
210
+ id="checkpoint-track"
211
+ class="timeline-track"
212
+ bind:this={trackRef}
213
+ onscroll={updateScrollIndicators}
214
+ role="list"
215
+ aria-label="Checkpoints"
216
+ >
217
+ <!-- Connecting Line -->
218
+ <div class="timeline-line"></div>
219
+
220
+ <!-- Checkpoint Nodes -->
221
+ {#each checkpoints as checkpoint, i (checkpoint.seqno)}
222
+ {@const isActive = isCheckpointActive(checkpoint)}
223
+ {@const isPreview = isCheckpointPreview(checkpoint)}
224
+ {@const isFocused = focusedIndex === i}
225
+ {@const colorVar = getNodeColor(checkpoint.componentType)}
226
+ {@const colorFallback = getNodeColorFallback(checkpoint.componentType)}
227
+
228
+ <button
229
+ class="checkpoint-node"
230
+ class:active={isActive}
231
+ class:preview={isPreview}
232
+ class:focused={isFocused}
233
+ style="--node-color: {colorVar}; --node-fallback: {colorFallback}; --node-size: {isActive || isPreview
234
+ ? CHECKPOINT_NODE_ACTIVE_SIZE
235
+ : CHECKPOINT_NODE_SIZE}px;"
236
+ onclick={() => handleCheckpointClick(checkpoint, i)}
237
+ ondblclick={() => handleCheckpointDoubleClick(checkpoint)}
238
+ aria-describedby="tooltip-{checkpoint.seqno}"
239
+ aria-label="Checkpoint {checkpoint.seqno}: {checkpoint.component} {checkpoint.version
240
+ ? `v${checkpoint.version.to}`
241
+ : ''}, {checkpoint.description}"
242
+ aria-current={isActive ? 'true' : undefined}
243
+ tabindex={isFocused ? 0 : -1}
244
+ >
245
+ <span class="node-symbol" style="color: var(--node-color, var(--node-fallback))">
246
+ {getNodeSymbol(checkpoint.status)}
247
+ </span>
248
+
249
+ <!-- Tooltip -->
250
+ <div id="tooltip-{checkpoint.seqno}" class="checkpoint-tooltip" role="tooltip">
251
+ <div class="tooltip-header">
252
+ Checkpoint {checkpoint.seqno}
253
+ {#if checkpoint.version}
254
+ <span class="version">v{checkpoint.version.to}</span>
255
+ {/if}
256
+ </div>
257
+ <div class="tooltip-component">{checkpoint.component}</div>
258
+ <div class="tooltip-desc">{checkpoint.description}</div>
259
+ {#if checkpoint.author}
260
+ <div class="tooltip-meta">by {checkpoint.author}</div>
261
+ {/if}
262
+ </div>
263
+ </button>
264
+ {/each}
265
+
266
+ <!-- Now Handle (drag-to-scrub: future enhancement) -->
267
+ <div class="now-handle" aria-label="Current position">
268
+ <span class="now-label">▶</span>
269
+ </div>
270
+ </div>
271
+
272
+ <!-- Scroll Right Indicator -->
273
+ <button
274
+ class="scroll-indicator scroll-right"
275
+ class:visible={canScrollRight}
276
+ onclick={scrollRight}
277
+ aria-label="View newer checkpoints"
278
+ >
279
+
280
+ </button>
281
+
282
+ <!-- Meta Display -->
283
+ <div class="timeline-meta">
284
+ {#if previewSeqno}
285
+ <span class="preview-badge">Previewing checkpoint {previewSeqno}</span>
286
+ {:else}
287
+ <span class="current-badge">Checkpoint {currentSeqno}</span>
288
+ {/if}
289
+ </div>
290
+ </div>
291
+
292
+ <style>
293
+ .checkpoint-bar {
294
+ display: flex;
295
+ align-items: center;
296
+ height: var(--checkpoint-bar-height, 48px);
297
+ background: rgba(26, 26, 26, 0.95);
298
+ border-top: 1px solid rgba(184, 115, 51, 0.2);
299
+ padding: 0 0.5rem;
300
+ gap: 0.5rem;
301
+ user-select: none;
302
+ outline: none;
303
+ }
304
+
305
+ .checkpoint-bar:focus-visible {
306
+ outline: 2px solid var(--color-accent, #b87333);
307
+ outline-offset: -2px;
308
+ }
309
+
310
+ .scroll-indicator {
311
+ display: flex;
312
+ align-items: center;
313
+ justify-content: center;
314
+ width: 24px;
315
+ height: 24px;
316
+ background: transparent;
317
+ border: none;
318
+ color: rgba(160, 152, 128, 0.5);
319
+ font-size: 10px;
320
+ cursor: pointer;
321
+ opacity: 0;
322
+ transition: opacity 150ms ease, color 150ms ease;
323
+ flex-shrink: 0;
324
+ }
325
+
326
+ .scroll-indicator.visible {
327
+ opacity: 1;
328
+ }
329
+
330
+ .scroll-indicator:hover {
331
+ color: var(--color-accent, #b87333);
332
+ }
333
+
334
+ .timeline-track {
335
+ display: flex;
336
+ align-items: center;
337
+ flex: 1;
338
+ gap: 0.5rem;
339
+ overflow-x: auto;
340
+ overflow-y: hidden;
341
+ scroll-behavior: smooth;
342
+ scrollbar-width: none; /* Firefox */
343
+ -ms-overflow-style: none; /* IE/Edge */
344
+ position: relative;
345
+ padding: 0 0.5rem;
346
+ }
347
+
348
+ .timeline-track::-webkit-scrollbar {
349
+ display: none;
350
+ }
351
+
352
+ .timeline-line {
353
+ position: absolute;
354
+ left: 0;
355
+ right: 0;
356
+ top: 50%;
357
+ height: 2px;
358
+ background: rgba(184, 115, 51, 0.3);
359
+ transform: translateY(-50%);
360
+ pointer-events: none;
361
+ z-index: 0;
362
+ }
363
+
364
+ .checkpoint-node {
365
+ position: relative;
366
+ display: flex;
367
+ align-items: center;
368
+ justify-content: center;
369
+ width: var(--node-size, 12px);
370
+ height: var(--node-size, 12px);
371
+ background: transparent;
372
+ border: none;
373
+ cursor: pointer;
374
+ z-index: 1;
375
+ flex-shrink: 0;
376
+ padding: 4px;
377
+ margin: -4px;
378
+ border-radius: 50%;
379
+ transition: transform 150ms ease;
380
+ }
381
+
382
+ .checkpoint-node:hover,
383
+ .checkpoint-node.focused {
384
+ transform: scale(1.3);
385
+ }
386
+
387
+ .checkpoint-node.active {
388
+ transform: scale(1.5);
389
+ }
390
+
391
+ .checkpoint-node.active::after {
392
+ content: '';
393
+ position: absolute;
394
+ inset: -4px;
395
+ border: 2px solid var(--node-color, var(--node-fallback, #b87333));
396
+ border-radius: 50%;
397
+ opacity: 0.5;
398
+ }
399
+
400
+ .checkpoint-node.preview {
401
+ transform: scale(1.5);
402
+ box-shadow: 0 0 8px var(--node-color, var(--node-fallback, #b87333));
403
+ }
404
+
405
+ .node-symbol {
406
+ font-size: var(--node-size, 12px);
407
+ line-height: 1;
408
+ pointer-events: none;
409
+ }
410
+
411
+ /* Tooltip */
412
+ .checkpoint-tooltip {
413
+ position: absolute;
414
+ bottom: calc(100% + 12px);
415
+ left: 50%;
416
+ transform: translateX(-50%) scale(0.95);
417
+ background: rgba(34, 34, 34, 0.98);
418
+ border: 1px solid rgba(184, 115, 51, 0.3);
419
+ border-radius: 6px;
420
+ padding: 0.75rem 1rem;
421
+ min-width: 200px;
422
+ max-width: 280px;
423
+ opacity: 0;
424
+ visibility: hidden;
425
+ transition: opacity 150ms ease, transform 150ms ease, visibility 150ms ease;
426
+ pointer-events: none;
427
+ z-index: 100;
428
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
429
+ }
430
+
431
+ .checkpoint-tooltip::after {
432
+ content: '';
433
+ position: absolute;
434
+ top: 100%;
435
+ left: 50%;
436
+ transform: translateX(-50%);
437
+ border: 6px solid transparent;
438
+ border-top-color: rgba(184, 115, 51, 0.3);
439
+ }
440
+
441
+ .checkpoint-node:hover .checkpoint-tooltip,
442
+ .checkpoint-node.focused .checkpoint-tooltip {
443
+ opacity: 1;
444
+ visibility: visible;
445
+ transform: translateX(-50%) scale(1);
446
+ }
447
+
448
+ .tooltip-header {
449
+ font-size: 0.75rem;
450
+ font-weight: 600;
451
+ color: var(--color-text-primary, #e8e0d0);
452
+ margin-bottom: 0.25rem;
453
+ display: flex;
454
+ align-items: center;
455
+ gap: 0.5rem;
456
+ }
457
+
458
+ .version {
459
+ font-size: 0.65rem;
460
+ color: var(--color-accent, #b87333);
461
+ font-weight: 500;
462
+ }
463
+
464
+ .tooltip-component {
465
+ font-size: 0.7rem;
466
+ color: var(--color-text-secondary, #a09880);
467
+ font-family: var(--font-mono, monospace);
468
+ margin-bottom: 0.25rem;
469
+ }
470
+
471
+ .tooltip-desc {
472
+ font-size: 0.75rem;
473
+ color: var(--color-text-primary, #e8e0d0);
474
+ line-height: 1.4;
475
+ }
476
+
477
+ .tooltip-meta {
478
+ font-size: 0.65rem;
479
+ color: var(--color-text-muted, #808080);
480
+ margin-top: 0.5rem;
481
+ padding-top: 0.5rem;
482
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
483
+ }
484
+
485
+ /* Now Handle */
486
+ .now-handle {
487
+ display: flex;
488
+ align-items: center;
489
+ justify-content: center;
490
+ width: 20px;
491
+ height: 20px;
492
+ margin-left: 0.5rem;
493
+ flex-shrink: 0;
494
+ cursor: grab;
495
+ color: var(--color-accent, #b87333);
496
+ }
497
+
498
+ .now-label {
499
+ font-size: 12px;
500
+ }
501
+
502
+ /* Meta Display */
503
+ .timeline-meta {
504
+ display: flex;
505
+ align-items: center;
506
+ padding-left: 0.75rem;
507
+ border-left: 1px solid rgba(184, 115, 51, 0.2);
508
+ margin-left: 0.25rem;
509
+ flex-shrink: 0;
510
+ }
511
+
512
+ .current-badge,
513
+ .preview-badge {
514
+ font-size: 0.7rem;
515
+ font-weight: 500;
516
+ letter-spacing: 0.02em;
517
+ white-space: nowrap;
518
+ }
519
+
520
+ .current-badge {
521
+ color: var(--color-text-secondary, #a09880);
522
+ }
523
+
524
+ .preview-badge {
525
+ color: var(--color-accent, #b87333);
526
+ animation: pulse 2s ease-in-out infinite;
527
+ }
528
+
529
+ @keyframes pulse {
530
+ 0%, 100% {
531
+ opacity: 1;
532
+ }
533
+ 50% {
534
+ opacity: 0.6;
535
+ }
536
+ }
537
+
538
+ /* High Contrast Mode */
539
+ @media (prefers-contrast: high) {
540
+ .checkpoint-bar {
541
+ background: #000;
542
+ border-top-color: #fff;
543
+ }
544
+
545
+ .node-symbol {
546
+ color: #fff !important;
547
+ }
548
+
549
+ .checkpoint-node.active::after {
550
+ border-color: #fff;
551
+ }
552
+
553
+ .timeline-line {
554
+ background: #666;
555
+ }
556
+ }
557
+
558
+ /* Reduced Motion */
559
+ @media (prefers-reduced-motion: reduce) {
560
+ .scroll-indicator,
561
+ .checkpoint-node,
562
+ .checkpoint-tooltip {
563
+ transition: none;
564
+ }
565
+
566
+ .preview-badge {
567
+ animation: none;
568
+ }
569
+
570
+ .timeline-track {
571
+ scroll-behavior: auto;
572
+ }
573
+ }
574
+ </style>