diff --git a/cptr/frontend/src/lib/components/Admin/Settings.svelte b/cptr/frontend/src/lib/components/Admin/Settings.svelte deleted file mode 100644 index 592f7c5..0000000 --- a/cptr/frontend/src/lib/components/Admin/Settings.svelte +++ /dev/null @@ -1,255 +0,0 @@ - - -
-

{$t('admin.settings')}

- - {#if loading} -
- -
- {:else} - -

{$t('admin.authentication')}

- -
- {$t('admin.allowSignUp')} - updateConfig('auth.signup_enabled', v)} - disabled={saving} - /> -
-

- {config['auth.signup_enabled'] ? $t('admin.signUpEnabled') : $t('admin.signUpDisabled')} -

- - -

{$t('admin.web')}

- - -
- {$t('admin.webEnabled')} - updateConfig('web.enabled', config['web.enabled'] === false)} - disabled={saving} - /> -
-

- {config['web.enabled'] !== false ? $t('admin.webEnabledHint') : $t('admin.webDisabledHint')} -

- - {#if config['web.enabled'] !== false} - -
- {$t('admin.webSearchProvider')} - -
- - - {@const provider = config['web.search_provider'] || 'auto'} - - {#if provider === 'auto'} -

{$t('admin.webAutoHint')}

- {:else if provider === 'exa'} -
- - saveKey('web.exa_api_key', exaKey)} - disabled={saving} - /> -

- {$t('admin.webExaHint')} -

-
- {:else if provider === 'tavily'} -
- - saveKey('web.tavily_api_key', tavilyKey)} - disabled={saving} - /> -

- {$t('admin.webTavilyHint')} -

-
- {:else if provider === 'brave'} -
- - saveKey('web.brave_api_key', braveKey)} - disabled={saving} - /> -

- {$t('admin.webBraveHint')} -

-
- {:else if provider === 'duckduckgo'} -

- {$t('admin.webDuckDuckGoNote')} -

- {:else if provider === 'perplexity'} -
- - saveKey('web.perplexity_api_key', perplexityKey)} - disabled={saving} - /> -

- {$t('admin.webPerplexityHint')} -

-
- {:else if provider === 'chat_completions'} -
-
- - saveKey('web.chat_completions_base_url', ccBaseUrl)} - disabled={saving} - /> -
-
- - saveKey('web.chat_completions_api_key', ccKey)} - disabled={saving} - /> -
-
- - saveKey('web.chat_completions_model', ccModel)} - disabled={saving} - /> -
-

- {$t('admin.webCcHint')} -

-
- {/if} - {/if} - -
- -
- {/if} -
diff --git a/cptr/frontend/src/lib/components/Admin/Users.svelte b/cptr/frontend/src/lib/components/Admin/Users.svelte index e9c7030..91de2d8 100644 --- a/cptr/frontend/src/lib/components/Admin/Users.svelte +++ b/cptr/frontend/src/lib/components/Admin/Users.svelte @@ -4,10 +4,11 @@ import CreateUserModal from './CreateUserModal.svelte'; import EditUserModal from './EditUserModal.svelte'; import { onMount } from 'svelte'; - import { listUsers } from '$lib/apis/admin'; + import { listUsers, getAdminConfig, updateConfig } from '$lib/apis/admin'; import { session } from '$lib/session'; import { t } from '$lib/i18n'; import Spinner from '$lib/components/common/Spinner.svelte'; + import ToggleSwitch from '$lib/components/common/ToggleSwitch.svelte'; interface User { user_id: string; @@ -24,6 +25,10 @@ let loading = $state(true); let page = $state(0); + // Auth config + let signupEnabled = $state(false); + let savingConfig = $state(false); + let showCreate = $state(false); let editUser = $state(null); @@ -41,6 +46,26 @@ } } + async function loadAuthConfig() { + try { + const config = await getAdminConfig(); + signupEnabled = config['auth.signup_enabled'] === true; + } catch {} + } + + async function toggleSignup(value: boolean) { + savingConfig = true; + try { + await updateConfig({ 'auth.signup_enabled': value }); + signupEnabled = value; + toast.success($t('settings.saved')); + } catch { + toast.error($t('admin.failedToSave')); + } finally { + savingConfig = false; + } + } + function handleCreated() { showCreate = false; loadUsers(); @@ -52,7 +77,10 @@ if (page >= totalPages) page = Math.max(0, totalPages - 1); } - onMount(loadUsers); + onMount(() => { + loadUsers(); + loadAuthConfig(); + });
@@ -65,6 +93,21 @@
+ +
+ +

+ {signupEnabled ? $t('admin.signUpEnabled') : $t('admin.signUpDisabled')} +

+
+ {#if loading}
diff --git a/cptr/frontend/src/lib/components/Admin/Web.svelte b/cptr/frontend/src/lib/components/Admin/Web.svelte new file mode 100644 index 0000000..40a12a5 --- /dev/null +++ b/cptr/frontend/src/lib/components/Admin/Web.svelte @@ -0,0 +1,314 @@ + + +
+

{$t('admin.web')}

+ + {#if loading} +
+ {:else} + +

Search

+ +
+ +

+ {webEnabled ? $t('admin.webEnabledHint') : $t('admin.webDisabledHint')} +

+ + {#if webEnabled} +
+ {$t('admin.webSearchProvider')} + +
+

+ {#if searchProvider === 'auto'} + {$t('admin.webAutoHint')} + {:else if searchProvider === 'duckduckgo'} + {$t('admin.webDuckDuckGoNote')} + {/if} +

+ + {#if searchProvider === 'exa'} +
+ + +

{$t('admin.webExaHint')}

+
+ {:else if searchProvider === 'tavily'} +
+ + +

{$t('admin.webTavilyHint')}

+
+ {:else if searchProvider === 'brave'} +
+ + +

{$t('admin.webBraveHint')}

+
+ {:else if searchProvider === 'perplexity'} +
+ + +

{$t('admin.webPerplexityHint')}

+
+ {:else if searchProvider === 'chat_completions'} +
+ + +
+
+ + +
+
+ + +
+

{$t('admin.webCcHint')}

+ {/if} + {/if} +
+ + +

Browser

+ +
+ +

+ Give the AI access to a web browser for navigating pages, clicking elements, and taking screenshots. +

+ + {#if browserEnabled} +
+ Provider + +
+

+ {#if browserProvider === 'local'} + Connects to Chrome via DevTools Protocol. Full interactive browsing with clicking, typing, and screenshots. + {:else if browserProvider === 'firecrawl'} + Cloud API that converts web pages to markdown. Fast extraction, no interactive browsing. + {:else} + Cloud API for LLM-driven browser tasks. Describe what you need in natural language. + {/if} +

+ + {#if browserProvider === 'local'} + + +
+ +
+ + +
+ {#if testResult} +

+ {testResult.message} +

+ {/if} +
+ +
+ +
+ + minutes +
+
+ {:else if browserProvider === 'firecrawl'} +
+ + +
+
+ + +

Change for self-hosted Firecrawl instances

+
+ {:else if browserProvider === 'browser_use'} +
+ + +
+
+ + +
+ {/if} + {/if} +
+ + +
+ +
+ {/if} +
diff --git a/cptr/frontend/src/lib/components/Icon.svelte b/cptr/frontend/src/lib/components/Icon.svelte index c9261a6..3137450 100644 --- a/cptr/frontend/src/lib/components/Icon.svelte +++ b/cptr/frontend/src/lib/components/Icon.svelte @@ -360,6 +360,13 @@ + {:else if name === 'globe'} + + + + + + {:else if name === 'microphone'} - import { toast } from 'svelte-sonner'; - import ToggleSwitch from '../common/ToggleSwitch.svelte'; - import Spinner from '../common/Spinner.svelte'; - import { onMount } from 'svelte'; - import { getAdminConfig, updateConfig } from '$lib/apis/admin'; - import { t } from '$lib/i18n'; - - let loading = $state(true); - let saving = $state(false); - let testing = $state(false); - let testResult = $state<{ ok: boolean; message: string } | null>(null); - - // Config state - let enabled = $state(false); - let provider = $state<'local' | 'firecrawl' | 'browser_use'>('local'); - let cdpUrl = $state('http://localhost:9222'); - let autoLaunch = $state(true); - let sessionTimeout = $state(10); - let firecrawlApiKey = $state(''); - let firecrawlBaseUrl = $state('https://api.firecrawl.dev'); - let browserUseApiKey = $state(''); - let browserUseBaseUrl = $state('https://api.browser-use.com'); - - onMount(async () => { - try { - const config = await getAdminConfig(); - enabled = config['browser.enabled'] === true || config['browser.enabled'] === 'true'; - provider = (config['browser.provider'] as typeof provider) || 'local'; - cdpUrl = (config['browser.cdp_url'] as string) || 'http://localhost:9222'; - autoLaunch = config['browser.auto_launch'] !== false && config['browser.auto_launch'] !== 'false'; - sessionTimeout = Number(config['browser.session_timeout_minutes']) || 10; - firecrawlApiKey = (config['browser.firecrawl_api_key'] as string) || ''; - firecrawlBaseUrl = (config['browser.firecrawl_base_url'] as string) || 'https://api.firecrawl.dev'; - browserUseApiKey = (config['browser.browser_use_api_key'] as string) || ''; - browserUseBaseUrl = (config['browser.browser_use_base_url'] as string) || 'https://api.browser-use.com'; - } catch {} - loading = false; - }); - - async function save() { - saving = true; - try { - await updateConfig({ - 'browser.enabled': enabled, - 'browser.provider': provider, - 'browser.cdp_url': cdpUrl, - 'browser.auto_launch': autoLaunch, - 'browser.session_timeout_minutes': sessionTimeout, - 'browser.firecrawl_api_key': firecrawlApiKey, - 'browser.firecrawl_base_url': firecrawlBaseUrl, - 'browser.browser_use_api_key': browserUseApiKey, - 'browser.browser_use_base_url': browserUseBaseUrl - }); - toast.success($t('settings.saved')); - } catch { - toast.error('Failed to save browser settings'); - } finally { - saving = false; - } - } - - async function testConnection() { - testing = true; - testResult = null; - try { - const resp = await fetch(`${cdpUrl}/json/version`); - if (resp.ok) { - const data = await resp.json(); - testResult = { ok: true, message: data.Browser || 'Connected' }; - } else { - testResult = { ok: false, message: `HTTP ${resp.status}` }; - } - } catch { - testResult = { ok: false, message: 'Could not connect' }; - } finally { - testing = false; - } - } - - -
-

Browser

- - {#if loading} -
- {:else} - -

Enable

- -
- -

- Give the AI access to a web browser for navigating pages, clicking elements, and taking screenshots. -

-
- - {#if enabled} - -

Provider

- -
- {#each [ - { value: 'local' as const, label: 'Local CDP' }, - { value: 'firecrawl' as const, label: 'Firecrawl' }, - { value: 'browser_use' as const, label: 'Browser-Use' } - ] as opt} - - {/each} -
-

- {#if provider === 'local'} - Connects to Chrome via DevTools Protocol. Full interactive browsing with clicking, typing, and screenshots. - {:else if provider === 'firecrawl'} - Cloud API that converts web pages to markdown. Fast extraction, no interactive browsing. - {:else} - Cloud API for LLM-driven browser tasks. Describe what you need in natural language. - {/if} -

- - - {#if provider === 'local'} -

Connection

- -
- - -
- -
- - -
- {#if testResult} -

- {testResult.message} -

- {/if} -
- -
- -
- - minutes -
-
-
- {/if} - - - {#if provider === 'firecrawl'} -

Firecrawl

- -
-
- - -
-
- - -

Change for self-hosted Firecrawl instances

-
-
- {/if} - - - {#if provider === 'browser_use'} -

Browser-Use

- -
-
- - -
-
- - -
-
- {/if} - {/if} - - -
- -
- {/if} -
diff --git a/cptr/frontend/src/lib/components/SettingsModal.svelte b/cptr/frontend/src/lib/components/SettingsModal.svelte index 936a54b..07a2b2c 100644 --- a/cptr/frontend/src/lib/components/SettingsModal.svelte +++ b/cptr/frontend/src/lib/components/SettingsModal.svelte @@ -4,7 +4,6 @@ import General from './Settings/General.svelte'; import Account from './Settings/Account.svelte'; import Keyboard from './Settings/Keyboard.svelte'; - import Browser from './Settings/Browser.svelte'; import About from './Settings/About.svelte'; import Users from './Admin/Users.svelte'; import Connections from './Admin/Connections.svelte'; @@ -12,14 +11,13 @@ import Messaging from './Admin/Messaging.svelte'; import Gateway from './Admin/Gateway.svelte'; import AudioSettings from './Admin/AudioSettings.svelte'; - import AdminSettings from './Admin/Settings.svelte'; + import AdminWeb from './Admin/Web.svelte'; import { session } from '$lib/session'; import { t } from '$lib/i18n'; type Tab = | 'general' | 'keyboard' - | 'browser' | 'account' | 'about' | 'users' @@ -28,7 +26,7 @@ | 'messaging' | 'gateway' | 'audio' - | 'admin_settings'; + | 'web'; interface Props { onclose: () => void; @@ -55,8 +53,7 @@ { id: 'messaging', label: $t('admin.messaging'), icon: 'chat-bubble' }, { id: 'gateway', label: $t('admin.gateway.tab'), icon: 'gateway' }, { id: 'audio', label: 'Audio', icon: 'microphone' }, - { id: 'browser', label: 'Browser', icon: 'browser' }, - { id: 'admin_settings', label: $t('settings.configuration'), icon: 'shield' } + { id: 'web', label: $t('admin.web'), icon: 'globe' } ]); @@ -117,8 +114,6 @@ {:else if activeTab === 'keyboard'} - {:else if activeTab === 'browser'} - {:else if activeTab === 'account'} {:else if activeTab === 'about'} @@ -135,8 +130,8 @@ {:else if activeTab === 'audio'} - {:else if activeTab === 'admin_settings'} - + {:else if activeTab === 'web'} + {/if}
diff --git a/cptr/frontend/src/lib/i18n/locales/en.json b/cptr/frontend/src/lib/i18n/locales/en.json index f0b91e0..d5b945d 100644 --- a/cptr/frontend/src/lib/i18n/locales/en.json +++ b/cptr/frontend/src/lib/i18n/locales/en.json @@ -322,7 +322,6 @@ "general.interruptDesc": "Sending a message cancels the current generation.", "settings.keyboard": "Keyboard", - "settings.configuration": "Configuration", "keyboard.title": "Keyboard Shortcuts", "keyboard.resetDefaults": "Reset defaults", diff --git a/cptr/frontend/src/lib/i18n/locales/fr.json b/cptr/frontend/src/lib/i18n/locales/fr.json index 5b6498b..02908e2 100644 --- a/cptr/frontend/src/lib/i18n/locales/fr.json +++ b/cptr/frontend/src/lib/i18n/locales/fr.json @@ -295,7 +295,6 @@ "general.queueDesc": "Les messages envoyés pendant la génération sont mis en file et envoyés après la fin.", "general.interruptDesc": "L'envoi d'un message annule la génération en cours.", "settings.keyboard": "Clavier", - "settings.configuration": "Configuration", "keyboard.title": "Raccourcis clavier", "keyboard.resetDefaults": "Réinitialiser", "keyboard.command": "Commande",