@burdenoff/microfe-vibecontrols 2026.530.8 → 2026.531.2
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/components/actions/ActionSchedulesList.js +3 -3
- package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js +4 -4
- package/dist/components/agents/AgentForm.js +6 -6
- package/dist/components/agents/OpenInBrowserEditorButton.js +3 -3
- package/dist/components/agents/OpenInEditorMenu.js +8 -8
- package/dist/components/agents/backup/AgentBackupTab.js +3 -3
- package/dist/components/analytics/SandboxAnalyticsSection.js +4 -4
- package/dist/components/docs/DocsSettingsPanel.js +3 -3
- package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
- package/dist/components/docs/DocsTabPanel.js +11 -11
- package/dist/components/docs/DocsTabPanel.js.map +1 -1
- package/dist/components/notes/NoteEditor.js +5 -5
- package/dist/components/notes/NotesTabPanel.js +84 -84
- package/dist/components/notes/NotesTabPanel.js.map +1 -1
- package/dist/components/sessions/SessionShareDialog.js +5 -5
- package/dist/components/shared/LinkToVibePanel.js +3 -3
- package/dist/components/tunnels/TunnelCreateWizard.js +109 -109
- package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
- package/dist/components/vibe/VibeAgentSwitcher.js +8 -8
- package/dist/components/vibe/VibeLinkedEntitiesPanel.js +3 -3
- package/dist/components/vibecalendar/ScheduleTaskModal.js +3 -3
- package/dist/components/vibedeck/VibeDeckButtonForm.js +4 -4
- package/dist/components/vibedeck/VibeDeckShareDialog.js +3 -3
- package/dist/generated/wspace-operations.js +54 -36
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/hooks/useAgentEvents.js +73 -93
- package/dist/hooks/useAgentEvents.js.map +1 -1
- package/dist/hooks/usePluginAvailability.js +42 -42
- package/dist/index.js +10 -10
- package/dist/pages/ActionsPage.js +168 -168
- package/dist/pages/ActionsPage.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +827 -817
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AgentsPage.js +296 -296
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js +36 -36
- package/dist/pages/CatalogPage.js +12 -12
- package/dist/pages/ComponentDetailPage.js +256 -256
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js +12 -12
- package/dist/pages/SessionDetailsPage.js +140 -140
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +701 -701
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +341 -341
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js +25 -25
- package/dist/pages/VibeDeckPage.js +322 -322
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibesPage.js +342 -342
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/ai/AIBookmarksPage.js +4 -4
- package/dist/pages/tunnels/AuditTab.js +3 -3
- package/dist/pages/tunnels/DomainsTab.js +5 -5
- package/dist/pages/tunnels/ProvidersTab.js +3 -3
- package/dist/pages/tunnels/SessionsTab.js +3 -3
- package/dist/pages/tunnels/TunnelDetailsPage.js +3 -3
- package/dist/pages/tunnels/TunnelsTab.js +59 -59
- package/dist/pages/tunnels/TunnelsTab.js.map +1 -1
- package/dist/pages/tunnels/UsageTab.js +3 -3
- package/dist/pages/tunnels/tabs/TunnelAuditTab.js +3 -3
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +4 -4
- package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +4 -4
- package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +5 -5
- package/dist/pages/tunnels/tabs/TunnelUsageTab.js +4 -4
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useGetScheduledJobsForCalendarQuery as e } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useTr as t } from "../../shared/hooks/useTr.js";
|
|
3
3
|
import { useMemo as n } from "react";
|
|
4
4
|
import { useI18n as r } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
5
5
|
import { AlarmClock as i, ArrowUpRight as a, Plus as o } from "lucide-react";
|
|
@@ -41,7 +41,7 @@ function h(e) {
|
|
|
41
41
|
return (t) => t.scheduleType === "CRON" && t.cronExpression ? t.cronExpression : t.scheduleType === "ONE_TIME" && t.scheduledAt ? e(t.scheduledAt) : p[t.scheduleType] ?? t.scheduleType;
|
|
42
42
|
}
|
|
43
43
|
function g({ actionId: p, workspaceId: g, actionName: _, onClose: v }) {
|
|
44
|
-
let y =
|
|
44
|
+
let y = t(), { locale: b } = r(), x = s(), S = n(() => m(b), [b]), C = n(() => h(S), [S]), { data: w, loading: T, error: E } = e({
|
|
45
45
|
variables: {
|
|
46
46
|
workspaceId: g,
|
|
47
47
|
pagination: { first: 100 }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useDeleteAgentMessageBookmarkMutation as e, useMyAgentMessageBookmarksQuery as t, useUpdateAgentMessageBookmarkMutation as n } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useTr as r } from "../../shared/hooks/useTr.js";
|
|
3
3
|
import { relativeTime as i } from "../../utils/relativeTime.js";
|
|
4
4
|
import { useState as a } from "react";
|
|
5
5
|
import { Bookmark as o, Check as s, ChevronRight as c, ExternalLink as l, Pencil as u, Trash2 as d, X as f } from "lucide-react";
|
|
@@ -13,11 +13,11 @@ function _(e) {
|
|
|
13
13
|
return t.length > g ? `${t.slice(0, g)}…` : t;
|
|
14
14
|
}
|
|
15
15
|
var v = ({ open: g, onClose: v, sessionId: y, messages: b, onJump: x }) => {
|
|
16
|
-
let S = p(), C =
|
|
16
|
+
let S = p(), C = r(), [w, T] = a(null), [E, D] = a(""), { data: O, loading: k } = t({
|
|
17
17
|
variables: { filter: { sessionId: y } },
|
|
18
18
|
skip: !g,
|
|
19
19
|
fetchPolicy: "cache-and-network"
|
|
20
|
-
}), [A] =
|
|
20
|
+
}), [A] = n({ refetchQueries: ["MyAgentMessageBookmarks"] }), [j] = e({ refetchQueries: ["MyAgentMessageBookmarks"] }), M = O?.myAgentMessageBookmarks.items ?? [], N = new Map(b.map((e) => [e.id, e])), P = (e, t) => {
|
|
21
21
|
T(e), D(t ?? "");
|
|
22
22
|
}, F = async (e) => {
|
|
23
23
|
await A({ variables: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useProbeAgentTunnelLazyQuery as e } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { isQuotaExhaustedError as t, wasQuotaHandledLocally as n } from "../../utils/quotaUtils.js";
|
|
3
3
|
import { SearchableSelect as r } from "../shared/SearchableSelect.js";
|
|
4
4
|
import "../shared/index.js";
|
|
5
5
|
import { useCallback as ee, useEffect as te, useRef as ne, useState as i } from "react";
|
|
@@ -26,7 +26,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: me, onNavigate: g, targets: _,
|
|
|
26
26
|
}, []), Fe = (e) => {
|
|
27
27
|
let t = e.trim();
|
|
28
28
|
return t.endsWith("/") && (t = t.slice(0, -1)), t;
|
|
29
|
-
}, [Ie] =
|
|
29
|
+
}, [Ie] = e({ fetchPolicy: "network-only" }), Le = async () => {
|
|
30
30
|
let e = Fe(k);
|
|
31
31
|
if (e) {
|
|
32
32
|
xe(!0), G(null), q(null);
|
|
@@ -109,9 +109,9 @@ function m({ onSubmit: m, onCancel: h, isLoading: me, onNavigate: g, targets: _,
|
|
|
109
109
|
B(!0), H(null);
|
|
110
110
|
try {
|
|
111
111
|
let e = await y(L.trim());
|
|
112
|
-
e ? (P(e.id), I(!1), R("")) :
|
|
113
|
-
} catch (
|
|
114
|
-
e
|
|
112
|
+
e ? (P(e.id), I(!1), R("")) : n() ? (I(!1), R("")) : H(b("agentsPage.inlineTargetFailed", "Failed to create target. Please try again."));
|
|
113
|
+
} catch (e) {
|
|
114
|
+
t(e) ? (I(!1), R("")) : H(e instanceof Error ? e.message : "Failed to create target");
|
|
115
115
|
} finally {
|
|
116
116
|
B(!1);
|
|
117
117
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useAgentCodeServerStartMutation as e, useAgentCodeServerStatusLazyQuery as t, useAgentCodeServerStopMutation as n, useMintAgentCapabilityMutation as r } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useTr as i } from "../../shared/hooks/useTr.js";
|
|
3
3
|
import { AgentCapabilityScope as a } from "../../generated/wspace-types.js";
|
|
4
4
|
import { useCallback as o, useState as s } from "react";
|
|
5
5
|
import { AlertCircle as c, Check as l, Code as u, Loader2 as d } from "lucide-react";
|
|
@@ -7,7 +7,7 @@ import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
|
7
7
|
//#region src/components/agents/OpenInBrowserEditorButton.tsx
|
|
8
8
|
var m = "default";
|
|
9
9
|
function h({ agentId: h, tunnelUrl: g, profile: _, workspacePath: v, mode: y = "newtab", onIframeUrl: b, className: x }) {
|
|
10
|
-
let S =
|
|
10
|
+
let S = i(), [C, w] = s("idle"), [T, E] = s(null), [D] = t({ fetchPolicy: "network-only" }), [O] = e(), [k] = n(), [A] = r(), j = o(async (e) => {
|
|
11
11
|
let t = (await D({ variables: {
|
|
12
12
|
agentId: h,
|
|
13
13
|
profile: e,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useAgentEditorOpenMutation as e } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useTr as t } from "../../shared/hooks/useTr.js";
|
|
3
3
|
import { OpenInBrowserEditorButton as n } from "./OpenInBrowserEditorButton.js";
|
|
4
4
|
import { IDE_PRESETS as r, buildProtocolUrl as i, getIdePreset as a, resolveEffectiveIdeCommand as o } from "./idePresets.js";
|
|
5
5
|
import { useCallback as s, useMemo as c, useState as l } from "react";
|
|
@@ -7,7 +7,7 @@ import { AlertCircle as u, Check as d, ChevronDown as f, Code as p, ExternalLink
|
|
|
7
7
|
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
8
8
|
//#region src/components/agents/OpenInEditorMenu.tsx
|
|
9
9
|
function b({ vibeId: m, workspacePath: b, preferredIdeCommand: S, vibeIdeOverrides: C, agentId: w, tunnelUrl: T, profile: E, browserEditorEnabled: D = !1, onSetDefaultForVibe: O, className: k }) {
|
|
10
|
-
let A =
|
|
10
|
+
let A = t(), [j, M] = l(!1), [N, P] = l({ kind: "idle" }), [F] = e(), I = c(() => o(m, S, C ?? null), [
|
|
11
11
|
m,
|
|
12
12
|
S,
|
|
13
13
|
C
|
|
@@ -169,8 +169,8 @@ function b({ vibeId: m, workspacePath: b, preferredIdeCommand: S, vibeIdeOverrid
|
|
|
169
169
|
]
|
|
170
170
|
});
|
|
171
171
|
}
|
|
172
|
-
function x({ preset:
|
|
173
|
-
let l =
|
|
172
|
+
function x({ preset: e, isDefault: n, isOverride: r, hasPath: i, agentReachable: a, onProtocol: o, onAgent: s, onSetDefault: c }) {
|
|
173
|
+
let l = t(), u = !!e.protocol && i, f = i && a, h = e.family === "terminal";
|
|
174
174
|
return /* @__PURE__ */ y("div", {
|
|
175
175
|
className: "group px-2 py-1.5 rounded-md hover:bg-bg-sunken",
|
|
176
176
|
children: [
|
|
@@ -181,7 +181,7 @@ function x({ preset: t, isDefault: n, isOverride: r, hasPath: i, agentReachable:
|
|
|
181
181
|
children: [
|
|
182
182
|
/* @__PURE__ */ v("span", {
|
|
183
183
|
className: "text-sm text-text-primary truncate",
|
|
184
|
-
children:
|
|
184
|
+
children: e.label
|
|
185
185
|
}),
|
|
186
186
|
r && /* @__PURE__ */ v("span", {
|
|
187
187
|
className: "text-[9px] uppercase tracking-wider px-1 py-0.5 rounded bg-status-info-bg text-status-info-text",
|
|
@@ -219,9 +219,9 @@ function x({ preset: t, isDefault: n, isOverride: r, hasPath: i, agentReachable:
|
|
|
219
219
|
]
|
|
220
220
|
})]
|
|
221
221
|
}),
|
|
222
|
-
|
|
222
|
+
e.hint && /* @__PURE__ */ v("div", {
|
|
223
223
|
className: "text-[10px] text-text-secondary mt-0.5",
|
|
224
|
-
children:
|
|
224
|
+
children: e.hint
|
|
225
225
|
}),
|
|
226
226
|
!u && !f && /* @__PURE__ */ v("div", {
|
|
227
227
|
className: "text-[10px] text-status-warning-text mt-0.5",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useAgentBackupRequestMutation as e } from "../../../generated/wspace-operations.js";
|
|
2
|
+
import { useTr as t } from "../../../shared/hooks/useTr.js";
|
|
3
3
|
import { useCallback as n, useEffect as r, useState as i } from "react";
|
|
4
4
|
import { CheckCircle2 as a, Clock as o, Database as s, Loader2 as c, Play as l, RefreshCw as u, RotateCcw as d, Settings as f, Trash2 as p, Upload as m, Wifi as h, XCircle as g } from "lucide-react";
|
|
5
5
|
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
@@ -19,7 +19,7 @@ function C(e) {
|
|
|
19
19
|
return new Date(e).toLocaleString();
|
|
20
20
|
}
|
|
21
21
|
function w({ agentId: w, profile: T }) {
|
|
22
|
-
let E =
|
|
22
|
+
let E = t(), [D, O] = i(null), [k, A] = i(null), [j, M] = i([]), [N, P] = i(!0), [F, I] = i(null), [L, R] = i(null), [z, B] = i(null), [V, H] = i(!1), [U] = e(), W = n(async (e, t) => {
|
|
23
23
|
let n = (await U({ variables: {
|
|
24
24
|
agentId: w,
|
|
25
25
|
profile: T,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useGetRecentSandboxActivityQuery as e, useGetSandboxStatsQuery as t } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useTr as n } from "../../shared/hooks/useTr.js";
|
|
3
3
|
import { useVibeControls as r } from "../../providers/VibeControlsProvider.js";
|
|
4
4
|
import { Activity as i, Box as a, Cpu as o, MemoryStick as s, TimerOff as c, TrendingUp as l, XCircle as u } from "lucide-react";
|
|
5
5
|
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
@@ -30,11 +30,11 @@ function p({ icon: e, label: t, value: n, subtext: r, accent: i = "text-text-pri
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
function m() {
|
|
33
|
-
let m =
|
|
33
|
+
let m = n(), { workspaceId: h } = r(), { data: g, loading: _ } = t({
|
|
34
34
|
variables: { context: { workspaceId: h } },
|
|
35
35
|
skip: !h,
|
|
36
36
|
fetchPolicy: "cache-and-network"
|
|
37
|
-
}), { data: v } =
|
|
37
|
+
}), { data: v } = e({
|
|
38
38
|
variables: {
|
|
39
39
|
context: { workspaceId: h },
|
|
40
40
|
limit: 10
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useDisableDocsBuildImageMutation as e, useListDocsBuildImagesQuery as t, useRegisterDocsBuildImageMutation as n, useUpdateDocsSiteRuntimeMutation as r, useValidateDocsBuildImageMutation as ee } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useTr as te } from "../../shared/hooks/useTr.js";
|
|
3
3
|
import { DOCS_PROVIDERS as ne } from "./types.js";
|
|
4
4
|
import { useEffect as i, useState as a } from "react";
|
|
5
5
|
import { AlertTriangle as re, Check as o, Globe as ie, Loader2 as ae, Lock as oe, Plus as se, RefreshCw as ce, X as s, XCircle as le } from "lucide-react";
|
|
@@ -42,7 +42,7 @@ function de(e, t, n, r) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
function f({ site: f, open: p, onClose: m, onSave: fe }) {
|
|
45
|
-
let h =
|
|
45
|
+
let h = te(), [g, _] = a(f.name), [v, y] = a(f.visibility), [b, x] = a(f.provider || "DOCUSAURUS"), S = f.providerConfig || f.docusaurusConfig || {}, [C, w] = a(S.tagline || ""), T = S.themeConfig || {}, [E, D] = a((T.colorMode || {}).defaultMode || "light"), [O, k] = a(!1), [A, j] = a(f.runtime || "MANAGED"), [M, N] = a(f.buildImageId || null), [P, F] = a(!1), [I, L] = a(""), [R, z] = a(""), [B, V] = a(""), [H, U] = a("build"), [W, G] = a(""), [K, q] = a(!1), [J, Y] = a(null), { data: pe, refetch: X } = t({ fetchPolicy: "cache-and-network" }), [me] = n(), [Z] = ee(), [he] = e(), [ge] = r(), Q = pe?.docsBuildImages?.items || [], _e = Q.filter((e) => e.status === "APPROVED");
|
|
46
46
|
i(() => {
|
|
47
47
|
_(f.name), y(f.visibility), x(f.provider || "DOCUSAURUS");
|
|
48
48
|
let e = f.providerConfig || f.docusaurusConfig || {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocsSettingsPanel.js","names":[],"sources":["../../../src/components/docs/DocsSettingsPanel.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport {\n X,\n Globe,\n Lock,\n Plus,\n Check,\n XCircle,\n Loader2,\n AlertTriangle,\n RefreshCw,\n} from 'lucide-react';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport {\n useListDocsBuildImagesQuery,\n useRegisterDocsBuildImageMutation,\n useValidateDocsBuildImageMutation,\n useDisableDocsBuildImageMutation,\n useUpdateDocsSiteRuntimeMutation,\n} from '@/generated/wspace-operations';\nimport type { DocsProvider, DocsSite } from './types';\nimport { DOCS_PROVIDERS } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/**\n * Build a provider-specific config payload from the shared form fields.\n * Each provider has its own JSON shape — we keep the persisted shape narrow so\n * the backend assembler can rely on it without parsing the union.\n */\nfunction buildProviderConfig(\n provider: DocsProvider,\n prev: Record<string, unknown>,\n themeConfig: Record<string, unknown>,\n fields: { name: string; tagline: string; theme: string }\n): Record<string, unknown> {\n switch (provider) {\n case 'MKDOCS':\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themePalette: (prev.themePalette as Record<string, unknown>) || {\n primary: 'indigo',\n accent: 'indigo',\n },\n };\n case 'VITEPRESS':\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themeConfig: {\n ...((prev.themeConfig as Record<string, unknown>) || {}),\n nav: (themeConfig.nav as unknown[]) || [],\n },\n };\n case 'DOCUSAURUS':\n default:\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themeConfig: {\n ...themeConfig,\n colorMode: {\n defaultMode: fields.theme,\n disableSwitch: false,\n respectPrefersColorScheme: true,\n },\n },\n };\n }\n}\n\ninterface DocsBuildImage {\n id: string;\n name: string;\n imageRef: string;\n imageDigest?: string | null;\n status: 'PENDING' | 'VALIDATING' | 'APPROVED' | 'REJECTED' | 'DISABLED';\n buildCommand?: string | null;\n outputDir?: string | null;\n description?: string | null;\n lastValidationError?: string | null;\n}\n\ninterface DocsSettingsPanelProps {\n site: DocsSite;\n open: boolean;\n onClose: () => void;\n onSave: (data: {\n name?: string;\n visibility?: 'PRIVATE' | 'PUBLIC';\n provider?: DocsProvider;\n providerConfig?: Record<string, unknown>;\n }) => Promise<void>;\n}\n\nexport function DocsSettingsPanel({ site, open, onClose, onSave }: DocsSettingsPanelProps) {\n const tr = useTr();\n const [name, setName] = useState(site.name);\n const [visibility, setVisibility] = useState<'PRIVATE' | 'PUBLIC'>(site.visibility);\n const [provider, setProvider] = useState<DocsProvider>(site.provider || 'DOCUSAURUS');\n const config = (site.providerConfig || site.docusaurusConfig || {}) as Record<string, unknown>;\n const [tagline, setTagline] = useState((config.tagline as string) || '');\n const themeConfig = (config.themeConfig || {}) as Record<string, unknown>;\n const colorMode = (themeConfig.colorMode || {}) as Record<string, unknown>;\n const [theme, setTheme] = useState<string>((colorMode.defaultMode as string) || 'light');\n const [saving, setSaving] = useState(false);\n\n const [runtime, setRuntime] = useState<'MANAGED' | 'CUSTOMER_IMAGE'>(\n (site as any).runtime || 'MANAGED'\n );\n const [selectedImageId, setSelectedImageId] = useState<string | null>(\n (site as any).buildImageId || null\n );\n const [showRegister, setShowRegister] = useState(false);\n const [regName, setRegName] = useState('');\n const [regImageRef, setRegImageRef] = useState('');\n const [regBuildCommand, setRegBuildCommand] = useState('');\n const [regOutputDir, setRegOutputDir] = useState('build');\n const [regDescription, setRegDescription] = useState('');\n const [registering, setRegistering] = useState(false);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const { data: imagesData, refetch: refetchImages } = useListDocsBuildImagesQuery({\n fetchPolicy: 'cache-and-network',\n });\n const [registerImage] = useRegisterDocsBuildImageMutation();\n const [validateImage] = useValidateDocsBuildImageMutation();\n const [disableImage] = useDisableDocsBuildImageMutation();\n const [updateRuntime] = useUpdateDocsSiteRuntimeMutation();\n\n const images = (imagesData?.docsBuildImages?.items || []) as DocsBuildImage[];\n const approvedImages = images.filter((i) => i.status === 'APPROVED');\n\n useEffect(() => {\n setName(site.name);\n setVisibility(site.visibility);\n setProvider(site.provider || 'DOCUSAURUS');\n const cfg = (site.providerConfig || site.docusaurusConfig || {}) as Record<string, unknown>;\n setTagline((cfg.tagline as string) || '');\n const tc = (cfg.themeConfig || {}) as Record<string, unknown>;\n const cm = (tc.colorMode || {}) as Record<string, unknown>;\n setTheme((cm.defaultMode as string) || 'light');\n setRuntime(((site as any).runtime as 'MANAGED' | 'CUSTOMER_IMAGE') || 'MANAGED');\n setSelectedImageId((site as any).buildImageId || null);\n }, [site]);\n\n // Poll during VALIDATING state so the UI reflects status transitions live.\n useEffect(() => {\n if (!open) return;\n const hasValidating = images.some((i) => i.status === 'VALIDATING');\n if (!hasValidating) return;\n const interval = setInterval(() => {\n void refetchImages();\n }, 3000);\n return () => clearInterval(interval);\n }, [open, images, refetchImages]);\n\n const handleSave = async () => {\n setSaving(true);\n setActionError(null);\n try {\n await onSave({\n name,\n visibility,\n provider,\n providerConfig: buildProviderConfig(provider, config, themeConfig, {\n name,\n tagline,\n theme,\n }),\n });\n await updateRuntime({\n variables: {\n id: site.id,\n runtime: runtime as any,\n buildImageId: runtime === 'CUSTOMER_IMAGE' ? selectedImageId : null,\n },\n });\n onClose();\n } catch (err) {\n setActionError((err as Error).message);\n } finally {\n setSaving(false);\n }\n };\n\n const handleRegister = async () => {\n setRegistering(true);\n setActionError(null);\n try {\n const { data } = await registerImage({\n variables: {\n input: {\n name: regName,\n imageRef: regImageRef,\n description: regDescription || null,\n buildCommand: regBuildCommand || null,\n outputDir: regOutputDir || 'build',\n },\n },\n });\n const newId = (data as any)?.registerDocsBuildImage?.id;\n if (newId) {\n await validateImage({ variables: { id: newId } });\n }\n setRegName('');\n setRegImageRef('');\n setRegBuildCommand('');\n setRegOutputDir('build');\n setRegDescription('');\n setShowRegister(false);\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n } finally {\n setRegistering(false);\n }\n };\n\n const handleValidate = async (id: string) => {\n setActionError(null);\n try {\n await validateImage({ variables: { id } });\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n }\n };\n\n const handleDisable = async (id: string) => {\n setActionError(null);\n try {\n await disableImage({ variables: { id } });\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n }\n };\n\n const statusBadge = (status: DocsBuildImage['status']) => {\n const base = 'inline-flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-medium';\n if (status === 'APPROVED')\n return (\n <span className={`${base} bg-status-success-bg text-status-success-text`}>\n <Check className=\"size-3\" />\n Approved\n </span>\n );\n if (status === 'VALIDATING')\n return (\n <span className={`${base} bg-status-info-bg text-status-info-text`}>\n <Loader2 className=\"size-3 animate-spin\" />\n Validating\n </span>\n );\n if (status === 'REJECTED')\n return (\n <span className={`${base} bg-status-error-bg text-status-error-text`}>\n <XCircle className=\"size-3\" />\n Rejected\n </span>\n );\n if (status === 'DISABLED')\n return <span className={`${base} bg-bg-sunken text-text-muted`}>Disabled</span>;\n return <span className={`${base} bg-bg-sunken text-text-secondary`}>Pending</span>;\n };\n\n return (\n <AnimatePresence>\n {open && (\n <>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className=\"fixed inset-0 bg-bg-overlay z-40\"\n onClick={onClose}\n />\n <motion.div\n initial={{ x: '100%' }}\n animate={{ x: 0 }}\n exit={{ x: '100%' }}\n transition={{ type: 'spring', damping: 30, stiffness: 300 }}\n className=\"fixed right-0 top-0 bottom-0 w-[28rem] bg-bg-surface border-l border-border-default z-50 overflow-y-auto\"\n >\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.docs.settings.title', 'Site Settings')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"p-4 space-y-6\">\n <div>\n <label\n htmlFor=\"docssettingspanel-17\"\n className=\"block text-sm font-medium text-text-secondary mb-1\"\n >\n Site Title\n </label>\n <input\n id=\"docssettingspanel-17\"\n aria-label=\"control\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-16\"\n className=\"block text-sm font-medium text-text-secondary mb-1\"\n >\n Tagline\n </label>\n <input\n id=\"docssettingspanel-16\"\n aria-label=\"A brief tagline for your docs\"\n type=\"text\"\n value={tagline}\n onChange={(e) => setTagline(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n placeholder=\"A brief tagline for your docs\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-15\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Visibility\n </label>\n <div className=\"space-y-2\">\n <label className=\"flex items-center gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n id=\"docssettingspanel-15\"\n aria-label=\"visibility\"\n type=\"radio\"\n name=\"visibility\"\n value=\"PRIVATE\"\n checked={visibility === 'PRIVATE'}\n onChange={() => setVisibility('PRIVATE')}\n className=\"accent-action-primary-bg\"\n />\n <Lock className=\"size-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Private</p>\n <p className=\"text-xs text-text-muted\">\n Signed URLs that expire periodically\n </p>\n </div>\n </label>\n <label className=\"flex items-center gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n aria-label=\"visibility\"\n type=\"radio\"\n name=\"visibility\"\n value=\"PUBLIC\"\n checked={visibility === 'PUBLIC'}\n onChange={() => setVisibility('PUBLIC')}\n className=\"accent-action-primary-bg\"\n />\n <Globe className=\"size-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Public</p>\n <p className=\"text-xs text-text-muted\">Static URL accessible to anyone</p>\n </div>\n </label>\n </div>\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-14\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Static-site generator\n </label>\n <div className=\"space-y-2\">\n {DOCS_PROVIDERS.map((p) => (\n <label\n key={p.value}\n data-testid={`docs-provider-${p.value.toLowerCase()}`}\n className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\"\n >\n <input\n id=\"docssettingspanel-14\"\n aria-label=\"provider\"\n type=\"radio\"\n name=\"provider\"\n value={p.value}\n checked={provider === p.value}\n onChange={() => setProvider(p.value)}\n className=\"mt-1 accent-action-primary-bg\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{p.label}</p>\n <p className=\"text-xs text-text-muted\">{p.description}</p>\n </div>\n </label>\n ))}\n </div>\n {provider !== (site.provider || 'DOCUSAURUS') && (\n <p className=\"mt-2 text-xs text-text-warning\">\n Switching providers rebuilds the site from scratch. Existing artifacts stay\n until the next compile.\n </p>\n )}\n </div>\n\n {provider === 'DOCUSAURUS' && (\n <div>\n <label\n htmlFor=\"docssettingspanel-13\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Theme\n </label>\n <div className=\"flex gap-2\">\n {(['light', 'dark'] as const).map((t) => (\n <button\n type=\"button\"\n key={t}\n onClick={() => setTheme(t)}\n className={`flex-1 px-3 py-2 text-sm rounded-md border transition-colors ${\n theme === t\n ? 'bg-action-primary-bg text-action-primary-text border-action-primary-bg'\n : 'bg-bg-sunken text-text-secondary border-border-default hover:border-action-primary-bg'\n }`}\n >\n {t.charAt(0).toUpperCase() + t.slice(1)}\n </button>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"pt-4 border-t border-border-default\">\n <label\n htmlFor=\"docssettingspanel-13\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Build runtime\n </label>\n <div className=\"space-y-2\">\n <label className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n id=\"docssettingspanel-13\"\n aria-label=\"runtime\"\n type=\"radio\"\n name=\"runtime\"\n value=\"MANAGED\"\n checked={runtime === 'MANAGED'}\n onChange={() => setRuntime('MANAGED')}\n className=\"accent-action-primary-bg mt-1\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Managed (default)</p>\n <p className=\"text-xs text-text-muted\">\n Uses the platform's built-in Docusaurus image. Zero setup — just compile and\n go.\n </p>\n </div>\n </label>\n <label className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n aria-label=\"runtime\"\n type=\"radio\"\n name=\"runtime\"\n value=\"CUSTOMER_IMAGE\"\n checked={runtime === 'CUSTOMER_IMAGE'}\n onChange={() => setRuntime('CUSTOMER_IMAGE')}\n className=\"accent-action-primary-bg mt-1\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Custom image</p>\n <p className=\"text-xs text-text-muted\">\n Bring your own Docker build image — useful for custom Docusaurus plugins,\n themes, or alternative generators (MkDocs, VitePress). The image must be\n registered and approved below before it can be used.\n </p>\n </div>\n </label>\n </div>\n\n {runtime === 'CUSTOMER_IMAGE' && (\n <div className=\"mt-3 space-y-3\">\n <div>\n <label\n htmlFor=\"docssettingspanel-12\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Select approved image\n </label>\n <select\n id=\"docssettingspanel-12\"\n value={selectedImageId ?? ''}\n onChange={(e) => setSelectedImageId(e.target.value || null)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n >\n <option value=\"\"> - Select an image - </option>\n {approvedImages.map((img) => (\n <option key={img.id} value={img.id}>\n {img.name} ({img.imageRef})\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"border border-border-default rounded-md overflow-hidden\">\n <div className=\"p-2 bg-bg-sunken border-b border-border-default flex items-center justify-between\">\n <span className=\"text-xs font-medium text-text-secondary\">\n Build images ({images.length})\n </span>\n <button\n type=\"button\"\n onClick={() => setShowRegister((v) => !v)}\n className=\"text-xs text-action-primary-text-alt hover:underline flex items-center gap-1\"\n data-testid=\"docs-register-image-toggle\"\n >\n <Plus className=\"size-3\" />\n {showRegister ? 'Cancel' : 'Register'}\n </button>\n </div>\n\n {showRegister && (\n <div className=\"p-3 space-y-2 bg-bg-base\">\n <input\n aria-label=\"Name (e.g. custom-theme)\"\n type=\"text\"\n value={regName}\n onChange={(e) => setRegName(e.target.value)}\n placeholder=\"Name (e.g. custom-theme)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-name\"\n />\n <input\n aria-label=\"harbor.tooling.internal.burdenoff.com/...\"\n type=\"text\"\n value={regImageRef}\n onChange={(e) => setRegImageRef(e.target.value)}\n placeholder=\"harbor.tooling.internal.burdenoff.com/...\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-ref\"\n />\n <input\n aria-label=\"Build command (default: npm install && npm run build)\"\n type=\"text\"\n value={regBuildCommand}\n onChange={(e) => setRegBuildCommand(e.target.value)}\n placeholder=\"Build command (default: npm install && npm run build)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-command\"\n />\n <input\n aria-label=\"Output dir (default: build)\"\n type=\"text\"\n value={regOutputDir}\n onChange={(e) => setRegOutputDir(e.target.value)}\n placeholder=\"Output dir (default: build)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n />\n <textarea\n aria-label=\"Description (optional)\"\n value={regDescription}\n onChange={(e) => setRegDescription(e.target.value)}\n placeholder=\"Description (optional)\"\n rows={2}\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n />\n <button\n type=\"button\"\n onClick={handleRegister}\n disabled={registering || !regName || !regImageRef}\n className=\"w-full px-2 py-1 bg-action-primary-bg text-action-primary-text rounded text-xs hover:bg-action-primary-bg-hover disabled:opacity-50\"\n data-testid=\"docs-register-image-submit\"\n >\n {registering ? 'Registering…' : 'Register & Validate'}\n </button>\n </div>\n )}\n\n {images.length === 0 ? (\n <p className=\"text-xs text-text-muted p-3\">\n No build images registered yet.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-default\">\n {images.map((img) => (\n <li\n key={img.id}\n className={`p-2 ${img.status === 'REJECTED' ? 'bg-status-error-bg/30' : ''}`}\n data-testid={`docs-build-image-${img.id}`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-xs font-medium text-text-primary truncate\">\n {img.name}\n </p>\n <p\n className=\"text-[10px] text-text-muted truncate\"\n title={img.imageRef}\n >\n {img.imageRef}\n </p>\n {img.lastValidationError && img.status === 'REJECTED' && (\n <div className=\"mt-1.5 flex items-start gap-1 p-1.5 rounded bg-status-error-bg text-status-error-text\">\n <AlertTriangle className=\"size-3 mt-0.5 shrink-0\" />\n <p className=\"text-[10px] leading-snug\">\n {img.lastValidationError}\n </p>\n </div>\n )}\n </div>\n <div className=\"flex flex-col items-end gap-1.5 shrink-0\">\n {statusBadge(img.status)}\n {img.status === 'REJECTED' && (\n <button\n type=\"button\"\n onClick={() => handleValidate(img.id)}\n className=\"flex items-center gap-1 px-2 py-0.5 rounded border border-status-error-text text-[10px] text-status-error-text hover:bg-status-error-bg transition-colors\"\n data-testid={`docs-validate-image-${img.id}`}\n >\n <RefreshCw className=\"size-2.5\" />\n Re-validate\n </button>\n )}\n {(img.status === 'PENDING' || img.status === 'DISABLED') && (\n <button\n type=\"button\"\n onClick={() => handleValidate(img.id)}\n className=\"flex items-center gap-1 px-2 py-0.5 rounded border border-border-default text-[10px] text-text-secondary hover:border-action-primary-bg hover:text-action-primary-text-alt transition-colors\"\n data-testid={`docs-validate-image-${img.id}`}\n >\n Validate\n </button>\n )}\n {img.status === 'APPROVED' && (\n <button\n type=\"button\"\n onClick={() => handleDisable(img.id)}\n className=\"text-[10px] text-text-muted hover:underline\"\n >\n Disable\n </button>\n )}\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n )}\n </div>\n\n {actionError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text rounded-md text-xs\">\n {actionError}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || (runtime === 'CUSTOMER_IMAGE' && !selectedImageId)}\n className=\"w-full px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n data-testid=\"docs-settings-save\"\n >\n {saving\n ? tr('vibecontrols.docs.settings.saving', 'Saving...')\n : tr('vibecontrols.docs.settings.save', 'Save Settings')}\n </button>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocsSettingsPanel.js","names":[],"sources":["../../../src/components/docs/DocsSettingsPanel.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport {\n X,\n Globe,\n Lock,\n Plus,\n Check,\n XCircle,\n Loader2,\n AlertTriangle,\n RefreshCw,\n} from 'lucide-react';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport {\n useListDocsBuildImagesQuery,\n useRegisterDocsBuildImageMutation,\n useValidateDocsBuildImageMutation,\n useDisableDocsBuildImageMutation,\n useUpdateDocsSiteRuntimeMutation,\n} from '@/generated/wspace-operations';\nimport type { DocsProvider, DocsSite } from './types';\nimport { DOCS_PROVIDERS } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/**\n * Build a provider-specific config payload from the shared form fields.\n * Each provider has its own JSON shape — we keep the persisted shape narrow so\n * the backend assembler can rely on it without parsing the union.\n */\nfunction buildProviderConfig(\n provider: DocsProvider,\n prev: Record<string, unknown>,\n themeConfig: Record<string, unknown>,\n fields: { name: string; tagline: string; theme: string }\n): Record<string, unknown> {\n switch (provider) {\n case 'MKDOCS':\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themePalette: (prev.themePalette as Record<string, unknown>) || {\n primary: 'indigo',\n accent: 'indigo',\n },\n };\n case 'VITEPRESS':\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themeConfig: {\n ...((prev.themeConfig as Record<string, unknown>) || {}),\n nav: (themeConfig.nav as unknown[]) || [],\n },\n };\n case 'DOCUSAURUS':\n default:\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themeConfig: {\n ...themeConfig,\n colorMode: {\n defaultMode: fields.theme,\n disableSwitch: false,\n respectPrefersColorScheme: true,\n },\n },\n };\n }\n}\n\ninterface DocsBuildImage {\n id: string;\n name: string;\n imageRef: string;\n imageDigest?: string | null;\n status: 'PENDING' | 'VALIDATING' | 'APPROVED' | 'REJECTED' | 'DISABLED';\n buildCommand?: string | null;\n outputDir?: string | null;\n description?: string | null;\n lastValidationError?: string | null;\n}\n\ninterface DocsSettingsPanelProps {\n site: DocsSite;\n open: boolean;\n onClose: () => void;\n onSave: (data: {\n name?: string;\n visibility?: 'PRIVATE' | 'PUBLIC';\n provider?: DocsProvider;\n providerConfig?: Record<string, unknown>;\n }) => Promise<void>;\n}\n\nexport function DocsSettingsPanel({ site, open, onClose, onSave }: DocsSettingsPanelProps) {\n const tr = useTr();\n const [name, setName] = useState(site.name);\n const [visibility, setVisibility] = useState<'PRIVATE' | 'PUBLIC'>(site.visibility);\n const [provider, setProvider] = useState<DocsProvider>(site.provider || 'DOCUSAURUS');\n const config = (site.providerConfig || site.docusaurusConfig || {}) as Record<string, unknown>;\n const [tagline, setTagline] = useState((config.tagline as string) || '');\n const themeConfig = (config.themeConfig || {}) as Record<string, unknown>;\n const colorMode = (themeConfig.colorMode || {}) as Record<string, unknown>;\n const [theme, setTheme] = useState<string>((colorMode.defaultMode as string) || 'light');\n const [saving, setSaving] = useState(false);\n\n const [runtime, setRuntime] = useState<'MANAGED' | 'CUSTOMER_IMAGE'>(\n (site as any).runtime || 'MANAGED'\n );\n const [selectedImageId, setSelectedImageId] = useState<string | null>(\n (site as any).buildImageId || null\n );\n const [showRegister, setShowRegister] = useState(false);\n const [regName, setRegName] = useState('');\n const [regImageRef, setRegImageRef] = useState('');\n const [regBuildCommand, setRegBuildCommand] = useState('');\n const [regOutputDir, setRegOutputDir] = useState('build');\n const [regDescription, setRegDescription] = useState('');\n const [registering, setRegistering] = useState(false);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const { data: imagesData, refetch: refetchImages } = useListDocsBuildImagesQuery({\n fetchPolicy: 'cache-and-network',\n });\n const [registerImage] = useRegisterDocsBuildImageMutation();\n const [validateImage] = useValidateDocsBuildImageMutation();\n const [disableImage] = useDisableDocsBuildImageMutation();\n const [updateRuntime] = useUpdateDocsSiteRuntimeMutation();\n\n const images = (imagesData?.docsBuildImages?.items || []) as DocsBuildImage[];\n const approvedImages = images.filter((i) => i.status === 'APPROVED');\n\n useEffect(() => {\n setName(site.name);\n setVisibility(site.visibility);\n setProvider(site.provider || 'DOCUSAURUS');\n const cfg = (site.providerConfig || site.docusaurusConfig || {}) as Record<string, unknown>;\n setTagline((cfg.tagline as string) || '');\n const tc = (cfg.themeConfig || {}) as Record<string, unknown>;\n const cm = (tc.colorMode || {}) as Record<string, unknown>;\n setTheme((cm.defaultMode as string) || 'light');\n setRuntime(((site as any).runtime as 'MANAGED' | 'CUSTOMER_IMAGE') || 'MANAGED');\n setSelectedImageId((site as any).buildImageId || null);\n }, [site]);\n\n // Poll during VALIDATING state so the UI reflects status transitions live.\n useEffect(() => {\n if (!open) return;\n const hasValidating = images.some((i) => i.status === 'VALIDATING');\n if (!hasValidating) return;\n const interval = setInterval(() => {\n void refetchImages();\n }, 3000);\n return () => clearInterval(interval);\n }, [open, images, refetchImages]);\n\n const handleSave = async () => {\n setSaving(true);\n setActionError(null);\n try {\n await onSave({\n name,\n visibility,\n provider,\n providerConfig: buildProviderConfig(provider, config, themeConfig, {\n name,\n tagline,\n theme,\n }),\n });\n await updateRuntime({\n variables: {\n id: site.id,\n runtime: runtime as any,\n buildImageId: runtime === 'CUSTOMER_IMAGE' ? selectedImageId : null,\n },\n });\n onClose();\n } catch (err) {\n setActionError((err as Error).message);\n } finally {\n setSaving(false);\n }\n };\n\n const handleRegister = async () => {\n setRegistering(true);\n setActionError(null);\n try {\n const { data } = await registerImage({\n variables: {\n input: {\n name: regName,\n imageRef: regImageRef,\n description: regDescription || null,\n buildCommand: regBuildCommand || null,\n outputDir: regOutputDir || 'build',\n },\n },\n });\n const newId = (data as any)?.registerDocsBuildImage?.id;\n if (newId) {\n await validateImage({ variables: { id: newId } });\n }\n setRegName('');\n setRegImageRef('');\n setRegBuildCommand('');\n setRegOutputDir('build');\n setRegDescription('');\n setShowRegister(false);\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n } finally {\n setRegistering(false);\n }\n };\n\n const handleValidate = async (id: string) => {\n setActionError(null);\n try {\n await validateImage({ variables: { id } });\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n }\n };\n\n const handleDisable = async (id: string) => {\n setActionError(null);\n try {\n await disableImage({ variables: { id } });\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n }\n };\n\n const statusBadge = (status: DocsBuildImage['status']) => {\n const base = 'inline-flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-medium';\n if (status === 'APPROVED')\n return (\n <span className={`${base} bg-status-success-bg text-status-success-text`}>\n <Check className=\"size-3\" />\n Approved\n </span>\n );\n if (status === 'VALIDATING')\n return (\n <span className={`${base} bg-status-info-bg text-status-info-text`}>\n <Loader2 className=\"size-3 animate-spin\" />\n Validating\n </span>\n );\n if (status === 'REJECTED')\n return (\n <span className={`${base} bg-status-error-bg text-status-error-text`}>\n <XCircle className=\"size-3\" />\n Rejected\n </span>\n );\n if (status === 'DISABLED')\n return <span className={`${base} bg-bg-sunken text-text-muted`}>Disabled</span>;\n return <span className={`${base} bg-bg-sunken text-text-secondary`}>Pending</span>;\n };\n\n return (\n <AnimatePresence>\n {open && (\n <>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className=\"fixed inset-0 bg-bg-overlay z-40\"\n onClick={onClose}\n />\n <motion.div\n initial={{ x: '100%' }}\n animate={{ x: 0 }}\n exit={{ x: '100%' }}\n transition={{ type: 'spring', damping: 30, stiffness: 300 }}\n className=\"fixed right-0 top-0 bottom-0 w-[28rem] bg-bg-surface border-l border-border-default z-50 overflow-y-auto\"\n >\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.docs.settings.title', 'Site Settings')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"p-4 space-y-6\">\n <div>\n <label\n htmlFor=\"docssettingspanel-17\"\n className=\"block text-sm font-medium text-text-secondary mb-1\"\n >\n Site Title\n </label>\n <input\n id=\"docssettingspanel-17\"\n aria-label=\"control\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-16\"\n className=\"block text-sm font-medium text-text-secondary mb-1\"\n >\n Tagline\n </label>\n <input\n id=\"docssettingspanel-16\"\n aria-label=\"A brief tagline for your docs\"\n type=\"text\"\n value={tagline}\n onChange={(e) => setTagline(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n placeholder=\"A brief tagline for your docs\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-15\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Visibility\n </label>\n <div className=\"space-y-2\">\n <label className=\"flex items-center gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n id=\"docssettingspanel-15\"\n aria-label=\"visibility\"\n type=\"radio\"\n name=\"visibility\"\n value=\"PRIVATE\"\n checked={visibility === 'PRIVATE'}\n onChange={() => setVisibility('PRIVATE')}\n className=\"accent-action-primary-bg\"\n />\n <Lock className=\"size-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Private</p>\n <p className=\"text-xs text-text-muted\">\n Signed URLs that expire periodically\n </p>\n </div>\n </label>\n <label className=\"flex items-center gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n aria-label=\"visibility\"\n type=\"radio\"\n name=\"visibility\"\n value=\"PUBLIC\"\n checked={visibility === 'PUBLIC'}\n onChange={() => setVisibility('PUBLIC')}\n className=\"accent-action-primary-bg\"\n />\n <Globe className=\"size-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Public</p>\n <p className=\"text-xs text-text-muted\">Static URL accessible to anyone</p>\n </div>\n </label>\n </div>\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-14\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Static-site generator\n </label>\n <div className=\"space-y-2\">\n {DOCS_PROVIDERS.map((p) => (\n <label\n key={p.value}\n data-testid={`docs-provider-${p.value.toLowerCase()}`}\n className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\"\n >\n <input\n id=\"docssettingspanel-14\"\n aria-label=\"provider\"\n type=\"radio\"\n name=\"provider\"\n value={p.value}\n checked={provider === p.value}\n onChange={() => setProvider(p.value)}\n className=\"mt-1 accent-action-primary-bg\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{p.label}</p>\n <p className=\"text-xs text-text-muted\">{p.description}</p>\n </div>\n </label>\n ))}\n </div>\n {provider !== (site.provider || 'DOCUSAURUS') && (\n <p className=\"mt-2 text-xs text-text-warning\">\n Switching providers rebuilds the site from scratch. Existing artifacts stay\n until the next compile.\n </p>\n )}\n </div>\n\n {provider === 'DOCUSAURUS' && (\n <div>\n <label\n htmlFor=\"docssettingspanel-13\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Theme\n </label>\n <div className=\"flex gap-2\">\n {(['light', 'dark'] as const).map((t) => (\n <button\n type=\"button\"\n key={t}\n onClick={() => setTheme(t)}\n className={`flex-1 px-3 py-2 text-sm rounded-md border transition-colors ${\n theme === t\n ? 'bg-action-primary-bg text-action-primary-text border-action-primary-bg'\n : 'bg-bg-sunken text-text-secondary border-border-default hover:border-action-primary-bg'\n }`}\n >\n {t.charAt(0).toUpperCase() + t.slice(1)}\n </button>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"pt-4 border-t border-border-default\">\n <label\n htmlFor=\"docssettingspanel-13\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Build runtime\n </label>\n <div className=\"space-y-2\">\n <label className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n id=\"docssettingspanel-13\"\n aria-label=\"runtime\"\n type=\"radio\"\n name=\"runtime\"\n value=\"MANAGED\"\n checked={runtime === 'MANAGED'}\n onChange={() => setRuntime('MANAGED')}\n className=\"accent-action-primary-bg mt-1\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Managed (default)</p>\n <p className=\"text-xs text-text-muted\">\n Uses the platform's built-in Docusaurus image. Zero setup — just compile and\n go.\n </p>\n </div>\n </label>\n <label className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n aria-label=\"runtime\"\n type=\"radio\"\n name=\"runtime\"\n value=\"CUSTOMER_IMAGE\"\n checked={runtime === 'CUSTOMER_IMAGE'}\n onChange={() => setRuntime('CUSTOMER_IMAGE')}\n className=\"accent-action-primary-bg mt-1\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Custom image</p>\n <p className=\"text-xs text-text-muted\">\n Bring your own Docker build image — useful for custom Docusaurus plugins,\n themes, or alternative generators (MkDocs, VitePress). The image must be\n registered and approved below before it can be used.\n </p>\n </div>\n </label>\n </div>\n\n {runtime === 'CUSTOMER_IMAGE' && (\n <div className=\"mt-3 space-y-3\">\n <div>\n <label\n htmlFor=\"docssettingspanel-12\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Select approved image\n </label>\n <select\n id=\"docssettingspanel-12\"\n value={selectedImageId ?? ''}\n onChange={(e) => setSelectedImageId(e.target.value || null)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n >\n <option value=\"\"> - Select an image - </option>\n {approvedImages.map((img) => (\n <option key={img.id} value={img.id}>\n {img.name} ({img.imageRef})\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"border border-border-default rounded-md overflow-hidden\">\n <div className=\"p-2 bg-bg-sunken border-b border-border-default flex items-center justify-between\">\n <span className=\"text-xs font-medium text-text-secondary\">\n Build images ({images.length})\n </span>\n <button\n type=\"button\"\n onClick={() => setShowRegister((v) => !v)}\n className=\"text-xs text-action-primary-text-alt hover:underline flex items-center gap-1\"\n data-testid=\"docs-register-image-toggle\"\n >\n <Plus className=\"size-3\" />\n {showRegister ? 'Cancel' : 'Register'}\n </button>\n </div>\n\n {showRegister && (\n <div className=\"p-3 space-y-2 bg-bg-base\">\n <input\n aria-label=\"Name (e.g. custom-theme)\"\n type=\"text\"\n value={regName}\n onChange={(e) => setRegName(e.target.value)}\n placeholder=\"Name (e.g. custom-theme)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-name\"\n />\n <input\n aria-label=\"harbor.tooling.internal.burdenoff.com/...\"\n type=\"text\"\n value={regImageRef}\n onChange={(e) => setRegImageRef(e.target.value)}\n placeholder=\"harbor.tooling.internal.burdenoff.com/...\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-ref\"\n />\n <input\n aria-label=\"Build command (default: npm install && npm run build)\"\n type=\"text\"\n value={regBuildCommand}\n onChange={(e) => setRegBuildCommand(e.target.value)}\n placeholder=\"Build command (default: npm install && npm run build)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-command\"\n />\n <input\n aria-label=\"Output dir (default: build)\"\n type=\"text\"\n value={regOutputDir}\n onChange={(e) => setRegOutputDir(e.target.value)}\n placeholder=\"Output dir (default: build)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n />\n <textarea\n aria-label=\"Description (optional)\"\n value={regDescription}\n onChange={(e) => setRegDescription(e.target.value)}\n placeholder=\"Description (optional)\"\n rows={2}\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n />\n <button\n type=\"button\"\n onClick={handleRegister}\n disabled={registering || !regName || !regImageRef}\n className=\"w-full px-2 py-1 bg-action-primary-bg text-action-primary-text rounded text-xs hover:bg-action-primary-bg-hover disabled:opacity-50\"\n data-testid=\"docs-register-image-submit\"\n >\n {registering ? 'Registering…' : 'Register & Validate'}\n </button>\n </div>\n )}\n\n {images.length === 0 ? (\n <p className=\"text-xs text-text-muted p-3\">\n No build images registered yet.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-default\">\n {images.map((img) => (\n <li\n key={img.id}\n className={`p-2 ${img.status === 'REJECTED' ? 'bg-status-error-bg/30' : ''}`}\n data-testid={`docs-build-image-${img.id}`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-xs font-medium text-text-primary truncate\">\n {img.name}\n </p>\n <p\n className=\"text-[10px] text-text-muted truncate\"\n title={img.imageRef}\n >\n {img.imageRef}\n </p>\n {img.lastValidationError && img.status === 'REJECTED' && (\n <div className=\"mt-1.5 flex items-start gap-1 p-1.5 rounded bg-status-error-bg text-status-error-text\">\n <AlertTriangle className=\"size-3 mt-0.5 shrink-0\" />\n <p className=\"text-[10px] leading-snug\">\n {img.lastValidationError}\n </p>\n </div>\n )}\n </div>\n <div className=\"flex flex-col items-end gap-1.5 shrink-0\">\n {statusBadge(img.status)}\n {img.status === 'REJECTED' && (\n <button\n type=\"button\"\n onClick={() => handleValidate(img.id)}\n className=\"flex items-center gap-1 px-2 py-0.5 rounded border border-status-error-text text-[10px] text-status-error-text hover:bg-status-error-bg transition-colors\"\n data-testid={`docs-validate-image-${img.id}`}\n >\n <RefreshCw className=\"size-2.5\" />\n Re-validate\n </button>\n )}\n {(img.status === 'PENDING' || img.status === 'DISABLED') && (\n <button\n type=\"button\"\n onClick={() => handleValidate(img.id)}\n className=\"flex items-center gap-1 px-2 py-0.5 rounded border border-border-default text-[10px] text-text-secondary hover:border-action-primary-bg hover:text-action-primary-text-alt transition-colors\"\n data-testid={`docs-validate-image-${img.id}`}\n >\n Validate\n </button>\n )}\n {img.status === 'APPROVED' && (\n <button\n type=\"button\"\n onClick={() => handleDisable(img.id)}\n className=\"text-[10px] text-text-muted hover:underline\"\n >\n Disable\n </button>\n )}\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n )}\n </div>\n\n {actionError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text rounded-md text-xs\">\n {actionError}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || (runtime === 'CUSTOMER_IMAGE' && !selectedImageId)}\n className=\"w-full px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n data-testid=\"docs-settings-save\"\n >\n {saving\n ? tr('vibecontrols.docs.settings.saving', 'Saving...')\n : tr('vibecontrols.docs.settings.save', 'Save Settings')}\n </button>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { useCompileDocsSiteMutation as e, useCreateDocsPageMutation as t, useCreateDocsSiteMutation as n, useDeleteDocsPageMutation as r, useDeleteDocsSiteMutation as i, useDocsContentQuery as a, useGenerateDocsSignedUrlMutation as ee, useGetDocsRevisionLazyQuery as te, useGetDocsSiteQuery as ne, useRollbackDocsRevisionMutation as re, useUpdateDocsPageMutation as ie, useUpdateDocsSiteMutation as o, useUpdateDocsSiteVisibilityMutation as s } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useTr as c } from "../../shared/hooks/useTr.js";
|
|
3
|
+
import { QuotaExhaustedDialog as l } from "../shared/QuotaExhaustedDialog.js";
|
|
4
|
+
import { isQuotaExhaustedError as u, markQuotaHandledLocally as d } from "../../utils/quotaUtils.js";
|
|
5
5
|
import { useVibeNavigate as f } from "../../providers/VibeControlsProvider.js";
|
|
6
6
|
import { LoadingState as p } from "../shared/LoadingState.js";
|
|
7
7
|
import { ErrorState as m } from "../shared/ErrorState.js";
|
|
@@ -15,14 +15,14 @@ import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
|
15
15
|
import { resolveGatewayUrls as se } from "@burdenoff/fe-libs/shared/config";
|
|
16
16
|
//#region src/components/docs/DocsTabPanel.tsx
|
|
17
17
|
function w({ vibeId: w, ownerType: ce, ownerId: le }) {
|
|
18
|
-
let T =
|
|
18
|
+
let T = c(), E = ce || "VIBE", D = le || w || "", [O, k] = b("preview"), [A, j] = b(null), [M, N] = b(!1), [P, F] = b(null), [I, L] = b(""), [R, z] = b(null), B = f(), [V, H] = b(!1), { data: U, loading: W, error: G, refetch: K } = ne({
|
|
19
19
|
variables: {
|
|
20
20
|
ownerType: E,
|
|
21
21
|
ownerId: D
|
|
22
22
|
},
|
|
23
23
|
skip: !D,
|
|
24
24
|
fetchPolicy: "cache-and-network"
|
|
25
|
-
}), q = U?.vibecontrolsDocsSiteByOwner ?? null, J = q?.pages || [], { data: ue, loading: de, refetch: fe } =
|
|
25
|
+
}), q = U?.vibecontrolsDocsSiteByOwner ?? null, J = q?.pages || [], { data: ue, loading: de, refetch: fe } = a({
|
|
26
26
|
variables: {
|
|
27
27
|
siteId: q?.id || "",
|
|
28
28
|
path: I
|
|
@@ -38,7 +38,7 @@ function w({ vibeId: w, ownerType: ce, ownerId: le }) {
|
|
|
38
38
|
}
|
|
39
39
|
e && e !== Y.current && (Y.current = e, fe());
|
|
40
40
|
}, [q?.currentRevisionId]);
|
|
41
|
-
let me = se(), he = q?.workspaceId ? `${me.workspaceBaseUrl}/workspaces/static/${q.workspaceId}/docs/${D}/` : null, [ge, { loading: _e }] =
|
|
41
|
+
let me = se(), he = q?.workspaceId ? `${me.workspaceBaseUrl}/workspaces/static/${q.workspaceId}/docs/${D}/` : null, [ge, { loading: _e }] = n(), [ve] = o(), [ye] = s(), [be, { loading: xe }] = t(), [Se] = ie(), [Ce] = r(), [we] = e(), [Te] = ee(), [Ee] = re(), [De] = i(), [Oe] = te({ fetchPolicy: "network-only" }), ke = async (e = "DOCUSAURUS") => {
|
|
42
42
|
try {
|
|
43
43
|
await ge({
|
|
44
44
|
variables: { input: {
|
|
@@ -51,8 +51,8 @@ function w({ vibeId: w, ownerType: ce, ownerId: le }) {
|
|
|
51
51
|
awaitRefetchQueries: !0
|
|
52
52
|
}), k("edit");
|
|
53
53
|
} catch (e) {
|
|
54
|
-
if (
|
|
55
|
-
|
|
54
|
+
if (u(e)) {
|
|
55
|
+
d(), H(!0);
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
throw e;
|
|
@@ -144,7 +144,7 @@ function w({ vibeId: w, ownerType: ce, ownerId: le }) {
|
|
|
144
144
|
if (!q) return /* @__PURE__ */ C(x, { children: [/* @__PURE__ */ S(h, {
|
|
145
145
|
onCreate: ke,
|
|
146
146
|
loading: _e
|
|
147
|
-
}), /* @__PURE__ */ S(
|
|
147
|
+
}), /* @__PURE__ */ S(l, {
|
|
148
148
|
isOpen: V,
|
|
149
149
|
onClose: () => H(!1),
|
|
150
150
|
onConfirm: () => {
|
|
@@ -210,7 +210,7 @@ function w({ vibeId: w, ownerType: ce, ownerId: le }) {
|
|
|
210
210
|
onPollRevision: Q,
|
|
211
211
|
compiling: M
|
|
212
212
|
}),
|
|
213
|
-
/* @__PURE__ */ S(
|
|
213
|
+
/* @__PURE__ */ S(l, {
|
|
214
214
|
isOpen: V,
|
|
215
215
|
onClose: () => H(!1),
|
|
216
216
|
onConfirm: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocsTabPanel.js","names":[],"sources":["../../../src/components/docs/DocsTabPanel.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport { AlertCircle, X } from 'lucide-react';\nimport { LoadingState, ErrorState, QuotaExhaustedDialog } from '@/components/shared';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { resolveGatewayUrls } from '@burdenoff/fe-libs/shared/config';\nimport { DocsEmptyState } from './DocsEmptyState';\nimport { DocsPreviewPanel } from './DocsPreviewPanel';\nimport { DocsEditPanel } from './DocsEditPanel';\nimport type { DocsSite, DocsRevision } from './types';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useGetDocsSiteQuery,\n useCreateDocsSiteMutation,\n useUpdateDocsSiteMutation,\n useUpdateDocsSiteVisibilityMutation,\n useCreateDocsPageMutation,\n useUpdateDocsPageMutation,\n useDeleteDocsPageMutation,\n useCompileDocsSiteMutation,\n useDocsContentQuery,\n useGetDocsRevisionLazyQuery,\n useGenerateDocsSignedUrlMutation,\n useRollbackDocsRevisionMutation,\n useDeleteDocsSiteMutation,\n} from '@/generated/wspace-operations';\n\ninterface DocsTabPanelProps {\n vibeId?: string;\n ownerType?: string;\n ownerId?: string;\n}\n\nexport function DocsTabPanel({ vibeId, ownerType, ownerId }: DocsTabPanelProps) {\n const tr = useTr();\n const effectiveOwnerType = ownerType || 'VIBE';\n const effectiveOwnerId = ownerId || vibeId || '';\n const [mode, setMode] = useState<'preview' | 'edit'>('preview');\n const [compileRevision, setCompileRevision] = useState<DocsRevision | null>(null);\n const [compiling, setCompiling] = useState(false);\n const [compileError, setCompileError] = useState<string | null>(null);\n const [selectedPageSlug, setSelectedPageSlug] = useState('');\n const [rollingBack, setRollingBack] = useState<string | null>(null);\n const navigate = useVibeNavigate();\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n // Queries\n const { data, loading, error, refetch } = useGetDocsSiteQuery({\n variables: { ownerType: effectiveOwnerType as any, ownerId: effectiveOwnerId },\n skip: !effectiveOwnerId,\n fetchPolicy: 'cache-and-network',\n });\n\n const site = (data?.vibecontrolsDocsSiteByOwner ?? null) as DocsSite | null;\n const pages = site?.pages || [];\n\n const {\n data: docsContentData,\n loading: docsContentLoading,\n refetch: refetchDocsContent,\n } = useDocsContentQuery({\n variables: { siteId: site?.id || '', path: selectedPageSlug },\n skip: !site?.id || !site.currentRevisionId,\n fetchPolicy: 'network-only',\n });\n const docsHtml = docsContentData?.docsContent?.html ?? null;\n\n // Re-fetch docs content whenever currentRevisionId changes (compile finished or first publish).\n // This runs post-render so skip:false is guaranteed — imperative calls during async handlers\n // are no-ops when skip is still true in the closure.\n const prevRevisionIdRef = useRef<string | null | undefined>(undefined);\n useEffect(() => {\n const currentId = site?.currentRevisionId ?? null;\n if (prevRevisionIdRef.current === undefined) {\n prevRevisionIdRef.current = currentId;\n return;\n }\n if (currentId && currentId !== prevRevisionIdRef.current) {\n prevRevisionIdRef.current = currentId;\n refetchDocsContent();\n }\n }, [site?.currentRevisionId]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Compute gateway docs URL for iframe src (workspaceId prefix for isolation)\n const gatewayUrls = resolveGatewayUrls();\n const docsBaseUrl = site?.workspaceId\n ? `${gatewayUrls.workspaceBaseUrl}/workspaces/static/${site.workspaceId}/docs/${effectiveOwnerId}/`\n : null;\n\n // Mutations\n const [createSiteMutation, { loading: creating }] = useCreateDocsSiteMutation();\n const [updateSiteMutation] = useUpdateDocsSiteMutation();\n const [updateVisibilityMutation] = useUpdateDocsSiteVisibilityMutation();\n const [createPageMutation, { loading: pageAdding }] = useCreateDocsPageMutation();\n const [updatePageMutation] = useUpdateDocsPageMutation();\n const [deletePageMutation] = useDeleteDocsPageMutation();\n const [compileMutation] = useCompileDocsSiteMutation();\n const [generateSignedUrl] = useGenerateDocsSignedUrlMutation();\n const [rollbackRevision] = useRollbackDocsRevisionMutation();\n const [deleteSiteMutation] = useDeleteDocsSiteMutation();\n const [fetchRevision] = useGetDocsRevisionLazyQuery({\n fetchPolicy: 'network-only',\n });\n\n // Handlers\n const handleCreateSite = async (\n provider: 'DOCUSAURUS' | 'MKDOCS' | 'VITEPRESS' = 'DOCUSAURUS'\n ) => {\n try {\n await createSiteMutation({\n variables: {\n input: {\n ownerType: effectiveOwnerType as any,\n ownerId: effectiveOwnerId,\n name: 'Documentation',\n provider: provider as any,\n },\n },\n refetchQueries: ['GetDocsSite'],\n awaitRefetchQueries: true,\n });\n setMode('edit');\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n throw err;\n }\n };\n\n const handleSavePage = async (\n id: string,\n data: { title?: string; content?: string; slug?: string }\n ) => {\n await updatePageMutation({ variables: { id, input: data } });\n };\n\n const handleAddPage = async (parentId?: string, category?: string) => {\n if (!site) return;\n const pageCount = pages.length;\n const slug = `page-${pageCount + 1}`;\n await createPageMutation({\n variables: {\n input: {\n siteId: site.id,\n parentId: parentId || null,\n slug,\n title: `New Page ${pageCount + 1}`,\n content: '# New Page\\n\\nStart writing here...',\n status: 'DRAFT',\n sidebarPosition: pageCount,\n category: category || null,\n },\n },\n });\n await refetch();\n };\n\n const handleDeletePage = async (pageId: string) => {\n if (!confirm('Delete this page?')) return;\n await deletePageMutation({ variables: { id: pageId } });\n await refetch();\n };\n\n const handleCompile = async () => {\n if (!site) return;\n setCompiling(true);\n setCompileError(null);\n try {\n const { data: compileData } = await compileMutation({\n variables: { siteId: site.id, publish: true },\n });\n if (compileData?.compileDocsSite) {\n setCompileRevision(compileData.compileDocsSite as DocsRevision);\n } else {\n setCompiling(false);\n setCompileError(\n 'Build could not be started — the server returned an empty response. Please try again.'\n );\n }\n } catch (err) {\n setCompiling(false);\n setCompileError(\n err instanceof Error ? err.message : 'Failed to start the build. Please try again.'\n );\n }\n };\n\n const handlePollRevision = async (revisionId: string): Promise<DocsRevision | null> => {\n const { data: revData } = await fetchRevision({ variables: { id: revisionId } });\n const rev = revData?.docsRevision as DocsRevision | null;\n if (rev) {\n setCompileRevision(rev);\n const done = ['SUCCEEDED', 'PUBLISHED', 'FAILED'].includes(rev.status);\n if (done) {\n setCompiling(false);\n if (rev.status === 'PUBLISHED' || rev.status === 'SUCCEEDED') {\n setSelectedPageSlug('');\n await refetch();\n }\n }\n }\n return rev;\n };\n\n const handleSaveSettings = async (data: {\n name?: string;\n visibility?: 'PRIVATE' | 'PUBLIC';\n provider?: 'DOCUSAURUS' | 'MKDOCS' | 'VITEPRESS';\n providerConfig?: Record<string, unknown>;\n }) => {\n if (!site) return;\n if (data.visibility && data.visibility !== site.visibility) {\n await updateVisibilityMutation({ variables: { id: site.id, visibility: data.visibility } });\n }\n const rest = {\n name: data.name,\n provider: data.provider,\n providerConfig: data.providerConfig,\n };\n if (Object.keys(rest).length > 0) {\n await updateSiteMutation({ variables: { id: site.id, input: rest } });\n }\n await refetch();\n };\n\n const handleGetShareUrl = async (): Promise<string | null> => {\n if (!site?.currentRevisionId) return null;\n try {\n const { data: urlData } = await generateSignedUrl({ variables: { siteId: site.id } });\n return urlData?.generateDocsSignedUrl?.url ?? null;\n } catch {\n return null;\n }\n };\n\n const handleRollback = async (revisionId: string) => {\n if (!site) return;\n if (!confirm('Roll back to this revision? The current published docs will be replaced.'))\n return;\n setRollingBack(revisionId);\n try {\n await rollbackRevision({ variables: { siteId: site.id, revisionId } });\n await refetch();\n } finally {\n setRollingBack(null);\n }\n };\n\n const handleDeleteSite = async () => {\n if (!site) return;\n if (!confirm('Delete this docs site and all its pages and revisions? This cannot be undone.'))\n return;\n await deleteSiteMutation({ variables: { id: site.id } });\n await refetch();\n };\n\n // Loading / Error states\n if (loading && !site) {\n return (\n <LoadingState message={tr('vibecontrols.docs.tab.loading', 'Loading documentation...')} />\n );\n }\n\n if (error && !site) {\n return (\n <ErrorState\n title={tr('vibecontrols.docs.tab.loadFailed', 'Failed to load docs')}\n message={error.message}\n onRetry={() => refetch()}\n />\n );\n }\n\n // No site yet\n if (!site) {\n return (\n <>\n <DocsEmptyState onCreate={handleCreateSite} loading={creating} />\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Docs Site Quota Reached\"\n message=\"You have reached the documentation site limit for your current plan. Upgrade to create more docs sites.\"\n />\n </>\n );\n }\n\n const compileErrorBanner = compileError ? (\n <div className=\"flex items-center gap-2 px-4 py-2 mb-2 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span className=\"flex-1\">{compileError}</span>\n <button\n type=\"button\"\n onClick={() => setCompileError(null)}\n className=\"p-0.5 hover:opacity-70\"\n >\n <X className=\"size-3.5\" />\n </button>\n </div>\n ) : null;\n\n // Edit mode\n if (mode === 'edit') {\n return (\n <>\n {compileErrorBanner}\n <DocsEditPanel\n site={site}\n pages={pages}\n onBack={() => setMode('preview')}\n onSavePage={handleSavePage}\n onAddPage={handleAddPage}\n pageAdding={pageAdding}\n onDeletePage={handleDeletePage}\n onCompile={handleCompile}\n onSaveSettings={handleSaveSettings}\n onRollback={handleRollback}\n onDeleteSite={handleDeleteSite}\n rollingBack={rollingBack}\n compileRevision={compileRevision}\n onPollRevision={handlePollRevision}\n compiling={compiling}\n />\n </>\n );\n }\n\n // Preview mode\n return (\n <>\n {compileErrorBanner}\n <DocsPreviewPanel\n site={site}\n pages={pages}\n selectedPageSlug={selectedPageSlug}\n onSelectPage={setSelectedPageSlug}\n docsBaseUrl={site.currentRevisionId ? (docsBaseUrl ?? null) : null}\n docsHtml={site.currentRevisionId ? docsHtml : null}\n docsContentLoading={docsContentLoading}\n onEdit={() => setMode('edit')}\n onCompile={handleCompile}\n onSettings={() => setMode('edit')}\n onGetShareUrl={handleGetShareUrl}\n onOpenFullScreen={() => {\n const pathMap: Record<string, string> = {\n VIBE: 'vibes',\n CATALOG: 'catalogs',\n COMPONENT: 'components',\n };\n const segment = pathMap[effectiveOwnerType] || 'vibes';\n navigate(`/${segment}/${effectiveOwnerId}/docs`);\n }}\n compileRevision={compileRevision}\n onPollRevision={handlePollRevision}\n compiling={compiling}\n />\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Docs Site Quota Reached\"\n message=\"You have reached the documentation site limit for your current plan. Upgrade to create more docs sites.\"\n />\n </>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocsTabPanel.js","names":[],"sources":["../../../src/components/docs/DocsTabPanel.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport { AlertCircle, X } from 'lucide-react';\nimport { LoadingState, ErrorState, QuotaExhaustedDialog } from '@/components/shared';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { resolveGatewayUrls } from '@burdenoff/fe-libs/shared/config';\nimport { DocsEmptyState } from './DocsEmptyState';\nimport { DocsPreviewPanel } from './DocsPreviewPanel';\nimport { DocsEditPanel } from './DocsEditPanel';\nimport type { DocsSite, DocsRevision } from './types';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useGetDocsSiteQuery,\n useCreateDocsSiteMutation,\n useUpdateDocsSiteMutation,\n useUpdateDocsSiteVisibilityMutation,\n useCreateDocsPageMutation,\n useUpdateDocsPageMutation,\n useDeleteDocsPageMutation,\n useCompileDocsSiteMutation,\n useDocsContentQuery,\n useGetDocsRevisionLazyQuery,\n useGenerateDocsSignedUrlMutation,\n useRollbackDocsRevisionMutation,\n useDeleteDocsSiteMutation,\n} from '@/generated/wspace-operations';\n\ninterface DocsTabPanelProps {\n vibeId?: string;\n ownerType?: string;\n ownerId?: string;\n}\n\nexport function DocsTabPanel({ vibeId, ownerType, ownerId }: DocsTabPanelProps) {\n const tr = useTr();\n const effectiveOwnerType = ownerType || 'VIBE';\n const effectiveOwnerId = ownerId || vibeId || '';\n const [mode, setMode] = useState<'preview' | 'edit'>('preview');\n const [compileRevision, setCompileRevision] = useState<DocsRevision | null>(null);\n const [compiling, setCompiling] = useState(false);\n const [compileError, setCompileError] = useState<string | null>(null);\n const [selectedPageSlug, setSelectedPageSlug] = useState('');\n const [rollingBack, setRollingBack] = useState<string | null>(null);\n const navigate = useVibeNavigate();\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n // Queries\n const { data, loading, error, refetch } = useGetDocsSiteQuery({\n variables: { ownerType: effectiveOwnerType as any, ownerId: effectiveOwnerId },\n skip: !effectiveOwnerId,\n fetchPolicy: 'cache-and-network',\n });\n\n const site = (data?.vibecontrolsDocsSiteByOwner ?? null) as DocsSite | null;\n const pages = site?.pages || [];\n\n const {\n data: docsContentData,\n loading: docsContentLoading,\n refetch: refetchDocsContent,\n } = useDocsContentQuery({\n variables: { siteId: site?.id || '', path: selectedPageSlug },\n skip: !site?.id || !site.currentRevisionId,\n fetchPolicy: 'network-only',\n });\n const docsHtml = docsContentData?.docsContent?.html ?? null;\n\n // Re-fetch docs content whenever currentRevisionId changes (compile finished or first publish).\n // This runs post-render so skip:false is guaranteed — imperative calls during async handlers\n // are no-ops when skip is still true in the closure.\n const prevRevisionIdRef = useRef<string | null | undefined>(undefined);\n useEffect(() => {\n const currentId = site?.currentRevisionId ?? null;\n if (prevRevisionIdRef.current === undefined) {\n prevRevisionIdRef.current = currentId;\n return;\n }\n if (currentId && currentId !== prevRevisionIdRef.current) {\n prevRevisionIdRef.current = currentId;\n refetchDocsContent();\n }\n }, [site?.currentRevisionId]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Compute gateway docs URL for iframe src (workspaceId prefix for isolation)\n const gatewayUrls = resolveGatewayUrls();\n const docsBaseUrl = site?.workspaceId\n ? `${gatewayUrls.workspaceBaseUrl}/workspaces/static/${site.workspaceId}/docs/${effectiveOwnerId}/`\n : null;\n\n // Mutations\n const [createSiteMutation, { loading: creating }] = useCreateDocsSiteMutation();\n const [updateSiteMutation] = useUpdateDocsSiteMutation();\n const [updateVisibilityMutation] = useUpdateDocsSiteVisibilityMutation();\n const [createPageMutation, { loading: pageAdding }] = useCreateDocsPageMutation();\n const [updatePageMutation] = useUpdateDocsPageMutation();\n const [deletePageMutation] = useDeleteDocsPageMutation();\n const [compileMutation] = useCompileDocsSiteMutation();\n const [generateSignedUrl] = useGenerateDocsSignedUrlMutation();\n const [rollbackRevision] = useRollbackDocsRevisionMutation();\n const [deleteSiteMutation] = useDeleteDocsSiteMutation();\n const [fetchRevision] = useGetDocsRevisionLazyQuery({\n fetchPolicy: 'network-only',\n });\n\n // Handlers\n const handleCreateSite = async (\n provider: 'DOCUSAURUS' | 'MKDOCS' | 'VITEPRESS' = 'DOCUSAURUS'\n ) => {\n try {\n await createSiteMutation({\n variables: {\n input: {\n ownerType: effectiveOwnerType as any,\n ownerId: effectiveOwnerId,\n name: 'Documentation',\n provider: provider as any,\n },\n },\n refetchQueries: ['GetDocsSite'],\n awaitRefetchQueries: true,\n });\n setMode('edit');\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n throw err;\n }\n };\n\n const handleSavePage = async (\n id: string,\n data: { title?: string; content?: string; slug?: string }\n ) => {\n await updatePageMutation({ variables: { id, input: data } });\n };\n\n const handleAddPage = async (parentId?: string, category?: string) => {\n if (!site) return;\n const pageCount = pages.length;\n const slug = `page-${pageCount + 1}`;\n await createPageMutation({\n variables: {\n input: {\n siteId: site.id,\n parentId: parentId || null,\n slug,\n title: `New Page ${pageCount + 1}`,\n content: '# New Page\\n\\nStart writing here...',\n status: 'DRAFT',\n sidebarPosition: pageCount,\n category: category || null,\n },\n },\n });\n await refetch();\n };\n\n const handleDeletePage = async (pageId: string) => {\n if (!confirm('Delete this page?')) return;\n await deletePageMutation({ variables: { id: pageId } });\n await refetch();\n };\n\n const handleCompile = async () => {\n if (!site) return;\n setCompiling(true);\n setCompileError(null);\n try {\n const { data: compileData } = await compileMutation({\n variables: { siteId: site.id, publish: true },\n });\n if (compileData?.compileDocsSite) {\n setCompileRevision(compileData.compileDocsSite as DocsRevision);\n } else {\n setCompiling(false);\n setCompileError(\n 'Build could not be started — the server returned an empty response. Please try again.'\n );\n }\n } catch (err) {\n setCompiling(false);\n setCompileError(\n err instanceof Error ? err.message : 'Failed to start the build. Please try again.'\n );\n }\n };\n\n const handlePollRevision = async (revisionId: string): Promise<DocsRevision | null> => {\n const { data: revData } = await fetchRevision({ variables: { id: revisionId } });\n const rev = revData?.docsRevision as DocsRevision | null;\n if (rev) {\n setCompileRevision(rev);\n const done = ['SUCCEEDED', 'PUBLISHED', 'FAILED'].includes(rev.status);\n if (done) {\n setCompiling(false);\n if (rev.status === 'PUBLISHED' || rev.status === 'SUCCEEDED') {\n setSelectedPageSlug('');\n await refetch();\n }\n }\n }\n return rev;\n };\n\n const handleSaveSettings = async (data: {\n name?: string;\n visibility?: 'PRIVATE' | 'PUBLIC';\n provider?: 'DOCUSAURUS' | 'MKDOCS' | 'VITEPRESS';\n providerConfig?: Record<string, unknown>;\n }) => {\n if (!site) return;\n if (data.visibility && data.visibility !== site.visibility) {\n await updateVisibilityMutation({ variables: { id: site.id, visibility: data.visibility } });\n }\n const rest = {\n name: data.name,\n provider: data.provider,\n providerConfig: data.providerConfig,\n };\n if (Object.keys(rest).length > 0) {\n await updateSiteMutation({ variables: { id: site.id, input: rest } });\n }\n await refetch();\n };\n\n const handleGetShareUrl = async (): Promise<string | null> => {\n if (!site?.currentRevisionId) return null;\n try {\n const { data: urlData } = await generateSignedUrl({ variables: { siteId: site.id } });\n return urlData?.generateDocsSignedUrl?.url ?? null;\n } catch {\n return null;\n }\n };\n\n const handleRollback = async (revisionId: string) => {\n if (!site) return;\n if (!confirm('Roll back to this revision? The current published docs will be replaced.'))\n return;\n setRollingBack(revisionId);\n try {\n await rollbackRevision({ variables: { siteId: site.id, revisionId } });\n await refetch();\n } finally {\n setRollingBack(null);\n }\n };\n\n const handleDeleteSite = async () => {\n if (!site) return;\n if (!confirm('Delete this docs site and all its pages and revisions? This cannot be undone.'))\n return;\n await deleteSiteMutation({ variables: { id: site.id } });\n await refetch();\n };\n\n // Loading / Error states\n if (loading && !site) {\n return (\n <LoadingState message={tr('vibecontrols.docs.tab.loading', 'Loading documentation...')} />\n );\n }\n\n if (error && !site) {\n return (\n <ErrorState\n title={tr('vibecontrols.docs.tab.loadFailed', 'Failed to load docs')}\n message={error.message}\n onRetry={() => refetch()}\n />\n );\n }\n\n // No site yet\n if (!site) {\n return (\n <>\n <DocsEmptyState onCreate={handleCreateSite} loading={creating} />\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Docs Site Quota Reached\"\n message=\"You have reached the documentation site limit for your current plan. Upgrade to create more docs sites.\"\n />\n </>\n );\n }\n\n const compileErrorBanner = compileError ? (\n <div className=\"flex items-center gap-2 px-4 py-2 mb-2 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span className=\"flex-1\">{compileError}</span>\n <button\n type=\"button\"\n onClick={() => setCompileError(null)}\n className=\"p-0.5 hover:opacity-70\"\n >\n <X className=\"size-3.5\" />\n </button>\n </div>\n ) : null;\n\n // Edit mode\n if (mode === 'edit') {\n return (\n <>\n {compileErrorBanner}\n <DocsEditPanel\n site={site}\n pages={pages}\n onBack={() => setMode('preview')}\n onSavePage={handleSavePage}\n onAddPage={handleAddPage}\n pageAdding={pageAdding}\n onDeletePage={handleDeletePage}\n onCompile={handleCompile}\n onSaveSettings={handleSaveSettings}\n onRollback={handleRollback}\n onDeleteSite={handleDeleteSite}\n rollingBack={rollingBack}\n compileRevision={compileRevision}\n onPollRevision={handlePollRevision}\n compiling={compiling}\n />\n </>\n );\n }\n\n // Preview mode\n return (\n <>\n {compileErrorBanner}\n <DocsPreviewPanel\n site={site}\n pages={pages}\n selectedPageSlug={selectedPageSlug}\n onSelectPage={setSelectedPageSlug}\n docsBaseUrl={site.currentRevisionId ? (docsBaseUrl ?? null) : null}\n docsHtml={site.currentRevisionId ? docsHtml : null}\n docsContentLoading={docsContentLoading}\n onEdit={() => setMode('edit')}\n onCompile={handleCompile}\n onSettings={() => setMode('edit')}\n onGetShareUrl={handleGetShareUrl}\n onOpenFullScreen={() => {\n const pathMap: Record<string, string> = {\n VIBE: 'vibes',\n CATALOG: 'catalogs',\n COMPONENT: 'components',\n };\n const segment = pathMap[effectiveOwnerType] || 'vibes';\n navigate(`/${segment}/${effectiveOwnerId}/docs`);\n }}\n compileRevision={compileRevision}\n onPollRevision={handlePollRevision}\n compiling={compiling}\n />\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Docs Site Quota Reached\"\n message=\"You have reached the documentation site limit for your current plan. Upgrade to create more docs sites.\"\n />\n </>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useAvailableNoteTagsQuery as e, useCreateNoteTagMutation as t } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useTr as n } from "../../shared/hooks/useTr.js";
|
|
3
3
|
import { useVibeControls as r } from "../../providers/VibeControlsProvider.js";
|
|
4
4
|
import { useEffect as i, useState as a } from "react";
|
|
5
5
|
import { Check as o, Edit as s, Eye as c, Globe as l, Lock as u, Plus as d, Save as f, Tag as p, X as m } from "lucide-react";
|
|
@@ -15,13 +15,13 @@ var _ = [
|
|
|
15
15
|
"var(--color-text-tertiary)"
|
|
16
16
|
];
|
|
17
17
|
function v({ note: v, vibeOptions: y = [], defaultVibeId: b, hideVibeSelect: x, onSubmit: S, onCancel: C, isLoading: w }) {
|
|
18
|
-
let T =
|
|
18
|
+
let T = n(), { workspaceId: E } = r(), [D, O] = a(!1), [k, A] = a({
|
|
19
19
|
title: v?.title || "",
|
|
20
20
|
content: v?.content || "",
|
|
21
21
|
vibeId: v?.vibeId || b || "",
|
|
22
22
|
isPinned: v?.isPinned ?? !1,
|
|
23
23
|
visibility: v?.visibility ?? "WORKSPACE"
|
|
24
|
-
}), [j, M] = a(/* @__PURE__ */ new Set()), [N, P] = a(!1), [F, I] = a(!1), { data: L, refetch: R } =
|
|
24
|
+
}), [j, M] = a(/* @__PURE__ */ new Set()), [N, P] = a(!1), [F, I] = a(!1), { data: L, refetch: R } = e(), z = (L?.availableNoteTags ?? []).map((e) => ({
|
|
25
25
|
id: e.id,
|
|
26
26
|
key: e.key,
|
|
27
27
|
label: e.label,
|
|
@@ -41,7 +41,7 @@ function v({ note: v, vibeOptions: y = [], defaultVibeId: b, hideVibeSelect: x,
|
|
|
41
41
|
v,
|
|
42
42
|
N
|
|
43
43
|
]);
|
|
44
|
-
let [B] =
|
|
44
|
+
let [B] = t(), [V, H] = a(""), [U, W] = a("var(--color-accent-purple)"), [G, K] = a(!1), q = async () => {
|
|
45
45
|
let e = V.trim();
|
|
46
46
|
if (!(!e || G)) {
|
|
47
47
|
K(!0);
|