@burdenoff/microfe-vibecontrols 2026.531.13 → 2026.531.15

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.
Files changed (41) hide show
  1. package/dist/components/docs/DocsTabPanel.js +146 -125
  2. package/dist/components/docs/DocsTabPanel.js.map +1 -1
  3. package/dist/components/gitops/VibeGitOpsTab.js +3 -3
  4. package/dist/components/gitops/VibeGitOpsTab.js.map +1 -1
  5. package/dist/components/notes/NotesTabPanel.js +1 -1
  6. package/dist/components/notes/NotesTabPanel.js.map +1 -1
  7. package/dist/components/shared/PluginGate.js +1 -1
  8. package/dist/components/shared/PluginGate.js.map +1 -1
  9. package/dist/components/tunnels/TunnelCreateWizard.js +1 -1
  10. package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
  11. package/dist/pages/ActionsPage.js +1 -1
  12. package/dist/pages/ActionsPage.js.map +1 -1
  13. package/dist/pages/AgentDetailsPage.js +38 -37
  14. package/dist/pages/AgentDetailsPage.js.map +1 -1
  15. package/dist/pages/AgentsPage.js +1 -1
  16. package/dist/pages/AgentsPage.js.map +1 -1
  17. package/dist/pages/CatalogPage.js +1 -1
  18. package/dist/pages/CatalogPage.js.map +1 -1
  19. package/dist/pages/ComponentDetailPage.js +1 -1
  20. package/dist/pages/ComponentDetailPage.js.map +1 -1
  21. package/dist/pages/ComponentsPage.js +1 -1
  22. package/dist/pages/ComponentsPage.js.map +1 -1
  23. package/dist/pages/SessionDetailsPage.js +1 -1
  24. package/dist/pages/SessionDetailsPage.js.map +1 -1
  25. package/dist/pages/SessionsPage.js +1 -1
  26. package/dist/pages/SessionsPage.js.map +1 -1
  27. package/dist/pages/TargetsPage.js +1 -1
  28. package/dist/pages/TargetsPage.js.map +1 -1
  29. package/dist/pages/TemplateDetailPage.js +1 -1
  30. package/dist/pages/TemplateDetailPage.js.map +1 -1
  31. package/dist/pages/VibeDeckPage.js +1 -1
  32. package/dist/pages/VibeDeckPage.js.map +1 -1
  33. package/dist/pages/VibesPage.js +1 -1
  34. package/dist/pages/VibesPage.js.map +1 -1
  35. package/dist/pages/tunnels/TunnelsTab.js +1 -1
  36. package/dist/pages/tunnels/TunnelsTab.js.map +1 -1
  37. package/dist/providers/QuotaExhaustedProvider.js +1 -1
  38. package/dist/providers/QuotaExhaustedProvider.js.map +1 -1
  39. package/dist/services/iframeToken.js +18 -18
  40. package/dist/services/iframeToken.js.map +1 -1
  41. package/package.json +1 -1
@@ -1,46 +1,46 @@
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
- import { useVibeNavigate as f } from "../../providers/VibeControlsProvider.js";
6
- import { LoadingState as p } from "../shared/LoadingState.js";
7
- import { ErrorState as m } from "../shared/ErrorState.js";
1
+ import { useCompileDocsSiteMutation as e, useCreateDocsPageMutation as t, useCreateDocsSiteMutation as n, useDeleteDocsPageMutation as r, useDeleteDocsSiteMutation as ee, useDocsContentQuery as te, useGenerateDocsSignedUrlMutation as ne, useGetDocsRevisionLazyQuery as i, useGetDocsSiteQuery as a, useRollbackDocsRevisionMutation as re, useUpdateDocsPageMutation as ie, useUpdateDocsSiteMutation as ae, useUpdateDocsSiteVisibilityMutation as oe } from "../../generated/wspace-operations.js";
2
+ import { useTr as se } from "../../shared/hooks/useTr.js";
3
+ import { QuotaExhaustedDialog as o } from "../shared/QuotaExhaustedDialog.js";
4
+ import { getQuotaDialogContent as s, isQuotaExhaustedError as c, markQuotaHandledLocally as l } from "../../utils/quotaUtils.js";
5
+ import { useVibeNavigate as u } from "../../providers/VibeControlsProvider.js";
6
+ import { LoadingState as d } from "../shared/LoadingState.js";
7
+ import { ErrorState as f } from "../shared/ErrorState.js";
8
8
  import "../shared/index.js";
9
- import { DocsEmptyState as h } from "./DocsEmptyState.js";
10
- import { DocsPreviewPanel as g } from "./DocsPreviewPanel.js";
11
- import { DocsEditPanel as _ } from "./DocsEditPanel.js";
12
- import { useEffect as v, useRef as y, useState as b } from "react";
13
- import { AlertCircle as ae, X as oe } from "lucide-react";
9
+ import { DocsEmptyState as p } from "./DocsEmptyState.js";
10
+ import { DocsPreviewPanel as m } from "./DocsPreviewPanel.js";
11
+ import { DocsEditPanel as h } from "./DocsEditPanel.js";
12
+ import { useEffect as g, useRef as _, useState as v } from "react";
13
+ import { AlertCircle as y, X as b } from "lucide-react";
14
14
  import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
15
- import { resolveGatewayUrls as se } from "@burdenoff/fe-libs/shared/config";
15
+ import { resolveGatewayUrls as ce } from "@burdenoff/fe-libs/shared/config";
16
16
  //#region src/components/docs/DocsTabPanel.tsx
17
- function w({ vibeId: w, ownerType: ce, ownerId: le }) {
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({
17
+ function w({ vibeId: w, ownerType: le, ownerId: ue }) {
18
+ let T = se(), E = le || "VIBE", D = ue || w || "", [de, O] = v("preview"), [k, A] = v(null), [j, M] = v(!1), [N, P] = v(null), [F, I] = v(""), [L, R] = v(null), z = u(), [B, V] = v(!1), [H, U] = v(!1), { data: fe, loading: pe, error: W, refetch: G } = a({
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 } = a({
25
+ }), K = fe?.vibecontrolsDocsSiteByOwner ?? null, q = K?.pages || [], { data: me, loading: he, refetch: ge } = te({
26
26
  variables: {
27
- siteId: q?.id || "",
28
- path: I
27
+ siteId: K?.id || "",
28
+ path: F
29
29
  },
30
- skip: !q?.id || !q.currentRevisionId,
30
+ skip: !K?.id || !K.currentRevisionId,
31
31
  fetchPolicy: "network-only"
32
- }), pe = ue?.docsContent?.html ?? null, Y = y(void 0);
33
- v(() => {
34
- let e = q?.currentRevisionId ?? null;
35
- if (Y.current === void 0) {
36
- Y.current = e;
32
+ }), _e = me?.docsContent?.html ?? null, J = _(void 0);
33
+ g(() => {
34
+ let e = K?.currentRevisionId ?? null;
35
+ if (J.current === void 0) {
36
+ J.current = e;
37
37
  return;
38
38
  }
39
- e && e !== Y.current && (Y.current = e, fe());
40
- }, [q?.currentRevisionId]);
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") => {
39
+ e && e !== J.current && (J.current = e, ge());
40
+ }, [K?.currentRevisionId]);
41
+ let ve = ce(), ye = K?.workspaceId ? `${ve.workspaceBaseUrl}/workspaces/static/${K.workspaceId}/docs/${D}/` : null, [be, { loading: xe }] = n(), [Se] = ae(), [Ce] = oe(), [we, { loading: Te }] = t(), [Ee] = ie(), [De] = r(), [Oe] = e(), [ke] = ne(), [Ae] = re(), [je] = ee(), [Y] = i({ fetchPolicy: "network-only" }), Me = async (e = "DOCUSAURUS") => {
42
42
  try {
43
- await ge({
43
+ await be({
44
44
  variables: { input: {
45
45
  ownerType: E,
46
46
  ownerId: D,
@@ -49,24 +49,24 @@ function w({ vibeId: w, ownerType: ce, ownerId: le }) {
49
49
  } },
50
50
  refetchQueries: ["GetDocsSite"],
51
51
  awaitRefetchQueries: !0
52
- }), k("edit");
52
+ }), O("edit");
53
53
  } catch (e) {
54
- if (u(e)) {
55
- d(), H(!0);
54
+ if (c(e)) {
55
+ l(), V(!0);
56
56
  return;
57
57
  }
58
58
  throw e;
59
59
  }
60
- }, Ae = async (e, t) => {
61
- await Se({ variables: {
60
+ }, Ne = async (e, t) => {
61
+ await Ee({ variables: {
62
62
  id: e,
63
63
  input: t
64
64
  } });
65
- }, X = async (e, t) => {
66
- if (!q) return;
67
- let n = J.length, r = `page-${n + 1}`;
68
- await be({ variables: { input: {
69
- siteId: q.id,
65
+ }, Pe = async (e, t) => {
66
+ if (!K) return;
67
+ let n = q.length, r = `page-${n + 1}`;
68
+ await we({ variables: { input: {
69
+ siteId: K.id,
70
70
  parentId: e || null,
71
71
  slug: r,
72
72
  title: `New Page ${n + 1}`,
@@ -74,33 +74,41 @@ function w({ vibeId: w, ownerType: ce, ownerId: le }) {
74
74
  status: "DRAFT",
75
75
  sidebarPosition: n,
76
76
  category: t || null
77
- } } }), await K();
78
- }, je = async (e) => {
79
- confirm("Delete this page?") && (await Ce({ variables: { id: e } }), await K());
80
- }, Z = async () => {
81
- if (q) {
82
- N(!0), F(null);
77
+ } } }), await G();
78
+ }, Fe = async (e) => {
79
+ confirm("Delete this page?") && (await De({ variables: { id: e } }), await G());
80
+ }, X = async () => {
81
+ if (K) {
82
+ M(!0), P(null);
83
83
  try {
84
- let { data: e } = await we({ variables: {
85
- siteId: q.id,
84
+ let e = await Oe({ variables: {
85
+ siteId: K.id,
86
86
  publish: !0
87
87
  } });
88
- e?.compileDocsSite ? j(e.compileDocsSite) : (N(!1), F("Build could not be started — the server returned an empty response. Please try again."));
88
+ if (e.error && c(e.error)) {
89
+ l(), M(!1), U(!0);
90
+ return;
91
+ }
92
+ e.data?.compileDocsSite ? A(e.data.compileDocsSite) : (M(!1), P("Build could not be started — the server returned an empty response. Please try again."));
89
93
  } catch (e) {
90
- N(!1), F(e instanceof Error ? e.message : "Failed to start the build. Please try again.");
94
+ if (M(!1), c(e)) {
95
+ l(), U(!0);
96
+ return;
97
+ }
98
+ P(e instanceof Error ? e.message : "Failed to start the build. Please try again.");
91
99
  }
92
100
  }
93
- }, Q = async (e) => {
94
- let { data: t } = await Oe({ variables: { id: e } }), n = t?.docsRevision;
95
- return n && (j(n), [
101
+ }, Z = async (e) => {
102
+ let { data: t } = await Y({ variables: { id: e } }), n = t?.docsRevision;
103
+ return n && (A(n), [
96
104
  "SUCCEEDED",
97
105
  "PUBLISHED",
98
106
  "FAILED"
99
- ].includes(n.status) && (N(!1), (n.status === "PUBLISHED" || n.status === "SUCCEEDED") && (L(""), await K()))), n;
100
- }, Me = async (e) => {
101
- if (!q) return;
102
- e.visibility && e.visibility !== q.visibility && await ye({ variables: {
103
- id: q.id,
107
+ ].includes(n.status) && (M(!1), (n.status === "PUBLISHED" || n.status === "SUCCEEDED") && (I(""), await G()))), n;
108
+ }, Ie = async (e) => {
109
+ if (!K) return;
110
+ e.visibility && e.visibility !== K.visibility && await Ce({ variables: {
111
+ id: K.id,
104
112
  visibility: e.visibility
105
113
  } });
106
114
  let t = {
@@ -108,113 +116,126 @@ function w({ vibeId: w, ownerType: ce, ownerId: le }) {
108
116
  provider: e.provider,
109
117
  providerConfig: e.providerConfig
110
118
  };
111
- Object.keys(t).length > 0 && await ve({ variables: {
112
- id: q.id,
119
+ Object.keys(t).length > 0 && await Se({ variables: {
120
+ id: K.id,
113
121
  input: t
114
- } }), await K();
115
- }, Ne = async () => {
116
- if (!q?.currentRevisionId) return null;
122
+ } }), await G();
123
+ }, Le = async () => {
124
+ if (!K?.currentRevisionId) return null;
117
125
  try {
118
- let { data: e } = await Te({ variables: { siteId: q.id } });
126
+ let { data: e } = await ke({ variables: { siteId: K.id } });
119
127
  return e?.generateDocsSignedUrl?.url ?? null;
120
128
  } catch {
121
129
  return null;
122
130
  }
123
- }, Pe = async (e) => {
124
- if (q && confirm("Roll back to this revision? The current published docs will be replaced.")) {
125
- z(e);
131
+ }, Re = async (e) => {
132
+ if (K && confirm("Roll back to this revision? The current published docs will be replaced.")) {
133
+ R(e);
126
134
  try {
127
- await Ee({ variables: {
128
- siteId: q.id,
135
+ await Ae({ variables: {
136
+ siteId: K.id,
129
137
  revisionId: e
130
- } }), await K();
138
+ } }), await G();
131
139
  } finally {
132
- z(null);
140
+ R(null);
133
141
  }
134
142
  }
135
- }, Fe = async () => {
136
- q && confirm("Delete this docs site and all its pages and revisions? This cannot be undone.") && (await De({ variables: { id: q.id } }), await K());
143
+ }, ze = async () => {
144
+ K && confirm("Delete this docs site and all its pages and revisions? This cannot be undone.") && (await je({ variables: { id: K.id } }), await G());
137
145
  };
138
- if (W && !q) return /* @__PURE__ */ S(p, { message: T("vibecontrols.docs.tab.loading", "Loading documentation...") });
139
- if (G && !q) return /* @__PURE__ */ S(m, {
146
+ if (pe && !K) return /* @__PURE__ */ S(d, { message: T("vibecontrols.docs.tab.loading", "Loading documentation...") });
147
+ if (W && !K) return /* @__PURE__ */ S(f, {
140
148
  title: T("vibecontrols.docs.tab.loadFailed", "Failed to load docs"),
141
- message: G.message,
142
- onRetry: () => K()
149
+ message: W.message,
150
+ onRetry: () => G()
143
151
  });
144
- if (!q) return /* @__PURE__ */ C(x, { children: [/* @__PURE__ */ S(h, {
145
- onCreate: ke,
146
- loading: _e
147
- }), /* @__PURE__ */ S(l, {
148
- isOpen: V,
149
- onClose: () => H(!1),
152
+ if (!K) return /* @__PURE__ */ C(x, { children: [/* @__PURE__ */ S(p, {
153
+ onCreate: Me,
154
+ loading: xe
155
+ }), /* @__PURE__ */ S(o, {
156
+ isOpen: B,
157
+ onClose: () => V(!1),
150
158
  onConfirm: () => {
151
- H(!1), B("/billing");
159
+ V(!1), z("/billing/plans");
152
160
  },
153
161
  title: "Docs Site Quota Reached",
154
162
  message: "You have reached the documentation site limit for your current plan. Upgrade to create more docs sites."
155
163
  })] });
156
- let $ = P ? /* @__PURE__ */ C("div", {
164
+ let Q = N ? /* @__PURE__ */ C("div", {
157
165
  className: "flex items-center gap-2 px-4 py-2 mb-2 bg-status-error-bg text-status-error-text text-sm rounded-md",
158
166
  children: [
159
- /* @__PURE__ */ S(ae, { className: "size-4 shrink-0" }),
167
+ /* @__PURE__ */ S(y, { className: "size-4 shrink-0" }),
160
168
  /* @__PURE__ */ S("span", {
161
169
  className: "flex-1",
162
- children: P
170
+ children: N
163
171
  }),
164
172
  /* @__PURE__ */ S("button", {
165
173
  type: "button",
166
- onClick: () => F(null),
174
+ onClick: () => P(null),
167
175
  className: "p-0.5 hover:opacity-70",
168
- children: /* @__PURE__ */ S(oe, { className: "size-3.5" })
176
+ children: /* @__PURE__ */ S(b, { className: "size-3.5" })
169
177
  })
170
178
  ]
171
- }) : null;
172
- return O === "edit" ? /* @__PURE__ */ C(x, { children: [$, /* @__PURE__ */ S(_, {
173
- site: q,
174
- pages: J,
175
- onBack: () => k("preview"),
176
- onSavePage: Ae,
177
- onAddPage: X,
178
- pageAdding: xe,
179
- onDeletePage: je,
180
- onCompile: Z,
181
- onSaveSettings: Me,
182
- onRollback: Pe,
183
- onDeleteSite: Fe,
184
- rollingBack: R,
185
- compileRevision: A,
186
- onPollRevision: Q,
187
- compiling: M
188
- })] }) : /* @__PURE__ */ C(x, { children: [
179
+ }) : null, $ = /* @__PURE__ */ S(o, {
180
+ isOpen: H,
181
+ onClose: () => U(!1),
182
+ onConfirm: () => {
183
+ U(!1), z("/billing/plans");
184
+ },
185
+ title: s("docs-compilation").title,
186
+ message: s("docs-compilation").message
187
+ });
188
+ return de === "edit" ? /* @__PURE__ */ C(x, { children: [
189
+ Q,
190
+ $,
191
+ /* @__PURE__ */ S(h, {
192
+ site: K,
193
+ pages: q,
194
+ onBack: () => O("preview"),
195
+ onSavePage: Ne,
196
+ onAddPage: Pe,
197
+ pageAdding: Te,
198
+ onDeletePage: Fe,
199
+ onCompile: X,
200
+ onSaveSettings: Ie,
201
+ onRollback: Re,
202
+ onDeleteSite: ze,
203
+ rollingBack: L,
204
+ compileRevision: k,
205
+ onPollRevision: Z,
206
+ compiling: j
207
+ })
208
+ ] }) : /* @__PURE__ */ C(x, { children: [
209
+ Q,
189
210
  $,
190
- /* @__PURE__ */ S(g, {
191
- site: q,
192
- pages: J,
193
- selectedPageSlug: I,
194
- onSelectPage: L,
195
- docsBaseUrl: q.currentRevisionId ? he ?? null : null,
196
- docsHtml: q.currentRevisionId ? pe : null,
197
- docsContentLoading: de,
198
- onEdit: () => k("edit"),
199
- onCompile: Z,
200
- onSettings: () => k("edit"),
201
- onGetShareUrl: Ne,
211
+ /* @__PURE__ */ S(m, {
212
+ site: K,
213
+ pages: q,
214
+ selectedPageSlug: F,
215
+ onSelectPage: I,
216
+ docsBaseUrl: K.currentRevisionId ? ye ?? null : null,
217
+ docsHtml: K.currentRevisionId ? _e : null,
218
+ docsContentLoading: he,
219
+ onEdit: () => O("edit"),
220
+ onCompile: X,
221
+ onSettings: () => O("edit"),
222
+ onGetShareUrl: Le,
202
223
  onOpenFullScreen: () => {
203
- B(`/${{
224
+ z(`/${{
204
225
  VIBE: "vibes",
205
226
  CATALOG: "catalogs",
206
227
  COMPONENT: "components"
207
228
  }[E] || "vibes"}/${D}/docs`);
208
229
  },
209
- compileRevision: A,
210
- onPollRevision: Q,
211
- compiling: M
230
+ compileRevision: k,
231
+ onPollRevision: Z,
232
+ compiling: j
212
233
  }),
213
- /* @__PURE__ */ S(l, {
214
- isOpen: V,
215
- onClose: () => H(!1),
234
+ /* @__PURE__ */ S(o, {
235
+ isOpen: B,
236
+ onClose: () => V(!1),
216
237
  onConfirm: () => {
217
- H(!1), B("/billing");
238
+ V(!1), z("/billing/plans");
218
239
  },
219
240
  title: "Docs Site Quota Reached",
220
241
  message: "You have reached the documentation site limit for your current plan. Upgrade to create more docs sites."
@@ -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 {\n isQuotaExhaustedError,\n markQuotaHandledLocally,\n getQuotaDialogContent,\n} 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 const [showCompileQuotaExhausted, setShowCompileQuotaExhausted] = 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 result = await compileMutation({\n variables: { siteId: site.id, publish: true },\n });\n if (result.error && isQuotaExhaustedError(result.error)) {\n markQuotaHandledLocally();\n setCompiling(false);\n setShowCompileQuotaExhausted(true);\n return;\n }\n if (result.data?.compileDocsSite) {\n setCompileRevision(result.data.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 if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowCompileQuotaExhausted(true);\n return;\n }\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/plans');\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 const compileQuotaDialog = (\n <QuotaExhaustedDialog\n isOpen={showCompileQuotaExhausted}\n onClose={() => setShowCompileQuotaExhausted(false)}\n onConfirm={() => {\n setShowCompileQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title={getQuotaDialogContent('docs-compilation').title}\n message={getQuotaDialogContent('docs-compilation').message}\n />\n );\n\n // Edit mode\n if (mode === 'edit') {\n return (\n <>\n {compileErrorBanner}\n {compileQuotaDialog}\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 {compileQuotaDialog}\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/plans');\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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"}
@@ -105,10 +105,10 @@ var x = ({ vibeId: x, agentId: w, profile: T, tunnelUrl: E, tr: D }) => {
105
105
  return;
106
106
  }
107
107
  try {
108
- let a = await r(w, T, "/ungit");
108
+ let a = await r(w, T, "/git");
109
109
  if (e) return;
110
- let o = `${E.replace(/\/$/, "")}/ungit/`;
111
- Q(n(o, a.token)), t = i(w, T, "/ungit", a, (e) => {
110
+ let o = `${E.replace(/\/$/, "")}/git/`;
111
+ Q(n(o, a.token)), t = i(w, T, "/git", a, (e) => {
112
112
  Q(n(o, e.token));
113
113
  });
114
114
  } catch (t) {
@@ -1 +1 @@
1
- {"version":3,"file":"VibeGitOpsTab.js","names":[],"sources":["../../../src/components/gitops/VibeGitOpsTab.tsx"],"sourcesContent":["/**\n * VibeGitOpsTab — GitOps tab content for the Vibe detail page.\n *\n * V2: All data is sourced from the agent over the tunnel via\n * `/api/profiles/<profile>/gitops/<provider>/*`. No backend GraphQL\n * involvement. The backend's old `gitopsProvider` records are deprecated.\n *\n * Sub-tabs:\n * 1. Repo Stats — repo metadata + PR + issue + security counts\n * 2. CI Pipelines — recent runs + analytics percentiles\n * 3. Git UI — Ungit iframe (when vibe-plugin-tool-git is installed)\n */\nimport { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { motion } from 'framer-motion';\nimport { GitMerge, BarChart3, Play, GitBranch, RefreshCw } from 'lucide-react';\nimport { useQueryClient } from '@tanstack/react-query';\n\nimport { RepoStatsTab } from './RepoStatsTab';\nimport { CITab } from './CITab';\nimport { GitOpsSetup } from './GitOpsSetup';\nimport { GitOpsProviderPicker } from './GitOpsProviderPicker';\nimport { PluginGate } from '@/components/shared';\nimport { GIT_PACKAGE } from '@/constants/pluginCatalog';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\nimport {\n useGitopsContributors,\n useGitopsIssues,\n useGitopsLanguages,\n useGitopsPipelineAnalytics,\n useGitopsProviders,\n useGitopsPullRequests,\n useGitopsRepos,\n useGitopsRuns,\n useGitopsSecurityAlerts,\n} from '@/hooks/useGitops';\nimport { composeCiStatus, composeRepoStats, type ProviderName, type RepoFqn } from './types';\nimport {\n mintIframeToken,\n scheduleIframeTokenRefresh,\n appendIframeTokenQuery,\n type IframeTokenAutoRefreshHandle,\n type IframeTokenResponse,\n} from '@/services/iframeToken';\n\ninterface VibeGitOpsTabProps {\n vibeId: string;\n agentId: string;\n /** Canonical agent profile name. */\n profile: string;\n /**\n * Tunnel URL — retained ONLY for the `git-ui` iframe handshake\n * (ungit subpath) which still bootstraps via a one-time iframe token.\n * GitOps REST calls no longer use this; they route through the svc\n * proxy (F4 Wave C).\n */\n tunnelUrl: string | null;\n tr: (key: string, fallback: string) => string;\n}\n\ntype GitOpsSubTab = 'repo-stats' | 'ci' | 'git-ui';\n\nexport const VibeGitOpsTab: FC<VibeGitOpsTabProps> = ({\n vibeId,\n agentId,\n profile,\n tunnelUrl,\n tr,\n}) => {\n const [subTab, setSubTab] = useState<GitOpsSubTab>('repo-stats');\n const [showSetup, setShowSetup] = useState(false);\n const [selectedProvider, setSelectedProvider] = useState<ProviderName | null>(null);\n const [selectedRepoFqn, setSelectedRepoFqn] = useState<RepoFqn | null>(null);\n const { workspaceId } = useVibeControls();\n const qc = useQueryClient();\n\n const ctx = useMemo(\n () => ({ profile, workspaceId: workspaceId ?? '', agentId }),\n [profile, workspaceId, agentId]\n );\n\n // Providers registered on the agent.\n const providersQ = useGitopsProviders(ctx);\n const authedProviders = useMemo(\n () => providersQ.data?.filter((p) => p.registered && p.authenticated) ?? [],\n [providersQ.data]\n );\n const hasProviders = authedProviders.length > 0;\n\n // Pick a default once data loads.\n useEffect(() => {\n if (!selectedProvider && authedProviders.length > 0) {\n const first = authedProviders[0];\n if (first) {\n setSelectedProvider(first.name as ProviderName);\n }\n }\n }, [authedProviders, selectedProvider]);\n\n // Once a provider is selected, fetch repos for it and pick the first one\n // by default. The user can switch repos via the picker.\n const reposQ = useGitopsRepos(ctx, selectedProvider, { limit: 30 });\n useEffect(() => {\n if (!selectedRepoFqn && reposQ.data?.items.length) {\n const first = reposQ.data.items[0];\n if (first) {\n setSelectedRepoFqn(first.fqn);\n }\n }\n }, [reposQ.data, selectedRepoFqn]);\n\n // Repo Stats composite — runs only when active sub-tab is repo-stats.\n const repoQ = useGitopsRepos(ctx, selectedProvider, { limit: 1 }); // metadata via list (1st item)\n const repoDetailQ = useGitopsRepos.length ? null : null; // keep TS happy; we use repoQ.items[0]\n const pullsQ = useGitopsPullRequests(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const issuesQ = useGitopsIssues(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const languagesQ = useGitopsLanguages(ctx, selectedProvider, selectedRepoFqn);\n const contributorsQ = useGitopsContributors(ctx, selectedProvider, selectedRepoFqn, 20);\n const securityQ = useGitopsSecurityAlerts(ctx, selectedProvider, selectedRepoFqn);\n\n const activeRepo = useMemo(\n () =>\n selectedRepoFqn\n ? (repoQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n reposQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n null)\n : (repoQ.data?.items[0] ?? null),\n [repoQ.data, reposQ.data, selectedRepoFqn]\n );\n\n const repoStats = useMemo(\n () =>\n composeRepoStats({\n repo: activeRepo,\n pulls: pullsQ.data,\n issues: issuesQ.data,\n languages: languagesQ.data,\n contributors: contributorsQ.data,\n securityAlerts: securityQ.data,\n }),\n [activeRepo, pullsQ.data, issuesQ.data, languagesQ.data, contributorsQ.data, securityQ.data]\n );\n\n // CI Status composite.\n const runsQ = useGitopsRuns(ctx, selectedProvider, selectedRepoFqn, { limit: 30 });\n const ciAnalyticsQ = useGitopsPipelineAnalytics(ctx, selectedProvider, selectedRepoFqn);\n const ciStatus = useMemo(\n () =>\n composeCiStatus({\n pipelines: undefined,\n runs: runsQ.data,\n analytics: ciAnalyticsQ.data,\n }),\n [runsQ.data, ciAnalyticsQ.data]\n );\n\n const repoStatsLoading = pullsQ.isLoading || issuesQ.isLoading || languagesQ.isLoading;\n const ciStatusLoading = runsQ.isLoading || ciAnalyticsQ.isLoading;\n const repoStatsError = (pullsQ.error || issuesQ.error || languagesQ.error)?.message;\n const ciStatusError = (runsQ.error || ciAnalyticsQ.error)?.message;\n\n const handleRefresh = useCallback(() => {\n void qc.invalidateQueries({ queryKey: ['gitops', workspaceId ?? '', agentId, profile] });\n }, [qc, workspaceId, agentId, profile]);\n\n const isRefreshing = pullsQ.isFetching || runsQ.isFetching;\n\n const subTabs: { id: GitOpsSubTab; label: string; icon: typeof BarChart3 }[] = [\n { id: 'repo-stats', label: tr('gitops.repoStats', 'Repo Stats'), icon: BarChart3 },\n { id: 'ci', label: tr('gitops.ci', 'CI Pipelines'), icon: Play },\n { id: 'git-ui', label: tr('gitops.gitUI', 'Git UI'), icon: GitBranch },\n ];\n\n // Ungit iframe state (unchanged from v1).\n const [ungitIframeUrl, setUngitIframeUrl] = useState<string | null>(null);\n const [ungitTokenError, setUngitTokenError] = useState<string | null>(null);\n useEffect(() => {\n let cancelled = false;\n let handle: IframeTokenAutoRefreshHandle | null = null;\n const setup = async () => {\n if (!agentId || !tunnelUrl || subTab !== 'git-ui') {\n setUngitIframeUrl(null);\n return;\n }\n try {\n // F5 residual — Ungit's reverse proxy lives at `/ungit/` (declared\n // by the external `vibe-plugin-tool-git` package's publicPaths +\n // meta.url override). The plugin's createUngitProxy validates the\n // entry-document auth via `?vt=<token>` (or `?apiKey=`), then\n // issues its own session cookie for sub-requests. Switching this\n // to a host-level hash→cookie shim (like F5 does for `/ui/<p>`)\n // would require an SDK contract change + coordinated republish of\n // the external plugin package. Until that ships, the iframe-token\n // sits in the URL once. Mitigations in place: F6 makes it single-\n // use (replayed token rejected); F7 strips the token before\n // upstream forwarding so Ungit never sees it; F10 enforces Origin\n // on non-GET/HEAD sub-requests. Residual surface: one query string\n // appears in CF logs + browser history for the initial document.\n const initial: IframeTokenResponse = await mintIframeToken(agentId, profile, '/ungit');\n if (cancelled) return;\n const baseUrl = `${tunnelUrl.replace(/\\/$/, '')}/ungit/`;\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, initial.token));\n handle = scheduleIframeTokenRefresh(\n agentId,\n profile,\n '/ungit',\n initial,\n (next: IframeTokenResponse) => {\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, next.token));\n }\n );\n } catch (err) {\n if (!cancelled) {\n setUngitTokenError(err instanceof Error ? err.message : 'Failed to fetch iframe token');\n }\n }\n };\n void setup();\n return () => {\n cancelled = true;\n handle?.cancel();\n };\n }, [agentId, profile, tunnelUrl, subTab]);\n\n return (\n <motion.div\n key=\"gitops\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n data-vibe-id={vibeId}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <GitMerge className=\"size-5 text-text-secondary\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibeDetails.gitopsTitle', 'GitOps')}\n </h3>\n </div>\n <div className=\"flex items-center gap-2\">\n {hasProviders && (\n <GitOpsProviderPicker\n providers={authedProviders.map((p) => p.name as ProviderName)}\n selected={selectedProvider}\n onChange={setSelectedProvider}\n />\n )}\n {hasProviders && (subTab === 'repo-stats' || subTab === 'ci') && (\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={isRefreshing}\n className=\"flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-md border border-border-default text-text-secondary hover:text-text-primary hover:border-border-strong disabled:opacity-50 transition-colors\"\n data-testid=\"gitops-refresh\"\n >\n <RefreshCw className={`size-3 ${isRefreshing ? 'animate-spin' : ''}`} />\n {tr('gitops.refresh', 'Refresh')}\n </button>\n )}\n {!showSetup && (\n <button\n type=\"button\"\n onClick={() => setShowSetup(true)}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n data-testid=\"gitops-add-provider\"\n >\n {hasProviders\n ? tr('gitops.addAnotherProvider', 'Add Provider')\n : tr('gitops.addProvider', 'Add Provider')}\n </button>\n )}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-4 lg:flex-row lg:gap-6\">\n <nav\n className=\"w-full flex-shrink-0 rounded-lg border border-border-default bg-bg-surface p-2 lg:w-56\"\n role=\"tablist\"\n aria-label={tr('gitops.navigation', 'GitOps navigation')}\n >\n {subTabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = subTab === tab.id;\n return (\n <button\n type=\"button\"\n key={tab.id}\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setSubTab(tab.id)}\n className={`flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm font-medium transition-colors ${\n isActive\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-secondary hover:bg-action-ghost-bg-hover hover:text-text-primary'\n }`}\n >\n <Icon className=\"size-4 shrink-0\" />\n <span className=\"truncate\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n\n <div className=\"min-w-0 flex-1 space-y-4\">\n {showSetup && (\n <GitOpsSetup\n ctx={ctx}\n onCreated={() => {\n setShowSetup(false);\n void providersQ.refetch();\n }}\n onCancel={() => setShowSetup(false)}\n />\n )}\n\n {subTab === 'repo-stats' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={GitMerge}\n message=\"gitops.noProvidersVibe\"\n fallback=\"No GitOps provider configured. Add a PAT to see repository stats.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice\n tr={tr}\n message=\"gitops.noTunnel\"\n fallback=\"Waiting for agent tunnel. Make sure the agent is running and a tunnel is active.\"\n />\n ) : (\n <RepoStatsTab\n stats={repoStats}\n loading={repoStatsLoading || providersQ.isLoading}\n error={repoStatsError}\n />\n )}\n </>\n )}\n\n {subTab === 'ci' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={Play}\n message=\"gitops.noProvidersCi\"\n fallback=\"No GitOps provider configured. Add a PAT to see CI pipeline status.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice tr={tr} message=\"gitops.noTunnelCi\" fallback=\"Waiting for agent tunnel.\" />\n ) : (\n <CITab\n status={ciStatus}\n loading={ciStatusLoading || providersQ.isLoading}\n error={ciStatusError}\n />\n )}\n </>\n )}\n\n {subTab === 'git-ui' && (\n <PluginGate\n agentId={agentId || null}\n required={[GIT_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('gitops.ungitEyebrow', 'Ungit plugin required')}\n title={tr('gitops.ungitTitle', 'Install the visual git client')}\n description={tr(\n 'gitops.ungitDescription',\n 'The Git UI embeds the Ungit visual git client served by your agent. Install the plugin to continue.'\n )}\n >\n {ungitTokenError ? (\n <div className=\"rounded-lg border border-border-default bg-status-error-bg-subtle p-4\">\n <p className=\"text-sm text-status-error-text\">{ungitTokenError}</p>\n </div>\n ) : ungitIframeUrl ? (\n <iframe\n src={ungitIframeUrl}\n title=\"Ungit\"\n className=\"w-full min-h-[600px] rounded-lg border border-border-default\"\n />\n ) : (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr('gitops.ungitLoading', 'Loading Ungit…')}\n </p>\n </div>\n )}\n </PluginGate>\n )}\n </div>\n </div>\n </motion.div>\n );\n};\n\ninterface EmptyStateProps {\n tr: (key: string, fallback: string) => string;\n icon: typeof BarChart3;\n message: string;\n fallback: string;\n ctaLabel?: string;\n onCta?: () => void;\n}\n\nconst EmptyState: FC<EmptyStateProps> = ({\n tr,\n icon: Icon,\n message,\n fallback,\n ctaLabel,\n onCta,\n}) => (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-6 text-center\">\n <Icon className=\"size-8 text-text-secondary mx-auto mb-3\" />\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n {ctaLabel && onCta && (\n <button\n type=\"button\"\n onClick={onCta}\n className=\"mt-3 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {ctaLabel}\n </button>\n )}\n </div>\n);\n\ninterface NoticeProps {\n tr: (key: string, fallback: string) => string;\n message: string;\n fallback: string;\n}\n\nconst Notice: FC<NoticeProps> = ({ tr, message, fallback }) => (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n </div>\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"VibeGitOpsTab.js","names":[],"sources":["../../../src/components/gitops/VibeGitOpsTab.tsx"],"sourcesContent":["/**\n * VibeGitOpsTab — GitOps tab content for the Vibe detail page.\n *\n * V2: All data is sourced from the agent over the tunnel via\n * `/api/profiles/<profile>/gitops/<provider>/*`. No backend GraphQL\n * involvement. The backend's old `gitopsProvider` records are deprecated.\n *\n * Sub-tabs:\n * 1. Repo Stats — repo metadata + PR + issue + security counts\n * 2. CI Pipelines — recent runs + analytics percentiles\n * 3. Git UI — Ungit iframe (when vibe-plugin-tool-git is installed)\n */\nimport { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { motion } from 'framer-motion';\nimport { GitMerge, BarChart3, Play, GitBranch, RefreshCw } from 'lucide-react';\nimport { useQueryClient } from '@tanstack/react-query';\n\nimport { RepoStatsTab } from './RepoStatsTab';\nimport { CITab } from './CITab';\nimport { GitOpsSetup } from './GitOpsSetup';\nimport { GitOpsProviderPicker } from './GitOpsProviderPicker';\nimport { PluginGate } from '@/components/shared';\nimport { GIT_PACKAGE } from '@/constants/pluginCatalog';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\nimport {\n useGitopsContributors,\n useGitopsIssues,\n useGitopsLanguages,\n useGitopsPipelineAnalytics,\n useGitopsProviders,\n useGitopsPullRequests,\n useGitopsRepos,\n useGitopsRuns,\n useGitopsSecurityAlerts,\n} from '@/hooks/useGitops';\nimport { composeCiStatus, composeRepoStats, type ProviderName, type RepoFqn } from './types';\nimport {\n mintIframeToken,\n scheduleIframeTokenRefresh,\n appendIframeTokenQuery,\n type IframeTokenAutoRefreshHandle,\n type IframeTokenResponse,\n} from '@/services/iframeToken';\n\ninterface VibeGitOpsTabProps {\n vibeId: string;\n agentId: string;\n /** Canonical agent profile name. */\n profile: string;\n /**\n * Tunnel URL — retained ONLY for the `git-ui` iframe handshake\n * (Ungit reverse-proxied at `/git/`) which still bootstraps via a\n * one-time iframe token. GitOps REST calls no longer use this; they\n * route through the svc proxy (F4 Wave C).\n */\n tunnelUrl: string | null;\n tr: (key: string, fallback: string) => string;\n}\n\ntype GitOpsSubTab = 'repo-stats' | 'ci' | 'git-ui';\n\nexport const VibeGitOpsTab: FC<VibeGitOpsTabProps> = ({\n vibeId,\n agentId,\n profile,\n tunnelUrl,\n tr,\n}) => {\n const [subTab, setSubTab] = useState<GitOpsSubTab>('repo-stats');\n const [showSetup, setShowSetup] = useState(false);\n const [selectedProvider, setSelectedProvider] = useState<ProviderName | null>(null);\n const [selectedRepoFqn, setSelectedRepoFqn] = useState<RepoFqn | null>(null);\n const { workspaceId } = useVibeControls();\n const qc = useQueryClient();\n\n const ctx = useMemo(\n () => ({ profile, workspaceId: workspaceId ?? '', agentId }),\n [profile, workspaceId, agentId]\n );\n\n // Providers registered on the agent.\n const providersQ = useGitopsProviders(ctx);\n const authedProviders = useMemo(\n () => providersQ.data?.filter((p) => p.registered && p.authenticated) ?? [],\n [providersQ.data]\n );\n const hasProviders = authedProviders.length > 0;\n\n // Pick a default once data loads.\n useEffect(() => {\n if (!selectedProvider && authedProviders.length > 0) {\n const first = authedProviders[0];\n if (first) {\n setSelectedProvider(first.name as ProviderName);\n }\n }\n }, [authedProviders, selectedProvider]);\n\n // Once a provider is selected, fetch repos for it and pick the first one\n // by default. The user can switch repos via the picker.\n const reposQ = useGitopsRepos(ctx, selectedProvider, { limit: 30 });\n useEffect(() => {\n if (!selectedRepoFqn && reposQ.data?.items.length) {\n const first = reposQ.data.items[0];\n if (first) {\n setSelectedRepoFqn(first.fqn);\n }\n }\n }, [reposQ.data, selectedRepoFqn]);\n\n // Repo Stats composite — runs only when active sub-tab is repo-stats.\n const repoQ = useGitopsRepos(ctx, selectedProvider, { limit: 1 }); // metadata via list (1st item)\n const repoDetailQ = useGitopsRepos.length ? null : null; // keep TS happy; we use repoQ.items[0]\n const pullsQ = useGitopsPullRequests(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const issuesQ = useGitopsIssues(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const languagesQ = useGitopsLanguages(ctx, selectedProvider, selectedRepoFqn);\n const contributorsQ = useGitopsContributors(ctx, selectedProvider, selectedRepoFqn, 20);\n const securityQ = useGitopsSecurityAlerts(ctx, selectedProvider, selectedRepoFqn);\n\n const activeRepo = useMemo(\n () =>\n selectedRepoFqn\n ? (repoQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n reposQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n null)\n : (repoQ.data?.items[0] ?? null),\n [repoQ.data, reposQ.data, selectedRepoFqn]\n );\n\n const repoStats = useMemo(\n () =>\n composeRepoStats({\n repo: activeRepo,\n pulls: pullsQ.data,\n issues: issuesQ.data,\n languages: languagesQ.data,\n contributors: contributorsQ.data,\n securityAlerts: securityQ.data,\n }),\n [activeRepo, pullsQ.data, issuesQ.data, languagesQ.data, contributorsQ.data, securityQ.data]\n );\n\n // CI Status composite.\n const runsQ = useGitopsRuns(ctx, selectedProvider, selectedRepoFqn, { limit: 30 });\n const ciAnalyticsQ = useGitopsPipelineAnalytics(ctx, selectedProvider, selectedRepoFqn);\n const ciStatus = useMemo(\n () =>\n composeCiStatus({\n pipelines: undefined,\n runs: runsQ.data,\n analytics: ciAnalyticsQ.data,\n }),\n [runsQ.data, ciAnalyticsQ.data]\n );\n\n const repoStatsLoading = pullsQ.isLoading || issuesQ.isLoading || languagesQ.isLoading;\n const ciStatusLoading = runsQ.isLoading || ciAnalyticsQ.isLoading;\n const repoStatsError = (pullsQ.error || issuesQ.error || languagesQ.error)?.message;\n const ciStatusError = (runsQ.error || ciAnalyticsQ.error)?.message;\n\n const handleRefresh = useCallback(() => {\n void qc.invalidateQueries({ queryKey: ['gitops', workspaceId ?? '', agentId, profile] });\n }, [qc, workspaceId, agentId, profile]);\n\n const isRefreshing = pullsQ.isFetching || runsQ.isFetching;\n\n const subTabs: { id: GitOpsSubTab; label: string; icon: typeof BarChart3 }[] = [\n { id: 'repo-stats', label: tr('gitops.repoStats', 'Repo Stats'), icon: BarChart3 },\n { id: 'ci', label: tr('gitops.ci', 'CI Pipelines'), icon: Play },\n { id: 'git-ui', label: tr('gitops.gitUI', 'Git UI'), icon: GitBranch },\n ];\n\n // Ungit iframe state (unchanged from v1).\n const [ungitIframeUrl, setUngitIframeUrl] = useState<string | null>(null);\n const [ungitTokenError, setUngitTokenError] = useState<string | null>(null);\n useEffect(() => {\n let cancelled = false;\n let handle: IframeTokenAutoRefreshHandle | null = null;\n const setup = async () => {\n if (!agentId || !tunnelUrl || subTab !== 'git-ui') {\n setUngitIframeUrl(null);\n return;\n }\n try {\n // audit-B P0-SEC-03 residual closure — Ungit's reverse proxy\n // now lives at `/git/` (was `/ungit/`). `/git` joins\n // `/terminal`, `/ui`, `/plan`, `/code-server` as a first-class\n // iframe-token surface in the agent's `isValidIframePathPrefix`\n // allow-list, so the microfe can mint a scoped single-use\n // token and pass it once as `?vt=` for the entry document. The\n // plugin proxy verifies the ticket via `iframeBridge`, issues\n // its own `__vibe_ungit_session` cookie, and strips the token\n // from upstream forwards. Sub-requests use the cookie + Origin\n // CSRF guard. Mitigations: F6 single-use; F7 upstream strip;\n // F10 Origin on non-GET/HEAD.\n const initial: IframeTokenResponse = await mintIframeToken(agentId, profile, '/git');\n if (cancelled) return;\n const baseUrl = `${tunnelUrl.replace(/\\/$/, '')}/git/`;\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, initial.token));\n handle = scheduleIframeTokenRefresh(\n agentId,\n profile,\n '/git',\n initial,\n (next: IframeTokenResponse) => {\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, next.token));\n }\n );\n } catch (err) {\n if (!cancelled) {\n setUngitTokenError(err instanceof Error ? err.message : 'Failed to fetch iframe token');\n }\n }\n };\n void setup();\n return () => {\n cancelled = true;\n handle?.cancel();\n };\n }, [agentId, profile, tunnelUrl, subTab]);\n\n return (\n <motion.div\n key=\"gitops\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n data-vibe-id={vibeId}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <GitMerge className=\"size-5 text-text-secondary\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibeDetails.gitopsTitle', 'GitOps')}\n </h3>\n </div>\n <div className=\"flex items-center gap-2\">\n {hasProviders && (\n <GitOpsProviderPicker\n providers={authedProviders.map((p) => p.name as ProviderName)}\n selected={selectedProvider}\n onChange={setSelectedProvider}\n />\n )}\n {hasProviders && (subTab === 'repo-stats' || subTab === 'ci') && (\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={isRefreshing}\n className=\"flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-md border border-border-default text-text-secondary hover:text-text-primary hover:border-border-strong disabled:opacity-50 transition-colors\"\n data-testid=\"gitops-refresh\"\n >\n <RefreshCw className={`size-3 ${isRefreshing ? 'animate-spin' : ''}`} />\n {tr('gitops.refresh', 'Refresh')}\n </button>\n )}\n {!showSetup && (\n <button\n type=\"button\"\n onClick={() => setShowSetup(true)}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n data-testid=\"gitops-add-provider\"\n >\n {hasProviders\n ? tr('gitops.addAnotherProvider', 'Add Provider')\n : tr('gitops.addProvider', 'Add Provider')}\n </button>\n )}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-4 lg:flex-row lg:gap-6\">\n <nav\n className=\"w-full flex-shrink-0 rounded-lg border border-border-default bg-bg-surface p-2 lg:w-56\"\n role=\"tablist\"\n aria-label={tr('gitops.navigation', 'GitOps navigation')}\n >\n {subTabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = subTab === tab.id;\n return (\n <button\n type=\"button\"\n key={tab.id}\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setSubTab(tab.id)}\n className={`flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm font-medium transition-colors ${\n isActive\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-secondary hover:bg-action-ghost-bg-hover hover:text-text-primary'\n }`}\n >\n <Icon className=\"size-4 shrink-0\" />\n <span className=\"truncate\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n\n <div className=\"min-w-0 flex-1 space-y-4\">\n {showSetup && (\n <GitOpsSetup\n ctx={ctx}\n onCreated={() => {\n setShowSetup(false);\n void providersQ.refetch();\n }}\n onCancel={() => setShowSetup(false)}\n />\n )}\n\n {subTab === 'repo-stats' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={GitMerge}\n message=\"gitops.noProvidersVibe\"\n fallback=\"No GitOps provider configured. Add a PAT to see repository stats.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice\n tr={tr}\n message=\"gitops.noTunnel\"\n fallback=\"Waiting for agent tunnel. Make sure the agent is running and a tunnel is active.\"\n />\n ) : (\n <RepoStatsTab\n stats={repoStats}\n loading={repoStatsLoading || providersQ.isLoading}\n error={repoStatsError}\n />\n )}\n </>\n )}\n\n {subTab === 'ci' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={Play}\n message=\"gitops.noProvidersCi\"\n fallback=\"No GitOps provider configured. Add a PAT to see CI pipeline status.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice tr={tr} message=\"gitops.noTunnelCi\" fallback=\"Waiting for agent tunnel.\" />\n ) : (\n <CITab\n status={ciStatus}\n loading={ciStatusLoading || providersQ.isLoading}\n error={ciStatusError}\n />\n )}\n </>\n )}\n\n {subTab === 'git-ui' && (\n <PluginGate\n agentId={agentId || null}\n required={[GIT_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('gitops.ungitEyebrow', 'Ungit plugin required')}\n title={tr('gitops.ungitTitle', 'Install the visual git client')}\n description={tr(\n 'gitops.ungitDescription',\n 'The Git UI embeds the Ungit visual git client served by your agent. Install the plugin to continue.'\n )}\n >\n {ungitTokenError ? (\n <div className=\"rounded-lg border border-border-default bg-status-error-bg-subtle p-4\">\n <p className=\"text-sm text-status-error-text\">{ungitTokenError}</p>\n </div>\n ) : ungitIframeUrl ? (\n <iframe\n src={ungitIframeUrl}\n title=\"Ungit\"\n className=\"w-full min-h-[600px] rounded-lg border border-border-default\"\n />\n ) : (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr('gitops.ungitLoading', 'Loading Ungit…')}\n </p>\n </div>\n )}\n </PluginGate>\n )}\n </div>\n </div>\n </motion.div>\n );\n};\n\ninterface EmptyStateProps {\n tr: (key: string, fallback: string) => string;\n icon: typeof BarChart3;\n message: string;\n fallback: string;\n ctaLabel?: string;\n onCta?: () => void;\n}\n\nconst EmptyState: FC<EmptyStateProps> = ({\n tr,\n icon: Icon,\n message,\n fallback,\n ctaLabel,\n onCta,\n}) => (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-6 text-center\">\n <Icon className=\"size-8 text-text-secondary mx-auto mb-3\" />\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n {ctaLabel && onCta && (\n <button\n type=\"button\"\n onClick={onCta}\n className=\"mt-3 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {ctaLabel}\n </button>\n )}\n </div>\n);\n\ninterface NoticeProps {\n tr: (key: string, fallback: string) => string;\n message: string;\n fallback: string;\n}\n\nconst Notice: FC<NoticeProps> = ({ tr, message, fallback }) => (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n </div>\n);\n"],"mappings":"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"}
@@ -431,7 +431,7 @@ function g({ context: g, vibeId: _, agentVibes: v = [] }) {
431
431
  isOpen: Le,
432
432
  onClose: () => P(!1),
433
433
  onConfirm: () => {
434
- P(!1), ke("/billing");
434
+ P(!1), ke("/billing/plans");
435
435
  },
436
436
  title: "Note Quota Reached",
437
437
  message: "You've used all available note quota on your current plan. Upgrade your plan to create or update more notes."