@actuate-media/cms-admin 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/views/ai-settings.render.test.d.ts +2 -0
- package/dist/__tests__/views/ai-settings.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/ai-settings.render.test.js +182 -0
- package/dist/__tests__/views/ai-settings.render.test.js.map +1 -0
- package/dist/__tests__/views/seo-settings.render.test.d.ts +2 -0
- package/dist/__tests__/views/seo-settings.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/seo-settings.render.test.js +152 -0
- package/dist/__tests__/views/seo-settings.render.test.js.map +1 -0
- package/dist/__tests__/views/settings-save-footer.render.test.js +16 -9
- package/dist/__tests__/views/settings-save-footer.render.test.js.map +1 -1
- package/dist/actuate-admin.css +1 -1
- package/dist/components/MediaPickerModal.d.ts +15 -1
- package/dist/components/MediaPickerModal.d.ts.map +1 -1
- package/dist/components/MediaPickerModal.js +17 -1
- package/dist/components/MediaPickerModal.js.map +1 -1
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +3 -47
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/settings/AISettingsTab.d.ts +6 -0
- package/dist/views/settings/AISettingsTab.d.ts.map +1 -0
- package/dist/views/settings/AISettingsTab.js +138 -0
- package/dist/views/settings/AISettingsTab.js.map +1 -0
- package/dist/views/settings/AiFeaturesCard.d.ts +12 -0
- package/dist/views/settings/AiFeaturesCard.d.ts.map +1 -0
- package/dist/views/settings/AiFeaturesCard.js +14 -0
- package/dist/views/settings/AiFeaturesCard.js.map +1 -0
- package/dist/views/settings/CapabilityBadges.d.ts +5 -0
- package/dist/views/settings/CapabilityBadges.d.ts.map +1 -0
- package/dist/views/settings/CapabilityBadges.js +26 -0
- package/dist/views/settings/CapabilityBadges.js.map +1 -0
- package/dist/views/settings/ModelSelector.d.ts +12 -0
- package/dist/views/settings/ModelSelector.d.ts.map +1 -0
- package/dist/views/settings/ModelSelector.js +29 -0
- package/dist/views/settings/ModelSelector.js.map +1 -0
- package/dist/views/settings/MonthlyUsageCard.d.ts +14 -0
- package/dist/views/settings/MonthlyUsageCard.d.ts.map +1 -0
- package/dist/views/settings/MonthlyUsageCard.js +30 -0
- package/dist/views/settings/MonthlyUsageCard.js.map +1 -0
- package/dist/views/settings/ProviderConnectionCard.d.ts +13 -0
- package/dist/views/settings/ProviderConnectionCard.d.ts.map +1 -0
- package/dist/views/settings/ProviderConnectionCard.js +48 -0
- package/dist/views/settings/ProviderConnectionCard.js.map +1 -0
- package/dist/views/settings/SeoSettingsTab.d.ts +7 -0
- package/dist/views/settings/SeoSettingsTab.d.ts.map +1 -0
- package/dist/views/settings/SeoSettingsTab.js +126 -0
- package/dist/views/settings/SeoSettingsTab.js.map +1 -0
- package/dist/views/settings/useAiSettings.d.ts +143 -0
- package/dist/views/settings/useAiSettings.d.ts.map +1 -0
- package/dist/views/settings/useAiSettings.js +278 -0
- package/dist/views/settings/useAiSettings.js.map +1 -0
- package/dist/views/settings/useSeoSettings.d.ts +79 -0
- package/dist/views/settings/useSeoSettings.d.ts.map +1 -0
- package/dist/views/settings/useSeoSettings.js +278 -0
- package/dist/views/settings/useSeoSettings.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/views/ai-settings.render.test.tsx +225 -0
- package/src/__tests__/views/seo-settings.render.test.tsx +219 -0
- package/src/__tests__/views/settings-save-footer.render.test.tsx +16 -9
- package/src/components/MediaPickerModal.tsx +39 -2
- package/src/views/Settings.tsx +4 -141
- package/src/views/settings/AISettingsTab.tsx +411 -0
- package/src/views/settings/AiFeaturesCard.tsx +49 -0
- package/src/views/settings/CapabilityBadges.tsx +66 -0
- package/src/views/settings/ModelSelector.tsx +115 -0
- package/src/views/settings/MonthlyUsageCard.tsx +118 -0
- package/src/views/settings/ProviderConnectionCard.tsx +245 -0
- package/src/views/settings/SeoSettingsTab.tsx +658 -0
- package/src/views/settings/useAiSettings.ts +448 -0
- package/src/views/settings/useSeoSettings.ts +374 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AlertTriangle, CheckCircle2, KeyRound, Loader2, Lock, Plug, RefreshCw, Trash2, } from 'lucide-react';
|
|
4
|
+
import { useId, useState } from 'react';
|
|
5
|
+
import { ConfirmDangerousSettingDialog } from './components.js';
|
|
6
|
+
const INPUT_CLASS = 'w-full rounded-md border border-border bg-input-background px-3 py-2 text-base text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-60';
|
|
7
|
+
const LABEL_CLASS = 'mb-1 flex items-center gap-2 text-sm font-medium text-foreground';
|
|
8
|
+
const STATUS_META = {
|
|
9
|
+
connected: { label: 'Connected', tone: 'ok' },
|
|
10
|
+
invalid_key: { label: 'Invalid key', tone: 'error' },
|
|
11
|
+
rate_limited: { label: 'Rate limited', tone: 'warn' },
|
|
12
|
+
provider_down: { label: 'Provider unavailable', tone: 'warn' },
|
|
13
|
+
needs_setup: { label: 'Needs API key', tone: 'idle' },
|
|
14
|
+
unknown: { label: 'Not tested', tone: 'idle' },
|
|
15
|
+
};
|
|
16
|
+
function StatusPill({ status }) {
|
|
17
|
+
const meta = STATUS_META[status];
|
|
18
|
+
const toneClass = meta.tone === 'ok'
|
|
19
|
+
? 'border-success/30 bg-success/10 text-success'
|
|
20
|
+
: meta.tone === 'error'
|
|
21
|
+
? 'border-destructive/30 bg-destructive/10 text-destructive'
|
|
22
|
+
: meta.tone === 'warn'
|
|
23
|
+
? 'border-warning/30 bg-warning/10 text-warning'
|
|
24
|
+
: 'border-border bg-muted text-muted-foreground';
|
|
25
|
+
return (_jsxs("span", { className: `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium ${toneClass}`, children: [meta.tone === 'ok' ? (_jsx(CheckCircle2, { size: 12, "aria-hidden": "true" })) : meta.tone === 'error' ? (_jsx(AlertTriangle, { size: 12, "aria-hidden": "true" })) : (_jsx(Plug, { size: 12, "aria-hidden": "true" })), meta.label] }));
|
|
26
|
+
}
|
|
27
|
+
export function ProviderConnectionCard({ provider, canEdit, busy, onTest, onSyncModels, onUpdateKey, onUpdateBaseUrl, onDisconnect, }) {
|
|
28
|
+
const keyId = useId();
|
|
29
|
+
const baseUrlId = useId();
|
|
30
|
+
const [keyInput, setKeyInput] = useState('');
|
|
31
|
+
const [baseUrlInput, setBaseUrlInput] = useState(provider.baseUrl ?? '');
|
|
32
|
+
const [confirmKey, setConfirmKey] = useState(false);
|
|
33
|
+
const [confirmDisconnect, setConfirmDisconnect] = useState(false);
|
|
34
|
+
const envManaged = provider.keyManagedExternally;
|
|
35
|
+
const keyEditable = canEdit && !envManaged;
|
|
36
|
+
async function applyKey() {
|
|
37
|
+
setConfirmKey(false);
|
|
38
|
+
await onUpdateKey(keyInput.trim() === '' ? null : keyInput.trim());
|
|
39
|
+
setKeyInput('');
|
|
40
|
+
}
|
|
41
|
+
return (_jsxs("div", { className: "border-border bg-card rounded-lg border p-4", children: [_jsxs("div", { className: "mb-3 flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-foreground text-base font-medium", children: provider.displayName }), _jsx(StatusPill, { status: provider.status })] }), _jsx("p", { className: "text-muted-foreground mt-0.5 text-sm capitalize", children: provider.provider })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { type: "button", onClick: onTest, disabled: !canEdit || busy, className: "border-border text-foreground hover:bg-accent inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-60", children: [busy ? (_jsx(Loader2, { size: 14, className: "animate-spin", "aria-hidden": "true" })) : (_jsx(Plug, { size: 14, "aria-hidden": "true" })), "Test connection"] }), _jsxs("button", { type: "button", onClick: onSyncModels, disabled: !canEdit || busy, "aria-label": `Refresh models for ${provider.displayName}`, className: "border-border text-foreground hover:bg-accent inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-60", children: [_jsx(RefreshCw, { size: 14, "aria-hidden": "true" }), "Refresh models"] }), _jsx("button", { type: "button", onClick: () => setConfirmDisconnect(true), disabled: !canEdit || busy, "aria-label": `Disconnect ${provider.displayName}`, className: "text-muted-foreground hover:text-destructive inline-flex items-center rounded-md p-1.5 transition-colors disabled:cursor-not-allowed disabled:opacity-60", children: _jsx(Trash2, { size: 16, "aria-hidden": "true" }) })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsxs("label", { htmlFor: keyId, className: LABEL_CLASS, children: [_jsx(KeyRound, { size: 13, "aria-hidden": "true" }), "API key", envManaged && (_jsxs("span", { className: "text-muted-foreground border-border bg-muted inline-flex items-center gap-1 rounded-md border px-1.5 py-0.5 text-xs", children: [_jsx(Lock, { size: 11, "aria-hidden": "true" }), "Managed by environment"] }))] }), envManaged ? (_jsxs("p", { className: "text-muted-foreground text-sm", children: ["This key is set via an environment variable and can't be edited here.", provider.apiKeyLast4 ? ` Ends in ••••${provider.apiKeyLast4}.` : ''] })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { id: keyId, type: "password", autoComplete: "off", spellCheck: false, value: keyInput, disabled: !keyEditable || busy, placeholder: provider.hasKey
|
|
42
|
+
? `Key set ••••${provider.apiKeyLast4 ?? ''} — enter a new key to replace`
|
|
43
|
+
: 'Paste API key', onChange: (e) => setKeyInput(e.target.value), className: INPUT_CLASS }), _jsx("button", { type: "button", onClick: () => setConfirmKey(true), disabled: !keyEditable || busy || keyInput.trim() === '', className: "bg-brand text-brand-foreground inline-flex shrink-0 items-center rounded-md px-3 py-2 text-sm font-medium transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50", children: "Save key" })] }))] }), provider.provider === 'custom' && (_jsxs("div", { children: [_jsx("label", { htmlFor: baseUrlId, className: LABEL_CLASS, children: "Base URL" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { id: baseUrlId, type: "url", value: baseUrlInput, disabled: !canEdit || busy, placeholder: "https://api.example.com/v1", onChange: (e) => setBaseUrlInput(e.target.value), className: INPUT_CLASS }), _jsx("button", { type: "button", onClick: () => onUpdateBaseUrl(baseUrlInput.trim()), disabled: !canEdit || busy || baseUrlInput.trim() === (provider.baseUrl ?? ''), className: "border-border text-foreground hover:bg-accent inline-flex shrink-0 items-center rounded-md border px-3 py-2 text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-60", children: "Save" })] })] })), provider.lastTestedAt && (_jsxs("p", { className: "text-muted-foreground text-sm", children: ["Last tested ", new Date(provider.lastTestedAt).toLocaleString(), "."] }))] }), _jsx(ConfirmDangerousSettingDialog, { open: confirmKey, title: "Replace the stored API key?", description: "The new key is encrypted and replaces the existing one. Existing AI features will use it immediately.", confirmLabel: "Save key", onConfirm: applyKey, onCancel: () => setConfirmKey(false) }), _jsx(ConfirmDangerousSettingDialog, { open: confirmDisconnect, title: `Disconnect ${provider.displayName}?`, description: "This removes the stored key and model catalog for this provider. AI features that rely on it will stop working until you reconnect.", confirmLabel: "Disconnect", onConfirm: () => {
|
|
44
|
+
setConfirmDisconnect(false);
|
|
45
|
+
onDisconnect();
|
|
46
|
+
}, onCancel: () => setConfirmDisconnect(false) })] }));
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=ProviderConnectionCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProviderConnectionCard.js","sourceRoot":"","sources":["../../../src/views/settings/ProviderConnectionCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface SeoSettingsTabProps {
|
|
2
|
+
onNavigate?: (path: string) => void;
|
|
3
|
+
/** Current user's role. Only ADMIN can persist changes. */
|
|
4
|
+
role?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function SeoSettingsTab({ onNavigate, role }: SeoSettingsTabProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=SeoSettingsTab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SeoSettingsTab.d.ts","sourceRoot":"","sources":["../../../src/views/settings/SeoSettingsTab.tsx"],"names":[],"mappings":"AAqEA,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,cAAc,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,mBAAmB,2CAmGvE"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useState } from 'react';
|
|
4
|
+
import { AlertTriangle, CheckCircle2, ExternalLink, ImageIcon, Info, Loader2, RefreshCw, Search, Upload, X, } from 'lucide-react';
|
|
5
|
+
import { MediaPickerModal } from '../../components/MediaPickerModal.js';
|
|
6
|
+
import { SEOConfigPanel } from '../../components/SEOConfigPanel.js';
|
|
7
|
+
import { ConfirmDangerousSettingDialog, SettingsSaveBar, SettingsSourceBadge, SettingsToggleRow, SettingsValidationSummary, } from './components.js';
|
|
8
|
+
import { META_DESCRIPTION_MAX, META_DESCRIPTION_MIN, useSeoSettings, validateTitleTemplateClient, } from './useSeoSettings.js';
|
|
9
|
+
const INPUT_CLASS = 'w-full rounded-md border border-border bg-input-background px-3 py-2 text-base text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-60';
|
|
10
|
+
const LABEL_CLASS = 'mb-1 flex items-center gap-2 text-sm font-medium text-foreground';
|
|
11
|
+
const SUPPORTED_TOKENS = [
|
|
12
|
+
'%page%',
|
|
13
|
+
'%site%',
|
|
14
|
+
'%type%',
|
|
15
|
+
'%separator%',
|
|
16
|
+
'%author%',
|
|
17
|
+
'%date%',
|
|
18
|
+
'%category%',
|
|
19
|
+
'%tag%',
|
|
20
|
+
];
|
|
21
|
+
/** Sample data used to render a live title preview. */
|
|
22
|
+
function previewTitle(template, siteName) {
|
|
23
|
+
const tpl = template.trim() === '' ? '%page% — %site%' : template;
|
|
24
|
+
const vars = {
|
|
25
|
+
page: 'Sample Page Title',
|
|
26
|
+
site: siteName || 'Your Site',
|
|
27
|
+
type: 'Page',
|
|
28
|
+
separator: '|',
|
|
29
|
+
author: 'Jane Doe',
|
|
30
|
+
date: new Date().toISOString().slice(0, 10),
|
|
31
|
+
category: 'News',
|
|
32
|
+
tag: 'guide',
|
|
33
|
+
};
|
|
34
|
+
return tpl
|
|
35
|
+
.replace(/%(\w+)%/g, (_, k) => vars[k] ?? '')
|
|
36
|
+
.replace(/\s+/g, ' ')
|
|
37
|
+
.replace(/^[\s\-–—|·•]+/, '')
|
|
38
|
+
.replace(/[\s\-–—|·•]+$/, '')
|
|
39
|
+
.trim();
|
|
40
|
+
}
|
|
41
|
+
export function SeoSettingsTab({ onNavigate, role }) {
|
|
42
|
+
const canEdit = role === 'ADMIN';
|
|
43
|
+
const seo = useSeoSettings(canEdit);
|
|
44
|
+
if (seo.loading) {
|
|
45
|
+
return (_jsxs("div", { className: "space-y-4", "aria-busy": "true", "aria-live": "polite", children: [_jsx("span", { className: "sr-only", children: "Loading SEO settings\u2026" }), [0, 1, 2, 3].map((i) => (_jsxs("div", { className: "border-border bg-card rounded-lg border p-4", children: [_jsx("div", { className: "bg-muted mb-4 h-4 w-40 animate-pulse rounded" }), _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "bg-muted h-9 w-full animate-pulse rounded" }), _jsx("div", { className: "bg-muted h-9 w-full animate-pulse rounded" })] })] }, i)))] }));
|
|
46
|
+
}
|
|
47
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(SeoWorkspaceCallout, { onNavigate: onNavigate }), seo.loadError && (_jsxs("div", { role: "alert", className: "border-destructive/30 bg-destructive/10 text-destructive flex items-center gap-3 rounded-lg border p-3 text-sm", children: [_jsx(AlertTriangle, { size: 16, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { className: "flex-1", children: seo.loadError }), _jsx("button", { type: "button", onClick: seo.refetch, className: "border-destructive/40 rounded-md border px-3 py-1 transition-colors hover:opacity-80", children: "Retry" })] })), !canEdit && (_jsxs("div", { className: "border-border bg-muted text-muted-foreground flex items-center gap-2 rounded-lg border p-3 text-sm", children: [_jsx(Info, { size: 16, className: "shrink-0", "aria-hidden": "true" }), "You have read-only access to SEO defaults. Only administrators can make changes."] })), _jsx(SettingsValidationSummary, { issues: seo.issues }), _jsx(MetaDefaultsCard, { seo: seo, canEdit: canEdit }), _jsx(SearchEngineVerificationCard, { seo: seo, canEdit: canEdit }), _jsx(SitemapDefaultsCard, { seo: seo, canEdit: canEdit, onNavigate: onNavigate }), _jsxs("details", { className: "border-border bg-card group rounded-lg border", children: [_jsxs("summary", { className: "text-foreground flex cursor-pointer list-none items-center justify-between p-4 text-base font-medium", children: [_jsx("span", { children: "Advanced & per-collection SEO" }), _jsx("span", { className: "text-muted-foreground text-sm font-normal group-open:hidden", children: "Show" }), _jsx("span", { className: "text-muted-foreground hidden text-sm font-normal group-open:inline", children: "Hide" })] }), _jsxs("div", { className: "border-border border-t p-4", children: [_jsx("p", { className: "text-muted-foreground mb-4 text-sm", children: "Site name, social handle, per-collection schema, and route toggles. These share the same SEO configuration as the defaults above." }), _jsx(SEOConfigPanel, {})] })] }), seo.saveState === 'error' && seo.saveError && (_jsxs("div", { role: "alert", className: "border-destructive/30 bg-destructive/10 text-destructive flex items-center gap-3 rounded-lg border p-3 text-sm", children: [_jsx(AlertTriangle, { size: 16, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { className: "flex-1", children: seo.saveError }), _jsx("button", { type: "button", onClick: seo.save, className: "border-destructive/40 rounded-md border px-3 py-1 transition-colors hover:opacity-80", children: "Retry" })] })), _jsx(SettingsSaveBar, { dirty: seo.dirty, canEdit: canEdit, hasErrors: seo.hasErrors, saveState: seo.saveState, onSave: seo.save, onReset: seo.reset })] }));
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Full SEO workspace callout
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
function SeoWorkspaceCallout({ onNavigate }) {
|
|
53
|
+
return (_jsxs("div", { className: "border-border bg-accent/40 flex items-center gap-4 rounded-lg border p-4", children: [_jsx("span", { className: "bg-brand/10 text-brand flex h-10 w-10 shrink-0 items-center justify-center rounded-lg", children: _jsx(Search, { size: 20, "aria-hidden": "true" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-foreground text-base font-medium", children: "Looking for the full SEO workspace?" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "These are site-wide defaults. Per-page meta, audits, redirects and AI recovery live in the SEO dashboard." })] }), _jsxs("button", { type: "button", onClick: () => onNavigate?.('/seo'), className: "border-border text-foreground hover:bg-accent inline-flex shrink-0 items-center gap-2 rounded-md border px-4 py-2 text-base font-medium transition-colors", children: [_jsx(ExternalLink, { size: 16, "aria-hidden": "true" }), "Open SEO"] })] }));
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Meta Defaults
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
function MetaDefaultsCard({ seo, canEdit }) {
|
|
59
|
+
const headingId = useId();
|
|
60
|
+
const templateId = useId();
|
|
61
|
+
const descId = useId();
|
|
62
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
63
|
+
const { form, setField, issues, sources } = seo;
|
|
64
|
+
const templateIssue = issues.find((i) => i.field === 'titleTemplate');
|
|
65
|
+
const descIssue = issues.find((i) => i.field === 'defaultMetaDescription');
|
|
66
|
+
const tplValidation = validateTitleTemplateClient(form.titleTemplate);
|
|
67
|
+
const descLen = form.defaultMetaDescription.trim().length;
|
|
68
|
+
const descCountClass = descLen > META_DESCRIPTION_MAX || (descLen > 0 && descLen < META_DESCRIPTION_MIN)
|
|
69
|
+
? 'text-warning'
|
|
70
|
+
: 'text-muted-foreground';
|
|
71
|
+
const templateEditable = sources.titleTemplate?.editable !== false && canEdit;
|
|
72
|
+
const descEditable = sources.defaultMetaDescription?.editable !== false && canEdit;
|
|
73
|
+
const imageEditable = sources.defaultOgImage?.editable !== false && canEdit;
|
|
74
|
+
return (_jsxs("section", { "aria-labelledby": headingId, className: "border-border bg-card rounded-lg border p-4", children: [_jsx("h3", { id: headingId, className: "text-foreground mb-4 text-base font-medium", children: "Meta Defaults" }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsxs("label", { htmlFor: templateId, className: LABEL_CLASS, children: ["Title template", _jsx(SettingsSourceBadge, { meta: sources.titleTemplate })] }), _jsx("input", { id: templateId, type: "text", value: form.titleTemplate, disabled: !templateEditable, placeholder: "%page% \u2014 %site%", "aria-invalid": templateIssue?.severity === 'error', onChange: (e) => setField('titleTemplate', e.target.value), className: INPUT_CLASS }), _jsxs("p", { className: "text-muted-foreground mt-1 text-sm", children: ["Tokens: ", SUPPORTED_TOKENS.join(' '), ". Applied to all pages without a custom SEO title."] }), templateIssue ? (_jsx("p", { className: "text-destructive mt-1 text-sm", children: templateIssue.message })) : (tplValidation.valid && (_jsxs("p", { className: "text-muted-foreground mt-1 text-sm", children: ["Preview:", ' ', _jsx("span", { className: "text-foreground font-medium", children: previewTitle(form.titleTemplate, seo.siteName) })] })))] }), _jsxs("div", { children: [_jsxs("label", { htmlFor: descId, className: LABEL_CLASS, children: ["Default meta description", _jsx(SettingsSourceBadge, { meta: sources.defaultMetaDescription })] }), _jsx("textarea", { id: descId, rows: 3, value: form.defaultMetaDescription, disabled: !descEditable, "aria-describedby": `${descId}-count`, onChange: (e) => setField('defaultMetaDescription', e.target.value), className: `${INPUT_CLASS} resize-y` }), _jsxs("div", { className: "mt-1 flex items-center justify-between gap-2", children: [_jsx("p", { className: "text-muted-foreground text-sm", children: "Used when a page or post defines no description of its own." }), _jsxs("span", { id: `${descId}-count`, className: `text-sm ${descCountClass}`, children: [descLen, "/", META_DESCRIPTION_MAX] })] }), descIssue && _jsx("p", { className: "text-warning mt-1 text-sm", children: descIssue.message })] }), _jsxs("div", { children: [_jsxs("span", { className: LABEL_CLASS, children: ["Default social share image", _jsx(SettingsSourceBadge, { meta: sources.defaultOgImage })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "border-border bg-muted flex h-16 w-28 shrink-0 items-center justify-center overflow-hidden rounded-md border", children: form.defaultOgImage ? (
|
|
75
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
76
|
+
_jsx("img", { src: form.defaultOgImage, alt: "Default social share preview", className: "h-full w-full object-cover" })) : (_jsx(ImageIcon, { size: 20, className: "text-muted-foreground", "aria-hidden": "true" })) }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { type: "button", onClick: () => setPickerOpen(true), disabled: !imageEditable, className: "border-border text-foreground hover:bg-accent inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-60", children: [_jsx(Upload, { size: 14, "aria-hidden": "true" }), form.defaultOgImage ? 'Replace' : 'Choose image'] }), form.defaultOgImage && imageEditable && (_jsxs("button", { type: "button", onClick: () => {
|
|
77
|
+
setField('defaultOgImage', '');
|
|
78
|
+
setField('defaultSocialImageAssetId', '');
|
|
79
|
+
}, className: "text-muted-foreground hover:text-destructive inline-flex items-center gap-1 rounded-md px-2 py-1.5 text-sm transition-colors", children: [_jsx(X, { size: 14, "aria-hidden": "true" }), "Remove"] }))] }), _jsx("p", { className: "text-muted-foreground text-sm", children: "1200\u00D7630 recommended" })] })] })] })] }), _jsx(SeoPreviewCard, { title: previewTitle(form.titleTemplate, seo.siteName), url: seo.siteUrl || 'https://example.com', description: form.defaultMetaDescription.trim() || 'Your default meta description appears here.', image: form.defaultOgImage, siteName: seo.siteName }), _jsx(MediaPickerModal, { open: pickerOpen, onClose: () => setPickerOpen(false), accept: "image/*", onSelect: (url) => setField('defaultOgImage', url), onSelectItem: (item) => setField('defaultSocialImageAssetId', item.id) })] }));
|
|
80
|
+
}
|
|
81
|
+
function SeoPreviewCard({ title, url, description, image, siteName, }) {
|
|
82
|
+
return (_jsxs("div", { className: "border-border mt-4 grid gap-4 border-t pt-4 sm:grid-cols-2", children: [_jsxs("div", { className: "border-border rounded-lg border p-3", children: [_jsx("p", { className: "text-muted-foreground mb-2 text-xs font-medium tracking-wide uppercase", children: "Search preview" }), _jsx("p", { className: "text-success truncate text-sm", children: url }), _jsx("p", { className: "text-primary truncate text-lg leading-snug", children: title }), _jsx("p", { className: "text-muted-foreground line-clamp-2 text-sm", children: description })] }), _jsxs("div", { className: "border-border overflow-hidden rounded-lg border", children: [_jsx("div", { className: "bg-muted flex aspect-1200/630 items-center justify-center", children: image ? (
|
|
83
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
84
|
+
_jsx("img", { src: image, alt: "Social share preview", className: "h-full w-full object-cover" })) : (_jsx(ImageIcon, { size: 24, className: "text-muted-foreground", "aria-hidden": "true" })) }), _jsxs("div", { className: "p-3", children: [_jsx("p", { className: "text-muted-foreground truncate text-xs uppercase", children: siteName || new URL(url).hostname }), _jsx("p", { className: "text-foreground truncate text-sm font-medium", children: title }), _jsx("p", { className: "text-muted-foreground line-clamp-1 text-xs", children: description })] })] })] }));
|
|
85
|
+
}
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Search Engine Verification
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
function VerificationField({ label, field, placeholder, value, onChange, disabled, }) {
|
|
90
|
+
const id = useId();
|
|
91
|
+
const hasValue = value.trim().length > 0;
|
|
92
|
+
return (_jsxs("div", { children: [_jsxs("label", { htmlFor: id, className: LABEL_CLASS, children: [label, hasValue && (_jsxs("span", { className: "text-muted-foreground border-border bg-muted inline-flex items-center gap-1 rounded-md border px-1.5 py-0.5 text-xs", children: [_jsx(CheckCircle2, { size: 11, className: "text-success", "aria-hidden": "true" }), "Saved"] }))] }), _jsx("input", { id: id, type: "text", autoComplete: "off", spellCheck: false, value: value, disabled: disabled, placeholder: placeholder, onChange: (e) => onChange(field, e.target.value), className: INPUT_CLASS })] }));
|
|
93
|
+
}
|
|
94
|
+
function SearchEngineVerificationCard({ seo, canEdit }) {
|
|
95
|
+
const headingId = useId();
|
|
96
|
+
const { form, setField } = seo;
|
|
97
|
+
const onChange = (key, value) => setField(key, value);
|
|
98
|
+
return (_jsxs("section", { "aria-labelledby": headingId, className: "border-border bg-card rounded-lg border p-4", children: [_jsx("h3", { id: headingId, className: "text-foreground mb-1 text-base font-medium", children: "Search Engine Verification" }), _jsx("p", { className: "text-muted-foreground mb-4 text-sm", children: "Paste the verification token from each provider. We render only safe meta tags into your site's head \u2014 never arbitrary HTML." }), _jsxs("div", { className: "space-y-4", children: [_jsx(VerificationField, { label: "Google Search Console", field: "googleVerification", placeholder: "google-site-verification\u2026", value: form.googleVerification, onChange: onChange, disabled: !canEdit }), _jsx(VerificationField, { label: "Bing Webmaster Tools", field: "bingVerification", placeholder: "msvalidate.01\u2026", value: form.bingVerification, onChange: onChange, disabled: !canEdit })] }), _jsxs("details", { className: "group mt-4", children: [_jsxs("summary", { className: "text-brand cursor-pointer list-none text-sm hover:underline", children: [_jsx("span", { className: "group-open:hidden", children: "More providers" }), _jsx("span", { className: "hidden group-open:inline", children: "Fewer providers" })] }), _jsxs("div", { className: "mt-4 space-y-4", children: [_jsx(VerificationField, { label: "Yandex", field: "yandexVerification", placeholder: "yandex-verification\u2026", value: form.yandexVerification, onChange: onChange, disabled: !canEdit }), _jsx(VerificationField, { label: "Pinterest", field: "pinterestVerification", placeholder: "p:domain_verify\u2026", value: form.pinterestVerification, onChange: onChange, disabled: !canEdit }), _jsx(VerificationField, { label: "Facebook domain verification", field: "facebookDomainVerification", placeholder: "facebook-domain-verification\u2026", value: form.facebookDomainVerification, onChange: onChange, disabled: !canEdit })] })] })] }));
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Sitemap Defaults
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
function SitemapDefaultsCard({ seo, canEdit, onNavigate, }) {
|
|
104
|
+
const headingId = useId();
|
|
105
|
+
const [pendingDisable, setPendingDisable] = useState(false);
|
|
106
|
+
const { form, setField, sitemap, siteUrl, isProduction, regenerateSitemap, regenerating } = seo;
|
|
107
|
+
function requestAutoGenerate(value) {
|
|
108
|
+
// Turning auto-generation off on production can let the sitemap go stale.
|
|
109
|
+
if (!value && isProduction) {
|
|
110
|
+
setPendingDisable(true);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
setField('autoGenerateSitemap', value);
|
|
114
|
+
}
|
|
115
|
+
const lastGenerated = sitemap?.lastGeneratedAt
|
|
116
|
+
? new Date(sitemap.lastGeneratedAt).toLocaleString()
|
|
117
|
+
: 'Never';
|
|
118
|
+
return (_jsxs("section", { "aria-labelledby": headingId, className: "border-border bg-card rounded-lg border p-4", children: [_jsx("h3", { id: headingId, className: "text-foreground mb-4 text-base font-medium", children: "Sitemap" }), !siteUrl && (_jsxs("div", { className: "border-warning/30 bg-warning/10 text-warning mb-4 flex items-start gap-2 rounded-md border p-3 text-sm", children: [_jsx(AlertTriangle, { size: 16, className: "mt-0.5 shrink-0", "aria-hidden": "true" }), "Site URL is not set \u2014 sitemap URLs may be incorrect. Set it in Settings \u2192 General."] })), _jsxs("div", { className: "space-y-4", children: [_jsx(SettingsToggleRow, { label: "Auto-generate XML sitemap", description: "Rebuild the sitemap whenever content is published.", checked: form.autoGenerateSitemap, disabled: !canEdit, onChange: requestAutoGenerate }), _jsx(SettingsToggleRow, { label: "Ping search engines on publish", description: "Notify Google and Bing when the sitemap changes.", checked: form.pingOnPublish, disabled: !canEdit || !form.autoGenerateSitemap, onChange: (v) => setField('pingOnPublish', v) })] }), _jsxs("div", { className: "border-border mt-4 space-y-2 border-t pt-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 text-sm", children: [_jsx("span", { className: "text-muted-foreground", children: "Sitemap URL" }), sitemap?.sitemapUrl ? (_jsxs("a", { href: sitemap.sitemapUrl, target: "_blank", rel: "noreferrer", className: "text-brand inline-flex items-center gap-1 font-medium hover:underline", children: ["View sitemap", _jsx(ExternalLink, { size: 13, "aria-hidden": "true" })] })) : (_jsx("span", { className: "text-foreground font-medium", children: "/sitemap.xml" }))] }), _jsx(SummaryRow, { label: "Last generated", value: lastGenerated }), _jsx(SummaryRow, { label: "Included URLs", value: String(sitemap?.pagesIncluded ?? '—') }), _jsx(SummaryRow, { label: "Excluded URLs", value: String(sitemap?.pagesExcluded ?? '—') }), _jsxs("div", { className: "flex items-center justify-between gap-2 pt-1", children: [_jsxs("button", { type: "button", onClick: regenerateSitemap, disabled: !canEdit || regenerating, className: "border-border text-foreground hover:bg-accent inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-60", children: [regenerating ? (_jsx(Loader2, { size: 14, className: "animate-spin", "aria-hidden": "true" })) : (_jsx(RefreshCw, { size: 14, "aria-hidden": "true" })), "Regenerate now"] }), onNavigate && (_jsx("button", { type: "button", onClick: () => onNavigate('/seo?tab=technical'), className: "text-brand text-sm hover:underline", children: "Manage in SEO Technical \u2192" }))] })] }), _jsx(ConfirmDangerousSettingDialog, { open: pendingDisable, title: "Stop auto-generating the sitemap on production?", description: "Your sitemap may go stale as content changes, which can hurt how quickly search engines discover new pages.", confirmLabel: "Turn Off", onConfirm: () => {
|
|
119
|
+
setField('autoGenerateSitemap', false);
|
|
120
|
+
setPendingDisable(false);
|
|
121
|
+
}, onCancel: () => setPendingDisable(false) })] }));
|
|
122
|
+
}
|
|
123
|
+
function SummaryRow({ label, value }) {
|
|
124
|
+
return (_jsxs("div", { className: "flex items-center justify-between gap-2 text-sm", children: [_jsx("span", { className: "text-muted-foreground", children: label }), _jsx("span", { className: "text-foreground font-medium", children: value })] }));
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=SeoSettingsTab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SeoSettingsTab.js","sourceRoot":"","sources":["../../../src/views/settings/SeoSettingsTab.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACvC,OAAO,EACL,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,IAAI,EACJ,OAAO,EACP,SAAS,EACT,MAAM,EACN,MAAM,EACN,CAAC,GACF,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAA;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AACnE,OAAO,EACL,6BAA6B,EAC7B,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,2BAA2B,GAG5B,MAAM,qBAAqB,CAAA;AAE5B,MAAM,WAAW,GACf,wNAAwN,CAAA;AAC1N,MAAM,WAAW,GAAG,kEAAkE,CAAA;AAEtF,MAAM,gBAAgB,GAAG;IACvB,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,aAAa;IACb,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,OAAO;CACR,CAAA;AAED,uDAAuD;AACvD,SAAS,YAAY,CAAC,QAAgB,EAAE,QAAgB;IACtD,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAA;IACjE,MAAM,IAAI,GAA2B;QACnC,IAAI,EAAE,mBAAmB;QACzB,IAAI,EAAE,QAAQ,IAAI,WAAW;QAC7B,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,GAAG;QACd,MAAM,EAAE,UAAU;QAClB,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC3C,QAAQ,EAAE,MAAM;QAChB,GAAG,EAAE,OAAO;KACb,CAAA;IACD,OAAO,GAAG;SACP,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpD,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;SAC5B,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;SAC5B,IAAI,EAAE,CAAA;AACX,CAAC;AAQD,MAAM,UAAU,cAAc,CAAC,EAAE,UAAU,EAAE,IAAI,EAAuB;IACtE,MAAM,OAAO,GAAG,IAAI,KAAK,OAAO,CAAA;IAChC,MAAM,GAAG,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;IAEnC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAChB,OAAO,CACL,eAAK,SAAS,EAAC,WAAW,eAAW,MAAM,eAAW,QAAQ,aAC5D,eAAM,SAAS,EAAC,SAAS,2CAA6B,EACrD,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACvB,eAAa,SAAS,EAAC,6CAA6C,aAClE,cAAK,SAAS,EAAC,8CAA8C,GAAG,EAChE,eAAK,SAAS,EAAC,WAAW,aACxB,cAAK,SAAS,EAAC,2CAA2C,GAAG,EAC7D,cAAK,SAAS,EAAC,2CAA2C,GAAG,IACzD,KALE,CAAC,CAML,CACP,CAAC,IACE,CACP,CAAA;IACH,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,mBAAmB,IAAC,UAAU,EAAE,UAAU,GAAI,EAE9C,GAAG,CAAC,SAAS,IAAI,CAChB,eACE,IAAI,EAAC,OAAO,EACZ,SAAS,EAAC,gHAAgH,aAE1H,KAAC,aAAa,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,UAAU,iBAAa,MAAM,GAAG,EACnE,eAAM,SAAS,EAAC,QAAQ,YAAE,GAAG,CAAC,SAAS,GAAQ,EAC/C,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,CAAC,OAAO,EACpB,SAAS,EAAC,sFAAsF,sBAGzF,IACL,CACP,EAEA,CAAC,OAAO,IAAI,CACX,eAAK,SAAS,EAAC,oGAAoG,aACjH,KAAC,IAAI,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,UAAU,iBAAa,MAAM,GAAG,wFAEtD,CACP,EAED,KAAC,yBAAyB,IAAC,MAAM,EAAE,GAAG,CAAC,MAAM,GAAI,EAEjD,KAAC,gBAAgB,IAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAI,EAChD,KAAC,4BAA4B,IAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAI,EAC5D,KAAC,mBAAmB,IAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,GAAI,EAE3E,mBAAS,SAAS,EAAC,+CAA+C,aAChE,mBAAS,SAAS,EAAC,sGAAsG,aACvH,2DAA8C,EAC9C,eAAM,SAAS,EAAC,6DAA6D,qBAAY,EACzF,eAAM,SAAS,EAAC,oEAAoE,qBAE7E,IACC,EACV,eAAK,SAAS,EAAC,4BAA4B,aACzC,YAAG,SAAS,EAAC,oCAAoC,kJAG7C,EACJ,KAAC,cAAc,KAAG,IACd,IACE,EAET,GAAG,CAAC,SAAS,KAAK,OAAO,IAAI,GAAG,CAAC,SAAS,IAAI,CAC7C,eACE,IAAI,EAAC,OAAO,EACZ,SAAS,EAAC,gHAAgH,aAE1H,KAAC,aAAa,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,UAAU,iBAAa,MAAM,GAAG,EACnE,eAAM,SAAS,EAAC,QAAQ,YAAE,GAAG,CAAC,SAAS,GAAQ,EAC/C,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,CAAC,IAAI,EACjB,SAAS,EAAC,sFAAsF,sBAGzF,IACL,CACP,EAED,KAAC,eAAe,IACd,KAAK,EAAE,GAAG,CAAC,KAAK,EAChB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,GAAG,CAAC,SAAS,EACxB,SAAS,EAAE,GAAG,CAAC,SAAS,EACxB,MAAM,EAAE,GAAG,CAAC,IAAI,EAChB,OAAO,EAAE,GAAG,CAAC,KAAK,GAClB,IACE,CACP,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E,SAAS,mBAAmB,CAAC,EAAE,UAAU,EAA2C;IAClF,OAAO,CACL,eAAK,SAAS,EAAC,0EAA0E,aACvF,eAAM,SAAS,EAAC,uFAAuF,YACrG,KAAC,MAAM,IAAC,IAAI,EAAE,EAAE,iBAAc,MAAM,GAAG,GAClC,EACP,eAAK,SAAS,EAAC,QAAQ,aACrB,YAAG,SAAS,EAAC,uCAAuC,oDAAwC,EAC5F,YAAG,SAAS,EAAC,+BAA+B,0HAGxC,IACA,EACN,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,EACnC,SAAS,EAAC,2JAA2J,aAErK,KAAC,YAAY,IAAC,IAAI,EAAE,EAAE,iBAAc,MAAM,GAAG,gBAEtC,IACL,CACP,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,SAAS,gBAAgB,CAAC,EAAE,GAAG,EAAE,OAAO,EAA6C;IACnF,MAAM,SAAS,GAAG,KAAK,EAAE,CAAA;IACzB,MAAM,UAAU,GAAG,KAAK,EAAE,CAAA;IAC1B,MAAM,MAAM,GAAG,KAAK,EAAE,CAAA;IACtB,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEnD,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAA;IAC/C,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,eAAe,CAAC,CAAA;IACrE,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,wBAAwB,CAAC,CAAA;IAC1E,MAAM,aAAa,GAAG,2BAA2B,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IAErE,MAAM,OAAO,GAAG,IAAI,CAAC,sBAAsB,CAAC,IAAI,EAAE,CAAC,MAAM,CAAA;IACzD,MAAM,cAAc,GAClB,OAAO,GAAG,oBAAoB,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,oBAAoB,CAAC;QAC/E,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,uBAAuB,CAAA;IAE7B,MAAM,gBAAgB,GAAG,OAAO,CAAC,aAAa,EAAE,QAAQ,KAAK,KAAK,IAAI,OAAO,CAAA;IAC7E,MAAM,YAAY,GAAG,OAAO,CAAC,sBAAsB,EAAE,QAAQ,KAAK,KAAK,IAAI,OAAO,CAAA;IAClF,MAAM,aAAa,GAAG,OAAO,CAAC,cAAc,EAAE,QAAQ,KAAK,KAAK,IAAI,OAAO,CAAA;IAE3E,OAAO,CACL,sCAA0B,SAAS,EAAE,SAAS,EAAC,6CAA6C,aAC1F,aAAI,EAAE,EAAE,SAAS,EAAE,SAAS,EAAC,4CAA4C,8BAEpE,EAEL,eAAK,SAAS,EAAC,WAAW,aAExB,0BACE,iBAAO,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,+BAEhD,KAAC,mBAAmB,IAAC,IAAI,EAAE,OAAO,CAAC,aAAa,GAAI,IAC9C,EACR,gBACE,EAAE,EAAE,UAAU,EACd,IAAI,EAAC,MAAM,EACX,KAAK,EAAE,IAAI,CAAC,aAAa,EACzB,QAAQ,EAAE,CAAC,gBAAgB,EAC3B,WAAW,EAAC,sBAAiB,kBACf,aAAa,EAAE,QAAQ,KAAK,OAAO,EACjD,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAC1D,SAAS,EAAE,WAAW,GACtB,EACF,aAAG,SAAS,EAAC,oCAAoC,yBACtC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,0DACjC,EACH,aAAa,CAAC,CAAC,CAAC,CACf,YAAG,SAAS,EAAC,+BAA+B,YAAE,aAAa,CAAC,OAAO,GAAK,CACzE,CAAC,CAAC,CAAC,CACF,aAAa,CAAC,KAAK,IAAI,CACrB,aAAG,SAAS,EAAC,oCAAoC,yBACtC,GAAG,EACZ,eAAM,SAAS,EAAC,6BAA6B,YAC1C,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,QAAQ,CAAC,GAC1C,IACL,CACL,CACF,IACG,EAGN,0BACE,iBAAO,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,yCAE5C,KAAC,mBAAmB,IAAC,IAAI,EAAE,OAAO,CAAC,sBAAsB,GAAI,IACvD,EACR,mBACE,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,IAAI,CAAC,sBAAsB,EAClC,QAAQ,EAAE,CAAC,YAAY,sBACL,GAAG,MAAM,QAAQ,EACnC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,wBAAwB,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACnE,SAAS,EAAE,GAAG,WAAW,WAAW,GACpC,EACF,eAAK,SAAS,EAAC,8CAA8C,aAC3D,YAAG,SAAS,EAAC,+BAA+B,4EAExC,EACJ,gBAAM,EAAE,EAAE,GAAG,MAAM,QAAQ,EAAE,SAAS,EAAE,WAAW,cAAc,EAAE,aAChE,OAAO,OAAG,oBAAoB,IAC1B,IACH,EACL,SAAS,IAAI,YAAG,SAAS,EAAC,2BAA2B,YAAE,SAAS,CAAC,OAAO,GAAK,IAC1E,EAGN,0BACE,gBAAM,SAAS,EAAE,WAAW,2CAE1B,KAAC,mBAAmB,IAAC,IAAI,EAAE,OAAO,CAAC,cAAc,GAAI,IAChD,EACP,eAAK,SAAS,EAAC,yBAAyB,aACtC,cAAK,SAAS,EAAC,8GAA8G,YAC1H,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;wCACrB,qDAAqD;wCACrD,cACE,GAAG,EAAE,IAAI,CAAC,cAAc,EACxB,GAAG,EAAC,8BAA8B,EAClC,SAAS,EAAC,4BAA4B,GACtC,CACH,CAAC,CAAC,CAAC,CACF,KAAC,SAAS,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,uBAAuB,iBAAa,MAAM,GAAG,CAC7E,GACG,EACN,eAAK,SAAS,EAAC,qBAAqB,aAClC,eAAK,SAAS,EAAC,yBAAyB,aACtC,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAClC,QAAQ,EAAE,CAAC,aAAa,EACxB,SAAS,EAAC,sLAAsL,aAEhM,KAAC,MAAM,IAAC,IAAI,EAAE,EAAE,iBAAc,MAAM,GAAG,EACtC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,IAC1C,EACR,IAAI,CAAC,cAAc,IAAI,aAAa,IAAI,CACvC,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE;4DACZ,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAA;4DAC9B,QAAQ,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAA;wDAC3C,CAAC,EACD,SAAS,EAAC,8HAA8H,aAExI,KAAC,CAAC,IAAC,IAAI,EAAE,EAAE,iBAAc,MAAM,GAAG,cAE3B,CACV,IACG,EACN,YAAG,SAAS,EAAC,+BAA+B,0CAAyB,IACjE,IACF,IACF,IACF,EAGN,KAAC,cAAc,IACb,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,QAAQ,CAAC,EACrD,GAAG,EAAE,GAAG,CAAC,OAAO,IAAI,qBAAqB,EACzC,WAAW,EACT,IAAI,CAAC,sBAAsB,CAAC,IAAI,EAAE,IAAI,6CAA6C,EAErF,KAAK,EAAE,IAAI,CAAC,cAAc,EAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ,GACtB,EAEF,KAAC,gBAAgB,IACf,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EACnC,MAAM,EAAC,SAAS,EAChB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,EAClD,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,2BAA2B,EAAE,IAAI,CAAC,EAAE,CAAC,GACtE,IACM,CACX,CAAA;AACH,CAAC;AAED,SAAS,cAAc,CAAC,EACtB,KAAK,EACL,GAAG,EACH,WAAW,EACX,KAAK,EACL,QAAQ,GAOT;IACC,OAAO,CACL,eAAK,SAAS,EAAC,4DAA4D,aACzE,eAAK,SAAS,EAAC,qCAAqC,aAClD,YAAG,SAAS,EAAC,wEAAwE,+BAEjF,EACJ,YAAG,SAAS,EAAC,+BAA+B,YAAE,GAAG,GAAK,EACtD,YAAG,SAAS,EAAC,4CAA4C,YAAE,KAAK,GAAK,EACrE,YAAG,SAAS,EAAC,4CAA4C,YAAE,WAAW,GAAK,IACvE,EACN,eAAK,SAAS,EAAC,iDAAiD,aAC9D,cAAK,SAAS,EAAC,2DAA2D,YACvE,KAAK,CAAC,CAAC,CAAC;wBACP,qDAAqD;wBACrD,cAAK,GAAG,EAAE,KAAK,EAAE,GAAG,EAAC,sBAAsB,EAAC,SAAS,EAAC,4BAA4B,GAAG,CACtF,CAAC,CAAC,CAAC,CACF,KAAC,SAAS,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,uBAAuB,iBAAa,MAAM,GAAG,CAC7E,GACG,EACN,eAAK,SAAS,EAAC,KAAK,aAClB,YAAG,SAAS,EAAC,kDAAkD,YAC5D,QAAQ,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,GAChC,EACJ,YAAG,SAAS,EAAC,8CAA8C,YAAE,KAAK,GAAK,EACvE,YAAG,SAAS,EAAC,4CAA4C,YAAE,WAAW,GAAK,IACvE,IACF,IACF,CACP,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E,SAAS,iBAAiB,CAAC,EACzB,KAAK,EACL,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,QAAQ,GAQT;IACC,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAA;IACxC,OAAO,CACL,0BACE,iBAAO,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,WAAW,aACvC,KAAK,EACL,QAAQ,IAAI,CACX,gBAAM,SAAS,EAAC,qHAAqH,aACnI,KAAC,YAAY,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,cAAc,iBAAa,MAAM,GAAG,aAEjE,CACR,IACK,EACR,gBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAC,MAAM,EACX,YAAY,EAAC,KAAK,EAClB,UAAU,EAAE,KAAK,EACjB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAChD,SAAS,EAAE,WAAW,GACtB,IACE,CACP,CAAA;AACH,CAAC;AAED,SAAS,4BAA4B,CAAC,EAAE,GAAG,EAAE,OAAO,EAA6C;IAC/F,MAAM,SAAS,GAAG,KAAK,EAAE,CAAA;IACzB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAA;IAC9B,MAAM,QAAQ,GAAG,CAAC,GAA0B,EAAE,KAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAc,CAAC,CAAA;IAE7F,OAAO,CACL,sCAA0B,SAAS,EAAE,SAAS,EAAC,6CAA6C,aAC1F,aAAI,EAAE,EAAE,SAAS,EAAE,SAAS,EAAC,4CAA4C,2CAEpE,EACL,YAAG,SAAS,EAAC,oCAAoC,kJAG7C,EAEJ,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,iBAAiB,IAChB,KAAK,EAAC,uBAAuB,EAC7B,KAAK,EAAC,oBAAoB,EAC1B,WAAW,EAAC,gCAA2B,EACvC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAC9B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,CAAC,OAAO,GAClB,EACF,KAAC,iBAAiB,IAChB,KAAK,EAAC,sBAAsB,EAC5B,KAAK,EAAC,kBAAkB,EACxB,WAAW,EAAC,qBAAgB,EAC5B,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAC5B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,CAAC,OAAO,GAClB,IACE,EAEN,mBAAS,SAAS,EAAC,YAAY,aAC7B,mBAAS,SAAS,EAAC,6DAA6D,aAC9E,eAAM,SAAS,EAAC,mBAAmB,+BAAsB,EACzD,eAAM,SAAS,EAAC,0BAA0B,gCAAuB,IACzD,EACV,eAAK,SAAS,EAAC,gBAAgB,aAC7B,KAAC,iBAAiB,IAChB,KAAK,EAAC,QAAQ,EACd,KAAK,EAAC,oBAAoB,EAC1B,WAAW,EAAC,2BAAsB,EAClC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAC9B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,CAAC,OAAO,GAClB,EACF,KAAC,iBAAiB,IAChB,KAAK,EAAC,WAAW,EACjB,KAAK,EAAC,uBAAuB,EAC7B,WAAW,EAAC,uBAAkB,EAC9B,KAAK,EAAE,IAAI,CAAC,qBAAqB,EACjC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,CAAC,OAAO,GAClB,EACF,KAAC,iBAAiB,IAChB,KAAK,EAAC,8BAA8B,EACpC,KAAK,EAAC,4BAA4B,EAClC,WAAW,EAAC,oCAA+B,EAC3C,KAAK,EAAE,IAAI,CAAC,0BAA0B,EACtC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,CAAC,OAAO,GAClB,IACE,IACE,IACF,CACX,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,SAAS,mBAAmB,CAAC,EAC3B,GAAG,EACH,OAAO,EACP,UAAU,GAKX;IACC,MAAM,SAAS,GAAG,KAAK,EAAE,CAAA;IACzB,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3D,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,GAAG,GAAG,CAAA;IAE/F,SAAS,mBAAmB,CAAC,KAAc;QACzC,0EAA0E;QAC1E,IAAI,CAAC,KAAK,IAAI,YAAY,EAAE,CAAC;YAC3B,iBAAiB,CAAC,IAAI,CAAC,CAAA;YACvB,OAAM;QACR,CAAC;QACD,QAAQ,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAA;IACxC,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,EAAE,eAAe;QAC5C,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,cAAc,EAAE;QACpD,CAAC,CAAC,OAAO,CAAA;IAEX,OAAO,CACL,sCAA0B,SAAS,EAAE,SAAS,EAAC,6CAA6C,aAC1F,aAAI,EAAE,EAAE,SAAS,EAAE,SAAS,EAAC,4CAA4C,wBAEpE,EAEJ,CAAC,OAAO,IAAI,CACX,eAAK,SAAS,EAAC,wGAAwG,aACrH,KAAC,aAAa,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,iBAAiB,iBAAa,MAAM,GAAG,oGAEtE,CACP,EAED,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,iBAAiB,IAChB,KAAK,EAAC,2BAA2B,EACjC,WAAW,EAAC,oDAAoD,EAChE,OAAO,EAAE,IAAI,CAAC,mBAAmB,EACjC,QAAQ,EAAE,CAAC,OAAO,EAClB,QAAQ,EAAE,mBAAmB,GAC7B,EACF,KAAC,iBAAiB,IAChB,KAAK,EAAC,gCAAgC,EACtC,WAAW,EAAC,kDAAkD,EAC9D,OAAO,EAAE,IAAI,CAAC,aAAa,EAC3B,QAAQ,EAAE,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAC/C,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC,CAAC,GAC7C,IACE,EAEN,eAAK,SAAS,EAAC,4CAA4C,aACzD,eAAK,SAAS,EAAC,iDAAiD,aAC9D,eAAM,SAAS,EAAC,uBAAuB,4BAAmB,EACzD,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CACrB,aACE,IAAI,EAAE,OAAO,CAAC,UAAU,EACxB,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,YAAY,EAChB,SAAS,EAAC,uEAAuE,6BAGjF,KAAC,YAAY,IAAC,IAAI,EAAE,EAAE,iBAAc,MAAM,GAAG,IAC3C,CACL,CAAC,CAAC,CAAC,CACF,eAAM,SAAS,EAAC,6BAA6B,6BAAoB,CAClE,IACG,EACN,KAAC,UAAU,IAAC,KAAK,EAAC,gBAAgB,EAAC,KAAK,EAAE,aAAa,GAAI,EAC3D,KAAC,UAAU,IAAC,KAAK,EAAC,eAAe,EAAC,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,aAAa,IAAI,GAAG,CAAC,GAAI,EAClF,KAAC,UAAU,IAAC,KAAK,EAAC,eAAe,EAAC,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,aAAa,IAAI,GAAG,CAAC,GAAI,EAClF,eAAK,SAAS,EAAC,8CAA8C,aAC3D,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,CAAC,OAAO,IAAI,YAAY,EAClC,SAAS,EAAC,sLAAsL,aAE/L,YAAY,CAAC,CAAC,CAAC,CACd,KAAC,OAAO,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,cAAc,iBAAa,MAAM,GAAG,CAClE,CAAC,CAAC,CAAC,CACF,KAAC,SAAS,IAAC,IAAI,EAAE,EAAE,iBAAc,MAAM,GAAG,CAC3C,sBAEM,EACR,UAAU,IAAI,CACb,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAC/C,SAAS,EAAC,oCAAoC,+CAGvC,CACV,IACG,IACF,EAEN,KAAC,6BAA6B,IAC5B,IAAI,EAAE,cAAc,EACpB,KAAK,EAAC,iDAAiD,EACvD,WAAW,EAAC,6GAA6G,EACzH,YAAY,EAAC,UAAU,EACvB,SAAS,EAAE,GAAG,EAAE;oBACd,QAAQ,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAA;oBACtC,iBAAiB,CAAC,KAAK,CAAC,CAAA;gBAC1B,CAAC,EACD,QAAQ,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,GACxC,IACM,CACX,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,EAAE,KAAK,EAAE,KAAK,EAAoC;IACpE,OAAO,CACL,eAAK,SAAS,EAAC,iDAAiD,aAC9D,eAAM,SAAS,EAAC,uBAAuB,YAAE,KAAK,GAAQ,EACtD,eAAM,SAAS,EAAC,6BAA6B,YAAE,KAAK,GAAQ,IACxD,CACP,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings → AI state.
|
|
3
|
+
*
|
|
4
|
+
* Two persistence surfaces, mirroring the SEO tab's multi-backend pattern:
|
|
5
|
+
* - Provider connections (create / update / test / disconnect / sync models)
|
|
6
|
+
* act IMMEDIATELY against `/ai/providers/*`. These touch encrypted secrets,
|
|
7
|
+
* so they shouldn't sit in a dirty buffer waiting for a Save click.
|
|
8
|
+
* - Defaults, feature toggles, and the monthly budget are buffered and saved
|
|
9
|
+
* together via `PUT /ai/settings` through the shared SettingsSaveBar.
|
|
10
|
+
*
|
|
11
|
+
* Provider API keys are never returned by the server; we only ever receive a
|
|
12
|
+
* masked `apiKeyLast4` + a source flag.
|
|
13
|
+
*/
|
|
14
|
+
export type AiProviderKey = 'openai' | 'anthropic' | 'xai' | 'custom';
|
|
15
|
+
export type AiConnectionStatus = 'connected' | 'invalid_key' | 'rate_limited' | 'provider_down' | 'needs_setup' | 'unknown';
|
|
16
|
+
export type AiModelStatus = 'available' | 'unavailable' | 'deprecated' | 'unknown';
|
|
17
|
+
export interface AiModelView {
|
|
18
|
+
id: string;
|
|
19
|
+
provider: AiProviderKey;
|
|
20
|
+
providerModelId: string;
|
|
21
|
+
displayName: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
status: AiModelStatus;
|
|
24
|
+
capabilities: Record<string, boolean>;
|
|
25
|
+
contextWindow?: number;
|
|
26
|
+
maxOutputTokens?: number;
|
|
27
|
+
inputModalities: string[];
|
|
28
|
+
outputModalities: string[];
|
|
29
|
+
pricing?: {
|
|
30
|
+
inputPerMillion?: number;
|
|
31
|
+
outputPerMillion?: number;
|
|
32
|
+
image?: number;
|
|
33
|
+
search?: number;
|
|
34
|
+
};
|
|
35
|
+
lastSyncedAt: string;
|
|
36
|
+
source: string;
|
|
37
|
+
}
|
|
38
|
+
export interface AiProviderView {
|
|
39
|
+
id: string;
|
|
40
|
+
provider: AiProviderKey;
|
|
41
|
+
displayName: string;
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
status: AiConnectionStatus;
|
|
44
|
+
apiKeyLast4?: string;
|
|
45
|
+
baseUrl?: string;
|
|
46
|
+
organizationId?: string;
|
|
47
|
+
projectId?: string;
|
|
48
|
+
lastTestedAt?: string;
|
|
49
|
+
lastModelSyncAt?: string;
|
|
50
|
+
apiKeySource: 'environment' | 'database' | 'config';
|
|
51
|
+
keyManagedExternally: boolean;
|
|
52
|
+
hasKey: boolean;
|
|
53
|
+
createdAt: string;
|
|
54
|
+
updatedAt: string;
|
|
55
|
+
updatedBy: string;
|
|
56
|
+
}
|
|
57
|
+
export interface AiFeatureView {
|
|
58
|
+
key: string;
|
|
59
|
+
label: string;
|
|
60
|
+
description: string;
|
|
61
|
+
enabled: boolean;
|
|
62
|
+
category: string;
|
|
63
|
+
requiredCapabilities: string[];
|
|
64
|
+
}
|
|
65
|
+
export interface AiUsageView {
|
|
66
|
+
totalTokens: number;
|
|
67
|
+
promptTokens: number;
|
|
68
|
+
completionTokens: number;
|
|
69
|
+
estimatedCost?: number;
|
|
70
|
+
monthlyTokenLimit?: number;
|
|
71
|
+
periodStart: string;
|
|
72
|
+
periodEnd: string;
|
|
73
|
+
runCount: number;
|
|
74
|
+
}
|
|
75
|
+
/** The buffered (Save-bar) portion of AI settings. */
|
|
76
|
+
export interface AiSettingsForm {
|
|
77
|
+
defaultProviderId: string;
|
|
78
|
+
defaultModelId: string;
|
|
79
|
+
features: AiFeatureView[];
|
|
80
|
+
monthlyTokenLimit: string;
|
|
81
|
+
}
|
|
82
|
+
type SaveState = 'idle' | 'saving' | 'success' | 'error';
|
|
83
|
+
export interface ProviderInput {
|
|
84
|
+
provider: AiProviderKey;
|
|
85
|
+
displayName?: string;
|
|
86
|
+
apiKey?: string;
|
|
87
|
+
baseUrl?: string;
|
|
88
|
+
organizationId?: string;
|
|
89
|
+
projectId?: string;
|
|
90
|
+
enabled?: boolean;
|
|
91
|
+
test?: boolean;
|
|
92
|
+
}
|
|
93
|
+
export interface UseAiSettings {
|
|
94
|
+
loading: boolean;
|
|
95
|
+
loadError: string | null;
|
|
96
|
+
providers: AiProviderView[];
|
|
97
|
+
models: AiModelView[];
|
|
98
|
+
usage: AiUsageView | null;
|
|
99
|
+
defaultModel: AiModelView | null;
|
|
100
|
+
form: AiSettingsForm;
|
|
101
|
+
setDefaultProvider: (id: string) => void;
|
|
102
|
+
setDefaultModel: (id: string) => void;
|
|
103
|
+
setMonthlyTokenLimit: (value: string) => void;
|
|
104
|
+
toggleFeature: (key: string, enabled: boolean) => void;
|
|
105
|
+
dirty: boolean;
|
|
106
|
+
budgetError: string | null;
|
|
107
|
+
saveState: SaveState;
|
|
108
|
+
saveError: string | null;
|
|
109
|
+
save: () => Promise<void>;
|
|
110
|
+
reset: () => void;
|
|
111
|
+
refetch: () => void;
|
|
112
|
+
/** Provider connection actions (persist immediately). */
|
|
113
|
+
createProvider: (input: ProviderInput) => Promise<{
|
|
114
|
+
ok: boolean;
|
|
115
|
+
error?: string;
|
|
116
|
+
}>;
|
|
117
|
+
updateProvider: (id: string, patch: Omit<Partial<ProviderInput>, 'apiKey'> & {
|
|
118
|
+
apiKey?: string | null;
|
|
119
|
+
}) => Promise<{
|
|
120
|
+
ok: boolean;
|
|
121
|
+
error?: string;
|
|
122
|
+
}>;
|
|
123
|
+
deleteProvider: (id: string) => Promise<{
|
|
124
|
+
ok: boolean;
|
|
125
|
+
error?: string;
|
|
126
|
+
}>;
|
|
127
|
+
testProvider: (id: string) => Promise<{
|
|
128
|
+
ok: boolean;
|
|
129
|
+
status?: AiConnectionStatus;
|
|
130
|
+
message?: string;
|
|
131
|
+
error?: string;
|
|
132
|
+
}>;
|
|
133
|
+
syncModels: (id: string) => Promise<{
|
|
134
|
+
ok: boolean;
|
|
135
|
+
fromFallback?: boolean;
|
|
136
|
+
warning?: string | null;
|
|
137
|
+
error?: string;
|
|
138
|
+
}>;
|
|
139
|
+
busyProviderId: string | null;
|
|
140
|
+
}
|
|
141
|
+
export declare function useAiSettings(canEdit: boolean): UseAiSettings;
|
|
142
|
+
export {};
|
|
143
|
+
//# sourceMappingURL=useAiSettings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAiSettings.d.ts","sourceRoot":"","sources":["../../../src/views/settings/useAiSettings.ts"],"names":[],"mappings":"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"}
|