@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/dist/index.cjs CHANGED
@@ -30,20 +30,38 @@ __export(src_exports, {
30
30
  InterruptRenderer: () => InterruptRenderer,
31
31
  KabooActivityProvider: () => KabooActivityProvider,
32
32
  KabooProvider: () => KabooProvider,
33
+ KabooReferenceInput: () => KabooReferenceInput,
33
34
  MarkdownContent: () => MarkdownContent,
34
35
  MiniTable: () => MiniTable,
36
+ REFERENCES_STATE_KEY: () => REFERENCES_STATE_KEY,
37
+ REFERENCE_METADATA_KEYS: () => REFERENCE_METADATA_KEYS,
38
+ ReferenceStateSync: () => ReferenceStateSync,
39
+ ReferencesProvider: () => ReferencesProvider,
35
40
  StructuredRenderersContext: () => StructuredRenderersContext,
36
41
  Timeline: () => Timeline,
37
42
  ToolRow: () => ToolRow,
43
+ UPLOAD_MARKER: () => UPLOAD_MARKER,
44
+ attachmentToInputContent: () => attachmentToInputContent,
45
+ buildAttachmentsConfig: () => buildAttachmentsConfig,
46
+ buildUserContent: () => buildUserContent,
38
47
  directChildren: () => directChildren,
39
48
  formatToolInput: () => formatToolInput,
40
49
  formatToolResult: () => formatToolResult,
50
+ isUploadProvider: () => isUploadProvider,
51
+ mintReferenceId: () => mintReferenceId,
41
52
  normalizeResult: () => normalizeResult,
53
+ objectToStateEntry: () => objectToStateEntry,
54
+ referenceMarker: () => referenceMarker,
55
+ serializeReferences: () => serializeReferences,
42
56
  topLevelGroups: () => topLevelGroups,
57
+ uploadFileToReference: () => uploadFileToReference,
58
+ uploadProvider: () => uploadProvider,
43
59
  useActivity: () => useActivity,
44
60
  useDrill: () => useDrill,
45
61
  useInterruptBridge: () => useInterruptBridge,
46
- useInterruptFor: () => useInterruptFor
62
+ useInterruptFor: () => useInterruptFor,
63
+ useReferences: () => useReferences,
64
+ withReferenceState: () => withReferenceState
47
65
  });
48
66
  module.exports = __toCommonJS(src_exports);
49
67
 
@@ -101,7 +119,7 @@ function DrillProvider({ children }) {
101
119
  }
102
120
 
103
121
  // src/context/KabooProvider.tsx
104
- var import_v26 = require("@copilotkit/react-core/v2");
122
+ var import_v27 = require("@copilotkit/react-core/v2");
105
123
 
106
124
  // src/context/InterruptBridge.tsx
107
125
  var import_react3 = require("react");
@@ -1191,8 +1209,164 @@ function InlineCardRenderer({
1191
1209
  );
1192
1210
  }
1193
1211
 
1194
- // src/context/KabooProvider.tsx
1212
+ // src/references/ReferencesProvider.tsx
1213
+ var import_react10 = require("react");
1214
+ var import_v26 = require("@copilotkit/react-core/v2");
1215
+
1216
+ // src/references/types.ts
1217
+ var REFERENCE_METADATA_KEYS = {
1218
+ /** Metadata key carrying the reference id. */
1219
+ id: "kaboo_id",
1220
+ /** Metadata key carrying the reference kind. */
1221
+ kind: "kaboo_kind",
1222
+ /** Metadata key carrying the reference display name. */
1223
+ name: "kaboo_name"
1224
+ };
1225
+ var REFERENCES_STATE_KEY = "kaboo_references";
1226
+
1227
+ // src/references/serialize.ts
1228
+ function mintReferenceId(kind) {
1229
+ const rand = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2);
1230
+ return `${kind}_${rand}`;
1231
+ }
1232
+ function referenceMarker(ref) {
1233
+ return `[${ref.kind}:${ref.id}]`;
1234
+ }
1235
+ function modalityFor(mimeType) {
1236
+ if (mimeType.startsWith("image/")) return "image";
1237
+ if (mimeType.startsWith("audio/")) return "audio";
1238
+ if (mimeType.startsWith("video/")) return "video";
1239
+ return "document";
1240
+ }
1241
+ function attachmentToInputContent(ref) {
1242
+ const source = "url" in ref.source ? { type: "url", value: ref.source.url, mimeType: ref.mimeType } : { type: "data", value: ref.source.data, mimeType: ref.mimeType };
1243
+ return {
1244
+ type: modalityFor(ref.mimeType),
1245
+ source,
1246
+ metadata: {
1247
+ [REFERENCE_METADATA_KEYS.id]: ref.id,
1248
+ [REFERENCE_METADATA_KEYS.kind]: ref.kind,
1249
+ [REFERENCE_METADATA_KEYS.name]: ref.name,
1250
+ filename: ref.name
1251
+ }
1252
+ };
1253
+ }
1254
+ function objectToStateEntry(ref) {
1255
+ return { kind: ref.kind, id: ref.id, name: ref.name, meta: ref.meta };
1256
+ }
1257
+ function serializeReferences(refs) {
1258
+ const attachmentParts = [];
1259
+ const objectReferences = [];
1260
+ for (const ref of refs) {
1261
+ if (ref.transport === "attachment") {
1262
+ attachmentParts.push(attachmentToInputContent(ref));
1263
+ } else {
1264
+ objectReferences.push(objectToStateEntry(ref));
1265
+ }
1266
+ }
1267
+ return { attachmentParts, objectReferences };
1268
+ }
1269
+ function withReferenceState(state, objectReferences) {
1270
+ const next = { ...state ?? {} };
1271
+ if (objectReferences.length > 0) {
1272
+ next[REFERENCES_STATE_KEY] = objectReferences;
1273
+ } else {
1274
+ delete next[REFERENCES_STATE_KEY];
1275
+ }
1276
+ return next;
1277
+ }
1278
+ function buildUserContent(text, attachmentParts) {
1279
+ const parts = [];
1280
+ if (text.length > 0) parts.push({ type: "text", text });
1281
+ parts.push(...attachmentParts);
1282
+ return parts;
1283
+ }
1284
+
1285
+ // src/references/ReferencesProvider.tsx
1195
1286
  var import_jsx_runtime15 = require("react/jsx-runtime");
1287
+ var ReferencesContext = (0, import_react10.createContext)({
1288
+ providers: [],
1289
+ pending: [],
1290
+ addReference: () => {
1291
+ },
1292
+ removeReference: () => {
1293
+ },
1294
+ clearReferences: () => {
1295
+ },
1296
+ messageReferences: {},
1297
+ recordMessageReferences: () => {
1298
+ }
1299
+ });
1300
+ function ReferencesProvider({
1301
+ providers = [],
1302
+ syncObjectStateTo = false,
1303
+ children
1304
+ }) {
1305
+ const [pending, setPending] = (0, import_react10.useState)([]);
1306
+ const [messageReferences, setMessageReferences] = (0, import_react10.useState)({});
1307
+ const addReference = (0, import_react10.useCallback)((ref) => {
1308
+ setPending(
1309
+ (prev) => prev.some((r) => r.id === ref.id) ? prev : [...prev, ref]
1310
+ );
1311
+ }, []);
1312
+ const removeReference = (0, import_react10.useCallback)((id) => {
1313
+ setPending((prev) => prev.filter((r) => r.id !== id));
1314
+ }, []);
1315
+ const clearReferences = (0, import_react10.useCallback)(() => setPending([]), []);
1316
+ const recordMessageReferences = (0, import_react10.useCallback)(
1317
+ (messageId, refs) => {
1318
+ if (refs.length === 0) return;
1319
+ setMessageReferences((prev) => ({ ...prev, [messageId]: refs }));
1320
+ },
1321
+ []
1322
+ );
1323
+ const value = (0, import_react10.useMemo)(
1324
+ () => ({
1325
+ providers,
1326
+ pending,
1327
+ addReference,
1328
+ removeReference,
1329
+ clearReferences,
1330
+ messageReferences,
1331
+ recordMessageReferences
1332
+ }),
1333
+ [
1334
+ providers,
1335
+ pending,
1336
+ addReference,
1337
+ removeReference,
1338
+ clearReferences,
1339
+ messageReferences,
1340
+ recordMessageReferences
1341
+ ]
1342
+ );
1343
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(ReferencesContext.Provider, { value, children: [
1344
+ syncObjectStateTo !== false && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ReferenceStateSync, { agentId: syncObjectStateTo, pending }),
1345
+ children
1346
+ ] });
1347
+ }
1348
+ function ReferenceStateSync({
1349
+ agentId,
1350
+ pending
1351
+ }) {
1352
+ const { agent } = (0, import_v26.useAgent)(agentId ? { agentId } : void 0);
1353
+ const objectRefs = (0, import_react10.useMemo)(
1354
+ () => pending.filter((r) => r.transport === "object").map(objectToStateEntry),
1355
+ [pending]
1356
+ );
1357
+ const key = JSON.stringify(objectRefs);
1358
+ (0, import_react10.useEffect)(() => {
1359
+ if (!agent || typeof agent.setState !== "function") return;
1360
+ agent.setState(withReferenceState(agent.state, objectRefs));
1361
+ }, [agent, key]);
1362
+ return null;
1363
+ }
1364
+ function useReferences() {
1365
+ return (0, import_react10.useContext)(ReferencesContext);
1366
+ }
1367
+
1368
+ // src/context/KabooProvider.tsx
1369
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1196
1370
  function KabooProvider({
1197
1371
  runtimeUrl,
1198
1372
  agent,
@@ -1201,28 +1375,911 @@ function KabooProvider({
1201
1375
  interruptRenderers,
1202
1376
  disableInterruptHandler = false,
1203
1377
  disableInlineCards = false,
1378
+ references,
1204
1379
  copilotKitProps,
1205
1380
  children
1206
1381
  }) {
1207
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1208
- import_v26.CopilotKit,
1382
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1383
+ import_v27.CopilotKit,
1209
1384
  {
1210
1385
  runtimeUrl,
1211
1386
  agent,
1212
1387
  threadId,
1213
1388
  useSingleEndpoint: false,
1214
1389
  ...copilotKitProps,
1215
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(KabooActivityProvider, { agentId: agent, structuredRenderers, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DrillProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(InterruptBridgeProvider, { children: [
1216
- !disableInterruptHandler && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(KabooInterruptHandler, { agentId: agent, renderers: interruptRenderers }),
1217
- !disableInlineCards && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(KabooInlineCards, {}),
1390
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KabooActivityProvider, { agentId: agent, structuredRenderers, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DrillProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(InterruptBridgeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(ReferencesProvider, { providers: references, syncObjectStateTo: agent, children: [
1391
+ !disableInterruptHandler && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KabooInterruptHandler, { agentId: agent, renderers: interruptRenderers }),
1392
+ !disableInlineCards && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KabooInlineCards, {}),
1218
1393
  children
1219
- ] }) }) })
1394
+ ] }) }) }) })
1395
+ }
1396
+ );
1397
+ }
1398
+
1399
+ // src/references/ReferenceInput.tsx
1400
+ var import_react11 = require("react");
1401
+ var import_react_dom = require("react-dom");
1402
+ var import_v28 = require("@copilotkit/react-core/v2");
1403
+
1404
+ // src/references/uploadProvider.tsx
1405
+ var UPLOAD_MARKER = "__kabooUpload";
1406
+ var DEFAULT_MAX_SIZE = 20 * 1024 * 1024;
1407
+ function readAsBase64(file) {
1408
+ return new Promise((resolve, reject) => {
1409
+ const reader = new FileReader();
1410
+ reader.onload = () => {
1411
+ const result = String(reader.result);
1412
+ const comma = result.indexOf(",");
1413
+ resolve(comma >= 0 ? result.slice(comma + 1) : result);
1414
+ };
1415
+ reader.onerror = () => reject(reader.error);
1416
+ reader.readAsDataURL(file);
1417
+ });
1418
+ }
1419
+ async function uploadFileToReference(file, config) {
1420
+ const kind = config.kind ?? "file";
1421
+ const id = mintReferenceId(kind);
1422
+ const maxSize = config.maxSize ?? DEFAULT_MAX_SIZE;
1423
+ if (file.size > maxSize) {
1424
+ throw new Error(`File "${file.name}" exceeds the ${maxSize}-byte limit.`);
1425
+ }
1426
+ if (config.onUpload) {
1427
+ const result = await config.onUpload(file);
1428
+ const mimeType = result.mimeType ?? file.type ?? "application/octet-stream";
1429
+ const source = result.type === "url" ? { url: result.value } : { data: result.value };
1430
+ return { transport: "attachment", kind, id, name: file.name, mimeType, source };
1431
+ }
1432
+ const data = await readAsBase64(file);
1433
+ return {
1434
+ transport: "attachment",
1435
+ kind,
1436
+ id,
1437
+ name: file.name,
1438
+ mimeType: file.type || "application/octet-stream",
1439
+ source: { data }
1440
+ };
1441
+ }
1442
+ function uploadProvider(config = {}) {
1443
+ return {
1444
+ id: config.id ?? "upload",
1445
+ label: config.label ?? "Upload",
1446
+ [UPLOAD_MARKER]: {
1447
+ kind: config.kind ?? "file",
1448
+ maxSize: config.maxSize ?? DEFAULT_MAX_SIZE,
1449
+ accept: config.accept,
1450
+ onUpload: config.onUpload
1451
+ }
1452
+ };
1453
+ }
1454
+ function isUploadProvider(provider) {
1455
+ return UPLOAD_MARKER in provider;
1456
+ }
1457
+ function buildAttachmentsConfig(config = {}) {
1458
+ const kind = config.kind ?? "file";
1459
+ return {
1460
+ enabled: true,
1461
+ accept: config.accept,
1462
+ maxSize: config.maxSize,
1463
+ onUpload: async (file) => {
1464
+ const id = mintReferenceId(kind);
1465
+ const kabooMeta = {
1466
+ [REFERENCE_METADATA_KEYS.id]: id,
1467
+ [REFERENCE_METADATA_KEYS.kind]: kind,
1468
+ [REFERENCE_METADATA_KEYS.name]: file.name
1469
+ };
1470
+ if (config.onUpload) {
1471
+ const result = await config.onUpload(file);
1472
+ return { ...result, metadata: { ...result.metadata ?? {}, ...kabooMeta } };
1473
+ }
1474
+ const value = await readAsBase64(file);
1475
+ return {
1476
+ type: "data",
1477
+ value,
1478
+ mimeType: file.type || "application/octet-stream",
1479
+ metadata: kabooMeta
1480
+ };
1481
+ }
1482
+ };
1483
+ }
1484
+
1485
+ // src/references/ReferenceInput.tsx
1486
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1487
+ var CHIP_CLASS = "kaboo-chip";
1488
+ var NBSP = "\xA0";
1489
+ 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>';
1490
+ 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>';
1491
+ 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>';
1492
+ var AttachIcon = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("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__ */ (0, import_jsx_runtime17.jsx)("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" }) });
1493
+ var AtIcon = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1494
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "12", cy: "12", r: "4" }),
1495
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94" })
1496
+ ] });
1497
+ var PlusIcon = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("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__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M5 12h14M12 5v14" }) });
1498
+ var SearchIcon = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1499
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "11", cy: "11", r: "7" }),
1500
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "m21 21-4.3-4.3" })
1501
+ ] });
1502
+ var POPOVER_WIDTH = 320;
1503
+ var VIEWPORT_MARGIN = 8;
1504
+ var ANCHOR_GAP = 6;
1505
+ function computePosition(rect) {
1506
+ if (!rect) return null;
1507
+ const width = Math.min(POPOVER_WIDTH, window.innerWidth - VIEWPORT_MARGIN * 2);
1508
+ const left = Math.max(
1509
+ VIEWPORT_MARGIN,
1510
+ Math.min(rect.left, window.innerWidth - width - VIEWPORT_MARGIN)
1511
+ );
1512
+ return { left, bottom: window.innerHeight - rect.top + ANCHOR_GAP, width };
1513
+ }
1514
+ function isImageMime(mime) {
1515
+ return mime.startsWith("image/");
1516
+ }
1517
+ function previewSrc(ref) {
1518
+ return "url" in ref.source ? ref.source.url : `data:${ref.mimeType};base64,${ref.source.data}`;
1519
+ }
1520
+ function serializeEditor(root) {
1521
+ let out = "";
1522
+ const walk = (node) => {
1523
+ if (node.nodeType === Node.TEXT_NODE) {
1524
+ out += node.textContent ?? "";
1525
+ return;
1526
+ }
1527
+ if (node instanceof HTMLElement) {
1528
+ if (node.classList.contains(CHIP_CLASS)) {
1529
+ const name = node.dataset.name ?? "";
1530
+ out += node.dataset.transport === "object" ? `@${name}` : name;
1531
+ return;
1532
+ }
1533
+ if (node.tagName === "BR") {
1534
+ out += "\n";
1535
+ return;
1536
+ }
1537
+ if (node.tagName === "DIV" && out.length > 0 && !out.endsWith("\n")) out += "\n";
1538
+ node.childNodes.forEach(walk);
1539
+ }
1540
+ };
1541
+ root.childNodes.forEach(walk);
1542
+ return out.replace(new RegExp(NBSP, "g"), " ");
1543
+ }
1544
+ function KabooReferenceInput({ trigger = "@", ...inputProps }) {
1545
+ const {
1546
+ providers,
1547
+ pending,
1548
+ addReference,
1549
+ removeReference,
1550
+ clearReferences,
1551
+ recordMessageReferences
1552
+ } = useReferences();
1553
+ const config = (0, import_v28.useCopilotChatConfiguration)();
1554
+ const configAgentId = config?.agentId;
1555
+ const { agent } = (0, import_v28.useAgent)(configAgentId ? { agentId: configAgentId } : void 0);
1556
+ const placeholder = config?.labels?.chatInputPlaceholder ?? "Type a message...";
1557
+ const wrapRef = (0, import_react11.useRef)(null);
1558
+ const trayRef = (0, import_react11.useRef)(null);
1559
+ const menuRef = (0, import_react11.useRef)(null);
1560
+ const editorRef = (0, import_react11.useRef)(null);
1561
+ const fileInputRef = (0, import_react11.useRef)(null);
1562
+ const addBtnRef = (0, import_react11.useRef)(null);
1563
+ const searchRef = (0, import_react11.useRef)(null);
1564
+ const nativeChangeRef = (0, import_react11.useRef)(void 0);
1565
+ const anchorRef = (0, import_react11.useRef)(() => null);
1566
+ const tokenRef = (0, import_react11.useRef)(null);
1567
+ const replacingRef = (0, import_react11.useRef)(null);
1568
+ const replacingTrayIdRef = (0, import_react11.useRef)(null);
1569
+ const inlineIdsRef = (0, import_react11.useRef)(/* @__PURE__ */ new Set());
1570
+ const commitTargetRef = (0, import_react11.useRef)("inline");
1571
+ const [open, setOpen] = (0, import_react11.useState)(false);
1572
+ const [rows, setRows] = (0, import_react11.useState)([]);
1573
+ const [active, setActive] = (0, import_react11.useState)(0);
1574
+ const [query, setQuery] = (0, import_react11.useState)("");
1575
+ const [mode, setMode] = (0, import_react11.useState)("token");
1576
+ const [pos, setPos] = (0, import_react11.useState)(null);
1577
+ const placeholderRef = (0, import_react11.useRef)(placeholder);
1578
+ placeholderRef.current = placeholder;
1579
+ const markEmpty = (0, import_react11.useCallback)(() => {
1580
+ const root = editorRef.current;
1581
+ if (!root) return;
1582
+ const isEmpty = root.querySelector(`.${CHIP_CLASS}`) === null && (root.textContent ?? "").trim().length === 0;
1583
+ root.dataset.empty = isEmpty ? "true" : "false";
1584
+ }, []);
1585
+ const uploadProviderDef = (0, import_react11.useMemo)(() => providers.find(isUploadProvider), [providers]);
1586
+ const searchable = (0, import_react11.useMemo)(() => providers.filter((p) => typeof p.search === "function"), [providers]);
1587
+ const actionProviders = (0, import_react11.useMemo)(
1588
+ () => providers.filter((p) => typeof p.search !== "function"),
1589
+ [providers]
1590
+ );
1591
+ const buildRows = (0, import_react11.useCallback)(
1592
+ async (q) => {
1593
+ const next = [];
1594
+ for (const provider of actionProviders) {
1595
+ const label = isUploadProvider(provider) ? "Attach" : provider.label;
1596
+ if (!q || label.toLowerCase().includes(q.toLowerCase())) {
1597
+ next.push({ kind: "action", group: label, provider });
1598
+ }
1599
+ }
1600
+ for (const provider of searchable) {
1601
+ try {
1602
+ const items = await provider.search(q);
1603
+ for (const item of items) next.push({ kind: "item", group: provider.label, provider, item });
1604
+ } catch {
1605
+ }
1606
+ }
1607
+ return next;
1608
+ },
1609
+ [actionProviders, searchable]
1610
+ );
1611
+ const refresh = (0, import_react11.useCallback)(
1612
+ async (q) => {
1613
+ const next = await buildRows(q);
1614
+ setRows(next);
1615
+ setActive(0);
1616
+ },
1617
+ [buildRows]
1618
+ );
1619
+ const close = (0, import_react11.useCallback)(() => {
1620
+ setOpen(false);
1621
+ setRows([]);
1622
+ setQuery("");
1623
+ tokenRef.current = null;
1624
+ replacingRef.current = null;
1625
+ replacingTrayIdRef.current = null;
1626
+ }, []);
1627
+ const emit = (0, import_react11.useCallback)(() => {
1628
+ const root = editorRef.current;
1629
+ if (!root) return;
1630
+ const text = serializeEditor(root);
1631
+ markEmpty();
1632
+ nativeChangeRef.current?.({ target: { value: text } });
1633
+ }, [markEmpty]);
1634
+ const reconcile = (0, import_react11.useCallback)(() => {
1635
+ const root = editorRef.current;
1636
+ if (!root) return;
1637
+ const live = new Set(
1638
+ Array.from(root.querySelectorAll(`.${CHIP_CLASS}`)).map((c) => c.dataset.refId)
1639
+ );
1640
+ for (const id of Array.from(inlineIdsRef.current)) {
1641
+ if (!live.has(id)) {
1642
+ inlineIdsRef.current.delete(id);
1643
+ removeReference(id);
1644
+ }
1645
+ }
1646
+ }, [removeReference]);
1647
+ const focusEditor = (0, import_react11.useCallback)(() => editorRef.current?.focus(), []);
1648
+ const createChip = (0, import_react11.useCallback)((ref) => {
1649
+ const chip = document.createElement("span");
1650
+ chip.className = `${CHIP_CLASS} ${CHIP_CLASS}-${ref.transport}`;
1651
+ chip.contentEditable = "false";
1652
+ chip.dataset.refId = ref.id;
1653
+ chip.dataset.kind = ref.kind;
1654
+ chip.dataset.name = ref.name;
1655
+ chip.dataset.transport = ref.transport;
1656
+ chip.title = ref.name;
1657
+ if (ref.transport === "attachment" && isImageMime(ref.mimeType)) {
1658
+ const img = document.createElement("img");
1659
+ img.className = "kaboo-chip-thumb";
1660
+ img.src = previewSrc(ref);
1661
+ img.alt = ref.name;
1662
+ chip.appendChild(img);
1663
+ } else {
1664
+ const ic = document.createElement("span");
1665
+ ic.className = "kaboo-chip-ic";
1666
+ ic.innerHTML = ref.transport === "object" ? AT_SVG : FILE_SVG;
1667
+ chip.appendChild(ic);
1668
+ }
1669
+ const label = document.createElement("span");
1670
+ label.className = "kaboo-chip-label";
1671
+ label.textContent = ref.name;
1672
+ chip.appendChild(label);
1673
+ const x = document.createElement("button");
1674
+ x.type = "button";
1675
+ x.className = "kaboo-chip-x";
1676
+ x.setAttribute("aria-label", `Remove ${ref.name}`);
1677
+ x.innerHTML = X_SVG;
1678
+ x.addEventListener("mousedown", (e) => {
1679
+ e.preventDefault();
1680
+ e.stopPropagation();
1681
+ handlersRef.current.removeChip(chip);
1682
+ });
1683
+ chip.appendChild(x);
1684
+ chip.addEventListener("mousedown", (e) => {
1685
+ if (e.target === x || x.contains(e.target)) return;
1686
+ e.preventDefault();
1687
+ handlersRef.current.replaceChip(chip);
1688
+ });
1689
+ return chip;
1690
+ }, []);
1691
+ const insertAtCaret = (0, import_react11.useCallback)((node) => {
1692
+ const root = editorRef.current;
1693
+ if (!root) return;
1694
+ const sel = window.getSelection();
1695
+ let range;
1696
+ if (sel && sel.rangeCount > 0 && root.contains(sel.focusNode)) {
1697
+ range = sel.getRangeAt(0);
1698
+ } else {
1699
+ range = document.createRange();
1700
+ range.selectNodeContents(root);
1701
+ range.collapse(false);
1702
+ }
1703
+ range.collapse(false);
1704
+ const space = document.createTextNode(NBSP);
1705
+ range.insertNode(space);
1706
+ range.insertNode(node);
1707
+ const after = document.createRange();
1708
+ after.setStartAfter(space);
1709
+ after.collapse(true);
1710
+ sel?.removeAllRanges();
1711
+ sel?.addRange(after);
1712
+ }, []);
1713
+ const placeChip = (0, import_react11.useCallback)(
1714
+ (ref) => {
1715
+ const chip = createChip(ref);
1716
+ const replacing = replacingRef.current;
1717
+ if (replacing) {
1718
+ const oldId = replacing.dataset.refId;
1719
+ if (oldId && oldId !== ref.id) removeReference(oldId);
1720
+ replacing.replaceWith(chip);
1721
+ replacingRef.current = null;
1722
+ } else if (tokenRef.current) {
1723
+ const { node, start } = tokenRef.current;
1724
+ const sel = window.getSelection();
1725
+ const range = document.createRange();
1726
+ range.setStart(node, Math.min(start, node.textContent?.length ?? 0));
1727
+ if (sel && sel.focusNode && editorRef.current?.contains(sel.focusNode)) {
1728
+ range.setEnd(sel.focusNode, sel.focusOffset);
1729
+ } else {
1730
+ range.setEnd(node, node.textContent?.length ?? 0);
1731
+ }
1732
+ range.deleteContents();
1733
+ const space = document.createTextNode(NBSP);
1734
+ range.insertNode(space);
1735
+ range.insertNode(chip);
1736
+ const after = document.createRange();
1737
+ after.setStartAfter(space);
1738
+ after.collapse(true);
1739
+ sel?.removeAllRanges();
1740
+ sel?.addRange(after);
1741
+ tokenRef.current = null;
1742
+ } else {
1743
+ insertAtCaret(chip);
1744
+ }
1745
+ emit();
1746
+ },
1747
+ [createChip, insertAtCaret, removeReference, emit]
1748
+ );
1749
+ const commit = (0, import_react11.useCallback)(
1750
+ (ref, target) => {
1751
+ addReference(ref);
1752
+ if (target === "inline") {
1753
+ inlineIdsRef.current.add(ref.id);
1754
+ placeChip(ref);
1755
+ } else {
1756
+ const oldId = replacingTrayIdRef.current;
1757
+ if (oldId && oldId !== ref.id) removeReference(oldId);
1758
+ replacingTrayIdRef.current = null;
1759
+ inlineIdsRef.current.delete(ref.id);
1760
+ }
1761
+ },
1762
+ [addReference, placeChip, removeReference]
1763
+ );
1764
+ const openFilePicker = (0, import_react11.useCallback)(
1765
+ (provider, target) => {
1766
+ const input = fileInputRef.current;
1767
+ if (!input) return;
1768
+ const cfg = isUploadProvider(provider) ? provider[UPLOAD_MARKER] : {};
1769
+ input.accept = cfg.accept ?? "";
1770
+ input.onchange = async () => {
1771
+ const file = input.files?.[0];
1772
+ input.value = "";
1773
+ if (!file) return;
1774
+ try {
1775
+ const ref = await uploadFileToReference(file, cfg);
1776
+ commit(ref, target);
1777
+ } catch (err) {
1778
+ console.error("[kaboo] upload failed", err);
1779
+ }
1780
+ };
1781
+ input.click();
1782
+ },
1783
+ [commit]
1784
+ );
1785
+ const stripToken = (0, import_react11.useCallback)(() => {
1786
+ const t = tokenRef.current;
1787
+ if (!t) return;
1788
+ const sel = window.getSelection();
1789
+ const range = document.createRange();
1790
+ range.setStart(t.node, Math.min(t.start, t.node.textContent?.length ?? 0));
1791
+ if (sel?.focusNode && editorRef.current?.contains(sel.focusNode)) {
1792
+ range.setEnd(sel.focusNode, sel.focusOffset);
1793
+ } else {
1794
+ range.setEnd(t.node, t.node.textContent?.length ?? 0);
1220
1795
  }
1796
+ range.deleteContents();
1797
+ emit();
1798
+ }, [emit]);
1799
+ const select = (0, import_react11.useCallback)(
1800
+ async (row) => {
1801
+ const target = commitTargetRef.current;
1802
+ if (row.kind === "action") {
1803
+ const provider = row.provider;
1804
+ if (target === "inline") stripToken();
1805
+ close();
1806
+ if (isUploadProvider(provider)) openFilePicker(provider, target);
1807
+ else await provider.onSelect?.({ id: provider.id, label: provider.label });
1808
+ focusEditor();
1809
+ return;
1810
+ }
1811
+ await row.provider.onSelect?.(row.item);
1812
+ const ref = row.provider.toReference?.(row.item);
1813
+ if (ref) commit(ref, target);
1814
+ close();
1815
+ focusEditor();
1816
+ },
1817
+ [stripToken, close, openFilePicker, focusEditor, commit]
1818
+ );
1819
+ const openFromToken = (0, import_react11.useCallback)(
1820
+ (q) => {
1821
+ commitTargetRef.current = "inline";
1822
+ replacingRef.current = null;
1823
+ replacingTrayIdRef.current = null;
1824
+ anchorRef.current = () => {
1825
+ const t = tokenRef.current;
1826
+ return t ? caretRectFor(t.node, t.start) : null;
1827
+ };
1828
+ setMode("token");
1829
+ setQuery(q);
1830
+ setPos(computePosition(anchorRef.current()));
1831
+ setOpen(true);
1832
+ void refresh(q);
1833
+ },
1834
+ [refresh]
1835
+ );
1836
+ const toggleFromButton = (0, import_react11.useCallback)(() => {
1837
+ setOpen((prev) => {
1838
+ if (prev) {
1839
+ close();
1840
+ return false;
1841
+ }
1842
+ commitTargetRef.current = "tray";
1843
+ tokenRef.current = null;
1844
+ replacingRef.current = null;
1845
+ replacingTrayIdRef.current = null;
1846
+ anchorRef.current = () => addBtnRef.current?.getBoundingClientRect() ?? null;
1847
+ setMode("menu");
1848
+ setQuery("");
1849
+ setPos(computePosition(anchorRef.current()));
1850
+ void refresh("");
1851
+ return true;
1852
+ });
1853
+ }, [refresh, close]);
1854
+ const onSearch = (0, import_react11.useCallback)(
1855
+ (value) => {
1856
+ setQuery(value);
1857
+ void refresh(value);
1858
+ },
1859
+ [refresh]
1221
1860
  );
1861
+ const handleNavKey = (0, import_react11.useCallback)(
1862
+ (e) => {
1863
+ if (!open || rows.length === 0) return false;
1864
+ if (e.key === "ArrowDown") {
1865
+ e.preventDefault();
1866
+ setActive((a) => (a + 1) % rows.length);
1867
+ return true;
1868
+ }
1869
+ if (e.key === "ArrowUp") {
1870
+ e.preventDefault();
1871
+ setActive((a) => (a - 1 + rows.length) % rows.length);
1872
+ return true;
1873
+ }
1874
+ if (e.key === "Enter" || e.key === "Tab") {
1875
+ e.preventDefault();
1876
+ void select(rows[active]);
1877
+ return true;
1878
+ }
1879
+ if (e.key === "Escape") {
1880
+ e.preventDefault();
1881
+ close();
1882
+ focusEditor();
1883
+ return true;
1884
+ }
1885
+ return false;
1886
+ },
1887
+ [open, rows, active, select, close, focusEditor]
1888
+ );
1889
+ const replaceChip = (0, import_react11.useCallback)(
1890
+ (chip) => {
1891
+ commitTargetRef.current = "inline";
1892
+ replacingRef.current = chip;
1893
+ replacingTrayIdRef.current = null;
1894
+ tokenRef.current = null;
1895
+ anchorRef.current = () => chip.getBoundingClientRect();
1896
+ setMode("menu");
1897
+ setQuery("");
1898
+ setPos(computePosition(chip.getBoundingClientRect()));
1899
+ setOpen(true);
1900
+ void refresh("");
1901
+ },
1902
+ [refresh]
1903
+ );
1904
+ const replaceTrayChip = (0, import_react11.useCallback)(
1905
+ (id, anchorEl) => {
1906
+ commitTargetRef.current = "tray";
1907
+ replacingTrayIdRef.current = id;
1908
+ replacingRef.current = null;
1909
+ tokenRef.current = null;
1910
+ anchorRef.current = () => anchorEl.getBoundingClientRect();
1911
+ setMode("menu");
1912
+ setQuery("");
1913
+ setPos(computePosition(anchorEl.getBoundingClientRect()));
1914
+ setOpen(true);
1915
+ void refresh("");
1916
+ },
1917
+ [refresh]
1918
+ );
1919
+ const removeChip = (0, import_react11.useCallback)(
1920
+ (chip) => {
1921
+ const id = chip.dataset.refId;
1922
+ const next = chip.nextSibling;
1923
+ chip.remove();
1924
+ if (next && next.nodeType === Node.TEXT_NODE && next.textContent === NBSP) next.remove();
1925
+ if (id) {
1926
+ inlineIdsRef.current.delete(id);
1927
+ removeReference(id);
1928
+ }
1929
+ emit();
1930
+ focusEditor();
1931
+ },
1932
+ [removeReference, emit, focusEditor]
1933
+ );
1934
+ const handlersRef = (0, import_react11.useRef)({ removeChip, replaceChip });
1935
+ handlersRef.current = { removeChip, replaceChip };
1936
+ const ctl = { open, rows, active, trigger, openFromToken, toggleFromButton, close, handleNavKey };
1937
+ const ctlRef = (0, import_react11.useRef)(ctl);
1938
+ ctlRef.current = ctl;
1939
+ const readToken = (0, import_react11.useCallback)(() => {
1940
+ const sel = window.getSelection();
1941
+ if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return null;
1942
+ const node = sel.focusNode;
1943
+ if (!node || node.nodeType !== Node.TEXT_NODE) return null;
1944
+ const text = node.textContent ?? "";
1945
+ const before = text.slice(0, sel.focusOffset);
1946
+ const at = before.lastIndexOf(ctlRef.current.trigger);
1947
+ if (at < 0) return null;
1948
+ const prev = at > 0 ? before[at - 1] : "";
1949
+ if (prev && !/\s/.test(prev)) return null;
1950
+ const q = before.slice(at + 1);
1951
+ if (/\s/.test(q)) return null;
1952
+ return { node, start: at, query: q };
1953
+ }, []);
1954
+ const onEditorInput = (0, import_react11.useCallback)(() => {
1955
+ reconcile();
1956
+ emit();
1957
+ const token = readToken();
1958
+ if (token) {
1959
+ tokenRef.current = { node: token.node, start: token.start };
1960
+ const c = ctlRef.current;
1961
+ if (!c.open || mode === "token") openFromToken(token.query);
1962
+ else {
1963
+ setQuery(token.query);
1964
+ void refresh(token.query);
1965
+ }
1966
+ } else if (ctlRef.current.open && mode === "token") {
1967
+ close();
1968
+ }
1969
+ }, [reconcile, emit, readToken, openFromToken, refresh, mode, close]);
1970
+ const TextAreaSlot = (0, import_react11.useMemo)(
1971
+ () => (0, import_react11.forwardRef)(function KabooRefEditor(props, ref) {
1972
+ const { onChange, onKeyDown, className, autoFocus } = props;
1973
+ nativeChangeRef.current = onChange;
1974
+ const setRefs = (node) => {
1975
+ editorRef.current = node;
1976
+ if (typeof ref === "function") ref(node);
1977
+ else if (ref) ref.current = node;
1978
+ };
1979
+ const handleKeyDown = (e) => {
1980
+ if (ctlRef.current.handleNavKey(e)) return;
1981
+ onKeyDown?.(e);
1982
+ };
1983
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1984
+ "div",
1985
+ {
1986
+ ref: setRefs,
1987
+ role: "textbox",
1988
+ "aria-multiline": "true",
1989
+ tabIndex: 0,
1990
+ contentEditable: true,
1991
+ suppressContentEditableWarning: true,
1992
+ "data-placeholder": placeholderRef.current,
1993
+ className: `kaboo-ref-editor cpk:bg-transparent cpk:outline-none cpk:antialiased cpk:leading-relaxed cpk:text-[16px] ${className ?? ""}`,
1994
+ "data-empty": "true",
1995
+ autoFocus,
1996
+ onInput: onEditorInput,
1997
+ onKeyDown: handleKeyDown
1998
+ }
1999
+ );
2000
+ }),
2001
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2002
+ []
2003
+ );
2004
+ const AddMenuButtonSlot = (0, import_react11.useMemo)(
2005
+ () => function KabooRefAddButton(props) {
2006
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2007
+ "button",
2008
+ {
2009
+ ref: addBtnRef,
2010
+ type: "button",
2011
+ className: "kaboo-ref-add",
2012
+ "aria-label": "Add attachment or reference",
2013
+ "aria-haspopup": "listbox",
2014
+ "aria-expanded": ctlRef.current.open,
2015
+ disabled: props.disabled,
2016
+ onMouseDown: (e) => e.preventDefault(),
2017
+ onClick: () => ctlRef.current.toggleFromButton(),
2018
+ children: PlusIcon
2019
+ }
2020
+ );
2021
+ },
2022
+ []
2023
+ );
2024
+ (0, import_react11.useEffect)(() => {
2025
+ if (!open) return;
2026
+ const onDocMouseDown = (e) => {
2027
+ const target = e.target;
2028
+ if (wrapRef.current?.contains(target) || menuRef.current?.contains(target)) return;
2029
+ close();
2030
+ };
2031
+ const onDocKey = (e) => {
2032
+ if (e.key === "Escape") close();
2033
+ };
2034
+ document.addEventListener("mousedown", onDocMouseDown);
2035
+ document.addEventListener("keydown", onDocKey);
2036
+ return () => {
2037
+ document.removeEventListener("mousedown", onDocMouseDown);
2038
+ document.removeEventListener("keydown", onDocKey);
2039
+ };
2040
+ }, [open, close]);
2041
+ (0, import_react11.useEffect)(() => {
2042
+ if (!open) return;
2043
+ const reposition = () => setPos(computePosition(anchorRef.current()));
2044
+ window.addEventListener("resize", reposition);
2045
+ window.addEventListener("scroll", reposition, true);
2046
+ return () => {
2047
+ window.removeEventListener("resize", reposition);
2048
+ window.removeEventListener("scroll", reposition, true);
2049
+ };
2050
+ }, [open]);
2051
+ (0, import_react11.useLayoutEffect)(() => {
2052
+ if (!open) return;
2053
+ setPos(computePosition(anchorRef.current()));
2054
+ if (mode === "menu") searchRef.current?.focus();
2055
+ }, [open, mode, rows.length]);
2056
+ (0, import_react11.useEffect)(() => {
2057
+ const target = agent;
2058
+ if (!target || typeof target.subscribe !== "function") return;
2059
+ const sub = target.subscribe({ onRunStartedEvent: () => clearReferences() });
2060
+ return () => sub?.unsubscribe?.();
2061
+ }, [agent, clearReferences]);
2062
+ (0, import_react11.useEffect)(() => {
2063
+ const root = editorRef.current;
2064
+ if (!root) return;
2065
+ if ((inputProps.value ?? "") === "" && root.childNodes.length > 0) {
2066
+ root.textContent = "";
2067
+ markEmpty();
2068
+ }
2069
+ }, [inputProps.value, markEmpty]);
2070
+ const handleSubmit = (0, import_react11.useCallback)(
2071
+ (message) => {
2072
+ const text = message.trim();
2073
+ const { attachmentParts } = serializeReferences(pending);
2074
+ const target = agent;
2075
+ if (text.length === 0 && attachmentParts.length === 0) return;
2076
+ if (!target?.addMessage || !target.runAgent) {
2077
+ inputProps.onSubmitMessage?.(message);
2078
+ close();
2079
+ return;
2080
+ }
2081
+ const content = attachmentParts.length > 0 ? buildUserContent(text, attachmentParts) : text;
2082
+ const id = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2);
2083
+ recordMessageReferences(
2084
+ id,
2085
+ pending.filter((r) => r.transport === "object")
2086
+ );
2087
+ target.addMessage({ id, role: "user", content });
2088
+ void Promise.resolve(target.runAgent()).catch(
2089
+ (err) => console.error("[kaboo] runAgent failed", err)
2090
+ );
2091
+ const root = editorRef.current;
2092
+ if (root) root.textContent = "";
2093
+ markEmpty();
2094
+ close();
2095
+ },
2096
+ [pending, agent, inputProps, close, markEmpty, recordMessageReferences]
2097
+ );
2098
+ const trayRefs = pending.filter((r) => !inlineIdsRef.current.has(r.id));
2099
+ (0, import_react11.useLayoutEffect)(() => {
2100
+ const tray = trayRef.current;
2101
+ const wrap = wrapRef.current;
2102
+ if (!tray || !wrap) return;
2103
+ const align = () => {
2104
+ const pill = wrap.querySelector(".copilotKitInput");
2105
+ if (!pill) return;
2106
+ const pr = pill.getBoundingClientRect();
2107
+ const wr = wrap.getBoundingClientRect();
2108
+ tray.style.marginLeft = `${Math.max(0, pr.left - wr.left)}px`;
2109
+ tray.style.width = `${pr.width}px`;
2110
+ };
2111
+ align();
2112
+ window.addEventListener("resize", align);
2113
+ return () => window.removeEventListener("resize", align);
2114
+ }, [trayRefs.length]);
2115
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: wrapRef, className: "kaboo-ref-input", children: [
2116
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { ref: fileInputRef, type: "file", className: "kaboo-ref-file", tabIndex: -1, "aria-hidden": "true" }),
2117
+ trayRefs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: trayRef, className: "kaboo-ref-tray", children: trayRefs.map((r) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2118
+ TrayChip,
2119
+ {
2120
+ reference: r,
2121
+ onRemove: () => removeReference(r.id),
2122
+ onReplace: (el) => replaceTrayChip(r.id, el)
2123
+ },
2124
+ r.id
2125
+ )) }),
2126
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2127
+ import_v28.CopilotChatInput,
2128
+ {
2129
+ ...inputProps,
2130
+ onSubmitMessage: handleSubmit,
2131
+ textArea: TextAreaSlot,
2132
+ addMenuButton: AddMenuButtonSlot
2133
+ }
2134
+ ),
2135
+ open && pos && (0, import_react_dom.createPortal)(
2136
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2137
+ ReferencePopover,
2138
+ {
2139
+ ref: menuRef,
2140
+ pos,
2141
+ rows,
2142
+ active,
2143
+ query,
2144
+ mode,
2145
+ searchRef,
2146
+ hasUpload: !!uploadProviderDef,
2147
+ onSearch,
2148
+ onSearchKeyDown: handleNavKey,
2149
+ onHover: setActive,
2150
+ onPick: select
2151
+ }
2152
+ ),
2153
+ document.body
2154
+ )
2155
+ ] });
2156
+ }
2157
+ function TrayChip({
2158
+ reference,
2159
+ onRemove,
2160
+ onReplace
2161
+ }) {
2162
+ const ref = (0, import_react11.useRef)(null);
2163
+ const isImage = reference.transport === "attachment" && isImageMime(reference.mimeType);
2164
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2165
+ "span",
2166
+ {
2167
+ ref,
2168
+ className: `kaboo-chip ${CHIP_CLASS}-${reference.transport} kaboo-chip-tray`,
2169
+ title: reference.name,
2170
+ role: "button",
2171
+ tabIndex: 0,
2172
+ onMouseDown: (e) => {
2173
+ e.preventDefault();
2174
+ if (ref.current) onReplace(ref.current);
2175
+ },
2176
+ children: [
2177
+ isImage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2178
+ "img",
2179
+ {
2180
+ className: "kaboo-chip-thumb",
2181
+ src: previewSrc(reference),
2182
+ alt: reference.name
2183
+ }
2184
+ ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "kaboo-chip-ic", children: reference.transport === "object" ? AtIcon : AttachIcon }),
2185
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "kaboo-chip-label", children: reference.name }),
2186
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2187
+ "button",
2188
+ {
2189
+ type: "button",
2190
+ className: "kaboo-chip-x",
2191
+ "aria-label": `Remove ${reference.name}`,
2192
+ onMouseDown: (e) => {
2193
+ e.preventDefault();
2194
+ e.stopPropagation();
2195
+ onRemove();
2196
+ },
2197
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("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__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M18 6 6 18M6 6l12 12" }) })
2198
+ }
2199
+ )
2200
+ ]
2201
+ }
2202
+ );
2203
+ }
2204
+ function caretRectFor(node, offset) {
2205
+ try {
2206
+ const range = document.createRange();
2207
+ range.setStart(node, Math.min(offset, node.textContent?.length ?? 0));
2208
+ range.collapse(true);
2209
+ const rect = range.getBoundingClientRect();
2210
+ if (rect.top === 0 && rect.left === 0) {
2211
+ const parent = node.parentElement;
2212
+ return parent ? parent.getBoundingClientRect() : null;
2213
+ }
2214
+ return rect;
2215
+ } catch {
2216
+ return null;
2217
+ }
1222
2218
  }
2219
+ var ReferencePopover = (0, import_react11.forwardRef)(function ReferencePopover2({ pos, rows, active, query, mode, searchRef, onSearch, onSearchKeyDown, onHover, onPick }, ref) {
2220
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2221
+ "div",
2222
+ {
2223
+ ref,
2224
+ className: "kaboo-refmenu",
2225
+ role: "listbox",
2226
+ style: { left: pos.left, bottom: pos.bottom, width: pos.width },
2227
+ children: [
2228
+ mode === "menu" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "kaboo-refmenu-search", children: [
2229
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "kaboo-refmenu-search-icon", children: SearchIcon }),
2230
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2231
+ "input",
2232
+ {
2233
+ ref: searchRef,
2234
+ type: "text",
2235
+ className: "kaboo-refmenu-search-input",
2236
+ placeholder: "Search\u2026",
2237
+ value: query,
2238
+ onChange: (e) => onSearch(e.target.value),
2239
+ onKeyDown: (e) => onSearchKeyDown(e)
2240
+ }
2241
+ )
2242
+ ] }),
2243
+ rows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "kaboo-refmenu-empty", children: "No matches" }),
2244
+ rows.map((row, i) => {
2245
+ const key = row.kind === "action" ? `action:${row.provider.id}` : `${row.provider.id}:${row.item.id}`;
2246
+ const showHeader = i === 0 || rows[i - 1].group !== row.group;
2247
+ const isUpload = row.kind === "action" && isUploadProvider(row.provider);
2248
+ const icon = isUpload ? AttachIcon : row.kind === "item" ? row.provider.icon ?? AtIcon : row.provider.icon ?? AtIcon;
2249
+ const title = row.kind === "action" ? isUpload ? "Attach a file" : row.provider.label : row.item.label;
2250
+ const subtitle = row.kind === "action" ? isUpload ? "Upload from your device" : void 0 : row.item.description;
2251
+ const custom = row.kind === "item" ? row.provider.renderItem?.(row.item) : void 0;
2252
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
2253
+ showHeader && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "kaboo-refmenu-group", children: row.group }),
2254
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2255
+ "div",
2256
+ {
2257
+ role: "option",
2258
+ "aria-selected": i === active,
2259
+ className: `kaboo-refmenu-item${i === active ? " is-active" : ""}`,
2260
+ onMouseEnter: () => onHover(i),
2261
+ onMouseDown: (e) => {
2262
+ e.preventDefault();
2263
+ onPick(row);
2264
+ },
2265
+ children: [
2266
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "kaboo-refmenu-icon", children: icon }),
2267
+ custom ?? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "kaboo-refmenu-text", children: [
2268
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "kaboo-refmenu-title", children: title }),
2269
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "kaboo-refmenu-sub", children: subtitle })
2270
+ ] })
2271
+ ]
2272
+ }
2273
+ )
2274
+ ] }, key);
2275
+ })
2276
+ ]
2277
+ }
2278
+ );
2279
+ });
1223
2280
 
1224
2281
  // src/components/ActivityPanel.tsx
1225
- var import_jsx_runtime16 = require("react/jsx-runtime");
2282
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1226
2283
  function ActivityPanel() {
1227
2284
  const { groups } = useActivity();
1228
2285
  const { activeDrill } = useDrill();
@@ -1231,14 +2288,14 @@ function ActivityPanel() {
1231
2288
  if (filtered.length === 0 && activeDrill) {
1232
2289
  const exact = groups[activeDrill];
1233
2290
  if (exact) {
1234
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "kaboo-activity-panel", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AgentCard, { groupId: activeDrill, group: exact }) });
2291
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "kaboo-activity-panel", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AgentCard, { groupId: activeDrill, group: exact }) });
1235
2292
  }
1236
2293
  }
1237
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "kaboo-activity-panel", children: filtered.map(([id, group]) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AgentCard, { groupId: id, group }, id)) });
2294
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "kaboo-activity-panel", children: filtered.map(([id, group]) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AgentCard, { groupId: id, group }, id)) });
1238
2295
  }
1239
2296
 
1240
2297
  // src/components/GlassTabs.tsx
1241
- var import_jsx_runtime17 = require("react/jsx-runtime");
2298
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1242
2299
  function GlassTabs() {
1243
2300
  const { drillPath, drillToRoot, drillToLevel } = useDrill();
1244
2301
  const { groups } = useActivity();
@@ -1251,11 +2308,11 @@ function GlassTabs() {
1251
2308
  level: i
1252
2309
  }))
1253
2310
  ];
1254
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("nav", { className: "kaboo-breadcrumb-bar", children: tabs.map((tab, i) => {
2311
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("nav", { className: "kaboo-breadcrumb-bar", children: tabs.map((tab, i) => {
1255
2312
  const isLast = i === tabs.length - 1;
1256
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "kaboo-breadcrumb-item", children: [
1257
- i > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { width: 12, height: 12, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-sep", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M6 4l4 4-4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }),
1258
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2313
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "kaboo-breadcrumb-item", children: [
2314
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { width: 12, height: 12, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-sep", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M6 4l4 4-4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }),
2315
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1259
2316
  "button",
1260
2317
  {
1261
2318
  className: `kaboo-breadcrumb-link ${isLast ? "kaboo-breadcrumb-current" : ""}`,
@@ -1266,7 +2323,7 @@ function GlassTabs() {
1266
2323
  },
1267
2324
  disabled: isLast,
1268
2325
  children: [
1269
- i === 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { width: 14, height: 14, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-icon", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("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" }) }),
2326
+ i === 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { width: 14, height: 14, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-icon", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("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" }) }),
1270
2327
  tab.label
1271
2328
  ]
1272
2329
  }
@@ -1276,20 +2333,20 @@ function GlassTabs() {
1276
2333
  }
1277
2334
 
1278
2335
  // src/components/DrillDetailView.tsx
1279
- var import_react10 = require("react");
1280
- var import_jsx_runtime18 = require("react/jsx-runtime");
2336
+ var import_react12 = require("react");
2337
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1281
2338
  function DrillDetailView() {
1282
2339
  const { activeDrill } = useDrill();
1283
2340
  const { groups } = useActivity();
1284
2341
  const { active: activeInterrupts } = useInterruptBridge();
1285
- const bodyRef = (0, import_react10.useRef)(null);
1286
- (0, import_react10.useEffect)(() => {
2342
+ const bodyRef = (0, import_react12.useRef)(null);
2343
+ (0, import_react12.useEffect)(() => {
1287
2344
  if (activeDrill && bodyRef.current) {
1288
2345
  bodyRef.current.scrollTop = 0;
1289
2346
  }
1290
2347
  }, [activeDrill]);
1291
2348
  if (!activeDrill) {
1292
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "kaboo-drill-detail kaboo-hidden" });
2349
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "kaboo-drill-detail kaboo-hidden" });
1293
2350
  }
1294
2351
  const group = groups[activeDrill];
1295
2352
  const timeline = group?.timeline ?? [];
@@ -1301,15 +2358,15 @@ function DrillDetailView() {
1301
2358
  const renderToolCard = (toolUseId) => {
1302
2359
  const entry = childByToolCall.get(toolUseId);
1303
2360
  if (!entry) return null;
1304
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AgentCard, { groupId: entry[0], group: entry[1] });
2361
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AgentCard, { groupId: entry[0], group: entry[1] });
1305
2362
  };
1306
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: bodyRef, className: "kaboo-drill-detail", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "kaboo-drill-detail-inner", children: [
1307
- group?.task && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "kaboo-agent-card-task kaboo-drill-task", children: [
1308
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("strong", { children: "Task:" }),
2363
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { ref: bodyRef, className: "kaboo-drill-detail", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "kaboo-drill-detail-inner", children: [
2364
+ group?.task && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "kaboo-agent-card-task kaboo-drill-task", children: [
2365
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("strong", { children: "Task:" }),
1309
2366
  " ",
1310
2367
  group.task
1311
2368
  ] }),
1312
- group && timeline.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "kaboo-drill-timeline", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2369
+ group && timeline.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "kaboo-drill-timeline", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1313
2370
  Timeline,
1314
2371
  {
1315
2372
  timeline,
@@ -1318,7 +2375,7 @@ function DrillDetailView() {
1318
2375
  renderToolCard
1319
2376
  }
1320
2377
  ) }),
1321
- unanchoredInterrupts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "kaboo-drill-interrupt", children: unanchoredInterrupts.map((it) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2378
+ unanchoredInterrupts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "kaboo-drill-interrupt", children: unanchoredInterrupts.map((it) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1322
2379
  InterruptRenderer,
1323
2380
  {
1324
2381
  reason: it.reason,
@@ -1328,12 +2385,12 @@ function DrillDetailView() {
1328
2385
  },
1329
2386
  it.id
1330
2387
  )) }),
1331
- leftoverChildren.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "kaboo-drill-children", children: [
1332
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "kaboo-drill-section-label", children: "Sub-agents" }),
1333
- leftoverChildren.map(([id, childGroup]) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AgentCard, { groupId: id, group: childGroup }, id))
2388
+ leftoverChildren.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "kaboo-drill-children", children: [
2389
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "kaboo-drill-section-label", children: "Sub-agents" }),
2390
+ leftoverChildren.map(([id, childGroup]) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AgentCard, { groupId: id, group: childGroup }, id))
1334
2391
  ] }),
1335
- group && timeline.length === 0 && childEntries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "kaboo-agent-card-empty", children: "Working..." }),
1336
- !group && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "kaboo-agent-card-empty", children: "No activity data for this group." })
2392
+ group && timeline.length === 0 && childEntries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "kaboo-agent-card-empty", children: "Working..." }),
2393
+ !group && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "kaboo-agent-card-empty", children: "No activity data for this group." })
1337
2394
  ] }) });
1338
2395
  }
1339
2396
  // Annotate the CommonJS export names for ESM import in node:
@@ -1348,19 +2405,37 @@ function DrillDetailView() {
1348
2405
  InterruptRenderer,
1349
2406
  KabooActivityProvider,
1350
2407
  KabooProvider,
2408
+ KabooReferenceInput,
1351
2409
  MarkdownContent,
1352
2410
  MiniTable,
2411
+ REFERENCES_STATE_KEY,
2412
+ REFERENCE_METADATA_KEYS,
2413
+ ReferenceStateSync,
2414
+ ReferencesProvider,
1353
2415
  StructuredRenderersContext,
1354
2416
  Timeline,
1355
2417
  ToolRow,
2418
+ UPLOAD_MARKER,
2419
+ attachmentToInputContent,
2420
+ buildAttachmentsConfig,
2421
+ buildUserContent,
1356
2422
  directChildren,
1357
2423
  formatToolInput,
1358
2424
  formatToolResult,
2425
+ isUploadProvider,
2426
+ mintReferenceId,
1359
2427
  normalizeResult,
2428
+ objectToStateEntry,
2429
+ referenceMarker,
2430
+ serializeReferences,
1360
2431
  topLevelGroups,
2432
+ uploadFileToReference,
2433
+ uploadProvider,
1361
2434
  useActivity,
1362
2435
  useDrill,
1363
2436
  useInterruptBridge,
1364
- useInterruptFor
2437
+ useInterruptFor,
2438
+ useReferences,
2439
+ withReferenceState
1365
2440
  });
1366
2441
  //# sourceMappingURL=index.cjs.map