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,223 @@
1
+ <script module lang="ts">
2
+ /** Live appending sparkline with a big current value, like a terminal KPI. */
3
+ export type StreamKind = 'elapsed' | 'ago';
4
+
5
+ export interface GraphStreamProps {
6
+ /** Caption on the frame's top edge. */
7
+ title: string;
8
+ /** Points kept in the rolling history. Default 40. */
9
+ length?: number;
10
+ /** Milliseconds between appends. Default 900. */
11
+ intervalMs?: number;
12
+ /** Initial history. Default a seeded, SSR-safe random walk from 50. */
13
+ values?: number[];
14
+ /** Next-value generator. Default a gentle random walk clamped to 0..100. */
15
+ generate?: (tick: number, previous: number) => number;
16
+ /** Suffix after the value, e.g. 'ms' or '%'. Default ''. */
17
+ unit?: string;
18
+ /** Caption flavor: 'ago' counts since the last append; 'elapsed' since mount. */
19
+ kind?: StreamKind;
20
+ /** Animate the appends. Default true. Reduced motion freezes the stream. */
21
+ animated?: boolean;
22
+ class?: string;
23
+ }
24
+
25
+ const SPARK: readonly string[] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
26
+
27
+ const DEFAULT_GENERATE = (_tick: number, previous: number): number => {
28
+ const drift = (Math.random() - 0.5) * 12;
29
+ return Math.min(100, Math.max(0, previous + drift));
30
+ };
31
+
32
+ /**
33
+ * Deterministic walk so server and client render the same default
34
+ * history — no hydration mismatch, and it still reads as random.
35
+ */
36
+ function seededWalk(count: number, start = 50): number[] {
37
+ let seed = 0x9e3779b9;
38
+
39
+ const rand = () => {
40
+ seed = (seed + 0x6d2b79f5) | 0;
41
+ let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
42
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
43
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
44
+ };
45
+
46
+ const out = [start];
47
+
48
+ for (let i = 1; i < count; i++) {
49
+ const previous = out[out.length - 1] ?? start;
50
+ out.push(Math.min(100, Math.max(0, previous + (rand() - 0.5) * 10)));
51
+ }
52
+
53
+ return out;
54
+ }
55
+ </script>
56
+
57
+ <script lang="ts">
58
+ import Graph from '../frame/Graph.svelte';
59
+ import GraphBody from '../frame/GraphBody.svelte';
60
+ import { formatAgo, formatHms, graphNow } from '../frame/clock';
61
+ import { reveal } from '../frame/motion';
62
+
63
+ let {
64
+ title,
65
+ length = 40,
66
+ intervalMs = 900,
67
+ values,
68
+ generate,
69
+ unit = '',
70
+ kind = 'ago',
71
+ animated = true,
72
+ class: className = ''
73
+ }: GraphStreamProps = $props();
74
+
75
+ // svelte-ignore state_referenced_locally
76
+ let history = $state(
77
+ values == null
78
+ ? seededWalk(Math.max(1, Math.floor(length)))
79
+ : values.slice(-Math.max(1, Math.floor(length)))
80
+ );
81
+ let tick = $state(0);
82
+ let updatedAt = $state(Date.now());
83
+ const startedAt = Date.now();
84
+
85
+ const now = graphNow();
86
+ const nextValue = $derived(generate ?? DEFAULT_GENERATE);
87
+
88
+ const moving = $derived(
89
+ animated &&
90
+ (typeof window === 'undefined' ||
91
+ !window.matchMedia('(prefers-reduced-motion: reduce)').matches)
92
+ );
93
+
94
+ const cap = $derived(Math.max(1, Math.floor(length)));
95
+
96
+ $effect(() => {
97
+ if (!moving) {
98
+ return;
99
+ }
100
+
101
+ const timer = window.setInterval(() => {
102
+ tick += 1;
103
+ const previous = history[history.length - 1] ?? 50;
104
+ const next = nextValue(tick, previous);
105
+ const keep = cap - 1;
106
+
107
+ history = keep > 0 ? [...history.slice(-keep), next] : [next];
108
+ updatedAt = Date.now();
109
+ }, intervalMs);
110
+
111
+ return () => window.clearInterval(timer);
112
+ });
113
+
114
+ const current = $derived(history.length > 0 ? (history[history.length - 1] ?? 0) : 0);
115
+ const display = $derived(
116
+ Number.isFinite(current) ? (Number.isInteger(current) ? String(current) : current.toFixed(1)) : '—'
117
+ );
118
+
119
+ /** Block-glyph mapping, same approach as GraphSpark. */
120
+ const max = $derived(Math.max(...history, 1));
121
+ const points = $derived(
122
+ history.map((value) => {
123
+ const index = Math.round((value / max) * (SPARK.length - 1));
124
+ return SPARK[index] ?? SPARK[0] ?? '▁';
125
+ })
126
+ );
127
+
128
+ const caption = $derived.by(() => {
129
+ const time = $now;
130
+
131
+ if (time == null) {
132
+ return kind === 'ago' ? 'updated 0s ago' : 'elapsed 00:00:00';
133
+ }
134
+
135
+ if (!moving || kind === 'elapsed') {
136
+ return `elapsed ${formatHms(Math.max(0, time - startedAt))}`;
137
+ }
138
+
139
+ return `updated ${formatAgo(Math.max(0, time - updatedAt))}`;
140
+ });
141
+ </script>
142
+
143
+ <Graph {title} class={className}>
144
+ <GraphBody>
145
+ <div class="stream" use:reveal={{ amount: 0.5 }}>
146
+ <p class="value">
147
+ {display}{#if unit}<span class="unit"> {unit}</span>{/if}
148
+ </p>
149
+ {#if points.length > 0}
150
+ <div class="spark" aria-hidden="true">
151
+ {#each points as glyph, i (i)}
152
+ <span class="cell" class:c-accent={i === points.length - 1}>{glyph}</span>
153
+ {/each}
154
+ </div>
155
+ {/if}
156
+ <p class="muted">{caption}</p>
157
+ </div>
158
+ <span class="sr-only">{display}{unit}. {caption}</span>
159
+ </GraphBody>
160
+ </Graph>
161
+
162
+ <style>
163
+ .stream {
164
+ display: flex;
165
+ flex-direction: column;
166
+ gap: 1rem;
167
+ }
168
+
169
+ .value {
170
+ margin: 0;
171
+ font-size: 1.875rem;
172
+ line-height: 1.2;
173
+ letter-spacing: -0.025em;
174
+ font-variant-numeric: tabular-nums;
175
+ color: var(--graph-foreground, oklch(0.93 0 0));
176
+ }
177
+
178
+ @media (min-width: 640px) {
179
+ .value {
180
+ font-size: 2.25rem;
181
+ }
182
+ }
183
+
184
+ .unit {
185
+ font-size: 1.25rem;
186
+ color: var(--graph-muted, oklch(0.62 0 0));
187
+ }
188
+
189
+ .spark {
190
+ display: flex;
191
+ justify-content: flex-start;
192
+ column-gap: 0.125rem;
193
+ width: 100%;
194
+ min-width: 0;
195
+ user-select: none;
196
+ }
197
+
198
+ .cell {
199
+ flex: none;
200
+ min-width: 1ch;
201
+ }
202
+
203
+ .c-accent {
204
+ color: var(--graph-accent, oklch(0.78 0.17 155));
205
+ }
206
+
207
+ .muted {
208
+ margin: 0;
209
+ color: var(--graph-muted, oklch(0.62 0 0));
210
+ }
211
+
212
+ .sr-only {
213
+ position: absolute;
214
+ width: 1px;
215
+ height: 1px;
216
+ padding: 0;
217
+ margin: -1px;
218
+ overflow: hidden;
219
+ clip-path: inset(50%);
220
+ white-space: nowrap;
221
+ border-width: 0;
222
+ }
223
+ </style>
@@ -0,0 +1,24 @@
1
+ /** Live appending sparkline with a big current value, like a terminal KPI. */
2
+ export type StreamKind = 'elapsed' | 'ago';
3
+ export interface GraphStreamProps {
4
+ /** Caption on the frame's top edge. */
5
+ title: string;
6
+ /** Points kept in the rolling history. Default 40. */
7
+ length?: number;
8
+ /** Milliseconds between appends. Default 900. */
9
+ intervalMs?: number;
10
+ /** Initial history. Default a seeded, SSR-safe random walk from 50. */
11
+ values?: number[];
12
+ /** Next-value generator. Default a gentle random walk clamped to 0..100. */
13
+ generate?: (tick: number, previous: number) => number;
14
+ /** Suffix after the value, e.g. 'ms' or '%'. Default ''. */
15
+ unit?: string;
16
+ /** Caption flavor: 'ago' counts since the last append; 'elapsed' since mount. */
17
+ kind?: StreamKind;
18
+ /** Animate the appends. Default true. Reduced motion freezes the stream. */
19
+ animated?: boolean;
20
+ class?: string;
21
+ }
22
+ declare const GraphStream: import("svelte").Component<GraphStreamProps, {}, "">;
23
+ type GraphStream = ReturnType<typeof GraphStream>;
24
+ export default GraphStream;
@@ -0,0 +1,268 @@
1
+ <script module lang="ts">
2
+ export interface GraphSuryaProps {
3
+ /** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
4
+ title: string;
5
+ /** Animate the day cycle. Default true. Reduced motion freezes a mid-morning frame. */
6
+ animated?: boolean;
7
+ /** Width of the scene in characters. Default 60. */
8
+ cols?: number;
9
+ /** Height of the scene in rows. Default 18. */
10
+ rows?: number;
11
+ /** Milliseconds per tick. Default 120. */
12
+ speedMs?: number;
13
+ /** Number of spokes on the wheel. Default 12. */
14
+ rays?: number;
15
+ /** Seed for the per-spoke length jitter. Default 5. */
16
+ seedNum?: number;
17
+ /** Short caption under the art; the art itself is decorative. */
18
+ label?: string;
19
+ class?: string;
20
+ }
21
+
22
+ /** Deterministic mulberry32 PRNG so a seed replays the same wheel. */
23
+ function mulberry32(seed: number): () => number {
24
+ let a = seed >>> 0;
25
+
26
+ return () => {
27
+ a = (a + 0x6d2b79f5) | 0;
28
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
29
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
30
+
31
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
32
+ };
33
+ }
34
+
35
+ interface Seg {
36
+ text: string;
37
+ cls?: string;
38
+ }
39
+ </script>
40
+
41
+ <script lang="ts">
42
+ import Graph from '../frame/Graph.svelte';
43
+ import GraphBody from '../frame/GraphBody.svelte';
44
+
45
+ let {
46
+ title,
47
+ animated = true,
48
+ cols = 60,
49
+ rows = 18,
50
+ speedMs = 120,
51
+ rays = 12,
52
+ seedNum = 5,
53
+ label = 'the mover of days',
54
+ class: className = ''
55
+ }: GraphSuryaProps = $props();
56
+
57
+ // svelte-ignore state_referenced_locally
58
+ const width = Math.max(32, cols);
59
+ // svelte-ignore state_referenced_locally
60
+ const height = Math.max(12, rows);
61
+ const horizonY = height - 3;
62
+ const radius = 5;
63
+ const centerX = Math.floor(width / 2);
64
+ const riseTicks = 90;
65
+ // Character cells are ~2x taller than wide; correct so the disc reads round.
66
+ const aspect = 2.1;
67
+ const halo = radius + 1.1;
68
+
69
+ // svelte-ignore state_referenced_locally
70
+ const rand = mulberry32(seedNum);
71
+ // svelte-ignore state_referenced_locally
72
+ const rayLens = Array.from({ length: Math.max(3, rays) }, () => 2.5 + 2 * rand());
73
+
74
+ // The sun rises from below the horizon to the apex and sets again, forever.
75
+ function sunY(tick: number): number {
76
+ const pp = tick % (riseTicks * 2);
77
+ const tri = pp < riseTicks ? pp / riseTicks : (riseTicks * 2 - pp) / riseTicks;
78
+ const ease = tri * tri * (3 - 2 * tri);
79
+
80
+ return horizonY - (radius + 1 + ease * Math.max(2, horizonY - radius - 3.5));
81
+ }
82
+
83
+ function initialTick(): number {
84
+ // Mid-morning: disc clear of the horizon, no ray clipped by the top edge.
85
+ return Math.round(riseTicks * 0.3);
86
+ }
87
+
88
+ // svelte-ignore state_referenced_locally
89
+ let tick = $state(initialTick());
90
+
91
+ const moving = $derived(
92
+ animated &&
93
+ (typeof window === 'undefined' ||
94
+ !window.matchMedia('(prefers-reduced-motion: reduce)').matches)
95
+ );
96
+
97
+ $effect(() => {
98
+ if (!moving) {
99
+ return;
100
+ }
101
+
102
+ const timer = window.setInterval(() => {
103
+ tick += 1;
104
+ }, speedMs);
105
+
106
+ return () => window.clearInterval(timer);
107
+ });
108
+
109
+ const view = $derived.by((): Seg[][] => {
110
+ const cy = sunY(tick);
111
+ const phase = tick * 0.09;
112
+ const sector = (Math.PI * 2) / Math.max(3, rays);
113
+ const n = Math.max(3, rays);
114
+
115
+ // Spokes are dense line segments painted from the halo outward; a
116
+ // brightness wave rides the wheel so one spoke glints at a time.
117
+ const rayMap = new Map<number, { u: number; b: number }>();
118
+
119
+ for (let k = 0; k < n; k++) {
120
+ const th = k * sector - Math.PI / 2;
121
+ const len = rayLens[k];
122
+ const b = 0.5 + 0.5 * Math.cos(th - phase);
123
+ const steps = Math.ceil(len * 4);
124
+
125
+ for (let i = 0; i <= steps; i++) {
126
+ const r = halo + 0.5 + (i / steps) * len;
127
+ const px = Math.round(centerX + r * Math.cos(th));
128
+ const py = Math.round(cy + (r * Math.sin(th)) / aspect);
129
+ const key = py * width + px;
130
+
131
+ if (px >= 0 && px < width && py >= 0 && py < horizonY && !rayMap.has(key)) {
132
+ rayMap.set(key, { u: i / steps, b });
133
+ }
134
+ }
135
+ }
136
+
137
+ const out: Seg[][] = [];
138
+
139
+ for (let y = 0; y < height; y++) {
140
+ const segs: Seg[] = [];
141
+ let buf = '';
142
+ let cls: string | undefined;
143
+
144
+ const flush = () => {
145
+ if (buf.length > 0) {
146
+ segs.push({ text: buf, cls });
147
+ }
148
+
149
+ buf = '';
150
+ };
151
+
152
+ for (let x = 0; x < width; x++) {
153
+ let ch = ' ';
154
+ let next: string | undefined;
155
+
156
+ if (y === horizonY) {
157
+ ch = '─';
158
+ } else if (y < horizonY) {
159
+ const dx = x - centerX;
160
+ const dy = (y - cy) * aspect;
161
+ const d = Math.sqrt(dx * dx + dy * dy);
162
+
163
+ if (d <= radius) {
164
+ const t = d / radius;
165
+
166
+ if (t < 0.45) {
167
+ ch = '█';
168
+ next = 'hot';
169
+ } else if (t < 0.8) {
170
+ ch = '▓';
171
+ } else {
172
+ ch = '▒';
173
+ next = 'mid';
174
+ }
175
+ } else if (d <= halo) {
176
+ ch = '░';
177
+ next = 'faint';
178
+ } else {
179
+ const rc = rayMap.get(y * width + x);
180
+
181
+ if (rc) {
182
+ if (rc.u < 0.45) {
183
+ ch = '▓';
184
+ } else if (rc.u < 0.8) {
185
+ ch = '▒';
186
+ next = 'mid';
187
+ } else {
188
+ ch = '░';
189
+ next = 'faint';
190
+ }
191
+
192
+ if (rc.b > 0.8) {
193
+ next = 'hot';
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ if (next === cls) {
200
+ buf += ch;
201
+ } else {
202
+ flush();
203
+ cls = next;
204
+ buf = ch;
205
+ }
206
+ }
207
+
208
+ flush();
209
+ out.push(segs);
210
+ }
211
+
212
+ return out;
213
+ });
214
+ </script>
215
+
216
+ <Graph {title} class={className}>
217
+ <GraphBody>
218
+ <div class="surya">
219
+ <div class="viewport">
220
+ <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>
221
+ </div>
222
+ {#if label}
223
+ <p class="caption">{label}</p>
224
+ {/if}
225
+ </div>
226
+ </GraphBody>
227
+ </Graph>
228
+
229
+ <style>
230
+ .surya {
231
+ display: flex;
232
+ flex-direction: column;
233
+ gap: 1rem;
234
+ min-width: 0;
235
+ font-size: 0.875rem;
236
+ }
237
+
238
+ .viewport {
239
+ overflow-x: auto;
240
+ }
241
+
242
+ .art {
243
+ margin: 0;
244
+ font-size: 0.85rem;
245
+ line-height: 1.15;
246
+ color: var(--graph-foreground, oklch(0.93 0 0));
247
+ white-space: pre;
248
+ }
249
+
250
+ .hot {
251
+ color: var(--graph-accent, oklch(0.78 0.17 155));
252
+ font-weight: 600;
253
+ }
254
+
255
+ .mid {
256
+ color: var(--graph-muted, oklch(0.62 0 0));
257
+ }
258
+
259
+ .faint {
260
+ color: var(--graph-faint, oklch(0.3 0 0));
261
+ }
262
+
263
+ .caption {
264
+ margin: 0;
265
+ font-size: 0.8rem;
266
+ color: var(--graph-muted, oklch(0.62 0 0));
267
+ }
268
+ </style>
@@ -0,0 +1,22 @@
1
+ export interface GraphSuryaProps {
2
+ /** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
3
+ title: string;
4
+ /** Animate the day cycle. Default true. Reduced motion freezes a mid-morning frame. */
5
+ animated?: boolean;
6
+ /** Width of the scene in characters. Default 60. */
7
+ cols?: number;
8
+ /** Height of the scene in rows. Default 18. */
9
+ rows?: number;
10
+ /** Milliseconds per tick. Default 120. */
11
+ speedMs?: number;
12
+ /** Number of spokes on the wheel. Default 12. */
13
+ rays?: number;
14
+ /** Seed for the per-spoke length jitter. Default 5. */
15
+ seedNum?: number;
16
+ /** Short caption under the art; the art itself is decorative. */
17
+ label?: string;
18
+ class?: string;
19
+ }
20
+ declare const GraphSurya: import("svelte").Component<GraphSuryaProps, {}, "">;
21
+ type GraphSurya = ReturnType<typeof GraphSurya>;
22
+ export default GraphSurya;