@shiyuphan/follow-me 0.1.1 → 0.3.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/README.md +119 -69
- package/dist/src/cli.js +1069 -140
- package/dist/src/comments.js +361 -0
- package/dist/src/git.js +32 -3
- package/dist/src/host.js +106 -9
- package/dist/src/install.js +22 -1
- package/dist/src/naive.js +26 -0
- package/dist/src/plan.js +282 -26
- package/dist/src/prompt.js +711 -98
- package/dist/src/server.js +573 -140
- package/dist/src/session.js +106 -104
- package/dist/src/syntax.js +199 -0
- package/dist/src/types.js +3 -1
- package/dist/ui/dist/assets/abnfDiagram-WEYOIA3C-DcmzRkVZ.js +1 -0
- package/dist/ui/dist/assets/arc-T5QB0T1C.js +1 -0
- package/dist/ui/dist/assets/architecture-WOLXFQ4H-DUIkQTeE.js +1 -0
- package/dist/ui/dist/assets/architectureDiagram-ZHVYKT53-BUYi4-bA.js +36 -0
- package/dist/ui/dist/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/dist/assets/blockDiagram-RJAE7QA3-dyJxtT9e.js +140 -0
- package/dist/ui/dist/assets/c4Diagram-XDPPPE54-CrAgiaZR.js +38 -0
- package/dist/ui/dist/assets/channel-wW7ebm0i.js +1 -0
- package/dist/ui/dist/assets/chunk-2E7WHVMD-C6fAf4Vj.js +88 -0
- package/dist/ui/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/ui/dist/assets/chunk-4EMNKR2T-BLZjwZ4Q.js +1 -0
- package/dist/ui/dist/assets/chunk-55XCU76K-DGLK2Q-L.js +1 -0
- package/dist/ui/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/ui/dist/assets/chunk-7S6XSTFO-BablszTX.js +2 -0
- package/dist/ui/dist/assets/chunk-AQDTSKZB-Cuk3B_vb.js +72 -0
- package/dist/ui/dist/assets/chunk-DSNLSHJZ-DiC78RqE.js +127 -0
- package/dist/ui/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/dist/ui/dist/assets/chunk-HAGFOTED-X_RbvixX.js +62 -0
- package/dist/ui/dist/assets/chunk-HNE4Q36P-C14_x5ck.js +1 -0
- package/dist/ui/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/dist/ui/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/ui/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
- package/dist/ui/dist/assets/chunk-NQOKRVIF-Dxd0dtYR.js +213 -0
- package/dist/ui/dist/assets/chunk-UHW7PFBP-BIxePNUS.js +1 -0
- package/dist/ui/dist/assets/chunk-VQ5W62QI-CKSKLbj4.js +1 -0
- package/dist/ui/dist/assets/chunk-WQQL4CDK-CWNmZGlR.js +10 -0
- package/dist/ui/dist/assets/chunk-XXDRQBXY-D-I5QHgG.js +1 -0
- package/dist/ui/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/ui/dist/assets/chunk-YZO67HFG-DFP68CFd.js +1 -0
- package/dist/ui/dist/assets/chunk-ZAUX2LTX-DY90d4QG.js +1 -0
- package/dist/ui/dist/assets/chunk-ZCHMQSOR-Dlpr-5UC.js +2 -0
- package/dist/ui/dist/assets/classDiagram-v2-4MYWR3UA-CH9uhoLJ.js +217 -0
- package/dist/ui/dist/assets/cose-bilkent-JH36ORCC-DtYHrp8D.js +1 -0
- package/dist/ui/dist/assets/cynefin-EF2NZ3EQ-BtslbtrY.js +1 -0
- package/dist/ui/dist/assets/cynefinDiagram-WF24UDRP-DUU1Jcc7.js +62 -0
- package/dist/ui/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
- package/dist/ui/dist/assets/dagre-NK4GIZVX-CDlP-dRW.js +4 -0
- package/dist/ui/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/ui/dist/assets/diagram-CBK3MWTC-DiV2fn-r.js +24 -0
- package/dist/ui/dist/assets/diagram-I5XIIV2D-Bg5RehGi.js +24 -0
- package/dist/ui/dist/assets/diagram-ODIL2UCW-CFmazz3f.js +30 -0
- package/dist/ui/dist/assets/diagram-PFBEJUXE-uw3ZBqDp.js +3 -0
- package/dist/ui/dist/assets/diagram-RJA3I4VV-uEOYYRYa.js +41 -0
- package/dist/ui/dist/assets/diagram-TYUNFSNR-BN58LF_3.js +200 -0
- package/dist/ui/dist/assets/dist-tWS_lXDS.js +1 -0
- package/dist/ui/dist/assets/ebnfDiagram-V4P3TTPT-B68Tz-w8.js +1 -0
- package/dist/ui/dist/assets/elk-QONTBCTQ-DZZohNea.js +27 -0
- package/dist/ui/dist/assets/erDiagram-KYQIGQAA-BH_m74pv.js +99 -0
- package/dist/ui/dist/assets/eventmodeling-K75KTNOO-CTp5FHos.js +1 -0
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-CsCvexry.js +1 -0
- package/dist/ui/dist/assets/ganttDiagram-R7GPNTJY-BDau7_0f.js +292 -0
- package/dist/ui/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/ui/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/ui/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/ui/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/ui/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/ui/dist/assets/gitGraph-VSP46ZUC-Bv84IoCm.js +1 -0
- package/dist/ui/dist/assets/gitGraphDiagram-PDYL7XHY-DzXcMlm8.js +106 -0
- package/dist/ui/dist/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/ui/dist/assets/index-BTJoqVVN.js +133 -0
- package/dist/ui/dist/assets/index-D60U3smc.css +1 -0
- package/dist/ui/dist/assets/info-OHQRW6UA-222J0O_j.js +1 -0
- package/dist/ui/dist/assets/infoDiagram-MVDRKAX5-DE2sa3Vz.js +2 -0
- package/dist/ui/dist/assets/init-C-OQMol4.js +1 -0
- package/dist/ui/dist/assets/ishikawaDiagram-HPEHV2GE-BJDk0Zcj.js +70 -0
- package/dist/ui/dist/assets/journeyDiagram-BOSHW7O7-JSUXX5Hb.js +139 -0
- package/dist/ui/dist/assets/kanban-definition-FNSA4HC5-Bg79b49M.js +89 -0
- package/dist/ui/dist/assets/katex-B_JcIwqj.js +257 -0
- package/dist/ui/dist/assets/line-Bk5qQSIa.js +1 -0
- package/dist/ui/dist/assets/linear-PFnVMru5.js +1 -0
- package/dist/ui/dist/assets/mermaid-parser.core-Dcxlzc-S.js +7 -0
- package/dist/ui/dist/assets/mermaid.core-TeOHQJ00.js +44 -0
- package/dist/ui/dist/assets/mindmap-definition-6OY57UG3-IaNk8NnW.js +98 -0
- package/dist/ui/dist/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/ui/dist/assets/packet-JDAUHWVQ-DMff5cVK.js +1 -0
- package/dist/ui/dist/assets/path-fybaL0A-.js +1 -0
- package/dist/ui/dist/assets/pegDiagram-ECIUJNBX-66Xu4YRc.js +1 -0
- package/dist/ui/dist/assets/pie-XZMESJXO-GEtzLCIM.js +1 -0
- package/dist/ui/dist/assets/pieDiagram-GNMPY74T-7jMBi-kU.js +39 -0
- package/dist/ui/dist/assets/prism-bash-D6zCJ74D.js +1 -0
- package/dist/ui/dist/assets/prism-c-04YixN25.js +1 -0
- package/dist/ui/dist/assets/prism-clike-DapgWyxx.js +1 -0
- package/dist/ui/dist/assets/prism-core-FDHlWQ43.js +1 -0
- package/dist/ui/dist/assets/prism-cpp-C-lJbC-6.js +1 -0
- package/dist/ui/dist/assets/prism-css-CPgOzxXp.js +1 -0
- package/dist/ui/dist/assets/prism-go-C_8qAH5n.js +1 -0
- package/dist/ui/dist/assets/prism-java-6wtusvIl.js +1 -0
- package/dist/ui/dist/assets/prism-javascript-BnO-swvr.js +1 -0
- package/dist/ui/dist/assets/prism-json-Dp_-W-Hv.js +1 -0
- package/dist/ui/dist/assets/prism-jsx-D-D0NWtC.js +1 -0
- package/dist/ui/dist/assets/prism-kotlin-BUlJgC-o.js +1 -0
- package/dist/ui/dist/assets/prism-markdown-Bc9-36OC.js +1 -0
- package/dist/ui/dist/assets/prism-markup-Cp04rIa1.js +1 -0
- package/dist/ui/dist/assets/prism-markup-templating-KkSMBj6Y.js +1 -0
- package/dist/ui/dist/assets/prism-php-Co4fM40K.js +1 -0
- package/dist/ui/dist/assets/prism-python-CfiSVtM6.js +1 -0
- package/dist/ui/dist/assets/prism-ruby-DFCASkuD.js +1 -0
- package/dist/ui/dist/assets/prism-rust-Bu9N8X3N.js +1 -0
- package/dist/ui/dist/assets/prism-sql-CYhVDMEE.js +1 -0
- package/dist/ui/dist/assets/prism-swift-B0kWc-ne.js +1 -0
- package/dist/ui/dist/assets/prism-toml-DKb54GAT.js +1 -0
- package/dist/ui/dist/assets/prism-tsx-p6J2Kc_4.js +1 -0
- package/dist/ui/dist/assets/prism-typescript-BMqONzxf.js +1 -0
- package/dist/ui/dist/assets/prism-yaml-C0JHu8gr.js +1 -0
- package/dist/ui/dist/assets/quadrantDiagram-D57K4MC3-8hIWayC2.js +7 -0
- package/dist/ui/dist/assets/radar-ABXABTNO-BTdwUOOF.js +1 -0
- package/dist/ui/dist/assets/railroad-I3PHUGI6-DcMbDqBL.js +1 -0
- package/dist/ui/dist/assets/railroad-abnf-LNEFI6M7-gTzUTWeI.js +1 -0
- package/dist/ui/dist/assets/railroad-ebnf-DWYD2UWJ-CNtdUPtk.js +1 -0
- package/dist/ui/dist/assets/railroad-peg-JR7BVNR7-DOiExAtE.js +1 -0
- package/dist/ui/dist/assets/railroadDiagram-Y72NRBL7-2kE_ygvM.js +1 -0
- package/dist/ui/dist/assets/requirementDiagram-EQYSW5RA-CoIAHRq2.js +84 -0
- package/dist/ui/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/dist/ui/dist/assets/sankeyDiagram-SY24JHO5-7p8D3wim.js +40 -0
- package/dist/ui/dist/assets/sequenceDiagram-BLEHNHQU-C6QFnMvn.js +169 -0
- package/dist/ui/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/ui/dist/assets/src-FgijoBVs.js +1 -0
- package/dist/ui/dist/assets/stateDiagram-v2-A6IESKWZ-n9eAb_bT.js +291 -0
- package/dist/ui/dist/assets/swimlanes-B3ENRKFQ-U18mtUzX.js +1 -0
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-DX07QVy9.js +8 -0
- package/dist/ui/dist/assets/timeline-definition-JXYKGLGB-CD2Z8QGP.js +120 -0
- package/dist/ui/dist/assets/treeView-D4JQ5SDB-sqUjH5gB.js +1 -0
- package/dist/ui/dist/assets/treemap-SAJKECNS-DvZ38FP7.js +1 -0
- package/dist/ui/dist/assets/usecaseDiagram-WPOLBNNX-CqOGidaZ.js +328 -0
- package/dist/ui/dist/assets/vennDiagram-Q3Y3IDCW-D1SXSBQ5.js +34 -0
- package/dist/ui/dist/assets/wardley-7MLQ67FV-Bpwio-Dx.js +1 -0
- package/dist/ui/dist/assets/wardleyDiagram-IVAIDKWA-JSUXHMnk.js +78 -0
- package/dist/ui/dist/assets/xychartDiagram-4URNHDU4-B4IFqQTN.js +7 -0
- package/dist/ui/dist/index.html +15 -0
- package/dist/ui/dist/style.css +121 -0
- package/package.json +31 -8
- package/skills/follow-me/SKILL.md +15 -61
- package/dist/src/anthropic.js +0 -101
- package/dist/ui/app.js +0 -797
- package/dist/ui/index.html +0 -40
- package/dist/ui/playback.js +0 -175
- package/dist/ui/style.css +0 -466
package/dist/ui/index.html
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<meta name="color-scheme" content="light dark" />
|
|
7
|
-
<title>follow-me review</title>
|
|
8
|
-
<link rel="stylesheet" href="/style.css" />
|
|
9
|
-
</head>
|
|
10
|
-
<body>
|
|
11
|
-
<header class="topbar">
|
|
12
|
-
<strong>follow-me</strong>
|
|
13
|
-
<span id="summary" class="muted"></span>
|
|
14
|
-
<nav id="sessions" class="sessions" aria-label="Sessions" hidden></nav>
|
|
15
|
-
<span id="status" class="status" role="status"></span>
|
|
16
|
-
<button id="view-toggle" type="button" title="Play the branch commit by commit (space)">
|
|
17
|
-
Play
|
|
18
|
-
</button>
|
|
19
|
-
<button id="end" type="button" class="primary">End review</button>
|
|
20
|
-
</header>
|
|
21
|
-
<section id="player" class="player" aria-label="Playback" hidden>
|
|
22
|
-
<button id="play" type="button" class="primary" title="Play or pause (space)">Play</button>
|
|
23
|
-
<button id="prev" type="button" title="Previous commit (p)">Previous</button>
|
|
24
|
-
<button id="next" type="button" title="Next commit (n)">Next</button>
|
|
25
|
-
<label>Speed <select id="speed"></select></label>
|
|
26
|
-
<label>Voice <select id="voice"></select></label>
|
|
27
|
-
<button id="mute" type="button" aria-pressed="false">Mute</button>
|
|
28
|
-
<p id="caption" class="caption" aria-live="polite"></p>
|
|
29
|
-
</section>
|
|
30
|
-
<div id="app" class="layout">
|
|
31
|
-
<nav class="sidebar" aria-label="Commits">
|
|
32
|
-
<ol id="commits" class="commit-list"></ol>
|
|
33
|
-
<section id="plan-note" class="plan-note"></section>
|
|
34
|
-
</nav>
|
|
35
|
-
<main id="main" class="main"></main>
|
|
36
|
-
</div>
|
|
37
|
-
<div id="done" class="done" hidden></div>
|
|
38
|
-
<script src="/app.js" type="module"></script>
|
|
39
|
-
</body>
|
|
40
|
-
</html>
|
package/dist/ui/playback.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
// Pure playback logic. The browser imports it from /playback.js and the tests import it
|
|
3
|
-
// directly, so nothing here may touch the DOM or speech APIs.
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @typedef {{ op: " ", text: string } | { op: "+" | "-", text: string, change: number }} HunkLine
|
|
7
|
-
* @typedef {{ kind: "hunk", id: string, path: string, body: HunkLine[] }
|
|
8
|
-
* | { kind: "file" | "mode", id: string, path: string }} Unit
|
|
9
|
-
* @typedef {{ kind: "rest" } | { kind: "lines", changes: number[] }} Selection
|
|
10
|
-
* @typedef {{ unit: string, select: Selection, narration?: string }} Ref
|
|
11
|
-
* @typedef {{ index: number, message: string, narration?: string, refs: Ref[] }} Commit
|
|
12
|
-
* @typedef {{ kind: "commit", commit: number, say: string }
|
|
13
|
-
* | { kind: "unit", commit: number, unit: string, say: string | null }
|
|
14
|
-
* | { kind: "line", commit: number, unit: string, line: number, change: number, op: "+" | "-" }} Step
|
|
15
|
-
* @typedef {"hidden" | "shown" | "gone"} LineState
|
|
16
|
-
* @typedef {{ name: string, lang: string, default: boolean }} VoiceInfo
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
export const SPEEDS = [0.5, 0.75, 1, 1.25, 1.5, 2, 3];
|
|
20
|
-
|
|
21
|
-
const LINES_PER_SECOND = 4;
|
|
22
|
-
const COMMIT_BEAT_MS = 700;
|
|
23
|
-
const UNIT_BEAT_MS = 350;
|
|
24
|
-
const WORDS_PER_SECOND = 2.5;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* The key a diff line carries in the DOM, unit id plus its index in the hunk body.
|
|
28
|
-
* @param {string} unit
|
|
29
|
-
* @param {number} line
|
|
30
|
-
*/
|
|
31
|
-
export function lineKey(unit, line) {
|
|
32
|
-
return `${unit}:${line}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* What is spoken when a commit starts: its narration, else its title.
|
|
37
|
-
* @param {Commit} commit
|
|
38
|
-
*/
|
|
39
|
-
export function commitNarration(commit) {
|
|
40
|
-
const narration = commit.narration?.trim();
|
|
41
|
-
if (narration) return narration;
|
|
42
|
-
return commit.message.split("\n")[0] ?? "";
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Every step of the playback in order: a commit begins, each of its units begins, and each of
|
|
47
|
-
* the commit's own change lines appears (+) or fades out (-) in hunk order.
|
|
48
|
-
* @param {readonly Commit[]} commits
|
|
49
|
-
* @param {readonly Unit[]} units
|
|
50
|
-
* @returns {Step[]}
|
|
51
|
-
*/
|
|
52
|
-
export function buildScript(commits, units) {
|
|
53
|
-
const byId = new Map(units.map((u) => [u.id, u]));
|
|
54
|
-
/** @type {Step[]} */
|
|
55
|
-
const steps = [];
|
|
56
|
-
for (const commit of commits) {
|
|
57
|
-
steps.push({ kind: "commit", commit: commit.index, say: commitNarration(commit) });
|
|
58
|
-
for (const ref of commit.refs) {
|
|
59
|
-
const unit = byId.get(ref.unit);
|
|
60
|
-
if (unit === undefined) continue;
|
|
61
|
-
const say = ref.narration?.trim() || null;
|
|
62
|
-
steps.push({ kind: "unit", commit: commit.index, unit: unit.id, say });
|
|
63
|
-
if (unit.kind !== "hunk" || ref.select.kind !== "lines") continue;
|
|
64
|
-
const mine = new Set(ref.select.changes);
|
|
65
|
-
unit.body.forEach((line, i) => {
|
|
66
|
-
if (line.op === " " || !mine.has(line.change)) return;
|
|
67
|
-
steps.push({
|
|
68
|
-
kind: "line",
|
|
69
|
-
commit: commit.index,
|
|
70
|
-
unit: unit.id,
|
|
71
|
-
line: i,
|
|
72
|
-
change: line.change,
|
|
73
|
-
op: line.op,
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return steps;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* How long to rest after a step at the given speed. Lines arrive a few per second; removals
|
|
83
|
-
* take half as long because nothing new needs reading.
|
|
84
|
-
* @param {Step} step
|
|
85
|
-
* @param {number} speed
|
|
86
|
-
*/
|
|
87
|
-
export function stepDelay(step, speed) {
|
|
88
|
-
if (step.kind === "commit") return COMMIT_BEAT_MS / speed;
|
|
89
|
-
if (step.kind === "unit") return UNIT_BEAT_MS / speed;
|
|
90
|
-
const line = 1000 / (LINES_PER_SECOND * speed);
|
|
91
|
-
return step.op === "+" ? line : line / 2;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* A generous upper bound on how long speaking the text takes, for when the browser never
|
|
96
|
-
* reports the end of an utterance.
|
|
97
|
-
* @param {string} text
|
|
98
|
-
* @param {number} speed
|
|
99
|
-
*/
|
|
100
|
-
export function speechTimeout(text, speed) {
|
|
101
|
-
const words = text.split(/\s+/).filter(Boolean).length;
|
|
102
|
-
return (2 * 1000 * words) / (WORDS_PER_SECOND * speed) + 2000;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* The first step of a commit, clamped to the script. Seeking past the end lands on the end.
|
|
107
|
-
* @param {readonly Step[]} script
|
|
108
|
-
* @param {number} commit
|
|
109
|
-
*/
|
|
110
|
-
export function commitStart(script, commit) {
|
|
111
|
-
const at = script.findIndex((s) => s.kind === "commit" && s.commit === commit);
|
|
112
|
-
if (at !== -1) return at;
|
|
113
|
-
return commit < 0 ? 0 : script.length;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* The commit on screen at a position: the one whose step was performed last.
|
|
118
|
-
* @param {readonly Step[]} script
|
|
119
|
-
* @param {number} position
|
|
120
|
-
*/
|
|
121
|
-
export function commitAt(script, position) {
|
|
122
|
-
return script[Math.max(0, Math.min(position, script.length) - 1)]?.commit ?? 0;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* How each change line of a commit looks after `position` steps, and which line was written
|
|
127
|
-
* last. Added lines are hidden until typed; removed lines stay shown until their step.
|
|
128
|
-
* @param {readonly Step[]} script
|
|
129
|
-
* @param {number} position
|
|
130
|
-
* @param {number} commit
|
|
131
|
-
* @returns {{ states: Map<string, LineState>, current: string | null }}
|
|
132
|
-
*/
|
|
133
|
-
export function lineStates(script, position, commit) {
|
|
134
|
-
/** @type {Map<string, LineState>} */
|
|
135
|
-
const states = new Map();
|
|
136
|
-
/** @type {string | null} */
|
|
137
|
-
let current = null;
|
|
138
|
-
script.forEach((step, i) => {
|
|
139
|
-
if (step.kind !== "line" || step.commit !== commit) return;
|
|
140
|
-
const key = lineKey(step.unit, step.line);
|
|
141
|
-
const done = i < position;
|
|
142
|
-
if (step.op === "+") states.set(key, done ? "shown" : "hidden");
|
|
143
|
-
else states.set(key, done ? "gone" : "shown");
|
|
144
|
-
if (done) current = key;
|
|
145
|
-
});
|
|
146
|
-
if (commitAt(script, position) !== commit) current = null;
|
|
147
|
-
return { states, current };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* A speed from storage or a select, snapped to the offered steps.
|
|
152
|
-
* @param {unknown} value
|
|
153
|
-
*/
|
|
154
|
-
export function parseSpeed(value) {
|
|
155
|
-
const n = Number(value);
|
|
156
|
-
return SPEEDS.includes(n) ? n : 1;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* The voice to preselect: the saved one if the browser still has it, else the browser's
|
|
161
|
-
* default, else the first voice in the reader's language, else none.
|
|
162
|
-
* @param {readonly VoiceInfo[]} voices
|
|
163
|
-
* @param {string | null} saved
|
|
164
|
-
* @param {string} language
|
|
165
|
-
* @returns {string | null}
|
|
166
|
-
*/
|
|
167
|
-
export function pickVoice(voices, saved, language) {
|
|
168
|
-
if (saved !== null && voices.some((v) => v.name === saved)) return saved;
|
|
169
|
-
const prefix = language.split("-")[0] ?? "";
|
|
170
|
-
const match =
|
|
171
|
-
voices.find((v) => v.default && v.lang.startsWith(prefix)) ??
|
|
172
|
-
voices.find((v) => v.lang === language) ??
|
|
173
|
-
voices.find((v) => v.lang.startsWith(prefix));
|
|
174
|
-
return match?.name ?? null;
|
|
175
|
-
}
|
package/dist/ui/style.css
DELETED
|
@@ -1,466 +0,0 @@
|
|
|
1
|
-
:root {
|
|
2
|
-
color-scheme: light dark;
|
|
3
|
-
--bg: #ffffff;
|
|
4
|
-
--panel: #f6f7f9;
|
|
5
|
-
--border: #d8dce2;
|
|
6
|
-
--text: #1c2026;
|
|
7
|
-
--muted: #6a7280;
|
|
8
|
-
--accent: #2459d6;
|
|
9
|
-
--accent-text: #ffffff;
|
|
10
|
-
--add-bg: #e3f6e8;
|
|
11
|
-
--add-text: #0f5a26;
|
|
12
|
-
--del-bg: #fde7e7;
|
|
13
|
-
--del-text: #8a1c1c;
|
|
14
|
-
--select-bg: #fff3c4;
|
|
15
|
-
--comment-bg: #eef3ff;
|
|
16
|
-
--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
@media (prefers-color-scheme: dark) {
|
|
20
|
-
:root {
|
|
21
|
-
--bg: #14171c;
|
|
22
|
-
--panel: #1c2027;
|
|
23
|
-
--border: #2f353f;
|
|
24
|
-
--text: #e4e7ec;
|
|
25
|
-
--muted: #8d96a5;
|
|
26
|
-
--accent: #6c9bff;
|
|
27
|
-
--accent-text: #0b1220;
|
|
28
|
-
--add-bg: #12301c;
|
|
29
|
-
--add-text: #7fdc9a;
|
|
30
|
-
--del-bg: #3a1a1c;
|
|
31
|
-
--del-text: #ff9b9b;
|
|
32
|
-
--select-bg: #4a4012;
|
|
33
|
-
--comment-bg: #1b2538;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
* {
|
|
38
|
-
box-sizing: border-box;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
body {
|
|
42
|
-
margin: 0;
|
|
43
|
-
background: var(--bg);
|
|
44
|
-
color: var(--text);
|
|
45
|
-
font:
|
|
46
|
-
14px / 1.5 system-ui,
|
|
47
|
-
sans-serif;
|
|
48
|
-
height: 100vh;
|
|
49
|
-
display: flex;
|
|
50
|
-
flex-direction: column;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.muted {
|
|
54
|
-
color: var(--muted);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.topbar {
|
|
58
|
-
display: flex;
|
|
59
|
-
align-items: center;
|
|
60
|
-
gap: 12px;
|
|
61
|
-
padding: 8px 16px;
|
|
62
|
-
border-bottom: 1px solid var(--border);
|
|
63
|
-
background: var(--panel);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.sessions {
|
|
67
|
-
display: flex;
|
|
68
|
-
gap: 8px;
|
|
69
|
-
font-size: 13px;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.sessions[hidden] {
|
|
73
|
-
display: none;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.sessions a {
|
|
77
|
-
color: var(--accent);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.sessions a[aria-current="page"] {
|
|
81
|
-
color: var(--text);
|
|
82
|
-
font-weight: 600;
|
|
83
|
-
text-decoration: none;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
body.index {
|
|
87
|
-
display: block;
|
|
88
|
-
height: auto;
|
|
89
|
-
padding: 16px 24px;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.session-index {
|
|
93
|
-
padding-left: 18px;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.session-index a {
|
|
97
|
-
color: var(--accent);
|
|
98
|
-
font-weight: 600;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.status {
|
|
102
|
-
margin-left: auto;
|
|
103
|
-
color: var(--del-text);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
button {
|
|
107
|
-
font: inherit;
|
|
108
|
-
padding: 3px 10px;
|
|
109
|
-
border: 1px solid var(--border);
|
|
110
|
-
border-radius: 6px;
|
|
111
|
-
background: var(--bg);
|
|
112
|
-
color: var(--text);
|
|
113
|
-
cursor: pointer;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
button:hover {
|
|
117
|
-
border-color: var(--accent);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
button.primary {
|
|
121
|
-
background: var(--accent);
|
|
122
|
-
color: var(--accent-text);
|
|
123
|
-
border-color: var(--accent);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
button.link {
|
|
127
|
-
border: none;
|
|
128
|
-
background: none;
|
|
129
|
-
color: var(--accent);
|
|
130
|
-
padding: 0 4px;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.layout {
|
|
134
|
-
flex: 1;
|
|
135
|
-
display: grid;
|
|
136
|
-
grid-template-columns: 300px 1fr;
|
|
137
|
-
min-height: 0;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.sidebar {
|
|
141
|
-
display: flex;
|
|
142
|
-
flex-direction: column;
|
|
143
|
-
border-right: 1px solid var(--border);
|
|
144
|
-
background: var(--panel);
|
|
145
|
-
min-height: 0;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.commit-list {
|
|
149
|
-
list-style: none;
|
|
150
|
-
margin: 0;
|
|
151
|
-
padding: 0;
|
|
152
|
-
overflow-y: auto;
|
|
153
|
-
flex: 1;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
.commit-list li {
|
|
157
|
-
display: flex;
|
|
158
|
-
gap: 8px;
|
|
159
|
-
padding: 8px 12px;
|
|
160
|
-
cursor: pointer;
|
|
161
|
-
border-left: 3px solid transparent;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.commit-list li:hover {
|
|
165
|
-
background: var(--bg);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.commit-list li.selected {
|
|
169
|
-
background: var(--bg);
|
|
170
|
-
border-left-color: var(--accent);
|
|
171
|
-
font-weight: 600;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.commit-list .index {
|
|
175
|
-
color: var(--muted);
|
|
176
|
-
min-width: 2ch;
|
|
177
|
-
text-align: right;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.commit-list .title {
|
|
181
|
-
flex: 1;
|
|
182
|
-
overflow: hidden;
|
|
183
|
-
text-overflow: ellipsis;
|
|
184
|
-
white-space: nowrap;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
.badge {
|
|
188
|
-
background: var(--accent);
|
|
189
|
-
color: var(--accent-text);
|
|
190
|
-
border-radius: 10px;
|
|
191
|
-
padding: 0 7px;
|
|
192
|
-
font-size: 12px;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.plan-note {
|
|
196
|
-
border-top: 1px solid var(--border);
|
|
197
|
-
padding: 10px 12px;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
.main {
|
|
201
|
-
overflow-y: auto;
|
|
202
|
-
padding: 16px 24px 64px;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
.commit-head h2 {
|
|
206
|
-
margin: 0 0 4px;
|
|
207
|
-
font-size: 16px;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.message {
|
|
211
|
-
font-family: var(--font-mono);
|
|
212
|
-
white-space: pre-wrap;
|
|
213
|
-
margin: 4px 0 8px;
|
|
214
|
-
padding: 8px 12px;
|
|
215
|
-
background: var(--panel);
|
|
216
|
-
border-radius: 6px;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.controls {
|
|
220
|
-
display: flex;
|
|
221
|
-
gap: 8px;
|
|
222
|
-
margin: 4px 0;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
.file-group {
|
|
226
|
-
margin-top: 20px;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.file-group > h3 {
|
|
230
|
-
margin: 0 0 6px;
|
|
231
|
-
font: 600 13px var(--font-mono);
|
|
232
|
-
color: var(--muted);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.unit {
|
|
236
|
-
border: 1px solid var(--border);
|
|
237
|
-
border-radius: 6px;
|
|
238
|
-
margin-bottom: 12px;
|
|
239
|
-
overflow: hidden;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
.unit-head {
|
|
243
|
-
display: flex;
|
|
244
|
-
gap: 8px;
|
|
245
|
-
align-items: center;
|
|
246
|
-
padding: 3px 10px;
|
|
247
|
-
background: var(--panel);
|
|
248
|
-
color: var(--muted);
|
|
249
|
-
font: 12px var(--font-mono);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
.unit-head .spacer {
|
|
253
|
-
flex: 1;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
.card {
|
|
257
|
-
padding: 6px 10px;
|
|
258
|
-
font-family: var(--font-mono);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
.diff {
|
|
262
|
-
font: 12.5px / 1.5 var(--font-mono);
|
|
263
|
-
overflow-x: auto;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
.dl {
|
|
267
|
-
display: flex;
|
|
268
|
-
white-space: pre;
|
|
269
|
-
min-width: max-content;
|
|
270
|
-
max-height: 2em;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
.dl .gutter {
|
|
274
|
-
width: 4ch;
|
|
275
|
-
text-align: right;
|
|
276
|
-
padding-right: 6px;
|
|
277
|
-
color: var(--muted);
|
|
278
|
-
user-select: none;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
.dl .op {
|
|
282
|
-
width: 2ch;
|
|
283
|
-
user-select: none;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
.dl .label {
|
|
287
|
-
margin-left: 12px;
|
|
288
|
-
font-size: 11px;
|
|
289
|
-
color: var(--muted);
|
|
290
|
-
font-style: italic;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
.dl.add.mine {
|
|
294
|
-
background: var(--add-bg);
|
|
295
|
-
color: var(--add-text);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
.dl.del.mine {
|
|
299
|
-
background: var(--del-bg);
|
|
300
|
-
color: var(--del-text);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.dl.other {
|
|
304
|
-
opacity: 0.4;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
.dl.mine.pickable {
|
|
308
|
-
cursor: pointer;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
.dl.picked {
|
|
312
|
-
outline: 2px solid var(--accent);
|
|
313
|
-
outline-offset: -2px;
|
|
314
|
-
background: var(--select-bg);
|
|
315
|
-
color: var(--text);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
.comments {
|
|
319
|
-
padding: 0 10px;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
.comment {
|
|
323
|
-
background: var(--comment-bg);
|
|
324
|
-
border-radius: 6px;
|
|
325
|
-
margin: 8px 0;
|
|
326
|
-
padding: 6px 10px;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
.comment .meta {
|
|
330
|
-
color: var(--muted);
|
|
331
|
-
font-size: 12px;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
.comment .text {
|
|
335
|
-
white-space: pre-wrap;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
textarea {
|
|
339
|
-
width: 100%;
|
|
340
|
-
min-height: 64px;
|
|
341
|
-
font: inherit;
|
|
342
|
-
padding: 6px 8px;
|
|
343
|
-
color: var(--text);
|
|
344
|
-
background: var(--bg);
|
|
345
|
-
border: 1px solid var(--border);
|
|
346
|
-
border-radius: 6px;
|
|
347
|
-
resize: vertical;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
.box {
|
|
351
|
-
margin: 8px 0;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
.box .row {
|
|
355
|
-
display: flex;
|
|
356
|
-
gap: 8px;
|
|
357
|
-
margin-top: 4px;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
.done {
|
|
361
|
-
position: fixed;
|
|
362
|
-
inset: 0;
|
|
363
|
-
display: grid;
|
|
364
|
-
place-items: center;
|
|
365
|
-
background: var(--bg);
|
|
366
|
-
font-size: 16px;
|
|
367
|
-
padding: 24px;
|
|
368
|
-
text-align: center;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
.done[hidden] {
|
|
372
|
-
display: none;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
.done code {
|
|
376
|
-
font-family: var(--font-mono);
|
|
377
|
-
background: var(--panel);
|
|
378
|
-
padding: 1px 6px;
|
|
379
|
-
border-radius: 4px;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
.player {
|
|
383
|
-
display: flex;
|
|
384
|
-
flex-wrap: wrap;
|
|
385
|
-
align-items: center;
|
|
386
|
-
gap: 8px 12px;
|
|
387
|
-
padding: 6px 16px;
|
|
388
|
-
border-bottom: 1px solid var(--border);
|
|
389
|
-
background: var(--panel);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
.player[hidden] {
|
|
393
|
-
display: none;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
.player label {
|
|
397
|
-
display: flex;
|
|
398
|
-
align-items: center;
|
|
399
|
-
gap: 6px;
|
|
400
|
-
color: var(--muted);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
select {
|
|
404
|
-
font: inherit;
|
|
405
|
-
padding: 2px 6px;
|
|
406
|
-
border: 1px solid var(--border);
|
|
407
|
-
border-radius: 6px;
|
|
408
|
-
background: var(--bg);
|
|
409
|
-
color: var(--text);
|
|
410
|
-
max-width: 220px;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
button:disabled,
|
|
414
|
-
select:disabled {
|
|
415
|
-
opacity: 0.5;
|
|
416
|
-
cursor: default;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.caption {
|
|
420
|
-
flex: 1 1 320px;
|
|
421
|
-
margin: 0;
|
|
422
|
-
font-style: italic;
|
|
423
|
-
color: var(--text);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
.dl.pending {
|
|
427
|
-
display: none;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
.dl.gone {
|
|
431
|
-
opacity: 0;
|
|
432
|
-
max-height: 0;
|
|
433
|
-
overflow: hidden;
|
|
434
|
-
transition:
|
|
435
|
-
opacity 0.5s ease,
|
|
436
|
-
max-height 0.3s ease 0.5s;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
.dl.current {
|
|
440
|
-
outline: 2px solid var(--accent);
|
|
441
|
-
outline-offset: -2px;
|
|
442
|
-
animation: type-in 0.25s steps(16, end);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
.dl.gone.current {
|
|
446
|
-
opacity: 0.45;
|
|
447
|
-
max-height: 2em;
|
|
448
|
-
text-decoration: line-through;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
@keyframes type-in {
|
|
452
|
-
from {
|
|
453
|
-
clip-path: inset(0 100% 0 0);
|
|
454
|
-
}
|
|
455
|
-
to {
|
|
456
|
-
clip-path: inset(0 0 0 0);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
@media (prefers-reduced-motion: reduce) {
|
|
461
|
-
.dl.current,
|
|
462
|
-
.dl.gone {
|
|
463
|
-
animation: none;
|
|
464
|
-
transition: none;
|
|
465
|
-
}
|
|
466
|
-
}
|