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,305 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface GraphGangaProps {
|
|
3
|
+
/** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
|
|
4
|
+
title: string;
|
|
5
|
+
/** Animate the flow and the floating diya. Default true. Reduced motion freezes a frame mid-flow. */
|
|
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 110. */
|
|
12
|
+
speedMs?: number;
|
|
13
|
+
/** Seed for the flow pattern and the diya's vertical bob. Default 17. */
|
|
14
|
+
seedNum?: number;
|
|
15
|
+
/** Short caption under the art; the art itself is decorative. */
|
|
16
|
+
label?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Deterministic mulberry32 PRNG so a seed replays the same flow. */
|
|
21
|
+
function mulberry32(seed: number): () => number {
|
|
22
|
+
let a = seed >>> 0;
|
|
23
|
+
|
|
24
|
+
return () => {
|
|
25
|
+
a = (a + 0x6d2b79f5) | 0;
|
|
26
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
27
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
28
|
+
|
|
29
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface Seg {
|
|
34
|
+
text: string;
|
|
35
|
+
cls?: string;
|
|
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
|
+
animated = true,
|
|
46
|
+
cols = 60,
|
|
47
|
+
rows = 18,
|
|
48
|
+
speedMs = 110,
|
|
49
|
+
seedNum = 17,
|
|
50
|
+
label = 'the descent',
|
|
51
|
+
class: className = ''
|
|
52
|
+
}: GraphGangaProps = $props();
|
|
53
|
+
|
|
54
|
+
// svelte-ignore state_referenced_locally
|
|
55
|
+
const width = Math.max(28, cols);
|
|
56
|
+
// svelte-ignore state_referenced_locally
|
|
57
|
+
const height = Math.max(10, rows);
|
|
58
|
+
const aspect = 2.1;
|
|
59
|
+
|
|
60
|
+
// svelte-ignore state_referenced_locally
|
|
61
|
+
const rand = mulberry32(seedNum);
|
|
62
|
+
|
|
63
|
+
// Three flow lines, evenly spaced vertically.
|
|
64
|
+
const lineYs = [Math.floor(height * 0.25), Math.floor(height * 0.5), Math.floor(height * 0.75)];
|
|
65
|
+
|
|
66
|
+
// Each line is a stepped polyline. We build it once: horizontal segments
|
|
67
|
+
// separated by a 1-row step down. Steps are at the same x positions on every
|
|
68
|
+
// line (so the rivers cascade in a rhythm), but the starting y differs.
|
|
69
|
+
interface Line {
|
|
70
|
+
cells: { x: number; y: number; step: boolean }[];
|
|
71
|
+
startY: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// svelte-ignore state_referenced_locally
|
|
75
|
+
const lines: Line[] = lineYs.map((startY) => {
|
|
76
|
+
const cells: Line['cells'] = [];
|
|
77
|
+
const xStart = 3;
|
|
78
|
+
const xEnd = width - 3;
|
|
79
|
+
const numSteps = 2; // each line steps down twice
|
|
80
|
+
const segLen = Math.floor((xEnd - xStart) / (numSteps + 1));
|
|
81
|
+
let x = xStart;
|
|
82
|
+
let y = startY;
|
|
83
|
+
const maxY = Math.min(height - 2, startY + numSteps);
|
|
84
|
+
|
|
85
|
+
for (let s = 0; s <= numSteps; s++) {
|
|
86
|
+
// Horizontal segment
|
|
87
|
+
for (let dx = 0; dx < segLen && x + dx < xEnd; dx++) {
|
|
88
|
+
cells.push({ x: x + dx, y, step: false });
|
|
89
|
+
}
|
|
90
|
+
x += segLen;
|
|
91
|
+
|
|
92
|
+
// Step down (a diagonal cell + the cell one row below).
|
|
93
|
+
if (s < numSteps && y < maxY) {
|
|
94
|
+
cells.push({ x, y, step: true });
|
|
95
|
+
cells.push({ x, y: y + 1, step: true });
|
|
96
|
+
y += 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { cells, startY };
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// The diya rides the bottom line; its position advances with the tick.
|
|
104
|
+
// svelte-ignore state_referenced_locally
|
|
105
|
+
const diyaLine = lines[2]!; // bottom line
|
|
106
|
+
// svelte-ignore state_referenced_locally
|
|
107
|
+
const diyaPathLen = diyaLine.cells.filter((c) => !c.step).length;
|
|
108
|
+
// svelte-ignore state_referenced_locally
|
|
109
|
+
const diyaBobAmp = 0.35; // vertical drift in rows
|
|
110
|
+
// svelte-ignore state_referenced_locally
|
|
111
|
+
const diyaBobPhase = rand() * 6.2832;
|
|
112
|
+
|
|
113
|
+
// svelte-ignore state_referenced_locally
|
|
114
|
+
const initialTick = animated ? Math.floor(diyaPathLen * 0.3) : Math.floor(diyaPathLen * 0.5);
|
|
115
|
+
|
|
116
|
+
// svelte-ignore state_referenced_locally
|
|
117
|
+
let tick = $state(initialTick);
|
|
118
|
+
|
|
119
|
+
const moving = $derived(
|
|
120
|
+
animated &&
|
|
121
|
+
(typeof window === 'undefined' ||
|
|
122
|
+
!window.matchMedia('(prefers-reduced-motion: reduce)').matches)
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
$effect(() => {
|
|
126
|
+
if (!moving) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const timer = window.setInterval(() => {
|
|
131
|
+
tick += 1;
|
|
132
|
+
}, speedMs);
|
|
133
|
+
|
|
134
|
+
return () => window.clearInterval(timer);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Build a cell map for the diya's flame. The diya rides the bottom line at
|
|
138
|
+
// the cell whose path-index == tick mod diyaPathLen. The flame hovers above
|
|
139
|
+
// with a slow vertical bob.
|
|
140
|
+
const diyaRow = $derived(() => {
|
|
141
|
+
const idx = ((tick % diyaPathLen) + diyaPathLen) % diyaPathLen;
|
|
142
|
+
// Find the idx-th non-step cell along the bottom line.
|
|
143
|
+
let count = 0;
|
|
144
|
+
for (const c of diyaLine.cells) {
|
|
145
|
+
if (c.step) continue;
|
|
146
|
+
if (count === idx) return c;
|
|
147
|
+
count += 1;
|
|
148
|
+
}
|
|
149
|
+
return diyaLine.cells[0]!;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const view = $derived.by((): Seg[][] => {
|
|
153
|
+
// Per-line flow pattern: a moving wave so the water "flows" downstream.
|
|
154
|
+
function flowGlyph(lineIdx: number, pathIdx: number, tick: number): { ch: string; cls: string | undefined } {
|
|
155
|
+
// Phase offset per line so the three rivers don't move in lockstep.
|
|
156
|
+
const phase = (pathIdx * 0.35 - tick * 0.25 + lineIdx * 1.7) % (Math.PI * 2);
|
|
157
|
+
const v = 0.5 + 0.5 * Math.sin(phase);
|
|
158
|
+
if (v > 0.78) {
|
|
159
|
+
return { ch: '▓', cls: 'mid' };
|
|
160
|
+
}
|
|
161
|
+
if (v > 0.4) {
|
|
162
|
+
return { ch: '▒', cls: 'faint' };
|
|
163
|
+
}
|
|
164
|
+
return { ch: '░', cls: 'faint' };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Build a map of flow cells: key y*width+x -> glyph.
|
|
168
|
+
const flowMap = new Map<number, { ch: string; cls: string | undefined }>();
|
|
169
|
+
|
|
170
|
+
lines.forEach((line, lineIdx) => {
|
|
171
|
+
let pathIdx = 0;
|
|
172
|
+
for (const cell of line.cells) {
|
|
173
|
+
if (cell.step) {
|
|
174
|
+
// Step cell: a single `╲` glyph (faint) — the cascade stair.
|
|
175
|
+
const key = cell.y * width + cell.x;
|
|
176
|
+
flowMap.set(key, { ch: '╲', cls: 'faint' });
|
|
177
|
+
} else {
|
|
178
|
+
const g = flowGlyph(lineIdx, pathIdx, tick);
|
|
179
|
+
const key = cell.y * width + cell.x;
|
|
180
|
+
flowMap.set(key, g);
|
|
181
|
+
pathIdx += 1;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Diya position and flame cells.
|
|
187
|
+
const d = diyaRow();
|
|
188
|
+
const bob = Math.round(diyaBobAmp * Math.sin(tick * 0.18 + diyaBobPhase));
|
|
189
|
+
const dx = d.x;
|
|
190
|
+
const dy = d.y + bob; // can drift up by 1
|
|
191
|
+
const flame = [
|
|
192
|
+
{ x: dx, y: dy - 1, ch: '║', cls: 'mid' as string | undefined }, // wick
|
|
193
|
+
{ x: dx, y: dy - 2, ch: '▒', cls: 'faint' as string | undefined }, // mid
|
|
194
|
+
{ x: dx, y: dy - 3, ch: '░', cls: 'faint' as string | undefined } // tip
|
|
195
|
+
];
|
|
196
|
+
const hotFlame = { x: dx, y: dy - 2, ch: '▓', cls: 'mid' as string | undefined };
|
|
197
|
+
|
|
198
|
+
const out: Seg[][] = [];
|
|
199
|
+
|
|
200
|
+
for (let y = 0; y < height; y++) {
|
|
201
|
+
const segs: Seg[] = [];
|
|
202
|
+
let buf = '';
|
|
203
|
+
let cls: string | undefined;
|
|
204
|
+
|
|
205
|
+
const flush = () => {
|
|
206
|
+
if (buf.length > 0) {
|
|
207
|
+
segs.push({ text: buf, cls });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
buf = '';
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
for (let x = 0; x < width; x++) {
|
|
214
|
+
let ch = ' ';
|
|
215
|
+
let next: string | undefined;
|
|
216
|
+
|
|
217
|
+
const f = flowMap.get(y * width + x);
|
|
218
|
+
if (f) {
|
|
219
|
+
ch = f.ch;
|
|
220
|
+
next = f.cls;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Flame overrides flow where the diya sits.
|
|
224
|
+
for (const fc of flame) {
|
|
225
|
+
if (fc.x === x && fc.y === y) {
|
|
226
|
+
ch = fc.ch;
|
|
227
|
+
next = fc.cls;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (hotFlame.x === x && hotFlame.y === y) {
|
|
232
|
+
ch = hotFlame.ch;
|
|
233
|
+
next = 'hot';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (next === cls) {
|
|
237
|
+
buf += ch;
|
|
238
|
+
} else {
|
|
239
|
+
flush();
|
|
240
|
+
cls = next;
|
|
241
|
+
buf = ch;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
flush();
|
|
246
|
+
out.push(segs);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return out;
|
|
250
|
+
});
|
|
251
|
+
</script>
|
|
252
|
+
|
|
253
|
+
<Graph {title} class={className}>
|
|
254
|
+
<GraphBody>
|
|
255
|
+
<div class="ganga">
|
|
256
|
+
<div class="viewport">
|
|
257
|
+
<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>
|
|
258
|
+
</div>
|
|
259
|
+
{#if label}
|
|
260
|
+
<p class="caption">{label}</p>
|
|
261
|
+
{/if}
|
|
262
|
+
</div>
|
|
263
|
+
</GraphBody>
|
|
264
|
+
</Graph>
|
|
265
|
+
|
|
266
|
+
<style>
|
|
267
|
+
.ganga {
|
|
268
|
+
display: flex;
|
|
269
|
+
flex-direction: column;
|
|
270
|
+
gap: 1rem;
|
|
271
|
+
min-width: 0;
|
|
272
|
+
font-size: 0.875rem;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.viewport {
|
|
276
|
+
overflow-x: auto;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.art {
|
|
280
|
+
margin: 0;
|
|
281
|
+
font-size: 0.85rem;
|
|
282
|
+
line-height: 1.15;
|
|
283
|
+
color: var(--graph-foreground, oklch(0.93 0 0));
|
|
284
|
+
white-space: pre;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.hot {
|
|
288
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
289
|
+
font-weight: 600;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.mid {
|
|
293
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.faint {
|
|
297
|
+
color: var(--graph-faint, oklch(0.3 0 0));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.caption {
|
|
301
|
+
margin: 0;
|
|
302
|
+
font-size: 0.8rem;
|
|
303
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
304
|
+
}
|
|
305
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface GraphGangaProps {
|
|
2
|
+
/** Caption drawn on the top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
|
|
3
|
+
title: string;
|
|
4
|
+
/** Animate the flow and the floating diya. Default true. Reduced motion freezes a frame mid-flow. */
|
|
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 110. */
|
|
11
|
+
speedMs?: number;
|
|
12
|
+
/** Seed for the flow pattern and the diya's vertical bob. Default 17. */
|
|
13
|
+
seedNum?: number;
|
|
14
|
+
/** Short caption under the art; the art itself is decorative. */
|
|
15
|
+
label?: string;
|
|
16
|
+
class?: string;
|
|
17
|
+
}
|
|
18
|
+
declare const GraphGanga: import("svelte").Component<GraphGangaProps, {}, "">;
|
|
19
|
+
type GraphGanga = ReturnType<typeof GraphGanga>;
|
|
20
|
+
export default GraphGanga;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface GraphHashProps {
|
|
3
|
+
/** Caption on the frame's top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
|
|
4
|
+
title: string;
|
|
5
|
+
/** Any string — its digest is the seed. Default 'markgraphy'. */
|
|
6
|
+
input?: string;
|
|
7
|
+
/** Grid width in cells. Default 16. */
|
|
8
|
+
cols?: number;
|
|
9
|
+
/** Grid height in rows. Default 6. */
|
|
10
|
+
rows?: number;
|
|
11
|
+
/** Milliseconds per tick of the scan-line sweep. Default 60. */
|
|
12
|
+
speedMs?: number;
|
|
13
|
+
/** Animate the scan line. Default true. Reduced motion freezes mid-sweep. */
|
|
14
|
+
animated?: boolean;
|
|
15
|
+
/** Short caption under the art. */
|
|
16
|
+
label?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script lang="ts">
|
|
22
|
+
import Graph from '../frame/Graph.svelte';
|
|
23
|
+
import GraphBody from '../frame/GraphBody.svelte';
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
title,
|
|
27
|
+
input = 'markgraphy',
|
|
28
|
+
cols = 16,
|
|
29
|
+
rows = 6,
|
|
30
|
+
speedMs = 60,
|
|
31
|
+
animated = true,
|
|
32
|
+
label = 'any string → a glyph fingerprint',
|
|
33
|
+
class: className = ''
|
|
34
|
+
}: GraphHashProps = $props();
|
|
35
|
+
|
|
36
|
+
interface Seg {
|
|
37
|
+
text: string;
|
|
38
|
+
cls?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// svelte-ignore state_referenced_locally
|
|
42
|
+
const W = Math.max(4, Math.min(32, Math.round(cols)));
|
|
43
|
+
// svelte-ignore state_referenced_locally
|
|
44
|
+
const H = Math.max(2, Math.min(16, Math.round(rows)));
|
|
45
|
+
|
|
46
|
+
// 4-tier glyph ladder: faint → mid → hot → solid
|
|
47
|
+
const TIER: readonly string[] = ['·', '░', '▒', '▓'];
|
|
48
|
+
// '·' is faint, '░' is low, '▒' is mid, '▓' is high. We don't use '█' so
|
|
49
|
+
// the fingerprint reads as a checkered density, not solid blocks.
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 64-bit FNV-1a-ish digest. Pure function of the input string — same input
|
|
53
|
+
* always yields the same 64-bit digest, no PRNG state. This is the
|
|
54
|
+
* "any string → deterministic fingerprint" property.
|
|
55
|
+
*/
|
|
56
|
+
function digest64(s: string): bigint {
|
|
57
|
+
// 64-bit FNV-1a constants
|
|
58
|
+
const FNV_OFFSET = 0xcbf29ce484222325n;
|
|
59
|
+
const FNV_PRIME = 0x100000001b3n;
|
|
60
|
+
const MASK = 0xffffffffffffffffn;
|
|
61
|
+
|
|
62
|
+
let h = FNV_OFFSET;
|
|
63
|
+
for (let i = 0; i < s.length; i++) {
|
|
64
|
+
h ^= BigInt(s.charCodeAt(i));
|
|
65
|
+
h = (h * FNV_PRIME) & MASK;
|
|
66
|
+
}
|
|
67
|
+
return h;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 4 chars * 4 bits = 16 hex chars
|
|
71
|
+
function digestHex(s: string): string {
|
|
72
|
+
return digest64(s).toString(16).padStart(16, '0');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// svelte-ignore state_referenced_locally
|
|
76
|
+
const hex = digestHex(input ?? '');
|
|
77
|
+
|
|
78
|
+
// Map the digest to a 2D tier grid. Each cell uses 2 bits of the digest
|
|
79
|
+
// to pick one of 4 tiers. We walk bits in row-major order.
|
|
80
|
+
// svelte-ignore state_referenced_locally
|
|
81
|
+
const tier: number[][] = (() => {
|
|
82
|
+
const d = digest64(input ?? '');
|
|
83
|
+
const out: number[][] = [];
|
|
84
|
+
let bit = 0;
|
|
85
|
+
|
|
86
|
+
for (let y = 0; y < H; y++) {
|
|
87
|
+
const row: number[] = [];
|
|
88
|
+
for (let x = 0; x < W; x++) {
|
|
89
|
+
// Take 2 bits starting at `bit`, LSB-first.
|
|
90
|
+
const b0 = Number((d >> BigInt(bit)) & 1n);
|
|
91
|
+
const b1 = Number((d >> BigInt(bit + 1)) & 1n);
|
|
92
|
+
row.push(b0 | (b1 << 1));
|
|
93
|
+
bit += 2;
|
|
94
|
+
}
|
|
95
|
+
out.push(row);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return out;
|
|
99
|
+
})();
|
|
100
|
+
|
|
101
|
+
// Scan line: a column that sweeps left-to-right then wraps.
|
|
102
|
+
// Period = W + 6 (so the line fully exits the grid before re-entering).
|
|
103
|
+
// svelte-ignore state_referenced_locally
|
|
104
|
+
const initialTick = animated ? Math.floor((W + 6) * 0.4) : Math.floor((W + 6) * 0.5);
|
|
105
|
+
// svelte-ignore state_referenced_locally
|
|
106
|
+
let tick = $state(initialTick);
|
|
107
|
+
|
|
108
|
+
const moving = $derived(
|
|
109
|
+
animated &&
|
|
110
|
+
(typeof window === 'undefined' ||
|
|
111
|
+
!window.matchMedia('(prefers-reduced-motion: reduce)').matches)
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
$effect(() => {
|
|
115
|
+
if (!moving) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const timer = window.setInterval(() => {
|
|
120
|
+
tick = (tick + 1) % (W + 6);
|
|
121
|
+
}, speedMs);
|
|
122
|
+
|
|
123
|
+
return () => window.clearInterval(timer);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Where the scan line currently is. -1..W-1; -W when wrapping.
|
|
127
|
+
const scanCol = $derived(tick - 2);
|
|
128
|
+
|
|
129
|
+
// Render the grid as RLE per row.
|
|
130
|
+
const view = $derived.by((): Seg[] => {
|
|
131
|
+
const out: Seg[] = [];
|
|
132
|
+
|
|
133
|
+
// Header: input label
|
|
134
|
+
const inputLabel = (input ?? '').slice(0, 28);
|
|
135
|
+
const headerPad = Math.max(0, W - inputLabel.length - 4);
|
|
136
|
+
out.push({ text: '·' });
|
|
137
|
+
out.push({ text: ' ' });
|
|
138
|
+
out.push({ text: inputLabel, cls: 'mid' });
|
|
139
|
+
out.push({ text: ' '.repeat(headerPad) });
|
|
140
|
+
out.push({ text: '\n' });
|
|
141
|
+
|
|
142
|
+
for (let y = 0; y < H; y++) {
|
|
143
|
+
out.push({ text: ' ' });
|
|
144
|
+
for (let x = 0; x < W; x++) {
|
|
145
|
+
const t = tier[y]![x]!;
|
|
146
|
+
const ch = TIER[t]!;
|
|
147
|
+
const onScan = x === scanCol;
|
|
148
|
+
const cls =
|
|
149
|
+
t === 0
|
|
150
|
+
? onScan
|
|
151
|
+
? 'faint-on'
|
|
152
|
+
: 'faint'
|
|
153
|
+
: t === 1
|
|
154
|
+
? onScan
|
|
155
|
+
? 'low-on'
|
|
156
|
+
: 'low'
|
|
157
|
+
: t === 2
|
|
158
|
+
? onScan
|
|
159
|
+
? 'mid-on'
|
|
160
|
+
: 'mid'
|
|
161
|
+
: onScan
|
|
162
|
+
? 'high-on'
|
|
163
|
+
: 'high';
|
|
164
|
+
out.push({ text: ch, cls });
|
|
165
|
+
}
|
|
166
|
+
out.push({ text: '\n' });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Hex digest on the bottom — split into 2 rows of 8 chars for width.
|
|
170
|
+
out.push({ text: ' ' });
|
|
171
|
+
out.push({ text: hex.slice(0, 8), cls: 'hex' });
|
|
172
|
+
out.push({ text: ' ' });
|
|
173
|
+
out.push({ text: hex.slice(8, 16), cls: 'hex' });
|
|
174
|
+
|
|
175
|
+
return out;
|
|
176
|
+
});
|
|
177
|
+
</script>
|
|
178
|
+
|
|
179
|
+
<Graph {title} class={className}>
|
|
180
|
+
<GraphBody>
|
|
181
|
+
<div class="hash">
|
|
182
|
+
<div class="viewport">
|
|
183
|
+
<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>
|
|
184
|
+
</div>
|
|
185
|
+
<p class="caption">{label}</p>
|
|
186
|
+
</div>
|
|
187
|
+
</GraphBody>
|
|
188
|
+
</Graph>
|
|
189
|
+
|
|
190
|
+
<style>
|
|
191
|
+
.hash {
|
|
192
|
+
display: flex;
|
|
193
|
+
flex-direction: column;
|
|
194
|
+
gap: 0.75rem;
|
|
195
|
+
min-width: 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.viewport {
|
|
199
|
+
overflow-x: auto;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.art {
|
|
203
|
+
margin: 0;
|
|
204
|
+
font-size: 0.95rem;
|
|
205
|
+
line-height: 1.1;
|
|
206
|
+
color: var(--graph-foreground, oklch(0.93 0 0));
|
|
207
|
+
white-space: pre;
|
|
208
|
+
font-variant-numeric: tabular-nums;
|
|
209
|
+
letter-spacing: 0.05em;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Tier colors */
|
|
213
|
+
.faint {
|
|
214
|
+
color: var(--graph-faint, oklch(0.3 0 0));
|
|
215
|
+
}
|
|
216
|
+
.low {
|
|
217
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
218
|
+
}
|
|
219
|
+
.mid {
|
|
220
|
+
color: var(--graph-foreground, oklch(0.93 0 0));
|
|
221
|
+
}
|
|
222
|
+
.high {
|
|
223
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* Scan-line accent: same tier, but brightened */
|
|
227
|
+
.faint-on {
|
|
228
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
229
|
+
}
|
|
230
|
+
.low-on {
|
|
231
|
+
color: var(--graph-foreground, oklch(0.93 0 0));
|
|
232
|
+
}
|
|
233
|
+
.mid-on {
|
|
234
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
235
|
+
}
|
|
236
|
+
.high-on {
|
|
237
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
238
|
+
font-weight: 600;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.hex {
|
|
242
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
243
|
+
font-weight: 600;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.caption {
|
|
247
|
+
margin: 0;
|
|
248
|
+
font-size: 0.8rem;
|
|
249
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
250
|
+
}
|
|
251
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface GraphHashProps {
|
|
2
|
+
/** Caption on the frame's top edge as `[ TITLE ]`. Uppercase, 1–2 words. */
|
|
3
|
+
title: string;
|
|
4
|
+
/** Any string — its digest is the seed. Default 'markgraphy'. */
|
|
5
|
+
input?: string;
|
|
6
|
+
/** Grid width in cells. Default 16. */
|
|
7
|
+
cols?: number;
|
|
8
|
+
/** Grid height in rows. Default 6. */
|
|
9
|
+
rows?: number;
|
|
10
|
+
/** Milliseconds per tick of the scan-line sweep. Default 60. */
|
|
11
|
+
speedMs?: number;
|
|
12
|
+
/** Animate the scan line. Default true. Reduced motion freezes mid-sweep. */
|
|
13
|
+
animated?: boolean;
|
|
14
|
+
/** Short caption under the art. */
|
|
15
|
+
label?: string;
|
|
16
|
+
class?: string;
|
|
17
|
+
}
|
|
18
|
+
declare const GraphHash: import("svelte").Component<GraphHashProps, {}, "">;
|
|
19
|
+
type GraphHash = ReturnType<typeof GraphHash>;
|
|
20
|
+
export default GraphHash;
|