Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions assets/theme-v2/css/status.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,7 @@
.toolbox-status-bar {
--toolbox-status-bar-height: var(--space-52);
--toolbox-status-game-max: 220px;
--toolbox-status-progress-max: 34vw;
width: 100%;
min-block-size: var(--toolbox-status-bar-height);
border-block: var(--border-standard);
Expand All@@ -50,7 +51,7 @@
margin: var(--space-0) auto;
padding: var(--space-10) var(--space-0);
display: grid;
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-16);
align-items: center
}
Expand DownExpand Up@@ -90,6 +91,19 @@
overflow-wrap: anywhere
}

.toolbox-status-bar__progress {
min-width: var(--space-0);
max-width: var(--toolbox-status-progress-max);
margin: var(--space-0);
color: var(--muted);
font-size: var(--font-size-sm);
line-height: var(--line-height-tight);
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap
}

.toolbox-status-bar[data-selected-game-state="active"] {
border-color: color-mix(in srgb, var(--green) 52%, var(--line))
}
Expand DownExpand Up@@ -249,16 +263,21 @@ body.tool-focus-mode .tool-center-panel {
@media (max-width: 720px) {
.toolbox-status-bar__inner {
width: var(--container-width);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-10);
text-align: center
}

.toolbox-status-bar__game-name {
max-width: 34vw
max-width: 24vw
}

.toolbox-status-bar__message {
.toolbox-status-bar__message,
.toolbox-status-bar__progress {
font-size: var(--font-size-sm)
}

.toolbox-status-bar__progress {
max-width: 30vw
}
}
139 changes: 138 additions & 1 deletion assets/theme-v2/js/toolbox-status-bar.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
import { readGameJourneyCompletionMetrics } from "/src/api/game-journey-completion-api-client.js";
import { createServerRepositoryClient } from "/src/api/server-api-client.js";
import { getToolBySlug } from "/src/shared/toolbox/tool-metadata-inventory.js";

const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]);
const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]";
const TOOL_PROGRESS_BUCKET_BY_SLUG = Object.freeze({
"achievements": "Progression",
"assets": "Graphics",
"audio": "Audio",
"audio-effects": "Audio",
"characters": "Objects",
"colors": "Graphics",
"community": "Share",
"controls": "Controls",
"events": "Rules",
"game-configuration": "Create",
"game-design": "Design",
"game-hub": "Create",
"game-testing": "Play Test",
"hitboxes": "Objects",
"idea-board": "Idea",
"input-mapping-v2": "Controls",
"marketplace": "Share",
"messages": "Interface",
"music": "Audio",
"objects": "Objects",
"publish": "Publish",
"ratings": "Share",
"sprites": "Graphics",
"tags": "Progression",
"text-to-speech": "Audio",
"videos": "Graphics",
"voices": "Audio",
"worlds": "Worlds",
});

let repository = null;
let messageObserver = null;
Expand DownExpand Up@@ -101,7 +133,10 @@ function createStatusBar() {
message.setAttribute("role", "status");
center.append(message);

inner.append(game, center);
const progress = createText("p", "toolbox-status-bar__progress", "toolboxStatusProgress");
progress.setAttribute("aria-label", "Tool and journey progress");

inner.append(game, center, progress);
bar.append(inner);
return bar;
}
Expand DownExpand Up@@ -233,6 +268,107 @@ function classifyToolContext(messageText, state, required) {
return { kind: "action" };
}

function normalizeTextKey(value) {
return String(value || "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "");
}

function formatToolSlug(slug) {
return String(slug || "Tool")
.split(/[-_\s]+/)
.filter(Boolean)
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
.join(" ") || "Tool";
}

function currentToolContext() {
const slug = toolSlugFromPath(mountOptions.pagePath);
const tool = getToolBySlug(slug);
return {
label: tool?.shortLabel || tool?.displayName || tool?.name || formatToolSlug(slug),
slug,
};
}

function normalizeProgressRecord(record) {
const total = Math.max(0, Number(record?.plannedCount) || 0);
const complete = Math.max(0, Math.min(Number(record?.completedCount) || 0, total));
const percent = Number.isFinite(Number(record?.percentComplete))
? Math.max(0, Math.min(Number(record.percentComplete), 100))
: total > 0
? Math.round((complete / total) * 100)
: 0;
return {
complete,
percent,
total,
};
}

function formatProgressRecord(label, record) {
const progress = normalizeProgressRecord(record);
return `${label} ${progress.complete}/${progress.total} (${progress.percent}%)`;
}

function findMetricForCurrentTool(snapshot, toolContext) {
if (toolContext.slug === "game-journey") {
return snapshot;
}

const records = Array.isArray(snapshot?.records) ? snapshot.records : [];
const explicitBucketName = TOOL_PROGRESS_BUCKET_BY_SLUG[toolContext.slug];
const explicitBucket = normalizeTextKey(explicitBucketName);
const toolLabel = normalizeTextKey(toolContext.label);
const toolSlug = normalizeTextKey(toolContext.slug);

return records.find((metric) => {
const bucketName = normalizeTextKey(metric?.bucketName);
return bucketName && (
bucketName === explicitBucket ||
bucketName === toolLabel ||
bucketName === toolSlug
);
}) || null;
}

function resolveProgressContext() {
const toolContext = currentToolContext();
const snapshot = readGameJourneyCompletionMetrics();
const currentMetric = findMetricForCurrentTool(snapshot, toolContext);
const journeyText = formatProgressRecord("Journey", snapshot);

if (!currentMetric) {
return {
state: "unmapped",
text: `${toolContext.label} progress unavailable | ${journeyText}`,
};
}

return {
state: "active",
text: `${formatProgressRecord(toolContext.label, currentMetric)} | ${journeyText}`,
};
}

function renderProgressContext(bar) {
const progress = bar.querySelector("[data-toolbox-status-progress]");
if (!progress) {
return;
}

try {
const context = resolveProgressContext();
bar.dataset.toolboxProgressState = context.state;
progress.textContent = context.text;
progress.removeAttribute("title");
} catch (error) {
bar.dataset.toolboxProgressState = "error";
progress.textContent = "Progress unavailable";
progress.removeAttribute("title");
}
}

function renderSelectedGame(bar, selectedGame, state, messageText) {
const required = pageRequiresSelectedGame();
const name = bar.querySelector("[data-toolbox-selected-game-name]");
Expand All@@ -247,6 +383,7 @@ function renderSelectedGame(bar, selectedGame, state, messageText) {
bar.dataset.selectedGameState = state;
bar.dataset.selectedGameRequired = String(required);
bar.dataset.toolboxStatusContextKind = context.kind;
renderProgressContext(bar);

if (selectedGame) {
name.textContent = selectedGame.name;
Expand Down
56 changes: 30 additions & 26 deletions docs_build/dev/BUILD_PR.md
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
# PR_26175_ALFA_009-status-bar-single-row-rebuild
# PR_26175_ALFA_011-status-bar-journey-progress-context

## Purpose
Rebuild the shared toolbox status bar on current `main` so it is a single-row creator context bar.
Add right-anchored progress context to the shared toolbox status bar using the existing Game Journey completion metrics/API pipeline.

## Source Of Truth
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_009-status-bar-single-row-rebuild`.
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_011-status-bar-journey-progress-context`.

## Exact Scope
- Display only the selected Game Hub game name on the left side of the toolbox status bar.
- Display only the current status message in the center of the toolbox status bar.
- Remove visible status bar labels: `Selected Game Name`, `Selected Game Purpose`, `Save State`, `Tool Action`, `Warning`, and `Error`.
- Remove selected-game purpose from the visible status bar.
- Preserve normal status bar placement above the footer.
- Remove extra status bar/footer spacing so the shared footer top padding resolves to `0px`.
- Preserve fullscreen/tool display mode bottom anchoring.
- Ensure fullscreen center scrollable content stops above the fixed status bar.
- Ensure fullscreen tool content is not hidden behind the status bar.
- Preserve Idea Board selected-game filtering exclusion.
- Preserve Game Hub as selected-game owner through the existing repository contract.
- Preserve the ALFA_009 single-row toolbox status bar behavior:
- left side displays only the selected Game Hub game name.
- center displays only the current status message.
- Add right-anchored progress text in this format:
- `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`
- Use existing Game Journey completion metrics/API pipeline for Journey totals.
- Derive current-tool progress from the existing completion metrics record that matches the current toolbox tool/section.
- Do not add new storage.
- Do not use browser-owned authoritative progress data.
- Preserve fullscreen bottom anchoring and existing fullscreen content bottom reserve.
- Preserve normal placement above the footer.
- Use shared Theme V2 CSS/classes only.
- Update targeted Playwright coverage for the single-row status bar, footer spacing, and fullscreen bottom reserve.
- Update targeted Playwright coverage for the right-anchored progress text and existing left/center behavior.

## Exact Targets
- `docs_build/dev/BUILD_PR.md`
- `assets/theme-v2/js/toolbox-status-bar.js`
- `assets/theme-v2/css/status.css`
- `assets/theme-v2/css/layout.css`
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_requirements-checklist.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md`
- `docs_build/dev/reports/codex_review.diff`
- `docs_build/dev/reports/codex_changed_files.txt`

## Evidence Sources
- `assets/js/shared/game-journey-api-client.js`
- `src/api/game-journey-completion-api-client.js`
- `src/dev-runtime/server/local-api-router.mjs`
- `src/dev-runtime/persistence/game-journey-completion-metrics-store.mjs`

## Out Of Scope
- No merge of PR #120.
- No reuse of stale ALFA_003 branch.
- No Game Journey API/service/repository contract changes.
- No new persistence/storage.
- No browser-owned product data as source of truth.
- No silent fallback data.
- No environment/server details in the status bar.
- No selected game purpose in the visible status bar.
- No visible status category labels in the status bar.
- No status action links in the visible status bar.
- No large banners.
- No modal messages or modal-style status messages.
- No row highlights.
- No API/service/repository contract changes.
- No browser-owned product data as source of truth.
- No inline styles, style blocks, or page-local CSS.
- No engine core changes.
- No `start_of_day` folder changes.
Expand All@@ -59,12 +63,12 @@ npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
Also verify changed source does not introduce inline styles or style blocks:

```powershell
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css assets/theme-v2/css/layout.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
```

## Artifact
Create repo-structured delta ZIP:

```text
tmp/PR_26175_ALFA_009-status-bar-single-row-rebuild_delta.zip
tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
# PR_26175_ALFA_011-status-bar-journey-progress-context Report

## Status
PASS

Added right-anchored progress context to the shared toolbox status bar while preserving the ALFA_009 left and center behavior.

## Changes
- `assets/theme-v2/js/toolbox-status-bar.js`
- Imports the existing Game Journey completion metrics API client at line 1.
- Adds current-tool-to-Journey-bucket matching at lines 7-35.
- Adds the right progress node at line 136.
- Formats progress as `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` at lines 309-350.
- Renders visible unavailable progress state without raw environment/server details at lines 354-370.
- `assets/theme-v2/css/status.css`
- Adds a three-column shared status bar grid at line 54.
- Adds right-aligned single-line progress styling at lines 94-106.
- Preserves existing fullscreen anchoring and bottom reserve rules at lines 119-150.
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- Routes `/api/game-journey/completion-metrics` through the existing server API response shape at lines 76-83.
- Verifies Game Design progress at lines 239 and 253.
- Verifies the requested Objects example at lines 275 and 279.
- Verifies right anchoring at lines 260 and 280.
- Verifies Game Hub, missing-game, Idea Board, and fullscreen behavior at lines 304, 328, 353, 371, and 398.

## Contract Notes
- No storage was added.
- No API/service/repository contract was changed.
- Progress data is read from the existing Game Journey completion metrics API pipeline.
- Browser state is not used as authoritative progress data.
- Existing selected-game ownership and left/center status bar behavior are preserved.

## Validation
- PASS: `npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1`
- PASS: `rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`

## Artifact
- `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
# PR_26175_ALFA_011 Requirements Checklist

| Requirement | Status | Evidence |
| --- | --- | --- |
| Implement right-anchored progress in the shared toolbox status bar. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:136`, `assets/theme-v2/css/status.css:94`. |
| Use format `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:309` and `:350`; tests at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:239`, `:275`. |
| Use existing Game Journey completion metrics/API pipeline. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:1`, `:337`; tests route `/api/game-journey/completion-metrics` at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:76`. |
| Do not add new storage. | PASS | No persistence files changed. |
| Do not use browser-owned authoritative progress data. | PASS | Progress is read from `readGameJourneyCompletionMetrics()` and not local/session storage. |
| Keep existing left status bar behavior from ALFA_009. | PASS | Selected game assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:237`, `:302`, `:391`. |
| Keep existing center status bar behavior from ALFA_009. | PASS | Message assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:238`, `:303`, `:370`, `:397`. |
| Preserve fullscreen bottom anchoring. | PASS | Existing CSS rules remain at `assets/theme-v2/css/status.css:119`; test coverage at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:315`. |
| Preserve normal placement above footer. | PASS | Existing status bar placement test remains at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:224`. |
| No inline styles, style blocks, or page-local CSS. | PASS | Style scan returned no matches. |
| Use Theme V2 shared CSS/classes. | PASS | Styling changes are limited to `assets/theme-v2/css/status.css`. |
| Update targeted Playwright coverage. | PASS | `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` now validates right progress text and anchoring. |
| Produce required reports and repo-structured ZIP. | PASS | PR reports added and ZIP target is `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`. |
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions assets/theme-v2/css/status.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,7 @@
.toolbox-status-bar {
--toolbox-status-bar-height: var(--space-52);
--toolbox-status-game-max: 220px;
--toolbox-status-progress-max: 34vw;
width: 100%;
min-block-size: var(--toolbox-status-bar-height);
border-block: var(--border-standard);
Expand All@@ -50,7 +51,7 @@
margin: var(--space-0) auto;
padding: var(--space-10) var(--space-0);
display: grid;
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-16);
align-items: center
}
Expand DownExpand Up@@ -90,6 +91,19 @@
overflow-wrap: anywhere
}

.toolbox-status-bar__progress {
min-width: var(--space-0);
max-width: var(--toolbox-status-progress-max);
margin: var(--space-0);
color: var(--muted);
font-size: var(--font-size-sm);
line-height: var(--line-height-tight);
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap
}

.toolbox-status-bar[data-selected-game-state="active"] {
border-color: color-mix(in srgb, var(--green) 52%, var(--line))
}
Expand DownExpand Up@@ -249,16 +263,21 @@ body.tool-focus-mode .tool-center-panel {
@media (max-width: 720px) {
.toolbox-status-bar__inner {
width: var(--container-width);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-10);
text-align: center
}

.toolbox-status-bar__game-name {
max-width: 34vw
max-width: 24vw
}

.toolbox-status-bar__message {
.toolbox-status-bar__message,
.toolbox-status-bar__progress {
font-size: var(--font-size-sm)
}

.toolbox-status-bar__progress {
max-width: 30vw
}
}
139 changes: 138 additions & 1 deletion assets/theme-v2/js/toolbox-status-bar.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
import { readGameJourneyCompletionMetrics } from "/src/api/game-journey-completion-api-client.js";
import { createServerRepositoryClient } from "/src/api/server-api-client.js";
import { getToolBySlug } from "/src/shared/toolbox/tool-metadata-inventory.js";

const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]);
const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]";
const TOOL_PROGRESS_BUCKET_BY_SLUG = Object.freeze({
"achievements": "Progression",
"assets": "Graphics",
"audio": "Audio",
"audio-effects": "Audio",
"characters": "Objects",
"colors": "Graphics",
"community": "Share",
"controls": "Controls",
"events": "Rules",
"game-configuration": "Create",
"game-design": "Design",
"game-hub": "Create",
"game-testing": "Play Test",
"hitboxes": "Objects",
"idea-board": "Idea",
"input-mapping-v2": "Controls",
"marketplace": "Share",
"messages": "Interface",
"music": "Audio",
"objects": "Objects",
"publish": "Publish",
"ratings": "Share",
"sprites": "Graphics",
"tags": "Progression",
"text-to-speech": "Audio",
"videos": "Graphics",
"voices": "Audio",
"worlds": "Worlds",
});

let repository = null;
let messageObserver = null;
Expand DownExpand Up@@ -101,7 +133,10 @@ function createStatusBar() {
message.setAttribute("role", "status");
center.append(message);

inner.append(game, center);
const progress = createText("p", "toolbox-status-bar__progress", "toolboxStatusProgress");
progress.setAttribute("aria-label", "Tool and journey progress");

inner.append(game, center, progress);
bar.append(inner);
return bar;
}
Expand DownExpand Up@@ -233,6 +268,107 @@ function classifyToolContext(messageText, state, required) {
return { kind: "action" };
}

function normalizeTextKey(value) {
return String(value || "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "");
}

function formatToolSlug(slug) {
return String(slug || "Tool")
.split(/[-_\s]+/)
.filter(Boolean)
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
.join(" ") || "Tool";
}

function currentToolContext() {
const slug = toolSlugFromPath(mountOptions.pagePath);
const tool = getToolBySlug(slug);
return {
label: tool?.shortLabel || tool?.displayName || tool?.name || formatToolSlug(slug),
slug,
};
}

function normalizeProgressRecord(record) {
const total = Math.max(0, Number(record?.plannedCount) || 0);
const complete = Math.max(0, Math.min(Number(record?.completedCount) || 0, total));
const percent = Number.isFinite(Number(record?.percentComplete))
? Math.max(0, Math.min(Number(record.percentComplete), 100))
: total > 0
? Math.round((complete / total) * 100)
: 0;
return {
complete,
percent,
total,
};
}

function formatProgressRecord(label, record) {
const progress = normalizeProgressRecord(record);
return `${label} ${progress.complete}/${progress.total} (${progress.percent}%)`;
}

function findMetricForCurrentTool(snapshot, toolContext) {
if (toolContext.slug === "game-journey") {
return snapshot;
}

const records = Array.isArray(snapshot?.records) ? snapshot.records : [];
const explicitBucketName = TOOL_PROGRESS_BUCKET_BY_SLUG[toolContext.slug];
const explicitBucket = normalizeTextKey(explicitBucketName);
const toolLabel = normalizeTextKey(toolContext.label);
const toolSlug = normalizeTextKey(toolContext.slug);

return records.find((metric) => {
const bucketName = normalizeTextKey(metric?.bucketName);
return bucketName && (
bucketName === explicitBucket ||
bucketName === toolLabel ||
bucketName === toolSlug
);
}) || null;
}

function resolveProgressContext() {
const toolContext = currentToolContext();
const snapshot = readGameJourneyCompletionMetrics();
const currentMetric = findMetricForCurrentTool(snapshot, toolContext);
const journeyText = formatProgressRecord("Journey", snapshot);

if (!currentMetric) {
return {
state: "unmapped",
text: `${toolContext.label} progress unavailable | ${journeyText}`,
};
}

return {
state: "active",
text: `${formatProgressRecord(toolContext.label, currentMetric)} | ${journeyText}`,
};
}

function renderProgressContext(bar) {
const progress = bar.querySelector("[data-toolbox-status-progress]");
if (!progress) {
return;
}

try {
const context = resolveProgressContext();
bar.dataset.toolboxProgressState = context.state;
progress.textContent = context.text;
progress.removeAttribute("title");
} catch (error) {
bar.dataset.toolboxProgressState = "error";
progress.textContent = "Progress unavailable";
progress.removeAttribute("title");
}
}

function renderSelectedGame(bar, selectedGame, state, messageText) {
const required = pageRequiresSelectedGame();
const name = bar.querySelector("[data-toolbox-selected-game-name]");
Expand All@@ -247,6 +383,7 @@ function renderSelectedGame(bar, selectedGame, state, messageText) {
bar.dataset.selectedGameState = state;
bar.dataset.selectedGameRequired = String(required);
bar.dataset.toolboxStatusContextKind = context.kind;
renderProgressContext(bar);

if (selectedGame) {
name.textContent = selectedGame.name;
Expand Down
56 changes: 30 additions & 26 deletions docs_build/dev/BUILD_PR.md
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
# PR_26175_ALFA_009-status-bar-single-row-rebuild
# PR_26175_ALFA_011-status-bar-journey-progress-context

## Purpose
Rebuild the shared toolbox status bar on current `main` so it is a single-row creator context bar.
Add right-anchored progress context to the shared toolbox status bar using the existing Game Journey completion metrics/API pipeline.

## Source Of Truth
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_009-status-bar-single-row-rebuild`.
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_011-status-bar-journey-progress-context`.

## Exact Scope
- Display only the selected Game Hub game name on the left side of the toolbox status bar.
- Display only the current status message in the center of the toolbox status bar.
- Remove visible status bar labels: `Selected Game Name`, `Selected Game Purpose`, `Save State`, `Tool Action`, `Warning`, and `Error`.
- Remove selected-game purpose from the visible status bar.
- Preserve normal status bar placement above the footer.
- Remove extra status bar/footer spacing so the shared footer top padding resolves to `0px`.
- Preserve fullscreen/tool display mode bottom anchoring.
- Ensure fullscreen center scrollable content stops above the fixed status bar.
- Ensure fullscreen tool content is not hidden behind the status bar.
- Preserve Idea Board selected-game filtering exclusion.
- Preserve Game Hub as selected-game owner through the existing repository contract.
- Preserve the ALFA_009 single-row toolbox status bar behavior:
- left side displays only the selected Game Hub game name.
- center displays only the current status message.
- Add right-anchored progress text in this format:
- `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`
- Use existing Game Journey completion metrics/API pipeline for Journey totals.
- Derive current-tool progress from the existing completion metrics record that matches the current toolbox tool/section.
- Do not add new storage.
- Do not use browser-owned authoritative progress data.
- Preserve fullscreen bottom anchoring and existing fullscreen content bottom reserve.
- Preserve normal placement above the footer.
- Use shared Theme V2 CSS/classes only.
- Update targeted Playwright coverage for the single-row status bar, footer spacing, and fullscreen bottom reserve.
- Update targeted Playwright coverage for the right-anchored progress text and existing left/center behavior.

## Exact Targets
- `docs_build/dev/BUILD_PR.md`
- `assets/theme-v2/js/toolbox-status-bar.js`
- `assets/theme-v2/css/status.css`
- `assets/theme-v2/css/layout.css`
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_requirements-checklist.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md`
- `docs_build/dev/reports/codex_review.diff`
- `docs_build/dev/reports/codex_changed_files.txt`

## Evidence Sources
- `assets/js/shared/game-journey-api-client.js`
- `src/api/game-journey-completion-api-client.js`
- `src/dev-runtime/server/local-api-router.mjs`
- `src/dev-runtime/persistence/game-journey-completion-metrics-store.mjs`

## Out Of Scope
- No merge of PR #120.
- No reuse of stale ALFA_003 branch.
- No Game Journey API/service/repository contract changes.
- No new persistence/storage.
- No browser-owned product data as source of truth.
- No silent fallback data.
- No environment/server details in the status bar.
- No selected game purpose in the visible status bar.
- No visible status category labels in the status bar.
- No status action links in the visible status bar.
- No large banners.
- No modal messages or modal-style status messages.
- No row highlights.
- No API/service/repository contract changes.
- No browser-owned product data as source of truth.
- No inline styles, style blocks, or page-local CSS.
- No engine core changes.
- No `start_of_day` folder changes.
Expand All@@ -59,12 +63,12 @@ npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
Also verify changed source does not introduce inline styles or style blocks:

```powershell
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css assets/theme-v2/css/layout.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
```

## Artifact
Create repo-structured delta ZIP:

```text
tmp/PR_26175_ALFA_009-status-bar-single-row-rebuild_delta.zip
tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
# PR_26175_ALFA_011-status-bar-journey-progress-context Report

## Status
PASS

Added right-anchored progress context to the shared toolbox status bar while preserving the ALFA_009 left and center behavior.

## Changes
- `assets/theme-v2/js/toolbox-status-bar.js`
- Imports the existing Game Journey completion metrics API client at line 1.
- Adds current-tool-to-Journey-bucket matching at lines 7-35.
- Adds the right progress node at line 136.
- Formats progress as `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` at lines 309-350.
- Renders visible unavailable progress state without raw environment/server details at lines 354-370.
- `assets/theme-v2/css/status.css`
- Adds a three-column shared status bar grid at line 54.
- Adds right-aligned single-line progress styling at lines 94-106.
- Preserves existing fullscreen anchoring and bottom reserve rules at lines 119-150.
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- Routes `/api/game-journey/completion-metrics` through the existing server API response shape at lines 76-83.
- Verifies Game Design progress at lines 239 and 253.
- Verifies the requested Objects example at lines 275 and 279.
- Verifies right anchoring at lines 260 and 280.
- Verifies Game Hub, missing-game, Idea Board, and fullscreen behavior at lines 304, 328, 353, 371, and 398.

## Contract Notes
- No storage was added.
- No API/service/repository contract was changed.
- Progress data is read from the existing Game Journey completion metrics API pipeline.
- Browser state is not used as authoritative progress data.
- Existing selected-game ownership and left/center status bar behavior are preserved.

## Validation
- PASS: `npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1`
- PASS: `rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`

## Artifact
- `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
# PR_26175_ALFA_011 Requirements Checklist

| Requirement | Status | Evidence |
| --- | --- | --- |
| Implement right-anchored progress in the shared toolbox status bar. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:136`, `assets/theme-v2/css/status.css:94`. |
| Use format `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:309` and `:350`; tests at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:239`, `:275`. |
| Use existing Game Journey completion metrics/API pipeline. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:1`, `:337`; tests route `/api/game-journey/completion-metrics` at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:76`. |
| Do not add new storage. | PASS | No persistence files changed. |
| Do not use browser-owned authoritative progress data. | PASS | Progress is read from `readGameJourneyCompletionMetrics()` and not local/session storage. |
| Keep existing left status bar behavior from ALFA_009. | PASS | Selected game assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:237`, `:302`, `:391`. |
| Keep existing center status bar behavior from ALFA_009. | PASS | Message assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:238`, `:303`, `:370`, `:397`. |
| Preserve fullscreen bottom anchoring. | PASS | Existing CSS rules remain at `assets/theme-v2/css/status.css:119`; test coverage at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:315`. |
| Preserve normal placement above footer. | PASS | Existing status bar placement test remains at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:224`. |
| No inline styles, style blocks, or page-local CSS. | PASS | Style scan returned no matches. |
| Use Theme V2 shared CSS/classes. | PASS | Styling changes are limited to `assets/theme-v2/css/status.css`. |
| Update targeted Playwright coverage. | PASS | `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` now validates right progress text and anchoring. |
| Produce required reports and repo-structured ZIP. | PASS | PR reports added and ZIP target is `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`. |
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions assets/theme-v2/css/status.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,7 @@
.toolbox-status-bar {
--toolbox-status-bar-height: var(--space-52);
--toolbox-status-game-max: 220px;
--toolbox-status-progress-max: 34vw;
width: 100%;
min-block-size: var(--toolbox-status-bar-height);
border-block: var(--border-standard);
Expand All@@ -50,7 +51,7 @@
margin: var(--space-0) auto;
padding: var(--space-10) var(--space-0);
display: grid;
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-16);
align-items: center
}
Expand DownExpand Up@@ -90,6 +91,19 @@
overflow-wrap: anywhere
}

.toolbox-status-bar__progress {
min-width: var(--space-0);
max-width: var(--toolbox-status-progress-max);
margin: var(--space-0);
color: var(--muted);
font-size: var(--font-size-sm);
line-height: var(--line-height-tight);
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap
}

.toolbox-status-bar[data-selected-game-state="active"] {
border-color: color-mix(in srgb, var(--green) 52%, var(--line))
}
Expand DownExpand Up@@ -249,16 +263,21 @@ body.tool-focus-mode .tool-center-panel {
@media (max-width: 720px) {
.toolbox-status-bar__inner {
width: var(--container-width);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-10);
text-align: center
}

.toolbox-status-bar__game-name {
max-width: 34vw
max-width: 24vw
}

.toolbox-status-bar__message {
.toolbox-status-bar__message,
.toolbox-status-bar__progress {
font-size: var(--font-size-sm)
}

.toolbox-status-bar__progress {
max-width: 30vw
}
}
139 changes: 138 additions & 1 deletion assets/theme-v2/js/toolbox-status-bar.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
import { readGameJourneyCompletionMetrics } from "/src/api/game-journey-completion-api-client.js";
import { createServerRepositoryClient } from "/src/api/server-api-client.js";
import { getToolBySlug } from "/src/shared/toolbox/tool-metadata-inventory.js";

const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]);
const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]";
const TOOL_PROGRESS_BUCKET_BY_SLUG = Object.freeze({
"achievements": "Progression",
"assets": "Graphics",
"audio": "Audio",
"audio-effects": "Audio",
"characters": "Objects",
"colors": "Graphics",
"community": "Share",
"controls": "Controls",
"events": "Rules",
"game-configuration": "Create",
"game-design": "Design",
"game-hub": "Create",
"game-testing": "Play Test",
"hitboxes": "Objects",
"idea-board": "Idea",
"input-mapping-v2": "Controls",
"marketplace": "Share",
"messages": "Interface",
"music": "Audio",
"objects": "Objects",
"publish": "Publish",
"ratings": "Share",
"sprites": "Graphics",
"tags": "Progression",
"text-to-speech": "Audio",
"videos": "Graphics",
"voices": "Audio",
"worlds": "Worlds",
});

let repository = null;
let messageObserver = null;
Expand DownExpand Up@@ -101,7 +133,10 @@ function createStatusBar() {
message.setAttribute("role", "status");
center.append(message);

inner.append(game, center);
const progress = createText("p", "toolbox-status-bar__progress", "toolboxStatusProgress");
progress.setAttribute("aria-label", "Tool and journey progress");

inner.append(game, center, progress);
bar.append(inner);
return bar;
}
Expand DownExpand Up@@ -233,6 +268,107 @@ function classifyToolContext(messageText, state, required) {
return { kind: "action" };
}

function normalizeTextKey(value) {
return String(value || "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "");
}

function formatToolSlug(slug) {
return String(slug || "Tool")
.split(/[-_\s]+/)
.filter(Boolean)
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
.join(" ") || "Tool";
}

function currentToolContext() {
const slug = toolSlugFromPath(mountOptions.pagePath);
const tool = getToolBySlug(slug);
return {
label: tool?.shortLabel || tool?.displayName || tool?.name || formatToolSlug(slug),
slug,
};
}

function normalizeProgressRecord(record) {
const total = Math.max(0, Number(record?.plannedCount) || 0);
const complete = Math.max(0, Math.min(Number(record?.completedCount) || 0, total));
const percent = Number.isFinite(Number(record?.percentComplete))
? Math.max(0, Math.min(Number(record.percentComplete), 100))
: total > 0
? Math.round((complete / total) * 100)
: 0;
return {
complete,
percent,
total,
};
}

function formatProgressRecord(label, record) {
const progress = normalizeProgressRecord(record);
return `${label} ${progress.complete}/${progress.total} (${progress.percent}%)`;
}

function findMetricForCurrentTool(snapshot, toolContext) {
if (toolContext.slug === "game-journey") {
return snapshot;
}

const records = Array.isArray(snapshot?.records) ? snapshot.records : [];
const explicitBucketName = TOOL_PROGRESS_BUCKET_BY_SLUG[toolContext.slug];
const explicitBucket = normalizeTextKey(explicitBucketName);
const toolLabel = normalizeTextKey(toolContext.label);
const toolSlug = normalizeTextKey(toolContext.slug);

return records.find((metric) => {
const bucketName = normalizeTextKey(metric?.bucketName);
return bucketName && (
bucketName === explicitBucket ||
bucketName === toolLabel ||
bucketName === toolSlug
);
}) || null;
}

function resolveProgressContext() {
const toolContext = currentToolContext();
const snapshot = readGameJourneyCompletionMetrics();
const currentMetric = findMetricForCurrentTool(snapshot, toolContext);
const journeyText = formatProgressRecord("Journey", snapshot);

if (!currentMetric) {
return {
state: "unmapped",
text: `${toolContext.label} progress unavailable | ${journeyText}`,
};
}

return {
state: "active",
text: `${formatProgressRecord(toolContext.label, currentMetric)} | ${journeyText}`,
};
}

function renderProgressContext(bar) {
const progress = bar.querySelector("[data-toolbox-status-progress]");
if (!progress) {
return;
}

try {
const context = resolveProgressContext();
bar.dataset.toolboxProgressState = context.state;
progress.textContent = context.text;
progress.removeAttribute("title");
} catch (error) {
bar.dataset.toolboxProgressState = "error";
progress.textContent = "Progress unavailable";
progress.removeAttribute("title");
}
}

function renderSelectedGame(bar, selectedGame, state, messageText) {
const required = pageRequiresSelectedGame();
const name = bar.querySelector("[data-toolbox-selected-game-name]");
Expand All@@ -247,6 +383,7 @@ function renderSelectedGame(bar, selectedGame, state, messageText) {
bar.dataset.selectedGameState = state;
bar.dataset.selectedGameRequired = String(required);
bar.dataset.toolboxStatusContextKind = context.kind;
renderProgressContext(bar);

if (selectedGame) {
name.textContent = selectedGame.name;
Expand Down
56 changes: 30 additions & 26 deletions docs_build/dev/BUILD_PR.md
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
# PR_26175_ALFA_009-status-bar-single-row-rebuild
# PR_26175_ALFA_011-status-bar-journey-progress-context

## Purpose
Rebuild the shared toolbox status bar on current `main` so it is a single-row creator context bar.
Add right-anchored progress context to the shared toolbox status bar using the existing Game Journey completion metrics/API pipeline.

## Source Of Truth
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_009-status-bar-single-row-rebuild`.
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_011-status-bar-journey-progress-context`.

## Exact Scope
- Display only the selected Game Hub game name on the left side of the toolbox status bar.
- Display only the current status message in the center of the toolbox status bar.
- Remove visible status bar labels: `Selected Game Name`, `Selected Game Purpose`, `Save State`, `Tool Action`, `Warning`, and `Error`.
- Remove selected-game purpose from the visible status bar.
- Preserve normal status bar placement above the footer.
- Remove extra status bar/footer spacing so the shared footer top padding resolves to `0px`.
- Preserve fullscreen/tool display mode bottom anchoring.
- Ensure fullscreen center scrollable content stops above the fixed status bar.
- Ensure fullscreen tool content is not hidden behind the status bar.
- Preserve Idea Board selected-game filtering exclusion.
- Preserve Game Hub as selected-game owner through the existing repository contract.
- Preserve the ALFA_009 single-row toolbox status bar behavior:
- left side displays only the selected Game Hub game name.
- center displays only the current status message.
- Add right-anchored progress text in this format:
- `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`
- Use existing Game Journey completion metrics/API pipeline for Journey totals.
- Derive current-tool progress from the existing completion metrics record that matches the current toolbox tool/section.
- Do not add new storage.
- Do not use browser-owned authoritative progress data.
- Preserve fullscreen bottom anchoring and existing fullscreen content bottom reserve.
- Preserve normal placement above the footer.
- Use shared Theme V2 CSS/classes only.
- Update targeted Playwright coverage for the single-row status bar, footer spacing, and fullscreen bottom reserve.
- Update targeted Playwright coverage for the right-anchored progress text and existing left/center behavior.

## Exact Targets
- `docs_build/dev/BUILD_PR.md`
- `assets/theme-v2/js/toolbox-status-bar.js`
- `assets/theme-v2/css/status.css`
- `assets/theme-v2/css/layout.css`
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_requirements-checklist.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md`
- `docs_build/dev/reports/codex_review.diff`
- `docs_build/dev/reports/codex_changed_files.txt`

## Evidence Sources
- `assets/js/shared/game-journey-api-client.js`
- `src/api/game-journey-completion-api-client.js`
- `src/dev-runtime/server/local-api-router.mjs`
- `src/dev-runtime/persistence/game-journey-completion-metrics-store.mjs`

## Out Of Scope
- No merge of PR #120.
- No reuse of stale ALFA_003 branch.
- No Game Journey API/service/repository contract changes.
- No new persistence/storage.
- No browser-owned product data as source of truth.
- No silent fallback data.
- No environment/server details in the status bar.
- No selected game purpose in the visible status bar.
- No visible status category labels in the status bar.
- No status action links in the visible status bar.
- No large banners.
- No modal messages or modal-style status messages.
- No row highlights.
- No API/service/repository contract changes.
- No browser-owned product data as source of truth.
- No inline styles, style blocks, or page-local CSS.
- No engine core changes.
- No `start_of_day` folder changes.
Expand All@@ -59,12 +63,12 @@ npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
Also verify changed source does not introduce inline styles or style blocks:

```powershell
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css assets/theme-v2/css/layout.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
```

## Artifact
Create repo-structured delta ZIP:

```text
tmp/PR_26175_ALFA_009-status-bar-single-row-rebuild_delta.zip
tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
# PR_26175_ALFA_011-status-bar-journey-progress-context Report

## Status
PASS

Added right-anchored progress context to the shared toolbox status bar while preserving the ALFA_009 left and center behavior.

## Changes
- `assets/theme-v2/js/toolbox-status-bar.js`
- Imports the existing Game Journey completion metrics API client at line 1.
- Adds current-tool-to-Journey-bucket matching at lines 7-35.
- Adds the right progress node at line 136.
- Formats progress as `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` at lines 309-350.
- Renders visible unavailable progress state without raw environment/server details at lines 354-370.
- `assets/theme-v2/css/status.css`
- Adds a three-column shared status bar grid at line 54.
- Adds right-aligned single-line progress styling at lines 94-106.
- Preserves existing fullscreen anchoring and bottom reserve rules at lines 119-150.
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- Routes `/api/game-journey/completion-metrics` through the existing server API response shape at lines 76-83.
- Verifies Game Design progress at lines 239 and 253.
- Verifies the requested Objects example at lines 275 and 279.
- Verifies right anchoring at lines 260 and 280.
- Verifies Game Hub, missing-game, Idea Board, and fullscreen behavior at lines 304, 328, 353, 371, and 398.

## Contract Notes
- No storage was added.
- No API/service/repository contract was changed.
- Progress data is read from the existing Game Journey completion metrics API pipeline.
- Browser state is not used as authoritative progress data.
- Existing selected-game ownership and left/center status bar behavior are preserved.

## Validation
- PASS: `npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1`
- PASS: `rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`

## Artifact
- `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
# PR_26175_ALFA_011 Requirements Checklist

| Requirement | Status | Evidence |
| --- | --- | --- |
| Implement right-anchored progress in the shared toolbox status bar. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:136`, `assets/theme-v2/css/status.css:94`. |
| Use format `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:309` and `:350`; tests at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:239`, `:275`. |
| Use existing Game Journey completion metrics/API pipeline. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:1`, `:337`; tests route `/api/game-journey/completion-metrics` at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:76`. |
| Do not add new storage. | PASS | No persistence files changed. |
| Do not use browser-owned authoritative progress data. | PASS | Progress is read from `readGameJourneyCompletionMetrics()` and not local/session storage. |
| Keep existing left status bar behavior from ALFA_009. | PASS | Selected game assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:237`, `:302`, `:391`. |
| Keep existing center status bar behavior from ALFA_009. | PASS | Message assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:238`, `:303`, `:370`, `:397`. |
| Preserve fullscreen bottom anchoring. | PASS | Existing CSS rules remain at `assets/theme-v2/css/status.css:119`; test coverage at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:315`. |
| Preserve normal placement above footer. | PASS | Existing status bar placement test remains at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:224`. |
| No inline styles, style blocks, or page-local CSS. | PASS | Style scan returned no matches. |
| Use Theme V2 shared CSS/classes. | PASS | Styling changes are limited to `assets/theme-v2/css/status.css`. |
| Update targeted Playwright coverage. | PASS | `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` now validates right progress text and anchoring. |
| Produce required reports and repo-structured ZIP. | PASS | PR reports added and ZIP target is `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`. |
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions assets/theme-v2/css/status.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,7 @@
.toolbox-status-bar {
--toolbox-status-bar-height: var(--space-52);
--toolbox-status-game-max: 220px;
--toolbox-status-progress-max: 34vw;
width: 100%;
min-block-size: var(--toolbox-status-bar-height);
border-block: var(--border-standard);
Expand All@@ -50,7 +51,7 @@
margin: var(--space-0) auto;
padding: var(--space-10) var(--space-0);
display: grid;
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-16);
align-items: center
}
Expand DownExpand Up@@ -90,6 +91,19 @@
overflow-wrap: anywhere
}

.toolbox-status-bar__progress {
min-width: var(--space-0);
max-width: var(--toolbox-status-progress-max);
margin: var(--space-0);
color: var(--muted);
font-size: var(--font-size-sm);
line-height: var(--line-height-tight);
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap
}

.toolbox-status-bar[data-selected-game-state="active"] {
border-color: color-mix(in srgb, var(--green) 52%, var(--line))
}
Expand DownExpand Up@@ -249,16 +263,21 @@ body.tool-focus-mode .tool-center-panel {
@media (max-width: 720px) {
.toolbox-status-bar__inner {
width: var(--container-width);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-10);
text-align: center
}

.toolbox-status-bar__game-name {
max-width: 34vw
max-width: 24vw
}

.toolbox-status-bar__message {
.toolbox-status-bar__message,
.toolbox-status-bar__progress {
font-size: var(--font-size-sm)
}

.toolbox-status-bar__progress {
max-width: 30vw
}
}
139 changes: 138 additions & 1 deletion assets/theme-v2/js/toolbox-status-bar.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
import { readGameJourneyCompletionMetrics } from "/src/api/game-journey-completion-api-client.js";
import { createServerRepositoryClient } from "/src/api/server-api-client.js";
import { getToolBySlug } from "/src/shared/toolbox/tool-metadata-inventory.js";

const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]);
const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]";
const TOOL_PROGRESS_BUCKET_BY_SLUG = Object.freeze({
"achievements": "Progression",
"assets": "Graphics",
"audio": "Audio",
"audio-effects": "Audio",
"characters": "Objects",
"colors": "Graphics",
"community": "Share",
"controls": "Controls",
"events": "Rules",
"game-configuration": "Create",
"game-design": "Design",
"game-hub": "Create",
"game-testing": "Play Test",
"hitboxes": "Objects",
"idea-board": "Idea",
"input-mapping-v2": "Controls",
"marketplace": "Share",
"messages": "Interface",
"music": "Audio",
"objects": "Objects",
"publish": "Publish",
"ratings": "Share",
"sprites": "Graphics",
"tags": "Progression",
"text-to-speech": "Audio",
"videos": "Graphics",
"voices": "Audio",
"worlds": "Worlds",
});

let repository = null;
let messageObserver = null;
Expand DownExpand Up@@ -101,7 +133,10 @@ function createStatusBar() {
message.setAttribute("role", "status");
center.append(message);

inner.append(game, center);
const progress = createText("p", "toolbox-status-bar__progress", "toolboxStatusProgress");
progress.setAttribute("aria-label", "Tool and journey progress");

inner.append(game, center, progress);
bar.append(inner);
return bar;
}
Expand DownExpand Up@@ -233,6 +268,107 @@ function classifyToolContext(messageText, state, required) {
return { kind: "action" };
}

function normalizeTextKey(value) {
return String(value || "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "");
}

function formatToolSlug(slug) {
return String(slug || "Tool")
.split(/[-_\s]+/)
.filter(Boolean)
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
.join(" ") || "Tool";
}

function currentToolContext() {
const slug = toolSlugFromPath(mountOptions.pagePath);
const tool = getToolBySlug(slug);
return {
label: tool?.shortLabel || tool?.displayName || tool?.name || formatToolSlug(slug),
slug,
};
}

function normalizeProgressRecord(record) {
const total = Math.max(0, Number(record?.plannedCount) || 0);
const complete = Math.max(0, Math.min(Number(record?.completedCount) || 0, total));
const percent = Number.isFinite(Number(record?.percentComplete))
? Math.max(0, Math.min(Number(record.percentComplete), 100))
: total > 0
? Math.round((complete / total) * 100)
: 0;
return {
complete,
percent,
total,
};
}

function formatProgressRecord(label, record) {
const progress = normalizeProgressRecord(record);
return `${label} ${progress.complete}/${progress.total} (${progress.percent}%)`;
}

function findMetricForCurrentTool(snapshot, toolContext) {
if (toolContext.slug === "game-journey") {
return snapshot;
}

const records = Array.isArray(snapshot?.records) ? snapshot.records : [];
const explicitBucketName = TOOL_PROGRESS_BUCKET_BY_SLUG[toolContext.slug];
const explicitBucket = normalizeTextKey(explicitBucketName);
const toolLabel = normalizeTextKey(toolContext.label);
const toolSlug = normalizeTextKey(toolContext.slug);

return records.find((metric) => {
const bucketName = normalizeTextKey(metric?.bucketName);
return bucketName && (
bucketName === explicitBucket ||
bucketName === toolLabel ||
bucketName === toolSlug
);
}) || null;
}

function resolveProgressContext() {
const toolContext = currentToolContext();
const snapshot = readGameJourneyCompletionMetrics();
const currentMetric = findMetricForCurrentTool(snapshot, toolContext);
const journeyText = formatProgressRecord("Journey", snapshot);

if (!currentMetric) {
return {
state: "unmapped",
text: `${toolContext.label} progress unavailable | ${journeyText}`,
};
}

return {
state: "active",
text: `${formatProgressRecord(toolContext.label, currentMetric)} | ${journeyText}`,
};
}

function renderProgressContext(bar) {
const progress = bar.querySelector("[data-toolbox-status-progress]");
if (!progress) {
return;
}

try {
const context = resolveProgressContext();
bar.dataset.toolboxProgressState = context.state;
progress.textContent = context.text;
progress.removeAttribute("title");
} catch (error) {
bar.dataset.toolboxProgressState = "error";
progress.textContent = "Progress unavailable";
progress.removeAttribute("title");
}
}

function renderSelectedGame(bar, selectedGame, state, messageText) {
const required = pageRequiresSelectedGame();
const name = bar.querySelector("[data-toolbox-selected-game-name]");
Expand All@@ -247,6 +383,7 @@ function renderSelectedGame(bar, selectedGame, state, messageText) {
bar.dataset.selectedGameState = state;
bar.dataset.selectedGameRequired = String(required);
bar.dataset.toolboxStatusContextKind = context.kind;
renderProgressContext(bar);

if (selectedGame) {
name.textContent = selectedGame.name;
Expand Down
56 changes: 30 additions & 26 deletions docs_build/dev/BUILD_PR.md
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
# PR_26175_ALFA_009-status-bar-single-row-rebuild
# PR_26175_ALFA_011-status-bar-journey-progress-context

## Purpose
Rebuild the shared toolbox status bar on current `main` so it is a single-row creator context bar.
Add right-anchored progress context to the shared toolbox status bar using the existing Game Journey completion metrics/API pipeline.

## Source Of Truth
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_009-status-bar-single-row-rebuild`.
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_011-status-bar-journey-progress-context`.

## Exact Scope
- Display only the selected Game Hub game name on the left side of the toolbox status bar.
- Display only the current status message in the center of the toolbox status bar.
- Remove visible status bar labels: `Selected Game Name`, `Selected Game Purpose`, `Save State`, `Tool Action`, `Warning`, and `Error`.
- Remove selected-game purpose from the visible status bar.
- Preserve normal status bar placement above the footer.
- Remove extra status bar/footer spacing so the shared footer top padding resolves to `0px`.
- Preserve fullscreen/tool display mode bottom anchoring.
- Ensure fullscreen center scrollable content stops above the fixed status bar.
- Ensure fullscreen tool content is not hidden behind the status bar.
- Preserve Idea Board selected-game filtering exclusion.
- Preserve Game Hub as selected-game owner through the existing repository contract.
- Preserve the ALFA_009 single-row toolbox status bar behavior:
- left side displays only the selected Game Hub game name.
- center displays only the current status message.
- Add right-anchored progress text in this format:
- `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`
- Use existing Game Journey completion metrics/API pipeline for Journey totals.
- Derive current-tool progress from the existing completion metrics record that matches the current toolbox tool/section.
- Do not add new storage.
- Do not use browser-owned authoritative progress data.
- Preserve fullscreen bottom anchoring and existing fullscreen content bottom reserve.
- Preserve normal placement above the footer.
- Use shared Theme V2 CSS/classes only.
- Update targeted Playwright coverage for the single-row status bar, footer spacing, and fullscreen bottom reserve.
- Update targeted Playwright coverage for the right-anchored progress text and existing left/center behavior.

## Exact Targets
- `docs_build/dev/BUILD_PR.md`
- `assets/theme-v2/js/toolbox-status-bar.js`
- `assets/theme-v2/css/status.css`
- `assets/theme-v2/css/layout.css`
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_requirements-checklist.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md`
- `docs_build/dev/reports/codex_review.diff`
- `docs_build/dev/reports/codex_changed_files.txt`

## Evidence Sources
- `assets/js/shared/game-journey-api-client.js`
- `src/api/game-journey-completion-api-client.js`
- `src/dev-runtime/server/local-api-router.mjs`
- `src/dev-runtime/persistence/game-journey-completion-metrics-store.mjs`

## Out Of Scope
- No merge of PR #120.
- No reuse of stale ALFA_003 branch.
- No Game Journey API/service/repository contract changes.
- No new persistence/storage.
- No browser-owned product data as source of truth.
- No silent fallback data.
- No environment/server details in the status bar.
- No selected game purpose in the visible status bar.
- No visible status category labels in the status bar.
- No status action links in the visible status bar.
- No large banners.
- No modal messages or modal-style status messages.
- No row highlights.
- No API/service/repository contract changes.
- No browser-owned product data as source of truth.
- No inline styles, style blocks, or page-local CSS.
- No engine core changes.
- No `start_of_day` folder changes.
Expand All@@ -59,12 +63,12 @@ npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
Also verify changed source does not introduce inline styles or style blocks:

```powershell
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css assets/theme-v2/css/layout.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
```

## Artifact
Create repo-structured delta ZIP:

```text
tmp/PR_26175_ALFA_009-status-bar-single-row-rebuild_delta.zip
tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
# PR_26175_ALFA_011-status-bar-journey-progress-context Report

## Status
PASS

Added right-anchored progress context to the shared toolbox status bar while preserving the ALFA_009 left and center behavior.

## Changes
- `assets/theme-v2/js/toolbox-status-bar.js`
- Imports the existing Game Journey completion metrics API client at line 1.
- Adds current-tool-to-Journey-bucket matching at lines 7-35.
- Adds the right progress node at line 136.
- Formats progress as `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` at lines 309-350.
- Renders visible unavailable progress state without raw environment/server details at lines 354-370.
- `assets/theme-v2/css/status.css`
- Adds a three-column shared status bar grid at line 54.
- Adds right-aligned single-line progress styling at lines 94-106.
- Preserves existing fullscreen anchoring and bottom reserve rules at lines 119-150.
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- Routes `/api/game-journey/completion-metrics` through the existing server API response shape at lines 76-83.
- Verifies Game Design progress at lines 239 and 253.
- Verifies the requested Objects example at lines 275 and 279.
- Verifies right anchoring at lines 260 and 280.
- Verifies Game Hub, missing-game, Idea Board, and fullscreen behavior at lines 304, 328, 353, 371, and 398.

## Contract Notes
- No storage was added.
- No API/service/repository contract was changed.
- Progress data is read from the existing Game Journey completion metrics API pipeline.
- Browser state is not used as authoritative progress data.
- Existing selected-game ownership and left/center status bar behavior are preserved.

## Validation
- PASS: `npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1`
- PASS: `rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`

## Artifact
- `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
# PR_26175_ALFA_011 Requirements Checklist

| Requirement | Status | Evidence |
| --- | --- | --- |
| Implement right-anchored progress in the shared toolbox status bar. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:136`, `assets/theme-v2/css/status.css:94`. |
| Use format `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:309` and `:350`; tests at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:239`, `:275`. |
| Use existing Game Journey completion metrics/API pipeline. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:1`, `:337`; tests route `/api/game-journey/completion-metrics` at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:76`. |
| Do not add new storage. | PASS | No persistence files changed. |
| Do not use browser-owned authoritative progress data. | PASS | Progress is read from `readGameJourneyCompletionMetrics()` and not local/session storage. |
| Keep existing left status bar behavior from ALFA_009. | PASS | Selected game assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:237`, `:302`, `:391`. |
| Keep existing center status bar behavior from ALFA_009. | PASS | Message assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:238`, `:303`, `:370`, `:397`. |
| Preserve fullscreen bottom anchoring. | PASS | Existing CSS rules remain at `assets/theme-v2/css/status.css:119`; test coverage at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:315`. |
| Preserve normal placement above footer. | PASS | Existing status bar placement test remains at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:224`. |
| No inline styles, style blocks, or page-local CSS. | PASS | Style scan returned no matches. |
| Use Theme V2 shared CSS/classes. | PASS | Styling changes are limited to `assets/theme-v2/css/status.css`. |
| Update targeted Playwright coverage. | PASS | `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` now validates right progress text and anchoring. |
| Produce required reports and repo-structured ZIP. | PASS | PR reports added and ZIP target is `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`. |
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions assets/theme-v2/css/status.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,7 @@
.toolbox-status-bar {
--toolbox-status-bar-height: var(--space-52);
--toolbox-status-game-max: 220px;
--toolbox-status-progress-max: 34vw;
width: 100%;
min-block-size: var(--toolbox-status-bar-height);
border-block: var(--border-standard);
Expand All@@ -50,7 +51,7 @@
margin: var(--space-0) auto;
padding: var(--space-10) var(--space-0);
display: grid;
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-16);
align-items: center
}
Expand DownExpand Up@@ -90,6 +91,19 @@
overflow-wrap: anywhere
}

.toolbox-status-bar__progress {
min-width: var(--space-0);
max-width: var(--toolbox-status-progress-max);
margin: var(--space-0);
color: var(--muted);
font-size: var(--font-size-sm);
line-height: var(--line-height-tight);
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap
}

.toolbox-status-bar[data-selected-game-state="active"] {
border-color: color-mix(in srgb, var(--green) 52%, var(--line))
}
Expand DownExpand Up@@ -249,16 +263,21 @@ body.tool-focus-mode .tool-center-panel {
@media (max-width: 720px) {
.toolbox-status-bar__inner {
width: var(--container-width);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-10);
text-align: center
}

.toolbox-status-bar__game-name {
max-width: 34vw
max-width: 24vw
}

.toolbox-status-bar__message {
.toolbox-status-bar__message,
.toolbox-status-bar__progress {
font-size: var(--font-size-sm)
}

.toolbox-status-bar__progress {
max-width: 30vw
}
}
139 changes: 138 additions & 1 deletion assets/theme-v2/js/toolbox-status-bar.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
import { readGameJourneyCompletionMetrics } from "/src/api/game-journey-completion-api-client.js";
import { createServerRepositoryClient } from "/src/api/server-api-client.js";
import { getToolBySlug } from "/src/shared/toolbox/tool-metadata-inventory.js";

const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]);
const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]";
const TOOL_PROGRESS_BUCKET_BY_SLUG = Object.freeze({
"achievements": "Progression",
"assets": "Graphics",
"audio": "Audio",
"audio-effects": "Audio",
"characters": "Objects",
"colors": "Graphics",
"community": "Share",
"controls": "Controls",
"events": "Rules",
"game-configuration": "Create",
"game-design": "Design",
"game-hub": "Create",
"game-testing": "Play Test",
"hitboxes": "Objects",
"idea-board": "Idea",
"input-mapping-v2": "Controls",
"marketplace": "Share",
"messages": "Interface",
"music": "Audio",
"objects": "Objects",
"publish": "Publish",
"ratings": "Share",
"sprites": "Graphics",
"tags": "Progression",
"text-to-speech": "Audio",
"videos": "Graphics",
"voices": "Audio",
"worlds": "Worlds",
});

let repository = null;
let messageObserver = null;
Expand DownExpand Up@@ -101,7 +133,10 @@ function createStatusBar() {
message.setAttribute("role", "status");
center.append(message);

inner.append(game, center);
const progress = createText("p", "toolbox-status-bar__progress", "toolboxStatusProgress");
progress.setAttribute("aria-label", "Tool and journey progress");

inner.append(game, center, progress);
bar.append(inner);
return bar;
}
Expand DownExpand Up@@ -233,6 +268,107 @@ function classifyToolContext(messageText, state, required) {
return { kind: "action" };
}

function normalizeTextKey(value) {
return String(value || "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "");
}

function formatToolSlug(slug) {
return String(slug || "Tool")
.split(/[-_\s]+/)
.filter(Boolean)
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
.join(" ") || "Tool";
}

function currentToolContext() {
const slug = toolSlugFromPath(mountOptions.pagePath);
const tool = getToolBySlug(slug);
return {
label: tool?.shortLabel || tool?.displayName || tool?.name || formatToolSlug(slug),
slug,
};
}

function normalizeProgressRecord(record) {
const total = Math.max(0, Number(record?.plannedCount) || 0);
const complete = Math.max(0, Math.min(Number(record?.completedCount) || 0, total));
const percent = Number.isFinite(Number(record?.percentComplete))
? Math.max(0, Math.min(Number(record.percentComplete), 100))
: total > 0
? Math.round((complete / total) * 100)
: 0;
return {
complete,
percent,
total,
};
}

function formatProgressRecord(label, record) {
const progress = normalizeProgressRecord(record);
return `${label} ${progress.complete}/${progress.total} (${progress.percent}%)`;
}

function findMetricForCurrentTool(snapshot, toolContext) {
if (toolContext.slug === "game-journey") {
return snapshot;
}

const records = Array.isArray(snapshot?.records) ? snapshot.records : [];
const explicitBucketName = TOOL_PROGRESS_BUCKET_BY_SLUG[toolContext.slug];
const explicitBucket = normalizeTextKey(explicitBucketName);
const toolLabel = normalizeTextKey(toolContext.label);
const toolSlug = normalizeTextKey(toolContext.slug);

return records.find((metric) => {
const bucketName = normalizeTextKey(metric?.bucketName);
return bucketName && (
bucketName === explicitBucket ||
bucketName === toolLabel ||
bucketName === toolSlug
);
}) || null;
}

function resolveProgressContext() {
const toolContext = currentToolContext();
const snapshot = readGameJourneyCompletionMetrics();
const currentMetric = findMetricForCurrentTool(snapshot, toolContext);
const journeyText = formatProgressRecord("Journey", snapshot);

if (!currentMetric) {
return {
state: "unmapped",
text: `${toolContext.label} progress unavailable | ${journeyText}`,
};
}

return {
state: "active",
text: `${formatProgressRecord(toolContext.label, currentMetric)} | ${journeyText}`,
};
}

function renderProgressContext(bar) {
const progress = bar.querySelector("[data-toolbox-status-progress]");
if (!progress) {
return;
}

try {
const context = resolveProgressContext();
bar.dataset.toolboxProgressState = context.state;
progress.textContent = context.text;
progress.removeAttribute("title");
} catch (error) {
bar.dataset.toolboxProgressState = "error";
progress.textContent = "Progress unavailable";
progress.removeAttribute("title");
}
}

function renderSelectedGame(bar, selectedGame, state, messageText) {
const required = pageRequiresSelectedGame();
const name = bar.querySelector("[data-toolbox-selected-game-name]");
Expand All@@ -247,6 +383,7 @@ function renderSelectedGame(bar, selectedGame, state, messageText) {
bar.dataset.selectedGameState = state;
bar.dataset.selectedGameRequired = String(required);
bar.dataset.toolboxStatusContextKind = context.kind;
renderProgressContext(bar);

if (selectedGame) {
name.textContent = selectedGame.name;
Expand Down
56 changes: 30 additions & 26 deletions docs_build/dev/BUILD_PR.md
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
# PR_26175_ALFA_009-status-bar-single-row-rebuild
# PR_26175_ALFA_011-status-bar-journey-progress-context

## Purpose
Rebuild the shared toolbox status bar on current `main` so it is a single-row creator context bar.
Add right-anchored progress context to the shared toolbox status bar using the existing Game Journey completion metrics/API pipeline.

## Source Of Truth
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_009-status-bar-single-row-rebuild`.
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_011-status-bar-journey-progress-context`.

## Exact Scope
- Display only the selected Game Hub game name on the left side of the toolbox status bar.
- Display only the current status message in the center of the toolbox status bar.
- Remove visible status bar labels: `Selected Game Name`, `Selected Game Purpose`, `Save State`, `Tool Action`, `Warning`, and `Error`.
- Remove selected-game purpose from the visible status bar.
- Preserve normal status bar placement above the footer.
- Remove extra status bar/footer spacing so the shared footer top padding resolves to `0px`.
- Preserve fullscreen/tool display mode bottom anchoring.
- Ensure fullscreen center scrollable content stops above the fixed status bar.
- Ensure fullscreen tool content is not hidden behind the status bar.
- Preserve Idea Board selected-game filtering exclusion.
- Preserve Game Hub as selected-game owner through the existing repository contract.
- Preserve the ALFA_009 single-row toolbox status bar behavior:
- left side displays only the selected Game Hub game name.
- center displays only the current status message.
- Add right-anchored progress text in this format:
- `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`
- Use existing Game Journey completion metrics/API pipeline for Journey totals.
- Derive current-tool progress from the existing completion metrics record that matches the current toolbox tool/section.
- Do not add new storage.
- Do not use browser-owned authoritative progress data.
- Preserve fullscreen bottom anchoring and existing fullscreen content bottom reserve.
- Preserve normal placement above the footer.
- Use shared Theme V2 CSS/classes only.
- Update targeted Playwright coverage for the single-row status bar, footer spacing, and fullscreen bottom reserve.
- Update targeted Playwright coverage for the right-anchored progress text and existing left/center behavior.

## Exact Targets
- `docs_build/dev/BUILD_PR.md`
- `assets/theme-v2/js/toolbox-status-bar.js`
- `assets/theme-v2/css/status.css`
- `assets/theme-v2/css/layout.css`
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_requirements-checklist.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md`
- `docs_build/dev/reports/codex_review.diff`
- `docs_build/dev/reports/codex_changed_files.txt`

## Evidence Sources
- `assets/js/shared/game-journey-api-client.js`
- `src/api/game-journey-completion-api-client.js`
- `src/dev-runtime/server/local-api-router.mjs`
- `src/dev-runtime/persistence/game-journey-completion-metrics-store.mjs`

## Out Of Scope
- No merge of PR #120.
- No reuse of stale ALFA_003 branch.
- No Game Journey API/service/repository contract changes.
- No new persistence/storage.
- No browser-owned product data as source of truth.
- No silent fallback data.
- No environment/server details in the status bar.
- No selected game purpose in the visible status bar.
- No visible status category labels in the status bar.
- No status action links in the visible status bar.
- No large banners.
- No modal messages or modal-style status messages.
- No row highlights.
- No API/service/repository contract changes.
- No browser-owned product data as source of truth.
- No inline styles, style blocks, or page-local CSS.
- No engine core changes.
- No `start_of_day` folder changes.
Expand All@@ -59,12 +63,12 @@ npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
Also verify changed source does not introduce inline styles or style blocks:

```powershell
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css assets/theme-v2/css/layout.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
```

## Artifact
Create repo-structured delta ZIP:

```text
tmp/PR_26175_ALFA_009-status-bar-single-row-rebuild_delta.zip
tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
# PR_26175_ALFA_011-status-bar-journey-progress-context Report

## Status
PASS

Added right-anchored progress context to the shared toolbox status bar while preserving the ALFA_009 left and center behavior.

## Changes
- `assets/theme-v2/js/toolbox-status-bar.js`
- Imports the existing Game Journey completion metrics API client at line 1.
- Adds current-tool-to-Journey-bucket matching at lines 7-35.
- Adds the right progress node at line 136.
- Formats progress as `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` at lines 309-350.
- Renders visible unavailable progress state without raw environment/server details at lines 354-370.
- `assets/theme-v2/css/status.css`
- Adds a three-column shared status bar grid at line 54.
- Adds right-aligned single-line progress styling at lines 94-106.
- Preserves existing fullscreen anchoring and bottom reserve rules at lines 119-150.
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- Routes `/api/game-journey/completion-metrics` through the existing server API response shape at lines 76-83.
- Verifies Game Design progress at lines 239 and 253.
- Verifies the requested Objects example at lines 275 and 279.
- Verifies right anchoring at lines 260 and 280.
- Verifies Game Hub, missing-game, Idea Board, and fullscreen behavior at lines 304, 328, 353, 371, and 398.

## Contract Notes
- No storage was added.
- No API/service/repository contract was changed.
- Progress data is read from the existing Game Journey completion metrics API pipeline.
- Browser state is not used as authoritative progress data.
- Existing selected-game ownership and left/center status bar behavior are preserved.

## Validation
- PASS: `npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1`
- PASS: `rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`

## Artifact
- `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
# PR_26175_ALFA_011 Requirements Checklist

| Requirement | Status | Evidence |
| --- | --- | --- |
| Implement right-anchored progress in the shared toolbox status bar. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:136`, `assets/theme-v2/css/status.css:94`. |
| Use format `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:309` and `:350`; tests at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:239`, `:275`. |
| Use existing Game Journey completion metrics/API pipeline. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:1`, `:337`; tests route `/api/game-journey/completion-metrics` at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:76`. |
| Do not add new storage. | PASS | No persistence files changed. |
| Do not use browser-owned authoritative progress data. | PASS | Progress is read from `readGameJourneyCompletionMetrics()` and not local/session storage. |
| Keep existing left status bar behavior from ALFA_009. | PASS | Selected game assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:237`, `:302`, `:391`. |
| Keep existing center status bar behavior from ALFA_009. | PASS | Message assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:238`, `:303`, `:370`, `:397`. |
| Preserve fullscreen bottom anchoring. | PASS | Existing CSS rules remain at `assets/theme-v2/css/status.css:119`; test coverage at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:315`. |
| Preserve normal placement above footer. | PASS | Existing status bar placement test remains at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:224`. |
| No inline styles, style blocks, or page-local CSS. | PASS | Style scan returned no matches. |
| Use Theme V2 shared CSS/classes. | PASS | Styling changes are limited to `assets/theme-v2/css/status.css`. |
| Update targeted Playwright coverage. | PASS | `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` now validates right progress text and anchoring. |
| Produce required reports and repo-structured ZIP. | PASS | PR reports added and ZIP target is `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`. |
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions assets/theme-v2/css/status.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,7 @@
.toolbox-status-bar {
--toolbox-status-bar-height: var(--space-52);
--toolbox-status-game-max: 220px;
--toolbox-status-progress-max: 34vw;
width: 100%;
min-block-size: var(--toolbox-status-bar-height);
border-block: var(--border-standard);
Expand All@@ -50,7 +51,7 @@
margin: var(--space-0) auto;
padding: var(--space-10) var(--space-0);
display: grid;
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-16);
align-items: center
}
Expand DownExpand Up@@ -90,6 +91,19 @@
overflow-wrap: anywhere
}

.toolbox-status-bar__progress {
min-width: var(--space-0);
max-width: var(--toolbox-status-progress-max);
margin: var(--space-0);
color: var(--muted);
font-size: var(--font-size-sm);
line-height: var(--line-height-tight);
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap
}

.toolbox-status-bar[data-selected-game-state="active"] {
border-color: color-mix(in srgb, var(--green) 52%, var(--line))
}
Expand DownExpand Up@@ -249,16 +263,21 @@ body.tool-focus-mode .tool-center-panel {
@media (max-width: 720px) {
.toolbox-status-bar__inner {
width: var(--container-width);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-10);
text-align: center
}

.toolbox-status-bar__game-name {
max-width: 34vw
max-width: 24vw
}

.toolbox-status-bar__message {
.toolbox-status-bar__message,
.toolbox-status-bar__progress {
font-size: var(--font-size-sm)
}

.toolbox-status-bar__progress {
max-width: 30vw
}
}
139 changes: 138 additions & 1 deletion assets/theme-v2/js/toolbox-status-bar.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
import { readGameJourneyCompletionMetrics } from "/src/api/game-journey-completion-api-client.js";
import { createServerRepositoryClient } from "/src/api/server-api-client.js";
import { getToolBySlug } from "/src/shared/toolbox/tool-metadata-inventory.js";

const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]);
const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]";
const TOOL_PROGRESS_BUCKET_BY_SLUG = Object.freeze({
"achievements": "Progression",
"assets": "Graphics",
"audio": "Audio",
"audio-effects": "Audio",
"characters": "Objects",
"colors": "Graphics",
"community": "Share",
"controls": "Controls",
"events": "Rules",
"game-configuration": "Create",
"game-design": "Design",
"game-hub": "Create",
"game-testing": "Play Test",
"hitboxes": "Objects",
"idea-board": "Idea",
"input-mapping-v2": "Controls",
"marketplace": "Share",
"messages": "Interface",
"music": "Audio",
"objects": "Objects",
"publish": "Publish",
"ratings": "Share",
"sprites": "Graphics",
"tags": "Progression",
"text-to-speech": "Audio",
"videos": "Graphics",
"voices": "Audio",
"worlds": "Worlds",
});

let repository = null;
let messageObserver = null;
Expand DownExpand Up@@ -101,7 +133,10 @@ function createStatusBar() {
message.setAttribute("role", "status");
center.append(message);

inner.append(game, center);
const progress = createText("p", "toolbox-status-bar__progress", "toolboxStatusProgress");
progress.setAttribute("aria-label", "Tool and journey progress");

inner.append(game, center, progress);
bar.append(inner);
return bar;
}
Expand DownExpand Up@@ -233,6 +268,107 @@ function classifyToolContext(messageText, state, required) {
return { kind: "action" };
}

function normalizeTextKey(value) {
return String(value || "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "");
}

function formatToolSlug(slug) {
return String(slug || "Tool")
.split(/[-_\s]+/)
.filter(Boolean)
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
.join(" ") || "Tool";
}

function currentToolContext() {
const slug = toolSlugFromPath(mountOptions.pagePath);
const tool = getToolBySlug(slug);
return {
label: tool?.shortLabel || tool?.displayName || tool?.name || formatToolSlug(slug),
slug,
};
}

function normalizeProgressRecord(record) {
const total = Math.max(0, Number(record?.plannedCount) || 0);
const complete = Math.max(0, Math.min(Number(record?.completedCount) || 0, total));
const percent = Number.isFinite(Number(record?.percentComplete))
? Math.max(0, Math.min(Number(record.percentComplete), 100))
: total > 0
? Math.round((complete / total) * 100)
: 0;
return {
complete,
percent,
total,
};
}

function formatProgressRecord(label, record) {
const progress = normalizeProgressRecord(record);
return `${label} ${progress.complete}/${progress.total} (${progress.percent}%)`;
}

function findMetricForCurrentTool(snapshot, toolContext) {
if (toolContext.slug === "game-journey") {
return snapshot;
}

const records = Array.isArray(snapshot?.records) ? snapshot.records : [];
const explicitBucketName = TOOL_PROGRESS_BUCKET_BY_SLUG[toolContext.slug];
const explicitBucket = normalizeTextKey(explicitBucketName);
const toolLabel = normalizeTextKey(toolContext.label);
const toolSlug = normalizeTextKey(toolContext.slug);

return records.find((metric) => {
const bucketName = normalizeTextKey(metric?.bucketName);
return bucketName && (
bucketName === explicitBucket ||
bucketName === toolLabel ||
bucketName === toolSlug
);
}) || null;
}

function resolveProgressContext() {
const toolContext = currentToolContext();
const snapshot = readGameJourneyCompletionMetrics();
const currentMetric = findMetricForCurrentTool(snapshot, toolContext);
const journeyText = formatProgressRecord("Journey", snapshot);

if (!currentMetric) {
return {
state: "unmapped",
text: `${toolContext.label} progress unavailable | ${journeyText}`,
};
}

return {
state: "active",
text: `${formatProgressRecord(toolContext.label, currentMetric)} | ${journeyText}`,
};
}

function renderProgressContext(bar) {
const progress = bar.querySelector("[data-toolbox-status-progress]");
if (!progress) {
return;
}

try {
const context = resolveProgressContext();
bar.dataset.toolboxProgressState = context.state;
progress.textContent = context.text;
progress.removeAttribute("title");
} catch (error) {
bar.dataset.toolboxProgressState = "error";
progress.textContent = "Progress unavailable";
progress.removeAttribute("title");
}
}

function renderSelectedGame(bar, selectedGame, state, messageText) {
const required = pageRequiresSelectedGame();
const name = bar.querySelector("[data-toolbox-selected-game-name]");
Expand All@@ -247,6 +383,7 @@ function renderSelectedGame(bar, selectedGame, state, messageText) {
bar.dataset.selectedGameState = state;
bar.dataset.selectedGameRequired = String(required);
bar.dataset.toolboxStatusContextKind = context.kind;
renderProgressContext(bar);

if (selectedGame) {
name.textContent = selectedGame.name;
Expand Down
56 changes: 30 additions & 26 deletions docs_build/dev/BUILD_PR.md
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
# PR_26175_ALFA_009-status-bar-single-row-rebuild
# PR_26175_ALFA_011-status-bar-journey-progress-context

## Purpose
Rebuild the shared toolbox status bar on current `main` so it is a single-row creator context bar.
Add right-anchored progress context to the shared toolbox status bar using the existing Game Journey completion metrics/API pipeline.

## Source Of Truth
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_009-status-bar-single-row-rebuild`.
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_011-status-bar-journey-progress-context`.

## Exact Scope
- Display only the selected Game Hub game name on the left side of the toolbox status bar.
- Display only the current status message in the center of the toolbox status bar.
- Remove visible status bar labels: `Selected Game Name`, `Selected Game Purpose`, `Save State`, `Tool Action`, `Warning`, and `Error`.
- Remove selected-game purpose from the visible status bar.
- Preserve normal status bar placement above the footer.
- Remove extra status bar/footer spacing so the shared footer top padding resolves to `0px`.
- Preserve fullscreen/tool display mode bottom anchoring.
- Ensure fullscreen center scrollable content stops above the fixed status bar.
- Ensure fullscreen tool content is not hidden behind the status bar.
- Preserve Idea Board selected-game filtering exclusion.
- Preserve Game Hub as selected-game owner through the existing repository contract.
- Preserve the ALFA_009 single-row toolbox status bar behavior:
- left side displays only the selected Game Hub game name.
- center displays only the current status message.
- Add right-anchored progress text in this format:
- `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`
- Use existing Game Journey completion metrics/API pipeline for Journey totals.
- Derive current-tool progress from the existing completion metrics record that matches the current toolbox tool/section.
- Do not add new storage.
- Do not use browser-owned authoritative progress data.
- Preserve fullscreen bottom anchoring and existing fullscreen content bottom reserve.
- Preserve normal placement above the footer.
- Use shared Theme V2 CSS/classes only.
- Update targeted Playwright coverage for the single-row status bar, footer spacing, and fullscreen bottom reserve.
- Update targeted Playwright coverage for the right-anchored progress text and existing left/center behavior.

## Exact Targets
- `docs_build/dev/BUILD_PR.md`
- `assets/theme-v2/js/toolbox-status-bar.js`
- `assets/theme-v2/css/status.css`
- `assets/theme-v2/css/layout.css`
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_requirements-checklist.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md`
- `docs_build/dev/reports/codex_review.diff`
- `docs_build/dev/reports/codex_changed_files.txt`

## Evidence Sources
- `assets/js/shared/game-journey-api-client.js`
- `src/api/game-journey-completion-api-client.js`
- `src/dev-runtime/server/local-api-router.mjs`
- `src/dev-runtime/persistence/game-journey-completion-metrics-store.mjs`

## Out Of Scope
- No merge of PR #120.
- No reuse of stale ALFA_003 branch.
- No Game Journey API/service/repository contract changes.
- No new persistence/storage.
- No browser-owned product data as source of truth.
- No silent fallback data.
- No environment/server details in the status bar.
- No selected game purpose in the visible status bar.
- No visible status category labels in the status bar.
- No status action links in the visible status bar.
- No large banners.
- No modal messages or modal-style status messages.
- No row highlights.
- No API/service/repository contract changes.
- No browser-owned product data as source of truth.
- No inline styles, style blocks, or page-local CSS.
- No engine core changes.
- No `start_of_day` folder changes.
Expand All@@ -59,12 +63,12 @@ npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
Also verify changed source does not introduce inline styles or style blocks:

```powershell
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css assets/theme-v2/css/layout.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
```

## Artifact
Create repo-structured delta ZIP:

```text
tmp/PR_26175_ALFA_009-status-bar-single-row-rebuild_delta.zip
tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
# PR_26175_ALFA_011-status-bar-journey-progress-context Report

## Status
PASS

Added right-anchored progress context to the shared toolbox status bar while preserving the ALFA_009 left and center behavior.

## Changes
- `assets/theme-v2/js/toolbox-status-bar.js`
- Imports the existing Game Journey completion metrics API client at line 1.
- Adds current-tool-to-Journey-bucket matching at lines 7-35.
- Adds the right progress node at line 136.
- Formats progress as `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` at lines 309-350.
- Renders visible unavailable progress state without raw environment/server details at lines 354-370.
- `assets/theme-v2/css/status.css`
- Adds a three-column shared status bar grid at line 54.
- Adds right-aligned single-line progress styling at lines 94-106.
- Preserves existing fullscreen anchoring and bottom reserve rules at lines 119-150.
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- Routes `/api/game-journey/completion-metrics` through the existing server API response shape at lines 76-83.
- Verifies Game Design progress at lines 239 and 253.
- Verifies the requested Objects example at lines 275 and 279.
- Verifies right anchoring at lines 260 and 280.
- Verifies Game Hub, missing-game, Idea Board, and fullscreen behavior at lines 304, 328, 353, 371, and 398.

## Contract Notes
- No storage was added.
- No API/service/repository contract was changed.
- Progress data is read from the existing Game Journey completion metrics API pipeline.
- Browser state is not used as authoritative progress data.
- Existing selected-game ownership and left/center status bar behavior are preserved.

## Validation
- PASS: `npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1`
- PASS: `rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`

## Artifact
- `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
# PR_26175_ALFA_011 Requirements Checklist

| Requirement | Status | Evidence |
| --- | --- | --- |
| Implement right-anchored progress in the shared toolbox status bar. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:136`, `assets/theme-v2/css/status.css:94`. |
| Use format `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:309` and `:350`; tests at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:239`, `:275`. |
| Use existing Game Journey completion metrics/API pipeline. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:1`, `:337`; tests route `/api/game-journey/completion-metrics` at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:76`. |
| Do not add new storage. | PASS | No persistence files changed. |
| Do not use browser-owned authoritative progress data. | PASS | Progress is read from `readGameJourneyCompletionMetrics()` and not local/session storage. |
| Keep existing left status bar behavior from ALFA_009. | PASS | Selected game assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:237`, `:302`, `:391`. |
| Keep existing center status bar behavior from ALFA_009. | PASS | Message assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:238`, `:303`, `:370`, `:397`. |
| Preserve fullscreen bottom anchoring. | PASS | Existing CSS rules remain at `assets/theme-v2/css/status.css:119`; test coverage at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:315`. |
| Preserve normal placement above footer. | PASS | Existing status bar placement test remains at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:224`. |
| No inline styles, style blocks, or page-local CSS. | PASS | Style scan returned no matches. |
| Use Theme V2 shared CSS/classes. | PASS | Styling changes are limited to `assets/theme-v2/css/status.css`. |
| Update targeted Playwright coverage. | PASS | `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` now validates right progress text and anchoring. |
| Produce required reports and repo-structured ZIP. | PASS | PR reports added and ZIP target is `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`. |
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions assets/theme-v2/css/status.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,7 @@
.toolbox-status-bar {
--toolbox-status-bar-height: var(--space-52);
--toolbox-status-game-max: 220px;
--toolbox-status-progress-max: 34vw;
width: 100%;
min-block-size: var(--toolbox-status-bar-height);
border-block: var(--border-standard);
Expand All@@ -50,7 +51,7 @@
margin: var(--space-0) auto;
padding: var(--space-10) var(--space-0);
display: grid;
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-16);
align-items: center
}
Expand DownExpand Up@@ -90,6 +91,19 @@
overflow-wrap: anywhere
}

.toolbox-status-bar__progress {
min-width: var(--space-0);
max-width: var(--toolbox-status-progress-max);
margin: var(--space-0);
color: var(--muted);
font-size: var(--font-size-sm);
line-height: var(--line-height-tight);
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap
}

.toolbox-status-bar[data-selected-game-state="active"] {
border-color: color-mix(in srgb, var(--green) 52%, var(--line))
}
Expand DownExpand Up@@ -249,16 +263,21 @@ body.tool-focus-mode .tool-center-panel {
@media (max-width: 720px) {
.toolbox-status-bar__inner {
width: var(--container-width);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-10);
text-align: center
}

.toolbox-status-bar__game-name {
max-width: 34vw
max-width: 24vw
}

.toolbox-status-bar__message {
.toolbox-status-bar__message,
.toolbox-status-bar__progress {
font-size: var(--font-size-sm)
}

.toolbox-status-bar__progress {
max-width: 30vw
}
}
139 changes: 138 additions & 1 deletion assets/theme-v2/js/toolbox-status-bar.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
import { readGameJourneyCompletionMetrics } from "/src/api/game-journey-completion-api-client.js";
import { createServerRepositoryClient } from "/src/api/server-api-client.js";
import { getToolBySlug } from "/src/shared/toolbox/tool-metadata-inventory.js";

const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]);
const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]";
const TOOL_PROGRESS_BUCKET_BY_SLUG = Object.freeze({
"achievements": "Progression",
"assets": "Graphics",
"audio": "Audio",
"audio-effects": "Audio",
"characters": "Objects",
"colors": "Graphics",
"community": "Share",
"controls": "Controls",
"events": "Rules",
"game-configuration": "Create",
"game-design": "Design",
"game-hub": "Create",
"game-testing": "Play Test",
"hitboxes": "Objects",
"idea-board": "Idea",
"input-mapping-v2": "Controls",
"marketplace": "Share",
"messages": "Interface",
"music": "Audio",
"objects": "Objects",
"publish": "Publish",
"ratings": "Share",
"sprites": "Graphics",
"tags": "Progression",
"text-to-speech": "Audio",
"videos": "Graphics",
"voices": "Audio",
"worlds": "Worlds",
});

let repository = null;
let messageObserver = null;
Expand DownExpand Up@@ -101,7 +133,10 @@ function createStatusBar() {
message.setAttribute("role", "status");
center.append(message);

inner.append(game, center);
const progress = createText("p", "toolbox-status-bar__progress", "toolboxStatusProgress");
progress.setAttribute("aria-label", "Tool and journey progress");

inner.append(game, center, progress);
bar.append(inner);
return bar;
}
Expand DownExpand Up@@ -233,6 +268,107 @@ function classifyToolContext(messageText, state, required) {
return { kind: "action" };
}

function normalizeTextKey(value) {
return String(value || "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "");
}

function formatToolSlug(slug) {
return String(slug || "Tool")
.split(/[-_\s]+/)
.filter(Boolean)
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
.join(" ") || "Tool";
}

function currentToolContext() {
const slug = toolSlugFromPath(mountOptions.pagePath);
const tool = getToolBySlug(slug);
return {
label: tool?.shortLabel || tool?.displayName || tool?.name || formatToolSlug(slug),
slug,
};
}

function normalizeProgressRecord(record) {
const total = Math.max(0, Number(record?.plannedCount) || 0);
const complete = Math.max(0, Math.min(Number(record?.completedCount) || 0, total));
const percent = Number.isFinite(Number(record?.percentComplete))
? Math.max(0, Math.min(Number(record.percentComplete), 100))
: total > 0
? Math.round((complete / total) * 100)
: 0;
return {
complete,
percent,
total,
};
}

function formatProgressRecord(label, record) {
const progress = normalizeProgressRecord(record);
return `${label} ${progress.complete}/${progress.total} (${progress.percent}%)`;
}

function findMetricForCurrentTool(snapshot, toolContext) {
if (toolContext.slug === "game-journey") {
return snapshot;
}

const records = Array.isArray(snapshot?.records) ? snapshot.records : [];
const explicitBucketName = TOOL_PROGRESS_BUCKET_BY_SLUG[toolContext.slug];
const explicitBucket = normalizeTextKey(explicitBucketName);
const toolLabel = normalizeTextKey(toolContext.label);
const toolSlug = normalizeTextKey(toolContext.slug);

return records.find((metric) => {
const bucketName = normalizeTextKey(metric?.bucketName);
return bucketName && (
bucketName === explicitBucket ||
bucketName === toolLabel ||
bucketName === toolSlug
);
}) || null;
}

function resolveProgressContext() {
const toolContext = currentToolContext();
const snapshot = readGameJourneyCompletionMetrics();
const currentMetric = findMetricForCurrentTool(snapshot, toolContext);
const journeyText = formatProgressRecord("Journey", snapshot);

if (!currentMetric) {
return {
state: "unmapped",
text: `${toolContext.label} progress unavailable | ${journeyText}`,
};
}

return {
state: "active",
text: `${formatProgressRecord(toolContext.label, currentMetric)} | ${journeyText}`,
};
}

function renderProgressContext(bar) {
const progress = bar.querySelector("[data-toolbox-status-progress]");
if (!progress) {
return;
}

try {
const context = resolveProgressContext();
bar.dataset.toolboxProgressState = context.state;
progress.textContent = context.text;
progress.removeAttribute("title");
} catch (error) {
bar.dataset.toolboxProgressState = "error";
progress.textContent = "Progress unavailable";
progress.removeAttribute("title");
}
}

function renderSelectedGame(bar, selectedGame, state, messageText) {
const required = pageRequiresSelectedGame();
const name = bar.querySelector("[data-toolbox-selected-game-name]");
Expand All@@ -247,6 +383,7 @@ function renderSelectedGame(bar, selectedGame, state, messageText) {
bar.dataset.selectedGameState = state;
bar.dataset.selectedGameRequired = String(required);
bar.dataset.toolboxStatusContextKind = context.kind;
renderProgressContext(bar);

if (selectedGame) {
name.textContent = selectedGame.name;
Expand Down
56 changes: 30 additions & 26 deletions docs_build/dev/BUILD_PR.md
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
# PR_26175_ALFA_009-status-bar-single-row-rebuild
# PR_26175_ALFA_011-status-bar-journey-progress-context

## Purpose
Rebuild the shared toolbox status bar on current `main` so it is a single-row creator context bar.
Add right-anchored progress context to the shared toolbox status bar using the existing Game Journey completion metrics/API pipeline.

## Source Of Truth
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_009-status-bar-single-row-rebuild`.
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_011-status-bar-journey-progress-context`.

## Exact Scope
- Display only the selected Game Hub game name on the left side of the toolbox status bar.
- Display only the current status message in the center of the toolbox status bar.
- Remove visible status bar labels: `Selected Game Name`, `Selected Game Purpose`, `Save State`, `Tool Action`, `Warning`, and `Error`.
- Remove selected-game purpose from the visible status bar.
- Preserve normal status bar placement above the footer.
- Remove extra status bar/footer spacing so the shared footer top padding resolves to `0px`.
- Preserve fullscreen/tool display mode bottom anchoring.
- Ensure fullscreen center scrollable content stops above the fixed status bar.
- Ensure fullscreen tool content is not hidden behind the status bar.
- Preserve Idea Board selected-game filtering exclusion.
- Preserve Game Hub as selected-game owner through the existing repository contract.
- Preserve the ALFA_009 single-row toolbox status bar behavior:
- left side displays only the selected Game Hub game name.
- center displays only the current status message.
- Add right-anchored progress text in this format:
- `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`
- Use existing Game Journey completion metrics/API pipeline for Journey totals.
- Derive current-tool progress from the existing completion metrics record that matches the current toolbox tool/section.
- Do not add new storage.
- Do not use browser-owned authoritative progress data.
- Preserve fullscreen bottom anchoring and existing fullscreen content bottom reserve.
- Preserve normal placement above the footer.
- Use shared Theme V2 CSS/classes only.
- Update targeted Playwright coverage for the single-row status bar, footer spacing, and fullscreen bottom reserve.
- Update targeted Playwright coverage for the right-anchored progress text and existing left/center behavior.

## Exact Targets
- `docs_build/dev/BUILD_PR.md`
- `assets/theme-v2/js/toolbox-status-bar.js`
- `assets/theme-v2/css/status.css`
- `assets/theme-v2/css/layout.css`
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_requirements-checklist.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md`
- `docs_build/dev/reports/codex_review.diff`
- `docs_build/dev/reports/codex_changed_files.txt`

## Evidence Sources
- `assets/js/shared/game-journey-api-client.js`
- `src/api/game-journey-completion-api-client.js`
- `src/dev-runtime/server/local-api-router.mjs`
- `src/dev-runtime/persistence/game-journey-completion-metrics-store.mjs`

## Out Of Scope
- No merge of PR #120.
- No reuse of stale ALFA_003 branch.
- No Game Journey API/service/repository contract changes.
- No new persistence/storage.
- No browser-owned product data as source of truth.
- No silent fallback data.
- No environment/server details in the status bar.
- No selected game purpose in the visible status bar.
- No visible status category labels in the status bar.
- No status action links in the visible status bar.
- No large banners.
- No modal messages or modal-style status messages.
- No row highlights.
- No API/service/repository contract changes.
- No browser-owned product data as source of truth.
- No inline styles, style blocks, or page-local CSS.
- No engine core changes.
- No `start_of_day` folder changes.
Expand All@@ -59,12 +63,12 @@ npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
Also verify changed source does not introduce inline styles or style blocks:

```powershell
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css assets/theme-v2/css/layout.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
```

## Artifact
Create repo-structured delta ZIP:

```text
tmp/PR_26175_ALFA_009-status-bar-single-row-rebuild_delta.zip
tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
# PR_26175_ALFA_011-status-bar-journey-progress-context Report

## Status
PASS

Added right-anchored progress context to the shared toolbox status bar while preserving the ALFA_009 left and center behavior.

## Changes
- `assets/theme-v2/js/toolbox-status-bar.js`
- Imports the existing Game Journey completion metrics API client at line 1.
- Adds current-tool-to-Journey-bucket matching at lines 7-35.
- Adds the right progress node at line 136.
- Formats progress as `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` at lines 309-350.
- Renders visible unavailable progress state without raw environment/server details at lines 354-370.
- `assets/theme-v2/css/status.css`
- Adds a three-column shared status bar grid at line 54.
- Adds right-aligned single-line progress styling at lines 94-106.
- Preserves existing fullscreen anchoring and bottom reserve rules at lines 119-150.
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- Routes `/api/game-journey/completion-metrics` through the existing server API response shape at lines 76-83.
- Verifies Game Design progress at lines 239 and 253.
- Verifies the requested Objects example at lines 275 and 279.
- Verifies right anchoring at lines 260 and 280.
- Verifies Game Hub, missing-game, Idea Board, and fullscreen behavior at lines 304, 328, 353, 371, and 398.

## Contract Notes
- No storage was added.
- No API/service/repository contract was changed.
- Progress data is read from the existing Game Journey completion metrics API pipeline.
- Browser state is not used as authoritative progress data.
- Existing selected-game ownership and left/center status bar behavior are preserved.

## Validation
- PASS: `npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1`
- PASS: `rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`

## Artifact
- `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
# PR_26175_ALFA_011 Requirements Checklist

| Requirement | Status | Evidence |
| --- | --- | --- |
| Implement right-anchored progress in the shared toolbox status bar. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:136`, `assets/theme-v2/css/status.css:94`. |
| Use format `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:309` and `:350`; tests at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:239`, `:275`. |
| Use existing Game Journey completion metrics/API pipeline. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:1`, `:337`; tests route `/api/game-journey/completion-metrics` at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:76`. |
| Do not add new storage. | PASS | No persistence files changed. |
| Do not use browser-owned authoritative progress data. | PASS | Progress is read from `readGameJourneyCompletionMetrics()` and not local/session storage. |
| Keep existing left status bar behavior from ALFA_009. | PASS | Selected game assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:237`, `:302`, `:391`. |
| Keep existing center status bar behavior from ALFA_009. | PASS | Message assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:238`, `:303`, `:370`, `:397`. |
| Preserve fullscreen bottom anchoring. | PASS | Existing CSS rules remain at `assets/theme-v2/css/status.css:119`; test coverage at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:315`. |
| Preserve normal placement above footer. | PASS | Existing status bar placement test remains at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:224`. |
| No inline styles, style blocks, or page-local CSS. | PASS | Style scan returned no matches. |
| Use Theme V2 shared CSS/classes. | PASS | Styling changes are limited to `assets/theme-v2/css/status.css`. |
| Update targeted Playwright coverage. | PASS | `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` now validates right progress text and anchoring. |
| Produce required reports and repo-structured ZIP. | PASS | PR reports added and ZIP target is `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`. |
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions assets/theme-v2/css/status.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,7 @@
.toolbox-status-bar {
--toolbox-status-bar-height: var(--space-52);
--toolbox-status-game-max: 220px;
--toolbox-status-progress-max: 34vw;
width: 100%;
min-block-size: var(--toolbox-status-bar-height);
border-block: var(--border-standard);
Expand All@@ -50,7 +51,7 @@
margin: var(--space-0) auto;
padding: var(--space-10) var(--space-0);
display: grid;
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-16);
align-items: center
}
Expand DownExpand Up@@ -90,6 +91,19 @@
overflow-wrap: anywhere
}

.toolbox-status-bar__progress {
min-width: var(--space-0);
max-width: var(--toolbox-status-progress-max);
margin: var(--space-0);
color: var(--muted);
font-size: var(--font-size-sm);
line-height: var(--line-height-tight);
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap
}

.toolbox-status-bar[data-selected-game-state="active"] {
border-color: color-mix(in srgb, var(--green) 52%, var(--line))
}
Expand DownExpand Up@@ -249,16 +263,21 @@ body.tool-focus-mode .tool-center-panel {
@media (max-width: 720px) {
.toolbox-status-bar__inner {
width: var(--container-width);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr);
grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content);
gap: var(--space-10);
text-align: center
}

.toolbox-status-bar__game-name {
max-width: 34vw
max-width: 24vw
}

.toolbox-status-bar__message {
.toolbox-status-bar__message,
.toolbox-status-bar__progress {
font-size: var(--font-size-sm)
}

.toolbox-status-bar__progress {
max-width: 30vw
}
}
139 changes: 138 additions & 1 deletion assets/theme-v2/js/toolbox-status-bar.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
import { readGameJourneyCompletionMetrics } from "/src/api/game-journey-completion-api-client.js";
import { createServerRepositoryClient } from "/src/api/server-api-client.js";
import { getToolBySlug } from "/src/shared/toolbox/tool-metadata-inventory.js";

const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]);
const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]";
const TOOL_PROGRESS_BUCKET_BY_SLUG = Object.freeze({
"achievements": "Progression",
"assets": "Graphics",
"audio": "Audio",
"audio-effects": "Audio",
"characters": "Objects",
"colors": "Graphics",
"community": "Share",
"controls": "Controls",
"events": "Rules",
"game-configuration": "Create",
"game-design": "Design",
"game-hub": "Create",
"game-testing": "Play Test",
"hitboxes": "Objects",
"idea-board": "Idea",
"input-mapping-v2": "Controls",
"marketplace": "Share",
"messages": "Interface",
"music": "Audio",
"objects": "Objects",
"publish": "Publish",
"ratings": "Share",
"sprites": "Graphics",
"tags": "Progression",
"text-to-speech": "Audio",
"videos": "Graphics",
"voices": "Audio",
"worlds": "Worlds",
});

let repository = null;
let messageObserver = null;
Expand DownExpand Up@@ -101,7 +133,10 @@ function createStatusBar() {
message.setAttribute("role", "status");
center.append(message);

inner.append(game, center);
const progress = createText("p", "toolbox-status-bar__progress", "toolboxStatusProgress");
progress.setAttribute("aria-label", "Tool and journey progress");

inner.append(game, center, progress);
bar.append(inner);
return bar;
}
Expand DownExpand Up@@ -233,6 +268,107 @@ function classifyToolContext(messageText, state, required) {
return { kind: "action" };
}

function normalizeTextKey(value) {
return String(value || "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "");
}

function formatToolSlug(slug) {
return String(slug || "Tool")
.split(/[-_\s]+/)
.filter(Boolean)
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
.join(" ") || "Tool";
}

function currentToolContext() {
const slug = toolSlugFromPath(mountOptions.pagePath);
const tool = getToolBySlug(slug);
return {
label: tool?.shortLabel || tool?.displayName || tool?.name || formatToolSlug(slug),
slug,
};
}

function normalizeProgressRecord(record) {
const total = Math.max(0, Number(record?.plannedCount) || 0);
const complete = Math.max(0, Math.min(Number(record?.completedCount) || 0, total));
const percent = Number.isFinite(Number(record?.percentComplete))
? Math.max(0, Math.min(Number(record.percentComplete), 100))
: total > 0
? Math.round((complete / total) * 100)
: 0;
return {
complete,
percent,
total,
};
}

function formatProgressRecord(label, record) {
const progress = normalizeProgressRecord(record);
return `${label} ${progress.complete}/${progress.total} (${progress.percent}%)`;
}

function findMetricForCurrentTool(snapshot, toolContext) {
if (toolContext.slug === "game-journey") {
return snapshot;
}

const records = Array.isArray(snapshot?.records) ? snapshot.records : [];
const explicitBucketName = TOOL_PROGRESS_BUCKET_BY_SLUG[toolContext.slug];
const explicitBucket = normalizeTextKey(explicitBucketName);
const toolLabel = normalizeTextKey(toolContext.label);
const toolSlug = normalizeTextKey(toolContext.slug);

return records.find((metric) => {
const bucketName = normalizeTextKey(metric?.bucketName);
return bucketName && (
bucketName === explicitBucket ||
bucketName === toolLabel ||
bucketName === toolSlug
);
}) || null;
}

function resolveProgressContext() {
const toolContext = currentToolContext();
const snapshot = readGameJourneyCompletionMetrics();
const currentMetric = findMetricForCurrentTool(snapshot, toolContext);
const journeyText = formatProgressRecord("Journey", snapshot);

if (!currentMetric) {
return {
state: "unmapped",
text: `${toolContext.label} progress unavailable | ${journeyText}`,
};
}

return {
state: "active",
text: `${formatProgressRecord(toolContext.label, currentMetric)} | ${journeyText}`,
};
}

function renderProgressContext(bar) {
const progress = bar.querySelector("[data-toolbox-status-progress]");
if (!progress) {
return;
}

try {
const context = resolveProgressContext();
bar.dataset.toolboxProgressState = context.state;
progress.textContent = context.text;
progress.removeAttribute("title");
} catch (error) {
bar.dataset.toolboxProgressState = "error";
progress.textContent = "Progress unavailable";
progress.removeAttribute("title");
}
}

function renderSelectedGame(bar, selectedGame, state, messageText) {
const required = pageRequiresSelectedGame();
const name = bar.querySelector("[data-toolbox-selected-game-name]");
Expand All@@ -247,6 +383,7 @@ function renderSelectedGame(bar, selectedGame, state, messageText) {
bar.dataset.selectedGameState = state;
bar.dataset.selectedGameRequired = String(required);
bar.dataset.toolboxStatusContextKind = context.kind;
renderProgressContext(bar);

if (selectedGame) {
name.textContent = selectedGame.name;
Expand Down
56 changes: 30 additions & 26 deletions docs_build/dev/BUILD_PR.md
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
# PR_26175_ALFA_009-status-bar-single-row-rebuild
# PR_26175_ALFA_011-status-bar-journey-progress-context

## Purpose
Rebuild the shared toolbox status bar on current `main` so it is a single-row creator context bar.
Add right-anchored progress context to the shared toolbox status bar using the existing Game Journey completion metrics/API pipeline.

## Source Of Truth
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_009-status-bar-single-row-rebuild`.
This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_011-status-bar-journey-progress-context`.

## Exact Scope
- Display only the selected Game Hub game name on the left side of the toolbox status bar.
- Display only the current status message in the center of the toolbox status bar.
- Remove visible status bar labels: `Selected Game Name`, `Selected Game Purpose`, `Save State`, `Tool Action`, `Warning`, and `Error`.
- Remove selected-game purpose from the visible status bar.
- Preserve normal status bar placement above the footer.
- Remove extra status bar/footer spacing so the shared footer top padding resolves to `0px`.
- Preserve fullscreen/tool display mode bottom anchoring.
- Ensure fullscreen center scrollable content stops above the fixed status bar.
- Ensure fullscreen tool content is not hidden behind the status bar.
- Preserve Idea Board selected-game filtering exclusion.
- Preserve Game Hub as selected-game owner through the existing repository contract.
- Preserve the ALFA_009 single-row toolbox status bar behavior:
- left side displays only the selected Game Hub game name.
- center displays only the current status message.
- Add right-anchored progress text in this format:
- `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`
- Use existing Game Journey completion metrics/API pipeline for Journey totals.
- Derive current-tool progress from the existing completion metrics record that matches the current toolbox tool/section.
- Do not add new storage.
- Do not use browser-owned authoritative progress data.
- Preserve fullscreen bottom anchoring and existing fullscreen content bottom reserve.
- Preserve normal placement above the footer.
- Use shared Theme V2 CSS/classes only.
- Update targeted Playwright coverage for the single-row status bar, footer spacing, and fullscreen bottom reserve.
- Update targeted Playwright coverage for the right-anchored progress text and existing left/center behavior.

## Exact Targets
- `docs_build/dev/BUILD_PR.md`
- `assets/theme-v2/js/toolbox-status-bar.js`
- `assets/theme-v2/css/status.css`
- `assets/theme-v2/css/layout.css`
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_requirements-checklist.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md`
- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md`
- `docs_build/dev/reports/codex_review.diff`
- `docs_build/dev/reports/codex_changed_files.txt`

## Evidence Sources
- `assets/js/shared/game-journey-api-client.js`
- `src/api/game-journey-completion-api-client.js`
- `src/dev-runtime/server/local-api-router.mjs`
- `src/dev-runtime/persistence/game-journey-completion-metrics-store.mjs`

## Out Of Scope
- No merge of PR #120.
- No reuse of stale ALFA_003 branch.
- No Game Journey API/service/repository contract changes.
- No new persistence/storage.
- No browser-owned product data as source of truth.
- No silent fallback data.
- No environment/server details in the status bar.
- No selected game purpose in the visible status bar.
- No visible status category labels in the status bar.
- No status action links in the visible status bar.
- No large banners.
- No modal messages or modal-style status messages.
- No row highlights.
- No API/service/repository contract changes.
- No browser-owned product data as source of truth.
- No inline styles, style blocks, or page-local CSS.
- No engine core changes.
- No `start_of_day` folder changes.
Expand All@@ -59,12 +63,12 @@ npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
Also verify changed source does not introduce inline styles or style blocks:

```powershell
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css assets/theme-v2/css/layout.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs
```

## Artifact
Create repo-structured delta ZIP:

```text
tmp/PR_26175_ALFA_009-status-bar-single-row-rebuild_delta.zip
tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
# PR_26175_ALFA_011-status-bar-journey-progress-context Report

## Status
PASS

Added right-anchored progress context to the shared toolbox status bar while preserving the ALFA_009 left and center behavior.

## Changes
- `assets/theme-v2/js/toolbox-status-bar.js`
- Imports the existing Game Journey completion metrics API client at line 1.
- Adds current-tool-to-Journey-bucket matching at lines 7-35.
- Adds the right progress node at line 136.
- Formats progress as `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` at lines 309-350.
- Renders visible unavailable progress state without raw environment/server details at lines 354-370.
- `assets/theme-v2/css/status.css`
- Adds a three-column shared status bar grid at line 54.
- Adds right-aligned single-line progress styling at lines 94-106.
- Preserves existing fullscreen anchoring and bottom reserve rules at lines 119-150.
- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`
- Routes `/api/game-journey/completion-metrics` through the existing server API response shape at lines 76-83.
- Verifies Game Design progress at lines 239 and 253.
- Verifies the requested Objects example at lines 275 and 279.
- Verifies right anchoring at lines 260 and 280.
- Verifies Game Hub, missing-game, Idea Board, and fullscreen behavior at lines 304, 328, 353, 371, and 398.

## Contract Notes
- No storage was added.
- No API/service/repository contract was changed.
- Progress data is read from the existing Game Journey completion metrics API pipeline.
- Browser state is not used as authoritative progress data.
- Existing selected-game ownership and left/center status bar behavior are preserved.

## Validation
- PASS: `npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1`
- PASS: `rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs`

## Artifact
- `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
# PR_26175_ALFA_011 Requirements Checklist

| Requirement | Status | Evidence |
| --- | --- | --- |
| Implement right-anchored progress in the shared toolbox status bar. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:136`, `assets/theme-v2/css/status.css:94`. |
| Use format `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:309` and `:350`; tests at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:239`, `:275`. |
| Use existing Game Journey completion metrics/API pipeline. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:1`, `:337`; tests route `/api/game-journey/completion-metrics` at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:76`. |
| Do not add new storage. | PASS | No persistence files changed. |
| Do not use browser-owned authoritative progress data. | PASS | Progress is read from `readGameJourneyCompletionMetrics()` and not local/session storage. |
| Keep existing left status bar behavior from ALFA_009. | PASS | Selected game assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:237`, `:302`, `:391`. |
| Keep existing center status bar behavior from ALFA_009. | PASS | Message assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:238`, `:303`, `:370`, `:397`. |
| Preserve fullscreen bottom anchoring. | PASS | Existing CSS rules remain at `assets/theme-v2/css/status.css:119`; test coverage at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:315`. |
| Preserve normal placement above footer. | PASS | Existing status bar placement test remains at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:224`. |
| No inline styles, style blocks, or page-local CSS. | PASS | Style scan returned no matches. |
| Use Theme V2 shared CSS/classes. | PASS | Styling changes are limited to `assets/theme-v2/css/status.css`. |
| Update targeted Playwright coverage. | PASS | `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` now validates right progress text and anchoring. |
| Produce required reports and repo-structured ZIP. | PASS | PR reports added and ZIP target is `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`. |
Loading
Loading