@ai-me-chat/react 0.2.1 → 0.3.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
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  AIMeCommandPalette: () => AIMeCommandPalette,
26
26
  AIMeConfirm: () => AIMeConfirm,
27
27
  AIMeProvider: () => AIMeProvider,
28
+ cleanAssistantText: () => cleanAssistantText,
28
29
  renderMarkdown: () => renderMarkdown,
29
30
  useAIMe: () => useAIMe,
30
31
  useAIMeContext: () => useAIMeContext
@@ -44,20 +45,35 @@ function useAIMeContext() {
44
45
 
45
46
  // src/provider.tsx
46
47
  var import_jsx_runtime = require("react/jsx-runtime");
47
- function AIMeProvider({ endpoint, headers, onAction, children }) {
48
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AIMeContext, { value: { endpoint, headers, onAction }, children });
48
+ function AIMeProvider({ endpoint, headers, onAction, stuckTimeout, children }) {
49
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AIMeContext, { value: { endpoint, headers, onAction, stuckTimeout }, children });
49
50
  }
50
51
 
51
52
  // src/chat.tsx
52
- var import_react4 = require("react");
53
+ var import_react5 = require("react");
53
54
 
54
55
  // src/use-ai-me.ts
55
56
  var import_react2 = require("@ai-sdk/react");
56
57
  var import_ai = require("ai");
57
58
  var import_react3 = require("react");
58
59
  var STORAGE_KEY = "ai-me-messages";
60
+ function cleanAssistantText(text) {
61
+ return text.replace(/<tools>[\s\S]*?<\/tools>/g, "").trim();
62
+ }
63
+ function trimIncompleteToolCalls(messages) {
64
+ if (messages.length === 0) return messages;
65
+ const last = messages[messages.length - 1];
66
+ if (last.role !== "assistant") return messages;
67
+ const hasToolCall = last.parts.some((p) => p.type === "tool-call");
68
+ const hasToolResult = last.parts.some((p) => p.type === "tool-result");
69
+ if (hasToolCall && !hasToolResult) {
70
+ return messages.slice(0, -1);
71
+ }
72
+ return messages;
73
+ }
59
74
  function useAIMe() {
60
- const { endpoint, headers } = useAIMeContext();
75
+ const { endpoint, headers, stuckTimeout: configuredTimeout } = useAIMeContext();
76
+ const stuckTimeout = configuredTimeout ?? 3e4;
61
77
  const [input, setInput] = (0, import_react3.useState)("");
62
78
  const initialized = (0, import_react3.useRef)(false);
63
79
  const chat = (0, import_react2.useChat)({
@@ -66,6 +82,24 @@ function useAIMe() {
66
82
  headers
67
83
  })
68
84
  });
85
+ const messages = (0, import_react3.useMemo)(() => {
86
+ return chat.messages.map((m) => {
87
+ if (m.role !== "assistant") return m;
88
+ let changed = false;
89
+ const cleanedParts = m.parts.map((p) => {
90
+ if (p.type !== "text") return p;
91
+ const cleaned = cleanAssistantText(p.text);
92
+ if (cleaned === p.text) return p;
93
+ changed = true;
94
+ return { ...p, text: cleaned };
95
+ });
96
+ if (!changed) return m;
97
+ const nonEmptyParts = cleanedParts.filter(
98
+ (p) => p.type !== "text" || p.text.length > 0
99
+ );
100
+ return { ...m, parts: nonEmptyParts };
101
+ });
102
+ }, [chat.messages]);
69
103
  (0, import_react3.useEffect)(() => {
70
104
  if (initialized.current) return;
71
105
  initialized.current = true;
@@ -74,7 +108,10 @@ function useAIMe() {
74
108
  if (stored) {
75
109
  const parsed = JSON.parse(stored);
76
110
  if (Array.isArray(parsed) && parsed.length > 0) {
77
- chat.setMessages(parsed);
111
+ const cleaned = trimIncompleteToolCalls(parsed);
112
+ if (cleaned.length > 0) {
113
+ chat.setMessages(cleaned);
114
+ }
78
115
  }
79
116
  }
80
117
  } catch {
@@ -91,6 +128,14 @@ function useAIMe() {
91
128
  } catch {
92
129
  }
93
130
  }, [chat.messages]);
131
+ (0, import_react3.useEffect)(() => {
132
+ if (stuckTimeout <= 0) return;
133
+ if (chat.status !== "submitted") return;
134
+ const timer = setTimeout(() => {
135
+ chat.stop();
136
+ }, stuckTimeout);
137
+ return () => clearTimeout(timer);
138
+ }, [chat.status, stuckTimeout, chat.stop]);
94
139
  const handleInputChange = (0, import_react3.useCallback)(
95
140
  (e) => {
96
141
  setInput(e.target.value);
@@ -115,7 +160,7 @@ function useAIMe() {
115
160
  }, [chat]);
116
161
  return {
117
162
  /** Conversation messages */
118
- messages: chat.messages,
163
+ messages,
119
164
  /** Current input value */
120
165
  input,
121
166
  /** Set input value */
@@ -135,7 +180,9 @@ function useAIMe() {
135
180
  /** Set messages */
136
181
  setMessages: chat.setMessages,
137
182
  /** Clear all messages and session storage */
138
- clearMessages
183
+ clearMessages,
184
+ /** Approve or reject a pending tool call (for confirmation flow) */
185
+ addToolApprovalResponse: chat.addToolApprovalResponse
139
186
  };
140
187
  }
141
188
 
@@ -337,8 +384,197 @@ var linkStyle = {
337
384
  textUnderlineOffset: "2px"
338
385
  };
339
386
 
340
- // src/chat.tsx
387
+ // src/confirm.tsx
388
+ var import_react4 = require("react");
341
389
  var import_jsx_runtime3 = require("react/jsx-runtime");
390
+ function AIMeConfirm({
391
+ action,
392
+ description,
393
+ parameters,
394
+ onConfirm,
395
+ onReject
396
+ }) {
397
+ const dialogRef = (0, import_react4.useRef)(null);
398
+ const cancelButtonRef = (0, import_react4.useRef)(null);
399
+ const titleId = (0, import_react4.useId)();
400
+ const descriptionId = (0, import_react4.useId)();
401
+ const handleKeyDown = (0, import_react4.useCallback)(
402
+ (e) => {
403
+ if (e.key === "Escape") {
404
+ e.preventDefault();
405
+ onReject();
406
+ return;
407
+ }
408
+ if (e.key !== "Tab") return;
409
+ const dialog = dialogRef.current;
410
+ if (!dialog) return;
411
+ const focusable = dialog.querySelectorAll(
412
+ 'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
413
+ );
414
+ if (focusable.length === 0) return;
415
+ const first = focusable[0];
416
+ const last = focusable[focusable.length - 1];
417
+ if (e.shiftKey) {
418
+ if (document.activeElement === first) {
419
+ e.preventDefault();
420
+ last.focus();
421
+ }
422
+ } else {
423
+ if (document.activeElement === last) {
424
+ e.preventDefault();
425
+ first.focus();
426
+ }
427
+ }
428
+ },
429
+ [onReject]
430
+ );
431
+ (0, import_react4.useEffect)(() => {
432
+ const previousFocus = document.activeElement;
433
+ cancelButtonRef.current?.focus();
434
+ window.addEventListener("keydown", handleKeyDown);
435
+ return () => {
436
+ window.removeEventListener("keydown", handleKeyDown);
437
+ previousFocus?.focus();
438
+ };
439
+ }, [handleKeyDown]);
440
+ const overlayStyle = {
441
+ ...defaultThemeVars,
442
+ position: "fixed",
443
+ inset: 0,
444
+ backgroundColor: "rgba(0, 0, 0, 0.4)",
445
+ display: "flex",
446
+ alignItems: "center",
447
+ justifyContent: "center",
448
+ zIndex: 1e4,
449
+ fontFamily: "var(--ai-me-font)"
450
+ };
451
+ const dialogStyle = {
452
+ backgroundColor: "var(--ai-me-bg)",
453
+ borderRadius: "var(--ai-me-radius)",
454
+ padding: 24,
455
+ maxWidth: 420,
456
+ width: "90%",
457
+ boxShadow: "var(--ai-me-shadow)",
458
+ color: "var(--ai-me-text)"
459
+ };
460
+ const focusStyle = {
461
+ outline: "2px solid transparent",
462
+ outlineOffset: 2
463
+ };
464
+ function applyFocusRing(el) {
465
+ el.style.outline = "2px solid var(--ai-me-primary)";
466
+ el.style.outlineOffset = "2px";
467
+ }
468
+ function removeFocusRing(el) {
469
+ el.style.outline = "2px solid transparent";
470
+ el.style.outlineOffset = "2px";
471
+ }
472
+ return (
473
+ // Overlay is presentational — role and aria go on the inner dialog
474
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
475
+ "div",
476
+ {
477
+ style: overlayStyle,
478
+ onClick: (e) => {
479
+ if (e.target === e.currentTarget) onReject();
480
+ },
481
+ "aria-hidden": "false",
482
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
483
+ "div",
484
+ {
485
+ ref: dialogRef,
486
+ style: dialogStyle,
487
+ role: "alertdialog",
488
+ "aria-modal": "true",
489
+ "aria-labelledby": titleId,
490
+ "aria-describedby": descriptionId,
491
+ tabIndex: -1,
492
+ onClick: (e) => e.stopPropagation(),
493
+ children: [
494
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { id: titleId, style: { margin: "0 0 8px", fontSize: 16 }, children: "Confirm Action" }),
495
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "0 0 4px", fontSize: 14, fontWeight: 600 }, children: action }),
496
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
497
+ "p",
498
+ {
499
+ id: descriptionId,
500
+ style: {
501
+ margin: "0 0 16px",
502
+ fontSize: 13,
503
+ color: "var(--ai-me-text-secondary)"
504
+ },
505
+ children: description
506
+ }
507
+ ),
508
+ parameters && Object.keys(parameters).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
509
+ "pre",
510
+ {
511
+ style: {
512
+ margin: "0 0 16px",
513
+ padding: 12,
514
+ backgroundColor: "var(--ai-me-bg-secondary)",
515
+ borderRadius: 8,
516
+ fontSize: 12,
517
+ overflow: "auto",
518
+ maxHeight: 200,
519
+ border: "1px solid var(--ai-me-border)"
520
+ },
521
+ children: JSON.stringify(parameters, null, 2)
522
+ }
523
+ ),
524
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
525
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
526
+ "button",
527
+ {
528
+ ref: cancelButtonRef,
529
+ type: "button",
530
+ onClick: onReject,
531
+ style: {
532
+ padding: "8px 16px",
533
+ border: "1px solid var(--ai-me-border)",
534
+ borderRadius: 8,
535
+ backgroundColor: "var(--ai-me-bg)",
536
+ color: "var(--ai-me-text)",
537
+ cursor: "pointer",
538
+ fontSize: 14,
539
+ ...focusStyle
540
+ },
541
+ onFocus: (e) => applyFocusRing(e.currentTarget),
542
+ onBlur: (e) => removeFocusRing(e.currentTarget),
543
+ children: "Cancel"
544
+ }
545
+ ),
546
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
547
+ "button",
548
+ {
549
+ type: "button",
550
+ onClick: onConfirm,
551
+ style: {
552
+ padding: "8px 16px",
553
+ border: "none",
554
+ borderRadius: 8,
555
+ // #fff on var(--ai-me-primary) = #6366f1 → contrast ≈ 4.6:1 (passes AA)
556
+ backgroundColor: "var(--ai-me-primary)",
557
+ color: "#fff",
558
+ cursor: "pointer",
559
+ fontSize: 14,
560
+ ...focusStyle
561
+ },
562
+ onFocus: (e) => applyFocusRing(e.currentTarget),
563
+ onBlur: (e) => removeFocusRing(e.currentTarget),
564
+ children: "Confirm"
565
+ }
566
+ )
567
+ ] })
568
+ ]
569
+ }
570
+ )
571
+ }
572
+ )
573
+ );
574
+ }
575
+
576
+ // src/chat.tsx
577
+ var import_jsx_runtime4 = require("react/jsx-runtime");
342
578
  var srOnly = {
343
579
  position: "absolute",
344
580
  width: 1,
@@ -350,6 +586,12 @@ var srOnly = {
350
586
  whiteSpace: "nowrap",
351
587
  borderWidth: 0
352
588
  };
589
+ function DefaultAIIcon() {
590
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
591
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5Z" }),
592
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M19 11l.75 2.25L22 14l-2.25.75L19 17l-.75-2.25L16 14l2.25-.75Z" })
593
+ ] });
594
+ }
353
595
  function AIMeChat({
354
596
  position = "bottom-right",
355
597
  theme,
@@ -358,15 +600,17 @@ function AIMeChat({
358
600
  defaultOpen = false,
359
601
  onToggle,
360
602
  onToolComplete,
361
- onMessageComplete
603
+ onMessageComplete,
604
+ triggerIcon,
605
+ renderConfirmation
362
606
  }) {
363
- const [open, setOpen] = (0, import_react4.useState)(defaultOpen);
364
- const messagesEndRef = (0, import_react4.useRef)(null);
365
- const inputRef = (0, import_react4.useRef)(null);
366
- const panelRef = (0, import_react4.useRef)(null);
367
- const triggerRef = (0, import_react4.useRef)(null);
368
- const firedToolResults = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
369
- const prevStatus = (0, import_react4.useRef)(null);
607
+ const [open, setOpen] = (0, import_react5.useState)(defaultOpen);
608
+ const messagesEndRef = (0, import_react5.useRef)(null);
609
+ const inputRef = (0, import_react5.useRef)(null);
610
+ const panelRef = (0, import_react5.useRef)(null);
611
+ const triggerRef = (0, import_react5.useRef)(null);
612
+ const firedToolResults = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
613
+ const prevStatus = (0, import_react5.useRef)(null);
370
614
  const {
371
615
  messages,
372
616
  input,
@@ -374,17 +618,18 @@ function AIMeChat({
374
618
  handleSubmit,
375
619
  status,
376
620
  error,
377
- setInput
621
+ setInput,
622
+ addToolApprovalResponse
378
623
  } = useAIMe();
379
- const titleId = (0, import_react4.useId)();
380
- const messagesId = (0, import_react4.useId)();
624
+ const titleId = (0, import_react5.useId)();
625
+ const messagesId = (0, import_react5.useId)();
381
626
  const isInline = position === "inline";
382
- const toggleOpen = (0, import_react4.useCallback)(() => {
627
+ const toggleOpen = (0, import_react5.useCallback)(() => {
383
628
  const next = !open;
384
629
  setOpen(next);
385
630
  onToggle?.(next);
386
631
  }, [open, onToggle]);
387
- (0, import_react4.useEffect)(() => {
632
+ (0, import_react5.useEffect)(() => {
388
633
  function handleKeyDown(e) {
389
634
  if ((e.metaKey || e.ctrlKey) && e.key === ".") {
390
635
  e.preventDefault();
@@ -394,10 +639,10 @@ function AIMeChat({
394
639
  window.addEventListener("keydown", handleKeyDown);
395
640
  return () => window.removeEventListener("keydown", handleKeyDown);
396
641
  }, [toggleOpen]);
397
- (0, import_react4.useEffect)(() => {
642
+ (0, import_react5.useEffect)(() => {
398
643
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
399
644
  }, [messages]);
400
- (0, import_react4.useEffect)(() => {
645
+ (0, import_react5.useEffect)(() => {
401
646
  if (!onToolComplete) return;
402
647
  for (const message of messages) {
403
648
  for (const part of message.parts) {
@@ -413,7 +658,7 @@ function AIMeChat({
413
658
  }
414
659
  }
415
660
  }, [messages, onToolComplete]);
416
- (0, import_react4.useEffect)(() => {
661
+ (0, import_react5.useEffect)(() => {
417
662
  const prev = prevStatus.current;
418
663
  prevStatus.current = status;
419
664
  if (!onMessageComplete) return;
@@ -435,7 +680,7 @@ function AIMeChat({
435
680
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0
436
681
  });
437
682
  }, [status, messages, onMessageComplete]);
438
- (0, import_react4.useEffect)(() => {
683
+ (0, import_react5.useEffect)(() => {
439
684
  if (open) {
440
685
  panelRef.current?.focus();
441
686
  setTimeout(() => inputRef.current?.focus(), 0);
@@ -443,7 +688,7 @@ function AIMeChat({
443
688
  triggerRef.current?.focus();
444
689
  }
445
690
  }, [open]);
446
- (0, import_react4.useEffect)(() => {
691
+ (0, import_react5.useEffect)(() => {
447
692
  if (!open || isInline) return;
448
693
  function handleKeyDown(e) {
449
694
  if (e.key === "Escape") {
@@ -475,6 +720,20 @@ function AIMeChat({
475
720
  window.addEventListener("keydown", handleKeyDown);
476
721
  return () => window.removeEventListener("keydown", handleKeyDown);
477
722
  }, [open, isInline, toggleOpen]);
723
+ const pendingToolCalls = (0, import_react5.useMemo)(() => {
724
+ const resultIds = /* @__PURE__ */ new Set();
725
+ const toolCalls = [];
726
+ for (const m of messages) {
727
+ for (const p of m.parts) {
728
+ if (p.type === "tool-result") {
729
+ resultIds.add(p.toolCallId);
730
+ } else if (p.type === "tool-call") {
731
+ toolCalls.push(p);
732
+ }
733
+ }
734
+ }
735
+ return toolCalls.filter((tc) => !resultIds.has(tc.toolCallId));
736
+ }, [messages]);
478
737
  const themeVars = {
479
738
  ...defaultThemeVars,
480
739
  ...themeToVars(theme)
@@ -530,8 +789,8 @@ function AIMeChat({
530
789
  zIndex: 9999
531
790
  };
532
791
  const isStreaming = status === "submitted" || status === "streaming";
533
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
534
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
792
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
793
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
535
794
  "button",
536
795
  {
537
796
  ref: triggerRef,
@@ -541,10 +800,10 @@ function AIMeChat({
541
800
  "aria-expanded": open,
542
801
  "aria-controls": isInline ? void 0 : "ai-me-chat-panel",
543
802
  type: "button",
544
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", children: "\u{1F4AC}" })
803
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", children: triggerIcon ?? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DefaultAIIcon, {}) })
545
804
  }
546
805
  ),
547
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
806
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
548
807
  "div",
549
808
  {
550
809
  id: "ai-me-chat-panel",
@@ -556,7 +815,7 @@ function AIMeChat({
556
815
  "aria-busy": isStreaming,
557
816
  tabIndex: -1,
558
817
  children: [
559
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
818
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
560
819
  "div",
561
820
  {
562
821
  style: {
@@ -568,8 +827,8 @@ function AIMeChat({
568
827
  backgroundColor: "var(--ai-me-bg-secondary)"
569
828
  },
570
829
  children: [
571
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { id: titleId, style: { fontWeight: 600, fontSize: 14 }, children: "AI Assistant" }),
572
- !isInline && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
830
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: titleId, style: { fontWeight: 600, fontSize: 14 }, children: "AI Assistant" }),
831
+ !isInline && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
573
832
  "button",
574
833
  {
575
834
  onClick: toggleOpen,
@@ -593,13 +852,13 @@ function AIMeChat({
593
852
  },
594
853
  "aria-label": "Close chat",
595
854
  type: "button",
596
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", children: "\u2715" })
855
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", children: "\u2715" })
597
856
  }
598
857
  )
599
858
  ]
600
859
  }
601
860
  ),
602
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
861
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
603
862
  "a",
604
863
  {
605
864
  href: "#ai-me-chat-input",
@@ -629,7 +888,7 @@ function AIMeChat({
629
888
  children: "Skip to message input"
630
889
  }
631
890
  ),
632
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
891
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
633
892
  "div",
634
893
  {
635
894
  id: messagesId,
@@ -645,9 +904,9 @@ function AIMeChat({
645
904
  gap: 12
646
905
  },
647
906
  children: [
648
- messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { color: "var(--ai-me-text-secondary)", fontSize: 14 }, children: [
649
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: welcomeMessage }),
650
- suggestedPrompts && suggestedPrompts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
907
+ messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { color: "var(--ai-me-text-secondary)", fontSize: 14 }, children: [
908
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: welcomeMessage }),
909
+ suggestedPrompts && suggestedPrompts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
651
910
  "div",
652
911
  {
653
912
  style: {
@@ -657,8 +916,8 @@ function AIMeChat({
657
916
  gap: 8
658
917
  },
659
918
  children: [
660
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "0 0 4px", fontSize: 12, fontWeight: 500 }, children: "Suggested questions:" }),
661
- suggestedPrompts.map((prompt) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
919
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: "0 0 4px", fontSize: 12, fontWeight: 500 }, children: "Suggested questions:" }),
920
+ suggestedPrompts.map((prompt) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
662
921
  "button",
663
922
  {
664
923
  type: "button",
@@ -695,7 +954,7 @@ function AIMeChat({
695
954
  messages.map((m) => {
696
955
  const hasTextContent = m.parts.some((p) => p.type === "text");
697
956
  if (!hasTextContent && m.role === "assistant") return null;
698
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
957
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
699
958
  "div",
700
959
  {
701
960
  style: {
@@ -711,16 +970,16 @@ function AIMeChat({
711
970
  wordBreak: "break-word"
712
971
  },
713
972
  children: [
714
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: srOnly, children: m.role === "user" ? "You: " : "Assistant: " }),
973
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: srOnly, children: m.role === "user" ? "You: " : "Assistant: " }),
715
974
  m.parts.map(
716
- (p, i) => p.type === "text" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: m.role === "assistant" ? renderMarkdown(p.text) : p.text }, i) : null
975
+ (p, i) => p.type === "text" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: m.role === "assistant" ? renderMarkdown(p.text) : p.text }, i) : null
717
976
  )
718
977
  ]
719
978
  },
720
979
  m.id
721
980
  );
722
981
  }),
723
- status === "submitted" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
982
+ status === "submitted" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
724
983
  "div",
725
984
  {
726
985
  "aria-label": "Assistant is thinking",
@@ -729,10 +988,10 @@ function AIMeChat({
729
988
  color: "var(--ai-me-text-secondary)",
730
989
  fontSize: 13
731
990
  },
732
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", children: "Thinking\u2026" })
991
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", children: "Thinking\u2026" })
733
992
  }
734
993
  ),
735
- error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
994
+ error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
736
995
  "div",
737
996
  {
738
997
  role: "alert",
@@ -748,11 +1007,11 @@ function AIMeChat({
748
1007
  children: "Something went wrong. Please try again."
749
1008
  }
750
1009
  ),
751
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: messagesEndRef, "aria-hidden": "true" })
1010
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: messagesEndRef, "aria-hidden": "true" })
752
1011
  ]
753
1012
  }
754
1013
  ),
755
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1014
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
756
1015
  "form",
757
1016
  {
758
1017
  onSubmit: handleSubmit,
@@ -763,7 +1022,7 @@ function AIMeChat({
763
1022
  gap: 8
764
1023
  },
765
1024
  children: [
766
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1025
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
767
1026
  "label",
768
1027
  {
769
1028
  htmlFor: "ai-me-chat-input",
@@ -771,7 +1030,7 @@ function AIMeChat({
771
1030
  children: "Message to AI Assistant"
772
1031
  }
773
1032
  ),
774
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1033
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
775
1034
  "input",
776
1035
  {
777
1036
  id: "ai-me-chat-input",
@@ -802,7 +1061,7 @@ function AIMeChat({
802
1061
  }
803
1062
  }
804
1063
  ),
805
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1064
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
806
1065
  "button",
807
1066
  {
808
1067
  type: "submit",
@@ -837,13 +1096,38 @@ function AIMeChat({
837
1096
  )
838
1097
  ]
839
1098
  }
840
- )
1099
+ ),
1100
+ pendingToolCalls.map((tc) => {
1101
+ const onConfirm = () => addToolApprovalResponse({ id: tc.toolCallId, approved: true });
1102
+ const onCancel = () => addToolApprovalResponse({ id: tc.toolCallId, approved: false, reason: "User cancelled" });
1103
+ return renderConfirmation ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react5.Fragment, { children: renderConfirmation({
1104
+ tool: {
1105
+ name: tc.toolName,
1106
+ httpMethod: "",
1107
+ path: "",
1108
+ description: tc.toolName
1109
+ },
1110
+ params: tc.args,
1111
+ onConfirm,
1112
+ onCancel
1113
+ }) }, tc.toolCallId) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1114
+ AIMeConfirm,
1115
+ {
1116
+ action: tc.toolName,
1117
+ description: `Execute ${tc.toolName}?`,
1118
+ parameters: tc.args,
1119
+ onConfirm,
1120
+ onReject: onCancel
1121
+ },
1122
+ tc.toolCallId
1123
+ );
1124
+ })
841
1125
  ] });
842
1126
  }
843
1127
 
844
1128
  // src/command-palette.tsx
845
- var import_react5 = require("react");
846
- var import_jsx_runtime4 = require("react/jsx-runtime");
1129
+ var import_react6 = require("react");
1130
+ var import_jsx_runtime5 = require("react/jsx-runtime");
847
1131
  var defaultCommands = [
848
1132
  {
849
1133
  id: "help",
@@ -887,17 +1171,17 @@ function AIMeCommandPalette({
887
1171
  shortcut = { key: "k", meta: true },
888
1172
  onToggle
889
1173
  }) {
890
- const [open, setOpen] = (0, import_react5.useState)(false);
891
- const [query, setQuery] = (0, import_react5.useState)("");
892
- const [selectedIndex, setSelectedIndex] = (0, import_react5.useState)(0);
893
- const inputRef = (0, import_react5.useRef)(null);
894
- const listRef = (0, import_react5.useRef)(null);
895
- const dialogRef = (0, import_react5.useRef)(null);
896
- const previousFocusRef = (0, import_react5.useRef)(null);
1174
+ const [open, setOpen] = (0, import_react6.useState)(false);
1175
+ const [query, setQuery] = (0, import_react6.useState)("");
1176
+ const [selectedIndex, setSelectedIndex] = (0, import_react6.useState)(0);
1177
+ const inputRef = (0, import_react6.useRef)(null);
1178
+ const listRef = (0, import_react6.useRef)(null);
1179
+ const dialogRef = (0, import_react6.useRef)(null);
1180
+ const previousFocusRef = (0, import_react6.useRef)(null);
897
1181
  const { sendMessage } = useAIMe();
898
- const titleId = (0, import_react5.useId)();
899
- const inputId = (0, import_react5.useId)();
900
- const toggle = (0, import_react5.useCallback)(
1182
+ const titleId = (0, import_react6.useId)();
1183
+ const inputId = (0, import_react6.useId)();
1184
+ const toggle = (0, import_react6.useCallback)(
901
1185
  (next) => {
902
1186
  setOpen(next);
903
1187
  setQuery("");
@@ -906,7 +1190,7 @@ function AIMeCommandPalette({
906
1190
  },
907
1191
  [onToggle]
908
1192
  );
909
- (0, import_react5.useEffect)(() => {
1193
+ (0, import_react6.useEffect)(() => {
910
1194
  function handleKeyDown2(e) {
911
1195
  const metaMatch = shortcut.meta ? e.metaKey : true;
912
1196
  const ctrlMatch = shortcut.ctrl ? e.ctrlKey : !shortcut.meta ? e.ctrlKey : true;
@@ -921,7 +1205,7 @@ function AIMeCommandPalette({
921
1205
  window.addEventListener("keydown", handleKeyDown2);
922
1206
  return () => window.removeEventListener("keydown", handleKeyDown2);
923
1207
  }, [open, shortcut, toggle]);
924
- (0, import_react5.useEffect)(() => {
1208
+ (0, import_react6.useEffect)(() => {
925
1209
  if (open) {
926
1210
  setTimeout(() => inputRef.current?.focus(), 0);
927
1211
  } else {
@@ -931,7 +1215,7 @@ function AIMeCommandPalette({
931
1215
  }
932
1216
  }
933
1217
  }, [open]);
934
- (0, import_react5.useEffect)(() => {
1218
+ (0, import_react6.useEffect)(() => {
935
1219
  if (!open) return;
936
1220
  function handleFocusTrap(e) {
937
1221
  if (e.key !== "Tab") return;
@@ -961,12 +1245,12 @@ function AIMeCommandPalette({
961
1245
  const filtered = query.trim() ? commands.filter(
962
1246
  (cmd) => cmd.label.toLowerCase().includes(query.toLowerCase()) || cmd.description?.toLowerCase().includes(query.toLowerCase()) || cmd.category?.toLowerCase().includes(query.toLowerCase())
963
1247
  ) : commands;
964
- (0, import_react5.useEffect)(() => {
1248
+ (0, import_react6.useEffect)(() => {
965
1249
  if (selectedIndex >= filtered.length) {
966
1250
  setSelectedIndex(Math.max(0, filtered.length - 1));
967
1251
  }
968
1252
  }, [filtered.length, selectedIndex]);
969
- (0, import_react5.useEffect)(() => {
1253
+ (0, import_react6.useEffect)(() => {
970
1254
  const list = listRef.current;
971
1255
  if (!list) return;
972
1256
  const selected = list.children[selectedIndex];
@@ -1011,8 +1295,8 @@ function AIMeCommandPalette({
1011
1295
  grouped.get(cat).push(cmd);
1012
1296
  }
1013
1297
  let flatIndex = 0;
1014
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1015
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1298
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1299
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1016
1300
  "div",
1017
1301
  {
1018
1302
  onClick: () => toggle(false),
@@ -1025,7 +1309,7 @@ function AIMeCommandPalette({
1025
1309
  "aria-hidden": "true"
1026
1310
  }
1027
1311
  ),
1028
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1312
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1029
1313
  "div",
1030
1314
  {
1031
1315
  ref: dialogRef,
@@ -1053,10 +1337,10 @@ function AIMeCommandPalette({
1053
1337
  "aria-labelledby": titleId,
1054
1338
  tabIndex: -1,
1055
1339
  children: [
1056
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { id: titleId, style: srOnly2, children: "Command Palette" }),
1057
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: "12px 16px", borderBottom: "1px solid var(--ai-me-border)" }, children: [
1058
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { htmlFor: inputId, style: srOnly2, children: "Search commands or ask AI" }),
1059
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1340
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { id: titleId, style: srOnly2, children: "Command Palette" }),
1341
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { padding: "12px 16px", borderBottom: "1px solid var(--ai-me-border)" }, children: [
1342
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: inputId, style: srOnly2, children: "Search commands or ask AI" }),
1343
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1060
1344
  "input",
1061
1345
  {
1062
1346
  id: inputId,
@@ -1094,7 +1378,7 @@ function AIMeCommandPalette({
1094
1378
  }
1095
1379
  )
1096
1380
  ] }),
1097
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1381
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1098
1382
  "div",
1099
1383
  {
1100
1384
  id: "ai-me-cmd-listbox",
@@ -1103,7 +1387,7 @@ function AIMeCommandPalette({
1103
1387
  role: "listbox",
1104
1388
  "aria-label": "Commands",
1105
1389
  children: [
1106
- filtered.length === 0 && query.trim() && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1390
+ filtered.length === 0 && query.trim() && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1107
1391
  "div",
1108
1392
  {
1109
1393
  role: "option",
@@ -1123,8 +1407,8 @@ function AIMeCommandPalette({
1123
1407
  ),
1124
1408
  Array.from(grouped.entries()).map(([category, items]) => (
1125
1409
  // role="group" with aria-label for the category heading
1126
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { role: "group", "aria-label": category, children: [
1127
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1410
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { role: "group", "aria-label": category, children: [
1411
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1128
1412
  "div",
1129
1413
  {
1130
1414
  "aria-hidden": "true",
@@ -1142,7 +1426,7 @@ function AIMeCommandPalette({
1142
1426
  items.map((cmd) => {
1143
1427
  const idx = flatIndex++;
1144
1428
  const isSelected = idx === selectedIndex;
1145
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1429
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1146
1430
  "div",
1147
1431
  {
1148
1432
  id: `cmd-${cmd.id}`,
@@ -1175,10 +1459,10 @@ function AIMeCommandPalette({
1175
1459
  },
1176
1460
  children: [
1177
1461
  cmd.icon && // Icon is decorative — label comes from cmd.label
1178
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", style: { fontSize: 16, flexShrink: 0 }, children: cmd.icon }),
1179
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
1180
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 14, fontWeight: 500 }, children: cmd.label }),
1181
- cmd.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1462
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": "true", style: { fontSize: 16, flexShrink: 0 }, children: cmd.icon }),
1463
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
1464
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 14, fontWeight: 500 }, children: cmd.label }),
1465
+ cmd.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1182
1466
  "div",
1183
1467
  {
1184
1468
  style: {
@@ -1202,7 +1486,7 @@ function AIMeCommandPalette({
1202
1486
  ]
1203
1487
  }
1204
1488
  ),
1205
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1489
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1206
1490
  "div",
1207
1491
  {
1208
1492
  "aria-hidden": "true",
@@ -1215,9 +1499,9 @@ function AIMeCommandPalette({
1215
1499
  gap: 16
1216
1500
  },
1217
1501
  children: [
1218
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u2191\u2193 Navigate" }),
1219
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u21B5 Select" }),
1220
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Esc Close" })
1502
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u2191\u2193 Navigate" }),
1503
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u21B5 Select" }),
1504
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Esc Close" })
1221
1505
  ]
1222
1506
  }
1223
1507
  )
@@ -1226,201 +1510,13 @@ function AIMeCommandPalette({
1226
1510
  )
1227
1511
  ] });
1228
1512
  }
1229
-
1230
- // src/confirm.tsx
1231
- var import_react6 = require("react");
1232
- var import_jsx_runtime5 = require("react/jsx-runtime");
1233
- function AIMeConfirm({
1234
- action,
1235
- description,
1236
- parameters,
1237
- onConfirm,
1238
- onReject
1239
- }) {
1240
- const dialogRef = (0, import_react6.useRef)(null);
1241
- const cancelButtonRef = (0, import_react6.useRef)(null);
1242
- const titleId = (0, import_react6.useId)();
1243
- const descriptionId = (0, import_react6.useId)();
1244
- const handleKeyDown = (0, import_react6.useCallback)(
1245
- (e) => {
1246
- if (e.key === "Escape") {
1247
- e.preventDefault();
1248
- onReject();
1249
- return;
1250
- }
1251
- if (e.key !== "Tab") return;
1252
- const dialog = dialogRef.current;
1253
- if (!dialog) return;
1254
- const focusable = dialog.querySelectorAll(
1255
- 'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
1256
- );
1257
- if (focusable.length === 0) return;
1258
- const first = focusable[0];
1259
- const last = focusable[focusable.length - 1];
1260
- if (e.shiftKey) {
1261
- if (document.activeElement === first) {
1262
- e.preventDefault();
1263
- last.focus();
1264
- }
1265
- } else {
1266
- if (document.activeElement === last) {
1267
- e.preventDefault();
1268
- first.focus();
1269
- }
1270
- }
1271
- },
1272
- [onReject]
1273
- );
1274
- (0, import_react6.useEffect)(() => {
1275
- const previousFocus = document.activeElement;
1276
- cancelButtonRef.current?.focus();
1277
- window.addEventListener("keydown", handleKeyDown);
1278
- return () => {
1279
- window.removeEventListener("keydown", handleKeyDown);
1280
- previousFocus?.focus();
1281
- };
1282
- }, [handleKeyDown]);
1283
- const overlayStyle = {
1284
- ...defaultThemeVars,
1285
- position: "fixed",
1286
- inset: 0,
1287
- backgroundColor: "rgba(0, 0, 0, 0.4)",
1288
- display: "flex",
1289
- alignItems: "center",
1290
- justifyContent: "center",
1291
- zIndex: 1e4,
1292
- fontFamily: "var(--ai-me-font)"
1293
- };
1294
- const dialogStyle = {
1295
- backgroundColor: "var(--ai-me-bg)",
1296
- borderRadius: "var(--ai-me-radius)",
1297
- padding: 24,
1298
- maxWidth: 420,
1299
- width: "90%",
1300
- boxShadow: "var(--ai-me-shadow)",
1301
- color: "var(--ai-me-text)"
1302
- };
1303
- const focusStyle = {
1304
- outline: "2px solid transparent",
1305
- outlineOffset: 2
1306
- };
1307
- function applyFocusRing(el) {
1308
- el.style.outline = "2px solid var(--ai-me-primary)";
1309
- el.style.outlineOffset = "2px";
1310
- }
1311
- function removeFocusRing(el) {
1312
- el.style.outline = "2px solid transparent";
1313
- el.style.outlineOffset = "2px";
1314
- }
1315
- return (
1316
- // Overlay is presentational — role and aria go on the inner dialog
1317
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1318
- "div",
1319
- {
1320
- style: overlayStyle,
1321
- onClick: (e) => {
1322
- if (e.target === e.currentTarget) onReject();
1323
- },
1324
- "aria-hidden": "false",
1325
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1326
- "div",
1327
- {
1328
- ref: dialogRef,
1329
- style: dialogStyle,
1330
- role: "alertdialog",
1331
- "aria-modal": "true",
1332
- "aria-labelledby": titleId,
1333
- "aria-describedby": descriptionId,
1334
- tabIndex: -1,
1335
- onClick: (e) => e.stopPropagation(),
1336
- children: [
1337
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { id: titleId, style: { margin: "0 0 8px", fontSize: 16 }, children: "Confirm Action" }),
1338
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: { margin: "0 0 4px", fontSize: 14, fontWeight: 600 }, children: action }),
1339
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1340
- "p",
1341
- {
1342
- id: descriptionId,
1343
- style: {
1344
- margin: "0 0 16px",
1345
- fontSize: 13,
1346
- color: "var(--ai-me-text-secondary)"
1347
- },
1348
- children: description
1349
- }
1350
- ),
1351
- parameters && Object.keys(parameters).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1352
- "pre",
1353
- {
1354
- style: {
1355
- margin: "0 0 16px",
1356
- padding: 12,
1357
- backgroundColor: "var(--ai-me-bg-secondary)",
1358
- borderRadius: 8,
1359
- fontSize: 12,
1360
- overflow: "auto",
1361
- maxHeight: 200,
1362
- border: "1px solid var(--ai-me-border)"
1363
- },
1364
- children: JSON.stringify(parameters, null, 2)
1365
- }
1366
- ),
1367
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
1368
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1369
- "button",
1370
- {
1371
- ref: cancelButtonRef,
1372
- type: "button",
1373
- onClick: onReject,
1374
- style: {
1375
- padding: "8px 16px",
1376
- border: "1px solid var(--ai-me-border)",
1377
- borderRadius: 8,
1378
- backgroundColor: "var(--ai-me-bg)",
1379
- color: "var(--ai-me-text)",
1380
- cursor: "pointer",
1381
- fontSize: 14,
1382
- ...focusStyle
1383
- },
1384
- onFocus: (e) => applyFocusRing(e.currentTarget),
1385
- onBlur: (e) => removeFocusRing(e.currentTarget),
1386
- children: "Cancel"
1387
- }
1388
- ),
1389
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1390
- "button",
1391
- {
1392
- type: "button",
1393
- onClick: onConfirm,
1394
- style: {
1395
- padding: "8px 16px",
1396
- border: "none",
1397
- borderRadius: 8,
1398
- // #fff on var(--ai-me-primary) = #6366f1 → contrast ≈ 4.6:1 (passes AA)
1399
- backgroundColor: "var(--ai-me-primary)",
1400
- color: "#fff",
1401
- cursor: "pointer",
1402
- fontSize: 14,
1403
- ...focusStyle
1404
- },
1405
- onFocus: (e) => applyFocusRing(e.currentTarget),
1406
- onBlur: (e) => removeFocusRing(e.currentTarget),
1407
- children: "Confirm"
1408
- }
1409
- )
1410
- ] })
1411
- ]
1412
- }
1413
- )
1414
- }
1415
- )
1416
- );
1417
- }
1418
1513
  // Annotate the CommonJS export names for ESM import in node:
1419
1514
  0 && (module.exports = {
1420
1515
  AIMeChat,
1421
1516
  AIMeCommandPalette,
1422
1517
  AIMeConfirm,
1423
1518
  AIMeProvider,
1519
+ cleanAssistantText,
1424
1520
  renderMarkdown,
1425
1521
  useAIMe,
1426
1522
  useAIMeContext