Skip to content

Commit ae4f28a

Browse files
author
DavidQ
committed
Finalize vector tool UI readiness for palette controls and default object selection - PR 10.6T
1 parent d821748 commit ae4f28a

8 files changed

Lines changed: 355 additions & 30 deletions

docs/dev/codex_commands.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
# Codex Command - PR 10.6S
1+
# Codex Commands - PR 10.6T
22

33
Model: GPT-5.4
44
Reasoning: high
55

6-
Run this from the repo root:
7-
86
```powershell
9-
codex exec --model gpt-5.4 --reasoning high "Execute BUILD PR 10.6S from docs/pr/BUILD_PR_LEVEL_10_6S_TOOL_UI_GAP_CLOSURE.md. Follow PROJECT_INSTRUCTIONS.md exactly. Fix only the PR 10.6R tool UI readiness gaps. Produce required reports under docs/dev/reports. Do not add fallback data, do not hardcode paths, and do not rewrite roadmap text except status markers if execution-backed."
7+
codex exec --model gpt-5.4 --reasoning high "Read docs/pr/BUILD_PR_LEVEL_10_6T_VECTOR_TOOL_UI_STATE_FINALIZATION.md and implement only the requested Vector Asset Studio and Vector Map Editor UI-state fixes. Preserve manifest-driven data flow, do not add fallback/demo data, do not hardcode sample paths, and produce the required report. Run npm run test:launch-smoke:games and npm run test:sample-standalone:data-flow. Create a repo-structured ZIP at tmp/PR_10_6T.zip containing changed files and reports."
108
```

docs/dev/commit_comment.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Close tool UI readiness gaps from control inventory and stabilize required input-to-control bindings - PR 10.6S
1+
Finalize vector tool UI readiness for palette controls and default object selection - PR 10.6T
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# PR 10.6T Expected Outputs
2+
3+
Codex must create:
4+
5+
- docs/dev/reports/PR_10_6T_vector_tool_ui_state_finalization_report.md
6+
- tmp/PR_10_6T.zip
7+
8+
UAT is not ready until Vector Asset Studio controls and Vector Map default selection pass the checks in the BUILD PR.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# PR 10.6T Vector Tool UI State Finalization Report
2+
3+
Date: 2026-04-27
4+
PR: 10.6T
5+
Scope: Vector Asset Studio and Vector Map Editor UI-state finalization only.
6+
7+
## Files Changed
8+
- `tools/Vector Asset Studio/main.js`
9+
- `tools/Vector Map Editor/editor/VectorMapEditorApp.js`
10+
- `docs/dev/reports/PR_10_6T_vector_tool_ui_state_finalization_report.md`
11+
12+
## Controls Fixed
13+
14+
### Vector Asset Studio
15+
Implemented minimal readiness-state fixes for preset and SVG load paths:
16+
- Added `bindPaintAndStrokeFromLoadedData()` and invoked it after SVG/preset loads to bind paint/stroke from loaded element colors, used colors, and loaded palette entries.
17+
- Updated palette target controls to stay disabled until canonical palette selection and palette entries are both present.
18+
- Kept disabled state only for missing dependencies; no fallback/demo palette data or hardcoded sample paths added.
19+
- Updated diagnostics control IDs to required contract IDs.
20+
21+
Expected diagnostic readiness after valid sample load:
22+
- `palette-swatches` -> `success`
23+
- `used-colors` -> `success`
24+
- `paint-control` -> `success`
25+
- `stroke-control` -> `success`
26+
27+
### Vector Map Editor
28+
Implemented minimal load-time UI-state fixes:
29+
- Added `selectFirstObjectWhenUnselected()` and invoked it after data loads from:
30+
- preset query load
31+
- JSON file import
32+
- JSON apply
33+
- Added explicit safe object-list empty state: `No objects loaded.` (no fake/default objects).
34+
- Added selected-object indicator to status center text (`Selected: <name|none>`).
35+
- Updated diagnostics control IDs and readiness shape to required IDs.
36+
37+
Expected diagnostic readiness after valid map load:
38+
- `object-list` -> `success`
39+
- `selected-object` -> `success`
40+
- `canvas-render` -> `success`
41+
42+
## Before/After Readiness Classification
43+
44+
### Vector Asset Studio
45+
Before:
46+
- Palette/paint/stroke controls could remain disabled/gray after loaded samples (1215/1216/1217).
47+
- Diagnostic IDs did not match required `palette-swatches` / `used-colors` / `paint-control` / `stroke-control` contract names.
48+
49+
After:
50+
- Controls bind from loaded canonical data and classify to `success` when dependencies are present.
51+
- Required control IDs are emitted.
52+
53+
### Vector Map Editor
54+
Before:
55+
- Loaded object sets could start with no active selection.
56+
- Canvas/list/selected-object readiness did not consistently reflect first-object selection state.
57+
- Status did not explicitly identify selected object.
58+
59+
After:
60+
- First valid object is auto-selected when loaded data has objects and selection is empty.
61+
- Object list shows active selection immediately; empty sets show explicit safe state.
62+
- Status includes selected object identity.
63+
- Required control IDs are emitted.
64+
65+
## Test Results
66+
67+
### 1) `npm run test:launch-smoke:games`
68+
- Result: PASS
69+
- Summary: PASS=12 FAIL=0 TOTAL=12
70+
71+
### 2) `npm run test:sample-standalone:data-flow`
72+
- Result: PASS
73+
- Failures: `schemaFailures=[]`, `contractFailures=[]`, `roundtripPathFailures=[]`, `genericFailures=[]`
74+
- Relevant signals:
75+
- `vector-asset-studio` sample IDs `1215`, `1216`, `1217` show `hasLoadSignal: true`, `hasFailureSignal: false`
76+
- `vector-map-editor` sample IDs `1212`, `1213`, `1214` show `hasLoadSignal: true`, `hasFailureSignal: false`
77+
78+
## Remaining Gaps
79+
- No automated assertion currently verifies exact UI clickability state for every Vector Asset Studio button in browser DOM after load.
80+
- No automated assertion currently verifies visual list highlight class and status text content for Vector Map Editor selection; behavior is implemented and covered by load-flow diagnostics and smoke/data-flow pass.

docs/dev/reports/launch_smoke_report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Launch Smoke Report
22

3-
Generated: 2026-04-27T20:59:51.555Z
3+
Generated: 2026-04-27T21:41:52.855Z
44

55
Filters: games=true, samples=false, tools=false, sampleRange=all
66

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# BUILD_PR_LEVEL_10_6T_VECTOR_TOOL_UI_STATE_FINALIZATION
2+
3+
## Purpose
4+
Close the remaining Vector tool UI readiness failures found after PR 10.6S.
5+
6+
## Scope
7+
One PR purpose only: finalize Vector Asset Studio and Vector Map Editor UI control readiness after data load.
8+
9+
## User UAT Evidence
10+
- Vector Asset Studio: samples 1215, 1216, 1217 show vector content, but palette + paint + stroke controls are not working. Swatches and used colors appear grayed out.
11+
- Vector Map Editor: objects are loaded, but canvas initially appears empty until the first object is manually selected. The tool should default-select the first object and clearly indicate the selected object.
12+
13+
## Required Fixes
14+
15+
### 1. Vector Asset Studio
16+
Codex must inspect the actual Vector Asset Studio controls and data flow, then fix the smallest required code path so:
17+
18+
- palette controls enable only after canonical palette data is loaded and valid
19+
- swatches are populated and interactive when palette data exists
20+
- used colors are populated and interactive when asset colors exist
21+
- paint/fill control is enabled and bound to the selected vector element or current paint target
22+
- stroke control is enabled and bound to the selected vector element or current stroke target
23+
- disabled/gray state is only used when a required dependency is truly missing
24+
- no hidden fallback palette or hardcoded default palette is introduced
25+
- no broad refactor or unrelated tool changes
26+
27+
Required diagnostics after fix:
28+
29+
```text
30+
[tool-ui:control-ready]
31+
toolId: vector-asset-studio
32+
controlId: palette-swatches
33+
classification: success
34+
```
35+
36+
```text
37+
[tool-ui:control-ready]
38+
toolId: vector-asset-studio
39+
controlId: used-colors
40+
classification: success
41+
```
42+
43+
```text
44+
[tool-ui:control-ready]
45+
toolId: vector-asset-studio
46+
controlId: paint-control
47+
classification: success
48+
```
49+
50+
```text
51+
[tool-ui:control-ready]
52+
toolId: vector-asset-studio
53+
controlId: stroke-control
54+
classification: success
55+
```
56+
57+
### 2. Vector Map Editor
58+
Codex must inspect the actual Vector Map Editor object/canvas state, then fix the smallest required code path so:
59+
60+
- when objects are loaded and no object is selected, the first valid object is selected automatically
61+
- the selected object is rendered on the canvas immediately after load
62+
- the object list visibly marks the selected object
63+
- the selected object detail/status panel identifies the selected object
64+
- changing selection updates canvas and selected-object indication
65+
- if no objects exist, show a safe empty state instead of fake/default objects
66+
- no broad refactor or unrelated tool changes
67+
68+
Required diagnostics after fix:
69+
70+
```text
71+
[tool-ui:control-ready]
72+
toolId: vector-map-editor
73+
controlId: object-list
74+
classification: success
75+
```
76+
77+
```text
78+
[tool-ui:control-ready]
79+
toolId: vector-map-editor
80+
controlId: selected-object
81+
classification: success
82+
```
83+
84+
```text
85+
[tool-ui:control-ready]
86+
toolId: vector-map-editor
87+
controlId: canvas-render
88+
classification: success
89+
```
90+
91+
## Acceptance Tests
92+
Run:
93+
94+
```powershell
95+
npm run test:launch-smoke:games
96+
npm run test:sample-standalone:data-flow
97+
```
98+
99+
Manual smoke checks:
100+
101+
```text
102+
samples 1215, 1216, 1217:
103+
- Vector Asset Studio renders vector content
104+
- palette swatches are not grayed out when palette exists
105+
- used colors are not grayed out when asset colors exist
106+
- paint and stroke controls work
107+
```
108+
109+
```text
110+
Vector Map Editor:
111+
- objects are visible in object list
112+
- first object is selected by default
113+
- canvas renders selected object immediately
114+
- selected object is visually indicated in list/status/details
115+
```
116+
117+
## Report Required
118+
Write:
119+
120+
```text
121+
docs/dev/reports/PR_10_6T_vector_tool_ui_state_finalization_report.md
122+
```
123+
124+
Report must include:
125+
126+
- files changed
127+
- controls fixed
128+
- before/after readiness classification
129+
- test results
130+
- remaining gaps, if any
131+
132+
## Roadmap
133+
Only update roadmap status markers if execution-backed. Do not rewrite roadmap text.

tools/Vector Asset Studio/main.js

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -763,11 +763,20 @@ function updatePaletteReadout() {
763763
}
764764

765765
function applyEnablementState() {
766+
const hasPaletteControls = hasPaletteSelection() && getVisiblePaletteEntries().length > 0;
766767
const hasStyleSelection = hasRequiredStyleSelection();
767768
const hasFill = hasFillSelection();
768769
const hasGradient = Boolean((normalizeColorValue(state.gradientFillFrom) || normalizeColorValue(state.fill)) && normalizeColorValue(state.gradientFillTo));
769770
const hasObjectSelection = Boolean(getSelectedElement());
770771

772+
refs.setPaletteTargetPaintButton.disabled = !hasPaletteControls;
773+
refs.setPaletteTargetStrokeButton.disabled = !hasPaletteControls;
774+
if (refs.setPaletteTargetGradientStartButton instanceof HTMLButtonElement) {
775+
refs.setPaletteTargetGradientStartButton.disabled = !hasPaletteControls;
776+
}
777+
if (refs.setPaletteTargetGradientEndButton instanceof HTMLButtonElement) {
778+
refs.setPaletteTargetGradientEndButton.disabled = !hasPaletteControls;
779+
}
771780
refs.applyCanvasSizeButton.disabled = !hasStyleSelection;
772781
refs.strokeWidthInput.disabled = !hasStyleSelection;
773782
if (refs.applyFillButton instanceof HTMLButtonElement) {
@@ -2291,6 +2300,7 @@ function loadSvgFromText(svgText, sourceName = "loaded-svg") {
22912300
if (embeddedEditorOptions) {
22922301
applySampleEditorOptions(embeddedEditorOptions);
22932302
}
2303+
bindPaintAndStrokeFromLoadedData();
22942304
applyEnablementState();
22952305
setStatus(embeddedEditorOptions ? `Loaded SVG: ${sourceName} (embedded editor options applied).` : `Loaded SVG: ${sourceName}`);
22962306
}
@@ -2622,6 +2632,52 @@ function ensurePaletteSelectionFromDeclaredInputs(editorOptions = {}, sampleId =
26222632
return normalizeColorValue(state.fill) && normalizeColorValue(state.stroke) ? "success" : "missing";
26232633
}
26242634

2635+
function bindPaintAndStrokeFromLoadedData() {
2636+
if (!hasPaletteSelection()) {
2637+
return false;
2638+
}
2639+
2640+
const paletteEntries = getVisiblePaletteEntries();
2641+
if (paletteEntries.length === 0) {
2642+
return false;
2643+
}
2644+
2645+
const paletteColors = paletteEntries
2646+
.map((entry) => normalizeColorValue(entry?.hex))
2647+
.filter(Boolean);
2648+
const usedColors = (Array.isArray(state.usedColors) ? state.usedColors : [])
2649+
.map((value) => normalizeColorValue(value))
2650+
.filter(Boolean);
2651+
const selectedElement = getSelectedElement();
2652+
const selectedFill = selectedElement instanceof SVGElement
2653+
? normalizeColorValue(selectedElement.getAttribute("fill") || selectedElement.style.fill || "")
2654+
: null;
2655+
const selectedStroke = selectedElement instanceof SVGElement
2656+
? normalizeColorValue(selectedElement.getAttribute("stroke") || selectedElement.style.stroke || "")
2657+
: null;
2658+
const currentPaint = normalizeColorValue(state.fill);
2659+
const currentStroke = normalizeColorValue(state.stroke);
2660+
2661+
if (!currentPaint) {
2662+
state.fill = selectedFill
2663+
|| usedColors[0]
2664+
|| paletteColors[0]
2665+
|| null;
2666+
}
2667+
2668+
if (!currentStroke) {
2669+
const preferredUsedStroke = usedColors.find((value) => value !== normalizeColorValue(state.fill)) || null;
2670+
const preferredPaletteStroke = paletteColors.find((value) => value !== normalizeColorValue(state.fill)) || null;
2671+
state.stroke = selectedStroke
2672+
|| preferredUsedStroke
2673+
|| preferredPaletteStroke
2674+
|| paletteColors[0]
2675+
|| null;
2676+
}
2677+
2678+
return Boolean(normalizeColorValue(state.fill) && normalizeColorValue(state.stroke));
2679+
}
2680+
26252681
function emitVectorAssetControlReadiness(sampleId = "", options = {}) {
26262682
const forceMissing = options.forceMissing === true;
26272683
const phase = typeof options.phase === "string" && options.phase.trim() ? options.phase.trim() : "load";
@@ -2630,6 +2686,7 @@ function emitVectorAssetControlReadiness(sampleId = "", options = {}) {
26302686
const hasSvgCanvas = !forceMissing && refs.editorSvg instanceof SVGSVGElement && drawableCount > 0;
26312687
const selectedPaletteEntries = hasPaletteSelection() ? getVisiblePaletteEntries() : [];
26322688
const hasPaletteControls = !forceMissing && hasPaletteSelection() && selectedPaletteEntries.length > 0;
2689+
const hasUsedColors = !forceMissing && Array.isArray(state.usedColors) && state.usedColors.length > 0;
26332690
const hasPaint = !forceMissing && Boolean(normalizeColorValue(state.fill));
26342691
const hasStroke = !forceMissing && Boolean(normalizeColorValue(state.stroke));
26352692
const paletteClassification = typeof options.paletteClassification === "string" && options.paletteClassification.trim()
@@ -2650,7 +2707,7 @@ function emitVectorAssetControlReadiness(sampleId = "", options = {}) {
26502707
logToolUiControlReady({
26512708
toolId: "vector-asset-studio",
26522709
sampleId,
2653-
controlId: "palette-controls",
2710+
controlId: "palette-swatches",
26542711
requiredData: "declared-palette-selection",
26552712
loaded: hasPaletteControls,
26562713
count: selectedPaletteEntries.length,
@@ -2661,21 +2718,32 @@ function emitVectorAssetControlReadiness(sampleId = "", options = {}) {
26612718
logToolUiControlReady({
26622719
toolId: "vector-asset-studio",
26632720
sampleId,
2664-
controlId: "paint-fill-control",
2721+
controlId: "used-colors",
2722+
requiredData: "vector-asset-used-colors",
2723+
loaded: hasUsedColors,
2724+
count: hasUsedColors ? state.usedColors.length : 0,
2725+
value: hasUsedColors ? state.usedColors.length : 0,
2726+
classification: hasUsedColors ? "success" : (hasSvgCanvas ? "empty" : "missing")
2727+
});
2728+
2729+
logToolUiControlReady({
2730+
toolId: "vector-asset-studio",
2731+
sampleId,
2732+
controlId: "paint-control",
26652733
requiredData: "declared-paint-color",
2666-
loaded: hasPaint,
2734+
loaded: hasPaletteControls && hasPaint,
26672735
value: normalizeColorValue(state.fill) || "none",
2668-
classification: hasPaint ? "success" : "missing"
2736+
classification: hasPaletteControls && hasPaint ? "success" : (hasPaletteControls ? "missing" : "empty")
26692737
});
26702738

26712739
logToolUiControlReady({
26722740
toolId: "vector-asset-studio",
26732741
sampleId,
26742742
controlId: "stroke-control",
26752743
requiredData: "declared-stroke-color",
2676-
loaded: hasStroke,
2744+
loaded: hasPaletteControls && hasStroke,
26772745
value: normalizeColorValue(state.stroke) || "none",
2678-
classification: hasStroke ? "success" : "missing"
2746+
classification: hasPaletteControls && hasStroke ? "success" : (hasPaletteControls ? "missing" : "empty")
26792747
});
26802748

26812749
logToolUiLifecycle({
@@ -2810,6 +2878,8 @@ async function tryLoadPresetFromQuery() {
28102878
applySampleEditorOptions(extractedPreset.editorOptions);
28112879
}
28122880
const paletteClassification = ensurePaletteSelectionFromDeclaredInputs(extractedPreset.editorOptions || {}, sampleId);
2881+
bindPaintAndStrokeFromLoadedData();
2882+
applyEnablementState();
28132883
emitVectorAssetControlReadiness(sampleId, { paletteClassification, phase: "loaded", lifecycleStable: true });
28142884
if (paletteClassification !== "success") {
28152885
logToolLoadWarning({

0 commit comments

Comments
 (0)