Skip to content

Commit 495fdb1

Browse files
author
DavidQ
committed
games index: drive Class filter from engineClassesUsed class names (no path) and filter/search by engine class names
1 parent a74d82e commit 495fdb1

2 files changed

Lines changed: 45 additions & 292 deletions

File tree

games/index.render.js

Lines changed: 36 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@ function normalizeTag(value) {
1515
return normalizeToken(value).replace(/[_\s]+/g, "-").replace(/-+/g, "-");
1616
}
1717

18+
function isThemeEngineClass(value) {
19+
const normalized = normalize(value).replace(/\\/g, "/");
20+
if (!normalized) {
21+
return false;
22+
}
23+
const leaf = normalized.split("/").pop() || "";
24+
return /^theme/i.test(leaf);
25+
}
26+
27+
function toEngineClassName(value) {
28+
const normalized = normalize(value).replace(/\\/g, "/");
29+
if (!normalized) {
30+
return "";
31+
}
32+
const leaf = normalized.split("/").pop() || "";
33+
return normalize(leaf);
34+
}
35+
1836
function levelSortKey(level) {
1937
const value = normalize(level);
2038
const numericMatch = value.match(/Level\s+(\d+)/i);
@@ -60,54 +78,6 @@ function normalizeGameHref(value) {
6078
return href;
6179
}
6280

63-
function normalizePresetPath(value) {
64-
const normalized = normalize(value).replace(/\\/g, "/");
65-
if (!normalized || normalized.includes("..")) {
66-
return "";
67-
}
68-
if (normalized.startsWith("/samples/")) {
69-
return normalized;
70-
}
71-
if (normalized.startsWith("/games/")) {
72-
return normalized;
73-
}
74-
if (normalized.startsWith("samples/")) {
75-
return `/${normalized}`;
76-
}
77-
if (normalized.startsWith("games/")) {
78-
return `/${normalized}`;
79-
}
80-
if (normalized.startsWith("./samples/")) {
81-
return `/${normalized.slice(2)}`;
82-
}
83-
if (normalized.startsWith("./games/")) {
84-
return `/${normalized.slice(2)}`;
85-
}
86-
return "";
87-
}
88-
89-
function getExplicitRoundtripPresetPath(game, toolId) {
90-
const safeToolId = normalizeToken(toolId);
91-
if (!safeToolId) {
92-
return "";
93-
}
94-
const mappedEntries = asArray(game?.roundtripToolPresets);
95-
for (const entry of mappedEntries) {
96-
if (!entry || typeof entry !== "object") {
97-
continue;
98-
}
99-
const mappedToolId = normalizeToken(entry.toolId);
100-
if (mappedToolId !== safeToolId) {
101-
continue;
102-
}
103-
const presetPath = normalizePresetPath(entry.presetPath);
104-
if (presetPath) {
105-
return presetPath;
106-
}
107-
}
108-
return "";
109-
}
110-
11181
function buildWorkspaceManagerHref(gameId) {
11282
const normalizedGameId = normalize(gameId);
11383
return normalizedGameId
@@ -126,47 +96,6 @@ function buildToolTokens(toolHints, toolLabelMap) {
12696
.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: "base" }));
12797
}
12898

129-
function buildRoundtripLinks(game, toolRegistryMap) {
130-
const orderedToolHints = asArray(game?.toolHints)
131-
.map((entry) => normalizeToken(entry))
132-
.filter(Boolean)
133-
.filter((toolId) => toolId !== "workspace-manager");
134-
const dedupedToolHints = [...new Set(orderedToolHints)];
135-
const links = [];
136-
137-
dedupedToolHints.forEach((toolId) => {
138-
const tool = toolRegistryMap.get(toolId);
139-
if (!tool) {
140-
return;
141-
}
142-
const workspaceHref = "/tools/Workspace%20Manager/index.html";
143-
const query = new URLSearchParams();
144-
query.set("tool", tool.id);
145-
query.set("game", game.id);
146-
query.set("gameId", game.id);
147-
if (game.title) {
148-
query.set("gameTitle", game.title);
149-
}
150-
if (game.href) {
151-
query.set("gameHref", game.href);
152-
}
153-
const presetPath = getExplicitRoundtripPresetPath(game, tool.id);
154-
if (presetPath) {
155-
query.set("samplePresetPath", presetPath);
156-
const sampleIdMatch = /sample-(\d{4})-[^.]+\.json$/i.exec(presetPath);
157-
if (sampleIdMatch?.[1]) {
158-
query.set("sampleId", sampleIdMatch[1]);
159-
}
160-
}
161-
query.set("workspaceHref", buildWorkspaceManagerHref(game.id));
162-
const href = `${workspaceHref}?${query.toString()}`;
163-
const label = `Open ${normalize(tool.displayName) || normalize(tool.name) || toolId}`;
164-
links.push({ toolId, href, label });
165-
});
166-
167-
return links;
168-
}
169-
17099
function statusLabel(status) {
171100
switch (status) {
172101
case "playable":
@@ -200,7 +129,7 @@ function writePinnedSet(pinnedSet) {
200129
window.localStorage.setItem(GAMES_PINNED_KEY, JSON.stringify([...pinnedSet].sort()));
201130
}
202131

203-
function buildRows(metadata, pinnedSet, toolLabelMap, toolRegistryMap) {
132+
function buildRows(metadata, pinnedSet, toolLabelMap) {
204133
const rows = asArray(metadata?.games)
205134
.map((game) => {
206135
const id = normalize(game?.id);
@@ -211,19 +140,17 @@ function buildRows(metadata, pinnedSet, toolLabelMap, toolRegistryMap) {
211140
}
212141
const classValues = [...new Set(asArray(game?.classValues).map((value) => normalize(value)).filter(Boolean))]
213142
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
214-
const engineClasses = [...new Set(asArray(game?.engineClassesUsed).map((value) => normalize(value)).filter(Boolean))]
143+
const engineClasses = [...new Set(asArray(game?.engineClassesUsed)
144+
.map((value) => normalize(value))
145+
.filter(Boolean)
146+
.filter((value) => !isThemeEngineClass(value)))]
147+
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
148+
const engineClassNames = [...new Set(engineClasses.map((value) => toEngineClassName(value)).filter(Boolean))]
215149
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
216150
const tags = [...new Set(asArray(game?.tags).map((value) => normalizeTag(value)).filter(Boolean))]
217151
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
218152
const href = normalizeGameHref(game?.href);
219153
const toolTokens = buildToolTokens(game?.toolHints, toolLabelMap);
220-
const roundtripLinks = buildRoundtripLinks({
221-
id,
222-
title,
223-
href,
224-
toolHints: game?.toolHints,
225-
roundtripToolPresets: game?.roundtripToolPresets
226-
}, toolRegistryMap);
227154
return {
228155
id,
229156
title,
@@ -232,8 +159,8 @@ function buildRows(metadata, pinnedSet, toolLabelMap, toolRegistryMap) {
232159
status: normalize(game?.status) || "planned",
233160
classValues,
234161
engineClasses,
162+
engineClassNames,
235163
toolTokens,
236-
roundtripLinks,
237164
tags,
238165
preview: normalize(game?.preview),
239166
href,
@@ -248,7 +175,7 @@ function buildRows(metadata, pinnedSet, toolLabelMap, toolRegistryMap) {
248175
.filter(Boolean);
249176

250177
const levels = [...new Set(rows.map((row) => row.level))].sort(sortLevels);
251-
const classes = [...new Set(rows.flatMap((row) => row.classValues))].sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
178+
const classes = [...new Set(rows.flatMap((row) => row.engineClassNames))].sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
252179
const tools = [...new Map(
253180
rows.flatMap((row) => row.toolTokens).map((token) => [token.value, token.label])
254181
).entries()]
@@ -269,7 +196,7 @@ function filterRows(rows, state) {
269196
if (state.level && row.level !== state.level) {
270197
return false;
271198
}
272-
if (state.classValue && !row.classValues.includes(state.classValue)) {
199+
if (state.classValue && !row.engineClassNames.includes(state.classValue)) {
273200
return false;
274201
}
275202
if (state.toolId && !row.toolTokens.some((token) => token.value === state.toolId)) {
@@ -282,7 +209,7 @@ function filterRows(rows, state) {
282209
return true;
283210
}
284211
const toolText = row.toolTokens.map((token) => `${token.label} ${token.value}`).join(" ");
285-
const haystack = `${row.level} ${row.title} ${row.description} ${row.classValues.join(" ")} ${toolText} ${row.tags.join(" ")}`.toLowerCase();
212+
const haystack = `${row.level} ${row.title} ${row.description} ${row.engineClassNames.join(" ")} ${toolText} ${row.tags.join(" ")}`.toLowerCase();
286213
return haystack.includes(query);
287214
});
288215
}
@@ -327,6 +254,9 @@ function renderCard(row, instanceKey = "main") {
327254
].filter(Boolean).join("");
328255

329256
const classEntries = row.engineClasses;
257+
const toolsUsedText = row.toolTokens.length > 0
258+
? row.toolTokens.map((token) => token.label).join(", ")
259+
: "none";
330260
const tagText = row.tags.length > 0 ? row.tags.join(", ") : "none";
331261
const workspaceSection = row.workspaceHref
332262
? `
@@ -343,6 +273,7 @@ function renderCard(row, instanceKey = "main") {
343273
<p>${escapeHtml(row.description)}</p>
344274
${workspaceSection}
345275
<p>Tags: ${escapeHtml(tagText)}</p>
276+
<p>Tools Used: ${escapeHtml(toolsUsedText)}</p>
346277
<section class="game-tool-roundtrip">
347278
<h4>Engine Classes Used</h4>
348279
${classEntries.length > 0
@@ -436,16 +367,10 @@ export async function initGamesIndex() {
436367
.map((tool) => [normalizeToken(tool.id), normalize(tool.displayName) || normalize(tool.name) || normalize(tool.id)])
437368
.filter((entry) => entry[0] && entry[1])
438369
);
439-
const toolRegistryMap = new Map(
440-
toolRegistry
441-
.filter((tool) => tool.id !== "workspace-manager")
442-
.map((tool) => [normalizeToken(tool.id), tool])
443-
.filter((entry) => entry[0] && entry[1])
444-
);
445370
let pinnedSet = readPinnedSet();
446-
let model = buildRows(metadata, pinnedSet, toolLabelMap, toolRegistryMap);
371+
let model = buildRows(metadata, pinnedSet, toolLabelMap);
447372
setSelect(levelSelect, model.levels, (value) => value);
448-
setSelect(classSelect, model.classes, (value) => value.split("/").at(-1) || value);
373+
setSelect(classSelect, model.classes, (value) => value);
449374
setSelect(toolSelect, model.tools.map((entry) => entry.value), (value) => {
450375
const found = model.tools.find((entry) => entry.value === value);
451376
return found?.label || value;
@@ -457,7 +382,7 @@ export async function initGamesIndex() {
457382
}
458383

459384
const apply = () => {
460-
model = buildRows(metadata, pinnedSet, toolLabelMap, toolRegistryMap);
385+
model = buildRows(metadata, pinnedSet, toolLabelMap);
461386
const state = {
462387
level: normalize(levelSelect.value),
463388
classValue: normalize(classSelect.value),

0 commit comments

Comments
 (0)