docs: Add live demos and update documentation content#17
docs: Add live demos and update documentation content#17canerdogan wants to merge 6 commits intomainfrom
Conversation
- Add 12 new interactive demo HTML files (2048, cookie-clicker, gem-collector, neon-pong, quiz-game, rps-game, simon-says, slingshot, whack-a-mole, etc.) - Update 24 documentation pages with LiveDemo components placed after title - Add demo placement rule to CLAUDE.md for consistent future docs - Enhance changelog, architecture, configuration, and getting-started pages - Improve rendering, physics, input, layout, and UI component docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add esc() helper to sanitize DOM values before interpolating into innerHTML template string. Prevents potential XSS via DOM manipulation of range/select input values. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
||
| // GameByte game instance and factory | ||
| let game; | ||
| const factory = graphics(); |
There was a problem hiding this comment.
Critical Bug: GraphicsEngine not initialized
The graphics() function is called at top-level script scope (line 65) before the game engine is initialized. This will throw an immediate error when the page loads:
Error: GraphicsEngine not initialized. Call GraphicsEngine.initialize(renderingMode) first.
Why this happens:
- Line 65:
const factory = graphics();executes synchronously when the script loads - Line 478:
initApp()runs later, which callscreateGame() createGame()initializes GraphicsEngine, but that hasn't happened yet when line 65 executes
Fix: Move the factory initialization inside initApp() after createGame():
async function initApp() {
await loadFrameworkFont();
game = await createGame({
container: '#game-container',
width: WIDTH,
height: HEIGHT,
mode: '2d',
backgroundColor: 0x0f172a
});
const factory = graphics(); // ✅ Now safe to call
// ... rest of initialization
}| if (gameState !== 'playing') return; | ||
|
|
||
| const isBomb = Math.random() < 0.15; | ||
| const gem = factory.createContainer(); |
There was a problem hiding this comment.
Critical Bug: GameTopBar API mismatch
This demo (and 5 others) use non-existent methods and config properties on GameTopBar.
The actual GameTopBar API (GameTopBar.ts:35-42):
Config interface:
export interface GameTopBarConfig {
width: number; // REQUIRED
height?: number;
padding?: number;
resources?: ResourceItemConfig[];
showSettings?: boolean;
onSettingsClick?: () => void;
}Public methods:
updateResource(type: ResourceType, value: number, animate?: boolean)setPosition(x: number, y: number)getContainer()destroy()
Issues in this file:
- ❌ Constructor missing required
widthparameter - ❌ Constructor passes non-existent properties:
title,showLives - ❌ Calls non-existent methods:
topBar.setScore(),topBar.setCustomValue()
Other affected demos:
2048-demo.html: CallstopBar.updateScore()cookie-clicker-demo.html: CallstopBar.updateTitle()neon-pong-demo.html: CallstopBar.updateLeftText(),topBar.updateRightText()simon-says-demo.html: CallstopBar.updateLevel()whack-a-mole-demo.html: CallstopBar.updateScore()
All of these method calls will throw TypeError: topBar.xxx is not a function at runtime.
Fix: Use the actual API with updateResource():
const topBar = new GameTopBar({
width: WIDTH,
resources: [
{ type: 'score', value: 0, label: 'Score' },
{ type: 'lives', value: 3, label: 'Lives' }
]
});
// Update score
topBar.updateResource('score', score, true);
// Update lives
topBar.updateResource('lives', lives, true);| height: 60 | ||
| }); | ||
| playBtn.x = 150; |
There was a problem hiding this comment.
Bug: Position set on EventEmitter instead of container
Setting x and y directly on the GameStyleButton instance doesn't work. The button will appear at position (0, 0) instead of (150, 350).
Why: GameStyleButton extends EventEmitter (GameStyleButton.ts:66). EventEmitter doesn't have display properties like x and y. Setting these properties creates arbitrary fields on the EventEmitter object, but doesn't affect the internal display container.
Fix: Use setPosition() or position the container directly:
| height: 60 | |
| }); | |
| playBtn.x = 150; | |
| const playBtn = new GameStyleButton({ | |
| text: 'PLAY', | |
| width: 200, | |
| height: 60 | |
| }); | |
| playBtn.setPosition(150, 350); | |
| playBtn.on('click', startGame); |
Or:
playBtn.getContainer().x = 150;
playBtn.getContainer().y = 350;| hole.container = moleContainer; | ||
| hole.type = moleType; | ||
| hole.timer = moleType.duration / difficultyMultiplier; |
There was a problem hiding this comment.
Bug: Timer unit mismatch (milliseconds vs seconds)
Mole timers are set in milliseconds but decremented in seconds, causing a ~1000x timing error. A mole intended to appear for 1.2 seconds will actually persist for ~20 minutes.
The issue:
Line 55-57: Durations defined in milliseconds:
const MoleType = {
NORMAL: { color: 0x8B4513, points: 10, duration: 1200 }, // 1200ms = 1.2s
GOLDEN: { color: 0xFFD700, points: 50, duration: 1500 }, // 1500ms = 1.5s
ANGRY: { color: 0xDC143C, points: 25, duration: 800 } // 800ms = 0.8s
};Line 299: Timer set to millisecond value:
hole.timer = moleType.duration / difficultyMultiplier; // e.g., 1200Line 422: deltaTime is in seconds:
const deltaTime = deltaMS / 1000; // ~0.016 at 60fpsLine 453: Timer decremented in seconds:
hole.timer -= deltaTime; // 1200 - 0.016 = 1199.984Result: It takes 1200 / 0.016 = 75,000 frames (~1,250 seconds) for a NORMAL mole to disappear, instead of the intended 1.2 seconds.
Fix: Convert durations to seconds when setting the timer:
| hole.container = moleContainer; | |
| hole.type = moleType; | |
| hole.timer = moleType.duration / difficultyMultiplier; | |
| const moleType = getRandomMoleType(); | |
| hole.moleType = moleType; | |
| hole.timer = moleType.duration / 1000 / difficultyMultiplier; // Convert ms to seconds | |
| hole.health = hole.moleType === MoleType.ANGRY ? 2 : 1; |
| checks.forEach((item, i) => { | ||
| const t = factory.createText(`${item.done ? '\u2705' : '\u274C'} ${item.text}`, { | ||
| fontSize: 12, | ||
| fill: item.done ? 0x4ade80 : 0xef4444 | ||
| }); | ||
| t.x = 20; | ||
| t.y = 300 + i * 0; | ||
| // Stack at bottom-left | ||
| }); |
There was a problem hiding this comment.
Bug: Checklist items invisible and overlapping
The installation verification checklist has two bugs:
-
Items never added to stage: The text elements are created but never appended with
stage.addChild(t), so they'll never be visible. -
Zero vertical spacing:
i * 0always equals 0, so all items will be at the same y-position (300) and overlap.
Fix:
| checks.forEach((item, i) => { | |
| const t = factory.createText(`${item.done ? '\u2705' : '\u274C'} ${item.text}`, { | |
| fontSize: 12, | |
| fill: item.done ? 0x4ade80 : 0xef4444 | |
| }); | |
| t.x = 20; | |
| t.y = 300 + i * 0; | |
| // Stack at bottom-left | |
| }); | |
| checks.forEach((check, i) => { | |
| const t = factory.createText(`✓ ${check}`, { | |
| fontSize: 16, | |
| fill: 0x4CAF50, | |
| fontFamily: 'Lilita One, sans-serif' | |
| }); | |
| t.x = 30; | |
| t.y = 300 + i * 18; // 18px spacing between items | |
| stage.addChild(t); | |
| }); |
docs-site/docs/overview.md
Outdated
| ## Live Demo | ||
| ## Live Demos | ||
|
|
||
| import LiveDemo from '@site/src/components/LiveDemo'; |
There was a problem hiding this comment.
CLAUDE.md violation: Import placement
The import LiveDemo statement should appear at the top of the file (after frontmatter, before the main title), not after a section heading.
CLAUDE.md rule (CLAUDE.md:320-321):
import LiveDemogoes after frontmatter, before the# Title
Current structure:
---
frontmatter
---
# GameByte Framework
[content...]
## Live Demos <-- Section heading
import LiveDemo <-- Import here ❌Expected structure:
---
frontmatter
---
import LiveDemo <-- Import here ✅
# GameByte Framework
[content...]| <div id="game-container"></div> | ||
| <div class="info">Arrow keys or swipe to slide tiles. Merge equal numbers!</div> | ||
| <script src="https://cdn.jsdelivr.net/npm/pixi.js@8/dist/pixi.min.js"></script> | ||
| <script src="/gamebyte.umd.js"></script> |
There was a problem hiding this comment.
CLAUDE.md violation: Inconsistent UMD bundle path
This demo (and 8 others) use /gamebyte.umd.js instead of the documented pattern ../gamebyte.umd.js.
CLAUDE.md rule (CLAUDE.md:324):
New demos should follow the existing pattern: Pixi CDN +
../gamebyte.umd.js, dark theme, self-contained
Affected demos using /gamebyte.umd.js:
- 2048-demo.html
- cookie-clicker-demo.html
- gem-collector-demo.html
- neon-pong-demo.html
- quiz-game-demo.html
- rps-game-demo.html
- simon-says-demo.html
- slingshot-game-demo.html
- whack-a-mole-demo.html
Compliant demos using ../gamebyte.umd.js:
- architecture-demo.html ✅
- config-playground-demo.html ✅
- hello-world-demo.html ✅
Fix: Use relative path for consistency and portability:
| <script src="/gamebyte.umd.js"></script> | |
| <script src="../gamebyte.umd.js"></script> |
- UIAnimationSystem: Guard setNestedProperty against prototype pollution (__proto__, constructor, prototype keys rejected) - GameSplash: Sanitize cfg.logoUrl in HTML template, fix incomplete style-tag stripping with iterative replacement loop - PixiVersionDetection: Add input length guard to prevent regex backtracking - archero-menu-demo: Add SRI integrity hash for GSAP CDN script - Rebuild UMD bundle with all source fixes applied Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CodeQL fixes: - UIAnimationSystem: Use inline key checks + hasOwnProperty for prototype pollution guard (CodeQL #11) - GameSplash: Use indexOf-based loop instead of regex for style-tag stripping to avoid polynomial backtracking (CodeQL #13) Demo bug fixes: - gem-collector: Move graphics() after createGame(), fix button positioning with getContainer().position.set() - whack-a-mole: Convert timer from ms to seconds (duration/1000) - hello-world: Add stage.addChild(t) and fix i*0 → i*18 spacing GameTopBar API alignment (6 demos): - Replace non-existent methods (updateScore, updateTitle, updateLevel, updateLeftText, updateRightText, setScore, setCustomValue) with actual API: resources[] config + updateResource(type, value) - Affected: 2048, cookie-clicker, gem-collector, neon-pong, simon-says, slingshot-game, whack-a-mole Standards compliance: - Fix UMD path in 9 demos: /gamebyte.umd.js → ../gamebyte.umd.js - Move import LiveDemo to after frontmatter in overview.md - Rebuild UMD bundle with source fixes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ion, prototype guard - GameSplash: Replace regex-based style tag stripping with pure indexOf+substring operations to fully resolve CodeQL polynomial regex alert - GameSplash: Add URL scheme allowlist for logoUrl to block javascript:/data: URIs - UIAnimationSystem: Add prototype pollution guard to getNestedProperty (matching setNestedProperty) - archero-menu-demo: Fix UMD path to ../gamebyte.umd.js - Rebuild UMD bundle Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
New demos
Updated docs sections
Test plan
npm run docs:buildsucceeds🤖 Generated with Claude Code