chore: add reference docs, scripts, tests, demo, prototypes

- Reference docs: known issues, user journey, location redesign, mission merge plan
- Scripts: fill_location_state, merge_missions, migrate_location, migrate_state
- Tests: e2e test suite, asset entries unit tests
- Demo: location board, hifi prototypes, test screenshots
- Data backup: asset log entries and state nodes
- Update .gitignore

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 15:29:47 +08:00
co-authored by Claude Opus 4.7
parent f4909765af
commit e1d601a774
190 changed files with 11464 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
"""Test the location board demo click functionality using Playwright."""
import asyncio
from pathlib import Path
from playwright.async_api import async_playwright
DEMO_PATH = Path(__file__).resolve().parent / "location_board_demo.html"
FILE_URL = f"file:///{DEMO_PATH.as_posix()}"
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
page = await browser.new_page(viewport={"width": 1400, "height": 900})
await page.goto(FILE_URL, wait_until="commit", timeout=10000)
await page.wait_for_timeout(3000) # let canvas render (skip font loading)
# --- Test 1: Check initial state ---
scope = await page.text_content("#scope-indicator")
print(f"Test 1 - Initial scope: {scope}")
# --- Test 2: Get canvas bounding box and try clicking a planet ---
canvas = page.locator("#map-canvas")
box = await canvas.bounding_box()
if not box:
print("ERROR: Canvas not found!")
await browser.close()
return
print(f"Canvas: x={box['x']:.0f} y={box['y']:.0f} w={box['width']:.0f} h={box['height']:.0f}")
# The solar system view has the sun at center of canvas
# Planets orbit around it. Let's log JS state to see where bodies are.
body_positions = await page.evaluate("""
() => {
const result = [];
const keys = ['sun','mercury','venus','earth','mars','jupiter','saturn','uranus','neptune','pluto'];
for (const k of keys) {
const b = BD[k];
if (b) result.push({
key: k,
name: b.n,
sx: b._x,
sy: b._y,
r: b.r,
hasMoons: (b.moons && typeof b.moons==='object') ? Object.keys(b.moons).join(',') : 'none'
});
}
return result;
}
""")
print("\nBody positions (CSS pixels from canvas top-left):")
for bp in body_positions:
sx = bp['sx']
sy = bp['sy']
if sx is not None and sy is not None:
# Convert to page coordinates (add canvas offset)
px = sx + box['x']
py = sy + box['y']
print(f" {bp['name']:12s} screen=({sx:6.1f},{sy:6.1f}) page=({px:6.1f},{py:6.1f}) r={bp['r']} moons={bp['hasMoons']}")
else:
print(f" {bp['name']:12s} screen=(undefined) - NOT RENDERED")
# --- Test 3: Try clicking Jupiter ---
jupiter = [b for b in body_positions if b['key'] == 'jupiter']
if jupiter and jupiter[0]['sx'] is not None:
jx = jupiter[0]['sx'] + box['x']
jy = jupiter[0]['sy'] + box['y']
jr = jupiter[0]['r'] + 5 # hit radius
print(f"\nTest 3 - Clicking Jupiter at page ({jx:.0f}, {jy:.0f}) r={jr}")
# Take pre-click screenshot
await page.screenshot(path=str(DEMO_PATH.parent / "pre_click.png"))
print(" Screenshot: pre_click.png")
# Click
await page.mouse.click(jx, jy)
await page.wait_for_timeout(500)
scope_after = await page.text_content("#scope-indicator")
back_visible = await page.evaluate("() => document.getElementById('back-btn').classList.contains('visible')")
print(f" After click - scope: {scope_after}")
print(f" Back button visible: {back_visible}")
print(f" View stack: {await page.evaluate('() => JSON.stringify(viewStack)')}")
await page.screenshot(path=str(DEMO_PATH.parent / "post_click.png"))
print(" Screenshot: post_click.png")
else:
print("\nTest 3 - Jupiter not found in rendered bodies!")
# --- Test 4: Check JS console for errors ---
console_msgs = []
page.on("console", lambda msg: console_msgs.append(f"[{msg.type}] {msg.text}"))
# Trigger a re-render
await page.evaluate("() => { render(); }")
await page.wait_for_timeout(300)
errors = [m for m in console_msgs if 'error' in m.lower() or 'Error' in m]
if errors:
print(f"\nJS errors: {errors}")
else:
print("\nNo JS errors detected")
# --- Test 5: Check didDrag state and event flow ---
print("\n--- Event Debug ---")
# Inject debug listener
await page.evaluate("""
() => {
const canvas = document.getElementById('map-canvas');
canvas.addEventListener('click', (e) => {
console.log('CLICK on canvas at', e.clientX, e.clientY, 'didDrag=', window._didDrag || didDrag);
}, true); // capture phase
// Store for inspection
window._debugClick = (x, y) => {
const r = canvas.getBoundingClientRect();
const t = hitTest(x - r.left, y - r.top);
console.log('hitTest result:', t, 'didDrag=', didDrag, 'dragOn=', dragOn);
return t;
};
}
""")
# Try click with explicit coordinates
if jupiter and jupiter[0]['sx'] is not None:
result = await page.evaluate(f"() => window._debugClick({jx - box['x']}, {jy - box['y']})")
print(f"hitTest at Jupiter position result: {result}")
# Also try clicking center of canvas minus offset
cx = box['width'] / 2
cy = box['height'] / 2
for offset_y in [0, -20, -50, -100, 50, 100]:
test_x = cx
test_y = cy + offset_y
r = await page.evaluate(f"() => window._debugClick({test_x}, {test_y})")
print(f" hitTest at canvas({test_x:.0f}, {test_y:.0f}): {r}")
await browser.close()
if __name__ == "__main__":
asyncio.run(main())