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.
- package/LICENSE.txt +21 -0
- package/README.md +154 -0
- package/dist/animated/GraphAgni.svelte +430 -0
- package/dist/animated/GraphAgni.svelte.d.ts +22 -0
- package/dist/animated/GraphAum.svelte +294 -0
- package/dist/animated/GraphAum.svelte.d.ts +20 -0
- package/dist/animated/GraphBoot.svelte +325 -0
- package/dist/animated/GraphBoot.svelte.d.ts +28 -0
- package/dist/animated/GraphCron.svelte +476 -0
- package/dist/animated/GraphCron.svelte.d.ts +42 -0
- package/dist/animated/GraphDamru.svelte +341 -0
- package/dist/animated/GraphDamru.svelte.d.ts +18 -0
- package/dist/animated/GraphDeps.svelte +245 -0
- package/dist/animated/GraphDeps.svelte.d.ts +29 -0
- package/dist/animated/GraphDiya.svelte +330 -0
- package/dist/animated/GraphDiya.svelte.d.ts +22 -0
- package/dist/animated/GraphFire.svelte +246 -0
- package/dist/animated/GraphFire.svelte.d.ts +22 -0
- package/dist/animated/GraphFlame.svelte +258 -0
- package/dist/animated/GraphFlame.svelte.d.ts +14 -0
- package/dist/animated/GraphFlowPlayer.svelte +346 -0
- package/dist/animated/GraphFlowPlayer.svelte.d.ts +27 -0
- package/dist/animated/GraphGanga.svelte +305 -0
- package/dist/animated/GraphGanga.svelte.d.ts +20 -0
- package/dist/animated/GraphHash.svelte +251 -0
- package/dist/animated/GraphHash.svelte.d.ts +20 -0
- package/dist/animated/GraphJapa.svelte +253 -0
- package/dist/animated/GraphJapa.svelte.d.ts +20 -0
- package/dist/animated/GraphLife.svelte +312 -0
- package/dist/animated/GraphLife.svelte.d.ts +27 -0
- package/dist/animated/GraphMandala.svelte +262 -0
- package/dist/animated/GraphMandala.svelte.d.ts +24 -0
- package/dist/animated/GraphMandel.svelte +290 -0
- package/dist/animated/GraphMandel.svelte.d.ts +27 -0
- package/dist/animated/GraphPulse.svelte +248 -0
- package/dist/animated/GraphPulse.svelte.d.ts +24 -0
- package/dist/animated/GraphRain.svelte +280 -0
- package/dist/animated/GraphRain.svelte.d.ts +22 -0
- package/dist/animated/GraphScatter.svelte +395 -0
- package/dist/animated/GraphScatter.svelte.d.ts +31 -0
- package/dist/animated/GraphScope.svelte +197 -0
- package/dist/animated/GraphScope.svelte.d.ts +22 -0
- package/dist/animated/GraphSequence.svelte +247 -0
- package/dist/animated/GraphSequence.svelte.d.ts +18 -0
- package/dist/animated/GraphSpinners.svelte +109 -0
- package/dist/animated/GraphSpinners.svelte.d.ts +22 -0
- package/dist/animated/GraphState.svelte +270 -0
- package/dist/animated/GraphState.svelte.d.ts +14 -0
- package/dist/animated/GraphStream.svelte +223 -0
- package/dist/animated/GraphStream.svelte.d.ts +24 -0
- package/dist/animated/GraphSurya.svelte +268 -0
- package/dist/animated/GraphSurya.svelte.d.ts +22 -0
- package/dist/animated/GraphTerminal.svelte +238 -0
- package/dist/animated/GraphTerminal.svelte.d.ts +28 -0
- package/dist/animated/GraphTicker.svelte +163 -0
- package/dist/animated/GraphTicker.svelte.d.ts +21 -0
- package/dist/animated/GraphTypewriter.svelte +130 -0
- package/dist/animated/GraphTypewriter.svelte.d.ts +22 -0
- package/dist/animated/GraphWorkflow.svelte +570 -0
- package/dist/animated/GraphWorkflow.svelte.d.ts +29 -0
- package/dist/assets/favicon.svg +21 -0
- package/dist/diagram/AmplifierDiagram.svelte +206 -0
- package/dist/diagram/AmplifierDiagram.svelte.d.ts +12 -0
- package/dist/diagram/AsciiDiagram.svelte +212 -0
- package/dist/diagram/AsciiDiagram.svelte.d.ts +24 -0
- package/dist/diagram/DiagramEditor.svelte +414 -0
- package/dist/diagram/DiagramEditor.svelte.d.ts +3 -0
- package/dist/diagram/MetricsTableDiagram.svelte +125 -0
- package/dist/diagram/MetricsTableDiagram.svelte.d.ts +18 -0
- package/dist/diagram/NestedRadiiDiagram.svelte +218 -0
- package/dist/diagram/NestedRadiiDiagram.svelte.d.ts +12 -0
- package/dist/diagram/PromptLoopDiagram.svelte +331 -0
- package/dist/diagram/PromptLoopDiagram.svelte.d.ts +12 -0
- package/dist/diagram/ascii.d.ts +35 -0
- package/dist/diagram/ascii.js +83 -0
- package/dist/diagram/parser.d.ts +16 -0
- package/dist/diagram/parser.js +71 -0
- package/dist/frame/Graph.svelte +141 -0
- package/dist/frame/Graph.svelte.d.ts +14 -0
- package/dist/frame/GraphArrow.svelte +49 -0
- package/dist/frame/GraphArrow.svelte.d.ts +9 -0
- package/dist/frame/GraphBody.svelte +39 -0
- package/dist/frame/GraphBody.svelte.d.ts +10 -0
- package/dist/frame/GraphRule.svelte +21 -0
- package/dist/frame/GraphRule.svelte.d.ts +6 -0
- package/dist/frame/GraphTick.svelte +23 -0
- package/dist/frame/GraphTick.svelte.d.ts +9 -0
- package/dist/frame/GraphTrack.svelte +29 -0
- package/dist/frame/GraphTrack.svelte.d.ts +14 -0
- package/dist/frame/clock.d.ts +10 -0
- package/dist/frame/clock.js +62 -0
- package/dist/frame/glyphs.d.ts +22 -0
- package/dist/frame/glyphs.js +53 -0
- package/dist/frame/motion.css +27 -0
- package/dist/frame/motion.d.ts +24 -0
- package/dist/frame/motion.js +47 -0
- package/dist/frame/tone.d.ts +11 -0
- package/dist/frame/tone.js +64 -0
- package/dist/graphs/GraphActivity.svelte +387 -0
- package/dist/graphs/GraphActivity.svelte.d.ts +22 -0
- package/dist/graphs/GraphBars.svelte +194 -0
- package/dist/graphs/GraphBars.svelte.d.ts +20 -0
- package/dist/graphs/GraphBullet.svelte +208 -0
- package/dist/graphs/GraphBullet.svelte.d.ts +22 -0
- package/dist/graphs/GraphCalendar.svelte +203 -0
- package/dist/graphs/GraphCalendar.svelte.d.ts +20 -0
- package/dist/graphs/GraphCells.svelte +181 -0
- package/dist/graphs/GraphCells.svelte.d.ts +18 -0
- package/dist/graphs/GraphCompare.svelte +192 -0
- package/dist/graphs/GraphCompare.svelte.d.ts +18 -0
- package/dist/graphs/GraphCountdown.svelte +96 -0
- package/dist/graphs/GraphCountdown.svelte.d.ts +14 -0
- package/dist/graphs/GraphDiff.svelte +181 -0
- package/dist/graphs/GraphDiff.svelte.d.ts +18 -0
- package/dist/graphs/GraphFlow.svelte +137 -0
- package/dist/graphs/GraphFlow.svelte.d.ts +21 -0
- package/dist/graphs/GraphFunnel.svelte +190 -0
- package/dist/graphs/GraphFunnel.svelte.d.ts +21 -0
- package/dist/graphs/GraphGantt.svelte +258 -0
- package/dist/graphs/GraphGantt.svelte.d.ts +25 -0
- package/dist/graphs/GraphHeatmap.svelte +287 -0
- package/dist/graphs/GraphHeatmap.svelte.d.ts +22 -0
- package/dist/graphs/GraphInvoice.svelte +294 -0
- package/dist/graphs/GraphInvoice.svelte.d.ts +34 -0
- package/dist/graphs/GraphKpi.svelte +166 -0
- package/dist/graphs/GraphKpi.svelte.d.ts +17 -0
- package/dist/graphs/GraphLatency.svelte +340 -0
- package/dist/graphs/GraphLatency.svelte.d.ts +28 -0
- package/dist/graphs/GraphMeter.svelte +121 -0
- package/dist/graphs/GraphMeter.svelte.d.ts +16 -0
- package/dist/graphs/GraphPlot.svelte +226 -0
- package/dist/graphs/GraphPlot.svelte.d.ts +18 -0
- package/dist/graphs/GraphQuota.svelte +303 -0
- package/dist/graphs/GraphQuota.svelte.d.ts +27 -0
- package/dist/graphs/GraphRank.svelte +141 -0
- package/dist/graphs/GraphRank.svelte.d.ts +21 -0
- package/dist/graphs/GraphSlo.svelte +325 -0
- package/dist/graphs/GraphSlo.svelte.d.ts +31 -0
- package/dist/graphs/GraphSlope.svelte +197 -0
- package/dist/graphs/GraphSlope.svelte.d.ts +19 -0
- package/dist/graphs/GraphSpark.svelte +125 -0
- package/dist/graphs/GraphSpark.svelte.d.ts +15 -0
- package/dist/graphs/GraphSpec.svelte +69 -0
- package/dist/graphs/GraphSpec.svelte.d.ts +14 -0
- package/dist/graphs/GraphStack.svelte +266 -0
- package/dist/graphs/GraphStack.svelte.d.ts +25 -0
- package/dist/graphs/GraphStat.svelte +103 -0
- package/dist/graphs/GraphStat.svelte.d.ts +16 -0
- package/dist/graphs/GraphTable.svelte +133 -0
- package/dist/graphs/GraphTable.svelte.d.ts +15 -0
- package/dist/graphs/GraphTimeline.svelte +164 -0
- package/dist/graphs/GraphTimeline.svelte.d.ts +18 -0
- package/dist/graphs/GraphTimer.svelte +117 -0
- package/dist/graphs/GraphTimer.svelte.d.ts +15 -0
- package/dist/graphs/GraphTree.svelte +147 -0
- package/dist/graphs/GraphTree.svelte.d.ts +16 -0
- package/dist/graphs/GraphUptime.svelte +210 -0
- package/dist/graphs/GraphUptime.svelte.d.ts +18 -0
- package/dist/graphs/GraphWaffle.svelte +131 -0
- package/dist/graphs/GraphWaffle.svelte.d.ts +17 -0
- package/dist/graphs/GraphWaterfall.svelte +258 -0
- package/dist/graphs/GraphWaterfall.svelte.d.ts +22 -0
- package/dist/index.d.ts +147 -0
- package/dist/index.js +87 -0
- package/dist/themes.css +54 -0
- package/package.json +67 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface GraphDiyaProps {
|
|
3
|
+
/** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
|
|
4
|
+
title: string;
|
|
5
|
+
/** Animate the lighting and the breathing. Default true. Reduced motion freezes a fully lit row. */
|
|
6
|
+
animated?: boolean;
|
|
7
|
+
/** Width of the scene in characters. Default 60. */
|
|
8
|
+
cols?: number;
|
|
9
|
+
/** Height of the scene in rows. Default 14. */
|
|
10
|
+
rows?: number;
|
|
11
|
+
/** Number of diyas in the row. Default 5. */
|
|
12
|
+
lamps?: number;
|
|
13
|
+
/** Milliseconds per tick. Default 110. */
|
|
14
|
+
speedMs?: number;
|
|
15
|
+
/** Seed for the per-lamp phase and gutter schedule. Default 13. */
|
|
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 lighting. */
|
|
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 = 14,
|
|
50
|
+
lamps = 5,
|
|
51
|
+
speedMs = 110,
|
|
52
|
+
seedNum = 13,
|
|
53
|
+
label = 'row of light',
|
|
54
|
+
class: className = ''
|
|
55
|
+
}: GraphDiyaProps = $props();
|
|
56
|
+
|
|
57
|
+
// svelte-ignore state_referenced_locally
|
|
58
|
+
const width = Math.max(24, cols);
|
|
59
|
+
// svelte-ignore state_referenced_locally
|
|
60
|
+
const height = Math.max(10, rows);
|
|
61
|
+
// svelte-ignore state_referenced_locally
|
|
62
|
+
const N = Math.max(1, Math.min(11, Math.round(lamps)));
|
|
63
|
+
|
|
64
|
+
// svelte-ignore state_referenced_locally
|
|
65
|
+
const rand = mulberry32(seedNum);
|
|
66
|
+
|
|
67
|
+
// Spread lamps evenly across the row.
|
|
68
|
+
const margin = 4;
|
|
69
|
+
const usable = width - margin * 2;
|
|
70
|
+
|
|
71
|
+
// svelte-ignore state_referenced_locally
|
|
72
|
+
const lampCenters: number[] = (() => {
|
|
73
|
+
const out: number[] = [];
|
|
74
|
+
|
|
75
|
+
if (N === 1) {
|
|
76
|
+
out.push(Math.floor(width / 2));
|
|
77
|
+
} else {
|
|
78
|
+
for (let i = 0; i < N; i++) {
|
|
79
|
+
out.push(margin + Math.round((i * usable) / (N - 1)));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return out;
|
|
84
|
+
})();
|
|
85
|
+
|
|
86
|
+
const litTicks = 8;
|
|
87
|
+
const sequenceTicks = N * litTicks;
|
|
88
|
+
const gutterEvery = 40;
|
|
89
|
+
|
|
90
|
+
// svelte-ignore state_referenced_locally
|
|
91
|
+
const lampPhase: number[] = Array.from({ length: N }, () => rand());
|
|
92
|
+
|
|
93
|
+
// Gutter schedule: each lamp has its own list of (start, end) gutter intervals.
|
|
94
|
+
// svelte-ignore state_referenced_locally
|
|
95
|
+
const gutterSchedule: { start: number; end: number }[][] = Array.from(
|
|
96
|
+
{ length: N },
|
|
97
|
+
() => {
|
|
98
|
+
const out: { start: number; end: number }[] = [];
|
|
99
|
+
let next = sequenceTicks + 16 + Math.floor(rand() * 24);
|
|
100
|
+
|
|
101
|
+
while (next < 1000) {
|
|
102
|
+
out.push({ start: next, end: next + 6 });
|
|
103
|
+
next += gutterEvery + Math.floor(rand() * 28);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
function flameIntensity(i: number, tick: number): number {
|
|
111
|
+
const litAt = i * litTicks;
|
|
112
|
+
if (tick < litAt) {
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Lighting phase
|
|
117
|
+
if (tick < litAt + litTicks) {
|
|
118
|
+
return (tick - litAt) / litTicks;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Gutter / relight
|
|
122
|
+
for (const g of gutterSchedule[i]!) {
|
|
123
|
+
if (tick >= g.start && tick < g.end) {
|
|
124
|
+
return Math.max(0, 1 - (tick - g.start) / (g.end - g.start));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (tick >= g.end && tick < g.end + litTicks) {
|
|
128
|
+
return (tick - g.end) / litTicks;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Breathing — kept tight so the static frame still reads all-lit.
|
|
133
|
+
return 0.92 + 0.08 * Math.sin(tick / 3 + lampPhase[i]! * 6.2832);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Pick the cell glyph and class for a single flame slot.
|
|
137
|
+
// row 0 = top tip, row 1 = mid, row 2 = base.
|
|
138
|
+
function flameSlot(row: 0 | 1 | 2, intensity: number): { ch: string; cls: string | undefined } {
|
|
139
|
+
if (intensity <= 0) {
|
|
140
|
+
return { ch: ' ', cls: undefined };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (row === 0) {
|
|
144
|
+
if (intensity < 0.55) {
|
|
145
|
+
return { ch: '·', cls: 'faint' };
|
|
146
|
+
}
|
|
147
|
+
return { ch: '░', cls: 'faint' };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (row === 1) {
|
|
151
|
+
return { ch: '▒', cls: 'mid' };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// row 2: hot base
|
|
155
|
+
if (intensity < 0.7) {
|
|
156
|
+
return { ch: '▓', cls: 'mid' };
|
|
157
|
+
}
|
|
158
|
+
return { ch: '█', cls: 'hot' };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// svelte-ignore state_referenced_locally
|
|
162
|
+
const initialTick = animated ? sequenceTicks : sequenceTicks;
|
|
163
|
+
|
|
164
|
+
// svelte-ignore state_referenced_locally
|
|
165
|
+
let tick = $state(initialTick);
|
|
166
|
+
|
|
167
|
+
const moving = $derived(
|
|
168
|
+
animated &&
|
|
169
|
+
(typeof window === 'undefined' ||
|
|
170
|
+
!window.matchMedia('(prefers-reduced-motion: reduce)').matches)
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
$effect(() => {
|
|
174
|
+
if (!moving) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const timer = window.setInterval(() => {
|
|
179
|
+
tick += 1;
|
|
180
|
+
}, speedMs);
|
|
181
|
+
|
|
182
|
+
return () => window.clearInterval(timer);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const view = $derived.by((): Seg[][] => {
|
|
186
|
+
const out: Seg[][] = [];
|
|
187
|
+
const intensities: number[] = Array.from({ length: N }, (_, i) =>
|
|
188
|
+
flameIntensity(i, tick)
|
|
189
|
+
);
|
|
190
|
+
const baseY = height - 1;
|
|
191
|
+
const wickY = baseY - 1;
|
|
192
|
+
const flameBaseY = wickY - 1; // row 2 of flame
|
|
193
|
+
const flameMidY = wickY - 2; // row 1
|
|
194
|
+
const flameTopY = wickY - 3; // row 0 (tip)
|
|
195
|
+
|
|
196
|
+
for (let y = 0; y < height; y++) {
|
|
197
|
+
const segs: Seg[] = [];
|
|
198
|
+
let buf = '';
|
|
199
|
+
let cls: string | undefined;
|
|
200
|
+
|
|
201
|
+
const flush = () => {
|
|
202
|
+
if (buf.length > 0) {
|
|
203
|
+
segs.push({ text: buf, cls });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
buf = '';
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
for (let x = 0; x < width; x++) {
|
|
210
|
+
let ch = ' ';
|
|
211
|
+
let next: string | undefined;
|
|
212
|
+
|
|
213
|
+
for (let i = 0; i < N; i++) {
|
|
214
|
+
const cx = lampCenters[i]!;
|
|
215
|
+
const inten = intensities[i]!;
|
|
216
|
+
|
|
217
|
+
if (y === baseY) {
|
|
218
|
+
if (x >= cx - 1 && x <= cx + 1) {
|
|
219
|
+
ch = '▀';
|
|
220
|
+
next = 'mid';
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
} else if (y === wickY) {
|
|
224
|
+
if (x === cx) {
|
|
225
|
+
ch = '║';
|
|
226
|
+
next = 'mid';
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
} else if (y === flameBaseY) {
|
|
230
|
+
// 3-wide base
|
|
231
|
+
if (x === cx) {
|
|
232
|
+
const f = flameSlot(2, inten);
|
|
233
|
+
ch = f.ch;
|
|
234
|
+
next = f.cls;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
if (x === cx - 1 || x === cx + 1) {
|
|
238
|
+
const f = flameSlot(2, inten);
|
|
239
|
+
ch = f.ch;
|
|
240
|
+
next = f.cls;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
} else if (y === flameMidY) {
|
|
244
|
+
// 3-wide mid
|
|
245
|
+
if (x >= cx - 1 && x <= cx + 1) {
|
|
246
|
+
const f = flameSlot(1, inten);
|
|
247
|
+
ch = f.ch;
|
|
248
|
+
next = f.cls;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
} else if (y === flameTopY) {
|
|
252
|
+
if (x === cx) {
|
|
253
|
+
const f = flameSlot(0, inten);
|
|
254
|
+
ch = f.ch;
|
|
255
|
+
next = f.cls;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (next === cls) {
|
|
262
|
+
buf += ch;
|
|
263
|
+
} else {
|
|
264
|
+
flush();
|
|
265
|
+
cls = next;
|
|
266
|
+
buf = ch;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
flush();
|
|
271
|
+
out.push(segs);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return out;
|
|
275
|
+
});
|
|
276
|
+
</script>
|
|
277
|
+
|
|
278
|
+
<Graph {title} class={className}>
|
|
279
|
+
<GraphBody>
|
|
280
|
+
<div class="diya">
|
|
281
|
+
<div class="viewport">
|
|
282
|
+
<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>
|
|
283
|
+
</div>
|
|
284
|
+
{#if label}
|
|
285
|
+
<p class="caption">{label}</p>
|
|
286
|
+
{/if}
|
|
287
|
+
</div>
|
|
288
|
+
</GraphBody>
|
|
289
|
+
</Graph>
|
|
290
|
+
|
|
291
|
+
<style>
|
|
292
|
+
.diya {
|
|
293
|
+
display: flex;
|
|
294
|
+
flex-direction: column;
|
|
295
|
+
gap: 1rem;
|
|
296
|
+
min-width: 0;
|
|
297
|
+
font-size: 0.875rem;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.viewport {
|
|
301
|
+
overflow-x: auto;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.art {
|
|
305
|
+
margin: 0;
|
|
306
|
+
font-size: 0.85rem;
|
|
307
|
+
line-height: 1.15;
|
|
308
|
+
color: var(--graph-foreground, oklch(0.93 0 0));
|
|
309
|
+
white-space: pre;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.hot {
|
|
313
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
314
|
+
font-weight: 600;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.mid {
|
|
318
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.faint {
|
|
322
|
+
color: var(--graph-faint, oklch(0.3 0 0));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.caption {
|
|
326
|
+
margin: 0;
|
|
327
|
+
font-size: 0.8rem;
|
|
328
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
329
|
+
}
|
|
330
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface GraphDiyaProps {
|
|
2
|
+
/** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
|
|
3
|
+
title: string;
|
|
4
|
+
/** Animate the lighting and the breathing. Default true. Reduced motion freezes a fully lit row. */
|
|
5
|
+
animated?: boolean;
|
|
6
|
+
/** Width of the scene in characters. Default 60. */
|
|
7
|
+
cols?: number;
|
|
8
|
+
/** Height of the scene in rows. Default 14. */
|
|
9
|
+
rows?: number;
|
|
10
|
+
/** Number of diyas in the row. Default 5. */
|
|
11
|
+
lamps?: number;
|
|
12
|
+
/** Milliseconds per tick. Default 110. */
|
|
13
|
+
speedMs?: number;
|
|
14
|
+
/** Seed for the per-lamp phase and gutter schedule. Default 13. */
|
|
15
|
+
seedNum?: number;
|
|
16
|
+
/** Short caption under the art; the art itself is decorative. */
|
|
17
|
+
label?: string;
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
declare const GraphDiya: import("svelte").Component<GraphDiyaProps, {}, "">;
|
|
21
|
+
type GraphDiya = ReturnType<typeof GraphDiya>;
|
|
22
|
+
export default GraphDiya;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface GraphFireProps {
|
|
3
|
+
/** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
|
|
4
|
+
title: string;
|
|
5
|
+
/** Animate the burn. Default true. Reduced motion freezes a mid-burn frame. */
|
|
6
|
+
animated?: boolean;
|
|
7
|
+
/** Width of the fire in characters. Default 60. */
|
|
8
|
+
cols?: number;
|
|
9
|
+
/** Height of the fire in rows. Default 14. */
|
|
10
|
+
rows?: number;
|
|
11
|
+
/** Milliseconds per simulation step. Default 90. */
|
|
12
|
+
speedMs?: number;
|
|
13
|
+
/** Random decay strength; higher burns out sooner. Default 0.6. */
|
|
14
|
+
cooling?: number;
|
|
15
|
+
/** Seed for the reproducible burn. Default 3. */
|
|
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 burn. */
|
|
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
|
+
/** One-time reduced-motion check, for choosing the opening frame only. */
|
|
36
|
+
function prefersReduced(): boolean {
|
|
37
|
+
return (
|
|
38
|
+
typeof window !== 'undefined' &&
|
|
39
|
+
window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface Seg {
|
|
44
|
+
text: string;
|
|
45
|
+
cls?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const HOT = 0.75;
|
|
49
|
+
const MID = 0.5;
|
|
50
|
+
const LOW = 0.3;
|
|
51
|
+
const EMBER = 0.12;
|
|
52
|
+
|
|
53
|
+
/** Run-length encode one row of heat into classed segments, never per-cell spans. */
|
|
54
|
+
function heatSegs(row: number[]): Seg[] {
|
|
55
|
+
const segs: Seg[] = [];
|
|
56
|
+
let buf = '';
|
|
57
|
+
let cls: string | undefined;
|
|
58
|
+
|
|
59
|
+
for (const heat of row) {
|
|
60
|
+
let ch = ' ';
|
|
61
|
+
let next: string | undefined;
|
|
62
|
+
|
|
63
|
+
if (heat >= HOT) {
|
|
64
|
+
ch = '█';
|
|
65
|
+
next = 'hot';
|
|
66
|
+
} else if (heat >= MID) {
|
|
67
|
+
ch = '▓';
|
|
68
|
+
} else if (heat >= LOW) {
|
|
69
|
+
ch = '▒';
|
|
70
|
+
next = 'mid';
|
|
71
|
+
} else if (heat >= EMBER) {
|
|
72
|
+
ch = '░';
|
|
73
|
+
next = 'faint';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (next === cls) {
|
|
77
|
+
buf += ch;
|
|
78
|
+
} else {
|
|
79
|
+
if (buf.length > 0) {
|
|
80
|
+
segs.push({ text: buf, cls });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
cls = next;
|
|
84
|
+
buf = ch;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (buf.length > 0) {
|
|
89
|
+
segs.push({ text: buf, cls });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return segs;
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<script lang="ts">
|
|
97
|
+
import Graph from '../frame/Graph.svelte';
|
|
98
|
+
import GraphBody from '../frame/GraphBody.svelte';
|
|
99
|
+
|
|
100
|
+
let {
|
|
101
|
+
title,
|
|
102
|
+
animated = true,
|
|
103
|
+
cols = 60,
|
|
104
|
+
rows = 14,
|
|
105
|
+
speedMs = 90,
|
|
106
|
+
cooling = 0.6,
|
|
107
|
+
seedNum = 3,
|
|
108
|
+
label = 'simulated fire',
|
|
109
|
+
class: className = ''
|
|
110
|
+
}: GraphFireProps = $props();
|
|
111
|
+
|
|
112
|
+
// svelte-ignore state_referenced_locally
|
|
113
|
+
const rand = mulberry32(seedNum);
|
|
114
|
+
// svelte-ignore state_referenced_locally
|
|
115
|
+
const width = Math.max(1, cols);
|
|
116
|
+
// svelte-ignore state_referenced_locally
|
|
117
|
+
const height = Math.max(1, rows);
|
|
118
|
+
|
|
119
|
+
function initialGrid(): number[][] {
|
|
120
|
+
const grid: number[][] = [];
|
|
121
|
+
|
|
122
|
+
for (let y = 0; y < height; y++) {
|
|
123
|
+
grid.push(new Array<number>(width).fill(0));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (let x = 0; x < width; x++) {
|
|
127
|
+
grid[height - 1][x] = 1;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return grid;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Each cell takes heat from below minus random cooling; the base row stays max. */
|
|
134
|
+
function stepFire(grid: number[][]): number[][] {
|
|
135
|
+
const next: number[][] = [];
|
|
136
|
+
|
|
137
|
+
for (let y = 0; y < height; y++) {
|
|
138
|
+
next.push(new Array<number>(width).fill(0));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (let y = 0; y < height - 1; y++) {
|
|
142
|
+
for (let x = 0; x < width; x++) {
|
|
143
|
+
const below =
|
|
144
|
+
(grid[y + 1][(x - 1 + width) % width] +
|
|
145
|
+
grid[y + 1][x] +
|
|
146
|
+
grid[y + 1][(x + 1) % width]) /
|
|
147
|
+
3;
|
|
148
|
+
|
|
149
|
+
next[y][x] = Math.max(0, below * (1 - rand() * cooling * 0.5));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
for (let x = 0; x < width; x++) {
|
|
154
|
+
next[height - 1][x] = 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return next;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const moving = $derived(
|
|
161
|
+
animated &&
|
|
162
|
+
(typeof window === 'undefined' ||
|
|
163
|
+
!window.matchMedia('(prefers-reduced-motion: reduce)').matches)
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
let sim = initialGrid();
|
|
167
|
+
|
|
168
|
+
// svelte-ignore state_referenced_locally
|
|
169
|
+
const preTicks = animated && !prefersReduced() ? 4 : height * 2 + 6;
|
|
170
|
+
|
|
171
|
+
// Just lit when animated; a developed mid-burn when frozen.
|
|
172
|
+
for (let i = 0; i < preTicks; i++) {
|
|
173
|
+
sim = stepFire(sim);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// svelte-ignore state_referenced_locally
|
|
177
|
+
let grid = $state<number[][]>(sim);
|
|
178
|
+
|
|
179
|
+
$effect(() => {
|
|
180
|
+
if (!moving) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const timer = window.setInterval(() => {
|
|
185
|
+
grid = stepFire(grid);
|
|
186
|
+
}, speedMs);
|
|
187
|
+
|
|
188
|
+
return () => window.clearInterval(timer);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const view = $derived.by((): Seg[][] => grid.map((row) => heatSegs(row)));
|
|
192
|
+
</script>
|
|
193
|
+
|
|
194
|
+
<Graph {title} class={className}>
|
|
195
|
+
<GraphBody>
|
|
196
|
+
<div class="fire">
|
|
197
|
+
<div class="viewport">
|
|
198
|
+
<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>
|
|
199
|
+
</div>
|
|
200
|
+
{#if label}
|
|
201
|
+
<p class="caption">{label}</p>
|
|
202
|
+
{/if}
|
|
203
|
+
</div>
|
|
204
|
+
</GraphBody>
|
|
205
|
+
</Graph>
|
|
206
|
+
|
|
207
|
+
<style>
|
|
208
|
+
.fire {
|
|
209
|
+
display: flex;
|
|
210
|
+
flex-direction: column;
|
|
211
|
+
gap: 1rem;
|
|
212
|
+
min-width: 0;
|
|
213
|
+
font-size: 0.875rem;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.viewport {
|
|
217
|
+
overflow-x: auto;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.art {
|
|
221
|
+
margin: 0;
|
|
222
|
+
font-size: 0.85rem;
|
|
223
|
+
line-height: 1.15;
|
|
224
|
+
color: var(--graph-foreground, oklch(0.93 0 0));
|
|
225
|
+
white-space: pre;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.hot {
|
|
229
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
230
|
+
font-weight: 600;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.mid {
|
|
234
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.faint {
|
|
238
|
+
color: var(--graph-faint, oklch(0.3 0 0));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.caption {
|
|
242
|
+
margin: 0;
|
|
243
|
+
font-size: 0.8rem;
|
|
244
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
245
|
+
}
|
|
246
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface GraphFireProps {
|
|
2
|
+
/** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
|
|
3
|
+
title: string;
|
|
4
|
+
/** Animate the burn. Default true. Reduced motion freezes a mid-burn frame. */
|
|
5
|
+
animated?: boolean;
|
|
6
|
+
/** Width of the fire in characters. Default 60. */
|
|
7
|
+
cols?: number;
|
|
8
|
+
/** Height of the fire in rows. Default 14. */
|
|
9
|
+
rows?: number;
|
|
10
|
+
/** Milliseconds per simulation step. Default 90. */
|
|
11
|
+
speedMs?: number;
|
|
12
|
+
/** Random decay strength; higher burns out sooner. Default 0.6. */
|
|
13
|
+
cooling?: number;
|
|
14
|
+
/** Seed for the reproducible burn. Default 3. */
|
|
15
|
+
seedNum?: number;
|
|
16
|
+
/** Short caption under the art; the art itself is decorative. */
|
|
17
|
+
label?: string;
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
declare const GraphFire: import("svelte").Component<GraphFireProps, {}, "">;
|
|
21
|
+
type GraphFire = ReturnType<typeof GraphFire>;
|
|
22
|
+
export default GraphFire;
|