From 67448fa15c645d08dfcefa30f9fd70b5db6e92e8 Mon Sep 17 00:00:00 2001 From: CrewCircle Date: Sat, 17 Oct 2026 17:45:05 +1100 Subject: [PATCH 1/4] docs: add user manual and correct supported banks list - Add docs/user-manual.md: user-facing manual covering the feature list, how to manage personal finances, and how to configure and use Folio - README.md: link the new documentation set and replace the inaccurate Supported Banks list with the verified Bank Connect connectors - docs/getting-started.md: cross-link the user manual from Next Steps --- README.md | 28 +- docs/getting-started.md | 2 + docs/user-manual.md | 548 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 572 insertions(+), 6 deletions(-) create mode 100644 docs/user-manual.md diff --git a/README.md b/README.md index 2a22ce31..471d5212 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,18 @@ Visit `http://localhost:3000` to access the application. --- +## πŸ“– Documentation + +- **[User Manual](docs/user-manual.md)** β€” the full feature list and how to + configure and use Folio to manage your personal finances. +- **[Getting Started](docs/getting-started.md)** β€” install, set up your first + account, and learn the core concepts. +- **[Activity Types](docs/activities/activity-types.md)** β€” the 14 transaction + types and when to use each one. +- **[Addons](docs/addons/index.md)** β€” extend Folio with community features. + +--- + ## πŸ› οΈ Tech Stack | Component | Technology | @@ -115,15 +127,19 @@ Visit `http://localhost:3000` to access the application. ## πŸ“Š Supported Banks -Folio supports automatic import from major Australian banks: +**Bank Connect** (automatic import from the desktop app) supports: -- Commonwealth Bank -- Westpac +- CommBank (CBA) - ANZ -- NAB - ING -- Macquarie -- And more... +- Bank of Melbourne +- Beyond Bank +- Interactive Brokers (broker, Beta) + +Coming soon: CommSec, Sharesies, SelfWealth, and Stake. + +Any other bank or broker can be tracked by importing a **CSV, OFX, or QIF** +statement file. Most Australian institutions offer a statement export. --- diff --git a/docs/getting-started.md b/docs/getting-started.md index aa210171..b0402f72 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -244,6 +244,8 @@ your devices β€” no third-party server can read your data. ## Next Steps +- Read the [user manual](./user-manual.md) for the full feature list and + step-by-step guides to managing your finances. - Add your first account and record a few activities manually to get a feel for the flow. - Review the [activity types reference](./activities/activity-types.md) before diff --git a/docs/user-manual.md b/docs/user-manual.md new file mode 100644 index 00000000..32c76381 --- /dev/null +++ b/docs/user-manual.md @@ -0,0 +1,548 @@ +# Sensible Folio β€” User Manual + +Sensible Folio is a local-first personal wealth application built for Australian +investors. It brings your investments (shares, ETFs, managed funds, crypto, +cash) and your bank accounts into one private dashboard, with automatic import +of Australian bank statements, portfolio performance analytics, income tracking, +and tax reporting. + +This manual is written from the **user's point of view**: what each feature +does, and how to configure and use it to manage your personal finances. + +> **New to Folio?** If you just want to install it and get running, start with +> [Getting Started](./getting-started.md). This manual is the full reference. + +--- + +## Table of contents + +1. [Introduction](#1-introduction) +2. [Key concepts](#2-key-concepts) +3. [Feature list at a glance](#3-feature-list-at-a-glance) +4. [Installing and first run](#4-installing-and-first-run) +5. [Configuring Folio](#5-configuring-folio) +6. [Managing your personal finances](#6-managing-your-personal-finances) +7. [Activity types reference](#7-activity-types-reference) +8. [Supported banks and brokers](#8-supported-banks-and-brokers) +9. [Privacy and security](#9-privacy-and-security) +10. [Troubleshooting and FAQ](#10-troubleshooting-and-faq) +11. [Getting help](#11-getting-help) + +--- + +## 1. Introduction + +### What Folio is + +Folio (distributed as **Sensible Folio**) is a wealth portfolio management +application. Everything β€” your accounts, transactions, holdings, performance, +and reports β€” lives in a SQLite database **on your own device**. There is no +cloud account, no subscription, and no third-party server holding your financial +data. + +### Who it is for + +- Australian investors who want to track shares, ETFs, managed funds, property, + crypto, and cash in one place. +- People who want to import transactions from Australian banks and brokers + without handing credentials to a third-party aggregation service. +- Anyone who values privacy: your data stays local unless *you* explicitly turn + on encrypted device sync. + +### What makes Folio different + +- **Local-first.** Your data is stored on your machine. It works offline. +- **Australian-focused.** Bank Connect supports Australian institutions, and + there is first-class support for importing bank and broker statements. +- **Unified view.** Investments and bank/cash accounts appear in one dashboard, + so you can see your complete financial picture β€” not just your brokerage. +- **Extensible.** An addon system lets you add new views and analysis without + modifying the core app. +- **Private sync.** Optional end-to-end encrypted sync replicates your database + between *your own* devices. + +--- + +## 2. Key concepts + +Understanding these terms makes the rest of the manual easy to follow. + +| Concept | What it means in Folio | +| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Account** | A single real-world account β€” a brokerage, bank, or cash account. You can have as many as you have in real life. | +| **Activity** | Any event that changes an account: a trade, a deposit, a dividend, a fee, and so on. There are 14 canonical activity types (see [Β§7](#7-activity-types-reference)). | +| **Holding / Position** | The quantity of a security you own in an account, derived from your activities. | +| **Portfolio** | The combined view of all your accounts and holdings. | +| **Asset & taxonomy** | A security or instrument you hold, plus the classification system (asset class, sector, region) used for allocation and grouping. | +| **Benchmark** | An index (for example an ASX index) you compare your portfolio performance against. | +| **Contribution limit** | A cap on contributions (for example superannuation concessional/non-concessional caps) that you can track against. | +| **Addon** | An optional, sandboxed extension that adds new views or features. | +| **Bank Connect** | The built-in browser panel that logs into your bank/broker site and imports transactions β€” desktop app only. | +| **Wealthfolio Connect**| The optional encrypted sync feature that replicates your data to your own devices. | + +--- + +## 3. Feature list at a glance + +### Portfolio and investments + +- **Multi-asset tracking** β€” shares, ETFs, managed funds, property, crypto, cash. +- **Holdings view** β€” current positions per account with cost basis. +- **Holdings insights** β€” deeper breakdown and analysis of what you hold. +- **Asset profiles** β€” a per-asset page showing your activity and exposure in + that instrument. +- **Performance analytics** β€” time-weighted return (TWR) and money-weighted + return (IRR). +- **Benchmark comparison** β€” compare your performance against a chosen index. +- **Allocation charts** β€” see how your money is split by asset class, sector, + currency, and taxonomy. + +### Income and tax + +- **Income tracking** β€” dividends, interest, and distributions over time. +- **Dividend tracking** β€” including dividend reinvestment (DRIP) handling. +- **Tax reporting support** β€” capital gains and income summaries, plus an + **Exports** screen for taking data out of the app. + +### Bank and broker integration + +- **Bank Connect** β€” automatic import from supported Australian banks and + brokers via an embedded browser panel. +- **Statement import** β€” import **CSV, OFX, and QIF** files from banks and + brokers. +- **AI-assisted import** β€” the AI assistant can help map and import a CSV. +- **Transaction categorisation and cash-flow analysis** β€” understand where your + money goes. + +### Planning and insight + +- **Net worth view** β€” your complete financial picture across accounts. +- **Goals** β€” set and track savings/investment targets. +- **Contribution limits** β€” track contributions against configured caps. +- **Portfolio insights** β€” analysis screens that surface patterns in your data. +- **Health view** β€” indicators that summarise the state of your finances. +- **AI assistant** β€” ask questions about your data in natural language. + +### Platform and privacy + +- **Local-first storage** β€” SQLite on your device; works offline. +- **Desktop and web parity** β€” the same app runs natively (macOS, Windows, + Linux) and in the browser via Docker. +- **Addons** β€” install community extensions from a file. +- **Private device sync** β€” optional E2EE sync between your own devices. +- **Multi-platform** β€” native desktop installers plus a Docker web image. + +--- + +## 4. Installing and first run + +### Desktop (macOS, Windows, Linux) + +Download the installer for your platform from GitHub Releases: + +``` +https://github.com/Sensible-Analytics/folio/releases +``` + +| Platform | File type | +| -------- | --------------------- | +| macOS | `.dmg` | +| Windows | `.msi` or NSIS `.exe` | +| Linux | `.AppImage` | + +Open the downloaded file and follow the standard install steps for your OS. No +configuration is required on first launch. + +### Web mode (Docker) + +If you prefer browser access, or want to run Folio on a home server: + +```bash +docker run -d \ + -p 7472:7472 \ + -v /path/to/your/data:/data \ + -e WF_DB_PATH=/data/folio.db \ + -e WF_SECRET_KEY=your-32-byte-secret-key-here \ + ghcr.io/sensible-analytics/folio:latest +``` + +Then open `http://localhost:7472`. + +> **Note:** Bank Connect is a desktop-only feature. In web mode, use CSV/OFX/QIF +> import instead. + +### First run: onboarding + +The first time you launch Folio you are guided through an **onboarding** flow. +It introduces the app and helps you create your first account and set up your +preferences. You can re-run or adjust these settings later at any time from +**Settings**. + +--- + +## 5. Configuring Folio + +All configuration lives under **Settings** in the sidebar. Below is what each +section controls and how to use it. + +### General settings + +Basic application preferences: default currency, language/format, and other +startup options. Set your **base currency** first β€” it drives how values are +displayed across the app. + +### Accounts + +Create and manage accounts. Each account has a name, a currency, and a type. +Use one account per real-world brokerage, bank, or cash account. + +> **Example:** "CommSec Trading", "ANZ Everyday", "Super β€” AustralianSuper". + +### Securities / market data + +- **Securities** β€” the list of assets (tickers/instruments) Folio knows about. +- **Market data** β€” configure where prices come from, and **import market data** + from a file if you have offline prices. + +Keeping market data current is what makes your valuation and performance numbers +accurate. + +### Taxonomies + +Classification schemes used to group your assets (for example asset class, +sector, region). Taxonomies power the allocation charts and breakdowns. Adjust +them if you want categories that match how *you* think about your portfolio. + +### Contribution limits + +Define contribution caps to track against β€” useful for superannuation +concessional and non-concessional limits. + +### Goals + +Create financial goals (for example, a savings target or a target portfolio +value). Goals give you a progress indicator you can monitor over time. + +### Appearance + +Theme and display preferences. + +### AI providers + +Configure the AI assistant. Add a provider (and credentials) here to enable the +**Assistant**. If you prefer not to use AI features, leave this empty β€” the rest +of the app works fully without it. + +### Connect (Wealthfolio Connect) + +Configure optional **encrypted device sync** so your Folio data appears on your +other devices. Keys stay on your devices; no third-party server can read your +data. Leave this off if you only use Folio on one machine. + +### Addons + +Install and manage addons (see [Β§6.10](#610-extend-folio-with-addons)). + +### Exports + +Export your data β€” for example, for backup, reporting, or handing to an +accountant. + +### About + +Version information and app details. + +--- + +## 6. Managing your personal finances + +This section walks through the day-to-day workflows, in the order most people +adopt them. + +### 6.1 Track your investments + +**Where:** Dashboard, **Holdings**, **Holdings Insights**, and individual +**Asset** pages. + +- The **Dashboard** (Portfolio page) is your home view: total value, allocation, + and performance at a glance. +- **Holdings** lists every position you own, per account. +- **Holdings Insights** drills into what you hold across accounts. +- Click any asset to open its **Asset Profile** page for activity and exposure + in that instrument. + +### 6.2 Import your transactions + +Everything in Folio is built from **activities**. There are three ways to get +them in: + +1. **Manual entry.** Open an account and use **Add Activity**, or go to + **Activities β†’ Manage** to review and edit what you have. Best for a small + number of transactions or corrections. + +2. **File import (CSV / OFX / QIF).** Export your history from your bank or + broker, then use the **Import** screen. Folio reads **CSV**, **OFX**, and + **QIF** files and maps the columns/fields to its activity types. You get to + review the mapping before it is committed. + +3. **Bank Connect (desktop).** Let Folio log into your bank or broker's site and + pull the transactions for you. See [Β§6.3](#63-bank-connect-automatic-import). + +> **Tip:** Before a large import, skim the +> [activity types reference](./activities/activity-types.md) so you map your +> provider's categories correctly. Unmapped rows become `UNKNOWN` placeholders +> that you can reclassify later. + +### 6.3 Bank Connect (automatic import) + +**Where:** **Bank Connect** in the sidebar (desktop app only). + +Bank Connect is an embedded browser panel inside Folio. It lets you log into +your bank or broker and automatically import your transactions, without ever +handing your credentials to a third-party aggregation service. + +How to use it: + +1. Open **Bank Connect** from the sidebar. +2. Choose your institution and open the connector. +3. The panel loads the institution's real website in an embedded browser. +4. Log in as you normally would β€” **on the bank's own site**. +5. Folio detects the transaction history on the page and offers to import it. +6. Review the detected transactions and confirm the import. + +Your credentials are entered directly on the bank's website inside the panel. +They are **never stored by Folio and never sent anywhere** β€” the panel runs +entirely on your local machine. + +Connector status is shown as you go: **Idle β†’ Awaiting Login β†’ Logged In β†’ +Downloading… β†’ Complete** (or **Error** if something goes wrong). + +Supported institutions are listed in [Β§8](#8-supported-banks-and-brokers). For +banks that are not yet supported by Bank Connect, use CSV/OFX/QIF import β€” most +Australian banks let you export a statement file. + +### 6.4 Understand your performance + +**Where:** **Performance**. + +Folio computes standard performance metrics so you can judge how your portfolio +is really doing: + +- **TWR (time-weighted return)** β€” how your investments performed, independent + of when you added or withdrew money. +- **IRR (money-weighted return)** β€” the return on the money you actually put in. + +Use **Benchmark comparison** on the performance view to line your results up +against a chosen index. + +### 6.5 Track income and dividends + +**Where:** **Income**. + +See the dividends, interest, and distributions you have received, over any period +you choose. Dividend reinvestment (DRIP) is handled so reinvested amounts are +reflected correctly rather than appearing as cash you never received. + +### 6.6 See your allocations and insights + +**Where:** **Insights**, **Holdings Insights**, allocation charts on the +dashboard. + +Allocation charts break your portfolio down by **asset class**, **sector**, and +**currency** (driven by your taxonomies). The **Insights** screens surface +patterns β€” concentration, drift, and other observations β€” to help you decide +what to do next. + +### 6.7 Track your net worth + +**Where:** Dashboard. + +Folio brings investment and cash/bank accounts into a single view so you can see +your **net worth** as one number, and track how it changes over time. + +### 6.8 Set and track goals + +**Where:** **Settings β†’ Goals** (and the goals widget on the dashboard). + +Create a goal with a target and a date. Folio shows progress toward it as your +balances change. The bundled **goal-progress-tracker** addon adds a dedicated +progress view (see [Β§6.10](#610-extend-folio-with-addons)). + +### 6.9 Plan for tax + +Folio helps you prepare for tax time: + +- **Capital gains and income summaries** are derived from your activities + (`SELL` activities are matched using FIFO cost basis by default). +- **Contribution limits** (Settings) let you watch contributions against caps. +- **Exports** and reports let you pull the numbers you need for your return or + for your accountant. + +> Folio is a tracking tool, not tax advice. Always confirm treatment with a +> qualified professional. + +### 6.10 Use the AI assistant + +**Where:** **Assistant** in the sidebar (requires an AI provider configured in +**Settings β†’ AI Providers**). + +The assistant lets you ask questions about your own data in natural language β€” +for example, to summarise your holdings, explain a change, or help map a CSV +during import. It runs against your local data and the provider you configured. + +### 6.11 Extend Folio with addons + +**Where:** **Settings β†’ Addons**. + +Addons are optional extensions that add new views or features. They run in a +sandboxed environment and reach your data only through a controlled API. + +To install an addon: + +1. Download the addon file (`.folio-addon`). +2. In Folio, go to **Settings β†’ Addons**. +3. Click **Install from file** and select the file. +4. The addon's views appear in the sidebar immediately. + +To remove one, go to **Settings β†’ Addons**, find it, and click **Uninstall**. + +Folio ships with three example addons: **goal-progress-tracker**, +**investment-fees-tracker**, and **swingfolio-addon**. For addon development, +see [./addons/addon-getting-started.md](./addons/addon-getting-started.md). + +--- + +## 7. Activity types reference + +Every transaction is one of 14 canonical types. Choosing the right type matters β€” +each affects your cash balance, holdings, cost basis, and performance +differently. + +| Type | What it represents | +| -------------- | ----------------------------------------------------------------------------------------------------------------------- | +| `BUY` | Purchase of a security. Decreases cash, increases holdings. | +| `SELL` | Sale of a security. Increases cash, decreases holdings (FIFO cost matching). | +| `SPLIT` | Stock split or reverse split. Adjusts quantity and per-share cost basis; total cost unchanged. | +| `DEPOSIT` | Cash arriving from outside your portfolio (e.g., funding your brokerage account). Counts as a new capital contribution. | +| `WITHDRAWAL` | Cash leaving to an external destination. Reduces net contribution. | +| `TRANSFER_IN` | Moving cash or assets into this account from another account you own. Cost basis is preserved. | +| `TRANSFER_OUT` | Moving cash or assets out of this account to another account you own. Cost basis is preserved. | +| `DIVIDEND` | Cash dividend paid by a holding. Increases cash; does not affect holdings or cost basis. | +| `INTEREST` | Interest earned on cash or fixed-income. Increases cash. | +| `CREDIT` | Refund, rebate, or bonus. Increases cash. (A `BONUS` subtype counts as new capital; `REBATE`/`REFUND` do not.) | +| `FEE` | Stand-alone fee not attached to a trade (e.g., account management fee). Decreases cash. | +| `TAX` | Tax paid from the account (e.g., withholding). Decreases cash. | +| `ADJUSTMENT` | Non-trade correction β€” use for corporate actions, option expirations, return-of-capital basis changes. | +| `UNKNOWN` | Placeholder for unrecognized imports. Has no automatic effect until you reclassify it. | + +**`TRANSFER_IN` vs `DEPOSIT`.** Use `DEPOSIT` when cash comes from *outside* +your tracked portfolio. Use `TRANSFER_IN` when moving between two accounts you +both track in Folio β€” that way the move nets to zero at the portfolio level and +does not inflate your net contribution. + +**`ADJUSTMENT` vs `UNKNOWN`.** Use `ADJUSTMENT` when you know what the +correction is but no other type fits. Use `UNKNOWN` as a temporary placeholder +for imported rows you have not classified yet. + +For the complete reference, including subtypes such as `DRIP` and +`STAKING_REWARD` and their field requirements, see +[./activities/activity-types.md](./activities/activity-types.md). + +--- + +## 8. Supported banks and brokers + +### Bank Connect (automatic import) + +The following institutions are supported by the built-in Bank Connect panel: + +| Institution | Type | +| -------------------- | ------ | +| CommBank (CBA) | Bank | +| ANZ | Bank | +| ING | Bank | +| Bank of Melbourne | Bank | +| Beyond Bank | Bank | +| Interactive Brokers | Broker | + +**Coming soon** (broker support in progress): CommSec, Sharesies, SelfWealth, +Stake. + +### Importing from any other institution + +If your bank or broker is not listed above, you can still track it. Most +Australian institutions let you export a statement, and Folio imports **CSV, +OFX, and QIF** files. Use the **Import** screen and map the columns to Folio's +activity types. + +--- + +## 9. Privacy and security + +- **Local-first.** Your data is stored in a SQLite database on your device. +- **No cloud storage.** Folio does not store your financial data on any server. +- **No tracking.** No analytics or tracking. +- **Open source.** You can audit the code yourself. +- **Credentials stay put.** Bank Connect enters your login directly on the + bank's own site inside a local panel; Folio never stores or transmits them. +- **Encrypted sync (optional).** If you enable Wealthfolio Connect, your + database is replicated between your own devices with end-to-end encryption. + Keys stay on your devices. + +Never commit secrets (API keys, tokens, database credentials) to the repository. +See [SECURITY.md](./SECURITY.md) for the full policy. + +--- + +## 10. Troubleshooting and FAQ + +**Where is my data stored?** + +In a local SQLite database. In desktop mode it lives in the app's data +directory; in web/Docker mode it is the file at `WF_DB_PATH`. + +**Can I use Folio on more than one computer?** + +Yes. Enable **Wealthfolio Connect** (Settings β†’ Connect) for encrypted sync +between your own devices, or copy the database file. + +**A transaction imported as `UNKNOWN`. What do I do?** + +Open **Activities β†’ Manage**, find the transaction, and reclassify it to the +correct type. `UNKNOWN` activities have no effect on your holdings until you do. + +**Why is my portfolio value or performance wrong?** + +Check that (a) all activities are recorded, (b) market data is current +(Settings β†’ Market Data), and (c) transaction types are classified correctly. + +**Bank Connect isn't available.** + +Bank Connect is a desktop-only feature. In web mode, use CSV/OFX/QIF import. + +**My bank isn't in the Bank Connect list.** + +Export a statement from your bank and import it (CSV/OFX/QIF). Support for more +institutions is being added over time. + +**Do I need the AI provider configured?** + +No. AI features are optional. Everything else works without a provider. + +--- + +## 11. Getting help + +- **Getting Started:** [./getting-started.md](./getting-started.md) +- **Activity types:** [./activities/activity-types.md](./activities/activity-types.md) +- **Addons:** [./addons/index.md](./addons/index.md) +- **Security policy:** [./SECURITY.md](./SECURITY.md) +- **Issues and questions:** + [GitHub Issues](https://github.com/Sensible-Analytics/folio/issues) + +--- + +## Disclaimer + +Folio is a personal finance tracking tool and is **not financial advice**. Always +consult a qualified financial advisor before making investment decisions. From 3bf7c11508c8911b09997f19d4c6e31cda66fbb3 Mon Sep 17 00:00:00 2001 From: CrewCircle Date: Sun, 18 Oct 2026 13:34:09 +1100 Subject: [PATCH 2/4] fix(ci): resolve pre-existing type-check, clippy, and CodeQL failures - ai-assistant: migrate useAssistantApi->useAui, useAssistantState->useAuiState (@assistant-ui/react 0.15.22 on main removed the deprecated aliases; PR merge refs inherit main's lockfile, so PR CI type-check failed on the merge ref even though this branch pins 0.12.24 where the aliases still exist) - market-data: allow clippy::double_must_use on MarketDataProvider async_trait; use sort_by_key instead of sort_by(..cmp..) (clippy::unnecessary_sort_by) - codeql: drop python from the analysis matrix (no python sources; job failed during python extraction) --- .github/workflows/security-codeql.yml | 2 +- .../ai-assistant/components/attachment.tsx | 20 +++++----- .../ai-assistant/components/reasoning.tsx | 6 +-- .../ai-assistant/components/thread.tsx | 38 +++++++++---------- .../src/provider/alpha_vantage/mod.rs | 6 +-- .../market-data/src/provider/finnhub/mod.rs | 2 +- crates/market-data/src/provider/traits.rs | 1 + 7 files changed, 38 insertions(+), 37 deletions(-) diff --git a/.github/workflows/security-codeql.yml b/.github/workflows/security-codeql.yml index 01232a69..2d8db3f6 100644 --- a/.github/workflows/security-codeql.yml +++ b/.github/workflows/security-codeql.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - language: ["javascript", "python"] + language: ["javascript"] steps: - name: Checkout uses: actions/checkout@v6 diff --git a/apps/frontend/src/features/ai-assistant/components/attachment.tsx b/apps/frontend/src/features/ai-assistant/components/attachment.tsx index abcd0b5a..92b02d84 100644 --- a/apps/frontend/src/features/ai-assistant/components/attachment.tsx +++ b/apps/frontend/src/features/ai-assistant/components/attachment.tsx @@ -2,8 +2,8 @@ import { AttachmentPrimitive, ComposerPrimitive, MessagePrimitive, - useAssistantApi, - useAssistantState, + useAui, + useAuiState, } from "@assistant-ui/react"; import { PropsWithChildren, useEffect, useState, type FC } from "react"; @@ -41,7 +41,7 @@ const useFileSrc = (file: File | undefined) => { }; const useAttachmentSrc = () => { - const { file, src } = useAssistantState( + const { file, src } = useAuiState( useShallow(({ attachment }): { file?: File; src?: string } => { if (attachment.type !== "image") return {}; if (attachment.file) return { file: attachment.file }; @@ -87,7 +87,7 @@ const AttachmentPreviewDialog: FC = ({ children }) => { > {children} - + Image Attachment Preview
@@ -98,7 +98,7 @@ const AttachmentPreviewDialog: FC = ({ children }) => { }; const AttachmentThumb: FC = () => { - const isImage = useAssistantState(({ attachment }) => attachment.type === "image"); + const isImage = useAuiState(({ attachment }) => attachment.type === "image"); const src = useAttachmentSrc(); return ( @@ -116,11 +116,11 @@ const AttachmentThumb: FC = () => { }; const AttachmentUI: FC = () => { - const api = useAssistantApi(); + const api = useAui(); const isComposer = api.attachment.source === "composer"; - const isImage = useAssistantState(({ attachment }) => attachment.type === "image"); - const typeLabel = useAssistantState(({ attachment }) => { + const isImage = useAuiState(({ attachment }) => attachment.type === "image"); + const typeLabel = useAuiState(({ attachment }) => { const type = attachment.type; switch (type) { case "image": @@ -171,7 +171,7 @@ const AttachmentRemove: FC = () => { @@ -190,7 +190,7 @@ export const UserMessageAttachments: FC = () => { export const ComposerAttachments: FC = () => { return ( -
+
); diff --git a/apps/frontend/src/features/ai-assistant/components/reasoning.tsx b/apps/frontend/src/features/ai-assistant/components/reasoning.tsx index 0cb9c8a1..a3dda0ff 100644 --- a/apps/frontend/src/features/ai-assistant/components/reasoning.tsx +++ b/apps/frontend/src/features/ai-assistant/components/reasoning.tsx @@ -14,7 +14,7 @@ import { import { cn } from "@/lib/utils"; import { - useAssistantState, + useAuiState, useMessagePartReasoning, type ReasoningGroupComponent, type ReasoningMessagePartComponent, @@ -32,7 +32,7 @@ const Shimmer = memo(({ children, duration = 2 }: { children: string; duration?: animate={{ backgroundPosition: "0% center" }} initial={{ backgroundPosition: "100% center" }} transition={{ duration, ease: "linear", repeat: Infinity }} - className="relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent [background-repeat:no-repeat,padding-box]" + className="relative inline-block bg-[length:250%_100%,auto] bg-clip-text [background-repeat:no-repeat,padding-box] text-transparent" style={ { "--spread": `${spread}px`, @@ -65,7 +65,7 @@ const ReasoningGroupImpl: ReasoningGroupComponent = ({ children, startIndex, end const [duration, setDuration] = useState(undefined); const startTimeRef = useRef(null); - const isStreaming = useAssistantState(({ message }) => { + const isStreaming = useAuiState(({ message }) => { if (message.status?.type !== "running") return false; const lastIndex = message.parts.length - 1; if (lastIndex < 0) return false; diff --git a/apps/frontend/src/features/ai-assistant/components/thread.tsx b/apps/frontend/src/features/ai-assistant/components/thread.tsx index a0e64df1..b4ad5a0e 100644 --- a/apps/frontend/src/features/ai-assistant/components/thread.tsx +++ b/apps/frontend/src/features/ai-assistant/components/thread.tsx @@ -5,7 +5,7 @@ import { ErrorPrimitive, MessagePrimitive, ThreadPrimitive, - useAssistantState, + useAuiState, } from "@assistant-ui/react"; import type { FC, ReactNode } from "react"; @@ -52,7 +52,7 @@ export const Thread: FC = ({ composerActions }) => { AssistantMessage, }} /> - +

@@ -81,7 +81,7 @@ const ThreadScrollToBottom: FC = () => { const ThreadWelcome: FC = () => { return ( -

+
@@ -128,7 +128,7 @@ const ThreadSuggestions: FC = () => { diff --git a/apps/frontend/src/components/app-launcher.tsx b/apps/frontend/src/components/app-launcher.tsx index d245292c..2be25af6 100644 --- a/apps/frontend/src/components/app-launcher.tsx +++ b/apps/frontend/src/components/app-launcher.tsx @@ -749,7 +749,7 @@ export function AppLauncher() { Quick launch @@ -758,7 +758,7 @@ export function AppLauncher() { className={cn( "flex flex-1 flex-col bg-transparent", "[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-4 [&_[cmdk-group-heading]]:font-medium", - "[&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2", + "[&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0", "[&_[cmdk-input-wrapper]]:px-5 [&_[cmdk-input]]:h-14 [&_[cmdk-input]]:text-base", "[&_[cmdk-item]]:px-4 [&_[cmdk-item]]:py-4 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5", "[&_[data-cmdk-input-wrapper]_svg]:h-5 [&_[data-cmdk-input-wrapper]_svg]:w-5", diff --git a/apps/frontend/src/components/benchmark-symbol-selector-mobile.tsx b/apps/frontend/src/components/benchmark-symbol-selector-mobile.tsx index b2e7bf7d..d3a25a47 100644 --- a/apps/frontend/src/components/benchmark-symbol-selector-mobile.tsx +++ b/apps/frontend/src/components/benchmark-symbol-selector-mobile.tsx @@ -187,7 +187,7 @@ export function BenchmarkSymbolSelectorMobile({ {!iconOnly && "Add Benchmark"} - + Select Benchmark Choose a benchmark or search for any symbol @@ -197,13 +197,13 @@ export function BenchmarkSymbolSelectorMobile({ {/* Search Input */}
- + setSearchQuery(e.target.value)} - className="bg-background border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring h-10 w-full rounded-md border px-3 py-2 pl-9 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2" + className="bg-background border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring h-10 w-full rounded-md border px-3 py-2 pl-9 text-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none" />
diff --git a/apps/frontend/src/components/classification/classification-sheet.tsx b/apps/frontend/src/components/classification/classification-sheet.tsx index 4b791f4c..2e362a28 100644 --- a/apps/frontend/src/components/classification/classification-sheet.tsx +++ b/apps/frontend/src/components/classification/classification-sheet.tsx @@ -78,7 +78,7 @@ export function ClassificationSheet({
-
+
{/* Loading State */} {isLoading && } diff --git a/apps/frontend/src/components/classification/multi-select-taxonomy.tsx b/apps/frontend/src/components/classification/multi-select-taxonomy.tsx index 69c620e7..22bf50dc 100644 --- a/apps/frontend/src/components/classification/multi-select-taxonomy.tsx +++ b/apps/frontend/src/components/classification/multi-select-taxonomy.tsx @@ -284,7 +284,7 @@ export function MultiSelectTaxonomy({ className="h-8 pr-6 text-sm" placeholder="100" /> - + %
diff --git a/apps/frontend/src/components/font-selector.tsx b/apps/frontend/src/components/font-selector.tsx index 8f83f923..590b3576 100644 --- a/apps/frontend/src/components/font-selector.tsx +++ b/apps/frontend/src/components/font-selector.tsx @@ -44,7 +44,7 @@ export function FontSelector({ value, onChange, className }: FontSelectorProps)
{font.label}
{value === font.value && ( -
+
)} ))} diff --git a/apps/frontend/src/components/liquid-glass.tsx b/apps/frontend/src/components/liquid-glass.tsx index 1a32be09..5fdbfcf5 100644 --- a/apps/frontend/src/components/liquid-glass.tsx +++ b/apps/frontend/src/components/liquid-glass.tsx @@ -53,7 +53,7 @@ export function LiquidGlass({ style={{ ...style }} >
{children}
-
+
); } diff --git a/apps/frontend/src/components/mobile-actions-menu.tsx b/apps/frontend/src/components/mobile-actions-menu.tsx index 92f59886..e207a447 100644 --- a/apps/frontend/src/components/mobile-actions-menu.tsx +++ b/apps/frontend/src/components/mobile-actions-menu.tsx @@ -38,7 +38,7 @@ export function MobileActionsMenu({ - + {title} {description} @@ -53,7 +53,7 @@ export function MobileActionsMenu({ action.onClick(); onOpenChange(false); }} - className="hover:bg-accent active:bg-accent/80 focus:ring-primary flex items-center gap-4 rounded-lg border p-4 text-left transition-colors focus:outline-none focus:ring-2" + className="hover:bg-accent active:bg-accent/80 focus:ring-primary flex items-center gap-4 rounded-lg border p-4 text-left transition-colors focus:ring-2 focus:outline-none" >
diff --git a/apps/frontend/src/components/mobile-loading-indicator.tsx b/apps/frontend/src/components/mobile-loading-indicator.tsx index c367da5e..8c1e79a7 100644 --- a/apps/frontend/src/components/mobile-loading-indicator.tsx +++ b/apps/frontend/src/components/mobile-loading-indicator.tsx @@ -8,7 +8,7 @@ export function MobileLoadingIndicator() { } return ( -
+
{/* Smooth indeterminate progress strip */}
onViewChange(view.value)} className={cn( "relative flex cursor-pointer items-center gap-2 rounded-full px-3 py-1.5 text-sm font-medium transition-colors duration-200", - "focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-2", + "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none", isActive ? "text-foreground" : "text-muted-foreground hover:text-foreground/80", )} aria-current={isActive ? "page" : undefined} @@ -102,7 +102,7 @@ function MobileNavigation({ onClick={() => onViewChange(item.value)} className={cn( "relative flex cursor-pointer items-center justify-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors duration-200", - "focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2", + "focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none", isActive ? "text-foreground" : "text-muted-foreground", )} aria-label={item.label} @@ -229,7 +229,7 @@ export function SwipablePage({ /* Desktop: Navigation at top center + content below */
{/* Header with Navigation and Actions */} -
+
{title &&

{title}

} @@ -200,7 +200,7 @@ export function SwipableRoutesPage({ - + {showCustomAssetForm ? ( <> {/* Custom Asset Form Header */} @@ -291,13 +291,13 @@ export const SymbolSelectorMobile = forwardRef
- + setSearchQuery(e.target.value)} - className="bg-background border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring h-14 w-full rounded-md border px-4 py-3 pl-12 text-base focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2" + className="bg-background border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring h-14 w-full rounded-md border px-4 py-3 pl-12 text-base focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none" autoFocus />
diff --git a/apps/frontend/src/components/ticker-search.tsx b/apps/frontend/src/components/ticker-search.tsx index 3cea90d9..1ab6c136 100644 --- a/apps/frontend/src/components/ticker-search.tsx +++ b/apps/frontend/src/components/ticker-search.tsx @@ -514,7 +514,7 @@ const TickerSearchInput = forwardRef( diff --git a/apps/frontend/src/components/update-dialog.tsx b/apps/frontend/src/components/update-dialog.tsx index 723edd6e..42cb63bd 100644 --- a/apps/frontend/src/components/update-dialog.tsx +++ b/apps/frontend/src/components/update-dialog.tsx @@ -117,10 +117,10 @@ export function UpdateDialog() { {/* Scrollable content area */}
{/* Header */} -
+
diff --git a/apps/frontend/src/features/devices-sync/components/pairing-flow/index.tsx b/apps/frontend/src/features/devices-sync/components/pairing-flow/index.tsx index e5ddbf91..1d12a6b3 100644 --- a/apps/frontend/src/features/devices-sync/components/pairing-flow/index.tsx +++ b/apps/frontend/src/features/devices-sync/components/pairing-flow/index.tsx @@ -20,12 +20,7 @@ interface PairingFlowProps { // Claimer flow steps type ClaimerStep = - | "enter_code" - | "connecting" - | "waiting_keys" - | "verify_sas" - | "success" - | "error"; + "enter_code" | "connecting" | "waiting_keys" | "verify_sas" | "success" | "error"; export function PairingFlow({ onComplete, onCancel }: PairingFlowProps) { const { state } = useDeviceSync(); diff --git a/apps/frontend/src/features/devices-sync/types.ts b/apps/frontend/src/features/devices-sync/types.ts index 4b511522..59c6c672 100644 --- a/apps/frontend/src/features/devices-sync/types.ts +++ b/apps/frontend/src/features/devices-sync/types.ts @@ -4,14 +4,7 @@ export type TrustState = "trusted" | "untrusted" | "revoked"; export type DevicePlatform = - | "ios" - | "android" - | "mac" - | "macos" - | "windows" - | "linux" - | "web" - | "server"; + "ios" | "android" | "mac" | "macos" | "windows" | "linux" | "web" | "server"; export type PairingRole = "issuer" | "claimer"; export type PairingStatus = "open" | "claimed" | "approved" | "completed" | "cancelled" | "expired"; export type KeyState = "ACTIVE" | "PENDING"; diff --git a/apps/frontend/src/features/wealthfolio-connect/components/connect-empty-state.tsx b/apps/frontend/src/features/wealthfolio-connect/components/connect-empty-state.tsx index 383eff4c..81ee2304 100644 --- a/apps/frontend/src/features/wealthfolio-connect/components/connect-empty-state.tsx +++ b/apps/frontend/src/features/wealthfolio-connect/components/connect-empty-state.tsx @@ -83,7 +83,7 @@ export function ConnectEmptyState() { {/* CTA */}