PLWL/development.md
2026-10-01 14:18:15 +02:00

4.2 KiB

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, 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.
  • 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)
npm start                           # click through the flow you touched