diff --git a/src/pages/Setup/index.tsx b/src/pages/Setup/index.tsx index f767fe5a5..c0f1d11a6 100644 --- a/src/pages/Setup/index.tsx +++ b/src/pages/Setup/index.tsx @@ -18,7 +18,6 @@ import { CheckCircle2, XCircle, ExternalLink, - BookOpen, Copy, } from 'lucide-react'; import { TitleBar } from '@/components/layout/TitleBar'; @@ -32,14 +31,6 @@ import { useSettingsStore } from '@/stores/settings'; import { useTranslation } from 'react-i18next'; import { SUPPORTED_LANGUAGES } from '@/i18n'; import { toast } from 'sonner'; -import { - CHANNEL_META, - getPrimaryChannels, - type ChannelType, - type ChannelMeta, - type ChannelConfigField, -} from '@/types/channel'; - interface SetupStep { id: string; title: string; @@ -50,9 +41,8 @@ const STEP = { WELCOME: 0, RUNTIME: 1, PROVIDER: 2, - CHANNEL: 3, - INSTALLING: 4, - COMPLETE: 5, + INSTALLING: 3, + COMPLETE: 4, } as const; const steps: SetupStep[] = [ @@ -71,11 +61,6 @@ const steps: SetupStep[] = [ title: 'AI Provider', description: 'Configure your AI service', }, - { - id: 'channel', - title: 'Connect a Channel', - description: 'Connect a messaging platform (optional)', - }, { id: 'installing', title: 'Setting Up', @@ -152,8 +137,6 @@ export function Setup() { return runtimeChecksPassed; case STEP.PROVIDER: return providerConfigured; - case STEP.CHANNEL: - return true; // Always allow proceeding — channel step is optional case STEP.INSTALLING: return false; // Cannot manually proceed, auto-proceeds when done case STEP.COMPLETE: @@ -259,7 +242,6 @@ export function Setup() { onConfiguredChange={setProviderConfigured} /> )} - {safeStepIndex === STEP.CHANNEL && } {safeStepIndex === STEP.INSTALLING && (
- {safeStepIndex === STEP.CHANNEL && ( - - )} - {!isLastStep && safeStepIndex !== STEP.RUNTIME && safeStepIndex !== STEP.CHANNEL && ( + {!isLastStep && safeStepIndex !== STEP.RUNTIME && ( @@ -1347,263 +1324,6 @@ function ProviderContent({ ); } -// ==================== Setup Channel Content ==================== - -function SetupChannelContent() { - const { t } = useTranslation(['setup', 'channels']); - const [selectedChannel, setSelectedChannel] = useState(null); - const [configValues, setConfigValues] = useState>({}); - const [showSecrets, setShowSecrets] = useState>({}); - const [saving, setSaving] = useState(false); - const [saved, setSaved] = useState(false); - const [validationError, setValidationError] = useState(null); - - const meta: ChannelMeta | null = selectedChannel ? CHANNEL_META[selectedChannel] : null; - const primaryChannels = getPrimaryChannels(); - - useEffect(() => { - let cancelled = false; - (async () => { - if (!selectedChannel) return; - try { - const result = await window.electron.ipcRenderer.invoke( - 'channel:getFormValues', - selectedChannel - ) as { success: boolean; values?: Record }; - if (cancelled) return; - if (result.success && result.values) { - setConfigValues(result.values); - } else { - setConfigValues({}); - } - } catch { - if (!cancelled) { - setConfigValues({}); - } - } - })(); - return () => { cancelled = true; }; - }, [selectedChannel]); - - const isFormValid = () => { - if (!meta) return false; - return meta.configFields - .filter((f: ChannelConfigField) => f.required) - .every((f: ChannelConfigField) => configValues[f.key]?.trim()); - }; - - const handleSave = async () => { - if (!selectedChannel || !meta || !isFormValid()) return; - - setSaving(true); - setValidationError(null); - - try { - // Validate credentials first - const validation = await window.electron.ipcRenderer.invoke( - 'channel:validateCredentials', - selectedChannel, - configValues - ) as { success: boolean; valid?: boolean; errors?: string[]; details?: Record }; - - if (!validation.valid) { - setValidationError((validation.errors || ['Validation failed']).join(', ')); - setSaving(false); - return; - } - - // Save config - await window.electron.ipcRenderer.invoke('channel:saveConfig', selectedChannel, { ...configValues }); - - const botName = validation.details?.botUsername ? ` (@${validation.details.botUsername})` : ''; - toast.success(`${meta.name} configured${botName}`); - setSaved(true); - } catch (error) { - setValidationError(String(error)); - } finally { - setSaving(false); - } - }; - - // Already saved — show success - if (saved) { - return ( -
-
-

- {t('channel.connected', { name: meta?.name || 'Channel' })} -

-

- {t('channel.connectedDesc')} -

- -
- ); - } - - // Channel type not selected — show picker - if (!selectedChannel) { - return ( -
-
-
📡
-

{t('channel.title')}

-

- {t('channel.subtitle')} -

-
-
- {primaryChannels.map((type) => { - const channelMeta = CHANNEL_META[type]; - if (channelMeta.connectionType !== 'token') return null; - return ( - - ); - })} -
-
- ); - } - - // Channel selected — show config form - return ( -
-
- -
-

- {meta?.icon} {t('channel.configure', { name: meta?.name })} -

-

{t(meta?.description || '')}

-
-
- - {/* Instructions */} -
-
-

{t('channel.howTo')}

- {meta?.docsUrl && ( - - )} -
-
    - {meta?.instructions.map((inst, i) => ( -
  1. {t(inst)}
  2. - ))} -
-
- - {/* Config fields */} - {meta?.configFields.map((field: ChannelConfigField) => { - const isPassword = field.type === 'password'; - return ( -
- -
- setConfigValues((prev) => ({ ...prev, [field.key]: e.target.value }))} - autoComplete="off" - className="font-mono text-sm bg-background border-input" - /> - {isPassword && ( - - )} -
- {field.description && ( -

{t(field.description)}

- )} -
- ); - })} - - {/* Validation error */} - {validationError && ( -
- - {validationError} -
- )} - - {/* Save button */} - -
- ); -} - // NOTE: SkillsContent component removed - auto-install essential skills // Installation status for each skill