PLWL/development.md

87 lines
4.7 KiB
Markdown

# Development
Short guide for anyone working on the PLWL codebase.
## Layout
```
src/ main process (Node/Electron backend)
main.js entry point: tray, single-instance handoff, --launch / --open
argv.js command-line parsing shared by boot and second-instance handoff
window.js main window creation
ipc/ all ipcMain handlers
index.js handler registration, icon protocol, dialogs
games.js games service: run/stop state, wine resolution, open-with handoff
lansync.js LAN sync handlers (identity, devices, re-roll, sync + progress)
runner.js spawns wine/proton (env, prefix, playtime, logs)
library.js apps registry (apps.json) + settings + playtime files
wines.js wine/proton build scan, registry, portable symlink healing
prefixes.js prefix registry (files/wineprefixes)
importer.js app import (copy/move/leave/prefix)
openwith.js "Open with PLWL": ad-hoc runs, no library entry
shortcuts.js .desktop shortcut generation
paths.js portable layout, FILES-relative path helpers, translation sync
i18n.js translation loading + {0} placeholder filling
preload.js contextBridge API exposed to the renderer
lansync.js LAN sync: identity + pairing, hashing, HTTP receiver (with its
receive-progress hook), UDP discovery
lansyncpush.js the pushing side (createPusher factory, keeps helper state out)
appicon.js window/taskbar icon
renderer/ UI (plain HTML/CSS/JS, no framework)
app.js views, modals, custom-select, all renderer logic
index.html static markup for every view
style.css the whole look; CSS variables at the top
files/ portable data the launcher ships/creates at runtime
translations/ en-us.json, pl.json (copied to the data dir on start)
test/ plain node scripts, run them with `node test/<name>.test.js`
tools/ install.sh / createshortcut.sh / build helpers
```
Main process vs renderer: the renderer never touches Node/Electron directly -
everything goes through the `window.plwl` bridge (preload.js) and ipcMain
handlers in src/ipc/. Keep new APIs in that pattern.
## Rules
- Keep it simple. Plain functions over classes, no abstractions "for later".
- Comments explain the WHY (quirks, workarounds, races), not the WHAT. If the
code says it, don't repeat it in words. No comments at all when nothing
needs explaining.
- No duplicate logic: the second time you copy something, move it into a
helper (a shared module in src/ or a function next to its users) and call
it from both places.
- Everything user-visible goes through the translation files (en-us.json,
pl.json) with `{0}`, `{1}` placeholders. No hardcoded strings in app.js.
- Paths stored on disk are FILES-relative (portable layout): write with
toPortablePath(), read with resolvePortablePath() - never store absolute
paths for anything inside files/.
- Registries (apps.json, wines.json, prefixes.json) are rewritten whole via
writeJson(); playtime lives in its own tiny file so the registry is not
rewritten while a game runs.
- Deleting user data needs a confirm modal; best-effort cleanup is
try/catch-and-ignore with a comment saying why it is safe to ignore.
- Windows apps run through runGame()/buildEnv() - never spawn wine directly
somewhere else (prefix handling, Proton env and playtime live there).
- CSS: reuse the existing variables/classes (.button, .field-label,
.custom-select, form-card); spacing rhythm comes from --button-margin and
the flex gap of the container, not per-element magic numbers.
- LAN sync has two sides: the receiver lives in lansync.js, the sender in
lansyncpush.js and is built by createPusher(helpers) - several launcher
states share the module, so nothing may be kept in its module scope. Both
sides report their own progress (the receiver through the onReceive hook),
so each machine can show the transfer.
- A modal showing a running operation (a sync) opens locked:
showModal(html, { locked: true }) ignores the backdrop and Escape, because
the user cannot cancel the operation by dismissing it.
- Tests are plain assert-by-print scripts (see test/): set PLWL_BUILD_DIR to
a temp dir, check(), exit 1 on failure. Add one when fixing a bug.
## Verify before you commit
```
node --check <changed js files> # syntax
node test/import.test.js # ...and the other test files
node test/lansync.test.js # LAN sync contract (identity, pairing, mirror)
node test/lansync-receiving.test.js # receive-side progress events (the popup)
npm start # click through the flow you touched
```