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,238 @@
1
+ <script module lang="ts">
2
+ export interface TerminalLine {
3
+ /** 'prompt' for the `$ ` prefix line, 'output' for streamed text. */
4
+ kind: 'prompt' | 'output';
5
+ /** Text content. For 'prompt' this is the typed command; for 'output' the streamed block. */
6
+ text: string;
7
+ }
8
+
9
+ export interface GraphTerminalProps {
10
+ /** Caption on the frame's top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
11
+ title: string;
12
+ /** Scripted session: prompt lines get typed character-by-character; output lines stream. */
13
+ lines: TerminalLine[];
14
+ /** Milliseconds per character (typing). Default 28. */
15
+ typeMs?: number;
16
+ /** Milliseconds per character (streaming output). Default 14. */
17
+ streamMs?: number;
18
+ /** Milliseconds to hold the fully-revealed script before restarting. Default 1600. */
19
+ holdMs?: number;
20
+ /** Loop the session. Default true. */
21
+ loop?: boolean;
22
+ /** Animate the typing and streaming. Default true. Reduced motion shows full script. */
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
+ lines,
37
+ typeMs = 28,
38
+ streamMs = 14,
39
+ holdMs = 1600,
40
+ loop = true,
41
+ animated = true,
42
+ label = 'a session, scripted',
43
+ class: className = ''
44
+ }: GraphTerminalProps = $props();
45
+
46
+ interface Seg {
47
+ text: string;
48
+ cls?: string;
49
+ }
50
+
51
+ // svelte-ignore state_referenced_locally
52
+ const script: TerminalLine[] = lines.length > 0
53
+ ? lines
54
+ : [
55
+ { kind: 'prompt', text: 'echo ready' },
56
+ { kind: 'output', text: 'ready' }
57
+ ];
58
+
59
+ // Per-character durations for each line, in ticks (1 char = 1 tick).
60
+ // svelte-ignore state_referenced_locally
61
+ const charMs: number[] = script.map((l) => (l.kind === 'prompt' ? typeMs : streamMs));
62
+ // svelte-ignore state_referenced_locally
63
+ const totalChars: number = script.reduce((sum, l) => sum + l.text.length, 0);
64
+
65
+ // svelte-ignore state_referenced_locally
66
+ const initialChar = animated ? Math.floor(totalChars * 0.55) : totalChars;
67
+
68
+ // svelte-ignore state_referenced_locally
69
+ let char = $state(initialChar);
70
+
71
+ const moving = $derived(
72
+ animated &&
73
+ (typeof window === 'undefined' ||
74
+ !window.matchMedia('(prefers-reduced-motion: reduce)').matches)
75
+ );
76
+
77
+ $effect(() => {
78
+ if (!moving) {
79
+ return;
80
+ }
81
+
82
+ if (char < totalChars) {
83
+ // We have to advance at a variable rate depending on the current line's kind.
84
+ // Find the active line by accumulating characters, then advance one char
85
+ // at the current line's per-char duration.
86
+ let acc = 0;
87
+ let active = 0;
88
+ for (let i = 0; i < script.length; i++) {
89
+ const next = acc + script[i]!.text.length;
90
+ if (char >= acc && char < next) {
91
+ active = i;
92
+ break;
93
+ }
94
+ acc = next;
95
+ }
96
+ const delay = charMs[active] ?? typeMs;
97
+
98
+ const timer = window.setTimeout(() => {
99
+ char += 1;
100
+ }, delay);
101
+
102
+ return () => window.clearTimeout(timer);
103
+ }
104
+
105
+ if (loop) {
106
+ const timer = window.setTimeout(() => {
107
+ char = 0;
108
+ }, holdMs);
109
+
110
+ return () => window.clearTimeout(timer);
111
+ }
112
+ });
113
+
114
+ // Reveal computation: how many chars of each line are visible.
115
+ // Frozen: char = totalChars (everything revealed).
116
+ const view = $derived.by((): Seg[] => {
117
+ const out: Seg[] = [];
118
+ const totalReveal = moving ? char : Number.POSITIVE_INFINITY;
119
+ let remaining = totalReveal;
120
+ let lastActiveIndex = -1;
121
+
122
+ for (let i = 0; i < script.length; i++) {
123
+ const line = script[i]!;
124
+ const take = Math.max(0, Math.min(line.text.length, remaining));
125
+
126
+ if (line.kind === 'prompt') {
127
+ out.push({ text: '$ ', cls: 'prompt' });
128
+ out.push({ text: line.text.slice(0, take), cls: 'cmd' });
129
+ } else {
130
+ out.push({ text: ' ' });
131
+ out.push({ text: line.text.slice(0, take), cls: 'out' });
132
+ }
133
+
134
+ if (take > 0) {
135
+ lastActiveIndex = i;
136
+ }
137
+
138
+ if (i < script.length - 1) {
139
+ out.push({ text: '\n' });
140
+ }
141
+
142
+ remaining -= take;
143
+ }
144
+
145
+ // Trailing cursor on the active prompt line, or on the last active line.
146
+ // Only show a cursor if not finished, or if still animating.
147
+ const finished = char >= totalChars;
148
+ const showCursor = !finished || (loop && moving);
149
+
150
+ if (showCursor && lastActiveIndex >= 0) {
151
+ // Find the most recent prompt line; if none, append cursor at the end.
152
+ let promptLine = -1;
153
+ for (let i = 0; i <= lastActiveIndex; i++) {
154
+ if (script[i]!.kind === 'prompt') {
155
+ promptLine = i;
156
+ }
157
+ }
158
+
159
+ if (promptLine >= 0) {
160
+ // Tag the cursor on the next push (after the line's text); we
161
+ // instead render a blinking cursor below all lines as a new
162
+ // segment so the line above stays simple.
163
+ out.push({ text: '\n' });
164
+ out.push({ text: '$ ', cls: 'prompt' });
165
+ out.push({ text: '█', cls: 'cursor blinking' });
166
+ }
167
+ }
168
+
169
+ return out;
170
+ });
171
+ </script>
172
+
173
+ <Graph {title} class={className}>
174
+ <GraphBody>
175
+ <div class="terminal">
176
+ <div class="viewport">
177
+ <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>
178
+ </div>
179
+ <p class="caption">{label}</p>
180
+ </div>
181
+ </GraphBody>
182
+ </Graph>
183
+
184
+ <style>
185
+ .terminal {
186
+ display: flex;
187
+ flex-direction: column;
188
+ gap: 0.75rem;
189
+ min-width: 0;
190
+ }
191
+
192
+ .viewport {
193
+ overflow-x: auto;
194
+ }
195
+
196
+ .art {
197
+ margin: 0;
198
+ font-size: 0.85rem;
199
+ line-height: 1.45;
200
+ color: var(--graph-foreground, oklch(0.93 0 0));
201
+ white-space: pre;
202
+ font-family: var(--graph-font, ui-monospace, monospace);
203
+ }
204
+
205
+ .prompt {
206
+ color: var(--graph-accent, oklch(0.78 0.17 155));
207
+ }
208
+
209
+ .cmd {
210
+ color: var(--graph-foreground, oklch(0.93 0 0));
211
+ }
212
+
213
+ .out {
214
+ color: var(--graph-muted, oklch(0.62 0 0));
215
+ }
216
+
217
+ .cursor {
218
+ color: var(--graph-accent, oklch(0.78 0.17 155));
219
+ }
220
+
221
+ @media (prefers-reduced-motion: no-preference) {
222
+ .cursor.blinking {
223
+ animation: blink 1s steps(1) infinite;
224
+ }
225
+
226
+ @keyframes blink {
227
+ 50% {
228
+ opacity: 0;
229
+ }
230
+ }
231
+ }
232
+
233
+ .caption {
234
+ margin: 0;
235
+ font-size: 0.8rem;
236
+ color: var(--graph-muted, oklch(0.62 0 0));
237
+ }
238
+ </style>
@@ -0,0 +1,28 @@
1
+ export interface TerminalLine {
2
+ /** 'prompt' for the `$ ` prefix line, 'output' for streamed text. */
3
+ kind: 'prompt' | 'output';
4
+ /** Text content. For 'prompt' this is the typed command; for 'output' the streamed block. */
5
+ text: string;
6
+ }
7
+ export interface GraphTerminalProps {
8
+ /** Caption on the frame's top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
9
+ title: string;
10
+ /** Scripted session: prompt lines get typed character-by-character; output lines stream. */
11
+ lines: TerminalLine[];
12
+ /** Milliseconds per character (typing). Default 28. */
13
+ typeMs?: number;
14
+ /** Milliseconds per character (streaming output). Default 14. */
15
+ streamMs?: number;
16
+ /** Milliseconds to hold the fully-revealed script before restarting. Default 1600. */
17
+ holdMs?: number;
18
+ /** Loop the session. Default true. */
19
+ loop?: boolean;
20
+ /** Animate the typing and streaming. Default true. Reduced motion shows full script. */
21
+ animated?: boolean;
22
+ /** Short caption under the art. */
23
+ label?: string;
24
+ class?: string;
25
+ }
26
+ declare const GraphTerminal: import("svelte").Component<GraphTerminalProps, {}, "">;
27
+ type GraphTerminal = ReturnType<typeof GraphTerminal>;
28
+ export default GraphTerminal;
@@ -0,0 +1,163 @@
1
+ <script module lang="ts">
2
+ /** One token of the marquee: a label plus its health status. */
3
+ export interface TickerItem {
4
+ label: string;
5
+ status: 'ok' | 'warn' | 'down';
6
+ }
7
+
8
+ export interface GraphTickerProps {
9
+ /** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
10
+ title: string;
11
+ /** Tokens rotated through the row, in order. */
12
+ items: TickerItem[];
13
+ /** Milliseconds per rotation step. Default 140. */
14
+ speedMs?: number;
15
+ /** Animate the rotation. Default true. Reduced motion shows the first items. */
16
+ animated?: boolean;
17
+ /** Character at each corner of the frame. Default '+'. */
18
+ corner?: string;
19
+ class?: string;
20
+ }
21
+
22
+ interface Seg {
23
+ text: string;
24
+ cls?: string;
25
+ }
26
+
27
+ /** Visible columns the row is padded to. */
28
+ const COLS = 44;
29
+ /** Separator between tokens. '·' is U+00B7, covered by Geist Mono. */
30
+ const SEP = ' · ';
31
+
32
+ const BADGE: Record<TickerItem['status'], string> = {
33
+ ok: '[ OK ]',
34
+ warn: '[ WARN ]',
35
+ down: '[ DOWN ]'
36
+ };
37
+ </script>
38
+
39
+ <script lang="ts">
40
+ import Graph from '../frame/Graph.svelte';
41
+ import GraphBody from '../frame/GraphBody.svelte';
42
+
43
+ let {
44
+ title,
45
+ items,
46
+ speedMs = 140,
47
+ animated = true,
48
+ corner,
49
+ class: className = ''
50
+ }: GraphTickerProps = $props();
51
+
52
+ let offset = $state(0);
53
+
54
+ const moving = $derived(
55
+ animated &&
56
+ (typeof window === 'undefined' ||
57
+ !window.matchMedia('(prefers-reduced-motion: reduce)').matches)
58
+ );
59
+
60
+ $effect(() => {
61
+ if (!moving || items.length < 2) {
62
+ return;
63
+ }
64
+
65
+ const timer = window.setInterval(() => {
66
+ offset = (offset + 1) % items.length;
67
+ }, speedMs);
68
+
69
+ return () => window.clearInterval(timer);
70
+ });
71
+
72
+ // Whole tokens rotate position, so status colors stay attached to their
73
+ // labels — the row is never sliced mid-token. Frozen state always renders
74
+ // from the first item.
75
+ const row = $derived.by((): Seg[] => {
76
+ const base = moving ? offset : 0;
77
+ const segs: Seg[] = [];
78
+ let used = 0;
79
+
80
+ for (let i = 0; i < items.length; i += 1) {
81
+ const item = items[(base + i) % items.length];
82
+ const token = `${BADGE[item.status]} ${item.label}`;
83
+
84
+ if (segs.length > 0 && used + SEP.length + token.length > COLS) {
85
+ break;
86
+ }
87
+
88
+ if (segs.length > 0) {
89
+ segs.push({ text: SEP, cls: 'sep' });
90
+ used += SEP.length;
91
+ }
92
+
93
+ segs.push({ text: token, cls: item.status });
94
+ used += token.length;
95
+ }
96
+
97
+ if (used < COLS) {
98
+ segs.push({ text: ' '.repeat(COLS - used) });
99
+ }
100
+
101
+ return segs;
102
+ });
103
+ </script>
104
+
105
+ <Graph {title} {corner} class={className}>
106
+ <GraphBody>
107
+ <div class="viewport">
108
+ <pre class="art"><code>{#key offset}<span class="tick" class:live={moving}>{#each row as seg, i (i)}{#if seg.cls}<span class={seg.cls}>{seg.text}</span>{:else}{seg.text}{/if}{/each}</span>{/key}</code></pre>
109
+ </div>
110
+ </GraphBody>
111
+ </Graph>
112
+
113
+ <style>
114
+ .viewport {
115
+ overflow-x: auto;
116
+ }
117
+
118
+ .art {
119
+ margin: 0;
120
+ font-size: 0.85rem;
121
+ line-height: 1.4;
122
+ color: var(--graph-muted, oklch(0.62 0 0));
123
+ white-space: pre;
124
+ }
125
+
126
+ .tick {
127
+ display: inline-block;
128
+ }
129
+
130
+ .ok {
131
+ color: var(--graph-accent, oklch(0.78 0.17 155));
132
+ }
133
+
134
+ .warn {
135
+ color: var(--graph-foreground, oklch(0.93 0 0));
136
+ }
137
+
138
+ .down {
139
+ color: var(--graph-faint, oklch(0.3 0 0));
140
+ }
141
+
142
+ .sep {
143
+ color: var(--graph-muted, oklch(0.62 0 0));
144
+ }
145
+
146
+ @media (prefers-reduced-motion: no-preference) {
147
+ .tick.live {
148
+ animation: tick 0.14s ease-out;
149
+ }
150
+
151
+ @keyframes tick {
152
+ from {
153
+ opacity: 0.4;
154
+ transform: translateX(0.3rem);
155
+ }
156
+
157
+ to {
158
+ opacity: 1;
159
+ transform: translateX(0);
160
+ }
161
+ }
162
+ }
163
+ </style>
@@ -0,0 +1,21 @@
1
+ /** One token of the marquee: a label plus its health status. */
2
+ export interface TickerItem {
3
+ label: string;
4
+ status: 'ok' | 'warn' | 'down';
5
+ }
6
+ export interface GraphTickerProps {
7
+ /** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
8
+ title: string;
9
+ /** Tokens rotated through the row, in order. */
10
+ items: TickerItem[];
11
+ /** Milliseconds per rotation step. Default 140. */
12
+ speedMs?: number;
13
+ /** Animate the rotation. Default true. Reduced motion shows the first items. */
14
+ animated?: boolean;
15
+ /** Character at each corner of the frame. Default '+'. */
16
+ corner?: string;
17
+ class?: string;
18
+ }
19
+ declare const GraphTicker: import("svelte").Component<GraphTickerProps, {}, "">;
20
+ type GraphTicker = ReturnType<typeof GraphTicker>;
21
+ export default GraphTicker;
@@ -0,0 +1,130 @@
1
+ <script module lang="ts">
2
+ export interface GraphTypewriterProps {
3
+ /** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
4
+ title: string;
5
+ /** Lines typed one character at a time, in order. */
6
+ lines: string[];
7
+ /** Milliseconds per character. Default 55. */
8
+ speedMs?: number;
9
+ /** Milliseconds to hold the full text before restarting. Default 1400. */
10
+ holdMs?: number;
11
+ /** Clear and retype after the hold. Default true. */
12
+ loop?: boolean;
13
+ /** Cursor character shown after the last revealed character. Default '█'. */
14
+ cursor?: string;
15
+ /** Animate the typing. Default true. Reduced motion shows the full text. */
16
+ animated?: boolean;
17
+ /** Character at each corner of the frame. Default '+'. */
18
+ corner?: string;
19
+ class?: string;
20
+ }
21
+ </script>
22
+
23
+ <script lang="ts">
24
+ import Graph from '../frame/Graph.svelte';
25
+ import GraphBody from '../frame/GraphBody.svelte';
26
+
27
+ let {
28
+ title,
29
+ lines,
30
+ speedMs = 55,
31
+ holdMs = 1400,
32
+ loop = true,
33
+ cursor = '█',
34
+ animated = true,
35
+ corner,
36
+ class: className = ''
37
+ }: GraphTypewriterProps = $props();
38
+
39
+ let count = $state(0);
40
+
41
+ const moving = $derived(
42
+ animated &&
43
+ (typeof window === 'undefined' ||
44
+ !window.matchMedia('(prefers-reduced-motion: reduce)').matches)
45
+ );
46
+
47
+ const total = $derived(lines.reduce((sum, line) => sum + line.length, 0));
48
+ const finished = $derived(count >= total);
49
+ const blinking = $derived(moving && !(finished && !loop));
50
+
51
+ $effect(() => {
52
+ if (!moving) {
53
+ return;
54
+ }
55
+
56
+ if (count < total) {
57
+ const timer = window.setTimeout(() => {
58
+ count += 1;
59
+ }, speedMs);
60
+
61
+ return () => window.clearTimeout(timer);
62
+ }
63
+
64
+ if (loop) {
65
+ const timer = window.setTimeout(() => {
66
+ count = 0;
67
+ }, holdMs);
68
+
69
+ return () => window.clearTimeout(timer);
70
+ }
71
+ });
72
+
73
+ const view = $derived.by(() => {
74
+ // Frozen (reduced motion or animated=false): everything revealed at once.
75
+ let left = moving ? count : Number.POSITIVE_INFINITY;
76
+ const rows: string[] = [];
77
+ let at = lines.length > 0 ? 0 : -1;
78
+
79
+ for (let i = 0; i < lines.length; i += 1) {
80
+ const take = Math.max(0, Math.min(lines[i].length, left));
81
+ rows.push(lines[i].slice(0, take));
82
+
83
+ if (take > 0) {
84
+ at = i;
85
+ }
86
+
87
+ left -= take;
88
+ }
89
+
90
+ return { rows, at };
91
+ });
92
+ </script>
93
+
94
+ <Graph {title} {corner} class={className}>
95
+ <GraphBody>
96
+ <div class="viewport">
97
+ <pre class="art"><code>{#each view.rows as row, i (i)}{row}{#if i === view.at}<span class="cursor" class:blinking>{cursor}</span>{/if}{#if i < view.rows.length - 1}{'\n'}{/if}{/each}</code></pre>
98
+ </div>
99
+ </GraphBody>
100
+ </Graph>
101
+
102
+ <style>
103
+ .viewport {
104
+ overflow-x: auto;
105
+ }
106
+
107
+ .art {
108
+ margin: 0;
109
+ font-size: 0.85rem;
110
+ line-height: 1.4;
111
+ color: var(--graph-muted, oklch(0.62 0 0));
112
+ white-space: pre;
113
+ }
114
+
115
+ .cursor {
116
+ color: var(--graph-accent, oklch(0.78 0.17 155));
117
+ }
118
+
119
+ @media (prefers-reduced-motion: no-preference) {
120
+ .cursor.blinking {
121
+ animation: blink 1.1s steps(1) infinite;
122
+ }
123
+
124
+ @keyframes blink {
125
+ 50% {
126
+ opacity: 0;
127
+ }
128
+ }
129
+ }
130
+ </style>
@@ -0,0 +1,22 @@
1
+ export interface GraphTypewriterProps {
2
+ /** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
3
+ title: string;
4
+ /** Lines typed one character at a time, in order. */
5
+ lines: string[];
6
+ /** Milliseconds per character. Default 55. */
7
+ speedMs?: number;
8
+ /** Milliseconds to hold the full text before restarting. Default 1400. */
9
+ holdMs?: number;
10
+ /** Clear and retype after the hold. Default true. */
11
+ loop?: boolean;
12
+ /** Cursor character shown after the last revealed character. Default '█'. */
13
+ cursor?: string;
14
+ /** Animate the typing. Default true. Reduced motion shows the full text. */
15
+ animated?: boolean;
16
+ /** Character at each corner of the frame. Default '+'. */
17
+ corner?: string;
18
+ class?: string;
19
+ }
20
+ declare const GraphTypewriter: import("svelte").Component<GraphTypewriterProps, {}, "">;
21
+ type GraphTypewriter = ReturnType<typeof GraphTypewriter>;
22
+ export default GraphTypewriter;