import { clamp, clone, EVENT_TYPES, makeEvent } from './shared.js'; export const WALKABLE_BOUNDS = Object.freeze({ minX: 70, maxX: 1530, minY: 90, maxY: 850 }); export const DISTRICTS = Object.freeze([ { id: 'src', name: 'SRC DISTRICT', path: '/src', title: 'Central Kernel Tower', glyph: '⌘', accent: '#62f2d2', x: 565, y: 230, width: 255, height: 220, summary: 'The living source tree: modules, kernel code, and the repository entry point.', stat: '42 files · 213 commits', files: [ { id: 'main-rs', name: 'main.rs', kind: 'Rust', size: '8.4 KB', detail: 'Repository bootstrap and agent scheduler.', code: 'fn main() {\n let world = GitWorld::spawn();\n world.walk();\n}' }, { id: 'kernel-cu', name: 'kernel.cu', kind: 'CUDA', size: '24.1 KB', detail: 'The compute floor is under construction.', code: '__global__ void kernel_step(Agent agent) {\n agent.advance();\n}' }, { id: 'lib-rs', name: 'lib.rs', kind: 'Rust', size: '12.7 KB', detail: 'Shared repository primitives and event contracts.', code: 'pub mod events;\npub mod districts;\npub mod builders;' }, { id: 'mod-rs', name: 'mod.rs', kind: 'Rust', size: '2.1 KB', detail: 'District module registry.', code: 'pub const DISTRICTS: &[&str] = &[\n "src", "tests", "docs"\n];' } ] }, { id: 'tests', name: 'TEST DISTRICT', path: '/tests', title: 'Test Factory', glyph: '✓', accent: '#91e85e', x: 230, y: 370, width: 235, height: 185, summary: 'Inspection robots verify each build before the city opens another street.', stat: '142 passing · 0 failing', files: [ { id: 'world-test', name: 'world.test.js', kind: 'Test', size: '9.7 KB', detail: 'Deterministic movement and event checks.', code: 'assert.equal(world.tick, 12);\nassert.equal(events.length, 4);' }, { id: 'contracts-test', name: 'contracts.test.js', kind: 'Test', size: '6.2 KB', detail: 'Shared UI/world event contract checks.', code: 'assert.ok(EVENT_TYPES.PLAYER_MOVE);\nassert.ok(EVENT_TYPES.BUILD_COMPLETED);' }, { id: 'fixtures', name: 'fixtures/', kind: 'Directory', size: '18 items', detail: 'Small simulated repository fixtures.', code: 'fixture("pax-coder");' } ] }, { id: 'docs', name: 'DOCS LIBRARY', path: '/docs', title: 'Documentation Library', glyph: '▤', accent: '#ffcf68', x: 1085, y: 245, width: 240, height: 205, summary: 'A warm archive of architecture decisions, guides, and the city map legend.', stat: '18 guides · 96% coverage', files: [ { id: 'architecture', name: 'architecture.md', kind: 'Markdown', size: '14.2 KB', detail: 'The frontend event-driven architecture.', code: '# GitWorld architecture\nInput -> simulation -> state -> HUD' }, { id: 'contributing', name: 'contributing.md', kind: 'Markdown', size: '4.8 KB', detail: 'How builders add a district.', code: '1. Choose a street\n2. Add a contract\n3. Run the inspectors' }, { id: 'readme', name: 'README.md', kind: 'Markdown', size: '8.3 KB', detail: 'The repository welcome sign.', code: '# pax-coder\nA simulated repository world.' } ] }, { id: 'issues', name: 'ISSUE QUESTS', path: '/issues', title: 'Issue Quest Board', glyph: '!', accent: '#ff9f58', x: 865, y: 470, width: 215, height: 160, summary: 'Open issues become quests with markers, owners, and a clear next action.', stat: '3 open quests · 1 in progress', files: [ { id: 'issue-201', name: '#201 bug in proof', kind: 'Issue', size: 'open', detail: 'Reproduce the proof boundary on the kernel floor.', code: 'status: open\nowner: proof-agent' }, { id: 'issue-202', name: '#202 add test', kind: 'Issue', size: 'open', detail: 'Add a regression test for agent movement.', code: 'status: open\nowner: test-agent' }, { id: 'issue-203', name: '#203 docs update', kind: 'Issue', size: 'queued', detail: 'Document the deploy launch sequence.', code: 'status: queued\nowner: doc-agent' } ] }, { id: 'prs', name: 'PR GATE', path: '/pulls', title: 'Pull Request Gate', glyph: '↔', accent: '#d37cff', x: 600, y: 475, width: 210, height: 180, summary: 'A glowing merge door: review the change before opening the next branch road.', stat: '2 open · review required', files: [ { id: 'pr-88', name: '#88 merge kernel fix', kind: 'Pull request', size: 'review', detail: 'Two approvals still needed before merge.', code: 'checks: passing\nreviews: 1 / 3' }, { id: 'pr-91', name: '#91 docs refresh', kind: 'Pull request', size: 'ready', detail: 'Ready to merge after the documentation quest.', code: 'checks: passing\nreviews: 3 / 3' } ] }, { id: 'actions', name: 'ACTIONS FACTORY', path: '/.github', title: 'Automation Center', glyph: '⚙', accent: '#66a9ff', x: 1250, y: 485, width: 235, height: 195, summary: 'CI factories turn commits into tested artifacts and light the deploy runway.', stat: 'BUILD #812 · passing', files: [ { id: 'ci-yml', name: 'ci.yml', kind: 'Actions', size: '5.2 KB', detail: 'Build, test, and artifact inspection pipeline.', code: 'jobs:\n build:\n test:\n deploy:' }, { id: 'pages-yml', name: 'pages.yml', kind: 'Actions', size: '2.4 KB', detail: 'Static Pages deployment path.', code: 'deploy-pages: enabled' }, { id: 'release', name: 'release.yml', kind: 'Actions', size: '3.0 KB', detail: 'Packages the next city snapshot.', code: 'tag: v0.8.2' } ] } ]); const INITIAL_AGENTS = [ { id: 'world-architect', name: 'WORLD ARCHITECT', role: 'World geometry', task: 'Building /src district', status: 'BUILDING', progress: 78, color: '#62f2d2' }, { id: 'repository-builder', name: 'REPOSITORY BUILDER', role: 'Repository mapping', task: 'Mapping 42 files', status: 'MAPPING', progress: 64, color: '#ffcf68' }, { id: 'reality-integrator', name: 'REALITY/UI INTEGRATOR', role: 'Interaction layer', task: 'Connecting terminal', status: 'WIRING', progress: 52, color: '#d37cff' } ]; const INITIAL_EVENTS = [ { id: 'seed-1', type: EVENT_TYPES.COMMIT, tick: 0, at: '00:00:00', detail: { message: '8f3a9c1 Fix kernel bounds', districtId: 'src' } }, { id: 'seed-2', type: EVENT_TYPES.AGENT_PROGRESS, tick: 0, at: '00:00:00', detail: { message: 'WORLD ARCHITECT placed a new tower window.', agentId: 'world-architect' } }, { id: 'seed-3', type: EVENT_TYPES.TEST_COMPLETED, tick: 0, at: '00:00:00', detail: { message: '142 inspectors passed the street check.', districtId: 'tests' } } ]; const SIMULATION_SEQUENCE = [ { type: EVENT_TYPES.AGENT_PROGRESS, detail: { agentId: 'world-architect', message: 'WORLD ARCHITECT raised the /src tower scaffold.' } }, { type: EVENT_TYPES.AGENT_PROGRESS, detail: { agentId: 'repository-builder', message: 'REPOSITORY BUILDER mapped kernel.cu into the compute floor.' } }, { type: EVENT_TYPES.CONSTRUCTION, detail: { districtId: 'src', amount: 0.03, message: 'A new source window flickers online.' } }, { type: EVENT_TYPES.TEST_STARTED, detail: { districtId: 'tests', message: 'Inspection robots started a deterministic street sweep.' } }, { type: EVENT_TYPES.TEST_COMPLETED, detail: { districtId: 'tests', message: '142 tests passed. The test district lights turn green.' } }, { type: EVENT_TYPES.BUILD_STARTED, detail: { districtId: 'actions', message: 'BUILD #813 started in the automation factory.' } }, { type: EVENT_TYPES.AGENT_PROGRESS, detail: { agentId: 'reality-integrator', message: 'REALITY/UI INTEGRATOR wired a contextual inspect panel.' } }, { type: EVENT_TYPES.BUILD_COMPLETED, detail: { districtId: 'actions', message: 'BUILD #813 completed and artifacts are staged.' } }, { type: EVENT_TYPES.COMMIT, detail: { districtId: 'src', message: '2h4d7e2 Add proof sketch' } }, { type: EVENT_TYPES.ISSUE, detail: { districtId: 'issues', message: '#202 quest marker activated: add test' } }, { type: EVENT_TYPES.PULL_REQUEST, detail: { districtId: 'prs', message: '#91 merge gate is ready for review.' } }, { type: EVENT_TYPES.DEPLOYMENT_STARTED, detail: { districtId: 'actions', message: 'Deploy rocket is fueling on the Pages runway.' } }, { type: EVENT_TYPES.DEPLOYMENT_COMPLETED, detail: { districtId: 'actions', message: 'Static city snapshot deployed to the local launch pad.' } } ]; export function createWorldState(seed = 20260828) { return { seed, tick: 0, repository: { owner: 'SNAPKITTYWEST', name: 'pax-coder', branch: 'main', visibility: 'Public', simulated: true, stars: 213, forks: 45, languages: ['Rust', 'CUDA', 'Markdown'], build: 'PASSING', tests: '142 PASS', deployment: 'STAGED', commits: [ { id: '8f3a9c1', hash: '8f3a9c1', message: 'Fix kernel bounds', author: 'AhmadParr' }, { id: '2h4d7e2', hash: '2h4d7e2', message: 'Add proof sketch', author: 'proof-agent' }, { id: '9ac1bd9', hash: '9ac1bd9', message: 'Update docs', author: 'doc-agent' }, { id: '7d0c812', hash: '7d0c812', message: 'Wire agent event bus', author: 'world-architect' } ], issues: [ { id: '201', title: '#201 Bug in proof', detail: 'Reproduce the proof boundary on the kernel floor.', status: 'OPEN' }, { id: '202', title: '#202 Add test', detail: 'Add a regression test for agent movement.', status: 'OPEN' }, { id: '203', title: '#203 Docs update', detail: 'Document the deploy launch sequence.', status: 'QUEUED' } ], pullRequests: [ { id: '88', title: '#88 Merge kernel fix', detail: 'Two approvals still needed before merge.', status: 'REVIEW' }, { id: '91', title: '#91 Docs refresh', detail: 'Ready to merge after the documentation quest.', status: 'READY' } ] }, districts: clone(DISTRICTS), agents: clone(INITIAL_AGENTS), events: clone(INITIAL_EVENTS), construction: { src: 0.78, tests: 1, docs: 0.96, issues: 0.7, prs: 0.82, actions: 0.88 }, player: { x: 800, y: 735, speed: 18, facing: 'north', step: 0 }, selectedDistrictId: 'src', selectedFileId: 'main-rs', selectedCollection: 'files', selectedRecord: null, activeTool: 'terminal', activePanel: 'world', terminal: { open: false, lines: ['$ local shell connected', '$ type `help` for commands'], command: '' }, feedback: 'Walk toward a district or click a glowing building.', unlocked: false, view: 'city' }; } function pushEvent(state, event) { state.events = [event, ...state.events].slice(0, 14); } function findDistrict(state, districtId) { return state.districts.find((district) => district.id === districtId) || state.districts[0]; } function selectedFile(state) { const district = findDistrict(state, state.selectedDistrictId); return district.files.find((file) => file.id === state.selectedFileId) || district.files[0]; } function circlesOverlap(x, y, district, margin = 28) { const left = district.x - margin; const right = district.x + district.width + margin; const top = district.y - margin; const bottom = district.y + district.height + margin; return x >= left && x <= right && y >= top && y <= bottom; } export function getNearbyDistrict(state, range = 185) { let nearest = null; let distance = Number.POSITIVE_INFINITY; for (const district of state.districts) { const centerX = district.x + district.width / 2; const centerY = district.y + district.height / 2; const nextDistance = Math.hypot(state.player.x - centerX, state.player.y - centerY); if (nextDistance < distance) { nearest = district; distance = nextDistance; } } return { district: nearest, distance, inRange: distance <= range }; } export function worldReducer(previous, event) { const state = clone(previous); switch (event.type) { case EVENT_TYPES.PLAYER_MOVE: { const dx = Number(event.dx) || 0; const dy = Number(event.dy) || 0; const magnitude = Math.hypot(dx, dy) || 1; const nextX = clamp(state.player.x + (dx / magnitude) * state.player.speed, WALKABLE_BOUNDS.minX, WALKABLE_BOUNDS.maxX); const nextY = clamp(state.player.y + (dy / magnitude) * state.player.speed, WALKABLE_BOUNDS.minY, WALKABLE_BOUNDS.maxY); if (!state.districts.some((district) => circlesOverlap(nextX, nextY, district))) { state.player.x = nextX; state.player.y = nextY; state.player.step = (state.player.step + 1) % 4; } state.player.facing = Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? 'east' : 'west') : (dy > 0 ? 'south' : 'north'); return state; } case EVENT_TYPES.SELECT_DISTRICT: { const district = findDistrict(state, event.districtId); state.selectedDistrictId = district.id; state.selectedFileId = district.files[0]?.id || null; state.feedback = `${district.name} selected. Press Enter or E to inspect.`; return state; } case EVENT_TYPES.SELECT_FILE: state.selectedFileId = event.fileId; state.activePanel = 'file'; state.feedback = `Inspecting ${selectedFile(state).name}`; return state; case EVENT_TYPES.SELECT_COLLECTION: state.selectedCollection = event.collection; state.activePanel = 'collection'; return state; case EVENT_TYPES.SELECT_RECORD: state.selectedRecord = { type: event.recordType, id: event.recordId }; state.activePanel = 'record'; return state; case EVENT_TYPES.OPEN_INSPECTOR: state.activePanel = 'inspector'; state.feedback = `Inspecting ${findDistrict(state, state.selectedDistrictId).path}`; return state; case EVENT_TYPES.CLOSE_INSPECTOR: state.activePanel = 'world'; return state; case EVENT_TYPES.ENTER_DISTRICT: { const district = findDistrict(state, event.districtId || state.selectedDistrictId); state.selectedDistrictId = district.id; state.selectedFileId = district.files[0]?.id || null; state.activePanel = 'inspector'; state.view = 'district'; state.feedback = `Entered ${district.path}. Code is physical here.`; pushEvent(state, makeEvent(EVENT_TYPES.ENTER_DISTRICT, state.tick, { message: `Player entered ${district.path}.`, districtId: district.id })); return state; } case EVENT_TYPES.OPEN_TERMINAL: state.terminal.open = true; state.activeTool = 'terminal'; state.activePanel = 'terminal'; state.feedback = 'Terminal phone online. Commands are simulated locally.'; return state; case EVENT_TYPES.CLOSE_TERMINAL: state.terminal.open = false; if (state.activePanel === 'terminal') state.activePanel = 'world'; return state; case EVENT_TYPES.RUN_COMMAND: { const command = String(event.command || '').trim().toLowerCase(); if (!command) return state; state.terminal.command = command; state.terminal.lines = [...state.terminal.lines, ...event.lines].slice(-28); state.feedback = 'Ran `' + command + '` in the simulated terminal.'; pushEvent(state, makeEvent(EVENT_TYPES.USE_TOOL, state.tick, { message: `Terminal ran ${command}.`, toolId: 'terminal' })); return state; } case EVENT_TYPES.USE_TOOL: { state.activeTool = event.toolId; if (event.toolId === 'terminal') state.terminal.open = true; if (event.toolId === 'git') { state.selectedCollection = 'commits'; state.activePanel = 'collection'; } if (event.toolId === 'docker') { state.selectedCollection = 'build'; state.activePanel = 'collection'; } if (event.toolId === 'code') state.activePanel = 'file'; if (event.toolId === 'ssh') { state.unlocked = true; state.activePanel = 'inspector'; } if (event.toolId === 'database') { state.selectedCollection = 'files'; state.activePanel = 'collection'; } if (event.toolId === 'deploy') { state.repository.deployment = 'LAUNCHING'; state.selectedCollection = 'build'; state.activePanel = 'collection'; } state.feedback = event.message || `Equipped ${event.toolId}.`; pushEvent(state, makeEvent(EVENT_TYPES.USE_TOOL, state.tick, { message: state.feedback, toolId: event.toolId })); return state; } case EVENT_TYPES.TICK: state.tick += 1; return state; case EVENT_TYPES.AGENT_PROGRESS: { const agent = state.agents.find((candidate) => candidate.id === event.detail.agentId); if (agent) agent.progress = Math.min(100, agent.progress + 2); pushEvent(state, makeEvent(event.type, state.tick, event.detail)); state.feedback = event.detail.message; return state; } case EVENT_TYPES.CONSTRUCTION: { const districtId = event.detail.districtId; state.construction[districtId] = clamp((state.construction[districtId] || 0) + (event.detail.amount || 0), 0, 1); pushEvent(state, makeEvent(event.type, state.tick, event.detail)); state.feedback = event.detail.message; return state; } case EVENT_TYPES.BUILD_STARTED: state.repository.build = 'RUNNING'; pushEvent(state, makeEvent(event.type, state.tick, event.detail)); state.feedback = event.detail.message; return state; case EVENT_TYPES.BUILD_COMPLETED: state.repository.build = 'PASSING'; pushEvent(state, makeEvent(event.type, state.tick, event.detail)); state.feedback = event.detail.message; return state; case EVENT_TYPES.TEST_STARTED: state.repository.tests = 'RUNNING'; pushEvent(state, makeEvent(event.type, state.tick, event.detail)); state.feedback = event.detail.message; return state; case EVENT_TYPES.TEST_COMPLETED: state.repository.tests = '142 PASS'; pushEvent(state, makeEvent(event.type, state.tick, event.detail)); state.feedback = event.detail.message; return state; case EVENT_TYPES.DEPLOYMENT_STARTED: state.repository.deployment = 'LAUNCHING'; pushEvent(state, makeEvent(event.type, state.tick, event.detail)); state.feedback = event.detail.message; return state; case EVENT_TYPES.DEPLOYMENT_COMPLETED: state.repository.deployment = 'STAGED'; pushEvent(state, makeEvent(event.type, state.tick, event.detail)); state.feedback = event.detail.message; return state; case EVENT_TYPES.COMMIT: case EVENT_TYPES.ISSUE: case EVENT_TYPES.PULL_REQUEST: pushEvent(state, makeEvent(event.type, state.tick, event.detail)); state.feedback = event.detail.message; return state; default: return state; } } export function advanceWorldState(state) { const ticked = worldReducer(state, { type: EVENT_TYPES.TICK }); const plan = SIMULATION_SEQUENCE[(ticked.tick - 1) % SIMULATION_SEQUENCE.length]; const event = makeEvent(plan.type, ticked.tick, plan.detail); return { state: worldReducer(ticked, event), event }; } function escapeHtml(value) { return String(value).replace(/[&<>"']/g, (character) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[character])); } function windowBlocks(count, color) { return Array.from({ length: count }, (_, index) => ``).join(''); } export function renderWorld(viewport, state, onBuildingSelect) { const viewportWidth = viewport.clientWidth || 960; const viewportHeight = viewport.clientHeight || 680; const centerX = viewportWidth / 2; const centerY = viewportHeight * 0.56; const translateX = centerX - state.player.x * 0.88; const translateY = centerY - state.player.y * 0.88; const buildingMarkup = state.districts.map((district) => { const selected = district.id === state.selectedDistrictId ? ' is-selected' : ''; const progress = Math.round((state.construction[district.id] || 0) * 100); return ``; }).join(''); viewport.innerHTML = `