* Simplify desktop client browser build
* [AI] Move browser build orchestration into vite config and lage
Moves loot-core worker build, public/ staging (migrations, default-db,
sql-wasm, data-file-index), and build-stats wiring from the deleted
packages/desktop-client/bin/build-browser shell script into a
lootCoreBackend vite plugin in packages/desktop-client/vite.config.mts.
Adds a build:browser target to lage.config.js so bin/package-browser
runs as a single `lage build:browser --to=@actual-app/web` call, with
crdt + loot-core built via lage's ^build dependency before the
desktop-client build.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Refactor e2e-test workflow and update desktop-client configurations
* [AI] Move plugins-service staging into desktop-client vite config
Declares plugins-service as a workspace devDependency of @actual-app/web
so lage's ^build edge picks it up automatically in the build:browser
pipeline, and moves the cross-package file staging (production copy +
dev serving) into vite.config.mts, mirroring the lootCoreBackend
pattern. Drops the plugins-service shell wrapper script and simplifies
its package.json scripts to invoke vite build directly. Updates root
start:browser to run plugins-service watch in parallel with the dev
server instead of pre-building once.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Sync tsconfig project references for plugins-service edge
Follow-up to the plugins-service workspace edge: adds the
../plugins-service project reference in packages/desktop-client/tsconfig.json
via yarn sync:tsconfig-references.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Release notes
* [AI] Ignore .venv/ so lage's git hasher skips Electron CI's Python venv
Electron CI provisions a Python virtualenv at the repo root for
setuptools. With browser builds now routed through lage, lage's
git hash-object pass walks untracked-not-ignored files and fails on
the venv's broken lib64 symlink ("fatal: Unable to hash .../.venv/lib64").
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Bake Weblate translations back into VRT/e2e bundle
build-web set download-translations: false and relied on bin/package-browser's
ad-hoc git clone + git pull. That path is fragile inside the playwright
container, so vite's import.meta.glob('/locale/*.json') frequently produced an
empty languages map and the bundle shipped with no en.json. VRTs then rendered
source-code English and diffed against snapshots authored from Weblate strings.
Route translation provisioning back through actions/checkout (download-translations: true)
in build-web and vrt-update-generate, and add --skip-translations to bin/package-browser
(mirroring bin/package-electron) so the in-script git pull is bypassed when CI
has already staged the locale dir.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Skip translation cloning in build-web bundle for VRT determinism
bin/package-browser used to unconditionally clone actualbudget/translations
before vite ran, baking Weblate en.json into the build artifact. With the
e2e-test pipeline now serving that artifact via serve-build.mjs, VRT
screenshots ended up rendering Weblate strings — drifting from the snapshots,
which were authored against source-code English (master VRTs ran on vite dev
without a locale dir).
Pass --skip-translations to bin/package-browser from build-web so the bundle
ships with no locale chunks. download-translations stays 'false' across the
e2e-test and vrt-update-generate workflows, matching the prior behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Add nightly CI scan for custom theme catalog
Adds a scheduled GitHub Actions workflow that fetches `actual.css` from
every repo in `customThemeCatalog.json` and runs it through the same
`embedThemeFonts` + `validateThemeCss` pipeline the app uses at install
time. Failing themes fail the job so maintainers get an alert when a
third-party repo introduces a regression.
The scan treats fetched CSS as opaque text: never executed, never
injected into a DOM, size-capped at 512 KB per file, 15s per fetch,
restricted to raw.githubusercontent.com with redirects disabled, and
run with `contents: read` permissions only. Each catalog `repo` is
schema-checked against `owner/repo` before being interpolated into
the URL.
* [AI] Simplify theme catalog scan
- Reuse `CatalogTheme` type from customThemes instead of duplicating as
`CatalogEntry` in the script.
- Hoist `appendFileSync` to the static `node:fs` import; drop the dynamic
import inside `writeStepSummary`.
- Drop the narrative header docstring and the trailing `// ...` comments
that just restated constant names.
- Drop the redundant URL-prefix re-check inside the CSS fetch helper;
the single call site constructs the URL from a pinned literal.
- Drop the 250 ms inter-request delay (GitHub Raw rate limits are not
relevant for 21 requests, and the trailing delay was idle wall-clock
against the 10-min job budget).
- Give each font fetch inside `embedThemeFonts` its own 15 s timeout
via `AbortSignal.any`, instead of sharing one signal across every
font in a theme. Drop the now-unnecessary caller-supplied signal
from the CI call site.
* [AI] Fix lint on theme catalog scan imports
* [AI] Speed up and stabilize Playwright e2e tests
- Serve the prebuilt browser bundle via bin/serve-build.mjs in CI to
skip per-shard Vite startup; 3-shard matrix with 4 workers each.
- Disable CSS animations in non-VRT runs via a fixture-level init
script; bump expect timeout to 10s for AutoSizer-bound assertions.
- Use page.evaluate() for React Aria button clicks and a native value
setter + single input event for controlled-input fills to eliminate
React Aria re-render races in createAccount and Payee/Category
autocompletes.
- Click the matching option directly (instead of Enter on a not-yet-
highlighted list) in mobile transaction and schedule autocompletes.
- FocusableAmountInput.applyText reads the DOM input value so the
typed amount survives a blur that fires before React flushes the
onChange state update under CPU contention.
- MobileTransactionEntryPage.fillAmount waits for the outer display
button (reads parent props.value) so async rules-run completes
before the next fillField snapshots the transaction.
- MobileNavigation dispatches nav link clicks through evaluate() to
bypass Playwright's viewport-stability check against the navbar's
react-spring transforms.
- MobileBudgetPage summary-button lookups use locator.or().waitFor()
instead of an isVisible() cascade.
- ConfigurationPage.startFresh/createTestFile wait for the account
header / budget table to mount before returning.
- Workflow hardening: persist-credentials=false on all actions/checkout
and top-level permissions: contents: read (zizmor findings).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Apply animation-disable init script to browser.newPage pages
The previous implementation extended the test-scoped `page` fixture,
but every test creates its own page via `browser.newPage()` and never
uses the fixture-provided page — so the init script was a no-op in
every test.
Move the wrap to the worker-scoped `browser` fixture: intercept
`browser.newPage` so each page created that way has `addInitScript`
applied before the caller can navigate to it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Refactor IS_GENERIC_BROWSER env var to --mode=browser
Replace the IS_GENERIC_BROWSER environment variable with Vite's built-in
--mode=browser flag to distinguish browser builds from Electron builds.
This aligns with the existing --mode=desktop pattern used for Electron
production builds.
Also fix build-shims.js to derive NODE_ENV from import.meta.env.DEV
instead of import.meta.env.MODE, so custom modes don't leak into
process.env.NODE_ENV.
https://claude.ai/code/session_014HvkpR59Ke4eUoiUzsUruv
* Add release notes for PR #7466
* [AI] Fix COOP/COEP headers not set with --mode=browser
The server.headers config was gated on mode === 'development', which
excluded --mode=browser. Since server.headers only applies during
vite serve (not builds), always set the COOP/COEP headers. These are
required for SharedArrayBuffer support used by the SQLite backend.
https://claude.ai/code/session_014HvkpR59Ke4eUoiUzsUruv
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Fix to show the notification for updating service worker
* cleanup
* Add release notes for PR #5963
* trigger pipeline
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Proof of concept for switching desktop-client to vite
* Fix other packages ts tests issues
* Update jsx tests to use vitest instead of jest
* Inject our global shims properly
* Add comment regarding new plugin
* Cleanup unnessary change after rebase
* Fix inter fonts pathing
* Remove manual chunks sizes for now
Just set the limit higher
* Bring back size compare
* Suppress victory warnings
* Remove craco config now that it's not used
* Add vite basic ssl plugin
- This autogenerates self-signed certs in dev mode when HTTPS env is set
- Made to match the CRA behaviour
* Add release note
* Remove warning suppression for victory
- Updated to a rollup version that includes the fix