Skip to content

Commit 88e4716

Browse files
author
DavidQ
committed
Fix workspace handoff sync: remove source-tool-state lock for tool surfaces and force Palette Browser to follow shared palette handoff on launch/update.
1 parent 47ff31b commit 88e4716

2 files changed

Lines changed: 122 additions & 0 deletions

File tree

games/shared/workspaceGameBoot.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { enforceWorkspaceGameLaunch } from "/games/shared/workspaceGameLaunchGuard.js";
22
import { hydrateWorkspaceGameRuntime } from "/games/shared/workspaceGameRuntimeHydrator.js";
3+
import { hydrateWorkspaceGameMetadata } from "/games/shared/workspaceGameMetadataHydrator.js";
34

45
function normalizeGameId(value) {
56
return typeof value === "string" ? value.trim() : "";
@@ -10,6 +11,7 @@ export function bootWorkspaceGame(gameId) {
1011
if (!normalizedGameId) {
1112
return null;
1213
}
14+
hydrateWorkspaceGameMetadata(normalizedGameId);
1315
enforceWorkspaceGameLaunch(normalizedGameId);
1416
return hydrateWorkspaceGameRuntime(normalizedGameId);
1517
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
const GAMES_METADATA_PATH = "/games/metadata/games.index.metadata.json";
2+
3+
let gamesMetadataPromise = null;
4+
5+
function normalizeText(value) {
6+
return typeof value === "string" ? value.trim() : "";
7+
}
8+
9+
function normalizeToken(value) {
10+
return normalizeText(value).toLowerCase();
11+
}
12+
13+
function whenDocumentReady(documentRef, callback) {
14+
if (!documentRef) {
15+
return;
16+
}
17+
if (documentRef.readyState === "loading") {
18+
documentRef.addEventListener("DOMContentLoaded", callback, { once: true });
19+
return;
20+
}
21+
callback();
22+
}
23+
24+
function getMetadataPromise() {
25+
if (gamesMetadataPromise) {
26+
return gamesMetadataPromise;
27+
}
28+
gamesMetadataPromise = fetch(GAMES_METADATA_PATH, { cache: "no-store" })
29+
.then((response) => (response.ok ? response.json() : null))
30+
.catch(() => null);
31+
return gamesMetadataPromise;
32+
}
33+
34+
function findGameMetadataEntry(metadata, gameId) {
35+
const safeGameId = normalizeToken(gameId);
36+
if (!safeGameId || !metadata || typeof metadata !== "object") {
37+
return null;
38+
}
39+
const games = Array.isArray(metadata.games) ? metadata.games : [];
40+
return games.find((entry) => normalizeToken(entry?.id) === safeGameId) || null;
41+
}
42+
43+
function toEngineClasses(value) {
44+
const entries = Array.isArray(value) ? value : [];
45+
return [...new Set(entries.map((entry) => normalizeText(entry)).filter(Boolean))];
46+
}
47+
48+
function findOrCreateEngineClassesSection(documentRef) {
49+
const main = documentRef.querySelector("main");
50+
if (!(main instanceof HTMLElement)) {
51+
return null;
52+
}
53+
54+
const sections = Array.from(main.querySelectorAll("section"));
55+
for (const section of sections) {
56+
if (!(section instanceof HTMLElement)) {
57+
continue;
58+
}
59+
const heading = section.querySelector("h3");
60+
const headingText = normalizeToken(heading?.textContent);
61+
if (headingText.includes("engine classes used") || headingText.includes("engine + debug classes used")) {
62+
return section;
63+
}
64+
}
65+
66+
const nextSection = documentRef.createElement("section");
67+
const heading = documentRef.createElement("h3");
68+
heading.textContent = "Engine Classes Used";
69+
const list = documentRef.createElement("ul");
70+
nextSection.append(heading, list);
71+
main.appendChild(nextSection);
72+
return nextSection;
73+
}
74+
75+
function renderEngineClasses(section, classes, documentRef) {
76+
const heading = section.querySelector("h3") || documentRef.createElement("h3");
77+
heading.textContent = "Engine Classes Used";
78+
if (!heading.parentElement) {
79+
section.prepend(heading);
80+
}
81+
82+
const list = section.querySelector("ul") || documentRef.createElement("ul");
83+
list.innerHTML = "";
84+
if (classes.length === 0) {
85+
const item = documentRef.createElement("li");
86+
item.textContent = "none";
87+
list.appendChild(item);
88+
} else {
89+
classes.forEach((value) => {
90+
const item = documentRef.createElement("li");
91+
item.textContent = value;
92+
list.appendChild(item);
93+
});
94+
}
95+
if (!list.parentElement) {
96+
section.appendChild(list);
97+
}
98+
}
99+
100+
export function hydrateWorkspaceGameMetadata(gameId, documentRef = globalThis.document ?? null) {
101+
const safeGameId = normalizeText(gameId);
102+
if (!safeGameId || !documentRef) {
103+
return;
104+
}
105+
106+
whenDocumentReady(documentRef, () => {
107+
void getMetadataPromise().then((metadata) => {
108+
const entry = findGameMetadataEntry(metadata, safeGameId);
109+
if (!entry) {
110+
return;
111+
}
112+
const section = findOrCreateEngineClassesSection(documentRef);
113+
if (!section) {
114+
return;
115+
}
116+
const classes = toEngineClasses(entry.engineClassesUsed);
117+
renderEngineClasses(section, classes, documentRef);
118+
});
119+
});
120+
}

0 commit comments

Comments
 (0)