@ok-ggwp/git-gloss 0.2.0 → 0.4.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 +70 -3
- package/dist/ary/browse.js +80 -0
- package/dist/ary/client.js +401 -0
- package/dist/ary/index.html +13 -0
- package/dist/ary/server.js +101 -0
- package/dist/ary/style.css +115 -0
- package/dist/attention.js +34 -0
- package/dist/carry.js +4 -1
- package/dist/cli.js +3 -1
- package/dist/commands/add.js +17 -1
- package/dist/commands/ary.js +35 -0
- package/dist/commands/html.js +1 -1
- package/dist/commands/hunks.js +8 -1
- package/dist/commands/init.js +6 -1
- package/dist/commands/show.js +44 -16
- package/dist/commands/signals.js +88 -0
- package/dist/config.js +192 -0
- package/dist/floor.js +148 -0
- package/dist/git.js +13 -2
- package/dist/glob.js +76 -0
- package/dist/html/client.js +1016 -296
- package/dist/html/data.js +26 -1
- package/dist/html/page.js +1 -1
- package/dist/html/style.css +284 -164
- package/dist/loader.js +1 -1
- package/dist/merge.js +5 -0
- package/dist/render.js +51 -3
- package/dist/safe-match.js +28 -0
- package/dist/schema.js +46 -2
- package/dist/signals.js +292 -0
- package/dist/validate.js +6 -0
- package/package.json +1 -1
- package/skills/gloss/SKILL.md +31 -1
package/dist/html/data.js
CHANGED
|
@@ -2,8 +2,10 @@ import { commitDiff, commitSubject, git } from '../git.js';
|
|
|
2
2
|
import { reviewStatus, statusPhrase, verdict } from '../reviews.js';
|
|
3
3
|
import { parseDiff } from '../diff.js';
|
|
4
4
|
import { loadGloss } from '../loader.js';
|
|
5
|
+
import { belowFloor } from '../render.js';
|
|
6
|
+
import { showsImpact, tryAnalyze } from '../floor.js';
|
|
5
7
|
import { bodyLines, changedCount, gapText, isGap, refLabel, slice, sliceStats } from '../selection.js';
|
|
6
|
-
import { NECESSITY, NECESSITY_HELP, servesOf } from '../schema.js';
|
|
8
|
+
import { IMPACT, IMPACT_HELP, NECESSITY, NECESSITY_HELP, servesOf } from '../schema.js';
|
|
7
9
|
function pageReviews(sha) {
|
|
8
10
|
return reviewStatus(sha).map((s) => ({
|
|
9
11
|
stage: s.stage,
|
|
@@ -57,10 +59,23 @@ function pageHunk(h, lines, status, alsoIn) {
|
|
|
57
59
|
lines: pageLines(h, lines),
|
|
58
60
|
};
|
|
59
61
|
}
|
|
62
|
+
// The order a reviewer should read a commit's groups in. Deterministic: a
|
|
63
|
+
// stable sort over the groups as they come, so ties keep their order.
|
|
64
|
+
export function reviewOrder(groups, focus = []) {
|
|
65
|
+
const rank = (g) => IMPACT.indexOf((g.impact ?? g.impact_derived)) + 1; // 0: unrated
|
|
66
|
+
const first = (g) => (focus.includes(g.id) ? focus.indexOf(g.id) : focus.length);
|
|
67
|
+
return groups
|
|
68
|
+
.map((g, i) => ({ g, i }))
|
|
69
|
+
.sort((a, b) => first(a.g) - first(b.g) || rank(b.g) - rank(a.g) || (b.g.added + b.g.removed) - (a.g.added + a.g.removed) || a.i - b.i)
|
|
70
|
+
.map((x) => x.g.id);
|
|
71
|
+
}
|
|
60
72
|
const allLines = (h) => Array.from({ length: changedCount(h) }, (_, i) => i + 1);
|
|
61
73
|
export function commitData(gloss) {
|
|
62
74
|
const [author, date] = git(['log', '-1', '--format=%an%x00%aI', gloss.sha]).trim().split('\0');
|
|
63
75
|
const a = gloss.note.annotation;
|
|
76
|
+
const an = tryAnalyze(gloss);
|
|
77
|
+
// Same threshold as `show`: derived impact badges only where the diff puts a group above module.
|
|
78
|
+
const derivedImpact = showsImpact(gloss.groups.map((x) => x.group), an);
|
|
64
79
|
// Which groups claim each changed line, so shared lines are split between
|
|
65
80
|
// their groups in the bar and each hunk can link to the groups it shares
|
|
66
81
|
// lines with. A header-only hunk counts as one shared unit.
|
|
@@ -85,6 +100,9 @@ export function commitData(gloss) {
|
|
|
85
100
|
id: g.id,
|
|
86
101
|
intent: g.intent,
|
|
87
102
|
necessity: g.necessity,
|
|
103
|
+
impact: g.impact,
|
|
104
|
+
impact_reason: g.impact_reason,
|
|
105
|
+
impact_derived: g.impact || !derivedImpact ? undefined : an?.groups.get(g.id)?.floor,
|
|
88
106
|
serves: servesOf(g),
|
|
89
107
|
removal_impact: g.removal_impact,
|
|
90
108
|
evidence: g.evidence,
|
|
@@ -108,8 +126,11 @@ export function commitData(gloss) {
|
|
|
108
126
|
glossed: true,
|
|
109
127
|
task: a.task,
|
|
110
128
|
summary: a.summary,
|
|
129
|
+
attention: a.attention ?? null,
|
|
130
|
+
computed: an ? { floor: an.floor, derived: an.derived, below: belowFloor(a.attention, an) } : null,
|
|
111
131
|
warnings: gloss.warnings,
|
|
112
132
|
groups,
|
|
133
|
+
order: reviewOrder(groups, a.attention?.focus),
|
|
113
134
|
unannotated: gloss.unannotated.map((u) => pageHunk(u.hunk, u.lines, 'unannotated', [])),
|
|
114
135
|
alternatives: a.alternatives ?? [],
|
|
115
136
|
risks: a.risks ?? [],
|
|
@@ -125,8 +146,11 @@ function unglossedData(sha) {
|
|
|
125
146
|
date,
|
|
126
147
|
glossed: false,
|
|
127
148
|
task: '',
|
|
149
|
+
attention: null,
|
|
150
|
+
computed: null,
|
|
128
151
|
warnings: [],
|
|
129
152
|
groups: [],
|
|
153
|
+
order: [],
|
|
130
154
|
unannotated: parseDiff(commitDiff(sha)).map((h) => pageHunk(h, null, 'unannotated', [])),
|
|
131
155
|
alternatives: [],
|
|
132
156
|
risks: [],
|
|
@@ -138,6 +162,7 @@ export function buildPageData(shas, range = null, merges = []) {
|
|
|
138
162
|
range,
|
|
139
163
|
merges: merges.map((sha) => ({ sha, subject: commitSubject(sha) })),
|
|
140
164
|
necessity: NECESSITY.map((name) => ({ name, help: NECESSITY_HELP[name] })),
|
|
165
|
+
impact: IMPACT.map((name) => ({ name, help: IMPACT_HELP[name] })),
|
|
141
166
|
commits: shas.map((sha) => {
|
|
142
167
|
const gloss = loadGloss(sha);
|
|
143
168
|
return gloss ? commitData(gloss) : unglossedData(sha);
|
package/dist/html/page.js
CHANGED
|
@@ -6,7 +6,7 @@ const asset = (name) => readFileSync(new URL(name, import.meta.url), 'utf8');
|
|
|
6
6
|
export function embedJson(data) {
|
|
7
7
|
return JSON.stringify(data).replace(/</g, '\\u003c');
|
|
8
8
|
}
|
|
9
|
-
const escapeHtml = (s) => s.replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
|
|
9
|
+
export const escapeHtml = (s) => s.replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
|
|
10
10
|
export function renderPage(data) {
|
|
11
11
|
// The page only runs our own inline script. If escaping ever failed, an
|
|
12
12
|
// injected script would lack the nonce and the CSP would block it.
|
package/dist/html/style.css
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
|
+
/* Review desk: a rail of groups (or commits) and a detail pane. System fonts
|
|
2
|
+
only: the page is one file and its CSP allows no other source. */
|
|
1
3
|
:root {
|
|
2
|
-
--bg: #
|
|
3
|
-
--
|
|
4
|
-
--
|
|
5
|
-
--
|
|
6
|
-
--
|
|
7
|
-
--
|
|
8
|
-
--
|
|
9
|
-
--accent: #
|
|
4
|
+
--bg: #f3f4f6;
|
|
5
|
+
--panel: #ffffff;
|
|
6
|
+
--ink: #14171f;
|
|
7
|
+
--muted: #586073;
|
|
8
|
+
--line: #dde0e6;
|
|
9
|
+
--accent: #3446d6;
|
|
10
|
+
--accent-soft: #e7eafb;
|
|
11
|
+
--accent-strong: #2536a8;
|
|
12
|
+
--on-accent: #ffffff;
|
|
13
|
+
--ok: #1b6b35;
|
|
14
|
+
--warn: #a5530a;
|
|
10
15
|
--warn-bg: #fff4e5;
|
|
11
16
|
--warn-border: #f0b35b;
|
|
12
17
|
--warn-text: #7a4300;
|
|
13
|
-
--add-bg: #
|
|
14
|
-
--add-mark: #
|
|
15
|
-
--del-bg: #
|
|
16
|
-
--del-mark: #
|
|
18
|
+
--add-bg: #e3f5e8;
|
|
19
|
+
--add-mark: #1b6b35;
|
|
20
|
+
--del-bg: #fbe7e8;
|
|
21
|
+
--del-mark: #b3262b;
|
|
17
22
|
--meta-bg: #eef1f8;
|
|
18
23
|
--meta-text: #4a5a85;
|
|
19
24
|
|
|
@@ -24,6 +29,19 @@
|
|
|
24
29
|
--n-speculative: #9c36b5;
|
|
25
30
|
--n-mechanical: #868e96;
|
|
26
31
|
|
|
32
|
+
/* Impact meter and attention gauge: one hue per step, and the steps differ
|
|
33
|
+
in lightness too, so they read without colour. */
|
|
34
|
+
--impact-local: #5b6373;
|
|
35
|
+
--impact-module: #0b6e83;
|
|
36
|
+
--impact-shared: #a5530a;
|
|
37
|
+
--impact-contract: #6b46c1;
|
|
38
|
+
--impact-sensitive: #b3262b;
|
|
39
|
+
--attention-skim: #5b6373;
|
|
40
|
+
--attention-look: #0b6e83;
|
|
41
|
+
--attention-careful: #a5530a;
|
|
42
|
+
--attention-must-read: #b3262b;
|
|
43
|
+
--attention-none: #586073;
|
|
44
|
+
|
|
27
45
|
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
|
|
28
46
|
--sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
29
47
|
color-scheme: light;
|
|
@@ -31,21 +49,24 @@
|
|
|
31
49
|
|
|
32
50
|
@media (prefers-color-scheme: dark) {
|
|
33
51
|
:root {
|
|
34
|
-
--bg: #
|
|
35
|
-
--
|
|
36
|
-
--
|
|
37
|
-
--
|
|
38
|
-
--
|
|
39
|
-
--
|
|
40
|
-
--
|
|
41
|
-
--accent: #
|
|
52
|
+
--bg: #101218;
|
|
53
|
+
--panel: #181b23;
|
|
54
|
+
--ink: #e8eaf0;
|
|
55
|
+
--muted: #a0a7b6;
|
|
56
|
+
--line: #2b303c;
|
|
57
|
+
--accent: #8fa0ff;
|
|
58
|
+
--accent-soft: #232a4a;
|
|
59
|
+
--accent-strong: #b3bfff;
|
|
60
|
+
--on-accent: #101218;
|
|
61
|
+
--ok: #62d089;
|
|
62
|
+
--warn: #f0a24a;
|
|
42
63
|
--warn-bg: #33270f;
|
|
43
64
|
--warn-border: #a2701e;
|
|
44
65
|
--warn-text: #f3c77a;
|
|
45
|
-
--add-bg: #
|
|
46
|
-
--add-mark: #
|
|
47
|
-
--del-bg: #
|
|
48
|
-
--del-mark: #
|
|
66
|
+
--add-bg: #14301d;
|
|
67
|
+
--add-mark: #62d089;
|
|
68
|
+
--del-bg: #3a1b1f;
|
|
69
|
+
--del-mark: #ff8085;
|
|
49
70
|
--meta-bg: #20263a;
|
|
50
71
|
--meta-text: #a3b1dc;
|
|
51
72
|
|
|
@@ -55,143 +76,183 @@
|
|
|
55
76
|
--n-incidental: #e0a43e;
|
|
56
77
|
--n-speculative: #cf7ee4;
|
|
57
78
|
--n-mechanical: #8d949b;
|
|
79
|
+
|
|
80
|
+
--impact-local: #a3aab8;
|
|
81
|
+
--impact-module: #4cc3d9;
|
|
82
|
+
--impact-shared: #f0a24a;
|
|
83
|
+
--impact-contract: #b39dff;
|
|
84
|
+
--impact-sensitive: #ff8085;
|
|
85
|
+
--attention-skim: #a3aab8;
|
|
86
|
+
--attention-look: #4cc3d9;
|
|
87
|
+
--attention-careful: #f0a24a;
|
|
88
|
+
--attention-must-read: #ff8085;
|
|
89
|
+
--attention-none: #a0a7b6;
|
|
58
90
|
color-scheme: dark;
|
|
59
91
|
}
|
|
60
92
|
}
|
|
61
93
|
|
|
62
94
|
* { box-sizing: border-box; }
|
|
63
95
|
html { -webkit-text-size-adjust: 100%; }
|
|
64
|
-
body {
|
|
65
|
-
margin: 0;
|
|
66
|
-
background: var(--bg);
|
|
67
|
-
color: var(--text);
|
|
68
|
-
font: 15px/1.55 var(--sans);
|
|
69
|
-
}
|
|
70
|
-
main { min-width: 0; }
|
|
96
|
+
body { margin: 0; background: var(--bg); color: var(--ink); font: 14px/1.5 var(--sans); }
|
|
71
97
|
a { color: var(--accent); text-decoration: none; }
|
|
72
98
|
a:hover { text-decoration: underline; }
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
margin: 0 auto;
|
|
81
|
-
padding: 24px 16px 64px;
|
|
82
|
-
gap: 32px;
|
|
83
|
-
}
|
|
84
|
-
@media (min-width: 1100px) {
|
|
85
|
-
.layout { grid-template-columns: 240px minmax(0, 1fr); padding: 32px 32px 80px; }
|
|
86
|
-
}
|
|
99
|
+
button { font: inherit; color: inherit; cursor: pointer; }
|
|
100
|
+
button:disabled { cursor: default; opacity: 0.5; }
|
|
101
|
+
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
102
|
+
code, .mono { font-family: var(--mono); font-size: 12px; }
|
|
103
|
+
.eyebrow { font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
|
|
104
|
+
.muted { color: var(--muted); }
|
|
105
|
+
kbd { font-family: var(--mono); font-size: 11px; border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 4px; padding: 0 5px; background: var(--panel); }
|
|
87
106
|
|
|
88
|
-
|
|
89
|
-
.toc { display: none; }
|
|
90
|
-
@media (min-width: 1100px) {
|
|
91
|
-
.toc { display: block; position: sticky; top: 24px; align-self: start; max-height: calc(100vh - 48px); overflow: auto; }
|
|
92
|
-
}
|
|
93
|
-
.toc h2 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin: 0 0 8px; }
|
|
94
|
-
.toc ol { list-style: none; margin: 0 0 24px; padding: 0; }
|
|
95
|
-
.toc li a {
|
|
96
|
-
display: flex; align-items: center; gap: 8px;
|
|
97
|
-
padding: 4px 8px; border-radius: 6px; color: var(--text);
|
|
98
|
-
font-family: var(--mono); font-size: 12.5px;
|
|
99
|
-
}
|
|
100
|
-
.toc li a:hover { background: var(--surface-2); text-decoration: none; }
|
|
101
|
-
.toc li.hidden a { opacity: 0.35; }
|
|
102
|
-
.toc li.active a { background: var(--surface-2); }
|
|
103
|
-
|
|
104
|
-
.dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--c); }
|
|
105
|
-
|
|
106
|
-
/* Header */
|
|
107
|
-
.commit { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; color: var(--muted); font-size: 13px; }
|
|
108
|
-
.sha { font-family: var(--mono); background: var(--surface-2); border: 1px solid var(--border); padding: 1px 6px; border-radius: 5px; color: var(--text); }
|
|
109
|
-
h1 { font-size: 24px; line-height: 1.3; margin: 8px 0 16px; letter-spacing: -0.01em; }
|
|
110
|
-
.statement { margin: 0 0 8px; max-width: 75ch; }
|
|
111
|
-
.statement .label { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; margin-right: 6px; }
|
|
112
|
-
|
|
113
|
-
.warning {
|
|
114
|
-
margin: 16px 0; padding: 12px 14px; border-radius: 8px;
|
|
115
|
-
background: var(--warn-bg); border: 1px solid var(--warn-border); color: var(--warn-text);
|
|
116
|
-
}
|
|
107
|
+
.desk { min-height: 100vh; display: flex; flex-direction: column; }
|
|
117
108
|
|
|
118
|
-
/*
|
|
119
|
-
.
|
|
120
|
-
.
|
|
121
|
-
.
|
|
122
|
-
.
|
|
123
|
-
.
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
109
|
+
/* Top bar */
|
|
110
|
+
.top { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 20px; padding: 10px 16px; background: var(--panel); border-bottom: 1px solid var(--line); }
|
|
111
|
+
.brand { font-weight: 600; letter-spacing: -0.01em; }
|
|
112
|
+
.sha { font-family: var(--mono); font-size: 12px; background: var(--bg); border: 1px solid var(--line); border-radius: 5px; padding: 1px 6px; color: var(--ink); }
|
|
113
|
+
.subject { flex: 1 1 260px; min-width: 0; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
114
|
+
.seg { display: flex; flex-wrap: wrap; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: var(--bg); }
|
|
115
|
+
.seg button { border: 0; background: transparent; padding: 6px 12px; min-height: 44px; color: var(--muted); }
|
|
116
|
+
.seg button[aria-pressed="true"] { background: var(--ink); color: var(--panel); font-weight: 500; }
|
|
117
|
+
.seg .n { opacity: 0.75; margin-left: 4px; font-variant-numeric: tabular-nums; }
|
|
118
|
+
.prog { display: flex; align-items: center; gap: 10px; color: var(--muted); font-size: 13px; }
|
|
119
|
+
.track { width: 120px; height: 6px; border-radius: 3px; background: var(--line); overflow: hidden; }
|
|
120
|
+
.track i { display: block; height: 100%; width: 0; background: var(--accent); }
|
|
121
|
+
|
|
122
|
+
/* Body: rail above the pane when narrow, beside it from 900px */
|
|
123
|
+
.body { flex: 1; display: grid; grid-template-columns: minmax(0, 1fr); align-items: start; }
|
|
124
|
+
@media (min-width: 900px) {
|
|
125
|
+
/* the rail's panel colour runs the full height, however tall the top bar is */
|
|
126
|
+
.body { grid-template-columns: 360px minmax(0, 1fr); background: linear-gradient(90deg, var(--panel) 359px, var(--line) 359px 360px, transparent 360px); }
|
|
127
|
+
.body.range { grid-template-columns: 400px minmax(0, 1fr); background: linear-gradient(90deg, var(--panel) 399px, var(--line) 399px 400px, transparent 400px); }
|
|
128
|
+
.rail { position: sticky; top: 0; max-height: 100vh; overflow: auto; overscroll-behavior: contain; border-bottom: 0; border-right: 1px solid var(--line); }
|
|
128
129
|
}
|
|
130
|
+
.rail { background: var(--panel); border-bottom: 1px solid var(--line); display: flex; flex-direction: column; min-width: 0; }
|
|
131
|
+
.main { min-width: 0; padding: 20px 16px 48px; }
|
|
132
|
+
@media (min-width: 900px) { .main { padding: 28px 40px 48px; } }
|
|
133
|
+
|
|
134
|
+
/* Attention: the card in the rail, the large one in a commit overview */
|
|
135
|
+
.att { margin: 20px 0 0; padding: 16px 18px; border: 1.5px solid var(--lv); border-radius: 12px; background: var(--panel); max-width: 720px; --lv: var(--attention-none); }
|
|
136
|
+
.att .lvl { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 22px; font-weight: 600; letter-spacing: 0.02em; color: var(--lv); }
|
|
137
|
+
.att .src { font-size: 12px; font-weight: 500; letter-spacing: 0; color: var(--muted); border: 1px solid var(--line); border-radius: 999px; padding: 0 8px; }
|
|
138
|
+
.att ul { margin: 10px 0 0; padding-left: 20px; }
|
|
139
|
+
.att li { margin-bottom: 4px; }
|
|
140
|
+
.att .look { margin: 10px 0 0; font-size: 13px; }
|
|
141
|
+
.att .override { margin: 10px 0 0; font-size: 13px; color: var(--warn-text); background: var(--warn-bg); border-radius: 6px; padding: 6px 10px; }
|
|
142
|
+
.att .override.mismatch { font-weight: 600; border: 1px solid var(--warn-border); }
|
|
143
|
+
.gauge { display: inline-flex; gap: 3px; }
|
|
144
|
+
.gauge i { display: block; width: 22px; height: 8px; border-radius: 2px; background: var(--line); }
|
|
145
|
+
.gauge i.on { background: var(--lv); }
|
|
146
|
+
.gauge.small i { width: 12px; height: 6px; }
|
|
147
|
+
|
|
148
|
+
/* Impact meter: five bars that grow, filled up to the level */
|
|
149
|
+
.meter { display: inline-flex; align-items: flex-end; gap: 2px; height: 16px; flex: none; }
|
|
150
|
+
.meter i { display: block; width: 4px; border-radius: 1px; background: var(--line); }
|
|
151
|
+
.meter i.on { background: var(--m, var(--impact-local)); }
|
|
152
|
+
.meter i:nth-child(1) { height: 6px; }
|
|
153
|
+
.meter i:nth-child(2) { height: 9px; }
|
|
154
|
+
.meter i:nth-child(3) { height: 11px; }
|
|
155
|
+
.meter i:nth-child(4) { height: 14px; }
|
|
156
|
+
.meter i:nth-child(5) { height: 16px; }
|
|
157
|
+
.meter.unrated i { background: transparent; border: 1px dashed var(--muted); }
|
|
158
|
+
|
|
159
|
+
/* Rail: groups */
|
|
160
|
+
.rail h2 { margin: 0; }
|
|
161
|
+
.railhead { padding: 4px 24px 8px; }
|
|
162
|
+
.necs { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 16px 10px; }
|
|
163
|
+
.chip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line); border-radius: 999px; padding: 2px 10px; font-size: 13px; background: var(--panel); color: var(--ink); }
|
|
164
|
+
button.chip { min-height: 32px; }
|
|
129
165
|
.chip .count { color: var(--muted); font-variant-numeric: tabular-nums; }
|
|
130
|
-
.chip[aria-pressed="false"] { opacity: 0.
|
|
131
|
-
.chip
|
|
132
|
-
.
|
|
133
|
-
|
|
134
|
-
.
|
|
135
|
-
.
|
|
136
|
-
.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
.
|
|
166
|
+
.chip[aria-pressed="false"] { opacity: 0.6; text-decoration: line-through; }
|
|
167
|
+
.chip.link { cursor: pointer; }
|
|
168
|
+
.chip.link:hover { background: var(--accent-soft); }
|
|
169
|
+
.rows { display: flex; flex-direction: column; padding: 0 8px; }
|
|
170
|
+
.row { display: flex; align-items: stretch; border-radius: 8px; border: 1px solid transparent; }
|
|
171
|
+
.row.sel { background: var(--accent-soft); border-color: var(--accent); }
|
|
172
|
+
.row.done .rtitle { color: var(--muted); }
|
|
173
|
+
.check { flex: 0 0 44px; min-height: 44px; border: 0; background: transparent; display: flex; align-items: center; justify-content: center; }
|
|
174
|
+
.check span { width: 18px; height: 18px; border: 1.5px solid var(--muted); border-radius: 5px; display: flex; align-items: center; justify-content: center; background: var(--panel); font-size: 13px; line-height: 1; }
|
|
175
|
+
.check[aria-pressed="true"] span { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
|
|
176
|
+
.pick { flex: 1; min-width: 0; border: 0; background: transparent; text-align: left; display: flex; align-items: center; gap: 10px; padding: 8px 8px 8px 0; min-height: 56px; }
|
|
177
|
+
.rtxt { display: flex; flex-direction: column; min-width: 0; flex: 1; }
|
|
178
|
+
.rtitle { font-weight: 500; line-height: 1.3; overflow-wrap: anywhere; }
|
|
179
|
+
.rsub { color: var(--muted); font-size: 12px; overflow-wrap: anywhere; }
|
|
180
|
+
.flag { flex: none; font-size: 11px; font-weight: 600; border: 1px solid var(--lv, var(--attention-careful)); color: var(--lv, var(--attention-careful)); border-radius: 4px; padding: 0 5px; }
|
|
181
|
+
.emptyrows { padding: 8px 24px 16px; color: var(--muted); }
|
|
182
|
+
.overviewrow { padding: 12px 8px 4px; }
|
|
183
|
+
.overviewrow .row .pick { min-height: 44px; padding-left: 16px; }
|
|
184
|
+
.railfoot { margin-top: auto; padding: 16px; border-top: 1px solid var(--line); font-size: 13px; }
|
|
185
|
+
.railfoot .eyebrow { margin-bottom: 6px; }
|
|
186
|
+
.railfoot a { display: inline-block; padding: 8px 0; }
|
|
141
187
|
|
|
142
|
-
/*
|
|
143
|
-
.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
.
|
|
148
|
-
.
|
|
149
|
-
.
|
|
150
|
-
.
|
|
151
|
-
.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
.
|
|
156
|
-
.
|
|
157
|
-
.
|
|
188
|
+
/* Rail: commits of a range */
|
|
189
|
+
.sum { margin: 16px; padding: 14px 16px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg); }
|
|
190
|
+
.sum .line { margin-top: 8px; color: var(--muted); font-size: 13px; }
|
|
191
|
+
.sum .line b { color: var(--ink); font-weight: 600; }
|
|
192
|
+
.strip { display: flex; gap: 3px; height: 14px; margin-top: 10px; }
|
|
193
|
+
.strip i { display: block; flex: 1; border-radius: 3px; background: var(--lv); min-width: 2px; }
|
|
194
|
+
.strip i.none { background: transparent; border: 1.5px dashed var(--attention-none); }
|
|
195
|
+
.commits { display: flex; flex-direction: column; padding: 0 8px 16px; }
|
|
196
|
+
.crow { margin-bottom: 2px; }
|
|
197
|
+
.crow > .pick { display: block; width: 100%; border: 1px solid transparent; border-radius: 8px; padding: 10px 12px; min-height: 64px; }
|
|
198
|
+
.crow.sel > .pick { background: var(--accent-soft); border-color: var(--accent); }
|
|
199
|
+
.rtop { display: flex; align-items: center; gap: 10px; }
|
|
200
|
+
.rtop .sha { border: 0; background: transparent; padding: 0; color: var(--muted); }
|
|
201
|
+
.lv { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; font-size: 11.5px; font-weight: 600; letter-spacing: 0.05em; color: var(--lv); }
|
|
202
|
+
.csubject { display: block; font-weight: 500; line-height: 1.3; margin-top: 2px; overflow-wrap: anywhere; }
|
|
203
|
+
.cmeta { display: block; color: var(--muted); font-size: 12px; margin-top: 2px; }
|
|
204
|
+
.crow .rows { padding: 2px 0 8px 12px; }
|
|
205
|
+
.mergelist { padding: 0 24px 16px; color: var(--muted); font-size: 13px; }
|
|
206
|
+
.mergelist .eyebrow { margin-bottom: 4px; }
|
|
207
|
+
|
|
208
|
+
/* Detail pane */
|
|
209
|
+
.crumbs { color: var(--muted); font-size: 13px; }
|
|
210
|
+
.commit { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; color: var(--muted); font-size: 13px; margin-bottom: 8px; }
|
|
211
|
+
h1 { font-size: 26px; line-height: 1.25; margin: 6px 0 14px; letter-spacing: -0.015em; font-weight: 600; max-width: 40ch; overflow-wrap: anywhere; }
|
|
212
|
+
h2 { font-size: 15px; margin: 28px 0 10px; }
|
|
213
|
+
.tags { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; }
|
|
214
|
+
.imp { display: inline-flex; align-items: center; gap: 8px; font-weight: 500; color: var(--m); }
|
|
215
|
+
.imp.derived { font-style: italic; }
|
|
216
|
+
.reason { margin: 10px 0 0; color: var(--muted); max-width: 70ch; overflow-wrap: anywhere; }
|
|
217
|
+
.stats { font-family: var(--mono); font-size: 12px; color: var(--muted); white-space: nowrap; }
|
|
158
218
|
.plus { color: var(--add-mark); }
|
|
159
219
|
.minus { color: var(--del-mark); }
|
|
160
220
|
.stale { font-size: 12px; color: var(--warn-text); background: var(--warn-bg); border-radius: 4px; padding: 0 6px; }
|
|
161
|
-
|
|
162
|
-
.
|
|
163
|
-
.
|
|
164
|
-
.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
.
|
|
177
|
-
.
|
|
178
|
-
.
|
|
221
|
+
.warning { margin: 16px 0 0; padding: 12px 14px; border-radius: 8px; max-width: 720px; background: var(--warn-bg); border: 1px solid var(--warn-border); color: var(--warn-text); }
|
|
222
|
+
.statement { margin: 12px 0 0; max-width: 75ch; overflow-wrap: anywhere; }
|
|
223
|
+
.statement .label { color: var(--muted); font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; margin-right: 8px; }
|
|
224
|
+
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; margin: 24px 0 8px; align-items: start; }
|
|
225
|
+
.card { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px; min-width: 0; }
|
|
226
|
+
.card p { margin: 4px 0 0; overflow-wrap: anywhere; }
|
|
227
|
+
.files { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
|
|
228
|
+
.frow { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 8px 16px; border-top: 1px solid var(--line); }
|
|
229
|
+
.frow:first-child { border-top: 0; }
|
|
230
|
+
.frow .mono { overflow-wrap: anywhere; min-width: 0; }
|
|
231
|
+
.chead { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; margin: 28px 0 10px; }
|
|
232
|
+
.chead h2 { margin: 0; }
|
|
233
|
+
.btn { display: inline-flex; align-items: center; border: 1px solid var(--line); background: var(--panel); border-radius: 8px; padding: 8px 16px; min-height: 44px; color: var(--ink); }
|
|
234
|
+
.btn:hover:not(:disabled) { background: var(--accent-soft); }
|
|
235
|
+
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 500; }
|
|
236
|
+
.btn.primary:hover:not(:disabled) { background: var(--accent-strong); border-color: var(--accent-strong); }
|
|
237
|
+
.btn.small { min-height: 36px; padding: 4px 12px; font-size: 13px; }
|
|
238
|
+
.nav { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; }
|
|
239
|
+
.keys { margin-left: auto; color: var(--muted); font-size: 12px; align-self: center; }
|
|
179
240
|
|
|
180
241
|
/* Hunks */
|
|
181
|
-
.hunk { border: 1px solid var(--
|
|
182
|
-
.hunk
|
|
183
|
-
.hunk-head {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
.
|
|
242
|
+
.hunk { margin-top: 12px; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--panel); }
|
|
243
|
+
.hunk-head { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; padding: 8px 14px; background: var(--bg); border-bottom: 1px solid var(--line); font-size: 12.5px; }
|
|
244
|
+
.hunk.closed .hunk-head { border-bottom: 0; }
|
|
245
|
+
.htoggle { display: inline-flex; align-items: center; gap: 8px; border: 0; background: transparent; padding: 6px 4px; min-height: 36px; text-align: left; border-radius: 6px; }
|
|
246
|
+
.htoggle:hover { background: var(--accent-soft); }
|
|
247
|
+
.hunk-head .file { font-family: var(--mono); font-weight: 500; overflow-wrap: anywhere; }
|
|
248
|
+
.chev { color: var(--muted); width: 1em; }
|
|
188
249
|
.hunk-head .range { font-family: var(--mono); color: var(--muted); }
|
|
189
250
|
.also { color: var(--muted); }
|
|
190
251
|
.also a { font-family: var(--mono); }
|
|
191
|
-
.scroll { overflow-x: auto; }
|
|
192
|
-
table.diff { border-collapse: collapse; width: 100%; font-family: var(--mono); font-size: 12.5px; line-height: 1.
|
|
252
|
+
.scroll { overflow-x: auto; overscroll-behavior-x: contain; }
|
|
253
|
+
table.diff { border-collapse: collapse; width: 100%; font-family: var(--mono); font-size: 12.5px; line-height: 1.6; }
|
|
193
254
|
table.diff td { padding: 0 8px; vertical-align: top; }
|
|
194
|
-
td.ln { width: 1%; min-width: 3.2em; text-align: right; color: var(--muted); user-select: none;
|
|
255
|
+
td.ln { width: 1%; min-width: 3.2em; text-align: right; color: var(--muted); user-select: none; }
|
|
195
256
|
td.mark { width: 1%; padding: 0 2px 0 4px; user-select: none; }
|
|
196
257
|
td.text { white-space: pre; }
|
|
197
258
|
tr.add { background: var(--add-bg); }
|
|
@@ -199,31 +260,90 @@ tr.add td.mark { color: var(--add-mark); }
|
|
|
199
260
|
tr.del { background: var(--del-bg); }
|
|
200
261
|
tr.del td.mark { color: var(--del-mark); }
|
|
201
262
|
tr.meta td { background: var(--meta-bg); color: var(--meta-text); }
|
|
202
|
-
tr.gap td { background: var(--
|
|
203
|
-
|
|
204
|
-
/*
|
|
205
|
-
.
|
|
206
|
-
.
|
|
207
|
-
.
|
|
208
|
-
.
|
|
209
|
-
|
|
263
|
+
tr.gap td { background: var(--bg); color: var(--muted); font-style: italic; font-family: var(--sans); padding: 2px 8px; }
|
|
264
|
+
|
|
265
|
+
/* A commit's groups in the detail pane (range pages) */
|
|
266
|
+
.filters { margin: 12px 0 0; }
|
|
267
|
+
.main .necs { padding: 10px 0; }
|
|
268
|
+
.main .rows { padding: 8px; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; }
|
|
269
|
+
.back { margin-bottom: 12px; max-width: 100%; text-align: left; }
|
|
270
|
+
|
|
271
|
+
/* Overview extras */
|
|
272
|
+
.bar { display: flex; height: 10px; border-radius: 5px; overflow: hidden; background: var(--line); max-width: 720px; }
|
|
273
|
+
.bar span { display: block; height: 100%; background: var(--c); min-width: 2px; }
|
|
274
|
+
.bar span + span { border-left: 2px solid var(--panel); }
|
|
275
|
+
.legend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 8px; font-size: 12.5px; color: var(--muted); }
|
|
276
|
+
.legend .dot { margin-right: 6px; }
|
|
277
|
+
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--c, var(--muted)); }
|
|
278
|
+
.legend + .chips { margin-top: 12px; }
|
|
279
|
+
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
280
|
+
.chips .chip b { font-family: var(--mono); font-weight: 500; font-size: 12px; }
|
|
281
|
+
.chips .chip span { color: var(--muted); }
|
|
282
|
+
.warnbox { margin: 20px 0 0; padding: 16px 18px; border: 1px dashed var(--warn); border-radius: 12px; background: var(--panel); max-width: 720px; }
|
|
283
|
+
.warnbox .t { font-weight: 600; }
|
|
284
|
+
.warnbox p { margin: 6px 0 0; }
|
|
285
|
+
.cmd { font-family: var(--mono); font-size: 12px; color: var(--muted); }
|
|
286
|
+
.unannotated { border: 1px dashed var(--del-mark); border-radius: 10px; padding: 12px 16px; background: var(--panel); }
|
|
287
|
+
.unannotated > p { margin: 0 0 4px; }
|
|
288
|
+
.list { margin: 0; padding-left: 20px; max-width: 70ch; }
|
|
289
|
+
.list li { margin-bottom: 6px; overflow-wrap: anywhere; }
|
|
210
290
|
.list .why { color: var(--muted); }
|
|
211
291
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
.
|
|
217
|
-
.
|
|
218
|
-
.
|
|
219
|
-
.
|
|
220
|
-
.
|
|
221
|
-
.
|
|
222
|
-
.
|
|
223
|
-
|
|
224
|
-
/* Reviews recorded for a commit */
|
|
225
|
-
.reviews { margin: 8px 0 12px; display: flex; flex-direction: column; gap: 4px; }
|
|
226
|
-
.review summary { cursor: pointer; }
|
|
292
|
+
/* Recorded reviews: a compact status block in the rail, the full view in the pane */
|
|
293
|
+
.reviews { display: flex; flex-direction: column; gap: 2px; }
|
|
294
|
+
.rvrow { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 44px; padding: 4px 8px; border: 0; border-radius: 8px; background: transparent; text-align: left; }
|
|
295
|
+
.rvrow:hover { background: var(--accent-soft); }
|
|
296
|
+
.rvdot.ok { color: var(--ok); }
|
|
297
|
+
.rvdot.warn { color: var(--warn); }
|
|
298
|
+
.rvdot.none { color: var(--muted); }
|
|
299
|
+
.rvdot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: currentColor; }
|
|
300
|
+
.rvstage { font-weight: 600; }
|
|
301
|
+
.rvtxt { color: var(--muted); font-size: 12.5px; flex: 1; min-width: 0; overflow-wrap: anywhere; }
|
|
302
|
+
.rvn { color: var(--muted); font-variant-numeric: tabular-nums; flex: none; }
|
|
227
303
|
.review-ok { color: var(--ok); font-weight: 600; }
|
|
228
|
-
.review-none { color: var(--muted);
|
|
229
|
-
.
|
|
304
|
+
.review-none { color: var(--muted); }
|
|
305
|
+
.rvstagepane { margin-top: 28px; }
|
|
306
|
+
.rvstagepane h2 { margin: 0 0 6px; font-size: 18px; }
|
|
307
|
+
.rvsum { margin: 0 0 12px; }
|
|
308
|
+
.rvsum .stale { background: transparent; padding: 0; font-size: inherit; color: var(--warn); font-weight: 600; }
|
|
309
|
+
.entry { margin: 0 0 20px; max-width: 75ch; }
|
|
310
|
+
.entry.old { border-top: 1px solid var(--line); padding-top: 8px; }
|
|
311
|
+
.entry summary { cursor: pointer; padding: 6px 0; min-height: 36px; }
|
|
312
|
+
.entry summary .ehead { display: inline-flex; }
|
|
313
|
+
.ehead { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin-bottom: 8px; }
|
|
314
|
+
.chip.good { color: var(--ok); border-color: var(--ok); }
|
|
315
|
+
.chip.bad { color: var(--warn); border-color: var(--warn); }
|
|
316
|
+
|
|
317
|
+
/* Rendered markdown */
|
|
318
|
+
.md { max-width: 75ch; overflow-wrap: anywhere; }
|
|
319
|
+
.md p { margin: 0 0 10px; }
|
|
320
|
+
.md h3, .md h4, .md h5, .md h6 { margin: 18px 0 6px; line-height: 1.3; }
|
|
321
|
+
.md h3 { font-size: 17px; }
|
|
322
|
+
.md h4 { font-size: 15px; }
|
|
323
|
+
.md h5, .md h6 { font-size: 14px; }
|
|
324
|
+
.md ul, .md ol { margin: 0 0 10px; padding-left: 22px; }
|
|
325
|
+
.md li { margin-bottom: 4px; }
|
|
326
|
+
.md code { background: var(--bg); border: 1px solid var(--line); border-radius: 4px; padding: 0 4px; }
|
|
327
|
+
.md pre { margin: 0 0 12px; padding: 10px 12px; background: var(--bg); border: 1px solid var(--line); border-radius: 8px; overflow-x: auto; overscroll-behavior-x: contain; }
|
|
328
|
+
.md pre code { border: 0; padding: 0; background: transparent; white-space: pre; overflow-wrap: normal; }
|
|
329
|
+
|
|
330
|
+
footer { padding: 16px; color: var(--muted); font-size: 12.5px; border-top: 1px solid var(--line); }
|
|
331
|
+
footer p { margin: 6px 0 0; }
|
|
332
|
+
|
|
333
|
+
@media (max-width: 600px) {
|
|
334
|
+
h1 { font-size: 22px; }
|
|
335
|
+
.keys { display: none; }
|
|
336
|
+
.track { width: 80px; }
|
|
337
|
+
}
|
|
338
|
+
main footer { padding-left: 0; padding-right: 0; margin-top: 32px; }
|
|
339
|
+
.linkbtn { border: 0; background: transparent; padding: 8px 0; min-height: 36px; color: var(--accent); text-decoration: underline; text-align: left; }
|
|
340
|
+
|
|
341
|
+
.railsec { margin-top: 12px; padding: 12px 16px 4px; border-top: 1px solid var(--line); font-size: 13px; }
|
|
342
|
+
.railsec .eyebrow { margin-bottom: 4px; }
|
|
343
|
+
.overviewrow .lv { margin-left: 8px; flex: none; }
|
|
344
|
+
.focuscards { display: flex; flex-direction: column; gap: 8px; max-width: 720px; }
|
|
345
|
+
.focuscard { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; width: 100%; text-align: left; padding: 12px 16px; min-height: 44px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); }
|
|
346
|
+
.focuscard:hover { background: var(--accent-soft); border-color: var(--accent); }
|
|
347
|
+
.fctitle { font-weight: 600; overflow-wrap: anywhere; }
|
|
348
|
+
.fcmeta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
|
|
349
|
+
.fcreason { color: var(--muted); overflow-wrap: anywhere; }
|
package/dist/loader.js
CHANGED
|
@@ -58,5 +58,5 @@ export function loadGloss(sha) {
|
|
|
58
58
|
warnings.push(`this gloss no longer matches the commit (${bad} glossed hunk(s) changed or gone, ${unknown} new); ` +
|
|
59
59
|
'the commit was probably amended or rebased after glossing. Re-gloss it with `git gloss add --force`.');
|
|
60
60
|
}
|
|
61
|
-
return { sha, subject: commitSubject(sha), note, groups, unannotated, stale: warnings.length > 0, warnings };
|
|
61
|
+
return { sha, subject: commitSubject(sha), note, hunks: current, groups, unannotated, stale: warnings.length > 0, warnings };
|
|
62
62
|
}
|
package/dist/merge.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { matchHunks } from './loader.js';
|
|
2
|
+
import { combineAttention } from './attention.js';
|
|
2
3
|
import { servesOf } from './schema.js';
|
|
3
4
|
import { bodyLines, changedCount, formatRanges, selections, uncovered } from './selection.js';
|
|
4
5
|
import { alsoServed } from './validate.js';
|
|
@@ -177,6 +178,9 @@ export function mergeGlosses(sources, target) {
|
|
|
177
178
|
const summaries = unique(notes.flatMap((a) => (a.summary ? [a.summary] : [])));
|
|
178
179
|
const alternatives = unique(notes.flatMap((a) => a.alternatives ?? []));
|
|
179
180
|
const risks = unique(notes.flatMap((a) => a.risks ?? []));
|
|
181
|
+
const rated = used.filter((i) => sources[i].note.annotation.attention);
|
|
182
|
+
// One unrated source makes the whole gloss unrated: its changes have no level to take.
|
|
183
|
+
const attention = rated.length < used.length ? undefined : combineAttention(rated.map((i) => sources[i].note.annotation.attention), (part, id) => newId.get(`${rated[part]}\0${id}`), groups);
|
|
180
184
|
const annotation = {
|
|
181
185
|
version: 1,
|
|
182
186
|
task: tasks.join('; '),
|
|
@@ -184,6 +188,7 @@ export function mergeGlosses(sources, target) {
|
|
|
184
188
|
groups,
|
|
185
189
|
...(alternatives.length ? { alternatives } : {}),
|
|
186
190
|
...(risks.length ? { risks } : {}),
|
|
191
|
+
...(attention ? { attention } : {}),
|
|
187
192
|
...(notes[0].author ? { author: notes[0].author } : {}),
|
|
188
193
|
};
|
|
189
194
|
const claims = kept.flatMap(({ byHunk }) => [...byHunk].map(([hunk, lines]) => ({ hunk, lines })));
|