@pgege/kaboo-react 0.1.0 → 0.2.0
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/AGENTS.md +3 -3
- package/CHANGELOG.md +36 -3
- package/README.md +58 -0
- package/dist/{chunk-RXZWOBSI.js → chunk-4TDUEMK5.js} +175 -2
- package/dist/chunk-4TDUEMK5.js.map +1 -0
- package/dist/copilotkit.cjs +158 -26
- package/dist/copilotkit.cjs.map +1 -1
- package/dist/copilotkit.d.cts +17 -2
- package/dist/copilotkit.d.ts +17 -2
- package/dist/copilotkit.js +118 -7
- package/dist/copilotkit.js.map +1 -1
- package/dist/index.cjs +1110 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +324 -4
- package/dist/index.d.ts +324 -4
- package/dist/index.js +951 -30
- package/dist/index.js.map +1 -1
- package/dist/styles.css +319 -0
- package/llms-full.txt +93 -5
- package/package.json +1 -1
- package/dist/chunk-RXZWOBSI.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -9,21 +9,33 @@ import {
|
|
|
9
9
|
KabooInterruptHandler,
|
|
10
10
|
MarkdownContent,
|
|
11
11
|
MiniTable,
|
|
12
|
+
REFERENCES_STATE_KEY,
|
|
13
|
+
REFERENCE_METADATA_KEYS,
|
|
14
|
+
ReferenceStateSync,
|
|
15
|
+
ReferencesProvider,
|
|
12
16
|
StructuredRenderersContext,
|
|
13
17
|
Timeline,
|
|
14
18
|
ToolRow,
|
|
19
|
+
attachmentToInputContent,
|
|
20
|
+
buildUserContent,
|
|
15
21
|
directChildren,
|
|
16
22
|
formatToolInput,
|
|
17
23
|
formatToolResult,
|
|
24
|
+
mintReferenceId,
|
|
18
25
|
normalizeResult,
|
|
26
|
+
objectToStateEntry,
|
|
19
27
|
partitionChildrenByToolCall,
|
|
20
28
|
pendingToolAnchorExists,
|
|
29
|
+
referenceMarker,
|
|
30
|
+
serializeReferences,
|
|
21
31
|
topLevelGroups,
|
|
22
32
|
useActivity,
|
|
23
33
|
useDrill,
|
|
24
34
|
useInterruptBridge,
|
|
25
|
-
useInterruptFor
|
|
26
|
-
|
|
35
|
+
useInterruptFor,
|
|
36
|
+
useReferences,
|
|
37
|
+
withReferenceState
|
|
38
|
+
} from "./chunk-4TDUEMK5.js";
|
|
27
39
|
|
|
28
40
|
// src/context/KabooProvider.tsx
|
|
29
41
|
import { CopilotKit } from "@copilotkit/react-core/v2";
|
|
@@ -36,6 +48,7 @@ function KabooProvider({
|
|
|
36
48
|
interruptRenderers,
|
|
37
49
|
disableInterruptHandler = false,
|
|
38
50
|
disableInlineCards = false,
|
|
51
|
+
references,
|
|
39
52
|
copilotKitProps,
|
|
40
53
|
children
|
|
41
54
|
}) {
|
|
@@ -47,17 +60,907 @@ function KabooProvider({
|
|
|
47
60
|
threadId,
|
|
48
61
|
useSingleEndpoint: false,
|
|
49
62
|
...copilotKitProps,
|
|
50
|
-
children: /* @__PURE__ */ jsx(KabooActivityProvider, { agentId: agent, structuredRenderers, children: /* @__PURE__ */ jsx(DrillProvider, { children: /* @__PURE__ */
|
|
63
|
+
children: /* @__PURE__ */ jsx(KabooActivityProvider, { agentId: agent, structuredRenderers, children: /* @__PURE__ */ jsx(DrillProvider, { children: /* @__PURE__ */ jsx(InterruptBridgeProvider, { children: /* @__PURE__ */ jsxs(ReferencesProvider, { providers: references, syncObjectStateTo: agent, children: [
|
|
51
64
|
!disableInterruptHandler && /* @__PURE__ */ jsx(KabooInterruptHandler, { agentId: agent, renderers: interruptRenderers }),
|
|
52
65
|
!disableInlineCards && /* @__PURE__ */ jsx(KabooInlineCards, {}),
|
|
53
66
|
children
|
|
54
|
-
] }) }) })
|
|
67
|
+
] }) }) }) })
|
|
55
68
|
}
|
|
56
69
|
);
|
|
57
70
|
}
|
|
58
71
|
|
|
72
|
+
// src/references/ReferenceInput.tsx
|
|
73
|
+
import {
|
|
74
|
+
forwardRef,
|
|
75
|
+
useCallback,
|
|
76
|
+
useEffect,
|
|
77
|
+
useLayoutEffect,
|
|
78
|
+
useMemo,
|
|
79
|
+
useRef,
|
|
80
|
+
useState
|
|
81
|
+
} from "react";
|
|
82
|
+
import { createPortal } from "react-dom";
|
|
83
|
+
import { CopilotChatInput, useAgent, useCopilotChatConfiguration } from "@copilotkit/react-core/v2";
|
|
84
|
+
|
|
85
|
+
// src/references/uploadProvider.tsx
|
|
86
|
+
var UPLOAD_MARKER = "__kabooUpload";
|
|
87
|
+
var DEFAULT_MAX_SIZE = 20 * 1024 * 1024;
|
|
88
|
+
function readAsBase64(file) {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const reader = new FileReader();
|
|
91
|
+
reader.onload = () => {
|
|
92
|
+
const result = String(reader.result);
|
|
93
|
+
const comma = result.indexOf(",");
|
|
94
|
+
resolve(comma >= 0 ? result.slice(comma + 1) : result);
|
|
95
|
+
};
|
|
96
|
+
reader.onerror = () => reject(reader.error);
|
|
97
|
+
reader.readAsDataURL(file);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async function uploadFileToReference(file, config) {
|
|
101
|
+
const kind = config.kind ?? "file";
|
|
102
|
+
const id = mintReferenceId(kind);
|
|
103
|
+
const maxSize = config.maxSize ?? DEFAULT_MAX_SIZE;
|
|
104
|
+
if (file.size > maxSize) {
|
|
105
|
+
throw new Error(`File "${file.name}" exceeds the ${maxSize}-byte limit.`);
|
|
106
|
+
}
|
|
107
|
+
if (config.onUpload) {
|
|
108
|
+
const result = await config.onUpload(file);
|
|
109
|
+
const mimeType = result.mimeType ?? file.type ?? "application/octet-stream";
|
|
110
|
+
const source = result.type === "url" ? { url: result.value } : { data: result.value };
|
|
111
|
+
return { transport: "attachment", kind, id, name: file.name, mimeType, source };
|
|
112
|
+
}
|
|
113
|
+
const data = await readAsBase64(file);
|
|
114
|
+
return {
|
|
115
|
+
transport: "attachment",
|
|
116
|
+
kind,
|
|
117
|
+
id,
|
|
118
|
+
name: file.name,
|
|
119
|
+
mimeType: file.type || "application/octet-stream",
|
|
120
|
+
source: { data }
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function uploadProvider(config = {}) {
|
|
124
|
+
return {
|
|
125
|
+
id: config.id ?? "upload",
|
|
126
|
+
label: config.label ?? "Upload",
|
|
127
|
+
[UPLOAD_MARKER]: {
|
|
128
|
+
kind: config.kind ?? "file",
|
|
129
|
+
maxSize: config.maxSize ?? DEFAULT_MAX_SIZE,
|
|
130
|
+
accept: config.accept,
|
|
131
|
+
onUpload: config.onUpload
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function isUploadProvider(provider) {
|
|
136
|
+
return UPLOAD_MARKER in provider;
|
|
137
|
+
}
|
|
138
|
+
function buildAttachmentsConfig(config = {}) {
|
|
139
|
+
const kind = config.kind ?? "file";
|
|
140
|
+
return {
|
|
141
|
+
enabled: true,
|
|
142
|
+
accept: config.accept,
|
|
143
|
+
maxSize: config.maxSize,
|
|
144
|
+
onUpload: async (file) => {
|
|
145
|
+
const id = mintReferenceId(kind);
|
|
146
|
+
const kabooMeta = {
|
|
147
|
+
[REFERENCE_METADATA_KEYS.id]: id,
|
|
148
|
+
[REFERENCE_METADATA_KEYS.kind]: kind,
|
|
149
|
+
[REFERENCE_METADATA_KEYS.name]: file.name
|
|
150
|
+
};
|
|
151
|
+
if (config.onUpload) {
|
|
152
|
+
const result = await config.onUpload(file);
|
|
153
|
+
return { ...result, metadata: { ...result.metadata ?? {}, ...kabooMeta } };
|
|
154
|
+
}
|
|
155
|
+
const value = await readAsBase64(file);
|
|
156
|
+
return {
|
|
157
|
+
type: "data",
|
|
158
|
+
value,
|
|
159
|
+
mimeType: file.type || "application/octet-stream",
|
|
160
|
+
metadata: kabooMeta
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/references/ReferenceInput.tsx
|
|
167
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
168
|
+
var CHIP_CLASS = "kaboo-chip";
|
|
169
|
+
var NBSP = "\xA0";
|
|
170
|
+
var AT_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"/></svg>';
|
|
171
|
+
var FILE_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>';
|
|
172
|
+
var X_SVG = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>';
|
|
173
|
+
var AttachIcon = /* @__PURE__ */ jsx2("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
|
|
174
|
+
var AtIcon = /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
175
|
+
/* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "4" }),
|
|
176
|
+
/* @__PURE__ */ jsx2("path", { d: "M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94" })
|
|
177
|
+
] });
|
|
178
|
+
var PlusIcon = /* @__PURE__ */ jsx2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "M5 12h14M12 5v14" }) });
|
|
179
|
+
var SearchIcon = /* @__PURE__ */ jsxs2("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
180
|
+
/* @__PURE__ */ jsx2("circle", { cx: "11", cy: "11", r: "7" }),
|
|
181
|
+
/* @__PURE__ */ jsx2("path", { d: "m21 21-4.3-4.3" })
|
|
182
|
+
] });
|
|
183
|
+
var POPOVER_WIDTH = 320;
|
|
184
|
+
var VIEWPORT_MARGIN = 8;
|
|
185
|
+
var ANCHOR_GAP = 6;
|
|
186
|
+
function computePosition(rect) {
|
|
187
|
+
if (!rect) return null;
|
|
188
|
+
const width = Math.min(POPOVER_WIDTH, window.innerWidth - VIEWPORT_MARGIN * 2);
|
|
189
|
+
const left = Math.max(
|
|
190
|
+
VIEWPORT_MARGIN,
|
|
191
|
+
Math.min(rect.left, window.innerWidth - width - VIEWPORT_MARGIN)
|
|
192
|
+
);
|
|
193
|
+
return { left, bottom: window.innerHeight - rect.top + ANCHOR_GAP, width };
|
|
194
|
+
}
|
|
195
|
+
function isImageMime(mime) {
|
|
196
|
+
return mime.startsWith("image/");
|
|
197
|
+
}
|
|
198
|
+
function previewSrc(ref) {
|
|
199
|
+
return "url" in ref.source ? ref.source.url : `data:${ref.mimeType};base64,${ref.source.data}`;
|
|
200
|
+
}
|
|
201
|
+
function serializeEditor(root) {
|
|
202
|
+
let out = "";
|
|
203
|
+
const walk = (node) => {
|
|
204
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
205
|
+
out += node.textContent ?? "";
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (node instanceof HTMLElement) {
|
|
209
|
+
if (node.classList.contains(CHIP_CLASS)) {
|
|
210
|
+
const name = node.dataset.name ?? "";
|
|
211
|
+
out += node.dataset.transport === "object" ? `@${name}` : name;
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (node.tagName === "BR") {
|
|
215
|
+
out += "\n";
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (node.tagName === "DIV" && out.length > 0 && !out.endsWith("\n")) out += "\n";
|
|
219
|
+
node.childNodes.forEach(walk);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
root.childNodes.forEach(walk);
|
|
223
|
+
return out.replace(new RegExp(NBSP, "g"), " ");
|
|
224
|
+
}
|
|
225
|
+
function KabooReferenceInput({ trigger = "@", ...inputProps }) {
|
|
226
|
+
const {
|
|
227
|
+
providers,
|
|
228
|
+
pending,
|
|
229
|
+
addReference,
|
|
230
|
+
removeReference,
|
|
231
|
+
clearReferences,
|
|
232
|
+
recordMessageReferences
|
|
233
|
+
} = useReferences();
|
|
234
|
+
const config = useCopilotChatConfiguration();
|
|
235
|
+
const configAgentId = config?.agentId;
|
|
236
|
+
const { agent } = useAgent(configAgentId ? { agentId: configAgentId } : void 0);
|
|
237
|
+
const placeholder = config?.labels?.chatInputPlaceholder ?? "Type a message...";
|
|
238
|
+
const wrapRef = useRef(null);
|
|
239
|
+
const trayRef = useRef(null);
|
|
240
|
+
const menuRef = useRef(null);
|
|
241
|
+
const editorRef = useRef(null);
|
|
242
|
+
const fileInputRef = useRef(null);
|
|
243
|
+
const addBtnRef = useRef(null);
|
|
244
|
+
const searchRef = useRef(null);
|
|
245
|
+
const nativeChangeRef = useRef(void 0);
|
|
246
|
+
const anchorRef = useRef(() => null);
|
|
247
|
+
const tokenRef = useRef(null);
|
|
248
|
+
const replacingRef = useRef(null);
|
|
249
|
+
const replacingTrayIdRef = useRef(null);
|
|
250
|
+
const inlineIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
251
|
+
const commitTargetRef = useRef("inline");
|
|
252
|
+
const [open, setOpen] = useState(false);
|
|
253
|
+
const [rows, setRows] = useState([]);
|
|
254
|
+
const [active, setActive] = useState(0);
|
|
255
|
+
const [query, setQuery] = useState("");
|
|
256
|
+
const [mode, setMode] = useState("token");
|
|
257
|
+
const [pos, setPos] = useState(null);
|
|
258
|
+
const placeholderRef = useRef(placeholder);
|
|
259
|
+
placeholderRef.current = placeholder;
|
|
260
|
+
const markEmpty = useCallback(() => {
|
|
261
|
+
const root = editorRef.current;
|
|
262
|
+
if (!root) return;
|
|
263
|
+
const isEmpty = root.querySelector(`.${CHIP_CLASS}`) === null && (root.textContent ?? "").trim().length === 0;
|
|
264
|
+
root.dataset.empty = isEmpty ? "true" : "false";
|
|
265
|
+
}, []);
|
|
266
|
+
const uploadProviderDef = useMemo(() => providers.find(isUploadProvider), [providers]);
|
|
267
|
+
const searchable = useMemo(() => providers.filter((p) => typeof p.search === "function"), [providers]);
|
|
268
|
+
const actionProviders = useMemo(
|
|
269
|
+
() => providers.filter((p) => typeof p.search !== "function"),
|
|
270
|
+
[providers]
|
|
271
|
+
);
|
|
272
|
+
const buildRows = useCallback(
|
|
273
|
+
async (q) => {
|
|
274
|
+
const next = [];
|
|
275
|
+
for (const provider of actionProviders) {
|
|
276
|
+
const label = isUploadProvider(provider) ? "Attach" : provider.label;
|
|
277
|
+
if (!q || label.toLowerCase().includes(q.toLowerCase())) {
|
|
278
|
+
next.push({ kind: "action", group: label, provider });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
for (const provider of searchable) {
|
|
282
|
+
try {
|
|
283
|
+
const items = await provider.search(q);
|
|
284
|
+
for (const item of items) next.push({ kind: "item", group: provider.label, provider, item });
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return next;
|
|
289
|
+
},
|
|
290
|
+
[actionProviders, searchable]
|
|
291
|
+
);
|
|
292
|
+
const refresh = useCallback(
|
|
293
|
+
async (q) => {
|
|
294
|
+
const next = await buildRows(q);
|
|
295
|
+
setRows(next);
|
|
296
|
+
setActive(0);
|
|
297
|
+
},
|
|
298
|
+
[buildRows]
|
|
299
|
+
);
|
|
300
|
+
const close = useCallback(() => {
|
|
301
|
+
setOpen(false);
|
|
302
|
+
setRows([]);
|
|
303
|
+
setQuery("");
|
|
304
|
+
tokenRef.current = null;
|
|
305
|
+
replacingRef.current = null;
|
|
306
|
+
replacingTrayIdRef.current = null;
|
|
307
|
+
}, []);
|
|
308
|
+
const emit = useCallback(() => {
|
|
309
|
+
const root = editorRef.current;
|
|
310
|
+
if (!root) return;
|
|
311
|
+
const text = serializeEditor(root);
|
|
312
|
+
markEmpty();
|
|
313
|
+
nativeChangeRef.current?.({ target: { value: text } });
|
|
314
|
+
}, [markEmpty]);
|
|
315
|
+
const reconcile = useCallback(() => {
|
|
316
|
+
const root = editorRef.current;
|
|
317
|
+
if (!root) return;
|
|
318
|
+
const live = new Set(
|
|
319
|
+
Array.from(root.querySelectorAll(`.${CHIP_CLASS}`)).map((c) => c.dataset.refId)
|
|
320
|
+
);
|
|
321
|
+
for (const id of Array.from(inlineIdsRef.current)) {
|
|
322
|
+
if (!live.has(id)) {
|
|
323
|
+
inlineIdsRef.current.delete(id);
|
|
324
|
+
removeReference(id);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}, [removeReference]);
|
|
328
|
+
const focusEditor = useCallback(() => editorRef.current?.focus(), []);
|
|
329
|
+
const createChip = useCallback((ref) => {
|
|
330
|
+
const chip = document.createElement("span");
|
|
331
|
+
chip.className = `${CHIP_CLASS} ${CHIP_CLASS}-${ref.transport}`;
|
|
332
|
+
chip.contentEditable = "false";
|
|
333
|
+
chip.dataset.refId = ref.id;
|
|
334
|
+
chip.dataset.kind = ref.kind;
|
|
335
|
+
chip.dataset.name = ref.name;
|
|
336
|
+
chip.dataset.transport = ref.transport;
|
|
337
|
+
chip.title = ref.name;
|
|
338
|
+
if (ref.transport === "attachment" && isImageMime(ref.mimeType)) {
|
|
339
|
+
const img = document.createElement("img");
|
|
340
|
+
img.className = "kaboo-chip-thumb";
|
|
341
|
+
img.src = previewSrc(ref);
|
|
342
|
+
img.alt = ref.name;
|
|
343
|
+
chip.appendChild(img);
|
|
344
|
+
} else {
|
|
345
|
+
const ic = document.createElement("span");
|
|
346
|
+
ic.className = "kaboo-chip-ic";
|
|
347
|
+
ic.innerHTML = ref.transport === "object" ? AT_SVG : FILE_SVG;
|
|
348
|
+
chip.appendChild(ic);
|
|
349
|
+
}
|
|
350
|
+
const label = document.createElement("span");
|
|
351
|
+
label.className = "kaboo-chip-label";
|
|
352
|
+
label.textContent = ref.name;
|
|
353
|
+
chip.appendChild(label);
|
|
354
|
+
const x = document.createElement("button");
|
|
355
|
+
x.type = "button";
|
|
356
|
+
x.className = "kaboo-chip-x";
|
|
357
|
+
x.setAttribute("aria-label", `Remove ${ref.name}`);
|
|
358
|
+
x.innerHTML = X_SVG;
|
|
359
|
+
x.addEventListener("mousedown", (e) => {
|
|
360
|
+
e.preventDefault();
|
|
361
|
+
e.stopPropagation();
|
|
362
|
+
handlersRef.current.removeChip(chip);
|
|
363
|
+
});
|
|
364
|
+
chip.appendChild(x);
|
|
365
|
+
chip.addEventListener("mousedown", (e) => {
|
|
366
|
+
if (e.target === x || x.contains(e.target)) return;
|
|
367
|
+
e.preventDefault();
|
|
368
|
+
handlersRef.current.replaceChip(chip);
|
|
369
|
+
});
|
|
370
|
+
return chip;
|
|
371
|
+
}, []);
|
|
372
|
+
const insertAtCaret = useCallback((node) => {
|
|
373
|
+
const root = editorRef.current;
|
|
374
|
+
if (!root) return;
|
|
375
|
+
const sel = window.getSelection();
|
|
376
|
+
let range;
|
|
377
|
+
if (sel && sel.rangeCount > 0 && root.contains(sel.focusNode)) {
|
|
378
|
+
range = sel.getRangeAt(0);
|
|
379
|
+
} else {
|
|
380
|
+
range = document.createRange();
|
|
381
|
+
range.selectNodeContents(root);
|
|
382
|
+
range.collapse(false);
|
|
383
|
+
}
|
|
384
|
+
range.collapse(false);
|
|
385
|
+
const space = document.createTextNode(NBSP);
|
|
386
|
+
range.insertNode(space);
|
|
387
|
+
range.insertNode(node);
|
|
388
|
+
const after = document.createRange();
|
|
389
|
+
after.setStartAfter(space);
|
|
390
|
+
after.collapse(true);
|
|
391
|
+
sel?.removeAllRanges();
|
|
392
|
+
sel?.addRange(after);
|
|
393
|
+
}, []);
|
|
394
|
+
const placeChip = useCallback(
|
|
395
|
+
(ref) => {
|
|
396
|
+
const chip = createChip(ref);
|
|
397
|
+
const replacing = replacingRef.current;
|
|
398
|
+
if (replacing) {
|
|
399
|
+
const oldId = replacing.dataset.refId;
|
|
400
|
+
if (oldId && oldId !== ref.id) removeReference(oldId);
|
|
401
|
+
replacing.replaceWith(chip);
|
|
402
|
+
replacingRef.current = null;
|
|
403
|
+
} else if (tokenRef.current) {
|
|
404
|
+
const { node, start } = tokenRef.current;
|
|
405
|
+
const sel = window.getSelection();
|
|
406
|
+
const range = document.createRange();
|
|
407
|
+
range.setStart(node, Math.min(start, node.textContent?.length ?? 0));
|
|
408
|
+
if (sel && sel.focusNode && editorRef.current?.contains(sel.focusNode)) {
|
|
409
|
+
range.setEnd(sel.focusNode, sel.focusOffset);
|
|
410
|
+
} else {
|
|
411
|
+
range.setEnd(node, node.textContent?.length ?? 0);
|
|
412
|
+
}
|
|
413
|
+
range.deleteContents();
|
|
414
|
+
const space = document.createTextNode(NBSP);
|
|
415
|
+
range.insertNode(space);
|
|
416
|
+
range.insertNode(chip);
|
|
417
|
+
const after = document.createRange();
|
|
418
|
+
after.setStartAfter(space);
|
|
419
|
+
after.collapse(true);
|
|
420
|
+
sel?.removeAllRanges();
|
|
421
|
+
sel?.addRange(after);
|
|
422
|
+
tokenRef.current = null;
|
|
423
|
+
} else {
|
|
424
|
+
insertAtCaret(chip);
|
|
425
|
+
}
|
|
426
|
+
emit();
|
|
427
|
+
},
|
|
428
|
+
[createChip, insertAtCaret, removeReference, emit]
|
|
429
|
+
);
|
|
430
|
+
const commit = useCallback(
|
|
431
|
+
(ref, target) => {
|
|
432
|
+
addReference(ref);
|
|
433
|
+
if (target === "inline") {
|
|
434
|
+
inlineIdsRef.current.add(ref.id);
|
|
435
|
+
placeChip(ref);
|
|
436
|
+
} else {
|
|
437
|
+
const oldId = replacingTrayIdRef.current;
|
|
438
|
+
if (oldId && oldId !== ref.id) removeReference(oldId);
|
|
439
|
+
replacingTrayIdRef.current = null;
|
|
440
|
+
inlineIdsRef.current.delete(ref.id);
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
[addReference, placeChip, removeReference]
|
|
444
|
+
);
|
|
445
|
+
const openFilePicker = useCallback(
|
|
446
|
+
(provider, target) => {
|
|
447
|
+
const input = fileInputRef.current;
|
|
448
|
+
if (!input) return;
|
|
449
|
+
const cfg = isUploadProvider(provider) ? provider[UPLOAD_MARKER] : {};
|
|
450
|
+
input.accept = cfg.accept ?? "";
|
|
451
|
+
input.onchange = async () => {
|
|
452
|
+
const file = input.files?.[0];
|
|
453
|
+
input.value = "";
|
|
454
|
+
if (!file) return;
|
|
455
|
+
try {
|
|
456
|
+
const ref = await uploadFileToReference(file, cfg);
|
|
457
|
+
commit(ref, target);
|
|
458
|
+
} catch (err) {
|
|
459
|
+
console.error("[kaboo] upload failed", err);
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
input.click();
|
|
463
|
+
},
|
|
464
|
+
[commit]
|
|
465
|
+
);
|
|
466
|
+
const stripToken = useCallback(() => {
|
|
467
|
+
const t = tokenRef.current;
|
|
468
|
+
if (!t) return;
|
|
469
|
+
const sel = window.getSelection();
|
|
470
|
+
const range = document.createRange();
|
|
471
|
+
range.setStart(t.node, Math.min(t.start, t.node.textContent?.length ?? 0));
|
|
472
|
+
if (sel?.focusNode && editorRef.current?.contains(sel.focusNode)) {
|
|
473
|
+
range.setEnd(sel.focusNode, sel.focusOffset);
|
|
474
|
+
} else {
|
|
475
|
+
range.setEnd(t.node, t.node.textContent?.length ?? 0);
|
|
476
|
+
}
|
|
477
|
+
range.deleteContents();
|
|
478
|
+
emit();
|
|
479
|
+
}, [emit]);
|
|
480
|
+
const select = useCallback(
|
|
481
|
+
async (row) => {
|
|
482
|
+
const target = commitTargetRef.current;
|
|
483
|
+
if (row.kind === "action") {
|
|
484
|
+
const provider = row.provider;
|
|
485
|
+
if (target === "inline") stripToken();
|
|
486
|
+
close();
|
|
487
|
+
if (isUploadProvider(provider)) openFilePicker(provider, target);
|
|
488
|
+
else await provider.onSelect?.({ id: provider.id, label: provider.label });
|
|
489
|
+
focusEditor();
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
await row.provider.onSelect?.(row.item);
|
|
493
|
+
const ref = row.provider.toReference?.(row.item);
|
|
494
|
+
if (ref) commit(ref, target);
|
|
495
|
+
close();
|
|
496
|
+
focusEditor();
|
|
497
|
+
},
|
|
498
|
+
[stripToken, close, openFilePicker, focusEditor, commit]
|
|
499
|
+
);
|
|
500
|
+
const openFromToken = useCallback(
|
|
501
|
+
(q) => {
|
|
502
|
+
commitTargetRef.current = "inline";
|
|
503
|
+
replacingRef.current = null;
|
|
504
|
+
replacingTrayIdRef.current = null;
|
|
505
|
+
anchorRef.current = () => {
|
|
506
|
+
const t = tokenRef.current;
|
|
507
|
+
return t ? caretRectFor(t.node, t.start) : null;
|
|
508
|
+
};
|
|
509
|
+
setMode("token");
|
|
510
|
+
setQuery(q);
|
|
511
|
+
setPos(computePosition(anchorRef.current()));
|
|
512
|
+
setOpen(true);
|
|
513
|
+
void refresh(q);
|
|
514
|
+
},
|
|
515
|
+
[refresh]
|
|
516
|
+
);
|
|
517
|
+
const toggleFromButton = useCallback(() => {
|
|
518
|
+
setOpen((prev) => {
|
|
519
|
+
if (prev) {
|
|
520
|
+
close();
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
commitTargetRef.current = "tray";
|
|
524
|
+
tokenRef.current = null;
|
|
525
|
+
replacingRef.current = null;
|
|
526
|
+
replacingTrayIdRef.current = null;
|
|
527
|
+
anchorRef.current = () => addBtnRef.current?.getBoundingClientRect() ?? null;
|
|
528
|
+
setMode("menu");
|
|
529
|
+
setQuery("");
|
|
530
|
+
setPos(computePosition(anchorRef.current()));
|
|
531
|
+
void refresh("");
|
|
532
|
+
return true;
|
|
533
|
+
});
|
|
534
|
+
}, [refresh, close]);
|
|
535
|
+
const onSearch = useCallback(
|
|
536
|
+
(value) => {
|
|
537
|
+
setQuery(value);
|
|
538
|
+
void refresh(value);
|
|
539
|
+
},
|
|
540
|
+
[refresh]
|
|
541
|
+
);
|
|
542
|
+
const handleNavKey = useCallback(
|
|
543
|
+
(e) => {
|
|
544
|
+
if (!open || rows.length === 0) return false;
|
|
545
|
+
if (e.key === "ArrowDown") {
|
|
546
|
+
e.preventDefault();
|
|
547
|
+
setActive((a) => (a + 1) % rows.length);
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
if (e.key === "ArrowUp") {
|
|
551
|
+
e.preventDefault();
|
|
552
|
+
setActive((a) => (a - 1 + rows.length) % rows.length);
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
if (e.key === "Enter" || e.key === "Tab") {
|
|
556
|
+
e.preventDefault();
|
|
557
|
+
void select(rows[active]);
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
if (e.key === "Escape") {
|
|
561
|
+
e.preventDefault();
|
|
562
|
+
close();
|
|
563
|
+
focusEditor();
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
return false;
|
|
567
|
+
},
|
|
568
|
+
[open, rows, active, select, close, focusEditor]
|
|
569
|
+
);
|
|
570
|
+
const replaceChip = useCallback(
|
|
571
|
+
(chip) => {
|
|
572
|
+
commitTargetRef.current = "inline";
|
|
573
|
+
replacingRef.current = chip;
|
|
574
|
+
replacingTrayIdRef.current = null;
|
|
575
|
+
tokenRef.current = null;
|
|
576
|
+
anchorRef.current = () => chip.getBoundingClientRect();
|
|
577
|
+
setMode("menu");
|
|
578
|
+
setQuery("");
|
|
579
|
+
setPos(computePosition(chip.getBoundingClientRect()));
|
|
580
|
+
setOpen(true);
|
|
581
|
+
void refresh("");
|
|
582
|
+
},
|
|
583
|
+
[refresh]
|
|
584
|
+
);
|
|
585
|
+
const replaceTrayChip = useCallback(
|
|
586
|
+
(id, anchorEl) => {
|
|
587
|
+
commitTargetRef.current = "tray";
|
|
588
|
+
replacingTrayIdRef.current = id;
|
|
589
|
+
replacingRef.current = null;
|
|
590
|
+
tokenRef.current = null;
|
|
591
|
+
anchorRef.current = () => anchorEl.getBoundingClientRect();
|
|
592
|
+
setMode("menu");
|
|
593
|
+
setQuery("");
|
|
594
|
+
setPos(computePosition(anchorEl.getBoundingClientRect()));
|
|
595
|
+
setOpen(true);
|
|
596
|
+
void refresh("");
|
|
597
|
+
},
|
|
598
|
+
[refresh]
|
|
599
|
+
);
|
|
600
|
+
const removeChip = useCallback(
|
|
601
|
+
(chip) => {
|
|
602
|
+
const id = chip.dataset.refId;
|
|
603
|
+
const next = chip.nextSibling;
|
|
604
|
+
chip.remove();
|
|
605
|
+
if (next && next.nodeType === Node.TEXT_NODE && next.textContent === NBSP) next.remove();
|
|
606
|
+
if (id) {
|
|
607
|
+
inlineIdsRef.current.delete(id);
|
|
608
|
+
removeReference(id);
|
|
609
|
+
}
|
|
610
|
+
emit();
|
|
611
|
+
focusEditor();
|
|
612
|
+
},
|
|
613
|
+
[removeReference, emit, focusEditor]
|
|
614
|
+
);
|
|
615
|
+
const handlersRef = useRef({ removeChip, replaceChip });
|
|
616
|
+
handlersRef.current = { removeChip, replaceChip };
|
|
617
|
+
const ctl = { open, rows, active, trigger, openFromToken, toggleFromButton, close, handleNavKey };
|
|
618
|
+
const ctlRef = useRef(ctl);
|
|
619
|
+
ctlRef.current = ctl;
|
|
620
|
+
const readToken = useCallback(() => {
|
|
621
|
+
const sel = window.getSelection();
|
|
622
|
+
if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return null;
|
|
623
|
+
const node = sel.focusNode;
|
|
624
|
+
if (!node || node.nodeType !== Node.TEXT_NODE) return null;
|
|
625
|
+
const text = node.textContent ?? "";
|
|
626
|
+
const before = text.slice(0, sel.focusOffset);
|
|
627
|
+
const at = before.lastIndexOf(ctlRef.current.trigger);
|
|
628
|
+
if (at < 0) return null;
|
|
629
|
+
const prev = at > 0 ? before[at - 1] : "";
|
|
630
|
+
if (prev && !/\s/.test(prev)) return null;
|
|
631
|
+
const q = before.slice(at + 1);
|
|
632
|
+
if (/\s/.test(q)) return null;
|
|
633
|
+
return { node, start: at, query: q };
|
|
634
|
+
}, []);
|
|
635
|
+
const onEditorInput = useCallback(() => {
|
|
636
|
+
reconcile();
|
|
637
|
+
emit();
|
|
638
|
+
const token = readToken();
|
|
639
|
+
if (token) {
|
|
640
|
+
tokenRef.current = { node: token.node, start: token.start };
|
|
641
|
+
const c = ctlRef.current;
|
|
642
|
+
if (!c.open || mode === "token") openFromToken(token.query);
|
|
643
|
+
else {
|
|
644
|
+
setQuery(token.query);
|
|
645
|
+
void refresh(token.query);
|
|
646
|
+
}
|
|
647
|
+
} else if (ctlRef.current.open && mode === "token") {
|
|
648
|
+
close();
|
|
649
|
+
}
|
|
650
|
+
}, [reconcile, emit, readToken, openFromToken, refresh, mode, close]);
|
|
651
|
+
const TextAreaSlot = useMemo(
|
|
652
|
+
() => forwardRef(function KabooRefEditor(props, ref) {
|
|
653
|
+
const { onChange, onKeyDown, className, autoFocus } = props;
|
|
654
|
+
nativeChangeRef.current = onChange;
|
|
655
|
+
const setRefs = (node) => {
|
|
656
|
+
editorRef.current = node;
|
|
657
|
+
if (typeof ref === "function") ref(node);
|
|
658
|
+
else if (ref) ref.current = node;
|
|
659
|
+
};
|
|
660
|
+
const handleKeyDown = (e) => {
|
|
661
|
+
if (ctlRef.current.handleNavKey(e)) return;
|
|
662
|
+
onKeyDown?.(e);
|
|
663
|
+
};
|
|
664
|
+
return /* @__PURE__ */ jsx2(
|
|
665
|
+
"div",
|
|
666
|
+
{
|
|
667
|
+
ref: setRefs,
|
|
668
|
+
role: "textbox",
|
|
669
|
+
"aria-multiline": "true",
|
|
670
|
+
tabIndex: 0,
|
|
671
|
+
contentEditable: true,
|
|
672
|
+
suppressContentEditableWarning: true,
|
|
673
|
+
"data-placeholder": placeholderRef.current,
|
|
674
|
+
className: `kaboo-ref-editor cpk:bg-transparent cpk:outline-none cpk:antialiased cpk:leading-relaxed cpk:text-[16px] ${className ?? ""}`,
|
|
675
|
+
"data-empty": "true",
|
|
676
|
+
autoFocus,
|
|
677
|
+
onInput: onEditorInput,
|
|
678
|
+
onKeyDown: handleKeyDown
|
|
679
|
+
}
|
|
680
|
+
);
|
|
681
|
+
}),
|
|
682
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
683
|
+
[]
|
|
684
|
+
);
|
|
685
|
+
const AddMenuButtonSlot = useMemo(
|
|
686
|
+
() => function KabooRefAddButton(props) {
|
|
687
|
+
return /* @__PURE__ */ jsx2(
|
|
688
|
+
"button",
|
|
689
|
+
{
|
|
690
|
+
ref: addBtnRef,
|
|
691
|
+
type: "button",
|
|
692
|
+
className: "kaboo-ref-add",
|
|
693
|
+
"aria-label": "Add attachment or reference",
|
|
694
|
+
"aria-haspopup": "listbox",
|
|
695
|
+
"aria-expanded": ctlRef.current.open,
|
|
696
|
+
disabled: props.disabled,
|
|
697
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
698
|
+
onClick: () => ctlRef.current.toggleFromButton(),
|
|
699
|
+
children: PlusIcon
|
|
700
|
+
}
|
|
701
|
+
);
|
|
702
|
+
},
|
|
703
|
+
[]
|
|
704
|
+
);
|
|
705
|
+
useEffect(() => {
|
|
706
|
+
if (!open) return;
|
|
707
|
+
const onDocMouseDown = (e) => {
|
|
708
|
+
const target = e.target;
|
|
709
|
+
if (wrapRef.current?.contains(target) || menuRef.current?.contains(target)) return;
|
|
710
|
+
close();
|
|
711
|
+
};
|
|
712
|
+
const onDocKey = (e) => {
|
|
713
|
+
if (e.key === "Escape") close();
|
|
714
|
+
};
|
|
715
|
+
document.addEventListener("mousedown", onDocMouseDown);
|
|
716
|
+
document.addEventListener("keydown", onDocKey);
|
|
717
|
+
return () => {
|
|
718
|
+
document.removeEventListener("mousedown", onDocMouseDown);
|
|
719
|
+
document.removeEventListener("keydown", onDocKey);
|
|
720
|
+
};
|
|
721
|
+
}, [open, close]);
|
|
722
|
+
useEffect(() => {
|
|
723
|
+
if (!open) return;
|
|
724
|
+
const reposition = () => setPos(computePosition(anchorRef.current()));
|
|
725
|
+
window.addEventListener("resize", reposition);
|
|
726
|
+
window.addEventListener("scroll", reposition, true);
|
|
727
|
+
return () => {
|
|
728
|
+
window.removeEventListener("resize", reposition);
|
|
729
|
+
window.removeEventListener("scroll", reposition, true);
|
|
730
|
+
};
|
|
731
|
+
}, [open]);
|
|
732
|
+
useLayoutEffect(() => {
|
|
733
|
+
if (!open) return;
|
|
734
|
+
setPos(computePosition(anchorRef.current()));
|
|
735
|
+
if (mode === "menu") searchRef.current?.focus();
|
|
736
|
+
}, [open, mode, rows.length]);
|
|
737
|
+
useEffect(() => {
|
|
738
|
+
const target = agent;
|
|
739
|
+
if (!target || typeof target.subscribe !== "function") return;
|
|
740
|
+
const sub = target.subscribe({ onRunStartedEvent: () => clearReferences() });
|
|
741
|
+
return () => sub?.unsubscribe?.();
|
|
742
|
+
}, [agent, clearReferences]);
|
|
743
|
+
useEffect(() => {
|
|
744
|
+
const root = editorRef.current;
|
|
745
|
+
if (!root) return;
|
|
746
|
+
if ((inputProps.value ?? "") === "" && root.childNodes.length > 0) {
|
|
747
|
+
root.textContent = "";
|
|
748
|
+
markEmpty();
|
|
749
|
+
}
|
|
750
|
+
}, [inputProps.value, markEmpty]);
|
|
751
|
+
const handleSubmit = useCallback(
|
|
752
|
+
(message) => {
|
|
753
|
+
const text = message.trim();
|
|
754
|
+
const { attachmentParts } = serializeReferences(pending);
|
|
755
|
+
const target = agent;
|
|
756
|
+
if (text.length === 0 && attachmentParts.length === 0) return;
|
|
757
|
+
if (!target?.addMessage || !target.runAgent) {
|
|
758
|
+
inputProps.onSubmitMessage?.(message);
|
|
759
|
+
close();
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const content = attachmentParts.length > 0 ? buildUserContent(text, attachmentParts) : text;
|
|
763
|
+
const id = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
764
|
+
recordMessageReferences(
|
|
765
|
+
id,
|
|
766
|
+
pending.filter((r) => r.transport === "object")
|
|
767
|
+
);
|
|
768
|
+
target.addMessage({ id, role: "user", content });
|
|
769
|
+
void Promise.resolve(target.runAgent()).catch(
|
|
770
|
+
(err) => console.error("[kaboo] runAgent failed", err)
|
|
771
|
+
);
|
|
772
|
+
const root = editorRef.current;
|
|
773
|
+
if (root) root.textContent = "";
|
|
774
|
+
markEmpty();
|
|
775
|
+
close();
|
|
776
|
+
},
|
|
777
|
+
[pending, agent, inputProps, close, markEmpty, recordMessageReferences]
|
|
778
|
+
);
|
|
779
|
+
const trayRefs = pending.filter((r) => !inlineIdsRef.current.has(r.id));
|
|
780
|
+
useLayoutEffect(() => {
|
|
781
|
+
const tray = trayRef.current;
|
|
782
|
+
const wrap = wrapRef.current;
|
|
783
|
+
if (!tray || !wrap) return;
|
|
784
|
+
const align = () => {
|
|
785
|
+
const pill = wrap.querySelector(".copilotKitInput");
|
|
786
|
+
if (!pill) return;
|
|
787
|
+
const pr = pill.getBoundingClientRect();
|
|
788
|
+
const wr = wrap.getBoundingClientRect();
|
|
789
|
+
tray.style.marginLeft = `${Math.max(0, pr.left - wr.left)}px`;
|
|
790
|
+
tray.style.width = `${pr.width}px`;
|
|
791
|
+
};
|
|
792
|
+
align();
|
|
793
|
+
window.addEventListener("resize", align);
|
|
794
|
+
return () => window.removeEventListener("resize", align);
|
|
795
|
+
}, [trayRefs.length]);
|
|
796
|
+
return /* @__PURE__ */ jsxs2("div", { ref: wrapRef, className: "kaboo-ref-input", children: [
|
|
797
|
+
/* @__PURE__ */ jsx2("input", { ref: fileInputRef, type: "file", className: "kaboo-ref-file", tabIndex: -1, "aria-hidden": "true" }),
|
|
798
|
+
trayRefs.length > 0 && /* @__PURE__ */ jsx2("div", { ref: trayRef, className: "kaboo-ref-tray", children: trayRefs.map((r) => /* @__PURE__ */ jsx2(
|
|
799
|
+
TrayChip,
|
|
800
|
+
{
|
|
801
|
+
reference: r,
|
|
802
|
+
onRemove: () => removeReference(r.id),
|
|
803
|
+
onReplace: (el) => replaceTrayChip(r.id, el)
|
|
804
|
+
},
|
|
805
|
+
r.id
|
|
806
|
+
)) }),
|
|
807
|
+
/* @__PURE__ */ jsx2(
|
|
808
|
+
CopilotChatInput,
|
|
809
|
+
{
|
|
810
|
+
...inputProps,
|
|
811
|
+
onSubmitMessage: handleSubmit,
|
|
812
|
+
textArea: TextAreaSlot,
|
|
813
|
+
addMenuButton: AddMenuButtonSlot
|
|
814
|
+
}
|
|
815
|
+
),
|
|
816
|
+
open && pos && createPortal(
|
|
817
|
+
/* @__PURE__ */ jsx2(
|
|
818
|
+
ReferencePopover,
|
|
819
|
+
{
|
|
820
|
+
ref: menuRef,
|
|
821
|
+
pos,
|
|
822
|
+
rows,
|
|
823
|
+
active,
|
|
824
|
+
query,
|
|
825
|
+
mode,
|
|
826
|
+
searchRef,
|
|
827
|
+
hasUpload: !!uploadProviderDef,
|
|
828
|
+
onSearch,
|
|
829
|
+
onSearchKeyDown: handleNavKey,
|
|
830
|
+
onHover: setActive,
|
|
831
|
+
onPick: select
|
|
832
|
+
}
|
|
833
|
+
),
|
|
834
|
+
document.body
|
|
835
|
+
)
|
|
836
|
+
] });
|
|
837
|
+
}
|
|
838
|
+
function TrayChip({
|
|
839
|
+
reference,
|
|
840
|
+
onRemove,
|
|
841
|
+
onReplace
|
|
842
|
+
}) {
|
|
843
|
+
const ref = useRef(null);
|
|
844
|
+
const isImage = reference.transport === "attachment" && isImageMime(reference.mimeType);
|
|
845
|
+
return /* @__PURE__ */ jsxs2(
|
|
846
|
+
"span",
|
|
847
|
+
{
|
|
848
|
+
ref,
|
|
849
|
+
className: `kaboo-chip ${CHIP_CLASS}-${reference.transport} kaboo-chip-tray`,
|
|
850
|
+
title: reference.name,
|
|
851
|
+
role: "button",
|
|
852
|
+
tabIndex: 0,
|
|
853
|
+
onMouseDown: (e) => {
|
|
854
|
+
e.preventDefault();
|
|
855
|
+
if (ref.current) onReplace(ref.current);
|
|
856
|
+
},
|
|
857
|
+
children: [
|
|
858
|
+
isImage ? /* @__PURE__ */ jsx2(
|
|
859
|
+
"img",
|
|
860
|
+
{
|
|
861
|
+
className: "kaboo-chip-thumb",
|
|
862
|
+
src: previewSrc(reference),
|
|
863
|
+
alt: reference.name
|
|
864
|
+
}
|
|
865
|
+
) : /* @__PURE__ */ jsx2("span", { className: "kaboo-chip-ic", children: reference.transport === "object" ? AtIcon : AttachIcon }),
|
|
866
|
+
/* @__PURE__ */ jsx2("span", { className: "kaboo-chip-label", children: reference.name }),
|
|
867
|
+
/* @__PURE__ */ jsx2(
|
|
868
|
+
"button",
|
|
869
|
+
{
|
|
870
|
+
type: "button",
|
|
871
|
+
className: "kaboo-chip-x",
|
|
872
|
+
"aria-label": `Remove ${reference.name}`,
|
|
873
|
+
onMouseDown: (e) => {
|
|
874
|
+
e.preventDefault();
|
|
875
|
+
e.stopPropagation();
|
|
876
|
+
onRemove();
|
|
877
|
+
},
|
|
878
|
+
children: /* @__PURE__ */ jsx2("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "M18 6 6 18M6 6l12 12" }) })
|
|
879
|
+
}
|
|
880
|
+
)
|
|
881
|
+
]
|
|
882
|
+
}
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
function caretRectFor(node, offset) {
|
|
886
|
+
try {
|
|
887
|
+
const range = document.createRange();
|
|
888
|
+
range.setStart(node, Math.min(offset, node.textContent?.length ?? 0));
|
|
889
|
+
range.collapse(true);
|
|
890
|
+
const rect = range.getBoundingClientRect();
|
|
891
|
+
if (rect.top === 0 && rect.left === 0) {
|
|
892
|
+
const parent = node.parentElement;
|
|
893
|
+
return parent ? parent.getBoundingClientRect() : null;
|
|
894
|
+
}
|
|
895
|
+
return rect;
|
|
896
|
+
} catch {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
var ReferencePopover = forwardRef(function ReferencePopover2({ pos, rows, active, query, mode, searchRef, onSearch, onSearchKeyDown, onHover, onPick }, ref) {
|
|
901
|
+
return /* @__PURE__ */ jsxs2(
|
|
902
|
+
"div",
|
|
903
|
+
{
|
|
904
|
+
ref,
|
|
905
|
+
className: "kaboo-refmenu",
|
|
906
|
+
role: "listbox",
|
|
907
|
+
style: { left: pos.left, bottom: pos.bottom, width: pos.width },
|
|
908
|
+
children: [
|
|
909
|
+
mode === "menu" && /* @__PURE__ */ jsxs2("div", { className: "kaboo-refmenu-search", children: [
|
|
910
|
+
/* @__PURE__ */ jsx2("span", { className: "kaboo-refmenu-search-icon", children: SearchIcon }),
|
|
911
|
+
/* @__PURE__ */ jsx2(
|
|
912
|
+
"input",
|
|
913
|
+
{
|
|
914
|
+
ref: searchRef,
|
|
915
|
+
type: "text",
|
|
916
|
+
className: "kaboo-refmenu-search-input",
|
|
917
|
+
placeholder: "Search\u2026",
|
|
918
|
+
value: query,
|
|
919
|
+
onChange: (e) => onSearch(e.target.value),
|
|
920
|
+
onKeyDown: (e) => onSearchKeyDown(e)
|
|
921
|
+
}
|
|
922
|
+
)
|
|
923
|
+
] }),
|
|
924
|
+
rows.length === 0 && /* @__PURE__ */ jsx2("div", { className: "kaboo-refmenu-empty", children: "No matches" }),
|
|
925
|
+
rows.map((row, i) => {
|
|
926
|
+
const key = row.kind === "action" ? `action:${row.provider.id}` : `${row.provider.id}:${row.item.id}`;
|
|
927
|
+
const showHeader = i === 0 || rows[i - 1].group !== row.group;
|
|
928
|
+
const isUpload = row.kind === "action" && isUploadProvider(row.provider);
|
|
929
|
+
const icon = isUpload ? AttachIcon : row.kind === "item" ? row.provider.icon ?? AtIcon : row.provider.icon ?? AtIcon;
|
|
930
|
+
const title = row.kind === "action" ? isUpload ? "Attach a file" : row.provider.label : row.item.label;
|
|
931
|
+
const subtitle = row.kind === "action" ? isUpload ? "Upload from your device" : void 0 : row.item.description;
|
|
932
|
+
const custom = row.kind === "item" ? row.provider.renderItem?.(row.item) : void 0;
|
|
933
|
+
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
934
|
+
showHeader && /* @__PURE__ */ jsx2("div", { className: "kaboo-refmenu-group", children: row.group }),
|
|
935
|
+
/* @__PURE__ */ jsxs2(
|
|
936
|
+
"div",
|
|
937
|
+
{
|
|
938
|
+
role: "option",
|
|
939
|
+
"aria-selected": i === active,
|
|
940
|
+
className: `kaboo-refmenu-item${i === active ? " is-active" : ""}`,
|
|
941
|
+
onMouseEnter: () => onHover(i),
|
|
942
|
+
onMouseDown: (e) => {
|
|
943
|
+
e.preventDefault();
|
|
944
|
+
onPick(row);
|
|
945
|
+
},
|
|
946
|
+
children: [
|
|
947
|
+
/* @__PURE__ */ jsx2("span", { className: "kaboo-refmenu-icon", children: icon }),
|
|
948
|
+
custom ?? /* @__PURE__ */ jsxs2("span", { className: "kaboo-refmenu-text", children: [
|
|
949
|
+
/* @__PURE__ */ jsx2("span", { className: "kaboo-refmenu-title", children: title }),
|
|
950
|
+
subtitle && /* @__PURE__ */ jsx2("span", { className: "kaboo-refmenu-sub", children: subtitle })
|
|
951
|
+
] })
|
|
952
|
+
]
|
|
953
|
+
}
|
|
954
|
+
)
|
|
955
|
+
] }, key);
|
|
956
|
+
})
|
|
957
|
+
]
|
|
958
|
+
}
|
|
959
|
+
);
|
|
960
|
+
});
|
|
961
|
+
|
|
59
962
|
// src/components/ActivityPanel.tsx
|
|
60
|
-
import { jsx as
|
|
963
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
61
964
|
function ActivityPanel() {
|
|
62
965
|
const { groups } = useActivity();
|
|
63
966
|
const { activeDrill } = useDrill();
|
|
@@ -66,14 +969,14 @@ function ActivityPanel() {
|
|
|
66
969
|
if (filtered.length === 0 && activeDrill) {
|
|
67
970
|
const exact = groups[activeDrill];
|
|
68
971
|
if (exact) {
|
|
69
|
-
return /* @__PURE__ */
|
|
972
|
+
return /* @__PURE__ */ jsx3("div", { className: "kaboo-activity-panel", children: /* @__PURE__ */ jsx3(AgentCard, { groupId: activeDrill, group: exact }) });
|
|
70
973
|
}
|
|
71
974
|
}
|
|
72
|
-
return /* @__PURE__ */
|
|
975
|
+
return /* @__PURE__ */ jsx3("div", { className: "kaboo-activity-panel", children: filtered.map(([id, group]) => /* @__PURE__ */ jsx3(AgentCard, { groupId: id, group }, id)) });
|
|
73
976
|
}
|
|
74
977
|
|
|
75
978
|
// src/components/GlassTabs.tsx
|
|
76
|
-
import { jsx as
|
|
979
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
77
980
|
function GlassTabs() {
|
|
78
981
|
const { drillPath, drillToRoot, drillToLevel } = useDrill();
|
|
79
982
|
const { groups } = useActivity();
|
|
@@ -86,11 +989,11 @@ function GlassTabs() {
|
|
|
86
989
|
level: i
|
|
87
990
|
}))
|
|
88
991
|
];
|
|
89
|
-
return /* @__PURE__ */
|
|
992
|
+
return /* @__PURE__ */ jsx4("nav", { className: "kaboo-breadcrumb-bar", children: tabs.map((tab, i) => {
|
|
90
993
|
const isLast = i === tabs.length - 1;
|
|
91
|
-
return /* @__PURE__ */
|
|
92
|
-
i > 0 && /* @__PURE__ */
|
|
93
|
-
/* @__PURE__ */
|
|
994
|
+
return /* @__PURE__ */ jsxs3("span", { className: "kaboo-breadcrumb-item", children: [
|
|
995
|
+
i > 0 && /* @__PURE__ */ jsx4("svg", { width: 12, height: 12, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-sep", children: /* @__PURE__ */ jsx4("path", { d: "M6 4l4 4-4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }),
|
|
996
|
+
/* @__PURE__ */ jsxs3(
|
|
94
997
|
"button",
|
|
95
998
|
{
|
|
96
999
|
className: `kaboo-breadcrumb-link ${isLast ? "kaboo-breadcrumb-current" : ""}`,
|
|
@@ -101,7 +1004,7 @@ function GlassTabs() {
|
|
|
101
1004
|
},
|
|
102
1005
|
disabled: isLast,
|
|
103
1006
|
children: [
|
|
104
|
-
i === 0 && /* @__PURE__ */
|
|
1007
|
+
i === 0 && /* @__PURE__ */ jsx4("svg", { width: 14, height: 14, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-icon", children: /* @__PURE__ */ jsx4("path", { d: "M3 8.5L8 4l5 4.5M4.5 7.5V12h2.5V9.5h2V12h2.5V7.5", fill: "none", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
105
1008
|
tab.label
|
|
106
1009
|
]
|
|
107
1010
|
}
|
|
@@ -111,20 +1014,20 @@ function GlassTabs() {
|
|
|
111
1014
|
}
|
|
112
1015
|
|
|
113
1016
|
// src/components/DrillDetailView.tsx
|
|
114
|
-
import { useEffect, useRef } from "react";
|
|
115
|
-
import { jsx as
|
|
1017
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
1018
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
116
1019
|
function DrillDetailView() {
|
|
117
1020
|
const { activeDrill } = useDrill();
|
|
118
1021
|
const { groups } = useActivity();
|
|
119
1022
|
const { active: activeInterrupts } = useInterruptBridge();
|
|
120
|
-
const bodyRef =
|
|
121
|
-
|
|
1023
|
+
const bodyRef = useRef2(null);
|
|
1024
|
+
useEffect2(() => {
|
|
122
1025
|
if (activeDrill && bodyRef.current) {
|
|
123
1026
|
bodyRef.current.scrollTop = 0;
|
|
124
1027
|
}
|
|
125
1028
|
}, [activeDrill]);
|
|
126
1029
|
if (!activeDrill) {
|
|
127
|
-
return /* @__PURE__ */
|
|
1030
|
+
return /* @__PURE__ */ jsx5("div", { className: "kaboo-drill-detail kaboo-hidden" });
|
|
128
1031
|
}
|
|
129
1032
|
const group = groups[activeDrill];
|
|
130
1033
|
const timeline = group?.timeline ?? [];
|
|
@@ -136,15 +1039,15 @@ function DrillDetailView() {
|
|
|
136
1039
|
const renderToolCard = (toolUseId) => {
|
|
137
1040
|
const entry = childByToolCall.get(toolUseId);
|
|
138
1041
|
if (!entry) return null;
|
|
139
|
-
return /* @__PURE__ */
|
|
1042
|
+
return /* @__PURE__ */ jsx5(AgentCard, { groupId: entry[0], group: entry[1] });
|
|
140
1043
|
};
|
|
141
|
-
return /* @__PURE__ */
|
|
142
|
-
group?.task && /* @__PURE__ */
|
|
143
|
-
/* @__PURE__ */
|
|
1044
|
+
return /* @__PURE__ */ jsx5("div", { ref: bodyRef, className: "kaboo-drill-detail", children: /* @__PURE__ */ jsxs4("div", { className: "kaboo-drill-detail-inner", children: [
|
|
1045
|
+
group?.task && /* @__PURE__ */ jsxs4("div", { className: "kaboo-agent-card-task kaboo-drill-task", children: [
|
|
1046
|
+
/* @__PURE__ */ jsx5("strong", { children: "Task:" }),
|
|
144
1047
|
" ",
|
|
145
1048
|
group.task
|
|
146
1049
|
] }),
|
|
147
|
-
group && timeline.length > 0 && /* @__PURE__ */
|
|
1050
|
+
group && timeline.length > 0 && /* @__PURE__ */ jsx5("div", { className: "kaboo-drill-timeline", children: /* @__PURE__ */ jsx5(
|
|
148
1051
|
Timeline,
|
|
149
1052
|
{
|
|
150
1053
|
timeline,
|
|
@@ -153,7 +1056,7 @@ function DrillDetailView() {
|
|
|
153
1056
|
renderToolCard
|
|
154
1057
|
}
|
|
155
1058
|
) }),
|
|
156
|
-
unanchoredInterrupts.length > 0 && /* @__PURE__ */
|
|
1059
|
+
unanchoredInterrupts.length > 0 && /* @__PURE__ */ jsx5("div", { className: "kaboo-drill-interrupt", children: unanchoredInterrupts.map((it) => /* @__PURE__ */ jsx5(
|
|
157
1060
|
InterruptRenderer,
|
|
158
1061
|
{
|
|
159
1062
|
reason: it.reason,
|
|
@@ -163,12 +1066,12 @@ function DrillDetailView() {
|
|
|
163
1066
|
},
|
|
164
1067
|
it.id
|
|
165
1068
|
)) }),
|
|
166
|
-
leftoverChildren.length > 0 && /* @__PURE__ */
|
|
167
|
-
/* @__PURE__ */
|
|
168
|
-
leftoverChildren.map(([id, childGroup]) => /* @__PURE__ */
|
|
1069
|
+
leftoverChildren.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "kaboo-drill-children", children: [
|
|
1070
|
+
/* @__PURE__ */ jsx5("div", { className: "kaboo-drill-section-label", children: "Sub-agents" }),
|
|
1071
|
+
leftoverChildren.map(([id, childGroup]) => /* @__PURE__ */ jsx5(AgentCard, { groupId: id, group: childGroup }, id))
|
|
169
1072
|
] }),
|
|
170
|
-
group && timeline.length === 0 && childEntries.length === 0 && /* @__PURE__ */
|
|
171
|
-
!group && /* @__PURE__ */
|
|
1073
|
+
group && timeline.length === 0 && childEntries.length === 0 && /* @__PURE__ */ jsx5("div", { className: "kaboo-agent-card-empty", children: "Working..." }),
|
|
1074
|
+
!group && /* @__PURE__ */ jsx5("div", { className: "kaboo-agent-card-empty", children: "No activity data for this group." })
|
|
172
1075
|
] }) });
|
|
173
1076
|
}
|
|
174
1077
|
export {
|
|
@@ -182,19 +1085,37 @@ export {
|
|
|
182
1085
|
InterruptRenderer,
|
|
183
1086
|
KabooActivityProvider,
|
|
184
1087
|
KabooProvider,
|
|
1088
|
+
KabooReferenceInput,
|
|
185
1089
|
MarkdownContent,
|
|
186
1090
|
MiniTable,
|
|
1091
|
+
REFERENCES_STATE_KEY,
|
|
1092
|
+
REFERENCE_METADATA_KEYS,
|
|
1093
|
+
ReferenceStateSync,
|
|
1094
|
+
ReferencesProvider,
|
|
187
1095
|
StructuredRenderersContext,
|
|
188
1096
|
Timeline,
|
|
189
1097
|
ToolRow,
|
|
1098
|
+
UPLOAD_MARKER,
|
|
1099
|
+
attachmentToInputContent,
|
|
1100
|
+
buildAttachmentsConfig,
|
|
1101
|
+
buildUserContent,
|
|
190
1102
|
directChildren,
|
|
191
1103
|
formatToolInput,
|
|
192
1104
|
formatToolResult,
|
|
1105
|
+
isUploadProvider,
|
|
1106
|
+
mintReferenceId,
|
|
193
1107
|
normalizeResult,
|
|
1108
|
+
objectToStateEntry,
|
|
1109
|
+
referenceMarker,
|
|
1110
|
+
serializeReferences,
|
|
194
1111
|
topLevelGroups,
|
|
1112
|
+
uploadFileToReference,
|
|
1113
|
+
uploadProvider,
|
|
195
1114
|
useActivity,
|
|
196
1115
|
useDrill,
|
|
197
1116
|
useInterruptBridge,
|
|
198
|
-
useInterruptFor
|
|
1117
|
+
useInterruptFor,
|
|
1118
|
+
useReferences,
|
|
1119
|
+
withReferenceState
|
|
199
1120
|
};
|
|
200
1121
|
//# sourceMappingURL=index.js.map
|