> ## Documentation Index
> Fetch the complete documentation index at: https://docs.suchi.page/llms.txt
> Use this file to discover all available pages before exploring further.

# Frontend: settings and account screens

> Archive Configuration, tags, filing trees, models and matching, scan processing, automations, people, account email and security, tokens, and mobile pairing.

Part of the [architecture guides](https://docs.suchi.page/architecture.md); see [Frontend architecture](https://docs.suchi.page/spa-architecture.md) for routing, request lifetimes, and conventions every screen follows.

## Metadata, filing trees, and configuration

`customFieldLabel` in `lib/format.js` owns user-facing labels for
extraction-managed definitions.
Document Detail, Settings, Views, and Automations use that label while retaining
the server's stable field name in IDs, automation parameters, and query text.
Document Detail puts the generic add-field selector behind **Edit** in its final
metadata row and excludes any definition with `managed_by`; populated managed
values remain editable.

`lib/TagPicker.svelte` is a local existing-tag combobox for document detail and
Documents selection; it filters the complete catalog by name/slug and calls a
screen-owned assignment callback on Enter/click. `api.js:listAllTags` follows
paginated tag results rather than borrowing the first-page-only Documents
filter. Detail edits one document immediately and keeps a refused query; when
`needs-review` is visible, a successful edit refreshes tags from the server
because the client cannot infer classifier ownership from slugs alone. Documents
snapshots selected IDs for one `bulk_edit add_tag`, refreshes server-backed pills,
and retains the selection on partial/total failure. Both invalidate late UI
updates on route or system changes. Only admins see the **Manage tags** link
beside a document's Tags; the Documents list stays limited to search, display and
filter controls, and document screens do not mutate the catalog.

`Documents.svelte` reuses its bounded correspondent facet for selection actions.
The selection sends one `bulk_edit set_correspondent` request and then reloads
the server-backed rows. The separate **No correspondent** filter means no role
relation at all; it is not the same as clearing sender roles.

`routes/FilingTreeSettings.svelte` owns the standalone Filing tree workspace:
ready-made selection, the one-set-per-lane composer, reviewed category mappings,
Blank confirmation, optional post-Apply Refile, and taxonomy import/export.
`TaxonomyImport.svelte` owns file/paste reads, TOML/HuML choice,
seed/remap/destination choices and preview freshness within that workspace.
TOML is the initial import/export choice; HuML remains selectable. File
extensions select uploaded content, while the required `format` header syntax
selects pasted content without duplicating server parsing. Schema validation
stays server-side. Late FileReader/network responses cannot replace new input,
and conflicting actions are disabled in flight.

Ready-made selection requires `current_preset_builtin` from setup state; an
imported/custom tree leaves every preset card unselected even when its file ID
matches a built-in. The current-tree panel remains the source of visible live
structure.

Preset/composer Preview renders the complete target tree and document counts for
each collision. The screen proposes replacement, free same-decade remap or skip,
then re-previews changed choices before Apply sends `expected_state_hash`.
Taxonomy-file Preview renders the declared file format/version, source story,
destination, `user_areas`, separate System/49 `generated_areas`, additions,
preserved disabled rules and dependency skips. 00–09 is explanatory, not a row.
First prefixed import offers using the original archive or preserving it under an
explicit other code. Named success refreshes the catalog, selects the returned
system and invalidates old requests. Taxonomy administration requires a web admin
session; mobile tokens cannot use it. See [Filing trees](https://docs.suchi.page/jd.md).

`lib/configuration.js` owns the Archive Configuration section inventory.
`ArchiveSettings.svelte` owns overview and section navigation.
`Settings.svelte` fills the shell's remaining dynamic viewport height, keeping
Settings tabs and the build footer outside its scrolling content row.
The archive frame fills that row: its right pane scrolls independently with a
stable scrollbar gutter. The sidebar can scroll in shorter windows and becomes
a horizontal section rail on narrow screens.

`App.svelte` passes URL `people` and `metadata` through Settings and Archive
Configuration to `PeopleSettings.svelte`; `lib/configuration.js` owns the Tags
deep-link hash. Archive navigation exposes People and Metadata as separate
sections. User/Group tabs update `people`; metadata type changes update
`metadata`, so reload and history restore the selected panel.

`App.svelte` owns the administrator's filing-tree reminder state and passes it
to `Dashboard.svelte`. Dashboard renders the first setup step itself; on other
screens, `SetupReminder.svelte` renders it in the sidebar, and Settings renders
its inline form. Failures remain visible with Retry. Choosing a preset,
confirmed Blank, or an imported tree refreshes the state; visiting Filing tree
alone does not dismiss it. `Dashboard.svelte` derives later first-document
steps from the real document, Inbox, review, and processing counts; no separate
onboarding state is stored.

`ConfigurationSection.svelte` owns the remaining direct Archive Configuration
forms and their required-load/retry gates. Mailbox editing delegates to
`EmailAccounts.svelte`; both automation entry points link to the existing
`Automations.svelte` editor.

`ConfigurationSection.svelte` owns the model dialog and separate drafts for
Local model, Hosted model, and Account subscription. The subscription panel
marks the feature **Experimental** above its Provider control and composes
connection state, model catalog, permission to send document text, the retained
confidence slider, and the connection-test result. Provider ID
`openai_chatgpt` is explicit server state; the browser never infers subscription
mode from a service URL.

Device authorization exposes only its code and verification URL, polls through
`subscriptionLoginAction(provider, action)`, and cancels timers on disposal or
provider changes. `getSubscriptionModels(provider)` loads the catalog after
connection, provider selection, and saved-state reload;
`saveSubscriptionModel(provider, model)` persists the preference without
activating inference. Selection falls back from the current available model to
the saved preference and then the first catalog entry. Generation checks discard
late login, catalog, and test results. Connecting still requires explicit egress
acknowledgement, a successful test, and **Enable model**; tokens remain
server-side. Verify with `bun run e2e --grep 'Account subscription'`.

Classification uses the same Suggestions and Ask your documents cards in both shells.
The Suggestion application radio group defaults to **Apply when confident** and
saves independently through **Save application mode**, using a standalone
`auto_apply` PATCH. **Review first** disables automatic application. Local thresholds
and research depth use disclosure controls; model connection and activation live
in the model dialog.
**Save matching options** calls `saveArchiveMatchingSettings` with only the
three `archive_*` fields in a standalone PATCH. It preserves the saved model and
unsaved connection form without checking account connectivity or reloading
inference. Verify disconnected-account behavior with
`bun run e2e --grep 'local matching saves after disconnect'`.
Model activation, local matching and research settings retain their own saves;
one save must not overwrite another form's saved or unsaved state. Automatic-only
threshold sliders are disabled, not cleared, in review mode. The connection test
validates a synthetic response without changing application mode.

`PeopleSettings.svelte` owns the separate People and Metadata destinations.
People contains Users and Groups; Metadata selects among tags, correspondents,
and custom fields. `FilingTreeSettings.svelte` owns presets, tree construction,
and taxonomy files. `ArchiveSettings.svelte` places
`FolderLayoutsSettings.svelte` first under Advanced; its editor calls the server
preview rather than duplicating template semantics in JavaScript.
`ConfigurationSection.svelte` reuses the archive-settings load/save shell and
existing radio-card controls for **Scan processing**. It reads and writes the
effective mode, QR prefix, and DPI through `/api/admin/settings/scan`, hides
inactive label details, and disables fields named in the response's `managed`
object. `ArchiveSettings.svelte` projects the same endpoint into the Advanced
overview status. Marker grammar, deployment precedence, and legacy-token
collision checks remain server-owned.
`Automations.svelte` retains action parameter schemas, reuses the existing
metadata, user-directory, and filing-system-membership APIs, and presents a
list-first workspace with a mutually exclusive local editor state. Compact rows
derive one-line trigger/action summaries from the loaded schema and facets;
custom rules PATCH their numeric order after drag or keyboard movement, while
filing-tree rules remain read-only in a collapsed section. Empty state patterns
seed ordinary drafts rather than introducing a separate persistence contract.
The sentence builder reveals only filters explicitly added to each trigger and
keeps JSON editing as the complete fallback.

Named/type-aware controls cover folder layouts, eligible owners, and custom
fields. The builder also consumes the server's bounded ask capabilities to edit
one question, closed answer choices, and per-action answer branches; enabling
that branch clears transient trigger filters and limits action/custom-field
pickers to the advertised safe subset. The browser does not infer model
authority, action mappings, limits, or destination validity. IDs remain an
internal persisted action contract.

`RuleWorkspace.svelte`, `RuleRow.svelte`, `RuleEmpty.svelte`, and
`RuleSection.svelte` own the shared list/editor shell, compact sentence row,
starter-pattern state, and progressive builder sections used by Views and
Automations. They own layout and responsive behavior only; each route retains
its API calls, draft semantics, validation, permissions, and navigation.

`UserCreateForm.svelte` owns user creation in People and submits no hidden member
capabilities for admins. Self-disable checks use
`session.user.user_id`; the API owns self-disable, last-admin and capability
normalization safeguards. Verify shared configuration with:
`cd ui && bun run e2e --grep 'archive setup|filing-tree|refreshes intake owners|failed configuration reads'`.

`PeopleSettings.svelte` alone creates, renames and deletes catalog tags. It loads
every tag page, keeps a searched multi-selection across filters, and prunes
selected IDs after a successful refresh. Single-row and bounded bulk deletion
share one native `ConfirmDialog` and `api.js:deleteTags`; cancel sends no request,
failures keep rows/selection for retry, and a late response cannot replace a
different metadata type or filing system. The server's admin/session-only,
transactional `/api/tags/` DELETE enforces ownership and child re-rooting; UI
selection is not an authorization boundary. Verify with
`cd ui && bun run e2e --grep 'document tag|bulk tag|tag management|settings tag'`.

`AccountSettings.svelte` sends only `display_name` for profile saves and derives
email controls exclusively from `whoami.email_change_mode`, never `authn_by`.
Password mode opens the focused native `EmailChangeDialog.svelte`; OIDC mode
uses a native same-origin POST form to `/oidc/email-change`; disabled/demo mode
is read-only and exposes no mutation request path.

`AccountSettings.svelte` shows `TwoFactorSettings.svelte` only for a row-backed
`local-auth` browser identity, including profile-only/no-filing-system mode.
OIDC and demo identities expose no factor routes or controls. The component owns
status loading, focused lifecycle dialogs, the explicitly `otpauth` QR, manual
secret copy, and one-time recovery-code copy/download/acknowledgement. Enable,
regenerate, and disable use named `api.js` operations that invalidate older
session requests only after the server rotates the cookie. Passwords, factor
codes, enrollment seed/token, and recovery codes are erased on failure, close,
account change, and destroy. It shows the enable response's `active_api_tokens`
note beside the new codes and a warning when two or fewer recovery codes remain.
`lib/twoFactorErrors.js` turns `too_many_attempts` (with the `Retry-After` wait
that `api.js` attaches to `ApiError.retryAfter`), `two_factor_locked`, and
`two_factor_required` into wording shared by sign-in and every factor dialog.

`PeopleSettings.svelte` renders the server's `two_factor_enabled` user flag.
Administrators can reset another enabled user through
`AdminTwoFactorResetDialog.svelte`, which reauthenticates the acting
administrator and clears both inputs on failure or exit. The browser updates the
row only after the server confirms the audited reset.

## Account and sign-in

The `/app` CSP admits only `self` plus the provider-discovered authorization
endpoint's HTTP(S) origin for form submissions, because that POST redirects to
the provider. Non-SPA responses keep `form-action 'self'`.

The password dialog owns new-email, exact confirmation, and current-password
validation. It traps focus, focuses the first field, closes with Escape, exposes
inline errors with `role=alert`, and disables fields/close while the request is
busy. Password memory is cleared on invalid input, mismatch, server failure,
close, account change, destroy, and before the successful state transition.
When the server answers `two_factor_required`, the dialog keeps the password and
reveals a code field for the retry.
`api.js:changeMyEmail` advances the session/request generation only after a
successful rotation response, so pending pre-change GETs cannot repopulate the
new account scope; failure leaves unrelated requests valid.

`Login.svelte` performs local sign-in in two requests only when the first
correct-password response carries `two_factor_required`. It retains the password
in component memory for that second request, accepts an authenticator or recovery
code, submits a six-digit code as soon as it is entered, preserves the generic
credential failure surface, and clears both values on account edit, success,
navigation, or destroy. The server-rendered `/login` form shows the
`two_factor_code` field only after a `two_factor_required` or
`two_factor_invalid` redirect and never persists it.

After success, the screen reloads `/api/whoami`, waits for the principal/scope
reset tick, and uses the shell's separate account notification channel. That
channel is deliberately outside the filing-scoped subtree, so **Sign-in email
changed** survives account/system reset. Both account and ordinary transient
notices use an atomic polite `role=status`; actionable failures remain persistent
inside their owning screen. Scoped token, pairing, mailbox, and vault feedback
continues through the ordinary notifier.

`App.svelte` allowlists only `identity_bound`, `email_changed`, and
`email_checked` from the OIDC callback's `account_notice` query parameter. It
renders fixed local copy, removes that one parameter with
`history.replaceState`, and preserves every other query parameter and the hash.
Unknown values are removed without rendering provider-controlled text.

Demo kinds receive a read-only profile without token, vault, mailbox or pairing
controls or requests.

## Mobile pairing and tokens

The mobile pairing
prompt loads only after its button is selected; it keeps the single-use secret
in component memory, clears expired QR/link displays, and discards it on
navigation, account change or system switch. When the original session remains
available, unused and late-created codes are cancelled against their captured
system. Generation and clipboard writes check the original account and system;
late responses never display in another cabinet's prompt.
`AccountSettings.svelte` owns one active-token list and renders the current
account's `mobile_pairing` credentials in Mobile app through a shared row snippet;
manual and legacy credentials remain in API tokens. Token reads refresh every
two seconds only while the prompt is open, immediately on close, and on window
focus. Reads are bounded, abortable, ordered, and checked against their original
account+system generation; either change clears the list, and unmount stops polling.
Revocation reloads the list so an earlier poll cannot restore a disconnected app.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.