claude-dev-env 8.56.2 → 8.57.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.
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
| Readable text | Text is 16px or larger and uses the full text color. Gray stays on lines and borders only. Text holds a 7:1 contrast ratio against its background. |
|
|
17
17
|
| Touch targets | Buttons and chips are 40px or taller, with a visible focus ring. |
|
|
18
18
|
| Copy fallback | The copy button uses the clipboard API and falls back to a hidden text area, then says when the copy is blocked. |
|
|
19
|
+
| Review rounds | Parts changed since the viewer's last look carry a thin bar on their left edge, and changed cards a "Changed" label. [review-rounds.md](review-rounds.md) holds the data fields and behavior. |
|
|
19
20
|
|
|
20
21
|
## House rules for a page built from it
|
|
21
22
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Marking what changed between review rounds
|
|
2
|
+
|
|
3
|
+
A page that a reviewer reads more than once shows what changed since their last look. The template marks each changed card and each changed part with a thin bar on its left edge, gives each changed card a small "Changed" label, and gives each changed board chip a dot in the same color. There is no diff and no list of changes.
|
|
4
|
+
|
|
5
|
+
## Data
|
|
6
|
+
|
|
7
|
+
| Field | Where | Meaning |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `round` | top of the data block | The review round this revision answers. Add one each time the page is republished after feedback. |
|
|
10
|
+
| `changed_round` | an item | The round in which this item last changed. |
|
|
11
|
+
| `changed_parts` | an item, optional | The parts that changed: `title`, `next`, `summary`, `steps`, `links`. With no list, only the card is marked. |
|
|
12
|
+
|
|
13
|
+
When a revision answers feedback, set `round` to the new number and stamp each item it changed with that number and the parts it touched. Leave older stamps in place; they stop showing once the reviewer has seen their round.
|
|
14
|
+
|
|
15
|
+
## Behavior
|
|
16
|
+
|
|
17
|
+
- The page keeps the last round each viewer saw in `localStorage`. Parts stamped after that round are marked.
|
|
18
|
+
- A first visit, or a browser that blocks storage, marks the parts stamped in the current round.
|
|
19
|
+
- The marks hold through a reload in the same tab, through `sessionStorage`, and clear on the next visit.
|
|
20
|
+
- A changed summary opens by itself.
|
|
21
|
+
|
|
22
|
+
## Other pages
|
|
23
|
+
|
|
24
|
+
Any element with `data-changed-round="N"` takes the bar. An element that holds a `data-change-slot` child gets the "Changed" label in that child; other elements get a screen reader label.
|
|
25
|
+
|
|
26
|
+
The `--change` and `--change-ink` tokens hold the mark colors in light and dark. Keep the label text at 7:1 or better against `--change`, and the bar at 3:1 or better against `--surface` and `--bg`.
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
--accent: #2245a2; --accent-soft: #e6ecfb; --accent-ink: #ffffff;
|
|
8
8
|
--ok: #165737; --ok-soft: #e3f3ea; --bad: #8b2621; --bad-soft: #fbe5e3; --warn: #6f4100; --warn-soft: #fbf0dc;
|
|
9
9
|
--violet: #553b95; --violet-soft: #efe9fa;
|
|
10
|
+
--change: #8a1f6b; --change-ink: #ffffff;
|
|
10
11
|
--font-body: "Manrope", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
11
12
|
}
|
|
12
13
|
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) {
|
|
@@ -14,12 +15,14 @@
|
|
|
14
15
|
--accent: #a8bfff; --accent-soft: #1f2a45; --accent-ink: #0d1424;
|
|
15
16
|
--ok: #6fd3a0; --ok-soft: #173327; --bad: #ffa8a0; --bad-soft: #3a1c1a; --warn: #f0b860; --warn-soft: #362a14;
|
|
16
17
|
--violet: #c5b1f2; --violet-soft: #251f38;
|
|
18
|
+
--change: #f5a3dc; --change-ink: #1f0a19;
|
|
17
19
|
color-scheme: dark } }
|
|
18
20
|
:root[data-theme="dark"] {
|
|
19
21
|
--bg: #12151b; --surface: #1a1f28; --fg: #e6e9ef; --edge: #6d7891; --line: #2b3240;
|
|
20
22
|
--accent: #a8bfff; --accent-soft: #1f2a45; --accent-ink: #0d1424;
|
|
21
23
|
--ok: #6fd3a0; --ok-soft: #173327; --bad: #ffa8a0; --bad-soft: #3a1c1a; --warn: #f0b860; --warn-soft: #362a14;
|
|
22
24
|
--violet: #c5b1f2; --violet-soft: #251f38;
|
|
25
|
+
--change: #f5a3dc; --change-ink: #1f0a19;
|
|
23
26
|
color-scheme: dark }
|
|
24
27
|
* { box-sizing: border-box }
|
|
25
28
|
body { background: var(--bg); color: var(--fg); font-family: var(--font-body); font-size: 17px; line-height: 1.4; padding-inline: 16px; padding-block: 24px 48px; margin: 0 }
|
|
@@ -95,6 +98,15 @@ details.what[open] summary::before { content: "▾" }
|
|
|
95
98
|
.steps .sh { font-weight: 800 }
|
|
96
99
|
.step { display: flex; gap: 10px; align-items: flex-start }
|
|
97
100
|
.step .num { flex: none; width: 30px; height: 30px; border-radius: 50%; background: var(--warn); color: var(--surface); display: grid; place-items: center; font-weight: 800 }
|
|
101
|
+
.lchip.changed::before { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--change); flex: none }
|
|
102
|
+
.changed { box-shadow: inset 4px 0 0 var(--change); padding-left: 12px; border-radius: 4px }
|
|
103
|
+
.card.changed { box-shadow: inset 6px 0 0 var(--change); padding-left: 0; border-radius: 16px }
|
|
104
|
+
.card.changed .band, .card.changed > .body { padding-left: 18px }
|
|
105
|
+
.card.changed .body .changed { padding-left: 12px }
|
|
106
|
+
.next.changed { border-radius: 12px }
|
|
107
|
+
.lchip.changed { box-shadow: none; border-radius: 999px; padding-left: 10px; border-color: var(--change); display: inline-flex; align-items: center; gap: 6px }
|
|
108
|
+
.pill-changed { display: inline-flex; align-items: center; min-height: 28px; padding: 0 10px; border-radius: 999px; background: var(--change); color: var(--change-ink); font-size: 16px; font-weight: 800 }
|
|
109
|
+
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap }
|
|
98
110
|
.empty { background: var(--surface); border: 2px dashed var(--edge); border-radius: 14px; padding: 20px; font-weight: 600 }
|
|
99
111
|
@media (hover: hover) { .lchip:hover, .fchip:hover, .tab:hover, .copy:hover { border-color: var(--accent) } }
|
|
100
112
|
</style>
|
|
@@ -117,6 +129,7 @@ details.what[open] summary::before { content: "▾" }
|
|
|
117
129
|
<script type="application/json" id="tracker-data">
|
|
118
130
|
{
|
|
119
131
|
"updated": "2026-01-15T17:30:00Z",
|
|
132
|
+
"round": 3,
|
|
120
133
|
"owners": [
|
|
121
134
|
{
|
|
122
135
|
"key": "web",
|
|
@@ -147,6 +160,8 @@ details.what[open] summary::before { content: "▾" }
|
|
|
147
160
|
"needs_you": true,
|
|
148
161
|
"card": true,
|
|
149
162
|
"next": "choose between the two dates in the linked plan.",
|
|
163
|
+
"changed_round": 3,
|
|
164
|
+
"changed_parts": ["next"],
|
|
150
165
|
"summary": [
|
|
151
166
|
"One release date everyone plans around",
|
|
152
167
|
"The launch checklist dated backward from it",
|
|
@@ -167,6 +182,8 @@ details.what[open] summary::before { content: "▾" }
|
|
|
167
182
|
"status": "blocked",
|
|
168
183
|
"card": true,
|
|
169
184
|
"next": "wait for the staging database to come back online.",
|
|
185
|
+
"changed_round": 2,
|
|
186
|
+
"changed_parts": ["next"],
|
|
170
187
|
"summary": [
|
|
171
188
|
"The login test passes ten runs in a row",
|
|
172
189
|
"The root cause written in the pull request"
|
|
@@ -186,6 +203,8 @@ details.what[open] summary::before { content: "▾" }
|
|
|
186
203
|
"status": "doing",
|
|
187
204
|
"card": true,
|
|
188
205
|
"next": "finish the index change and rerun the timing check.",
|
|
206
|
+
"changed_round": 3,
|
|
207
|
+
"changed_parts": ["summary"],
|
|
189
208
|
"summary": [
|
|
190
209
|
"Search answers in under one second",
|
|
191
210
|
"A timing check that guards it",
|
|
@@ -288,6 +307,8 @@ const STAGE_BY_KEY = Object.fromEntries(STAGES.map(s => [s.key, s]));
|
|
|
288
307
|
const TRACK = ["Card", "Builder", "Pull request", "Merged"];
|
|
289
308
|
const state = { tab: "open", owner: "all", query: "" };
|
|
290
309
|
try { const saved = JSON.parse(localStorage.getItem("work-tracker-view") || "{}"); if (saved.tab) state.tab = saved.tab; if (saved.owner) state.owner = saved.owner; } catch (e) {}
|
|
310
|
+
const REVIEW_ROUND = Number(TRACKER.round) || 0;
|
|
311
|
+
const CHANGE_BASELINE = changeBaseline();
|
|
291
312
|
const ownerByKey = Object.fromEntries(TRACKER.owners.map(o => [o.key, o]));
|
|
292
313
|
const isDone = it => it.status === "done";
|
|
293
314
|
function stageOf(it) {
|
|
@@ -300,6 +321,34 @@ function stageOf(it) {
|
|
|
300
321
|
const slug = it => "item-" + it.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
301
322
|
const fmt = iso => { if (!iso) return ""; try { return new Date(iso).toLocaleString(undefined, { month: "long", day: "numeric", hour: "numeric", minute: "2-digit" }); } catch (e) { return iso; } };
|
|
302
323
|
const el = (tag, cls, text) => { const n = document.createElement(tag); if (cls) n.className = cls; if (text != null) n.textContent = text; return n; };
|
|
324
|
+
function changeBaseline() {
|
|
325
|
+
try {
|
|
326
|
+
const held = JSON.parse(sessionStorage.getItem("work-tracker-baseline") || "null");
|
|
327
|
+
if (held && held.round === REVIEW_ROUND) return held.baseline;
|
|
328
|
+
} catch (e) {}
|
|
329
|
+
let baseline = REVIEW_ROUND - 1;
|
|
330
|
+
try {
|
|
331
|
+
const seen = localStorage.getItem("work-tracker-seen-round");
|
|
332
|
+
if (seen !== null && Number.isFinite(Number(seen))) baseline = Math.min(Number(seen), REVIEW_ROUND);
|
|
333
|
+
} catch (e) {}
|
|
334
|
+
try { sessionStorage.setItem("work-tracker-baseline", JSON.stringify({ round: REVIEW_ROUND, baseline })); } catch (e) {}
|
|
335
|
+
try { localStorage.setItem("work-tracker-seen-round", String(REVIEW_ROUND)); } catch (e) {}
|
|
336
|
+
return baseline;
|
|
337
|
+
}
|
|
338
|
+
function stampChange(node, it, part) {
|
|
339
|
+
if (it.changed_round && (!part || [].concat(it.changed_parts || []).includes(part))) node.dataset.changedRound = it.changed_round;
|
|
340
|
+
return node;
|
|
341
|
+
}
|
|
342
|
+
function markChanges() {
|
|
343
|
+
for (const node of document.querySelectorAll("[data-changed-round]")) {
|
|
344
|
+
if (Number(node.dataset.changedRound) <= CHANGE_BASELINE || node.classList.contains("changed")) continue;
|
|
345
|
+
node.classList.add("changed");
|
|
346
|
+
const slot = node.querySelector("[data-change-slot]");
|
|
347
|
+
if (slot) { slot.prepend(el("span", "pill-changed", "Changed")); continue; }
|
|
348
|
+
const label = node.querySelector(":scope > summary") || node;
|
|
349
|
+
label.prepend(el("span", "sr", "Changed. "));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
303
352
|
function save() { try { localStorage.setItem("work-tracker-view", JSON.stringify({ tab: state.tab, owner: state.owner })); } catch (e) {} }
|
|
304
353
|
function inView(it) {
|
|
305
354
|
if ((state.tab === "done") !== isDone(it)) return false;
|
|
@@ -329,7 +378,7 @@ function renderBoard(items) {
|
|
|
329
378
|
if (mine.length) {
|
|
330
379
|
const chips = el("div", "lchips");
|
|
331
380
|
for (const it of mine) {
|
|
332
|
-
const b = el("button", "lchip", it.title);
|
|
381
|
+
const b = stampChange(el("button", "lchip", it.title), it);
|
|
333
382
|
b.setAttribute("aria-label", "Go to " + it.title);
|
|
334
383
|
b.onclick = () => jumpTo(it);
|
|
335
384
|
chips.append(b);
|
|
@@ -389,25 +438,27 @@ function copyButton(it) {
|
|
|
389
438
|
}
|
|
390
439
|
function itemCard(it) {
|
|
391
440
|
const key = stageOf(it), s = STAGE_BY_KEY[key], owner = ownerByKey[it.owner] || { name: it.owner };
|
|
392
|
-
const card = el("article", "card " + key);
|
|
441
|
+
const card = stampChange(el("article", "card " + key), it);
|
|
393
442
|
card.id = slug(it);
|
|
394
443
|
const band = el("div", "band " + key);
|
|
444
|
+
band.dataset.changeSlot = "";
|
|
395
445
|
band.append(el("span", null, s.icon), el("span", null, s.label));
|
|
396
446
|
const proj = el("span", "proj");
|
|
397
447
|
proj.append(el("span", null, (owner.icon || "📁")), el("span", null, owner.name));
|
|
398
448
|
band.append(proj);
|
|
399
449
|
card.append(band);
|
|
400
450
|
const body = el("div", "body");
|
|
401
|
-
body.append(el("h3", "title", it.title));
|
|
451
|
+
body.append(stampChange(el("h3", "title", it.title), it, "title"));
|
|
402
452
|
body.append(trackFor(it));
|
|
403
453
|
if (it.next) {
|
|
404
|
-
const next = el("div", "next");
|
|
454
|
+
const next = stampChange(el("div", "next"), it, "next");
|
|
405
455
|
next.append(el("span", "ic", isDone(it) ? "✅" : "👉"), el("span", null, it.next.charAt(0).toUpperCase() + it.next.slice(1)));
|
|
406
456
|
body.append(next);
|
|
407
457
|
}
|
|
408
458
|
const points = [].concat(it.summary || []);
|
|
409
459
|
if (points.length) {
|
|
410
|
-
const d = el("details", "what");
|
|
460
|
+
const d = stampChange(el("details", "what"), it, "summary");
|
|
461
|
+
d.open = d.dataset.changedRound > CHANGE_BASELINE;
|
|
411
462
|
d.append(el("summary", null, "What it delivers"));
|
|
412
463
|
const ul = el("ul", "points");
|
|
413
464
|
for (const p of points) ul.append(el("li", null, p));
|
|
@@ -415,12 +466,12 @@ function itemCard(it) {
|
|
|
415
466
|
body.append(d);
|
|
416
467
|
}
|
|
417
468
|
if (it.proposal && !isDone(it) && it.owner_steps && it.owner_steps.length) {
|
|
418
|
-
const st = el("div", "steps");
|
|
469
|
+
const st = stampChange(el("div", "steps"), it, "steps");
|
|
419
470
|
st.append(el("div", "sh", "Your steps to create " + it.proposal.name));
|
|
420
471
|
it.owner_steps.forEach((text, i) => { const row = el("div", "step"); row.append(el("span", "num", String(i + 1)), el("span", null, text)); st.append(row); });
|
|
421
472
|
body.append(st);
|
|
422
473
|
}
|
|
423
|
-
const actions = el("div", "actions");
|
|
474
|
+
const actions = stampChange(el("div", "actions"), it, "links");
|
|
424
475
|
for (const l of it.links || []) {
|
|
425
476
|
const a = el("a", "link");
|
|
426
477
|
a.append(el("span", null, "🔗"), el("span", null, l.label));
|
|
@@ -458,6 +509,7 @@ function render() {
|
|
|
458
509
|
list.append(h);
|
|
459
510
|
mine.forEach(i => list.append(itemCard(i)));
|
|
460
511
|
}
|
|
512
|
+
markChanges();
|
|
461
513
|
}
|
|
462
514
|
document.querySelectorAll(".tab").forEach(t => t.onclick = () => { state.tab = t.dataset.tab; save(); render(); });
|
|
463
515
|
document.getElementById("search").oninput = e => { state.query = e.target.value; render(); };
|