feat(workspace,settings): model menu chip in the composer, compact settings panels
The workspace home's composer takes ONE Model chip (ModelMenu: model list plus Effort) and moves AI Router / Permission / Local-New worktree into the row under it; Settings drops the General-page cost card and export row, PricingSettings and the empty states are compacted for settings panels.
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
*/
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Plus, Pencil, Trash2, Router as RouterIcon, Lock } from "lucide-react";
|
||||
import { Plus, Pencil, Trash2, Lock } from "lucide-react";
|
||||
import { api, type AiRouter, type AiRouterInput, type AiRouterModels } from "../lib/api";
|
||||
|
||||
const TIERS: (keyof AiRouterModels)[] = ["opus", "sonnet", "haiku", "smallFast", "subagent"];
|
||||
@@ -181,13 +181,8 @@ export function AiRouterConfig() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="flex items-center gap-2 text-sm font-semibold text-fg-primary">
|
||||
<RouterIcon className="h-4 w-4 text-fg-muted" />
|
||||
{t("aiRouter.title")}
|
||||
</h2>
|
||||
<p className="mt-1 max-w-2xl text-xs text-fg-muted">{t("aiRouter.description")}</p>
|
||||
</div>
|
||||
{/* The page header above already names the section. */}
|
||||
<p className="max-w-2xl text-xs text-fg-muted">{t("aiRouter.description")}</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={startAdd}
|
||||
|
||||
@@ -47,14 +47,14 @@ function DefaultModel({ toolId, saved }: { toolId: string; saved: string }) {
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-fg-muted">{t("toolTab.defaultModelHint")}</p>
|
||||
<div className="flex flex-wrap items-start gap-2">
|
||||
<div className="min-w-[16rem] flex-1">
|
||||
<div className="w-72 max-w-full">
|
||||
<ModelPicker value={value} onChange={setValue} tool={toolId} />
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={save}
|
||||
disabled={value === saved}
|
||||
className="btn-ghost text-xs disabled:opacity-50"
|
||||
className="btn-primary text-xs disabled:opacity-50"
|
||||
>
|
||||
{t("toolTab.saveDefaultModel")}
|
||||
</button>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { KeyRound, Pencil, Plus, Trash2 } from "lucide-react";
|
||||
import { Pencil, Plus, Trash2 } from "lucide-react";
|
||||
import { api, type GitCredential } from "../lib/api";
|
||||
|
||||
const INPUT =
|
||||
@@ -69,13 +69,8 @@ export function GitCredentialsConfig() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="flex items-center gap-2 text-sm font-semibold text-fg-primary">
|
||||
<KeyRound className="h-4 w-4 text-fg-muted" />
|
||||
{t("gitCreds.title")}
|
||||
</h2>
|
||||
<p className="mt-1 max-w-2xl text-xs text-fg-muted">{t("gitCreds.description")}</p>
|
||||
</div>
|
||||
{/* The page header above already names the section. */}
|
||||
<p className="max-w-2xl text-xs text-fg-muted">{t("gitCreds.description")}</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setForm({ url: "", username: "", token: "" })}
|
||||
|
||||
@@ -392,22 +392,6 @@ export function Settings({
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in space-y-8">
|
||||
{section === "general" && (
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<a
|
||||
href={api.settings.exportData()}
|
||||
download
|
||||
className="inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-md border border-border text-fg-secondary hover:text-fg-primary hover:border-border-light transition-colors"
|
||||
>
|
||||
<FileDown className="w-3.5 h-3.5" />
|
||||
{t("exportData")}
|
||||
</a>
|
||||
<button onClick={load} className="btn-ghost">
|
||||
<RefreshCw className="w-4 h-4" /> {t("common:refresh")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="px-4 py-2.5 bg-status-danger/10 border border-status-danger/20 rounded-lg text-sm text-status-danger">
|
||||
{error}
|
||||
@@ -433,35 +417,6 @@ export function Settings({
|
||||
/>
|
||||
</SettingsSection>
|
||||
|
||||
{/* Cost summary card */}
|
||||
<div className="card p-6">
|
||||
<div className="flex items-center justify-between flex-wrap gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 rounded-xl bg-status-success/10 border border-status-success/20 flex items-center justify-center">
|
||||
<DollarSign className="w-6 h-6 text-status-success" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-fg-muted">{t("common:cost.totalEstimatedCost")}</p>
|
||||
<p className="text-2xl font-semibold text-fg-primary">
|
||||
<Tip
|
||||
raw={
|
||||
totalCost !== null
|
||||
? `$${totalCost.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{totalCost !== null ? fmtCost(animatedTotalCost) : "$-.--"}
|
||||
</Tip>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right text-xs text-fg-muted">
|
||||
<p>{t("acrossSessions")}</p>
|
||||
<p>{t("basedOnUsage")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── LANES ─── */}
|
||||
<section id="lanes" className="scroll-mt-24">
|
||||
<h3 className="text-sm font-medium text-fg-secondary flex items-center gap-2 mb-1">
|
||||
@@ -752,13 +707,58 @@ export function Settings({
|
||||
<>
|
||||
{/* ─── DATA MANAGEMENT ─── */}
|
||||
<section id="data" className="scroll-mt-24">
|
||||
<h3 className="text-sm font-medium text-fg-secondary flex items-center gap-2 mb-1">
|
||||
<Database className="w-4 h-4 text-fg-muted" />
|
||||
{t("data.title")}
|
||||
</h3>
|
||||
<p className="text-xs text-fg-muted mb-4">{t("data.description")}</p>
|
||||
<div className="mb-4 flex flex-wrap items-start justify-between gap-2">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-fg-secondary flex items-center gap-2 mb-1">
|
||||
<Database className="w-4 h-4 text-fg-muted" />
|
||||
{t("data.title")}
|
||||
</h3>
|
||||
<p className="text-xs text-fg-muted">{t("data.description")}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<a
|
||||
href={api.settings.exportData()}
|
||||
download
|
||||
className="inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-md border border-border text-fg-secondary hover:text-fg-primary hover:border-border-light transition-colors"
|
||||
>
|
||||
<FileDown className="w-3.5 h-3.5" />
|
||||
{t("exportData")}
|
||||
</a>
|
||||
<button onClick={load} className="btn-ghost">
|
||||
<RefreshCw className="w-4 h-4" /> {t("common:refresh")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Cost summary card */}
|
||||
<div className="card p-6">
|
||||
<div className="flex items-center justify-between flex-wrap gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 rounded-xl bg-status-success/10 border border-status-success/20 flex items-center justify-center">
|
||||
<DollarSign className="w-6 h-6 text-status-success" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-fg-muted">{t("common:cost.totalEstimatedCost")}</p>
|
||||
<p className="text-2xl font-semibold text-fg-primary">
|
||||
<Tip
|
||||
raw={
|
||||
totalCost !== null
|
||||
? `$${totalCost.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{totalCost !== null ? fmtCost(animatedTotalCost) : "$-.--"}
|
||||
</Tip>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right text-xs text-fg-muted">
|
||||
<p>{t("acrossSessions")}</p>
|
||||
<p>{t("basedOnUsage")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card p-5 space-y-4">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<p className="text-xs text-fg-muted uppercase tracking-wider font-semibold flex-shrink-0">
|
||||
|
||||
@@ -52,8 +52,6 @@ interface Section {
|
||||
title: string;
|
||||
description: string;
|
||||
Icon: ComponentType<{ className?: string }>;
|
||||
/** Full content width (config editors and tables), else `max-w-3xl`. */
|
||||
wide?: boolean;
|
||||
render: () => ReactNode;
|
||||
}
|
||||
|
||||
@@ -119,7 +117,6 @@ export function SettingsHub() {
|
||||
id: "agents",
|
||||
group: "agents",
|
||||
Icon: Bot,
|
||||
wide: true,
|
||||
...s("agents"),
|
||||
render: () => <AiToolsSettings />,
|
||||
},
|
||||
@@ -129,7 +126,6 @@ export function SettingsHub() {
|
||||
id: "agents/claude-code",
|
||||
group: "agents" as const,
|
||||
Icon: Boxes,
|
||||
wide: true,
|
||||
...s("claudeCode"),
|
||||
render: () => <CcConfig />,
|
||||
},
|
||||
@@ -141,7 +137,6 @@ export function SettingsHub() {
|
||||
id: "agents/codex",
|
||||
group: "agents" as const,
|
||||
Icon: SquareTerminal,
|
||||
wide: true,
|
||||
...s("codex"),
|
||||
render: () => <CodexConfig />,
|
||||
},
|
||||
@@ -157,14 +152,12 @@ export function SettingsHub() {
|
||||
Icon: x.id === "antigravity" ? Orbit : Puzzle,
|
||||
title: x.label,
|
||||
description: t("settingsNav.section.tool.description", { tool: x.label }),
|
||||
wide: true,
|
||||
render: () => <AiToolConfig key={x.id} toolId={x.id} />,
|
||||
})),
|
||||
{
|
||||
id: "ai-router",
|
||||
group: "routing",
|
||||
Icon: Router,
|
||||
wide: true,
|
||||
...s("aiRouter"),
|
||||
render: () => <AiRouterConfig />,
|
||||
},
|
||||
@@ -172,13 +165,25 @@ export function SettingsHub() {
|
||||
id: "pricing",
|
||||
group: "routing",
|
||||
Icon: Coins,
|
||||
wide: true,
|
||||
...s("pricing"),
|
||||
render: () => (
|
||||
<div className="space-y-6">
|
||||
<PricingSettings vendor="claude" allowReset id="pricing-claude" />
|
||||
<PricingSettings vendor="openai" id="pricing-openai" />
|
||||
<PricingSettings vendor="google" id="pricing-google" />
|
||||
<PricingSettings
|
||||
vendor="claude"
|
||||
allowReset
|
||||
id="pricing-claude"
|
||||
heading={t("pricing.vendor.claude")}
|
||||
/>
|
||||
<PricingSettings
|
||||
vendor="openai"
|
||||
id="pricing-openai"
|
||||
heading={t("pricing.vendor.openai")}
|
||||
/>
|
||||
<PricingSettings
|
||||
vendor="google"
|
||||
id="pricing-google"
|
||||
heading={t("pricing.vendor.google")}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -304,7 +309,7 @@ export function SettingsHub() {
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div className={`min-w-0 flex-1 ${active.wide ? "" : "max-w-3xl"}`}>
|
||||
<div className="min-w-0 flex-1">
|
||||
<select
|
||||
aria-label={t("settingsNav.label")}
|
||||
value={active.id}
|
||||
|
||||
@@ -642,10 +642,10 @@ exports[`screen snapshots > Activity feed 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col items-center justify-center py-20 text-center"
|
||||
class="flex flex-col items-center justify-center text-center py-20"
|
||||
>
|
||||
<div
|
||||
class="w-14 h-14 rounded-2xl bg-surface-4 flex items-center justify-center mb-5"
|
||||
class="rounded-2xl bg-surface-4 flex items-center justify-center w-14 h-14 mb-5"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-activity w-6 h-6 text-fg-muted"
|
||||
@@ -2787,82 +2787,11 @@ exports[`screen snapshots > Claude Config 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="card overflow-x-auto mt-4"
|
||||
<p
|
||||
class="card mt-4 px-4 py-6 text-center text-xs text-fg-muted"
|
||||
>
|
||||
<table
|
||||
class="w-full min-w-[1000px]"
|
||||
>
|
||||
<thead>
|
||||
<tr
|
||||
class="border-b border-border text-left"
|
||||
>
|
||||
<th
|
||||
class="px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider"
|
||||
>
|
||||
Pattern
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider"
|
||||
>
|
||||
Model
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider text-right"
|
||||
>
|
||||
Input
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider text-right"
|
||||
>
|
||||
Output
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider text-right"
|
||||
>
|
||||
Cache Read
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider text-right"
|
||||
>
|
||||
Cache Write (5m)
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider text-right"
|
||||
>
|
||||
Cache Write (1h)
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider text-right"
|
||||
>
|
||||
Fast Input
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider text-right"
|
||||
>
|
||||
Fast Output
|
||||
</th>
|
||||
<th
|
||||
class="w-24 px-4 py-3 text-[11px] font-semibold text-fg-muted uppercase tracking-wider"
|
||||
>
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
class="divide-y divide-border"
|
||||
>
|
||||
<tr>
|
||||
<td
|
||||
class="px-4 py-6 text-center text-xs text-fg-muted"
|
||||
colspan="10"
|
||||
>
|
||||
No Claude model prices. Reset Defaults restores them.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
No Claude model prices. Reset Defaults restores them.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@@ -6833,10 +6762,10 @@ exports[`screen snapshots > Sessions 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col items-center justify-center py-20 text-center"
|
||||
class="flex flex-col items-center justify-center text-center py-20"
|
||||
>
|
||||
<div
|
||||
class="w-14 h-14 rounded-2xl bg-surface-4 flex items-center justify-center mb-5"
|
||||
class="rounded-2xl bg-surface-4 flex items-center justify-center w-14 h-14 mb-5"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-folder-open w-6 h-6 text-fg-muted"
|
||||
@@ -6875,73 +6804,6 @@ exports[`screen snapshots > Settings 1`] = `
|
||||
<div
|
||||
class="animate-fade-in space-y-8"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-end gap-2"
|
||||
>
|
||||
<a
|
||||
class="inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-md border border-border text-fg-secondary hover:text-fg-primary hover:border-border-light transition-colors"
|
||||
download=""
|
||||
href="/api/settings/export"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-file-down w-3.5 h-3.5"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
|
||||
/>
|
||||
<path
|
||||
d="M14 2v4a2 2 0 0 0 2 2h4"
|
||||
/>
|
||||
<path
|
||||
d="M12 18v-6"
|
||||
/>
|
||||
<path
|
||||
d="m9 15 3 3 3-3"
|
||||
/>
|
||||
</svg>
|
||||
Export Data
|
||||
</a>
|
||||
<button
|
||||
class="btn-ghost"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-refresh-cw w-4 h-4"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"
|
||||
/>
|
||||
<path
|
||||
d="M21 3v5h-5"
|
||||
/>
|
||||
<path
|
||||
d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"
|
||||
/>
|
||||
<path
|
||||
d="M8 16H3v5"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
<section
|
||||
class="card p-5"
|
||||
>
|
||||
@@ -6999,70 +6861,6 @@ exports[`screen snapshots > Settings 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div
|
||||
class="card p-6"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-between flex-wrap gap-4"
|
||||
>
|
||||
<div
|
||||
class="flex items-center gap-4"
|
||||
>
|
||||
<div
|
||||
class="w-12 h-12 rounded-xl bg-status-success/10 border border-status-success/20 flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-dollar-sign w-6 h-6 text-status-success"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<line
|
||||
x1="12"
|
||||
x2="12"
|
||||
y1="2"
|
||||
y2="22"
|
||||
/>
|
||||
<path
|
||||
d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p
|
||||
class="text-sm text-fg-muted"
|
||||
>
|
||||
Total Estimated Cost
|
||||
</p>
|
||||
<p
|
||||
class="text-2xl font-semibold text-fg-primary"
|
||||
>
|
||||
<span
|
||||
class="relative inline-block cursor-default"
|
||||
>
|
||||
$0.00
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-right text-xs text-fg-muted"
|
||||
>
|
||||
<p>
|
||||
Across all tracked sessions
|
||||
</p>
|
||||
<p>
|
||||
Based on per-model token usage
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section
|
||||
class="scroll-mt-24"
|
||||
id="lanes"
|
||||
@@ -8125,44 +7923,181 @@ exports[`screen snapshots > Settings 1`] = `
|
||||
class="scroll-mt-24"
|
||||
id="data"
|
||||
>
|
||||
<h3
|
||||
class="text-sm font-medium text-fg-secondary flex items-center gap-2 mb-1"
|
||||
<div
|
||||
class="mb-4 flex flex-wrap items-start justify-between gap-2"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-database w-4 h-4 text-fg-muted"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<div>
|
||||
<h3
|
||||
class="text-sm font-medium text-fg-secondary flex items-center gap-2 mb-1"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-database w-4 h-4 text-fg-muted"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<ellipse
|
||||
cx="12"
|
||||
cy="5"
|
||||
rx="9"
|
||||
ry="3"
|
||||
/>
|
||||
<path
|
||||
d="M3 5V19A9 3 0 0 0 21 19V5"
|
||||
/>
|
||||
<path
|
||||
d="M3 12A9 3 0 0 0 21 12"
|
||||
/>
|
||||
</svg>
|
||||
Data Management
|
||||
</h3>
|
||||
<p
|
||||
class="text-xs text-fg-muted"
|
||||
>
|
||||
Database info, import/export, and cleanup.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<ellipse
|
||||
cx="12"
|
||||
cy="5"
|
||||
rx="9"
|
||||
ry="3"
|
||||
/>
|
||||
<path
|
||||
d="M3 5V19A9 3 0 0 0 21 19V5"
|
||||
/>
|
||||
<path
|
||||
d="M3 12A9 3 0 0 0 21 12"
|
||||
/>
|
||||
</svg>
|
||||
Data Management
|
||||
</h3>
|
||||
<p
|
||||
class="text-xs text-fg-muted mb-4"
|
||||
>
|
||||
Database info, import/export, and cleanup.
|
||||
</p>
|
||||
<a
|
||||
class="inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-md border border-border text-fg-secondary hover:text-fg-primary hover:border-border-light transition-colors"
|
||||
download=""
|
||||
href="/api/settings/export"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-file-down w-3.5 h-3.5"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
|
||||
/>
|
||||
<path
|
||||
d="M14 2v4a2 2 0 0 0 2 2h4"
|
||||
/>
|
||||
<path
|
||||
d="M12 18v-6"
|
||||
/>
|
||||
<path
|
||||
d="m9 15 3 3 3-3"
|
||||
/>
|
||||
</svg>
|
||||
Export Data
|
||||
</a>
|
||||
<button
|
||||
class="btn-ghost"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-refresh-cw w-4 h-4"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"
|
||||
/>
|
||||
<path
|
||||
d="M21 3v5h-5"
|
||||
/>
|
||||
<path
|
||||
d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"
|
||||
/>
|
||||
<path
|
||||
d="M8 16H3v5"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="space-y-4"
|
||||
>
|
||||
<div
|
||||
class="card p-6"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-between flex-wrap gap-4"
|
||||
>
|
||||
<div
|
||||
class="flex items-center gap-4"
|
||||
>
|
||||
<div
|
||||
class="w-12 h-12 rounded-xl bg-status-success/10 border border-status-success/20 flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-dollar-sign w-6 h-6 text-status-success"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<line
|
||||
x1="12"
|
||||
x2="12"
|
||||
y1="2"
|
||||
y2="22"
|
||||
/>
|
||||
<path
|
||||
d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p
|
||||
class="text-sm text-fg-muted"
|
||||
>
|
||||
Total Estimated Cost
|
||||
</p>
|
||||
<p
|
||||
class="text-2xl font-semibold text-fg-primary"
|
||||
>
|
||||
<span
|
||||
class="relative inline-block cursor-default"
|
||||
>
|
||||
$0.00
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-right text-xs text-fg-muted"
|
||||
>
|
||||
<p>
|
||||
Across all tracked sessions
|
||||
</p>
|
||||
<p>
|
||||
Based on per-model token usage
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="card p-5 space-y-4"
|
||||
>
|
||||
@@ -9123,10 +9058,10 @@ exports[`screen snapshots > Settings — notifications 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col items-center justify-center py-20 text-center"
|
||||
class="flex flex-col items-center justify-center text-center py-8"
|
||||
>
|
||||
<div
|
||||
class="w-14 h-14 rounded-2xl bg-surface-4 flex items-center justify-center mb-5"
|
||||
class="rounded-2xl bg-surface-4 flex items-center justify-center w-10 h-10 mb-3"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-list-checks w-6 h-6 text-fg-muted"
|
||||
@@ -9160,10 +9095,10 @@ exports[`screen snapshots > Settings — notifications 1`] = `
|
||||
<h3
|
||||
class="text-base font-medium text-fg-secondary mb-2"
|
||||
>
|
||||
No alert rules yet. Add one to start monitoring for stuck agents, error bursts, idle sessions, or token spikes.
|
||||
No alert rules yet
|
||||
</h3>
|
||||
<p
|
||||
class="text-sm text-fg-muted max-w-md mb-6"
|
||||
class="text-sm text-fg-muted max-w-md "
|
||||
>
|
||||
Add a rule to start alerting on errors, inactivity, stuck agents, or token thresholds.
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user