markgraphy 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +154 -0
  3. package/dist/animated/GraphAgni.svelte +430 -0
  4. package/dist/animated/GraphAgni.svelte.d.ts +22 -0
  5. package/dist/animated/GraphAum.svelte +294 -0
  6. package/dist/animated/GraphAum.svelte.d.ts +20 -0
  7. package/dist/animated/GraphBoot.svelte +325 -0
  8. package/dist/animated/GraphBoot.svelte.d.ts +28 -0
  9. package/dist/animated/GraphCron.svelte +476 -0
  10. package/dist/animated/GraphCron.svelte.d.ts +42 -0
  11. package/dist/animated/GraphDamru.svelte +341 -0
  12. package/dist/animated/GraphDamru.svelte.d.ts +18 -0
  13. package/dist/animated/GraphDeps.svelte +245 -0
  14. package/dist/animated/GraphDeps.svelte.d.ts +29 -0
  15. package/dist/animated/GraphDiya.svelte +330 -0
  16. package/dist/animated/GraphDiya.svelte.d.ts +22 -0
  17. package/dist/animated/GraphFire.svelte +246 -0
  18. package/dist/animated/GraphFire.svelte.d.ts +22 -0
  19. package/dist/animated/GraphFlame.svelte +258 -0
  20. package/dist/animated/GraphFlame.svelte.d.ts +14 -0
  21. package/dist/animated/GraphFlowPlayer.svelte +346 -0
  22. package/dist/animated/GraphFlowPlayer.svelte.d.ts +27 -0
  23. package/dist/animated/GraphGanga.svelte +305 -0
  24. package/dist/animated/GraphGanga.svelte.d.ts +20 -0
  25. package/dist/animated/GraphHash.svelte +251 -0
  26. package/dist/animated/GraphHash.svelte.d.ts +20 -0
  27. package/dist/animated/GraphJapa.svelte +253 -0
  28. package/dist/animated/GraphJapa.svelte.d.ts +20 -0
  29. package/dist/animated/GraphLife.svelte +312 -0
  30. package/dist/animated/GraphLife.svelte.d.ts +27 -0
  31. package/dist/animated/GraphMandala.svelte +262 -0
  32. package/dist/animated/GraphMandala.svelte.d.ts +24 -0
  33. package/dist/animated/GraphMandel.svelte +290 -0
  34. package/dist/animated/GraphMandel.svelte.d.ts +27 -0
  35. package/dist/animated/GraphPulse.svelte +248 -0
  36. package/dist/animated/GraphPulse.svelte.d.ts +24 -0
  37. package/dist/animated/GraphRain.svelte +280 -0
  38. package/dist/animated/GraphRain.svelte.d.ts +22 -0
  39. package/dist/animated/GraphScatter.svelte +395 -0
  40. package/dist/animated/GraphScatter.svelte.d.ts +31 -0
  41. package/dist/animated/GraphScope.svelte +197 -0
  42. package/dist/animated/GraphScope.svelte.d.ts +22 -0
  43. package/dist/animated/GraphSequence.svelte +247 -0
  44. package/dist/animated/GraphSequence.svelte.d.ts +18 -0
  45. package/dist/animated/GraphSpinners.svelte +109 -0
  46. package/dist/animated/GraphSpinners.svelte.d.ts +22 -0
  47. package/dist/animated/GraphState.svelte +270 -0
  48. package/dist/animated/GraphState.svelte.d.ts +14 -0
  49. package/dist/animated/GraphStream.svelte +223 -0
  50. package/dist/animated/GraphStream.svelte.d.ts +24 -0
  51. package/dist/animated/GraphSurya.svelte +268 -0
  52. package/dist/animated/GraphSurya.svelte.d.ts +22 -0
  53. package/dist/animated/GraphTerminal.svelte +238 -0
  54. package/dist/animated/GraphTerminal.svelte.d.ts +28 -0
  55. package/dist/animated/GraphTicker.svelte +163 -0
  56. package/dist/animated/GraphTicker.svelte.d.ts +21 -0
  57. package/dist/animated/GraphTypewriter.svelte +130 -0
  58. package/dist/animated/GraphTypewriter.svelte.d.ts +22 -0
  59. package/dist/animated/GraphWorkflow.svelte +570 -0
  60. package/dist/animated/GraphWorkflow.svelte.d.ts +29 -0
  61. package/dist/assets/favicon.svg +21 -0
  62. package/dist/diagram/AmplifierDiagram.svelte +206 -0
  63. package/dist/diagram/AmplifierDiagram.svelte.d.ts +12 -0
  64. package/dist/diagram/AsciiDiagram.svelte +212 -0
  65. package/dist/diagram/AsciiDiagram.svelte.d.ts +24 -0
  66. package/dist/diagram/DiagramEditor.svelte +414 -0
  67. package/dist/diagram/DiagramEditor.svelte.d.ts +3 -0
  68. package/dist/diagram/MetricsTableDiagram.svelte +125 -0
  69. package/dist/diagram/MetricsTableDiagram.svelte.d.ts +18 -0
  70. package/dist/diagram/NestedRadiiDiagram.svelte +218 -0
  71. package/dist/diagram/NestedRadiiDiagram.svelte.d.ts +12 -0
  72. package/dist/diagram/PromptLoopDiagram.svelte +331 -0
  73. package/dist/diagram/PromptLoopDiagram.svelte.d.ts +12 -0
  74. package/dist/diagram/ascii.d.ts +35 -0
  75. package/dist/diagram/ascii.js +83 -0
  76. package/dist/diagram/parser.d.ts +16 -0
  77. package/dist/diagram/parser.js +71 -0
  78. package/dist/frame/Graph.svelte +141 -0
  79. package/dist/frame/Graph.svelte.d.ts +14 -0
  80. package/dist/frame/GraphArrow.svelte +49 -0
  81. package/dist/frame/GraphArrow.svelte.d.ts +9 -0
  82. package/dist/frame/GraphBody.svelte +39 -0
  83. package/dist/frame/GraphBody.svelte.d.ts +10 -0
  84. package/dist/frame/GraphRule.svelte +21 -0
  85. package/dist/frame/GraphRule.svelte.d.ts +6 -0
  86. package/dist/frame/GraphTick.svelte +23 -0
  87. package/dist/frame/GraphTick.svelte.d.ts +9 -0
  88. package/dist/frame/GraphTrack.svelte +29 -0
  89. package/dist/frame/GraphTrack.svelte.d.ts +14 -0
  90. package/dist/frame/clock.d.ts +10 -0
  91. package/dist/frame/clock.js +62 -0
  92. package/dist/frame/glyphs.d.ts +22 -0
  93. package/dist/frame/glyphs.js +53 -0
  94. package/dist/frame/motion.css +27 -0
  95. package/dist/frame/motion.d.ts +24 -0
  96. package/dist/frame/motion.js +47 -0
  97. package/dist/frame/tone.d.ts +11 -0
  98. package/dist/frame/tone.js +64 -0
  99. package/dist/graphs/GraphActivity.svelte +387 -0
  100. package/dist/graphs/GraphActivity.svelte.d.ts +22 -0
  101. package/dist/graphs/GraphBars.svelte +194 -0
  102. package/dist/graphs/GraphBars.svelte.d.ts +20 -0
  103. package/dist/graphs/GraphBullet.svelte +208 -0
  104. package/dist/graphs/GraphBullet.svelte.d.ts +22 -0
  105. package/dist/graphs/GraphCalendar.svelte +203 -0
  106. package/dist/graphs/GraphCalendar.svelte.d.ts +20 -0
  107. package/dist/graphs/GraphCells.svelte +181 -0
  108. package/dist/graphs/GraphCells.svelte.d.ts +18 -0
  109. package/dist/graphs/GraphCompare.svelte +192 -0
  110. package/dist/graphs/GraphCompare.svelte.d.ts +18 -0
  111. package/dist/graphs/GraphCountdown.svelte +96 -0
  112. package/dist/graphs/GraphCountdown.svelte.d.ts +14 -0
  113. package/dist/graphs/GraphDiff.svelte +181 -0
  114. package/dist/graphs/GraphDiff.svelte.d.ts +18 -0
  115. package/dist/graphs/GraphFlow.svelte +137 -0
  116. package/dist/graphs/GraphFlow.svelte.d.ts +21 -0
  117. package/dist/graphs/GraphFunnel.svelte +190 -0
  118. package/dist/graphs/GraphFunnel.svelte.d.ts +21 -0
  119. package/dist/graphs/GraphGantt.svelte +258 -0
  120. package/dist/graphs/GraphGantt.svelte.d.ts +25 -0
  121. package/dist/graphs/GraphHeatmap.svelte +287 -0
  122. package/dist/graphs/GraphHeatmap.svelte.d.ts +22 -0
  123. package/dist/graphs/GraphInvoice.svelte +294 -0
  124. package/dist/graphs/GraphInvoice.svelte.d.ts +34 -0
  125. package/dist/graphs/GraphKpi.svelte +166 -0
  126. package/dist/graphs/GraphKpi.svelte.d.ts +17 -0
  127. package/dist/graphs/GraphLatency.svelte +340 -0
  128. package/dist/graphs/GraphLatency.svelte.d.ts +28 -0
  129. package/dist/graphs/GraphMeter.svelte +121 -0
  130. package/dist/graphs/GraphMeter.svelte.d.ts +16 -0
  131. package/dist/graphs/GraphPlot.svelte +226 -0
  132. package/dist/graphs/GraphPlot.svelte.d.ts +18 -0
  133. package/dist/graphs/GraphQuota.svelte +303 -0
  134. package/dist/graphs/GraphQuota.svelte.d.ts +27 -0
  135. package/dist/graphs/GraphRank.svelte +141 -0
  136. package/dist/graphs/GraphRank.svelte.d.ts +21 -0
  137. package/dist/graphs/GraphSlo.svelte +325 -0
  138. package/dist/graphs/GraphSlo.svelte.d.ts +31 -0
  139. package/dist/graphs/GraphSlope.svelte +197 -0
  140. package/dist/graphs/GraphSlope.svelte.d.ts +19 -0
  141. package/dist/graphs/GraphSpark.svelte +125 -0
  142. package/dist/graphs/GraphSpark.svelte.d.ts +15 -0
  143. package/dist/graphs/GraphSpec.svelte +69 -0
  144. package/dist/graphs/GraphSpec.svelte.d.ts +14 -0
  145. package/dist/graphs/GraphStack.svelte +266 -0
  146. package/dist/graphs/GraphStack.svelte.d.ts +25 -0
  147. package/dist/graphs/GraphStat.svelte +103 -0
  148. package/dist/graphs/GraphStat.svelte.d.ts +16 -0
  149. package/dist/graphs/GraphTable.svelte +133 -0
  150. package/dist/graphs/GraphTable.svelte.d.ts +15 -0
  151. package/dist/graphs/GraphTimeline.svelte +164 -0
  152. package/dist/graphs/GraphTimeline.svelte.d.ts +18 -0
  153. package/dist/graphs/GraphTimer.svelte +117 -0
  154. package/dist/graphs/GraphTimer.svelte.d.ts +15 -0
  155. package/dist/graphs/GraphTree.svelte +147 -0
  156. package/dist/graphs/GraphTree.svelte.d.ts +16 -0
  157. package/dist/graphs/GraphUptime.svelte +210 -0
  158. package/dist/graphs/GraphUptime.svelte.d.ts +18 -0
  159. package/dist/graphs/GraphWaffle.svelte +131 -0
  160. package/dist/graphs/GraphWaffle.svelte.d.ts +17 -0
  161. package/dist/graphs/GraphWaterfall.svelte +258 -0
  162. package/dist/graphs/GraphWaterfall.svelte.d.ts +22 -0
  163. package/dist/index.d.ts +147 -0
  164. package/dist/index.js +87 -0
  165. package/dist/themes.css +54 -0
  166. package/package.json +67 -0
@@ -0,0 +1,294 @@
1
+ <script module lang="ts">
2
+ export interface GraphAumProps {
3
+ /** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
4
+ title: string;
5
+ /** Animate the ink-in and resonance. Default true. Reduced motion freezes a settled frame. */
6
+ animated?: boolean;
7
+ /** Width of the scene in characters; at least 46 so the figure fits. Default 56. */
8
+ cols?: number;
9
+ /** Height of the scene in rows. Default 22. */
10
+ rows?: number;
11
+ /** Milliseconds per tick. Default 110. */
12
+ speedMs?: number;
13
+ /** Seed offsets the resonance wave along the stroke. Default 7. */
14
+ seedNum?: number;
15
+ /** Short caption under the art; the art itself is decorative. */
16
+ label?: string;
17
+ class?: string;
18
+ }
19
+
20
+ // The figure is drawn from five stroke polylines in paint order: bindu,
21
+ // crescent, the two humps with the tail, and the om loop. It is rasterized
22
+ // once at module load; every instance shares the same cell order.
23
+ // The art box is pre-widened (~1.8x) because terminal cells are ~2x taller
24
+ // than wide; on screen the figure reads round.
25
+ const ART_W = 44;
26
+ const ART_H = 22;
27
+
28
+ const STROKES: number[][][] = [
29
+ // bindu — the dot above
30
+ [[32, 0]],
31
+ // crescent (chandra) — shallow cup opening up
32
+ [
33
+ [25, 2],
34
+ [30, 3],
35
+ [36, 3],
36
+ [41, 2]
37
+ ],
38
+ // upper hump: tip at upper-left, arc right, return to the cusp
39
+ [
40
+ [7, 8],
41
+ [14, 5],
42
+ [23, 5],
43
+ [31, 7],
44
+ [29, 10],
45
+ [22, 12],
46
+ [13, 12]
47
+ ],
48
+ // lower hump + tail: bulge right, descend, curl left along the bottom
49
+ [
50
+ [13, 12],
51
+ [23, 12],
52
+ [32, 14],
53
+ [36, 17],
54
+ [34, 19],
55
+ [27, 21],
56
+ [14, 21],
57
+ [5, 20]
58
+ ],
59
+ // the om loop — small circle hanging at the left, painted last
60
+ [
61
+ [9, 13],
62
+ [4, 12],
63
+ [0, 14],
64
+ [2, 17],
65
+ [5, 18],
66
+ [9, 16],
67
+ [9, 14],
68
+ [7, 13]
69
+ ]
70
+ ];
71
+
72
+ interface InkCell {
73
+ x: number;
74
+ y: number;
75
+ /** Paint order along the strokes; drives the ink-in and the wave. */
76
+ s: number;
77
+ }
78
+
79
+ function rasterizeStrokes(): InkCell[] {
80
+ const seen = new Set<number>();
81
+ const cells: InkCell[] = [];
82
+ let s = 0;
83
+
84
+ for (const pts of STROKES) {
85
+ for (let i = 0; i < pts.length - 1; i++) {
86
+ const [x0, y0] = pts[i] as [number, number];
87
+ const [x1, y1] = pts[i + 1] as [number, number];
88
+ const steps = Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0)) * 4;
89
+
90
+ for (let t = 0; t <= steps; t++) {
91
+ const x = Math.round(x0 + ((x1 - x0) * t) / steps);
92
+ const y = Math.round(y0 + ((y1 - y0) * t) / steps);
93
+ const key = y * ART_W + x;
94
+
95
+ if (x >= 0 && x < ART_W && y >= 0 && y < ART_H && !seen.has(key)) {
96
+ seen.add(key);
97
+ cells.push({ x, y, s });
98
+ s += 1;
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ return cells;
105
+ }
106
+
107
+ const INK_CELLS = rasterizeStrokes();
108
+ const INK_MAP = new Map<number, number>(
109
+ INK_CELLS.map((cell) => [cell.y * ART_W + cell.x, cell.s])
110
+ );
111
+ const INK_TOTAL = INK_CELLS.length;
112
+
113
+ interface Seg {
114
+ text: string;
115
+ cls?: string;
116
+ }
117
+ </script>
118
+
119
+ <script lang="ts">
120
+ import Graph from '../frame/Graph.svelte';
121
+ import GraphBody from '../frame/GraphBody.svelte';
122
+
123
+ let {
124
+ title,
125
+ animated = true,
126
+ cols = 56,
127
+ rows = 22,
128
+ speedMs = 110,
129
+ seedNum = 7,
130
+ label = 'the primordial vibration',
131
+ class: className = ''
132
+ }: GraphAumProps = $props();
133
+
134
+ // svelte-ignore state_referenced_locally
135
+ const width = Math.max(46, cols);
136
+ // svelte-ignore state_referenced_locally
137
+ const height = Math.max(ART_H, rows);
138
+ const offsetX = Math.floor((width - ART_W) / 2);
139
+ const offsetY = Math.floor((height - ART_H) / 2);
140
+
141
+ const inkTicks = 46;
142
+ const waveSpeed = 2;
143
+ const waveGap = 44;
144
+ const waveLength = INK_TOTAL + waveGap;
145
+
146
+ // svelte-ignore state_referenced_locally
147
+ const seedPhase = (((seedNum * 7) % waveLength) + waveLength) % waveLength;
148
+
149
+ // svelte-ignore state_referenced_locally
150
+ const initialTick = animated
151
+ ? inkTicks + Math.floor((waveLength * 0.4) / waveSpeed)
152
+ : inkTicks;
153
+
154
+ // svelte-ignore state_referenced_locally
155
+ let tick = $state(initialTick);
156
+
157
+ const moving = $derived(
158
+ animated &&
159
+ (typeof window === 'undefined' ||
160
+ !window.matchMedia('(prefers-reduced-motion: reduce)').matches)
161
+ );
162
+
163
+ $effect(() => {
164
+ if (!moving) {
165
+ return;
166
+ }
167
+
168
+ const timer = window.setInterval(() => {
169
+ tick += 1;
170
+ }, speedMs);
171
+
172
+ return () => window.clearInterval(timer);
173
+ });
174
+
175
+ const view = $derived.by((): Seg[][] => {
176
+ const reveal = Math.min(INK_TOTAL, Math.floor((tick / inkTicks) * INK_TOTAL));
177
+ const wavePos =
178
+ reveal >= INK_TOTAL
179
+ ? (seedPhase + (tick - inkTicks) * waveSpeed) % waveLength
180
+ : -1;
181
+ const out: Seg[][] = [];
182
+
183
+ for (let y = 0; y < height; y++) {
184
+ const segs: Seg[] = [];
185
+ let buf = '';
186
+ let cls: string | undefined;
187
+
188
+ const flush = () => {
189
+ if (buf.length > 0) {
190
+ segs.push({ text: buf, cls });
191
+ }
192
+
193
+ buf = '';
194
+ };
195
+
196
+ for (let x = 0; x < width; x++) {
197
+ const ax = x - offsetX;
198
+ const ay = y - offsetY;
199
+ const s = ax >= 0 && ax < ART_W && ay >= 0 && ay < ART_H
200
+ ? INK_MAP.get(ay * ART_W + ax)
201
+ : undefined;
202
+ let ch = ' ';
203
+ let next: string | undefined;
204
+
205
+ if (s !== undefined) {
206
+ if (reveal < INK_TOTAL) {
207
+ // Wet ink: freshly drawn cells glow, then settle to the foreground.
208
+ if (s < reveal) {
209
+ ch = '█';
210
+
211
+ if (reveal - s <= 6) {
212
+ next = 'hot';
213
+ }
214
+ }
215
+ } else {
216
+ // Resonance: one crest travels the stroke order and rests.
217
+ const d = (wavePos - s + waveLength) % waveLength;
218
+
219
+ ch = '█';
220
+
221
+ if (d <= 3) {
222
+ next = 'hot';
223
+ } else if (d <= 12) {
224
+ next = 'mid';
225
+ }
226
+ }
227
+ }
228
+
229
+ if (next === cls) {
230
+ buf += ch;
231
+ } else {
232
+ flush();
233
+ cls = next;
234
+ buf = ch;
235
+ }
236
+ }
237
+
238
+ flush();
239
+ out.push(segs);
240
+ }
241
+
242
+ return out;
243
+ });
244
+ </script>
245
+
246
+ <Graph {title} class={className}>
247
+ <GraphBody>
248
+ <div class="aum">
249
+ <div class="viewport">
250
+ <pre class="art" aria-hidden="true"><code>{#each view as line, i (i)}{#each line as seg, j (j)}{#if seg.cls}<span class={seg.cls}>{seg.text}</span>{:else}{seg.text}{/if}{/each}{#if i < view.length - 1}{'\n'}{/if}{/each}</code></pre>
251
+ </div>
252
+ {#if label}
253
+ <p class="caption">{label}</p>
254
+ {/if}
255
+ </div>
256
+ </GraphBody>
257
+ </Graph>
258
+
259
+ <style>
260
+ .aum {
261
+ display: flex;
262
+ flex-direction: column;
263
+ gap: 1rem;
264
+ min-width: 0;
265
+ font-size: 0.875rem;
266
+ }
267
+
268
+ .viewport {
269
+ overflow-x: auto;
270
+ }
271
+
272
+ .art {
273
+ margin: 0;
274
+ font-size: 0.85rem;
275
+ line-height: 1.15;
276
+ color: var(--graph-foreground, oklch(0.93 0 0));
277
+ white-space: pre;
278
+ }
279
+
280
+ .hot {
281
+ color: var(--graph-accent, oklch(0.78 0.17 155));
282
+ font-weight: 600;
283
+ }
284
+
285
+ .mid {
286
+ color: var(--graph-muted, oklch(0.62 0 0));
287
+ }
288
+
289
+ .caption {
290
+ margin: 0;
291
+ font-size: 0.8rem;
292
+ color: var(--graph-muted, oklch(0.62 0 0));
293
+ }
294
+ </style>
@@ -0,0 +1,20 @@
1
+ export interface GraphAumProps {
2
+ /** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
3
+ title: string;
4
+ /** Animate the ink-in and resonance. Default true. Reduced motion freezes a settled frame. */
5
+ animated?: boolean;
6
+ /** Width of the scene in characters; at least 46 so the figure fits. Default 56. */
7
+ cols?: number;
8
+ /** Height of the scene in rows. Default 22. */
9
+ rows?: number;
10
+ /** Milliseconds per tick. Default 110. */
11
+ speedMs?: number;
12
+ /** Seed offsets the resonance wave along the stroke. Default 7. */
13
+ seedNum?: number;
14
+ /** Short caption under the art; the art itself is decorative. */
15
+ label?: string;
16
+ class?: string;
17
+ }
18
+ declare const GraphAum: import("svelte").Component<GraphAumProps, {}, "">;
19
+ type GraphAum = ReturnType<typeof GraphAum>;
20
+ export default GraphAum;
@@ -0,0 +1,325 @@
1
+ <script module lang="ts">
2
+ export interface GraphBootStep {
3
+ /** Short label, e.g. 'compose up', 'migrate', 'seed'. */
4
+ label: string;
5
+ /** Optional ETA in 'ms' to surface in the per-step tag, e.g. '~1.4s'. */
6
+ eta?: string;
7
+ /** If true, this step fails; the final summary shows ✗ and stops. Default false. */
8
+ fail?: boolean;
9
+ }
10
+
11
+ export interface GraphBootProps {
12
+ /** Caption on the frame's top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
13
+ title: string;
14
+ /** Ordered list of deploy/boot steps to show. */
15
+ steps: GraphBootStep[];
16
+ /** Milliseconds per tick of the boot animation. Default 90. */
17
+ speedMs?: number;
18
+ /** Total ticks each step is "in progress" before flipping to ✓/✗. Default 14. */
19
+ stepTicks?: number;
20
+ /** Seed for any stochastic timing jitter per step. Default 5. */
21
+ seedNum?: number;
22
+ /** Animate the boot. Default true. Reduced motion freezes a mid-run frame. */
23
+ animated?: boolean;
24
+ /** Short caption under the art. */
25
+ label?: string;
26
+ class?: string;
27
+ }
28
+ </script>
29
+
30
+ <script lang="ts">
31
+ import Graph from '../frame/Graph.svelte';
32
+ import GraphBody from '../frame/GraphBody.svelte';
33
+
34
+ let {
35
+ title,
36
+ steps,
37
+ speedMs = 90,
38
+ stepTicks = 14,
39
+ seedNum = 5,
40
+ animated = true,
41
+ label = 'from cold to live',
42
+ class: className = ''
43
+ }: GraphBootProps = $props();
44
+
45
+ interface Seg {
46
+ text: string;
47
+ cls?: string;
48
+ }
49
+
50
+ // svelte-ignore state_referenced_locally
51
+ const list: GraphBootStep[] = steps.length > 0 ? steps : [{ label: 'idle' }];
52
+
53
+ // svelte-ignore state_referenced_locally
54
+ const stepDur = Math.max(2, Math.round(stepTicks));
55
+
56
+ // Seeded per-step duration jitter so a step takes 70%..130% of the default.
57
+ function mulberry32(seed: number): () => number {
58
+ let a = seed >>> 0;
59
+ return () => {
60
+ a = (a + 0x6d2b79f5) | 0;
61
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
62
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
63
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
64
+ };
65
+ }
66
+
67
+ // svelte-ignore state_referenced_locally
68
+ const rand = mulberry32(seedNum);
69
+ // svelte-ignore state_referenced_locally
70
+ const jitter: number[] = list.map(() => 0.7 + rand() * 0.6);
71
+
72
+ // Per-step tick length = stepDur * jitter (rounded, >= 3).
73
+ // svelte-ignore state_referenced_locally
74
+ const stepLens: number[] = list.map((_, i) =>
75
+ Math.max(3, Math.round(stepDur * (jitter[i] ?? 1)))
76
+ );
77
+
78
+ // Total ticks to complete the whole boot (sum of all step lengths) plus 6 ticks of post-summary.
79
+ const totalTicks = stepLens.reduce((a, b) => a + b, 0) + 6;
80
+ // svelte-ignore state_referenced_locally
81
+ const initialTick = animated ? Math.min(totalTicks, Math.floor(totalTicks * 0.6)) : totalTicks;
82
+
83
+ // svelte-ignore state_referenced_locally
84
+ let tick = $state(initialTick);
85
+
86
+ const moving = $derived(
87
+ animated &&
88
+ (typeof window === 'undefined' ||
89
+ !window.matchMedia('(prefers-reduced-motion: reduce)').matches)
90
+ );
91
+
92
+ $effect(() => {
93
+ if (!moving) {
94
+ return;
95
+ }
96
+
97
+ const timer = window.setInterval(() => {
98
+ if (tick < totalTicks) {
99
+ tick += 1;
100
+ } else {
101
+ tick = 0;
102
+ }
103
+ }, speedMs);
104
+
105
+ return () => window.clearInterval(timer);
106
+ });
107
+
108
+ // For each step, derive: status, fill (0..1), and tickSinceEnd.
109
+ // status: 'pending' | 'running' | 'ok' | 'fail'
110
+ const stepState = $derived.by(() => {
111
+ const out: { status: 'pending' | 'running' | 'ok' | 'fail'; fill: number }[] = [];
112
+ let acc = 0;
113
+
114
+ for (let i = 0; i < list.length; i++) {
115
+ const len = stepLens[i] ?? stepDur;
116
+ const start = acc;
117
+ const end = acc + len;
118
+ acc = end;
119
+
120
+ if (tick < start) {
121
+ out.push({ status: 'pending', fill: 0 });
122
+ } else if (tick < end) {
123
+ const fill = (tick - start) / Math.max(1, len - 1);
124
+ out.push({ status: 'running', fill: Math.min(1, fill) });
125
+ } else {
126
+ // A failing step fails on its final tick and everything after.
127
+ const failed = !!list[i]?.fail && tick >= end;
128
+ out.push({ status: failed ? 'fail' : 'ok', fill: 1 });
129
+ }
130
+ }
131
+
132
+ return out;
133
+ });
134
+
135
+ // A failing step halts later steps — if any prior step is 'fail', all subsequent
136
+ // are marked skipped (kept pending) so the summary makes sense.
137
+ const haltedAt = $derived.by(() => {
138
+ for (let i = 0; i < stepState.length; i++) {
139
+ if (stepState[i]!.status === 'fail') {
140
+ return i;
141
+ }
142
+ }
143
+ return -1;
144
+ });
145
+
146
+ // For rendering, an effective state honors the halt.
147
+ const effectiveState = $derived(
148
+ stepState.map((s, i) => {
149
+ if (haltedAt >= 0 && i > haltedAt) {
150
+ return { status: 'pending' as const, fill: 0 };
151
+ }
152
+ return s;
153
+ })
154
+ );
155
+
156
+ // Total elapsed time: a synthetic ETA in seconds. Each step = 0.6s base.
157
+ // svelte-ignore state_referenced_locally
158
+ const totalEta = $derived(
159
+ (stepLens.reduce((a, b) => a + b, 0) * (speedMs / 1000)).toFixed(1)
160
+ );
161
+ // svelte-ignore state_referenced_locally
162
+ const elapsedS = $derived(
163
+ Math.min(parseFloat(totalEta), (tick * speedMs) / 1000).toFixed(1)
164
+ );
165
+
166
+ // Pass / fail summary
167
+ const summary = $derived.by(() => {
168
+ const anyFail = haltedAt >= 0;
169
+ const okCount = effectiveState.filter((s) => s.status === 'ok').length;
170
+ const failCount = effectiveState.filter((s) => s.status === 'fail').length;
171
+ const total = list.length;
172
+
173
+ if (anyFail) {
174
+ return { kind: 'fail' as const, ok: okCount, fail: failCount, total };
175
+ }
176
+ return { kind: 'ok' as const, ok: total, fail: 0, total };
177
+ });
178
+
179
+ // RLE view of the per-step rows
180
+ const view = $derived.by((): Seg[] => {
181
+ const out: Seg[] = [];
182
+ // Find the longest label, cap at 18 chars to keep rows tidy
183
+ const labelWidth = Math.min(18, Math.max(8, ...list.map((s) => s.label.length + 2)));
184
+
185
+ for (let i = 0; i < list.length; i++) {
186
+ const step = list[i]!;
187
+ const state = effectiveState[i]!;
188
+
189
+ // "[ label ] [▰▰▰▱▱▱▱▱▱▱] ·1.2s ✓"
190
+ const pad = labelWidth - step.label.length;
191
+ const paddedLabel = step.label + ' '.repeat(Math.max(0, pad));
192
+
193
+ out.push({ text: ' ' });
194
+ out.push({ text: paddedLabel, cls: 'mid' });
195
+
196
+ // bar — 10 cells wide
197
+ const barWidth = 10;
198
+ const filled = Math.round(state.fill * barWidth);
199
+
200
+ if (state.status === 'pending') {
201
+ out.push({ text: '[' });
202
+ out.push({ text: '·'.repeat(barWidth), cls: 'faint' });
203
+ out.push({ text: ']' });
204
+ out.push({ text: ' ' });
205
+ out.push({ text: '…', cls: 'faint' });
206
+ } else if (state.status === 'running') {
207
+ out.push({ text: '[' });
208
+ out.push({ text: '▰'.repeat(filled), cls: 'hot' });
209
+ out.push({ text: '▱'.repeat(barWidth - filled), cls: 'faint' });
210
+ out.push({ text: ']' });
211
+ out.push({ text: ' ' });
212
+ if (step.eta) {
213
+ out.push({ text: step.eta, cls: 'mid' });
214
+ } else {
215
+ out.push({ text: '…', cls: 'faint' });
216
+ }
217
+ } else if (state.status === 'ok') {
218
+ out.push({ text: '[' });
219
+ out.push({ text: '▰'.repeat(barWidth), cls: 'mid' });
220
+ out.push({ text: ']' });
221
+ out.push({ text: ' ' });
222
+ if (step.eta) {
223
+ out.push({ text: step.eta, cls: 'mid' });
224
+ } else {
225
+ out.push({ text: 'ok', cls: 'mid' });
226
+ }
227
+ out.push({ text: ' ' });
228
+ out.push({ text: '✓', cls: 'hot' });
229
+ } else {
230
+ out.push({ text: '[' });
231
+ out.push({ text: '▰'.repeat(filled) + '▱'.repeat(barWidth - filled), cls: 'faint' });
232
+ out.push({ text: ']' });
233
+ out.push({ text: ' ' });
234
+ if (step.eta) {
235
+ out.push({ text: step.eta, cls: 'mid' });
236
+ } else {
237
+ out.push({ text: '—', cls: 'faint' });
238
+ }
239
+ out.push({ text: ' ' });
240
+ out.push({ text: '✗', cls: 'fail' });
241
+ }
242
+
243
+ out.push({ text: '\n' });
244
+ }
245
+
246
+ // Summary row
247
+ out.push({ text: ' ' });
248
+ out.push({ text: '─'.repeat(labelWidth), cls: 'faint' });
249
+ out.push({ text: '\n' });
250
+
251
+ out.push({ text: ' ' });
252
+ out.push({ text: 'total', cls: 'mid' });
253
+ out.push({ text: ' '.repeat(Math.max(0, labelWidth - 5)) });
254
+
255
+ const mark = summary.kind === 'ok' ? '✓' : '✗';
256
+ const markCls = summary.kind === 'ok' ? 'hot' : 'fail';
257
+ const passedText = `${summary.ok}/${summary.total} passed`;
258
+ const space = ' ';
259
+
260
+ out.push({ text: passedText, cls: 'mid' });
261
+ out.push({ text: space });
262
+ out.push({ text: `${elapsedS}s / ${totalEta}s`, cls: 'mid' });
263
+ out.push({ text: ' ' });
264
+ out.push({ text: mark, cls: markCls });
265
+
266
+ return out;
267
+ });
268
+ </script>
269
+
270
+ <Graph {title} class={className}>
271
+ <GraphBody>
272
+ <div class="boot">
273
+ <div class="viewport">
274
+ <pre class="art" aria-hidden="true"><code>{#each view as seg, i (i)}{#if seg.cls}<span class={seg.cls}>{seg.text}</span>{:else}{seg.text}{/if}{/each}</code></pre>
275
+ </div>
276
+ <p class="caption">{label}</p>
277
+ </div>
278
+ </GraphBody>
279
+ </Graph>
280
+
281
+ <style>
282
+ .boot {
283
+ display: flex;
284
+ flex-direction: column;
285
+ gap: 0.75rem;
286
+ min-width: 0;
287
+ }
288
+
289
+ .viewport {
290
+ overflow-x: auto;
291
+ }
292
+
293
+ .art {
294
+ margin: 0;
295
+ font-size: 0.85rem;
296
+ line-height: 1.5;
297
+ color: var(--graph-foreground, oklch(0.93 0 0));
298
+ white-space: pre;
299
+ font-variant-numeric: tabular-nums;
300
+ }
301
+
302
+ .hot {
303
+ color: var(--graph-accent, oklch(0.78 0.17 155));
304
+ font-weight: 600;
305
+ }
306
+
307
+ .mid {
308
+ color: var(--graph-muted, oklch(0.62 0 0));
309
+ }
310
+
311
+ .faint {
312
+ color: var(--graph-faint, oklch(0.3 0 0));
313
+ }
314
+
315
+ .fail {
316
+ color: var(--graph-fail, oklch(0.7 0.18 25));
317
+ font-weight: 600;
318
+ }
319
+
320
+ .caption {
321
+ margin: 0;
322
+ font-size: 0.8rem;
323
+ color: var(--graph-muted, oklch(0.62 0 0));
324
+ }
325
+ </style>
@@ -0,0 +1,28 @@
1
+ export interface GraphBootStep {
2
+ /** Short label, e.g. 'compose up', 'migrate', 'seed'. */
3
+ label: string;
4
+ /** Optional ETA in 'ms' to surface in the per-step tag, e.g. '~1.4s'. */
5
+ eta?: string;
6
+ /** If true, this step fails; the final summary shows ✗ and stops. Default false. */
7
+ fail?: boolean;
8
+ }
9
+ export interface GraphBootProps {
10
+ /** Caption on the frame's top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
11
+ title: string;
12
+ /** Ordered list of deploy/boot steps to show. */
13
+ steps: GraphBootStep[];
14
+ /** Milliseconds per tick of the boot animation. Default 90. */
15
+ speedMs?: number;
16
+ /** Total ticks each step is "in progress" before flipping to ✓/✗. Default 14. */
17
+ stepTicks?: number;
18
+ /** Seed for any stochastic timing jitter per step. Default 5. */
19
+ seedNum?: number;
20
+ /** Animate the boot. Default true. Reduced motion freezes a mid-run frame. */
21
+ animated?: boolean;
22
+ /** Short caption under the art. */
23
+ label?: string;
24
+ class?: string;
25
+ }
26
+ declare const GraphBoot: import("svelte").Component<GraphBootProps, {}, "">;
27
+ type GraphBoot = ReturnType<typeof GraphBoot>;
28
+ export default GraphBoot;