Offline-only desktop diff viewer. Electron + electron-vite + Vue 3 + Pinia +
Monaco. Roadmap lives in DEVELOPMENT_PLAN.md — keep its checkboxes current.
npm run dev— run natively.make test-env/npm run docker:up— full app in Docker with noVNC at http://localhost:6080/vnc.html (loopback only).npm run check— lint + tests. Run it before declaring any task done.npm test/npm run lint/npm run format— individually.npm run build— bundles tobuild/(NOT electron-builder's default;buildResourcesisresources/). Installers:build:win/build:mac.
- Offline guarantee. The app never makes network requests. Never weaken
the session-level kill switch, the CSP,
sandbox: true,contextIsolation, the deny-all permission handler, or thewill-navigateblock insrc/main/index.js. No telemetry, no auto-update, no CDN assets — ever. - New dependencies need a network audit. Before adding any package:
confirm it makes no runtime network calls, then run
npm audit. Prefer zero new production dependencies. - Renderer never touches Node or Electron. All fs, dialog, and crypto
work lives in the main process behind small, validated IPC handlers.
The renderer talks only to
window.api(preload). ESLint enforces this (no-restricted-imports) — do not disable the rule. - Keys never cross the IPC boundary. Vault crypto goes through
vault:encrypt/vault:decrypt; identity private keys stay behindsafeStoragein userData. Never add an IPC handler that returns key material to the renderer. - Crypto invariants (see
src/main/sealing.jsandvaultCrypt.js): sealed shares are sign-then-encrypt, bound to one recipient in both the signature (payload ‖ recipient-fp) and the GCM AAD; saved-diff metadata is authenticated as AAD; expiry is capped at 24 h and enforced on both the sealing and opening side. Any change to these files requires updatingtests/in the same change AND re-verifying the share roundtrip in the Docker env. - Untrusted input is hostile. Files chosen for import (
.diffbro,.diffbrokey) get size caps, shape validation, and recomputed fingerprints before use. Keep it that way for any new import surface. - No injection sinks.
v-html,eval,new Function,innerHTMLare banned (ESLint-enforced). User-influenced strings render only through Vue text interpolation.
- Prettier (
.prettierrc.json: no semicolons, single quotes, width 100) and ESLint (eslint.config.mjs) are the style authority — runnpm run formatrather than hand-formatting, and never commit code that failsnpm run check. - Vue SFCs use
<script setup>, scoped styles, and CSS variables fromstyles/tokens.cssfor every color — hardcoded colors break the light/dark theme. New UI must be checked in both themes. - Comments explain why or state invariants; never narrate what the next line does. Match the density and tone of the surrounding file.
- New file formats go through the adapter registry
(
src/renderer/src/adapters/) returning a{ kind, ... }comparable — never special-case a format insideDiffViewer. - Keyboard shortcuts: add to BOTH the hidden application menu
(
src/main/index.js) and the customMenuBar.vue, useCmdOrCtrlin accelerators and theMODconstant (keys.js) in labels.
- Vitest, jsdom environment (
tests/setup.jsprovides localStorage — Node's built-in one is broken in workers). The tree undertests/mirrorssrc/:tests/main/,tests/renderer/{stores,utils,adapters}/. A new test goes in the directory matching its subject's source path. Fixtures live intests/data/. - Every behavior change in
src/main/sealing.js,vaultCrypt.js, the Pinia stores, or the adapters needs a test in the same change. Crypto code additionally needs negative tests (tamper, wrong key, expiry). - Keep pure logic out of Electron-importing files so it stays unit-testable:
share.js/index.jsare thin glue;sealing.js/vaultCrypt.jsare the testable cores. Follow that split for new main-process logic. - UI-level changes are verified in the Docker test env (screenshots via
xdotool/scrotinside the container; keyboard viaxdotool, not the noVNC page — seedocker/README.md).
- Never
git commitunless explicitly asked. - Temp/test artifacts (generated key files,
.diffbrofiles) must be cleaned up fromtests/data/after verification; onlyconfig-v1.jsonandconfig-v2.jsonbelong there. - After dependency changes, the Docker env needs
make rebuild(volume-shadowednode_modules). Prefermake installfor adding or updating dependencies — it writespackage-lock.jsonwith the container's npm, which is pinned to the same major as the host's (npm 11). npm majors disagree about optional/platform packages in the lock, and a lock written by one failsnpm ciunder the other; keep host npm and the Dockerfile'snpm install -g npm@11in step. @emnapi/core/@emnapi/runtimeare pinned in devDependencies only to work around npm dropping them from the lock (they are transitive optionals of vitest's wasm toolchain) — do not remove them just because nothing imports them.- Update
README.md(including the mermaid diagram) andDEVELOPMENT_PLAN.mdwhen architecture or feature status changes.