From b4d3b27caf1587b783a46268c769132eb9211c80 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 10 Aug 2026 18:52:27 -0600 Subject: [PATCH] refac --- .../components/admin/Settings/General.svelte | 74 +- .../components/chat/Settings/Interface.svelte | 1595 +--------------- .../common/InterfaceSettings.svelte | 1617 +++++++++++++++++ 3 files changed, 1697 insertions(+), 1589 deletions(-) create mode 100644 src/lib/components/common/InterfaceSettings.svelte diff --git a/src/lib/components/admin/Settings/General.svelte b/src/lib/components/admin/Settings/General.svelte index 2c6f53c435..1cb55bca8d 100644 --- a/src/lib/components/admin/Settings/General.svelte +++ b/src/lib/components/admin/Settings/General.svelte @@ -5,6 +5,7 @@ import { getBackendConfig, getVersionUpdates } from '$lib/apis'; import { getAdminConfig, updateAdminConfig } from '$lib/apis/auths'; import { getBanners, setBanners } from '$lib/apis/configs'; + import InterfaceSettings from '$lib/components/common/InterfaceSettings.svelte'; import SettingsSelect from '$lib/components/common/SettingsSelect.svelte'; import Switch from '$lib/components/common/Switch.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte'; @@ -33,13 +34,14 @@ }; let adminConfig: any = null; + let defaultUserUiSettings: Record = {}; + let showUserUiDefaults = false; let banners: Banner[] = []; const inputClass = 'w-full h-7 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500'; const textareaClass = 'w-full rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500'; - const checkForVersionUpdates = async () => { updateAvailable = null; version = await getVersionUpdates(localStorage.token).catch((error) => { @@ -59,7 +61,13 @@ _banners.set(await setBanners(localStorage.token, banners)); }; + const saveDefaultUserUiSettings = (updated: Record) => { + defaultUserUiSettings = { ...defaultUserUiSettings, ...updated }; + }; + const updateHandler = async () => { + adminConfig.DEFAULT_USER_UI_SETTINGS = defaultUserUiSettings; + const res = await updateAdminConfig(localStorage.token, adminConfig); await updateBanners(); @@ -75,6 +83,12 @@ onMount(async () => { adminConfig = await getAdminConfig(localStorage.token); + defaultUserUiSettings = + adminConfig?.DEFAULT_USER_UI_SETTINGS && + typeof adminConfig.DEFAULT_USER_UI_SETTINGS === 'object' && + !Array.isArray(adminConfig.DEFAULT_USER_UI_SETTINGS) + ? adminConfig.DEFAULT_USER_UI_SETTINGS + : {}; banners = [...$_banners]; }); @@ -370,6 +384,64 @@ +
+
+ + + +
+ + {#if showUserUiDefaults} +
+
+
+ {Object.keys(defaultUserUiSettings).length} {$i18n.t('settings configured')} +
+ + {#if Object.keys(defaultUserUiSettings).length > 0} + + {/if} +
+ +
+ +
+
+ {/if} +
+
diff --git a/src/lib/components/chat/Settings/Interface.svelte b/src/lib/components/chat/Settings/Interface.svelte index ab88758005..e8ff82df58 100644 --- a/src/lib/components/chat/Settings/Interface.svelte +++ b/src/lib/components/chat/Settings/Interface.svelte @@ -1,1608 +1,27 @@ - { - floatingActionButtons = buttons; - saveSettings({ floatingActionButtons }); - }} -/> - - { - saveSettings({ imageCompressionSize: size }); - }} -/> -
{ - updateInterfaceHandler(); + on:submit|preventDefault={async () => { + await interfaceSettings?.save(); dispatch('save'); }} >

{$i18n.t('Interface')}

- { - let reader = new FileReader(); - reader.onload = (event) => { - let originalImageUrl = `${event.target.result}`; - - backgroundImageUrl = originalImageUrl; - saveSettings({ backgroundImageUrl }); - }; - - if ( - inputFiles && - inputFiles.length > 0 && - ['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(inputFiles[0]['type']) - ) { - reader.readAsDataURL(inputFiles[0]); - } else { - console.log(`Unsupported File Type '${inputFiles[0]['type']}'.`); - inputFiles = null; - } - }} - /> -
-
-

{$i18n.t('UI')}

- -
-
- - -
- -
-
- - {#if textScale !== null} -
- - -
- { - setTextScaleHandler(textScale); - }} - aria-labelledby="ui-scale-label" - aria-valuemin="1" - aria-valuemax="1.5" - aria-valuenow={textScale} - aria-valuetext={`${textScale}x`} - /> -
- - -
- {/if} -

- {$i18n.t('Set a local zoom level for the app interface.')} -

-
- -
-
-
- {$i18n.t('High Contrast Mode')} -
- -
- { - saveSettings({ highContrastMode }); - }} - /> -
-
-

- {$i18n.t('Increase contrast for controls and input surfaces.')} -

-
- -
-
-
- {$i18n.t('Display Chat Title in Tab')} -
- -
- { - saveSettings({ showChatTitleInTab }); - }} - /> -
-
-

- {$i18n.t('Use the active chat title as the browser tab title.')} -

-
- -
-
-
{$i18n.t('Allow User Location')}
- -
- { - toggleUserLocation(); - }} - /> -
-
-

- {$i18n.t('Share your current location with features that can use it.')} -

-
- -
-
-
- {$i18n.t('Haptic Feedback')} ({$i18n.t('Android')}) -
- -
- { - saveSettings({ hapticFeedback }); - }} - /> -
-
-

- {$i18n.t('Use device vibration feedback on supported Android devices.')} -

-
- -
-
-
- {$i18n.t('Copy Formatted Text')} -
- -
- { - saveSettings({ copyFormatted }); - }} - /> -
-
-

- {$i18n.t('Copy rich formatted content instead of plain text.')} -

-
- - {#if $user?.role === 'admin'} -
-
-
- {$i18n.t('Toast Notifications for New Updates')} -
- -
- { - saveSettings({ showUpdateToast }); - }} - /> -
-
-

- {$i18n.t('Show update toasts to admins when new versions are available.')} -

-
- -
-
-
- {$i18n.t(`Show "What's New" Modal on Login`)} -
- -
- { - saveSettings({ showChangelog }); - }} - /> -
-
-

- {$i18n.t('Open the changelog modal after sign-in when enabled.')} -

-
- {/if} - -
{$i18n.t('Chat')}
- -
-
-
- {$i18n.t('Enable Message Queue')} -
- -
- { - saveSettings({ enableMessageQueue }); - }} - /> -
-
-

- {$i18n.t('Queue outgoing messages instead of interrupting active responses.')} -

-
- -
-
-
- {$i18n.t('Chat Direction')} -
- - -
-

- {$i18n.t('Choose automatic, left-to-right, or right-to-left text flow.')} -

-
- -
-
-
- {$i18n.t('Landing Page Mode')} -
- - -
-

- {$i18n.t('Choose whether the app opens to the default home or chat view.')} -

-
- -
-
-
- {$i18n.t('Chat Background Image')} -
- - -
-

- {$i18n.t('Upload or reset the image shown behind chat content.')} -

-
- -
-
-
- {$i18n.t('Chat Bubble UI')} -
- -
- { - saveSettings({ chatBubble }); - }} - /> -
-
-

- {$i18n.t('Render messages in compact bubble containers.')} -

-
- - {#if !$settings.chatBubble} -
-
-
- {$i18n.t('Display the Username Instead of You in the Chat')} -
- -
- { - saveSettings({ showUsername }); - }} - /> -
-
-

- {$i18n.t('Show your username label instead of You in chat bubbles.')} -

-
- {/if} - -
-
-
- {$i18n.t('Widescreen Mode')} -
- -
- { - saveSettings({ widescreenMode }); - }} - /> -
-
-

- {$i18n.t('Use a wider chat layout on large displays.')} -

-
- - {#if $user.role === 'admin' || $user?.permissions?.chat?.temporary} -
-
-
- {$i18n.t('Temporary Chat by Default')} -
- -
- { - saveSettings({ temporaryChatByDefault }); - }} - /> -
-
-

- {$i18n.t('Start new chats as temporary unless changed.')} -

-
- {/if} - -
-
-
- {$i18n.t('Fade Effect for Streaming Text')} -
- -
- { - saveSettings({ chatFadeStreamingText }); - }} - /> -
-
-

- {$i18n.t('Fade streaming text as it arrives.')} -

-
- -
-
-
- {$i18n.t('Render Markdown in User Messages')} -
- -
- { - saveSettings({ renderMarkdownInUserMessages }); - }} - /> -
-
-

- {$i18n.t('Format Markdown syntax in your own messages.')} -

-
- -
-
-
- {$i18n.t('Render Markdown in Assistant Messages')} -
- -
- { - saveSettings({ renderMarkdownInAssistantMessages }); - }} - /> -
-
-

- {$i18n.t('Format Markdown syntax in assistant responses.')} -

-
- -
-
-
- {$i18n.t('Title Auto-Generation')} -
- -
- { - toggleTitleAutoGenerate(); - }} - /> -
-
-

- {$i18n.t('Generate chat titles automatically from conversation content.')} -

-
- -
-
-
- {$i18n.t('Follow-Up Auto-Generation')} -
- -
- { - saveSettings({ autoFollowUps }); - }} - /> -
-
-

- {$i18n.t('Generate suggested follow-up prompts after responses.')} -

-
- -
-
-
- {$i18n.t('Chat Tags Auto-Generation')} -
- -
- { - saveSettings({ autoTags }); - }} - /> -
-
-

- {$i18n.t('Generate tags for chats automatically.')} -

-
- -
-
-
- {$i18n.t('Auto-Copy Response to Clipboard')} -
- -
- { - toggleResponseAutoCopy(); - }} - /> -
-
-

- {$i18n.t('Copy the latest assistant response when it completes.')} -

-
- -
-
-
- {$i18n.t('Response Auto-Scroll')} -
- -
- { - saveSettings({ scrollOnResponseGeneration }); - }} - /> -
-
-

- {$i18n.t('Follow assistant responses as they are generated.')} -

-
- -
-
-
- {$i18n.t('Insert Suggestion Prompt to Input')} -
- -
- { - saveSettings({ insertSuggestionPrompt }); - }} - /> -
-
-

- {$i18n.t('Place selected suggestion text into the composer.')} -

-
- -
-
-
- {$i18n.t('Keep Follow-Up Prompts in Chat')} -
- -
- { - saveSettings({ keepFollowUpPrompts }); - }} - /> -
-
-

- {$i18n.t('Keep generated follow-up prompts visible in the chat.')} -

-
- -
-
-
- {$i18n.t('Insert Follow-Up Prompt to Input')} -
- -
- { - saveSettings({ insertFollowUpPrompt }); - }} - /> -
-
-

- {$i18n.t('Insert selected follow-up prompts directly into the composer.')} -

-
- -
-
-
- {$i18n.t('Regenerate Menu')} -
- -
- { - saveSettings({ regenerateMenu }); - }} - /> -
-
-

- {$i18n.t('Show the regenerate action menu for assistant responses.')} -

-
- -
-
-
- {$i18n.t('Always Collapse Code Blocks')} -
- -
- { - saveSettings({ collapseCodeBlocks }); - }} - /> -
-
-

- {$i18n.t('Collapse code blocks by default.')} -

-
- -
-
-
- {$i18n.t('Always Expand Details')} -
- -
- { - saveSettings({ expandDetails }); - }} - /> -
-
-

- {$i18n.t('Open detail blocks by default.')} -

-
- -
-
-
- {$i18n.t('Render Markdown in Previews')} -
- -
- { - saveSettings({ renderMarkdownInPreviews }); - }} - /> -
-
-

- {$i18n.t('Format Markdown in previews and compact content surfaces.')} -

-
- -
-
-
- {$i18n.t('Display Multi-model Responses in Tabs')} -
- -
- { - saveSettings({ displayMultiModelResponsesInTabs }); - }} - /> -
-
-

- {$i18n.t('Group multi-model responses into tabs.')} -

-
- -
-
-
- {$i18n.t('Scroll On Branch Change')} -
- -
- { - saveSettings({ scrollOnBranchChange }); - }} - /> -
-
-

- {$i18n.t('Scroll to the active branch when switching response branches.')} -

-
- -
-
-
- {$i18n.t('Show Files on Terminal Select')} -
- -
- { - saveSettings({ showFilesOnTerminalSelect }); - }} - /> -
-
-

- {$i18n.t('Open the file browser after selecting a terminal.')} -

-
- -
-
-
- {$i18n.t('Stylized PDF Export')} -
- -
- { - saveSettings({ stylizedPdfExport }); - }} - /> -
-
-

- {$i18n.t('Use styled formatting when exporting chats to PDF.')} -

-
- -
-
- - -
- {#if showFloatingActionButtons} - - {/if} - - { - saveSettings({ showFloatingActionButtons }); - }} - /> -
-
-

- {$i18n.t('Show the floating quick-action toolbar in chat.')} -

-
- -
-
-
- {$i18n.t('Web Search in Chat')} -
- - -
-

- {$i18n.t('Set web search availability for new chats.')} -

-
- -
{$i18n.t('Input')}
- -
-
-
- {$i18n.t('Enter Key Behavior')} -
- - -
-

- {$i18n.t('Choose whether Enter sends immediately or uses Ctrl+Enter.')} -

-
- -
-
-
- {$i18n.t('Rich Text Input for Chat')} -
- -
- { - saveSettings({ richTextInput }); - }} - /> -
-
-

- {$i18n.t('Use the rich composer instead of a plain textarea.')} -

-
- - {#if $config?.features?.enable_autocomplete_generation} -
-
-
- {$i18n.t('Prompt Autocompletion')} -
- -
- { - saveSettings({ promptAutocomplete }); - }} - /> -
-
-

- {$i18n.t('Suggest completions while composing prompts.')} -

-
- {/if} - - {#if richTextInput} -
-
-
- {$i18n.t('Show Formatting Toolbar')} -
- -
- { - saveSettings({ showFormattingToolbar }); - }} - /> -
-
-

- {$i18n.t('Show formatting controls in the rich text composer.')} -

-
- -
-
-
- {$i18n.t('Insert Prompt as Rich Text')} -
- -
- { - saveSettings({ insertPromptAsRichText }); - }} - /> -
-
-

- {$i18n.t('Paste inserted prompts as rich text when possible.')} -

-
- {/if} - -
-
-
- {$i18n.t('Paste Large Text as File')} -
- -
- { - saveSettings({ largeTextAsFile }); - }} - /> -
-
-

- {$i18n.t('Convert long pasted text into a file attachment.')} -

-
- -
{$i18n.t('Artifacts')}
- -
-
-
- {$i18n.t('Detect Artifacts Automatically')} -
- -
- { - saveSettings({ detectArtifacts }); - }} - /> -
-
-

- {$i18n.t('Detect generated artifacts and show them in the artifact workspace.')} -

-
- -
-
-
- {$i18n.t('iframe Sandbox Allow Same Origin')} -
- -
- { - saveSettings({ iframeSandboxAllowSameOrigin }); - }} - /> -
-
-

- {$i18n.t('Allow artifacts to access same-origin browser APIs inside the sandbox.')} -

-
- -
-
-
- {$i18n.t('iframe Sandbox Allow Forms')} -
- -
- { - saveSettings({ iframeSandboxAllowForms }); - }} - /> -
-
-

- {$i18n.t('Allow forms inside sandboxed artifact iframes.')} -

-
- -
{$i18n.t('Voice')}
- -
-
-
- {$i18n.t('Allow Voice Interruption in Call')} -
- -
- { - saveSettings({ voiceInterruption }); - }} - /> -
-
-

- {$i18n.t('Let speech interrupt the assistant during a voice call.')} -

-
- -
-
-
- {$i18n.t('Display Emoji in Call')} -
- -
- { - saveSettings({ showEmojiInCall }); - }} - /> -
-
-

- {$i18n.t('Show emoji feedback in the call interface.')} -

-
- -
{$i18n.t('File')}
- -
-
-
- {$i18n.t('Default Upload Mode')} -
- - -
-

- {$i18n.t('Attach files with full content or focused retrieval by default.')} -

-
- -
-
-
- {$i18n.t('Image Compression')} -
- -
- {#if imageCompression} - - {/if} - - { - saveSettings({ imageCompression }); - }} - /> -
-
-

- {$i18n.t('Compress uploaded images before sending or storage.')} -

-
- - {#if imageCompression} -
-
-
- {$i18n.t('Compress Images in Channels')} -
- -
- { - saveSettings({ imageCompressionInChannels }); - }} - /> -
-
-

- {$i18n.t('Apply image compression to channel uploads too.')} -

-
- {/if} -
+
diff --git a/src/lib/components/common/InterfaceSettings.svelte b/src/lib/components/common/InterfaceSettings.svelte new file mode 100644 index 0000000000..9b54a01fa2 --- /dev/null +++ b/src/lib/components/common/InterfaceSettings.svelte @@ -0,0 +1,1617 @@ + + + { + saveSettings({ floatingActionButtons }); + }} +/> + + { + saveSettings({ imageCompressionSize }); + }} +/> + + { + let reader = new FileReader(); + reader.onload = (event) => { + let originalImageUrl = `${(event.target as FileReader).result}`; + + backgroundImageUrl = originalImageUrl; + saveSettings({ backgroundImageUrl }); + }; + + if ( + inputFiles && + inputFiles.length > 0 && + ['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(inputFiles[0]['type']) + ) { + reader.readAsDataURL(inputFiles[0]); + } else { + console.log(`Unsupported File Type '${inputFiles?.[0]?.type}'.`); + inputFiles = null; + } + }} +/> + +
+

{$i18n.t('UI')}

+ +
+
+ + +
+ +
+
+ + {#if textScale !== null} +
+ + +
+ { + setTextScaleHandler(textScale ?? 1); + }} + aria-labelledby="ui-scale-label" + aria-valuemin="1" + aria-valuemax="1.5" + aria-valuenow={textScale} + aria-valuetext={`${textScale}x`} + /> +
+ + +
+ {/if} +

+ {$i18n.t('Set a local zoom level for the app interface.')} +

+
+ +
+
+
+ {$i18n.t('High Contrast Mode')} +
+ +
+ { + saveSettings({ highContrastMode }); + }} + /> +
+
+

+ {$i18n.t('Increase contrast for controls and input surfaces.')} +

+
+ +
+
+
+ {$i18n.t('Display Chat Title in Tab')} +
+ +
+ { + saveSettings({ showChatTitleInTab }); + }} + /> +
+
+

+ {$i18n.t('Use the active chat title as the browser tab title.')} +

+
+ +
+
+
{$i18n.t('Allow User Location')}
+ +
+ { + toggleUserLocation(); + }} + /> +
+
+

+ {$i18n.t('Share your current location with features that can use it.')} +

+
+ +
+
+
+ {$i18n.t('Haptic Feedback')} ({$i18n.t('Android')}) +
+ +
+ { + saveSettings({ hapticFeedback }); + }} + /> +
+
+

+ {$i18n.t('Use device vibration feedback on supported Android devices.')} +

+
+ +
+
+
+ {$i18n.t('Copy Formatted Text')} +
+ +
+ { + saveSettings({ copyFormatted }); + }} + /> +
+
+

+ {$i18n.t('Copy rich formatted content instead of plain text.')} +

+
+ + {#if $user?.role === 'admin'} +
+
+
+ {$i18n.t('Toast Notifications for New Updates')} +
+ +
+ { + saveSettings({ showUpdateToast }); + }} + /> +
+
+

+ {$i18n.t('Show update toasts to admins when new versions are available.')} +

+
+ +
+
+
+ {$i18n.t(`Show "What's New" Modal on Login`)} +
+ +
+ { + saveSettings({ showChangelog }); + }} + /> +
+
+

+ {$i18n.t('Open the changelog modal after sign-in when enabled.')} +

+
+ {/if} + +
{$i18n.t('Chat')}
+ +
+
+
+ {$i18n.t('Enable Message Queue')} +
+ +
+ { + saveSettings({ enableMessageQueue }); + }} + /> +
+
+

+ {$i18n.t('Queue outgoing messages instead of interrupting active responses.')} +

+
+ +
+
+
+ {$i18n.t('Chat Direction')} +
+ + +
+

+ {$i18n.t('Choose automatic, left-to-right, or right-to-left text flow.')} +

+
+ +
+
+
+ {$i18n.t('Landing Page Mode')} +
+ + +
+

+ {$i18n.t('Choose whether the app opens to the default home or chat view.')} +

+
+ +
+
+
+ {$i18n.t('Chat Background Image')} +
+ + +
+

+ {$i18n.t('Upload or reset the image shown behind chat content.')} +

+
+ +
+
+
+ {$i18n.t('Chat Bubble UI')} +
+ +
+ { + saveSettings({ chatBubble }); + }} + /> +
+
+

+ {$i18n.t('Render messages in compact bubble containers.')} +

+
+ + {#if !chatBubble} +
+
+
+ {$i18n.t('Display the Username Instead of You in the Chat')} +
+ +
+ { + saveSettings({ showUsername }); + }} + /> +
+
+

+ {$i18n.t('Show your username label instead of You in chat bubbles.')} +

+
+ {/if} + +
+
+
+ {$i18n.t('Widescreen Mode')} +
+ +
+ { + saveSettings({ widescreenMode }); + }} + /> +
+
+

+ {$i18n.t('Use a wider chat layout on large displays.')} +

+
+ + {#if $user?.role === 'admin' || $user?.permissions?.chat?.temporary} +
+
+
+ {$i18n.t('Temporary Chat by Default')} +
+ +
+ { + saveSettings({ temporaryChatByDefault }); + }} + /> +
+
+

+ {$i18n.t('Start new chats as temporary unless changed.')} +

+
+ {/if} + +
+
+
+ {$i18n.t('Fade Effect for Streaming Text')} +
+ +
+ { + saveSettings({ chatFadeStreamingText }); + }} + /> +
+
+

+ {$i18n.t('Fade streaming text as it arrives.')} +

+
+ +
+
+
+ {$i18n.t('Render Markdown in User Messages')} +
+ +
+ { + saveSettings({ renderMarkdownInUserMessages }); + }} + /> +
+
+

+ {$i18n.t('Format Markdown syntax in your own messages.')} +

+
+ +
+
+
+ {$i18n.t('Render Markdown in Assistant Messages')} +
+ +
+ { + saveSettings({ renderMarkdownInAssistantMessages }); + }} + /> +
+
+

+ {$i18n.t('Format Markdown syntax in assistant responses.')} +

+
+ +
+
+
+ {$i18n.t('Title Auto-Generation')} +
+ +
+ { + toggleTitleAutoGenerate(); + }} + /> +
+
+

+ {$i18n.t('Generate chat titles automatically from conversation content.')} +

+
+ +
+
+
+ {$i18n.t('Follow-Up Auto-Generation')} +
+ +
+ { + saveSettings({ autoFollowUps }); + }} + /> +
+
+

+ {$i18n.t('Generate suggested follow-up prompts after responses.')} +

+
+ +
+
+
+ {$i18n.t('Chat Tags Auto-Generation')} +
+ +
+ { + saveSettings({ autoTags }); + }} + /> +
+
+

+ {$i18n.t('Generate tags for chats automatically.')} +

+
+ +
+
+
+ {$i18n.t('Auto-Copy Response to Clipboard')} +
+ +
+ { + toggleResponseAutoCopy(); + }} + /> +
+
+

+ {$i18n.t('Copy the latest assistant response when it completes.')} +

+
+ +
+
+
+ {$i18n.t('Response Auto-Scroll')} +
+ +
+ { + saveSettings({ scrollOnResponseGeneration }); + }} + /> +
+
+

+ {$i18n.t('Follow assistant responses as they are generated.')} +

+
+ +
+
+
+ {$i18n.t('Insert Suggestion Prompt to Input')} +
+ +
+ { + saveSettings({ insertSuggestionPrompt }); + }} + /> +
+
+

+ {$i18n.t('Place selected suggestion text into the composer.')} +

+
+ +
+
+
+ {$i18n.t('Keep Follow-Up Prompts in Chat')} +
+ +
+ { + saveSettings({ keepFollowUpPrompts }); + }} + /> +
+
+

+ {$i18n.t('Keep generated follow-up prompts visible in the chat.')} +

+
+ +
+
+
+ {$i18n.t('Insert Follow-Up Prompt to Input')} +
+ +
+ { + saveSettings({ insertFollowUpPrompt }); + }} + /> +
+
+

+ {$i18n.t('Insert selected follow-up prompts directly into the composer.')} +

+
+ +
+
+
+ {$i18n.t('Regenerate Menu')} +
+ +
+ { + saveSettings({ regenerateMenu }); + }} + /> +
+
+

+ {$i18n.t('Show the regenerate action menu for assistant responses.')} +

+
+ +
+
+
+ {$i18n.t('Always Collapse Code Blocks')} +
+ +
+ { + saveSettings({ collapseCodeBlocks }); + }} + /> +
+
+

+ {$i18n.t('Collapse code blocks by default.')} +

+
+ +
+
+
+ {$i18n.t('Always Expand Details')} +
+ +
+ { + saveSettings({ expandDetails }); + }} + /> +
+
+

+ {$i18n.t('Open detail blocks by default.')} +

+
+ +
+
+
+ {$i18n.t('Render Markdown in Previews')} +
+ +
+ { + saveSettings({ renderMarkdownInPreviews }); + }} + /> +
+
+

+ {$i18n.t('Format Markdown in previews and compact content surfaces.')} +

+
+ +
+
+
+ {$i18n.t('Display Multi-model Responses in Tabs')} +
+ +
+ { + saveSettings({ displayMultiModelResponsesInTabs }); + }} + /> +
+
+

+ {$i18n.t('Group multi-model responses into tabs.')} +

+
+ +
+
+
+ {$i18n.t('Scroll On Branch Change')} +
+ +
+ { + saveSettings({ scrollOnBranchChange }); + }} + /> +
+
+

+ {$i18n.t('Scroll to the active branch when switching response branches.')} +

+
+ +
+
+
+ {$i18n.t('Show Files on Terminal Select')} +
+ +
+ { + saveSettings({ showFilesOnTerminalSelect }); + }} + /> +
+
+

+ {$i18n.t('Open the file browser after selecting a terminal.')} +

+
+ +
+
+
+ {$i18n.t('Stylized PDF Export')} +
+ +
+ { + saveSettings({ stylizedPdfExport }); + }} + /> +
+
+

+ {$i18n.t('Use styled formatting when exporting chats to PDF.')} +

+
+ +
+
+
+ {$i18n.t('Floating Quick Actions')} +
+ +
+ {#if showFloatingActionButtons} + + {/if} + + { + saveSettings({ showFloatingActionButtons }); + }} + /> +
+
+

+ {$i18n.t('Show the floating quick-action toolbar in chat.')} +

+
+ +
+
+
+ {$i18n.t('Web Search in Chat')} +
+ + +
+

+ {$i18n.t('Set web search availability for new chats.')} +

+
+ +
{$i18n.t('Input')}
+ +
+
+
+ {$i18n.t('Enter Key Behavior')} +
+ + +
+

+ {$i18n.t('Choose whether Enter sends immediately or uses Ctrl+Enter.')} +

+
+ +
+
+
+ {$i18n.t('Rich Text Input for Chat')} +
+ +
+ { + saveSettings({ richTextInput }); + }} + /> +
+
+

+ {$i18n.t('Use the rich composer instead of a plain textarea.')} +

+
+ + {#if $config?.features?.enable_autocomplete_generation} +
+
+
+ {$i18n.t('Prompt Autocompletion')} +
+ +
+ { + saveSettings({ promptAutocomplete }); + }} + /> +
+
+

+ {$i18n.t('Suggest completions while composing prompts.')} +

+
+ {/if} + + {#if richTextInput} +
+
+
+ {$i18n.t('Show Formatting Toolbar')} +
+ +
+ { + saveSettings({ showFormattingToolbar }); + }} + /> +
+
+

+ {$i18n.t('Show formatting controls in the rich text composer.')} +

+
+ +
+
+
+ {$i18n.t('Insert Prompt as Rich Text')} +
+ +
+ { + saveSettings({ insertPromptAsRichText }); + }} + /> +
+
+

+ {$i18n.t('Paste inserted prompts as rich text when possible.')} +

+
+ {/if} + +
+
+
+ {$i18n.t('Paste Large Text as File')} +
+ +
+ { + saveSettings({ largeTextAsFile }); + }} + /> +
+
+

+ {$i18n.t('Convert long pasted text into a file attachment.')} +

+
+ +
{$i18n.t('Artifacts')}
+ +
+
+
+ {$i18n.t('Detect Artifacts Automatically')} +
+ +
+ { + saveSettings({ detectArtifacts }); + }} + /> +
+
+

+ {$i18n.t('Detect generated artifacts and show them in the artifact workspace.')} +

+
+ +
+
+
+ {$i18n.t('iframe Sandbox Allow Same Origin')} +
+ +
+ { + saveSettings({ iframeSandboxAllowSameOrigin }); + }} + /> +
+
+

+ {$i18n.t('Allow artifacts to access same-origin browser APIs inside the sandbox.')} +

+
+ +
+
+
+ {$i18n.t('iframe Sandbox Allow Forms')} +
+ +
+ { + saveSettings({ iframeSandboxAllowForms }); + }} + /> +
+
+

+ {$i18n.t('Allow forms inside sandboxed artifact iframes.')} +

+
+ +
{$i18n.t('Voice')}
+ +
+
+
+ {$i18n.t('Allow Voice Interruption in Call')} +
+ +
+ { + saveSettings({ voiceInterruption }); + }} + /> +
+
+

+ {$i18n.t('Let speech interrupt the assistant during a voice call.')} +

+
+ +
+
+
+ {$i18n.t('Display Emoji in Call')} +
+ +
+ { + saveSettings({ showEmojiInCall }); + }} + /> +
+
+

+ {$i18n.t('Show emoji feedback in the call interface.')} +

+
+ +
{$i18n.t('File')}
+ +
+
+
+ {$i18n.t('Default Upload Mode')} +
+ + +
+

+ {$i18n.t('Attach files with full content or focused retrieval by default.')} +

+
+ +
+
+
+ {$i18n.t('Image Compression')} +
+ +
+ {#if imageCompression} + + {/if} + + { + saveSettings({ imageCompression }); + }} + /> +
+
+

+ {$i18n.t('Compress uploaded images before sending or storage.')} +

+
+ + {#if imageCompression} +
+
+
+ {$i18n.t('Compress Images in Channels')} +
+ +
+ { + saveSettings({ imageCompressionInChannels }); + }} + /> +
+
+

+ {$i18n.t('Apply image compression to channel uploads too.')} +

+
+ {/if} +