From a8957a2e7b4c0f529e5aeb12f81d808f55f234c6 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 16 Dec 2025 09:20:09 +0000 Subject: [PATCH] feat(ui): make AI settings page more compact and user-friendly - Replace verbose info banner with streamlined layout - Add collapsible 'Advanced Model Selection' accordion for Chat/Patrol models - Make AI Patrol Settings section collapsible with inline summary badges - Compact Cost Controls into single-row inline layout - Reduce form spacing for tighter presentation - Remove unused formHelpText import Also includes: - OpenAI provider fixes for max_tokens parameters - Security setup CSRF and 401 fixes - Minor UI tweaks --- frontend-modern/src/components/AI/AIChat.tsx | 1 + .../src/components/Dashboard/Dashboard.tsx | 12 +- .../src/components/Settings/AISettings.tsx | 555 ++++++++---------- .../Settings/QuickSecuritySetup.tsx | 26 +- internal/ai/providers/anthropic.go | 11 +- internal/ai/providers/anthropic_oauth.go | 10 +- internal/ai/providers/openai.go | 105 +++- internal/api/router.go | 8 +- 8 files changed, 362 insertions(+), 366 deletions(-) diff --git a/frontend-modern/src/components/AI/AIChat.tsx b/frontend-modern/src/components/AI/AIChat.tsx index 6d4fcfd..af1d5c1 100644 --- a/frontend-modern/src/components/AI/AIChat.tsx +++ b/frontend-modern/src/components/AI/AIChat.tsx @@ -560,6 +560,7 @@ export const AIChat: Component = (props) => { // Emit event for metadata refresh if AI set a resource URL if (data.name === 'set_resource_url' && data.success) { + logger.info('[AIChat] Emitting metadata-changed event for set_resource_url'); window.dispatchEvent(new CustomEvent('pulse:metadata-changed', { detail: { source: 'ai', tool: data.name } })); diff --git a/frontend-modern/src/components/Dashboard/Dashboard.tsx b/frontend-modern/src/components/Dashboard/Dashboard.tsx index 48b7548..28022ba 100644 --- a/frontend-modern/src/components/Dashboard/Dashboard.tsx +++ b/frontend-modern/src/components/Dashboard/Dashboard.tsx @@ -304,17 +304,19 @@ export function Dashboard(props: DashboardProps) { // Load all guest metadata on mount (single API call for all guests) onMount(async () => { await refreshGuestMetadata(); - }); - // Listen for metadata changes from AI or other sources - createEffect(() => { - const handleMetadataChanged = () => { + // Listen for metadata changes from AI or other sources + const handleMetadataChanged = (event: Event) => { + console.log('[Dashboard] Metadata changed event received:', event); logger.debug('Metadata changed event received, refreshing...'); refreshGuestMetadata(); }; + console.log('[Dashboard] Adding pulse:metadata-changed listener'); window.addEventListener('pulse:metadata-changed', handleMetadataChanged); - return () => window.removeEventListener('pulse:metadata-changed', handleMetadataChanged); + + // Note: SolidJS onMount doesn't support cleanup return, so we rely on component unmount + // In practice, Dashboard is always mounted so this is fine }); // Callback to update a guest's custom URL in metadata diff --git a/frontend-modern/src/components/Settings/AISettings.tsx b/frontend-modern/src/components/Settings/AISettings.tsx index a2c000d..80e9585 100644 --- a/frontend-modern/src/components/Settings/AISettings.tsx +++ b/frontend-modern/src/components/Settings/AISettings.tsx @@ -3,7 +3,7 @@ import { createStore } from 'solid-js/store'; import { Card } from '@/components/shared/Card'; import { SectionHeader } from '@/components/shared/SectionHeader'; import { Toggle } from '@/components/shared/Toggle'; -import { formField, labelClass, controlClass, formHelpText } from '@/components/shared/Form'; +import { formField, labelClass, controlClass } from '@/components/shared/Form'; import { notificationStore } from '@/stores/notifications'; import { logger } from '@/utils/logger'; import { AIAPI } from '@/api/ai'; @@ -79,6 +79,10 @@ export const AISettings: Component = () => { const [setupOllamaUrl, setSetupOllamaUrl] = createSignal('http://localhost:11434'); const [setupSaving, setSetupSaving] = createSignal(false); + // UI state for collapsible sections - START COLLAPSED for compact view + const [showAdvancedModels, setShowAdvancedModels] = createSignal(false); + const [showPatrolSettings, setShowPatrolSettings] = createSignal(false); + const [form, setForm] = createStore({ enabled: false, provider: 'anthropic' as AIProvider, // Legacy - kept for compatibility @@ -492,16 +496,7 @@ export const AISettings: Component = () => { -
-
-

AI Assistant helps you:

-
    -
  • Diagnose infrastructure issues with context-aware analysis
  • -
  • Get remediation suggestions based on your specific environment
  • -
  • Understand alerts and metrics with plain-language explanations
  • -
-
- +
@@ -511,7 +506,7 @@ export const AISettings: Component = () => {
- {/* Model Selection */} + {/* Default Model Selection - Always visible */}
0} fallback={ @@ -565,89 +560,107 @@ export const AISettings: Component = () => { -

- Main model used when no specific override is set. {availableModels().length === 0 && 'Save API key and refresh to see available models.'} -

- {/* Chat Model Override */} -
- - 0} fallback={ - setForm('chatModel', e.currentTarget.value)} - placeholder="Leave empty to use default model" - class={controlClass()} - disabled={saving()} - /> - }> - setForm('chatModel', e.currentTarget.value)} + placeholder="Use default model" + class={controlClass()} + disabled={saving()} + /> + }> + - -

- Model for interactive AI chat. Use a more capable model for complex reasoning. -

-
- - {/* Patrol Model Override */} -
- - 0} fallback={ - setForm('patrolModel', e.currentTarget.value)} - placeholder="Leave empty to use default model" - class={controlClass()} - disabled={saving()} - /> - }> - + +
+ {/* Patrol Model */} +
+ + 0} fallback={ + setForm('patrolModel', e.currentTarget.value)} + placeholder="Use default model" + class={controlClass()} + disabled={saving()} + /> + }> + + + +
+
-

- Model for background patrol analysis. Use a cheaper/faster model to save tokens. -

{/* AI Provider Configuration - Configure API keys for all providers */} @@ -975,129 +988,81 @@ export const AISettings: Component = () => { - {/* AI Patrol & Efficiency Settings */} -
-
-