Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
487a319
Add the new shell: sites list, page area and footer, in a larger wind…
zaerl Oct 1, 2026
da19ac6
Merge trunk into redesign/wpds
zaerl Oct 1, 2026
3344172
Merge trunk into redesign/wpds
zaerl Oct 2, 2026
f066f94
Put the site's menu in the page header and its details in a sidebar (…
zaerl Oct 2, 2026
5388e5d
Put a site's server and build watch in the page header and the detail…
zaerl Oct 2, 2026
d5bbd47
Draw the ticket card with the design system, as its own component (#596)
zaerl Oct 2, 2026
74f3c23
Draw the apply card with the design system, and preview a patch in a …
zaerl Oct 2, 2026
1319e26
Draw the list of a site's tickets with the design system (#599)
zaerl Oct 3, 2026
abb1442
Draw the create-site dialog with the design system, as the prototype …
zaerl Oct 3, 2026
6c7e679
Draw the rename dialog with the design system, and ask before a delet…
zaerl Oct 3, 2026
4bee60f
Draw the window's confirmations as the design system's notices, and s…
zaerl Oct 3, 2026
daefeb3
Draw the site page's notices, update card and setup checklist with th…
zaerl Oct 3, 2026
9570450
Paint the terminal, logs and mail with the design system's tokens, an…
zaerl Oct 3, 2026
23d46e7
Merge trunk into redesign/wpds
zaerl Oct 4, 2026
5b7e1fb
Paint the review, update and mail dialogs with the design system's to…
zaerl Oct 4, 2026
1e9133c
Add lint rules that keep colours and inline styles out of the rendere…
zaerl Oct 4, 2026
bda9083
Lint the window's stylesheet with Stylelint, and write its tokens bar…
zaerl Oct 4, 2026
4179dd4
Hold the site menu's "Update to latest trunk" while an update, instal…
zaerl Oct 4, 2026
2ef8177
Add the tray along the bottom of the window, and move the terminal in…
zaerl Oct 4, 2026
6be1ee1
Move the logs into the bottom tray, and bring them up for a watch or …
zaerl Oct 4, 2026
351d34b
Count unseen debug.log lines on the footer's Logs button (#614)
zaerl Oct 5, 2026
548c020
Move the mail into the bottom tray, the last of the three (#615)
zaerl Oct 5, 2026
3f18fae
Fix the toast journey failing with "Cannot fast-forward to the past" …
zaerl Oct 5, 2026
c7c9acc
Correct the comments the finished redesign made untrue, and drop a fi…
zaerl Oct 5, 2026
0098484
Name the server's button in AGENTS.md by what it now says (#618)
zaerl Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/instructions/code-review.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Where the tool allows it, run this pass with fresh context — a subagent given

## Scope

Review **judgement**, not style. ESLint (`eslint.config.mjs`, `npm run lint`) already covers formatting, unused variables, JSDoc, React hooks and the rest of the mechanical layer. Repeating those here buries the findings that matter. When style and process nits share space with substantive findings, authors learn to skim past them — and the substantive findings go with them.
Review **judgement**, not style. ESLint (`eslint.config.mjs`, `npm run lint`) already covers formatting, unused variables, JSDoc, React hooks and the rest of the mechanical layer, and Stylelint (`stylelint.config.mjs`, the same command) covers the window's stylesheet: tokens that exist, written bare, and no colour written by hand. Repeating those here buries the findings that matter. When style and process nits share space with substantive findings, authors learn to skim past them — and the substantive findings go with them.

Five dimensions, in priority order: **architecture · security · performance · cross-platform · tests**.

Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ESLint over the whole repo.
# ESLint over the whole repo, and Stylelint over the window's stylesheet.
#
# This used to lint only the files a PR touched, because the linter arrived on top of a
# backlog of pre-existing findings and running it repo-wide would have failed every PR for
Expand Down Expand Up @@ -51,3 +51,8 @@ jobs:

- name: ESLint
run: npx eslint . --max-warnings=0

# The window's stylesheet: tokens that exist, written bare, and no
# colour written by hand. See stylelint.config.mjs.
- name: Stylelint
run: npx stylelint "src/renderer/**/*.css" --max-warnings=0
4 changes: 4 additions & 0 deletions .stylelintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# esbuild output: the bundle of every stylesheet the window loads, its own and
# the libraries'. The window's own part of it is src/renderer/shell.css, which
# is linted.
src/renderer/index.css
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ When asked to add an existing pull request to an existing stack, preserve its co
- **Git has no host dependency.** The app ships its own Git binary via `dugite` (#364), unpacked from `app.asar` and resolved only through `src/git-binary.cjs`, which also builds the environment it runs with (host system and global config off, prompting off, no `GIT_*` variable inherited from the host and nothing from a Node child). `require('dugite')` lives in that one file; `src/git-run.cjs` is the only place the binary is spawned, and `src/git-read.cjs` holds every read and its parser. Spawning a `git` found on `PATH`, or calling dugite's `GitProcess` from anywhere else, is a regression. As of #384 every read outside the write flows (status, branches, history, blobs, the patch walk) runs on the bundled binary, and since #385's first flow so does the new-site clone (`src/git-clone.cjs`: partial, `--filter=blob:none`, repo config written at clone time); Ticket branches (park, start, switch, delete, and the move onto the current trunk, one three-way `merge-tree` of the single WIP commit read in `src/git-read.cjs` and committed by the primitives) write through `src/git-write.cjs`, one Git command per primitive, with the checkout's progress read by `src/git-progress.cjs`, the single parser of Git's human-facing lines that the clone shares; the trunk update (`src/trunk-update.js`: fetch from the site's own `origin`, update-ref, forced checkout; the two discards: forced checkout plus `clean -fd`) writes through the same primitives, with `streamGit` in `src/git-run.cjs` as the one runner for the commands that report progress; patch apply and revert (`src/patch-apply.js`) run on `git apply` through the same primitives, all or nothing by Git's own rule, with the Trac path rewrite, the per-hunk wording of a refusal and the snapshot that undoes a half-written apply kept in JS and never writing on their own. A pull request is checked out, not applied (#458): `src/pr-checkout.js` fetches `refs/pull/N/head` from the site's own `origin` through `fetchBranch`, puts `pr/N` at it and switches through `ticket-branches.js`; `appliedPatch` remains the record for Trac attachments and local `.diff`/`.patch` files only. Since #386 the app has one Git engine: `isomorphic-git` is gone from `package.json` (it reaches the bundle only as a transitive dependency of `@wp-playground/storage`), and every test fixture is built by the bundled binary. The suites that used to build theirs with the old engine go through the fixture layer in `tests/unit/helpers/git.cjs`; the suites that cover a single primitive (`git-read`, `git-write`, `git-clone`, `git-run`, the fetch suite) still call the binary directly, because a fixture built by the layer above the primitive under test would beg the question. A site the old engine cloned (shallow, no `remote.origin.promisor`) is detected by `isLegacySite` in `src/git-read.cjs` and refused by every write handler in `main.js` through `legacySiteBlock` (`code: 'legacy-site'`); only reads, the patch export of the checked-out branch, opening a pull request and deletion still work there, and the sentence both sides show lives in `src/renderer/legacy-site.cjs`. A merge, rebase, cherry-pick, revert or three-way apply started outside the app and left unfinished is read by `mergeInProgress` in `src/git-read.cjs` (the `u` records of `status --porcelain=v2` plus the head files and rebase directories `git status` itself reads under `.git`, never remembered in process) and refused by `mergeInProgressBlock` (`code: 'merge-in-progress'`) in front of the trunk update, both discards, linking and unlinking a ticket, switching, the move onto trunk, applying and reverting a patch, and deleting the ticket that is checked out; the sentence naming the files and the terminal commands that end it lives in `src/renderer/merge-in-progress.cjs`, and the app offers no action of its own for it (#352). Only porcelain-stable output (`--porcelain=v2`, `-z`, explicit `--format`) is ever parsed. Patch/diff generation is done by hand in `main.js`, not `git diff`: one status scan against the branch point each ticket recorded (#108), nothing staged, and `/dev/null` naming whichever side of an addition or a deletion does not exist — the app reads its own patches back when a mentor applies one, and that filename is the only thing its parser reads an add or a delete from (#85).
- **`electron-store` is the only persistence layer** — no separate DB. It holds the site registry and per-site metadata; treat it as the single source of truth for "known sites." **Nothing may yield between reading the store and writing it back.** The `get` and `set` are synchronous, so the event loop is the only thing serialising writers, and it only serialises them while nothing awaits in between. A writer that reads, awaits anything, and then writes what it read hands back a snapshot another writer has moved on from, and that writer's change is gone (#172). The value that made it matter is a ticket branch's recorded base, the one thing a branch cannot recompute. Handlers that write a fixed set of top-level keys do this inline and are fine as they are; a change that has to be computed from the record, or that replaces a nested map such as `branches`, goes through `changeSiteMeta` in `src/main.js`, which takes a function of the record and applies it at the moment of the write. Computing that value from an earlier read is the bug, whichever helper writes it.
- Long-running child-process output (installs, scripts, server) is streamed to the renderer via correlated IDs (`installId`/`runId`), not returned synchronously — expect async event handlers, not return values, when tracing that flow.
- **Electron is pinned at 43.6.0 and must not move above it until the upstream V8 bug is fixed.** Electron 43.7.0 and later crash the Playground server child with `SIGBUS` (`KERN_PROTECTION_FAILURE` on a `V8Worker` thread) about a second after "Start dev server" on Apple Silicon macOS; 43.7.5 crashes 5/5 and 43.6.0 starts 5/5 in a bisect of `src/server-runner.js` run directly on each Electron Helper as Node. Plain Node 24.21.0 and both Playground CLI versions in range are unaffected, and the crash disappears only under V8's `--liftoff-only`, so it lives in the optimizing WebAssembly compiler of the V8 backports 43.7.0 carries (electron/electron#53766). Nothing that runs on a pull request starts the Playground server on Electron's bundled Node: the packaged smoke never clicks "Start dev server", the journeys that do (in `mail.spec.js`, `logs.spec.js` and `dev-server.spec.js`) replace the handler that starts the server with a stub, and only the manual-dispatch `real-setup.yml` workflow starts it for real. So an Electron bump that passes CI can still kill the dev server. Before moving the pin, start the dev server on an Apple Silicon Mac with the candidate version, or run `real-setup.yml` on it, and say which in the PR.
- **Electron is pinned at 43.6.0 and must not move above it until the upstream V8 bug is fixed.** Electron 43.7.0 and later crash the Playground server child with `SIGBUS` (`KERN_PROTECTION_FAILURE` on a `V8Worker` thread) about a second after "Start development server" on Apple Silicon macOS; 43.7.5 crashes 5/5 and 43.6.0 starts 5/5 in a bisect of `src/server-runner.js` run directly on each Electron Helper as Node. Plain Node 24.21.0 and both Playground CLI versions in range are unaffected, and the crash disappears only under V8's `--liftoff-only`, so it lives in the optimizing WebAssembly compiler of the V8 backports 43.7.0 carries (electron/electron#53766). Nothing that runs on a pull request starts the Playground server on Electron's bundled Node: the packaged smoke never clicks "Start development server", the journeys that do (in `mail.spec.js`, `logs.spec.js` and `dev-server.spec.js`) replace the handler that starts the server with a stub, and only the manual-dispatch `real-setup.yml` workflow starts it for real. So an Electron bump that passes CI can still kill the dev server. Before moving the pin, start the dev server on an Apple Silicon Mac with the candidate version, or run `real-setup.yml` on it, and say which in the PR.

## Signing & CI

Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Three GitHub Actions workflows run on every PR, and also on push to `trunk` so t

ESLint over the **whole repo**: `eslint . --max-warnings=0`. A single warning fails the check. The backlog that once made a repo-wide run impractical was cleared (#117), so the check now covers every file rather than only the ones a PR touched.

The same job then runs Stylelint over the window's stylesheet, `src/renderer/shell.css`. Its rules are only about design-system tokens and colours: a token has to exist, is not set in the stylesheet, is written bare with no fallback value beside it, and a colour is never written by hand (`stylelint.config.mjs` says why for each). `npm run lint` runs both locally.

It installs with `npm ci --ignore-scripts`, which skips lifecycle scripts — so linting **never executes the PR's code** (this repo's `postinstall` would otherwise pull the Electron binary, which the linter does not need). If ESLint flags something mechanical, `npm run lint:fix` handles it; check what it rewrote before committing, since it too is repo-wide.

### Tests — [`unit-tests.yml`](.github/workflows/unit-tests.yml) and [`e2e.yml`](.github/workflows/e2e.yml)
Expand Down
Loading
Loading