@burdenoff/microfe-vibecontrols 2026.523.2 → 2026.524.1
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/pages/VibesPage.js +321 -319
- package/dist/pages/VibesPage.js.map +1 -1
- package/package.json +1 -1
package/dist/pages/VibesPage.js
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
import { useArchiveVibeMutation as e,
|
|
2
|
-
import { useVibeControls as
|
|
3
|
-
import { useVibeControlsStore as
|
|
4
|
-
import
|
|
5
|
-
import { PageHeader as
|
|
6
|
-
import { LoadingState as
|
|
7
|
-
import { ErrorState as
|
|
1
|
+
import { useArchiveVibeMutation as e, useAttachAgentToVibeMutation as t, useCreateVibeMutation as n, useListAgentsLazyQuery as r, useListVibesQuery as i, useUpdateVibeStatusMutation as ee } from "../generated/wspace-operations.js";
|
|
2
|
+
import { useVibeControls as te, useVibeNavigate as ne } from "../providers/VibeControlsProvider.js";
|
|
3
|
+
import { useVibeControlsStore as re } from "../store/vibeControlsStore.js";
|
|
4
|
+
import ie from "../hooks/useCachedInitialData.js";
|
|
5
|
+
import { PageHeader as ae } from "../components/shared/PageHeader.js";
|
|
6
|
+
import { LoadingState as oe } from "../components/shared/LoadingState.js";
|
|
7
|
+
import { ErrorState as se } from "../components/shared/ErrorState.js";
|
|
8
8
|
import { ConfirmDialog as a } from "../components/shared/ConfirmDialog.js";
|
|
9
|
-
import { QuotaExhaustedDialog as
|
|
10
|
-
import { isQuotaExhaustedError as
|
|
11
|
-
import { EntityTagPicker as
|
|
12
|
-
import { useDraftEntityTags as
|
|
13
|
-
import { EmptyState as
|
|
14
|
-
import { SearchInput as
|
|
15
|
-
import { FilterDropdown as
|
|
16
|
-
import { GetHelpButton as
|
|
17
|
-
import { SelectionCheckbox as
|
|
9
|
+
import { QuotaExhaustedDialog as ce } from "../components/shared/QuotaExhaustedDialog.js";
|
|
10
|
+
import { isQuotaExhaustedError as le } from "../utils/quotaUtils.js";
|
|
11
|
+
import { EntityTagPicker as ue } from "../components/shared/EntityTagPicker.js";
|
|
12
|
+
import { useDraftEntityTags as de } from "../hooks/useDraftEntityTags.js";
|
|
13
|
+
import { EmptyState as fe } from "../components/shared/EmptyState.js";
|
|
14
|
+
import { SearchInput as pe } from "../components/shared/SearchInput.js";
|
|
15
|
+
import { FilterDropdown as me } from "../components/shared/FilterDropdown.js";
|
|
16
|
+
import { GetHelpButton as he } from "../components/shared/GetHelpButton.js";
|
|
17
|
+
import { SelectionCheckbox as ge } from "../components/shared/SelectionCheckbox.js";
|
|
18
18
|
import "../components/shared/index.js";
|
|
19
|
-
import { VIBE_PERMISSIONS as
|
|
20
|
-
import { PageSkeleton as
|
|
21
|
-
import { buildProtocolUrl as
|
|
22
|
-
import { VibeCard as
|
|
23
|
-
import { VibeList as
|
|
19
|
+
import { VIBE_PERMISSIONS as o } from "../constants/permissions.js";
|
|
20
|
+
import { PageSkeleton as _e } from "../components/shared/PageSkeleton.js";
|
|
21
|
+
import { buildProtocolUrl as ve, getIdePreset as ye, resolveEffectiveIdeCommand as be } from "../components/agents/idePresets.js";
|
|
22
|
+
import { VibeCard as xe } from "../components/vibes/VibeCard.js";
|
|
23
|
+
import { VibeList as Se } from "../components/vibes/VibeList.js";
|
|
24
24
|
import "../components/vibes/index.js";
|
|
25
|
-
import { useCallback as
|
|
26
|
-
import { useI18n as
|
|
27
|
-
import { Archive as
|
|
28
|
-
import { usePermissions as
|
|
29
|
-
import { AnimatePresence as
|
|
30
|
-
import { Fragment as
|
|
31
|
-
import { MotionItem as
|
|
32
|
-
import { AccessDenied as
|
|
25
|
+
import { useCallback as s, useEffect as Ce, useMemo as c, useRef as l, useState as u } from "react";
|
|
26
|
+
import { useI18n as we } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
27
|
+
import { Archive as Te, Grid as Ee, List as De, Play as Oe, Plus as ke, RefreshCw as Ae, Zap as je } from "lucide-react";
|
|
28
|
+
import { usePermissions as Me } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
29
|
+
import { AnimatePresence as Ne } from "framer-motion";
|
|
30
|
+
import { Fragment as Pe, jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
31
|
+
import { MotionItem as Fe, MotionList as p, MotionPage as Ie } from "@burdenoff/fe-libs/shared/motion";
|
|
32
|
+
import { AccessDenied as Le, PermissionButton as Re, PermissionGate as ze } from "@burdenoff/fe-libs/shared/components";
|
|
33
33
|
//#region src/pages/VibesPage.tsx
|
|
34
|
-
function
|
|
35
|
-
let { onVibeActivate:
|
|
36
|
-
let r =
|
|
34
|
+
function m() {
|
|
35
|
+
let { onVibeActivate: m } = te(), h = ne(), { hasPermission: g, isLoading: _, permissions: v } = Me(), { t: y } = we(), b = s((e, t, n) => {
|
|
36
|
+
let r = y(e, n), i = r === e ? t : r;
|
|
37
37
|
return n ? Object.entries(n).reduce((e, [t, n]) => e.replace(`{{${t}}}`, String(n)), i) : i;
|
|
38
|
-
}, [
|
|
38
|
+
}, [y]), Be = [
|
|
39
39
|
{
|
|
40
40
|
value: "all",
|
|
41
|
-
label:
|
|
41
|
+
label: b("vibesPage.statusAll", "All Statuses")
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
value: "ACTIVE",
|
|
45
|
-
label:
|
|
45
|
+
label: b("vibesPage.statusActive", "Active")
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
value: "INACTIVE",
|
|
49
|
-
label:
|
|
49
|
+
label: b("vibesPage.statusInactive", "Inactive")
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
value: "ARCHIVED",
|
|
53
|
-
label:
|
|
53
|
+
label: b("vibesPage.statusArchived", "Archived")
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
value: "ERROR",
|
|
57
|
-
label:
|
|
57
|
+
label: b("vibesPage.statusError", "Error")
|
|
58
58
|
}
|
|
59
59
|
];
|
|
60
|
-
|
|
61
|
-
let
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
b("vibesPage.typeAll", "All Types"), b("vibesPage.typeProject", "Project"), b("vibesPage.typeRepository", "Repository"), b("vibesPage.typeWorkspace", "Workspace"), b("vibesPage.typeMonorepo", "Monorepo"), b("vibesPage.typePackage", "Package"), b("vibesPage.typeCustom", "Custom");
|
|
61
|
+
let x = g(o.VIBE_LIST), S = g(o.VIBE_UPDATE), C = _ || !x, [w, Ve] = u(""), [T, He] = u("all"), [E, Ue] = u("all"), [D, We] = u("all"), [O, k] = u("grid"), [Ge, A] = u(!1), [Ke, j] = u(null), [M, qe] = u(!1), [Je, N] = u(null), [Ye, P] = u(!1), [F, I] = u({}), [Xe, L] = u(null), [R, z] = u(/* @__PURE__ */ new Set()), [Ze, B] = u(null), V = l(null);
|
|
62
|
+
Ce(() => () => {
|
|
63
|
+
V.current && clearTimeout(V.current);
|
|
64
64
|
}, []);
|
|
65
|
-
let
|
|
66
|
-
|
|
67
|
-
}, []),
|
|
68
|
-
variables:
|
|
69
|
-
status:
|
|
70
|
-
type:
|
|
71
|
-
attachedAgentIds:
|
|
65
|
+
let Qe = s(() => {
|
|
66
|
+
L(null), V.current &&= (clearTimeout(V.current), null);
|
|
67
|
+
}, []), $e = l(null), et = l(null), tt = l(null), nt = l(null), [rt] = n(), [it] = t(), { setDraftTagIds: at, flushTags: ot } = de("VIBECONTROLS_VIBE"), [st] = ee(), [ct] = e(), { data: lt, loading: H, error: ut, refetch: U } = i({
|
|
68
|
+
variables: c(() => ({ filter: {
|
|
69
|
+
status: T === "all" ? void 0 : T,
|
|
70
|
+
type: E === "all" ? void 0 : E,
|
|
71
|
+
attachedAgentIds: D === "all" ? void 0 : [D]
|
|
72
72
|
} }), [
|
|
73
|
+
T,
|
|
73
74
|
E,
|
|
74
|
-
D
|
|
75
|
-
O
|
|
75
|
+
D
|
|
76
76
|
]),
|
|
77
|
-
fetchPolicy:
|
|
77
|
+
fetchPolicy: T === "all" && E === "all" && D === "all" ? "cache-first" : "cache-and-network",
|
|
78
78
|
nextFetchPolicy: "cache-first",
|
|
79
|
-
skip:
|
|
79
|
+
skip: C,
|
|
80
80
|
notifyOnNetworkStatusChange: !0
|
|
81
|
-
}), { agents:
|
|
82
|
-
|
|
83
|
-
}, [
|
|
84
|
-
let e =
|
|
81
|
+
}), { agents: dt } = ie(), [ft, { data: pt }] = r({ fetchPolicy: "cache-first" }), mt = l(!1), W = s(() => {
|
|
82
|
+
mt.current || C || (mt.current = !0, ft({ variables: { pagination: { limit: 100 } } }));
|
|
83
|
+
}, [ft, C]), ht = c(() => {
|
|
84
|
+
let e = pt?.vibecontrolsAgents ?? null, t = e ? e.map((e) => ({
|
|
85
85
|
id: e.id,
|
|
86
86
|
name: e.name
|
|
87
|
-
})) :
|
|
87
|
+
})) : dt.map((e) => ({
|
|
88
88
|
id: e.id,
|
|
89
89
|
name: e.name
|
|
90
90
|
}));
|
|
91
91
|
return [{
|
|
92
92
|
value: "all",
|
|
93
|
-
label:
|
|
93
|
+
label: b("vibesPage.allAgents", "All Agents")
|
|
94
94
|
}, ...t.map((e) => ({
|
|
95
95
|
value: e.id,
|
|
96
96
|
label: e.name
|
|
97
97
|
}))];
|
|
98
98
|
}, [
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
]),
|
|
99
|
+
pt?.vibecontrolsAgents,
|
|
100
|
+
dt,
|
|
101
|
+
b
|
|
102
|
+
]), G = c(() => (lt?.vibes?.items ?? []).map((e) => ({
|
|
103
103
|
id: e.id,
|
|
104
104
|
workspaceId: e.workspaceId,
|
|
105
105
|
name: e.name,
|
|
@@ -122,20 +122,20 @@ function h() {
|
|
|
122
122
|
isActive: e.isActive,
|
|
123
123
|
ownerUserId: e.ownerUserId ?? null
|
|
124
124
|
}))
|
|
125
|
-
})), [
|
|
126
|
-
if (
|
|
127
|
-
let t =
|
|
125
|
+
})), [lt?.vibes?.items]), K = c(() => G.filter((e) => {
|
|
126
|
+
if (w) {
|
|
127
|
+
let t = w.toLowerCase();
|
|
128
128
|
if (!(e.name.toLowerCase().includes(t) || e.description?.toLowerCase().includes(t) || e.path?.toLowerCase().includes(t) || e.tags?.some((e) => e.toLowerCase().includes(t)))) return !1;
|
|
129
129
|
}
|
|
130
130
|
return !0;
|
|
131
|
-
}), [
|
|
132
|
-
|
|
133
|
-
},
|
|
131
|
+
}), [G, w]), gt = c(() => G.filter((e) => R.has(e.id)), [G, R]), q = gt.filter((e) => e.status !== "ACTIVE"), J = gt.filter((e) => e.status !== "ARCHIVED"), Y = K.length > 0 && K.every((e) => R.has(e.id)), _t = K.some((e) => R.has(e.id)) && !Y, vt = (e) => {
|
|
132
|
+
h(`/vibes/${e.id}`);
|
|
133
|
+
}, yt = async (e) => {
|
|
134
134
|
try {
|
|
135
|
-
(await
|
|
135
|
+
(await st({ variables: {
|
|
136
136
|
id: e.id,
|
|
137
137
|
status: "ACTIVE"
|
|
138
|
-
} })).data?.updateVibeStatus &&
|
|
138
|
+
} })).data?.updateVibeStatus && m?.({
|
|
139
139
|
id: e.id,
|
|
140
140
|
name: e.name,
|
|
141
141
|
type: e.type,
|
|
@@ -144,55 +144,55 @@ function h() {
|
|
|
144
144
|
} catch (e) {
|
|
145
145
|
console.error("Failed to activate vibe:", e);
|
|
146
146
|
}
|
|
147
|
-
},
|
|
147
|
+
}, bt = async (e) => {
|
|
148
148
|
try {
|
|
149
|
-
await
|
|
149
|
+
await ct({ variables: { id: e.id } });
|
|
150
150
|
} catch (e) {
|
|
151
151
|
console.error("Failed to archive vibe:", e);
|
|
152
152
|
}
|
|
153
|
-
},
|
|
153
|
+
}, xt = re((e) => e.preferredIdeCommand), St = re((e) => e.vibeIdeOverrides), X = (e) => {
|
|
154
154
|
if (!e.path) {
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
L(b("vibesPage.openEditorPathMissing", "No path available to open in editor")), V.current = setTimeout(() => {
|
|
156
|
+
L(null), V.current = null;
|
|
157
157
|
}, 3e3);
|
|
158
158
|
return;
|
|
159
159
|
}
|
|
160
|
-
let t =
|
|
161
|
-
r ? window.location.href = r : (
|
|
162
|
-
|
|
160
|
+
let t = be(e.id, xt, St), n = ye(t), r = n ? ve(n, e.path) : `vscode://file/${e.path}`;
|
|
161
|
+
r ? window.location.href = r : (L(b("vibesPage.openEditorNoProtocol", `${n?.label ?? t} has no URL handler — open this Vibe and use the editor menu.`)), V.current = setTimeout(() => {
|
|
162
|
+
L(null), V.current = null;
|
|
163
163
|
}, 4e3));
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
},
|
|
167
|
-
|
|
164
|
+
}, Ct = () => {
|
|
165
|
+
j(null), A(!0), W();
|
|
166
|
+
}, wt = (e, t) => {
|
|
167
|
+
z((n) => {
|
|
168
168
|
let r = new Set(n);
|
|
169
169
|
return t ? r.add(e.id) : r.delete(e.id), r;
|
|
170
170
|
});
|
|
171
|
-
},
|
|
172
|
-
|
|
171
|
+
}, Tt = (e) => {
|
|
172
|
+
z((t) => {
|
|
173
173
|
let n = new Set(t);
|
|
174
|
-
return
|
|
174
|
+
return K.forEach((t) => {
|
|
175
175
|
e ? n.add(t.id) : n.delete(t.id);
|
|
176
176
|
}), n;
|
|
177
177
|
});
|
|
178
|
-
}, Z = () =>
|
|
179
|
-
if (
|
|
180
|
-
let e = await Promise.allSettled(
|
|
178
|
+
}, Z = () => z(/* @__PURE__ */ new Set()), Et = async () => {
|
|
179
|
+
if (q.length === 0) return;
|
|
180
|
+
let e = await Promise.allSettled(q.map((e) => st({ variables: {
|
|
181
181
|
id: e.id,
|
|
182
182
|
status: "ACTIVE"
|
|
183
183
|
} }))), t = e.filter((e) => e.status === "fulfilled").length, n = e.length - t;
|
|
184
|
-
|
|
185
|
-
},
|
|
186
|
-
if (
|
|
187
|
-
let e = await Promise.allSettled(
|
|
188
|
-
|
|
189
|
-
},
|
|
190
|
-
let e =
|
|
191
|
-
if (e || (i.name =
|
|
192
|
-
|
|
184
|
+
N(`Activated ${t} vibe${t === 1 ? "" : "s"}${n > 0 ? ` • ${n} failed` : ""}`), setTimeout(() => N(null), 4e3), Z(), B(null), await U();
|
|
185
|
+
}, Dt = async () => {
|
|
186
|
+
if (J.length === 0) return;
|
|
187
|
+
let e = await Promise.allSettled(J.map((e) => ct({ variables: { id: e.id } }))), t = e.filter((e) => e.status === "fulfilled").length, n = e.length - t;
|
|
188
|
+
N(`Archived ${t} vibe${t === 1 ? "" : "s"}${n > 0 ? ` • ${n} failed` : ""}`), setTimeout(() => N(null), 4e3), Z(), B(null), await U();
|
|
189
|
+
}, Ot = async () => {
|
|
190
|
+
let e = $e.current?.value?.trim(), t = et.current?.value?.trim(), n = tt.current?.value, r = nt.current?.value, i = {};
|
|
191
|
+
if (e || (i.name = b("vibesPage.nameRequired", "Name is required")), t || (i.path = b("vibesPage.pathRequired", "Path is required")), i.name || i.path) {
|
|
192
|
+
I(i), j(b("vibesPage.fillRequired", "Please fill in all required fields"));
|
|
193
193
|
return;
|
|
194
194
|
}
|
|
195
|
-
|
|
195
|
+
I({});
|
|
196
196
|
let ee = [
|
|
197
197
|
"PROJECT",
|
|
198
198
|
"REPOSITORY",
|
|
@@ -201,176 +201,178 @@ function h() {
|
|
|
201
201
|
"PACKAGE",
|
|
202
202
|
"CUSTOM"
|
|
203
203
|
].includes(r) ? r : "PROJECT";
|
|
204
|
-
|
|
204
|
+
qe(!0), j(null);
|
|
205
205
|
try {
|
|
206
|
-
let r = await
|
|
206
|
+
let r = await rt({ variables: { input: {
|
|
207
207
|
name: e,
|
|
208
208
|
path: t,
|
|
209
|
-
type: ee
|
|
210
|
-
agentId: n || void 0
|
|
209
|
+
type: ee
|
|
211
210
|
} } });
|
|
212
|
-
if (!r.data?.createVibe?.id) throw Error(
|
|
211
|
+
if (!r.data?.createVibe?.id) throw Error(b("vibesPage.failedCreate", "Failed to create vibe"));
|
|
213
212
|
let i = r.data.createVibe.id;
|
|
214
|
-
|
|
213
|
+
n && await it({ variables: { input: {
|
|
214
|
+
vibeId: i,
|
|
215
|
+
agentId: n
|
|
216
|
+
} } }), A(!1), N(b("vibesPage.createdSuccess", "Vibe created successfully")), await ot(i), await U(), setTimeout(() => N(null), 3e3);
|
|
215
217
|
} catch (e) {
|
|
216
|
-
if (
|
|
217
|
-
A(!1),
|
|
218
|
+
if (le(e)) {
|
|
219
|
+
A(!1), j(null), I({}), P(!0);
|
|
218
220
|
return;
|
|
219
221
|
}
|
|
220
|
-
|
|
222
|
+
j(e instanceof Error ? e.message : b("vibesPage.failedCreate", "Failed to create vibe"));
|
|
221
223
|
} finally {
|
|
222
|
-
|
|
224
|
+
qe(!1);
|
|
223
225
|
}
|
|
224
|
-
},
|
|
225
|
-
total:
|
|
226
|
-
active:
|
|
227
|
-
inactive:
|
|
228
|
-
}, Q =
|
|
229
|
-
return /* @__PURE__ */
|
|
226
|
+
}, kt = {
|
|
227
|
+
total: G.length,
|
|
228
|
+
active: G.filter((e) => e.status === "ACTIVE").length,
|
|
229
|
+
inactive: G.filter((e) => e.status === "INACTIVE").length
|
|
230
|
+
}, Q = _, $ = !_ && !!v && (v.permissionStrings?.length ?? 0) > 0 && !x, At = !Q && !$ && H && G.length === 0, jt = !Q && !$ && !!ut && G.length === 0, Mt = !Q && !$ && !jt;
|
|
231
|
+
return /* @__PURE__ */ f(Ie, {
|
|
230
232
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
231
233
|
children: [
|
|
232
|
-
Q && /* @__PURE__ */
|
|
233
|
-
$ && /* @__PURE__ */
|
|
234
|
-
title:
|
|
235
|
-
description:
|
|
234
|
+
Q && /* @__PURE__ */ d(oe, { message: b("vibesPage.loadingPermissions", "Loading permissions...") }),
|
|
235
|
+
$ && /* @__PURE__ */ d(Le, {
|
|
236
|
+
title: b("vibesPage.accessDeniedTitle", "Access Denied"),
|
|
237
|
+
description: b("vibesPage.accessDeniedDescription", "You don't have permission to view vibes. Contact your workspace administrator for access.")
|
|
236
238
|
}),
|
|
237
|
-
|
|
238
|
-
title:
|
|
239
|
-
message:
|
|
240
|
-
onRetry: () =>
|
|
239
|
+
jt && /* @__PURE__ */ d(se, {
|
|
240
|
+
title: b("vibesPage.failedLoad", "Failed to load vibes"),
|
|
241
|
+
message: ut?.message ?? "",
|
|
242
|
+
onRetry: () => U()
|
|
241
243
|
}),
|
|
242
|
-
|
|
243
|
-
/* @__PURE__ */
|
|
244
|
-
title:
|
|
245
|
-
description:
|
|
246
|
-
active:
|
|
247
|
-
total:
|
|
244
|
+
Mt && /* @__PURE__ */ f(Pe, { children: [
|
|
245
|
+
/* @__PURE__ */ d(ae, {
|
|
246
|
+
title: b("vibesPage.title", "Vibes"),
|
|
247
|
+
description: b("vibesPage.descriptionActive", "{{active}} of {{total}} vibes active", {
|
|
248
|
+
active: kt.active,
|
|
249
|
+
total: kt.total
|
|
248
250
|
}),
|
|
249
|
-
actions: /* @__PURE__ */
|
|
251
|
+
actions: /* @__PURE__ */ f("div", {
|
|
250
252
|
className: "flex items-center gap-2",
|
|
251
|
-
children: [/* @__PURE__ */
|
|
252
|
-
onClick: () =>
|
|
253
|
-
disabled:
|
|
253
|
+
children: [/* @__PURE__ */ d("button", {
|
|
254
|
+
onClick: () => U(),
|
|
255
|
+
disabled: H,
|
|
254
256
|
className: "flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50",
|
|
255
|
-
title:
|
|
256
|
-
children: /* @__PURE__ */
|
|
257
|
-
}), /* @__PURE__ */
|
|
258
|
-
permission:
|
|
257
|
+
title: b("vibesPage.refreshTitle", "Refresh vibes"),
|
|
258
|
+
children: /* @__PURE__ */ d(Ae, { className: `w-4 h-4 ${H ? "animate-spin" : ""}` })
|
|
259
|
+
}), /* @__PURE__ */ d(Re, {
|
|
260
|
+
permission: o.VIBE_CREATE,
|
|
259
261
|
deniedBehavior: "disable",
|
|
260
|
-
deniedTooltip:
|
|
261
|
-
children: /* @__PURE__ */
|
|
262
|
-
onClick:
|
|
262
|
+
deniedTooltip: b("vibesPage.deniedCreateTooltip", "You don't have permission to create vibes"),
|
|
263
|
+
children: /* @__PURE__ */ f("button", {
|
|
264
|
+
onClick: Ct,
|
|
263
265
|
className: "flex items-center gap-2 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 disabled:cursor-not-allowed",
|
|
264
|
-
children: [/* @__PURE__ */
|
|
266
|
+
children: [/* @__PURE__ */ d(ke, { className: "w-4 h-4" }), b("vibesPage.addVibe", "Add Vibe")]
|
|
265
267
|
})
|
|
266
268
|
})]
|
|
267
269
|
})
|
|
268
270
|
}),
|
|
269
|
-
/* @__PURE__ */
|
|
271
|
+
/* @__PURE__ */ f("div", {
|
|
270
272
|
className: "space-y-3",
|
|
271
|
-
children: [/* @__PURE__ */
|
|
273
|
+
children: [/* @__PURE__ */ f("div", {
|
|
272
274
|
className: "flex items-center gap-2",
|
|
273
|
-
children: [/* @__PURE__ */
|
|
274
|
-
value:
|
|
275
|
-
onChange:
|
|
276
|
-
placeholder:
|
|
275
|
+
children: [/* @__PURE__ */ d(pe, {
|
|
276
|
+
value: w,
|
|
277
|
+
onChange: Ve,
|
|
278
|
+
placeholder: b("vibesPage.searchPlaceholder", "Search vibes..."),
|
|
277
279
|
className: "flex-1 sm:w-64 sm:flex-none"
|
|
278
|
-
}), /* @__PURE__ */
|
|
280
|
+
}), /* @__PURE__ */ f("div", {
|
|
279
281
|
className: "flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0",
|
|
280
|
-
children: [/* @__PURE__ */
|
|
281
|
-
onClick: () =>
|
|
282
|
-
className: `p-1.5 sm:p-2 rounded ${
|
|
283
|
-
title:
|
|
284
|
-
children: /* @__PURE__ */
|
|
285
|
-
}), /* @__PURE__ */
|
|
286
|
-
onClick: () =>
|
|
287
|
-
className: `p-1.5 sm:p-2 rounded ${
|
|
288
|
-
title:
|
|
289
|
-
children: /* @__PURE__ */
|
|
282
|
+
children: [/* @__PURE__ */ d("button", {
|
|
283
|
+
onClick: () => k("grid"),
|
|
284
|
+
className: `p-1.5 sm:p-2 rounded ${O === "grid" ? "bg-bg-surface shadow-sm" : "hover:bg-bg-surface/50"}`,
|
|
285
|
+
title: b("vibesPage.gridViewTitle", "Grid view"),
|
|
286
|
+
children: /* @__PURE__ */ d(Ee, { className: "w-4 h-4 text-text-secondary" })
|
|
287
|
+
}), /* @__PURE__ */ d("button", {
|
|
288
|
+
onClick: () => k("list"),
|
|
289
|
+
className: `p-1.5 sm:p-2 rounded ${O === "list" ? "bg-bg-surface shadow-sm" : "hover:bg-bg-surface/50"}`,
|
|
290
|
+
title: b("vibesPage.listViewTitle", "List view"),
|
|
291
|
+
children: /* @__PURE__ */ d(De, { className: "w-4 h-4 text-text-secondary" })
|
|
290
292
|
})]
|
|
291
293
|
})]
|
|
292
|
-
}), /* @__PURE__ */
|
|
294
|
+
}), /* @__PURE__ */ f("div", {
|
|
293
295
|
className: "flex items-center gap-2 sm:gap-3 flex-wrap",
|
|
294
|
-
children: [/* @__PURE__ */
|
|
295
|
-
label:
|
|
296
|
-
value:
|
|
297
|
-
options:
|
|
298
|
-
onChange:
|
|
299
|
-
}), /* @__PURE__ */
|
|
300
|
-
onMouseDown:
|
|
301
|
-
onFocus:
|
|
302
|
-
children: /* @__PURE__ */
|
|
303
|
-
label:
|
|
304
|
-
value:
|
|
305
|
-
options:
|
|
306
|
-
onChange:
|
|
296
|
+
children: [/* @__PURE__ */ d(me, {
|
|
297
|
+
label: b("vibesPage.filterStatus", "Status"),
|
|
298
|
+
value: T,
|
|
299
|
+
options: Be,
|
|
300
|
+
onChange: He
|
|
301
|
+
}), /* @__PURE__ */ d("div", {
|
|
302
|
+
onMouseDown: W,
|
|
303
|
+
onFocus: W,
|
|
304
|
+
children: /* @__PURE__ */ d(me, {
|
|
305
|
+
label: b("vibesPage.filterAgent", "Agent"),
|
|
306
|
+
value: D,
|
|
307
|
+
options: ht,
|
|
308
|
+
onChange: We
|
|
307
309
|
})
|
|
308
310
|
})]
|
|
309
311
|
})]
|
|
310
312
|
}),
|
|
311
|
-
|
|
313
|
+
w || T !== "all" || E !== "all" || D !== "all" ? /* @__PURE__ */ f("p", {
|
|
312
314
|
className: "text-sm text-text-secondary",
|
|
313
|
-
children: [
|
|
314
|
-
filtered:
|
|
315
|
-
total:
|
|
316
|
-
}),
|
|
315
|
+
children: [b("vibesPage.showingResults", "Showing {{filtered}} of {{total}} vibes", {
|
|
316
|
+
filtered: K.length,
|
|
317
|
+
total: G.length
|
|
318
|
+
}), H && /* @__PURE__ */ d("span", {
|
|
317
319
|
className: "ml-2 text-text-muted",
|
|
318
|
-
children:
|
|
320
|
+
children: b("vibesPage.refreshing", "(refreshing...)")
|
|
319
321
|
})]
|
|
320
322
|
}) : null,
|
|
321
|
-
|
|
323
|
+
K.length > 0 && /* @__PURE__ */ d("div", {
|
|
322
324
|
className: "rounded-lg border border-border-default bg-bg-surface p-4",
|
|
323
|
-
children: /* @__PURE__ */
|
|
325
|
+
children: /* @__PURE__ */ f("div", {
|
|
324
326
|
className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between",
|
|
325
|
-
children: [/* @__PURE__ */
|
|
327
|
+
children: [/* @__PURE__ */ f("div", {
|
|
326
328
|
className: "flex flex-wrap items-center gap-3",
|
|
327
|
-
children: [/* @__PURE__ */
|
|
329
|
+
children: [/* @__PURE__ */ f("div", {
|
|
328
330
|
className: "flex items-center gap-2",
|
|
329
|
-
children: [/* @__PURE__ */
|
|
330
|
-
checked:
|
|
331
|
-
indeterminate:
|
|
332
|
-
onChange:
|
|
331
|
+
children: [/* @__PURE__ */ d(ge, {
|
|
332
|
+
checked: Y,
|
|
333
|
+
indeterminate: _t,
|
|
334
|
+
onChange: Tt,
|
|
333
335
|
ariaLabel: "Select all visible vibes"
|
|
334
|
-
}), /* @__PURE__ */
|
|
336
|
+
}), /* @__PURE__ */ d("span", {
|
|
335
337
|
className: "text-sm font-medium text-text-primary",
|
|
336
338
|
children: "Select all visible"
|
|
337
339
|
})]
|
|
338
|
-
}),
|
|
340
|
+
}), R.size > 0 && /* @__PURE__ */ d("button", {
|
|
339
341
|
onClick: Z,
|
|
340
342
|
className: "text-sm text-text-secondary hover:text-text-primary",
|
|
341
343
|
children: "Clear selection"
|
|
342
344
|
})]
|
|
343
|
-
}), /* @__PURE__ */
|
|
345
|
+
}), /* @__PURE__ */ f("div", {
|
|
344
346
|
className: "flex flex-wrap items-center gap-2",
|
|
345
347
|
children: [
|
|
346
|
-
/* @__PURE__ */
|
|
348
|
+
/* @__PURE__ */ f("span", {
|
|
347
349
|
className: "rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary",
|
|
348
|
-
children: [
|
|
350
|
+
children: [R.size, " selected"]
|
|
349
351
|
}),
|
|
350
|
-
/* @__PURE__ */
|
|
351
|
-
onClick: () =>
|
|
352
|
-
disabled:
|
|
352
|
+
/* @__PURE__ */ d("button", {
|
|
353
|
+
onClick: () => B("activate"),
|
|
354
|
+
disabled: q.length === 0,
|
|
353
355
|
title: "Mark selected vibes as ACTIVE so they appear in the default vibe list and can run sessions/tunnels via their attached agent(s).",
|
|
354
356
|
className: "rounded-md border border-status-success-border px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg disabled:cursor-not-allowed disabled:opacity-50",
|
|
355
|
-
children: /* @__PURE__ */
|
|
357
|
+
children: /* @__PURE__ */ f("span", {
|
|
356
358
|
className: "inline-flex items-center gap-2",
|
|
357
359
|
children: [
|
|
358
|
-
/* @__PURE__ */
|
|
360
|
+
/* @__PURE__ */ d(Oe, { className: "w-4 h-4" }),
|
|
359
361
|
"Activate Selected",
|
|
360
|
-
|
|
362
|
+
q.length > 0 ? ` (${q.length})` : ""
|
|
361
363
|
]
|
|
362
364
|
})
|
|
363
365
|
}),
|
|
364
|
-
/* @__PURE__ */
|
|
365
|
-
onClick: () =>
|
|
366
|
-
disabled:
|
|
366
|
+
/* @__PURE__ */ d("button", {
|
|
367
|
+
onClick: () => B("archive"),
|
|
368
|
+
disabled: J.length === 0,
|
|
367
369
|
className: "rounded-md border border-status-warning-border px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg disabled:cursor-not-allowed disabled:opacity-50",
|
|
368
|
-
children: /* @__PURE__ */
|
|
370
|
+
children: /* @__PURE__ */ f("span", {
|
|
369
371
|
className: "inline-flex items-center gap-2",
|
|
370
372
|
children: [
|
|
371
|
-
/* @__PURE__ */
|
|
373
|
+
/* @__PURE__ */ d(Te, { className: "w-4 h-4" }),
|
|
372
374
|
"Archive Selected",
|
|
373
|
-
|
|
375
|
+
J.length > 0 ? ` (${J.length})` : ""
|
|
374
376
|
]
|
|
375
377
|
})
|
|
376
378
|
})
|
|
@@ -378,219 +380,219 @@ function h() {
|
|
|
378
380
|
})]
|
|
379
381
|
})
|
|
380
382
|
}),
|
|
381
|
-
|
|
383
|
+
At ? /* @__PURE__ */ d(_e, {
|
|
382
384
|
variant: "cards",
|
|
383
385
|
count: 6
|
|
384
|
-
}) :
|
|
386
|
+
}) : K.length > 0 ? /* @__PURE__ */ d(Ne, {
|
|
385
387
|
mode: "wait",
|
|
386
|
-
children:
|
|
388
|
+
children: O === "grid" ? /* @__PURE__ */ d(p, {
|
|
387
389
|
stagger: .04,
|
|
388
390
|
className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4",
|
|
389
|
-
children:
|
|
391
|
+
children: K.map((e) => /* @__PURE__ */ d(Fe, { children: /* @__PURE__ */ d(xe, {
|
|
390
392
|
vibe: e,
|
|
391
|
-
onOpen:
|
|
392
|
-
onActivate:
|
|
393
|
-
onArchive:
|
|
394
|
-
onOpenInEditor:
|
|
395
|
-
selected:
|
|
396
|
-
onToggleSelect: (t) =>
|
|
393
|
+
onOpen: vt,
|
|
394
|
+
onActivate: S ? yt : void 0,
|
|
395
|
+
onArchive: S ? bt : void 0,
|
|
396
|
+
onOpenInEditor: X,
|
|
397
|
+
selected: R.has(e.id),
|
|
398
|
+
onToggleSelect: (t) => wt(e, t)
|
|
397
399
|
}) }, e.id))
|
|
398
|
-
}, "grid") : /* @__PURE__ */
|
|
399
|
-
vibes:
|
|
400
|
-
onOpen:
|
|
401
|
-
onActivate:
|
|
402
|
-
onArchive:
|
|
403
|
-
onOpenInEditor:
|
|
404
|
-
selectedVibeIds:
|
|
405
|
-
onToggleSelect:
|
|
406
|
-
onToggleAllVisible:
|
|
407
|
-
allVisibleSelected:
|
|
408
|
-
partiallySelected:
|
|
400
|
+
}, "grid") : /* @__PURE__ */ d(p, { children: /* @__PURE__ */ d(Se, {
|
|
401
|
+
vibes: K,
|
|
402
|
+
onOpen: vt,
|
|
403
|
+
onActivate: yt,
|
|
404
|
+
onArchive: bt,
|
|
405
|
+
onOpenInEditor: X,
|
|
406
|
+
selectedVibeIds: R,
|
|
407
|
+
onToggleSelect: wt,
|
|
408
|
+
onToggleAllVisible: Tt,
|
|
409
|
+
allVisibleSelected: Y,
|
|
410
|
+
partiallySelected: _t
|
|
409
411
|
}) }, "list")
|
|
410
|
-
}) : /* @__PURE__ */
|
|
411
|
-
icon: /* @__PURE__ */
|
|
412
|
-
title:
|
|
413
|
-
description:
|
|
414
|
-
action: !
|
|
412
|
+
}) : /* @__PURE__ */ d(fe, {
|
|
413
|
+
icon: /* @__PURE__ */ d(je, { className: "w-8 h-8 text-text-secondary" }),
|
|
414
|
+
title: w || T !== "all" || E !== "all" || D !== "all" ? b("vibesPage.emptyNoVibesFound", "No vibes found") : b("vibesPage.emptyNoVibesConfigured", "No vibes configured"),
|
|
415
|
+
description: w || T !== "all" || E !== "all" || D !== "all" ? b("vibesPage.emptyAdjustSearch", "Try adjusting your search or filters") : b("vibesPage.emptyNoVibesDescription", "Add your first vibe to start managing your development projects"),
|
|
416
|
+
action: !w && T === "all" && E === "all" && D === "all" ? /* @__PURE__ */ f("div", {
|
|
415
417
|
className: "flex items-center gap-2",
|
|
416
|
-
children: [/* @__PURE__ */
|
|
417
|
-
permission:
|
|
418
|
-
children: /* @__PURE__ */
|
|
419
|
-
onClick:
|
|
418
|
+
children: [/* @__PURE__ */ d(ze, {
|
|
419
|
+
permission: o.VIBE_CREATE,
|
|
420
|
+
children: /* @__PURE__ */ f("button", {
|
|
421
|
+
onClick: Ct,
|
|
420
422
|
className: "flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
421
|
-
children: [/* @__PURE__ */
|
|
423
|
+
children: [/* @__PURE__ */ d(ke, { className: "w-4 h-4" }), b("vibesPage.addVibe", "Add Vibe")]
|
|
422
424
|
})
|
|
423
|
-
}), /* @__PURE__ */
|
|
425
|
+
}), /* @__PURE__ */ d(he, { prompt: "What is a Vibe in VibeControls and how do I create one?" })]
|
|
424
426
|
}) : void 0
|
|
425
427
|
}),
|
|
426
|
-
|
|
428
|
+
Je && /* @__PURE__ */ d("div", {
|
|
427
429
|
className: "fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm",
|
|
428
|
-
children:
|
|
430
|
+
children: Je
|
|
429
431
|
}),
|
|
430
|
-
/* @__PURE__ */
|
|
431
|
-
isOpen:
|
|
432
|
-
onClose: () =>
|
|
433
|
-
onConfirm:
|
|
432
|
+
/* @__PURE__ */ d(a, {
|
|
433
|
+
isOpen: Ze === "activate",
|
|
434
|
+
onClose: () => B(null),
|
|
435
|
+
onConfirm: Et,
|
|
434
436
|
title: "Activate selected vibes",
|
|
435
|
-
message: `Activate ${
|
|
437
|
+
message: `Activate ${q.length} selected vibe${q.length === 1 ? "" : "s"}?`,
|
|
436
438
|
confirmLabel: "Activate",
|
|
437
439
|
variant: "default"
|
|
438
440
|
}),
|
|
439
|
-
/* @__PURE__ */
|
|
440
|
-
isOpen:
|
|
441
|
-
onClose: () =>
|
|
442
|
-
onConfirm:
|
|
441
|
+
/* @__PURE__ */ d(a, {
|
|
442
|
+
isOpen: Ze === "archive",
|
|
443
|
+
onClose: () => B(null),
|
|
444
|
+
onConfirm: Dt,
|
|
443
445
|
title: "Archive selected vibes",
|
|
444
|
-
message: `Archive ${
|
|
446
|
+
message: `Archive ${J.length} selected vibe${J.length === 1 ? "" : "s"}? Archived vibes will be hidden from active workflows.`,
|
|
445
447
|
confirmLabel: "Archive",
|
|
446
448
|
variant: "warning"
|
|
447
449
|
}),
|
|
448
|
-
|
|
450
|
+
Xe && /* @__PURE__ */ f("div", {
|
|
449
451
|
role: "alert",
|
|
450
452
|
className: "fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-error-bg text-status-error-text rounded-md shadow-lg text-sm",
|
|
451
|
-
children: [
|
|
452
|
-
onClick:
|
|
453
|
+
children: [Xe, /* @__PURE__ */ d("button", {
|
|
454
|
+
onClick: Qe,
|
|
453
455
|
className: "ml-3 text-xs underline opacity-80 hover:opacity-100",
|
|
454
|
-
children:
|
|
456
|
+
children: b("vibesPage.dismiss", "Dismiss")
|
|
455
457
|
})]
|
|
456
458
|
}),
|
|
457
|
-
|
|
459
|
+
Ge && /* @__PURE__ */ d("div", {
|
|
458
460
|
className: "fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4",
|
|
459
|
-
children: /* @__PURE__ */
|
|
461
|
+
children: /* @__PURE__ */ f("div", {
|
|
460
462
|
className: "bg-bg-surface rounded-lg p-6 w-full max-w-lg",
|
|
461
463
|
children: [
|
|
462
|
-
/* @__PURE__ */
|
|
464
|
+
/* @__PURE__ */ d("h2", {
|
|
463
465
|
className: "text-lg font-semibold text-text-primary mb-4",
|
|
464
|
-
children:
|
|
466
|
+
children: b("vibesPage.addNewVibe", "Add New Vibe")
|
|
465
467
|
}),
|
|
466
|
-
/* @__PURE__ */
|
|
468
|
+
/* @__PURE__ */ f("div", {
|
|
467
469
|
className: "space-y-4",
|
|
468
470
|
children: [
|
|
469
|
-
|
|
471
|
+
Ke && /* @__PURE__ */ d("div", {
|
|
470
472
|
className: "p-3 bg-status-error-bg text-status-error-text text-sm rounded-md",
|
|
471
|
-
children:
|
|
473
|
+
children: Ke
|
|
472
474
|
}),
|
|
473
|
-
/* @__PURE__ */
|
|
474
|
-
/* @__PURE__ */
|
|
475
|
+
/* @__PURE__ */ f("div", { children: [
|
|
476
|
+
/* @__PURE__ */ d("label", {
|
|
475
477
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
476
|
-
children:
|
|
478
|
+
children: b("vibesPage.nameLabel", "Name *")
|
|
477
479
|
}),
|
|
478
|
-
/* @__PURE__ */
|
|
479
|
-
ref:
|
|
480
|
+
/* @__PURE__ */ d("input", {
|
|
481
|
+
ref: $e,
|
|
480
482
|
type: "text",
|
|
481
|
-
placeholder:
|
|
482
|
-
onChange: () =>
|
|
483
|
+
placeholder: b("vibesPage.namePlaceholder", "My Project"),
|
|
484
|
+
onChange: () => F.name && I((e) => ({
|
|
483
485
|
...e,
|
|
484
486
|
name: void 0
|
|
485
487
|
})),
|
|
486
|
-
className: `w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${
|
|
488
|
+
className: `w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${F.name ? "border-status-error-border" : "border-border-default"}`
|
|
487
489
|
}),
|
|
488
|
-
|
|
490
|
+
F.name && /* @__PURE__ */ d("p", {
|
|
489
491
|
className: "mt-1 text-xs text-status-error-text",
|
|
490
|
-
children:
|
|
492
|
+
children: F.name
|
|
491
493
|
})
|
|
492
494
|
] }),
|
|
493
|
-
/* @__PURE__ */
|
|
494
|
-
/* @__PURE__ */
|
|
495
|
+
/* @__PURE__ */ f("div", { children: [
|
|
496
|
+
/* @__PURE__ */ d("label", {
|
|
495
497
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
496
|
-
children:
|
|
498
|
+
children: b("vibesPage.pathLabel", "Path *")
|
|
497
499
|
}),
|
|
498
|
-
/* @__PURE__ */
|
|
499
|
-
ref:
|
|
500
|
+
/* @__PURE__ */ d("input", {
|
|
501
|
+
ref: et,
|
|
500
502
|
type: "text",
|
|
501
|
-
placeholder:
|
|
502
|
-
onChange: () =>
|
|
503
|
+
placeholder: b("vibesPage.pathPlaceholder", "/path/to/project"),
|
|
504
|
+
onChange: () => F.path && I((e) => ({
|
|
503
505
|
...e,
|
|
504
506
|
path: void 0
|
|
505
507
|
})),
|
|
506
|
-
className: `w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${
|
|
508
|
+
className: `w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${F.path ? "border-status-error-border" : "border-border-default"}`
|
|
507
509
|
}),
|
|
508
|
-
|
|
510
|
+
F.path && /* @__PURE__ */ d("p", {
|
|
509
511
|
className: "mt-1 text-xs text-status-error-text",
|
|
510
|
-
children:
|
|
512
|
+
children: F.path
|
|
511
513
|
})
|
|
512
514
|
] }),
|
|
513
|
-
/* @__PURE__ */
|
|
515
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
514
516
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
515
|
-
children:
|
|
516
|
-
}), /* @__PURE__ */
|
|
517
|
-
ref:
|
|
517
|
+
children: b("vibesPage.agentLabel", "Agent")
|
|
518
|
+
}), /* @__PURE__ */ f("select", {
|
|
519
|
+
ref: tt,
|
|
518
520
|
className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary",
|
|
519
|
-
children: [/* @__PURE__ */
|
|
521
|
+
children: [/* @__PURE__ */ d("option", {
|
|
520
522
|
value: "",
|
|
521
|
-
children:
|
|
522
|
-
}),
|
|
523
|
+
children: b("vibesPage.selectAgent", "Select an agent")
|
|
524
|
+
}), ht.filter((e) => e.value !== "all").map((e) => /* @__PURE__ */ d("option", {
|
|
523
525
|
value: e.value,
|
|
524
526
|
children: e.label
|
|
525
527
|
}, e.value))]
|
|
526
528
|
})] }),
|
|
527
|
-
/* @__PURE__ */
|
|
529
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
528
530
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
529
|
-
children:
|
|
530
|
-
}), /* @__PURE__ */
|
|
531
|
-
ref:
|
|
531
|
+
children: b("vibesPage.typeLabel", "Type")
|
|
532
|
+
}), /* @__PURE__ */ f("select", {
|
|
533
|
+
ref: nt,
|
|
532
534
|
className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary",
|
|
533
535
|
children: [
|
|
534
|
-
/* @__PURE__ */
|
|
536
|
+
/* @__PURE__ */ d("option", {
|
|
535
537
|
value: "PROJECT",
|
|
536
|
-
children:
|
|
538
|
+
children: b("vibesPage.typeProject", "Project")
|
|
537
539
|
}),
|
|
538
|
-
/* @__PURE__ */
|
|
540
|
+
/* @__PURE__ */ d("option", {
|
|
539
541
|
value: "REPOSITORY",
|
|
540
|
-
children:
|
|
542
|
+
children: b("vibesPage.typeRepository", "Repository")
|
|
541
543
|
}),
|
|
542
|
-
/* @__PURE__ */
|
|
544
|
+
/* @__PURE__ */ d("option", {
|
|
543
545
|
value: "WORKSPACE",
|
|
544
|
-
children:
|
|
546
|
+
children: b("vibesPage.typeWorkspace", "Workspace")
|
|
545
547
|
}),
|
|
546
|
-
/* @__PURE__ */
|
|
548
|
+
/* @__PURE__ */ d("option", {
|
|
547
549
|
value: "MONOREPO",
|
|
548
|
-
children:
|
|
550
|
+
children: b("vibesPage.typeMonorepo", "Monorepo")
|
|
549
551
|
}),
|
|
550
|
-
/* @__PURE__ */
|
|
552
|
+
/* @__PURE__ */ d("option", {
|
|
551
553
|
value: "PACKAGE",
|
|
552
|
-
children:
|
|
554
|
+
children: b("vibesPage.typePackage", "Package")
|
|
553
555
|
}),
|
|
554
|
-
/* @__PURE__ */
|
|
556
|
+
/* @__PURE__ */ d("option", {
|
|
555
557
|
value: "CUSTOM",
|
|
556
|
-
children:
|
|
558
|
+
children: b("vibesPage.typeCustom", "Custom")
|
|
557
559
|
})
|
|
558
560
|
]
|
|
559
561
|
})] })
|
|
560
562
|
]
|
|
561
563
|
}),
|
|
562
|
-
/* @__PURE__ */
|
|
564
|
+
/* @__PURE__ */ f("div", {
|
|
563
565
|
className: "mt-4 border-t border-border-default pt-4",
|
|
564
|
-
children: [/* @__PURE__ */
|
|
566
|
+
children: [/* @__PURE__ */ d("h3", {
|
|
565
567
|
className: "text-sm font-medium text-text-secondary mb-2",
|
|
566
|
-
children:
|
|
567
|
-
}), /* @__PURE__ */
|
|
568
|
+
children: b("vibesPage.tags", "Tags")
|
|
569
|
+
}), /* @__PURE__ */ d(ue, {
|
|
568
570
|
entityType: "VIBECONTROLS_VIBE",
|
|
569
|
-
onDraftChange:
|
|
571
|
+
onDraftChange: at
|
|
570
572
|
})]
|
|
571
573
|
}),
|
|
572
|
-
/* @__PURE__ */
|
|
574
|
+
/* @__PURE__ */ f("div", {
|
|
573
575
|
className: "flex flex-col sm:flex-row justify-end gap-3 mt-6",
|
|
574
|
-
children: [/* @__PURE__ */
|
|
576
|
+
children: [/* @__PURE__ */ d("button", {
|
|
575
577
|
onClick: () => A(!1),
|
|
576
|
-
disabled:
|
|
578
|
+
disabled: M,
|
|
577
579
|
className: "px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50",
|
|
578
|
-
children:
|
|
579
|
-
}), /* @__PURE__ */
|
|
580
|
-
onClick:
|
|
581
|
-
disabled:
|
|
580
|
+
children: b("vibesPage.cancel", "Cancel")
|
|
581
|
+
}), /* @__PURE__ */ d("button", {
|
|
582
|
+
onClick: Ot,
|
|
583
|
+
disabled: M,
|
|
582
584
|
className: "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",
|
|
583
|
-
children:
|
|
585
|
+
children: M ? b("vibesPage.creating", "Creating...") : b("vibesPage.createVibe", "Create Vibe")
|
|
584
586
|
})]
|
|
585
587
|
})
|
|
586
588
|
]
|
|
587
589
|
})
|
|
588
590
|
}),
|
|
589
|
-
/* @__PURE__ */
|
|
590
|
-
isOpen:
|
|
591
|
-
onClose: () =>
|
|
591
|
+
/* @__PURE__ */ d(ce, {
|
|
592
|
+
isOpen: Ye,
|
|
593
|
+
onClose: () => P(!1),
|
|
592
594
|
onConfirm: () => {
|
|
593
|
-
|
|
595
|
+
P(!1), h("/billing");
|
|
594
596
|
},
|
|
595
597
|
title: "Vibe Quota Reached",
|
|
596
598
|
message: "You've used all available vibe creation quota on your current plan. Upgrade your plan to create more vibes."
|
|
@@ -600,6 +602,6 @@ function h() {
|
|
|
600
602
|
});
|
|
601
603
|
}
|
|
602
604
|
//#endregion
|
|
603
|
-
export {
|
|
605
|
+
export { m as VibesPage };
|
|
604
606
|
|
|
605
607
|
//# sourceMappingURL=VibesPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibesPage.js","names":[],"sources":["../../src/pages/VibesPage.tsx"],"sourcesContent":["import { useState, useMemo, useRef, useEffect, useCallback } from 'react';\nimport { Plus, Zap, Grid, List, RefreshCw, Archive, Play } from 'lucide-react';\nimport { VibeCard, VibeList } from '@/components/vibes';\nimport type { Vibe } from '@/components/vibes';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n QuotaExhaustedDialog,\n SelectionCheckbox,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport {\n useListVibesQuery,\n useCreateVibeMutation,\n useUpdateVibeStatusMutation,\n useArchiveVibeMutation,\n useListAgentsLazyQuery,\n type ListAgentsQuery,\n} from '@/generated/wspace-operations';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\n\ntype Agent = NonNullable<ListAgentsQuery['vibecontrolsAgents']>[number];\nimport {\n PermissionGate,\n PermissionButton,\n AccessDenied,\n} from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport {\n buildProtocolUrl,\n getIdePreset,\n resolveEffectiveIdeCommand,\n} from '@/components/agents/idePresets';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\ntype ViewMode = 'grid' | 'list';\n\n/**\n * Vibes (projects/repositories) management page\n */\nexport function VibesPage() {\n const { onVibeActivate } = useVibeControls();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const { t } = useI18n();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const statusOptions = [\n { value: 'all', label: tr('vibesPage.statusAll', 'All Statuses') },\n { value: 'ACTIVE', label: tr('vibesPage.statusActive', 'Active') },\n { value: 'INACTIVE', label: tr('vibesPage.statusInactive', 'Inactive') },\n { value: 'ARCHIVED', label: tr('vibesPage.statusArchived', 'Archived') },\n { value: 'ERROR', label: tr('vibesPage.statusError', 'Error') },\n ];\n\n const typeOptions = [\n { value: 'all', label: tr('vibesPage.typeAll', 'All Types') },\n { value: 'PROJECT', label: tr('vibesPage.typeProject', 'Project') },\n { value: 'REPOSITORY', label: tr('vibesPage.typeRepository', 'Repository') },\n { value: 'WORKSPACE', label: tr('vibesPage.typeWorkspace', 'Workspace') },\n { value: 'MONOREPO', label: tr('vibesPage.typeMonorepo', 'Monorepo') },\n { value: 'PACKAGE', label: tr('vibesPage.typePackage', 'Package') },\n { value: 'CUSTOM', label: tr('vibesPage.typeCustom', 'Custom') },\n ];\n\n // Check permissions\n const canListVibes = hasPermission(VIBE_PERMISSIONS.VIBE_LIST);\n const canUpdateVibe = hasPermission(VIBE_PERMISSIONS.VIBE_UPDATE);\n\n // Skip queries until permissions are loaded and user has permission\n // This prevents FORBIDDEN errors from triggering auth error handlers\n const shouldSkipQueries = permissionsLoading || !canListVibes;\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState('all');\n const [typeFilter, setTypeFilter] = useState('all');\n const [agentFilter, setAgentFilter] = useState('all');\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [createLoading, setCreateLoading] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{ name?: string; path?: string }>({});\n const [pageError, setPageError] = useState<string | null>(null);\n const [selectedVibeIds, setSelectedVibeIds] = useState<Set<string>>(new Set());\n const [bulkAction, setBulkAction] = useState<'activate' | 'archive' | null>(null);\n const pageErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (pageErrorTimer.current) clearTimeout(pageErrorTimer.current);\n };\n }, []);\n\n const clearPageError = useCallback(() => {\n setPageError(null);\n if (pageErrorTimer.current) {\n clearTimeout(pageErrorTimer.current);\n pageErrorTimer.current = null;\n }\n }, []);\n\n // Form refs for create modal\n const nameRef = useRef<HTMLInputElement>(null);\n const pathRef = useRef<HTMLInputElement>(null);\n const agentRef = useRef<HTMLSelectElement>(null);\n const typeRef = useRef<HTMLSelectElement>(null);\n\n const [createVibe] = useCreateVibeMutation();\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_VIBE');\n const [updateVibeStatus] = useUpdateVibeStatusMutation();\n const [archiveVibe] = useArchiveVibeMutation();\n\n // Memoise variables so a stable identity keeps Apollo from treating each\n // render as a new query (which caused multiple loading flashes).\n // NOTE: we intentionally do NOT include `searchQuery` in the query\n // variables — the list is filtered client-side by name below, and keying\n // the query on `searchQuery` caused a fresh refetch on every keystroke\n // (previously mitigated by an unusable `limit: 1000` fire-hose). Rely on\n // the default backend pagination and filter client-side within the page.\n const listVibesVariables = useMemo(\n () => ({\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as\n | 'PROJECT'\n | 'REPOSITORY'\n | 'WORKSPACE'\n | 'MONOREPO'\n | 'PACKAGE'\n | 'CUSTOM')\n : undefined,\n // Multi-agent: server-side filter takes a list. Single-select UI maps\n // to a one-element array; \"all\" clears the filter.\n attachedAgentIds: agentFilter !== 'all' ? [agentFilter] : undefined,\n },\n }),\n [statusFilter, typeFilter, agentFilter]\n );\n\n // When no filters are applied, the variables match the InitialData\n // prefetch (no filters) — use cache-first to avoid a redundant network\n // hit on every page visit. When user actually narrows by filter, fall\n // back to cache-and-network for fresh server-side filtering.\n const noVibeFilters = statusFilter === 'all' && typeFilter === 'all' && agentFilter === 'all';\n const vibesFetchPolicy = noVibeFilters ? 'cache-first' : 'cache-and-network';\n\n // Fetch vibes from GraphQL API. cache-and-network returns the cached list\n // immediately while revalidating once in the background — avoids the\n // \"loading → cache → network → cache\" double-flash seen with cache-first\n // plus mutation refetchQueries. `notifyOnNetworkStatusChange` keeps the\n // loading flag true during refetches so the UI can show a spinner rather\n // than showing stale data silently.\n const { data, loading, error, refetch } = useListVibesQuery({\n variables: listVibesVariables,\n fetchPolicy: vibesFetchPolicy,\n nextFetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n notifyOnNetworkStatusChange: true,\n });\n\n // Cached agents (already prefetched by InitialDataPrefetcher) cover the\n // initial agent-filter dropdown without firing any network query. The\n // lazy ListAgentsQuery below is only triggered on first interaction with\n // the agent filter (in case the prefetch was stale).\n const { agents: cachedAgentsForFilter } = useCachedInitialData();\n const [fetchAgents, { data: agentsData }] = useListAgentsLazyQuery({\n fetchPolicy: 'cache-first',\n });\n const triggeredAgentsFetchRef = useRef(false);\n const handleAgentFilterInteract = useCallback(() => {\n if (triggeredAgentsFetchRef.current || shouldSkipQueries) return;\n triggeredAgentsFetchRef.current = true;\n void fetchAgents({ variables: { pagination: { limit: 100 } } });\n }, [fetchAgents, shouldSkipQueries]);\n\n // Build agent options for filter — prefer freshly-fetched data once the\n // dropdown has been opened, otherwise use the cached set from\n // InitialDataPrefetcher.\n const agentOptions = useMemo(() => {\n const live = agentsData?.vibecontrolsAgents ?? null;\n const source: Array<{ id: string; name: string }> = live\n ? live.map((a: Agent) => ({ id: a.id, name: a.name }))\n : cachedAgentsForFilter.map((a) => ({ id: a.id, name: a.name }));\n return [\n { value: 'all', label: tr('vibesPage.allAgents', 'All Agents') },\n ...source.map((agent) => ({\n value: agent.id,\n label: agent.name,\n })),\n ];\n }, [agentsData?.vibecontrolsAgents, cachedAgentsForFilter, tr]);\n\n // Map GraphQL response to Vibe type\n const vibes: Vibe[] = useMemo(() => {\n const rawVibes = data?.vibes?.items ?? [];\n return rawVibes.map((vibe: NonNullable<typeof rawVibes>[number]) => ({\n id: vibe.id,\n workspaceId: vibe.workspaceId,\n name: vibe.name,\n description: vibe.description || null,\n type: vibe.type as Vibe['type'],\n status: vibe.status as Vibe['status'],\n path: vibe.path || null,\n parentId: vibe.parentId || null,\n gitBranch: vibe.gitBranch || null,\n gitRemote: vibe.gitRemote || null,\n gitStatus: vibe.gitStatus\n ? typeof vibe.gitStatus === 'string'\n ? vibe.gitStatus\n : typeof vibe.gitStatus === 'object' &&\n vibe.gitStatus !== null &&\n 'status' in vibe.gitStatus\n ? String((vibe.gitStatus as Record<string, unknown>).status)\n : JSON.stringify(vibe.gitStatus)\n : null,\n environmentVariables: (vibe.environmentVariables as Record<string, string>) || {},\n tags: vibe.tags || [],\n metadata: (vibe.metadata as Record<string, unknown>) || {},\n createdAt: vibe.createdAt,\n updatedAt: vibe.updatedAt,\n agents: (vibe.agents ?? []).map((a: NonNullable<typeof vibe.agents>[number]) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n ownerUserId: a.ownerUserId ?? null,\n })),\n }));\n }, [data?.vibes?.items]);\n\n // Filter vibes based on search (client-side filtering for search)\n const filteredVibes = useMemo(() => {\n return vibes.filter((vibe) => {\n // Search filter\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n vibe.name.toLowerCase().includes(query) ||\n vibe.description?.toLowerCase().includes(query) ||\n vibe.path?.toLowerCase().includes(query) ||\n vibe.tags?.some((tag) => tag.toLowerCase().includes(query));\n if (!matchesSearch) return false;\n }\n\n return true;\n });\n }, [vibes, searchQuery]);\n\n const selectedVibes = useMemo(\n () => vibes.filter((vibe) => selectedVibeIds.has(vibe.id)),\n [vibes, selectedVibeIds]\n );\n const activatableVibes = selectedVibes.filter((vibe) => vibe.status !== 'ACTIVE');\n const archivableVibes = selectedVibes.filter((vibe) => vibe.status !== 'ARCHIVED');\n const allVisibleSelected =\n filteredVibes.length > 0 && filteredVibes.every((vibe) => selectedVibeIds.has(vibe.id));\n const partiallySelected =\n filteredVibes.some((vibe) => selectedVibeIds.has(vibe.id)) && !allVisibleSelected;\n\n const handleVibeSelect = (vibe: Vibe) => {\n navigate(`/vibes/${vibe.id}`);\n };\n\n const handleVibeActivate = async (vibe: Vibe) => {\n try {\n const result = await updateVibeStatus({\n variables: { id: vibe.id, status: 'ACTIVE' as const },\n });\n if (result.data?.updateVibeStatus) {\n onVibeActivate?.({ id: vibe.id, name: vibe.name, type: vibe.type, status: 'ACTIVE' });\n }\n } catch (err) {\n console.error('Failed to activate vibe:', err);\n }\n };\n\n const handleVibeArchive = async (vibe: Vibe) => {\n try {\n await archiveVibe({ variables: { id: vibe.id } });\n } catch (err) {\n console.error('Failed to archive vibe:', err);\n }\n };\n\n // Honour the user's preferred IDE + per-Vibe override when opening from\n // the card. Falls back to `vscode://` for IDEs without a registered URL\n // protocol; for richer launching (agent-exec, browser editor) use the\n // VibeOpenInEditorMenu on the Vibe detail page.\n const preferredIdeCommand = useVibeControlsStore((s) => s.preferredIdeCommand);\n const vibeIdeOverrides = useVibeControlsStore((s) => s.vibeIdeOverrides);\n\n const handleOpenInEditor = (vibe: Vibe) => {\n if (!vibe.path) {\n setPageError(tr('vibesPage.openEditorPathMissing', 'No path available to open in editor'));\n pageErrorTimer.current = setTimeout(() => {\n setPageError(null);\n pageErrorTimer.current = null;\n }, 3000);\n return;\n }\n const cmd = resolveEffectiveIdeCommand(vibe.id, preferredIdeCommand, vibeIdeOverrides);\n const preset = getIdePreset(cmd);\n const url = preset ? buildProtocolUrl(preset, vibe.path) : `vscode://file/${vibe.path}`;\n if (url) {\n window.location.href = url;\n } else {\n setPageError(\n tr(\n 'vibesPage.openEditorNoProtocol',\n `${preset?.label ?? cmd} has no URL handler — open this Vibe and use the editor menu.`\n )\n );\n pageErrorTimer.current = setTimeout(() => {\n setPageError(null);\n pageErrorTimer.current = null;\n }, 4000);\n }\n };\n\n const handleCreateVibe = () => {\n setCreateError(null);\n setShowCreateForm(true);\n // Ensure the create-form agent dropdown has options even if the user\n // never opened the page-level agent filter.\n handleAgentFilterInteract();\n };\n\n const toggleVibeSelection = (vibe: Vibe, checked: boolean) => {\n setSelectedVibeIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(vibe.id);\n } else {\n next.delete(vibe.id);\n }\n return next;\n });\n };\n\n const toggleAllVisibleVibes = (checked: boolean) => {\n setSelectedVibeIds((prev) => {\n const next = new Set(prev);\n filteredVibes.forEach((vibe) => {\n if (checked) {\n next.add(vibe.id);\n } else {\n next.delete(vibe.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedVibeIds(new Set());\n\n const handleBulkActivate = async () => {\n if (activatableVibes.length === 0) return;\n\n const results = await Promise.allSettled(\n activatableVibes.map((vibe) =>\n updateVibeStatus({ variables: { id: vibe.id, status: 'ACTIVE' as const } })\n )\n );\n\n const succeeded = results.filter((result) => result.status === 'fulfilled').length;\n const failed = results.length - succeeded;\n\n setSuccessMessage(\n `Activated ${succeeded} vibe${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`\n );\n setTimeout(() => setSuccessMessage(null), 4000);\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n const handleBulkArchive = async () => {\n if (archivableVibes.length === 0) return;\n\n const results = await Promise.allSettled(\n archivableVibes.map((vibe) => archiveVibe({ variables: { id: vibe.id } }))\n );\n\n const succeeded = results.filter((result) => result.status === 'fulfilled').length;\n const failed = results.length - succeeded;\n\n setSuccessMessage(\n `Archived ${succeeded} vibe${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`\n );\n setTimeout(() => setSuccessMessage(null), 4000);\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n const handleSubmitCreateVibe = async () => {\n const name = nameRef.current?.value?.trim();\n const path = pathRef.current?.value?.trim();\n const agentId = agentRef.current?.value;\n const type = typeRef.current?.value;\n\n const errors: { name?: string; path?: string } = {};\n if (!name) errors.name = tr('vibesPage.nameRequired', 'Name is required');\n if (!path) errors.path = tr('vibesPage.pathRequired', 'Path is required');\n if (errors.name || errors.path) {\n setFieldErrors(errors);\n setCreateError(tr('vibesPage.fillRequired', 'Please fill in all required fields'));\n return;\n }\n setFieldErrors({});\n\n // Validate type against allowed enum values\n const validTypes = [\n 'PROJECT',\n 'REPOSITORY',\n 'WORKSPACE',\n 'MONOREPO',\n 'PACKAGE',\n 'CUSTOM',\n ] as const;\n const resolvedType = validTypes.includes(type as (typeof validTypes)[number])\n ? (type as (typeof validTypes)[number])\n : 'PROJECT';\n\n setCreateLoading(true);\n setCreateError(null);\n try {\n const result = await createVibe({\n variables: {\n input: {\n name,\n path,\n type: resolvedType,\n agentId: agentId || undefined,\n },\n },\n });\n if (!result.data?.createVibe?.id) {\n throw new Error(tr('vibesPage.failedCreate', 'Failed to create vibe'));\n }\n const newVibeId = result.data.createVibe.id;\n setShowCreateForm(false);\n setSuccessMessage(tr('vibesPage.createdSuccess', 'Vibe created successfully'));\n await flushTags(newVibeId);\n await refetch();\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowCreateForm(false);\n setCreateError(null);\n setFieldErrors({});\n setShowQuotaExhausted(true);\n return;\n }\n\n setCreateError(\n err instanceof Error ? err.message : tr('vibesPage.failedCreate', 'Failed to create vibe')\n );\n } finally {\n setCreateLoading(false);\n }\n };\n\n // Stats\n const stats = {\n total: vibes.length,\n active: vibes.filter((v) => v.status === 'ACTIVE').length,\n inactive: vibes.filter((v) => v.status === 'INACTIVE').length,\n };\n\n // Collapse guard states into a single MotionPage shell so transitioning\n // between loading / error / data does not unmount+remount and replay the\n // entrance animation. Contents are swapped in-place.\n const showPermissionsLoading = permissionsLoading;\n const showAccessDenied =\n !permissionsLoading &&\n !!permissions &&\n (permissions.permissionStrings?.length ?? 0) > 0 &&\n !canListVibes;\n const showInitialLoading =\n !showPermissionsLoading && !showAccessDenied && loading && vibes.length === 0;\n const showInitialError =\n !showPermissionsLoading && !showAccessDenied && !!error && vibes.length === 0;\n const showDataShell = !showPermissionsLoading && !showAccessDenied && !showInitialError;\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {showPermissionsLoading && (\n <LoadingState message={tr('vibesPage.loadingPermissions', 'Loading permissions...')} />\n )}\n {showAccessDenied && (\n <AccessDenied\n title={tr('vibesPage.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'vibesPage.accessDeniedDescription',\n \"You don't have permission to view vibes. Contact your workspace administrator for access.\"\n )}\n />\n )}\n {showInitialError && (\n <ErrorState\n title={tr('vibesPage.failedLoad', 'Failed to load vibes')}\n message={error?.message ?? ''}\n onRetry={() => refetch()}\n />\n )}\n {showDataShell && (\n <>\n <PageHeader\n title={tr('vibesPage.title', 'Vibes')}\n description={tr('vibesPage.descriptionActive', '{{active}} of {{total}} vibes active', {\n active: stats.active,\n total: stats.total,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('vibesPage.refreshTitle', 'Refresh vibes')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <PermissionButton\n permission={VIBE_PERMISSIONS.VIBE_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tr(\n 'vibesPage.deniedCreateTooltip',\n \"You don't have permission to create vibes\"\n )}\n >\n <button\n onClick={handleCreateVibe}\n className=\"flex items-center gap-2 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 disabled:cursor-not-allowed\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('vibesPage.addVibe', 'Add Vibe')}\n </button>\n </PermissionButton>\n </div>\n }\n />\n\n {/* Filters Bar */}\n <div className=\"space-y-3\">\n {/* Row 1: Search + View Toggle */}\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('vibesPage.searchPlaceholder', 'Search vibes...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('vibesPage.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"w-4 h-4 text-text-secondary\" />\n </button>\n <button\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('vibesPage.listViewTitle', 'List view')}\n >\n <List className=\"w-4 h-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n {/* Row 2: Filters - wrap on all screens */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('vibesPage.filterStatus', 'Status')}\n value={statusFilter}\n options={statusOptions}\n onChange={setStatusFilter}\n />\n <div onMouseDown={handleAgentFilterInteract} onFocus={handleAgentFilterInteract}>\n <FilterDropdown\n label={tr('vibesPage.filterAgent', 'Agent')}\n value={agentFilter}\n options={agentOptions}\n onChange={setAgentFilter}\n />\n </div>\n </div>\n </div>\n\n {/* Results Count */}\n {searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all' ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('vibesPage.showingResults', 'Showing {{filtered}} of {{total}} vibes', {\n filtered: filteredVibes.length,\n total: vibes.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('vibesPage.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n ) : null}\n\n {filteredVibes.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVibes}\n ariaLabel=\"Select all visible vibes\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n Select all visible\n </span>\n </div>\n {selectedVibeIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedVibeIds.size} selected\n </span>\n <button\n onClick={() => setBulkAction('activate')}\n disabled={activatableVibes.length === 0}\n title=\"Mark selected vibes as ACTIVE so they appear in the default vibe list and can run sessions/tunnels via their attached agent(s).\"\n className=\"rounded-md border border-status-success-border px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Play className=\"w-4 h-4\" />\n Activate Selected\n {activatableVibes.length > 0 ? ` (${activatableVibes.length})` : ''}\n </span>\n </button>\n <button\n onClick={() => setBulkAction('archive')}\n disabled={archivableVibes.length === 0}\n className=\"rounded-md border border-status-warning-border px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Archive className=\"w-4 h-4\" />\n Archive Selected\n {archivableVibes.length > 0 ? ` (${archivableVibes.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Vibes Display */}\n {showInitialLoading ? (\n <PageSkeleton variant=\"cards\" count={6} />\n ) : filteredVibes.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n {viewMode === 'grid' ? (\n <MotionList\n key=\"grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredVibes.map((vibe) => (\n <MotionItem key={vibe.id}>\n <VibeCard\n vibe={vibe}\n onOpen={handleVibeSelect}\n onActivate={canUpdateVibe ? handleVibeActivate : undefined}\n onArchive={canUpdateVibe ? handleVibeArchive : undefined}\n onOpenInEditor={handleOpenInEditor}\n selected={selectedVibeIds.has(vibe.id)}\n onToggleSelect={(checked) => toggleVibeSelection(vibe, checked)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <MotionList key=\"list\">\n <VibeList\n vibes={filteredVibes}\n onOpen={handleVibeSelect}\n onActivate={handleVibeActivate}\n onArchive={handleVibeArchive}\n onOpenInEditor={handleOpenInEditor}\n selectedVibeIds={selectedVibeIds}\n onToggleSelect={toggleVibeSelection}\n onToggleAllVisible={toggleAllVisibleVibes}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n />\n </MotionList>\n )}\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Zap className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('vibesPage.emptyNoVibesFound', 'No vibes found')\n : tr('vibesPage.emptyNoVibesConfigured', 'No vibes configured')\n }\n description={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('vibesPage.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'vibesPage.emptyNoVibesDescription',\n 'Add your first vibe to start managing your development projects'\n )\n }\n action={\n !searchQuery &&\n statusFilter === 'all' &&\n typeFilter === 'all' &&\n agentFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n <PermissionGate permission={VIBE_PERMISSIONS.VIBE_CREATE}>\n <button\n onClick={handleCreateVibe}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('vibesPage.addVibe', 'Add Vibe')}\n </button>\n </PermissionGate>\n <GetHelpButton prompt=\"What is a Vibe in VibeControls and how do I create one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n\n <ConfirmDialog\n isOpen={bulkAction === 'activate'}\n onClose={() => setBulkAction(null)}\n onConfirm={handleBulkActivate}\n title=\"Activate selected vibes\"\n message={`Activate ${activatableVibes.length} selected vibe${activatableVibes.length === 1 ? '' : 's'}?`}\n confirmLabel=\"Activate\"\n variant=\"default\"\n />\n\n <ConfirmDialog\n isOpen={bulkAction === 'archive'}\n onClose={() => setBulkAction(null)}\n onConfirm={handleBulkArchive}\n title=\"Archive selected vibes\"\n message={`Archive ${archivableVibes.length} selected vibe${archivableVibes.length === 1 ? '' : 's'}? Archived vibes will be hidden from active workflows.`}\n confirmLabel=\"Archive\"\n variant=\"warning\"\n />\n\n {/* Page-level Error Toast */}\n {pageError && (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-error-bg text-status-error-text rounded-md shadow-lg text-sm\"\n >\n {pageError}\n <button\n onClick={clearPageError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('vibesPage.dismiss', 'Dismiss')}\n </button>\n </div>\n )}\n\n {/* Create Vibe Modal */}\n {showCreateForm && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('vibesPage.addNewVibe', 'Add New Vibe')}\n </h2>\n <div className=\"space-y-4\">\n {createError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {createError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.nameLabel', 'Name *')}\n </label>\n <input\n ref={nameRef}\n type=\"text\"\n placeholder={tr('vibesPage.namePlaceholder', 'My Project')}\n onChange={() =>\n fieldErrors.name && setFieldErrors((e) => ({ ...e, name: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.name ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.name && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.name}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.pathLabel', 'Path *')}\n </label>\n <input\n ref={pathRef}\n type=\"text\"\n placeholder={tr('vibesPage.pathPlaceholder', '/path/to/project')}\n onChange={() =>\n fieldErrors.path && setFieldErrors((e) => ({ ...e, path: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.path ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.path && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.path}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.agentLabel', 'Agent')}\n </label>\n <select\n ref={agentRef}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"\">{tr('vibesPage.selectAgent', 'Select an agent')}</option>\n {agentOptions\n .filter((opt) => opt.value !== 'all')\n .map((agent) => (\n <option key={agent.value} value={agent.value}>\n {agent.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.typeLabel', 'Type')}\n </label>\n <select\n ref={typeRef}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"PROJECT\">{tr('vibesPage.typeProject', 'Project')}</option>\n <option value=\"REPOSITORY\">\n {tr('vibesPage.typeRepository', 'Repository')}\n </option>\n <option value=\"WORKSPACE\">\n {tr('vibesPage.typeWorkspace', 'Workspace')}\n </option>\n <option value=\"MONOREPO\">{tr('vibesPage.typeMonorepo', 'Monorepo')}</option>\n <option value=\"PACKAGE\">{tr('vibesPage.typePackage', 'Package')}</option>\n <option value=\"CUSTOM\">{tr('vibesPage.typeCustom', 'Custom')}</option>\n </select>\n </div>\n </div>\n <div className=\"mt-4 border-t border-border-default pt-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibesPage.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" onDraftChange={setDraftTagIds} />\n </div>\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={() => setShowCreateForm(false)}\n disabled={createLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('vibesPage.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleSubmitCreateVibe}\n disabled={createLoading}\n className=\"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 >\n {createLoading\n ? tr('vibesPage.creating', 'Creating...')\n : tr('vibesPage.createVibe', 'Create Vibe')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Vibe Quota Reached\"\n message=\"You've used all available vibe creation quota on your current plan. Upgrade your plan to create more vibes.\"\n />\n </>\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibesPage.js","names":[],"sources":["../../src/pages/VibesPage.tsx"],"sourcesContent":["import { useState, useMemo, useRef, useEffect, useCallback } from 'react';\nimport { Plus, Zap, Grid, List, RefreshCw, Archive, Play } from 'lucide-react';\nimport { VibeCard, VibeList } from '@/components/vibes';\nimport type { Vibe } from '@/components/vibes';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n QuotaExhaustedDialog,\n SelectionCheckbox,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport {\n useListVibesQuery,\n useCreateVibeMutation,\n useAttachAgentToVibeMutation,\n useUpdateVibeStatusMutation,\n useArchiveVibeMutation,\n useListAgentsLazyQuery,\n type ListAgentsQuery,\n} from '@/generated/wspace-operations';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\n\ntype Agent = NonNullable<ListAgentsQuery['vibecontrolsAgents']>[number];\nimport {\n PermissionGate,\n PermissionButton,\n AccessDenied,\n} from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport {\n buildProtocolUrl,\n getIdePreset,\n resolveEffectiveIdeCommand,\n} from '@/components/agents/idePresets';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\ntype ViewMode = 'grid' | 'list';\n\n/**\n * Vibes (projects/repositories) management page\n */\nexport function VibesPage() {\n const { onVibeActivate } = useVibeControls();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const { t } = useI18n();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const statusOptions = [\n { value: 'all', label: tr('vibesPage.statusAll', 'All Statuses') },\n { value: 'ACTIVE', label: tr('vibesPage.statusActive', 'Active') },\n { value: 'INACTIVE', label: tr('vibesPage.statusInactive', 'Inactive') },\n { value: 'ARCHIVED', label: tr('vibesPage.statusArchived', 'Archived') },\n { value: 'ERROR', label: tr('vibesPage.statusError', 'Error') },\n ];\n\n const typeOptions = [\n { value: 'all', label: tr('vibesPage.typeAll', 'All Types') },\n { value: 'PROJECT', label: tr('vibesPage.typeProject', 'Project') },\n { value: 'REPOSITORY', label: tr('vibesPage.typeRepository', 'Repository') },\n { value: 'WORKSPACE', label: tr('vibesPage.typeWorkspace', 'Workspace') },\n { value: 'MONOREPO', label: tr('vibesPage.typeMonorepo', 'Monorepo') },\n { value: 'PACKAGE', label: tr('vibesPage.typePackage', 'Package') },\n { value: 'CUSTOM', label: tr('vibesPage.typeCustom', 'Custom') },\n ];\n\n // Check permissions\n const canListVibes = hasPermission(VIBE_PERMISSIONS.VIBE_LIST);\n const canUpdateVibe = hasPermission(VIBE_PERMISSIONS.VIBE_UPDATE);\n\n // Skip queries until permissions are loaded and user has permission\n // This prevents FORBIDDEN errors from triggering auth error handlers\n const shouldSkipQueries = permissionsLoading || !canListVibes;\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState('all');\n const [typeFilter, setTypeFilter] = useState('all');\n const [agentFilter, setAgentFilter] = useState('all');\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [createLoading, setCreateLoading] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{ name?: string; path?: string }>({});\n const [pageError, setPageError] = useState<string | null>(null);\n const [selectedVibeIds, setSelectedVibeIds] = useState<Set<string>>(new Set());\n const [bulkAction, setBulkAction] = useState<'activate' | 'archive' | null>(null);\n const pageErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (pageErrorTimer.current) clearTimeout(pageErrorTimer.current);\n };\n }, []);\n\n const clearPageError = useCallback(() => {\n setPageError(null);\n if (pageErrorTimer.current) {\n clearTimeout(pageErrorTimer.current);\n pageErrorTimer.current = null;\n }\n }, []);\n\n // Form refs for create modal\n const nameRef = useRef<HTMLInputElement>(null);\n const pathRef = useRef<HTMLInputElement>(null);\n const agentRef = useRef<HTMLSelectElement>(null);\n const typeRef = useRef<HTMLSelectElement>(null);\n\n const [createVibe] = useCreateVibeMutation();\n const [attachAgentToVibe] = useAttachAgentToVibeMutation();\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_VIBE');\n const [updateVibeStatus] = useUpdateVibeStatusMutation();\n const [archiveVibe] = useArchiveVibeMutation();\n\n // Memoise variables so a stable identity keeps Apollo from treating each\n // render as a new query (which caused multiple loading flashes).\n // NOTE: we intentionally do NOT include `searchQuery` in the query\n // variables — the list is filtered client-side by name below, and keying\n // the query on `searchQuery` caused a fresh refetch on every keystroke\n // (previously mitigated by an unusable `limit: 1000` fire-hose). Rely on\n // the default backend pagination and filter client-side within the page.\n const listVibesVariables = useMemo(\n () => ({\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as\n | 'PROJECT'\n | 'REPOSITORY'\n | 'WORKSPACE'\n | 'MONOREPO'\n | 'PACKAGE'\n | 'CUSTOM')\n : undefined,\n // Multi-agent: server-side filter takes a list. Single-select UI maps\n // to a one-element array; \"all\" clears the filter.\n attachedAgentIds: agentFilter !== 'all' ? [agentFilter] : undefined,\n },\n }),\n [statusFilter, typeFilter, agentFilter]\n );\n\n // When no filters are applied, the variables match the InitialData\n // prefetch (no filters) — use cache-first to avoid a redundant network\n // hit on every page visit. When user actually narrows by filter, fall\n // back to cache-and-network for fresh server-side filtering.\n const noVibeFilters = statusFilter === 'all' && typeFilter === 'all' && agentFilter === 'all';\n const vibesFetchPolicy = noVibeFilters ? 'cache-first' : 'cache-and-network';\n\n // Fetch vibes from GraphQL API. cache-and-network returns the cached list\n // immediately while revalidating once in the background — avoids the\n // \"loading → cache → network → cache\" double-flash seen with cache-first\n // plus mutation refetchQueries. `notifyOnNetworkStatusChange` keeps the\n // loading flag true during refetches so the UI can show a spinner rather\n // than showing stale data silently.\n const { data, loading, error, refetch } = useListVibesQuery({\n variables: listVibesVariables,\n fetchPolicy: vibesFetchPolicy,\n nextFetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n notifyOnNetworkStatusChange: true,\n });\n\n // Cached agents (already prefetched by InitialDataPrefetcher) cover the\n // initial agent-filter dropdown without firing any network query. The\n // lazy ListAgentsQuery below is only triggered on first interaction with\n // the agent filter (in case the prefetch was stale).\n const { agents: cachedAgentsForFilter } = useCachedInitialData();\n const [fetchAgents, { data: agentsData }] = useListAgentsLazyQuery({\n fetchPolicy: 'cache-first',\n });\n const triggeredAgentsFetchRef = useRef(false);\n const handleAgentFilterInteract = useCallback(() => {\n if (triggeredAgentsFetchRef.current || shouldSkipQueries) return;\n triggeredAgentsFetchRef.current = true;\n void fetchAgents({ variables: { pagination: { limit: 100 } } });\n }, [fetchAgents, shouldSkipQueries]);\n\n // Build agent options for filter — prefer freshly-fetched data once the\n // dropdown has been opened, otherwise use the cached set from\n // InitialDataPrefetcher.\n const agentOptions = useMemo(() => {\n const live = agentsData?.vibecontrolsAgents ?? null;\n const source: Array<{ id: string; name: string }> = live\n ? live.map((a: Agent) => ({ id: a.id, name: a.name }))\n : cachedAgentsForFilter.map((a) => ({ id: a.id, name: a.name }));\n return [\n { value: 'all', label: tr('vibesPage.allAgents', 'All Agents') },\n ...source.map((agent) => ({\n value: agent.id,\n label: agent.name,\n })),\n ];\n }, [agentsData?.vibecontrolsAgents, cachedAgentsForFilter, tr]);\n\n // Map GraphQL response to Vibe type\n const vibes: Vibe[] = useMemo(() => {\n const rawVibes = data?.vibes?.items ?? [];\n return rawVibes.map((vibe: NonNullable<typeof rawVibes>[number]) => ({\n id: vibe.id,\n workspaceId: vibe.workspaceId,\n name: vibe.name,\n description: vibe.description || null,\n type: vibe.type as Vibe['type'],\n status: vibe.status as Vibe['status'],\n path: vibe.path || null,\n parentId: vibe.parentId || null,\n gitBranch: vibe.gitBranch || null,\n gitRemote: vibe.gitRemote || null,\n gitStatus: vibe.gitStatus\n ? typeof vibe.gitStatus === 'string'\n ? vibe.gitStatus\n : typeof vibe.gitStatus === 'object' &&\n vibe.gitStatus !== null &&\n 'status' in vibe.gitStatus\n ? String((vibe.gitStatus as Record<string, unknown>).status)\n : JSON.stringify(vibe.gitStatus)\n : null,\n environmentVariables: (vibe.environmentVariables as Record<string, string>) || {},\n tags: vibe.tags || [],\n metadata: (vibe.metadata as Record<string, unknown>) || {},\n createdAt: vibe.createdAt,\n updatedAt: vibe.updatedAt,\n agents: (vibe.agents ?? []).map((a: NonNullable<typeof vibe.agents>[number]) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n ownerUserId: a.ownerUserId ?? null,\n })),\n }));\n }, [data?.vibes?.items]);\n\n // Filter vibes based on search (client-side filtering for search)\n const filteredVibes = useMemo(() => {\n return vibes.filter((vibe) => {\n // Search filter\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n vibe.name.toLowerCase().includes(query) ||\n vibe.description?.toLowerCase().includes(query) ||\n vibe.path?.toLowerCase().includes(query) ||\n vibe.tags?.some((tag) => tag.toLowerCase().includes(query));\n if (!matchesSearch) return false;\n }\n\n return true;\n });\n }, [vibes, searchQuery]);\n\n const selectedVibes = useMemo(\n () => vibes.filter((vibe) => selectedVibeIds.has(vibe.id)),\n [vibes, selectedVibeIds]\n );\n const activatableVibes = selectedVibes.filter((vibe) => vibe.status !== 'ACTIVE');\n const archivableVibes = selectedVibes.filter((vibe) => vibe.status !== 'ARCHIVED');\n const allVisibleSelected =\n filteredVibes.length > 0 && filteredVibes.every((vibe) => selectedVibeIds.has(vibe.id));\n const partiallySelected =\n filteredVibes.some((vibe) => selectedVibeIds.has(vibe.id)) && !allVisibleSelected;\n\n const handleVibeSelect = (vibe: Vibe) => {\n navigate(`/vibes/${vibe.id}`);\n };\n\n const handleVibeActivate = async (vibe: Vibe) => {\n try {\n const result = await updateVibeStatus({\n variables: { id: vibe.id, status: 'ACTIVE' as const },\n });\n if (result.data?.updateVibeStatus) {\n onVibeActivate?.({ id: vibe.id, name: vibe.name, type: vibe.type, status: 'ACTIVE' });\n }\n } catch (err) {\n console.error('Failed to activate vibe:', err);\n }\n };\n\n const handleVibeArchive = async (vibe: Vibe) => {\n try {\n await archiveVibe({ variables: { id: vibe.id } });\n } catch (err) {\n console.error('Failed to archive vibe:', err);\n }\n };\n\n // Honour the user's preferred IDE + per-Vibe override when opening from\n // the card. Falls back to `vscode://` for IDEs without a registered URL\n // protocol; for richer launching (agent-exec, browser editor) use the\n // VibeOpenInEditorMenu on the Vibe detail page.\n const preferredIdeCommand = useVibeControlsStore((s) => s.preferredIdeCommand);\n const vibeIdeOverrides = useVibeControlsStore((s) => s.vibeIdeOverrides);\n\n const handleOpenInEditor = (vibe: Vibe) => {\n if (!vibe.path) {\n setPageError(tr('vibesPage.openEditorPathMissing', 'No path available to open in editor'));\n pageErrorTimer.current = setTimeout(() => {\n setPageError(null);\n pageErrorTimer.current = null;\n }, 3000);\n return;\n }\n const cmd = resolveEffectiveIdeCommand(vibe.id, preferredIdeCommand, vibeIdeOverrides);\n const preset = getIdePreset(cmd);\n const url = preset ? buildProtocolUrl(preset, vibe.path) : `vscode://file/${vibe.path}`;\n if (url) {\n window.location.href = url;\n } else {\n setPageError(\n tr(\n 'vibesPage.openEditorNoProtocol',\n `${preset?.label ?? cmd} has no URL handler — open this Vibe and use the editor menu.`\n )\n );\n pageErrorTimer.current = setTimeout(() => {\n setPageError(null);\n pageErrorTimer.current = null;\n }, 4000);\n }\n };\n\n const handleCreateVibe = () => {\n setCreateError(null);\n setShowCreateForm(true);\n // Ensure the create-form agent dropdown has options even if the user\n // never opened the page-level agent filter.\n handleAgentFilterInteract();\n };\n\n const toggleVibeSelection = (vibe: Vibe, checked: boolean) => {\n setSelectedVibeIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(vibe.id);\n } else {\n next.delete(vibe.id);\n }\n return next;\n });\n };\n\n const toggleAllVisibleVibes = (checked: boolean) => {\n setSelectedVibeIds((prev) => {\n const next = new Set(prev);\n filteredVibes.forEach((vibe) => {\n if (checked) {\n next.add(vibe.id);\n } else {\n next.delete(vibe.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedVibeIds(new Set());\n\n const handleBulkActivate = async () => {\n if (activatableVibes.length === 0) return;\n\n const results = await Promise.allSettled(\n activatableVibes.map((vibe) =>\n updateVibeStatus({ variables: { id: vibe.id, status: 'ACTIVE' as const } })\n )\n );\n\n const succeeded = results.filter((result) => result.status === 'fulfilled').length;\n const failed = results.length - succeeded;\n\n setSuccessMessage(\n `Activated ${succeeded} vibe${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`\n );\n setTimeout(() => setSuccessMessage(null), 4000);\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n const handleBulkArchive = async () => {\n if (archivableVibes.length === 0) return;\n\n const results = await Promise.allSettled(\n archivableVibes.map((vibe) => archiveVibe({ variables: { id: vibe.id } }))\n );\n\n const succeeded = results.filter((result) => result.status === 'fulfilled').length;\n const failed = results.length - succeeded;\n\n setSuccessMessage(\n `Archived ${succeeded} vibe${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`\n );\n setTimeout(() => setSuccessMessage(null), 4000);\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n const handleSubmitCreateVibe = async () => {\n const name = nameRef.current?.value?.trim();\n const path = pathRef.current?.value?.trim();\n const agentId = agentRef.current?.value;\n const type = typeRef.current?.value;\n\n const errors: { name?: string; path?: string } = {};\n if (!name) errors.name = tr('vibesPage.nameRequired', 'Name is required');\n if (!path) errors.path = tr('vibesPage.pathRequired', 'Path is required');\n if (errors.name || errors.path) {\n setFieldErrors(errors);\n setCreateError(tr('vibesPage.fillRequired', 'Please fill in all required fields'));\n return;\n }\n setFieldErrors({});\n\n // Validate type against allowed enum values\n const validTypes = [\n 'PROJECT',\n 'REPOSITORY',\n 'WORKSPACE',\n 'MONOREPO',\n 'PACKAGE',\n 'CUSTOM',\n ] as const;\n const resolvedType = validTypes.includes(type as (typeof validTypes)[number])\n ? (type as (typeof validTypes)[number])\n : 'PROJECT';\n\n setCreateLoading(true);\n setCreateError(null);\n try {\n const result = await createVibe({\n variables: {\n input: {\n name,\n path,\n type: resolvedType,\n },\n },\n });\n if (!result.data?.createVibe?.id) {\n throw new Error(tr('vibesPage.failedCreate', 'Failed to create vibe'));\n }\n const newVibeId = result.data.createVibe.id;\n // Multi-agent: vibes are created agent-less; attach the optionally\n // selected agent afterward via the attachment mutation (the old\n // `agentId` field on CreateVibeInput was removed server-side).\n if (agentId) {\n await attachAgentToVibe({\n variables: { input: { vibeId: newVibeId, agentId } },\n });\n }\n setShowCreateForm(false);\n setSuccessMessage(tr('vibesPage.createdSuccess', 'Vibe created successfully'));\n await flushTags(newVibeId);\n await refetch();\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowCreateForm(false);\n setCreateError(null);\n setFieldErrors({});\n setShowQuotaExhausted(true);\n return;\n }\n\n setCreateError(\n err instanceof Error ? err.message : tr('vibesPage.failedCreate', 'Failed to create vibe')\n );\n } finally {\n setCreateLoading(false);\n }\n };\n\n // Stats\n const stats = {\n total: vibes.length,\n active: vibes.filter((v) => v.status === 'ACTIVE').length,\n inactive: vibes.filter((v) => v.status === 'INACTIVE').length,\n };\n\n // Collapse guard states into a single MotionPage shell so transitioning\n // between loading / error / data does not unmount+remount and replay the\n // entrance animation. Contents are swapped in-place.\n const showPermissionsLoading = permissionsLoading;\n const showAccessDenied =\n !permissionsLoading &&\n !!permissions &&\n (permissions.permissionStrings?.length ?? 0) > 0 &&\n !canListVibes;\n const showInitialLoading =\n !showPermissionsLoading && !showAccessDenied && loading && vibes.length === 0;\n const showInitialError =\n !showPermissionsLoading && !showAccessDenied && !!error && vibes.length === 0;\n const showDataShell = !showPermissionsLoading && !showAccessDenied && !showInitialError;\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {showPermissionsLoading && (\n <LoadingState message={tr('vibesPage.loadingPermissions', 'Loading permissions...')} />\n )}\n {showAccessDenied && (\n <AccessDenied\n title={tr('vibesPage.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'vibesPage.accessDeniedDescription',\n \"You don't have permission to view vibes. Contact your workspace administrator for access.\"\n )}\n />\n )}\n {showInitialError && (\n <ErrorState\n title={tr('vibesPage.failedLoad', 'Failed to load vibes')}\n message={error?.message ?? ''}\n onRetry={() => refetch()}\n />\n )}\n {showDataShell && (\n <>\n <PageHeader\n title={tr('vibesPage.title', 'Vibes')}\n description={tr('vibesPage.descriptionActive', '{{active}} of {{total}} vibes active', {\n active: stats.active,\n total: stats.total,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('vibesPage.refreshTitle', 'Refresh vibes')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <PermissionButton\n permission={VIBE_PERMISSIONS.VIBE_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tr(\n 'vibesPage.deniedCreateTooltip',\n \"You don't have permission to create vibes\"\n )}\n >\n <button\n onClick={handleCreateVibe}\n className=\"flex items-center gap-2 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 disabled:cursor-not-allowed\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('vibesPage.addVibe', 'Add Vibe')}\n </button>\n </PermissionButton>\n </div>\n }\n />\n\n {/* Filters Bar */}\n <div className=\"space-y-3\">\n {/* Row 1: Search + View Toggle */}\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('vibesPage.searchPlaceholder', 'Search vibes...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('vibesPage.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"w-4 h-4 text-text-secondary\" />\n </button>\n <button\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('vibesPage.listViewTitle', 'List view')}\n >\n <List className=\"w-4 h-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n {/* Row 2: Filters - wrap on all screens */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('vibesPage.filterStatus', 'Status')}\n value={statusFilter}\n options={statusOptions}\n onChange={setStatusFilter}\n />\n <div onMouseDown={handleAgentFilterInteract} onFocus={handleAgentFilterInteract}>\n <FilterDropdown\n label={tr('vibesPage.filterAgent', 'Agent')}\n value={agentFilter}\n options={agentOptions}\n onChange={setAgentFilter}\n />\n </div>\n </div>\n </div>\n\n {/* Results Count */}\n {searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all' ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('vibesPage.showingResults', 'Showing {{filtered}} of {{total}} vibes', {\n filtered: filteredVibes.length,\n total: vibes.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('vibesPage.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n ) : null}\n\n {filteredVibes.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVibes}\n ariaLabel=\"Select all visible vibes\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n Select all visible\n </span>\n </div>\n {selectedVibeIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedVibeIds.size} selected\n </span>\n <button\n onClick={() => setBulkAction('activate')}\n disabled={activatableVibes.length === 0}\n title=\"Mark selected vibes as ACTIVE so they appear in the default vibe list and can run sessions/tunnels via their attached agent(s).\"\n className=\"rounded-md border border-status-success-border px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Play className=\"w-4 h-4\" />\n Activate Selected\n {activatableVibes.length > 0 ? ` (${activatableVibes.length})` : ''}\n </span>\n </button>\n <button\n onClick={() => setBulkAction('archive')}\n disabled={archivableVibes.length === 0}\n className=\"rounded-md border border-status-warning-border px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Archive className=\"w-4 h-4\" />\n Archive Selected\n {archivableVibes.length > 0 ? ` (${archivableVibes.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Vibes Display */}\n {showInitialLoading ? (\n <PageSkeleton variant=\"cards\" count={6} />\n ) : filteredVibes.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n {viewMode === 'grid' ? (\n <MotionList\n key=\"grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredVibes.map((vibe) => (\n <MotionItem key={vibe.id}>\n <VibeCard\n vibe={vibe}\n onOpen={handleVibeSelect}\n onActivate={canUpdateVibe ? handleVibeActivate : undefined}\n onArchive={canUpdateVibe ? handleVibeArchive : undefined}\n onOpenInEditor={handleOpenInEditor}\n selected={selectedVibeIds.has(vibe.id)}\n onToggleSelect={(checked) => toggleVibeSelection(vibe, checked)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <MotionList key=\"list\">\n <VibeList\n vibes={filteredVibes}\n onOpen={handleVibeSelect}\n onActivate={handleVibeActivate}\n onArchive={handleVibeArchive}\n onOpenInEditor={handleOpenInEditor}\n selectedVibeIds={selectedVibeIds}\n onToggleSelect={toggleVibeSelection}\n onToggleAllVisible={toggleAllVisibleVibes}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n />\n </MotionList>\n )}\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Zap className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('vibesPage.emptyNoVibesFound', 'No vibes found')\n : tr('vibesPage.emptyNoVibesConfigured', 'No vibes configured')\n }\n description={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('vibesPage.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'vibesPage.emptyNoVibesDescription',\n 'Add your first vibe to start managing your development projects'\n )\n }\n action={\n !searchQuery &&\n statusFilter === 'all' &&\n typeFilter === 'all' &&\n agentFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n <PermissionGate permission={VIBE_PERMISSIONS.VIBE_CREATE}>\n <button\n onClick={handleCreateVibe}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('vibesPage.addVibe', 'Add Vibe')}\n </button>\n </PermissionGate>\n <GetHelpButton prompt=\"What is a Vibe in VibeControls and how do I create one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n\n <ConfirmDialog\n isOpen={bulkAction === 'activate'}\n onClose={() => setBulkAction(null)}\n onConfirm={handleBulkActivate}\n title=\"Activate selected vibes\"\n message={`Activate ${activatableVibes.length} selected vibe${activatableVibes.length === 1 ? '' : 's'}?`}\n confirmLabel=\"Activate\"\n variant=\"default\"\n />\n\n <ConfirmDialog\n isOpen={bulkAction === 'archive'}\n onClose={() => setBulkAction(null)}\n onConfirm={handleBulkArchive}\n title=\"Archive selected vibes\"\n message={`Archive ${archivableVibes.length} selected vibe${archivableVibes.length === 1 ? '' : 's'}? Archived vibes will be hidden from active workflows.`}\n confirmLabel=\"Archive\"\n variant=\"warning\"\n />\n\n {/* Page-level Error Toast */}\n {pageError && (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-error-bg text-status-error-text rounded-md shadow-lg text-sm\"\n >\n {pageError}\n <button\n onClick={clearPageError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('vibesPage.dismiss', 'Dismiss')}\n </button>\n </div>\n )}\n\n {/* Create Vibe Modal */}\n {showCreateForm && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('vibesPage.addNewVibe', 'Add New Vibe')}\n </h2>\n <div className=\"space-y-4\">\n {createError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {createError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.nameLabel', 'Name *')}\n </label>\n <input\n ref={nameRef}\n type=\"text\"\n placeholder={tr('vibesPage.namePlaceholder', 'My Project')}\n onChange={() =>\n fieldErrors.name && setFieldErrors((e) => ({ ...e, name: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.name ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.name && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.name}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.pathLabel', 'Path *')}\n </label>\n <input\n ref={pathRef}\n type=\"text\"\n placeholder={tr('vibesPage.pathPlaceholder', '/path/to/project')}\n onChange={() =>\n fieldErrors.path && setFieldErrors((e) => ({ ...e, path: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.path ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.path && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.path}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.agentLabel', 'Agent')}\n </label>\n <select\n ref={agentRef}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"\">{tr('vibesPage.selectAgent', 'Select an agent')}</option>\n {agentOptions\n .filter((opt) => opt.value !== 'all')\n .map((agent) => (\n <option key={agent.value} value={agent.value}>\n {agent.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.typeLabel', 'Type')}\n </label>\n <select\n ref={typeRef}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"PROJECT\">{tr('vibesPage.typeProject', 'Project')}</option>\n <option value=\"REPOSITORY\">\n {tr('vibesPage.typeRepository', 'Repository')}\n </option>\n <option value=\"WORKSPACE\">\n {tr('vibesPage.typeWorkspace', 'Workspace')}\n </option>\n <option value=\"MONOREPO\">{tr('vibesPage.typeMonorepo', 'Monorepo')}</option>\n <option value=\"PACKAGE\">{tr('vibesPage.typePackage', 'Package')}</option>\n <option value=\"CUSTOM\">{tr('vibesPage.typeCustom', 'Custom')}</option>\n </select>\n </div>\n </div>\n <div className=\"mt-4 border-t border-border-default pt-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibesPage.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" onDraftChange={setDraftTagIds} />\n </div>\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={() => setShowCreateForm(false)}\n disabled={createLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('vibesPage.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleSubmitCreateVibe}\n disabled={createLoading}\n className=\"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 >\n {createLoading\n ? tr('vibesPage.creating', 'Creating...')\n : tr('vibesPage.createVibe', 'Create Vibe')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Vibe Quota Reached\"\n message=\"You've used all available vibe creation quota on your current plan. Upgrade your plan to create more vibes.\"\n />\n </>\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|