- commit
- c00fc51be3c0fc4b65b5d727d0997b4306ff164b
- parent
- 2dd42fc28864aa8a3031ba340f02d3ef33f22e52
- author
- Dami <dami.etoile@gmail.com>
| M | src/ | 9 | +++++++++ |
| M | src/ | 11 | +++-------- |
| M | tests/ | 10 | +++++++++- |
@@ -79,6 +79,15 @@ function accentColor(world, colors) {
7979 return ACCENTS.has(token) ? colors[token] || colors.fg : colors.fg;8080 }8181 82+export function worldCardDescription(world) {83+ const details = [84+ worldValue(world, 'name', 'World'),85+ worldValue(world, 'tagline'),86+ worldValue(world, 'author') ? `By ${worldValue(world, 'author')}` : '',87+ ].filter(Boolean).map(value => /[.!?]$/.test(value) ? value : `${value}.`);88+ return `${details.join(' ')} Press Enter to enter.`;89+}90+8291 function drawKeycap(ctx, x, y, options = {}) {8392 const key = 'ENTER';8493 const label = 'enter world';@@ -13,7 +13,7 @@ import { pauseMenuItems } from "./stage/dispatch.js";
1313 import { createWinFlow, WIN_ENTRANCE_END } from './stage/win-flow.js';1414 import { SCENE_MOTION_TIMING, sceneMotionPhase } from './stage/scene-motion.js';1515 import { installStageEvents } from './stage/events.js';16-import { drawWorldCard } from './render/world-card.js';16+import { drawWorldCard, worldCardDescription } from './render/world-card.js';1717 1818 const SCENES = {1919 title: titleScene,@@ -295,7 +295,7 @@ export function createStage(options = {}) {
295295 if (!world?.id || worldCardsSeen.has(world.id)) return false;296296 worldCardsSeen.add(world.id);297297 worldCard = { world, origin, openedAt: visualNow(), geometry: null };298- announce(`${world.name || 'World'}. ${world.tagline || ''} by ${world.author || 'unknown author'}. Press Enter to enter.`.trim());298+ announce(worldCardDescription(world));299299 render();300300 if (!reducedMotion) visualFrame(animateVisuals);301301 return true;@@ -461,12 +461,7 @@ export function createStage(options = {}) {
461461 reducedMotion,462462 coarsePointer: globalThis.matchMedia?.("(pointer: coarse)").matches ?? false,463463 });464- const cardDescription = [465- worldCard.world.name || 'World',466- worldCard.world.tagline,467- worldCard.world.author ? `by ${worldCard.world.author}` : null,468- ].filter(Boolean).join('. ');469- canvas.setAttribute("aria-label", `${cardDescription}. Press Enter to enter.`);464+ canvas.setAttribute("aria-label", worldCardDescription(worldCard.world));470465 }471466 } else if (scene === "pause") {472467 renderPauseScene(ctx, {@@ -1,6 +1,6 @@
11 import test from 'node:test';22 import assert from 'node:assert/strict';3-import { drawWorldCard, WORLD_CARD_COLORS } from '../src/render/world-card.js';3+import { drawWorldCard, worldCardDescription, WORLD_CARD_COLORS } from '../src/render/world-card.js';44 55 function context() {66 const calls = [];@@ -82,6 +82,14 @@ test('world card omits an absent byline while preserving proof metadata', () =>
8282 assert.ok(!labels.some((label) => String(label).includes('by ')));8383 });8484 85+test('world-card narration shares the visible optional-author copy', () => {86+ assert.equal(87+ worldCardDescription({ name: 'Crossword', tagline: 'Read across and down.', author: 'World team' }),88+ 'Crossword. Read across and down. By World team. Press Enter to enter.',89+ );90+ assert.equal(worldCardDescription({ name: 'Uncredited' }), 'Uncredited. Press Enter to enter.');91+});92+8593 test('reduced motion settles the iris without clipping or changing card geometry', () => {8694 const animated = drawWorldCard(context(), { name: 'Core', glyph: '🐈' }, {8795 width: 640, height: 420, irisProgress: 0.25,