fix: name the switches in Admin Settings rows (WCAG 4.1.2, 1.3.1) (#27510)

`common/Switch.svelte` already accepts `id`, `ariaLabel` and `ariaLabelledbyId`, but **not one of the 148 `<Switch>` instances under `src/lib/components/admin/` passes any of them**.

`admin/Settings/AdminSettingRow.svelte` renders the row label as a plain `<div>` and the control in a **sibling** slot, so there is nothing tying them together. bits-ui renders the switch as a `<button role="switch">` whose subtree is a text free thumb, so it has no accessible name from any source.

A screen reader user working through Admin Settings hears a long run of "switch, on" and "switch, off" with no indication of what any of them controls.

Breaks WCAG 4.1.2 Name, Role, Value (Level A) and 1.3.1 Info and Relationships (Level A).

Fix: `AdminSettingRow` mints a per instance id, puts it on the label element, and hands it to the default slot, so each row's switch can point at the label that is already rendered next to it. This is the pattern `chat/Settings/Interface.svelte` already uses by hand in 45 places, hoisted into the shared row component so call sites stop hand authoring ids.

`aria-labelledby` rather than a wrapping `<label>`: per HTML-AAM a `<button>` takes its name from `aria-labelledby`, then `aria-label`, then its own subtree, never from an associated `<label>`. `chat/Settings/Subagents.svelte` already wraps two switches in a `<label>` and they are still unnamed, which is the same trap. Using the existing label element also guarantees the accessible name is byte identical to the visible text, which keeps voice control working.

The `description` paragraph deliberately sits outside the referenced element, so verbose help text is not pulled into the name.

Scope: this covers the **72** switches that live inside an `AdminSettingRow`, which is every switch that flows through the shared row component. There are no rows containing more than one switch, so nothing is silently skipped.

The remaining 76 admin switches are not in this component and are not touched. 64 of them are in `admin/Users/Groups/Permissions.svelte`, which hand rolls its own row markup, and the other 12 are per entity toggles in lists and dropdowns where the label is a dynamic row name. `Permissions.svelte` is the worst remaining case, 64 toggles with near identical adjacent labels, and it needs either its own labelling pass or a conversion to `AdminSettingRow` that changes its visual styling. Either way that is not an accessibility only diff and belongs in its own PR.

All 12 touched files compile with the Svelte compiler with no new warnings and are Prettier clean.

Severity: Serious. Admin Settings is unusable with a screen reader.

### Contributor License Agreement

<!--
🚨 DO NOT DELETE THE TEXT BELOW 🚨
Keep the "Contributor License Agreement" confirmation text intact.
Deleting it will trigger the CLA-Bot to INVALIDATE your PR.

Your PR will NOT be reviewed or merged until you check the box below confirming that you have read and agree to the terms of the CLA.
-->

- [x] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.

> [!NOTE]
> Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.
This commit is contained in:
Classic298
2026-07-26 23:35:34 +02:00
committed by GitHub
parent 94b1b7e6b6
commit 7effaa05d1
12 changed files with 213 additions and 72 deletions
@@ -1,17 +1,21 @@
<script lang="ts">
import { v4 as uuidv4 } from 'uuid';
export let label = '';
export let description = '';
export let className = '';
export let labelClassName = '';
const labelId = `admin-setting-${uuidv4()}`;
</script>
<div class="flex items-center justify-between gap-4 {className}">
<div class="min-w-0 text-xs text-gray-600 dark:text-gray-400 {labelClassName}">
<div id={labelId} class="min-w-0 text-xs text-gray-600 dark:text-gray-400 {labelClassName}">
<slot name="label">{label}</slot>
</div>
<div class="shrink-0">
<slot />
<slot {labelId} />
</div>
</div>
@@ -415,8 +415,9 @@
description={$i18n.t(
'Use /v1/chat/completions endpoint instead of /v1/audio/transcriptions for potentially better accuracy.'
)}
let:labelId
>
<Switch bind:state={STT_MISTRAL_USE_CHAT_COMPLETIONS} />
<Switch bind:state={STT_MISTRAL_USE_CHAT_COMPLETIONS} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{:else if STT_ENGINE === ''}
<AdminSettingField
@@ -168,23 +168,29 @@
<AdminSettingRow
label={$i18n.t('New Sign Ups')}
description={$i18n.t('Allow new users to create accounts.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_SIGNUP} />
<Switch bind:state={adminConfig.ENABLE_SIGNUP} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('API Keys')}
description={$i18n.t('Allow users to create API keys for programmatic access.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_API_KEYS} />
<Switch bind:state={adminConfig.ENABLE_API_KEYS} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if adminConfig?.ENABLE_API_KEYS}
<AdminSettingRow
label={$i18n.t('API Key Endpoint Restrictions')}
description={$i18n.t('Limit API keys to configured endpoints.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS} />
<Switch
bind:state={adminConfig.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
{#if adminConfig?.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS}
@@ -238,8 +244,9 @@
<AdminSettingRow
label={$i18n.t('Admin Details')}
description={$i18n.t('Show admin contact details while an account waits for approval.')}
let:labelId
>
<Switch bind:state={adminConfig.SHOW_ADMIN_DETAILS} />
<Switch bind:state={adminConfig.SHOW_ADMIN_DETAILS} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if adminConfig.SHOW_ADMIN_DETAILS}
@@ -288,8 +295,9 @@
<AdminSettingRow
label={$i18n.t('LDAP')}
description={$i18n.t('Allow users to authenticate with an LDAP directory.')}
let:labelId
>
<Switch bind:state={ENABLE_LDAP} />
<Switch bind:state={ENABLE_LDAP} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if ENABLE_LDAP}
@@ -441,8 +449,9 @@
<AdminSettingRow
label={$i18n.t('TLS')}
description={$i18n.t('Use TLS when connecting to the LDAP server.')}
let:labelId
>
<Switch bind:state={LDAP_SERVER.use_tls} />
<Switch bind:state={LDAP_SERVER.use_tls} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if LDAP_SERVER.use_tls}
@@ -460,8 +469,9 @@
<AdminSettingRow
label={$i18n.t('Validate Certificate')}
description={$i18n.t('Verify the LDAP server certificate when TLS is enabled.')}
let:labelId
>
<Switch bind:state={LDAP_SERVER.validate_cert} />
<Switch bind:state={LDAP_SERVER.validate_cert} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingField
@@ -481,16 +491,18 @@
<AdminSettingRow
label={$i18n.t('Group Mapping')}
description={$i18n.t('Map LDAP groups to Open WebUI groups.')}
let:labelId
>
<Switch bind:state={LDAP_SERVER.enable_group_management} />
<Switch bind:state={LDAP_SERVER.enable_group_management} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if LDAP_SERVER.enable_group_management}
<AdminSettingRow
label={$i18n.t('Auto-Create Groups')}
description={$i18n.t('Create missing groups from LDAP groups.')}
let:labelId
>
<Switch bind:state={LDAP_SERVER.enable_group_creation} />
<Switch bind:state={LDAP_SERVER.enable_group_creation} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingField
@@ -631,22 +643,28 @@
<AdminSettingRow
label={$i18n.t('OAuth Signup')}
description={$i18n.t('Allow users to create accounts through OAuth.')}
let:labelId
>
<Switch bind:state={oauthConfig.ENABLE_OAUTH_SIGNUP} />
<Switch bind:state={oauthConfig.ENABLE_OAUTH_SIGNUP} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Merge Accounts by Email')}
description={$i18n.t('Link OAuth sign-ins to existing accounts with the same email.')}
let:labelId
>
<Switch bind:state={oauthConfig.OAUTH_MERGE_ACCOUNTS_BY_EMAIL} />
<Switch
bind:state={oauthConfig.OAUTH_MERGE_ACCOUNTS_BY_EMAIL}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Auto Redirect')}
description={$i18n.t('Send users directly to the OAuth provider from the sign-in page.')}
let:labelId
>
<Switch bind:state={oauthConfig.OAUTH_AUTO_REDIRECT} />
<Switch bind:state={oauthConfig.OAUTH_AUTO_REDIRECT} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingField
@@ -663,8 +681,12 @@
<AdminSettingRow
label={$i18n.t('Role Mapping')}
description={$i18n.t('Map OAuth claims to Open WebUI roles.')}
let:labelId
>
<Switch bind:state={oauthConfig.ENABLE_OAUTH_ROLE_MANAGEMENT} />
<Switch
bind:state={oauthConfig.ENABLE_OAUTH_ROLE_MANAGEMENT}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
{#if oauthConfig.ENABLE_OAUTH_ROLE_MANAGEMENT}
@@ -707,16 +729,24 @@
<AdminSettingRow
label={$i18n.t('Group Mapping')}
description={$i18n.t('Map OAuth claims to Open WebUI groups.')}
let:labelId
>
<Switch bind:state={oauthConfig.ENABLE_OAUTH_GROUP_MANAGEMENT} />
<Switch
bind:state={oauthConfig.ENABLE_OAUTH_GROUP_MANAGEMENT}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
{#if oauthConfig.ENABLE_OAUTH_GROUP_MANAGEMENT}
<AdminSettingRow
label={$i18n.t('Auto-Create Groups')}
description={$i18n.t('Create missing groups from OAuth claims.')}
let:labelId
>
<Switch bind:state={oauthConfig.ENABLE_OAUTH_GROUP_CREATION} />
<Switch
bind:state={oauthConfig.ENABLE_OAUTH_GROUP_CREATION}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
@@ -747,22 +777,28 @@
<AdminSettingRow
label={$i18n.t('Update Email')}
description={$i18n.t('Refresh the account email from OAuth on sign-in.')}
let:labelId
>
<Switch bind:state={oauthConfig.OAUTH_UPDATE_EMAIL_ON_LOGIN} />
<Switch bind:state={oauthConfig.OAUTH_UPDATE_EMAIL_ON_LOGIN} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Update Name')}
description={$i18n.t('Refresh the account name from OAuth on sign-in.')}
let:labelId
>
<Switch bind:state={oauthConfig.OAUTH_UPDATE_NAME_ON_LOGIN} />
<Switch bind:state={oauthConfig.OAUTH_UPDATE_NAME_ON_LOGIN} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Update Picture')}
description={$i18n.t('Refresh the profile picture from OAuth on sign-in.')}
let:labelId
>
<Switch bind:state={oauthConfig.OAUTH_UPDATE_PICTURE_ON_LOGIN} />
<Switch
bind:state={oauthConfig.OAUTH_UPDATE_PICTURE_ON_LOGIN}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
</AdminSettingSection>
{/if}
@@ -53,8 +53,9 @@
<AdminSettingRow
label={$i18n.t('Enable Code Execution')}
description={$i18n.t('Allow models to run generated code and return execution results.')}
let:labelId
>
<Switch bind:state={config.ENABLE_CODE_EXECUTION} />
<Switch bind:state={config.ENABLE_CODE_EXECUTION} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if config.ENABLE_CODE_EXECUTION}
@@ -153,8 +154,9 @@
<AdminSettingRow
label={$i18n.t('Enable Code Interpreter')}
description={$i18n.t('Allow models to use the code interpreter tool during chats.')}
let:labelId
>
<Switch bind:state={config.ENABLE_CODE_INTERPRETER} />
<Switch bind:state={config.ENABLE_CODE_INTERPRETER} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if config.ENABLE_CODE_INTERPRETER}
@@ -223,12 +223,13 @@
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if ENABLE_OPENAI_API !== null && ENABLE_OLLAMA_API !== null && connectionsConfig !== null}
<AdminSettingSection first>
<AdminSettingRow label={$i18n.t('OpenAI API')}>
<AdminSettingRow label={$i18n.t('OpenAI API')} let:labelId>
<Switch
bind:state={ENABLE_OPENAI_API}
on:change={async () => {
updateOpenAIHandler();
}}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
@@ -281,12 +282,13 @@
</div>
{/if}
<AdminSettingRow label={$i18n.t('Ollama API')}>
<AdminSettingRow label={$i18n.t('Ollama API')} let:labelId>
<Switch
bind:state={ENABLE_OLLAMA_API}
on:change={async () => {
updateOllamaHandler();
}}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
@@ -352,12 +354,14 @@
description={$i18n.t(
'Direct Connections allow users to connect to their own OpenAI compatible API endpoints.'
)}
let:labelId
>
<Switch
bind:state={connectionsConfig.ENABLE_DIRECT_CONNECTIONS}
on:change={async () => {
updateConnectionsHandler();
}}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
@@ -366,12 +370,14 @@
description={$i18n.t(
'Base Model List Cache speeds up access by fetching base models only at startup or on settings save—faster, but may not show recent base model changes.'
)}
let:labelId
>
<Switch
bind:state={connectionsConfig.ENABLE_BASE_MODELS_CACHE}
on:change={async () => {
updateConnectionsHandler();
}}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
</AdminSettingSection>
@@ -408,8 +408,9 @@
<AdminSettingRow
label={$i18n.t('PDF Extract Images (OCR)')}
description={$i18n.t('Extract images from PDFs so OCR can process image-only pages.')}
let:labelId
>
<Switch bind:state={RAGConfig.PDF_EXTRACT_IMAGES} />
<Switch bind:state={RAGConfig.PDF_EXTRACT_IMAGES} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
@@ -471,44 +472,57 @@
description={$i18n.t(
'Use an LLM to improve tables, forms, math, and layout detection.'
)}
let:labelId
>
<Switch bind:state={RAGConfig.DATALAB_MARKER_USE_LLM} />
<Switch bind:state={RAGConfig.DATALAB_MARKER_USE_LLM} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Skip Cache')}
description={$i18n.t('Skip cached Marker results and rerun inference.')}
let:labelId
>
<Switch bind:state={RAGConfig.DATALAB_MARKER_SKIP_CACHE} />
<Switch bind:state={RAGConfig.DATALAB_MARKER_SKIP_CACHE} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Force OCR')}
description={$i18n.t('Run OCR on all PDF pages, even pages with embedded text.')}
let:labelId
>
<Switch bind:state={RAGConfig.DATALAB_MARKER_FORCE_OCR} />
<Switch bind:state={RAGConfig.DATALAB_MARKER_FORCE_OCR} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Paginate')}
description={$i18n.t('Separate output by page with page markers.')}
let:labelId
>
<Switch bind:state={RAGConfig.DATALAB_MARKER_PAGINATE} />
<Switch bind:state={RAGConfig.DATALAB_MARKER_PAGINATE} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Strip Existing OCR')}
description={$i18n.t('Remove existing OCR text and rerun OCR when Force OCR is off.')}
let:labelId
>
<Switch bind:state={RAGConfig.DATALAB_MARKER_STRIP_EXISTING_OCR} />
<Switch
bind:state={RAGConfig.DATALAB_MARKER_STRIP_EXISTING_OCR}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Disable Image Extraction')}
description={$i18n.t('Do not extract images from PDFs during Marker processing.')}
let:labelId
>
<Switch bind:state={RAGConfig.DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION} />
<Switch
bind:state={RAGConfig.DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Format Lines')}
description={$i18n.t('Format lines to detect inline math and styles.')}
let:labelId
>
<Switch bind:state={RAGConfig.DATALAB_MARKER_FORMAT_LINES} />
<Switch bind:state={RAGConfig.DATALAB_MARKER_FORMAT_LINES} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Output Format')}
@@ -695,8 +709,9 @@
<AdminSettingRow
label={$i18n.t('Use Base64')}
description={$i18n.t('Send PDFs as base64 data URLs instead of uploading first.')}
let:labelId
>
<Switch bind:state={RAGConfig.MISTRAL_OCR_USE_BASE64} />
<Switch bind:state={RAGConfig.MISTRAL_OCR_USE_BASE64} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'paddleocr_vl'}
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
@@ -815,8 +830,12 @@
description={RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL
? $i18n.t('Inject the entire content as context for comprehensive processing.')
: $i18n.t('Use segmented retrieval for focused and relevant context.')}
let:labelId
>
<Switch bind:state={RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL} />
<Switch
bind:state={RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
{#if !RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
@@ -853,8 +872,12 @@
description={$i18n.t(
'Split documents by markdown headers before character or token splitting.'
)}
let:labelId
>
<Switch bind:state={RAGConfig.ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER} />
<Switch
bind:state={RAGConfig.ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
@@ -1094,8 +1117,9 @@
<AdminSettingRow
label={$i18n.t('Async Embedding Processing')}
description={$i18n.t('Run embedding tasks concurrently to speed up processing.')}
let:labelId
>
<Switch bind:state={ENABLE_ASYNC_EMBEDDING} />
<Switch bind:state={ENABLE_ASYNC_EMBEDDING} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Embedding Concurrent Requests')}
@@ -1122,16 +1146,18 @@
description={RAGConfig.RAG_FULL_CONTEXT
? $i18n.t('Inject entire documents as context for comprehensive processing.')
: $i18n.t('Use segmented retrieval for focused context.')}
let:labelId
>
<Switch bind:state={RAGConfig.RAG_FULL_CONTEXT} />
<Switch bind:state={RAGConfig.RAG_FULL_CONTEXT} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if !RAGConfig.RAG_FULL_CONTEXT}
<AdminSettingRow
label={$i18n.t('Hybrid Search')}
description={$i18n.t('Combine semantic and keyword retrieval.')}
let:labelId
>
<Switch bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH} />
<Switch bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
@@ -1140,8 +1166,12 @@
description={$i18n.t(
'Add filenames, titles, sections, and snippets to improve lexical recall.'
)}
let:labelId
>
<Switch bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH_ENRICHED_TEXTS} />
<Switch
bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH_ENRICHED_TEXTS}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<AdminSettingRow
@@ -1428,14 +1458,19 @@
<AdminSettingRow
label={$i18n.t('Google Drive')}
description={$i18n.t('Allow Google Drive as a document source.')}
let:labelId
>
<Switch bind:state={RAGConfig.ENABLE_GOOGLE_DRIVE_INTEGRATION} />
<Switch
bind:state={RAGConfig.ENABLE_GOOGLE_DRIVE_INTEGRATION}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('OneDrive')}
description={$i18n.t('Allow OneDrive as a document source.')}
let:labelId
>
<Switch bind:state={RAGConfig.ENABLE_ONEDRIVE_INTEGRATION} />
<Switch bind:state={RAGConfig.ENABLE_ONEDRIVE_INTEGRATION} ariaLabelledbyId={labelId} />
</AdminSettingRow>
</AdminSettingSection>
@@ -112,9 +112,13 @@
<AdminSettingRow
label={$i18n.t('Arena Models')}
description={$i18n.t('Message rating should be enabled to use this feature')}
let:labelId
>
<Tooltip content={$i18n.t(`Message rating should be enabled to use this feature`)}>
<Switch bind:state={evaluationConfig.ENABLE_EVALUATION_ARENA_MODELS} />
<Switch
bind:state={evaluationConfig.ENABLE_EVALUATION_ARENA_MODELS}
ariaLabelledbyId={labelId}
/>
</Tooltip>
</AdminSettingRow>
</AdminSettingSection>
@@ -216,20 +216,23 @@
<AdminSettingRow
label={$i18n.t('Community Sharing')}
description={$i18n.t('Allow users to share chats with the Open WebUI community.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_COMMUNITY_SHARING} />
<Switch bind:state={adminConfig.ENABLE_COMMUNITY_SHARING} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Message Rating')}
description={$i18n.t('Let users rate assistant responses.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_MESSAGE_RATING} />
<Switch bind:state={adminConfig.ENABLE_MESSAGE_RATING} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Folders')}
description={$i18n.t('Allow users to organize chats into folders.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_FOLDERS} />
<Switch bind:state={adminConfig.ENABLE_FOLDERS} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if adminConfig.ENABLE_FOLDERS}
@@ -250,53 +253,64 @@
<AdminSettingRow
label={$i18n.t('Memories')}
description={$i18n.t('Allow users to save memories for more personalized responses.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_MEMORIES} />
<Switch bind:state={adminConfig.ENABLE_MEMORIES} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if adminConfig.ENABLE_MEMORIES}
<AdminSettingRow
label={$i18n.t('Memory System Context')}
description={$i18n.t('Include saved memories in the system context.')}
labelClassName="text-gray-500 dark:text-gray-500"
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_MEMORY_SYSTEM_CONTEXT} />
<Switch
bind:state={adminConfig.ENABLE_MEMORY_SYSTEM_CONTEXT}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
{/if}
<AdminSettingRow
label={$i18n.t('Notes')}
description={$i18n.t('Allow users to create and manage notes.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_NOTES} />
<Switch bind:state={adminConfig.ENABLE_NOTES} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Channels')}
description={$i18n.t('Allow users to use channels for shared conversations.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_CHANNELS} />
<Switch bind:state={adminConfig.ENABLE_CHANNELS} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Calendar')}
description={$i18n.t('Allow users to access calendar features.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_CALENDAR} />
<Switch bind:state={adminConfig.ENABLE_CALENDAR} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Automations')}
description={$i18n.t('Allow users to create and run automations.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_AUTOMATIONS} />
<Switch bind:state={adminConfig.ENABLE_AUTOMATIONS} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('User Webhooks')}
description={$i18n.t('Allow users to configure webhooks from their account.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_USER_WEBHOOKS} />
<Switch bind:state={adminConfig.ENABLE_USER_WEBHOOKS} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('User Status')}
description={$i18n.t('Show user status information in the app.')}
let:labelId
>
<Switch bind:state={adminConfig.ENABLE_USER_STATUS} />
<Switch bind:state={adminConfig.ENABLE_USER_STATUS} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingField
@@ -307,8 +307,9 @@
<AdminSettingRow
label={$i18n.t('Image Generation')}
description={$i18n.t('Allow users to generate images from prompts.')}
let:labelId
>
<Switch bind:state={config.ENABLE_IMAGE_GENERATION} />
<Switch bind:state={config.ENABLE_IMAGE_GENERATION} ariaLabelledbyId={labelId} />
</AdminSettingRow>
</AdminSettingSection>
@@ -369,8 +370,12 @@
<AdminSettingRow
label={$i18n.t('Image Prompt Generation')}
description={$i18n.t('Generate an image prompt before sending the request.')}
let:labelId
>
<Switch bind:state={config.ENABLE_IMAGE_PROMPT_GENERATION} />
<Switch
bind:state={config.ENABLE_IMAGE_PROMPT_GENERATION}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
{/if}
@@ -708,8 +713,9 @@
<AdminSettingRow
label={$i18n.t('Image Edit')}
description={$i18n.t('Allow users to edit existing images.')}
let:labelId
>
<Switch bind:state={config.ENABLE_IMAGE_EDIT} />
<Switch bind:state={config.ENABLE_IMAGE_EDIT} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
@@ -239,8 +239,9 @@
description={$i18n.t(
'Summarize older chat history when the conversation context grows large.'
)}
let:labelId
>
<Switch bind:state={chatConfig.ENABLE_CONTEXT_COMPACTION} />
<Switch bind:state={chatConfig.ENABLE_CONTEXT_COMPACTION} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if chatConfig.ENABLE_CONTEXT_COMPACTION}
@@ -319,8 +320,9 @@
<AdminSettingRow
label={$i18n.t('Title Generation')}
description={$i18n.t('Allow automatic names for new chats.')}
let:labelId
>
<Switch bind:state={taskConfig.ENABLE_TITLE_GENERATION} />
<Switch bind:state={taskConfig.ENABLE_TITLE_GENERATION} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if taskConfig.ENABLE_TITLE_GENERATION}
@@ -341,8 +343,9 @@
<AdminSettingRow
label={$i18n.t('Voice Mode Prompt')}
description={$i18n.t('Apply voice-specific instructions while voice mode is active.')}
let:labelId
>
<Switch bind:state={taskConfig.ENABLE_VOICE_MODE_PROMPT} />
<Switch bind:state={taskConfig.ENABLE_VOICE_MODE_PROMPT} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if taskConfig.ENABLE_VOICE_MODE_PROMPT}
@@ -363,8 +366,9 @@
<AdminSettingRow
label={$i18n.t('Follow Up Generation')}
description={$i18n.t('Show suggested next questions after assistant responses.')}
let:labelId
>
<Switch bind:state={taskConfig.ENABLE_FOLLOW_UP_GENERATION} />
<Switch bind:state={taskConfig.ENABLE_FOLLOW_UP_GENERATION} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if taskConfig.ENABLE_FOLLOW_UP_GENERATION}
@@ -385,8 +389,9 @@
<AdminSettingRow
label={$i18n.t('Tags Generation')}
description={$i18n.t('Create chat tags from conversation content.')}
let:labelId
>
<Switch bind:state={taskConfig.ENABLE_TAGS_GENERATION} />
<Switch bind:state={taskConfig.ENABLE_TAGS_GENERATION} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if taskConfig.ENABLE_TAGS_GENERATION}
@@ -407,15 +412,23 @@
<AdminSettingRow
label={$i18n.t('Retrieval Query Generation')}
description={$i18n.t('Rewrite user requests for knowledge retrieval.')}
let:labelId
>
<Switch bind:state={taskConfig.ENABLE_RETRIEVAL_QUERY_GENERATION} />
<Switch
bind:state={taskConfig.ENABLE_RETRIEVAL_QUERY_GENERATION}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Web Search Query Generation')}
description={$i18n.t('Rewrite user requests into web-search queries.')}
let:labelId
>
<Switch bind:state={taskConfig.ENABLE_SEARCH_QUERY_GENERATION} />
<Switch
bind:state={taskConfig.ENABLE_SEARCH_QUERY_GENERATION}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<AdminSettingField
@@ -432,8 +445,12 @@
<AdminSettingRow
label={$i18n.t('Autocomplete Generation')}
description={$i18n.t('Suggest completions while users type chat messages.')}
let:labelId
>
<Switch bind:state={taskConfig.ENABLE_AUTOCOMPLETE_GENERATION} />
<Switch
bind:state={taskConfig.ENABLE_AUTOCOMPLETE_GENERATION}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
{#if taskConfig.ENABLE_AUTOCOMPLETE_GENERATION}
@@ -422,8 +422,9 @@
<AdminSettingRow
label={valves[property] ? $i18n.t('Enabled') : $i18n.t('Disabled')}
labelClassName="text-gray-400 dark:text-gray-600"
let:labelId
>
<Switch bind:state={valves[property]} />
<Switch bind:state={valves[property]} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{:else}
<input
@@ -166,15 +166,20 @@
<AdminSettingRow
label={$i18n.t('Web Search')}
description={$i18n.t('Allow users to search the web from chats.')}
let:labelId
>
<Switch bind:state={webConfig.ENABLE_WEB_SEARCH} />
<Switch bind:state={webConfig.ENABLE_WEB_SEARCH} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Web Search Confirmation')}
description={$i18n.t('Require users to confirm before using Web Search.')}
let:labelId
>
<Switch bind:state={webConfig.ENABLE_WEB_SEARCH_CONFIRMATION} />
<Switch
bind:state={webConfig.ENABLE_WEB_SEARCH_CONFIRMATION}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
{#if webConfig.ENABLE_WEB_SEARCH_CONFIRMATION}
@@ -1111,15 +1116,20 @@
description={webConfig.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL
? $i18n.t('Inject the entire content as context for comprehensive processing.')
: $i18n.t('Use segmented retrieval for focused and relevant content extraction.')}
let:labelId
>
<Switch bind:state={webConfig.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL} />
<Switch
bind:state={webConfig.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
<AdminSettingRow
label={$i18n.t('Bypass Web Loader')}
description={$i18n.t('Use search results without fetching page contents.')}
let:labelId
>
<Switch bind:state={webConfig.BYPASS_WEB_SEARCH_WEB_LOADER} />
<Switch bind:state={webConfig.BYPASS_WEB_SEARCH_WEB_LOADER} ariaLabelledbyId={labelId} />
</AdminSettingRow>
<AdminSettingRow
@@ -1127,8 +1137,9 @@
description={webConfig.WEB_SEARCH_TRUST_ENV
? $i18n.t('Use proxy environment variables to fetch page contents.')
: $i18n.t('Fetch page contents without proxy environment variables.')}
let:labelId
>
<Switch bind:state={webConfig.WEB_SEARCH_TRUST_ENV} />
<Switch bind:state={webConfig.WEB_SEARCH_TRUST_ENV} ariaLabelledbyId={labelId} />
</AdminSettingRow>
</AdminSettingSection>
@@ -1163,8 +1174,12 @@
<AdminSettingRow
label={$i18n.t('Verify SSL Certificate')}
description={$i18n.t('Validate SSL certificates when fetching web content.')}
let:labelId
>
<Switch bind:state={webConfig.ENABLE_WEB_LOADER_SSL_VERIFICATION} />
<Switch
bind:state={webConfig.ENABLE_WEB_LOADER_SSL_VERIFICATION}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow>
{:else if webConfig.WEB_LOADER_ENGINE === 'playwright'}
<div class="mb-2.5 flex w-full flex-col">