prompt-profile 0.1.0 → 0.1.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 +25 -9
- package/dist/bin.js +236 -150
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,14 +5,27 @@ personal profile.
|
|
|
5
5
|
|
|
6
6
|

|
|
7
7
|
|
|
8
|
-
[Sample HTML report](https://
|
|
8
|
+
[Sample HTML report](https://erictsai83.github.io/prompt-profile/sample-report.html)
|
|
9
9
|
· [Sample profile JSON](docs/sample-profile.json) (all sample data is hand-written and fictional).
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
### A real report
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
The author's own report, from about 1,700 real prompts over six months, published as written:
|
|
14
|
+
|
|
15
|
+
- [Full report](https://erictsai83.github.io/prompt-profile/demo/full/report.html): what you see on your own machine, with quotes from
|
|
16
|
+
real prompts, suggested rewrites, project names, exact dates and fun facts.
|
|
17
|
+
- [Shareable report](https://erictsai83.github.io/prompt-profile/demo/without-personal/report.html): the same profile rendered with
|
|
18
|
+
`--exclude personal`: no quotes, rewrites or fun facts, projects shown as "Project A, B…",
|
|
19
|
+
dates by month. This is what you would post publicly.
|
|
20
|
+
|
|
21
|
+
The share card is the same for both: it never contains quotes or project names. The full report
|
|
22
|
+
in every look (`--look`; render only, no new analysis):
|
|
23
|
+
|
|
24
|
+
| [Classic](https://erictsai83.github.io/prompt-profile/demo/full/report.html) (default) | [Signal](https://erictsai83.github.io/prompt-profile/demo/signal/report.html) |
|
|
25
|
+
| -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
26
|
+
|  |  |
|
|
27
|
+
| [Afterhours](https://erictsai83.github.io/prompt-profile/demo/afterhours/report.html) | [Studio](https://erictsai83.github.io/prompt-profile/demo/studio/report.html) |
|
|
28
|
+
|  |  |
|
|
16
29
|
|
|
17
30
|
## Quick start
|
|
18
31
|
|
|
@@ -22,24 +35,27 @@ The same profile in the other looks (`--look signal`, `afterhours`, `studio`):
|
|
|
22
35
|
2. Run it (Node.js ≥ 22):
|
|
23
36
|
|
|
24
37
|
```bash
|
|
25
|
-
npx prompt-profile
|
|
38
|
+
npx prompt-profile@latest
|
|
26
39
|
```
|
|
27
40
|
|
|
41
|
+
`@latest` makes npx fetch the newest version instead of reusing one it cached earlier.
|
|
42
|
+
|
|
28
43
|
3. It reads your local history, shows how many prompts it found and how many CLI calls the
|
|
29
44
|
analysis will take, and asks `Continue?`. The calls use your Claude / ChatGPT subscription
|
|
30
|
-
quota. To see what would be sent first, run `npx prompt-profile --dry-run` (calls nothing).
|
|
45
|
+
quota. To see what would be sent first, run `npx prompt-profile@latest --dry-run` (calls nothing).
|
|
31
46
|
4. When it is done, the report opens in your browser. Everything is written to
|
|
32
47
|
`./prompt-profile-report/`.
|
|
33
48
|
|
|
34
|
-
Want to look around for free first? `npx prompt-profile stats` shows statistics without any LLM
|
|
49
|
+
Want to look around for free first? `npx prompt-profile@latest stats` shows statistics without any LLM
|
|
35
50
|
call.
|
|
36
51
|
|
|
37
52
|
## Install
|
|
38
53
|
|
|
39
54
|
```bash
|
|
40
|
-
npx prompt-profile
|
|
55
|
+
npx prompt-profile@latest # run without installing (always the newest version)
|
|
41
56
|
npm i -g prompt-profile # or install the `prompt-profile` command
|
|
42
57
|
prompt-profile --help
|
|
58
|
+
npm i -g prompt-profile@latest # update the installed command
|
|
43
59
|
```
|
|
44
60
|
|
|
45
61
|
No Node.js? Every [GitHub Release](https://github.com/EricTsai83/prompt-profile/releases) has a
|
package/dist/bin.js
CHANGED
|
@@ -1302,7 +1302,7 @@ const fileCacheLayer = (dir) => Layer.effect(AnalysisCache, Effect.gen(function*
|
|
|
1302
1302
|
* Package version. Injected by tsdown at build time; during development and tests
|
|
1303
1303
|
* (tsx / vitest) it falls back to reading package.json.
|
|
1304
1304
|
*/
|
|
1305
|
-
const VERSION = "0.1.
|
|
1305
|
+
const VERSION = "0.1.2";
|
|
1306
1306
|
// src/cli/errors.ts
|
|
1307
1307
|
/** No prompts found at all, or none in the selected range. */
|
|
1308
1308
|
var NoData = class extends Data.TaggedError("NoData") {};
|
|
@@ -2401,7 +2401,94 @@ const withRetry = (runner, options = {}) => {
|
|
|
2401
2401
|
generate
|
|
2402
2402
|
};
|
|
2403
2403
|
};
|
|
2404
|
-
// src/report/
|
|
2404
|
+
// src/report/derive.ts
|
|
2405
|
+
/** The busiest four consecutive hours (wrapping past midnight); `end` is exclusive. */
|
|
2406
|
+
const chronotype = (s) => {
|
|
2407
|
+
const total = s.hourHistogram.reduce((a, b) => a + b, 0);
|
|
2408
|
+
if (total === 0) return null;
|
|
2409
|
+
let best = {
|
|
2410
|
+
start: 0,
|
|
2411
|
+
sum: -1
|
|
2412
|
+
};
|
|
2413
|
+
for (let start = 0; start < 24; start++) {
|
|
2414
|
+
let sum = 0;
|
|
2415
|
+
for (let i = 0; i < 4; i++) sum += s.hourHistogram[(start + i) % 24];
|
|
2416
|
+
if (sum > best.sum) best = {
|
|
2417
|
+
start,
|
|
2418
|
+
sum
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
return {
|
|
2422
|
+
start: best.start,
|
|
2423
|
+
end: (best.start + 4) % 24,
|
|
2424
|
+
share: best.sum / total
|
|
2425
|
+
};
|
|
2426
|
+
};
|
|
2427
|
+
/** Mean prompts per session, one decimal; null without sessions. */
|
|
2428
|
+
const promptsPerSession = (s) => s.totalSessions === 0 ? null : Math.round(10 * s.totalPrompts / s.totalSessions) / 10;
|
|
2429
|
+
/** "1 in N prompts corrects the AI"; null without corrections. */
|
|
2430
|
+
const correctionEvery = (s) => s.correctionRate <= 0 ? null : Math.round(1 / s.correctionRate);
|
|
2431
|
+
/**
|
|
2432
|
+
* The dominant project per month, with consecutive months of the same project merged. "Other
|
|
2433
|
+
* folders" only names a month when no named project has prompts in it.
|
|
2434
|
+
*/
|
|
2435
|
+
const chapters = (s) => {
|
|
2436
|
+
const slots = s.activity.projects.length + 1;
|
|
2437
|
+
const months = /* @__PURE__ */ new Map();
|
|
2438
|
+
for (const d of s.activity.days) {
|
|
2439
|
+
const key = d.date.slice(0, 7);
|
|
2440
|
+
const counts = months.get(key) ?? Array.from({ length: slots }, () => 0);
|
|
2441
|
+
d.byProject.forEach((n, i) => counts[i] += n);
|
|
2442
|
+
months.set(key, counts);
|
|
2443
|
+
}
|
|
2444
|
+
const out = [];
|
|
2445
|
+
for (const [month, counts] of [...months.entries()].toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
2446
|
+
const named = counts.slice(0, slots - 1);
|
|
2447
|
+
const pool = named.some((n) => n > 0) ? named : counts;
|
|
2448
|
+
const slot = pool.indexOf(Math.max(...pool));
|
|
2449
|
+
const prompts = counts.reduce((a, b) => a + b, 0);
|
|
2450
|
+
const last = out.at(-1);
|
|
2451
|
+
if (last && last.slot === slot) out[out.length - 1] = {
|
|
2452
|
+
...last,
|
|
2453
|
+
to: month,
|
|
2454
|
+
prompts: last.prompts + prompts
|
|
2455
|
+
};
|
|
2456
|
+
else out.push({
|
|
2457
|
+
from: month,
|
|
2458
|
+
to: month,
|
|
2459
|
+
slot,
|
|
2460
|
+
prompts
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
return out;
|
|
2464
|
+
};
|
|
2465
|
+
/**
|
|
2466
|
+
* The 24 radial bars of the hour clock (260 × 260, centre 130): length by prompts in that hour,
|
|
2467
|
+
* `on` for the busiest window. Shared by the report's hero and the classic card.
|
|
2468
|
+
*/
|
|
2469
|
+
const clockBars = (hours, window) => {
|
|
2470
|
+
const max = Math.max(1, ...hours);
|
|
2471
|
+
const inWindow = (h) => window !== null && (h - window.start + 24) % 24 < (window.end - window.start + 24) % 24;
|
|
2472
|
+
return hours.map((n, h) => {
|
|
2473
|
+
const a = h / 24 * Math.PI * 2 - Math.PI / 2;
|
|
2474
|
+
const r0 = 62;
|
|
2475
|
+
const r1 = 66 + 54 * n / max;
|
|
2476
|
+
const p = (r) => `${(130 + Math.cos(a) * r).toFixed(1)} ${(130 + Math.sin(a) * r).toFixed(1)}`;
|
|
2477
|
+
return {
|
|
2478
|
+
from: p(r0),
|
|
2479
|
+
to: p(r1),
|
|
2480
|
+
on: inWindow(h)
|
|
2481
|
+
};
|
|
2482
|
+
});
|
|
2483
|
+
};
|
|
2484
|
+
/** The report's hour clock as a standalone SVG (no hour labels: the card has no SVG fonts). */
|
|
2485
|
+
const clockSvg = (hours, color) => {
|
|
2486
|
+
return {
|
|
2487
|
+
svg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 260 260"><circle cx="130" cy="130" r="60" fill="none" stroke="${color}" stroke-opacity="0.25"/>${clockBars(hours, chronotype({ hourHistogram: hours })).map((b) => `<path d="M${b.from}L${b.to}" stroke="${color}" stroke-width="7" stroke-linecap="round" stroke-opacity="${b.on ? 1 : .35}"/>`).join("")}</svg>`,
|
|
2488
|
+
width: 260,
|
|
2489
|
+
height: 260
|
|
2490
|
+
};
|
|
2491
|
+
};
|
|
2405
2492
|
/** A look's signature graphic as a standalone SVG, drawn from the 24-hour histogram only. */
|
|
2406
2493
|
const graphicSvg = (kind, hours, identity) => {
|
|
2407
2494
|
const max = Math.max(1, ...hours);
|
|
@@ -2603,6 +2690,31 @@ const LOOKS = [
|
|
|
2603
2690
|
];
|
|
2604
2691
|
/** The look's data, or null for `classic`. */
|
|
2605
2692
|
const lookOf = (id) => LOOKS.find((l) => l.id === id) ?? null;
|
|
2693
|
+
/** Colors of the classic report (no look); the classic card uses the light ones. */
|
|
2694
|
+
const CLASSIC_TOKENS = {
|
|
2695
|
+
light: {
|
|
2696
|
+
bg: "#f6f6f3",
|
|
2697
|
+
fg: "#1d1d1f",
|
|
2698
|
+
muted: "#6b6b70",
|
|
2699
|
+
card: "#ffffff",
|
|
2700
|
+
border: "#e6e6e1",
|
|
2701
|
+
accent: "#5b5bd6",
|
|
2702
|
+
accentSoft: "#ececfb",
|
|
2703
|
+
warn: "#9a5800",
|
|
2704
|
+
empty: "#ececea"
|
|
2705
|
+
},
|
|
2706
|
+
dark: {
|
|
2707
|
+
bg: "#121214",
|
|
2708
|
+
fg: "#ececee",
|
|
2709
|
+
muted: "#9a9aa2",
|
|
2710
|
+
card: "#1c1c20",
|
|
2711
|
+
border: "#2c2c33",
|
|
2712
|
+
accent: "#9d9dff",
|
|
2713
|
+
accentSoft: "#25254a",
|
|
2714
|
+
warn: "#f0b35a",
|
|
2715
|
+
empty: "#26262c"
|
|
2716
|
+
}
|
|
2717
|
+
};
|
|
2606
2718
|
// src/report/content.ts
|
|
2607
2719
|
const contentIn = (p, lang) => {
|
|
2608
2720
|
const t = lang === "en" ? void 0 : p.translations[lang];
|
|
@@ -2618,67 +2730,6 @@ const contentIn = (p, lang) => {
|
|
|
2618
2730
|
improvement: (i) => t?.improvements[i] ?? p.improvements[i]
|
|
2619
2731
|
};
|
|
2620
2732
|
};
|
|
2621
|
-
// src/report/derive.ts
|
|
2622
|
-
/** The busiest four consecutive hours (wrapping past midnight); `end` is exclusive. */
|
|
2623
|
-
const chronotype = (s) => {
|
|
2624
|
-
const total = s.hourHistogram.reduce((a, b) => a + b, 0);
|
|
2625
|
-
if (total === 0) return null;
|
|
2626
|
-
let best = {
|
|
2627
|
-
start: 0,
|
|
2628
|
-
sum: -1
|
|
2629
|
-
};
|
|
2630
|
-
for (let start = 0; start < 24; start++) {
|
|
2631
|
-
let sum = 0;
|
|
2632
|
-
for (let i = 0; i < 4; i++) sum += s.hourHistogram[(start + i) % 24];
|
|
2633
|
-
if (sum > best.sum) best = {
|
|
2634
|
-
start,
|
|
2635
|
-
sum
|
|
2636
|
-
};
|
|
2637
|
-
}
|
|
2638
|
-
return {
|
|
2639
|
-
start: best.start,
|
|
2640
|
-
end: (best.start + 4) % 24,
|
|
2641
|
-
share: best.sum / total
|
|
2642
|
-
};
|
|
2643
|
-
};
|
|
2644
|
-
/** Mean prompts per session, one decimal; null without sessions. */
|
|
2645
|
-
const promptsPerSession = (s) => s.totalSessions === 0 ? null : Math.round(10 * s.totalPrompts / s.totalSessions) / 10;
|
|
2646
|
-
/** "1 in N prompts corrects the AI"; null without corrections. */
|
|
2647
|
-
const correctionEvery = (s) => s.correctionRate <= 0 ? null : Math.round(1 / s.correctionRate);
|
|
2648
|
-
/**
|
|
2649
|
-
* The dominant project per month, with consecutive months of the same project merged. "Other
|
|
2650
|
-
* folders" only names a month when no named project has prompts in it.
|
|
2651
|
-
*/
|
|
2652
|
-
const chapters = (s) => {
|
|
2653
|
-
const slots = s.activity.projects.length + 1;
|
|
2654
|
-
const months = /* @__PURE__ */ new Map();
|
|
2655
|
-
for (const d of s.activity.days) {
|
|
2656
|
-
const key = d.date.slice(0, 7);
|
|
2657
|
-
const counts = months.get(key) ?? Array.from({ length: slots }, () => 0);
|
|
2658
|
-
d.byProject.forEach((n, i) => counts[i] += n);
|
|
2659
|
-
months.set(key, counts);
|
|
2660
|
-
}
|
|
2661
|
-
const out = [];
|
|
2662
|
-
for (const [month, counts] of [...months.entries()].toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
2663
|
-
const named = counts.slice(0, slots - 1);
|
|
2664
|
-
const pool = named.some((n) => n > 0) ? named : counts;
|
|
2665
|
-
const slot = pool.indexOf(Math.max(...pool));
|
|
2666
|
-
const prompts = counts.reduce((a, b) => a + b, 0);
|
|
2667
|
-
const last = out.at(-1);
|
|
2668
|
-
if (last && last.slot === slot) out[out.length - 1] = {
|
|
2669
|
-
...last,
|
|
2670
|
-
to: month,
|
|
2671
|
-
prompts: last.prompts + prompts
|
|
2672
|
-
};
|
|
2673
|
-
else out.push({
|
|
2674
|
-
from: month,
|
|
2675
|
-
to: month,
|
|
2676
|
-
slot,
|
|
2677
|
-
prompts
|
|
2678
|
-
});
|
|
2679
|
-
}
|
|
2680
|
-
return out;
|
|
2681
|
-
};
|
|
2682
2733
|
// src/report/messages.ts
|
|
2683
2734
|
const plural = (n, one, many) => `${n.toLocaleString("en")} ${n === 1 ? one : many}`;
|
|
2684
2735
|
/** Every text of the report and the card, per language. */
|
|
@@ -3192,76 +3243,143 @@ const cardMetrics = (profile, lang, n) => {
|
|
|
3192
3243
|
[t.cardStreak, t.days(s.longestStreakDays)]
|
|
3193
3244
|
].slice(0, n);
|
|
3194
3245
|
};
|
|
3195
|
-
/**
|
|
3246
|
+
/** Clock size on the classic card; the report's 260 × 260 drawing is scaled to it. */
|
|
3247
|
+
const CARD_CLOCK = 300;
|
|
3248
|
+
/**
|
|
3249
|
+
* The classic card: the classic report's hero on its page, in the same colors, with the same
|
|
3250
|
+
* hour clock. The hour labels are text nodes over the drawing (SVG images get no fonts).
|
|
3251
|
+
*/
|
|
3196
3252
|
const classicTree = (profile, hide, lang) => {
|
|
3197
3253
|
const t = MESSAGES[lang];
|
|
3198
3254
|
const c = contentIn(profile, lang);
|
|
3199
|
-
const
|
|
3255
|
+
const k = CLASSIC_TOKENS.light;
|
|
3256
|
+
const name = clip(c.archetype.name, 56);
|
|
3257
|
+
const showGraphic = !hide.includes("graphic");
|
|
3258
|
+
const clock = clockSvg(profile.stats.hourHistogram, k.accent);
|
|
3259
|
+
const left = 808;
|
|
3260
|
+
const top = 165;
|
|
3261
|
+
const scale = CARD_CLOCK / clock.width;
|
|
3262
|
+
const ticks = [
|
|
3263
|
+
0,
|
|
3264
|
+
6,
|
|
3265
|
+
12,
|
|
3266
|
+
18
|
|
3267
|
+
].map((hour) => {
|
|
3268
|
+
const a = hour / 24 * Math.PI * 2 - Math.PI / 2;
|
|
3269
|
+
return h("div", {
|
|
3270
|
+
position: "absolute",
|
|
3271
|
+
left: left + (130 + Math.cos(a) * 46) * scale - 20,
|
|
3272
|
+
top: top + (130 + Math.sin(a) * 46) * scale - 10,
|
|
3273
|
+
width: 40,
|
|
3274
|
+
display: "flex",
|
|
3275
|
+
justifyContent: "center",
|
|
3276
|
+
fontSize: 14,
|
|
3277
|
+
color: k.muted
|
|
3278
|
+
}, String(hour).padStart(2, "0"));
|
|
3279
|
+
});
|
|
3200
3280
|
return h("div", {
|
|
3201
3281
|
width: CARD_WIDTH,
|
|
3202
3282
|
height: 630,
|
|
3203
3283
|
boxSizing: "border-box",
|
|
3204
3284
|
display: "flex",
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
backgroundImage: "linear-gradient(135deg, #17172b 0%, #2b2b6b 60%, #5b5bd6 100%)",
|
|
3209
|
-
color: "#f4f4ff",
|
|
3285
|
+
position: "relative",
|
|
3286
|
+
backgroundColor: k.bg,
|
|
3287
|
+
color: k.fg,
|
|
3210
3288
|
fontFamily: "Inter"
|
|
3211
3289
|
}, [
|
|
3290
|
+
h("div", {
|
|
3291
|
+
position: "absolute",
|
|
3292
|
+
left: 36,
|
|
3293
|
+
top: 36,
|
|
3294
|
+
width: 1128,
|
|
3295
|
+
height: 558,
|
|
3296
|
+
backgroundColor: k.card,
|
|
3297
|
+
border: `1.5px solid ${k.border}`,
|
|
3298
|
+
borderRadius: 32
|
|
3299
|
+
}),
|
|
3300
|
+
...showGraphic ? [{
|
|
3301
|
+
type: "img",
|
|
3302
|
+
props: {
|
|
3303
|
+
src: svgDataUrl(clock.svg),
|
|
3304
|
+
width: CARD_CLOCK,
|
|
3305
|
+
height: CARD_CLOCK,
|
|
3306
|
+
style: {
|
|
3307
|
+
position: "absolute",
|
|
3308
|
+
left,
|
|
3309
|
+
top
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
}, ...ticks] : [],
|
|
3212
3313
|
h("div", {
|
|
3213
3314
|
display: "flex",
|
|
3214
|
-
flexDirection: "column"
|
|
3315
|
+
flexDirection: "column",
|
|
3316
|
+
justifyContent: "space-between",
|
|
3317
|
+
position: "absolute",
|
|
3318
|
+
left: 36,
|
|
3319
|
+
top: 36,
|
|
3320
|
+
width: 1128,
|
|
3321
|
+
height: 558,
|
|
3322
|
+
padding: "40px 56px",
|
|
3323
|
+
boxSizing: "border-box"
|
|
3215
3324
|
}, [
|
|
3216
3325
|
h("div", {
|
|
3217
|
-
fontSize:
|
|
3218
|
-
|
|
3219
|
-
letterSpacing: 1
|
|
3326
|
+
fontSize: 20,
|
|
3327
|
+
fontWeight: 700
|
|
3220
3328
|
}, t.project),
|
|
3221
3329
|
h("div", {
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3330
|
+
display: "flex",
|
|
3331
|
+
flexDirection: "column",
|
|
3332
|
+
maxWidth: showGraphic ? 700 : 1e3
|
|
3333
|
+
}, [
|
|
3334
|
+
h("div", {
|
|
3335
|
+
fontFamily: DISPLAY_FONT,
|
|
3336
|
+
fontWeight: 800,
|
|
3337
|
+
fontSize: titleSize(name),
|
|
3338
|
+
lineHeight: .98,
|
|
3339
|
+
letterSpacing: -2
|
|
3340
|
+
}, name),
|
|
3341
|
+
h("div", {
|
|
3342
|
+
fontSize: 24,
|
|
3343
|
+
lineHeight: 1.4,
|
|
3344
|
+
marginTop: 18,
|
|
3345
|
+
color: k.muted
|
|
3346
|
+
}, clip(c.headline, 120)),
|
|
3347
|
+
hide.includes("traits") ? null : h("div", {
|
|
3348
|
+
display: "flex",
|
|
3349
|
+
gap: 10,
|
|
3350
|
+
flexWrap: "wrap",
|
|
3351
|
+
marginTop: 20
|
|
3352
|
+
}, profile.traits.slice(0, 3).map((_tr, i) => h("div", {
|
|
3353
|
+
display: "flex",
|
|
3354
|
+
fontSize: 18,
|
|
3355
|
+
padding: "6px 16px",
|
|
3356
|
+
borderRadius: 999,
|
|
3357
|
+
color: k.accent,
|
|
3358
|
+
backgroundColor: k.accentSoft
|
|
3359
|
+
}, clip(c.trait(i).name, 32))))
|
|
3360
|
+
]),
|
|
3227
3361
|
h("div", {
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
}, [h("div", {
|
|
3250
|
-
display: "flex",
|
|
3251
|
-
gap: 48
|
|
3252
|
-
}, (hide.includes("stats") ? [] : numbers).map(([label, value]) => h("div", {
|
|
3253
|
-
display: "flex",
|
|
3254
|
-
flexDirection: "column"
|
|
3255
|
-
}, [h("div", {
|
|
3256
|
-
fontSize: 40,
|
|
3257
|
-
fontWeight: 700
|
|
3258
|
-
}, value), h("div", {
|
|
3259
|
-
fontSize: 18,
|
|
3260
|
-
color: "#b9b9ff"
|
|
3261
|
-
}, label)]))), h("div", {
|
|
3262
|
-
fontSize: 20,
|
|
3263
|
-
color: "#b9b9ff"
|
|
3264
|
-
}, "npx prompt-profile")])
|
|
3362
|
+
display: "flex",
|
|
3363
|
+
justifyContent: "space-between",
|
|
3364
|
+
alignItems: "flex-end"
|
|
3365
|
+
}, [h("div", {
|
|
3366
|
+
display: "flex",
|
|
3367
|
+
gap: 44
|
|
3368
|
+
}, (hide.includes("stats") ? [] : cardMetrics(profile, lang, 3)).map(([label, value]) => h("div", {
|
|
3369
|
+
display: "flex",
|
|
3370
|
+
flexDirection: "column"
|
|
3371
|
+
}, [h("div", {
|
|
3372
|
+
fontFamily: DISPLAY_FONT,
|
|
3373
|
+
fontWeight: 800,
|
|
3374
|
+
fontSize: 36
|
|
3375
|
+
}, value), h("div", {
|
|
3376
|
+
fontSize: 16,
|
|
3377
|
+
color: k.muted
|
|
3378
|
+
}, label)]))), h("div", {
|
|
3379
|
+
fontSize: 18,
|
|
3380
|
+
color: k.muted
|
|
3381
|
+
}, "npx prompt-profile")])
|
|
3382
|
+
])
|
|
3265
3383
|
]);
|
|
3266
3384
|
};
|
|
3267
3385
|
/** Archetype size steps: long names get smaller type instead of overflowing. */
|
|
@@ -3543,15 +3661,7 @@ const heatmap = (s, loc, view = PRIVATE_VIEW) => {
|
|
|
3543
3661
|
};
|
|
3544
3662
|
/** 24 radial bars around a dial; the busiest four hours are drawn in full colour. */
|
|
3545
3663
|
const radialClock = (s, window, label) => {
|
|
3546
|
-
const
|
|
3547
|
-
const inWindow = (h) => window !== null && (h - window.start + 24) % 24 < (window.end - window.start + 24) % 24;
|
|
3548
|
-
const bars = s.hourHistogram.map((n, h) => {
|
|
3549
|
-
const a = h / 24 * Math.PI * 2 - Math.PI / 2;
|
|
3550
|
-
const r0 = 62;
|
|
3551
|
-
const r1 = 66 + 54 * n / max;
|
|
3552
|
-
const p = (r) => `${(130 + Math.cos(a) * r).toFixed(1)} ${(130 + Math.sin(a) * r).toFixed(1)}`;
|
|
3553
|
-
return html`<path d="M${p(r0)}L${p(r1)}" class="${inWindow(h) ? "clock-bar on" : "clock-bar"}" />`;
|
|
3554
|
-
});
|
|
3664
|
+
const bars = clockBars(s.hourHistogram, window).map((b) => html`<path d="M${b.from}L${b.to}" class="${b.on ? "clock-bar on" : "clock-bar"}" />`);
|
|
3555
3665
|
const ticks = [
|
|
3556
3666
|
0,
|
|
3557
3667
|
6,
|
|
@@ -3569,7 +3679,7 @@ const radialClock = (s, window, label) => {
|
|
|
3569
3679
|
});
|
|
3570
3680
|
return html`<figure class="clock">
|
|
3571
3681
|
<svg viewBox="0 0 260 260" aria-hidden="true">
|
|
3572
|
-
<circle cx="130" cy="130" r="60" class="clock-dial" />
|
|
3682
|
+
<circle cx="130" cy="130" r="${60}" class="clock-dial" />
|
|
3573
3683
|
${bars}${ticks}
|
|
3574
3684
|
</svg>
|
|
3575
3685
|
<figcaption>${label}</figcaption>
|
|
@@ -4305,30 +4415,6 @@ const progressBlock = (p, loc, v, scrub) => {
|
|
|
4305
4415
|
const signatureTile = (value, label, tip, i) => html`<div class="sig tip ${i === 0 ? "tip-l" : i === 3 ? "tip-r" : ""}" ${tip} tabindex="-1">
|
|
4306
4416
|
<b>${value}</b><span>${label}</span>
|
|
4307
4417
|
</div>`;
|
|
4308
|
-
const CLASSIC_TOKENS = {
|
|
4309
|
-
light: {
|
|
4310
|
-
bg: "#f6f6f3",
|
|
4311
|
-
fg: "#1d1d1f",
|
|
4312
|
-
muted: "#6b6b70",
|
|
4313
|
-
card: "#ffffff",
|
|
4314
|
-
border: "#e6e6e1",
|
|
4315
|
-
accent: "#5b5bd6",
|
|
4316
|
-
accentSoft: "#ececfb",
|
|
4317
|
-
warn: "#9a5800",
|
|
4318
|
-
empty: "#ececea"
|
|
4319
|
-
},
|
|
4320
|
-
dark: {
|
|
4321
|
-
bg: "#121214",
|
|
4322
|
-
fg: "#ececee",
|
|
4323
|
-
muted: "#9a9aa2",
|
|
4324
|
-
card: "#1c1c20",
|
|
4325
|
-
border: "#2c2c33",
|
|
4326
|
-
accent: "#9d9dff",
|
|
4327
|
-
accentSoft: "#25254a",
|
|
4328
|
-
warn: "#f0b35a",
|
|
4329
|
-
empty: "#26262c"
|
|
4330
|
-
}
|
|
4331
|
-
};
|
|
4332
4418
|
/** The classic hero: today's colors on a card, with the hour graphic. */
|
|
4333
4419
|
const CLASSIC_IDENTITY = {
|
|
4334
4420
|
background: "var(--card)",
|