@burdenoff/microfe-vibecontrols 2026.611.1 → 2026.611.3

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.
@@ -0,0 +1,165 @@
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { LoadingState as t } from "../shared/LoadingState.js";
3
+ import { ErrorState as n } from "../shared/ErrorState.js";
4
+ import { EmptyState as r } from "../shared/EmptyState.js";
5
+ import { Card as i } from "../shared/Card.js";
6
+ import "../shared/index.js";
7
+ import { cancelQueueItem as a, fetchQueueItems as o } from "../../services/aiApi.js";
8
+ import { notify as s } from "../../lib/toast.js";
9
+ import { useCallback as c, useEffect as l, useState as u } from "react";
10
+ import { AlertCircle as d, Ban as f, CheckCircle2 as p, Clock as m, ListChecks as h, Loader2 as g, RefreshCw as _, X as v } from "lucide-react";
11
+ import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
12
+ //#region src/components/ai/AIQueueTab.tsx
13
+ var S = {
14
+ pending: {
15
+ label: "Pending",
16
+ cls: "bg-status-warning-bg text-status-warning-text",
17
+ icon: m
18
+ },
19
+ processing: {
20
+ label: "Processing",
21
+ cls: "bg-action-primary-bg/20 text-action-primary-bg",
22
+ icon: g
23
+ },
24
+ completed: {
25
+ label: "Completed",
26
+ cls: "bg-status-success-bg text-status-success-text",
27
+ icon: p
28
+ },
29
+ failed: {
30
+ label: "Failed",
31
+ cls: "bg-status-error-bg text-status-error-text",
32
+ icon: d
33
+ },
34
+ cancelled: {
35
+ label: "Cancelled",
36
+ cls: "bg-bg-sunken text-text-secondary",
37
+ icon: f
38
+ }
39
+ };
40
+ function C({ agent: d, onNavigateToSession: f }) {
41
+ let p = e(), [m, g] = u([]), [C, w] = u(!0), [T, E] = u(null), [D, O] = u(null), k = c(async (e) => {
42
+ if (d.id) {
43
+ e && w(!0);
44
+ try {
45
+ g(await o(d)), E(null);
46
+ } catch (e) {
47
+ E(e.message);
48
+ } finally {
49
+ e && w(!1);
50
+ }
51
+ }
52
+ }, [d]);
53
+ l(() => {
54
+ k(!0);
55
+ let e = window.setInterval(() => void k(!1), 15e3);
56
+ return () => window.clearInterval(e);
57
+ }, [k]);
58
+ let A = async (e) => {
59
+ O(e);
60
+ try {
61
+ await a(d, e), s.success(p("aiQueue.cancelled", "Queued item cancelled")), await k(!1);
62
+ } catch (e) {
63
+ s.error(p("aiQueue.cancelFailed", "Could not cancel"), e.message);
64
+ } finally {
65
+ O(null);
66
+ }
67
+ };
68
+ if (!d.id) return /* @__PURE__ */ b(r, {
69
+ icon: /* @__PURE__ */ b(h, { className: "size-10" }),
70
+ title: p("aiQueue.noAgentTitle", "No active agent"),
71
+ description: p("aiQueue.noAgentDesc", "Start or attach an agent to queue AI prompts for background processing.")
72
+ });
73
+ if (C && m.length === 0) return /* @__PURE__ */ b(t, {});
74
+ if (T && m.length === 0) return /* @__PURE__ */ b(n, {
75
+ message: T,
76
+ onRetry: () => void k(!0)
77
+ });
78
+ let j = m.filter((e) => e.status === "pending" || e.status === "processing").length;
79
+ return /* @__PURE__ */ x("div", {
80
+ className: "space-y-4",
81
+ children: [/* @__PURE__ */ x("div", {
82
+ className: "flex items-center justify-between",
83
+ children: [/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ x("h3", {
84
+ className: "flex items-center gap-2 text-base font-semibold text-text-primary",
85
+ children: [/* @__PURE__ */ b(h, { className: "size-5" }), p("aiQueue.title", "AI Queue")]
86
+ }), /* @__PURE__ */ b("p", {
87
+ className: "text-sm text-text-secondary",
88
+ children: p("aiQueue.subtitle", "Prompts queued for background processing on this agent — queue at night, review in the morning.")
89
+ })] }), /* @__PURE__ */ x("button", {
90
+ type: "button",
91
+ onClick: () => void k(!0),
92
+ className: "flex items-center gap-1.5 rounded-md border border-border-default px-3 py-1.5 text-sm text-text-primary hover:bg-bg-sunken",
93
+ children: [/* @__PURE__ */ b(_, { className: `size-4 ${C ? "animate-spin" : ""}` }), p("common.refresh", "Refresh")]
94
+ })]
95
+ }), m.length === 0 ? /* @__PURE__ */ b(r, {
96
+ icon: /* @__PURE__ */ b(h, { className: "size-10" }),
97
+ title: p("aiQueue.emptyTitle", "Queue is empty"),
98
+ description: p("aiQueue.emptyDesc", "Dispatch a prompt with \"Queue\" instead of \"Run\" to have the agent process it in the background.")
99
+ }) : /* @__PURE__ */ x(y, { children: [j > 0 && /* @__PURE__ */ x("p", {
100
+ className: "text-xs text-text-tertiary",
101
+ children: [
102
+ j,
103
+ " ",
104
+ p("aiQueue.inFlight", "in flight"),
105
+ " · ",
106
+ m.length,
107
+ " ",
108
+ p("aiQueue.total", "total")
109
+ ]
110
+ }), /* @__PURE__ */ b(i, { children: /* @__PURE__ */ b("ul", {
111
+ className: "divide-y divide-border-subtle",
112
+ children: m.map((e) => {
113
+ let t = S[e.status], n = t.icon;
114
+ return /* @__PURE__ */ x("li", {
115
+ className: "flex items-center gap-3 px-4 py-3",
116
+ children: [
117
+ /* @__PURE__ */ x("span", {
118
+ className: `inline-flex shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${t.cls}`,
119
+ children: [/* @__PURE__ */ b(n, { className: `size-3.5 ${e.status === "processing" ? "animate-spin" : ""}` }), t.label]
120
+ }),
121
+ /* @__PURE__ */ x("div", {
122
+ className: "min-w-0 flex-1",
123
+ children: [
124
+ /* @__PURE__ */ b("button", {
125
+ type: "button",
126
+ onClick: () => f?.(e.sessionId),
127
+ className: "truncate text-left font-mono text-sm text-text-primary hover:underline",
128
+ title: p("aiQueue.openSession", "Open the session this prompt runs in"),
129
+ children: e.sessionId
130
+ }),
131
+ /* @__PURE__ */ x("div", {
132
+ className: "text-xs text-text-tertiary",
133
+ children: [
134
+ p("aiQueue.attempt", "attempt"),
135
+ " ",
136
+ e.attempts,
137
+ "/",
138
+ e.maxAttempts,
139
+ e.createdAt ? ` · ${new Date(e.createdAt).toLocaleString()}` : ""
140
+ ]
141
+ }),
142
+ e.lastError && /* @__PURE__ */ b("div", {
143
+ className: "mt-0.5 truncate text-xs text-status-error-text",
144
+ children: e.lastError
145
+ })
146
+ ]
147
+ }),
148
+ e.status === "pending" && /* @__PURE__ */ b("button", {
149
+ type: "button",
150
+ onClick: () => void A(e.id),
151
+ disabled: D === e.id,
152
+ className: "shrink-0 rounded p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-status-error-text disabled:opacity-50",
153
+ "aria-label": p("common.cancel", "Cancel"),
154
+ children: /* @__PURE__ */ b(v, { className: "size-4" })
155
+ })
156
+ ]
157
+ }, e.id);
158
+ })
159
+ }) })] })]
160
+ });
161
+ }
162
+ //#endregion
163
+ export { C as AIQueueTab };
164
+
165
+ //# sourceMappingURL=AIQueueTab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AIQueueTab.js","names":[],"sources":["../../../src/components/ai/AIQueueTab.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport {\n ListChecks,\n RefreshCw,\n X,\n AlertCircle,\n Clock,\n Loader2,\n CheckCircle2,\n Ban,\n} from 'lucide-react';\nimport { Card, LoadingState, ErrorState, EmptyState } from '@/components/shared';\nimport type { AIQueueItem, AgentRef } from '@/services/aiApi';\nimport { fetchQueueItems, cancelQueueItem } from '@/services/aiApi';\nimport { notify } from '@/lib/toast';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AIQueueTabProps {\n agent: AgentRef;\n onNavigateToSession?: (sessionId: string) => void;\n}\n\nconst STATUS_META: Record<\n AIQueueItem['status'],\n { label: string; cls: string; icon: typeof Clock }\n> = {\n pending: { label: 'Pending', cls: 'bg-status-warning-bg text-status-warning-text', icon: Clock },\n processing: {\n label: 'Processing',\n cls: 'bg-action-primary-bg/20 text-action-primary-bg',\n icon: Loader2,\n },\n completed: {\n label: 'Completed',\n cls: 'bg-status-success-bg text-status-success-text',\n icon: CheckCircle2,\n },\n failed: { label: 'Failed', cls: 'bg-status-error-bg text-status-error-text', icon: AlertCircle },\n cancelled: { label: 'Cancelled', cls: 'bg-bg-sunken text-text-secondary', icon: Ban },\n};\n\n/**\n * AIQueueTab — the \"queue overnight, review in the morning\" surface. Lists the queued AI prompts on\n * the active agent (the agent runs them in the background), with per-item status, attempts, and\n * errors, plus cancel for anything still pending. Auto-refreshes so a morning check shows the latest\n * results. Backed by the agent's `/ai/queue` API (fetchQueueItems / cancelQueueItem) — the same\n * governed agent surface the rest of AI mode uses.\n */\nexport function AIQueueTab({ agent, onNavigateToSession }: AIQueueTabProps): React.ReactElement {\n const tr = useTr();\n const [items, setItems] = useState<AIQueueItem[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [cancelling, setCancelling] = useState<string | null>(null);\n\n const load = useCallback(\n async (showSpinner: boolean): Promise<void> => {\n if (!agent.id) return;\n if (showSpinner) setLoading(true);\n try {\n const data = await fetchQueueItems(agent);\n setItems(data);\n setError(null);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n if (showSpinner) setLoading(false);\n }\n },\n [agent]\n );\n\n useEffect(() => {\n void load(true);\n // Poll while items are still in flight so a left-open tab updates itself overnight.\n const timer = window.setInterval(() => void load(false), 15000);\n return () => window.clearInterval(timer);\n }, [load]);\n\n const handleCancel = async (id: string): Promise<void> => {\n setCancelling(id);\n try {\n await cancelQueueItem(agent, id);\n notify.success(tr('aiQueue.cancelled', 'Queued item cancelled'));\n await load(false);\n } catch (err) {\n notify.error(tr('aiQueue.cancelFailed', 'Could not cancel'), (err as Error).message);\n } finally {\n setCancelling(null);\n }\n };\n\n if (!agent.id) {\n return (\n <EmptyState\n icon={<ListChecks className=\"size-10\" />}\n title={tr('aiQueue.noAgentTitle', 'No active agent')}\n description={tr(\n 'aiQueue.noAgentDesc',\n 'Start or attach an agent to queue AI prompts for background processing.'\n )}\n />\n );\n }\n\n if (loading && items.length === 0) return <LoadingState />;\n if (error && items.length === 0) {\n return <ErrorState message={error} onRetry={() => void load(true)} />;\n }\n\n const pendingCount = items.filter(\n (i) => i.status === 'pending' || i.status === 'processing'\n ).length;\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"flex items-center gap-2 text-base font-semibold text-text-primary\">\n <ListChecks className=\"size-5\" />\n {tr('aiQueue.title', 'AI Queue')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'aiQueue.subtitle',\n 'Prompts queued for background processing on this agent — queue at night, review in the morning.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void load(true)}\n className=\"flex items-center gap-1.5 rounded-md border border-border-default px-3 py-1.5 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n {tr('common.refresh', 'Refresh')}\n </button>\n </div>\n\n {items.length === 0 ? (\n <EmptyState\n icon={<ListChecks className=\"size-10\" />}\n title={tr('aiQueue.emptyTitle', 'Queue is empty')}\n description={tr(\n 'aiQueue.emptyDesc',\n 'Dispatch a prompt with \"Queue\" instead of \"Run\" to have the agent process it in the background.'\n )}\n />\n ) : (\n <>\n {pendingCount > 0 && (\n <p className=\"text-xs text-text-tertiary\">\n {pendingCount} {tr('aiQueue.inFlight', 'in flight')} · {items.length}{' '}\n {tr('aiQueue.total', 'total')}\n </p>\n )}\n <Card>\n <ul className=\"divide-y divide-border-subtle\">\n {items.map((item) => {\n const meta = STATUS_META[item.status];\n const StatusIcon = meta.icon;\n return (\n <li key={item.id} className=\"flex items-center gap-3 px-4 py-3\">\n <span\n className={`inline-flex shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${meta.cls}`}\n >\n <StatusIcon\n className={`size-3.5 ${item.status === 'processing' ? 'animate-spin' : ''}`}\n />\n {meta.label}\n </span>\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={() => onNavigateToSession?.(item.sessionId)}\n className=\"truncate text-left font-mono text-sm text-text-primary hover:underline\"\n title={tr('aiQueue.openSession', 'Open the session this prompt runs in')}\n >\n {item.sessionId}\n </button>\n <div className=\"text-xs text-text-tertiary\">\n {tr('aiQueue.attempt', 'attempt')} {item.attempts}/{item.maxAttempts}\n {item.createdAt ? ` · ${new Date(item.createdAt).toLocaleString()}` : ''}\n </div>\n {item.lastError && (\n <div className=\"mt-0.5 truncate text-xs text-status-error-text\">\n {item.lastError}\n </div>\n )}\n </div>\n {item.status === 'pending' && (\n <button\n type=\"button\"\n onClick={() => void handleCancel(item.id)}\n disabled={cancelling === item.id}\n className=\"shrink-0 rounded p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-status-error-text disabled:opacity-50\"\n aria-label={tr('common.cancel', 'Cancel')}\n >\n <X className=\"size-4\" />\n </button>\n )}\n </li>\n );\n })}\n </ul>\n </Card>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAsBA,IAAM,IAGF;CACF,SAAS;EAAE,OAAO;EAAW,KAAK;EAAiD,MAAM;EAAO;CAChG,YAAY;EACV,OAAO;EACP,KAAK;EACL,MAAM;EACP;CACD,WAAW;EACT,OAAO;EACP,KAAK;EACL,MAAM;EACP;CACD,QAAQ;EAAE,OAAO;EAAU,KAAK;EAA6C,MAAM;EAAa;CAChG,WAAW;EAAE,OAAO;EAAa,KAAK;EAAoC,MAAM;EAAK;CACtF;AASD,SAAgB,EAAW,EAAE,UAAO,0BAA4D;CAC9F,IAAM,IAAK,GAAO,EACZ,CAAC,GAAO,KAAY,EAAwB,EAAE,CAAC,EAC/C,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAE3D,IAAO,EACX,OAAO,MAAwC;AACxC,QAAM,IACX;GAAI,KAAa,EAAW,GAAK;AACjC,OAAI;AAGF,IADA,EADa,MAAM,EAAgB,EAAM,CAC3B,EACd,EAAS,KAAK;YACP,GAAK;AACZ,MAAU,EAAc,QAAQ;aACxB;AACR,IAAI,KAAa,EAAW,GAAM;;;IAGtC,CAAC,EAAM,CACR;AAED,SAAgB;AACT,IAAK,GAAK;EAEf,IAAM,IAAQ,OAAO,kBAAkB,KAAK,EAAK,GAAM,EAAE,KAAM;AAC/D,eAAa,OAAO,cAAc,EAAM;IACvC,CAAC,EAAK,CAAC;CAEV,IAAM,IAAe,OAAO,MAA8B;AACxD,IAAc,EAAG;AACjB,MAAI;AAGF,GAFA,MAAM,EAAgB,GAAO,EAAG,EAChC,EAAO,QAAQ,EAAG,qBAAqB,wBAAwB,CAAC,EAChE,MAAM,EAAK,GAAM;WACV,GAAK;AACZ,KAAO,MAAM,EAAG,wBAAwB,mBAAmB,EAAG,EAAc,QAAQ;YAC5E;AACR,KAAc,KAAK;;;AAIvB,KAAI,CAAC,EAAM,GACT,QACE,kBAAC,GAAD;EACE,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;EACxC,OAAO,EAAG,wBAAwB,kBAAkB;EACpD,aAAa,EACX,uBACA,0EACD;EACD,CAAA;AAIN,KAAI,KAAW,EAAM,WAAW,EAAG,QAAO,kBAAC,GAAD,EAAgB,CAAA;AAC1D,KAAI,KAAS,EAAM,WAAW,EAC5B,QAAO,kBAAC,GAAD;EAAY,SAAS;EAAO,eAAe,KAAK,EAAK,GAAK;EAAI,CAAA;CAGvE,IAAM,IAAe,EAAM,QACxB,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,aAC/C,CAAC;AAEF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAAd,CACE,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA,EAChC,EAAG,iBAAiB,WAAW,CAC7B;OACL,kBAAC,KAAD;IAAG,WAAU;cACV,EACC,oBACA,kGACD;IACC,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,KAAK,EAAK,GAAK;IAC9B,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,WAAW,UAAU,IAAU,iBAAiB,MAAQ,CAAA,EAClE,EAAG,kBAAkB,UAAU,CACzB;MACL;MAEL,EAAM,WAAW,IAChB,kBAAC,GAAD;GACE,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;GACxC,OAAO,EAAG,sBAAsB,iBAAiB;GACjD,aAAa,EACX,qBACA,sGACD;GACD,CAAA,GAEF,kBAAA,GAAA,EAAA,UAAA,CACG,IAAe,KACd,kBAAC,KAAD;GAAG,WAAU;aAAb;IACG;IAAa;IAAE,EAAG,oBAAoB,YAAY;IAAC;IAAI,EAAM;IAAQ;IACrE,EAAG,iBAAiB,QAAQ;IAC3B;MAEN,kBAAC,GAAD,EAAA,UACE,kBAAC,MAAD;GAAI,WAAU;aACX,EAAM,KAAK,MAAS;IACnB,IAAM,IAAO,EAAY,EAAK,SACxB,IAAa,EAAK;AACxB,WACE,kBAAC,MAAD;KAAkB,WAAU;eAA5B;MACE,kBAAC,QAAD;OACE,WAAW,wFAAwF,EAAK;iBAD1G,CAGE,kBAAC,GAAD,EACE,WAAW,YAAY,EAAK,WAAW,eAAe,iBAAiB,MACvE,CAAA,EACD,EAAK,MACD;;MACP,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,IAAsB,EAAK,UAAU;SACpD,WAAU;SACV,OAAO,EAAG,uBAAuB,uCAAuC;mBAEvE,EAAK;SACC,CAAA;QACT,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACG,EAAG,mBAAmB,UAAU;UAAC;UAAE,EAAK;UAAS;UAAE,EAAK;UACxD,EAAK,YAAY,MAAM,IAAI,KAAK,EAAK,UAAU,CAAC,gBAAgB,KAAK;UAClE;;QACL,EAAK,aACJ,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAK;SACF,CAAA;QAEJ;;MACL,EAAK,WAAW,aACf,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,KAAK,EAAa,EAAK,GAAG;OACzC,UAAU,MAAe,EAAK;OAC9B,WAAU;OACV,cAAY,EAAG,iBAAiB,SAAS;iBAEzC,kBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;OACjB,CAAA;MAER;OAvCI,EAAK,GAuCT;KAEP;GACC,CAAA,EACA,CAAA,CACN,EAAA,CAAA,CAED"}