Architecture Overview - drizztdourden08/relic-of-the-past GitHub Wiki
The map for keeping this project structured: the zones, the dependency invariants
that must hold, and a placement guide for new code. Every feature is analyzed against
this before code is written (see the architecture skill).
Companion docs: @docs/contributing/design-system.md (UI tiers), @docs/contributing/coding-standards.md (file
rules), and the refactoring-guru skill (patterns/smells). Build/bridge specifics:
the electron, add-wasm-function, build-wasm skills.
flowchart TD
subgraph CORE["C / WASM core (core/)"]
Z["zelda3/ vendored · game-hooks/ Wasm*/GameHook* · wasm-build/<br/>Touch only to add bridge fns / cheats / queries"]
end
subgraph BRIDGE["Bridge (apps/web/src/lib/game/)"]
B["The ONLY TS that talks to the WASM module. A Facade over Wasm* calls.<br/>Renderer reaches the game through here, never raw ccall elsewhere"]
end
subgraph SHARED["Shared domain (shared/, used by BOTH renderer & electron)"]
S["game/ pure domain: navigation, checks, items, data, logic, seed<br/>asset-extraction/ ROM→.dat · input/ presets · types/<br/>No React/DOM/Node/Electron/window in shared/game/"]
end
subgraph EL["Electron main / preload"]
E["Node/OS/fs/native HID/USB · windows · protocols<br/>profiles · saves · ROMs · dialogs · ipc-handlers → register*()"]
end
subgraph REN["Renderer (apps/web/src/)"]
R["design-system/ tokens · components/ 4 tiers<br/>primitive / composite / compound / view + widgets<br/>stores/ · lib/ · hooks/ · hud/"]
end
CORE -->|"ccall / HEAPU8 / EM_ASM"| BRIDGE
BRIDGE --> SHARED
SHARED -->|"@shared/*"| EL
SHARED -->|"@shared/*"| REN
EL <-->|"IPC"| REN
Aliases: @shared/* → shared/, @app/* → apps/web/src/.
-
shared/*is the leaf. It imports only othershared/*plus stdlib, never@app/*and neverelectron. -
shared/game/*is pure: no React, no DOM, no Node, no Electron, nowindow. It must run in a plain test process. - The renderer (
@app/*) may import@shared/*, design tokens, andwindow.api. It cannot import fromapps/desktop/electron/*; cross that boundary only through IPC. - Electron (
apps/desktop/electron/*) may import@shared/*. It cannot import renderer code (@app/*) or React. - The WASM module is reached only through
lib/game/, the bridge Facade. No rawccall/HEAPU8lives outside it. - Bare UI tiers (primitive/composite/compound) cannot import stores,
window.api,lib/game, or navigation. Data flows in via props, and Views do the wiring. See @docs/contributing/design-system.md. - The native SDL3 controller addon lives in electron main only.
-
core/C is edited only per the bridge rules (add-wasm-function).
If a change would break an invariant, the code is in the wrong zone. Re-place it.
| The new code | Goes in | Notes |
|---|---|---|
| Needs Node/OS/fs/native, windows, ROM/profile/save IO |
apps/desktop/electron/<domain>/ behind a domain:action IPC handler |
Add the channel to the shared/ipc/ contract + join map (window.api type derives). Use the electron skill. |
| Talks to the running game / WASM | apps/web/src/lib/game/ |
New C function → add-wasm-function (C + bridge; EMSCRIPTEN_KEEPALIVE auto-exports, so there is no export list). |
| Pure game rule/algorithm/data, no React/Node |
shared/game/ (navigation / checks / items / data / logic / seed) |
Must stay pure & testable. |
| Parses a ROM / builds assets | shared/asset-extraction/ |
One compile-* per domain. |
| Type shared by renderer + electron | shared/types/ |
Otherwise a local types.ts. |
| Generic UI atom (Button, Select) | ui/design-system/primitives/ |
presentational, no data |
| Generic structural combo (Card, Dialog, Overlay) | ui/design-system/composites/ |
presentational, no data |
| Domain-specific presentational card/form (ProfileCard, SaveSlot) | ui/domains/app/compounds/ |
takes a domain prop, fetches nothing |
| Feature/page with logic + data |
ui/domains/app/views/ or ui/domains/widgets/
|
owns stores/IPC/game; logic in behavior/
|
| A view that draws game state the way the game draws it | compose ui/domains/hud/ compounds from the view or widget |
the HUD compounds are presentational, so a widget may lay hit zones over them (the cheat console does) |
| Renderer UI state | apps/web/src/stores/ |
Zustand |
| Shared renderer hook | apps/web/src/hooks/ |
non-feature-specific |
| Pure renderer helper | apps/web/src/utils/ |
no side effects |
Before writing a feature, run the architecture analysis (the architecture skill):
- Decompose the feature into pieces: UI, state, domain logic, data/IO, bridge, C.
- Place each piece via the guide above.
- Verify the dependency invariants hold for every placement.
- Run a pattern pass (
refactoring-guru) and name the pattern(s) each piece uses. - Emit the plan per @docs/contributing/plan-format.md, with a CRUD filetree, the data model in TS, and the flow.
No feature work starts without this analysis and a plan.