feat(app): add browser toolkit support

This commit is contained in:
2026-06-12 08:19:40 -05:00
parent 255e5a6b24
commit 26bdc8747f
71 changed files with 2746 additions and 314 deletions
+297 -55
View File
@@ -2,8 +2,8 @@
"schemaVersion": "1.0.0",
"project": {
"name": "blb-throne-of-the-bone-king",
"displayName": "Bone Lord Bob Throne of the Bone King",
"purpose": "Cross-platform game shell with shared React Native UI across mobile, web, and desktop.",
"displayName": "Bone Lord Bob - Throne of the Bone King",
"purpose": "Landscape-first game shell with shared React Native UI across browser, Electron, and future native mobile targets.",
"root": ".",
"model": "single-repo",
"repo": {
@@ -11,19 +11,20 @@
"slug": "jacob-mathison/blb-throne-of-the-bone-king",
"defaultBranch": "main"
},
"universe": "Bone Lord Bob (BLB) Mathison Projects Inc.",
"universe": "Bone Lord Bob (BLB) - Mathison Projects Inc.",
"status": "early-development",
"summary": "Menu shell with home navigation, settings (resolution/volume/autosave), policies, toolkit pages, and Zustand-persisted state. Primary iteration targets are web (Vite) and Electron; mobile targets share the same src/ UI layer."
"summary": "Menu shell with persisted route navigation, settings, policies, credits, background music, browser/Electron rendering, and a browser-only socket-gated Toolkit for volatile gameplay/content systems."
},
"principles": [
"Start with llm.txt for human-readable orientation.",
"Use manifest.llm.json for machine-readable routing, commands, targets, and known gaps.",
"Shared UI under src/ is React Native — not raw HTML.",
"Read AGENTS.md for coding-agent operating rules before broad changes.",
"Shared UI under src/ is React Native, not raw HTML.",
"Do not use DOM APIs in shared src/ code; platform shims belong in .web.ts files or src/web/.",
"Metro bundles mobile; Vite bundles web/Electron renderer — they are not interchangeable.",
"Metro bundles mobile; Vite bundles web and the Electron renderer.",
"Navigation is Zustand-based; do not add React Navigation unless explicitly requested.",
"Persistence is platform-split via persistStorage.ts (mobile) and persistStorage.web.ts (web/Electron).",
"Treat knownGaps in this manifest as the canonical repository-wide gap list.",
"Persistence is platform-split via persistStorage.ts for native and persistStorage.web.ts for web/Electron.",
"Toolkit is browser-only, requires Browser resolution, and requires an active Socket.IO connection for entry.",
"Commit as Jacob Mathison (jacob@mathisonprojects.com); never commit as an AI identity."
],
"guideFiles": [
@@ -44,12 +45,18 @@
"type": "agent-instructions",
"purpose": "Operational instructions for coding agents: ground rules, structure conventions, and common tasks.",
"alwaysRead": true
},
{
"path": "README.md",
"type": "human-overview",
"purpose": "Human-facing overview for setup, commands, architecture, toolkit behavior, and validation."
}
],
"globalReadOrder": [
"llm.txt",
"manifest.llm.json",
"AGENTS.md"
"AGENTS.md",
"README.md"
],
"globalReadOrderSemantics": "Broad orientation order. Task routers should prefer taskRoutes read arrays to avoid over-reading.",
"documentationFiles": [
@@ -71,7 +78,12 @@
{
"path": "README.md",
"type": "human-overview",
"purpose": "Human-facing overview (currently default React Native template; stale relative to web/Electron targets)."
"purpose": "Human-facing setup and development guide."
},
{
"path": "socket/README.md",
"type": "toolkit-socket-notes",
"purpose": "Socket folder notes."
}
],
"targets": [
@@ -80,7 +92,7 @@
"label": "Web",
"runtime": "react-native-web in browser",
"bundler": "Vite",
"entry": "index.html src/web/main.tsx App.tsx",
"entry": "index.html -> src/web/main.tsx -> App.tsx",
"devCommand": "npm run web",
"devUrl": "http://127.0.0.1:5173",
"primary": true
@@ -88,10 +100,21 @@
{
"id": "electron",
"label": "Desktop",
"runtime": "Electron (Chromium renderer)",
"bundler": "Vite (renderer) + tsc (main)",
"entry": "electron/main.ts loads Vite dev server or dist/renderer/",
"runtime": "Electron Chromium renderer",
"bundler": "Vite renderer + TypeScript main/preload compile",
"entry": "electron/main.ts loads the Vite dev server in development or dist/renderer/ in production",
"devCommand": "npm run electron:dev",
"buildCommand": "npm run electron:build",
"packageCommand": "npm run electron:package",
"primary": true
},
{
"id": "toolkit-socket",
"label": "Toolkit Socket",
"runtime": "Node + Socket.IO",
"entry": "socket/server.mjs",
"devCommand": "npm run dev:toolkit",
"healthUrl": "http://127.0.0.1:5174/health",
"primary": true
},
{
@@ -99,31 +122,52 @@
"label": "Android",
"runtime": "React Native",
"bundler": "Metro",
"entry": "index.js App.tsx",
"devCommand": "npm run android"
"entry": "index.js -> App.tsx",
"devCommand": "npm run android",
"status": "native android/ folder is not currently present in this checkout"
},
{
"id": "ios",
"label": "iOS",
"runtime": "React Native",
"bundler": "Metro",
"entry": "index.js App.tsx",
"devCommand": "npm run ios"
"entry": "index.js -> App.tsx",
"devCommand": "npm run ios",
"status": "native ios/ folder is not currently present in this checkout"
}
],
"stack": [
"React Native 0.85.3",
"react-native-web",
"React 19.2.3",
"react-native-web ^0.21.2",
"TypeScript ^5.8.3",
"Zustand ^5.0.14",
"Vite ^8",
"Electron ^42",
"socket.io / socket.io-client ^4.8 (declared, not wired)",
"@react-native-async-storage/async-storage ^3.1.1",
"Vite ^8.0.16",
"Electron ^42.4.0",
"Socket.IO and socket.io-client ^4.8.3",
"Sass ^1.101.0",
"Jest",
"ESLint + Prettier",
"Husky pre-commit validation",
"Node >= 22.12.0"
],
"scripts": {
"dev": "npm run web",
"web": "vite --host 127.0.0.1",
"web:build": "vite build",
"web:preview": "vite preview --host 127.0.0.1",
"dev:toolkit": "node socket/server.mjs",
"dev:all": "concurrently -k -n app,toolkit -c blue,magenta \"npm run dev\" \"npm run dev:toolkit\"",
"dev:clear": "node socket/clear.mjs",
"electron:dev": "npm run electron:compile && cross-env NODE_ENV=development VITE_DEV_SERVER_URL=http://127.0.0.1:5173 concurrently -k \"vite --host 127.0.0.1\" \"wait-on http://127.0.0.1:5173 && electron .\"",
"electron:build": "npm run web:build && npm run electron:compile",
"electron:compile": "tsc -p tsconfig.electron.json",
"electron:package": "npm run electron:build && electron-builder",
"lint": "eslint .",
"test": "jest",
"validate:commit": "npm run lint && npm run test -- --runInBand && npm run electron:build"
},
"architecture": {
"rootComponent": "App.tsx",
"componentTree": [
@@ -132,21 +176,32 @@
"AppNavigator"
],
"navigation": {
"type": "zustand-store",
"type": "zustand-store with animated fade transitions",
"routes": "src/navigation/routes.ts",
"navigator": "src/navigation/AppNavigator.tsx",
"store": "src/state/navigationStore.ts"
"store": "src/state/navigationStore.ts",
"persistKey": "blb-navigation"
},
"state": {
"library": "zustand",
"stores": [
{
"file": "src/state/navigationStore.ts",
"persistKey": "blb-navigation"
"persistKey": "blb-navigation",
"purpose": "Current route and pending route persistence."
},
{
"file": "src/state/gameSettingsStore.ts",
"persistKey": "blb-game-settings"
"persistKey": "blb-game-settings",
"purpose": "Resolution, autosave, mute, and audio volume settings."
},
{
"file": "src/state/toolkitSocketStore.ts",
"purpose": "Toolkit socket connection status."
},
{
"file": "src/state/toolkitContentStore.ts",
"purpose": "Toolkit credits content, revision, load status, and socket refresh/save actions."
}
],
"persistHelper": "src/state/createPersistOptions.ts",
@@ -155,7 +210,23 @@
"web": "src/state/persistStorage.web.ts"
}
},
"settings": "src/settings/gameSettings.ts",
"settings": {
"file": "src/settings/gameSettings.ts",
"resolutionIds": [
"browser",
"iphoneMini",
"iphoneDefault",
"androidDefault",
"phoneLarge"
],
"volumeKeys": [
"master",
"music",
"ambience",
"speech",
"sfx"
]
},
"screens": [
"src/screens/HomeScreen.tsx",
"src/screens/SettingsScreen.tsx",
@@ -169,6 +240,67 @@
"src/components/MenuButton.tsx",
"src/components/BackHomeButton.tsx"
],
"assets": {
"images": "src/assets/images/",
"fonts": "src/assets/fonts/",
"music": "src/assets/music/",
"scss": "src/assets/scss/",
"favicon": "src/assets/images/favicon.svg"
},
"data": {
"credits": {
"json": "src/data/credits.json",
"typedExport": "src/data/credits.ts"
},
"legal": {
"json": "src/data/legal.json"
}
},
"audio": {
"hook": "src/hooks/useBackgroundMusic.ts",
"webHook": "src/hooks/useBackgroundMusic.web.ts",
"defaultTrack": "src/assets/music/throne-of-the-bone-king.mp3",
"volumeControls": [
"master",
"music",
"mute"
]
},
"toolkit": {
"screen": "src/screens/ToolkitScreen.tsx",
"accessRules": [
"resolution setting must be browser",
"socket status must be connected"
],
"routes": [
"toolkitOpening",
"toolkitPetitioners",
"toolkitRulings",
"toolkitSeed",
"toolkitResources",
"toolkitMap",
"toolkitFactions",
"toolkitFlags",
"toolkitStory",
"toolkitLore",
"toolkitAudio",
"toolkitCredits",
"toolkitEndings",
"toolkitReferences"
],
"contentSync": {
"server": "socket/server.mjs",
"serverHelpers": "socket/contentStore.cjs",
"client": "src/socket/toolkitSocketClient.ts",
"store": "src/state/toolkitContentStore.ts",
"events": [
"toolkit:content:read",
"toolkit:content:save",
"toolkit:content:changed",
"toolkit:content:error"
]
}
},
"webEntry": "src/web/main.tsx",
"electronMain": "electron/main.ts",
"electronPreload": "electron/preload.ts",
@@ -195,7 +327,7 @@
]
},
"state": {
"description": "Zustand stores, persistence, or game settings.",
"description": "Zustand stores, persistence, navigation, or game settings.",
"read": [
"llm.txt",
"manifest.llm.json",
@@ -205,34 +337,105 @@
"src/state/persistStorage.web.ts"
],
"policies": [
"Extend persistStorage.ts / persistStorage.web.ts for platform storage changes.",
"Do not embed DOM or AsyncStorage calls directly in stores."
"Extend persistStorage.ts and persistStorage.web.ts for platform storage changes.",
"Do not embed DOM or AsyncStorage calls directly in stores.",
"Add merge handling when persisted state shape changes."
],
"update": [
"manifest.llm.json when stores, persist keys, or settings surface change"
]
},
"web-electron": {
"description": "Vite config, web entry, Electron shell, or platform-specific .web.ts overrides.",
"description": "Vite config, web entry, Electron shell, browser assets, SCSS, or platform-specific .web.ts overrides.",
"read": [
"llm.txt",
"manifest.llm.json",
"vite.config.ts",
"index.html",
"src/web/",
"src/assets/",
"electron/",
"tsconfig.electron.json"
],
"policies": [
"DOM APIs are allowed in src/web/ and .web.ts shims only.",
"Electron main/preload compile to dist/electron/."
"Electron main/preload compile to dist/electron/.",
"Vite emits renderer assets to dist/renderer/."
],
"verify": [
"npm run lint",
"npm run web:build",
"npm run electron:build"
],
"update": [
"manifest.llm.json when targets, build outputs, or dev commands change"
]
},
"toolkit": {
"description": "Toolkit screens, socket-gated browser tooling, JSON content sync, or Toolkit content panels.",
"read": [
"llm.txt",
"manifest.llm.json",
"src/screens/ToolkitScreen.tsx",
"src/navigation/routes.ts",
"src/state/toolkitSocketStore.ts",
"src/state/toolkitContentStore.ts",
"src/socket/toolkitSocketClient.ts",
"socket/"
],
"policies": [
"Keep Toolkit browser-only unless explicitly requested.",
"Keep Toolkit accessible only through Browser resolution and connected socket status.",
"Use src/data JSON as canonical editable content until schemas stabilize.",
"Do not add full CRUD forms unless explicitly requested."
],
"verify": [
"npm run test -- --runInBand",
"npm run dev:toolkit and check /health"
]
},
"data-content": {
"description": "Editable JSON content, typed data exports, and content validation.",
"read": [
"llm.txt",
"manifest.llm.json",
"src/data/",
"socket/contentStore.cjs"
],
"policies": [
"Keep JSON valid and typed through src/data exports.",
"Add runtime validation for data imported by stores or socket helpers.",
"Use atomic writes for server-side JSON save behavior."
],
"verify": [
"npm run test -- --runInBand"
]
},
"realtime": {
"description": "Socket.IO server/client wiring for Toolkit connection status and content sync.",
"read": [
"llm.txt",
"manifest.llm.json",
"socket/server.mjs",
"socket/contentStore.cjs",
"src/hooks/useToolkitSocketConnection.ts",
"src/socket/toolkitSocketClient.ts",
"src/state/toolkitSocketStore.ts",
"src/state/toolkitContentStore.ts",
"package.json"
],
"policies": [
"Keep socket host/port configurable through environment variables.",
"Broadcast content changes after valid reads or saves.",
"Emit toolkit:content:error for read, parse, validation, or write failures."
],
"verify": [
"npm run test -- --runInBand",
"npm run dev:toolkit and check http://127.0.0.1:5174/health"
]
},
"mobile": {
"description": "React Native mobile target changes (Metro, native deps).",
"description": "React Native mobile target changes, Metro behavior, or native dependency setup.",
"read": [
"llm.txt",
"manifest.llm.json",
@@ -242,12 +445,13 @@
"App.tsx"
],
"policies": [
"Do not modify android/ or ios/ native files unless explicitly requested.",
"Run pod install inside ios/ after native dependency changes."
"Do not modify android/ or ios/ native files unless explicitly requested and those folders exist.",
"Run pod install inside ios/ after native dependency changes when ios/ exists.",
"Keep shared src/ free of browser-only APIs."
]
},
"testing": {
"description": "Jest tests for logic, stores, routes, or screen scaffolding.",
"description": "Jest tests for logic, stores, routes, socket helpers, or screen scaffolding.",
"read": [
"llm.txt",
"manifest.llm.json",
@@ -255,23 +459,31 @@
"<related __tests__ file>"
],
"verify": [
"npm run test"
"npm run test -- --runInBand"
]
},
"realtime": {
"description": "Socket.io server/client wiring (planned; not yet implemented).",
"docs": {
"description": "README, AGENTS, llm.txt, manifest, docs, or wiki maintenance.",
"read": [
"README.md",
"AGENTS.md",
"llm.txt",
"manifest.llm.json",
"socket/",
"package.json"
"package.json",
"src/navigation/routes.ts"
],
"policies": [
"socket/ is currently a stub — confirm scope before implementing."
"verify": [
"node -e \"JSON.parse(require('fs').readFileSync('manifest.llm.json','utf8')); console.log('manifest ok')\"",
"npm run lint"
]
}
},
"rootCommands": [
{
"command": "npm run dev",
"purpose": "Alias for browser development.",
"target": "web"
},
{
"command": "npm run web",
"purpose": "Vite dev server for browser iteration.",
@@ -287,6 +499,21 @@
"purpose": "Preview production web build.",
"target": "web"
},
{
"command": "npm run dev:toolkit",
"purpose": "Start local Toolkit Socket.IO server.",
"target": "toolkit-socket"
},
{
"command": "npm run dev:all",
"purpose": "Start browser app and Toolkit socket together.",
"target": "web+toolkit-socket"
},
{
"command": "npm run dev:clear",
"purpose": "Stop related local dev processes if a run hangs.",
"target": "local-dev"
},
{
"command": "npm run electron:dev",
"purpose": "Run Vite dev server and Electron concurrently.",
@@ -294,7 +521,7 @@
},
{
"command": "npm run electron:build",
"purpose": "Build renderer and compile Electron main process.",
"purpose": "Build renderer and compile Electron main/preload files.",
"target": "electron"
},
{
@@ -305,16 +532,16 @@
{
"command": "npm run start",
"purpose": "Start Metro bundler.",
"target": "android"
"target": "mobile"
},
{
"command": "npm run android",
"purpose": "Run on Android emulator or device.",
"purpose": "Run on Android emulator or device when android/ exists.",
"target": "android"
},
{
"command": "npm run ios",
"purpose": "Run on iOS simulator or device.",
"purpose": "Run on iOS simulator or device when ios/ exists.",
"target": "ios"
},
{
@@ -324,30 +551,45 @@
{
"command": "npm run test",
"purpose": "Jest test suite."
},
{
"command": "npm run validate:commit",
"purpose": "Pre-commit safety check: lint, Jest, and Electron build."
}
],
"knownGaps": [
"socket/ is a stub — socket.io and socket.io-client are installed but no server/client wiring exists.",
"docs/ and wiki/ are placeholder README stubs.",
"AGENTS.md still describes a mobile-only app and forbids all DOM APIs; it is stale relative to web/Electron targets.",
"README.md is the default React Native Community CLI template and does not document web or Electron workflows.",
"No JSON Schema validator exists yet for manifest.llm.json."
"No JSON Schema validator exists yet for manifest.llm.json.",
"Toolkit content panels are read-only scaffolds except for socket/store save plumbing.",
"Native android/ and ios/ folders are not currently present in this checkout.",
"No database exists yet; editable content is intentionally JSON-backed until schemas stabilize."
],
"verificationBaseline": {
"logicOrStoreChanges": [
"npm run test"
"npm run test -- --runInBand"
],
"uiChanges": [
"npm run lint",
"npm run test"
"npm run test -- --runInBand",
"npm run web:build"
],
"webElectronChanges": [
"npm run lint",
"npm run test"
"npm run test -- --runInBand",
"npm run electron:build"
],
"socketChanges": [
"npm run test -- --runInBand",
"npm run dev:toolkit",
"curl http://127.0.0.1:5174/health"
],
"mobileChanges": [
"npm run lint",
"npm run test"
"npm run test -- --runInBand"
],
"docsChanges": [
"node -e \"JSON.parse(require('fs').readFileSync('manifest.llm.json','utf8')); console.log('manifest ok')\"",
"npm run lint"
]
}
}