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,166 @@
1
+ <script module lang="ts">
2
+ import type { Glyphs } from '../frame/glyphs';
3
+ import type { GraphPalette } from '../frame/tone';
4
+
5
+ /** One headline number with label, optional hint, and a glyph sparkline. */
6
+ export interface GraphKpiProps {
7
+ title: string;
8
+ value: string;
9
+ label: string;
10
+ hint?: string;
11
+ data: number[];
12
+ glyphs?: Glyphs;
13
+ palette?: GraphPalette;
14
+ corner?: string;
15
+ class?: string;
16
+ }
17
+ </script>
18
+
19
+ <script lang="ts">
20
+ import Graph from '../frame/Graph.svelte';
21
+ import GraphBody from '../frame/GraphBody.svelte';
22
+ import { resolveGlyphs } from '../frame/glyphs';
23
+ import { isMonoPalette } from '../frame/tone';
24
+ import { reveal } from '../frame/motion';
25
+
26
+ let {
27
+ title,
28
+ value,
29
+ label,
30
+ hint,
31
+ data,
32
+ glyphs,
33
+ palette,
34
+ corner,
35
+ class: className = ''
36
+ }: GraphKpiProps = $props();
37
+
38
+ const SPARK_DEFAULT = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
39
+
40
+ const set = $derived(glyphs == null ? SPARK_DEFAULT : resolveGlyphs(glyphs));
41
+ const max = $derived(Math.max(...data, 1));
42
+ const last = $derived(data.length - 1);
43
+ const mono = $derived(isMonoPalette(palette));
44
+
45
+ const points = $derived.by(() =>
46
+ data.map((entry) => {
47
+ const index = Math.round((entry / max) * (set.length - 1));
48
+ return set[index] ?? set[0] ?? '▁';
49
+ })
50
+ );
51
+ </script>
52
+
53
+ <Graph {title} {corner} class={className}>
54
+ <GraphBody>
55
+ <div class="col">
56
+ <div class="head" use:reveal={{ amount: 0.5 }}>
57
+ <p class="value c-accent">{value}</p>
58
+ <div class="meta">
59
+ <p class="muted">{label}</p>
60
+ {#if hint}
61
+ <p class="muted">{hint}</p>
62
+ {/if}
63
+ </div>
64
+ </div>
65
+ {#if points.length > 0}
66
+ <div class="spark" aria-hidden="true" use:reveal={{ amount: 0.5 }}>
67
+ {#each points as glyph, i}
68
+ <span
69
+ class="pt"
70
+ class:c-accent={i === last}
71
+ class:c-muted={i !== last && mono}
72
+ class:c-accent2={i !== last && !mono}
73
+ class:dim={i !== last && mono}
74
+ >
75
+ {glyph}
76
+ </span>
77
+ {/each}
78
+ </div>
79
+ {/if}
80
+ <span class="sr-only">
81
+ {value}
82
+ {label}{#if hint}. {hint}{/if}
83
+ </span>
84
+ </div>
85
+ </GraphBody>
86
+ </Graph>
87
+
88
+ <style>
89
+ .col {
90
+ display: flex;
91
+ flex-direction: column;
92
+ gap: 1rem;
93
+ }
94
+
95
+ .head {
96
+ display: flex;
97
+ flex-direction: column;
98
+ gap: 0.5rem;
99
+ }
100
+
101
+ .value {
102
+ margin: 0;
103
+ font-size: 1.875rem;
104
+ line-height: 1.2;
105
+ letter-spacing: -0.025em;
106
+ }
107
+
108
+ @media (min-width: 640px) {
109
+ .value {
110
+ font-size: 2.25rem;
111
+ }
112
+ }
113
+
114
+ .meta {
115
+ display: flex;
116
+ align-items: baseline;
117
+ gap: 0.75rem;
118
+ }
119
+
120
+ .muted {
121
+ margin: 0;
122
+ color: var(--graph-muted, oklch(0.62 0 0));
123
+ }
124
+
125
+ .spark {
126
+ display: flex;
127
+ justify-content: flex-start;
128
+ gap: 0.125rem;
129
+ width: 100%;
130
+ min-width: 0;
131
+ user-select: none;
132
+ }
133
+
134
+ .pt {
135
+ flex: none;
136
+ min-width: 1ch;
137
+ }
138
+
139
+ .c-accent {
140
+ color: var(--graph-accent, oklch(0.78 0.17 155));
141
+ }
142
+
143
+ .c-accent2 {
144
+ color: var(--graph-accent-2, oklch(0.78 0.12 70));
145
+ }
146
+
147
+ .c-muted {
148
+ color: var(--graph-muted, oklch(0.62 0 0));
149
+ }
150
+
151
+ .dim {
152
+ opacity: 0.4;
153
+ }
154
+
155
+ .sr-only {
156
+ position: absolute;
157
+ width: 1px;
158
+ height: 1px;
159
+ padding: 0;
160
+ margin: -1px;
161
+ overflow: hidden;
162
+ clip-path: inset(50%);
163
+ white-space: nowrap;
164
+ border-width: 0;
165
+ }
166
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { Glyphs } from '../frame/glyphs';
2
+ import type { GraphPalette } from '../frame/tone';
3
+ /** One headline number with label, optional hint, and a glyph sparkline. */
4
+ export interface GraphKpiProps {
5
+ title: string;
6
+ value: string;
7
+ label: string;
8
+ hint?: string;
9
+ data: number[];
10
+ glyphs?: Glyphs;
11
+ palette?: GraphPalette;
12
+ corner?: string;
13
+ class?: string;
14
+ }
15
+ declare const GraphKpi: import("svelte").Component<GraphKpiProps, {}, "">;
16
+ type GraphKpi = ReturnType<typeof GraphKpi>;
17
+ export default GraphKpi;
@@ -0,0 +1,340 @@
1
+ <script module lang="ts">
2
+ import type { GraphPalette } from '../frame/tone';
3
+
4
+ /** One percentile-banded data point: p50, p95, p99 in the same unit. */
5
+ export interface LatencyPoint {
6
+ p50: number;
7
+ p95: number;
8
+ p99: number;
9
+ /** Mark the column as a spike. The ^ marker is drawn above it. */
10
+ spike?: boolean;
11
+ }
12
+
13
+ /**
14
+ * Percentile band chart. Each column is stacked: the p50 fill (█) at the
15
+ * bottom, then p50→p95 (▓), then p95→p99 (▒). Spike markers appear above
16
+ * columns whose point sets `spike: true`.
17
+ */
18
+ export interface GraphLatencyProps {
19
+ title: string;
20
+ data: LatencyPoint[];
21
+ unit?: string;
22
+ height?: number;
23
+ labels?: [string, string];
24
+ max?: number;
25
+ palette?: GraphPalette;
26
+ corner?: string;
27
+ class?: string;
28
+ }
29
+ </script>
30
+
31
+ <script lang="ts">
32
+ import Graph from '../frame/Graph.svelte';
33
+ import GraphBody from '../frame/GraphBody.svelte';
34
+ import GraphRule from '../frame/GraphRule.svelte';
35
+ import { isMonoPalette } from '../frame/tone';
36
+ import { reveal, stagger } from '../frame/motion';
37
+
38
+ let {
39
+ title,
40
+ data,
41
+ unit = 'ms',
42
+ height = 8,
43
+ labels,
44
+ max,
45
+ palette,
46
+ corner,
47
+ class: className = ''
48
+ }: GraphLatencyProps = $props();
49
+
50
+ const mono = $derived(isMonoPalette(palette));
51
+
52
+ function formatTick(value: number): string {
53
+ if (!Number.isFinite(value)) {
54
+ return '0';
55
+ }
56
+ if (Number.isInteger(value)) {
57
+ return value.toString();
58
+ }
59
+ return value.toFixed(1);
60
+ }
61
+
62
+ function formatMs(value: number): string {
63
+ if (value >= 1000) {
64
+ return `${(value / 1000).toFixed(value >= 10_000 ? 0 : 1)}s`;
65
+ }
66
+ return `${Math.round(value)}`;
67
+ }
68
+
69
+ const view = $derived.by(() => {
70
+ const rows = Math.max(3, Math.min(15, height));
71
+ const cap = max ?? data.reduce((m, point) => Math.max(m, point.p99, point.p95, point.p50), 0);
72
+ const scale = cap > 0 ? cap : 1;
73
+ const start = labels?.[0];
74
+ const end = labels?.[1];
75
+ const lastP50 = data.at(-1)?.p50 ?? 0;
76
+ const lastP95 = data.at(-1)?.p95 ?? 0;
77
+ const lastP99 = data.at(-1)?.p99 ?? 0;
78
+ const peakP99 = data.reduce((m, point) => Math.max(m, point.p99), 0);
79
+ const spikeCount = data.filter((point) => point.spike).length;
80
+ return { rows, cap, scale, start, end, lastP50, lastP95, lastP99, peakP99, spikeCount };
81
+ });
82
+
83
+ function levelOf(value: number, scale: number, rows: number): number {
84
+ return Math.min(rows, Math.max(0, Math.round((value / scale) * rows)));
85
+ }
86
+ </script>
87
+
88
+ <Graph {title} {corner} class={className}>
89
+ <GraphBody>
90
+ <div class="lat">
91
+ <div class="spikes" aria-hidden="true">
92
+ <span class="yscale" aria-hidden="true">&nbsp;</span>
93
+ <span class="row">
94
+ {#each data as point, i (i)}
95
+ <span class="spike" class:on={point.spike}>{point.spike ? '^' : ' '}</span>
96
+ {/each}
97
+ </span>
98
+ </div>
99
+ <div class="plotrow" style:--plot-h={view.rows}>
100
+ <div class="yscale">
101
+ <span>{formatTick(view.cap)}</span>
102
+ <span>{formatTick(0)}</span>
103
+ </div>
104
+ <div class="plot" aria-hidden="true">
105
+ {#each data as point, i (i)}
106
+ {@const p50 = Math.max(1, levelOf(point.p50, view.scale, view.rows))}
107
+ {@const p95 = Math.max(p50, levelOf(point.p95, view.scale, view.rows))}
108
+ {@const p99 = Math.max(p95, levelOf(point.p99, view.scale, view.rows))}
109
+ <span class="col" use:reveal={{ delay: stagger(i, 18), amount: 0.4 }}>
110
+ {#each Array.from({ length: view.rows }) as _, rowIndex}
111
+ {@const fromBottom = view.rows - 1 - rowIndex}
112
+ {@const tier =
113
+ fromBottom < p50 ? 0 : fromBottom < p95 ? 1 : fromBottom < p99 ? 2 : 3}
114
+ <span
115
+ class="cellrow"
116
+ class:c-p50={tier === 0}
117
+ class:c-p95={tier === 1 && !mono}
118
+ class:c-p95m={tier === 1 && mono}
119
+ class:c-p99={tier === 2 && !mono}
120
+ class:c-p99m={tier === 2 && mono}
121
+ class:transparent={tier === 3}
122
+ >
123
+ {tier === 0 ? '█' : tier === 1 ? '▓' : tier === 2 ? '▒' : ' '}
124
+ </span>
125
+ {/each}
126
+ </span>
127
+ {/each}
128
+ </div>
129
+ </div>
130
+ {#if view.start || view.end}
131
+ <div class="axisrow">
132
+ <span class="spacer" aria-hidden="true">{formatTick(view.cap)}</span>
133
+ <div class="rulewrap">
134
+ <GraphRule />
135
+ </div>
136
+ </div>
137
+ <div class="axisrow">
138
+ <span class="spacer" aria-hidden="true">{formatTick(view.cap)}</span>
139
+ <div class="axislabels">
140
+ <span>{view.start}</span>
141
+ {#if view.end && view.end !== view.start}
142
+ <span>{view.end}</span>
143
+ {/if}
144
+ </div>
145
+ </div>
146
+ {/if}
147
+
148
+ <dl class="stats" aria-label="percentile summary">
149
+ <div class="stat">
150
+ <dt>p50</dt>
151
+ <dd>{formatMs(view.lastP50)}{unit}</dd>
152
+ </div>
153
+ <div class="stat">
154
+ <dt>p95</dt>
155
+ <dd class:accent={!mono}>{formatMs(view.lastP95)}{unit}</dd>
156
+ </div>
157
+ <div class="stat">
158
+ <dt>p99</dt>
159
+ <dd class:accent={!mono && palette === 'multi'}>{formatMs(view.lastP99)}{unit}</dd>
160
+ </div>
161
+ <div class="stat">
162
+ <dt>peak p99</dt>
163
+ <dd>{formatMs(view.peakP99)}{unit}</dd>
164
+ </div>
165
+ <div class="stat">
166
+ <dt>spikes</dt>
167
+ <dd class:accent={view.spikeCount > 0 && !mono}>{view.spikeCount}</dd>
168
+ </div>
169
+ </dl>
170
+ <span class="sr-only">
171
+ Latency band chart. {data.length} points. Last p50 {formatMs(view.lastP50)} {unit},
172
+ p95 {formatMs(view.lastP95)} {unit}, p99 {formatMs(view.lastP99)} {unit}.
173
+ {view.spikeCount} spike{view.spikeCount === 1 ? '' : 's'}.
174
+ </span>
175
+ </div>
176
+ </GraphBody>
177
+ </Graph>
178
+
179
+ <style>
180
+ .lat {
181
+ display: flex;
182
+ flex-direction: column;
183
+ gap: 0.6rem;
184
+ }
185
+
186
+ .spikes {
187
+ display: flex;
188
+ gap: 0.75rem;
189
+ height: 1em;
190
+ }
191
+
192
+ .plotrow {
193
+ display: flex;
194
+ gap: 0.75rem;
195
+ }
196
+
197
+ .yscale {
198
+ display: flex;
199
+ width: 4ch;
200
+ flex-shrink: 0;
201
+ flex-direction: column;
202
+ align-items: flex-end;
203
+ justify-content: space-between;
204
+ padding: 1px 0;
205
+ height: calc(var(--plot-h, 8) * 1em);
206
+ text-align: right;
207
+ color: var(--graph-muted, oklch(0.62 0 0));
208
+ }
209
+
210
+ .spacer {
211
+ width: 4ch;
212
+ flex-shrink: 0;
213
+ visibility: hidden;
214
+ }
215
+
216
+ .row {
217
+ display: flex;
218
+ flex: 1;
219
+ min-width: 0;
220
+ justify-content: center;
221
+ gap: 0.125rem;
222
+ user-select: none;
223
+ }
224
+
225
+ .spike {
226
+ flex: none;
227
+ min-width: 1ch;
228
+ text-align: center;
229
+ color: transparent;
230
+ }
231
+
232
+ .spike.on {
233
+ color: var(--graph-accent, oklch(0.78 0.17 155));
234
+ }
235
+
236
+ .plot {
237
+ display: flex;
238
+ flex: 1;
239
+ min-width: 0;
240
+ align-items: flex-end;
241
+ justify-content: center;
242
+ gap: 0.125rem;
243
+ height: calc(var(--plot-h, 8) * 1em);
244
+ user-select: none;
245
+ }
246
+
247
+ .col {
248
+ display: flex;
249
+ height: 100%;
250
+ min-width: 1ch;
251
+ flex-direction: column;
252
+ justify-content: flex-end;
253
+ }
254
+
255
+ .cellrow {
256
+ width: 100%;
257
+ height: 1em;
258
+ text-align: center;
259
+ }
260
+
261
+ .transparent {
262
+ color: transparent;
263
+ }
264
+
265
+ .axisrow {
266
+ display: flex;
267
+ gap: 0.75rem;
268
+ }
269
+
270
+ .rulewrap {
271
+ flex: 1;
272
+ min-width: 0;
273
+ }
274
+
275
+ .axislabels {
276
+ display: flex;
277
+ flex: 1;
278
+ justify-content: space-between;
279
+ color: var(--graph-muted, oklch(0.62 0 0));
280
+ }
281
+
282
+ .stats {
283
+ display: flex;
284
+ flex-wrap: wrap;
285
+ justify-content: center;
286
+ gap: 0.5rem 1.25rem;
287
+ margin: 0.25rem 0 0;
288
+ }
289
+
290
+ .stat {
291
+ display: flex;
292
+ align-items: baseline;
293
+ gap: 0.4rem;
294
+ }
295
+
296
+ .stat dt {
297
+ margin: 0;
298
+ color: var(--graph-muted, oklch(0.62 0 0));
299
+ }
300
+
301
+ .stat dd {
302
+ margin: 0;
303
+ }
304
+
305
+ .c-p50 {
306
+ color: var(--graph-accent, oklch(0.78 0.17 155));
307
+ }
308
+
309
+ .c-p95 {
310
+ color: var(--graph-accent-2, oklch(0.78 0.12 70));
311
+ }
312
+
313
+ .c-p95m {
314
+ color: var(--graph-foreground, oklch(0.93 0 0));
315
+ }
316
+
317
+ .c-p99 {
318
+ color: var(--graph-accent-3, oklch(0.72 0.13 30));
319
+ }
320
+
321
+ .c-p99m {
322
+ color: var(--graph-muted, oklch(0.62 0 0));
323
+ }
324
+
325
+ .accent {
326
+ color: var(--graph-accent, oklch(0.78 0.17 155));
327
+ }
328
+
329
+ .sr-only {
330
+ position: absolute;
331
+ width: 1px;
332
+ height: 1px;
333
+ padding: 0;
334
+ margin: -1px;
335
+ overflow: hidden;
336
+ clip-path: inset(50%);
337
+ white-space: nowrap;
338
+ border-width: 0;
339
+ }
340
+ </style>
@@ -0,0 +1,28 @@
1
+ import type { GraphPalette } from '../frame/tone';
2
+ /** One percentile-banded data point: p50, p95, p99 in the same unit. */
3
+ export interface LatencyPoint {
4
+ p50: number;
5
+ p95: number;
6
+ p99: number;
7
+ /** Mark the column as a spike. The ^ marker is drawn above it. */
8
+ spike?: boolean;
9
+ }
10
+ /**
11
+ * Percentile band chart. Each column is stacked: the p50 fill (█) at the
12
+ * bottom, then p50→p95 (▓), then p95→p99 (▒). Spike markers appear above
13
+ * columns whose point sets `spike: true`.
14
+ */
15
+ export interface GraphLatencyProps {
16
+ title: string;
17
+ data: LatencyPoint[];
18
+ unit?: string;
19
+ height?: number;
20
+ labels?: [string, string];
21
+ max?: number;
22
+ palette?: GraphPalette;
23
+ corner?: string;
24
+ class?: string;
25
+ }
26
+ declare const GraphLatency: import("svelte").Component<GraphLatencyProps, {}, "">;
27
+ type GraphLatency = ReturnType<typeof GraphLatency>;
28
+ export default GraphLatency;
@@ -0,0 +1,121 @@
1
+ <script module lang="ts">
2
+ import type { Glyphs } from '../frame/glyphs';
3
+ import type { GraphPalette } from '../frame/tone';
4
+
5
+ /** Horizontal progress bar that spans the frame: [ = = = - - - ] 42%. */
6
+ export interface GraphMeterProps {
7
+ title: string;
8
+ value: number;
9
+ ticks?: number;
10
+ caption?: string;
11
+ glyphs?: Glyphs;
12
+ palette?: GraphPalette;
13
+ corner?: string;
14
+ class?: string;
15
+ }
16
+ </script>
17
+
18
+ <script lang="ts">
19
+ import Graph from '../frame/Graph.svelte';
20
+ import GraphBody from '../frame/GraphBody.svelte';
21
+ import GraphTrack from '../frame/GraphTrack.svelte';
22
+ import GraphTick from '../frame/GraphTick.svelte';
23
+ import { reveal } from '../frame/motion';
24
+ import { trackMarks } from '../frame/glyphs';
25
+
26
+ let {
27
+ title,
28
+ value,
29
+ ticks = 14,
30
+ caption,
31
+ glyphs,
32
+ palette,
33
+ corner,
34
+ class: className = ''
35
+ }: GraphMeterProps = $props();
36
+
37
+ const clamped = $derived(Math.min(1, Math.max(0, value)));
38
+ const filled = $derived(Math.round(clamped * ticks));
39
+ const marks = $derived(trackMarks(glyphs, { empty: '-', rest: '=', fill: '=' }));
40
+ const indexes = $derived(Array.from({ length: ticks }, (_, i) => i));
41
+ const percent = $derived(Math.round(clamped * 100));
42
+ const spoken = $derived(`${percent} percent${caption ? ` ${caption}` : ''}`);
43
+ </script>
44
+
45
+ <Graph {title} {corner} class={className}>
46
+ <GraphBody>
47
+ <div class="meter" use:reveal={{ amount: 0.5 }}>
48
+ <p class="bar">
49
+ <span class="brace" aria-hidden="true">[</span>
50
+ <GraphTrack>
51
+ {#each indexes as index (index)}
52
+ <GraphTick>
53
+ <span class="g" class:fill={index < filled}>
54
+ {index < filled ? marks.fill : marks.empty}
55
+ </span>
56
+ </GraphTick>
57
+ {/each}
58
+ </GraphTrack>
59
+ <span class="brace" aria-hidden="true">]</span>
60
+ <span class="pct">{percent}%</span>
61
+ </p>
62
+ {#if caption}
63
+ <p class="muted">{caption}</p>
64
+ {/if}
65
+ <span class="sr-only">{spoken}</span>
66
+ </div>
67
+ </GraphBody>
68
+ </Graph>
69
+
70
+ <style>
71
+ .meter {
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: 1rem;
75
+ }
76
+
77
+ .bar {
78
+ display: flex;
79
+ width: 100%;
80
+ align-items: center;
81
+ column-gap: 0.75rem;
82
+ margin: 0;
83
+ }
84
+
85
+ .brace {
86
+ user-select: none;
87
+ color: var(--graph-frame, oklch(0.6 0 0 / 0.5));
88
+ }
89
+
90
+ .g {
91
+ color: var(--graph-frame, oklch(0.6 0 0 / 0.5));
92
+ }
93
+
94
+ .g.fill {
95
+ color: var(--graph-accent, oklch(0.78 0.17 155));
96
+ }
97
+
98
+ .pct {
99
+ width: 4ch;
100
+ flex-shrink: 0;
101
+ text-align: right;
102
+ color: var(--graph-accent, oklch(0.78 0.17 155));
103
+ }
104
+
105
+ .muted {
106
+ margin: 0;
107
+ color: var(--graph-muted, oklch(0.62 0 0));
108
+ }
109
+
110
+ .sr-only {
111
+ position: absolute;
112
+ width: 1px;
113
+ height: 1px;
114
+ padding: 0;
115
+ margin: -1px;
116
+ overflow: hidden;
117
+ clip-path: inset(50%);
118
+ white-space: nowrap;
119
+ border-width: 0;
120
+ }
121
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { Glyphs } from '../frame/glyphs';
2
+ import type { GraphPalette } from '../frame/tone';
3
+ /** Horizontal progress bar that spans the frame: [ = = = - - - ] 42%. */
4
+ export interface GraphMeterProps {
5
+ title: string;
6
+ value: number;
7
+ ticks?: number;
8
+ caption?: string;
9
+ glyphs?: Glyphs;
10
+ palette?: GraphPalette;
11
+ corner?: string;
12
+ class?: string;
13
+ }
14
+ declare const GraphMeter: import("svelte").Component<GraphMeterProps, {}, "">;
15
+ type GraphMeter = ReturnType<typeof GraphMeter>;
16
+ export default GraphMeter;