From 6745e522a796cbac22e13fa09536522199d70609 Mon Sep 17 00:00:00 2001 From: otivm Date: Sun, 3 May 2026 15:10:43 +0000 Subject: [PATCH] iv: wire Shell into App.jsx, replace tab navigation with context dropdown --- src/App.jsx | 150 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 92 insertions(+), 58 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 45da7e0..169eb51 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,18 +2,21 @@ import { useState, useEffect } from 'react' import { generateToken, loadState, saveState } from './api.js' import { createState } from './gameState.js' import { BACKGROUNDS } from './constants.js' -import Ledger from './screens/Ledger.jsx' +import Shell from './components/Shell.jsx' +import Actor from './screens/Actor.jsx' +import Forum from './screens/Forum.jsx' import Map from './screens/Map.jsx' -import Prologue from './screens/Prologue.jsx' import './App.css' +import contextsJson from './config/contexts.json' + const TOKEN_KEY = 'otivm_token' export default function App() { - const [state, setState] = useState(null) - const [token, setToken] = useState(null) + const [state, setState] = useState(null) + const [token, setToken] = useState(null) const [loading, setLoading] = useState(true) - const [screen, setScreen] = useState('prologue') + const [context, setContext] = useState('actor') useEffect(() => { async function bootstrap() { @@ -26,13 +29,12 @@ export default function App() { const saved = await loadState(tok) if (saved) { setState(saved) - // If background already chosen, open on ledger - setScreen(saved.background_id ? 'ledger' : 'prologue') + setContext(saved.background_id && saved.background_id !== 'unknown' ? 'forum' : 'actor') } else { const fresh = createState(tok) setState(fresh) await saveState(tok, fresh) - setScreen('prologue') + setContext('actor') } setLoading(false) } @@ -44,8 +46,6 @@ export default function App() { await saveState(token, newState) } - // Called from Prologue when player confirms a background. - // Seeds background_id into state, saves, switches to Ledger. async function onSelectBackground(backgroundId) { const bg = BACKGROUNDS.find(b => b.id === backgroundId) if (!bg) return @@ -56,15 +56,9 @@ export default function App() { } setState(updated) await saveState(token, updated) - setScreen('ledger') + setContext('forum') } - // Session abandonment — forward-looking lifecycle handler. - // Does not delete the old save. Appends a terminal event so the record - // is complete. The old save becomes a historical artefact on disk. - // In the Simulator this event will have social and ecological consequences - // for the clan — a Constructor who stops participating leaves a gap. - // For now: mark abandoned, generate new token, bootstrap fresh in-place. async function onNewGame() { if (state && token) { const abandoned = { @@ -82,53 +76,93 @@ export default function App() { const fresh = createState(newTok) setState(fresh) await saveState(newTok, fresh) - setScreen('prologue') + setContext('actor') } if (loading) { - return ( -
- Consulting the ledger... -
- ) + return
Consulting the ledger...
} - return ( -
- -
-
- -
-
- -
-
- -
+ // Active context config for Shell + const activeCtx = contextsJson.find(c => c.id === context) || contextsJson[0] + const subitems = activeCtx.subitems || [] + + // Context header text + const ctxHeader = ( +
+
+ Context + {activeCtx.name} — {activeCtx.subtitle}
) + + // Screen content + let screen + if (context === 'actor') { + screen = ( + + ) + } else if (context === 'forum') { + screen = ( + + ) + } else if (context === 'map') { + screen = + } + + return ( + + {ctxHeader} +
+ {context === 'actor' && ( +
+ {activeCtx.layout === 'three-col' ? ( + <> +
{filterCol(screen, 'left')}
+
{filterCol(screen, 'center')}
+
{filterCol(screen, 'right')}
+ + ) : screen} +
+ )} + {context === 'forum' && ( +
+ {activeCtx.layout === 'two-col' ? ( + <> +
{filterCol(screen, 'left')}
+
{filterCol(screen, 'right')}
+ + ) : screen} +
+ )} + {context === 'map' && screen} +
+
+ ) +} + +// Extract children from a screen component by their col prop. +// Actor and Forum return arrays of Section elements with col props. +function filterCol(screen, col) { + if (!screen) return null + const children = screen.props?.children + if (!children) return null + const arr = Array.isArray(children) ? children : [children] + return arr.filter(c => c?.props?.col === col) }