diff --git a/app/globals.css b/app/globals.css index aabc354..1f50907 100644 --- a/app/globals.css +++ b/app/globals.css @@ -66,6 +66,19 @@ html[data-theme="dark"] { --rah-backdrop: rgba(0, 0, 0, 0.85); --rah-shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(255, 255, 255, 0.05); --rah-shadow-floating: 0 4px 12px rgba(0, 0, 0, 0.4); + --settings-bg: rgba(10, 10, 10, 0.95); + --settings-sidebar-bg: rgba(0, 0, 0, 0.3); + --settings-card-bg: rgba(255, 255, 255, 0.02); + --settings-code-bg: rgba(0, 0, 0, 0.24); + --settings-border: rgba(255, 255, 255, 0.06); + --settings-border-strong: rgba(255, 255, 255, 0.14); + --settings-text: #e5e7eb; + --settings-subtext: #d1d5db; + --settings-muted: #6b7280; + --settings-active-bg: rgba(255, 255, 255, 0.04); + --settings-active-border: rgba(255, 255, 255, 0.12); + --settings-chip-bg: rgba(255, 255, 255, 0.04); + --settings-danger: #f87171; } html[data-theme="light"] { @@ -97,6 +110,19 @@ html[data-theme="light"] { --rah-backdrop: rgba(0, 0, 0, 0.5); --rah-shadow-modal: 0 10px 36px rgba(0, 0, 0, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.04); --rah-shadow-floating: 0 6px 20px rgba(0, 0, 0, 0.16); + --settings-bg: #ffffff; + --settings-sidebar-bg: #f5f5f5; + --settings-card-bg: #fafafa; + --settings-code-bg: #f3f4f6; + --settings-border: rgba(0, 0, 0, 0.08); + --settings-border-strong: rgba(0, 0, 0, 0.16); + --settings-text: #111111; + --settings-subtext: #333333; + --settings-muted: #6b7280; + --settings-active-bg: rgba(0, 0, 0, 0.06); + --settings-active-border: rgba(0, 0, 0, 0.08); + --settings-chip-bg: rgba(0, 0, 0, 0.03); + --settings-danger: #b91c1c; } /* Modern Clean Design System - RA-H */ @@ -254,4 +280,4 @@ html, body { -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis; -} \ No newline at end of file +} diff --git a/src/components/settings/ApiKeysViewer.tsx b/src/components/settings/ApiKeysViewer.tsx index 0ac93b3..51edfd5 100644 --- a/src/components/settings/ApiKeysViewer.tsx +++ b/src/components/settings/ApiKeysViewer.tsx @@ -37,8 +37,11 @@ export default function ApiKeysViewer() { OpenAI API Key {status === 'configured' ? 'Configured' : status === 'checking' ? 'Checking...' : 'Not configured'} @@ -52,7 +55,7 @@ export default function ApiKeysViewer() {
OPENAI_API_KEY=sk-your-key-here
-

+

Restart the app after changing the key.

@@ -84,8 +87,8 @@ const containerStyle: CSSProperties = { }; const featuresBoxStyle: CSSProperties = { - background: 'rgba(34, 197, 94, 0.08)', - border: '1px solid rgba(34, 197, 94, 0.2)', + background: 'var(--settings-card-bg)', + border: '1px solid var(--settings-border)', borderRadius: 8, padding: 16, marginBottom: 20, @@ -94,7 +97,7 @@ const featuresBoxStyle: CSSProperties = { const featuresHeaderStyle: CSSProperties = { fontSize: 13, fontWeight: 500, - color: '#22c55e', + color: 'var(--settings-text)', marginBottom: 8, }; @@ -102,20 +105,20 @@ const featuresListStyle: CSSProperties = { margin: 0, paddingLeft: 20, fontSize: 13, - color: '#d1d5db', + color: 'var(--settings-subtext)', lineHeight: 1.6, }; const noteStyle: CSSProperties = { marginTop: 12, fontSize: 12, - color: '#6b7280', + color: 'var(--settings-muted)', fontStyle: 'italic', }; const cardStyle: CSSProperties = { - background: 'rgba(255, 255, 255, 0.02)', - border: '1px solid rgba(255, 255, 255, 0.06)', + background: 'var(--settings-card-bg)', + border: '1px solid var(--settings-border)', borderRadius: 8, padding: 16, marginBottom: 12, @@ -131,46 +134,47 @@ const cardHeaderStyle: CSSProperties = { const cardTitleStyle: CSSProperties = { fontSize: 13, fontWeight: 500, - color: '#e5e7eb', + color: 'var(--settings-text)', }; const instructionsStyle: CSSProperties = { fontSize: 13, - color: '#d1d5db', + color: 'var(--settings-subtext)', lineHeight: 1.5, }; const codeInlineStyle: CSSProperties = { - background: 'rgba(255, 255, 255, 0.08)', + background: 'var(--settings-code-bg)', padding: '2px 6px', borderRadius: 4, fontSize: 12, fontFamily: 'monospace', - color: '#22c55e', + color: 'var(--settings-text)', }; const codeBlockStyle: CSSProperties = { - background: 'rgba(0, 0, 0, 0.4)', - border: '1px solid rgba(255, 255, 255, 0.08)', + background: 'var(--settings-code-bg)', + border: '1px solid var(--settings-border)', borderRadius: 6, padding: '10px 12px', fontSize: 13, fontFamily: 'monospace', - color: '#e5e7eb', + color: 'var(--settings-text)', marginBottom: 8, }; const helpStyle: CSSProperties = { fontSize: 12, - color: '#6b7280', + color: 'var(--settings-muted)', }; const linkStyle: CSSProperties = { - color: '#22c55e', + color: 'var(--settings-text)', textDecoration: 'none', background: 'transparent', - border: 'none', - padding: 0, + border: '1px solid var(--settings-border-strong)', + borderRadius: 6, + padding: '10px 14px', cursor: 'pointer', font: 'inherit', }; diff --git a/src/components/settings/ContextViewer.tsx b/src/components/settings/ContextViewer.tsx index eb47786..d5e969f 100644 --- a/src/components/settings/ContextViewer.tsx +++ b/src/components/settings/ContextViewer.tsx @@ -90,7 +90,7 @@ export default function ContextViewer() { disabled={loadingSettings || saving} style={{ ...toggleStyle, - background: enabled ? '#22c55e' : 'rgba(255, 255, 255, 0.1)', + background: enabled ? 'var(--settings-active-bg)' : 'var(--settings-code-bg)', }} > -

External Agents

-

+

+

Connect Claude, ChatGPT, Gemini, or any MCP-compatible assistant to your local RA-H database. - Everything stays on device—tools simply call this connector to add or search nodes. + Everything stays on device. External tools only talk to the local MCP connector you expose here.

-
+
-
Connector URL
-
+
Connector URL
+
{loading ? 'Loading…' : connectorUrl ?? 'Unavailable (MCP server not running)'}
{status.last_updated && ( -
+
Updated {new Date(status.last_updated).toLocaleTimeString()}
)} @@ -97,36 +88,24 @@ export default function ExternalAgentsPanel() { onClick={handleCopy} disabled={!connectorUrl} style={{ - background: connectorUrl ? '#22c55e' : '#1f2937', - color: connectorUrl ? '#000' : '#475569', - border: 'none', - borderRadius: '6px', - padding: '10px 16px', + ...buttonStyle, cursor: connectorUrl ? 'pointer' : 'not-allowed', - fontWeight: 600 + opacity: connectorUrl ? 1 : 0.4, }} > - {copyState === 'copied' ? 'Copied ✓' : 'Copy URL'} + {copyState === 'copied' ? 'Copied' : 'Copy URL'}
{status.last_error && ( -
- ⚠️ {status.last_error} +
+ {status.last_error}
)}
-
-

How to use in Claude or ChatGPT

-
    +
    +
    How to use in Claude or ChatGPT
    +
    1. Open the MCP / connectors settings in your assistant.
    2. Select “Add connector” → choose HTTP → paste the URL above.
    3. Give the connector a friendly name (e.g., “RA-H”).
    4. @@ -134,23 +113,15 @@ export default function ExternalAgentsPanel() {
    -
    - External agents can edit your local graph. Only enable trusted connectors and monitor their output. - Disconnect the connector or close RA-H if anything unexpected happens. +
    +
    + External agents can edit your local graph. Only enable trusted connectors and monitor their output. + Disconnect the connector or close RA-H if anything unexpected happens. +
    {error && ( -
    {error}
    +
    {error}
    )}
    @@ -173,16 +144,92 @@ export default function ExternalAgentsPanel() { function HelperCard({ title, body }: { title: string; body: string }) { return ( -
    -
    {title}
    -
    {body}
    +
    +
    {title}
    +
    {body}
    ); } + +const containerStyle: CSSProperties = { + padding: 24, + height: '100%', + overflow: 'auto', +}; + +const descStyle: CSSProperties = { + fontSize: 13, + color: 'var(--settings-muted)', + marginBottom: 20, + lineHeight: 1.5, +}; + +const cardStyle: CSSProperties = { + background: 'var(--settings-card-bg)', + border: '1px solid var(--settings-border)', + borderRadius: 8, + padding: 16, + marginBottom: 16, +}; + +const labelStyle: CSSProperties = { + fontSize: 12, + fontWeight: 500, + color: 'var(--settings-text)', + marginBottom: 8, +}; + +const statusTextStyle: CSSProperties = { + fontSize: 13, + marginTop: 4, +}; + +const microcopyStyle: CSSProperties = { + fontSize: 11, + color: 'var(--settings-muted)', + marginTop: 6, +}; + +const buttonStyle: CSSProperties = { + padding: '10px 16px', + background: 'transparent', + color: 'var(--settings-text)', + border: '1px solid var(--settings-border-strong)', + borderRadius: 6, + fontSize: 12, + fontWeight: 500, +}; + +const stepsStyle: CSSProperties = { + paddingLeft: 20, + lineHeight: 1.6, + color: 'var(--settings-subtext)', + fontSize: 13, + margin: 0, +}; + +const helperTextStyle: CSSProperties = { + fontSize: 12, + color: 'var(--settings-subtext)', + lineHeight: 1.5, +}; + +const helperCardStyle: CSSProperties = { + border: '1px solid var(--settings-border)', + borderRadius: 8, + padding: 14, + background: 'var(--settings-card-bg)', +}; + +const helperCardTitleStyle: CSSProperties = { + fontWeight: 600, + marginBottom: 6, + color: 'var(--settings-text)', + fontSize: 13, +}; + +const helperCardBodyStyle: CSSProperties = { + color: 'var(--settings-subtext)', + fontSize: 13, + lineHeight: 1.5, +}; diff --git a/src/components/settings/SettingsModal.tsx b/src/components/settings/SettingsModal.tsx index 7eec010..c1801c2 100644 --- a/src/components/settings/SettingsModal.tsx +++ b/src/components/settings/SettingsModal.tsx @@ -1,22 +1,12 @@ "use client"; -import { useEffect, useState } from 'react'; +import { useEffect, useState, type CSSProperties } from 'react'; import { createPortal } from 'react-dom'; -import LogsViewer from './LogsViewer'; -import ToolsViewer from './ToolsViewer'; import ApiKeysViewer from './ApiKeysViewer'; -import DatabaseViewer from './DatabaseViewer'; import ExternalAgentsPanel from './ExternalAgentsPanel'; import ContextViewer from './ContextViewer'; -import SkillsViewer from './GuidesViewer'; -export type SettingsTab = - | 'logs' - | 'tools' - | 'guides' - | 'apikeys' - | 'database' - | 'context' - | 'agents'; + +export type SettingsTab = 'apikeys' | 'context' | 'agents'; interface SettingsModalProps { isOpen: boolean; @@ -24,17 +14,26 @@ interface SettingsModalProps { initialTab?: SettingsTab; } -type TabType = SettingsTab; +const DEFAULT_TAB: SettingsTab = 'apikeys'; +const TAB_ORDER: SettingsTab[] = ['apikeys', 'context', 'agents']; +const TAB_LABELS: Record = { + apikeys: 'API Keys', + context: 'Context', + agents: 'Agents', +}; + +function isSettingsTab(value: unknown): value is SettingsTab { + return typeof value === 'string' && TAB_ORDER.includes(value as SettingsTab); +} export default function SettingsModal({ isOpen, onClose, initialTab }: SettingsModalProps) { - const [activeTab, setActiveTab] = useState('logs'); + const [activeTab, setActiveTab] = useState(DEFAULT_TAB); + useEffect(() => { if (!isOpen) return; const handleEscape = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - onClose(); - } + if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', handleEscape); @@ -42,271 +41,55 @@ export default function SettingsModal({ isOpen, onClose, initialTab }: SettingsM }, [isOpen, onClose]); useEffect(() => { - if (!isOpen || !initialTab) return; - setActiveTab(initialTab); + if (!isOpen) return; + setActiveTab(isSettingsTab(initialTab) ? initialTab : DEFAULT_TAB); }, [initialTab, isOpen]); if (!isOpen) return null; - const modalContent = ( -
    -
    e.stopPropagation()} - > - {/* Sidebar */} -
    -
    - Settings -
    -