Commit Graph

5254 Commits

Author SHA1 Message Date
youngcw
49d5a1519b [AI] Delay initial server sync until account data has loaded
On startup the server sync was kicked off 100ms after mount, racing
the first visible page's spreadsheet queries. Sync applies messages in
long synchronous stretches on the single worker thread, so account
balance calculations queued behind it (most noticeable on mobile).

Hold the initial sync until the accounts query has settled (with a 5s
fallback), so the first screen computes its balances before the sync
starts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 08:32:21 -06:00
lelemm
4556e9e244 🐛 Prevent Pluggy bank sync from repeatedly rewriting transactions (#8392)
* Prevent Pluggy bank sync from repeatedly rewriting uncategorized and transfer transactions

* Regression test

* Changed solution for the fix

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-08 19:33:28 +00:00
Stephen Brown II
f803a918ad [AI] Skip unused transfer payees in nYNAB import (#8424)
* [AI] fix(ynab-import): skip literal transfer payees in nYNAB import

YNAB auto-generates a 'Transfer : <Account Name>' payee for every
account-to-account link. Actual already has its own account-transfer
payee per account and transactions resolve to that instead, so the
literal-text payee was being created but never used, just cluttering
the Payees list after import.

* [AI] test(ynab-import): cover transfer-payee resolution in importTransactions

Regression test proving a transfer transaction resolves to the real
Actual account-transfer payee via transfer_account_id, and never
needs an entityIdMap entry for the (now-skipped) YNAB transfer payee.

* [AI] docs: add release note for skipping ynab transfer payees
2026-07-08 14:13:02 +00:00
Matt Fiddaman
9dbee1078a custom report clickthrough for category groups (#8391)
* hide focus styling

* allow clickthrough to group filters

* note
2026-07-07 14:54:54 +00:00
youngcw
b611eff4f1 [AI] Fix mobile swipe-to-delete broken by React Aria Button stopping pointerdown propagation (#8421)
The a11y commit (65dee4c) replaced the plain div with a React Aria Button
in ActionableGridListItem. React Aria's usePress unconditionally calls
stopPropagation() on pointerdown, preventing the event from reaching the
parent animated.div where useDrag listens. Adding onPressStart with
continuePropagation() lets the event bubble through so the swipe gesture works.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 21:21:38 +00:00
tabedzki
6519bbecb6 [AI] feat(budget analysis report): export csv feature (#8163)
* [AI] feat(budget-report): add CSV export for budget analysis report

Add an 'Export CSV' button that downloads a CSV file containing Month,
Budgeted, Spent, Overspending Adjustment, and Balance columns for the
currently displayed date range.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] feat(budget-report): replace CSV export button with download icon

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] refactor(csv-export): centralize CSV utility to loot-core with formula injection protection

Extracts CSV field escaping and row building into a shared
loot-core/shared/csv.ts module so desktop-client and cli packages can
reuse it without duplication. Adds single-quote prefix guard against
spreadsheet formula injection for values starting with =, +, -, @, tab,
or carriage-return.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(csv-export): fix loot-core import path and remove over-broad formula injection protection for negative numbers

* fix: added in release note

* Update packages/desktop-client/src/components/reports/reports/BudgetAnalysis.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update BudgetAnalysis.tsx

removed extra </Tooltip> introduced by coderabbitai

* fix: applied coderabbitai's suggestions for this PR

* [autofix.ci] apply automated fixes

* [AI] fix: resolve typecheck errors and test failures in csv export

- Make labels param optional with English defaults in buildBudgetAnalysisCsv
- Pass translated labels from BudgetAnalysis component call site
- Fix escapeCsvField: remove tab from quote-wrapping check (formula prefix handles it)

Assisted-by: ClaudeCode:claude-sonnet-4-6

* [AI] refactor(budget-analysis-export): use csv-stringify, move labels inside buildBudgetAnalysisCsv

- Replace custom buildCsv with csv-stringify/sync (already used in loot-core)
- Move translated column labels inside buildBudgetAnalysisCsv; callers no longer pass labels
- Pass numbers directly to csv-stringify instead of converting with toFixed(2)
- Add csv-stringify to desktop-client package.json deps
- Fix release notes title to user-facing prose

Assisted-by: ClaudeCode:claude-sonnet-4-6

* [AI] fix: remove orphaned csv.ts, fix release note wording

- Delete loot-core/src/shared/csv.ts and csv.test.ts (orphaned after switch to csv-stringify)
- Remove [AI] prefix from release note body (commit-style prefix inappropriate in release note content)

Assisted-by: ClaudeCode:claude-sonnet-4-6

* [AI] fix: add csv-stringify to yarn.lock to fix immutable install

The rebase onto main reset yarn.lock without the csv-stringify entry under the @actual-app/web workspace, while package.json still declared it. CI runs yarn install --immutable, which refused to reconcile the mismatch and failed the build.

Assisted-by: ClaudeCode:claude-opus-4.8

* attempt to resolve the hanging error

* [AI] fix: restore Tooltip import dropped during rebase

Resolving the import-collision conflict against master accidentally dropped
the Tooltip import used by the CSV export button. Restore it.

Assisted-by: ClaudeCode:claude-opus-4.8

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-06 20:19:20 +00:00
Matiss Janis Aboltins
168fe0d68b [AI] Add api.importBudget() and api.exportBudget() (#8414)
* [AI] Add api.importBudget() and api.exportBudget()

Add first-class import/export functions to @actual-app/api so consumers
no longer have to drive the low-level worker RPC and the engine
filesystem by hand.

- api.importBudget(pathOrBytes, { type?, filename? }) imports an Actual
  .zip export (or a YNAB4/YNAB5 export), loads it, and resolves with the
  imported budget id. Raw bytes are passed straight to the import-budget
  handler instead of the upload-file-web temp-file dance, so it works in
  both the Node and browser builds and leaves no temp files behind.
- api.exportBudget() exports the currently-loaded budget and resolves
  with the zip contents as a Uint8Array.
- Both reject with an Error on failure instead of resolving { error }.
- The import-budget handler now optionally accepts raw file contents
  (buffer + filename) alongside the existing filepath input, and returns
  the id of the loaded budget on success; existing callers are
  unaffected.
- Add round-trip, file-path, and failure integration tests; update the
  API reference docs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wq2oMcHb99mLb9qpaR8SN

* [AI] Move ImportableBudgetType re-export to models.ts

Type re-exports for API consumers live in models.ts (like Preferences),
not methods.ts — follow the existing pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wq2oMcHb99mLb9qpaR8SN

* [AI] Address review: relax error assertions, document type union

- Assert importBudget failures on the stable "Error importing budget:"
  prefix instead of backend-specific error codes
- Document options.type as the 'actual' | 'ynab4' | 'ynab5' union in the
  API reference instead of a generic string

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wq2oMcHb99mLb9qpaR8SN

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-06 17:42:45 +00:00
Lucas
c64c0aa049 Improve crossover reloads (#8352)
* fix: Preserve crossover report view during refresh

* fix: Preserve crossover report view during refresh

---------

Co-authored-by: youngcw <calebyoung94@gmail.com>
2026-07-06 17:35:58 +00:00
Matiss Janis Aboltins
af799718f8 [AI] Add api.getPreferences() for reading synced preferences (#8410)
* [AI] Add api.getPreferences() for reading synced preferences

Expose the internal 'preferences/get' handler as a public API method so
consumers can read the budget's synced preferences (number format,
currency, date format, etc.) without calling internal handlers directly.
Re-export the SyncedPrefs type from @actual-app/api/models and document
the new method in the API reference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxwSQJiPo5mEk79mA54TWg

* Update models.ts

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-05 05:45:03 +00:00
lelemm
744dd1eaff Bank Sync Provider per budget file [2/3] - Sync server - Pluggy.ai use per-budget credentials when available (#8317)
* PR for pluggy ai wiring per file budget ni pluggy

* Fixes from rebase

* Code rabbit code review

* [autofix.ci] apply automated fixes

* added md

* Restoring client cache

* code review

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-04 19:10:51 +00:00
Matt Fiddaman
73c1fb19e9 recognise EUA expired GoCardless responses (#8383)
* recognise EUA expired GoCardless responses

* note

* add test
2026-07-04 13:08:32 +00:00
Matt Fiddaman
88e1e5fc17 prevent duplicate draft releases (#8381)
* prevent duplicate draft releases

* note
2026-07-04 12:22:39 +00:00
lelemm
c46e64f445 Change single quote to double quotes usage (#8402) 2026-07-04 09:25:44 +00:00
Matiss Janis Aboltins
c2745187e5 [AI] Add writing-release-notes skill (#8398)
* [AI] Add writing-release-notes skill

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015pA2AZaXmt8ebDNNYEdBDA

* [AI] Remove em-dashes from writing-release-notes skill

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015pA2AZaXmt8ebDNNYEdBDA

* [AI] Add release note for writing-release-notes skill

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015pA2AZaXmt8ebDNNYEdBDA

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-04 05:58:12 +00:00
Julian Dominguez-Schatz
5fc6f7638c Check in mobile app scaffolding generated by Capacitor (#8387)
* Check in app scaffolding generated by Capacitor

All files generated by `npx cap init` and `npx cap sync`.

* Add release notes

* Add package.json file

* Update capacitor

* Prune deps

* CodeRabbit feedback
2026-07-03 21:31:30 +00:00
deathblade666
d19312f374 add Zero Dark theme (#8396)
* Add Zero-Dark theme

* Update customThemeCatalog.json

* Create add-zero-dark-theme.md
2026-07-03 20:59:57 +00:00
Shivam S
541caba5b7 fix: add scoped ErrorBoundaries to mobile budget, account, and transaction pages (#8336)
* fix: add scoped ErrorBoundaries to mobile budget, account and transaction pages

Wrap the mobile BudgetPage, AccountPage and TransactionEdit in
<ErrorBoundary FallbackComponent={FeatureErrorFallback}> so a rendering
error in one of these pages is contained instead of crashing the whole app.

Relates to #7391

* [autofix.ci] apply automated fixes

* fix(desktop-client): enclose mobile page hooks within scoped ErrorBoundary

The scoped ErrorBoundary added for the mobile budget, account, and
transaction-edit pages only wrapped the returned JSX, so errors thrown by
the pages' own hooks and initialization logic (data loading, useEffect)
still escaped to the global fatal screen, defeating the boundary.

Move each page's body into a child component rendered inside the boundary
so the boundary encloses the full hook/render path, and add resetKeys so
the feature recovers when the account (accountIdParam) or month
(startMonth) changes.

Addresses CodeRabbit review feedback on the mobile error-boundary PR.

* refactor(desktop-client): remove prose comments from mobile error boundaries

Drop the explanatory comments added with the scoped error boundaries on the
mobile account, budget, and transaction edit pages, per maintainer review
feedback. The code is self-explanatory without them; behavior is unchanged.

* refactor(desktop-client): move mobile error boundaries to the route level

Wrap the mobile budget, account, and transaction edit routes in FinancesApp
with the same ErrorBoundary/FeatureErrorFallback pattern already used for the
/rules routes, instead of splitting each page into a wrapper and inner
component. Each page renders as a child of the boundary, so its hooks and
initialization logic are still enclosed, and the page components are left
unchanged.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-03 20:31:52 +00:00
lelemm
b054e9a013 Bank Sync Provider per budget file [1/3] - Sync server - Add file-scoped bank sync secrets (#8316)
* First PR for secrets per file

* md

* Code Review

* Code rabbit code review

* Removed migration (new column) in favor of string concatenation

* automation UI: reference schedule by ID not name (#8308)

* schedule by id

* migrate schedule indicators

* add tests

* update release note

* fix loading states

* [AI] feat(budget analysis report): Add Balance & Category selector (#8162)

* [AI] feat(budget-report): add balance-only view mode

Add a 'Balance only' toggle to the Budget Analysis report that hides the
Budgeted, Spent, and Overspending Adjustment series and renders the balance
as a plain line chart, similar to the Net Worth graph.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-report): always display ending balance for consistent header spacing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] feat(budget-report): replace balance toggle buttons with series dropdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-report): capitalize Categories in balance mode dropdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-report): fix test imports and lint errors in BudgetAnalysisGraph test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: added in release note

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] Fix Age of Money report widget title not saving correctly (#8320)

* [AI] Fix Age of Money report widget title not saving correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] Use mutateAsync to properly await mutations in AgeOfMoney widget

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix flaky vrt by increasing timeout (#8323)

* fix test

* note

* Update docs with Actuali iOS app (#8324)

* Update docs with Actuali iOS app

* Update packages/docs/docs/community-repos.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Initial payee location documentation (#8241)

* Initial payee location documentation

* Move to dedicated experimental section

Based on PR feedback

* [AI] feat(budget analysis report): show hidden categories (#8164)

* [AI] feat(budget-report): add toggle to include hidden categories

Add a 'Show hidden categories' button that includes hidden expense
categories in the Budget Analysis report. This prevents historic data
from being misrepresented when a category (e.g. a car fund) is hidden.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] feat(budget-report): replace hidden categories button with icon toggle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-analysis): use correct package alias in test import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: added in release note

* [AI] refactor(budget-analysis): extract isBaseCategory helper and reuse in tests

Assisted-by: ClaudeCode:claude-sonnet-4-6

* [AI] feat(budget-report): clarify hidden categories toggle state

Replace the ambiguous icon-only toggle with a highlighted active state
and explicit click-to-show/hide tooltip text so the current selection is
clear. Add aria-pressed for accessibility.

Assisted-by: ClaudeCode:claude-opus-4.8

* [AI] feat(budget-report): consolidate chart options into Options dropdown menu

Replace the separate chart-type icon toggle, balance/category Select dropdown,
and hidden-categories eye icon with a single Options popover (Popover + Menu)
matching the pattern used by other reports (Sankey). The menu contains:
- Switch to line/bar chart
- Show balance (toggle)
- Show categories (toggle, replaces balanceOnly)
- Show hidden categories (toggle)

Also rebases onto upstream/master to incorporate the balanceOnly/Select
changes from #8162 before consolidating them.

Assisted-by: ClaudeCode:claude-sonnet-4-6

* [AI] fix(budget-report): use static 'Bar chart' toggle in Options menu for i18n

Replace the dynamic 'Switch to line/bar chart' string with a static
'Bar chart' label and a boolean toggle (on = bar, off = line), matching
the toggle pattern used by other menu items and making the string
straightforward to translate.

Assisted-by: ClaudeCode:claude-sonnet-4-6

* Enhance budget analysis report with dropdown options

Introduced a dropdown to group options and added the ability to show hidden categories in the budget analysis report.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* update release process (#8334)

* [AI] Add cross-platform hook adding 🤖 emoji to comments (#8331)

* [AI] Add cross-platform hook keeping GitHub comments/issues in Chinese or pirate voice

Adds a shared agent hook that requires anything an agent posts to GitHub
(PR/issue comments, PR reviews, created issues) to be written in 简体中文
(preferred) or, failing that, a fun pirate voice — never plain English.

- scripts/agent-hooks/github-comment-style.sh: shared guard. Reads
  tool_input.body/.title, allows CJK or pirate-flavoured text, blocks plain
  English via exit 2. Fails open on malformed payloads.
- Wired for Claude (.claude/settings.json PreToolUse), Codex
  (.codex/config.toml PreToolUse) and Cursor (beforeMCPExecution adapter).
- Documents the rule in AGENTS.md, the canonical pr-and-commit-rules.md and
  the Cursor rule so platforms without hook support apply it manually.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Use a slug for the release-note filename; document slug naming in AGENTS.md

Release-note filenames don't need to be the PR number — a short descriptive
slug works too (the PR link is resolved at release time). Rename the new note
accordingly and note this in AGENTS.md's directory reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [autofix.ci] apply automated fixes

* [AI] Auto-label PRs with Chinese descriptions as "ai spam" via CodeRabbit

Add a CodeRabbit labeling instruction (auto_apply_labels is already on) that
applies the "ai spam" label when a PR description contains Chinese/CJK
characters — a strong signal of AI-generated spam for this English-language
project.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Exempt CodeRabbit-addressed comments from the Chinese/pirate voice hook

CodeRabbit parses its commands (@coderabbitai review/resolve/etc.) as plain
English, so the github-comment-style guard now skips any comment mentioning
@coderabbitai / @coderabbit instead of forcing it into Chinese or pirate.
Documented the exception in the canonical and Cursor rule files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Key CodeRabbit voice exemption on authorship, not mentions

The hook only ever runs on the agent's own outgoing comments, so CodeRabbit's
own comments are already out of scope (it posts under its own identity). Drop
the @coderabbitai mention bypass — a comment merely name-dropping the bot is
still the agent's prose and must follow the Chinese/pirate rule, closing an
easy plain-English bypass. Docs updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Pin CodeRabbit reviews to English

CodeRabbit was reading AGENTS.md / pr-and-commit-rules.md (which ask
contributors to comment in Chinese/pirate) and applying that to its own
reviews, posting them in Chinese. Set `language: en-US` and `tone_instructions`
so the bot's own reviews, summaries and replies stay in English; the
Chinese/pirate rule is for contributor/agent comments, not CodeRabbit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Replace Chinese/pirate comment voice with a robot-emoji prefix

Scrap the 简体中文/pirate voice scheme (and its CodeRabbit language override and
Chinese-based "ai spam" label) in favour of one simple rule: every GitHub
comment, review or issue an agent posts must be prefixed with 🤖. The shared
guard now blocks any body — or, for issues, title — that doesn't start with the
robot emoji; docs and per-platform wiring updated to match. .coderabbit.yaml is
restored to its original state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Silence SC1007 false positive on CDPATH= cd in Cursor MCP adapter

shellcheck flags the intentional `CDPATH= cd` empty-env prefix as SC1007
(mistaking it for an assignment). Add a scoped inline disable directive on that
line; behaviour is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Fail closed on guard execution errors in Cursor MCP adapter

The adapter previously allowed the call on any non-0/2 exit from
github-comment-style.sh, so a broken or missing guard would silently disable
enforcement. Deny on unexpected exits instead (matching guard-shell.sh), with a
message that marks it as an execution problem rather than a real policy denial.
The shared guard still fails open on malformed payloads (its own exit-0 choice).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Add hook requiring a blank PR template when agents create PRs

New shared guard pr-template-blank.sh blocks mcp__github__create_pull_request
unless the body is the repo's PR template, unmodified (cosmetic whitespace
aside) — agents must leave it blank for the human, per AGENTS.md. Wired for
Claude (PreToolUse), Codex (PreToolUse) and Cursor (the beforeMCPExecution
adapter now dispatches per-tool to the right guard). Docs and a release note
updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Simplify agent-hook guard scripts

- github-comment-style.sh: fold the empty/whitespace-only case into the
  prefix check, dropping a redundant tr subshell per field.
- pr-template-blank.sh: drop the dead CR strip in canon() (the trailing
  whitespace sub already removes a trailing carriage return).
- before-mcp-github.sh: emit the constant allow payload with printf instead
  of spawning jq on the common (allow) path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Drop duplicated PR-template/robot-emoji rules now that hooks enforce them

The PR-template-blank and GitHub-comment robot-emoji-prefix rules are enforced
by cross-platform hooks, so the copies scattered across AGENTS.md and the
Cursor rules file are redundant. Remove them and point at the canonical
pr-and-commit-rules.md, which keeps the full rule (including the PR-template
Chinese exception that no hook can enforce). Also delete the internal
PR-template-guard release note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Drop "enforced by hooks" wording from the PR/comment rule docs

State the rules plainly without the meta-commentary about hook enforcement.
Keeps the actual rules (don't fill the PR template, prefix GitHub
comments/reviews/issues with 🤖, the Chinese exception, and the
your-own-comments-only note) intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Trim enforcement meta-commentary; shorten robot-emoji release note

Drop the "isn't enforced automatically" / "handled by tooling" framing from
AGENTS.md and pr-and-commit-rules.md (keeping the rule and the "apply it
yourself" responsibility), and shorten the robot-emoji-prefix release note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Fail closed on unreadable payloads in the GitHub agent guards

Align the new guards with the git-guard.sh / guard-shell.sh convention: fail
closed when the hook payload can't be read, fail open only on a genuinely
absent optional field.

- github-comment-style.sh: block on invalid JSON / missing / non-object
  .tool_input; allow only an absent body/title within a valid object.
- before-mcp-github.sh: deny on a jq parse failure / missing .tool_name
  instead of falling through to allow.
- pr-template-blank.sh: an absent/null body now fails open (previously it
  normalized to "" and wrongly blocked PR creation); an empty-string body is
  still treated as a real non-template submission.
- .codex/config.toml: describe the full guard scope (comment body + issue
  title) in the hook comment and statusMessage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* speed up electron builds (#8337)

* size up runners, skip translations where possible

* note

* mdx -> md (#8338)

* mdx -> md

* [AI] Outlaw .mdx in the docs package

Docusaurus 3 compiles .md through MDX, so JSX and imports work in plain
.md files and there is no need for the .mdx extension. Narrow the
enforce-doc-links remark plugin to only accept .md: reject any .mdx
source file at build time, flag links that target a .mdx file, and drop
the .mdx branches from the directory scanner and link resolution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] Add performance indexes for transactions table (#8335)

* [AI] Add performance indexes for transactions table

* release notes

* [AI] Remove unselective tombstone-only index from performance migration

* [AI] api: make the browser build work in consumer production bundles (#8289)

* api: make the browser build work in consumer production bundles

The browser build shipped `new Worker(new URL("./worker.js", import.meta.url))`
in dist/browser.js. A consumer bundler (Vite/Rollup) recognizes that as a worker
entry and re-bundles the already-prebuilt worker.js from scratch, which desyncs
the absurd-sql/connection RPC so `init` throws a structured-clone error. It only
worked in dev and in the package's own e2e because both serve dist verbatim.

Make the browser build fully self-contained instead:

- Inline the worker into browser.js and spawn it from a Blob URL, so consumer
  bundlers never see a worker entry to re-bundle. The worker is built as an IIFE
  (classic worker), mirroring the web app's kcab.worker.
- Embed the sql.js wasm and the default filesystem data (migrations + default
  DB) into the worker so it performs no PUBLIC_URL asset fetches:
  - loot-core sqlite gains an opt-in `setWasmBinary` (dormant for web/node);
  - the worker installs a scoped fetch shim serving the embedded data/* files
    from a sentinel base URL.
- Share the asset-collection logic between the Node disk copy and the embedded
  build via scripts/embedded-assets.mjs so the two never drift.

Result: `import '@actual-app/api'` + `init()` works in any bundler with zero
config; only COOP/COEP headers remain required (SharedArrayBuffer).

Adds an e2e (e2e/consumer + e2e/global-setup.mjs) that builds a real consumer
app for production and boots it under COOP/COEP — coverage the verbatim-dist
harness can't provide. Docs note the build is self-contained and that
cross-origin isolation is still required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: address review feedback (guard DOM lookup, revoke blob URL on worker failure)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: address review nitpicks (drop BodyInit cast, reword release note)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: build the browser worker via Vite ?worker&inline

Replace the hand-rolled worker inlining (a custom Vite plugin that read the
prebuilt worker.js off disk and inlined it as a string, plus a second build
config and a build-ordering dependency) with Vite's native `?worker&inline`
import. This collapses the two browser build configs into one, removes the
`virtual:actual-worker-code` module and the manual Blob URL spawn, and drops
the worker-before-facade build step.

Vite's `?worker&inline` sub-build doesn't receive vite-plugin-node-polyfills'
global-shim injection (the plugin writes it to `build.rollupOptions`, but the
worker sub-build reads `worker.rollupOptions`), so the worker crashed on
`process is not defined`. Patch the plugin to also emit the injection on
`worker.rollupOptions` — additive and backwards compatible (the web app build,
including loot-core's nodePolyfills worker, is unaffected).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: embed browser assets via Vite, drop the embedded-assets script

Replace scripts/embedded-assets.mjs with Vite-native asset imports in the
worker entry: `?inline` for the sql.js wasm and default DB, `import.meta.glob`
(`?raw`) for the migrations. The worker builds the default-filesystem wire
format from those at module load, so the embedded set comes straight from
loot-core and can't drift.

The Node build only needs migrations + the default DB on disk (it reads them at
runtime); the old script also wrote sql-wasm.wasm and the data/ fetch tree,
which are dead now that the browser worker is self-contained. Replace
writeEmbeddedAssetsToDist with a small closeBundle copy of just those two.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: simplify embedded data map; drop loot-core comment churn

Merge the worker's binData/textData into a single dataFiles map (Response
accepts both bytes and strings), collapsing the fetch shim's two lookups into
one. Revert the unrelated comment edits to loot-core's backend-worker.ts and
fs/index.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: rename e2e consumer-dist to consumer/dist; drop a prose comment

Use the standard `dist` directory name for the consumer fixture's build output
(now e2e/consumer/dist), and remove the verbose comment from index.browser.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] loot-core: own the default-filesystem assets; consumers stop reaching in

Add packages/loot-core/default-filesystem.mjs as the single source of truth for
loot-core's runtime assets (sql.js wasm, default DB, migrations) and the
data-file-index wire format, exported as @actual-app/core/default-filesystem.

- @actual-app/api: the browser worker embeds them via the actual-embedded-assets
  Vite plugin (which calls collectEmbeddedAssets), replacing the relative
  ?inline/?raw/import.meta.glob reaches into ../loot-core; the Node build copies
  migrations + default DB using the helper's paths. Drops the direct
  @jlongster/sql.js devDependency (loot-core resolves the wasm).
- @actual-app/web: stagePublicData copies from the helper's paths instead of
  hardcoding loot-core's tree and the sql.js wasm location.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] loot-core: scan migrations once when collecting embedded assets

collectEmbeddedAssets() read the migrations directory twice — once in its
own loop and once via buildDataFileIndex(). List the names once and pass
them through, keeping the index wire-format defined in a single place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Restore budget table scroll position when navigating back from spent transactions (#8144)

* feat: Restore budget table scroll position when navigating back from spent transactions page

* fix: Update transaction tests to include new categories and adjust assertions

* Add release notes for budget scroll position restoration

* refactor: Improve error handling when clicking on visible spent-amount cells, per suggestion from coderabbit bot

* refactor: revert changes to test budget, add separate scroll-test test budget

* revert previous snapshot changes

* Add gitignore pattern for personal devcontainer config

* fix: broken tests

* refactor: address coderabbitai feedback

https://github.com/actualbudget/actual/pull/8144#discussion_r3446850158

* fix: update VRT snapshots for onboarding screen

* [autofix.ci] apply automated fixes

* refactor: modify scrolling test to add categories to existing test budget instead of creating separate budget

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* [AI] Add design competition blog post for sidenav redesign (#8297)

* [AI] Add design competition blog post for sidenav redesign

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF

* [AI] Set design competition blog post publish date to 27th July 2026

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF

* [AI] Remove stray closing tags from design competition blog post

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF

* Update 2026-07-27-design-competition-sidenav.md

---------

Co-authored-by: Claude <noreply@anthropic.com>

* Always show stacked bar graph tooltip label on hover (#8356)

* Always show stacked bar graph tooltip label on hover

* Handle edge case with maxToolTipItems+1 labels showing

* Replace last item label instead of adding new one

* Filter visible items on non zero values for consistency

* make SimpleFIN token 'forbidden' check more flexible (#8339)

* make SimpleFIN token 'forbidden' check more flexible

* clear accessKey when token is updated

* https -> fetch

* [AI] Add tests for SimpleFIN token claim fix

Cover the flexible 'Forbidden' matching, full claim-response handling, and access-key invalidation when the setup token changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* note

* Update upcoming-release-notes/fix-simplefin-token-claim.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* coderabbit feedback

* [AI] Test SimpleFIN blank access key handling

Cover rejecting a blank claim response (not persisted) and re-claiming when an empty access key is cached.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
Co-authored-by: tabedzki <35670232+tabedzki@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: youngcw <calebyoung94@gmail.com>
Co-authored-by: Matt Farrell <10377148+MattFaz@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Dustin Brewer <mannkind@thenullpointer.net>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Alec Bakholdin <43560338+alecbakholdin@users.noreply.github.com>
Co-authored-by: clintharris <336401+clintharris@users.noreply.github.com>
Co-authored-by: Darwin Do <8429648+dsmaugy@users.noreply.github.com>
2026-07-03 19:58:35 +00:00
Alec Bakholdin
0b239e0a6d [AI] Add tag button mobile (#8344)
* added hash button to enable one-click mobile usage of tags feature

* added tags to demo

* release notes

* [autofix.ci] apply automated fixes

* fixed long tags wrapping

* removed duplicate <Button> usages and fixed coderabbit smell

* removed lint

* added null check per coderabbit

* moved Menus to after the menu to be consistent with transaction menus

* textWrap => whiteSpace for coderabbit

* updated tsconfig to fix typecheck issue

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #8344

* typecheck

* improved button ergonomics

* also don't add space if beginning of note

* fixed not working on iOS

* handle focus properly for add tag button

* fixed focus issue when clicking button on new transaction

* added delay because without it chrome doesnt work

* Added some more logic for button focusing

* fix typecheck

* extracted common style into iconStyle

* added aria label

* removed log statement

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-03 10:33:00 +00:00
Matt Fiddaman
e8c7f8040e bcrypt -> argon2id (#7829)
* bcrypt -> argon2id

* coderabbit feedback

* copilot review

* switch hashing settings

* [AI] Add unit tests for sync-server password hashing

Cover password.js, focused on the bcrypt -> argon2id migration:
- loginWithPassword upgrades a legacy bcrypt hash to argon2id on a
  successful login, leaves it untouched on a failed login, and does not
  needlessly rehash an existing argon2id hash
- hashPassword/verifyPassword round-trip, bcrypt backward-compat, and
  graceful handling of non-string/malformed hashes
- bootstrapPassword/changePassword/checkPassword behaviour

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* dedupe minimatch

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:04:07 +00:00
Archit Goyal
a120faf81b [AI] Clarify nYNAB export alternatives (#8330) 2026-07-02 23:37:30 +00:00
Martin Lukas
62ced9aa2b Reload when PWA shared worker is killed by Android (#8322)
* Reload when PWA shared worker is killed by OS

Android can kill a SharedWorker process while the PWA is
in the background. The the PWA become unresponsive when user reopens
it. This change introduces a 1s reload timer whenever the tab becomes
visible, and uses the 'resume association' mechanism to track if
the shared worker is responsive (which cancels the timer). If it's not,
it reloads the page and recovers the shared worker.

* [autofix.ci] apply automated fixes

* Improve release note

* Simplify guard for app init (started safer, initialized was redundant)

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-02 19:53:30 +00:00
dependabot[bot]
cbc056536b Bump js-yaml in the npm_and_yarn group across 1 directory (#8379)
Bumps the npm_and_yarn group with 1 update in the / directory: [js-yaml](https://github.com/nodeca/js-yaml).


Updates `js-yaml` from 3.14.2 to 3.15.0
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.2...3.15.0)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 3.15.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 19:13:07 +00:00
lelemm
72b28a6d2a 🐛 Fix Formula query sliding-window timeframes (#8244)
* clean up sliding window to use the default feature

* more feedbacks

* Fix formula sliding window helper import

* Use report time range for formula queries

* fixes

* Trim formula sliding window fix

* code rabbit code review

* removing local helpers

* change query definitions to show the mode when not static / sliding window

* [autofix.ci] apply automated fixes

* Simplified even more the code

* Added test to sliding window + feedback

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-02 19:12:11 +00:00
lelemm
ae0594d294 Added support to pluggy date correction based on installments (#8380)
* Added support to pluggy date correction based on installments

* md

* Update packages/sync-server/src/app-pluggyai/app-pluggyai.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* date now considers instalments, originalDate is the date without it

* creating new instance instead of same obj

* code review

* aligning to code review

* simpler

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-07-02 19:07:08 +00:00
Alec Bakholdin
74c925aaf6 [AI] Improved context menu (#8106)
* Context Menu is implemented

* allow Document and Window in ref event listener hook

* did stupid examples to showcase api usage

* recreated TransactionMenu using new context action

* fixed styling and removed excess calls to useContextMenuAction

* removed extra call

* extra imports

* started converting files to useContextMenu

* fixed context menus in reports

* linting and formatting

* deleted TransactionMenu

* release notes

* fixed coderabbit

* light context menu refactor

* added nested contexts for state and actions

* refactored useContextMenuAction

* [autofix.ci] apply automated fixes

* removed log

* updated contextMenuSlice

* removed package.json change

* filter => some

* tried to improve test

* [AI] test: fix payees e2e right-click test by scoping row query to table

* linting

* [AI] chore: fix all linting issues from new context menu files

* fixed compiler

* fixed linting/formatting

* coderabbit fix

* [AI] fix(electron): connect to sync server on 127.0.0.1 instead of localhost

Node.js > 17 resolves localhost to ::1 (IPv6) by default, while the Electron
sync server binds explicitly to 127.0.0.1 (IPv4). This caused setServerUrl to
fail when the desktop app starts its internal sync server, trapping the user
in an onboarding loop between /config-server and /bootstrap.

This commit updates the URL to use 127.0.0.1 to match the server binding.

* [AI] Fix isPlaywright detection for Electron Playwright tests

The userAgent check was changed from includes('playwright') to
=== 'playwright', but the Electron main process appends ' playwright'
to the default userAgent (preserving the 'Electron' substring that
isElectron() relies on). Use includes() so the flag is detected
correctly in Electron e2e tests.

Also reverts the localhost→127.0.0.1 change which did not resolve the
VRT pixel diffs.

* re-added missing menu option

---------

Co-authored-by: Alec Bakholdin <alecbakholdin.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-02 17:44:03 +00:00
github-actions[bot]
8b5b182dbc 🔖 (26.7.0) (#8332)
* 🔖 (26.7.0)

* Generate release notes for v26.7.0

* update blog author

* Generate release notes for v26.7.0

* release notes

* Generate release notes for v26.7.0

* Generate release notes for v26.7.0

* Generate release notes for v26.7.0

* relink PRs from cherry-picked changes

* tweak wording of release summary

* update blog post date

* akahu experimental

---------

Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-07-02 16:10:16 +00:00
Matt Fiddaman
b1d713ac55 [AI] resolve release-note PR links from base branch history (#8368)
* [AI] Resolve release-note PR links from base branch history

The release-note generator recovered each note's PR number by reading the
"(#NNNN)" suffix that GitHub appends to squash-merge commit subjects, searching
HEAD — the release-notes/X.Y.Z branch it commits to. That branch only carries
the note file *contents* (checked out from origin/release), not the commit
history, so any note added to the release branch after it was cut (e.g. release
cherry-picks) has no add-commit reachable from HEAD and silently loses its link.

Search origin/master instead, where every note traces back to its original
squash commit with the correct PR number. The release branch is unsuitable
because "bring release branch up to date" squashes collapse original PRs.

Verified across all 63 descriptively-named notes in the 26.7.0 set: 60 resolve
identically to before, 3 previously-unlinked cherry-picks (#8301, #8335, #8339)
are now linked, 0 regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* make the note human

* Update upcoming-release-notes/release-notes-resolve-pr-link-from-master.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-07-01 17:42:41 +00:00
Frithjof Engel
6e270a1a2b Added an importer for German DKB and comdirect banks to the list of community importers (#8371)
* Added link to German DKB and Comdirect bank importer

* made text bold
2026-07-01 17:40:13 +00:00
lelemm
f00bde7a17 Formulas code clean up (#8369)
* Clean up

* [autofix.ci] apply automated fixes

* md

* code review from coderabbit

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-30 20:15:38 +00:00
edwei06
8c0e8ea8af Fix mobile category delete confirmation (#8301) 2026-06-30 16:50:52 +00:00
Julien Vincent
d9c69b08ed Resolve payee name during rule evaluation (#8360)
When a rule action sets payee_name, resolve it to the actual payee ID
immediately during rule execution instead of waiting until finalization.

This allows later staged rules to match on the resolved payee.
Previously, payee_name actions temporarily set payee to "new", and the
real payee ID was only resolved in finalizeTransactionForRules after
rule matching had completed.

Fixes #8359
2026-06-29 21:25:12 +00:00
Matt Fiddaman
e891c2e30e reorganise the SimpleFIN key saving logic (#8362)
* remove the simplefin intercept in the secrets handler

* note
2026-06-29 21:04:27 +00:00
Carlisle Nightingale
02e477e95f Add Thyrium to custom theme catalog (#8295)
* Update customThemeCatalog.json

Add Thyrium to custom theme catalog

* Create add-thyrium-theme.md

* Update repository reference for Thyrium theme

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2026-06-29 20:59:22 +00:00
Matt Fiddaman
617fe63196 make SimpleFIN token 'forbidden' check more flexible (#8339)
* make SimpleFIN token 'forbidden' check more flexible

* clear accessKey when token is updated

* https -> fetch

* [AI] Add tests for SimpleFIN token claim fix

Cover the flexible 'Forbidden' matching, full claim-response handling, and access-key invalidation when the setup token changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* note

* Update upcoming-release-notes/fix-simplefin-token-claim.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* coderabbit feedback

* [AI] Test SimpleFIN blank access key handling

Cover rejecting a blank claim response (not persisted) and re-claiming when an empty access key is cached.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-06-28 22:29:29 +00:00
Darwin Do
09a6d9badd Always show stacked bar graph tooltip label on hover (#8356)
* Always show stacked bar graph tooltip label on hover

* Handle edge case with maxToolTipItems+1 labels showing

* Replace last item label instead of adding new one

* Filter visible items on non zero values for consistency
2026-06-28 21:45:30 +00:00
Matiss Janis Aboltins
33da5a3d26 [AI] Add design competition blog post for sidenav redesign (#8297)
* [AI] Add design competition blog post for sidenav redesign

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF

* [AI] Set design competition blog post publish date to 27th July 2026

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF

* [AI] Remove stray closing tags from design competition blog post

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF

* Update 2026-07-27-design-competition-sidenav.md

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-27 19:38:04 +00:00
clintharris
f6c3877675 Restore budget table scroll position when navigating back from spent transactions (#8144)
* feat: Restore budget table scroll position when navigating back from spent transactions page

* fix: Update transaction tests to include new categories and adjust assertions

* Add release notes for budget scroll position restoration

* refactor: Improve error handling when clicking on visible spent-amount cells, per suggestion from coderabbit bot

* refactor: revert changes to test budget, add separate scroll-test test budget

* revert previous snapshot changes

* Add gitignore pattern for personal devcontainer config

* fix: broken tests

* refactor: address coderabbitai feedback

https://github.com/actualbudget/actual/pull/8144#discussion_r3446850158

* fix: update VRT snapshots for onboarding screen

* [autofix.ci] apply automated fixes

* refactor: modify scrolling test to add categories to existing test budget instead of creating separate budget

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-27 13:32:00 +00:00
Matiss Janis Aboltins
8ad218b7d9 [AI] api: make the browser build work in consumer production bundles (#8289)
* api: make the browser build work in consumer production bundles

The browser build shipped `new Worker(new URL("./worker.js", import.meta.url))`
in dist/browser.js. A consumer bundler (Vite/Rollup) recognizes that as a worker
entry and re-bundles the already-prebuilt worker.js from scratch, which desyncs
the absurd-sql/connection RPC so `init` throws a structured-clone error. It only
worked in dev and in the package's own e2e because both serve dist verbatim.

Make the browser build fully self-contained instead:

- Inline the worker into browser.js and spawn it from a Blob URL, so consumer
  bundlers never see a worker entry to re-bundle. The worker is built as an IIFE
  (classic worker), mirroring the web app's kcab.worker.
- Embed the sql.js wasm and the default filesystem data (migrations + default
  DB) into the worker so it performs no PUBLIC_URL asset fetches:
  - loot-core sqlite gains an opt-in `setWasmBinary` (dormant for web/node);
  - the worker installs a scoped fetch shim serving the embedded data/* files
    from a sentinel base URL.
- Share the asset-collection logic between the Node disk copy and the embedded
  build via scripts/embedded-assets.mjs so the two never drift.

Result: `import '@actual-app/api'` + `init()` works in any bundler with zero
config; only COOP/COEP headers remain required (SharedArrayBuffer).

Adds an e2e (e2e/consumer + e2e/global-setup.mjs) that builds a real consumer
app for production and boots it under COOP/COEP — coverage the verbatim-dist
harness can't provide. Docs note the build is self-contained and that
cross-origin isolation is still required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: address review feedback (guard DOM lookup, revoke blob URL on worker failure)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: address review nitpicks (drop BodyInit cast, reword release note)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: build the browser worker via Vite ?worker&inline

Replace the hand-rolled worker inlining (a custom Vite plugin that read the
prebuilt worker.js off disk and inlined it as a string, plus a second build
config and a build-ordering dependency) with Vite's native `?worker&inline`
import. This collapses the two browser build configs into one, removes the
`virtual:actual-worker-code` module and the manual Blob URL spawn, and drops
the worker-before-facade build step.

Vite's `?worker&inline` sub-build doesn't receive vite-plugin-node-polyfills'
global-shim injection (the plugin writes it to `build.rollupOptions`, but the
worker sub-build reads `worker.rollupOptions`), so the worker crashed on
`process is not defined`. Patch the plugin to also emit the injection on
`worker.rollupOptions` — additive and backwards compatible (the web app build,
including loot-core's nodePolyfills worker, is unaffected).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: embed browser assets via Vite, drop the embedded-assets script

Replace scripts/embedded-assets.mjs with Vite-native asset imports in the
worker entry: `?inline` for the sql.js wasm and default DB, `import.meta.glob`
(`?raw`) for the migrations. The worker builds the default-filesystem wire
format from those at module load, so the embedded set comes straight from
loot-core and can't drift.

The Node build only needs migrations + the default DB on disk (it reads them at
runtime); the old script also wrote sql-wasm.wasm and the data/ fetch tree,
which are dead now that the browser worker is self-contained. Replace
writeEmbeddedAssetsToDist with a small closeBundle copy of just those two.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: simplify embedded data map; drop loot-core comment churn

Merge the worker's binData/textData into a single dataFiles map (Response
accepts both bytes and strings), collapsing the fetch shim's two lookups into
one. Revert the unrelated comment edits to loot-core's backend-worker.ts and
fs/index.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: rename e2e consumer-dist to consumer/dist; drop a prose comment

Use the standard `dist` directory name for the consumer fixture's build output
(now e2e/consumer/dist), and remove the verbose comment from index.browser.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] loot-core: own the default-filesystem assets; consumers stop reaching in

Add packages/loot-core/default-filesystem.mjs as the single source of truth for
loot-core's runtime assets (sql.js wasm, default DB, migrations) and the
data-file-index wire format, exported as @actual-app/core/default-filesystem.

- @actual-app/api: the browser worker embeds them via the actual-embedded-assets
  Vite plugin (which calls collectEmbeddedAssets), replacing the relative
  ?inline/?raw/import.meta.glob reaches into ../loot-core; the Node build copies
  migrations + default DB using the helper's paths. Drops the direct
  @jlongster/sql.js devDependency (loot-core resolves the wasm).
- @actual-app/web: stagePublicData copies from the helper's paths instead of
  hardcoding loot-core's tree and the sql.js wasm location.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] loot-core: scan migrations once when collecting embedded assets

collectEmbeddedAssets() read the migrations directory twice — once in its
own loop and once via buildDataFileIndex(). List the names once and pass
them through, keeping the index wire-format defined in a single place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 20:36:21 +00:00
Alec Bakholdin
d75261c42f [AI] Add performance indexes for transactions table (#8335)
* [AI] Add performance indexes for transactions table

* release notes

* [AI] Remove unselective tombstone-only index from performance migration
2026-06-26 17:33:00 +00:00
Matt Fiddaman
1f8ca46576 mdx -> md (#8338)
* mdx -> md

* [AI] Outlaw .mdx in the docs package

Docusaurus 3 compiles .md through MDX, so JSX and imports work in plain
.md files and there is no need for the .mdx extension. Narrow the
enforce-doc-links remark plugin to only accept .md: reject any .mdx
source file at build time, flag links that target a .mdx file, and drop
the .mdx branches from the directory scanner and link resolution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 14:59:00 +00:00
Matt Fiddaman
d56cb52161 speed up electron builds (#8337)
* size up runners, skip translations where possible

* note
2026-06-26 13:14:54 +00:00
Matiss Janis Aboltins
7c005ffc50 [AI] Add cross-platform hook adding 🤖 emoji to comments (#8331)
* [AI] Add cross-platform hook keeping GitHub comments/issues in Chinese or pirate voice

Adds a shared agent hook that requires anything an agent posts to GitHub
(PR/issue comments, PR reviews, created issues) to be written in 简体中文
(preferred) or, failing that, a fun pirate voice — never plain English.

- scripts/agent-hooks/github-comment-style.sh: shared guard. Reads
  tool_input.body/.title, allows CJK or pirate-flavoured text, blocks plain
  English via exit 2. Fails open on malformed payloads.
- Wired for Claude (.claude/settings.json PreToolUse), Codex
  (.codex/config.toml PreToolUse) and Cursor (beforeMCPExecution adapter).
- Documents the rule in AGENTS.md, the canonical pr-and-commit-rules.md and
  the Cursor rule so platforms without hook support apply it manually.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Use a slug for the release-note filename; document slug naming in AGENTS.md

Release-note filenames don't need to be the PR number — a short descriptive
slug works too (the PR link is resolved at release time). Rename the new note
accordingly and note this in AGENTS.md's directory reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [autofix.ci] apply automated fixes

* [AI] Auto-label PRs with Chinese descriptions as "ai spam" via CodeRabbit

Add a CodeRabbit labeling instruction (auto_apply_labels is already on) that
applies the "ai spam" label when a PR description contains Chinese/CJK
characters — a strong signal of AI-generated spam for this English-language
project.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Exempt CodeRabbit-addressed comments from the Chinese/pirate voice hook

CodeRabbit parses its commands (@coderabbitai review/resolve/etc.) as plain
English, so the github-comment-style guard now skips any comment mentioning
@coderabbitai / @coderabbit instead of forcing it into Chinese or pirate.
Documented the exception in the canonical and Cursor rule files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Key CodeRabbit voice exemption on authorship, not mentions

The hook only ever runs on the agent's own outgoing comments, so CodeRabbit's
own comments are already out of scope (it posts under its own identity). Drop
the @coderabbitai mention bypass — a comment merely name-dropping the bot is
still the agent's prose and must follow the Chinese/pirate rule, closing an
easy plain-English bypass. Docs updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Pin CodeRabbit reviews to English

CodeRabbit was reading AGENTS.md / pr-and-commit-rules.md (which ask
contributors to comment in Chinese/pirate) and applying that to its own
reviews, posting them in Chinese. Set `language: en-US` and `tone_instructions`
so the bot's own reviews, summaries and replies stay in English; the
Chinese/pirate rule is for contributor/agent comments, not CodeRabbit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Replace Chinese/pirate comment voice with a robot-emoji prefix

Scrap the 简体中文/pirate voice scheme (and its CodeRabbit language override and
Chinese-based "ai spam" label) in favour of one simple rule: every GitHub
comment, review or issue an agent posts must be prefixed with 🤖. The shared
guard now blocks any body — or, for issues, title — that doesn't start with the
robot emoji; docs and per-platform wiring updated to match. .coderabbit.yaml is
restored to its original state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Silence SC1007 false positive on CDPATH= cd in Cursor MCP adapter

shellcheck flags the intentional `CDPATH= cd` empty-env prefix as SC1007
(mistaking it for an assignment). Add a scoped inline disable directive on that
line; behaviour is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Fail closed on guard execution errors in Cursor MCP adapter

The adapter previously allowed the call on any non-0/2 exit from
github-comment-style.sh, so a broken or missing guard would silently disable
enforcement. Deny on unexpected exits instead (matching guard-shell.sh), with a
message that marks it as an execution problem rather than a real policy denial.
The shared guard still fails open on malformed payloads (its own exit-0 choice).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Add hook requiring a blank PR template when agents create PRs

New shared guard pr-template-blank.sh blocks mcp__github__create_pull_request
unless the body is the repo's PR template, unmodified (cosmetic whitespace
aside) — agents must leave it blank for the human, per AGENTS.md. Wired for
Claude (PreToolUse), Codex (PreToolUse) and Cursor (the beforeMCPExecution
adapter now dispatches per-tool to the right guard). Docs and a release note
updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Simplify agent-hook guard scripts

- github-comment-style.sh: fold the empty/whitespace-only case into the
  prefix check, dropping a redundant tr subshell per field.
- pr-template-blank.sh: drop the dead CR strip in canon() (the trailing
  whitespace sub already removes a trailing carriage return).
- before-mcp-github.sh: emit the constant allow payload with printf instead
  of spawning jq on the common (allow) path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Drop duplicated PR-template/robot-emoji rules now that hooks enforce them

The PR-template-blank and GitHub-comment robot-emoji-prefix rules are enforced
by cross-platform hooks, so the copies scattered across AGENTS.md and the
Cursor rules file are redundant. Remove them and point at the canonical
pr-and-commit-rules.md, which keeps the full rule (including the PR-template
Chinese exception that no hook can enforce). Also delete the internal
PR-template-guard release note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Drop "enforced by hooks" wording from the PR/comment rule docs

State the rules plainly without the meta-commentary about hook enforcement.
Keeps the actual rules (don't fill the PR template, prefix GitHub
comments/reviews/issues with 🤖, the Chinese exception, and the
your-own-comments-only note) intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Trim enforcement meta-commentary; shorten robot-emoji release note

Drop the "isn't enforced automatically" / "handled by tooling" framing from
AGENTS.md and pr-and-commit-rules.md (keeping the rule and the "apply it
yourself" responsibility), and shorten the robot-emoji-prefix release note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Fail closed on unreadable payloads in the GitHub agent guards

Align the new guards with the git-guard.sh / guard-shell.sh convention: fail
closed when the hook payload can't be read, fail open only on a genuinely
absent optional field.

- github-comment-style.sh: block on invalid JSON / missing / non-object
  .tool_input; allow only an absent body/title within a valid object.
- before-mcp-github.sh: deny on a jq parse failure / missing .tool_name
  instead of falling through to allow.
- pr-template-blank.sh: an absent/null body now fails open (previously it
  normalized to "" and wrongly blocked PR creation); an empty-string body is
  still treated as a real non-template submission.
- .codex/config.toml: describe the full guard scope (comment body + issue
  title) in the hook comment and statusMessage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-25 22:06:04 +00:00
Matt Fiddaman
104631b826 update release process (#8334) 2026-06-25 21:19:02 +00:00
tabedzki
644256ddc1 [AI] feat(budget analysis report): show hidden categories (#8164)
* [AI] feat(budget-report): add toggle to include hidden categories

Add a 'Show hidden categories' button that includes hidden expense
categories in the Budget Analysis report. This prevents historic data
from being misrepresented when a category (e.g. a car fund) is hidden.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] feat(budget-report): replace hidden categories button with icon toggle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-analysis): use correct package alias in test import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: added in release note

* [AI] refactor(budget-analysis): extract isBaseCategory helper and reuse in tests

Assisted-by: ClaudeCode:claude-sonnet-4-6

* [AI] feat(budget-report): clarify hidden categories toggle state

Replace the ambiguous icon-only toggle with a highlighted active state
and explicit click-to-show/hide tooltip text so the current selection is
clear. Add aria-pressed for accessibility.

Assisted-by: ClaudeCode:claude-opus-4.8

* [AI] feat(budget-report): consolidate chart options into Options dropdown menu

Replace the separate chart-type icon toggle, balance/category Select dropdown,
and hidden-categories eye icon with a single Options popover (Popover + Menu)
matching the pattern used by other reports (Sankey). The menu contains:
- Switch to line/bar chart
- Show balance (toggle)
- Show categories (toggle, replaces balanceOnly)
- Show hidden categories (toggle)

Also rebases onto upstream/master to incorporate the balanceOnly/Select
changes from #8162 before consolidating them.

Assisted-by: ClaudeCode:claude-sonnet-4-6

* [AI] fix(budget-report): use static 'Bar chart' toggle in Options menu for i18n

Replace the dynamic 'Switch to line/bar chart' string with a static
'Bar chart' label and a boolean toggle (on = bar, off = line), matching
the toggle pattern used by other menu items and making the string
straightforward to translate.

Assisted-by: ClaudeCode:claude-sonnet-4-6

* Enhance budget analysis report with dropdown options

Introduced a dropdown to group options and added the ability to show hidden categories in the budget analysis report.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 20:12:36 +00:00
Dustin Brewer
597bd86db5 Initial payee location documentation (#8241)
* Initial payee location documentation

* Move to dedicated experimental section

Based on PR feedback
2026-06-24 17:32:54 +00:00
Matt Farrell
4bd1633938 Update docs with Actuali iOS app (#8324)
* Update docs with Actuali iOS app

* Update packages/docs/docs/community-repos.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-06-24 16:16:19 +00:00
Matt Fiddaman
9d204f195b fix flaky vrt by increasing timeout (#8323)
* fix test

* note
2026-06-24 16:13:00 +00:00
youngcw
e353bc5797 [AI] Fix Age of Money report widget title not saving correctly (#8320)
* [AI] Fix Age of Money report widget title not saving correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] Use mutateAsync to properly await mutations in AgeOfMoney widget

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 21:27:52 +00:00
tabedzki
37ac0adea0 [AI] feat(budget analysis report): Add Balance & Category selector (#8162)
* [AI] feat(budget-report): add balance-only view mode

Add a 'Balance only' toggle to the Budget Analysis report that hides the
Budgeted, Spent, and Overspending Adjustment series and renders the balance
as a plain line chart, similar to the Net Worth graph.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-report): always display ending balance for consistent header spacing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] feat(budget-report): replace balance toggle buttons with series dropdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-report): capitalize Categories in balance mode dropdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-report): fix test imports and lint errors in BudgetAnalysisGraph test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: added in release note

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 18:40:57 +00:00