@burdenoff/microfe-vibecontrols 2026.529.10 → 2026.529.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/agents/ConnectionLogPanel.js +206 -0
- package/dist/components/agents/ConnectionLogPanel.js.map +1 -0
- package/dist/components/vibedeck/VibeDeckGrid.js +40 -39
- package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
- package/dist/generated/wspace-operations.js +804 -766
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/dist/index.js +10 -10
- package/dist/pages/AgentDetailsPage.js +813 -784
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/VibeCalendarPage.js +145 -140
- package/dist/pages/VibeCalendarPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +2 -1
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { useGetAgentConnectionEventsQuery as e } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useCallback as t, useMemo as n, useState as r } from "react";
|
|
3
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/agents/ConnectionLogPanel.tsx
|
|
5
|
+
var o = {
|
|
6
|
+
info: "text-status-info-text",
|
|
7
|
+
warn: "text-status-warning-text",
|
|
8
|
+
error: "text-status-danger-text",
|
|
9
|
+
debug: "text-fg-muted"
|
|
10
|
+
}, s = {
|
|
11
|
+
info: "bg-status-info-bg",
|
|
12
|
+
warn: "bg-status-warning-bg",
|
|
13
|
+
error: "bg-status-danger-bg",
|
|
14
|
+
debug: "bg-bg-muted"
|
|
15
|
+
};
|
|
16
|
+
function c(e) {
|
|
17
|
+
try {
|
|
18
|
+
return new Date(e).toISOString().replace("T", " ").replace("Z", "");
|
|
19
|
+
} catch {
|
|
20
|
+
return e;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function l(e) {
|
|
24
|
+
return e.map((e) => {
|
|
25
|
+
let t = e.detailsJson ? `\n details: ${e.detailsJson}` : "";
|
|
26
|
+
return `[${e.timestamp}] #${e.sequence} ${e.level.toUpperCase().padEnd(5)} ${e.step.padEnd(28)} ${e.message}${t}`;
|
|
27
|
+
}).join("\n");
|
|
28
|
+
}
|
|
29
|
+
function u(e, t, n) {
|
|
30
|
+
return JSON.stringify({
|
|
31
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32
|
+
agentId: t,
|
|
33
|
+
agentName: n,
|
|
34
|
+
count: e.length,
|
|
35
|
+
events: e.map((e) => ({
|
|
36
|
+
id: e.id,
|
|
37
|
+
timestamp: e.timestamp,
|
|
38
|
+
sequence: e.sequence,
|
|
39
|
+
step: e.step,
|
|
40
|
+
level: e.level,
|
|
41
|
+
message: e.message,
|
|
42
|
+
details: e.detailsJson ? JSON.parse(e.detailsJson) : null
|
|
43
|
+
}))
|
|
44
|
+
}, null, 2);
|
|
45
|
+
}
|
|
46
|
+
function d(e, t, n) {
|
|
47
|
+
let r = new Blob([n], { type: t }), i = URL.createObjectURL(r), a = document.createElement("a");
|
|
48
|
+
a.href = i, a.download = e, document.body.appendChild(a), a.click(), document.body.removeChild(a), URL.revokeObjectURL(i);
|
|
49
|
+
}
|
|
50
|
+
function f({ agentId: f, agentName: p }) {
|
|
51
|
+
let { data: m, loading: h, error: g, refetch: _ } = e({
|
|
52
|
+
variables: {
|
|
53
|
+
agentId: f,
|
|
54
|
+
limit: 200
|
|
55
|
+
},
|
|
56
|
+
pollInterval: 3e3,
|
|
57
|
+
fetchPolicy: "cache-and-network"
|
|
58
|
+
}), [v, y] = r("none"), b = n(() => (m?.vibecontrolsAgentConnectionEvents ?? []).map((e) => ({
|
|
59
|
+
id: e.id,
|
|
60
|
+
timestamp: e.timestamp,
|
|
61
|
+
sequence: e.sequence,
|
|
62
|
+
step: e.step,
|
|
63
|
+
level: e.level,
|
|
64
|
+
message: e.message,
|
|
65
|
+
detailsJson: e.detailsJson ?? null
|
|
66
|
+
})), [m]), x = t(async (e) => {
|
|
67
|
+
let t = e === "text" ? l(b) : u(b, f, p);
|
|
68
|
+
try {
|
|
69
|
+
await navigator.clipboard.writeText(t), y(e), setTimeout(() => y("none"), 1500);
|
|
70
|
+
} catch {
|
|
71
|
+
d(`agent-${f}-connection-log.${e === "text" ? "txt" : "json"}`, e === "text" ? "text/plain" : "application/json", t);
|
|
72
|
+
}
|
|
73
|
+
}, [
|
|
74
|
+
b,
|
|
75
|
+
f,
|
|
76
|
+
p
|
|
77
|
+
]), S = t((e) => {
|
|
78
|
+
let t = e === "text" ? l(b) : u(b, f, p), n = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
79
|
+
d(`agent-${p ?? f}-connection-log-${n}.${e === "text" ? "txt" : "json"}`, e === "text" ? "text/plain" : "application/json", t);
|
|
80
|
+
}, [
|
|
81
|
+
b,
|
|
82
|
+
f,
|
|
83
|
+
p
|
|
84
|
+
]);
|
|
85
|
+
return /* @__PURE__ */ a("div", {
|
|
86
|
+
className: "flex flex-col h-full",
|
|
87
|
+
children: [
|
|
88
|
+
/* @__PURE__ */ a("div", {
|
|
89
|
+
className: "flex items-center justify-between p-4 border-b border-border-default",
|
|
90
|
+
children: [/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("h3", {
|
|
91
|
+
className: "text-fg-default font-semibold text-base",
|
|
92
|
+
children: "Connection Log"
|
|
93
|
+
}), /* @__PURE__ */ i("p", {
|
|
94
|
+
className: "text-fg-muted text-xs mt-0.5",
|
|
95
|
+
children: h && b.length === 0 ? "Loading…" : `${b.length} event${b.length === 1 ? "" : "s"} · auto-refresh every 3s`
|
|
96
|
+
})] }), /* @__PURE__ */ a("div", {
|
|
97
|
+
className: "flex gap-2",
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ i("button", {
|
|
100
|
+
type: "button",
|
|
101
|
+
onClick: () => _(),
|
|
102
|
+
className: "px-3 py-1.5 text-xs rounded bg-bg-subtle hover:bg-bg-muted text-fg-default border border-border-default",
|
|
103
|
+
children: "Refresh"
|
|
104
|
+
}),
|
|
105
|
+
/* @__PURE__ */ i("button", {
|
|
106
|
+
type: "button",
|
|
107
|
+
onClick: () => x("text"),
|
|
108
|
+
className: "px-3 py-1.5 text-xs rounded bg-bg-subtle hover:bg-bg-muted text-fg-default border border-border-default",
|
|
109
|
+
children: v === "text" ? "Copied!" : "Copy text"
|
|
110
|
+
}),
|
|
111
|
+
/* @__PURE__ */ i("button", {
|
|
112
|
+
type: "button",
|
|
113
|
+
onClick: () => x("json"),
|
|
114
|
+
className: "px-3 py-1.5 text-xs rounded bg-bg-subtle hover:bg-bg-muted text-fg-default border border-border-default",
|
|
115
|
+
children: v === "json" ? "Copied!" : "Copy JSON"
|
|
116
|
+
}),
|
|
117
|
+
/* @__PURE__ */ i("button", {
|
|
118
|
+
type: "button",
|
|
119
|
+
onClick: () => S("text"),
|
|
120
|
+
className: "px-3 py-1.5 text-xs rounded bg-action-primary hover:bg-action-primary-hover text-action-primary-text border border-action-primary",
|
|
121
|
+
children: "Export .txt"
|
|
122
|
+
}),
|
|
123
|
+
/* @__PURE__ */ i("button", {
|
|
124
|
+
type: "button",
|
|
125
|
+
onClick: () => S("json"),
|
|
126
|
+
className: "px-3 py-1.5 text-xs rounded bg-action-primary hover:bg-action-primary-hover text-action-primary-text border border-action-primary",
|
|
127
|
+
children: "Export .json"
|
|
128
|
+
})
|
|
129
|
+
]
|
|
130
|
+
})]
|
|
131
|
+
}),
|
|
132
|
+
g && /* @__PURE__ */ a("div", {
|
|
133
|
+
className: "m-4 p-3 rounded bg-status-danger-bg text-status-danger-text text-sm",
|
|
134
|
+
children: ["Failed to load events: ", g.message]
|
|
135
|
+
}),
|
|
136
|
+
/* @__PURE__ */ a("div", {
|
|
137
|
+
className: "flex-1 overflow-auto p-4",
|
|
138
|
+
children: [b.length === 0 && !h && /* @__PURE__ */ a("div", {
|
|
139
|
+
className: "text-fg-muted text-sm text-center py-12",
|
|
140
|
+
children: [
|
|
141
|
+
"No connection events recorded for this agent yet.",
|
|
142
|
+
/* @__PURE__ */ i("br", {}),
|
|
143
|
+
"Events appear here every time the platform probes, registers, or syncs the agent."
|
|
144
|
+
]
|
|
145
|
+
}), /* @__PURE__ */ i("ol", {
|
|
146
|
+
className: "space-y-2",
|
|
147
|
+
children: b.map((e) => {
|
|
148
|
+
let t = o[e.level] ?? o.info, n = s[e.level] ?? s.info, r = (() => {
|
|
149
|
+
if (!e.detailsJson) return null;
|
|
150
|
+
try {
|
|
151
|
+
return JSON.stringify(JSON.parse(e.detailsJson), null, 2);
|
|
152
|
+
} catch {
|
|
153
|
+
return e.detailsJson;
|
|
154
|
+
}
|
|
155
|
+
})();
|
|
156
|
+
return /* @__PURE__ */ i("li", {
|
|
157
|
+
className: "rounded border border-border-default bg-bg-subtle",
|
|
158
|
+
children: /* @__PURE__ */ a("div", {
|
|
159
|
+
className: "flex items-start gap-3 p-3",
|
|
160
|
+
children: [/* @__PURE__ */ i("div", {
|
|
161
|
+
className: `px-2 py-0.5 rounded text-[10px] uppercase font-mono ${n} ${t} shrink-0`,
|
|
162
|
+
children: e.level
|
|
163
|
+
}), /* @__PURE__ */ a("div", {
|
|
164
|
+
className: "flex-1 min-w-0",
|
|
165
|
+
children: [
|
|
166
|
+
/* @__PURE__ */ a("div", {
|
|
167
|
+
className: "flex items-center gap-2 text-xs text-fg-muted font-mono",
|
|
168
|
+
children: [
|
|
169
|
+
/* @__PURE__ */ a("span", { children: ["#", e.sequence] }),
|
|
170
|
+
/* @__PURE__ */ i("span", { children: "·" }),
|
|
171
|
+
/* @__PURE__ */ i("span", { children: c(e.timestamp) }),
|
|
172
|
+
/* @__PURE__ */ i("span", { children: "·" }),
|
|
173
|
+
/* @__PURE__ */ i("span", {
|
|
174
|
+
className: "text-fg-default font-medium",
|
|
175
|
+
children: e.step
|
|
176
|
+
})
|
|
177
|
+
]
|
|
178
|
+
}),
|
|
179
|
+
/* @__PURE__ */ i("div", {
|
|
180
|
+
className: "text-sm text-fg-default mt-1 break-words",
|
|
181
|
+
children: e.message
|
|
182
|
+
}),
|
|
183
|
+
r && /* @__PURE__ */ a("details", {
|
|
184
|
+
className: "mt-2",
|
|
185
|
+
children: [/* @__PURE__ */ i("summary", {
|
|
186
|
+
className: "text-xs text-fg-muted cursor-pointer hover:text-fg-default",
|
|
187
|
+
children: "Details"
|
|
188
|
+
}), /* @__PURE__ */ i("pre", {
|
|
189
|
+
className: "mt-2 p-2 rounded bg-bg-default text-xs text-fg-default overflow-x-auto whitespace-pre-wrap break-words",
|
|
190
|
+
children: r
|
|
191
|
+
})]
|
|
192
|
+
})
|
|
193
|
+
]
|
|
194
|
+
})]
|
|
195
|
+
})
|
|
196
|
+
}, e.id);
|
|
197
|
+
})
|
|
198
|
+
})]
|
|
199
|
+
})
|
|
200
|
+
]
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
204
|
+
export { f as ConnectionLogPanel };
|
|
205
|
+
|
|
206
|
+
//# sourceMappingURL=ConnectionLogPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConnectionLogPanel.js","names":[],"sources":["../../../src/components/agents/ConnectionLogPanel.tsx"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\nimport { useGetAgentConnectionEventsQuery } from '@/generated/wspace-operations';\n\ninterface Props {\n agentId: string;\n agentName?: string;\n}\n\ninterface EventRow {\n id: string;\n timestamp: string;\n sequence: number;\n step: string;\n level: string;\n message: string;\n detailsJson?: string | null;\n}\n\nconst LEVEL_COLOR: Record<string, string> = {\n info: 'text-status-info-text',\n warn: 'text-status-warning-text',\n error: 'text-status-danger-text',\n debug: 'text-fg-muted',\n};\n\nconst LEVEL_BG: Record<string, string> = {\n info: 'bg-status-info-bg',\n warn: 'bg-status-warning-bg',\n error: 'bg-status-danger-bg',\n debug: 'bg-bg-muted',\n};\n\nfunction formatTimestamp(iso: string): string {\n try {\n return new Date(iso).toISOString().replace('T', ' ').replace('Z', '');\n } catch {\n return iso;\n }\n}\n\nfunction eventsAsText(rows: EventRow[]): string {\n return rows\n .map((e) => {\n const detail = e.detailsJson ? `\\n details: ${e.detailsJson}` : '';\n return `[${e.timestamp}] #${e.sequence} ${e.level.toUpperCase().padEnd(5)} ${e.step.padEnd(28)} ${e.message}${detail}`;\n })\n .join('\\n');\n}\n\nfunction eventsAsJson(rows: EventRow[], agentId: string, agentName?: string): string {\n return JSON.stringify(\n {\n exportedAt: new Date().toISOString(),\n agentId,\n agentName,\n count: rows.length,\n events: rows.map((e) => ({\n id: e.id,\n timestamp: e.timestamp,\n sequence: e.sequence,\n step: e.step,\n level: e.level,\n message: e.message,\n details: e.detailsJson ? JSON.parse(e.detailsJson) : null,\n })),\n },\n null,\n 2,\n );\n}\n\nfunction downloadBlob(filename: string, mime: string, body: string): void {\n const blob = new Blob([body], { type: mime });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n\nexport function ConnectionLogPanel({ agentId, agentName }: Props) {\n // Poll every 3s while the panel is mounted so the operator sees new\n // events appear as the svc records them — no manual refresh needed\n // while debugging \"stuck\" agents.\n const { data, loading, error, refetch } = useGetAgentConnectionEventsQuery({\n variables: { agentId, limit: 200 },\n pollInterval: 3000,\n fetchPolicy: 'cache-and-network',\n });\n const [copied, setCopied] = useState<'none' | 'text' | 'json'>('none');\n\n const events: EventRow[] = useMemo(() => {\n const raw = data?.vibecontrolsAgentConnectionEvents ?? [];\n return raw.map((e: { id: string; timestamp: string; sequence: number; step: string; level: string; message: string; detailsJson?: string | null }) => ({\n id: e.id,\n timestamp: e.timestamp,\n sequence: e.sequence,\n step: e.step,\n level: e.level,\n message: e.message,\n detailsJson: e.detailsJson ?? null,\n }));\n }, [data]);\n\n const handleCopy = useCallback(\n async (mode: 'text' | 'json') => {\n const body = mode === 'text' ? eventsAsText(events) : eventsAsJson(events, agentId, agentName);\n try {\n await navigator.clipboard.writeText(body);\n setCopied(mode);\n setTimeout(() => setCopied('none'), 1500);\n } catch {\n /* clipboard denied — fall back to download */\n downloadBlob(`agent-${agentId}-connection-log.${mode === 'text' ? 'txt' : 'json'}`, mode === 'text' ? 'text/plain' : 'application/json', body);\n }\n },\n [events, agentId, agentName],\n );\n\n const handleDownload = useCallback(\n (mode: 'text' | 'json') => {\n const body = mode === 'text' ? eventsAsText(events) : eventsAsJson(events, agentId, agentName);\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\n downloadBlob(\n `agent-${agentName ?? agentId}-connection-log-${stamp}.${mode === 'text' ? 'txt' : 'json'}`,\n mode === 'text' ? 'text/plain' : 'application/json',\n body,\n );\n },\n [events, agentId, agentName],\n );\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"flex items-center justify-between p-4 border-b border-border-default\">\n <div>\n <h3 className=\"text-fg-default font-semibold text-base\">Connection Log</h3>\n <p className=\"text-fg-muted text-xs mt-0.5\">\n {loading && events.length === 0\n ? 'Loading…'\n : `${events.length} event${events.length === 1 ? '' : 's'} · auto-refresh every 3s`}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-3 py-1.5 text-xs rounded bg-bg-subtle hover:bg-bg-muted text-fg-default border border-border-default\"\n >\n Refresh\n </button>\n <button\n type=\"button\"\n onClick={() => handleCopy('text')}\n className=\"px-3 py-1.5 text-xs rounded bg-bg-subtle hover:bg-bg-muted text-fg-default border border-border-default\"\n >\n {copied === 'text' ? 'Copied!' : 'Copy text'}\n </button>\n <button\n type=\"button\"\n onClick={() => handleCopy('json')}\n className=\"px-3 py-1.5 text-xs rounded bg-bg-subtle hover:bg-bg-muted text-fg-default border border-border-default\"\n >\n {copied === 'json' ? 'Copied!' : 'Copy JSON'}\n </button>\n <button\n type=\"button\"\n onClick={() => handleDownload('text')}\n className=\"px-3 py-1.5 text-xs rounded bg-action-primary hover:bg-action-primary-hover text-action-primary-text border border-action-primary\"\n >\n Export .txt\n </button>\n <button\n type=\"button\"\n onClick={() => handleDownload('json')}\n className=\"px-3 py-1.5 text-xs rounded bg-action-primary hover:bg-action-primary-hover text-action-primary-text border border-action-primary\"\n >\n Export .json\n </button>\n </div>\n </div>\n\n {error && (\n <div className=\"m-4 p-3 rounded bg-status-danger-bg text-status-danger-text text-sm\">\n Failed to load events: {error.message}\n </div>\n )}\n\n <div className=\"flex-1 overflow-auto p-4\">\n {events.length === 0 && !loading && (\n <div className=\"text-fg-muted text-sm text-center py-12\">\n No connection events recorded for this agent yet.\n <br />\n Events appear here every time the platform probes, registers, or syncs the agent.\n </div>\n )}\n <ol className=\"space-y-2\">\n {events.map((e) => {\n const levelColor = LEVEL_COLOR[e.level] ?? LEVEL_COLOR.info;\n const levelBg = LEVEL_BG[e.level] ?? LEVEL_BG.info;\n const details = (() => {\n if (!e.detailsJson) return null;\n try {\n return JSON.stringify(JSON.parse(e.detailsJson), null, 2);\n } catch {\n return e.detailsJson;\n }\n })();\n return (\n <li\n key={e.id}\n className=\"rounded border border-border-default bg-bg-subtle\"\n >\n <div className=\"flex items-start gap-3 p-3\">\n <div className={`px-2 py-0.5 rounded text-[10px] uppercase font-mono ${levelBg} ${levelColor} shrink-0`}>\n {e.level}\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 text-xs text-fg-muted font-mono\">\n <span>#{e.sequence}</span>\n <span>·</span>\n <span>{formatTimestamp(e.timestamp)}</span>\n <span>·</span>\n <span className=\"text-fg-default font-medium\">{e.step}</span>\n </div>\n <div className=\"text-sm text-fg-default mt-1 break-words\">{e.message}</div>\n {details && (\n <details className=\"mt-2\">\n <summary className=\"text-xs text-fg-muted cursor-pointer hover:text-fg-default\">\n Details\n </summary>\n <pre className=\"mt-2 p-2 rounded bg-bg-default text-xs text-fg-default overflow-x-auto whitespace-pre-wrap break-words\">\n {details}\n </pre>\n </details>\n )}\n </div>\n </div>\n </li>\n );\n })}\n </ol>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAkBA,IAAM,IAAsC;CAC1C,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACR,EAEK,IAAmC;CACvC,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACR;AAED,SAAS,EAAgB,GAAqB;AAC5C,KAAI;AACF,SAAO,IAAI,KAAK,EAAI,CAAC,aAAa,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ,KAAK,GAAG;SAC/D;AACN,SAAO;;;AAIX,SAAS,EAAa,GAA0B;AAC9C,QAAO,EACJ,KAAK,MAAM;EACV,IAAM,IAAS,EAAE,cAAc,kBAAkB,EAAE,gBAAgB;AACnE,SAAO,IAAI,EAAE,UAAU,KAAK,EAAE,SAAS,GAAG,EAAE,MAAM,aAAa,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,OAAO,GAAG,CAAC,GAAG,EAAE,UAAU;GAC9G,CACD,KAAK,KAAK;;AAGf,SAAS,EAAa,GAAkB,GAAiB,GAA4B;AACnF,QAAO,KAAK,UACV;EACE,6BAAY,IAAI,MAAM,EAAC,aAAa;EACpC;EACA;EACA,OAAO,EAAK;EACZ,QAAQ,EAAK,KAAK,OAAO;GACvB,IAAI,EAAE;GACN,WAAW,EAAE;GACb,UAAU,EAAE;GACZ,MAAM,EAAE;GACR,OAAO,EAAE;GACT,SAAS,EAAE;GACX,SAAS,EAAE,cAAc,KAAK,MAAM,EAAE,YAAY,GAAG;GACtD,EAAE;EACJ,EACD,MACA,EACD;;AAGH,SAAS,EAAa,GAAkB,GAAc,GAAoB;CACxE,IAAM,IAAO,IAAI,KAAK,CAAC,EAAK,EAAE,EAAE,MAAM,GAAM,CAAC,EACvC,IAAM,IAAI,gBAAgB,EAAK,EAC/B,IAAI,SAAS,cAAc,IAAI;AAMrC,CALA,EAAE,OAAO,GACT,EAAE,WAAW,GACb,SAAS,KAAK,YAAY,EAAE,EAC5B,EAAE,OAAO,EACT,SAAS,KAAK,YAAY,EAAE,EAC5B,IAAI,gBAAgB,EAAI;;AAG1B,SAAgB,EAAmB,EAAE,YAAS,gBAAoB;CAIhE,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAiC;EACzE,WAAW;GAAE;GAAS,OAAO;GAAK;EAClC,cAAc;EACd,aAAa;EACd,CAAC,EACI,CAAC,GAAQ,KAAa,EAAmC,OAAO,EAEhE,IAAqB,SACb,GAAM,qCAAqC,EAAE,EAC9C,KAAK,OAAuI;EACrJ,IAAI,EAAE;EACN,WAAW,EAAE;EACb,UAAU,EAAE;EACZ,MAAM,EAAE;EACR,OAAO,EAAE;EACT,SAAS,EAAE;EACX,aAAa,EAAE,eAAe;EAC/B,EAAE,EACF,CAAC,EAAK,CAAC,EAEJ,IAAa,EACjB,OAAO,MAA0B;EAC/B,IAAM,IAAO,MAAS,SAAS,EAAa,EAAO,GAAG,EAAa,GAAQ,GAAS,EAAU;AAC9F,MAAI;AAGF,GAFA,MAAM,UAAU,UAAU,UAAU,EAAK,EACzC,EAAU,EAAK,EACf,iBAAiB,EAAU,OAAO,EAAE,KAAK;UACnC;AAEN,KAAa,SAAS,EAAQ,kBAAkB,MAAS,SAAS,QAAQ,UAAU,MAAS,SAAS,eAAe,oBAAoB,EAAK;;IAGlJ;EAAC;EAAQ;EAAS;EAAU,CAC7B,EAEK,IAAiB,GACpB,MAA0B;EACzB,IAAM,IAAO,MAAS,SAAS,EAAa,EAAO,GAAG,EAAa,GAAQ,GAAS,EAAU,EACxF,qBAAQ,IAAI,MAAM,EAAC,aAAa,CAAC,QAAQ,SAAS,IAAI;AAC5D,IACE,SAAS,KAAa,EAAQ,kBAAkB,EAAM,GAAG,MAAS,SAAS,QAAQ,UACnF,MAAS,SAAS,eAAe,oBACjC,EACD;IAEH;EAAC;EAAQ;EAAS;EAAU,CAC7B;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAA0C;KAAmB,CAAA,EAC3E,kBAAC,KAAD;KAAG,WAAU;eACV,KAAW,EAAO,WAAW,IAC1B,aACA,GAAG,EAAO,OAAO,QAAQ,EAAO,WAAW,IAAI,KAAK,IAAI;KAC1D,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,GAAS;OACxB,WAAU;iBACX;OAEQ,CAAA;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,OAAO;OACjC,WAAU;iBAET,MAAW,SAAS,YAAY;OAC1B,CAAA;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,OAAO;OACjC,WAAU;iBAET,MAAW,SAAS,YAAY;OAC1B,CAAA;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAe,OAAO;OACrC,WAAU;iBACX;OAEQ,CAAA;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAe,OAAO;OACrC,WAAU;iBACX;OAEQ,CAAA;MACL;OACF;;GAEL,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CAAqF,2BAC3D,EAAM,QAC1B;;GAGR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAO,WAAW,KAAK,CAAC,KACvB,kBAAC,OAAD;KAAK,WAAU;eAAf;MAAyD;MAEvD,kBAAC,MAAD,EAAM,CAAA;;MAEF;QAER,kBAAC,MAAD;KAAI,WAAU;eACX,EAAO,KAAK,MAAM;MACjB,IAAM,IAAa,EAAY,EAAE,UAAU,EAAY,MACjD,IAAU,EAAS,EAAE,UAAU,EAAS,MACxC,WAAiB;AACrB,WAAI,CAAC,EAAE,YAAa,QAAO;AAC3B,WAAI;AACF,eAAO,KAAK,UAAU,KAAK,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE;eACnD;AACN,eAAO,EAAE;;UAET;AACJ,aACE,kBAAC,MAAD;OAEE,WAAU;iBAEV,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAW,uDAAuD,EAAQ,GAAG,EAAW;mBAC1F,EAAE;SACC,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACE,kBAAC,QAAD,EAAA,UAAA,CAAM,KAAE,EAAE,SAAgB,EAAA,CAAA;YAC1B,kBAAC,QAAD,EAAA,UAAM,KAAQ,CAAA;YACd,kBAAC,QAAD,EAAA,UAAO,EAAgB,EAAE,UAAU,EAAQ,CAAA;YAC3C,kBAAC,QAAD,EAAA,UAAM,KAAQ,CAAA;YACd,kBAAC,QAAD;aAAM,WAAU;uBAA+B,EAAE;aAAY,CAAA;YACzD;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAA4C,EAAE;WAAc,CAAA;UAC1E,KACC,kBAAC,WAAD;WAAS,WAAU;qBAAnB,CACE,kBAAC,WAAD;YAAS,WAAU;sBAA6D;YAEtE,CAAA,EACV,kBAAC,OAAD;YAAK,WAAU;sBACZ;YACG,CAAA,CACE;;UAER;WACF;;OACH,EA5BE,EAAE,GA4BJ;OAEP;KACC,CAAA,CACD;;GACF"}
|
|
@@ -4,14 +4,14 @@ import { useI18n as i } from "@burdenoff/fe-libs/shared/providers/shell/I18nProv
|
|
|
4
4
|
import { Plus as a } from "lucide-react";
|
|
5
5
|
import { jsx as o } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/vibedeck/VibeDeckGrid.tsx
|
|
7
|
-
var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditButton: d, onDeleteButton: f, onDropButton: p, onAddButton: m, executingButtonId: h, canCreate: g, canUpdate: _, canDelete: v, canExecute: y }) => {
|
|
8
|
-
let { t:
|
|
9
|
-
let n =
|
|
7
|
+
var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditButton: d, onDeleteButton: f, onDropButton: p, onAddButton: m, executingButtonId: h, canCreate: g, canUpdate: _, canDelete: v, canExecute: y, previewMode: b = !1 }) => {
|
|
8
|
+
let { t: x } = i(), S = (e, t) => {
|
|
9
|
+
let n = x(e);
|
|
10
10
|
return n === e ? t : n;
|
|
11
|
-
}, [
|
|
11
|
+
}, [C, w] = r({
|
|
12
12
|
draggingId: null,
|
|
13
13
|
targetKey: null
|
|
14
|
-
}), { occupiedCells:
|
|
14
|
+
}), { occupiedCells: T, buttonAtCell: E } = n(() => {
|
|
15
15
|
let e = /* @__PURE__ */ new Set(), t = /* @__PURE__ */ new Map();
|
|
16
16
|
for (let n of l) {
|
|
17
17
|
for (let t = n.row; t < n.row + n.rowSpan; t++) for (let r = n.column; r < n.column + n.colSpan; r++) e.add(`${t},${r}`);
|
|
@@ -21,61 +21,61 @@ var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditBu
|
|
|
21
21
|
occupiedCells: e,
|
|
22
22
|
buttonAtCell: t
|
|
23
23
|
};
|
|
24
|
-
}, [l]),
|
|
25
|
-
e.dataTransfer.setData("text/plain", t), e.dataTransfer.effectAllowed = "move",
|
|
24
|
+
}, [l]), D = t((e, t) => {
|
|
25
|
+
e.dataTransfer.setData("text/plain", t), e.dataTransfer.effectAllowed = "move", w({
|
|
26
26
|
draggingId: t,
|
|
27
27
|
targetKey: null
|
|
28
28
|
});
|
|
29
|
-
}, []),
|
|
30
|
-
if (!
|
|
31
|
-
let n = l.find((e) => e.id ===
|
|
29
|
+
}, []), O = t((e, t) => {
|
|
30
|
+
if (!C.draggingId) return !1;
|
|
31
|
+
let n = l.find((e) => e.id === C.draggingId);
|
|
32
32
|
if (!n || e + n.rowSpan > c || t + n.colSpan > s) return !1;
|
|
33
|
-
for (let r = e; r < e + n.rowSpan; r++) for (let e = t; e < t + n.colSpan; e++) if (
|
|
33
|
+
for (let r = e; r < e + n.rowSpan; r++) for (let e = t; e < t + n.colSpan; e++) if (T.has(`${r},${e}`) && !(r >= n.row && r < n.row + n.rowSpan && e >= n.column && e < n.column + n.colSpan)) return !1;
|
|
34
34
|
return !0;
|
|
35
35
|
}, [
|
|
36
|
-
|
|
36
|
+
C.draggingId,
|
|
37
37
|
l,
|
|
38
38
|
c,
|
|
39
39
|
s,
|
|
40
|
-
|
|
41
|
-
]),
|
|
42
|
-
e.preventDefault(), e.dataTransfer.dropEffect = "move",
|
|
40
|
+
T
|
|
41
|
+
]), k = t((e, t, n, r) => {
|
|
42
|
+
e.preventDefault(), e.dataTransfer.dropEffect = "move", O(n, r) ? w((e) => ({
|
|
43
43
|
...e,
|
|
44
44
|
targetKey: t
|
|
45
|
-
})) :
|
|
45
|
+
})) : w((e) => ({
|
|
46
46
|
...e,
|
|
47
47
|
targetKey: null
|
|
48
48
|
}));
|
|
49
|
-
}, [
|
|
50
|
-
|
|
49
|
+
}, [O]), A = t(() => {
|
|
50
|
+
w((e) => ({
|
|
51
51
|
...e,
|
|
52
52
|
targetKey: null
|
|
53
53
|
}));
|
|
54
|
-
}, []),
|
|
54
|
+
}, []), j = t((e, t, n) => {
|
|
55
55
|
e.preventDefault();
|
|
56
56
|
let r = e.dataTransfer.getData("text/plain");
|
|
57
|
-
|
|
57
|
+
w({
|
|
58
58
|
draggingId: null,
|
|
59
59
|
targetKey: null
|
|
60
|
-
}), r &&
|
|
61
|
-
}, [p,
|
|
62
|
-
|
|
60
|
+
}), r && O(t, n) && p(r, t, n);
|
|
61
|
+
}, [p, O]), M = t(() => {
|
|
62
|
+
w({
|
|
63
63
|
draggingId: null,
|
|
64
64
|
targetKey: null
|
|
65
65
|
});
|
|
66
|
-
}, []),
|
|
66
|
+
}, []), N = [];
|
|
67
67
|
for (let t = 0; t < c; t++) for (let n = 0; n < s; n++) {
|
|
68
|
-
let r = `${t},${n}`, i =
|
|
68
|
+
let r = `${t},${n}`, i = E.get(r);
|
|
69
69
|
if (i) {
|
|
70
|
-
let a =
|
|
71
|
-
|
|
70
|
+
let a = C.draggingId === i.id;
|
|
71
|
+
N.push(/* @__PURE__ */ o("div", {
|
|
72
72
|
style: {
|
|
73
73
|
gridRow: `${t + 1} / span ${i.rowSpan}`,
|
|
74
74
|
gridColumn: `${n + 1} / span ${i.colSpan}`
|
|
75
75
|
},
|
|
76
76
|
draggable: _,
|
|
77
|
-
onDragStart: (e) =>
|
|
78
|
-
onDragEnd:
|
|
77
|
+
onDragStart: (e) => D(e, i.id),
|
|
78
|
+
onDragEnd: M,
|
|
79
79
|
className: `${a ? "opacity-40" : ""} ${_ ? "cursor-grab active:cursor-grabbing" : ""}`,
|
|
80
80
|
children: /* @__PURE__ */ o(e, {
|
|
81
81
|
button: i,
|
|
@@ -85,24 +85,25 @@ var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditBu
|
|
|
85
85
|
canUpdate: _,
|
|
86
86
|
canDelete: v,
|
|
87
87
|
canExecute: y,
|
|
88
|
-
executing: h === i.id
|
|
88
|
+
executing: h === i.id,
|
|
89
|
+
previewMode: b
|
|
89
90
|
})
|
|
90
91
|
}, r));
|
|
91
|
-
} else if (!
|
|
92
|
-
let e =
|
|
93
|
-
|
|
92
|
+
} else if (!T.has(r)) {
|
|
93
|
+
let e = C.draggingId && C.targetKey === r;
|
|
94
|
+
N.push(/* @__PURE__ */ o("div", {
|
|
94
95
|
style: {
|
|
95
96
|
gridRow: t + 1,
|
|
96
97
|
gridColumn: n + 1
|
|
97
98
|
},
|
|
98
|
-
onDragOver: (e) =>
|
|
99
|
-
onDragLeave:
|
|
100
|
-
onDrop: (e) =>
|
|
101
|
-
children:
|
|
99
|
+
onDragOver: (e) => k(e, r, t, n),
|
|
100
|
+
onDragLeave: A,
|
|
101
|
+
onDrop: (e) => j(e, t, n),
|
|
102
|
+
children: C.draggingId ? /* @__PURE__ */ o("div", {
|
|
102
103
|
className: `w-full h-full min-h-[80px] rounded-lg border-2 border-dashed flex items-center justify-center transition-all ${e ? "border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg" : "border-border-default/30 text-text-tertiary/30"}`,
|
|
103
104
|
children: e && /* @__PURE__ */ o("span", {
|
|
104
105
|
className: "text-xs font-medium",
|
|
105
|
-
children:
|
|
106
|
+
children: S("vibeDeck.dropHere", "Drop here")
|
|
106
107
|
})
|
|
107
108
|
}) : g ? /* @__PURE__ */ o("button", {
|
|
108
109
|
type: "button",
|
|
@@ -119,7 +120,7 @@ var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditBu
|
|
|
119
120
|
gridTemplateColumns: `repeat(${s}, 1fr)`,
|
|
120
121
|
gridTemplateRows: `repeat(${c}, auto)`
|
|
121
122
|
},
|
|
122
|
-
children:
|
|
123
|
+
children: N
|
|
123
124
|
});
|
|
124
125
|
};
|
|
125
126
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeDeckGrid.js","names":[],"sources":["../../../src/components/vibedeck/VibeDeckGrid.tsx"],"sourcesContent":["import { type FC, type ReactElement, useState, useCallback, useMemo } from 'react';\nimport { Plus } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VibeDeckButtonTile } from './VibeDeckButtonTile';\n\ninterface ActionRef {\n id: string;\n name: string;\n displayName: string | null;\n actionType: string;\n command: string | null;\n script: string | null;\n webhookUrl: string | null;\n targetAgentId: string | null;\n}\n\ninterface ButtonData {\n id: string;\n label: string;\n icon: string | null;\n color: string | null;\n command: string | null;\n actionType: string;\n category: string | null;\n confirmationRequired: boolean;\n keyboardShortcut: string | null;\n targetAgentId: string | null;\n scriptInterpreter: string | null;\n webhookUrl: string | null;\n webhookMethod: string | null;\n actionId?: string | null;\n action?: ActionRef | null;\n row: number;\n column: number;\n rowSpan: number;\n colSpan: number;\n}\n\ninterface VibeDeckGridProps {\n gridColumns: number;\n gridRows: number;\n buttons: ButtonData[];\n onExecuteButton: (id: string) => void;\n onEditButton: (id: string) => void;\n onDeleteButton: (id: string) => void;\n onDropButton: (buttonId: string, targetRow: number, targetColumn: number) => void;\n onAddButton: (row: number, column: number) => void;\n executingButtonId: string | null;\n canCreate: boolean;\n canUpdate: boolean;\n canDelete: boolean;\n canExecute: boolean;\n}\n\nexport const VibeDeckGrid: FC<VibeDeckGridProps> = ({\n gridColumns,\n gridRows,\n buttons,\n onExecuteButton,\n onEditButton,\n onDeleteButton,\n onDropButton,\n onAddButton,\n executingButtonId,\n canCreate,\n canUpdate,\n canDelete,\n canExecute,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const [dragState, setDragState] = useState<{\n draggingId: string | null;\n targetKey: string | null;\n }>({ draggingId: null, targetKey: null });\n\n // Build a map of occupied cells (memoized to stabilize hook dependencies)\n const { occupiedCells, buttonAtCell } = useMemo(() => {\n const occupied = new Set<string>();\n const cellMap = new Map<string, ButtonData>();\n for (const btn of buttons) {\n for (let r = btn.row; r < btn.row + btn.rowSpan; r++) {\n for (let c = btn.column; c < btn.column + btn.colSpan; c++) {\n occupied.add(`${r},${c}`);\n }\n }\n cellMap.set(`${btn.row},${btn.column}`, btn);\n }\n return { occupiedCells: occupied, buttonAtCell: cellMap };\n }, [buttons]);\n\n const handleDragStart = useCallback((e: React.DragEvent, buttonId: string) => {\n e.dataTransfer.setData('text/plain', buttonId);\n e.dataTransfer.effectAllowed = 'move';\n setDragState({ draggingId: buttonId, targetKey: null });\n }, []);\n\n const isDropValid = useCallback(\n (targetRow: number, targetCol: number) => {\n if (!dragState.draggingId) return false;\n const btn = buttons.find((b) => b.id === dragState.draggingId);\n if (!btn) return false;\n // Check grid boundary\n if (targetRow + btn.rowSpan > gridRows || targetCol + btn.colSpan > gridColumns) return false;\n // Check collision with other buttons\n for (let r = targetRow; r < targetRow + btn.rowSpan; r++) {\n for (let c = targetCol; c < targetCol + btn.colSpan; c++) {\n if (occupiedCells.has(`${r},${c}`)) {\n // Allow overlap with the dragged button's own current footprint\n const isOwnCell =\n r >= btn.row &&\n r < btn.row + btn.rowSpan &&\n c >= btn.column &&\n c < btn.column + btn.colSpan;\n if (!isOwnCell) return false;\n }\n }\n }\n return true;\n },\n [dragState.draggingId, buttons, gridRows, gridColumns, occupiedCells]\n );\n\n const handleDragOver = useCallback(\n (e: React.DragEvent, key: string, row: number, col: number) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'move';\n if (isDropValid(row, col)) {\n setDragState((prev) => ({ ...prev, targetKey: key }));\n } else {\n setDragState((prev) => ({ ...prev, targetKey: null }));\n }\n },\n [isDropValid]\n );\n\n const handleDragLeave = useCallback(() => {\n setDragState((prev) => ({ ...prev, targetKey: null }));\n }, []);\n\n const handleDrop = useCallback(\n (e: React.DragEvent, row: number, col: number) => {\n e.preventDefault();\n const buttonId = e.dataTransfer.getData('text/plain');\n setDragState({ draggingId: null, targetKey: null });\n if (buttonId && isDropValid(row, col)) {\n onDropButton(buttonId, row, col);\n }\n },\n [onDropButton, isDropValid]\n );\n\n const handleDragEnd = useCallback(() => {\n setDragState({ draggingId: null, targetKey: null });\n }, []);\n\n // Build grid cells\n const cells: ReactElement[] = [];\n for (let row = 0; row < gridRows; row++) {\n for (let col = 0; col < gridColumns; col++) {\n const key = `${row},${col}`;\n const button = buttonAtCell.get(key);\n\n if (button) {\n const isDragging = dragState.draggingId === button.id;\n cells.push(\n <div\n key={key}\n style={{\n gridRow: `${row + 1} / span ${button.rowSpan}`,\n gridColumn: `${col + 1} / span ${button.colSpan}`,\n }}\n draggable={canUpdate}\n onDragStart={(e) => handleDragStart(e, button.id)}\n onDragEnd={handleDragEnd}\n className={`${isDragging ? 'opacity-40' : ''} ${canUpdate ? 'cursor-grab active:cursor-grabbing' : ''}`}\n >\n <VibeDeckButtonTile\n button={button}\n onExecute={onExecuteButton}\n onEdit={onEditButton}\n onDelete={onDeleteButton}\n canUpdate={canUpdate}\n canDelete={canDelete}\n canExecute={canExecute}\n executing={executingButtonId === button.id}\n />\n </div>\n );\n } else if (!occupiedCells.has(key)) {\n const isDropTarget = dragState.draggingId && dragState.targetKey === key;\n cells.push(\n <div\n key={key}\n style={{ gridRow: row + 1, gridColumn: col + 1 }}\n onDragOver={(e) => handleDragOver(e, key, row, col)}\n onDragLeave={handleDragLeave}\n onDrop={(e) => handleDrop(e, row, col)}\n >\n {dragState.draggingId ? (\n <div\n className={`w-full h-full min-h-[80px] rounded-lg border-2 border-dashed flex items-center justify-center transition-all ${\n isDropTarget\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default/30 text-text-tertiary/30'\n }`}\n >\n {isDropTarget && (\n <span className=\"text-xs font-medium\">\n {tr('vibeDeck.dropHere', 'Drop here')}\n </span>\n )}\n </div>\n ) : canCreate ? (\n <button\n type=\"button\"\n onClick={() => onAddButton(row, col)}\n className=\"w-full h-full min-h-[80px] rounded-lg border-2 border-dashed border-border-strong text-text-tertiary hover:border-border-focus hover:text-text-secondary flex items-center justify-center transition-all hover:bg-bg-secondary/50\"\n >\n <Plus size={18} />\n </button>\n ) : (\n <div className=\"w-full h-full min-h-[80px] rounded-lg border-2 border-dashed border-border-default\" />\n )}\n </div>\n );\n }\n }\n }\n\n return (\n <div\n className=\"grid gap-3\"\n style={{\n gridTemplateColumns: `repeat(${gridColumns}, 1fr)`,\n gridTemplateRows: `repeat(${gridRows}, auto)`,\n }}\n >\n {cells}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibeDeckGrid.js","names":[],"sources":["../../../src/components/vibedeck/VibeDeckGrid.tsx"],"sourcesContent":["import { type FC, type ReactElement, useState, useCallback, useMemo } from 'react';\nimport { Plus } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VibeDeckButtonTile } from './VibeDeckButtonTile';\n\ninterface ActionRef {\n id: string;\n name: string;\n displayName: string | null;\n actionType: string;\n command: string | null;\n script: string | null;\n webhookUrl: string | null;\n targetAgentId: string | null;\n}\n\ninterface ButtonData {\n id: string;\n label: string;\n icon: string | null;\n color: string | null;\n command: string | null;\n actionType: string;\n category: string | null;\n confirmationRequired: boolean;\n keyboardShortcut: string | null;\n targetAgentId: string | null;\n scriptInterpreter: string | null;\n webhookUrl: string | null;\n webhookMethod: string | null;\n actionId?: string | null;\n action?: ActionRef | null;\n row: number;\n column: number;\n rowSpan: number;\n colSpan: number;\n}\n\ninterface VibeDeckGridProps {\n gridColumns: number;\n gridRows: number;\n buttons: ButtonData[];\n onExecuteButton: (id: string) => void;\n onEditButton: (id: string) => void;\n onDeleteButton: (id: string) => void;\n onDropButton: (buttonId: string, targetRow: number, targetColumn: number) => void;\n onAddButton: (row: number, column: number) => void;\n executingButtonId: string | null;\n canCreate: boolean;\n canUpdate: boolean;\n canDelete: boolean;\n canExecute: boolean;\n /**\n * When true, the grid is in Preview mode — tile clicks execute (and route\n * through the parent's confirmation check for buttons with\n * `confirmationRequired: true`). Defaults to false so the tile behaves as a\n * static editor surface.\n */\n previewMode?: boolean;\n}\n\nexport const VibeDeckGrid: FC<VibeDeckGridProps> = ({\n gridColumns,\n gridRows,\n buttons,\n onExecuteButton,\n onEditButton,\n onDeleteButton,\n onDropButton,\n onAddButton,\n executingButtonId,\n canCreate,\n canUpdate,\n canDelete,\n canExecute,\n previewMode = false,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const [dragState, setDragState] = useState<{\n draggingId: string | null;\n targetKey: string | null;\n }>({ draggingId: null, targetKey: null });\n\n // Build a map of occupied cells (memoized to stabilize hook dependencies)\n const { occupiedCells, buttonAtCell } = useMemo(() => {\n const occupied = new Set<string>();\n const cellMap = new Map<string, ButtonData>();\n for (const btn of buttons) {\n for (let r = btn.row; r < btn.row + btn.rowSpan; r++) {\n for (let c = btn.column; c < btn.column + btn.colSpan; c++) {\n occupied.add(`${r},${c}`);\n }\n }\n cellMap.set(`${btn.row},${btn.column}`, btn);\n }\n return { occupiedCells: occupied, buttonAtCell: cellMap };\n }, [buttons]);\n\n const handleDragStart = useCallback((e: React.DragEvent, buttonId: string) => {\n e.dataTransfer.setData('text/plain', buttonId);\n e.dataTransfer.effectAllowed = 'move';\n setDragState({ draggingId: buttonId, targetKey: null });\n }, []);\n\n const isDropValid = useCallback(\n (targetRow: number, targetCol: number) => {\n if (!dragState.draggingId) return false;\n const btn = buttons.find((b) => b.id === dragState.draggingId);\n if (!btn) return false;\n // Check grid boundary\n if (targetRow + btn.rowSpan > gridRows || targetCol + btn.colSpan > gridColumns) return false;\n // Check collision with other buttons\n for (let r = targetRow; r < targetRow + btn.rowSpan; r++) {\n for (let c = targetCol; c < targetCol + btn.colSpan; c++) {\n if (occupiedCells.has(`${r},${c}`)) {\n // Allow overlap with the dragged button's own current footprint\n const isOwnCell =\n r >= btn.row &&\n r < btn.row + btn.rowSpan &&\n c >= btn.column &&\n c < btn.column + btn.colSpan;\n if (!isOwnCell) return false;\n }\n }\n }\n return true;\n },\n [dragState.draggingId, buttons, gridRows, gridColumns, occupiedCells]\n );\n\n const handleDragOver = useCallback(\n (e: React.DragEvent, key: string, row: number, col: number) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'move';\n if (isDropValid(row, col)) {\n setDragState((prev) => ({ ...prev, targetKey: key }));\n } else {\n setDragState((prev) => ({ ...prev, targetKey: null }));\n }\n },\n [isDropValid]\n );\n\n const handleDragLeave = useCallback(() => {\n setDragState((prev) => ({ ...prev, targetKey: null }));\n }, []);\n\n const handleDrop = useCallback(\n (e: React.DragEvent, row: number, col: number) => {\n e.preventDefault();\n const buttonId = e.dataTransfer.getData('text/plain');\n setDragState({ draggingId: null, targetKey: null });\n if (buttonId && isDropValid(row, col)) {\n onDropButton(buttonId, row, col);\n }\n },\n [onDropButton, isDropValid]\n );\n\n const handleDragEnd = useCallback(() => {\n setDragState({ draggingId: null, targetKey: null });\n }, []);\n\n // Build grid cells\n const cells: ReactElement[] = [];\n for (let row = 0; row < gridRows; row++) {\n for (let col = 0; col < gridColumns; col++) {\n const key = `${row},${col}`;\n const button = buttonAtCell.get(key);\n\n if (button) {\n const isDragging = dragState.draggingId === button.id;\n cells.push(\n <div\n key={key}\n style={{\n gridRow: `${row + 1} / span ${button.rowSpan}`,\n gridColumn: `${col + 1} / span ${button.colSpan}`,\n }}\n draggable={canUpdate}\n onDragStart={(e) => handleDragStart(e, button.id)}\n onDragEnd={handleDragEnd}\n className={`${isDragging ? 'opacity-40' : ''} ${canUpdate ? 'cursor-grab active:cursor-grabbing' : ''}`}\n >\n <VibeDeckButtonTile\n button={button}\n onExecute={onExecuteButton}\n onEdit={onEditButton}\n onDelete={onDeleteButton}\n canUpdate={canUpdate}\n canDelete={canDelete}\n canExecute={canExecute}\n executing={executingButtonId === button.id}\n previewMode={previewMode}\n />\n </div>\n );\n } else if (!occupiedCells.has(key)) {\n const isDropTarget = dragState.draggingId && dragState.targetKey === key;\n cells.push(\n <div\n key={key}\n style={{ gridRow: row + 1, gridColumn: col + 1 }}\n onDragOver={(e) => handleDragOver(e, key, row, col)}\n onDragLeave={handleDragLeave}\n onDrop={(e) => handleDrop(e, row, col)}\n >\n {dragState.draggingId ? (\n <div\n className={`w-full h-full min-h-[80px] rounded-lg border-2 border-dashed flex items-center justify-center transition-all ${\n isDropTarget\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default/30 text-text-tertiary/30'\n }`}\n >\n {isDropTarget && (\n <span className=\"text-xs font-medium\">\n {tr('vibeDeck.dropHere', 'Drop here')}\n </span>\n )}\n </div>\n ) : canCreate ? (\n <button\n type=\"button\"\n onClick={() => onAddButton(row, col)}\n className=\"w-full h-full min-h-[80px] rounded-lg border-2 border-dashed border-border-strong text-text-tertiary hover:border-border-focus hover:text-text-secondary flex items-center justify-center transition-all hover:bg-bg-secondary/50\"\n >\n <Plus size={18} />\n </button>\n ) : (\n <div className=\"w-full h-full min-h-[80px] rounded-lg border-2 border-dashed border-border-default\" />\n )}\n </div>\n );\n }\n }\n }\n\n return (\n <div\n className=\"grid gap-3\"\n style={{\n gridTemplateColumns: `repeat(${gridColumns}, 1fr)`,\n gridTemplateRows: `repeat(${gridRows}, auto)`,\n }}\n >\n {cells}\n </div>\n );\n};\n"],"mappings":"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"}
|