hacklab 0.3.1 → 0.4.2
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/README.md +66 -85
- package/dist/belt.d.ts +25 -0
- package/dist/belt.d.ts.map +1 -0
- package/dist/belt.js +113 -0
- package/dist/belt.js.map +1 -0
- package/dist/commands/join.d.ts.map +1 -1
- package/dist/commands/join.js +100 -32
- package/dist/commands/join.js.map +1 -1
- package/dist/index.js +35 -3
- package/dist/index.js.map +1 -1
- package/dist/scanners/util.d.ts +10 -0
- package/dist/scanners/util.d.ts.map +1 -1
- package/dist/scanners/util.js +39 -0
- package/dist/scanners/util.js.map +1 -1
- package/dist/session.d.ts +15 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +18 -1
- package/dist/session.js.map +1 -1
- package/dist/share-card.d.ts +3 -0
- package/dist/share-card.d.ts.map +1 -1
- package/dist/share-card.js +327 -290
- package/dist/share-card.js.map +1 -1
- package/dist/share.d.ts +16 -0
- package/dist/share.d.ts.map +1 -0
- package/dist/share.js +59 -0
- package/dist/share.js.map +1 -0
- package/package.json +1 -1
package/dist/share-card.js
CHANGED
|
@@ -1,36 +1,30 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "satori/jsx/jsx-runtime";
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { Resvg } from '@resvg/resvg-js';
|
|
7
7
|
import satori from 'satori';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
tatsujin: '\u9054\u4EBA',
|
|
29
|
-
kensei: '\u5263\u8056',
|
|
30
|
-
oni: '\u9B3C',
|
|
31
|
-
ryujin: '\u9F8D\u795E',
|
|
32
|
-
sensei: '\u5E2B\u7BC4',
|
|
33
|
-
};
|
|
8
|
+
// ── Design tokens (DESIGN.md) ──────────────────────────────────────────────
|
|
9
|
+
// Phosphor Mint is the one signal colour: corner brackets, the belt progress
|
|
10
|
+
// bar, the ASCII proficiency bars, and the lit heatmap cells. Everything else
|
|
11
|
+
// is neutral graphite + grey. Numbers + the nametag are Orbitron; every other
|
|
12
|
+
// glyph is JetBrains Mono.
|
|
13
|
+
const ACCENT = '#82F5C6';
|
|
14
|
+
const ACCENT_TRACK = 'rgba(130,245,198,0.1)'; // unfilled bar / belt track
|
|
15
|
+
const PAGE_BG = '#0B0D0C';
|
|
16
|
+
const CARD_BG = '#101513';
|
|
17
|
+
const BORDER = '#1D221F';
|
|
18
|
+
const WHITE = '#FFFFFF';
|
|
19
|
+
const LABEL = '#B4B4B4';
|
|
20
|
+
const LABEL_DIM = '#9A9A9A';
|
|
21
|
+
const FOOTER_FG = '#8A8A8A';
|
|
22
|
+
// Activity heatmap: 26 weeks × 7 days, matching the web profile (column = week,
|
|
23
|
+
// row = day-of-week), so the CLI card and the site read identically.
|
|
24
|
+
const HEAT_COLS = 26;
|
|
25
|
+
const HEAT_ROWS = 7;
|
|
26
|
+
const HEAT_CELL = 14;
|
|
27
|
+
const HEAT_GAP = 3;
|
|
34
28
|
function formatTokens(n) {
|
|
35
29
|
if (n >= 1_000_000_000)
|
|
36
30
|
return `${(n / 1_000_000_000).toFixed(1)}B`;
|
|
@@ -40,304 +34,347 @@ function formatTokens(n) {
|
|
|
40
34
|
return `${(n / 1_000).toFixed(0)}K`;
|
|
41
35
|
return String(n);
|
|
42
36
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
// Fold a raw model id into a short display label, keeping the version:
|
|
38
|
+
// "claude-opus-4-1-20250805" → "OPUS 4.1"
|
|
39
|
+
// "claude-3-5-sonnet-latest" → "SONNET 3.5"
|
|
40
|
+
// "gpt-5.3-codex" → "GPT-5.3-CODEX"
|
|
41
|
+
export function shortModelName(raw) {
|
|
42
|
+
const s = raw.trim().toLowerCase();
|
|
43
|
+
if (!s)
|
|
44
|
+
return '';
|
|
45
|
+
// Drop a trailing release date (…-20250805) so it doesn't crowd the label.
|
|
46
|
+
const t = s.replace(/[-_]?\d{8}$/, '');
|
|
47
|
+
const tier = t.includes('opus')
|
|
48
|
+
? 'opus'
|
|
49
|
+
: t.includes('sonnet')
|
|
50
|
+
? 'sonnet'
|
|
51
|
+
: t.includes('haiku')
|
|
52
|
+
? 'haiku'
|
|
53
|
+
: null;
|
|
54
|
+
if (tier) {
|
|
55
|
+
const ver = t
|
|
56
|
+
.split(/[-_.\s]+/)
|
|
57
|
+
.filter((p) => /^\d+$/.test(p))
|
|
58
|
+
.slice(0, 2)
|
|
59
|
+
.join('.');
|
|
60
|
+
return (ver ? `${tier} ${ver}` : tier).toUpperCase();
|
|
61
|
+
}
|
|
62
|
+
return t.toUpperCase().slice(0, 16);
|
|
63
|
+
}
|
|
64
|
+
// ── Heatmap ─────────────────────────────────────────────────────────────────
|
|
65
|
+
// Opacity per intensity tier (0 = empty), matching the web's 18/38/62/100% mix.
|
|
66
|
+
const TIER_OPACITY = [0, 0.18, 0.38, 0.62, 1];
|
|
67
|
+
// Build the HEAT_ROWS × HEAT_COLS grid from real daily usage. Mirrors the web
|
|
68
|
+
// profile's buildRecentWeeks: columns are the latest 26 weeks ending on the
|
|
69
|
+
// Saturday on/after today, rows are Sun→Sat, intensity bucketed vs the window
|
|
70
|
+
// max. matrix[day][week] holds the tier for that date.
|
|
71
|
+
function buildMatrix(daily) {
|
|
72
|
+
const perDay = new Map();
|
|
73
|
+
for (const e of daily) {
|
|
74
|
+
perDay.set(e.date, (perDay.get(e.date) ?? 0) + e.tokens);
|
|
53
75
|
}
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
76
|
+
const today = new Date();
|
|
77
|
+
const gridEnd = new Date(today);
|
|
78
|
+
gridEnd.setUTCDate(today.getUTCDate() + (6 - today.getUTCDay()));
|
|
79
|
+
const gridStart = new Date(gridEnd);
|
|
80
|
+
gridStart.setUTCDate(gridEnd.getUTCDate() - HEAT_COLS * 7 + 1);
|
|
81
|
+
const values = [];
|
|
82
|
+
let max = 0;
|
|
83
|
+
for (let d = 0; d < HEAT_ROWS; d++) {
|
|
84
|
+
const row = [];
|
|
85
|
+
for (let w = 0; w < HEAT_COLS; w++) {
|
|
86
|
+
const date = new Date(gridStart);
|
|
87
|
+
date.setUTCDate(gridStart.getUTCDate() + w * 7 + d);
|
|
88
|
+
const v = perDay.get(date.toISOString().slice(0, 10)) ?? 0;
|
|
89
|
+
row.push(v);
|
|
90
|
+
if (v > max)
|
|
91
|
+
max = v;
|
|
92
|
+
}
|
|
93
|
+
values.push(row);
|
|
61
94
|
}
|
|
62
|
-
return (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
_jsx("div", { style: {
|
|
95
|
+
return values.map((row) => row.map((v) => {
|
|
96
|
+
if (v <= 0 || max === 0)
|
|
97
|
+
return 0;
|
|
98
|
+
const r = v / max;
|
|
99
|
+
return r > 0.75 ? 4 : r > 0.5 ? 3 : r > 0.25 ? 2 : 1;
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
function Heatmap({ matrix }) {
|
|
103
|
+
return (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: HEAT_GAP }, children: matrix.map((row) => (
|
|
104
|
+
// biome-ignore lint/correctness/useJsxKeyInIterable: satori has no keys
|
|
105
|
+
_jsx("div", { style: { display: 'flex', gap: HEAT_GAP }, children: row.map((tier) => tier === 0 ? (
|
|
106
|
+
// Empty slot: a tiny centred dot, not a full cell.
|
|
107
|
+
// biome-ignore lint/correctness/useJsxKeyInIterable: satori has no keys
|
|
108
|
+
_jsx("div", { style: {
|
|
109
|
+
display: 'flex',
|
|
110
|
+
width: HEAT_CELL,
|
|
111
|
+
height: HEAT_CELL,
|
|
112
|
+
alignItems: 'center',
|
|
113
|
+
justifyContent: 'center',
|
|
114
|
+
}, children: _jsx("div", { style: {
|
|
82
115
|
display: 'flex',
|
|
83
|
-
width:
|
|
84
|
-
height:
|
|
85
|
-
backgroundColor:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
width: 3,
|
|
117
|
+
height: 3,
|
|
118
|
+
backgroundColor: 'rgba(130,245,198,0.35)',
|
|
119
|
+
} }) })) : (
|
|
120
|
+
// biome-ignore lint/correctness/useJsxKeyInIterable: satori has no keys
|
|
121
|
+
_jsx("div", { style: {
|
|
122
|
+
display: 'flex',
|
|
123
|
+
width: HEAT_CELL,
|
|
124
|
+
height: HEAT_CELL,
|
|
125
|
+
backgroundColor: `rgba(130,245,198,${TIER_OPACITY[tier]})`,
|
|
126
|
+
} }))) }))) }));
|
|
90
127
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
{ label: 'Cursor', short: 'CUR', tokens: data.toolBreakdown.cursor },
|
|
108
|
-
].filter((t) => t.tokens > 0);
|
|
109
|
-
const toolMax = Math.max(...tools.map((t) => t.tokens), 1);
|
|
110
|
-
const modelMax = data.models.length > 0 ? data.models[0]?.tokens : 1;
|
|
128
|
+
// ── Proficiency bar: solid mint fill over a 10%-mint track. ──────────────────
|
|
129
|
+
function Bar({ pct }) {
|
|
130
|
+
const fill = Math.max(0, Math.min(100, pct));
|
|
131
|
+
return (_jsx("div", { style: {
|
|
132
|
+
display: 'flex',
|
|
133
|
+
width: 64,
|
|
134
|
+
height: 6,
|
|
135
|
+
backgroundColor: ACCENT_TRACK,
|
|
136
|
+
}, children: _jsx("div", { style: {
|
|
137
|
+
display: 'flex',
|
|
138
|
+
height: 6,
|
|
139
|
+
width: `${fill}%`,
|
|
140
|
+
backgroundColor: ACCENT,
|
|
141
|
+
} }) }));
|
|
142
|
+
}
|
|
143
|
+
function StatCard({ value, label }) {
|
|
111
144
|
return (_jsxs("div", { style: {
|
|
112
|
-
width: '100%',
|
|
113
|
-
height: '100%',
|
|
114
145
|
display: 'flex',
|
|
115
146
|
flexDirection: 'column',
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
147
|
+
flex: 1,
|
|
148
|
+
justifyContent: 'center',
|
|
149
|
+
backgroundColor: CARD_BG,
|
|
150
|
+
border: `1px solid ${BORDER}`,
|
|
151
|
+
paddingLeft: 16,
|
|
152
|
+
gap: 20,
|
|
153
|
+
}, children: [_jsx("div", { style: {
|
|
121
154
|
display: 'flex',
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
padding: '6px 14px',
|
|
130
|
-
fontSize: 18,
|
|
131
|
-
fontWeight: 700,
|
|
132
|
-
}, children: [kanji, " ", data.title, " lv.", data.level] }), _jsxs("div", { style: {
|
|
133
|
-
display: 'flex',
|
|
134
|
-
fontSize: 22,
|
|
135
|
-
color: '#A3A3A3',
|
|
136
|
-
}, children: ["@", data.handle] })] }), _jsxs("div", { style: { display: 'flex', fontSize: 20, color: accent }, children: ["#", data.rank] })] }), _jsxs("div", { style: {
|
|
155
|
+
fontFamily: 'Orbitron',
|
|
156
|
+
fontWeight: 700,
|
|
157
|
+
fontSize: 33,
|
|
158
|
+
lineHeight: 1,
|
|
159
|
+
letterSpacing: 0.5,
|
|
160
|
+
color: WHITE,
|
|
161
|
+
}, children: value }), _jsx("div", { style: {
|
|
137
162
|
display: 'flex',
|
|
163
|
+
fontSize: 16,
|
|
164
|
+
letterSpacing: 2,
|
|
165
|
+
color: LABEL,
|
|
166
|
+
}, children: label })] }));
|
|
167
|
+
}
|
|
168
|
+
function PillStat({ value, label }) {
|
|
169
|
+
return (_jsxs("div", { style: {
|
|
170
|
+
display: 'flex',
|
|
171
|
+
flex: 1,
|
|
172
|
+
alignItems: 'center',
|
|
173
|
+
justifyContent: 'space-between',
|
|
174
|
+
backgroundColor: CARD_BG,
|
|
175
|
+
border: `1px solid ${BORDER}`,
|
|
176
|
+
paddingLeft: 17,
|
|
177
|
+
paddingRight: 17,
|
|
178
|
+
}, children: [_jsx("div", { style: {
|
|
179
|
+
display: 'flex',
|
|
180
|
+
fontFamily: 'Orbitron',
|
|
181
|
+
fontWeight: 700,
|
|
182
|
+
fontSize: 20,
|
|
183
|
+
letterSpacing: 0.5,
|
|
184
|
+
color: WHITE,
|
|
185
|
+
}, children: value }), _jsx("div", { style: {
|
|
186
|
+
display: 'flex',
|
|
187
|
+
fontSize: 16,
|
|
188
|
+
letterSpacing: 2,
|
|
189
|
+
color: LABEL,
|
|
190
|
+
}, children: label })] }));
|
|
191
|
+
}
|
|
192
|
+
function CornerBracket({ pos }) {
|
|
193
|
+
const arm = 11;
|
|
194
|
+
const thick = 2;
|
|
195
|
+
const inset = 0;
|
|
196
|
+
const vert = pos[0] === 't' ? 'top' : 'bottom';
|
|
197
|
+
const horiz = pos[1] === 'l' ? 'left' : 'right';
|
|
198
|
+
return (_jsx("div", { style: {
|
|
199
|
+
display: 'flex',
|
|
200
|
+
position: 'absolute',
|
|
201
|
+
width: arm,
|
|
202
|
+
height: arm,
|
|
203
|
+
[vert]: inset,
|
|
204
|
+
[horiz]: inset,
|
|
205
|
+
[`border${vert === 'top' ? 'Top' : 'Bottom'}`]: `${thick}px solid ${ACCENT}`,
|
|
206
|
+
[`border${horiz === 'left' ? 'Left' : 'Right'}`]: `${thick}px solid ${ACCENT}`,
|
|
207
|
+
} }));
|
|
208
|
+
}
|
|
209
|
+
function ShareCard({ data, matrix, avatar, }) {
|
|
210
|
+
const models = data.models.slice(0, 4);
|
|
211
|
+
const modelMax = models.length > 0 ? (models[0]?.tokens ?? 1) : 1;
|
|
212
|
+
const cost = Math.round(data.estimatedCost).toLocaleString('en-US');
|
|
213
|
+
const progress = Math.max(0, Math.min(100, data.progressPercent));
|
|
214
|
+
const initial = (data.handle[0] ?? '?').toUpperCase();
|
|
215
|
+
return (_jsxs("div", { style: {
|
|
216
|
+
position: 'relative',
|
|
217
|
+
width: 800,
|
|
218
|
+
height: 500,
|
|
219
|
+
display: 'flex',
|
|
220
|
+
flexDirection: 'column',
|
|
221
|
+
backgroundColor: PAGE_BG,
|
|
222
|
+
padding: 16,
|
|
223
|
+
fontFamily: 'JetBrains Mono',
|
|
224
|
+
color: WHITE,
|
|
225
|
+
}, children: [_jsx(CornerBracket, { pos: 'tl' }), _jsx(CornerBracket, { pos: 'tr' }), _jsx(CornerBracket, { pos: 'bl' }), _jsx(CornerBracket, { pos: 'br' }), _jsxs("div", { style: {
|
|
226
|
+
display: 'flex',
|
|
227
|
+
height: 70,
|
|
228
|
+
alignItems: 'center',
|
|
138
229
|
justifyContent: 'space-between',
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
230
|
+
backgroundColor: CARD_BG,
|
|
231
|
+
border: `1px solid ${BORDER}`,
|
|
232
|
+
paddingLeft: 12,
|
|
233
|
+
paddingRight: 16,
|
|
234
|
+
}, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 18 }, children: [_jsx("div", { style: {
|
|
142
235
|
display: 'flex',
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
236
|
+
width: 46,
|
|
237
|
+
height: 46,
|
|
238
|
+
border: `1px solid ${BORDER}`,
|
|
239
|
+
backgroundColor: '#0C100E',
|
|
240
|
+
alignItems: 'center',
|
|
241
|
+
justifyContent: 'center',
|
|
242
|
+
overflow: 'hidden',
|
|
243
|
+
}, children: avatar ? (
|
|
244
|
+
// biome-ignore lint/performance/noImgElement: satori rasterises to SVG, not a DOM img
|
|
245
|
+
_jsx("img", { src: avatar, width: 46, height: 46, alt: '' })) : (_jsx("div", { style: {
|
|
246
|
+
display: 'flex',
|
|
247
|
+
fontFamily: 'Orbitron',
|
|
248
|
+
fontWeight: 700,
|
|
249
|
+
fontSize: 22,
|
|
250
|
+
color: ACCENT,
|
|
251
|
+
}, children: initial })) }), _jsxs("div", { style: {
|
|
148
252
|
display: 'flex',
|
|
253
|
+
fontFamily: 'Orbitron',
|
|
254
|
+
fontWeight: 700,
|
|
149
255
|
fontSize: 20,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}, children: "
|
|
256
|
+
letterSpacing: 0.5,
|
|
257
|
+
color: WHITE,
|
|
258
|
+
}, children: ["@", data.handle.toUpperCase()] })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 14 }, children: [_jsxs("div", { style: {
|
|
153
259
|
display: 'flex',
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}, children: [_jsxs("div", { style: {
|
|
161
|
-
display: 'flex',
|
|
162
|
-
flexDirection: 'column',
|
|
163
|
-
flex: 1,
|
|
164
|
-
gap: 6,
|
|
165
|
-
}, children: [_jsx("div", { style: {
|
|
260
|
+
fontFamily: 'Orbitron',
|
|
261
|
+
fontWeight: 700,
|
|
262
|
+
fontSize: 17,
|
|
263
|
+
letterSpacing: 1,
|
|
264
|
+
color: WHITE,
|
|
265
|
+
}, children: ["L", data.level] }), _jsx("div", { style: {
|
|
166
266
|
display: 'flex',
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}, children: "
|
|
171
|
-
const pct = Math.round((tool.tokens / toolMax) * 100);
|
|
172
|
-
return (
|
|
173
|
-
// biome-ignore lint/correctness/useJsxKeyInIterable: satori JSX types don't support key
|
|
174
|
-
_jsxs("div", { style: {
|
|
267
|
+
width: 104,
|
|
268
|
+
height: 8,
|
|
269
|
+
backgroundColor: ACCENT_TRACK,
|
|
270
|
+
}, children: _jsx("div", { style: {
|
|
175
271
|
display: 'flex',
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
fontSize: 13,
|
|
181
|
-
color: '#525252',
|
|
182
|
-
width: 32,
|
|
183
|
-
}, children: tool.short }), _jsx("div", { style: {
|
|
184
|
-
display: 'flex',
|
|
185
|
-
flex: 1,
|
|
186
|
-
height: 16,
|
|
187
|
-
backgroundColor: '#171717',
|
|
188
|
-
}, children: _jsx("div", { style: {
|
|
189
|
-
display: 'flex',
|
|
190
|
-
width: `${pct}%`,
|
|
191
|
-
height: '100%',
|
|
192
|
-
backgroundColor: accent,
|
|
193
|
-
} }) }), _jsx("div", { style: {
|
|
194
|
-
display: 'flex',
|
|
195
|
-
fontSize: 13,
|
|
196
|
-
color: '#A3A3A3',
|
|
197
|
-
width: 60,
|
|
198
|
-
justifyContent: 'flex-end',
|
|
199
|
-
}, children: formatTokens(tool.tokens) })] }));
|
|
200
|
-
})] }), _jsxs("div", { style: {
|
|
272
|
+
width: `${progress}%`,
|
|
273
|
+
height: 8,
|
|
274
|
+
backgroundColor: ACCENT,
|
|
275
|
+
} }) })] })] }), _jsxs("div", { style: { display: 'flex', height: 114, gap: 26, marginTop: 18 }, children: [_jsx(StatCard, { value: formatTokens(data.tokensTotal), label: 'TOKENS BURNED' }), _jsx(StatCard, { value: `${data.streak}D`, label: 'STREAK' }), _jsx(StatCard, { value: `$${cost}`, label: 'EST. COST' })] }), _jsxs("div", { style: { display: 'flex', height: 142, gap: 18, marginTop: 18 }, children: [_jsx("div", { style: {
|
|
201
276
|
display: 'flex',
|
|
277
|
+
width: 284,
|
|
202
278
|
flexDirection: 'column',
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
279
|
+
justifyContent: 'center',
|
|
280
|
+
backgroundColor: CARD_BG,
|
|
281
|
+
border: `1px solid ${BORDER}`,
|
|
282
|
+
paddingLeft: 17,
|
|
283
|
+
paddingRight: 17,
|
|
284
|
+
gap: 14,
|
|
285
|
+
}, children: models.map((m) => (
|
|
286
|
+
// biome-ignore lint/correctness/useJsxKeyInIterable: satori has no keys
|
|
287
|
+
_jsxs("div", { style: {
|
|
288
|
+
display: 'flex',
|
|
289
|
+
alignItems: 'center',
|
|
290
|
+
justifyContent: 'space-between',
|
|
291
|
+
}, children: [_jsx("div", { style: {
|
|
216
292
|
display: 'flex',
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
fontSize: 11,
|
|
222
|
-
color: '#525252',
|
|
223
|
-
width: 110,
|
|
224
|
-
}, children: shortName }), _jsx("div", { style: {
|
|
225
|
-
display: 'flex',
|
|
226
|
-
flex: 1,
|
|
227
|
-
height: 16,
|
|
228
|
-
backgroundColor: '#171717',
|
|
229
|
-
}, children: _jsx("div", { style: {
|
|
230
|
-
display: 'flex',
|
|
231
|
-
width: `${pct}%`,
|
|
232
|
-
height: '100%',
|
|
233
|
-
backgroundColor: accent,
|
|
234
|
-
opacity: 0.7,
|
|
235
|
-
} }) }), _jsx("div", { style: {
|
|
236
|
-
display: 'flex',
|
|
237
|
-
fontSize: 11,
|
|
238
|
-
color: '#A3A3A3',
|
|
239
|
-
width: 50,
|
|
240
|
-
justifyContent: 'flex-end',
|
|
241
|
-
}, children: formatTokens(model.tokens) })] }));
|
|
242
|
-
})] })] }), _jsxs("div", { style: { display: 'flex', gap: 16, marginBottom: 24 }, children: [_jsxs("div", { style: {
|
|
293
|
+
fontSize: 15,
|
|
294
|
+
letterSpacing: 0.4,
|
|
295
|
+
color: LABEL_DIM,
|
|
296
|
+
}, children: shortModelName(m.name) }), _jsx(Bar, { pct: (m.tokens / modelMax) * 100 })] }))) }), _jsx("div", { style: {
|
|
243
297
|
display: 'flex',
|
|
244
|
-
flexDirection: 'column',
|
|
245
|
-
flex: 1,
|
|
246
|
-
backgroundColor: '#0a0a0a',
|
|
247
|
-
border: '1px solid #262626',
|
|
248
|
-
padding: 16,
|
|
249
|
-
alignItems: 'center',
|
|
250
|
-
}, children: [_jsx("div", { style: {
|
|
251
|
-
display: 'flex',
|
|
252
|
-
fontSize: 11,
|
|
253
|
-
color: '#525252',
|
|
254
|
-
}, children: "STREAK" }), _jsxs("div", { style: {
|
|
255
|
-
display: 'flex',
|
|
256
|
-
fontSize: 28,
|
|
257
|
-
fontWeight: 700,
|
|
258
|
-
color: accent,
|
|
259
|
-
}, children: [data.streak, "d"] })] }), _jsxs("div", { style: {
|
|
260
|
-
display: 'flex',
|
|
261
|
-
flexDirection: 'column',
|
|
262
|
-
flex: 1,
|
|
263
|
-
backgroundColor: '#0a0a0a',
|
|
264
|
-
border: '1px solid #262626',
|
|
265
|
-
padding: 16,
|
|
266
|
-
alignItems: 'center',
|
|
267
|
-
}, children: [_jsx("div", { style: {
|
|
268
|
-
display: 'flex',
|
|
269
|
-
fontSize: 11,
|
|
270
|
-
color: '#525252',
|
|
271
|
-
}, children: "BEST STREAK" }), _jsxs("div", { style: {
|
|
272
|
-
display: 'flex',
|
|
273
|
-
fontSize: 28,
|
|
274
|
-
fontWeight: 700,
|
|
275
|
-
color: accent,
|
|
276
|
-
}, children: [data.longestStreak, "d"] })] }), _jsxs("div", { style: {
|
|
277
|
-
display: 'flex',
|
|
278
|
-
flexDirection: 'column',
|
|
279
|
-
flex: 1,
|
|
280
|
-
backgroundColor: '#0a0a0a',
|
|
281
|
-
border: '1px solid #262626',
|
|
282
|
-
padding: 16,
|
|
283
|
-
alignItems: 'center',
|
|
284
|
-
}, children: [_jsx("div", { style: {
|
|
285
|
-
display: 'flex',
|
|
286
|
-
fontSize: 11,
|
|
287
|
-
color: '#525252',
|
|
288
|
-
}, children: "EST. COST" }), _jsxs("div", { style: {
|
|
289
|
-
display: 'flex',
|
|
290
|
-
fontSize: 28,
|
|
291
|
-
fontWeight: 700,
|
|
292
|
-
color: accent,
|
|
293
|
-
}, children: ["$", Math.round(data.estimatedCost)] })] }), _jsxs("div", { style: {
|
|
294
|
-
display: 'flex',
|
|
295
|
-
flexDirection: 'column',
|
|
296
298
|
flex: 1,
|
|
297
|
-
backgroundColor: '#0a0a0a',
|
|
298
|
-
border: '1px solid #262626',
|
|
299
|
-
padding: 16,
|
|
300
299
|
alignItems: 'center',
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}, children: "RANK" }), _jsxs("div", { style: {
|
|
306
|
-
display: 'flex',
|
|
307
|
-
fontSize: 28,
|
|
308
|
-
fontWeight: 700,
|
|
309
|
-
color: accent,
|
|
310
|
-
}, children: ["#", data.rank] })] })] }), _jsxs("div", { style: {
|
|
300
|
+
justifyContent: 'center',
|
|
301
|
+
backgroundColor: CARD_BG,
|
|
302
|
+
border: `1px solid ${BORDER}`,
|
|
303
|
+
}, children: _jsx(Heatmap, { matrix: matrix }) })] }), _jsxs("div", { style: { display: 'flex', height: 42, gap: 17, marginTop: 18 }, children: [_jsx(PillStat, { value: `${data.longestStreak}D`, label: 'BEST STREAK' }), _jsx(PillStat, { value: `#${data.rank}`, label: 'RANK' }), _jsx(PillStat, { value: String(data.followers ?? 0), label: 'FOLLOWERS' })] }), _jsxs("div", { style: {
|
|
311
304
|
display: 'flex',
|
|
312
305
|
justifyContent: 'space-between',
|
|
313
|
-
alignItems: 'center',
|
|
314
306
|
marginTop: 'auto',
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}, children: [_jsxs("div", { style: { display: 'flex', fontSize:
|
|
307
|
+
paddingLeft: 2,
|
|
308
|
+
paddingRight: 2,
|
|
309
|
+
}, children: [_jsxs("div", { style: { display: 'flex', fontSize: 15, color: FOOTER_FG }, children: ["hacklab.so/", data.handle] }), _jsx("div", { style: { display: 'flex', fontSize: 15, color: FOOTER_FG }, children: "#sweatysunday" })] })] }));
|
|
310
|
+
}
|
|
311
|
+
// ── Font loading (disk-cached so repeat renders are offline + fast) ──────────
|
|
312
|
+
const FONT_SOURCES = {
|
|
313
|
+
'orbitron-700.woff': 'https://cdn.jsdelivr.net/fontsource/fonts/orbitron@latest/latin-700-normal.woff',
|
|
314
|
+
'jetbrains-400.woff': 'https://cdn.jsdelivr.net/fontsource/fonts/jetbrains-mono@latest/latin-400-normal.woff',
|
|
315
|
+
'jetbrains-500.woff': 'https://cdn.jsdelivr.net/fontsource/fonts/jetbrains-mono@latest/latin-500-normal.woff',
|
|
316
|
+
};
|
|
317
|
+
async function loadFont(name) {
|
|
318
|
+
const dir = join(homedir(), '.hacklab', 'fonts');
|
|
319
|
+
const file = join(dir, name);
|
|
320
|
+
try {
|
|
321
|
+
const cached = await readFile(file);
|
|
322
|
+
return cached.buffer.slice(cached.byteOffset, cached.byteOffset + cached.byteLength);
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
const url = FONT_SOURCES[name];
|
|
326
|
+
if (!url)
|
|
327
|
+
throw new Error(`unknown font: ${name}`);
|
|
328
|
+
const buf = await fetch(url).then((r) => r.arrayBuffer());
|
|
329
|
+
await mkdir(dir, { recursive: true });
|
|
330
|
+
await writeFile(file, Buffer.from(buf));
|
|
331
|
+
return buf;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
async function loadAvatar(url) {
|
|
335
|
+
if (!url)
|
|
336
|
+
return null;
|
|
337
|
+
try {
|
|
338
|
+
const res = await fetch(url);
|
|
339
|
+
if (!res.ok)
|
|
340
|
+
return null;
|
|
341
|
+
const type = res.headers.get('content-type') ?? 'image/png';
|
|
342
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
343
|
+
return `data:${type};base64,${buf.toString('base64')}`;
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
318
348
|
}
|
|
319
349
|
export async function generateShareCard(data) {
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
350
|
+
const [orbitron700, jetbrains400, jetbrains500, avatar] = await Promise.all([
|
|
351
|
+
loadFont('orbitron-700.woff'),
|
|
352
|
+
loadFont('jetbrains-400.woff'),
|
|
353
|
+
loadFont('jetbrains-500.woff'),
|
|
354
|
+
loadAvatar(data.avatarUrl),
|
|
325
355
|
]);
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
356
|
+
const matrix = buildMatrix(data.dailyActivity);
|
|
357
|
+
const svg = await satori(_jsx(ShareCard, { data: data, matrix: matrix, avatar: avatar }), {
|
|
358
|
+
width: 800,
|
|
359
|
+
height: 500,
|
|
329
360
|
fonts: [
|
|
330
|
-
{ name: '
|
|
361
|
+
{ name: 'Orbitron', data: orbitron700, weight: 700, style: 'normal' },
|
|
362
|
+
{
|
|
363
|
+
name: 'JetBrains Mono',
|
|
364
|
+
data: jetbrains400,
|
|
365
|
+
weight: 400,
|
|
366
|
+
style: 'normal',
|
|
367
|
+
},
|
|
331
368
|
{
|
|
332
|
-
name: '
|
|
333
|
-
data:
|
|
334
|
-
weight:
|
|
369
|
+
name: 'JetBrains Mono',
|
|
370
|
+
data: jetbrains500,
|
|
371
|
+
weight: 500,
|
|
335
372
|
style: 'normal',
|
|
336
373
|
},
|
|
337
374
|
],
|
|
338
375
|
});
|
|
339
376
|
const resvg = new Resvg(svg, {
|
|
340
|
-
fitTo: { mode: 'width', value:
|
|
377
|
+
fitTo: { mode: 'width', value: 1600 },
|
|
341
378
|
});
|
|
342
379
|
const png = Buffer.from(resvg.render().asPng());
|
|
343
380
|
const dir = join(homedir(), '.hacklab');
|