ui
45 modules
ui/account_sessions_state.svelte.ts
Reactive state for managing the authenticated account's auth sessions on a settings page. Reads and mutations flow through a narrow RPC adapter backed by auth/account_actions.ts.
Holds two AsyncSlots —
listfor the fetch,revoke_allfor the bulk revoke — plus oneKeyedAsyncSlot<string, void>(revoke) keyed bysession_idfor per-row revoke (independent supersession across concurrent rows; per-row error surfacing). Method names use thesubmit_*prefix to avoid slot-name collisions.ui/AccountSessions.svelte
Self-serve session list for the logged-in account. Instantiates an AccountSessionsState against account_sessions_rpc_context and renders a Datatable with per-row revoke and a confirmed revoke-all. The row the server marks
is_currentis this browser's session: revoking it logs out throughauth_state.logout, as does a revoke-all that succeeded, so the UI falls back to login instead of acting on a dead session.ui/admin_accounts_state.svelte.ts
Reactive state for admin account management.
Holds one fetch AsyncSlot (
list) plus three KeyedAsyncSlots —grant(offer creation, keyed byaccount_id:roleoraccount_id:role:to_actor_id),revoke(role_grant revoke, keyed byrole_grant_id),retract(offer retraction, keyed byoffer_id). Per-row supersession is correct (clicking row B no longer aborts row A) anderror(key)surfaces failure per-row. Method names use thesubmit_*prefix to avoid slot-name collisions.ui/admin_invites_state.svelte.ts
Reactive state for admin invite management.
Flows every operation through an injected AdminInvitesRpc adapter — the class stays decoupled from the concrete RPC client so tests can inject plain-function stubs. Mirrors AdminAccountsRpc / AuditLogRpc.
Holds two AsyncSlots —
list(fetch) andcreate(singular write) — plus oneKeyedAsyncSlot<Uuid>(remove) for the per-row delete with correct per-row supersession and per-row error surfacing. Method names use thesubmit_*prefix to avoid slot-name collisions (deleteis reserved at top-level positions; renamed for symmetry).ui/admin_rpc_adapters.ts
Admin RPC adapter helpers for consumer UIs.
Bridges a typed throwing RPC client to the four narrow admin RPC interfaces the state classes consume — AdminAccountsRpc, AdminInvitesRpc, AuditLogRpc, AppSettingsRpc. Two calls at the admin shell layout wire everything.
Intentionally admin-only despite the backend-side create_standard_rpc_actions rename (admin + role-grant-offer + account). Account-surface methods flow through account_sessions_rpc_context (wired at the self-service layout), and role-grant-offer methods that surface in the admin UI (
role_grant_offer_create,role_grant_revoke,role_grant_offer_retract) live inside the AdminAccountsRpc interface — they belong to the admin UX, not a separate wire pairing. The UI side and backend factory names diverge by design.// `api` is the typed throwing Proxy from `create_frontend_rpc_client`. provide_admin_rpc_contexts(create_admin_rpc_adapters(api));The throwing Proxy spreads the JSON-RPC
{code, message, data?}onto the thrownErrorso form components (e.g. ui/RoleGrantOfferForm.svelte) can match onerror.data?.reasonviaERROR_ROLE_GRANT_OFFER_*constants — optional chaining is required because JSON-RPCdatais spec-level optional. Consumers that need a custom unwrap strategy can construct their own object satisfying AdminRpcApi and pass it directly.No
.svelte.tssuffix — this module holds no reactive state, only method-name mappings.ui/admin_sessions_state.svelte.ts
Reactive state for admin session overview.
Both the listing and the two revoke-all mutations flow through the shared AdminAccountsRpc adapter (
list_sessions,session_revoke_all,token_revoke_all); the listing wraps theadmin_session_listRPC method.Holds one fetch AsyncSlot (
list) plus two KeyedAsyncSlots keyed byaccount_id—revoke_sessionsandrevoke_tokens. Per-account concurrent revokes are independent (clicking row B does not abort row A) and per-row errors surface viarevoke_sessions.error(account_id)/revoke_tokens.error(account_id).ui/AdminAccounts.svelte
Admin accounts table — users with their role_grants and pending offers. Consumes admin_accounts_rpc_context (read via AdminAccountsState) and format_scope_context for label rendering. Per-row actions: grant role (
role_grant_offer_create), revoke role_grant (role_grant_revoke, keyed byactor_id), retract pending offer (role_grant_offer_retract). The grant button offers the role globally, so can_offer_global_role hides it only for a global grant or pending global offer of that role.ui/AdminAuditLog.svelte
Admin audit log viewer. Consumes audit_log_rpc_context; uses
audit_log_listRPC for fetches and a separateEventSourceagainst the SSE stream URL for live tailing (toggle via the stream button). Filter byevent_type(applied to the stream too), manual refresh, and live-stream status are surfaced in the header; a stream the server closed offers a retry in place of the stream button, and a failed or truncated gap fill says events may be missing. Changing the filter reopens a live stream with it, but leaves a closed one closed until retried.ui/AdminInvites.svelte
Admin invites manager — list, create (
invite_create), and delete (invite_delete) RPC actions through admin_invites_rpc_context. Embeds OpenSignupToggle so the same surface controls both the invite-only and open-signup flows.ui/AdminOverview.svelte
Admin dashboard — summary panels (accounts, sessions, invites, recent activity, security, system) fed by parallel
fetch()calls on mount. Consumes the admin RPC contexts plus auth_state_context. Recent activity is the newest slice of the audit log; the security counts come from their own queries (failed logins filtered server-side, role grant changes from the role-grant history), each fetched up to AUDIT_LOG_LIST_LIMIT_MAX rows and counted over the last 24 hours (SECURITY_WINDOW_HOURS) —N+when the cap may cut the window off.ui/AdminRoleGrantHistory.svelte
Role grant create/revoke history table. Consumes audit_log_rpc_context, calls
audit_log.fetch_role_grant_history()once on mount (theaudit_log_role_grant_historyRPC). Uses format_scope_context to render scope ids as human labels.ui/AdminSessions.svelte
Cross-account active session list with per-account revoke-all controls for sessions and tokens. Listing (
admin_session_list) and both revoke-all mutations (admin_session_revoke_all,admin_token_revoke_all) reuse admin_accounts_rpc_context — a single adapter backs both AdminSessionsState and AdminAccountsState.ui/AdminSettings.svelte
Admin settings shell — composes OpenSignupToggle, the logged-in account line, and a confirm-protected logout button. No direct RPC calls; reads auth_state_context and delegates settings to its children.
ui/AdminSurface.svelte
Attack-surface viewer. Fetches
GET /api/surface(REST — not RPC, since the surface dump exists outside the action surface) and delegates rendering to SurfaceExplorer. Surfaces a retry button on fetch failures. The server must opt in to the route (create_app_server({surface_route: true})), which is admin-only and refused to a narrowed API token.ui/app_settings_state.svelte.ts
Reactive state for admin app settings management.
Flows every operation through an injected AppSettingsRpc adapter — mirrors AdminInvitesRpc / AuditLogRpc. Tests can inject plain-function stubs and consumers adapt their typed RPC client to the same shape.
Holds two AsyncSlots —
list(the initial fetch) andupdate(theapp_settings_updatewrite). Slots track status/error; the canonicalsettingslives on the class so consumers don't unwrapslot.data.ui/AppShell.svelte
Sidebar-and-main app shell. Provisions sidebar_state_context (creating a fresh SidebarState if
sidebar_stateis not supplied) so descendants can read sidebar visibility and toggle it. Optionally binds a global keyboard shortcut and renders a built-in toggle button (or a custom one via thetoggle_buttonsnippet) while the sidebar is enabled. Requires the SvelteKit runtime — it closes the overlay fromafterNavigate.Two modes, split by the state's
narrow_query. Outside it the sidebar sits beside the content, padded by--sidebar_offset, followingshow_sidebar_wide. Inside it the sidebar starts closed and opens as an overlay over a scrim, followingshow_sidebar_narrow; the content isinertwhile it's open, and navigation, Escape, a scrim click, or widening out of narrow mode closes it. Escape never prevents the default, so the browser's close requests stay intact; it does nothing while a modal (:modal) is open, when a windowkeydownlistener registered before the overlay opened swallows it (a contextmenu, say), or when a visible Popover'sdocumentlistener already handled it.The open overlay is a modal dialog: the sidebar takes
role="dialog",aria-modal,tabindex="-1", andsidebar_labelas its name, and focus moves into it, to its first control that takes focus, else to the sidebar itself — the content went inert and dropped it. The attributes key on the open overlay, which reads the viewport — that's what keeps the wide sidebar from ever being a dialog — and the server render matches hydration because the overlay always starts closed, as with the content'sinert.aria-modallets assistive tech hide everything outside the sidebar, the built-in toggle included, so a sidebar whose screen-reader users need a close control beyond Escape and navigation renders its own.Closing with Escape, the keyboard shortcut, or a scrim click returns focus to what opened the overlay, or else to the first element whose
aria-controlsnames the sidebar's id and that takes it — when focus was inside the sidebar or had fallen to the body. The id is the state'ssidebar_idwhen set, so a toggle outside the shell can carryaria-controlstoo; Safari doesn't focus a clicked button, so without it a mouse-opened overlay has no opener to return to.The shell's rules sit in two cascade layers nested under
fuz_app.fuz_app.app_shell_base, in the scoped style, holds the base and state rules, which apply in both modes;fuz_app.app_shell_narrowoverrides some of them from a style element insvelte:headcarryingmedia={narrow_query}, so the server render paints the right mode before hydration and the markup's classes never depend on the viewport. The narrow layer is ordered after the base layer, so it wins whatever the selectors' specificity or the sheets' order. Its rules are global — they match every.app-shellon the page — so shells mounted at the same time must share anarrow_query. Pages served under a strict CSP needstyle-src-elem 'unsafe-inline'or the hash of that constant style.Public class hooks:
.app-shell(root, with state classes.wide-open/.narrow-open),.app-shell-content,.app-shell-sidebar,.app-shell-scrim,.app-shell-toggle,.app-shell-toggle-glyph. Any unlayered rule beats both layers at any specificity, so overrides need no specificity games; to layer them instead, orderfuz_appfirst (@layer fuz_app, overrides;) in a sheet that loads before the shell. The same rule means an unlayered reset overrides the shell too — the fuz_css reset zeroespadding,margin, andborderon every element — so the layers set none of those. The one they need, the content's wide offset, goes through--sidebar_offset: the layers set it on.app-shell-content(--sidebar_widthwhile the wide sidebar is open, else0px), and one unlayered rule at (0,0,1) reads it into the content'spadding-left. Descendants of the content can read it too. Override the offset by setting--sidebar_offseton.app-shell-contentor with apadding-leftrule there; apaddingshorthand there overrides it as well. Like any unlayered override, these apply in both modes, open or closed; to change only the wide open offset, target.app-shell.wide-open > .app-shell-contentinside the narrow query's negation — the state classes don't track the viewport, so.wide-openstays set in narrow mode too. CSS variables:--sidebar_bg,--sidebar_z_index(default200in both modes; the toggle sits one above),--sidebar_scrim_bg,--sidebar_width(set from thesidebar_widthprop; always the configured width, not the offset), and--sidebar_offset.ui/async_slot.svelte.ts
Composable async-operation slot for Svelte 5 reactive state classes.
A state class HOLDS one or more AsyncSlots via composition — one slot per distinct async operation (e.g.
list+create+revoke). Each slot tracks the status, payload, and error of its operation independently, so state classes with multiple write paths don't accumulate ad-hoccreating/updatingfields beside a single sharedloading/errorpair.Core surface:
- Explicit four-value
status—AsyncStatusfrom@fuzdev/fuz_util/async.ts: `'initial' | 'pending' | 'success' | 'failure'.loading: false, error: null` would be ambiguous between "never tried" and "succeeded once and now resting"; the four-value status removes the need for a per-classsubmitted/hydratedflag. - Owns
data: T | undefined— the success payload persists across retries (stale-while-revalidate). The sentinel isundefined(notnull) sonullstays available as a legitimate success value for nullableTs. PassT = voidfor write-only actions whose response isn't worth keeping. - Supersession via internal
AbortController— a secondrun()aborts the first, and superseded results are silently discarded without writing to state. Removes the "in-flight call resolves after the locator advanced" race that locator-style state classes would otherwise need to compensate for. AbortSignalthreaded to the callback — RPC clients that accept a signal (orfetch) get cancellation for free; callers can also pass an externalsignalvia RunOptions to bind the slot's lifetime to a component / page.preserve_error_on_retry— opt-in to keeping the previous error visible while a retry is pending (default clears at the start of eachrun()).- Per-slot
map_error— set once in the constructor ({map_error: to_rpc_error_message}); everyrun()gets the right normalization without re-passing per call. - Public
run()— slots are composed, not subclassed, so call sites can invokestate.list.run(...)directly.
- Explicit four-value
ui/audit_log_state.svelte.ts
Reactive state for the audit log viewer.
Two fetch primitives (
fetchfor events,fetch_role_grant_historyfor the grant/revoke shortcut) flow through an injected RPC adapter; the SSE stream continues to useEventSourcedirectly — streams aren't an RPC concern.Holds AsyncSlots for
list(the main event list),role_grant_history(the dedicated role-grant history endpoint), andgap_fill(the catch-up fetch run each time the stream opens). Data lives on the class so SSE pushes and gap-fill calls update it directly; both merge byseq, soeventsstays newest-first however they interleave.ui/auth_state.svelte.ts
Reactive state for cookie-based authentication.
SPA auth pattern: prerendered static HTML served by Hono, no SvelteKit server for SSR sessions. On load, fetches
GET /api/account/statuswhich returns the current account (200) or 401 with optionalbootstrap_available. Login sends username + password once, then a signed httpOnly cookie handles all subsequent requests.ui/backend_probe.svelte.ts
Backend liveness probe — whether the app's backend is serving, read from its
/healthroute.AuthState.check_sessioncan't tell a backend that's down from a logged-out caller, so a login gate probes/healthto say the backend is needed instead of showing a login form that can't work. Only the health reply counts: a static server or an SPA fallback answering every path with a200HTML page is not a backend, and neither is a network error. Browser-safe — no server imports.ui/BootstrapForm.svelte
First-keeper bootstrap form — used once on a fresh deployment to claim the bootstrap token and create the initial admin account.
Calls
AuthState.bootstrap(POST /api/account/bootstrap) via auth_state_context. Validates token + Username schema + PASSWORD_LENGTH_MIN + password match client-side; submit focuses the first invalid field. Once an account exists, the bootstrap path is disabled server-side and this form should not be mounted.ui/ColumnLayout.svelte
Two-column layout - fixed-width
asideon the left, fluidchildrencolumn on the right. Both columns scroll independently.Below a threshold of the layout's own width - a container query, not a media query, so a nested layout decides for itself - the columns stack: the aside collapses behind a toggle button labelled
toggle_label, and when opened it sits in flow above the content, pushing it down, until the toggle closes it or a navigation does. Opening scrolls the aside into view, under the toggle, when it would land above the view.With a bounded height, the stacked layout scrolls as one column and the toggle sticks to its top; under a parent that grows with its content, the page scrolls and the toggle scrolls away with it.
The threshold matches fuz_css's
smbreakpoint. Above it the toggle isdisplay: none. The switch is CSS alone, so the server render shows the right form at any width and hydration changes nothing.The root is an
inline-sizequery container, so its width can't come from its content: a layout placed where it would shrink to fit (an inline-block, an absolutely positioned or floated box without a width, a flex row item that doesn't grow) collapses to zero width. Give it a width from its context, as a block or a stretched or growing flex item.Requires the SvelteKit runtime - it closes the aside from
afterNavigate. Without JS the stacked aside can't be opened, since the toggle is client state; above the threshold it's always shown.The aside column is a plain
<div>, not a landmark, as with AppShell's sidebar: render the landmark inside theasidesnippet - a<nav aria-label>for navigation links, or an<aside aria-label>for other content.Styling hooks:
--column_width(set fromcolumn_width) and--column_toggle_bg, the stacked toggle bar's opaque background so content scrolls under it unseen (default--shade_00, the page ground).ui/ConfirmButton.svelte
Confirmation popover wrapping PopoverButton.
Clicking the trigger opens a popover with a confirm button. On confirm, calls
onconfirmand hides the popover (controlled byhide_on_confirm). Defaults toposition="left".Trigger content: pass
labelfor a simple string, or achildrensnippet for custom content (the two are mutually exclusive — DEV errors when both are set).pending: booleanoverlays a spinner and disables the trigger, mirroringPendingButtonsemantics so the label stays put while an async operation runs.ui/Datatable.svelte
Generic CSS-subgrid datatable. Sticky header, pointer-driven column resize, and optional
header/cell/emptysnippets for custom rendering. Default cell rendering usescolumn.format(value, row)if present, else format_value. Resize deltas are kept in component-local state, keyed bycolumn.key— they don't outlive the component.Rows are keyed by the required
row_keyaccessor, so per-row state (popovers, pending buttons) follows its row across deletes and refetches. A row whose key isnullorundefinedthrows in DEV.Exposed as an ARIA
table(notgrid— there's no grid keyboard model):row/columnheader/cellroles, with the empty state a full-width row of its own.ui/datatable.ts
Types and constants for the Datatable component.
ui/form_state.svelte.ts
Reactive form state for controlling when field errors appear and focusing invalid fields.
Tracks per-field
touchedstate (set on blur via delegatedfocusout) and form-levelattemptedstate (set on submit attempt). Errors show after a field is blurred or after a submit attempt, avoiding premature validation while the user is still typing.The
FormState.formattachment also handles the Enter key: in an input followed by another input it moves focus to that input (past any button between), and in the last input it submits the form (requestSubmit) — so Enter in a login form's password field logs in, even when the form's button istype="button", asPendingButtonis. Hidden inputs don't count, and an Enter that commits IME composition is left alone.While a submit is in flight, make the fields
readonlyrather thandisabled(and the buttonaria-disabled, guarding the handler against re-entry) — disabling the focused element drops focus to<body>. Enter still advances through and submits fromreadonlyinputs.The type parameter names the form's fields, so
focus,show,touch, andis_touchedreject a misspelled field at compile time.All trackable inputs must have a
nameattribute — an error is thrown in dev if an input withoutnameloses focus.ui/format_scope.ts
Shared
format_scopecallback contract for role-grant-display components.Role grants and offers carry a
scope_idthat names a consumer-owned resource (e.g. a classroom uuid). The default render is the truncated uuid. Consumers wire a FormatScope via context to render a human label without per-page lookup or forking the components.ui/keyed_async_slot.svelte.ts
Keyed sibling of AsyncSlot — fans the per-instance supersession machinery out across an open set of keys.
Each key gets its own lazily-created AsyncSlot, so concurrent
run(key_a, ...)andrun(key_b, ...)calls are independent: a secondrun()onkey_aaborts onlykey_a's in-flight call, leavingkey_brunning. The keyed shape replaces the AsyncSlot +SvelteSet<id>pair that state classes previously carried for per-row in-flight tracking, with two genuine wins:- Cross-key supersession is correct — clicking row B while row A is in flight no longer aborts A; each row has its own AbortController.
- Per-key error surfacing —
error(key)carries the failure for that key only, instead of the last-error-wins shape of a shared slot.
The backing
SvelteMapkeeps entries even after arun()resolves — components can readerror(key)to render an inline per-row failure indicator. Calldelete(key)to dismiss an entry, orreset()to clear everything (e.g. on page leave).ui/LoginForm.svelte
Username + password login form. Calls
AuthState.login(which posts toPOST /api/account/login) via the auth_state_context. On success navigates toredirect_on_login; surfaces 401 / 429 errors viaauth_state.verify_error. Companion to BootstrapForm and SignupForm.ui/LogoutButton.svelte
Pending-aware log-out button. Wraps
PendingButtonand callsAuthState.logout(POST /api/account/logout) via auth_state_context. If the caller providesonclickand callse.preventDefault()from it, the logout is skipped — useful for confirm-before-logout flows.ui/MenuLink.svelte
SvelteKit-aware navigation link. Resolves
pathviaresolvefrom$app/paths, then derivesselectedfrompage.url.pathname— fires for both the exact page (/admin/repos) and any descendant page (/admin/repos/foo/log). Exact matches additionally carryaria-current="page"so CSS / assistive tech can distinguish "this exact page" from "current section" when needed.The earlier
highlightedflag (which fired only on the prefix-only case) was dropped: in every practical use it sat next to.selectedwith a near-identical visual treatment and just made callers compute two booleans for what reads to users as one "you are here" state. Thehighlighted*name* is now free for orthogonal emphasis (recent activity, unread badges, search matches) — that's howfuz_ui'sDocsPageLinksalready uses it.ui/OpenSignupToggle.svelte
Single checkbox bound to
AppSettings.open_signup. Consumes app_settings_rpc_context; the toggle callsapp_settings_updateRPC viaAppSettingsState.update_open_signup.The browser flips the box before the write lands, so once the write settles the box is re-synced to
settings— after a failure it shows the server's state again, not the refused one.ui/popover.svelte.ts
Popover state class with trigger, content, and container attachments.
Popover manages visibility, positioning, outside-click and Escape dismissal, and ARIA attributes. The
trigger,content, andcontainerattachments wire up the DOM via Svelte's{@attach}directive.While visible, the trigger carries
aria-expanded="true"andaria-controlsnaming the content (cleared on hide, when the content usually leaves the DOM), and the content —role="dialog"unless it sets its own role — isaria-labelledbythe trigger unless it carries its own name. Escape hides the popover while it's visible, returning focus to the trigger when focus was inside the content. The listener is ondocument, so it runs beforewindowlisteners, and it prevents the default — so an enclosing layer that skips handled Escapes, like AppShell's narrow overlay, stays open, and only the popover closes. An Escape another listener already prevented is left alone. With several popovers visible, only the newest handles Escape, so nested ones close innermost first. While a modal (:modal) is open, a popover outside it leaves Escape to the modal; a popover inside the modal still closes first.For the common button + popover pattern, see PopoverButton.
ui/PopoverButton.svelte
Button + popover composition for the common toggle-on-click pattern.
Wraps a Popover instance with a
<button>, a positioned content area, and scale transitions. Spreads extra props onto the button element. Passpopover_contentfor the popover body, and eitherchildrenfor simple button content orbuttonfor a fully custom trigger — both receive the Popover instance.ui/position_helpers.ts
CSS position calculation helpers for popovers and floating UI elements.
ui/role_grant_offers_state.svelte.ts
Reactive state for the consentful-role-grants offer flow.
Maintains one offer cache keyed by id, seeded by the RPC list/history actions and kept live by the six role-grant-offer WebSocket notifications.
incoming(recipient-side pending) andoutgoing(grantor-side pending) are derived views;historyis the full cache ordered newest-first for the grantor/admin history view.Wiring is transport-agnostic: the ctor accepts a narrow RPC interface the consumer adapts from their typed client, plus an
account_id/actor_idgetter pair (typically bound toauth_state). Notification delivery is pull-only viasubscribe()— the consumer plumbs their FrontendWebsocketClient / ActionDispatcher receiver toapply_notification.Holds one slot per RPC verb: AsyncSlots for
list,list_historyandcreate, and KeyedAsyncSlots keyed by offer id for the per-row verbsaccept,declineandretract, so each row carries its own pending state and error. The cache#offerslives on the class (multiple ops write into it via#merge_offers/#remove_offer); thecreateslot is typedAsyncSlot<RoleGrantOfferJson>sosubmit_createcan return the new offer via the slot's supersession-safedatapath, but the other slots'datais unused (no single op owns the cache). Each terminal verb stamps the offer it answers into the cache, sohistoryshows it whether or not its WS notification arrives. Method names use thesubmit_*prefix to avoid slot-name collisions; thehistoryview stayed natural by naming the fetch slotlist_history.ui/RoleGrantOfferForm.svelte
Grantor-side role_grant offer form.
Caller supplies
to_account_id, the subset of roles the grantor may offer (typically filtered by admin-grant-path —RoleSpec.grant_pathsincludes'admin'), an optional scope —scope_kindandscope_idtogether, or neither for a global offer — and an optionalon_createdcallback for post-submit UX. Errors from the RPC surface their distinct reason codes (self-target, role-not-grantable, not-authorized, a scoped builtin role, actor mismatches) so consumers can render them appropriately.ui/RoleGrantOfferHistory.svelte
ui/RoleGrantOfferInbox.svelte
ui/sidebar_state.svelte.ts
Reactive sidebar visibility state. Provisioned by ui/AppShell.svelte via sidebar_state_context; consumers read
show_sidebarand calltoggle_sidebar/activate.Visibility is two viewport-free fields —
show_sidebar_wide(the persistent sidebar beside the content) andshow_sidebar_narrow(the overlay belownarrow_query) — so server and client render the same classes and CSS picks which field is in effect.narrowreads the viewport and is only for input handling and ARIA, never for markup that must match SSR.ui/SignupForm.svelte
Account signup form — username + password (+ optional email).
Calls
AuthState.signup(POST /api/account/signup) via auth_state_context. Validates the Username schema and PASSWORD_LENGTH_MIN client-side; surfaces 403 (no invite), 409 (duplicate), and 429 (rate limited) inline. Submit focuses the first invalid field. Companion to LoginForm and BootstrapForm.ui/SurfaceExplorer.svelte
Read-only AppSurface renderer. Tables routes, middleware, environment variables, events, and diagnostics. Routes can be filtered by auth type and expanded to dump
params/query/input/output/errorsschemas as JSON — each expandable row's first cell is a disclosure<button aria-expanded>, so expanding works from the keyboard. Pure presentational — AdminSurface handles the fetch.ui/table_state.svelte.ts
Reactive state for database table pagination and data fetching.
Holds one AsyncSlot —
list(the paginated row fetch). Per-row delete uses plain try/catch + scalardeleting/delete_errorfields (no slot —delete_errormust persist pastlist.run()retries so the failure message stays visible while the user refetches).ui/ui_fetch.ts
Authenticated fetch helper for cookie-based session auth.
Wraps the standard
fetchwithcredentials: 'include'so cookies are sent with every request. Use for all API calls in apps that rely on@fuzdev/fuz_appsession middleware.ui/ui_format.ts
Formatting utilities for UI display.
Value formatting, relative timestamps, uptime display, absolute timestamp formatting, and audit metadata formatting.