@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 +399 -299
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -4
- package/dist/index.d.ts +23 -4
- package/dist/index.js +395 -296
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -13,20 +13,35 @@ function useAIMeContext() {
|
|
|
13
13
|
|
|
14
14
|
// src/provider.tsx
|
|
15
15
|
import { jsx } from "react/jsx-runtime";
|
|
16
|
-
function AIMeProvider({ endpoint, headers, onAction, children }) {
|
|
17
|
-
return /* @__PURE__ */ jsx(AIMeContext, { value: { endpoint, headers, onAction }, children });
|
|
16
|
+
function AIMeProvider({ endpoint, headers, onAction, stuckTimeout, children }) {
|
|
17
|
+
return /* @__PURE__ */ jsx(AIMeContext, { value: { endpoint, headers, onAction, stuckTimeout }, children });
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// src/chat.tsx
|
|
21
|
-
import { useState as useState2, useRef as
|
|
21
|
+
import { useState as useState2, useRef as useRef3, useEffect as useEffect3, useCallback as useCallback3, useId as useId2, useMemo as useMemo2, Fragment } from "react";
|
|
22
22
|
|
|
23
23
|
// src/use-ai-me.ts
|
|
24
24
|
import { useChat } from "@ai-sdk/react";
|
|
25
25
|
import { DefaultChatTransport } from "ai";
|
|
26
|
-
import { useState, useCallback, useEffect, useRef } from "react";
|
|
26
|
+
import { useState, useCallback, useEffect, useRef, useMemo } from "react";
|
|
27
27
|
var STORAGE_KEY = "ai-me-messages";
|
|
28
|
+
function cleanAssistantText(text) {
|
|
29
|
+
return text.replace(/<tools>[\s\S]*?<\/tools>/g, "").trim();
|
|
30
|
+
}
|
|
31
|
+
function trimIncompleteToolCalls(messages) {
|
|
32
|
+
if (messages.length === 0) return messages;
|
|
33
|
+
const last = messages[messages.length - 1];
|
|
34
|
+
if (last.role !== "assistant") return messages;
|
|
35
|
+
const hasToolCall = last.parts.some((p) => p.type === "tool-call");
|
|
36
|
+
const hasToolResult = last.parts.some((p) => p.type === "tool-result");
|
|
37
|
+
if (hasToolCall && !hasToolResult) {
|
|
38
|
+
return messages.slice(0, -1);
|
|
39
|
+
}
|
|
40
|
+
return messages;
|
|
41
|
+
}
|
|
28
42
|
function useAIMe() {
|
|
29
|
-
const { endpoint, headers } = useAIMeContext();
|
|
43
|
+
const { endpoint, headers, stuckTimeout: configuredTimeout } = useAIMeContext();
|
|
44
|
+
const stuckTimeout = configuredTimeout ?? 3e4;
|
|
30
45
|
const [input, setInput] = useState("");
|
|
31
46
|
const initialized = useRef(false);
|
|
32
47
|
const chat = useChat({
|
|
@@ -35,6 +50,24 @@ function useAIMe() {
|
|
|
35
50
|
headers
|
|
36
51
|
})
|
|
37
52
|
});
|
|
53
|
+
const messages = useMemo(() => {
|
|
54
|
+
return chat.messages.map((m) => {
|
|
55
|
+
if (m.role !== "assistant") return m;
|
|
56
|
+
let changed = false;
|
|
57
|
+
const cleanedParts = m.parts.map((p) => {
|
|
58
|
+
if (p.type !== "text") return p;
|
|
59
|
+
const cleaned = cleanAssistantText(p.text);
|
|
60
|
+
if (cleaned === p.text) return p;
|
|
61
|
+
changed = true;
|
|
62
|
+
return { ...p, text: cleaned };
|
|
63
|
+
});
|
|
64
|
+
if (!changed) return m;
|
|
65
|
+
const nonEmptyParts = cleanedParts.filter(
|
|
66
|
+
(p) => p.type !== "text" || p.text.length > 0
|
|
67
|
+
);
|
|
68
|
+
return { ...m, parts: nonEmptyParts };
|
|
69
|
+
});
|
|
70
|
+
}, [chat.messages]);
|
|
38
71
|
useEffect(() => {
|
|
39
72
|
if (initialized.current) return;
|
|
40
73
|
initialized.current = true;
|
|
@@ -43,7 +76,10 @@ function useAIMe() {
|
|
|
43
76
|
if (stored) {
|
|
44
77
|
const parsed = JSON.parse(stored);
|
|
45
78
|
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
46
|
-
|
|
79
|
+
const cleaned = trimIncompleteToolCalls(parsed);
|
|
80
|
+
if (cleaned.length > 0) {
|
|
81
|
+
chat.setMessages(cleaned);
|
|
82
|
+
}
|
|
47
83
|
}
|
|
48
84
|
}
|
|
49
85
|
} catch {
|
|
@@ -60,6 +96,14 @@ function useAIMe() {
|
|
|
60
96
|
} catch {
|
|
61
97
|
}
|
|
62
98
|
}, [chat.messages]);
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (stuckTimeout <= 0) return;
|
|
101
|
+
if (chat.status !== "submitted") return;
|
|
102
|
+
const timer = setTimeout(() => {
|
|
103
|
+
chat.stop();
|
|
104
|
+
}, stuckTimeout);
|
|
105
|
+
return () => clearTimeout(timer);
|
|
106
|
+
}, [chat.status, stuckTimeout, chat.stop]);
|
|
63
107
|
const handleInputChange = useCallback(
|
|
64
108
|
(e) => {
|
|
65
109
|
setInput(e.target.value);
|
|
@@ -84,7 +128,7 @@ function useAIMe() {
|
|
|
84
128
|
}, [chat]);
|
|
85
129
|
return {
|
|
86
130
|
/** Conversation messages */
|
|
87
|
-
messages
|
|
131
|
+
messages,
|
|
88
132
|
/** Current input value */
|
|
89
133
|
input,
|
|
90
134
|
/** Set input value */
|
|
@@ -104,7 +148,9 @@ function useAIMe() {
|
|
|
104
148
|
/** Set messages */
|
|
105
149
|
setMessages: chat.setMessages,
|
|
106
150
|
/** Clear all messages and session storage */
|
|
107
|
-
clearMessages
|
|
151
|
+
clearMessages,
|
|
152
|
+
/** Approve or reject a pending tool call (for confirmation flow) */
|
|
153
|
+
addToolApprovalResponse: chat.addToolApprovalResponse
|
|
108
154
|
};
|
|
109
155
|
}
|
|
110
156
|
|
|
@@ -306,8 +352,197 @@ var linkStyle = {
|
|
|
306
352
|
textUnderlineOffset: "2px"
|
|
307
353
|
};
|
|
308
354
|
|
|
355
|
+
// src/confirm.tsx
|
|
356
|
+
import { useRef as useRef2, useEffect as useEffect2, useId, useCallback as useCallback2 } from "react";
|
|
357
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
358
|
+
function AIMeConfirm({
|
|
359
|
+
action,
|
|
360
|
+
description,
|
|
361
|
+
parameters,
|
|
362
|
+
onConfirm,
|
|
363
|
+
onReject
|
|
364
|
+
}) {
|
|
365
|
+
const dialogRef = useRef2(null);
|
|
366
|
+
const cancelButtonRef = useRef2(null);
|
|
367
|
+
const titleId = useId();
|
|
368
|
+
const descriptionId = useId();
|
|
369
|
+
const handleKeyDown = useCallback2(
|
|
370
|
+
(e) => {
|
|
371
|
+
if (e.key === "Escape") {
|
|
372
|
+
e.preventDefault();
|
|
373
|
+
onReject();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (e.key !== "Tab") return;
|
|
377
|
+
const dialog = dialogRef.current;
|
|
378
|
+
if (!dialog) return;
|
|
379
|
+
const focusable = dialog.querySelectorAll(
|
|
380
|
+
'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
381
|
+
);
|
|
382
|
+
if (focusable.length === 0) return;
|
|
383
|
+
const first = focusable[0];
|
|
384
|
+
const last = focusable[focusable.length - 1];
|
|
385
|
+
if (e.shiftKey) {
|
|
386
|
+
if (document.activeElement === first) {
|
|
387
|
+
e.preventDefault();
|
|
388
|
+
last.focus();
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
if (document.activeElement === last) {
|
|
392
|
+
e.preventDefault();
|
|
393
|
+
first.focus();
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
[onReject]
|
|
398
|
+
);
|
|
399
|
+
useEffect2(() => {
|
|
400
|
+
const previousFocus = document.activeElement;
|
|
401
|
+
cancelButtonRef.current?.focus();
|
|
402
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
403
|
+
return () => {
|
|
404
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
405
|
+
previousFocus?.focus();
|
|
406
|
+
};
|
|
407
|
+
}, [handleKeyDown]);
|
|
408
|
+
const overlayStyle = {
|
|
409
|
+
...defaultThemeVars,
|
|
410
|
+
position: "fixed",
|
|
411
|
+
inset: 0,
|
|
412
|
+
backgroundColor: "rgba(0, 0, 0, 0.4)",
|
|
413
|
+
display: "flex",
|
|
414
|
+
alignItems: "center",
|
|
415
|
+
justifyContent: "center",
|
|
416
|
+
zIndex: 1e4,
|
|
417
|
+
fontFamily: "var(--ai-me-font)"
|
|
418
|
+
};
|
|
419
|
+
const dialogStyle = {
|
|
420
|
+
backgroundColor: "var(--ai-me-bg)",
|
|
421
|
+
borderRadius: "var(--ai-me-radius)",
|
|
422
|
+
padding: 24,
|
|
423
|
+
maxWidth: 420,
|
|
424
|
+
width: "90%",
|
|
425
|
+
boxShadow: "var(--ai-me-shadow)",
|
|
426
|
+
color: "var(--ai-me-text)"
|
|
427
|
+
};
|
|
428
|
+
const focusStyle = {
|
|
429
|
+
outline: "2px solid transparent",
|
|
430
|
+
outlineOffset: 2
|
|
431
|
+
};
|
|
432
|
+
function applyFocusRing(el) {
|
|
433
|
+
el.style.outline = "2px solid var(--ai-me-primary)";
|
|
434
|
+
el.style.outlineOffset = "2px";
|
|
435
|
+
}
|
|
436
|
+
function removeFocusRing(el) {
|
|
437
|
+
el.style.outline = "2px solid transparent";
|
|
438
|
+
el.style.outlineOffset = "2px";
|
|
439
|
+
}
|
|
440
|
+
return (
|
|
441
|
+
// Overlay is presentational — role and aria go on the inner dialog
|
|
442
|
+
/* @__PURE__ */ jsx3(
|
|
443
|
+
"div",
|
|
444
|
+
{
|
|
445
|
+
style: overlayStyle,
|
|
446
|
+
onClick: (e) => {
|
|
447
|
+
if (e.target === e.currentTarget) onReject();
|
|
448
|
+
},
|
|
449
|
+
"aria-hidden": "false",
|
|
450
|
+
children: /* @__PURE__ */ jsxs(
|
|
451
|
+
"div",
|
|
452
|
+
{
|
|
453
|
+
ref: dialogRef,
|
|
454
|
+
style: dialogStyle,
|
|
455
|
+
role: "alertdialog",
|
|
456
|
+
"aria-modal": "true",
|
|
457
|
+
"aria-labelledby": titleId,
|
|
458
|
+
"aria-describedby": descriptionId,
|
|
459
|
+
tabIndex: -1,
|
|
460
|
+
onClick: (e) => e.stopPropagation(),
|
|
461
|
+
children: [
|
|
462
|
+
/* @__PURE__ */ jsx3("h3", { id: titleId, style: { margin: "0 0 8px", fontSize: 16 }, children: "Confirm Action" }),
|
|
463
|
+
/* @__PURE__ */ jsx3("p", { style: { margin: "0 0 4px", fontSize: 14, fontWeight: 600 }, children: action }),
|
|
464
|
+
/* @__PURE__ */ jsx3(
|
|
465
|
+
"p",
|
|
466
|
+
{
|
|
467
|
+
id: descriptionId,
|
|
468
|
+
style: {
|
|
469
|
+
margin: "0 0 16px",
|
|
470
|
+
fontSize: 13,
|
|
471
|
+
color: "var(--ai-me-text-secondary)"
|
|
472
|
+
},
|
|
473
|
+
children: description
|
|
474
|
+
}
|
|
475
|
+
),
|
|
476
|
+
parameters && Object.keys(parameters).length > 0 && /* @__PURE__ */ jsx3(
|
|
477
|
+
"pre",
|
|
478
|
+
{
|
|
479
|
+
style: {
|
|
480
|
+
margin: "0 0 16px",
|
|
481
|
+
padding: 12,
|
|
482
|
+
backgroundColor: "var(--ai-me-bg-secondary)",
|
|
483
|
+
borderRadius: 8,
|
|
484
|
+
fontSize: 12,
|
|
485
|
+
overflow: "auto",
|
|
486
|
+
maxHeight: 200,
|
|
487
|
+
border: "1px solid var(--ai-me-border)"
|
|
488
|
+
},
|
|
489
|
+
children: JSON.stringify(parameters, null, 2)
|
|
490
|
+
}
|
|
491
|
+
),
|
|
492
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
|
|
493
|
+
/* @__PURE__ */ jsx3(
|
|
494
|
+
"button",
|
|
495
|
+
{
|
|
496
|
+
ref: cancelButtonRef,
|
|
497
|
+
type: "button",
|
|
498
|
+
onClick: onReject,
|
|
499
|
+
style: {
|
|
500
|
+
padding: "8px 16px",
|
|
501
|
+
border: "1px solid var(--ai-me-border)",
|
|
502
|
+
borderRadius: 8,
|
|
503
|
+
backgroundColor: "var(--ai-me-bg)",
|
|
504
|
+
color: "var(--ai-me-text)",
|
|
505
|
+
cursor: "pointer",
|
|
506
|
+
fontSize: 14,
|
|
507
|
+
...focusStyle
|
|
508
|
+
},
|
|
509
|
+
onFocus: (e) => applyFocusRing(e.currentTarget),
|
|
510
|
+
onBlur: (e) => removeFocusRing(e.currentTarget),
|
|
511
|
+
children: "Cancel"
|
|
512
|
+
}
|
|
513
|
+
),
|
|
514
|
+
/* @__PURE__ */ jsx3(
|
|
515
|
+
"button",
|
|
516
|
+
{
|
|
517
|
+
type: "button",
|
|
518
|
+
onClick: onConfirm,
|
|
519
|
+
style: {
|
|
520
|
+
padding: "8px 16px",
|
|
521
|
+
border: "none",
|
|
522
|
+
borderRadius: 8,
|
|
523
|
+
// #fff on var(--ai-me-primary) = #6366f1 → contrast ≈ 4.6:1 (passes AA)
|
|
524
|
+
backgroundColor: "var(--ai-me-primary)",
|
|
525
|
+
color: "#fff",
|
|
526
|
+
cursor: "pointer",
|
|
527
|
+
fontSize: 14,
|
|
528
|
+
...focusStyle
|
|
529
|
+
},
|
|
530
|
+
onFocus: (e) => applyFocusRing(e.currentTarget),
|
|
531
|
+
onBlur: (e) => removeFocusRing(e.currentTarget),
|
|
532
|
+
children: "Confirm"
|
|
533
|
+
}
|
|
534
|
+
)
|
|
535
|
+
] })
|
|
536
|
+
]
|
|
537
|
+
}
|
|
538
|
+
)
|
|
539
|
+
}
|
|
540
|
+
)
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
309
544
|
// src/chat.tsx
|
|
310
|
-
import { Fragment, jsx as
|
|
545
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
311
546
|
var srOnly = {
|
|
312
547
|
position: "absolute",
|
|
313
548
|
width: 1,
|
|
@@ -319,6 +554,12 @@ var srOnly = {
|
|
|
319
554
|
whiteSpace: "nowrap",
|
|
320
555
|
borderWidth: 0
|
|
321
556
|
};
|
|
557
|
+
function DefaultAIIcon() {
|
|
558
|
+
return /* @__PURE__ */ jsxs2("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
559
|
+
/* @__PURE__ */ jsx4("path", { d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5Z" }),
|
|
560
|
+
/* @__PURE__ */ jsx4("path", { d: "M19 11l.75 2.25L22 14l-2.25.75L19 17l-.75-2.25L16 14l2.25-.75Z" })
|
|
561
|
+
] });
|
|
562
|
+
}
|
|
322
563
|
function AIMeChat({
|
|
323
564
|
position = "bottom-right",
|
|
324
565
|
theme,
|
|
@@ -327,15 +568,17 @@ function AIMeChat({
|
|
|
327
568
|
defaultOpen = false,
|
|
328
569
|
onToggle,
|
|
329
570
|
onToolComplete,
|
|
330
|
-
onMessageComplete
|
|
571
|
+
onMessageComplete,
|
|
572
|
+
triggerIcon,
|
|
573
|
+
renderConfirmation
|
|
331
574
|
}) {
|
|
332
575
|
const [open, setOpen] = useState2(defaultOpen);
|
|
333
|
-
const messagesEndRef =
|
|
334
|
-
const inputRef =
|
|
335
|
-
const panelRef =
|
|
336
|
-
const triggerRef =
|
|
337
|
-
const firedToolResults =
|
|
338
|
-
const prevStatus =
|
|
576
|
+
const messagesEndRef = useRef3(null);
|
|
577
|
+
const inputRef = useRef3(null);
|
|
578
|
+
const panelRef = useRef3(null);
|
|
579
|
+
const triggerRef = useRef3(null);
|
|
580
|
+
const firedToolResults = useRef3(/* @__PURE__ */ new Set());
|
|
581
|
+
const prevStatus = useRef3(null);
|
|
339
582
|
const {
|
|
340
583
|
messages,
|
|
341
584
|
input,
|
|
@@ -343,17 +586,18 @@ function AIMeChat({
|
|
|
343
586
|
handleSubmit,
|
|
344
587
|
status,
|
|
345
588
|
error,
|
|
346
|
-
setInput
|
|
589
|
+
setInput,
|
|
590
|
+
addToolApprovalResponse
|
|
347
591
|
} = useAIMe();
|
|
348
|
-
const titleId =
|
|
349
|
-
const messagesId =
|
|
592
|
+
const titleId = useId2();
|
|
593
|
+
const messagesId = useId2();
|
|
350
594
|
const isInline = position === "inline";
|
|
351
|
-
const toggleOpen =
|
|
595
|
+
const toggleOpen = useCallback3(() => {
|
|
352
596
|
const next = !open;
|
|
353
597
|
setOpen(next);
|
|
354
598
|
onToggle?.(next);
|
|
355
599
|
}, [open, onToggle]);
|
|
356
|
-
|
|
600
|
+
useEffect3(() => {
|
|
357
601
|
function handleKeyDown(e) {
|
|
358
602
|
if ((e.metaKey || e.ctrlKey) && e.key === ".") {
|
|
359
603
|
e.preventDefault();
|
|
@@ -363,10 +607,10 @@ function AIMeChat({
|
|
|
363
607
|
window.addEventListener("keydown", handleKeyDown);
|
|
364
608
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
365
609
|
}, [toggleOpen]);
|
|
366
|
-
|
|
610
|
+
useEffect3(() => {
|
|
367
611
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
368
612
|
}, [messages]);
|
|
369
|
-
|
|
613
|
+
useEffect3(() => {
|
|
370
614
|
if (!onToolComplete) return;
|
|
371
615
|
for (const message of messages) {
|
|
372
616
|
for (const part of message.parts) {
|
|
@@ -382,7 +626,7 @@ function AIMeChat({
|
|
|
382
626
|
}
|
|
383
627
|
}
|
|
384
628
|
}, [messages, onToolComplete]);
|
|
385
|
-
|
|
629
|
+
useEffect3(() => {
|
|
386
630
|
const prev = prevStatus.current;
|
|
387
631
|
prevStatus.current = status;
|
|
388
632
|
if (!onMessageComplete) return;
|
|
@@ -404,7 +648,7 @@ function AIMeChat({
|
|
|
404
648
|
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
405
649
|
});
|
|
406
650
|
}, [status, messages, onMessageComplete]);
|
|
407
|
-
|
|
651
|
+
useEffect3(() => {
|
|
408
652
|
if (open) {
|
|
409
653
|
panelRef.current?.focus();
|
|
410
654
|
setTimeout(() => inputRef.current?.focus(), 0);
|
|
@@ -412,7 +656,7 @@ function AIMeChat({
|
|
|
412
656
|
triggerRef.current?.focus();
|
|
413
657
|
}
|
|
414
658
|
}, [open]);
|
|
415
|
-
|
|
659
|
+
useEffect3(() => {
|
|
416
660
|
if (!open || isInline) return;
|
|
417
661
|
function handleKeyDown(e) {
|
|
418
662
|
if (e.key === "Escape") {
|
|
@@ -444,6 +688,20 @@ function AIMeChat({
|
|
|
444
688
|
window.addEventListener("keydown", handleKeyDown);
|
|
445
689
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
446
690
|
}, [open, isInline, toggleOpen]);
|
|
691
|
+
const pendingToolCalls = useMemo2(() => {
|
|
692
|
+
const resultIds = /* @__PURE__ */ new Set();
|
|
693
|
+
const toolCalls = [];
|
|
694
|
+
for (const m of messages) {
|
|
695
|
+
for (const p of m.parts) {
|
|
696
|
+
if (p.type === "tool-result") {
|
|
697
|
+
resultIds.add(p.toolCallId);
|
|
698
|
+
} else if (p.type === "tool-call") {
|
|
699
|
+
toolCalls.push(p);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return toolCalls.filter((tc) => !resultIds.has(tc.toolCallId));
|
|
704
|
+
}, [messages]);
|
|
447
705
|
const themeVars = {
|
|
448
706
|
...defaultThemeVars,
|
|
449
707
|
...themeToVars(theme)
|
|
@@ -499,8 +757,8 @@ function AIMeChat({
|
|
|
499
757
|
zIndex: 9999
|
|
500
758
|
};
|
|
501
759
|
const isStreaming = status === "submitted" || status === "streaming";
|
|
502
|
-
return /* @__PURE__ */
|
|
503
|
-
/* @__PURE__ */
|
|
760
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
761
|
+
/* @__PURE__ */ jsx4(
|
|
504
762
|
"button",
|
|
505
763
|
{
|
|
506
764
|
ref: triggerRef,
|
|
@@ -510,10 +768,10 @@ function AIMeChat({
|
|
|
510
768
|
"aria-expanded": open,
|
|
511
769
|
"aria-controls": isInline ? void 0 : "ai-me-chat-panel",
|
|
512
770
|
type: "button",
|
|
513
|
-
children: /* @__PURE__ */
|
|
771
|
+
children: /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: triggerIcon ?? /* @__PURE__ */ jsx4(DefaultAIIcon, {}) })
|
|
514
772
|
}
|
|
515
773
|
),
|
|
516
|
-
/* @__PURE__ */
|
|
774
|
+
/* @__PURE__ */ jsxs2(
|
|
517
775
|
"div",
|
|
518
776
|
{
|
|
519
777
|
id: "ai-me-chat-panel",
|
|
@@ -525,7 +783,7 @@ function AIMeChat({
|
|
|
525
783
|
"aria-busy": isStreaming,
|
|
526
784
|
tabIndex: -1,
|
|
527
785
|
children: [
|
|
528
|
-
/* @__PURE__ */
|
|
786
|
+
/* @__PURE__ */ jsxs2(
|
|
529
787
|
"div",
|
|
530
788
|
{
|
|
531
789
|
style: {
|
|
@@ -537,8 +795,8 @@ function AIMeChat({
|
|
|
537
795
|
backgroundColor: "var(--ai-me-bg-secondary)"
|
|
538
796
|
},
|
|
539
797
|
children: [
|
|
540
|
-
/* @__PURE__ */
|
|
541
|
-
!isInline && /* @__PURE__ */
|
|
798
|
+
/* @__PURE__ */ jsx4("span", { id: titleId, style: { fontWeight: 600, fontSize: 14 }, children: "AI Assistant" }),
|
|
799
|
+
!isInline && /* @__PURE__ */ jsx4(
|
|
542
800
|
"button",
|
|
543
801
|
{
|
|
544
802
|
onClick: toggleOpen,
|
|
@@ -562,13 +820,13 @@ function AIMeChat({
|
|
|
562
820
|
},
|
|
563
821
|
"aria-label": "Close chat",
|
|
564
822
|
type: "button",
|
|
565
|
-
children: /* @__PURE__ */
|
|
823
|
+
children: /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: "\u2715" })
|
|
566
824
|
}
|
|
567
825
|
)
|
|
568
826
|
]
|
|
569
827
|
}
|
|
570
828
|
),
|
|
571
|
-
/* @__PURE__ */
|
|
829
|
+
/* @__PURE__ */ jsx4(
|
|
572
830
|
"a",
|
|
573
831
|
{
|
|
574
832
|
href: "#ai-me-chat-input",
|
|
@@ -598,7 +856,7 @@ function AIMeChat({
|
|
|
598
856
|
children: "Skip to message input"
|
|
599
857
|
}
|
|
600
858
|
),
|
|
601
|
-
/* @__PURE__ */
|
|
859
|
+
/* @__PURE__ */ jsxs2(
|
|
602
860
|
"div",
|
|
603
861
|
{
|
|
604
862
|
id: messagesId,
|
|
@@ -614,9 +872,9 @@ function AIMeChat({
|
|
|
614
872
|
gap: 12
|
|
615
873
|
},
|
|
616
874
|
children: [
|
|
617
|
-
messages.length === 0 && /* @__PURE__ */
|
|
618
|
-
/* @__PURE__ */
|
|
619
|
-
suggestedPrompts && suggestedPrompts.length > 0 && /* @__PURE__ */
|
|
875
|
+
messages.length === 0 && /* @__PURE__ */ jsxs2("div", { style: { color: "var(--ai-me-text-secondary)", fontSize: 14 }, children: [
|
|
876
|
+
/* @__PURE__ */ jsx4("p", { children: welcomeMessage }),
|
|
877
|
+
suggestedPrompts && suggestedPrompts.length > 0 && /* @__PURE__ */ jsxs2(
|
|
620
878
|
"div",
|
|
621
879
|
{
|
|
622
880
|
style: {
|
|
@@ -626,8 +884,8 @@ function AIMeChat({
|
|
|
626
884
|
gap: 8
|
|
627
885
|
},
|
|
628
886
|
children: [
|
|
629
|
-
/* @__PURE__ */
|
|
630
|
-
suggestedPrompts.map((prompt) => /* @__PURE__ */
|
|
887
|
+
/* @__PURE__ */ jsx4("p", { style: { margin: "0 0 4px", fontSize: 12, fontWeight: 500 }, children: "Suggested questions:" }),
|
|
888
|
+
suggestedPrompts.map((prompt) => /* @__PURE__ */ jsx4(
|
|
631
889
|
"button",
|
|
632
890
|
{
|
|
633
891
|
type: "button",
|
|
@@ -661,31 +919,35 @@ function AIMeChat({
|
|
|
661
919
|
}
|
|
662
920
|
)
|
|
663
921
|
] }),
|
|
664
|
-
messages.map((m) =>
|
|
665
|
-
"
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
922
|
+
messages.map((m) => {
|
|
923
|
+
const hasTextContent = m.parts.some((p) => p.type === "text");
|
|
924
|
+
if (!hasTextContent && m.role === "assistant") return null;
|
|
925
|
+
return /* @__PURE__ */ jsxs2(
|
|
926
|
+
"div",
|
|
927
|
+
{
|
|
928
|
+
style: {
|
|
929
|
+
alignSelf: m.role === "user" ? "flex-end" : "flex-start",
|
|
930
|
+
maxWidth: "85%",
|
|
931
|
+
padding: "8px 12px",
|
|
932
|
+
borderRadius: 8,
|
|
933
|
+
backgroundColor: m.role === "user" ? "var(--ai-me-primary)" : "var(--ai-me-bg-secondary)",
|
|
934
|
+
color: m.role === "user" ? "#fff" : "var(--ai-me-text)",
|
|
935
|
+
fontSize: 14,
|
|
936
|
+
lineHeight: 1.5,
|
|
937
|
+
whiteSpace: "pre-wrap",
|
|
938
|
+
wordBreak: "break-word"
|
|
939
|
+
},
|
|
940
|
+
children: [
|
|
941
|
+
/* @__PURE__ */ jsx4("span", { style: srOnly, children: m.role === "user" ? "You: " : "Assistant: " }),
|
|
942
|
+
m.parts.map(
|
|
943
|
+
(p, i) => p.type === "text" ? /* @__PURE__ */ jsx4("span", { children: m.role === "assistant" ? renderMarkdown(p.text) : p.text }, i) : null
|
|
944
|
+
)
|
|
945
|
+
]
|
|
678
946
|
},
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
)
|
|
684
|
-
]
|
|
685
|
-
},
|
|
686
|
-
m.id
|
|
687
|
-
)),
|
|
688
|
-
status === "submitted" && /* @__PURE__ */ jsx3(
|
|
947
|
+
m.id
|
|
948
|
+
);
|
|
949
|
+
}),
|
|
950
|
+
status === "submitted" && /* @__PURE__ */ jsx4(
|
|
689
951
|
"div",
|
|
690
952
|
{
|
|
691
953
|
"aria-label": "Assistant is thinking",
|
|
@@ -694,10 +956,10 @@ function AIMeChat({
|
|
|
694
956
|
color: "var(--ai-me-text-secondary)",
|
|
695
957
|
fontSize: 13
|
|
696
958
|
},
|
|
697
|
-
children: /* @__PURE__ */
|
|
959
|
+
children: /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: "Thinking\u2026" })
|
|
698
960
|
}
|
|
699
961
|
),
|
|
700
|
-
error && /* @__PURE__ */
|
|
962
|
+
error && /* @__PURE__ */ jsx4(
|
|
701
963
|
"div",
|
|
702
964
|
{
|
|
703
965
|
role: "alert",
|
|
@@ -713,11 +975,11 @@ function AIMeChat({
|
|
|
713
975
|
children: "Something went wrong. Please try again."
|
|
714
976
|
}
|
|
715
977
|
),
|
|
716
|
-
/* @__PURE__ */
|
|
978
|
+
/* @__PURE__ */ jsx4("div", { ref: messagesEndRef, "aria-hidden": "true" })
|
|
717
979
|
]
|
|
718
980
|
}
|
|
719
981
|
),
|
|
720
|
-
/* @__PURE__ */
|
|
982
|
+
/* @__PURE__ */ jsxs2(
|
|
721
983
|
"form",
|
|
722
984
|
{
|
|
723
985
|
onSubmit: handleSubmit,
|
|
@@ -728,7 +990,7 @@ function AIMeChat({
|
|
|
728
990
|
gap: 8
|
|
729
991
|
},
|
|
730
992
|
children: [
|
|
731
|
-
/* @__PURE__ */
|
|
993
|
+
/* @__PURE__ */ jsx4(
|
|
732
994
|
"label",
|
|
733
995
|
{
|
|
734
996
|
htmlFor: "ai-me-chat-input",
|
|
@@ -736,7 +998,7 @@ function AIMeChat({
|
|
|
736
998
|
children: "Message to AI Assistant"
|
|
737
999
|
}
|
|
738
1000
|
),
|
|
739
|
-
/* @__PURE__ */
|
|
1001
|
+
/* @__PURE__ */ jsx4(
|
|
740
1002
|
"input",
|
|
741
1003
|
{
|
|
742
1004
|
id: "ai-me-chat-input",
|
|
@@ -767,7 +1029,7 @@ function AIMeChat({
|
|
|
767
1029
|
}
|
|
768
1030
|
}
|
|
769
1031
|
),
|
|
770
|
-
/* @__PURE__ */
|
|
1032
|
+
/* @__PURE__ */ jsx4(
|
|
771
1033
|
"button",
|
|
772
1034
|
{
|
|
773
1035
|
type: "submit",
|
|
@@ -802,13 +1064,38 @@ function AIMeChat({
|
|
|
802
1064
|
)
|
|
803
1065
|
]
|
|
804
1066
|
}
|
|
805
|
-
)
|
|
1067
|
+
),
|
|
1068
|
+
pendingToolCalls.map((tc) => {
|
|
1069
|
+
const onConfirm = () => addToolApprovalResponse({ id: tc.toolCallId, approved: true });
|
|
1070
|
+
const onCancel = () => addToolApprovalResponse({ id: tc.toolCallId, approved: false, reason: "User cancelled" });
|
|
1071
|
+
return renderConfirmation ? /* @__PURE__ */ jsx4(Fragment, { children: renderConfirmation({
|
|
1072
|
+
tool: {
|
|
1073
|
+
name: tc.toolName,
|
|
1074
|
+
httpMethod: "",
|
|
1075
|
+
path: "",
|
|
1076
|
+
description: tc.toolName
|
|
1077
|
+
},
|
|
1078
|
+
params: tc.args,
|
|
1079
|
+
onConfirm,
|
|
1080
|
+
onCancel
|
|
1081
|
+
}) }, tc.toolCallId) : /* @__PURE__ */ jsx4(
|
|
1082
|
+
AIMeConfirm,
|
|
1083
|
+
{
|
|
1084
|
+
action: tc.toolName,
|
|
1085
|
+
description: `Execute ${tc.toolName}?`,
|
|
1086
|
+
parameters: tc.args,
|
|
1087
|
+
onConfirm,
|
|
1088
|
+
onReject: onCancel
|
|
1089
|
+
},
|
|
1090
|
+
tc.toolCallId
|
|
1091
|
+
);
|
|
1092
|
+
})
|
|
806
1093
|
] });
|
|
807
1094
|
}
|
|
808
1095
|
|
|
809
1096
|
// src/command-palette.tsx
|
|
810
|
-
import { useState as useState3, useEffect as
|
|
811
|
-
import { Fragment as
|
|
1097
|
+
import { useState as useState3, useEffect as useEffect4, useRef as useRef4, useCallback as useCallback4, useId as useId3 } from "react";
|
|
1098
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
812
1099
|
var defaultCommands = [
|
|
813
1100
|
{
|
|
814
1101
|
id: "help",
|
|
@@ -855,14 +1142,14 @@ function AIMeCommandPalette({
|
|
|
855
1142
|
const [open, setOpen] = useState3(false);
|
|
856
1143
|
const [query, setQuery] = useState3("");
|
|
857
1144
|
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
858
|
-
const inputRef =
|
|
859
|
-
const listRef =
|
|
860
|
-
const dialogRef =
|
|
861
|
-
const previousFocusRef =
|
|
1145
|
+
const inputRef = useRef4(null);
|
|
1146
|
+
const listRef = useRef4(null);
|
|
1147
|
+
const dialogRef = useRef4(null);
|
|
1148
|
+
const previousFocusRef = useRef4(null);
|
|
862
1149
|
const { sendMessage } = useAIMe();
|
|
863
|
-
const titleId =
|
|
864
|
-
const inputId =
|
|
865
|
-
const toggle =
|
|
1150
|
+
const titleId = useId3();
|
|
1151
|
+
const inputId = useId3();
|
|
1152
|
+
const toggle = useCallback4(
|
|
866
1153
|
(next) => {
|
|
867
1154
|
setOpen(next);
|
|
868
1155
|
setQuery("");
|
|
@@ -871,7 +1158,7 @@ function AIMeCommandPalette({
|
|
|
871
1158
|
},
|
|
872
1159
|
[onToggle]
|
|
873
1160
|
);
|
|
874
|
-
|
|
1161
|
+
useEffect4(() => {
|
|
875
1162
|
function handleKeyDown2(e) {
|
|
876
1163
|
const metaMatch = shortcut.meta ? e.metaKey : true;
|
|
877
1164
|
const ctrlMatch = shortcut.ctrl ? e.ctrlKey : !shortcut.meta ? e.ctrlKey : true;
|
|
@@ -886,7 +1173,7 @@ function AIMeCommandPalette({
|
|
|
886
1173
|
window.addEventListener("keydown", handleKeyDown2);
|
|
887
1174
|
return () => window.removeEventListener("keydown", handleKeyDown2);
|
|
888
1175
|
}, [open, shortcut, toggle]);
|
|
889
|
-
|
|
1176
|
+
useEffect4(() => {
|
|
890
1177
|
if (open) {
|
|
891
1178
|
setTimeout(() => inputRef.current?.focus(), 0);
|
|
892
1179
|
} else {
|
|
@@ -896,7 +1183,7 @@ function AIMeCommandPalette({
|
|
|
896
1183
|
}
|
|
897
1184
|
}
|
|
898
1185
|
}, [open]);
|
|
899
|
-
|
|
1186
|
+
useEffect4(() => {
|
|
900
1187
|
if (!open) return;
|
|
901
1188
|
function handleFocusTrap(e) {
|
|
902
1189
|
if (e.key !== "Tab") return;
|
|
@@ -926,12 +1213,12 @@ function AIMeCommandPalette({
|
|
|
926
1213
|
const filtered = query.trim() ? commands.filter(
|
|
927
1214
|
(cmd) => cmd.label.toLowerCase().includes(query.toLowerCase()) || cmd.description?.toLowerCase().includes(query.toLowerCase()) || cmd.category?.toLowerCase().includes(query.toLowerCase())
|
|
928
1215
|
) : commands;
|
|
929
|
-
|
|
1216
|
+
useEffect4(() => {
|
|
930
1217
|
if (selectedIndex >= filtered.length) {
|
|
931
1218
|
setSelectedIndex(Math.max(0, filtered.length - 1));
|
|
932
1219
|
}
|
|
933
1220
|
}, [filtered.length, selectedIndex]);
|
|
934
|
-
|
|
1221
|
+
useEffect4(() => {
|
|
935
1222
|
const list = listRef.current;
|
|
936
1223
|
if (!list) return;
|
|
937
1224
|
const selected = list.children[selectedIndex];
|
|
@@ -976,8 +1263,8 @@ function AIMeCommandPalette({
|
|
|
976
1263
|
grouped.get(cat).push(cmd);
|
|
977
1264
|
}
|
|
978
1265
|
let flatIndex = 0;
|
|
979
|
-
return /* @__PURE__ */
|
|
980
|
-
/* @__PURE__ */
|
|
1266
|
+
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1267
|
+
/* @__PURE__ */ jsx5(
|
|
981
1268
|
"div",
|
|
982
1269
|
{
|
|
983
1270
|
onClick: () => toggle(false),
|
|
@@ -990,7 +1277,7 @@ function AIMeCommandPalette({
|
|
|
990
1277
|
"aria-hidden": "true"
|
|
991
1278
|
}
|
|
992
1279
|
),
|
|
993
|
-
/* @__PURE__ */
|
|
1280
|
+
/* @__PURE__ */ jsxs3(
|
|
994
1281
|
"div",
|
|
995
1282
|
{
|
|
996
1283
|
ref: dialogRef,
|
|
@@ -1018,10 +1305,10 @@ function AIMeCommandPalette({
|
|
|
1018
1305
|
"aria-labelledby": titleId,
|
|
1019
1306
|
tabIndex: -1,
|
|
1020
1307
|
children: [
|
|
1021
|
-
/* @__PURE__ */
|
|
1022
|
-
/* @__PURE__ */
|
|
1023
|
-
/* @__PURE__ */
|
|
1024
|
-
/* @__PURE__ */
|
|
1308
|
+
/* @__PURE__ */ jsx5("h2", { id: titleId, style: srOnly2, children: "Command Palette" }),
|
|
1309
|
+
/* @__PURE__ */ jsxs3("div", { style: { padding: "12px 16px", borderBottom: "1px solid var(--ai-me-border)" }, children: [
|
|
1310
|
+
/* @__PURE__ */ jsx5("label", { htmlFor: inputId, style: srOnly2, children: "Search commands or ask AI" }),
|
|
1311
|
+
/* @__PURE__ */ jsx5(
|
|
1025
1312
|
"input",
|
|
1026
1313
|
{
|
|
1027
1314
|
id: inputId,
|
|
@@ -1059,7 +1346,7 @@ function AIMeCommandPalette({
|
|
|
1059
1346
|
}
|
|
1060
1347
|
)
|
|
1061
1348
|
] }),
|
|
1062
|
-
/* @__PURE__ */
|
|
1349
|
+
/* @__PURE__ */ jsxs3(
|
|
1063
1350
|
"div",
|
|
1064
1351
|
{
|
|
1065
1352
|
id: "ai-me-cmd-listbox",
|
|
@@ -1068,7 +1355,7 @@ function AIMeCommandPalette({
|
|
|
1068
1355
|
role: "listbox",
|
|
1069
1356
|
"aria-label": "Commands",
|
|
1070
1357
|
children: [
|
|
1071
|
-
filtered.length === 0 && query.trim() && /* @__PURE__ */
|
|
1358
|
+
filtered.length === 0 && query.trim() && /* @__PURE__ */ jsxs3(
|
|
1072
1359
|
"div",
|
|
1073
1360
|
{
|
|
1074
1361
|
role: "option",
|
|
@@ -1088,8 +1375,8 @@ function AIMeCommandPalette({
|
|
|
1088
1375
|
),
|
|
1089
1376
|
Array.from(grouped.entries()).map(([category, items]) => (
|
|
1090
1377
|
// role="group" with aria-label for the category heading
|
|
1091
|
-
/* @__PURE__ */
|
|
1092
|
-
/* @__PURE__ */
|
|
1378
|
+
/* @__PURE__ */ jsxs3("div", { role: "group", "aria-label": category, children: [
|
|
1379
|
+
/* @__PURE__ */ jsx5(
|
|
1093
1380
|
"div",
|
|
1094
1381
|
{
|
|
1095
1382
|
"aria-hidden": "true",
|
|
@@ -1107,7 +1394,7 @@ function AIMeCommandPalette({
|
|
|
1107
1394
|
items.map((cmd) => {
|
|
1108
1395
|
const idx = flatIndex++;
|
|
1109
1396
|
const isSelected = idx === selectedIndex;
|
|
1110
|
-
return /* @__PURE__ */
|
|
1397
|
+
return /* @__PURE__ */ jsxs3(
|
|
1111
1398
|
"div",
|
|
1112
1399
|
{
|
|
1113
1400
|
id: `cmd-${cmd.id}`,
|
|
@@ -1140,10 +1427,10 @@ function AIMeCommandPalette({
|
|
|
1140
1427
|
},
|
|
1141
1428
|
children: [
|
|
1142
1429
|
cmd.icon && // Icon is decorative — label comes from cmd.label
|
|
1143
|
-
/* @__PURE__ */
|
|
1144
|
-
/* @__PURE__ */
|
|
1145
|
-
/* @__PURE__ */
|
|
1146
|
-
cmd.description && /* @__PURE__ */
|
|
1430
|
+
/* @__PURE__ */ jsx5("span", { "aria-hidden": "true", style: { fontSize: 16, flexShrink: 0 }, children: cmd.icon }),
|
|
1431
|
+
/* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1432
|
+
/* @__PURE__ */ jsx5("div", { style: { fontSize: 14, fontWeight: 500 }, children: cmd.label }),
|
|
1433
|
+
cmd.description && /* @__PURE__ */ jsx5(
|
|
1147
1434
|
"div",
|
|
1148
1435
|
{
|
|
1149
1436
|
style: {
|
|
@@ -1167,7 +1454,7 @@ function AIMeCommandPalette({
|
|
|
1167
1454
|
]
|
|
1168
1455
|
}
|
|
1169
1456
|
),
|
|
1170
|
-
/* @__PURE__ */
|
|
1457
|
+
/* @__PURE__ */ jsxs3(
|
|
1171
1458
|
"div",
|
|
1172
1459
|
{
|
|
1173
1460
|
"aria-hidden": "true",
|
|
@@ -1180,9 +1467,9 @@ function AIMeCommandPalette({
|
|
|
1180
1467
|
gap: 16
|
|
1181
1468
|
},
|
|
1182
1469
|
children: [
|
|
1183
|
-
/* @__PURE__ */
|
|
1184
|
-
/* @__PURE__ */
|
|
1185
|
-
/* @__PURE__ */
|
|
1470
|
+
/* @__PURE__ */ jsx5("span", { children: "\u2191\u2193 Navigate" }),
|
|
1471
|
+
/* @__PURE__ */ jsx5("span", { children: "\u21B5 Select" }),
|
|
1472
|
+
/* @__PURE__ */ jsx5("span", { children: "Esc Close" })
|
|
1186
1473
|
]
|
|
1187
1474
|
}
|
|
1188
1475
|
)
|
|
@@ -1191,200 +1478,12 @@ function AIMeCommandPalette({
|
|
|
1191
1478
|
)
|
|
1192
1479
|
] });
|
|
1193
1480
|
}
|
|
1194
|
-
|
|
1195
|
-
// src/confirm.tsx
|
|
1196
|
-
import { useRef as useRef4, useEffect as useEffect4, useId as useId3, useCallback as useCallback4 } from "react";
|
|
1197
|
-
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1198
|
-
function AIMeConfirm({
|
|
1199
|
-
action,
|
|
1200
|
-
description,
|
|
1201
|
-
parameters,
|
|
1202
|
-
onConfirm,
|
|
1203
|
-
onReject
|
|
1204
|
-
}) {
|
|
1205
|
-
const dialogRef = useRef4(null);
|
|
1206
|
-
const cancelButtonRef = useRef4(null);
|
|
1207
|
-
const titleId = useId3();
|
|
1208
|
-
const descriptionId = useId3();
|
|
1209
|
-
const handleKeyDown = useCallback4(
|
|
1210
|
-
(e) => {
|
|
1211
|
-
if (e.key === "Escape") {
|
|
1212
|
-
e.preventDefault();
|
|
1213
|
-
onReject();
|
|
1214
|
-
return;
|
|
1215
|
-
}
|
|
1216
|
-
if (e.key !== "Tab") return;
|
|
1217
|
-
const dialog = dialogRef.current;
|
|
1218
|
-
if (!dialog) return;
|
|
1219
|
-
const focusable = dialog.querySelectorAll(
|
|
1220
|
-
'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
1221
|
-
);
|
|
1222
|
-
if (focusable.length === 0) return;
|
|
1223
|
-
const first = focusable[0];
|
|
1224
|
-
const last = focusable[focusable.length - 1];
|
|
1225
|
-
if (e.shiftKey) {
|
|
1226
|
-
if (document.activeElement === first) {
|
|
1227
|
-
e.preventDefault();
|
|
1228
|
-
last.focus();
|
|
1229
|
-
}
|
|
1230
|
-
} else {
|
|
1231
|
-
if (document.activeElement === last) {
|
|
1232
|
-
e.preventDefault();
|
|
1233
|
-
first.focus();
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
},
|
|
1237
|
-
[onReject]
|
|
1238
|
-
);
|
|
1239
|
-
useEffect4(() => {
|
|
1240
|
-
const previousFocus = document.activeElement;
|
|
1241
|
-
cancelButtonRef.current?.focus();
|
|
1242
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
1243
|
-
return () => {
|
|
1244
|
-
window.removeEventListener("keydown", handleKeyDown);
|
|
1245
|
-
previousFocus?.focus();
|
|
1246
|
-
};
|
|
1247
|
-
}, [handleKeyDown]);
|
|
1248
|
-
const overlayStyle = {
|
|
1249
|
-
...defaultThemeVars,
|
|
1250
|
-
position: "fixed",
|
|
1251
|
-
inset: 0,
|
|
1252
|
-
backgroundColor: "rgba(0, 0, 0, 0.4)",
|
|
1253
|
-
display: "flex",
|
|
1254
|
-
alignItems: "center",
|
|
1255
|
-
justifyContent: "center",
|
|
1256
|
-
zIndex: 1e4,
|
|
1257
|
-
fontFamily: "var(--ai-me-font)"
|
|
1258
|
-
};
|
|
1259
|
-
const dialogStyle = {
|
|
1260
|
-
backgroundColor: "var(--ai-me-bg)",
|
|
1261
|
-
borderRadius: "var(--ai-me-radius)",
|
|
1262
|
-
padding: 24,
|
|
1263
|
-
maxWidth: 420,
|
|
1264
|
-
width: "90%",
|
|
1265
|
-
boxShadow: "var(--ai-me-shadow)",
|
|
1266
|
-
color: "var(--ai-me-text)"
|
|
1267
|
-
};
|
|
1268
|
-
const focusStyle = {
|
|
1269
|
-
outline: "2px solid transparent",
|
|
1270
|
-
outlineOffset: 2
|
|
1271
|
-
};
|
|
1272
|
-
function applyFocusRing(el) {
|
|
1273
|
-
el.style.outline = "2px solid var(--ai-me-primary)";
|
|
1274
|
-
el.style.outlineOffset = "2px";
|
|
1275
|
-
}
|
|
1276
|
-
function removeFocusRing(el) {
|
|
1277
|
-
el.style.outline = "2px solid transparent";
|
|
1278
|
-
el.style.outlineOffset = "2px";
|
|
1279
|
-
}
|
|
1280
|
-
return (
|
|
1281
|
-
// Overlay is presentational — role and aria go on the inner dialog
|
|
1282
|
-
/* @__PURE__ */ jsx5(
|
|
1283
|
-
"div",
|
|
1284
|
-
{
|
|
1285
|
-
style: overlayStyle,
|
|
1286
|
-
onClick: (e) => {
|
|
1287
|
-
if (e.target === e.currentTarget) onReject();
|
|
1288
|
-
},
|
|
1289
|
-
"aria-hidden": "false",
|
|
1290
|
-
children: /* @__PURE__ */ jsxs3(
|
|
1291
|
-
"div",
|
|
1292
|
-
{
|
|
1293
|
-
ref: dialogRef,
|
|
1294
|
-
style: dialogStyle,
|
|
1295
|
-
role: "alertdialog",
|
|
1296
|
-
"aria-modal": "true",
|
|
1297
|
-
"aria-labelledby": titleId,
|
|
1298
|
-
"aria-describedby": descriptionId,
|
|
1299
|
-
tabIndex: -1,
|
|
1300
|
-
onClick: (e) => e.stopPropagation(),
|
|
1301
|
-
children: [
|
|
1302
|
-
/* @__PURE__ */ jsx5("h3", { id: titleId, style: { margin: "0 0 8px", fontSize: 16 }, children: "Confirm Action" }),
|
|
1303
|
-
/* @__PURE__ */ jsx5("p", { style: { margin: "0 0 4px", fontSize: 14, fontWeight: 600 }, children: action }),
|
|
1304
|
-
/* @__PURE__ */ jsx5(
|
|
1305
|
-
"p",
|
|
1306
|
-
{
|
|
1307
|
-
id: descriptionId,
|
|
1308
|
-
style: {
|
|
1309
|
-
margin: "0 0 16px",
|
|
1310
|
-
fontSize: 13,
|
|
1311
|
-
color: "var(--ai-me-text-secondary)"
|
|
1312
|
-
},
|
|
1313
|
-
children: description
|
|
1314
|
-
}
|
|
1315
|
-
),
|
|
1316
|
-
parameters && Object.keys(parameters).length > 0 && /* @__PURE__ */ jsx5(
|
|
1317
|
-
"pre",
|
|
1318
|
-
{
|
|
1319
|
-
style: {
|
|
1320
|
-
margin: "0 0 16px",
|
|
1321
|
-
padding: 12,
|
|
1322
|
-
backgroundColor: "var(--ai-me-bg-secondary)",
|
|
1323
|
-
borderRadius: 8,
|
|
1324
|
-
fontSize: 12,
|
|
1325
|
-
overflow: "auto",
|
|
1326
|
-
maxHeight: 200,
|
|
1327
|
-
border: "1px solid var(--ai-me-border)"
|
|
1328
|
-
},
|
|
1329
|
-
children: JSON.stringify(parameters, null, 2)
|
|
1330
|
-
}
|
|
1331
|
-
),
|
|
1332
|
-
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
|
|
1333
|
-
/* @__PURE__ */ jsx5(
|
|
1334
|
-
"button",
|
|
1335
|
-
{
|
|
1336
|
-
ref: cancelButtonRef,
|
|
1337
|
-
type: "button",
|
|
1338
|
-
onClick: onReject,
|
|
1339
|
-
style: {
|
|
1340
|
-
padding: "8px 16px",
|
|
1341
|
-
border: "1px solid var(--ai-me-border)",
|
|
1342
|
-
borderRadius: 8,
|
|
1343
|
-
backgroundColor: "var(--ai-me-bg)",
|
|
1344
|
-
color: "var(--ai-me-text)",
|
|
1345
|
-
cursor: "pointer",
|
|
1346
|
-
fontSize: 14,
|
|
1347
|
-
...focusStyle
|
|
1348
|
-
},
|
|
1349
|
-
onFocus: (e) => applyFocusRing(e.currentTarget),
|
|
1350
|
-
onBlur: (e) => removeFocusRing(e.currentTarget),
|
|
1351
|
-
children: "Cancel"
|
|
1352
|
-
}
|
|
1353
|
-
),
|
|
1354
|
-
/* @__PURE__ */ jsx5(
|
|
1355
|
-
"button",
|
|
1356
|
-
{
|
|
1357
|
-
type: "button",
|
|
1358
|
-
onClick: onConfirm,
|
|
1359
|
-
style: {
|
|
1360
|
-
padding: "8px 16px",
|
|
1361
|
-
border: "none",
|
|
1362
|
-
borderRadius: 8,
|
|
1363
|
-
// #fff on var(--ai-me-primary) = #6366f1 → contrast ≈ 4.6:1 (passes AA)
|
|
1364
|
-
backgroundColor: "var(--ai-me-primary)",
|
|
1365
|
-
color: "#fff",
|
|
1366
|
-
cursor: "pointer",
|
|
1367
|
-
fontSize: 14,
|
|
1368
|
-
...focusStyle
|
|
1369
|
-
},
|
|
1370
|
-
onFocus: (e) => applyFocusRing(e.currentTarget),
|
|
1371
|
-
onBlur: (e) => removeFocusRing(e.currentTarget),
|
|
1372
|
-
children: "Confirm"
|
|
1373
|
-
}
|
|
1374
|
-
)
|
|
1375
|
-
] })
|
|
1376
|
-
]
|
|
1377
|
-
}
|
|
1378
|
-
)
|
|
1379
|
-
}
|
|
1380
|
-
)
|
|
1381
|
-
);
|
|
1382
|
-
}
|
|
1383
1481
|
export {
|
|
1384
1482
|
AIMeChat,
|
|
1385
1483
|
AIMeCommandPalette,
|
|
1386
1484
|
AIMeConfirm,
|
|
1387
1485
|
AIMeProvider,
|
|
1486
|
+
cleanAssistantText,
|
|
1388
1487
|
renderMarkdown,
|
|
1389
1488
|
useAIMe,
|
|
1390
1489
|
useAIMeContext
|