Skip to content

Commit 1b055b6

Browse files
author
DavidQ
committed
PR 10.4: remove remaining tool sample dropdowns
1 parent 46714f4 commit 1b055b6

9 files changed

Lines changed: 58 additions & 483 deletions

File tree

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,10 @@
11
MODEL: GPT-5.3-codex
2-
REASONING: high
32

43
TASK:
5-
Apply BUILD_PR_LEVEL_10_3_MIGRATE_TOOL_LOCAL_SAMPLES_TO_SAMPLES.
4+
- scan all tools for sample dropdown/select UI
5+
- remove them
6+
- ensure tools still work with manifest input
7+
- update tests if needed
68

7-
STEPS:
8-
1. Read docs/pr/PLAN_PR_LEVEL_10_3_MIGRATE_TOOL_LOCAL_SAMPLES_TO_SAMPLES.md.
9-
2. Read docs/pr/BUILD_PR_LEVEL_10_3_MIGRATE_TOOL_LOCAL_SAMPLES_TO_SAMPLES.md.
10-
3. Inspect these tool folders for local samples/demo entries:
11-
- tools/Vector Map Editor
12-
- tools/Vector Asset Studio
13-
- tools/Parallax Scene Studio
14-
- tools/Tilemap Studio
15-
4. Identify each tool-local sample/demo payload.
16-
5. Move/copy each sample into the correct `samples/phase-*` folder using next available sample IDs.
17-
6. Preserve sample behavior and required data.
18-
7. Update `samples/index.html`.
19-
8. Remove tool-local sample dropdown/select entries from the processed tools after migrated samples exist.
20-
9. Ensure tools still accept explicit manifest/input data.
21-
10. Add/update tests for:
22-
- migrated sample presence in samples/index.html
23-
- migrated sample launch
24-
- removal of tool-local sample dropdown/select entries
25-
11. Write docs/dev/reports/level_10_3_tool_local_sample_migration_report.md.
26-
12. Update docs/dev/roadmaps/MASTER_ROADMAP_ENGINE.md status only if needed:
27-
- [ ] -> [.]
28-
- [.] -> [x]
29-
- no prose rewrite/delete
30-
13. Do not modify start_of_day.
31-
14. Do not add validators.
32-
15. Create Codex delta ZIP:
33-
tmp/BUILD_PR_LEVEL_10_3_MIGRATE_TOOL_LOCAL_SAMPLES_TO_SAMPLES_delta.zip
34-
35-
ACCEPTANCE:
36-
- all discovered tool-local samples from listed tools are migrated
37-
- samples/index.html updated
38-
- local sample dropdown/selects removed
39-
- delta ZIP exists
9+
OUTPUT:
10+
tmp/BUILD_PR_LEVEL_10_4_REMOVE_REMAINING_TOOL_SAMPLE_DROPDOWNS_delta.zip
Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1 @@
1-
BUILD_PR_LEVEL_10_3_MIGRATE_TOOL_LOCAL_SAMPLES_TO_SAMPLES
2-
3-
- Migrated tool-local samples into /samples
4-
- Updated samples index
5-
- Removed local sample dropdown/select entries from processed tools
6-
- Preserved tool explicit-input behavior
7-
8-
PR Details:
9-
- Processes Vector Map Editor, Vector Asset Studio, Parallax Scene Studio, and Tilemap Studio
10-
- No validators
11-
- No start_of_day changes
1+
PR 10.4: remove remaining tool sample dropdowns
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Remove any remaining tool-local sample dropdowns across all tools.
2+
3+
Ensure:
4+
- tools do NOT expose samples internally
5+
- all samples come from /samples only
6+
- tools accept manifest-driven input only

tests/tools/ToolLocalSampleMigration.test.mjs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,49 @@ function assertToolSampleControlsRemoved() {
4848
assert.equal(parallaxHtml.includes('id="loadSampleButton"'), false, "Parallax Scene Studio sample load button should be removed.");
4949
}
5050

51+
function listToolIndexFiles(rootPath) {
52+
const toolRoot = path.join(rootPath, "tools");
53+
const indexFiles = [];
54+
const stack = [toolRoot];
55+
while (stack.length > 0) {
56+
const current = stack.pop();
57+
const entries = fs.readdirSync(current, { withFileTypes: true });
58+
entries.forEach((entry) => {
59+
const fullPath = path.join(current, entry.name);
60+
if (entry.isDirectory()) {
61+
stack.push(fullPath);
62+
return;
63+
}
64+
if (entry.isFile() && entry.name.toLowerCase() === "index.html") {
65+
indexFiles.push(fullPath);
66+
}
67+
});
68+
}
69+
return indexFiles;
70+
}
71+
72+
function assertNoSampleDropdownSelectInToolIndexes() {
73+
const indexFiles = listToolIndexFiles(REPO_ROOT);
74+
const violations = [];
75+
76+
indexFiles.forEach((filePath) => {
77+
const html = readText(filePath);
78+
const hasSampleSelectId = /id\s*=\s*["']sampleSelect["']/i.test(html);
79+
const hasSampleSelectTag = /<select\b[^>]*(id|name|class)\s*=\s*["'][^"']*sample[^"']*["'][^>]*>/i.test(html);
80+
const hasSamplesLabelSelect = /<label[^>]*>\s*Samples?\b[\s\S]{0,220}<select\b/i.test(html);
81+
const hasLegacyNoSamplesCopy = /No samples loaded/i.test(html);
82+
if (hasSampleSelectId || hasSampleSelectTag || hasSamplesLabelSelect || hasLegacyNoSamplesCopy) {
83+
violations.push(path.relative(REPO_ROOT, filePath).replace(/\\/g, "/"));
84+
}
85+
});
86+
87+
assert.equal(
88+
violations.length,
89+
0,
90+
`Tool sample dropdown/select UI residues found in: ${violations.join(", ")}`
91+
);
92+
}
93+
5194
function assertExplicitPresetInputSupportRetained() {
5295
const vectorMapJs = readText(path.join(REPO_ROOT, "tools", "Vector Map Editor", "editor", "VectorMapEditorApp.js"));
5396
const vectorAssetJs = readText(path.join(REPO_ROOT, "tools", "Vector Asset Studio", "main.js"));
@@ -103,5 +146,6 @@ function assertMigratedSamplesIndexedAndLaunchable() {
103146
export function run() {
104147
assertMigratedSamplesIndexedAndLaunchable();
105148
assertToolSampleControlsRemoved();
149+
assertNoSampleDropdownSelectInToolIndexes();
106150
assertExplicitPresetInputSupportRetained();
107-
}
151+
}

tools/Parallax Scene Studio/main.js

Lines changed: 0 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,8 @@ import { buildEditorExperienceLayer, summarizeEditorExperienceLayer } from "../s
3232
import { buildDebugVisualizationLayer, summarizeDebugVisualizationLayer } from "../shared/debugVisualizationLayer.js";
3333
import { registerToolBootContract } from "../shared/toolBootContract.js";
3434
import { createLivePreviewSyncBridge, validateStateBindingPayload } from "../shared/livePreviewSyncChannel.js";
35-
import { normalizeToolSamplePath, toToolSampleLabel } from "../shared/toolSampleCatalog.js";
3635
import { addToolModeMetadata, assertStandaloneToolDocument, offerImportMismatchOptions } from "../shared/documentModeGuards.js";
3736

38-
const SAMPLE_DIRECTORY_PATH = "./samples/";
39-
const SAMPLE_MANIFEST_PATH = "./samples/sample-manifest.json";
40-
4137
function normalizeSamplePresetPath(pathValue) {
4238
if (typeof pathValue !== "string") {
4339
return "";
@@ -384,7 +380,6 @@ class ParallaxEditorApp {
384380
this.refs = {};
385381
this.imageCache = new Map();
386382
this.imageCacheVersion = 0;
387-
this.sampleEntries = [];
388383
this.isSimulationMode = false;
389384
this.simulation = {
390385
rafId: 0,
@@ -460,10 +455,6 @@ class ParallaxEditorApp {
460455
this.renderAll();
461456
this.bindRuntimeStateSync();
462457
this.queueLivePreviewSync("init");
463-
const hasSamplePresetQuery = new URLSearchParams(window.location.search).has("samplePresetPath");
464-
if (this.refs.sampleSelect instanceof HTMLSelectElement && this.refs.loadSampleButton instanceof HTMLButtonElement) {
465-
this.loadSampleManifest({ quiet: hasSamplePresetQuery });
466-
}
467458
void this.tryLoadPresetFromQuery();
468459
}
469460

@@ -498,8 +489,6 @@ class ParallaxEditorApp {
498489
this.refs.mapHeightInput = rootDocument.getElementById("mapHeightInput");
499490
this.refs.tileSizeInput = rootDocument.getElementById("tileSizeInput");
500491
this.refs.applyMapMetaButton = rootDocument.getElementById("applyMapMetaButton");
501-
this.refs.sampleSelect = rootDocument.getElementById("sampleSelect");
502-
this.refs.loadSampleButton = rootDocument.getElementById("loadSampleButton");
503492

504493
this.refs.layerList = rootDocument.getElementById("layerList");
505494
this.refs.newLayerNameInput = rootDocument.getElementById("newLayerNameInput");
@@ -588,13 +577,6 @@ class ParallaxEditorApp {
588577

589578
this.refs.applyMapMetaButton.addEventListener("click", () => this.applyMapMetaFromInputs());
590579
this.refs.projectNameInput.addEventListener("change", () => this.applyMapMetaFromInputs());
591-
if (this.refs.loadSampleButton instanceof HTMLButtonElement && this.refs.sampleSelect instanceof HTMLSelectElement) {
592-
this.refs.loadSampleButton.addEventListener("click", () => this.handleLoadSelectedSample());
593-
this.refs.sampleSelect.addEventListener("change", () => this.handleSampleSelectionChanged());
594-
this.refs.sampleSelect.addEventListener("focus", () => {
595-
void this.loadSampleManifest({ quiet: true });
596-
});
597-
}
598580

599581
this.refs.addLayerButton.addEventListener("click", () => this.addLayer());
600582
this.refs.removeLayerButton.addEventListener("click", () => this.removeSelectedLayer());
@@ -989,192 +971,6 @@ class ParallaxEditorApp {
989971
}
990972
}
991973

992-
createSampleEntry(pathValue, labelHint = "", idHint = "") {
993-
const path = normalizeToolSamplePath(pathValue);
994-
if (!path) {
995-
return null;
996-
}
997-
const normalizedLabel = typeof labelHint === "string" ? labelHint.trim() : "";
998-
const fallbackLabel = toToolSampleLabel(path);
999-
return {
1000-
id: typeof idHint === "string" && idHint.trim() ? idHint.trim() : path,
1001-
label: normalizedLabel && !/[\\/]/.test(normalizedLabel) ? normalizedLabel : fallbackLabel,
1002-
path
1003-
};
1004-
}
1005-
1006-
async discoverSampleEntriesFromDirectory() {
1007-
const directoryUrl = new URL(SAMPLE_DIRECTORY_PATH, window.location.href);
1008-
const response = await fetch(directoryUrl.toString(), { cache: "no-store" });
1009-
if (!response.ok) {
1010-
throw new Error(`Directory request failed (${response.status}).`);
1011-
}
1012-
1013-
const html = await response.text();
1014-
const parser = new DOMParser();
1015-
const documentModel = parser.parseFromString(html, "text/html");
1016-
const anchors = Array.from(documentModel.querySelectorAll("a[href]"));
1017-
const discovered = [];
1018-
const seenPaths = new Set();
1019-
1020-
anchors.forEach((anchor) => {
1021-
const href = anchor.getAttribute("href");
1022-
if (!href) {
1023-
return;
1024-
}
1025-
const resolved = new URL(href, directoryUrl);
1026-
const fileName = decodeURIComponent((resolved.pathname.split("/").pop() || "").trim());
1027-
if (!fileName || !fileName.toLowerCase().endsWith(".json")) {
1028-
return;
1029-
}
1030-
if (fileName.toLowerCase() === "sample-manifest.json") {
1031-
return;
1032-
}
1033-
1034-
const entry = this.createSampleEntry(fileName, anchor.textContent || "", fileName);
1035-
if (!entry || seenPaths.has(entry.path)) {
1036-
return;
1037-
}
1038-
seenPaths.add(entry.path);
1039-
discovered.push(entry);
1040-
});
1041-
1042-
discovered.sort((left, right) => left.label.localeCompare(right.label));
1043-
return discovered;
1044-
}
1045-
1046-
collectSampleEntriesFromManifest(manifest) {
1047-
const rawSamples = Array.isArray(manifest?.samples) ? manifest.samples : [];
1048-
return rawSamples
1049-
.map((entry) => this.createSampleEntry(entry?.path, entry?.label, entry?.id))
1050-
.filter((entry) => entry !== null);
1051-
}
1052-
1053-
async loadSampleManifest(options = {}) {
1054-
if (!(this.refs.sampleSelect instanceof HTMLSelectElement) || !(this.refs.loadSampleButton instanceof HTMLButtonElement)) {
1055-
this.sampleEntries = [];
1056-
return;
1057-
}
1058-
const quiet = options.quiet === true;
1059-
const previousSelection = normalizeToolSamplePath(this.refs.sampleSelect.value);
1060-
if (!quiet) {
1061-
this.refs.sampleSelect.innerHTML = "<option value=\"\">Loading samples...</option>";
1062-
this.refs.loadSampleButton.disabled = true;
1063-
}
1064-
1065-
try {
1066-
const sampleEntries = await this.discoverSampleEntriesFromDirectory();
1067-
if (sampleEntries.length === 0) {
1068-
throw new Error("No sample JSON files were discovered in ./samples/.");
1069-
}
1070-
this.sampleEntries = sampleEntries;
1071-
this.renderSampleOptions(previousSelection);
1072-
if (!quiet) {
1073-
this.updateStatus(`Loaded ${sampleEntries.length} samples from ${SAMPLE_DIRECTORY_PATH}.`);
1074-
}
1075-
return;
1076-
} catch (directoryError) {
1077-
try {
1078-
const manifestUrl = new URL(SAMPLE_MANIFEST_PATH, window.location.href);
1079-
const response = await fetch(manifestUrl.toString(), { cache: "no-store" });
1080-
if (!response.ok) {
1081-
throw new Error(`Manifest request failed (${response.status}).`);
1082-
}
1083-
1084-
const manifest = await response.json();
1085-
const sampleEntries = this.collectSampleEntriesFromManifest(manifest);
1086-
if (sampleEntries.length === 0) {
1087-
throw new Error("Sample manifest had no valid entries.");
1088-
}
1089-
1090-
this.sampleEntries = sampleEntries;
1091-
this.renderSampleOptions(previousSelection);
1092-
if (!quiet) {
1093-
this.updateStatus(`Loaded ${sampleEntries.length} samples from ${SAMPLE_MANIFEST_PATH}.`);
1094-
}
1095-
} catch (error) {
1096-
this.sampleEntries = [];
1097-
this.renderSampleOptions(previousSelection);
1098-
if (!quiet) {
1099-
this.updateStatus(`Sample discovery unavailable: ${error instanceof Error ? error.message : "unknown error"}`);
1100-
}
1101-
}
1102-
}
1103-
}
1104-
1105-
renderSampleOptions(preferredPath = "") {
1106-
if (!(this.refs.sampleSelect instanceof HTMLSelectElement) || !(this.refs.loadSampleButton instanceof HTMLButtonElement)) {
1107-
return;
1108-
}
1109-
const select = this.refs.sampleSelect;
1110-
select.innerHTML = "";
1111-
1112-
if (this.sampleEntries.length === 0) {
1113-
const option = document.createElement("option");
1114-
option.value = "";
1115-
option.textContent = "No samples available";
1116-
select.appendChild(option);
1117-
this.refs.loadSampleButton.disabled = true;
1118-
return;
1119-
}
1120-
1121-
const promptOption = document.createElement("option");
1122-
promptOption.value = "";
1123-
promptOption.textContent = "Select a sample...";
1124-
select.appendChild(promptOption);
1125-
1126-
this.sampleEntries.forEach((entry) => {
1127-
const option = document.createElement("option");
1128-
option.value = entry.path;
1129-
option.textContent = entry.label;
1130-
select.appendChild(option);
1131-
});
1132-
1133-
if (preferredPath && this.sampleEntries.some((entry) => entry.path === preferredPath)) {
1134-
select.value = preferredPath;
1135-
}
1136-
this.refs.loadSampleButton.disabled = false;
1137-
}
1138-
1139-
handleSampleSelectionChanged() {
1140-
if (!(this.refs.sampleSelect instanceof HTMLSelectElement)) {
1141-
return;
1142-
}
1143-
const selectedPath = normalizeToolSamplePath(this.refs.sampleSelect.value);
1144-
if (!selectedPath) {
1145-
return;
1146-
}
1147-
this.updateStatus(`Sample selected: ${selectedPath}`);
1148-
}
1149-
1150-
async handleLoadSelectedSample() {
1151-
if (!(this.refs.sampleSelect instanceof HTMLSelectElement)) {
1152-
this.updateStatus("Preset loading is available from sample launch context.");
1153-
return;
1154-
}
1155-
const selectedPath = normalizeToolSamplePath(this.refs.sampleSelect.value);
1156-
if (!selectedPath) {
1157-
this.updateStatus("Select a sample before loading.");
1158-
return;
1159-
}
1160-
1161-
this.exitSimulationMode();
1162-
try {
1163-
const sampleUrl = new URL(selectedPath, window.location.href);
1164-
const response = await fetch(sampleUrl.toString(), { cache: "no-store" });
1165-
if (!response.ok) {
1166-
throw new Error(`Sample request failed (${response.status}).`);
1167-
}
1168-
1169-
const raw = await response.json();
1170-
this.applyParallaxDocument(extractParallaxDocument(raw));
1171-
this.queueLivePreviewSync("load-sample");
1172-
this.updateStatus(`Loaded sample ${selectedPath}.`);
1173-
} catch (error) {
1174-
this.updateStatus(`Sample load failed: ${error instanceof Error ? error.message : "unknown error"}`);
1175-
}
1176-
}
1177-
1178974
async tryLoadPresetFromQuery() {
1179975
const searchParams = new URLSearchParams(window.location.search);
1180976
const samplePresetPath = normalizeSamplePresetPath(searchParams.get("samplePresetPath") || "");

tools/Replay Visualizer/index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
<section class="panel debug-tool-panel">
2424
<div class="debug-tool-actions">
2525
<button type="button" id="loadReplayButton">Load Replay JSON</button>
26-
<button type="button" id="loadSampleReplayButton">Load Sample Replay</button>
2726
<button type="button" id="playReplayButton">Play</button>
2827
<button type="button" id="pauseReplayButton">Pause</button>
2928
<button type="button" id="resetReplayButton">Reset</button>

0 commit comments

Comments
 (0)