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,21 @@
|
|
|
1
|
+
import type { Glyphs } from '../frame/glyphs';
|
|
2
|
+
import type { GraphPalette } from '../frame/tone';
|
|
3
|
+
/** One ranked row: label, a full-width glyph bar, and the value. */
|
|
4
|
+
export interface RankItem {
|
|
5
|
+
label: string;
|
|
6
|
+
value: number;
|
|
7
|
+
display?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface GraphRankProps {
|
|
10
|
+
title: string;
|
|
11
|
+
items: RankItem[];
|
|
12
|
+
max?: number;
|
|
13
|
+
ticks?: number;
|
|
14
|
+
glyphs?: Glyphs;
|
|
15
|
+
palette?: GraphPalette;
|
|
16
|
+
corner?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
declare const GraphRank: import("svelte").Component<GraphRankProps, {}, "">;
|
|
20
|
+
type GraphRank = ReturnType<typeof GraphRank>;
|
|
21
|
+
export default GraphRank;
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Error-budget burn bar. One fill for spent, a dim trail for the remaining
|
|
4
|
+
* budget, plus window, burn rate, time-to-empty, target, and actual.
|
|
5
|
+
* Pairs with GraphLatency, GraphUptime, and GraphPulse.
|
|
6
|
+
*/
|
|
7
|
+
export interface GraphSloProps {
|
|
8
|
+
title: string;
|
|
9
|
+
/** Fraction of the error budget consumed, 0–1. */
|
|
10
|
+
budget: number;
|
|
11
|
+
/** Window length in the chosen unit. Default 30. */
|
|
12
|
+
window?: number;
|
|
13
|
+
/** Window unit. Default 'd'. */
|
|
14
|
+
unit?: 'h' | 'd' | 'w' | 'mo';
|
|
15
|
+
/** Allowed error rate as a fraction, e.g. 0.001 for 99.9%. */
|
|
16
|
+
target?: number;
|
|
17
|
+
/** Actual error rate as a fraction. */
|
|
18
|
+
actual?: number;
|
|
19
|
+
/** Burn rate multiplier (1 = on pace). If set, takes precedence over elapsed. */
|
|
20
|
+
burnRate?: number;
|
|
21
|
+
/** Days (or hours, weeks) elapsed so far. Used to compute time-to-empty. */
|
|
22
|
+
elapsed?: number;
|
|
23
|
+
/** Optional sparkline of daily burn samples, 0..1 each. */
|
|
24
|
+
daily?: number[];
|
|
25
|
+
/** Optional caption below the panel. */
|
|
26
|
+
caption?: string;
|
|
27
|
+
corner?: string;
|
|
28
|
+
class?: string;
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<script lang="ts">
|
|
33
|
+
import Graph from '../frame/Graph.svelte';
|
|
34
|
+
import GraphBody from '../frame/GraphBody.svelte';
|
|
35
|
+
import GraphTrack from '../frame/GraphTrack.svelte';
|
|
36
|
+
import GraphTick from '../frame/GraphTick.svelte';
|
|
37
|
+
import { reveal, stagger } from '../frame/motion';
|
|
38
|
+
import { trackMarks } from '../frame/glyphs';
|
|
39
|
+
|
|
40
|
+
let {
|
|
41
|
+
title,
|
|
42
|
+
budget,
|
|
43
|
+
window = 30,
|
|
44
|
+
unit = 'd',
|
|
45
|
+
target,
|
|
46
|
+
actual,
|
|
47
|
+
burnRate,
|
|
48
|
+
elapsed,
|
|
49
|
+
daily,
|
|
50
|
+
caption,
|
|
51
|
+
corner,
|
|
52
|
+
class: className = ''
|
|
53
|
+
}: GraphSloProps = $props();
|
|
54
|
+
|
|
55
|
+
const ticks = 24;
|
|
56
|
+
|
|
57
|
+
const view = $derived.by(() => {
|
|
58
|
+
const spent = Math.min(1, Math.max(0, budget));
|
|
59
|
+
const remaining = 1 - spent;
|
|
60
|
+
const filled = Math.round(spent * ticks);
|
|
61
|
+
// burnRate, when set, is “× the on-pace rate”. When inferred from
|
|
62
|
+
// elapsed, the per-day pace is spent / elapsed, which is on-pace when
|
|
63
|
+
// spent equals elapsed/window. So inferredRate = pace * window.
|
|
64
|
+
const pace = elapsed != null && elapsed > 0 ? spent / elapsed : null;
|
|
65
|
+
const inferredRate = burnRate ?? (pace != null ? pace * window : null);
|
|
66
|
+
// timeToEmpty is in the chosen unit (days by default). Two paths:
|
|
67
|
+
// burnRate path: remaining window-budget at the given rate
|
|
68
|
+
// → remaining * window / burnRate
|
|
69
|
+
// elapsed path: remaining budget at the observed per-unit pace
|
|
70
|
+
// → remaining * elapsed / spent
|
|
71
|
+
const timeToEmpty =
|
|
72
|
+
burnRate != null
|
|
73
|
+
? (remaining * window) / burnRate
|
|
74
|
+
: pace != null && spent > 0
|
|
75
|
+
? (remaining * elapsed) / spent
|
|
76
|
+
: null;
|
|
77
|
+
const willExhaust =
|
|
78
|
+
timeToEmpty != null && elapsed != null ? timeToEmpty < window - elapsed : false;
|
|
79
|
+
const percent = Math.round(spent * 100);
|
|
80
|
+
const targetPct = target != null ? target * 100 : null;
|
|
81
|
+
const actualPct = actual != null ? actual * 100 : null;
|
|
82
|
+
return {
|
|
83
|
+
spent,
|
|
84
|
+
remaining,
|
|
85
|
+
filled,
|
|
86
|
+
inferredRate,
|
|
87
|
+
timeToEmpty,
|
|
88
|
+
willExhaust,
|
|
89
|
+
percent,
|
|
90
|
+
targetPct,
|
|
91
|
+
actualPct
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const marks = $derived(trackMarks(undefined, { empty: '·', rest: '·', fill: '█' }));
|
|
96
|
+
|
|
97
|
+
function formatPct(n: number | null): string {
|
|
98
|
+
if (n == null) {
|
|
99
|
+
return '—';
|
|
100
|
+
}
|
|
101
|
+
if (n === 0) {
|
|
102
|
+
return '0%';
|
|
103
|
+
}
|
|
104
|
+
if (n < 0.01) {
|
|
105
|
+
return `${n.toFixed(3)}%`;
|
|
106
|
+
}
|
|
107
|
+
if (n < 1) {
|
|
108
|
+
return `${n.toFixed(2)}%`;
|
|
109
|
+
}
|
|
110
|
+
return `${n.toFixed(1)}%`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function formatRate(n: number | null): string {
|
|
114
|
+
if (n == null) {
|
|
115
|
+
return '—';
|
|
116
|
+
}
|
|
117
|
+
return `${n.toFixed(n < 10 ? 2 : 1)}×`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function formatEta(n: number | null, u: string): string {
|
|
121
|
+
if (n == null) {
|
|
122
|
+
return '—';
|
|
123
|
+
}
|
|
124
|
+
if (n < 0) {
|
|
125
|
+
return 'past due';
|
|
126
|
+
}
|
|
127
|
+
return `${n.toFixed(n < 10 ? 1 : 0)}${u}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const dailyPoints = $derived.by(() => {
|
|
131
|
+
if (!daily || daily.length === 0) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const set = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
|
135
|
+
const max = Math.max(...daily, 0.001);
|
|
136
|
+
return daily.map((value) => {
|
|
137
|
+
const ratio = Math.min(1, Math.max(0, value / max));
|
|
138
|
+
return set[Math.min(7, Math.max(0, Math.round(ratio * 7)))] ?? '·';
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const indexes = $derived(Array.from({ length: ticks }, (_, i) => i));
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<Graph {title} {corner} class={className}>
|
|
146
|
+
<GraphBody>
|
|
147
|
+
<div class="slo" use:reveal={{ amount: 0.5 }}>
|
|
148
|
+
{#if dailyPoints}
|
|
149
|
+
<p class="daily" aria-hidden="true">
|
|
150
|
+
<span class="daily-label">daily burn</span>
|
|
151
|
+
<span class="daily-spark">
|
|
152
|
+
{#each dailyPoints as glyph, i (`${glyph}-${i}`)}
|
|
153
|
+
<span class="cell">{glyph}</span>
|
|
154
|
+
{/each}
|
|
155
|
+
</span>
|
|
156
|
+
</p>
|
|
157
|
+
{/if}
|
|
158
|
+
<p class="bar">
|
|
159
|
+
<span class="brace" aria-hidden="true">[</span>
|
|
160
|
+
<GraphTrack>
|
|
161
|
+
{#each indexes as index, i (index)}
|
|
162
|
+
<GraphTick>
|
|
163
|
+
<span
|
|
164
|
+
class="g"
|
|
165
|
+
class:fill={index < view.filled}
|
|
166
|
+
use:reveal={{ delay: stagger(i, 14), amount: 0.4 }}
|
|
167
|
+
>
|
|
168
|
+
{index < view.filled ? marks.fill : marks.empty}
|
|
169
|
+
</span>
|
|
170
|
+
</GraphTick>
|
|
171
|
+
{/each}
|
|
172
|
+
</GraphTrack>
|
|
173
|
+
<span class="brace" aria-hidden="true">]</span>
|
|
174
|
+
<span class="pct" class:warn={view.willExhaust}>{view.percent}%</span>
|
|
175
|
+
</p>
|
|
176
|
+
<dl class="rows">
|
|
177
|
+
<div class="row">
|
|
178
|
+
<dt>window</dt>
|
|
179
|
+
<dd>{window}{unit}</dd>
|
|
180
|
+
</div>
|
|
181
|
+
<div class="row">
|
|
182
|
+
<dt>burn rate</dt>
|
|
183
|
+
<dd class:warn={view.inferredRate != null && view.inferredRate > 1}>
|
|
184
|
+
{formatRate(view.inferredRate)}
|
|
185
|
+
</dd>
|
|
186
|
+
</div>
|
|
187
|
+
<div class="row">
|
|
188
|
+
<dt>time-to-empty</dt>
|
|
189
|
+
<dd class:warn={view.willExhaust}>{formatEta(view.timeToEmpty, unit)}</dd>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="row">
|
|
192
|
+
<dt>target</dt>
|
|
193
|
+
<dd>{formatPct(view.targetPct)}</dd>
|
|
194
|
+
</div>
|
|
195
|
+
<div class="row">
|
|
196
|
+
<dt>actual</dt>
|
|
197
|
+
<dd class:warn={view.targetPct != null && view.actualPct != null && view.actualPct > view.targetPct}>
|
|
198
|
+
{formatPct(view.actualPct)}
|
|
199
|
+
</dd>
|
|
200
|
+
</div>
|
|
201
|
+
</dl>
|
|
202
|
+
{#if view.willExhaust}
|
|
203
|
+
<p class="flag">↗ will exhaust the budget before the window closes</p>
|
|
204
|
+
{/if}
|
|
205
|
+
{#if caption}
|
|
206
|
+
<p class="muted">{caption}</p>
|
|
207
|
+
{/if}
|
|
208
|
+
<span class="sr-only">
|
|
209
|
+
Error budget {view.percent} percent spent of a {window}{unit} window
|
|
210
|
+
{#if view.inferredRate != null}, burn rate {formatRate(view.inferredRate)}{/if}
|
|
211
|
+
{#if view.timeToEmpty != null}, time to empty {formatEta(view.timeToEmpty, unit)}{/if}.
|
|
212
|
+
</span>
|
|
213
|
+
</div>
|
|
214
|
+
</GraphBody>
|
|
215
|
+
</Graph>
|
|
216
|
+
|
|
217
|
+
<style>
|
|
218
|
+
.slo {
|
|
219
|
+
display: flex;
|
|
220
|
+
flex-direction: column;
|
|
221
|
+
gap: 0.85rem;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.daily {
|
|
225
|
+
display: flex;
|
|
226
|
+
align-items: center;
|
|
227
|
+
gap: 0.75rem;
|
|
228
|
+
margin: 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.daily-label {
|
|
232
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.daily-spark {
|
|
236
|
+
display: inline-flex;
|
|
237
|
+
gap: 0.125rem;
|
|
238
|
+
user-select: none;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.cell {
|
|
242
|
+
min-width: 1ch;
|
|
243
|
+
text-align: center;
|
|
244
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.bar {
|
|
248
|
+
display: flex;
|
|
249
|
+
width: 100%;
|
|
250
|
+
align-items: center;
|
|
251
|
+
column-gap: 0.75rem;
|
|
252
|
+
margin: 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.brace {
|
|
256
|
+
user-select: none;
|
|
257
|
+
color: var(--graph-frame, oklch(0.6 0 0 / 0.5));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.g {
|
|
261
|
+
color: var(--graph-frame, oklch(0.6 0 0 / 0.5));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.g.fill {
|
|
265
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.pct {
|
|
269
|
+
width: 5ch;
|
|
270
|
+
flex-shrink: 0;
|
|
271
|
+
text-align: right;
|
|
272
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.pct.warn,
|
|
276
|
+
.warn {
|
|
277
|
+
color: var(--graph-accent-3, oklch(0.72 0.13 30));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.rows {
|
|
281
|
+
display: flex;
|
|
282
|
+
flex-wrap: wrap;
|
|
283
|
+
justify-content: center;
|
|
284
|
+
gap: 0.35rem 1.25rem;
|
|
285
|
+
margin: 0;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.row {
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: baseline;
|
|
291
|
+
gap: 0.4rem;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.row dt {
|
|
295
|
+
margin: 0;
|
|
296
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.row dd {
|
|
300
|
+
margin: 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.flag {
|
|
304
|
+
margin: 0;
|
|
305
|
+
text-align: center;
|
|
306
|
+
color: var(--graph-accent-3, oklch(0.72 0.13 30));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.muted {
|
|
310
|
+
margin: 0;
|
|
311
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.sr-only {
|
|
315
|
+
position: absolute;
|
|
316
|
+
width: 1px;
|
|
317
|
+
height: 1px;
|
|
318
|
+
padding: 0;
|
|
319
|
+
margin: -1px;
|
|
320
|
+
overflow: hidden;
|
|
321
|
+
clip-path: inset(50%);
|
|
322
|
+
white-space: nowrap;
|
|
323
|
+
border-width: 0;
|
|
324
|
+
}
|
|
325
|
+
</style>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error-budget burn bar. One fill for spent, a dim trail for the remaining
|
|
3
|
+
* budget, plus window, burn rate, time-to-empty, target, and actual.
|
|
4
|
+
* Pairs with GraphLatency, GraphUptime, and GraphPulse.
|
|
5
|
+
*/
|
|
6
|
+
export interface GraphSloProps {
|
|
7
|
+
title: string;
|
|
8
|
+
/** Fraction of the error budget consumed, 0–1. */
|
|
9
|
+
budget: number;
|
|
10
|
+
/** Window length in the chosen unit. Default 30. */
|
|
11
|
+
window?: number;
|
|
12
|
+
/** Window unit. Default 'd'. */
|
|
13
|
+
unit?: 'h' | 'd' | 'w' | 'mo';
|
|
14
|
+
/** Allowed error rate as a fraction, e.g. 0.001 for 99.9%. */
|
|
15
|
+
target?: number;
|
|
16
|
+
/** Actual error rate as a fraction. */
|
|
17
|
+
actual?: number;
|
|
18
|
+
/** Burn rate multiplier (1 = on pace). If set, takes precedence over elapsed. */
|
|
19
|
+
burnRate?: number;
|
|
20
|
+
/** Days (or hours, weeks) elapsed so far. Used to compute time-to-empty. */
|
|
21
|
+
elapsed?: number;
|
|
22
|
+
/** Optional sparkline of daily burn samples, 0..1 each. */
|
|
23
|
+
daily?: number[];
|
|
24
|
+
/** Optional caption below the panel. */
|
|
25
|
+
caption?: string;
|
|
26
|
+
corner?: string;
|
|
27
|
+
class?: string;
|
|
28
|
+
}
|
|
29
|
+
declare const GraphSlo: import("svelte").Component<GraphSloProps, {}, "">;
|
|
30
|
+
type GraphSlo = ReturnType<typeof GraphSlo>;
|
|
31
|
+
export default GraphSlo;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { GraphPalette } from '../frame/tone';
|
|
3
|
+
|
|
4
|
+
/** One before → after row. */
|
|
5
|
+
export interface SlopeItem {
|
|
6
|
+
label: string;
|
|
7
|
+
from: number;
|
|
8
|
+
to: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface GraphSlopeProps {
|
|
12
|
+
title: string;
|
|
13
|
+
fromLabel: string;
|
|
14
|
+
toLabel: string;
|
|
15
|
+
items: SlopeItem[];
|
|
16
|
+
palette?: GraphPalette;
|
|
17
|
+
corner?: string;
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<script lang="ts">
|
|
23
|
+
import Graph from '../frame/Graph.svelte';
|
|
24
|
+
import GraphBody from '../frame/GraphBody.svelte';
|
|
25
|
+
import { toneRole } from '../frame/tone';
|
|
26
|
+
import { reveal, stagger } from '../frame/motion';
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
title,
|
|
30
|
+
fromLabel,
|
|
31
|
+
toLabel,
|
|
32
|
+
items,
|
|
33
|
+
palette,
|
|
34
|
+
corner,
|
|
35
|
+
class: className = ''
|
|
36
|
+
}: GraphSlopeProps = $props();
|
|
37
|
+
|
|
38
|
+
function format(value: number) {
|
|
39
|
+
return value.toLocaleString('en-US', {
|
|
40
|
+
maximumFractionDigits: Number.isInteger(value) ? 0 : 1
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const rows = $derived.by(() =>
|
|
45
|
+
items.map((row) => {
|
|
46
|
+
const up = row.to > row.from;
|
|
47
|
+
const down = row.to < row.from;
|
|
48
|
+
const arrowRole = up
|
|
49
|
+
? toneRole(palette, 'primary')
|
|
50
|
+
: down
|
|
51
|
+
? toneRole(palette, 'secondary')
|
|
52
|
+
: 'empty';
|
|
53
|
+
const toRole = up
|
|
54
|
+
? 'accent'
|
|
55
|
+
: down
|
|
56
|
+
? toneRole(palette, 'secondary')
|
|
57
|
+
: 'foreground';
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
label: row.label,
|
|
61
|
+
spoken: `${row.label} from ${format(row.from)} to ${format(row.to)}`,
|
|
62
|
+
from: format(row.from),
|
|
63
|
+
to: format(row.to),
|
|
64
|
+
arrow: up || down ? '→' : '–',
|
|
65
|
+
arrowRole,
|
|
66
|
+
toRole
|
|
67
|
+
};
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<Graph {title} {corner} class={className}>
|
|
73
|
+
<GraphBody>
|
|
74
|
+
<div class="col">
|
|
75
|
+
<div class="head">
|
|
76
|
+
<span></span>
|
|
77
|
+
<span class="hlabel">{fromLabel}</span>
|
|
78
|
+
<span></span>
|
|
79
|
+
<span class="hlabel">{toLabel}</span>
|
|
80
|
+
</div>
|
|
81
|
+
<ul class="rows" role="list">
|
|
82
|
+
{#each rows as row, i (row.label)}
|
|
83
|
+
<li
|
|
84
|
+
class="row"
|
|
85
|
+
aria-label={row.spoken}
|
|
86
|
+
use:reveal={{ delay: stagger(i, 40), amount: 0.4 }}
|
|
87
|
+
>
|
|
88
|
+
<span class="label">{row.label}</span>
|
|
89
|
+
<span class="num muted">{row.from}</span>
|
|
90
|
+
<span
|
|
91
|
+
class="arrow"
|
|
92
|
+
aria-hidden="true"
|
|
93
|
+
class:c-accent={row.arrowRole === 'accent'}
|
|
94
|
+
class:c-accent2={row.arrowRole === 'accent2'}
|
|
95
|
+
class:c-accent3={row.arrowRole === 'accent3'}
|
|
96
|
+
class:c-muted={row.arrowRole === 'muted'}
|
|
97
|
+
class:c-frame={row.arrowRole === 'empty'}
|
|
98
|
+
>
|
|
99
|
+
{row.arrow}
|
|
100
|
+
</span>
|
|
101
|
+
<span
|
|
102
|
+
class="num"
|
|
103
|
+
class:c-accent={row.toRole === 'accent'}
|
|
104
|
+
class:c-accent2={row.toRole === 'accent2'}
|
|
105
|
+
class:c-accent3={row.toRole === 'accent3'}
|
|
106
|
+
class:c-muted={row.toRole === 'muted'}
|
|
107
|
+
class:c-fg={row.toRole === 'foreground'}
|
|
108
|
+
>
|
|
109
|
+
{row.to}
|
|
110
|
+
</span>
|
|
111
|
+
</li>
|
|
112
|
+
{/each}
|
|
113
|
+
</ul>
|
|
114
|
+
</div>
|
|
115
|
+
</GraphBody>
|
|
116
|
+
</Graph>
|
|
117
|
+
|
|
118
|
+
<style>
|
|
119
|
+
.col {
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
gap: 0.75rem;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.head {
|
|
126
|
+
display: grid;
|
|
127
|
+
grid-template-columns: minmax(0, 1fr) 6.5rem 2rem 6.5rem;
|
|
128
|
+
column-gap: 0.75rem;
|
|
129
|
+
align-items: end;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.hlabel {
|
|
133
|
+
text-align: right;
|
|
134
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.rows {
|
|
138
|
+
margin: 0;
|
|
139
|
+
padding: 0;
|
|
140
|
+
list-style: none;
|
|
141
|
+
display: flex;
|
|
142
|
+
flex-direction: column;
|
|
143
|
+
gap: 0.5rem;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.row {
|
|
147
|
+
display: grid;
|
|
148
|
+
grid-template-columns: minmax(0, 1fr) 6.5rem 2rem 6.5rem;
|
|
149
|
+
column-gap: 0.75rem;
|
|
150
|
+
align-items: baseline;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.label {
|
|
154
|
+
min-width: 0;
|
|
155
|
+
overflow: hidden;
|
|
156
|
+
text-overflow: ellipsis;
|
|
157
|
+
white-space: nowrap;
|
|
158
|
+
color: var(--graph-foreground, oklch(0.93 0 0));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.num {
|
|
162
|
+
text-align: right;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.muted {
|
|
166
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.arrow {
|
|
170
|
+
text-align: center;
|
|
171
|
+
user-select: none;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.c-accent {
|
|
175
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.c-accent2 {
|
|
179
|
+
color: var(--graph-accent-2, oklch(0.78 0.12 70));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.c-accent3 {
|
|
183
|
+
color: var(--graph-accent-3, oklch(0.75 0.1 200));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.c-muted {
|
|
187
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.c-frame {
|
|
191
|
+
color: var(--graph-frame, oklch(0.6 0 0 / 0.5));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.c-fg {
|
|
195
|
+
color: var(--graph-foreground, oklch(0.93 0 0));
|
|
196
|
+
}
|
|
197
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { GraphPalette } from '../frame/tone';
|
|
2
|
+
/** One before → after row. */
|
|
3
|
+
export interface SlopeItem {
|
|
4
|
+
label: string;
|
|
5
|
+
from: number;
|
|
6
|
+
to: number;
|
|
7
|
+
}
|
|
8
|
+
export interface GraphSlopeProps {
|
|
9
|
+
title: string;
|
|
10
|
+
fromLabel: string;
|
|
11
|
+
toLabel: string;
|
|
12
|
+
items: SlopeItem[];
|
|
13
|
+
palette?: GraphPalette;
|
|
14
|
+
corner?: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
declare const GraphSlope: import("svelte").Component<GraphSlopeProps, {}, "">;
|
|
18
|
+
type GraphSlope = ReturnType<typeof GraphSlope>;
|
|
19
|
+
export default GraphSlope;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { Glyphs } from '../frame/glyphs';
|
|
3
|
+
import type { GraphPalette } from '../frame/tone';
|
|
4
|
+
|
|
5
|
+
/** Packed 1ch sparkline, centered with a small gap. Never stretched. */
|
|
6
|
+
export interface GraphSparkProps {
|
|
7
|
+
title: string;
|
|
8
|
+
data: number[];
|
|
9
|
+
caption?: string;
|
|
10
|
+
glyphs?: Glyphs;
|
|
11
|
+
palette?: GraphPalette;
|
|
12
|
+
corner?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import Graph from '../frame/Graph.svelte';
|
|
19
|
+
import GraphBody from '../frame/GraphBody.svelte';
|
|
20
|
+
import { reveal } from '../frame/motion';
|
|
21
|
+
import { resolveGlyphs } from '../frame/glyphs';
|
|
22
|
+
import { isMonoPalette, toneRole } from '../frame/tone';
|
|
23
|
+
|
|
24
|
+
const SPARK_DEFAULT: readonly string[] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
title,
|
|
28
|
+
data,
|
|
29
|
+
caption,
|
|
30
|
+
glyphs,
|
|
31
|
+
palette,
|
|
32
|
+
corner,
|
|
33
|
+
class: className = ''
|
|
34
|
+
}: GraphSparkProps = $props();
|
|
35
|
+
|
|
36
|
+
const max = $derived(Math.max(...data, 1));
|
|
37
|
+
const last = $derived(data.length - 1);
|
|
38
|
+
const mono = $derived(isMonoPalette(palette));
|
|
39
|
+
const set = $derived(glyphs == null ? SPARK_DEFAULT : resolveGlyphs(glyphs));
|
|
40
|
+
const points = $derived(
|
|
41
|
+
data.map((value) => {
|
|
42
|
+
const index = Math.round((value / max) * (set.length - 1));
|
|
43
|
+
return set[index] ?? set[0] ?? '▁';
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<Graph {title} {corner} class={className}>
|
|
49
|
+
<GraphBody>
|
|
50
|
+
<div class="sparkwrap" use:reveal={{ amount: 0.5 }}>
|
|
51
|
+
<span class="spark" aria-hidden="true">
|
|
52
|
+
{#each points as glyph, index (`${glyph}-${index}`)}
|
|
53
|
+
{@const live = index === last}
|
|
54
|
+
{@const role = live ? 'accent' : toneRole(palette, 'secondary')}
|
|
55
|
+
<span
|
|
56
|
+
class="cell"
|
|
57
|
+
class:c-accent={role === 'accent'}
|
|
58
|
+
class:c-accent2={role === 'accent2'}
|
|
59
|
+
class:c-muted={role === 'muted'}
|
|
60
|
+
class:recede={!live && mono}
|
|
61
|
+
>{glyph}</span>
|
|
62
|
+
{/each}
|
|
63
|
+
</span>
|
|
64
|
+
{#if caption}
|
|
65
|
+
<p class="muted">{caption}</p>
|
|
66
|
+
{/if}
|
|
67
|
+
<span class="sr-only">Sparkline with {data.length} points{caption ? `. ${caption}` : ''}</span>
|
|
68
|
+
</div>
|
|
69
|
+
</GraphBody>
|
|
70
|
+
</Graph>
|
|
71
|
+
|
|
72
|
+
<style>
|
|
73
|
+
.sparkwrap {
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-direction: column;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: 1rem;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.spark {
|
|
81
|
+
display: flex;
|
|
82
|
+
justify-content: center;
|
|
83
|
+
column-gap: 0.125rem;
|
|
84
|
+
user-select: none;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.cell {
|
|
88
|
+
flex: none;
|
|
89
|
+
min-width: 1ch;
|
|
90
|
+
text-align: center;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.cell.recede {
|
|
94
|
+
opacity: 0.4;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.c-accent {
|
|
98
|
+
color: var(--graph-accent, oklch(0.78 0.17 155));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.c-accent2 {
|
|
102
|
+
color: var(--graph-accent-2, oklch(0.78 0.12 70));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.c-muted {
|
|
106
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.muted {
|
|
110
|
+
margin: 0;
|
|
111
|
+
color: var(--graph-muted, oklch(0.62 0 0));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.sr-only {
|
|
115
|
+
position: absolute;
|
|
116
|
+
width: 1px;
|
|
117
|
+
height: 1px;
|
|
118
|
+
padding: 0;
|
|
119
|
+
margin: -1px;
|
|
120
|
+
overflow: hidden;
|
|
121
|
+
clip-path: inset(50%);
|
|
122
|
+
white-space: nowrap;
|
|
123
|
+
border-width: 0;
|
|
124
|
+
}
|
|
125
|
+
</style>
|