Skip to content

Commit 631fef5

Browse files
author
DavidQ
committed
Add persistent Sprite Editor sample source details and fix sample 1414 preset preview layering/positioning
1 parent cd04cbe commit 631fef5

6 files changed

Lines changed: 114 additions & 22 deletions

File tree

samples/index.render.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ function buildRoundtripLinks(sample, toolRegistryMap) {
116116
let label = `Open ${normalize(tool.displayName) || normalize(tool.name) || toolId}`;
117117
if (shouldUsePresetRoundtrip(sample, toolId)) {
118118
const presetPath = getRoundtripPresetPath(sample, toolId);
119-
href = `${baseHref}?sampleId=${encodeURIComponent(sample.id)}&samplePresetPath=${encodeURIComponent(presetPath)}`;
119+
href = `${baseHref}?sampleId=${encodeURIComponent(sample.id)}&sampleTitle=${encodeURIComponent(sample.title || "")}&samplePresetPath=${encodeURIComponent(presetPath)}`;
120120
}
121121

122122
links.push({ toolId, href, label });
Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,79 @@
1-
import { Scene } from '/src/engine/scene/index.js'; import { drawFrame, drawPanel } from '/src/engine/debug/index.js'; import { Theme, ThemeTokens } from '/src/engine/theme/index.js'; import { TexturePreprocessPipeline } from '/tools/shared/pipeline/index.js'; import { drawSpriteProjectFrame, loadSpriteProjectPreset } from '/samples/shared/spritePresetRuntime.js';
1+
import { Scene } from "/src/engine/scene/index.js";
2+
import { drawFrame, drawPanel } from "/src/engine/debug/index.js";
3+
import { Theme, ThemeTokens } from "/src/engine/theme/index.js";
4+
import { TexturePreprocessPipeline } from "/tools/shared/pipeline/index.js";
5+
import { drawSpriteProjectFrame, loadSpriteProjectPreset } from "/samples/shared/spritePresetRuntime.js";
6+
27
const theme = new Theme(ThemeTokens);
3-
const SPRITE_PRESET_PATH = '/samples/phase-14/1414/sample-1414-sprite-editor.json';
8+
const SPRITE_PRESET_PATH = "/samples/phase-14/1414/sample-1414-sprite-editor.json";
9+
410
export default class TextureSpritePreprocessPipelineScene extends Scene {
5-
constructor() { super(); this.pipeline = new TexturePreprocessPipeline(); this.texture = null; this.status = 'Preprocess a texture record.'; this.spriteProject = null; this.spriteStatus = 'loading'; this.spriteError = ''; void this.loadSpriteProject(); }
6-
async loadSpriteProject() { try { this.spriteProject = await loadSpriteProjectPreset(SPRITE_PRESET_PATH); this.spriteStatus = 'loaded'; this.spriteError = ''; } catch (error) { this.spriteProject = null; this.spriteStatus = 'fallback'; this.spriteError = error instanceof Error ? error.message : 'unknown preset error'; } }
7-
run() { this.texture = this.pipeline.run({ width: 64, height: 32 }); this.status = 'Texture preprocessing complete.'; }
11+
constructor() {
12+
super();
13+
this.pipeline = new TexturePreprocessPipeline();
14+
this.texture = null;
15+
this.status = "Preprocess a texture record.";
16+
this.spriteProject = null;
17+
this.spriteStatus = "loading";
18+
this.spriteError = "";
19+
void this.loadSpriteProject();
20+
}
21+
22+
async loadSpriteProject() {
23+
try {
24+
this.spriteProject = await loadSpriteProjectPreset(SPRITE_PRESET_PATH);
25+
this.spriteStatus = "loaded";
26+
this.spriteError = "";
27+
} catch (error) {
28+
this.spriteProject = null;
29+
this.spriteStatus = "fallback";
30+
this.spriteError = error instanceof Error ? error.message : "unknown preset error";
31+
}
32+
}
33+
34+
run() {
35+
this.texture = this.pipeline.run({ width: 64, height: 32 });
36+
this.status = "Texture preprocessing complete.";
37+
}
38+
839
render(renderer) {
9-
const presetStatus = this.spriteStatus === 'loaded'
10-
? 'Sprite preset loaded from sample-1414-sprite-editor.json'
11-
: this.spriteStatus === 'loading'
12-
? 'Loading shared sprite preset...'
13-
: `Sprite preset unavailable (${this.spriteError || 'using pipeline-only view'})`;
14-
drawFrame(renderer, theme, ['Engine Sample 1414', 'Texture preprocessing normalizes visual content in one centralized step.', 'This sample and Sprite Editor load the same sample-1414-sprite-editor.json source.', this.status, presetStatus]);
15-
drawPanel(renderer, 120, 220, 540, 220, 'Texture Output', [`Width: ${this.texture?.width ?? 0}`, `Height: ${this.texture?.height ?? 0}`, `Padded: ${this.texture?.padded ?? false}`, `Atlas Ready: ${this.texture?.atlasReady ?? false}`, `Preset Frames: ${this.spriteProject?.frames?.length ?? 0}`]);
16-
if (this.spriteProject) {
17-
drawSpriteProjectFrame(renderer, this.spriteProject, 0, { x: 690, y: 260, pixelSize: 4 });
18-
drawPanel(renderer, 660, 220, 240, 220, 'Sprite Preset Preview', ['Frame source: sample-1414-sprite-editor.json', `Size: ${this.spriteProject.width}x${this.spriteProject.height}`, 'Edit in Sprite Editor, then relaunch sample.']);
40+
const presetStatus = this.spriteStatus === "loaded"
41+
? "Sprite preset loaded from sample-1414-sprite-editor.json"
42+
: this.spriteStatus === "loading"
43+
? "Loading shared sprite preset..."
44+
: `Sprite preset unavailable (${this.spriteError || "using pipeline-only view"})`;
45+
46+
drawFrame(renderer, theme, [
47+
"Engine Sample 1414",
48+
"Texture preprocessing normalizes visual content in one centralized step.",
49+
"This sample and Sprite Editor load the same sample-1414-sprite-editor.json source.",
50+
this.status,
51+
presetStatus
52+
]);
53+
54+
drawPanel(renderer, 120, 220, 540, 220, "Texture Output", [
55+
`Width: ${this.texture?.width ?? 0}`,
56+
`Height: ${this.texture?.height ?? 0}`,
57+
`Padded: ${this.texture?.padded ?? false}`,
58+
`Atlas Ready: ${this.texture?.atlasReady ?? false}`,
59+
`Preset Frames: ${this.spriteProject?.frames?.length ?? 0}`
60+
]);
61+
62+
if (!this.spriteProject) {
63+
return;
1964
}
65+
66+
const previewPanel = { x: 660, y: 220, width: 240, height: 220 };
67+
drawPanel(renderer, previewPanel.x, previewPanel.y, previewPanel.width, previewPanel.height, "Sprite Preset Preview", [
68+
"Frame source: sample-1414-sprite-editor.json",
69+
`Size: ${this.spriteProject.width}x${this.spriteProject.height}`,
70+
"Edit in Sprite Editor, then relaunch sample."
71+
]);
72+
73+
drawSpriteProjectFrame(renderer, this.spriteProject, 0, {
74+
x: previewPanel.x + 16,
75+
y: previewPanel.y + 98,
76+
pixelSize: 4
77+
});
2078
}
2179
}

tools/Sprite Editor/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ <h3>Recent</h3>
181181
<details id="rightPanelStatus" class="panel-accordion">
182182
<summary class="panel-accordion__summary"><h2>Status</h2></summary>
183183
<div id="statusPanel" class="panel-accordion__body">
184+
<p id="sampleSourceDetailText" class="status-text">Source details: Tool mode.</p>
184185
<p id="statusText" class="status-text">Ready.</p>
185186
<p id="remediationSummaryText" class="status-text">No remediation actions available.</p>
186187
<div class="io-controls">

tools/Sprite Editor/modules/spriteEditorApp.js

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,14 @@ function setSampleSource(state, source = {}) {
731731
};
732732
}
733733

734+
function buildSampleSourceDetailLabel(source = {}) {
735+
const summary = buildSampleSourceLabel(source);
736+
const samplePresetPath = typeof source.samplePresetPath === "string" ? source.samplePresetPath.trim() : "";
737+
const fileName = typeof source.fileName === "string" ? source.fileName.trim() : "";
738+
const detail = samplePresetPath || fileName;
739+
return detail ? `${summary} (${detail})` : summary;
740+
}
741+
734742
function isPaletteSelected(project) {
735743
return Boolean(project.paletteRef?.id && project.paletteRef.id !== NO_PALETTE_ID);
736744
}
@@ -982,6 +990,7 @@ function updateToolStateText(state) {
982990
const toolName = state.projectTool[0].toUpperCase() + state.projectTool.slice(1);
983991
state.elements.toolStateText.textContent = `Tool: ${toolName}`;
984992
state.elements.sampleSourceText.textContent = `Source: ${buildSampleSourceLabel(state.sampleSource)}`;
993+
state.elements.sampleSourceDetailText.textContent = `Source details: ${buildSampleSourceDetailLabel(state.sampleSource)}.`;
985994
state.elements.activeColorText.textContent = `Color: ${state.project.activeColor ?? "none"}`;
986995
state.elements.frameStateText.textContent = `Frame: ${state.project.currentFrameIndex + 1} / ${state.project.frames.length}`;
987996
state.elements.toggleStateText.textContent = `Grid: ${state.project.showGrid ? "On" : "Off"} | Onion: ${state.project.onionSkin ? "On" : "Off"}`;
@@ -1486,7 +1495,7 @@ function extractSpriteAssetRegistryFromSamplePreset(rawPreset) {
14861495
return null;
14871496
}
14881497

1489-
function applySamplePreset(state, rawPreset, sampleId, samplePresetPath) {
1498+
function applySamplePreset(state, rawPreset, sampleId, samplePresetPath, sampleTitleHint = "") {
14901499
const presetProject = extractSpriteProjectFromSamplePreset(rawPreset);
14911500
if (!presetProject || typeof presetProject !== "object") {
14921501
throw new Error("Preset payload did not include a sprite project.");
@@ -1495,9 +1504,11 @@ function applySamplePreset(state, rawPreset, sampleId, samplePresetPath) {
14951504
state.project = ensureProjectShape(presetProject);
14961505

14971506
const presetAssetRegistry = extractSpriteAssetRegistryFromSamplePreset(rawPreset);
1498-
const presetTitle = typeof rawPreset?.title === "string"
1499-
? rawPreset.title
1500-
: (typeof rawPreset?.payload?.title === "string" ? rawPreset.payload.title : "");
1507+
const presetTitle = typeof sampleTitleHint === "string" && sampleTitleHint.trim()
1508+
? sampleTitleHint.trim()
1509+
: (typeof rawPreset?.title === "string"
1510+
? rawPreset.title
1511+
: (typeof rawPreset?.payload?.title === "string" ? rawPreset.payload.title : ""));
15011512
state.assetRegistry = presetAssetRegistry
15021513
? sanitizeAssetRegistry(presetAssetRegistry)
15031514
: createAssetRegistry({ projectId: "sprite-project" });
@@ -1533,16 +1544,31 @@ async function tryLoadPresetFromQuery(state) {
15331544
return false;
15341545
}
15351546
const sampleId = String(searchParams.get("sampleId") || "").trim();
1547+
const sampleTitle = normalizeSampleLabel(searchParams.get("sampleTitle") || "");
1548+
setSampleSource(state, {
1549+
mode: "sample",
1550+
sampleId,
1551+
sampleTitle,
1552+
samplePresetPath
1553+
});
1554+
renderHud(state);
15361555
try {
15371556
const presetUrl = new URL(samplePresetPath, window.location.href);
15381557
const response = await fetch(presetUrl.toString(), { cache: "no-store" });
15391558
if (!response.ok) {
15401559
throw new Error(`Preset request failed (${response.status}).`);
15411560
}
15421561
const rawPreset = await response.json();
1543-
applySamplePreset(state, rawPreset, sampleId, samplePresetPath);
1562+
applySamplePreset(state, rawPreset, sampleId, samplePresetPath, sampleTitle);
15441563
return true;
15451564
} catch (error) {
1565+
setSampleSource(state, {
1566+
mode: "sample",
1567+
sampleId,
1568+
sampleTitle,
1569+
samplePresetPath
1570+
});
1571+
renderHud(state);
15461572
setStatus(state, `Preset load failed: ${error instanceof Error ? error.message : "unknown error"}`);
15471573
return false;
15481574
}
@@ -2189,6 +2215,7 @@ export function initializeSpriteEditorApp() {
21892215
applyRemediationButton: getRequiredElement("applyRemediationButton"),
21902216
saveAssetRegistryButton: getRequiredElement("saveAssetRegistryButton"),
21912217
saveProjectButton: getRequiredElement("saveProjectButton"),
2218+
sampleSourceDetailText: getRequiredElement("sampleSourceDetailText"),
21922219
sampleSourceText: getRequiredElement("sampleSourceText"),
21932220
statusBarText: getRequiredElement("statusBarText"),
21942221
statusText: getRequiredElement("statusText"),

tools/shared/platformShell.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,10 +783,15 @@ function initPlatformShell() {
783783

784784
const currentToolId = document.body.dataset.toolId || "";
785785
const currentTool = currentToolId ? getToolById(currentToolId) : null;
786+
const searchParams = typeof window !== "undefined"
787+
? new URLSearchParams(window.location.search)
788+
: new URLSearchParams();
789+
const launchedFromSamplePreset = searchParams.has("samplePresetPath");
786790

787791
if (currentToolId) {
788792
workspaceController = createWorkspaceSystemController({
789793
toolId: currentToolId,
794+
skipInitialToolStateApply: launchedFromSamplePreset,
790795
onChange(payload) {
791796
const manifest = payload?.manifest || {};
792797
const uiStateKey = JSON.stringify({

tools/shared/projectSystem.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export function createWorkspaceSystemController(options = {}) {
7171
const toolId = safeString(options.toolId, "");
7272
const toolEntry = getToolById(toolId);
7373
const isReadOnlyTool = toolEntry?.readOnly === true;
74+
const skipInitialToolStateApply = options.skipInitialToolStateApply === true;
7475
const onChange = typeof options.onChange === "function" ? options.onChange : () => {};
7576
const onStatus = typeof options.onStatus === "function" ? options.onStatus : () => {};
7677
const adapter = () => getProjectAdapter(toolId);
@@ -80,7 +81,7 @@ export function createWorkspaceSystemController(options = {}) {
8081
baselineHash: "",
8182
lastObservedHash: "",
8283
adapterReady: false,
83-
appliedInitialState: false
84+
appliedInitialState: skipInitialToolStateApply
8485
};
8586

8687
function serializeForDirtyComparison(manifest) {

0 commit comments

Comments
 (0)