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.
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.