diff --git a/apps/web/src/components/Settings.tsx b/apps/web/src/components/Settings.tsx index 50dca83..df96fef 100644 --- a/apps/web/src/components/Settings.tsx +++ b/apps/web/src/components/Settings.tsx @@ -20,7 +20,7 @@ import { } from "@pubscript/ui"; import type { ProviderKind, ThinkingEffort } from "@pubscript/ai"; -type SettingsTab = "appearance" | "writing" | "ai" | "server"; +type SettingsTab = "appearance" | "writing" | "providers" | "agents" | "server"; const ACCENT_PRESETS = [ "#7c8bf5", @@ -290,14 +290,36 @@ export function Settings() { return (
-
-

Settings

-
+
+

Settings

+
{( [ { id: "appearance", label: "🎨 Appearance & Themes" }, { id: "writing", label: "📁 Projects & Writing" }, - { id: "ai", label: "✨ AI Providers & Agents" }, + { id: "providers", label: "🔌 AI Providers" }, + { id: "agents", label: "🤖 AI Agents" }, { id: "server", label: "🖧 Server & System" }, ] as const ).map((tab) => { @@ -310,18 +332,22 @@ export function Settings() { aria-selected={isActive} onClick={() => setActiveTab(tab.id)} style={{ - padding: "7px 14px", + display: "inline-flex", + alignItems: "center", + gap: 6, + padding: "8px 16px", borderRadius: 8, border: isActive ? "1.5px solid var(--ps-accent)" : "1px solid var(--ps-border)", background: isActive - ? "color-mix(in srgb, var(--ps-accent) 16%, var(--ps-surface-2))" + ? "color-mix(in srgb, var(--ps-accent) 18%, var(--ps-surface-2))" : "var(--ps-surface)", color: "var(--ps-text)", fontWeight: isActive ? 600 : 500, - fontSize: 13, + fontSize: 13.5, cursor: "pointer", + whiteSpace: "nowrap", }} > {tab.label} @@ -734,291 +760,292 @@ export function Settings() {
)} - {/* TAB 3: AI PROVIDERS & AGENTS */} - {activeTab === "ai" && ( - <> -
-

AI Providers & Connection

-

- Configure local AI servers (Ollama, llama.cpp, LM Studio) or cloud endpoints (OpenAI, Anthropic, Google Gemini, OpenRouter). -

+ {/* TAB 3: AI PROVIDERS */} + {activeTab === "providers" && ( +
+

AI Providers & Connection

+

+ Configure local AI servers (Ollama, llama.cpp, LM Studio) or cloud endpoints (OpenAI, Anthropic, Google Gemini, OpenRouter). +

-
-
-
- updateProvider(currentProvider.id, { name: e.target.value })} - /> +
+
+
updateProvider(currentProvider.id, { baseUrl: e.target.value })} - /> - updateProvider(currentProvider.id, { apiKey: e.target.value })} - /> - { - const list = e.target.value - .split(",") - .map((x) => x.trim()) - .filter(Boolean); - if (list.length > 0) { - updateProvider(currentProvider.id, { - models: list, - defaultModel: list.includes(currentProvider.defaultModel) - ? currentProvider.defaultModel - : list[0]!, + const id = e.target.value; + setSelectedProviderId(id); + const found = s.aiProviders.find((p) => p.id === id); + if (found) { + set({ + activeProviderId: id, + activeModel: found.defaultModel || found.models[0] || "", }); } }} + options={s.aiProviders.map((p) => ({ + value: p.id, + label: `${p.name} (${p.kind})`, + }))} /> -
-
-
- -
-

AI Agents & Role Prompts

-

- Customize temperature, context window (ctx), max output tokens, reasoning effort, and system prompts for each AI co-writing agent. -

- -
-
-
- updateAgent(currentAgent.id, { name: e.target.value })} - /> -
- - - updateAgent(currentAgent.id, { temperature: Number(e.target.value) }) - } - /> - - Lower (0.2) = precise editing · Higher (0.8+) = creative brainstorming - -
- - updateAgent(currentAgent.id, { maxTokens: Number(e.target.value) }) - } - options={[ - { value: "512", label: "512 tokens" }, - { value: "1024", label: "1,024 tokens" }, - { value: "2048", label: "2,048 tokens" }, - { value: "4096", label: "4,096 tokens" }, - { value: "8192", label: "8,192 tokens" }, - ]} - /> -
- - -
- {currentAgent.thinkingEnabled && ( -