fix: name the group permission switches (WCAG 4.1.2) (#27513)

`admin/Users/Groups/Permissions.svelte` contains **64** `<Switch>` instances and not one of them passes `ariaLabel`, `ariaLabelledbyId` or `id`. bits-ui renders the switch as a `<button role="switch">` whose subtree is a text free thumb, so all 64 have **no accessible name**. The visible label is a sibling `<div>` with no association to the control.

This is the worst remaining case in the admin area: 64 toggles in one dialog, many with near identical adjacent labels (Import Models / Export Models / Import Prompts / Export Prompts / Import Tools / Export Tools). A screen reader user hears 64 consecutive "switch, on" and "switch, off" with no way to tell which permission is which.

Breaks WCAG 4.1.2 Name, Role, Value (Level A).

Fix: pass the row's own label to each switch. The `ariaLabel` expression is the **same `$i18n.t()` key** as the visible text two lines above it, so the accessible name equals the visible label in every locale, which also satisfies 2.5.3 Label in Name and keeps voice control working.

`ariaLabel` rather than `ariaLabelledbyId`, which is what `chat/Settings/Interface.svelte` uses for the same row shape. The difference is that `Interface.svelte` is a singleton, whereas this component is rendered from `EditGroupModal`, which is instantiated in three places including once per group in `GroupItem.svelte`. Only one can be visible today, but nothing enforces that, and 64 hardcoded ids would fail silently the day two coexist, since `aria-labelledby` resolves to the first matching id. `aria-label` has no such failure mode and needs half the edits.

All 64 mappings were checked individually rather than assumed. The nearest preceding label is the correct one in every case, including the three rows wrapped in a `<Tooltip>` (whose `content` attribute precedes the label in source order) and the ~60 `{#if}` / `{:else if}` explanatory strings (which always follow their switch). All 64 resulting labels are distinct.

The nested sub toggles are unambiguous on their own because upstream already labelled them fully ("Import Models" rather than "Import"), so no extra scoping is needed.

Two known follow ups, deliberately not bundled:

- The warning tooltips on Tools Access, Skills Access and Automations ("Warning: Enabling this will allow users to upload arbitrary code on the server.") are attached to a non focusable wrapper `<div>`, so keyboard and screen reader users never receive them. That needs a change in `common/Tooltip.svelte` or an `ariaDescribedbyId` on `Switch`, not a naming change.
- This file is a ~14 line block repeated 64 times where only the label and permission key vary, and it wants a shared `PermissionRow` component. Extracting it here would bundle a large structural refactor into an accessibility fix and make the diff unreviewable against the claim, so it is left alone.

The diff is +164/−65 rather than 64 changed lines, because 33 of the switches exceed the 100 column print width and Prettier reflows them to the multi line form. The file is Prettier clean and compiles with no new warnings.

Severity: Serious.

### 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:38:35 +02:00 committed by GitHub
parent ba4c92c4f0
commit a0ee66c145
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -48,7 +48,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Models Access')}
</div>
<Switch bind:state={permissions.workspace.models} />
<Switch bind:state={permissions.workspace.models} ariaLabel={$i18n.t('Models Access')} />
</div>
{#if permissions.workspace.models}
@ -57,13 +57,19 @@
<div class="self-center text-xs">
{$i18n.t('Import Models')}
</div>
<Switch bind:state={permissions.workspace.models_import} />
<Switch
bind:state={permissions.workspace.models_import}
ariaLabel={$i18n.t('Import Models')}
/>
</div>
<div class="flex w-full justify-between">
<div class="self-center text-xs">
{$i18n.t('Export Models')}
</div>
<Switch bind:state={permissions.workspace.models_export} />
<Switch
bind:state={permissions.workspace.models_export}
ariaLabel={$i18n.t('Export Models')}
/>
</div>
</div>
{:else if defaultPermissions?.workspace?.models}
@ -80,7 +86,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Knowledge Access')}
</div>
<Switch bind:state={permissions.workspace.knowledge} />
<Switch
bind:state={permissions.workspace.knowledge}
ariaLabel={$i18n.t('Knowledge Access')}
/>
</div>
{#if defaultPermissions?.workspace?.knowledge && !permissions.workspace.knowledge}
<div>
@ -96,7 +105,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Prompts Access')}
</div>
<Switch bind:state={permissions.workspace.prompts} />
<Switch bind:state={permissions.workspace.prompts} ariaLabel={$i18n.t('Prompts Access')} />
</div>
{#if permissions.workspace.prompts}
@ -105,13 +114,19 @@
<div class="self-center text-xs">
{$i18n.t('Import Prompts')}
</div>
<Switch bind:state={permissions.workspace.prompts_import} />
<Switch
bind:state={permissions.workspace.prompts_import}
ariaLabel={$i18n.t('Import Prompts')}
/>
</div>
<div class="flex w-full justify-between">
<div class="self-center text-xs">
{$i18n.t('Export Prompts')}
</div>
<Switch bind:state={permissions.workspace.prompts_export} />
<Switch
bind:state={permissions.workspace.prompts_export}
ariaLabel={$i18n.t('Export Prompts')}
/>
</div>
</div>
{:else if defaultPermissions?.workspace?.prompts}
@ -135,7 +150,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Tools Access')}
</div>
<Switch bind:state={permissions.workspace.tools} />
<Switch bind:state={permissions.workspace.tools} ariaLabel={$i18n.t('Tools Access')} />
</Tooltip>
{#if permissions.workspace.tools}
@ -144,13 +159,19 @@
<div class="self-center text-xs">
{$i18n.t('Import Tools')}
</div>
<Switch bind:state={permissions.workspace.tools_import} />
<Switch
bind:state={permissions.workspace.tools_import}
ariaLabel={$i18n.t('Import Tools')}
/>
</div>
<div class="flex w-full justify-between">
<div class="self-center text-xs">
{$i18n.t('Export Tools')}
</div>
<Switch bind:state={permissions.workspace.tools_export} />
<Switch
bind:state={permissions.workspace.tools_export}
ariaLabel={$i18n.t('Export Tools')}
/>
</div>
</div>
{:else if defaultPermissions?.workspace?.tools}
@ -174,7 +195,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Skills Access')}
</div>
<Switch bind:state={permissions.workspace.skills} />
<Switch bind:state={permissions.workspace.skills} ariaLabel={$i18n.t('Skills Access')} />
</Tooltip>
{#if permissions.workspace.skills}
@ -183,13 +204,19 @@
<div class="self-center text-xs">
{$i18n.t('Import Skills')}
</div>
<Switch bind:state={permissions.workspace.skills_import} />
<Switch
bind:state={permissions.workspace.skills_import}
ariaLabel={$i18n.t('Import Skills')}
/>
</div>
<div class="flex w-full justify-between">
<div class="self-center text-xs">
{$i18n.t('Export Skills')}
</div>
<Switch bind:state={permissions.workspace.skills_export} />
<Switch
bind:state={permissions.workspace.skills_export}
ariaLabel={$i18n.t('Export Skills')}
/>
</div>
</div>
{:else if defaultPermissions?.workspace?.skills}
@ -212,7 +239,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Models Sharing')}
</div>
<Switch bind:state={permissions.sharing.models} />
<Switch bind:state={permissions.sharing.models} ariaLabel={$i18n.t('Models Sharing')} />
</div>
{#if defaultPermissions?.sharing?.models && !permissions.sharing.models}
<div>
@ -229,7 +256,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Models Public Sharing')}
</div>
<Switch bind:state={permissions.sharing.public_models} />
<Switch
bind:state={permissions.sharing.public_models}
ariaLabel={$i18n.t('Models Public Sharing')}
/>
</div>
{#if defaultPermissions?.sharing?.public_models && !permissions.sharing.public_models}
<div>
@ -246,7 +276,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Knowledge Sharing')}
</div>
<Switch bind:state={permissions.sharing.knowledge} />
<Switch
bind:state={permissions.sharing.knowledge}
ariaLabel={$i18n.t('Knowledge Sharing')}
/>
</div>
{#if defaultPermissions?.sharing?.knowledge && !permissions.sharing.knowledge}
<div>
@ -263,7 +296,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Knowledge Public Sharing')}
</div>
<Switch bind:state={permissions.sharing.public_knowledge} />
<Switch
bind:state={permissions.sharing.public_knowledge}
ariaLabel={$i18n.t('Knowledge Public Sharing')}
/>
</div>
{#if defaultPermissions?.sharing?.public_knowledge && !permissions.sharing.public_knowledge}
<div>
@ -280,7 +316,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Prompts Sharing')}
</div>
<Switch bind:state={permissions.sharing.prompts} />
<Switch bind:state={permissions.sharing.prompts} ariaLabel={$i18n.t('Prompts Sharing')} />
</div>
{#if defaultPermissions?.sharing?.prompts && !permissions.sharing.prompts}
<div>
@ -297,7 +333,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Prompts Public Sharing')}
</div>
<Switch bind:state={permissions.sharing.public_prompts} />
<Switch
bind:state={permissions.sharing.public_prompts}
ariaLabel={$i18n.t('Prompts Public Sharing')}
/>
</div>
{#if defaultPermissions?.sharing?.public_prompts && !permissions.sharing.public_prompts}
<div>
@ -314,7 +353,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Tools Sharing')}
</div>
<Switch bind:state={permissions.sharing.tools} />
<Switch bind:state={permissions.sharing.tools} ariaLabel={$i18n.t('Tools Sharing')} />
</div>
{#if defaultPermissions?.sharing?.tools && !permissions.sharing.tools}
<div>
@ -331,7 +370,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Tools Public Sharing')}
</div>
<Switch bind:state={permissions.sharing.public_tools} />
<Switch
bind:state={permissions.sharing.public_tools}
ariaLabel={$i18n.t('Tools Public Sharing')}
/>
</div>
{#if defaultPermissions?.sharing?.public_tools && !permissions.sharing.public_tools}
<div>
@ -348,7 +390,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Skills Sharing')}
</div>
<Switch bind:state={permissions.sharing.skills} />
<Switch bind:state={permissions.sharing.skills} ariaLabel={$i18n.t('Skills Sharing')} />
</div>
{#if defaultPermissions?.sharing?.skills && !permissions.sharing.skills}
<div>
@ -365,7 +407,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Skills Public Sharing')}
</div>
<Switch bind:state={permissions.sharing.public_skills} />
<Switch
bind:state={permissions.sharing.public_skills}
ariaLabel={$i18n.t('Skills Public Sharing')}
/>
</div>
{#if defaultPermissions?.sharing?.public_skills && !permissions.sharing.public_skills}
<div>
@ -382,7 +427,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Notes Sharing')}
</div>
<Switch bind:state={permissions.sharing.notes} />
<Switch bind:state={permissions.sharing.notes} ariaLabel={$i18n.t('Notes Sharing')} />
</div>
{#if defaultPermissions?.sharing?.notes && !permissions.sharing.notes}
<div>
@ -399,7 +444,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Notes Public Sharing')}
</div>
<Switch bind:state={permissions.sharing.public_notes} />
<Switch
bind:state={permissions.sharing.public_notes}
ariaLabel={$i18n.t('Notes Public Sharing')}
/>
</div>
{#if defaultPermissions?.sharing?.public_notes && !permissions.sharing.public_notes}
<div>
@ -416,7 +464,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Folders Sharing')}
</div>
<Switch bind:state={permissions.sharing.folders} />
<Switch bind:state={permissions.sharing.folders} ariaLabel={$i18n.t('Folders Sharing')} />
</div>
{#if defaultPermissions?.sharing?.folders && !permissions.sharing.folders}
<div>
@ -433,7 +481,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Chats Public Sharing')}
</div>
<Switch bind:state={permissions.sharing.public_chats} />
<Switch
bind:state={permissions.sharing.public_chats}
ariaLabel={$i18n.t('Chats Public Sharing')}
/>
</div>
{#if defaultPermissions?.sharing?.public_chats && !permissions.sharing.public_chats}
<div>
@ -451,7 +502,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Calendars Public Sharing')}
</div>
<Switch bind:state={permissions.sharing.public_calendars} />
<Switch
bind:state={permissions.sharing.public_calendars}
ariaLabel={$i18n.t('Calendars Public Sharing')}
/>
</div>
{#if defaultPermissions?.sharing?.public_calendars && !permissions.sharing.public_calendars}
<div>
@ -474,7 +528,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Sharing With Users')}
</div>
<Switch bind:state={permissions.access_grants.allow_users} />
<Switch
bind:state={permissions.access_grants.allow_users}
ariaLabel={$i18n.t('Allow Sharing With Users')}
/>
</div>
{#if defaultPermissions?.access_grants?.allow_users && !permissions.access_grants.allow_users}
<div>
@ -490,7 +547,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Sharing With Groups')}
</div>
<Switch bind:state={permissions.access_grants.allow_groups} />
<Switch
bind:state={permissions.access_grants.allow_groups}
ariaLabel={$i18n.t('Allow Sharing With Groups')}
/>
</div>
{#if defaultPermissions?.access_grants?.allow_groups && !permissions.access_grants.allow_groups}
<div>
@ -512,7 +572,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow File Upload')}
</div>
<Switch bind:state={permissions.chat.file_upload} />
<Switch
bind:state={permissions.chat.file_upload}
ariaLabel={$i18n.t('Allow File Upload')}
/>
</div>
{#if defaultPermissions?.chat?.file_upload && !permissions.chat.file_upload}
<div>
@ -528,7 +591,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Web Upload')}
</div>
<Switch bind:state={permissions.chat.web_upload} />
<Switch bind:state={permissions.chat.web_upload} ariaLabel={$i18n.t('Allow Web Upload')} />
</div>
{#if defaultPermissions?.chat?.web_upload && !permissions.chat.web_upload}
<div>
@ -544,7 +607,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Chat Controls')}
</div>
<Switch bind:state={permissions.chat.controls} />
<Switch bind:state={permissions.chat.controls} ariaLabel={$i18n.t('Allow Chat Controls')} />
</div>
{#if defaultPermissions?.chat?.controls && !permissions.chat.controls}
<div>
@ -561,7 +624,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Chat Valves')}
</div>
<Switch bind:state={permissions.chat.valves} />
<Switch bind:state={permissions.chat.valves} ariaLabel={$i18n.t('Allow Chat Valves')} />
</div>
{#if defaultPermissions?.chat?.valves && !permissions.chat.valves}
<div>
@ -577,7 +640,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Chat System Prompt')}
</div>
<Switch bind:state={permissions.chat.system_prompt} />
<Switch
bind:state={permissions.chat.system_prompt}
ariaLabel={$i18n.t('Allow Chat System Prompt')}
/>
</div>
{#if defaultPermissions?.chat?.system_prompt && !permissions.chat.system_prompt}
<div>
@ -593,7 +659,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Chat Params')}
</div>
<Switch bind:state={permissions.chat.params} />
<Switch bind:state={permissions.chat.params} ariaLabel={$i18n.t('Allow Chat Params')} />
</div>
{#if defaultPermissions?.chat?.params && !permissions.chat.params}
<div>
@ -610,7 +676,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Chat Edit')}
</div>
<Switch bind:state={permissions.chat.edit} />
<Switch bind:state={permissions.chat.edit} ariaLabel={$i18n.t('Allow Chat Edit')} />
</div>
{#if defaultPermissions?.chat?.edit && !permissions.chat.edit}
<div>
@ -626,7 +692,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Chat Delete')}
</div>
<Switch bind:state={permissions.chat.delete} />
<Switch bind:state={permissions.chat.delete} ariaLabel={$i18n.t('Allow Chat Delete')} />
</div>
{#if defaultPermissions?.chat?.delete && !permissions.chat.delete}
<div>
@ -642,7 +708,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Delete Messages')}
</div>
<Switch bind:state={permissions.chat.delete_message} />
<Switch
bind:state={permissions.chat.delete_message}
ariaLabel={$i18n.t('Allow Delete Messages')}
/>
</div>
{#if defaultPermissions?.chat?.delete_message && !permissions.chat.delete_message}
<div>
@ -658,7 +727,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Continue Response')}
</div>
<Switch bind:state={permissions.chat.continue_response} />
<Switch
bind:state={permissions.chat.continue_response}
ariaLabel={$i18n.t('Allow Continue Response')}
/>
</div>
{#if defaultPermissions?.chat?.continue_response && !permissions.chat.continue_response}
<div>
@ -674,7 +746,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Regenerate Response')}
</div>
<Switch bind:state={permissions.chat.regenerate_response} />
<Switch
bind:state={permissions.chat.regenerate_response}
ariaLabel={$i18n.t('Allow Regenerate Response')}
/>
</div>
{#if defaultPermissions?.chat?.regenerate_response && !permissions.chat.regenerate_response}
<div>
@ -690,7 +765,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Rate Response')}
</div>
<Switch bind:state={permissions.chat.rate_response} />
<Switch
bind:state={permissions.chat.rate_response}
ariaLabel={$i18n.t('Allow Rate Response')}
/>
</div>
{#if defaultPermissions?.chat?.rate_response && !permissions.chat.rate_response}
<div>
@ -706,7 +784,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Chat Share')}
</div>
<Switch bind:state={permissions.chat.share} />
<Switch bind:state={permissions.chat.share} ariaLabel={$i18n.t('Allow Chat Share')} />
</div>
{#if defaultPermissions?.chat?.share && !permissions.chat.share}
<div>
@ -722,7 +800,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Chat Export')}
</div>
<Switch bind:state={permissions.chat.export} />
<Switch bind:state={permissions.chat.export} ariaLabel={$i18n.t('Allow Chat Export')} />
</div>
{#if defaultPermissions?.chat?.export && !permissions.chat.export}
<div>
@ -738,7 +816,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Chat Import')}
</div>
<Switch bind:state={permissions.chat['import']} />
<Switch bind:state={permissions.chat['import']} ariaLabel={$i18n.t('Allow Chat Import')} />
</div>
{#if defaultPermissions?.chat?.import && !permissions.chat['import']}
<div>
@ -754,7 +832,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Speech to Text')}
</div>
<Switch bind:state={permissions.chat.stt} />
<Switch bind:state={permissions.chat.stt} ariaLabel={$i18n.t('Allow Speech to Text')} />
</div>
{#if defaultPermissions?.chat?.stt && !permissions.chat.stt}
<div>
@ -770,7 +848,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Text to Speech')}
</div>
<Switch bind:state={permissions.chat.tts} />
<Switch bind:state={permissions.chat.tts} ariaLabel={$i18n.t('Allow Text to Speech')} />
</div>
{#if defaultPermissions?.chat?.tts && !permissions.chat.tts}
<div>
@ -786,7 +864,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Call')}
</div>
<Switch bind:state={permissions.chat.call} />
<Switch bind:state={permissions.chat.call} ariaLabel={$i18n.t('Allow Call')} />
</div>
{#if defaultPermissions?.chat?.call && !permissions.chat.call}
<div>
@ -802,7 +880,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Multiple Models in Chat')}
</div>
<Switch bind:state={permissions.chat.multiple_models} />
<Switch
bind:state={permissions.chat.multiple_models}
ariaLabel={$i18n.t('Allow Multiple Models in Chat')}
/>
</div>
{#if defaultPermissions?.chat?.multiple_models && !permissions.chat.multiple_models}
<div>
@ -818,7 +899,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Allow Temporary Chat')}
</div>
<Switch bind:state={permissions.chat.temporary} />
<Switch
bind:state={permissions.chat.temporary}
ariaLabel={$i18n.t('Allow Temporary Chat')}
/>
</div>
{#if defaultPermissions?.chat?.temporary && !permissions.chat.temporary}
<div>
@ -835,7 +919,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Enforce Temporary Chat')}
</div>
<Switch bind:state={permissions.chat.temporary_enforced} />
<Switch
bind:state={permissions.chat.temporary_enforced}
ariaLabel={$i18n.t('Enforce Temporary Chat')}
/>
</div>
{#if defaultPermissions?.chat?.temporary_enforced && !permissions.chat.temporary_enforced}
<div>
@ -858,7 +945,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('API Keys')}
</div>
<Switch bind:state={permissions.features.api_keys} />
<Switch bind:state={permissions.features.api_keys} ariaLabel={$i18n.t('API Keys')} />
</div>
{#if defaultPermissions?.features?.api_keys && !permissions.features.api_keys}
<div>
@ -874,7 +961,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Notes')}
</div>
<Switch bind:state={permissions.features.notes} />
<Switch bind:state={permissions.features.notes} ariaLabel={$i18n.t('Notes')} />
</div>
{#if defaultPermissions?.features?.notes && !permissions.features.notes}
<div>
@ -890,7 +977,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Channels')}
</div>
<Switch bind:state={permissions.features.channels} />
<Switch bind:state={permissions.features.channels} ariaLabel={$i18n.t('Channels')} />
</div>
{#if defaultPermissions?.features?.channels && !permissions.features.channels}
<div>
@ -906,7 +993,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Folders')}
</div>
<Switch bind:state={permissions.features.folders} />
<Switch bind:state={permissions.features.folders} ariaLabel={$i18n.t('Folders')} />
</div>
{#if defaultPermissions?.features?.folders && !permissions.features.folders}
<div>
@ -922,7 +1009,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Direct Tool Servers')}
</div>
<Switch bind:state={permissions.features.direct_tool_servers} />
<Switch
bind:state={permissions.features.direct_tool_servers}
ariaLabel={$i18n.t('Direct Tool Servers')}
/>
</div>
{#if defaultPermissions?.features?.direct_tool_servers && !permissions.features.direct_tool_servers}
<div>
@ -938,7 +1028,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Web Search')}
</div>
<Switch bind:state={permissions.features.web_search} />
<Switch bind:state={permissions.features.web_search} ariaLabel={$i18n.t('Web Search')} />
</div>
{#if defaultPermissions?.features?.web_search && !permissions.features.web_search}
<div>
@ -954,7 +1044,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Image Generation')}
</div>
<Switch bind:state={permissions.features.image_generation} />
<Switch
bind:state={permissions.features.image_generation}
ariaLabel={$i18n.t('Image Generation')}
/>
</div>
{#if defaultPermissions?.features?.image_generation && !permissions.features.image_generation}
<div>
@ -970,7 +1063,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Code Interpreter')}
</div>
<Switch bind:state={permissions.features.code_interpreter} />
<Switch
bind:state={permissions.features.code_interpreter}
ariaLabel={$i18n.t('Code Interpreter')}
/>
</div>
{#if defaultPermissions?.features?.code_interpreter && !permissions.features.code_interpreter}
<div>
@ -986,7 +1082,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Memories')}
</div>
<Switch bind:state={permissions.features.memories} />
<Switch bind:state={permissions.features.memories} ariaLabel={$i18n.t('Memories')} />
</div>
{#if defaultPermissions?.features?.memories && !permissions.features.memories}
<div>
@ -1008,7 +1104,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Automations')}
</div>
<Switch bind:state={permissions.features.automations} />
<Switch bind:state={permissions.features.automations} ariaLabel={$i18n.t('Automations')} />
</Tooltip>
{#if defaultPermissions?.features?.automations && !permissions.features.automations}
<div>
@ -1024,7 +1120,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Calendar')}
</div>
<Switch bind:state={permissions.features.calendar} />
<Switch bind:state={permissions.features.calendar} ariaLabel={$i18n.t('Calendar')} />
</div>
{#if defaultPermissions?.features?.calendar && !permissions.features.calendar}
<div>
@ -1040,7 +1136,7 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('User Webhooks')}
</div>
<Switch bind:state={permissions.features.webhooks} />
<Switch bind:state={permissions.features.webhooks} ariaLabel={$i18n.t('User Webhooks')} />
</div>
{#if defaultPermissions?.features?.webhooks && !permissions.features.webhooks}
<div>
@ -1062,7 +1158,10 @@
<div class=" self-center text-xs font-normal">
{$i18n.t('Interface Settings Access')}
</div>
<Switch bind:state={permissions.settings.interface} />
<Switch
bind:state={permissions.settings.interface}
ariaLabel={$i18n.t('Interface Settings Access')}
/>
</div>
{#if defaultPermissions?.settings?.interface && !permissions.settings.interface}
<div>