@ai-me-chat/react 0.2.0 → 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",
@@ -692,31 +951,35 @@ function AIMeChat({
692
951
  }
693
952
  )
694
953
  ] }),
695
- messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
696
- "div",
697
- {
698
- style: {
699
- alignSelf: m.role === "user" ? "flex-end" : "flex-start",
700
- maxWidth: "85%",
701
- padding: "8px 12px",
702
- borderRadius: 8,
703
- backgroundColor: m.role === "user" ? "var(--ai-me-primary)" : "var(--ai-me-bg-secondary)",
704
- color: m.role === "user" ? "#fff" : "var(--ai-me-text)",
705
- fontSize: 14,
706
- lineHeight: 1.5,
707
- whiteSpace: "pre-wrap",
708
- wordBreak: "break-word"
954
+ messages.map((m) => {
955
+ const hasTextContent = m.parts.some((p) => p.type === "text");
956
+ if (!hasTextContent && m.role === "assistant") return null;
957
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
958
+ "div",
959
+ {
960
+ style: {
961
+ alignSelf: m.role === "user" ? "flex-end" : "flex-start",
962
+ maxWidth: "85%",
963
+ padding: "8px 12px",
964
+ borderRadius: 8,
965
+ backgroundColor: m.role === "user" ? "var(--ai-me-primary)" : "var(--ai-me-bg-secondary)",
966
+ color: m.role === "user" ? "#fff" : "var(--ai-me-text)",
967
+ fontSize: 14,
968
+ lineHeight: 1.5,
969
+ whiteSpace: "pre-wrap",
970
+ wordBreak: "break-word"
971
+ },
972
+ children: [
973
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: srOnly, children: m.role === "user" ? "You: " : "Assistant: " }),
974
+ m.parts.map(
975
+ (p, i) => p.type === "text" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: m.role === "assistant" ? renderMarkdown(p.text) : p.text }, i) : null
976
+ )
977
+ ]
709
978
  },
710
- children: [
711
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: srOnly, children: m.role === "user" ? "You: " : "Assistant: " }),
712
- m.parts.map(
713
- (p, i) => p.type === "text" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: m.role === "assistant" ? renderMarkdown(p.text) : p.text }, i) : null
714
- )
715
- ]
716
- },
717
- m.id
718
- )),
719
- status === "submitted" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
979
+ m.id
980
+ );
981
+ }),
982
+ status === "submitted" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
720
983
  "div",
721
984
  {
722
985
  "aria-label": "Assistant is thinking",
@@ -725,10 +988,10 @@ function AIMeChat({
725
988
  color: "var(--ai-me-text-secondary)",
726
989
  fontSize: 13
727
990
  },
728
- 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" })
729
992
  }
730
993
  ),
731
- error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
994
+ error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
732
995
  "div",
733
996
  {
734
997
  role: "alert",
@@ -744,11 +1007,11 @@ function AIMeChat({
744
1007
  children: "Something went wrong. Please try again."
745
1008
  }
746
1009
  ),
747
- /* @__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" })
748
1011
  ]
749
1012
  }
750
1013
  ),
751
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1014
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
752
1015
  "form",
753
1016
  {
754
1017
  onSubmit: handleSubmit,
@@ -759,7 +1022,7 @@ function AIMeChat({
759
1022
  gap: 8
760
1023
  },
761
1024
  children: [
762
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1025
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
763
1026
  "label",
764
1027
  {
765
1028
  htmlFor: "ai-me-chat-input",
@@ -767,7 +1030,7 @@ function AIMeChat({
767
1030
  children: "Message to AI Assistant"
768
1031
  }
769
1032
  ),
770
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1033
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
771
1034
  "input",
772
1035
  {
773
1036
  id: "ai-me-chat-input",
@@ -798,7 +1061,7 @@ function AIMeChat({
798
1061
  }
799
1062
  }
800
1063
  ),
801
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1064
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
802
1065
  "button",
803
1066
  {
804
1067
  type: "submit",
@@ -833,13 +1096,38 @@ function AIMeChat({
833
1096
  )
834
1097
  ]
835
1098
  }
836
- )
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
+ })
837
1125
  ] });
838
1126
  }
839
1127
 
840
1128
  // src/command-palette.tsx
841
- var import_react5 = require("react");
842
- var import_jsx_runtime4 = require("react/jsx-runtime");
1129
+ var import_react6 = require("react");
1130
+ var import_jsx_runtime5 = require("react/jsx-runtime");
843
1131
  var defaultCommands = [
844
1132
  {
845
1133
  id: "help",
@@ -883,17 +1171,17 @@ function AIMeCommandPalette({
883
1171
  shortcut = { key: "k", meta: true },
884
1172
  onToggle
885
1173
  }) {
886
- const [open, setOpen] = (0, import_react5.useState)(false);
887
- const [query, setQuery] = (0, import_react5.useState)("");
888
- const [selectedIndex, setSelectedIndex] = (0, import_react5.useState)(0);
889
- const inputRef = (0, import_react5.useRef)(null);
890
- const listRef = (0, import_react5.useRef)(null);
891
- const dialogRef = (0, import_react5.useRef)(null);
892
- 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);
893
1181
  const { sendMessage } = useAIMe();
894
- const titleId = (0, import_react5.useId)();
895
- const inputId = (0, import_react5.useId)();
896
- 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)(
897
1185
  (next) => {
898
1186
  setOpen(next);
899
1187
  setQuery("");
@@ -902,7 +1190,7 @@ function AIMeCommandPalette({
902
1190
  },
903
1191
  [onToggle]
904
1192
  );
905
- (0, import_react5.useEffect)(() => {
1193
+ (0, import_react6.useEffect)(() => {
906
1194
  function handleKeyDown2(e) {
907
1195
  const metaMatch = shortcut.meta ? e.metaKey : true;
908
1196
  const ctrlMatch = shortcut.ctrl ? e.ctrlKey : !shortcut.meta ? e.ctrlKey : true;
@@ -917,7 +1205,7 @@ function AIMeCommandPalette({
917
1205
  window.addEventListener("keydown", handleKeyDown2);
918
1206
  return () => window.removeEventListener("keydown", handleKeyDown2);
919
1207
  }, [open, shortcut, toggle]);
920
- (0, import_react5.useEffect)(() => {
1208
+ (0, import_react6.useEffect)(() => {
921
1209
  if (open) {
922
1210
  setTimeout(() => inputRef.current?.focus(), 0);
923
1211
  } else {
@@ -927,7 +1215,7 @@ function AIMeCommandPalette({
927
1215
  }
928
1216
  }
929
1217
  }, [open]);
930
- (0, import_react5.useEffect)(() => {
1218
+ (0, import_react6.useEffect)(() => {
931
1219
  if (!open) return;
932
1220
  function handleFocusTrap(e) {
933
1221
  if (e.key !== "Tab") return;
@@ -957,12 +1245,12 @@ function AIMeCommandPalette({
957
1245
  const filtered = query.trim() ? commands.filter(
958
1246
  (cmd) => cmd.label.toLowerCase().includes(query.toLowerCase()) || cmd.description?.toLowerCase().includes(query.toLowerCase()) || cmd.category?.toLowerCase().includes(query.toLowerCase())
959
1247
  ) : commands;
960
- (0, import_react5.useEffect)(() => {
1248
+ (0, import_react6.useEffect)(() => {
961
1249
  if (selectedIndex >= filtered.length) {
962
1250
  setSelectedIndex(Math.max(0, filtered.length - 1));
963
1251
  }
964
1252
  }, [filtered.length, selectedIndex]);
965
- (0, import_react5.useEffect)(() => {
1253
+ (0, import_react6.useEffect)(() => {
966
1254
  const list = listRef.current;
967
1255
  if (!list) return;
968
1256
  const selected = list.children[selectedIndex];
@@ -1007,8 +1295,8 @@ function AIMeCommandPalette({
1007
1295
  grouped.get(cat).push(cmd);
1008
1296
  }
1009
1297
  let flatIndex = 0;
1010
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1011
- /* @__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)(
1012
1300
  "div",
1013
1301
  {
1014
1302
  onClick: () => toggle(false),
@@ -1021,7 +1309,7 @@ function AIMeCommandPalette({
1021
1309
  "aria-hidden": "true"
1022
1310
  }
1023
1311
  ),
1024
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1312
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1025
1313
  "div",
1026
1314
  {
1027
1315
  ref: dialogRef,
@@ -1049,10 +1337,10 @@ function AIMeCommandPalette({
1049
1337
  "aria-labelledby": titleId,
1050
1338
  tabIndex: -1,
1051
1339
  children: [
1052
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { id: titleId, style: srOnly2, children: "Command Palette" }),
1053
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: "12px 16px", borderBottom: "1px solid var(--ai-me-border)" }, children: [
1054
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { htmlFor: inputId, style: srOnly2, children: "Search commands or ask AI" }),
1055
- /* @__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)(
1056
1344
  "input",
1057
1345
  {
1058
1346
  id: inputId,
@@ -1090,7 +1378,7 @@ function AIMeCommandPalette({
1090
1378
  }
1091
1379
  )
1092
1380
  ] }),
1093
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1381
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1094
1382
  "div",
1095
1383
  {
1096
1384
  id: "ai-me-cmd-listbox",
@@ -1099,7 +1387,7 @@ function AIMeCommandPalette({
1099
1387
  role: "listbox",
1100
1388
  "aria-label": "Commands",
1101
1389
  children: [
1102
- filtered.length === 0 && query.trim() && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1390
+ filtered.length === 0 && query.trim() && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1103
1391
  "div",
1104
1392
  {
1105
1393
  role: "option",
@@ -1119,8 +1407,8 @@ function AIMeCommandPalette({
1119
1407
  ),
1120
1408
  Array.from(grouped.entries()).map(([category, items]) => (
1121
1409
  // role="group" with aria-label for the category heading
1122
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { role: "group", "aria-label": category, children: [
1123
- /* @__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)(
1124
1412
  "div",
1125
1413
  {
1126
1414
  "aria-hidden": "true",
@@ -1138,7 +1426,7 @@ function AIMeCommandPalette({
1138
1426
  items.map((cmd) => {
1139
1427
  const idx = flatIndex++;
1140
1428
  const isSelected = idx === selectedIndex;
1141
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1429
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1142
1430
  "div",
1143
1431
  {
1144
1432
  id: `cmd-${cmd.id}`,
@@ -1171,10 +1459,10 @@ function AIMeCommandPalette({
1171
1459
  },
1172
1460
  children: [
1173
1461
  cmd.icon && // Icon is decorative — label comes from cmd.label
1174
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", style: { fontSize: 16, flexShrink: 0 }, children: cmd.icon }),
1175
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
1176
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 14, fontWeight: 500 }, children: cmd.label }),
1177
- 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)(
1178
1466
  "div",
1179
1467
  {
1180
1468
  style: {
@@ -1198,7 +1486,7 @@ function AIMeCommandPalette({
1198
1486
  ]
1199
1487
  }
1200
1488
  ),
1201
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1489
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1202
1490
  "div",
1203
1491
  {
1204
1492
  "aria-hidden": "true",
@@ -1211,9 +1499,9 @@ function AIMeCommandPalette({
1211
1499
  gap: 16
1212
1500
  },
1213
1501
  children: [
1214
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u2191\u2193 Navigate" }),
1215
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u21B5 Select" }),
1216
- /* @__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" })
1217
1505
  ]
1218
1506
  }
1219
1507
  )
@@ -1222,201 +1510,13 @@ function AIMeCommandPalette({
1222
1510
  )
1223
1511
  ] });
1224
1512
  }
1225
-
1226
- // src/confirm.tsx
1227
- var import_react6 = require("react");
1228
- var import_jsx_runtime5 = require("react/jsx-runtime");
1229
- function AIMeConfirm({
1230
- action,
1231
- description,
1232
- parameters,
1233
- onConfirm,
1234
- onReject
1235
- }) {
1236
- const dialogRef = (0, import_react6.useRef)(null);
1237
- const cancelButtonRef = (0, import_react6.useRef)(null);
1238
- const titleId = (0, import_react6.useId)();
1239
- const descriptionId = (0, import_react6.useId)();
1240
- const handleKeyDown = (0, import_react6.useCallback)(
1241
- (e) => {
1242
- if (e.key === "Escape") {
1243
- e.preventDefault();
1244
- onReject();
1245
- return;
1246
- }
1247
- if (e.key !== "Tab") return;
1248
- const dialog = dialogRef.current;
1249
- if (!dialog) return;
1250
- const focusable = dialog.querySelectorAll(
1251
- 'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
1252
- );
1253
- if (focusable.length === 0) return;
1254
- const first = focusable[0];
1255
- const last = focusable[focusable.length - 1];
1256
- if (e.shiftKey) {
1257
- if (document.activeElement === first) {
1258
- e.preventDefault();
1259
- last.focus();
1260
- }
1261
- } else {
1262
- if (document.activeElement === last) {
1263
- e.preventDefault();
1264
- first.focus();
1265
- }
1266
- }
1267
- },
1268
- [onReject]
1269
- );
1270
- (0, import_react6.useEffect)(() => {
1271
- const previousFocus = document.activeElement;
1272
- cancelButtonRef.current?.focus();
1273
- window.addEventListener("keydown", handleKeyDown);
1274
- return () => {
1275
- window.removeEventListener("keydown", handleKeyDown);
1276
- previousFocus?.focus();
1277
- };
1278
- }, [handleKeyDown]);
1279
- const overlayStyle = {
1280
- ...defaultThemeVars,
1281
- position: "fixed",
1282
- inset: 0,
1283
- backgroundColor: "rgba(0, 0, 0, 0.4)",
1284
- display: "flex",
1285
- alignItems: "center",
1286
- justifyContent: "center",
1287
- zIndex: 1e4,
1288
- fontFamily: "var(--ai-me-font)"
1289
- };
1290
- const dialogStyle = {
1291
- backgroundColor: "var(--ai-me-bg)",
1292
- borderRadius: "var(--ai-me-radius)",
1293
- padding: 24,
1294
- maxWidth: 420,
1295
- width: "90%",
1296
- boxShadow: "var(--ai-me-shadow)",
1297
- color: "var(--ai-me-text)"
1298
- };
1299
- const focusStyle = {
1300
- outline: "2px solid transparent",
1301
- outlineOffset: 2
1302
- };
1303
- function applyFocusRing(el) {
1304
- el.style.outline = "2px solid var(--ai-me-primary)";
1305
- el.style.outlineOffset = "2px";
1306
- }
1307
- function removeFocusRing(el) {
1308
- el.style.outline = "2px solid transparent";
1309
- el.style.outlineOffset = "2px";
1310
- }
1311
- return (
1312
- // Overlay is presentational — role and aria go on the inner dialog
1313
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1314
- "div",
1315
- {
1316
- style: overlayStyle,
1317
- onClick: (e) => {
1318
- if (e.target === e.currentTarget) onReject();
1319
- },
1320
- "aria-hidden": "false",
1321
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1322
- "div",
1323
- {
1324
- ref: dialogRef,
1325
- style: dialogStyle,
1326
- role: "alertdialog",
1327
- "aria-modal": "true",
1328
- "aria-labelledby": titleId,
1329
- "aria-describedby": descriptionId,
1330
- tabIndex: -1,
1331
- onClick: (e) => e.stopPropagation(),
1332
- children: [
1333
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { id: titleId, style: { margin: "0 0 8px", fontSize: 16 }, children: "Confirm Action" }),
1334
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: { margin: "0 0 4px", fontSize: 14, fontWeight: 600 }, children: action }),
1335
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1336
- "p",
1337
- {
1338
- id: descriptionId,
1339
- style: {
1340
- margin: "0 0 16px",
1341
- fontSize: 13,
1342
- color: "var(--ai-me-text-secondary)"
1343
- },
1344
- children: description
1345
- }
1346
- ),
1347
- parameters && Object.keys(parameters).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1348
- "pre",
1349
- {
1350
- style: {
1351
- margin: "0 0 16px",
1352
- padding: 12,
1353
- backgroundColor: "var(--ai-me-bg-secondary)",
1354
- borderRadius: 8,
1355
- fontSize: 12,
1356
- overflow: "auto",
1357
- maxHeight: 200,
1358
- border: "1px solid var(--ai-me-border)"
1359
- },
1360
- children: JSON.stringify(parameters, null, 2)
1361
- }
1362
- ),
1363
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
1364
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1365
- "button",
1366
- {
1367
- ref: cancelButtonRef,
1368
- type: "button",
1369
- onClick: onReject,
1370
- style: {
1371
- padding: "8px 16px",
1372
- border: "1px solid var(--ai-me-border)",
1373
- borderRadius: 8,
1374
- backgroundColor: "var(--ai-me-bg)",
1375
- color: "var(--ai-me-text)",
1376
- cursor: "pointer",
1377
- fontSize: 14,
1378
- ...focusStyle
1379
- },
1380
- onFocus: (e) => applyFocusRing(e.currentTarget),
1381
- onBlur: (e) => removeFocusRing(e.currentTarget),
1382
- children: "Cancel"
1383
- }
1384
- ),
1385
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1386
- "button",
1387
- {
1388
- type: "button",
1389
- onClick: onConfirm,
1390
- style: {
1391
- padding: "8px 16px",
1392
- border: "none",
1393
- borderRadius: 8,
1394
- // #fff on var(--ai-me-primary) = #6366f1 → contrast ≈ 4.6:1 (passes AA)
1395
- backgroundColor: "var(--ai-me-primary)",
1396
- color: "#fff",
1397
- cursor: "pointer",
1398
- fontSize: 14,
1399
- ...focusStyle
1400
- },
1401
- onFocus: (e) => applyFocusRing(e.currentTarget),
1402
- onBlur: (e) => removeFocusRing(e.currentTarget),
1403
- children: "Confirm"
1404
- }
1405
- )
1406
- ] })
1407
- ]
1408
- }
1409
- )
1410
- }
1411
- )
1412
- );
1413
- }
1414
1513
  // Annotate the CommonJS export names for ESM import in node:
1415
1514
  0 && (module.exports = {
1416
1515
  AIMeChat,
1417
1516
  AIMeCommandPalette,
1418
1517
  AIMeConfirm,
1419
1518
  AIMeProvider,
1519
+ cleanAssistantText,
1420
1520
  renderMarkdown,
1421
1521
  useAIMe,
1422
1522
  useAIMeContext