Skip to content

Settings & Preferences

This area covers 50 features. Watch the walkthrough, then use the reference below — each feature links to the exact moment it appears (▶).

Who this is for: Owner / org admin (org+project settings) · any user (personal prefs). Each feature below lists the role/permission it requires.

Features

Rename organization ▶ 00:00

As an org manager/maintainer, I want to rename my organization, so that the org name reflects its current identity.

How it works. User clicks 'Rename' button (only visible if role level >= 600/Maintainer) → text input appears with current name pre-filled → user edits and clicks 'Save' → API call to renameOrg fires → on success, the org name updates and edit mode closes. Validation: disabled save button if name is empty after trim.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/pages/Settings.tsx:36-177
src/lib/frontier/orgs (renameOrg function)

View organization statistics ▶ 00:00

As an org member, I want to see at-a-glance stats about my org, so that I can quickly understand the org's size.

How it works. On /settings page, display two cards: one showing member count (from useOrgMembers), one showing project count (fetched from getPortfolio). Both values update on page load. Shows '—' for project count while loading or on fetch error.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/pages/Settings.tsx:42-51
src/pages/Settings.tsx:183-192

Set export permissions floor (org-level) ▶ 00:13

As an org owner, I want to set a minimum role required to export project deliverables (USFM, zip), so that I can control who can download project outputs.

How it works. Owner-only dropdown selector (disabled for non-owners, shows tooltip 'Only org owners can change the export permission policy') lets user choose export floor from {Viewer(100), Contributor(400), Project Lead(500), Maintainer(600, default), Owner(700)}. On change, fires patchOrgSettings with {exportMinRole: newLevel}. Shows 'Saved' on success, error message on 409 or 403, clears message after 2.5s. Server-side enforcement in auth-worker.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/pages/Settings.tsx:54-101
src/pages/Settings.tsx:193-237

Sidebar tab layout preference ▶ 00:00

As a translator, I want to choose whether the editor sidebar tabs (Files, Chat, Search) appear as a vertical left rail or horizontal top bar, so that I can optimize my workspace layout.

How it works. On /preferences page under 'Workspace' section, toggle buttons 'Left rail' vs 'Top bar' persist choice via useDockRailPosition hook. Selection updates immediately on click (no save needed, stored locally).

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/pages/Preferences.tsx:27-30
src/pages/Preferences.tsx:139-165
src/hooks/useDockRailPosition

Share usage data (analytics consent) ▶ 00:13

As a translator, I want to control whether my app usage events (project creation, exports, AI translations) are sent to the analytics backend, so that I can protect my privacy.

How it works. On /preferences under 'Privacy' section, a switch toggle labeled 'Share usage data' allows user to opt in/out. When disabled, shows warning: 'With analytics disabled, we may not be able to help diagnose problems you encounter.' State persists via useAnalyticsConsent hook.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/pages/Preferences.tsx:107-197
src/hooks/useAnalyticsConsent

Edit translator profile ▶ 00:00

As a translator, I want to add personal context (age, gender, education, experience, location, etc.) to my profile, so that the AI can tailor summaries and responses to my background.

How it works. On /preferences under 'Translator profile' section, text input fields for: responseLanguage, age, gender, educationLevel, religiousBackground, translationExperience, geographicalSetting. Also a textarea for 'otherInfo'. Each field persists immediately via setTranslatorProfile (no explicit save button). Changes are stored device-locally and sent to AI for context. All fields optional.

WhoAny user (personal preference) PermissionsAny authenticated user — scoped to own account
Key files

src/pages/Preferences.tsx:32-104
src/lib/translator-profile (getTranslatorProfile, setTranslatorProfile)

View personal usage statistics ▶ 00:00

As a user, I want to see my audio generation minutes and AI request count (today and 7-day history), so that I can understand my resource consumption.

How it works. On /preferences under 'Usage' section, shows: audio generated today (formatted as 'X min Y s' or 'Z s'), AI requests today (ttsRequests + llmRequests). If data available, also shows 7-day audio history as a bar chart. Fetches from getMyUsage(jwt) on mount. Swallows fetch errors silently (no error state shown). Shows 'Loading…' while fetching, 'No usage recorded yet.' if empty.

WhoAny user (personal preference) PermissionsAny authenticated user — scoped to own account
Key files

src/components/settings/UsageSection.tsx:1-136
src/lib/sync/usage (getMyUsage)

Configure personal AI provider override ▶ 00:00

As an advanced user, I want to set a device-scoped AI provider endpoint that overrides all per-project settings, so that I can test custom LLM endpoints without changing every project.

How it works. On /preferences under 'AI provider (advanced)', collapsible disclosure. When expanded: endpoint URL (required), model (optional), API key (optional, password input) fields. 'Save override' button (enabled if endpoint.trim() is non-empty). On click, persists to localStorage via setUserProviderOverride. Shows 'Saved' confirmation for 2.5s. When active, shows 'Active — your projects use this endpoint on this device.' If override exists, 'Remove override' button appears to clear it via clearUserProviderOverride.

WhoAny user (personal preference) PermissionsAny authenticated user — scoped to own account
Key files

src/components/settings/PersonalProviderSection.tsx:1-160
src/lib/store/user-provider-override

Download/manage local AI models (Whisper, Kokoro, MMS) ▶ 00:00

As a translator working offline, I want to download and manage local AI model weights (transcription, TTS) on my device, so that I can use AI features without internet connectivity.

How it works. On /preferences under 'Local AI models' section, displays three models: Whisper (140 MB), Kokoro (80 MB), MMS (130 MB). For each: status badge (Downloaded, Downloading, Failed, or size + 'not downloaded'), action button (Download / Re-download for ready, Retry for error, Downloading… with spinner). Progress bar during download showing % complete. On download success, model cache is populated (shared across all projects). Users can 'Clear' model to free disk (also clears stored consent flag). Error message if download fails.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings/LocalModelsSection.tsx:1-199
src/lib/audio/prefetch (prefetchAiModels, useModelStatus)

Edit project name ▶ 00:00

As a translator, I want to rename my project, so that the name reflects the translation work's current status or purpose.

How it works. On /project/:id/settings, 'Project Info' section, text input id='pname' pre-filled with current name. User edits → isDirty flag triggers 'Save changes' button. On click, handleSave() writes to IDB via updateProject. On success, baseline is re-seeded and page navigates to /project/:id. Unsaved changes trigger beforeunload warning.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:244-252
src/components/ProjectSettings.tsx:469-623
src/components/ProjectSettings.tsx:780-809

Edit project source and target languages ▶ 00:00

As a project manager, I want to configure the source and target languages for a project, so that the AI and validators know what language pair they're working with.

How it works. On /project/:id/settings, 'Project Info' section, two text inputs for sourceLanguage and targetLanguage. Both are server-synced (shared field, requires Maintainer 600+). Disabled if canEditShared=false (offline or insufficient role). Shows tooltip 'Maintainer or higher can edit shared settings.' Changes queued in local form state; on Save, fires patchShared with {sourceLanguage, targetLanguage}. On 409 conflict, surfaces error and revert flow.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:794-806
src/components/ProjectSettings.tsx:519-521

Set username/author name ▶ 00:00

As a translator, I want to set a username that appears as the author in translation history, so that others can see who made each edit.

How it works. On /project/:id/settings, 'User' section, text input id='un' for username (defaults to 'local'). User edits value. On Save, written to IDB (local only, no server sync). Placeholder text shows 'local'. Help text: 'Used as author name in translation history.'

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:246-248
src/components/ProjectSettings.tsx:812-821

Configure AI system prompt ▶ 00:06

As a project manager, I want to provide custom instructions to the AI (e.g., 'Translate this Bible text in a formal register'), so that AI completions match the translation project's style and tone.

How it works. On /project/:id/settings, 'AI Instructions' section, textarea id='sp' showing DEFAULT_SYSTEM_PROMPT as placeholder. User can edit. Server-synced (requires Maintainer 600+). Disabled if canEditShared=false. Supports placeholders {sourceLanguage} and {targetLanguage}. On Save, fires patchShared with {systemPrompt}. Help text explains purpose.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:255
src/components/ProjectSettings.tsx:522
src/components/ProjectSettings.tsx:823-847
src/lib/completion/completion-service (DEFAULT_SYSTEM_PROMPT)

Set reference example count (top_k) ▶ 00:00

As a project manager, I want to control how many reference examples the AI retrieves per translation, so that I can balance quality vs. latency.

How it works. On /project/:id/settings, 'AI Instructions' section, number input id='top-k' (min=1, max=20, default=5). Clamped on input change. Help text: 'How many reference examples the AI retrieves per translation (1–20). Default: 5.' On Save, fires patchShared with {top_k: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:257
src/components/ProjectSettings.tsx:482
src/components/ProjectSettings.tsx:849-863

Set context window size ▶ 00:00

As a project manager, I want to choose how much surrounding passage context the AI sees (tight, paragraph, chapter), so that AI completions reflect the right semantic scope.

How it works. On /project/:id/settings, 'AI Instructions' section, select dropdown id='context-size' with options: 'Small — tight window' (small), 'Medium — paragraph (default)' (medium), 'Large — chapter' (large). On change, updates form state. On Save, fires patchShared with {contextSize: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:258
src/components/ProjectSettings.tsx:483
src/components/ProjectSettings.tsx:865-890

Set assistant chat language ▶ 00:00

As a translator, I want to specify the language the AI assistant uses in chat responses, so that responses are in my preferred language (independent of project language or UI locale).

How it works. On /project/:id/settings, 'AI Instructions' section, text input id='main-chat-language' (e.g., 'English', 'Français', 'Español'). Placeholder shows examples. On Save, fires patchShared with {main_chat_language: value || undefined}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:261
src/components/ProjectSettings.tsx:486
src/components/ProjectSettings.tsx:892-903

Toggle validated-examples-only mode ▶ 00:00

As a project manager, I want to restrict AI reference examples to only human-validated cells, so that AI completions are based on high-confidence translations.

How it works. On /project/:id/settings, 'AI Instructions' section, checkbox id='validated-only' with label 'Validated examples only'. When checked, only validated cells are used as examples; unvalidated results excluded. On Save, fires patchShared with {useOnlyValidatedExamples: checked}.

WhoReviewer / translation consultant PermissionsValidate/unvalidate: reviewer(300)
Key files

src/components/ProjectSettings.tsx:259
src/components/ProjectSettings.tsx:484
src/components/ProjectSettings.tsx:905-918

Choose reference example format ▶ 00:00

As a project manager, I want to choose whether AI examples show both source and target or target-only, so that I can control the AI's conditioning data.

How it works. On /project/:id/settings, 'AI Instructions' section, select id='few-shot-example-format' with options: 'Source + target (default)' or 'Target only'. Target-only is useful when source alignment is unavailable or undesirable; model is told these are reference translations to imitate. On Save, fires patchShared with {fewShotExampleFormat: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:260
src/components/ProjectSettings.tsx:485
src/components/ProjectSettings.tsx:920-943

Configure preceding target cells for drafting ▶ 00:06

As a project manager, I want to set how many preceding committed target cells are included as draft context, so that the AI's paragraph drafting reflects local discourse coherence.

How it works. On /project/:id/settings, 'Draft Context' section, number input id='preceding-target-cells' (min=0, max=10, default from DEFAULT_DRAFT_CONTEXT). Clamped on input. Help text: 'How many immediately preceding committed target cells to include as discourse left-context when drafting. 0 disables preceding-context. Default: [value].' On Save, fires patchShared with {draftContext: {precedingTargetCells: value}}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:274
src/components/ProjectSettings.tsx:534-536
src/components/ProjectSettings.tsx:949-978

Switch AI provider (Frontier vs. Custom endpoint) ▶ 00:06

As a translator, I want to switch between Frontier's managed AI and my own OpenAI-compatible endpoint, so that I can choose between convenience and control.

How it works. On /project/:id/settings, 'Advanced LLM settings' details/summary section, radio group for 'Frontier' (default, no config needed) or 'Custom endpoint' (requires URL, optional key/model). When Frontier selected, optional model-override input. When Custom selected, endpoint URL input + Connect button + optional API key field + model selector (if models returned) or manual input. On Save, fires patchShared with {provider: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:248
src/components/ProjectSettings.tsx:475
src/components/ProjectSettings.tsx:980-1143

Connect to custom AI endpoint and fetch models ▶ 00:00

As a translator, I want to test connectivity to a custom LLM endpoint and discover available models, so that I can verify the endpoint works before committing settings.

How it works. On /project/:id/settings, 'Advanced LLM settings' section, 'Connect' button (enabled if endpoint URL is non-empty). On click: spinner appears, fetchModels(endpoint, apiKey) fires asynchronously. On success: shows CheckCircle badge 'Connected — N model(s)', populates model selector. On error: shows XCircle badge with error message. Retries automatically if endpoint/apiKey changes (debounced).

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:340-362
src/components/ProjectSettings.tsx:1037-1064
src/lib/completion/completion-service (fetchModels)

Set custom AI model ID ▶ 00:00

As a translator, I want to specify which AI model to use (e.g., 'gpt-4o', 'claude-3-sonnet'), so that I can choose the right model for my project's latency and quality needs.

How it works. On /project/:id/settings, 'Advanced LLM settings' section, model selector (if models list available after Connect) or manual text input id='mdl-manual' (if no models available or for custom provider without model discovery). User can select from dropdown or type manually. On Save, fires patchShared with {model: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:252
src/components/ProjectSettings.tsx:478
src/components/ProjectSettings.tsx:1077-1105

Set API key for custom endpoint (project-scoped or user-scoped) ▶ 00:00

As a translator, I want to provide an API key for my custom LLM endpoint, so that the endpoint can authenticate my requests.

How it works. On /project/:id/settings, 'Advanced LLM settings' section, ApiKeyField component renders (only for custom provider). Two-tier input: projectKey (this project only) and userKey (device-scoped, all projects). User can enter in either field. Stored separately; effective key is projectKey || userKey. Help text: 'Sent as Authorization: Bearer <key>. Stored locally in your browser; never uploaded to Frontier.' On Save, projectKey written to project settings, userKey written to localStorage via setUserApiKey.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:250
src/components/ProjectSettings.tsx:279
src/components/ProjectSettings.tsx:338
src/components/ProjectSettings.tsx:477
src/components/ProjectSettings.tsx:1065-1076
src/components/ApiKeyField (component)

Set max tokens for AI completions ▶ 00:00

As a project manager, I want to control the maximum number of tokens the AI generates per completion, so that I can cap response length and latency.

How it works. On /project/:id/settings, 'Advanced LLM settings' section, number input id='mt' for maxTokens. On Save, fires patchShared with {maxTokens: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:253
src/components/ProjectSettings.tsx:479
src/components/ProjectSettings.tsx:1125-1128

Set temperature for AI randomness ▶ 00:00

As a project manager, I want to adjust temperature (0–1) to control how creative/deterministic the AI is, so that translations are more or less varied.

How it works. On /project/:id/settings, 'Advanced LLM settings' section, range input (min=0, max=1, step=0.05) with label showing current value 'Temperature (X)'. On Save, fires patchShared with {temperature: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:254
src/components/ProjectSettings.tsx:480
src/components/ProjectSettings.tsx:1129-1132

Set LLM health penalty ▶ 00:00

As a project manager, I want to penalize AI translations in health calculations, so that I can reduce their weight vs. human translations.

How it works. On /project/:id/settings, 'Advanced LLM settings' section, range input (min=0, max=0.5, step=0.05) with label showing 'LLM Health Penalty (X%)'. Help text: 'LLM translations are penalized by this amount in health calculations. 0% = full trust, 50% = heavy penalty. Default: 10%.' On Save, fires patchShared with {llmHealthPenalty: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:256
src/components/ProjectSettings.tsx:481
src/components/ProjectSettings.tsx:1135-1141

Configure Gemini TTS API key ▶ 00:00

As a project manager, I want to provide a Gemini API key for text-to-speech, so that the AI can synthesize voice audio for the project.

How it works. On /project/:id/settings, 'Voice' section, ApiKeyField component for Gemini API key (separate from completion key). Two-tier: projectKey (project-scoped) and userKey (device-scoped). Help text: 'Used for Gemini-powered text-to-speech. Get a key at aistudio.google.com/apikey. Sent directly to Google; never uploaded to Frontier.' On Save, projectKey written to ttsSettings.apiKey (preserves voices/castAssignments), userKey written to localStorage.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:283
src/components/ProjectSettings.tsx:1154-1162
src/components/ProjectSettings.tsx:512-515

Navigate to Voice Studio from settings ▶ 00:00

As a translator, I want to quickly navigate to the Voice Studio to manage voice library and cast assignments, so that I can configure voice settings for the project.

How it works. On /project/:id/settings, 'Voice' section, button 'Open Voice Studio'. On click, sets localStorage key `codex:editorLens:${id}` to 'audio' (so workspace opens in audio mode) and navigates to /project/:id. Note: requestNavigate checks isDirty and shows discard dialog if needed.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:1167-1174

Set audio media loading strategy ▶ 00:00

As a translator working on a project with many recordings, I want to choose when audio is downloaded (lazy, eager, stream, manual), so that I can manage bandwidth and offline review.

How it works. On /project/:id/settings, 'Audio loading' section, four radio-like button options: 'Lazy' (download on-demand), 'Eager' (pre-download all), 'Stream' (continuous), 'Manual' (user-initiated). Each button shows name and description. On click, updates audioMediaStrategy. On Save, fires patchShared with {audioMediaStrategy: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:273
src/components/ProjectSettings.tsx:492
src/components/ProjectSettings/AudioMediaStrategySection.tsx:1-57

Configure decay/staleness & health settings ▶ 00:00

As a project manager, I want to tune staleness detection and health scoring, so that the system correctly flags cells needing attention based on validated neighbors.

How it works. On /project/:id/settings, collapsible 'Staleness & health' details section. Shows: maxHops input (1–20, default 4) — 'Propagation radius from validated cells'; decayWarnThreshold input (0–1, step 0.01, default from DECAY_DEFAULTS) — 'Staleness above this threshold shows attention marker'. On Save, fires patchShared with {decaySettings: {maxHops, decayWarnThreshold}}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:272
src/components/ProjectSettings.tsx:491
src/components/ProjectSettings/DecaySettingsSection.tsx:1-86

Set validation count threshold (text) ▶ 00:00

As a project manager, I want to specify how many distinct validators a text cell needs to be marked as 'fully validated', so that I can control approval rigor.

How it works. On /project/:id/settings, 'Validation' section, number input id='validation-count' (min=1, max=15, clamped). Help text: 'Cells need this many distinct validators to count as fully validated.' On change, fires onChange with {validationCount: clamp(value)}. On Save, fires patchShared with {validationCount}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:264
src/components/ProjectSettings.tsx:523
src/components/ProjectSettings/ValidationSettingsSection.tsx:95-113

Set validation count threshold (audio) ▶ 00:00

As a project manager, I want to set a separate validation threshold for audio translations, so that audio approval rigor can differ from text.

How it works. On /project/:id/settings, 'Validation' section, number input id='validation-count-audio' (min=1, max=15). Disabled if: disabled=true (shared role gate) OR !hasAnyAudioData (no audio in project yet). Help text: 'Applies to audio translations' or 'Enabled once audio translations exist.' On Save, fires patchShared with {validationCountAudio}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:265
src/components/ProjectSettings.tsx:524-526
src/components/ProjectSettings/ValidationSettingsSection.tsx:114-136

Set minimum validator role ▶ 00:00

As a project manager, I want to restrict validation votes to users with a minimum role (reviewer, project lead, maintainer), so that I can control who can validate translations.

How it works. On /project/:id/settings, 'Validation' section, select id='validation-role-floor' with options: Reviewer (default), Project Lead, Maintainer. Disabled if shared role gate applies. On change, fires onChange with {validationRoleFloor: value}. On Save, fires patchShared with {validationRoleFloor}. Help text: 'Only users with at least this role can cast a validation vote.' Note: server enforcement deferred (SWARM-TODO).

WhoReviewer / translation consultant PermissionsValidate/unvalidate: reviewer(300)
Key files

src/components/ProjectSettings.tsx:266
src/components/ProjectSettings.tsx:527
src/components/ProjectSettings/ValidationSettingsSection.tsx:138-170

Toggle allow self-validation ▶ 00:00

As a project manager, I want to control whether contributors can validate their own translations, so that I can enforce independent review if desired.

How it works. On /project/:id/settings, 'Validation' section, switch id='allow-self-validation' with label 'Allow self-validation'. When off, a contributor's vote on their own commit is ignored. Disabled if shared role gate applies. On change, fires onChange with {allowSelfValidation: checked}. On Save, fires patchShared with {allowSelfValidation}. Help text explains the behavior. Note: server enforcement deferred (SWARM-TODO).

WhoReviewer / translation consultant PermissionsValidate/unvalidate: reviewer(300)
Key files

src/components/ProjectSettings.tsx:268
src/components/ProjectSettings.tsx:531
src/components/ProjectSettings/ValidationSettingsSection.tsx:172-189

Configure named-user validator allowlist ▶ 00:00

As a project manager, I want to restrict validation votes to specific named users (AND'd with role floor), so that I can require pre-approved validators.

How it works. On /project/:id/settings, 'Validation' section, text input id='validation-named-users' with placeholder 'alice, bob, carol'. Accepts comma-separated usernames; parses and trims on input. Disabled if shared role gate applies. On Save, fires patchShared with {validationNamedUsers: [parsed array]}. Help text: 'Comma-separated usernames. When set, only these users' votes count toward the threshold (AND'd with the role floor). Leave empty to allow any sufficiently-privileged user.' Note: server enforcement + TypeAhead UX improvements deferred (SWARM-TODO).

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:267
src/components/ProjectSettings.tsx:528-530
src/components/ProjectSettings/ValidationSettingsSection.tsx:191-215

Set harmonization minimum role ▶ 00:00

As a project manager, I want to set a minimum role (project lead or maintainer) for running harmonization sweeps, so that I can control who can use this advanced feature.

How it works. On /project/:id/settings, 'Harmonization' section (appears alongside validation), select id='harmonize-min-role' with options: 'Project Lead (default)' or 'Maintainer'. Disabled if shared role gate applies. Help text: 'Only users with at least this role can open a harmonization sweep on this project. The floor cannot be lowered below Project Lead (hard floor per spec).' On Save, fires patchShared with {harmonize_min_role: value}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:270
src/components/ProjectSettings.tsx:532
src/components/ProjectSettings.tsx:1236-1273

Toggle Bible resources availability ▶ 00:00

As a project manager, I want to enable scholarly Bible reference data for translators, so that they can access bibletranslation.org resources in Search and the agent.

How it works. On /project/:id/settings, 'Bible resources' section, switch id='bible-resources-enabled' with label 'Enable Bible resources'. Help text: 'Enable scholarly reference data from bibletranslation.org in Search and the agent.' Disabled if shared role gate applies. On change, fires onChange with {bibleResourcesEnabled: checked}. On Save, fires patchShared with {bibleResourcesEnabled}.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:271
src/components/ProjectSettings.tsx:533
src/components/ProjectSettings.tsx:1314-1340

Navigate to Terminology Library ▶ 00:00

As a project manager, I want to access the Terminology Library to manage approved terms, renderings, and the project glossary, so that I can curate terminology standards.

How it works. On /project/:id/settings, 'Terminology Library' section, button 'Open Terminology Library'. On click, calls requestNavigate(`/project/${id}/terminology`). Note: requestNavigate checks isDirty and shows discard dialog if needed.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:1341-1357

Detach project from upstream source ▶ 00:00

As a project lead, I want to sever the live link to an upstream source project and snapshot current cells, so that the project becomes independent.

How it works. On /project/:id/settings, 'Source link' section (only visible if sourceProjectId is non-null), shows upstream project ID. Destructive action: 'Detach from source' button (only if roleLevel >= 500/project_lead, shows tooltip otherwise). On click, opens confirmation dialog requiring user to type 'DETACH'. On confirm: POST /api/v2/projects/:id/detach-source fires → server emits project.link-source(null) + bursts source.cell.commit snapshots → on success, dialog closes, section removed (onDetached callback refreshes project record). Shows error if 403/4xx.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:772-779
src/components/ProjectSettings/SourceLinkSection.tsx:1-203

Configure auto-sync for Git projects ▶ 00:00

As a translator working with a Git-backed project, I want to enable automatic periodic syncing to the upstream Git repo, so that changes are backed up without manual intervention.

How it works. On /project/:id/settings, 'Git Sync' section (only visible if project.origin.kind === 'git'), shows clone URL and branch. Checkbox id='auto-sync' 'Auto-sync every' [number input for minutes (1–60)] 'minutes (only when there are changes)'. On change, updates form state. On Save, writes syncSettings.autoSync to IDB. Help text: 'Interval is floored at 1 minute. Sync will only push when there are local changes.'

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:262-263
src/components/ProjectSettings.tsx:495-501
src/components/ProjectSettings.tsx:1284-1313

View AI post-edit metrics (NED) ▶ 00:00

As a project manager, I want to see how much humans edit AI-generated drafts (post-edit magnitude), so that I can evaluate AI quality and translator effort.

How it works. On /project/:id/settings, 'AI Post-Edit Magnitude' section (only for cloud projects with metrics data). Shows: overall avg NED (%) + 'effort level' badge (minimal/light/moderate/heavy/complete rewrite) + total edit pair count. Weekly trend bar chart (avg NED per week + edit count pill). By-user table (click row to filter trend chart). NED = normalized Levenshtein distance (0%=accepted unchanged, 100%=completely rewritten). Handles: loading state (skeleton), no-data state, error state, no-cloud state. Refresh button to revalidate.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:209-227
src/components/metrics/PostEditMetricsSection.tsx:1-332
src/lib/metrics/use-post-edit-metrics

Publish project termbase to org (Termbase Sharing) ▶ 00:00

As a project maintainer, I want to publish my project's termbase to the org so other projects can subscribe to it, so that terminology is shared across the organization.

How it works. On /project/:id/settings, 'Term Base Sharing' section (HIDDEN as of 2026-06-11; requires SHOW_TERMBASE_SHARING_IN_SETTINGS=true). Switch to toggle publish state. On enable: POST /publish → sets published=true. On disable: POST /unpublish → sets published=false. Disabled if roleLevel < 600 (maintainer) or !orgId (not org-owned). Shows list of other published termbases in org available to subscribe to.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:78
src/components/ProjectSettings/TermbaseSharingSection.tsx:59-156

Subscribe to published termbases in org ▶ 00:00

As a project maintainer, I want to subscribe to other published termbases in my org and reorder their priority, so that concepts from those termbases are enforced in my project.

How it works. On /project/:id/settings, 'Term Base Sharing' section (HIDDEN), list of subscribed termbases with drag-to-reorder (index 0 = highest precedence) and unsubscribe buttons. Unsubscribe fires POST /unsubscribe. Drag reorders locally (optimistic) then fires POST /reorder. Available termbases (published in org, not this project, not already subscribed) shown in a separate dropdown to subscribe. Disabled if roleLevel < 600 or !orgId.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings/TermbaseSharingSection.tsx:125-193

Discard unsaved project settings changes ▶ 00:00

As a translator, I want to discard my unsaved changes and exit settings without saving, so that I can abandon accidental edits.

How it works. On /project/:id/settings, if isDirty, clicking 'Close without saving' (or back button with pending nav) opens confirmation dialog 'Discard changes?' with 'Keep editing' / 'Discard' buttons. On Discard: handleDiscardConfirm resets form to baseline via applyBaseline, closes dialog, navigates to target (default /project/:id). On Keep: handleDiscardCancel closes dialog without navigating. Also on browser back/reload with isDirty, beforeunload warning: 'Unsaved changes'.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:408-436
src/components/ProjectSettings.tsx:625-636
src/components/ProjectSettings.tsx:1379-1392

Search and filter settings sections by keyword ▶ 00:00

As a translator in a project with many settings, I want to search settings by keyword or section name, so that I can quickly find the setting I need.

How it works. On /project/:id/settings, SettingsNav component renders search input with Search icon. User types query → filters ALL_SECTIONS.visible sections to those matching label or keywords (case-insensitive substring match). Filtered sections displayed in nav sidebar (desktop) or modal search (mobile). Clicking a section link scrolls to its anchor id. Mobile nav shows search inline when rail is hidden (lg:hidden breakpoint).

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:662-674
src/components/ProjectSettings/SettingsNav.tsx:1-88

Navigate to settings section by clicking nav link ▶ 00:00

As a translator, I want to click a section in the settings sidebar to jump directly to it, so that I can navigate quickly through a long settings page.

How it works. On /project/:id/settings, SettingsNav buttons each correspond to a visibleSection. On click, calls scrollTo(id) which smoothly scrolls to document.getElementById(id), offsetting by header height (64px). Scroll spy hook useScrollSpy tracks which section is nearest the top (OFFSET=80px) and highlights corresponding nav button.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings/SettingsNav.tsx:24-87

Detect and surface concurrent settings edits (conflict detection) ▶ 00:00

As a translator, I want to be notified if someone else edits shared settings while I'm editing, so that I don't accidentally overwrite their changes.

How it works. On /project/:id/settings, patchShared can return kind='conflict' if server returns 409. On conflict: show alert 'Settings changed elsewhere — refresh to reapply.' with dismissible ✕. User must refresh page to re-sync. conflictBy tracks who changed settings; a toast notification appears: 'Synced settings update from [username].' Can be dismissed. Conflict state stored via useProjectSettings hook.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:185-190
src/components/ProjectSettings.tsx:540-544
src/components/ProjectSettings.tsx:756-771
src/components/ProjectSettings.tsx:1394-1401

Persist changes via deferred save orchestration ▶ 00:00

As a translator, I want my settings to be saved correctly even if some fields are local and others sync to the server, so that I can have a consistent experience.

How it works. On /project/:id/settings, clicking 'Save changes' triggers handleSave: (1) local IDB writes first (cheap, idempotent), (2) then shared/server PATCH (only one that can 409). If PATCH fails, local writes remain (not source of truth for shared fields anyway). On success, re-baseline from form state (reading IDB was racy due to fire-and-forget). Unsaved-changes bar shows 'Unsaved changes' until save completes; saveError shown on failure.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:469-623
src/components/ProjectSettings.tsx:681-729

Show settings modification metadata ▶ 00:00

As a project member, I want to see who last edited the shared project settings and when, so that I know the currency and owner of the settings.

How it works. On /project/:id/settings, 'Project Info' section, beneath the project name input, shows optional metadata line (only if sharedUpdatedBy, sharedUpdatedAt, and sharedVersion > 0): 'Last edited by [username] · [date]' (formatted as 'Mon DD, YYYY').

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:788-793

Show 'More save options' menu ▶ 00:00

As a translator, I want a quick way to close the settings without saving, so that I can abandon edits and return to the editor.

How it works. On /project/:id/settings header, when isDirty, 'Save changes' button appears with dropdown menu (ChevronDown). Menu shows 'Close without saving' option. On click, opens discard-changes dialog.

WhoOwner / org admin (org+project settings) · any user (personal prefs) PermissionsPersonal prefs (theme, shortcuts, profile): any user; project/org settings: maintainer/owner(≥600)
Key files

src/components/ProjectSettings.tsx:696-718