Skip to content

Commit d4252b7

Browse files
author
DavidQ
committed
Run npm run test:workspace-v2.
Expected pass: Workspace V2 lifecycle, palette baseline, payload validation, import/export, and active tool state behavior remain unchanged. Expected fail: Any Workspace V2 regression fails the PR. Playwright impact: No intentional Workspace V2 behavior change. This is Palette Manager V2 baseline hardening.
1 parent a0e799f commit d4252b7

17 files changed

Lines changed: 4553 additions & 1222 deletions

File tree

docs/dev/codex_commands.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,28 @@
1-
# Codex Commands - PR_26124_057-palette-manager-source-pin-all
1+
# Codex Commands - PR_26124_058-palette-manager-baseline-restore-and-hardening
22

33
## Workflow
44
- Read `docs/dev/PROJECT_INSTRUCTIONS.md`.
55
- Used `.codex/skills/repo-build/SKILL.md`.
6-
- Created `docs/pr/PR_26124_057-palette-manager-source-pin-all/PLAN_PR.md`.
7-
- Created `docs/pr/PR_26124_057-palette-manager-source-pin-all/BUILD_PR.md`.
6+
- Created `docs/pr/PR_26124_058-palette-manager-baseline-restore-and-hardening/PLAN_PR.md`.
7+
- Created `docs/pr/PR_26124_058-palette-manager-baseline-restore-and-hardening/BUILD_PR.md`.
8+
- Created `docs/dev/reports/PR_26124_058-palette-manager-restore-point/` before runtime edits.
89
- Updated Palette Manager V2 only.
9-
- Created `docs/pr/PR_26124_057-palette-manager-source-pin-all/APPLY_PR.md`.
10+
- Created `docs/pr/PR_26124_058-palette-manager-baseline-restore-and-hardening/APPLY_PR.md`.
1011

1112
## Validation Commands
1213
- `node --check tools/palette-manager-v2/modules/PaletteManagerApp.js`
1314
- `node --check tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js`
14-
- `node --check tools/palette-manager-v2/controls/UserPaletteControl.js`
15-
- Targeted served-browser Palette Manager V2 source sort and `Pin All` validation using Playwright from Node.
16-
- Targeted served-browser `Pin All` source dropdown placement and size validation using Playwright from Node.
15+
- Targeted served-browser Palette Manager V2 hardening validation using Playwright from Node.
1716
- `git diff --check`
1817
- `npm run test:workspace-v2`
1918
- `git diff --cached --check`
2019
- `npm run codex:review-artifacts`
21-
- Python `zipfile` packaging for `tmp/PR_26124_057-palette-manager-source-pin-all_delta_2.zip`
20+
- Python `zipfile` packaging for `tmp/PR_26124_058-palette-manager-baseline-restore-and-hardening_delta.zip`
2221

2322
## Validation Outcome
2423
- JavaScript syntax checks: PASS.
25-
- Targeted served-browser Palette Manager V2 behavior check: PASS.
26-
- Targeted served-browser `Pin All` placement and size check: PASS.
27-
- `git diff --check`: PASS with Git LF-to-CRLF warning for `tools/palette-manager-v2/index.html`.
24+
- Targeted served-browser Palette Manager V2 hardening check: PASS.
25+
- `git diff --check`: PASS with Git LF-to-CRLF warnings for changed Palette Manager files.
2826
- `git diff --cached --check`: PASS.
2927
- `npm run test:workspace-v2`: FAILED because the script is missing from `package.json`.
3028
- Review artifacts: generated.

docs/dev/commit_comment.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Add Palette Manager source Pin All action - PR_26124_057-palette-manager-source-pin-all
1+
Harden Palette Manager baseline with restore point - PR_26124_058-palette-manager-baseline-restore-and-hardening
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# PR_26124_058 Palette Manager Restore Point
2+
3+
This folder is the pre-hardening rollback snapshot for Palette Manager V2.
4+
5+
It contains copies of the current runtime files that PR_26124_058 changes after this restore point was created.
6+
Use these files only as a manual rollback reference for the pre-hardening Palette Manager V2 state.
7+
8+
No start_of_day files were created.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { SwatchRow } from "../modules/SwatchRow.js";
2+
3+
export class SourcePaletteBrowserControl {
4+
constructor({ documentRef, refs, app }) {
5+
this.document = documentRef;
6+
this.refs = refs;
7+
this.app = app;
8+
}
9+
10+
bind() {
11+
this.renderSourceOptions();
12+
this.renderSortOptions();
13+
this.renderSizeOptions();
14+
this.refs.sourcePaletteSelect.addEventListener("change", () => {
15+
this.app.setSourcePaletteId(this.refs.sourcePaletteSelect.value);
16+
});
17+
this.refs.sourceSearchInput.addEventListener("input", () => {
18+
this.app.setSourceSearch(this.refs.sourceSearchInput.value);
19+
});
20+
this.refs.pinAllSourceButton.addEventListener("click", () => {
21+
this.app.pinVisibleSourceSwatches();
22+
});
23+
}
24+
25+
render() {
26+
this.refs.sourcePaletteSelect.value = this.app.getCurrentSourcePaletteId();
27+
this.refs.sourceSearchInput.value = this.app.getSourceSearch();
28+
this.refs.sourceSwatchList.dataset.swatchSize = this.app.getSourceSwatchSize();
29+
this.renderActiveSortButton();
30+
this.renderActiveSizeButton();
31+
this.refs.sourceSwatchList.replaceChildren();
32+
33+
const visibleSwatches = this.app.getVisibleSourceSwatches();
34+
if (visibleSwatches.length === 0) {
35+
const empty = this.document.createElement("p");
36+
empty.className = "palette-manager-v2__meta";
37+
empty.textContent = "No source swatches match the search.";
38+
this.refs.sourceSwatchList.appendChild(empty);
39+
return;
40+
}
41+
42+
visibleSwatches.forEach((swatch) => {
43+
const userIndex = this.app.findUserSwatchIndex(swatch);
44+
this.refs.sourceSwatchList.appendChild(SwatchRow.createSourceTile(this.document, swatch, {
45+
pinned: userIndex >= 0,
46+
onSelect: () => this.app.browseSourceSwatch(swatch),
47+
onTack: () => {
48+
if (userIndex >= 0) {
49+
this.app.removeUserSwatch(userIndex);
50+
return;
51+
}
52+
this.app.pinSourceSwatch(swatch, this.app.getCurrentSourcePaletteId());
53+
}
54+
}));
55+
});
56+
}
57+
58+
renderSourceOptions() {
59+
this.refs.sourcePaletteSelect.replaceChildren();
60+
this.app.getSourcePaletteIds().forEach((sourceId) => {
61+
const option = this.document.createElement("option");
62+
option.value = sourceId;
63+
option.textContent = this.app.getSourcePaletteLabel(sourceId);
64+
this.refs.sourcePaletteSelect.appendChild(option);
65+
});
66+
}
67+
68+
renderSortOptions() {
69+
this.refs.sourcePaletteSortControls.replaceChildren();
70+
this.app.getSourceSortOptions().forEach((option) => {
71+
const button = this.document.createElement("button");
72+
button.type = "button";
73+
button.className = "palette-manager-v2__sort-button";
74+
button.dataset.sortKey = option.value;
75+
button.dataset.sortLabel = option.label;
76+
button.setAttribute("role", "radio");
77+
button.textContent = option.label;
78+
button.addEventListener("click", () => {
79+
this.app.setSourceSortKey(option.value);
80+
});
81+
this.refs.sourcePaletteSortControls.appendChild(button);
82+
});
83+
this.renderActiveSortButton();
84+
}
85+
86+
renderSizeOptions() {
87+
this.refs.sourceSwatchSizeControls.replaceChildren();
88+
this.app.getSwatchSizeOptions().forEach((size) => {
89+
const button = this.document.createElement("button");
90+
button.type = "button";
91+
button.className = "palette-manager-v2__sort-button palette-manager-v2__size-button";
92+
button.dataset.swatchSize = size.value;
93+
button.setAttribute("role", "radio");
94+
button.textContent = size.label;
95+
button.addEventListener("click", () => {
96+
this.app.setSourceSwatchSize(size.value);
97+
});
98+
this.refs.sourceSwatchSizeControls.appendChild(button);
99+
});
100+
this.renderActiveSizeButton();
101+
}
102+
103+
renderActiveSortButton() {
104+
const activeKey = this.app.getSourceSortKey();
105+
const directionIndicator = this.app.getSourceSortDirection() === "descending" ? "▼" : "▲";
106+
this.refs.sourcePaletteSortControls.querySelectorAll("[data-sort-key]").forEach((button) => {
107+
const isActive = button.dataset.sortKey === activeKey;
108+
button.classList.toggle("is-active", isActive);
109+
button.setAttribute("aria-checked", String(isActive));
110+
button.textContent = isActive ? `${button.dataset.sortLabel} ${directionIndicator}` : button.dataset.sortLabel;
111+
});
112+
}
113+
114+
renderActiveSizeButton() {
115+
const activeSize = this.app.getSourceSwatchSize();
116+
this.refs.sourceSwatchSizeControls.querySelectorAll("[data-swatch-size]").forEach((button) => {
117+
const isActive = button.dataset.swatchSize === activeSize;
118+
button.classList.toggle("is-active", isActive);
119+
button.setAttribute("aria-checked", String(isActive));
120+
});
121+
}
122+
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
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.0" />
6+
<title>Palette Manager V2</title>
7+
<link rel="stylesheet" href="../../src/engine/theme/main.css" />
8+
<link rel="stylesheet" href="../../src/engine/ui/hubCommon.css" />
9+
<link rel="stylesheet" href="../../src/engine/theme/accordionV2/accordionV2.css" />
10+
<link rel="stylesheet" href="../shared/platformShell.css" />
11+
<link rel="stylesheet" href="./paletteManagerV2.css" />
12+
</head>
13+
<body class="tools-platform-tool-page hub-page-tools" data-tool-id="palette-manager-v2" data-tools-platform-page="tool">
14+
<details class="is-collapsible" open>
15+
<summary class="is-collapsible__summary" data-tools-platform-summary></summary>
16+
<div class="is-collapsible__content">
17+
<div id="shared-theme-header"></div>
18+
<div data-tools-platform-header></div>
19+
</div>
20+
</details>
21+
22+
<nav class="palette-manager-v2__menu-sample" aria-label="menuSample">
23+
<div class="palette-manager-v2__menu-actions">
24+
<button id="importPaletteButton" type="button">Import JSON</button>
25+
<input id="importPaletteInput" type="file" accept=".json,application/json" hidden />
26+
<button id="copyPaletteButton" type="button">Copy JSON</button>
27+
<button id="exportPaletteButton" type="button">Export JSON</button>
28+
</div>
29+
</nav>
30+
31+
<div class="palette-manager-v2 app-shell">
32+
<div class="palette-manager-v2__layout tools-platform-layout-grid">
33+
<aside class="palette-manager-v2__panel palette-manager-v2__panel--left tools-platform-resize-panel" data-panel-side="left">
34+
<details class="palette-manager-v2__accordion" open>
35+
<summary class="palette-manager-v2__accordion-summary">
36+
<span>Selected Swatch</span>
37+
<span class="palette-manager-v2__accordion-icon" aria-hidden="true">+</span>
38+
</summary>
39+
<div class="palette-manager-v2__accordion-content">
40+
<div id="selectedSwatchPreview" class="palette-manager-v2__preview" aria-label="Selected swatch preview"></div>
41+
<div class="palette-manager-v2__form-grid palette-manager-v2__form-grid--stacked palette-manager-v2__form-grid--compact">
42+
<label class="palette-manager-v2__field palette-manager-v2__field--compact">
43+
Symbol
44+
<input id="selectedSwatchSymbolInput" type="text" autocomplete="off" readonly />
45+
</label>
46+
<label class="palette-manager-v2__field palette-manager-v2__field--compact">
47+
Hex
48+
<input id="selectedSwatchHexInput" type="text" autocomplete="off" readonly />
49+
</label>
50+
<label class="palette-manager-v2__field palette-manager-v2__field--compact">
51+
Name
52+
<input id="selectedSwatchNameInput" type="text" autocomplete="off" readonly />
53+
</label>
54+
<label class="palette-manager-v2__field palette-manager-v2__field--compact">
55+
Source
56+
<input id="selectedSwatchSourceInput" type="text" autocomplete="off" readonly />
57+
</label>
58+
<div class="palette-manager-v2__field palette-manager-v2__field--compact">
59+
<span>Tags</span>
60+
<div id="selectedSwatchTagList" class="palette-manager-v2__tag-list" aria-label="Selected swatch tags"></div>
61+
</div>
62+
</div>
63+
</div>
64+
</details>
65+
66+
<details class="palette-manager-v2__accordion" open>
67+
<summary class="palette-manager-v2__accordion-summary">
68+
<span>User Defined Swatch</span>
69+
<span class="palette-manager-v2__accordion-icon" aria-hidden="true">+</span>
70+
</summary>
71+
<div class="palette-manager-v2__accordion-content">
72+
<div id="userDefinedSwatchPreview" class="palette-manager-v2__preview" aria-label="User defined swatch preview"></div>
73+
<div class="palette-manager-v2__form-grid palette-manager-v2__form-grid--stacked palette-manager-v2__form-grid--compact">
74+
<label class="palette-manager-v2__field palette-manager-v2__field--compact">
75+
Symbol
76+
<input id="swatchSymbolInput" type="text" maxlength="1" autocomplete="off" />
77+
</label>
78+
<label class="palette-manager-v2__field palette-manager-v2__field--compact">
79+
Hex
80+
<input id="swatchHexInput" type="text" maxlength="9" autocomplete="off" placeholder="#RRGGBB or #RRGGBBAA" />
81+
</label>
82+
<label class="palette-manager-v2__field palette-manager-v2__field--compact">
83+
Name
84+
<input id="swatchNameInput" type="text" autocomplete="off" />
85+
</label>
86+
</div>
87+
<div class="palette-manager-v2__controls">
88+
<button id="addSwatchButton" type="button">Add</button>
89+
<button id="updateSwatchButton" type="button">Update</button>
90+
<button id="clearFormButton" type="button">Clear Form</button>
91+
</div>
92+
</div>
93+
</details>
94+
95+
<details class="palette-manager-v2__accordion" open>
96+
<summary class="palette-manager-v2__accordion-summary">
97+
<span>Tags</span>
98+
<span class="palette-manager-v2__accordion-icon" aria-hidden="true">+</span>
99+
</summary>
100+
<div class="palette-manager-v2__accordion-content">
101+
<div id="availableTagList" class="palette-manager-v2__tag-list palette-manager-v2__tag-list--available" aria-label="Available user palette tags"></div>
102+
<div class="palette-manager-v2__tag-entry">
103+
<label class="palette-manager-v2__field palette-manager-v2__field--compact">
104+
Tag name
105+
<input id="tagEntryInput" type="text" autocomplete="off" />
106+
</label>
107+
<button id="addTagButton" type="button">Add</button>
108+
<div id="tagSuggestions" class="palette-manager-v2__tag-suggestions" role="listbox" aria-label="Tag suggestions"></div>
109+
</div>
110+
</div>
111+
</details>
112+
</aside>
113+
114+
<main class="palette-manager-v2__panel palette-manager-v2__panel--center">
115+
<section class="accordion-v2 is-open" data-accordion-v2-open="true">
116+
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="userPaletteAccordionContent">
117+
<span>User Palette</span>
118+
<span class="accordion-v2__icon" aria-hidden="true">+</span>
119+
</button>
120+
<div id="userPaletteAccordionContent" class="accordion-v2__content">
121+
<div class="palette-manager-v2__sort-row">
122+
<div class="palette-manager-v2__field">
123+
<span>Sort user palette</span>
124+
<div id="userPaletteSortControls" class="palette-manager-v2__sort-buttons" role="radiogroup" aria-label="Sort user palette"></div>
125+
</div>
126+
<div class="palette-manager-v2__field palette-manager-v2__size-field">
127+
<span>Size</span>
128+
<div id="userSwatchSizeControls" class="palette-manager-v2__size-buttons" role="radiogroup" aria-label="User palette tile size"></div>
129+
</div>
130+
</div>
131+
<div id="userPaletteCount" class="palette-manager-v2__meta">0 user swatches</div>
132+
<div id="userSwatchList" class="palette-manager-v2__tile-grid palette-manager-v2__tile-grid--user" aria-label="User palette swatches"></div>
133+
</div>
134+
</section>
135+
136+
<section class="accordion-v2 is-open" data-accordion-v2-open="true">
137+
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="sourcePaletteAccordionContent">
138+
<span>Sample Palette Swatch</span>
139+
<span class="accordion-v2__icon" aria-hidden="true">+</span>
140+
</button>
141+
<div id="sourcePaletteAccordionContent" class="accordion-v2__content">
142+
<div class="palette-manager-v2__source-palette-row">
143+
<label class="palette-manager-v2__field palette-manager-v2__field--source-control">
144+
Source palette
145+
<select id="sourcePaletteSelect"></select>
146+
</label>
147+
<button id="pinAllSourceButton" class="palette-manager-v2__pin-all-source-button" type="button">Pin All</button>
148+
</div>
149+
<label class="palette-manager-v2__field palette-manager-v2__field--source-control">
150+
Search source swatches
151+
<input id="sourceSearchInput" type="search" autocomplete="off" />
152+
</label>
153+
<div class="palette-manager-v2__sort-row">
154+
<div class="palette-manager-v2__field">
155+
<span>Sort source palette</span>
156+
<div id="sourcePaletteSortControls" class="palette-manager-v2__sort-buttons" role="radiogroup" aria-label="Sort source palette"></div>
157+
</div>
158+
<div class="palette-manager-v2__field palette-manager-v2__size-field">
159+
<span>Size</span>
160+
<div id="sourceSwatchSizeControls" class="palette-manager-v2__size-buttons" role="radiogroup" aria-label="Source palette tile size"></div>
161+
</div>
162+
</div>
163+
<div id="sourceSwatchList" class="palette-manager-v2__tile-grid palette-manager-v2__tile-grid--source" aria-label="Browse palette swatches"></div>
164+
</div>
165+
</section>
166+
</main>
167+
168+
<aside class="palette-manager-v2__panel palette-manager-v2__panel--right tools-platform-resize-panel" data-panel-side="right">
169+
<section class="accordion-v2 palette-manager-v2__right-accordion palette-manager-v2__right-accordion--import is-open" data-accordion-v2-open="true">
170+
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="importExportAccordionContent">
171+
<span>Palette JSON</span>
172+
<span class="accordion-v2__icon" aria-hidden="true">+</span>
173+
</button>
174+
<div id="importExportAccordionContent" class="accordion-v2__content">
175+
<pre id="paletteJsonPreview" class="palette-manager-v2__json-preview">No user palette data.</pre>
176+
</div>
177+
</section>
178+
179+
<section class="accordion-v2 palette-manager-v2__right-accordion palette-manager-v2__right-accordion--validation is-open" data-accordion-v2-open="true">
180+
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="validationErrorAccordionContent">
181+
<span>Validation/Error Viewer</span>
182+
<span class="accordion-v2__icon" aria-hidden="true">+</span>
183+
</button>
184+
<div id="validationErrorAccordionContent" class="accordion-v2__content">
185+
<div id="paletteStatus" class="palette-manager-v2__status" role="status">Ready.</div>
186+
<ul id="paletteErrorList" class="palette-manager-v2__errors" aria-label="Validation errors"></ul>
187+
</div>
188+
</section>
189+
</aside>
190+
</div>
191+
</div>
192+
193+
<div data-tools-platform-status></div>
194+
<script type="module" src="../shared/platformShell.js"></script>
195+
<script src="../../src/engine/paletteList.js"></script>
196+
<script type="module" src="../../src/engine/theme/accordionV2/accordionV2.js"></script>
197+
<script type="module" src="./main.js"></script>
198+
<script type="module" src="../../src/engine/theme/mount-shared-header.js"></script>
199+
</body>
200+
</html>

0 commit comments

Comments
 (0)