@burdenoff/microfe-vibecontrols 2026.727.2 → 2026.730.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.
@@ -1,12 +1,17 @@
1
1
  import { useTr as e } from "../shared/hooks/useTr.js";
2
- import { PLUGIN_CATALOG as t } from "../constants/pluginCatalog.js";
3
- import { useCallback as n, useMemo as r, useState as i } from "react";
4
- import { Check as a, ChevronDown as o, Copy as s, Plug as c, Search as l, X as u } from "lucide-react";
5
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
6
- import { Card as p, IllustratedEmptyState as m, PageContainer as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
7
- import { MotionPage as _ } from "@burdenoff/fe-libs/shared/motion";
2
+ import { QuotaExhaustedDialog as t } from "../components/shared/QuotaExhaustedDialog.js";
3
+ import { useVibeNavigate as n } from "../providers/VibeControlsProvider.js";
4
+ import { useVibeControlsStore as r } from "../store/vibeControlsStore.js";
5
+ import { usePluginAvailability as i } from "../hooks/usePluginAvailability.js";
6
+ import { PLUGIN_CATALOG as a } from "../constants/pluginCatalog.js";
7
+ import { AgentSelector as o } from "../components/shared/AgentSelector.js";
8
+ import { useCallback as s, useMemo as c, useState as l } from "react";
9
+ import { Check as u, ChevronDown as d, Copy as f, Download as p, Loader2 as m, Plug as h, Search as g, X as _ } from "lucide-react";
10
+ import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
11
+ import { Card as x, IllustratedEmptyState as S, PageContainer as C, PagePurpose as w } from "@burdenoff/fe-libs/ui";
12
+ import { MotionPage as T } from "@burdenoff/fe-libs/shared/motion";
8
13
  //#region src/pages/PluginsCatalogPage.tsx
9
- var v = [
14
+ var E = [
10
15
  {
11
16
  value: "all",
12
17
  labelKey: "vibecontrols.plugins.category.all",
@@ -83,107 +88,122 @@ var v = [
83
88
  labelFallback: "Tools"
84
89
  }
85
90
  ];
86
- function y(e) {
91
+ function D(e) {
87
92
  return e.endsWith("-core") ? "Meta" : e.endsWith("-provider") ? "Provider" : "Plugin";
88
93
  }
89
- function b(e) {
94
+ function O(e) {
90
95
  switch (e) {
91
96
  case "Meta": return "bg-accent-purple-subtle text-text-primary border-accent-purple/30";
92
97
  case "Provider": return "bg-status-info-bg text-status-info-text border-status-info-border";
93
98
  default: return "bg-bg-sunken text-text-secondary border-border-default";
94
99
  }
95
100
  }
96
- function x({ text: e, ariaLabel: t }) {
97
- let [r, o] = i(!1);
98
- return /* @__PURE__ */ d("button", {
101
+ function k({ text: e, ariaLabel: t }) {
102
+ let [n, r] = l(!1);
103
+ return /* @__PURE__ */ y("button", {
99
104
  type: "button",
100
- onClick: n(async () => {
105
+ onClick: s(async () => {
101
106
  try {
102
- typeof navigator < "u" && navigator.clipboard && await navigator.clipboard.writeText(e), o(!0), window.setTimeout(() => o(!1), 1500);
107
+ typeof navigator < "u" && navigator.clipboard && await navigator.clipboard.writeText(e), r(!0), window.setTimeout(() => r(!1), 1500);
103
108
  } catch {
104
- o(!1);
109
+ r(!1);
105
110
  }
106
111
  }, [e]),
107
112
  "aria-label": t,
108
113
  className: "inline-flex items-center justify-center size-7 rounded border border-border-default bg-bg-surface text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors flex-shrink-0",
109
- children: d(r ? a : s, { className: "size-3.5" })
114
+ children: y(n ? u : f, { className: "size-3.5" })
110
115
  });
111
116
  }
112
- function S({ plugin: t }) {
113
- let n = e(), r = y(t.category), a = `npm install ${t.packageName}`, s = `import plugin from '${t.packageName}';`, [c, l] = i(!1), [u, m] = i(!1);
114
- return /* @__PURE__ */ d(p, {
117
+ function A({ plugin: t, agentId: n, installed: r, installing: i, installBusy: a, onInstall: o }) {
118
+ let s = e(), c = D(t.category), f = `npm install ${t.packageName}`, h = `import plugin from '${t.packageName}';`, [g, _] = l(!1), [S, C] = l(!1);
119
+ return /* @__PURE__ */ y(x, {
115
120
  className: "flex flex-col h-full p-0 bg-bg-surface border-border-default",
116
- children: /* @__PURE__ */ f("div", {
121
+ children: /* @__PURE__ */ b("div", {
117
122
  className: "flex flex-col gap-3 p-4",
118
123
  children: [
119
- /* @__PURE__ */ f("div", {
124
+ /* @__PURE__ */ b("div", {
120
125
  className: "flex items-start justify-between gap-3",
121
- children: [/* @__PURE__ */ f("div", {
126
+ children: [/* @__PURE__ */ b("div", {
122
127
  className: "min-w-0 flex-1",
123
- children: [/* @__PURE__ */ d("h3", {
128
+ children: [/* @__PURE__ */ y("h3", {
124
129
  className: "text-base font-semibold text-text-primary truncate",
125
130
  children: t.displayName
126
- }), /* @__PURE__ */ d("p", {
131
+ }), /* @__PURE__ */ y("p", {
127
132
  className: "text-xs text-text-secondary font-mono truncate mt-1",
128
133
  children: t.packageName
129
134
  })]
130
- }), /* @__PURE__ */ d("span", {
131
- className: `inline-flex items-center gap-1 px-2 py-0.5 text-[10px] uppercase tracking-wide font-semibold rounded-full border ${b(r)} flex-shrink-0`,
132
- children: r
135
+ }), /* @__PURE__ */ y("span", {
136
+ className: `inline-flex items-center gap-1 px-2 py-0.5 text-[10px] uppercase tracking-wide font-semibold rounded-full border ${O(c)} flex-shrink-0`,
137
+ children: c
133
138
  })]
134
139
  }),
135
- /* @__PURE__ */ d("p", {
140
+ /* @__PURE__ */ y("p", {
136
141
  className: "text-sm text-text-secondary leading-snug",
137
142
  children: t.description
138
143
  }),
139
- /* @__PURE__ */ f("div", {
144
+ r ? /* @__PURE__ */ b("div", {
145
+ className: "flex items-center gap-1.5 rounded-md border border-status-success-border bg-status-success-bg px-2.5 py-1.5 text-xs font-medium text-status-success-text",
146
+ "data-testid": `plugin-installed-${t.packageName}`,
147
+ children: [/* @__PURE__ */ y(u, { className: "size-3.5 flex-shrink-0" }), s("vibecontrols.plugins.installedOnAgent", "Installed on this agent")]
148
+ }) : n ? /* @__PURE__ */ y("button", {
149
+ type: "button",
150
+ onClick: () => o(t.packageName),
151
+ disabled: a,
152
+ className: "inline-flex items-center justify-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
153
+ "data-testid": `plugin-install-${t.packageName}`,
154
+ children: i ? /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(m, { className: "size-3.5 animate-spin" }), s("vibecontrols.plugins.installing", "Installing…")] }) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(p, { className: "size-3.5" }), s("vibecontrols.plugins.installToAgent", "Install to agent")] })
155
+ }) : /* @__PURE__ */ y("p", {
156
+ className: "text-xs text-text-placeholder",
157
+ children: s("vibecontrols.plugins.selectAgentHint", "Select an agent above to install this with one click.")
158
+ }),
159
+ /* @__PURE__ */ b("div", {
140
160
  className: "mt-1 space-y-1.5",
141
- children: [/* @__PURE__ */ d("p", {
161
+ children: [/* @__PURE__ */ y("p", {
142
162
  className: "text-[11px] font-medium uppercase tracking-wide text-text-secondary",
143
- children: n("vibecontrols.plugins.install", "Install")
144
- }), /* @__PURE__ */ f("div", {
163
+ children: s("vibecontrols.plugins.installCli", "Or install via CLI")
164
+ }), /* @__PURE__ */ b("div", {
145
165
  className: "flex items-center gap-2 rounded-md border border-border-default bg-bg-sunken px-2.5 py-1.5",
146
- children: [/* @__PURE__ */ d("code", {
166
+ children: [/* @__PURE__ */ y("code", {
147
167
  className: "flex-1 text-xs font-mono text-text-primary truncate",
148
- children: a
149
- }), /* @__PURE__ */ d(x, {
150
- text: a,
151
- ariaLabel: n("vibecontrols.plugins.copyInstall", "Copy install command for {{name}}", { name: t.displayName })
168
+ children: f
169
+ }), /* @__PURE__ */ y(k, {
170
+ text: f,
171
+ ariaLabel: s("vibecontrols.plugins.copyInstall", "Copy install command for {{name}}", { name: t.displayName })
152
172
  })]
153
173
  })]
154
174
  }),
155
- /* @__PURE__ */ f("div", {
175
+ /* @__PURE__ */ b("div", {
156
176
  className: "space-y-2",
157
- children: [/* @__PURE__ */ f("button", {
177
+ children: [/* @__PURE__ */ b("button", {
158
178
  type: "button",
159
- onClick: () => l((e) => !e),
179
+ onClick: () => _((e) => !e),
160
180
  className: "w-full flex items-center justify-between gap-2 text-left text-xs font-medium text-text-secondary hover:text-text-primary transition-colors",
161
- "aria-expanded": c,
162
- children: [/* @__PURE__ */ d("span", { children: n("vibecontrols.plugins.usage", "Usage") }), /* @__PURE__ */ d(o, { className: `size-3.5 transition-transform ${c ? "rotate-180" : ""}` })]
163
- }), c && /* @__PURE__ */ f("div", {
181
+ "aria-expanded": g,
182
+ children: [/* @__PURE__ */ y("span", { children: s("vibecontrols.plugins.usage", "Usage") }), /* @__PURE__ */ y(d, { className: `size-3.5 transition-transform ${g ? "rotate-180" : ""}` })]
183
+ }), g && /* @__PURE__ */ b("div", {
164
184
  className: "flex items-start gap-2 rounded-md border border-border-default bg-bg-sunken px-2.5 py-2",
165
- children: [/* @__PURE__ */ d("code", {
185
+ children: [/* @__PURE__ */ y("code", {
166
186
  className: "flex-1 text-xs font-mono text-text-primary whitespace-pre-wrap break-all",
167
- children: s
168
- }), /* @__PURE__ */ d(x, {
169
- text: s,
170
- ariaLabel: n("vibecontrols.plugins.copyUsage", "Copy usage snippet for {{name}}", { name: t.displayName })
187
+ children: h
188
+ }), /* @__PURE__ */ y(k, {
189
+ text: h,
190
+ ariaLabel: s("vibecontrols.plugins.copyUsage", "Copy usage snippet for {{name}}", { name: t.displayName })
171
191
  })]
172
192
  })]
173
193
  }),
174
- /* @__PURE__ */ f("div", {
194
+ /* @__PURE__ */ b("div", {
175
195
  className: "space-y-2",
176
- children: [/* @__PURE__ */ f("button", {
196
+ children: [/* @__PURE__ */ b("button", {
177
197
  type: "button",
178
- onClick: () => m((e) => !e),
198
+ onClick: () => C((e) => !e),
179
199
  className: "w-full flex items-center justify-between gap-2 text-left text-xs font-medium text-text-secondary hover:text-text-primary transition-colors",
180
- "aria-expanded": u,
181
- children: [/* @__PURE__ */ d("span", { children: n("vibecontrols.plugins.commands", "Command reference") }), /* @__PURE__ */ d(o, { className: `size-3.5 transition-transform ${u ? "rotate-180" : ""}` })]
182
- }), u && /* @__PURE__ */ d("div", {
200
+ "aria-expanded": S,
201
+ children: [/* @__PURE__ */ y("span", { children: s("vibecontrols.plugins.commands", "Command reference") }), /* @__PURE__ */ y(d, { className: `size-3.5 transition-transform ${S ? "rotate-180" : ""}` })]
202
+ }), S && /* @__PURE__ */ y("div", {
183
203
  className: "rounded-md border border-border-default bg-bg-sunken px-2.5 py-2",
184
- children: /* @__PURE__ */ d("p", {
204
+ children: /* @__PURE__ */ y("p", {
185
205
  className: "text-xs text-text-secondary",
186
- children: n("vibecontrols.plugins.commandsHint", "See the agent admin sub-panel or the plugin README for the documented command list.")
206
+ children: s("vibecontrols.plugins.commandsHint", "See the agent admin sub-panel or the plugin README for the documented command list.")
187
207
  })
188
208
  })]
189
209
  })
@@ -191,94 +211,127 @@ function S({ plugin: t }) {
191
211
  })
192
212
  });
193
213
  }
194
- function C() {
195
- let n = e(), [a, o] = i(""), [s, p] = i("all"), y = r(() => Object.values(t).sort((e, t) => e.displayName.localeCompare(t.displayName)), []), b = r(() => {
196
- let e = a.trim().toLowerCase();
197
- return y.filter((t) => s !== "all" && t.category !== s ? !1 : e ? `${t.displayName} ${t.description} ${t.packageName}`.toLowerCase().includes(e) : !0);
214
+ function j() {
215
+ let u = e(), d = n(), [f, p] = l(""), [m, v] = l("all"), x = c(() => Object.values(a).sort((e, t) => e.displayName.localeCompare(t.displayName)), []), D = c(() => x.map((e) => e.packageName), [x]), O = r((e) => e.activeAgentId), { installed: k, installing: j, install: M, loading: N, error: P, errorIsAgentState: F, quotaExhausted: I, clearQuotaExhausted: L } = i(D, O), R = s((e) => {
216
+ M(e);
217
+ }, [M]), z = c(() => {
218
+ let e = f.trim().toLowerCase();
219
+ return x.filter((t) => m !== "all" && t.category !== m ? !1 : e ? `${t.displayName} ${t.description} ${t.packageName}`.toLowerCase().includes(e) : !0);
198
220
  }, [
199
- y,
200
- a,
201
- s
221
+ x,
222
+ f,
223
+ m
202
224
  ]);
203
- return /* @__PURE__ */ d(h, {
225
+ return /* @__PURE__ */ b(C, {
204
226
  padded: !1,
205
- children: /* @__PURE__ */ f(_, {
227
+ children: [/* @__PURE__ */ b(T, {
206
228
  className: "flex flex-col h-full",
207
- children: [/* @__PURE__ */ f("div", {
229
+ children: [/* @__PURE__ */ b("div", {
208
230
  className: "sticky top-0 z-10 border-b border-border-subtle bg-bg-surface px-6 lg:px-8 pt-5 pb-4 space-y-3",
209
231
  children: [
210
- /* @__PURE__ */ f("div", {
232
+ /* @__PURE__ */ b("div", {
211
233
  className: "flex items-start gap-3",
212
- children: [/* @__PURE__ */ d("div", {
213
- className: "flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)]",
214
- children: /* @__PURE__ */ d(c, { className: "size-5 text-action-primary-bg" })
215
- }), /* @__PURE__ */ f("div", {
216
- className: "min-w-0 flex-1",
217
- children: [/* @__PURE__ */ d("h1", {
218
- className: "text-2xl font-semibold tracking-tight text-text-primary",
219
- children: n("vibecontrols.plugins.title", "Plugins")
220
- }), /* @__PURE__ */ d("p", {
221
- className: "text-sm text-text-secondary mt-0.5",
222
- children: n("vibecontrols.plugins.description", "Browse and install plugins to extend your VibeControls agents.")
223
- })]
224
- })]
234
+ children: [
235
+ /* @__PURE__ */ y("div", {
236
+ className: "flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)]",
237
+ children: /* @__PURE__ */ y(h, { className: "size-5 text-action-primary-bg" })
238
+ }),
239
+ /* @__PURE__ */ b("div", {
240
+ className: "min-w-0 flex-1",
241
+ children: [/* @__PURE__ */ y("h1", {
242
+ className: "text-2xl font-semibold tracking-tight text-text-primary",
243
+ children: u("vibecontrols.plugins.title", "Plugins")
244
+ }), /* @__PURE__ */ y("p", {
245
+ className: "text-sm text-text-secondary mt-0.5",
246
+ children: u("vibecontrols.plugins.description", "Browse and install plugins to extend your VibeControls agents.")
247
+ })]
248
+ }),
249
+ /* @__PURE__ */ y(o, {
250
+ compact: !0,
251
+ className: "w-56 flex-shrink-0"
252
+ })
253
+ ]
254
+ }),
255
+ P && !F && /* @__PURE__ */ y("div", {
256
+ role: "alert",
257
+ className: "flex items-start gap-2 rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text",
258
+ children: /* @__PURE__ */ y("span", { children: P })
225
259
  }),
226
- /* @__PURE__ */ f("div", {
260
+ P && F && /* @__PURE__ */ y("div", {
261
+ className: "flex items-start gap-2 rounded-md border border-status-warning-border bg-status-warning-bg px-3 py-2 text-xs text-status-warning-text",
262
+ children: /* @__PURE__ */ y("span", { children: P })
263
+ }),
264
+ /* @__PURE__ */ b("div", {
227
265
  className: "relative",
228
266
  children: [
229
- /* @__PURE__ */ d(l, { className: "absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary" }),
230
- /* @__PURE__ */ d("input", {
267
+ /* @__PURE__ */ y(g, { className: "absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary" }),
268
+ /* @__PURE__ */ y("input", {
231
269
  type: "search",
232
- value: a,
233
- onChange: (e) => o(e.target.value),
234
- placeholder: n("vibecontrols.plugins.searchPlaceholder", "Search by name, description, or package…"),
235
- "aria-label": n("vibecontrols.plugins.searchLabel", "Search plugins"),
270
+ value: f,
271
+ onChange: (e) => p(e.target.value),
272
+ placeholder: u("vibecontrols.plugins.searchPlaceholder", "Search by name, description, or package…"),
273
+ "aria-label": u("vibecontrols.plugins.searchLabel", "Search plugins"),
236
274
  className: "w-full pl-10 pr-10 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-transparent transition-all"
237
275
  }),
238
- a && /* @__PURE__ */ d("button", {
276
+ f && /* @__PURE__ */ y("button", {
239
277
  type: "button",
240
- onClick: () => o(""),
241
- "aria-label": n("vibecontrols.plugins.clearSearch", "Clear search"),
278
+ onClick: () => p(""),
279
+ "aria-label": u("vibecontrols.plugins.clearSearch", "Clear search"),
242
280
  className: "absolute right-3 top-1/2 -translate-y-1/2 p-0.5 rounded hover:bg-bg-elevated transition-colors",
243
- children: /* @__PURE__ */ d(u, { className: "size-4 text-text-secondary" })
281
+ children: /* @__PURE__ */ y(_, { className: "size-4 text-text-secondary" })
244
282
  })
245
283
  ]
246
284
  }),
247
- /* @__PURE__ */ d("div", {
285
+ /* @__PURE__ */ y("div", {
248
286
  className: "flex flex-wrap gap-2",
249
287
  role: "tablist",
250
- "aria-label": n("vibecontrols.plugins.categories", "Plugin categories"),
251
- children: v.map((e) => {
252
- let t = s === e.value;
253
- return /* @__PURE__ */ d("button", {
288
+ "aria-label": u("vibecontrols.plugins.categories", "Plugin categories"),
289
+ children: E.map((e) => {
290
+ let t = m === e.value;
291
+ return /* @__PURE__ */ y("button", {
254
292
  type: "button",
255
293
  role: "tab",
256
294
  "aria-selected": t,
257
- onClick: () => p(e.value),
295
+ onClick: () => v(e.value),
258
296
  className: `px-3 py-1 text-xs rounded-full border transition-colors ${t ? "bg-action-primary-bg text-action-primary-text border-action-primary-bg" : "bg-bg-surface text-text-secondary border-border-default hover:bg-bg-sunken hover:text-text-primary"}`,
259
- children: n(e.labelKey, e.labelFallback)
297
+ children: u(e.labelKey, e.labelFallback)
260
298
  }, e.value);
261
299
  })
262
300
  })
263
301
  ]
264
- }), /* @__PURE__ */ f("div", {
302
+ }), /* @__PURE__ */ b("div", {
265
303
  className: "flex-1 px-4 sm:px-6 py-4 space-y-4",
266
- children: [/* @__PURE__ */ d(g, {
267
- icon: /* @__PURE__ */ d(c, { className: "size-4" }),
268
- children: n("vibecontrols.plugins.purpose", "Plugins extend what your agents can do — adding capabilities like Git, a GraphQL playground, or session providers. Browse this catalog to find a capability you need, install it onto an agent, and unlock new tabs and actions across VibeControls without writing any glue code yourself.")
269
- }), b.length === 0 ? /* @__PURE__ */ d(m, {
304
+ children: [/* @__PURE__ */ y(w, {
305
+ icon: /* @__PURE__ */ y(h, { className: "size-4" }),
306
+ children: u("vibecontrols.plugins.purpose", "Plugins extend what your agents can do — adding capabilities like Git, a GraphQL playground, or session providers. Browse this catalog to find a capability you need, install it onto an agent, and unlock new tabs and actions across VibeControls without writing any glue code yourself.")
307
+ }), z.length === 0 ? /* @__PURE__ */ y(S, {
270
308
  illustration: "empty-search",
271
- title: n("vibecontrols.plugins.empty.title", "No matching plugins"),
272
- description: n("vibecontrols.plugins.empty.description", "Try a different search term or category.")
273
- }) : /* @__PURE__ */ d("div", {
309
+ title: u("vibecontrols.plugins.empty.title", "No matching plugins"),
310
+ description: u("vibecontrols.plugins.empty.description", "Try a different search term or category.")
311
+ }) : /* @__PURE__ */ y("div", {
274
312
  className: "grid gap-3 grid-cols-1 sm:grid-cols-2 xl:grid-cols-3",
275
- children: b.map((e) => /* @__PURE__ */ d(S, { plugin: e }, e.packageName))
313
+ children: z.map((e) => /* @__PURE__ */ y(A, {
314
+ plugin: e,
315
+ agentId: O,
316
+ installed: !!k[e.packageName],
317
+ installing: j === e.packageName,
318
+ installBusy: N || j !== null,
319
+ onInstall: R
320
+ }, e.packageName))
276
321
  })]
277
322
  })]
278
- })
323
+ }), /* @__PURE__ */ y(t, {
324
+ isOpen: I,
325
+ onClose: L,
326
+ onConfirm: () => {
327
+ L(), d("/billing/plans");
328
+ },
329
+ title: u("vibecontrols.plugins.quotaTitle", "Plugin quota reached"),
330
+ message: u("vibecontrols.plugins.quotaMessage", "You've used all plugin install quota on your current plan. Upgrade to install more plugins.")
331
+ })]
279
332
  });
280
333
  }
281
334
  //#endregion
282
- export { C as PluginsCatalogPage };
335
+ export { j as PluginsCatalogPage };
283
336
 
284
337
  //# sourceMappingURL=PluginsCatalogPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PluginsCatalogPage.js","names":[],"sources":["../../src/pages/PluginsCatalogPage.tsx"],"sourcesContent":["import { useMemo, useState, useCallback } from 'react';\nimport { Check, ChevronDown, Copy, Plug, Search, X } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { PageContainer, Card, PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { PLUGIN_CATALOG, type PluginMeta } from '@/constants/pluginCatalog';\nimport { useTr } from '@/shared/hooks/useTr';\n\ntype PluginType = 'Meta' | 'Provider' | 'Plugin';\n\ninterface CategoryOption {\n value: string;\n labelKey: string;\n labelFallback: string;\n}\n\nconst CATEGORY_OPTIONS: CategoryOption[] = [\n { value: 'all', labelKey: 'vibecontrols.plugins.category.all', labelFallback: 'All' },\n { value: 'ai-core', labelKey: 'vibecontrols.plugins.category.aiCore', labelFallback: 'AI Core' },\n {\n value: 'ai-provider',\n labelKey: 'vibecontrols.plugins.category.aiProvider',\n labelFallback: 'AI Providers',\n },\n {\n value: 'plan-core',\n labelKey: 'vibecontrols.plugins.category.planCore',\n labelFallback: 'Plan Core',\n },\n {\n value: 'plan-provider',\n labelKey: 'vibecontrols.plugins.category.planProvider',\n labelFallback: 'Plan Providers',\n },\n {\n value: 'session-core',\n labelKey: 'vibecontrols.plugins.category.sessionCore',\n labelFallback: 'Session Core',\n },\n {\n value: 'session-provider',\n labelKey: 'vibecontrols.plugins.category.sessionProvider',\n labelFallback: 'Session Providers',\n },\n { value: 'tunnel', labelKey: 'vibecontrols.plugins.category.tunnel', labelFallback: 'Tunnel' },\n { value: 'vcs', labelKey: 'vibecontrols.plugins.category.vcs', labelFallback: 'VCS' },\n {\n value: 'api-playground',\n labelKey: 'vibecontrols.plugins.category.apiPlayground',\n labelFallback: 'API Playground',\n },\n {\n value: 'gitops-core',\n labelKey: 'vibecontrols.plugins.category.gitopsCore',\n labelFallback: 'GitOps Core',\n },\n {\n value: 'gitops-provider',\n labelKey: 'vibecontrols.plugins.category.gitopsProvider',\n labelFallback: 'GitOps Providers',\n },\n {\n value: 'security-core',\n labelKey: 'vibecontrols.plugins.category.securityCore',\n labelFallback: 'Security Core',\n },\n {\n value: 'security-provider',\n labelKey: 'vibecontrols.plugins.category.securityProvider',\n labelFallback: 'Security Providers',\n },\n { value: 'tool', labelKey: 'vibecontrols.plugins.category.tool', labelFallback: 'Tools' },\n];\n\nfunction derivePluginType(category: PluginMeta['category']): PluginType {\n if (category.endsWith('-core')) return 'Meta';\n if (category.endsWith('-provider')) return 'Provider';\n return 'Plugin';\n}\n\nfunction typeBadgeClasses(type: PluginType): string {\n switch (type) {\n case 'Meta':\n return 'bg-accent-purple-subtle text-text-primary border-accent-purple/30';\n case 'Provider':\n return 'bg-status-info-bg text-status-info-text border-status-info-border';\n default:\n return 'bg-bg-sunken text-text-secondary border-border-default';\n }\n}\n\ninterface CopyButtonProps {\n text: string;\n ariaLabel: string;\n}\n\nfunction CopyButton({ text, ariaLabel }: CopyButtonProps) {\n const [copied, setCopied] = useState(false);\n const handleCopy = useCallback(async () => {\n try {\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n await navigator.clipboard.writeText(text);\n }\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n } catch {\n setCopied(false);\n }\n }, [text]);\n\n return (\n <button\n type=\"button\"\n onClick={handleCopy}\n aria-label={ariaLabel}\n className=\"inline-flex items-center justify-center size-7 rounded border border-border-default bg-bg-surface text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors flex-shrink-0\"\n >\n {copied ? <Check className=\"size-3.5\" /> : <Copy className=\"size-3.5\" />}\n </button>\n );\n}\n\ninterface PluginCardProps {\n plugin: PluginMeta;\n}\n\nfunction PluginCard({ plugin }: PluginCardProps) {\n const tr = useTr();\n const type = derivePluginType(plugin.category);\n const installCommand = `npm install ${plugin.packageName}`;\n const usageSnippet = `import plugin from '${plugin.packageName}';`;\n\n const [showUsage, setShowUsage] = useState(false);\n const [showCommands, setShowCommands] = useState(false);\n\n return (\n <Card className=\"flex flex-col h-full p-0 bg-bg-surface border-border-default\">\n <div className=\"flex flex-col gap-3 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary truncate\">\n {plugin.displayName}\n </h3>\n <p className=\"text-xs text-text-secondary font-mono truncate mt-1\">\n {plugin.packageName}\n </p>\n </div>\n <span\n className={`inline-flex items-center gap-1 px-2 py-0.5 text-[10px] uppercase tracking-wide font-semibold rounded-full border ${typeBadgeClasses(type)} flex-shrink-0`}\n >\n {type}\n </span>\n </div>\n\n <p className=\"text-sm text-text-secondary leading-snug\">{plugin.description}</p>\n\n <div className=\"mt-1 space-y-1.5\">\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-text-secondary\">\n {tr('vibecontrols.plugins.install', 'Install')}\n </p>\n <div className=\"flex items-center gap-2 rounded-md border border-border-default bg-bg-sunken px-2.5 py-1.5\">\n <code className=\"flex-1 text-xs font-mono text-text-primary truncate\">\n {installCommand}\n </code>\n <CopyButton\n text={installCommand}\n ariaLabel={tr(\n 'vibecontrols.plugins.copyInstall',\n 'Copy install command for {{name}}',\n {\n name: plugin.displayName,\n }\n )}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <button\n type=\"button\"\n onClick={() => setShowUsage((value) => !value)}\n className=\"w-full flex items-center justify-between gap-2 text-left text-xs font-medium text-text-secondary hover:text-text-primary transition-colors\"\n aria-expanded={showUsage}\n >\n <span>{tr('vibecontrols.plugins.usage', 'Usage')}</span>\n <ChevronDown\n className={`size-3.5 transition-transform ${showUsage ? 'rotate-180' : ''}`}\n />\n </button>\n {showUsage && (\n <div className=\"flex items-start gap-2 rounded-md border border-border-default bg-bg-sunken px-2.5 py-2\">\n <code className=\"flex-1 text-xs font-mono text-text-primary whitespace-pre-wrap break-all\">\n {usageSnippet}\n </code>\n <CopyButton\n text={usageSnippet}\n ariaLabel={tr('vibecontrols.plugins.copyUsage', 'Copy usage snippet for {{name}}', {\n name: plugin.displayName,\n })}\n />\n </div>\n )}\n </div>\n\n <div className=\"space-y-2\">\n <button\n type=\"button\"\n onClick={() => setShowCommands((value) => !value)}\n className=\"w-full flex items-center justify-between gap-2 text-left text-xs font-medium text-text-secondary hover:text-text-primary transition-colors\"\n aria-expanded={showCommands}\n >\n <span>{tr('vibecontrols.plugins.commands', 'Command reference')}</span>\n <ChevronDown\n className={`size-3.5 transition-transform ${showCommands ? 'rotate-180' : ''}`}\n />\n </button>\n {showCommands && (\n <div className=\"rounded-md border border-border-default bg-bg-sunken px-2.5 py-2\">\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'vibecontrols.plugins.commandsHint',\n 'See the agent admin sub-panel or the plugin README for the documented command list.'\n )}\n </p>\n </div>\n )}\n </div>\n </div>\n </Card>\n );\n}\n\nexport function PluginsCatalogPage() {\n const tr = useTr();\n const [search, setSearch] = useState('');\n const [activeCategory, setActiveCategory] = useState<string>('all');\n\n const pluginList = useMemo(() => {\n return Object.values(PLUGIN_CATALOG).sort((a, b) => a.displayName.localeCompare(b.displayName));\n }, []);\n\n const filteredPlugins = useMemo(() => {\n const term = search.trim().toLowerCase();\n return pluginList.filter((plugin) => {\n if (activeCategory !== 'all' && plugin.category !== activeCategory) return false;\n if (!term) return true;\n const haystack =\n `${plugin.displayName} ${plugin.description} ${plugin.packageName}`.toLowerCase();\n return haystack.includes(term);\n });\n }, [pluginList, search, activeCategory]);\n\n return (\n <PageContainer padded={false}>\n <MotionPage className=\"flex flex-col h-full\">\n {/* Sticky header with search + filter chips */}\n <div className=\"sticky top-0 z-10 border-b border-border-subtle bg-bg-surface px-6 lg:px-8 pt-5 pb-4 space-y-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)]\">\n <Plug className=\"size-5 text-action-primary-bg\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"text-2xl font-semibold tracking-tight text-text-primary\">\n {tr('vibecontrols.plugins.title', 'Plugins')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {tr(\n 'vibecontrols.plugins.description',\n 'Browse and install plugins to extend your VibeControls agents.'\n )}\n </p>\n </div>\n </div>\n\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n type=\"search\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder={tr(\n 'vibecontrols.plugins.searchPlaceholder',\n 'Search by name, description, or package…'\n )}\n aria-label={tr('vibecontrols.plugins.searchLabel', 'Search plugins')}\n className=\"w-full pl-10 pr-10 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-transparent transition-all\"\n />\n {search && (\n <button\n type=\"button\"\n onClick={() => setSearch('')}\n aria-label={tr('vibecontrols.plugins.clearSearch', 'Clear search')}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 p-0.5 rounded hover:bg-bg-elevated transition-colors\"\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n )}\n </div>\n\n <div\n className=\"flex flex-wrap gap-2\"\n role=\"tablist\"\n aria-label={tr('vibecontrols.plugins.categories', 'Plugin categories')}\n >\n {CATEGORY_OPTIONS.map((option) => {\n const isActive = activeCategory === option.value;\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setActiveCategory(option.value)}\n className={`px-3 py-1 text-xs rounded-full border transition-colors ${\n isActive\n ? 'bg-action-primary-bg text-action-primary-text border-action-primary-bg'\n : 'bg-bg-surface text-text-secondary border-border-default hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tr(option.labelKey, option.labelFallback)}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Grid body */}\n <div className=\"flex-1 px-4 sm:px-6 py-4 space-y-4\">\n <PagePurpose icon={<Plug className=\"size-4\" />}>\n {tr(\n 'vibecontrols.plugins.purpose',\n 'Plugins extend what your agents can do — adding capabilities like Git, a GraphQL playground, or session providers. Browse this catalog to find a capability you need, install it onto an agent, and unlock new tabs and actions across VibeControls without writing any glue code yourself.'\n )}\n </PagePurpose>\n {filteredPlugins.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr('vibecontrols.plugins.empty.title', 'No matching plugins')}\n description={tr(\n 'vibecontrols.plugins.empty.description',\n 'Try a different search term or category.'\n )}\n />\n ) : (\n <div className=\"grid gap-3 grid-cols-1 sm:grid-cols-2 xl:grid-cols-3\">\n {filteredPlugins.map((plugin) => (\n <PluginCard key={plugin.packageName} plugin={plugin} />\n ))}\n </div>\n )}\n </div>\n </MotionPage>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PluginsCatalogPage.js","names":[],"sources":["../../src/pages/PluginsCatalogPage.tsx"],"sourcesContent":["import { useMemo, useState, useCallback } from 'react';\nimport { Check, ChevronDown, Copy, Download, Loader2, Plug, Search, X } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { PageContainer, Card, PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { PLUGIN_CATALOG, type PluginMeta } from '@/constants/pluginCatalog';\nimport { AgentSelector } from '@/components/shared/AgentSelector';\nimport { QuotaExhaustedDialog } from '@/components/shared/QuotaExhaustedDialog';\nimport { usePluginAvailability } from '@/hooks/usePluginAvailability';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useTr } from '@/shared/hooks/useTr';\n\ntype PluginType = 'Meta' | 'Provider' | 'Plugin';\n\ninterface CategoryOption {\n value: string;\n labelKey: string;\n labelFallback: string;\n}\n\nconst CATEGORY_OPTIONS: CategoryOption[] = [\n { value: 'all', labelKey: 'vibecontrols.plugins.category.all', labelFallback: 'All' },\n { value: 'ai-core', labelKey: 'vibecontrols.plugins.category.aiCore', labelFallback: 'AI Core' },\n {\n value: 'ai-provider',\n labelKey: 'vibecontrols.plugins.category.aiProvider',\n labelFallback: 'AI Providers',\n },\n {\n value: 'plan-core',\n labelKey: 'vibecontrols.plugins.category.planCore',\n labelFallback: 'Plan Core',\n },\n {\n value: 'plan-provider',\n labelKey: 'vibecontrols.plugins.category.planProvider',\n labelFallback: 'Plan Providers',\n },\n {\n value: 'session-core',\n labelKey: 'vibecontrols.plugins.category.sessionCore',\n labelFallback: 'Session Core',\n },\n {\n value: 'session-provider',\n labelKey: 'vibecontrols.plugins.category.sessionProvider',\n labelFallback: 'Session Providers',\n },\n { value: 'tunnel', labelKey: 'vibecontrols.plugins.category.tunnel', labelFallback: 'Tunnel' },\n { value: 'vcs', labelKey: 'vibecontrols.plugins.category.vcs', labelFallback: 'VCS' },\n {\n value: 'api-playground',\n labelKey: 'vibecontrols.plugins.category.apiPlayground',\n labelFallback: 'API Playground',\n },\n {\n value: 'gitops-core',\n labelKey: 'vibecontrols.plugins.category.gitopsCore',\n labelFallback: 'GitOps Core',\n },\n {\n value: 'gitops-provider',\n labelKey: 'vibecontrols.plugins.category.gitopsProvider',\n labelFallback: 'GitOps Providers',\n },\n {\n value: 'security-core',\n labelKey: 'vibecontrols.plugins.category.securityCore',\n labelFallback: 'Security Core',\n },\n {\n value: 'security-provider',\n labelKey: 'vibecontrols.plugins.category.securityProvider',\n labelFallback: 'Security Providers',\n },\n { value: 'tool', labelKey: 'vibecontrols.plugins.category.tool', labelFallback: 'Tools' },\n];\n\nfunction derivePluginType(category: PluginMeta['category']): PluginType {\n if (category.endsWith('-core')) return 'Meta';\n if (category.endsWith('-provider')) return 'Provider';\n return 'Plugin';\n}\n\nfunction typeBadgeClasses(type: PluginType): string {\n switch (type) {\n case 'Meta':\n return 'bg-accent-purple-subtle text-text-primary border-accent-purple/30';\n case 'Provider':\n return 'bg-status-info-bg text-status-info-text border-status-info-border';\n default:\n return 'bg-bg-sunken text-text-secondary border-border-default';\n }\n}\n\ninterface CopyButtonProps {\n text: string;\n ariaLabel: string;\n}\n\nfunction CopyButton({ text, ariaLabel }: CopyButtonProps) {\n const [copied, setCopied] = useState(false);\n const handleCopy = useCallback(async () => {\n try {\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n await navigator.clipboard.writeText(text);\n }\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n } catch {\n setCopied(false);\n }\n }, [text]);\n\n return (\n <button\n type=\"button\"\n onClick={handleCopy}\n aria-label={ariaLabel}\n className=\"inline-flex items-center justify-center size-7 rounded border border-border-default bg-bg-surface text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors flex-shrink-0\"\n >\n {copied ? <Check className=\"size-3.5\" /> : <Copy className=\"size-3.5\" />}\n </button>\n );\n}\n\ninterface PluginCardProps {\n plugin: PluginMeta;\n /** Active agent id, or null if none selected — gates the one-click install action. */\n agentId: string | null;\n /** True once the agent already reports this package installed. */\n installed: boolean;\n /** True while THIS package's install mutation is in flight. */\n installing: boolean;\n /** True while any package's install mutation is in flight (disables all buttons). */\n installBusy: boolean;\n onInstall: (packageName: string) => void;\n}\n\nfunction PluginCard({\n plugin,\n agentId,\n installed,\n installing,\n installBusy,\n onInstall,\n}: PluginCardProps) {\n const tr = useTr();\n const type = derivePluginType(plugin.category);\n const installCommand = `npm install ${plugin.packageName}`;\n const usageSnippet = `import plugin from '${plugin.packageName}';`;\n\n const [showUsage, setShowUsage] = useState(false);\n const [showCommands, setShowCommands] = useState(false);\n\n return (\n <Card className=\"flex flex-col h-full p-0 bg-bg-surface border-border-default\">\n <div className=\"flex flex-col gap-3 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary truncate\">\n {plugin.displayName}\n </h3>\n <p className=\"text-xs text-text-secondary font-mono truncate mt-1\">\n {plugin.packageName}\n </p>\n </div>\n <span\n className={`inline-flex items-center gap-1 px-2 py-0.5 text-[10px] uppercase tracking-wide font-semibold rounded-full border ${typeBadgeClasses(type)} flex-shrink-0`}\n >\n {type}\n </span>\n </div>\n\n <p className=\"text-sm text-text-secondary leading-snug\">{plugin.description}</p>\n\n {installed ? (\n <div\n className=\"flex items-center gap-1.5 rounded-md border border-status-success-border bg-status-success-bg px-2.5 py-1.5 text-xs font-medium text-status-success-text\"\n data-testid={`plugin-installed-${plugin.packageName}`}\n >\n <Check className=\"size-3.5 flex-shrink-0\" />\n {tr('vibecontrols.plugins.installedOnAgent', 'Installed on this agent')}\n </div>\n ) : agentId ? (\n <button\n type=\"button\"\n onClick={() => onInstall(plugin.packageName)}\n disabled={installBusy}\n className=\"inline-flex items-center justify-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n data-testid={`plugin-install-${plugin.packageName}`}\n >\n {installing ? (\n <>\n <Loader2 className=\"size-3.5 animate-spin\" />\n {tr('vibecontrols.plugins.installing', 'Installing…')}\n </>\n ) : (\n <>\n <Download className=\"size-3.5\" />\n {tr('vibecontrols.plugins.installToAgent', 'Install to agent')}\n </>\n )}\n </button>\n ) : (\n <p className=\"text-xs text-text-placeholder\">\n {tr(\n 'vibecontrols.plugins.selectAgentHint',\n 'Select an agent above to install this with one click.'\n )}\n </p>\n )}\n\n <div className=\"mt-1 space-y-1.5\">\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-text-secondary\">\n {tr('vibecontrols.plugins.installCli', 'Or install via CLI')}\n </p>\n <div className=\"flex items-center gap-2 rounded-md border border-border-default bg-bg-sunken px-2.5 py-1.5\">\n <code className=\"flex-1 text-xs font-mono text-text-primary truncate\">\n {installCommand}\n </code>\n <CopyButton\n text={installCommand}\n ariaLabel={tr(\n 'vibecontrols.plugins.copyInstall',\n 'Copy install command for {{name}}',\n {\n name: plugin.displayName,\n }\n )}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <button\n type=\"button\"\n onClick={() => setShowUsage((value) => !value)}\n className=\"w-full flex items-center justify-between gap-2 text-left text-xs font-medium text-text-secondary hover:text-text-primary transition-colors\"\n aria-expanded={showUsage}\n >\n <span>{tr('vibecontrols.plugins.usage', 'Usage')}</span>\n <ChevronDown\n className={`size-3.5 transition-transform ${showUsage ? 'rotate-180' : ''}`}\n />\n </button>\n {showUsage && (\n <div className=\"flex items-start gap-2 rounded-md border border-border-default bg-bg-sunken px-2.5 py-2\">\n <code className=\"flex-1 text-xs font-mono text-text-primary whitespace-pre-wrap break-all\">\n {usageSnippet}\n </code>\n <CopyButton\n text={usageSnippet}\n ariaLabel={tr('vibecontrols.plugins.copyUsage', 'Copy usage snippet for {{name}}', {\n name: plugin.displayName,\n })}\n />\n </div>\n )}\n </div>\n\n <div className=\"space-y-2\">\n <button\n type=\"button\"\n onClick={() => setShowCommands((value) => !value)}\n className=\"w-full flex items-center justify-between gap-2 text-left text-xs font-medium text-text-secondary hover:text-text-primary transition-colors\"\n aria-expanded={showCommands}\n >\n <span>{tr('vibecontrols.plugins.commands', 'Command reference')}</span>\n <ChevronDown\n className={`size-3.5 transition-transform ${showCommands ? 'rotate-180' : ''}`}\n />\n </button>\n {showCommands && (\n <div className=\"rounded-md border border-border-default bg-bg-sunken px-2.5 py-2\">\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'vibecontrols.plugins.commandsHint',\n 'See the agent admin sub-panel or the plugin README for the documented command list.'\n )}\n </p>\n </div>\n )}\n </div>\n </div>\n </Card>\n );\n}\n\nexport function PluginsCatalogPage() {\n const tr = useTr();\n const navigate = useVibeNavigate();\n const [search, setSearch] = useState('');\n const [activeCategory, setActiveCategory] = useState<string>('all');\n\n const pluginList = useMemo(() => {\n return Object.values(PLUGIN_CATALOG).sort((a, b) => a.displayName.localeCompare(b.displayName));\n }, []);\n\n const packageNames = useMemo(() => pluginList.map((p) => p.packageName), [pluginList]);\n\n const activeAgentId = useVibeControlsStore((s) => s.activeAgentId);\n\n // Real one-click install for the catalog (BOFF-4263) — the same\n // agentPlugins/installAgentPlugin machinery PluginGate/AddTabDialog already\n // use elsewhere, wired up here so browsing the catalog isn't copy-paste-only.\n const {\n installed,\n installing,\n install,\n loading: installLoading,\n error: installError,\n errorIsAgentState,\n quotaExhausted,\n clearQuotaExhausted,\n } = usePluginAvailability(packageNames, activeAgentId);\n\n const handleInstall = useCallback(\n (packageName: string) => {\n void install(packageName);\n },\n [install]\n );\n\n const filteredPlugins = useMemo(() => {\n const term = search.trim().toLowerCase();\n return pluginList.filter((plugin) => {\n if (activeCategory !== 'all' && plugin.category !== activeCategory) return false;\n if (!term) return true;\n const haystack =\n `${plugin.displayName} ${plugin.description} ${plugin.packageName}`.toLowerCase();\n return haystack.includes(term);\n });\n }, [pluginList, search, activeCategory]);\n\n return (\n <PageContainer padded={false}>\n <MotionPage className=\"flex flex-col h-full\">\n {/* Sticky header with search + filter chips */}\n <div className=\"sticky top-0 z-10 border-b border-border-subtle bg-bg-surface px-6 lg:px-8 pt-5 pb-4 space-y-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)]\">\n <Plug className=\"size-5 text-action-primary-bg\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"text-2xl font-semibold tracking-tight text-text-primary\">\n {tr('vibecontrols.plugins.title', 'Plugins')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {tr(\n 'vibecontrols.plugins.description',\n 'Browse and install plugins to extend your VibeControls agents.'\n )}\n </p>\n </div>\n <AgentSelector compact className=\"w-56 flex-shrink-0\" />\n </div>\n\n {installError && !errorIsAgentState && (\n <div\n role=\"alert\"\n className=\"flex items-start gap-2 rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text\"\n >\n <span>{installError}</span>\n </div>\n )}\n {installError && errorIsAgentState && (\n <div className=\"flex items-start gap-2 rounded-md border border-status-warning-border bg-status-warning-bg px-3 py-2 text-xs text-status-warning-text\">\n <span>{installError}</span>\n </div>\n )}\n\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n type=\"search\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder={tr(\n 'vibecontrols.plugins.searchPlaceholder',\n 'Search by name, description, or package…'\n )}\n aria-label={tr('vibecontrols.plugins.searchLabel', 'Search plugins')}\n className=\"w-full pl-10 pr-10 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-transparent transition-all\"\n />\n {search && (\n <button\n type=\"button\"\n onClick={() => setSearch('')}\n aria-label={tr('vibecontrols.plugins.clearSearch', 'Clear search')}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 p-0.5 rounded hover:bg-bg-elevated transition-colors\"\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n )}\n </div>\n\n <div\n className=\"flex flex-wrap gap-2\"\n role=\"tablist\"\n aria-label={tr('vibecontrols.plugins.categories', 'Plugin categories')}\n >\n {CATEGORY_OPTIONS.map((option) => {\n const isActive = activeCategory === option.value;\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setActiveCategory(option.value)}\n className={`px-3 py-1 text-xs rounded-full border transition-colors ${\n isActive\n ? 'bg-action-primary-bg text-action-primary-text border-action-primary-bg'\n : 'bg-bg-surface text-text-secondary border-border-default hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tr(option.labelKey, option.labelFallback)}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Grid body */}\n <div className=\"flex-1 px-4 sm:px-6 py-4 space-y-4\">\n <PagePurpose icon={<Plug className=\"size-4\" />}>\n {tr(\n 'vibecontrols.plugins.purpose',\n 'Plugins extend what your agents can do — adding capabilities like Git, a GraphQL playground, or session providers. Browse this catalog to find a capability you need, install it onto an agent, and unlock new tabs and actions across VibeControls without writing any glue code yourself.'\n )}\n </PagePurpose>\n {filteredPlugins.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr('vibecontrols.plugins.empty.title', 'No matching plugins')}\n description={tr(\n 'vibecontrols.plugins.empty.description',\n 'Try a different search term or category.'\n )}\n />\n ) : (\n <div className=\"grid gap-3 grid-cols-1 sm:grid-cols-2 xl:grid-cols-3\">\n {filteredPlugins.map((plugin) => (\n <PluginCard\n key={plugin.packageName}\n plugin={plugin}\n agentId={activeAgentId}\n installed={!!installed[plugin.packageName]}\n installing={installing === plugin.packageName}\n installBusy={installLoading || installing !== null}\n onInstall={handleInstall}\n />\n ))}\n </div>\n )}\n </div>\n </MotionPage>\n\n <QuotaExhaustedDialog\n isOpen={quotaExhausted}\n onClose={clearQuotaExhausted}\n onConfirm={() => {\n clearQuotaExhausted();\n navigate('/billing/plans');\n }}\n title={tr('vibecontrols.plugins.quotaTitle', 'Plugin quota reached')}\n message={tr(\n 'vibecontrols.plugins.quotaMessage',\n \"You've used all plugin install quota on your current plan. Upgrade to install more plugins.\"\n )}\n />\n </PageContainer>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-vibecontrols",
3
- "version": "2026.727.2",
3
+ "version": "2026.730.1",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [