Skip to content

Commit c83836e

Browse files
committed
Add Sprites reference protection
1 parent 6c9a0c9 commit c83836e

11 files changed

Lines changed: 506 additions & 385 deletions

assets/toolbox/sprites/js/index.js

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const elements = {
2020
paletteStatus: document.querySelector("[data-sprites-palette-status]"),
2121
previewPanel: document.querySelector("[data-sprites-preview-panel]"),
2222
refresh: document.querySelector("[data-sprites-refresh]"),
23+
referencePanel: document.querySelector("[data-sprites-reference-panel]"),
24+
referenceStatus: document.querySelector("[data-sprites-reference-status]"),
2325
replace: document.querySelector("[data-sprites-replace]"),
2426
replaceStatus: document.querySelector("[data-sprites-replace-status]"),
2527
search: document.querySelector("[data-sprites-search]"),
@@ -193,8 +195,12 @@ function usageCountFor(sprite) {
193195
return Number.isFinite(count) && count >= 0 ? String(count) : "0";
194196
}
195197

198+
function referencesFor(sprite) {
199+
return Array.isArray(sprite?.references) ? sprite.references : [];
200+
}
201+
196202
function numericUsageCount(sprite) {
197-
const count = Number(sprite?.usageCount ?? sprite?.usage_count ?? sprite?.references?.length);
203+
const count = Number(sprite?.usageCount ?? sprite?.usage_count ?? referencesFor(sprite).length);
198204
return Number.isFinite(count) && count >= 0 ? count : 0;
199205
}
200206

@@ -324,8 +330,10 @@ function renderUnavailable(message) {
324330
setText(elements.paletteSelectionStatus, "Palette/Colors selection unavailable until API-backed key records are available.");
325331
setText(elements.storageStatus, "Storage import unavailable because the Sprites API is not responding.");
326332
setText(elements.filterStatus, "Filters unavailable until Sprites records load from the API.");
333+
setText(elements.referenceStatus, "References unavailable until Sprites records load from the API.");
327334
setText(elements.replaceStatus, "Replace metadata unavailable until the Sprites API responds.");
328335
renderPreviewPanel(null);
336+
renderReferencePanel(null);
329337
setText(elements.updated, new Date().toLocaleTimeString());
330338
setHidden(elements.emptyState, false);
331339
setHidden(elements.errorState, false);
@@ -400,18 +408,66 @@ function renderPreviewPanel(sprite) {
400408
setDisabled(elements.replace, false);
401409
}
402410

411+
function renderReferencePanel(sprite) {
412+
if (!elements.referencePanel) {
413+
return;
414+
}
415+
elements.referencePanel.replaceChildren();
416+
if (!sprite) {
417+
setText(elements.referenceStatus, "Select a sprite to review API-provided usage references.");
418+
elements.referencePanel.append(createParagraph("No sprite selected for reference review.", "status"));
419+
return;
420+
}
421+
const references = referencesFor(sprite);
422+
const usageCount = numericUsageCount(sprite);
423+
setText(
424+
elements.referenceStatus,
425+
usageCount > 0
426+
? `${usageCount} usage reference${usageCount === 1 ? "" : "s"} reported by the Sprites API.`
427+
: "No usage references reported by the Sprites API."
428+
);
429+
if (!references.length) {
430+
elements.referencePanel.append(createParagraph("No references reported yet. Future Objects and Worlds references will appear here when the API supplies them.", "status"));
431+
return;
432+
}
433+
const wrapper = document.createElement("div");
434+
wrapper.className = "table-wrapper";
435+
const table = document.createElement("table");
436+
table.className = "data-table";
437+
table.setAttribute("aria-label", "Sprite usage references");
438+
const head = document.createElement("thead");
439+
const headRow = document.createElement("tr");
440+
headRow.append(createCell("Source Type"), createCell("Source Key"), createCell("Label"));
441+
head.append(headRow);
442+
const body = document.createElement("tbody");
443+
references.forEach((reference) => {
444+
const row = document.createElement("tr");
445+
row.append(
446+
createCell(normalizeText(reference.sourceType)),
447+
createCell(normalizeText(reference.sourceKey)),
448+
createCell(normalizeText(reference.label, "No label"))
449+
);
450+
body.append(row);
451+
});
452+
table.append(head, body);
453+
wrapper.append(table);
454+
elements.referencePanel.append(wrapper);
455+
}
456+
403457
function selectSprite(sprite) {
404458
selectedSpriteKey = sprite?.key || "";
405459
if (!sprite) {
406460
setText(elements.metadata, "Select a sprite row to review its metadata.");
407461
renderPreviewPanel(null);
462+
renderReferencePanel(null);
408463
return;
409464
}
410465
const key = normalizeText(sprite?.key, "Unavailable");
411466
const mimeType = normalizeText(sprite?.mimeType ?? sprite?.mime_type, "Unavailable");
412467
const sizeBytes = normalizeText(sprite?.sizeBytes ?? sprite?.size_bytes, "Unavailable");
413468
setText(elements.metadata, `${normalizeText(sprite?.name)} (${key}) | ${mimeType} | ${formatDimensions(sprite)} | ${sizeBytes} bytes`);
414469
renderPreviewPanel(sprite);
470+
renderReferencePanel(sprite);
415471
}
416472

417473
function renderRows(sprites, emptyMessage = "No Sprites records returned by the API.") {
@@ -491,9 +547,10 @@ function createSpriteRow(sprite) {
491547
actions.append(
492548
createButton("Edit", "spritesEdit", sprite?.key || "", { label: `Edit ${name}` }),
493549
createButton("Duplicate", "spritesDuplicateRow", sprite?.key || "", { label: `Duplicate ${name}` }),
494-
createButton(archived ? "Archived" : "Archive", "spritesArchive", sprite?.key || "", {
550+
createButton(archived ? "Archived" : usageCount > 0 ? "Archive Safely" : "Archive", "spritesArchive", sprite?.key || "", {
495551
disabled: archived,
496-
label: archived ? `${name} is already archived` : `Archive ${name}`,
552+
label: archived ? `${name} is already archived` : usageCount > 0 ? `Archive safely ${name}` : `Archive ${name}`,
553+
title: usageCount > 0 ? "Sprite is referenced. Archive is the safe action; destructive delete is blocked." : "",
497554
}),
498555
createButton(usageCount > 0 ? "Delete Blocked" : "Delete", "spritesDelete", sprite?.key || "", {
499556
disabled: usageCount > 0,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# PR_26177_CHARLIE_015 Branch Validation
2+
3+
Status: PASS
4+
5+
## Checks
6+
7+
- PASS: PR015 was created as a stacked branch from `PR_26177_CHARLIE_014-sprites-tags-categories-search`.
8+
- PASS: Stacking is required because reference protection builds on the PR014 Sprites table/actions.
9+
- PASS: Current work branch is `PR_26177_CHARLIE_015-sprites-reference-protection`.
10+
- PASS: Branch contains only the Sprites reference protection PR scope relative to PR014.
11+
- PASS: No merge was performed.
12+
- PASS: No `start_of_day` path is changed.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# PR_26177_CHARLIE_015 Manual Validation Notes
2+
3+
Status: PASS
4+
5+
## Manual Review
6+
7+
- Verified API-provided references render in the inspector.
8+
- Verified empty reference state appears when the API returns no references.
9+
- Verified referenced sprites show usage counts.
10+
- Verified delete is disabled for referenced sprites.
11+
- Verified archive remains available and clearly labeled as the safe action for referenced sprites.
12+
- Verified no fake Objects or Worlds references were added.
13+
- Verified future reference ownership remains API/database-backed.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# PR_26177_CHARLIE_015 Requirements Checklist
2+
3+
Status: PASS
4+
5+
- PASS: Added reference viewer for where a sprite is used.
6+
- PASS: Reference viewer consumes API-provided references only.
7+
- PASS: Did not invent fake references.
8+
- PASS: Prevents destructive delete when a sprite is referenced.
9+
- PASS: Allows safe archive action with clear labeling for referenced sprites.
10+
- PASS: Shows usage counts in the table.
11+
- PASS: Shows usage reference details in the inspector.
12+
- PASS: Establishes future Objects/Worlds reference contract through API/database references.
13+
- PASS: Shows visible empty state when no real references are available.
14+
- PASS: Did not add browser storage product-data source of truth.
15+
- PASS: Did not introduce MEM DB, local-mem, fake-login, or silent fallback.
16+
- PASS: Targeted Playwright coverage passed.
17+
- PASS: Required report artifacts were created.
18+
- PASS: Repo-structured ZIP artifact was created under `tmp/`.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# PR_26177_CHARLIE_015 Validation Lane
2+
3+
Status: PASS
4+
5+
## Commands
6+
7+
```powershell
8+
rg -n "<style|style=|onclick=|onchange=|oninput=|onsubmit=|<script>" toolbox/sprites/index.html assets/toolbox/sprites/js/index.js tests/playwright/tools/SpritesToolShell.spec.mjs
9+
```
10+
11+
Result: PASS, no matches.
12+
13+
```powershell
14+
rg -n "localStorage|sessionStorage|indexedDB|imageDataUrl|MEM DB|local-mem|fake-login|silent fallback" toolbox/sprites/index.html assets/toolbox/sprites/js/index.js tests/playwright/tools/SpritesToolShell.spec.mjs
15+
```
16+
17+
Result: PASS, no matches.
18+
19+
```powershell
20+
git diff --check
21+
```
22+
23+
Result: PASS. Git reported only repository line-ending warnings for changed HTML/test files.
24+
25+
```powershell
26+
node ./node_modules/@playwright/test/cli.js test tests/playwright/tools/SpritesToolShell.spec.mjs --project=playwright --workers=1 --reporter=list
27+
```
28+
29+
Result: PASS, 10 passed.
30+
31+
## Playwright Coverage
32+
33+
Targeted Playwright coverage updated `docs_build/dev/reports/playwright_v8_coverage_report.txt` for the Sprites browser module.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# PR_26177_CHARLIE_015-sprites-reference-protection
2+
3+
Team: Charlie
4+
5+
Status: PASS
6+
7+
## Scope
8+
9+
Added API-provided reference visibility and strengthened destructive delete protection for Sprites.
10+
11+
## Changed Files
12+
13+
- `toolbox/sprites/index.html`
14+
- `assets/toolbox/sprites/js/index.js`
15+
- `tests/playwright/tools/SpritesToolShell.spec.mjs`
16+
- `docs_build/dev/reports/playwright_v8_coverage_report.txt`
17+
- `docs_build/dev/reports/codex_review.diff`
18+
- `docs_build/dev/reports/codex_changed_files.txt`
19+
- `docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection.md`
20+
- `docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-branch-validation.md`
21+
- `docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-requirements-checklist.md`
22+
- `docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-validation-lane.md`
23+
- `docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-manual-validation-notes.md`
24+
25+
## Implementation Notes
26+
27+
- Added reference viewer for API-provided usage references.
28+
- Displays source type, source key, and label for each usage reference.
29+
- Shows empty reference state when the API reports no references.
30+
- Keeps usage count visible in table and details.
31+
- Destructive delete remains disabled when usage count is greater than zero.
32+
- Referenced sprites show `Archive Safely` to make the safe action explicit.
33+
- No fake Objects or Worlds references were added.
34+
35+
## Reference Contract
36+
37+
The UI consumes the existing Sprites API `references` array. Future Objects/Worlds integrations should supply real usage rows through the API/database reference contract rather than browser-local inference.
38+
39+
## Validation
40+
41+
- PASS: `git diff --check`
42+
- PASS: inline CSS/script/handler scan for Sprites files found no matches.
43+
- PASS: browser storage and forbidden local data pattern scan found no matches.
44+
- PASS: no `start_of_day` files changed.
45+
- PASS: `node ./node_modules/@playwright/test/cli.js test tests/playwright/tools/SpritesToolShell.spec.mjs --project=playwright --workers=1 --reporter=list`
46+
47+
## ZIP Artifact
48+
49+
- `tmp/PR_26177_CHARLIE_015-sprites-reference-protection_delta.zip`

docs_build/dev/reports/codex_changed_files.txt

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@ M assets/toolbox/sprites/js/index.js
33
M docs_build/dev/reports/playwright_v8_coverage_report.txt
44
M tests/playwright/tools/SpritesToolShell.spec.mjs
55
M toolbox/sprites/index.html
6-
?? docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search-branch-validation.md
7-
?? docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search-manual-validation-notes.md
8-
?? docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search-requirements-checklist.md
9-
?? docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search-validation-lane.md
10-
?? docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search.md
6+
?? docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-branch-validation.md
7+
?? docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-manual-validation-notes.md
8+
?? docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-requirements-checklist.md
9+
?? docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-validation-lane.md
10+
?? docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection.md
1111

1212
# git ls-files --others --exclude-standard
13-
docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search-branch-validation.md
14-
docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search-manual-validation-notes.md
15-
docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search-requirements-checklist.md
16-
docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search-validation-lane.md
17-
docs_build/dev/reports/PR_26177_CHARLIE_014-sprites-tags-categories-search.md
13+
docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-branch-validation.md
14+
docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-manual-validation-notes.md
15+
docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-requirements-checklist.md
16+
docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection-validation-lane.md
17+
docs_build/dev/reports/PR_26177_CHARLIE_015-sprites-reference-protection.md
1818

1919
# git diff --stat
20-
assets/toolbox/sprites/js/index.js | 151 ++++++++++++++++++++-
21-
.../dev/reports/playwright_v8_coverage_report.txt | 4 +-
22-
tests/playwright/tools/SpritesToolShell.spec.mjs | 71 ++++++++++
23-
toolbox/sprites/index.html | 15 ++
24-
4 files changed, 232 insertions(+), 9 deletions(-)
20+
assets/toolbox/sprites/js/index.js | 63 +++++++++++++++++++-
21+
.../dev/reports/playwright_v8_coverage_report.txt | 2 +-
22+
tests/playwright/tools/SpritesToolShell.spec.mjs | 69 +++++++++++++++++++++-
23+
toolbox/sprites/index.html | 9 +++
24+
4 files changed, 138 insertions(+), 5 deletions(-)

0 commit comments

Comments
 (0)