feat(app): add the Tauri shell for Windows and Linux (phase 15) - #19
Merged
Conversation
Phase 15, and the first Rust in the repository. A Tauri v2 app that opens, renders a real `CanonicalProduct[]`, and does not fetch anything — that is phase 16. **Rust is thin and the line is drawn where §15 draws it**: one command reporting the host, a setup hook, and the WebView. Everything else is TypeScript. A rule written in Rust cannot be shared with the extension or the companion, so it gets written twice and the copies drift — and the rules most tempting to "just handle natively" are exactly the ones where a second copy is dangerous. **The front end is vanilla TypeScript, same idiom as the popup.** Not a framework, because the app's reactive surface is a table, a settings pane and a confirmation step, and one language across three surfaces means whoever has read `popup.ts` can read this. If that stops being true it is a reason to revisit — a better one than adopting a framework because the package was new. **`currency.ts` is the one safety-critical file here** and is separate so it can be reasoned about on its own. §7.8: reading toman as rial multiplies every price by ten and nothing downstream catches it. Two rules are enforced in the module rather than left to the view — there is no default, because `undefined` means unanswered and unanswered blocks export, and the question is only asked when at least one price really is IRR with no unit stated, since a prompt that fires when there is nothing to decide is one people learn to dismiss. Each choice shows what the first price *becomes* under that reading, because the ten-times difference should be visible rather than arithmetic. **§18's design system**: the popup's warm palette at a size that is not 360px, dark and light with the override winning in both directions, Persian and English with real RTL through logical properties rather than `left`/`right`. Persian digits are a display transform only — the data stays ASCII all the way to the CSV. The 44px touch target and the single phone breakpoint are in now rather than retrofitted later. **Rust joins CI as its own job, not as part of `npm run check`.** Phase 15 asked this directly. `npm run check` is what somebody runs on every save, and putting a ten-minute cold `cargo build` plus a hard dependency on rustup and three system libraries in front of a typo fix in an exporter is the wrong trade when most changes never touch this package. A Rust failure still blocks a merge, because the gate is the workflow rather than any one script. Two tooling fixes of the same kind as the earlier `.claude/` one: ESLint walks the directory tree rather than git, so it was linting Cargo's `target/` output and Tauri's generated schemas. Both ignored. `target/` and `gen/` are gitignored; `Cargo.lock` is not, because this is a binary and the lockfile is what makes its build reproducible. Verified on Linux: builds, opens, renders, and `cargo fmt`, `clippy -D warnings` and `cargo test` are clean. Windows is not verified yet, so phase 15 stays `next`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decided after seeing phase 15 run. Windows and Linux get completed — 16, 17 and 20 — and Android starts after them. Android brings a second toolchain and an unresolved distribution question, so starting it now buys two half-finished platforms instead of one finished one. The stronger reason is that the desktop UI is about to be designed directly rather than scaffolded, and designing once against a shipped app then porting is a different job from designing for two form factors with neither settled. The phase numbers do not move. They are cited from commit messages, prompt files and phases.json, and renumbering to express an ordering change would invalidate all of that to say something a sentence says better. Only the order they are worked in changes. What keeps this a deferral rather than a decision to un-make later is already in the code: phase 15 put in the 44px touch target, the single phone breakpoint and logical properties throughout. The constraint that goes with the deferral is that no desktop-only shortcut gets taken in 16, 17 or 20 on the grounds that Android is far away. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Phase 15's own "Done when" asked for Windows and Linux. Linux is verified — it builds, opens and renders, and CI builds the Rust side on every pull request. Windows is not, and rather than hold the phase open for a check that phase 20's build matrix performs anyway, the Windows verification moves there and is written into the roadmap as something that phase owns rather than something that got lost. Two concrete things it inherits, both already known: make the CI job a matrix over ubuntu-latest and windows-latest, which is the `runs-on` line because the npm scripts are already platform-neutral, and generate `icon.ico`, without which the MSI bundle will not build. Leaving phase 15 as `next` was the alternative and it is worse. docs/prompts/README.md says a finished phase that still says `next` gets started again by somebody, and that is a real cost for a box that a later phase is better placed to tick. Phase 16 becomes next. The README gains the app in its package table and its test count catches up to 831. Also records in packages/app/README.md that the visual design is not settled in this repository and should not be — what is in `styles.css` is a working neutral. The structure is already built for that: every colour and gap is a custom property in one block and `main.ts` hard-codes neither, so a redesign touches one file rather than the views. Two things a redesign must not drop are named, because they are §18 requirements rather than taste — the currency step keeps its own weight and never becomes a skippable checkbox, and directional rules stay logical properties or the Persian layout quietly stops being correct. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Phase 15, and the first Rust in the repository. A Tauri v2 app that opens, renders a real
CanonicalProduct[], and deliberately does not fetch anything — that is phase 16.Rust is thin, and the line is where §15 draws it. One command reporting the host, a setup hook, the WebView. Everything else is TypeScript. A rule written in Rust cannot be shared with the extension or the companion, so it gets written twice and the copies drift — and the rules most tempting to handle natively are exactly the ones where a second copy is dangerous.
The front end is vanilla TypeScript, same idiom as the popup. Not a framework: the app's reactive surface is a table, a settings pane and a confirmation step, and one language across three surfaces means whoever has read
popup.tscan read this. If that stops being true it is a reason to revisit — a better one than adopting a framework because the package was new.currency.tsis the one safety-critical file here, and it is separate so it can be reasoned about alone. §7.8: reading toman as rial multiplies every price by ten and nothing downstream catches it. Two rules live in the module rather than in the view:undefinedmeans unanswered, and unanswered blocks export. A pre-selected radio button is a guess wearing the costume of a decision.Each choice shows what the first price becomes under that reading (460,000 against 46,000), because the ten-times difference should be visible rather than arithmetic the user does in their head.
§18's design system: the popup's warm palette at a size that is not 360px, dark and light with the override winning in both directions, Persian and English with real RTL through logical properties rather than
left/right. Persian digits are a display transform only — the data stays ASCII all the way to the CSV.The question phase 15 asked directly
Rust joins CI as its own job, not as part of
npm run check.npm run checkis what somebody runs on every save. Putting a ten-minute coldcargo build— plus a hard dependency on rustup and three system libraries — in front of a typo fix in an exporter is the wrong trade when most changes to this repository never touchpackages/app. A Rust failure still blocks a merge, because the gate is the workflow rather than any one npm script. There is acargo fmt/clippy -D warnings/cargo testjob on this PR and it has to pass.Also here
Two tooling fixes of the same kind as the earlier
.claude/one: ESLint walks the directory tree rather than git, so it was linting Cargo'starget/output and Tauri's generated schemas. Both ignored.target/andgen/are gitignored;Cargo.lockis not, because this is a binary and the lockfile is what makes its build reproducible.The second commit records a sequencing decision taken after seeing the app run: Windows and Linux get finished before Android starts, so the working order becomes 16, 17, 20, then 18. Phase numbers do not move — they are cited from commits, prompt files and
phases.json, and renumbering to express an ordering change would invalidate all of that to say something a sentence says better. What keeps it a deferral rather than a decision to un-make later is already in this PR: the 44px touch target, the single phone breakpoint, logical properties throughout.How it was verified
npm run checkpasses — 831 tests, unchanged, since this phase adds a surface rather than engine behaviour.On Linux:
cargo buildsucceeds, the app opens, and the shell renders the fixture.cargo fmt --check,cargo clippy --all-targets -- -D warningsandcargo testare all clean. Reviewed running by the maintainer.Windows is not verified, which is why
scripts/release/phases.jsonstill saysnextfor phase 15 rather thandone— its own "Done when" asks for both platforms. There is a Windows machine available and that is the remaining box; a native build there is the check, not a cross-compile from Linux (see below).Two things that are absent on purpose and would be bugs if present: the app makes no network request of any kind, and Export does not write a file. Both are phase 16.
Notes for phase 16, already written into
phase-16.mdcorealready takes an injectedHttpClient; the app needs one implementation overinvoke, and Layer A, the crawler and politeness then work unchanged because they never knew what was underneath.core. A native shell removes the browser's rate limits, which makes §10 more important rather than less.ubuntu-latestandwindows-latest— a change to theruns-online and nothing else. Windows will also need an.ico, whichsrc-tauri/icons/does not have yet.Checklist
npm run checkpasses (format, lint, typecheck, tests)🤖 Generated with Claude Code