howamiprompting 0.0.0-stage → 0.1.0
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 +21 -0
- package/README.md +123 -2
- package/bin/howamiprompting.js +11 -0
- package/package.json +46 -4
- package/src/agents.js +160 -0
- package/src/analyze.js +167 -0
- package/src/bank.json +687 -0
- package/src/clean.js +75 -0
- package/src/cli.js +189 -0
- package/src/collect/claude.js +44 -0
- package/src/collect/codex.js +36 -0
- package/src/collect/gemini.js +31 -0
- package/src/collect/generic.js +42 -0
- package/src/collect/index.js +40 -0
- package/src/collect/others.js +79 -0
- package/src/collect/vscode.js +112 -0
- package/src/practice.js +119 -0
- package/src/server.js +97 -0
- package/src/sqlite.js +51 -0
- package/src/stats.js +208 -0
- package/src/store.js +24 -0
- package/src/util.js +89 -0
- package/ui/app.css +227 -0
- package/ui/app.js +464 -0
- package/ui/index.html +55 -0
package/src/util.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import fsp from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
|
|
7
|
+
export const HOME = os.homedir();
|
|
8
|
+
export const PLATFORM = process.platform;
|
|
9
|
+
|
|
10
|
+
/** Per-OS application-support directory for VS Code–family apps. */
|
|
11
|
+
export function appSupport() {
|
|
12
|
+
if (PLATFORM === 'darwin') return path.join(HOME, 'Library', 'Application Support');
|
|
13
|
+
if (PLATFORM === 'win32') return process.env.APPDATA || path.join(HOME, 'AppData', 'Roaming');
|
|
14
|
+
return process.env.XDG_CONFIG_HOME || path.join(HOME, '.config');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const exists = p => { try { fs.accessSync(p); return true; } catch { return false; } };
|
|
18
|
+
export const isDir = p => { try { return fs.statSync(p).isDirectory(); } catch { return false; } };
|
|
19
|
+
|
|
20
|
+
export async function readText(p, max = 200 * 1024 * 1024) {
|
|
21
|
+
try {
|
|
22
|
+
const st = await fsp.stat(p);
|
|
23
|
+
if (st.size > max) return null;
|
|
24
|
+
return await fsp.readFile(p, 'utf8');
|
|
25
|
+
} catch { return null; }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function readJson(p) {
|
|
29
|
+
const t = await readText(p);
|
|
30
|
+
if (t == null) return null;
|
|
31
|
+
try { return JSON.parse(t); } catch { return null; }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Iterate JSONL lines lazily-ish (file is read whole; sessions are rarely huge). */
|
|
35
|
+
export async function* jsonl(p) {
|
|
36
|
+
const t = await readText(p);
|
|
37
|
+
if (!t) return;
|
|
38
|
+
for (const line of t.split(/\r?\n/)) {
|
|
39
|
+
if (!line || line[0] !== '{') continue;
|
|
40
|
+
try { yield JSON.parse(line); } catch { /* skip */ }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Recursive file finder with depth + count limits. */
|
|
45
|
+
export async function find(root, test, { depth = 6, limit = 20000, skipDirs = SKIP } = {}) {
|
|
46
|
+
const out = [];
|
|
47
|
+
async function walk(dir, d) {
|
|
48
|
+
if (out.length >= limit || d > depth) return;
|
|
49
|
+
let ents;
|
|
50
|
+
try { ents = await fsp.readdir(dir, { withFileTypes: true }); } catch { return; }
|
|
51
|
+
for (const e of ents) {
|
|
52
|
+
if (out.length >= limit) return;
|
|
53
|
+
const p = path.join(dir, e.name);
|
|
54
|
+
if (e.isDirectory()) {
|
|
55
|
+
if (skipDirs.has(e.name)) continue;
|
|
56
|
+
await walk(p, d + 1);
|
|
57
|
+
} else if (e.isFile() && test(p, e.name)) out.push(p);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
await walk(root, 0);
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
export const SKIP = new Set(['node_modules', '.git', 'Library', '.Trash', 'vendor', '.cache', 'dist', 'build', '.next', '.venv', 'venv', '__pycache__', 'Pods', 'DerivedData', '.npm', '.cargo', '.rustup', 'go', '.gradle', '.m2', 'Applications', 'Music', 'Movies', 'Pictures', 'Photos Library.photoslibrary', '.docker', 'AppData', 'snap']);
|
|
64
|
+
|
|
65
|
+
export function which(cmd) {
|
|
66
|
+
const r = spawnSync(PLATFORM === 'win32' ? 'where' : 'which', [cmd], { encoding: 'utf8' });
|
|
67
|
+
if (r.status !== 0) return null;
|
|
68
|
+
return (r.stdout || '').split(/\r?\n/)[0].trim() || null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function toISO(t) {
|
|
72
|
+
if (t == null || t === '') return null;
|
|
73
|
+
if (typeof t === 'number') return new Date(t < 1e12 ? t * 1000 : t).toISOString();
|
|
74
|
+
const d = new Date(t);
|
|
75
|
+
return isNaN(d) ? null : d.toISOString();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function projectName(p) {
|
|
79
|
+
if (!p) return null;
|
|
80
|
+
return path.basename(String(p).replace(/[\\/]+$/, '')) || null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
84
|
+
|
|
85
|
+
export function dataDir() {
|
|
86
|
+
const d = process.env.HOWAMIPROMPTING_HOME || path.join(HOME, '.howamiprompting');
|
|
87
|
+
fs.mkdirSync(d, { recursive: true });
|
|
88
|
+
return d;
|
|
89
|
+
}
|
package/ui/app.css
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--ink: #16142b; --cream: #fff7e8; --paper: #fff; --yellow: #ffd84d; --pink: #ff5c8a; --blue: #4b5cff;
|
|
3
|
+
--mint: #2ee6a6; --orange: #ff9a3d; --purple: #9b6bff; --muted: #6b6880; --soft-pink: #ffd2dc; --soft-mint: #e3fff4; --soft-blue: #e5e8ff;
|
|
4
|
+
--line: 3px solid var(--ink); --shadow: 6px 6px 0 var(--ink); --shadow-sm: 4px 4px 0 var(--ink); --r: 22px;
|
|
5
|
+
--font: 'Bricolage Grotesque', 'Space Grotesk', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
6
|
+
--mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
|
|
7
|
+
}
|
|
8
|
+
[hidden] { display: none !important; }
|
|
9
|
+
* { box-sizing: border-box; }
|
|
10
|
+
html { scroll-behavior: smooth; }
|
|
11
|
+
body { margin: 0; font-family: var(--font); color: var(--ink); background: var(--cream); -webkit-font-smoothing: antialiased; overflow-x: hidden; }
|
|
12
|
+
body::before { content: ''; position: fixed; inset: 0; z-index: -2; background-image: radial-gradient(rgba(22,20,43,.11) 1.4px, transparent 1.4px); background-size: 22px 22px; }
|
|
13
|
+
a { color: inherit; }
|
|
14
|
+
h1, h2, h3, h4 { margin: 0 0 .4em; letter-spacing: -.03em; line-height: 1.05; }
|
|
15
|
+
h2 { font-size: clamp(30px, 4vw, 44px); font-weight: 800; }
|
|
16
|
+
h3 { font-size: 21px; font-weight: 800; }
|
|
17
|
+
p { line-height: 1.55; }
|
|
18
|
+
.muted { color: var(--muted); }
|
|
19
|
+
code, .mono { font-family: var(--mono); }
|
|
20
|
+
.blob { position: fixed; z-index: -1; border-radius: 50%; opacity: .5; animation: float 14s ease-in-out infinite; pointer-events: none; }
|
|
21
|
+
.blob.b1 { width: 360px; height: 360px; background: var(--yellow); top: -110px; right: -90px; }
|
|
22
|
+
.blob.b2 { width: 260px; height: 260px; background: var(--pink); bottom: -90px; left: -80px; animation-delay: -5s; }
|
|
23
|
+
.blob.b3 { width: 180px; height: 180px; background: var(--mint); top: 48%; right: -70px; animation-delay: -9s; }
|
|
24
|
+
@keyframes float { 50% { transform: translate(-30px, 25px) scale(1.08); } }
|
|
25
|
+
|
|
26
|
+
/* nav */
|
|
27
|
+
.nav { position: sticky; top: 0; z-index: 30; display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 14px 26px; background: rgba(255,247,232,.88); backdrop-filter: blur(10px); border-bottom: 3px solid var(--ink); }
|
|
28
|
+
.logo { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 19px; letter-spacing: -.02em; white-space: nowrap; }
|
|
29
|
+
.logo .q { width: 32px; height: 32px; border-radius: 50%; background: var(--yellow); border: var(--line); display: grid; place-items: center; box-shadow: 2px 2px 0 var(--ink); font-size: 15px; }
|
|
30
|
+
.logo em { font-style: normal; color: var(--pink); }
|
|
31
|
+
.tabs { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
|
|
32
|
+
.tabs button { font: inherit; font-weight: 800; font-size: 14px; border: 2px solid var(--ink); background: var(--paper); border-radius: 99px; padding: 7px 14px; cursor: pointer; transition: transform .12s; }
|
|
33
|
+
.tabs button:hover { transform: translateY(-2px); }
|
|
34
|
+
.tabs button.on { background: var(--ink); color: var(--yellow); }
|
|
35
|
+
.star { font-weight: 800; font-size: 14px; text-decoration: none; border: 2px solid var(--ink); background: var(--yellow); border-radius: 99px; padding: 7px 14px; box-shadow: 2px 2px 0 var(--ink); white-space: nowrap; }
|
|
36
|
+
@media (max-width: 900px) { .nav { flex-wrap: wrap; padding: 12px 16px; } .tabs { order: 3; width: 100%; justify-content: flex-start; overflow-x: auto; flex-wrap: nowrap; } }
|
|
37
|
+
|
|
38
|
+
main { max-width: 1080px; margin: 0 auto; padding: 26px 22px 90px; }
|
|
39
|
+
@media (max-width: 640px) { main { padding: 18px 16px 70px; } }
|
|
40
|
+
.view { display: none; }
|
|
41
|
+
.view.on { display: block; animation: rise .5s cubic-bezier(.2,1.3,.4,1) both; }
|
|
42
|
+
@keyframes rise { from { opacity: 0; transform: translateY(24px); } }
|
|
43
|
+
@keyframes pop { from { opacity: 0; transform: scale(.6) rotate(-6deg); } }
|
|
44
|
+
|
|
45
|
+
.card { background: var(--paper); border: var(--line); border-radius: var(--r); box-shadow: var(--shadow); padding: 26px; }
|
|
46
|
+
.card + .card, .section + .section { margin-top: 24px; }
|
|
47
|
+
.section { margin-top: 40px; }
|
|
48
|
+
.section-head { display: flex; align-items: baseline; gap: 12px; margin: 46px 0 14px; flex-wrap: wrap; }
|
|
49
|
+
.section-head h2 { margin: 0; }
|
|
50
|
+
.tag { display: inline-block; font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; background: var(--ink); color: var(--yellow); padding: 5px 10px; border-radius: 8px; }
|
|
51
|
+
.grid2 { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 22px; }
|
|
52
|
+
.grid3 { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 18px; }
|
|
53
|
+
.grid4 { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 14px; }
|
|
54
|
+
@media (max-width: 860px) { .grid3, .grid4 { grid-template-columns: 1fr 1fr; } .grid2 { grid-template-columns: 1fr; } }
|
|
55
|
+
@media (max-width: 480px) { .grid4, .grid3 { grid-template-columns: 1fr 1fr; gap: 10px; } }
|
|
56
|
+
.row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
|
|
57
|
+
.between { justify-content: space-between; }
|
|
58
|
+
.hl { background: linear-gradient(transparent 55%, var(--yellow) 55%); padding: 0 4px; }
|
|
59
|
+
.hl.pink { background: linear-gradient(transparent 55%, var(--pink) 55%); }
|
|
60
|
+
.hl.mint { background: linear-gradient(transparent 55%, var(--mint) 55%); }
|
|
61
|
+
|
|
62
|
+
.btn { font-family: inherit; font-weight: 800; font-size: 17px; border: var(--line); border-radius: 15px; padding: 12px 22px; cursor: pointer; background: var(--yellow); color: var(--ink); box-shadow: var(--shadow-sm); transition: transform .12s, box-shadow .12s; display: inline-flex; align-items: center; gap: 9px; text-decoration: none; }
|
|
63
|
+
.btn:hover { transform: translate(-2px,-2px); box-shadow: 6px 6px 0 var(--ink); }
|
|
64
|
+
.btn:active { transform: translate(3px,3px); box-shadow: 1px 1px 0 var(--ink); }
|
|
65
|
+
.btn.pink { background: var(--pink); color: #fff; } .btn.blue { background: var(--blue); color: #fff; } .btn.mint { background: var(--mint); } .btn.ghost { background: var(--paper); }
|
|
66
|
+
.btn.small { font-size: 14px; padding: 8px 14px; border-radius: 11px; } .btn.big { font-size: 21px; padding: 16px 30px; }
|
|
67
|
+
.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }
|
|
68
|
+
.pill { border: 2px solid var(--ink); border-radius: 99px; padding: 4px 12px; font-weight: 700; font-size: 13px; background: var(--paper); display: inline-flex; gap: 6px; align-items: center; }
|
|
69
|
+
.chip { border: 2px solid var(--ink); border-radius: 99px; padding: 6px 12px; font-weight: 700; background: var(--paper); color: var(--ink); font-size: 14px; display: inline-flex; gap: 6px; align-items: center; animation: pop .45s cubic-bezier(.2,1.6,.4,1) both; }
|
|
70
|
+
.chip b { background: var(--pink); color: #fff; border-radius: 99px; padding: 0 8px; }
|
|
71
|
+
.chips { display: flex; flex-wrap: wrap; gap: 9px; }
|
|
72
|
+
.copy { cursor: pointer; }
|
|
73
|
+
|
|
74
|
+
/* hero */
|
|
75
|
+
.hero { display: grid; grid-template-columns: 280px 1fr; gap: 34px; align-items: center; }
|
|
76
|
+
@media (max-width: 760px) { .hero { grid-template-columns: 1fr; text-align: center; } .hero .row { justify-content: center; } }
|
|
77
|
+
.dial { position: relative; width: 250px; height: 250px; margin: 0 auto; }
|
|
78
|
+
.dial svg { transform: rotate(-90deg); }
|
|
79
|
+
.dial .c { position: absolute; inset: 0; display: grid; place-items: center; text-align: center; }
|
|
80
|
+
.dial .num { font-size: 84px; font-weight: 800; line-height: .9; }
|
|
81
|
+
.dial .of { font-weight: 800; color: var(--muted); font-size: 14px; letter-spacing: .06em; text-transform: uppercase; }
|
|
82
|
+
.level { display: inline-block; font-size: 22px; font-weight: 800; border: var(--line); border-radius: 13px; padding: 4px 14px; background: var(--yellow); box-shadow: var(--shadow-sm); transform: rotate(-3deg); }
|
|
83
|
+
.headline { font-size: clamp(28px, 3.6vw, 42px); font-weight: 800; letter-spacing: -.03em; line-height: 1.08; margin: 14px 0 10px; }
|
|
84
|
+
.delta { font-weight: 800; border-radius: 99px; padding: 3px 10px; border: 2px solid var(--ink); font-size: 14px; }
|
|
85
|
+
.delta.up { background: var(--mint); } .delta.down { background: var(--soft-pink); }
|
|
86
|
+
|
|
87
|
+
.persona { display: flex; gap: 18px; align-items: center; background: var(--ink); color: #fff; border-radius: var(--r); padding: 22px 24px; border: var(--line); box-shadow: var(--shadow); }
|
|
88
|
+
.persona .e { font-size: 58px; line-height: 1; animation: wobble 3s ease-in-out infinite; }
|
|
89
|
+
@keyframes wobble { 50% { transform: rotate(8deg) scale(1.06); } }
|
|
90
|
+
.persona h3 { color: var(--yellow); font-size: 26px; margin-bottom: 4px; }
|
|
91
|
+
.persona p { margin: 0; color: #dcd9f0; }
|
|
92
|
+
|
|
93
|
+
/* bars */
|
|
94
|
+
.bars { display: grid; gap: 15px; }
|
|
95
|
+
.bar .lab { display: flex; justify-content: space-between; font-weight: 800; margin-bottom: 6px; gap: 8px; }
|
|
96
|
+
.bar .lab small { font-weight: 600; color: var(--muted); }
|
|
97
|
+
.bar .track { height: 20px; border: var(--line); border-radius: 99px; background: var(--paper); overflow: hidden; }
|
|
98
|
+
.bar .fill { height: 100%; width: 0; border-right: 3px solid var(--ink); transition: width 1.4s cubic-bezier(.2,1,.3,1); }
|
|
99
|
+
|
|
100
|
+
.stat { border: var(--line); border-radius: 18px; padding: 16px; background: var(--paper); box-shadow: var(--shadow-sm); animation: pop .5s cubic-bezier(.2,1.6,.4,1) both; }
|
|
101
|
+
.stat .v { font-size: 34px; font-weight: 800; line-height: 1; }
|
|
102
|
+
.stat .k { font-weight: 600; color: var(--muted); font-size: 14px; margin-top: 6px; }
|
|
103
|
+
|
|
104
|
+
/* before/after */
|
|
105
|
+
.ba { border: var(--line); border-radius: 18px; background: var(--paper); overflow: hidden; margin-top: 14px; }
|
|
106
|
+
.ba .hd { display: flex; justify-content: space-between; gap: 10px; align-items: center; padding: 12px 16px; border-bottom: 2px dashed var(--ink); flex-wrap: wrap; }
|
|
107
|
+
.ba .hd h4 { margin: 0; font-size: 18px; }
|
|
108
|
+
.ba .body { display: grid; grid-template-columns: 1fr 36px 1fr; }
|
|
109
|
+
.ba .body > div { padding: 14px 16px; font-size: 15px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
|
|
110
|
+
.ba .was { background: #fff1f4; font-family: var(--mono); font-size: 13.5px; }
|
|
111
|
+
.ba .now { background: var(--soft-mint); font-weight: 600; }
|
|
112
|
+
.ba .arr { display: grid; place-items: center; font-weight: 800; font-size: 20px; padding: 0 !important; }
|
|
113
|
+
.ba .why { padding: 10px 16px; border-top: 2px solid #16142b14; font-size: 14px; color: var(--muted); font-weight: 600; }
|
|
114
|
+
.ba small.l { display: block; font-family: var(--font); font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 6px; color: var(--muted); }
|
|
115
|
+
@media (max-width: 640px) { .ba .body { grid-template-columns: 1fr; } .ba .arr { height: 28px; transform: rotate(90deg); } }
|
|
116
|
+
.sev { font-size: 11px; font-weight: 800; border: 2px solid var(--ink); border-radius: 99px; padding: 2px 9px; text-transform: uppercase; letter-spacing: .05em; }
|
|
117
|
+
.sev.high { background: var(--pink); color: #fff; } .sev.medium { background: var(--orange); } .sev.low { background: var(--yellow); }
|
|
118
|
+
.good { border: 2px solid var(--ink); border-radius: 14px; padding: 13px 15px; background: var(--soft-mint); margin-top: 12px; }
|
|
119
|
+
.good b { display: block; margin-bottom: 4px; }
|
|
120
|
+
.good q, .quote { font-family: var(--mono); font-size: 13px; color: #0b6b4b; quotes: '“' '”'; }
|
|
121
|
+
|
|
122
|
+
.code { background: var(--ink); color: #f1efff; border-radius: 16px; padding: 18px 20px; font-family: var(--mono); font-size: 14px; line-height: 1.7; white-space: pre-wrap; position: relative; border: var(--line); }
|
|
123
|
+
.code .cbtn { position: absolute; top: 10px; right: 10px; }
|
|
124
|
+
.code .c { color: var(--mint); }
|
|
125
|
+
.ftabs { display: flex; gap: 6px; margin-bottom: 10px; flex-wrap: wrap; }
|
|
126
|
+
.ftabs button { font: inherit; font-family: var(--mono); font-size: 13px; font-weight: 600; border: 2px solid var(--ink); background: var(--paper); border-radius: 10px; padding: 5px 10px; cursor: pointer; }
|
|
127
|
+
.ftabs button.on { background: var(--yellow); }
|
|
128
|
+
|
|
129
|
+
table.t { width: 100%; border-collapse: collapse; font-size: 14.5px; }
|
|
130
|
+
table.t td, table.t th { padding: 9px 10px; border-bottom: 2px solid #16142b14; text-align: left; vertical-align: top; }
|
|
131
|
+
table.t th { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
|
|
132
|
+
.err td:first-child { font-family: var(--mono); font-size: 13px; color: #b0164c; }
|
|
133
|
+
.err td:nth-child(2) { font-weight: 700; color: #0b6b4b; }
|
|
134
|
+
|
|
135
|
+
.heat { display: grid; grid-template-columns: 34px repeat(24, 1fr); gap: 3px; font-size: 11px; font-weight: 700; color: var(--muted); }
|
|
136
|
+
.heat i { aspect-ratio: 1; border-radius: 4px; background: #16142b0d; display: block; }
|
|
137
|
+
.hbars { display: flex; align-items: flex-end; gap: 3px; height: 120px; }
|
|
138
|
+
.hbars i { flex: 1; background: var(--blue); border: 2px solid var(--ink); border-radius: 5px 5px 0 0; min-height: 3px; transition: height 1s; }
|
|
139
|
+
|
|
140
|
+
.plan { counter-reset: d; list-style: none; padding: 0; margin: 0; display: grid; gap: 10px; }
|
|
141
|
+
.plan li { counter-increment: d; display: flex; gap: 12px; align-items: center; font-weight: 600; border: 2px solid var(--ink); border-radius: 14px; padding: 10px 14px; background: var(--paper); }
|
|
142
|
+
.plan li::before { content: 'Day ' counter(d); font-weight: 800; background: var(--yellow); border: 2px solid var(--ink); border-radius: 9px; padding: 2px 9px; white-space: nowrap; font-size: 13px; }
|
|
143
|
+
.habit { display: grid; grid-template-columns: 1fr 1fr; border: 2px solid var(--ink); border-radius: 14px; overflow: hidden; margin-top: 10px; }
|
|
144
|
+
.habit div { padding: 11px 14px; font-weight: 600; font-size: 15px; }
|
|
145
|
+
.habit .s { background: var(--soft-mint); } .habit .x { background: #fff1f4; text-decoration: line-through; text-decoration-color: rgba(255,92,138,.6); }
|
|
146
|
+
|
|
147
|
+
.banner { border: var(--line); border-radius: 18px; padding: 16px 20px; background: var(--soft-blue); font-weight: 600; box-shadow: var(--shadow-sm); margin-bottom: 22px; }
|
|
148
|
+
.banner code { background: var(--ink); color: var(--mint); padding: 2px 8px; border-radius: 6px; font-size: 13px; }
|
|
149
|
+
|
|
150
|
+
/* wrapped story */
|
|
151
|
+
.story { position: fixed; inset: 0; z-index: 60; display: none; }
|
|
152
|
+
.story.on { display: block; }
|
|
153
|
+
.slide { position: absolute; inset: 0; display: none; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 60px 24px; overflow: hidden; }
|
|
154
|
+
.slide.on { display: flex; }
|
|
155
|
+
.slide .big { font-size: clamp(64px, 14vw, 170px); font-weight: 800; letter-spacing: -.05em; line-height: .9; animation: zoomIn .8s cubic-bezier(.2,1.4,.4,1) both; }
|
|
156
|
+
.slide .t1 { font-size: clamp(22px, 3.2vw, 36px); font-weight: 800; max-width: 820px; line-height: 1.15; animation: rise .7s .25s both; }
|
|
157
|
+
.slide .t2 { font-size: clamp(16px, 2vw, 21px); font-weight: 600; max-width: 680px; opacity: .85; margin-top: 16px; animation: rise .7s .5s both; line-height: 1.45; }
|
|
158
|
+
.slide .q { font-family: var(--mono); font-size: clamp(14px, 1.8vw, 18px); background: rgba(0,0,0,.18); padding: 14px 18px; border-radius: 14px; max-width: 720px; margin-top: 22px; animation: rise .7s .7s both; text-align: left; }
|
|
159
|
+
@keyframes zoomIn { from { transform: scale(2.2); opacity: 0; } }
|
|
160
|
+
.s-yellow { background: var(--yellow); } .s-pink { background: var(--pink); color: #fff; } .s-blue { background: var(--blue); color: #fff; } .s-mint { background: var(--mint); } .s-ink { background: var(--ink); color: #fff; } .s-purple { background: var(--purple); color: #fff; } .s-orange { background: var(--orange); }
|
|
161
|
+
.story .prog { position: absolute; top: 14px; left: 14px; right: 14px; display: flex; gap: 6px; z-index: 2; }
|
|
162
|
+
.story .prog i { flex: 1; height: 5px; border-radius: 9px; background: rgba(0,0,0,.18); overflow: hidden; }
|
|
163
|
+
.story .prog i b { display: block; height: 100%; width: 0; background: currentColor; }
|
|
164
|
+
.story .prog i.done b { width: 100%; }
|
|
165
|
+
.story .prog i.now b { animation: fillbar var(--dur, 5s) linear forwards; }
|
|
166
|
+
@keyframes fillbar { to { width: 100%; } }
|
|
167
|
+
.story .ctrl { position: absolute; bottom: 22px; left: 0; right: 0; display: flex; justify-content: center; gap: 12px; z-index: 2; }
|
|
168
|
+
.story .tap { position: absolute; top: 0; bottom: 80px; width: 35%; z-index: 1; cursor: pointer; }
|
|
169
|
+
.story .tap.l { left: 0; } .story .tap.r { right: 0; }
|
|
170
|
+
.tools-race { width: min(620px, 92vw); display: grid; gap: 10px; margin-top: 26px; text-align: left; }
|
|
171
|
+
.tools-race div { display: grid; grid-template-columns: 150px 1fr 60px; gap: 10px; align-items: center; font-weight: 800; }
|
|
172
|
+
.tools-race i { height: 26px; background: #fff; border: 3px solid var(--ink); border-radius: 99px; display: block; transform-origin: left; animation: grow 1s cubic-bezier(.2,1,.3,1) both; }
|
|
173
|
+
@keyframes grow { from { transform: scaleX(0); } }
|
|
174
|
+
.counters { display: grid; grid-template-columns: repeat(3, minmax(0, 200px)); gap: 16px; margin-top: 28px; }
|
|
175
|
+
.counters div { background: rgba(255,255,255,.9); color: var(--ink); border: var(--line); border-radius: 18px; padding: 16px; box-shadow: var(--shadow-sm); animation: pop .6s both; }
|
|
176
|
+
.counters b { display: block; font-size: 44px; font-weight: 800; line-height: 1; }
|
|
177
|
+
@media (max-width: 600px) { .counters { grid-template-columns: 1fr 1fr; } .tools-race div { grid-template-columns: 100px 1fr 44px; } }
|
|
178
|
+
|
|
179
|
+
/* practice */
|
|
180
|
+
.timer { position: relative; width: 70px; height: 70px; flex: 0 0 auto; }
|
|
181
|
+
.timer svg { transform: rotate(-90deg); }
|
|
182
|
+
.timer .tt { position: absolute; inset: 0; display: grid; place-items: center; font-weight: 800; font-size: 19px; font-variant-numeric: tabular-nums; }
|
|
183
|
+
.timer.warn .tt { color: var(--pink); }
|
|
184
|
+
.passage { font-size: clamp(19px, 2.3vw, 23px); line-height: 1.6; font-weight: 500; padding: 24px; border: var(--line); border-radius: var(--r); background: #fffdf4; box-shadow: var(--shadow); user-select: none; }
|
|
185
|
+
.passage.poof { animation: poof 1s forwards; }
|
|
186
|
+
@keyframes poof { 100% { filter: blur(18px); opacity: 0; transform: scale(.85) rotate(-2deg); } }
|
|
187
|
+
.broken { font-family: var(--mono); font-size: 15px; background: #fff1f4; border: 2px solid var(--ink); border-radius: 12px; padding: 12px 14px; margin-bottom: 10px; text-decoration: underline wavy var(--pink); text-underline-offset: 5px; user-select: none; }
|
|
188
|
+
textarea, input[type=text] { width: 100%; font: inherit; font-size: 17px; border: var(--line); border-radius: 14px; padding: 12px 14px; background: var(--paper); color: var(--ink); outline: none; }
|
|
189
|
+
textarea { min-height: 120px; resize: vertical; line-height: 1.5; }
|
|
190
|
+
textarea:focus, input:focus { box-shadow: var(--shadow-sm); }
|
|
191
|
+
.flow { display: flex; align-items: center; justify-content: center; padding: 22px 0; }
|
|
192
|
+
.flow .box { flex: 1 1 0; max-width: 220px; min-height: 130px; border: var(--line); border-radius: 18px; padding: 14px 10px; text-align: center; box-shadow: var(--shadow-sm); font-weight: 800; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; animation: pop .6s both; }
|
|
193
|
+
.flow .box .e { font-size: 36px; }
|
|
194
|
+
.flow .b0 { background: var(--yellow); } .flow .b1 { background: var(--soft-pink); animation-delay: .5s; } .flow .b2 { background: var(--mint); animation-delay: 1s; }
|
|
195
|
+
.flow .arrow { flex: 0 0 44px; text-align: center; font-size: 26px; font-weight: 800; }
|
|
196
|
+
@media (max-width: 640px) { .flow { flex-direction: column; } .flow .box { max-width: none; width: 100%; min-height: 0; } .flow .arrow { transform: rotate(90deg); } }
|
|
197
|
+
.original { font-family: var(--mono); font-size: 15px; background: var(--ink); color: #f5f3ff; border-radius: 16px; padding: 16px 18px; line-height: 1.6; user-select: none; }
|
|
198
|
+
.original::before { content: 'you › '; color: var(--mint); font-weight: 700; }
|
|
199
|
+
.meter { border: 2px solid var(--ink); border-radius: 99px; padding: 3px 11px; font-weight: 700; font-size: 13px; background: var(--paper); }
|
|
200
|
+
.meter.ok { background: var(--mint); } .meter.bad { background: var(--soft-pink); }
|
|
201
|
+
.target { display: inline-flex; gap: 8px; background: var(--ink); color: #fff; border-radius: 12px; padding: 7px 13px; font-weight: 700; font-size: 14px; margin-bottom: 14px; }
|
|
202
|
+
.target b { color: var(--yellow); }
|
|
203
|
+
.cmp { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 10px 0; }
|
|
204
|
+
.cmp > div { border: 2px solid var(--ink); border-radius: 12px; padding: 10px 12px; font-size: 15px; line-height: 1.5; }
|
|
205
|
+
.cmp .y { background: #fff7e0; } .cmp .m { background: var(--soft-mint); }
|
|
206
|
+
.cmp small { display: block; font-weight: 800; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 4px; }
|
|
207
|
+
@media (max-width: 640px) { .cmp { grid-template-columns: 1fr; } }
|
|
208
|
+
|
|
209
|
+
/* prompts explorer */
|
|
210
|
+
.plist .p { border: 2px solid var(--ink); border-radius: 14px; padding: 12px 14px; background: var(--paper); margin-top: 10px; }
|
|
211
|
+
.plist .p .m { font-size: 12px; font-weight: 700; color: var(--muted); margin-bottom: 6px; display: flex; gap: 10px; flex-wrap: wrap; }
|
|
212
|
+
.plist .p .x { font-family: var(--mono); font-size: 13.5px; white-space: pre-wrap; word-break: break-word; max-height: 220px; overflow: auto; }
|
|
213
|
+
|
|
214
|
+
.overlay { position: fixed; inset: 0; background: rgba(22,20,43,.78); display: none; place-items: center; z-index: 80; color: #fff; text-align: center; }
|
|
215
|
+
.overlay.on { display: grid; }
|
|
216
|
+
.dots { display: flex; gap: 12px; justify-content: center; margin-bottom: 22px; }
|
|
217
|
+
.dots i { width: 24px; height: 24px; border-radius: 50%; border: 3px solid var(--ink); animation: hop .9s ease-in-out infinite; }
|
|
218
|
+
.dots i:nth-child(1) { background: var(--yellow); } .dots i:nth-child(2) { background: var(--pink); animation-delay: .15s; } .dots i:nth-child(3) { background: var(--mint); animation-delay: .3s; } .dots i:nth-child(4) { background: var(--blue); animation-delay: .45s; }
|
|
219
|
+
@keyframes hop { 50% { transform: translateY(-24px); } }
|
|
220
|
+
.toast { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%) translateY(200%); opacity: 0; background: var(--ink); color: #fff; padding: 13px 20px; border-radius: 15px; font-weight: 700; z-index: 90; transition: all .35s cubic-bezier(.2,1.5,.4,1); border: 3px solid var(--yellow); max-width: calc(100% - 32px); }
|
|
221
|
+
.toast.on { transform: translateX(-50%); opacity: 1; }
|
|
222
|
+
#confetti { position: fixed; inset: 0; pointer-events: none; z-index: 95; }
|
|
223
|
+
.foot { text-align: center; color: var(--muted); font-weight: 600; font-size: 14px; margin-top: 60px; }
|
|
224
|
+
canvas.cardprev { width: 100%; max-width: 720px; border: var(--line); border-radius: 18px; box-shadow: var(--shadow); display: block; }
|
|
225
|
+
@media print { .nav, .blob, .no-print, body::before { display: none !important; } .card { box-shadow: none; } }
|
|
226
|
+
|
|
227
|
+
.fixes { padding-left: 22px; margin: 0; } .fixes li { font-weight: 600; line-height: 1.5; margin-bottom: 10px; }
|