@ai-me-chat/react 0.0.1

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 ADDED
@@ -0,0 +1,1381 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ AIMeChat: () => AIMeChat,
25
+ AIMeCommandPalette: () => AIMeCommandPalette,
26
+ AIMeConfirm: () => AIMeConfirm,
27
+ AIMeProvider: () => AIMeProvider,
28
+ renderMarkdown: () => renderMarkdown,
29
+ useAIMe: () => useAIMe,
30
+ useAIMeContext: () => useAIMeContext
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/context.tsx
35
+ var import_react = require("react");
36
+ var AIMeContext = (0, import_react.createContext)(null);
37
+ function useAIMeContext() {
38
+ const ctx = (0, import_react.useContext)(AIMeContext);
39
+ if (!ctx) {
40
+ throw new Error("useAIMe must be used within an <AIMeProvider>");
41
+ }
42
+ return ctx;
43
+ }
44
+
45
+ // src/provider.tsx
46
+ var import_jsx_runtime = require("react/jsx-runtime");
47
+ function AIMeProvider({ endpoint, headers, children }) {
48
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AIMeContext, { value: { endpoint, headers }, children });
49
+ }
50
+
51
+ // src/chat.tsx
52
+ var import_react4 = require("react");
53
+
54
+ // src/use-ai-me.ts
55
+ var import_react2 = require("@ai-sdk/react");
56
+ var import_ai = require("ai");
57
+ var import_react3 = require("react");
58
+ var STORAGE_KEY = "ai-me-messages";
59
+ function useAIMe() {
60
+ const { endpoint, headers } = useAIMeContext();
61
+ const [input, setInput] = (0, import_react3.useState)("");
62
+ const initialized = (0, import_react3.useRef)(false);
63
+ const chat = (0, import_react2.useChat)({
64
+ transport: new import_ai.DefaultChatTransport({
65
+ api: endpoint,
66
+ headers
67
+ })
68
+ });
69
+ (0, import_react3.useEffect)(() => {
70
+ if (initialized.current) return;
71
+ initialized.current = true;
72
+ try {
73
+ const stored = sessionStorage.getItem(STORAGE_KEY);
74
+ if (stored) {
75
+ const parsed = JSON.parse(stored);
76
+ if (Array.isArray(parsed) && parsed.length > 0) {
77
+ chat.setMessages(parsed);
78
+ }
79
+ }
80
+ } catch {
81
+ }
82
+ }, []);
83
+ (0, import_react3.useEffect)(() => {
84
+ if (!initialized.current) return;
85
+ try {
86
+ if (chat.messages.length > 0) {
87
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify(chat.messages));
88
+ } else {
89
+ sessionStorage.removeItem(STORAGE_KEY);
90
+ }
91
+ } catch {
92
+ }
93
+ }, [chat.messages]);
94
+ const handleInputChange = (0, import_react3.useCallback)(
95
+ (e) => {
96
+ setInput(e.target.value);
97
+ },
98
+ []
99
+ );
100
+ const handleSubmit = (0, import_react3.useCallback)(
101
+ (e) => {
102
+ e?.preventDefault();
103
+ if (!input.trim()) return;
104
+ chat.sendMessage({ text: input });
105
+ setInput("");
106
+ },
107
+ [input, chat]
108
+ );
109
+ const clearMessages = (0, import_react3.useCallback)(() => {
110
+ chat.setMessages([]);
111
+ try {
112
+ sessionStorage.removeItem(STORAGE_KEY);
113
+ } catch {
114
+ }
115
+ }, [chat]);
116
+ return {
117
+ /** Conversation messages */
118
+ messages: chat.messages,
119
+ /** Current input value */
120
+ input,
121
+ /** Set input value */
122
+ setInput,
123
+ /** Handle input change */
124
+ handleInputChange,
125
+ /** Submit the current message */
126
+ handleSubmit,
127
+ /** Send a message directly */
128
+ sendMessage: chat.sendMessage,
129
+ /** Chat status: "ready" | "submitted" | "streaming" */
130
+ status: chat.status,
131
+ /** Error if any */
132
+ error: chat.error,
133
+ /** Stop streaming */
134
+ stop: chat.stop,
135
+ /** Set messages */
136
+ setMessages: chat.setMessages,
137
+ /** Clear all messages and session storage */
138
+ clearMessages
139
+ };
140
+ }
141
+
142
+ // src/styles.ts
143
+ var defaultThemeVars = {
144
+ "--ai-me-primary": "#6366f1",
145
+ "--ai-me-primary-hover": "#4f46e5",
146
+ "--ai-me-bg": "#ffffff",
147
+ "--ai-me-bg-secondary": "#f9fafb",
148
+ "--ai-me-text": "#111827",
149
+ "--ai-me-text-secondary": "#6b7280",
150
+ "--ai-me-border": "#e5e7eb",
151
+ "--ai-me-radius": "12px",
152
+ "--ai-me-font": "system-ui, -apple-system, sans-serif",
153
+ "--ai-me-shadow": "0 4px 24px rgba(0, 0, 0, 0.12)"
154
+ };
155
+ function themeToVars(theme) {
156
+ if (!theme) return {};
157
+ const vars = {};
158
+ if (theme.primaryColor) vars["--ai-me-primary"] = theme.primaryColor;
159
+ if (theme.backgroundColor) vars["--ai-me-bg"] = theme.backgroundColor;
160
+ if (theme.textColor) vars["--ai-me-text"] = theme.textColor;
161
+ if (theme.borderRadius) vars["--ai-me-radius"] = theme.borderRadius;
162
+ if (theme.fontFamily) vars["--ai-me-font"] = theme.fontFamily;
163
+ return vars;
164
+ }
165
+
166
+ // src/markdown.tsx
167
+ var import_jsx_runtime2 = require("react/jsx-runtime");
168
+ function renderMarkdown(text) {
169
+ const lines = text.split("\n");
170
+ const result = [];
171
+ let i = 0;
172
+ while (i < lines.length) {
173
+ const line = lines[i];
174
+ if (line.trimStart().startsWith("```")) {
175
+ const lang = line.trimStart().slice(3).trim();
176
+ const codeLines = [];
177
+ i++;
178
+ while (i < lines.length && !lines[i].trimStart().startsWith("```")) {
179
+ codeLines.push(lines[i]);
180
+ i++;
181
+ }
182
+ i++;
183
+ result.push(
184
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
185
+ "pre",
186
+ {
187
+ style: codeBlockStyle,
188
+ "data-lang": lang || void 0,
189
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: codeLines.join("\n") })
190
+ },
191
+ `code-${result.length}`
192
+ )
193
+ );
194
+ continue;
195
+ }
196
+ if (/^[\s]*[-*]\s/.test(line)) {
197
+ const listItems = [];
198
+ while (i < lines.length && /^[\s]*[-*]\s/.test(lines[i])) {
199
+ listItems.push(
200
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { style: { marginBottom: 2 }, children: renderInline(lines[i].replace(/^[\s]*[-*]\s/, "")) }, listItems.length)
201
+ );
202
+ i++;
203
+ }
204
+ result.push(
205
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
206
+ "ul",
207
+ {
208
+ style: { margin: "4px 0", paddingLeft: 20, listStyleType: "disc" },
209
+ children: listItems
210
+ },
211
+ `ul-${result.length}`
212
+ )
213
+ );
214
+ continue;
215
+ }
216
+ if (/^[\s]*\d+\.\s/.test(line)) {
217
+ const listItems = [];
218
+ while (i < lines.length && /^[\s]*\d+\.\s/.test(lines[i])) {
219
+ listItems.push(
220
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { style: { marginBottom: 2 }, children: renderInline(lines[i].replace(/^[\s]*\d+\.\s/, "")) }, listItems.length)
221
+ );
222
+ i++;
223
+ }
224
+ result.push(
225
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
226
+ "ol",
227
+ {
228
+ style: { margin: "4px 0", paddingLeft: 20 },
229
+ children: listItems
230
+ },
231
+ `ol-${result.length}`
232
+ )
233
+ );
234
+ continue;
235
+ }
236
+ const headingMatch = /^(#{1,6})\s+(.+)$/.exec(line);
237
+ if (headingMatch) {
238
+ const level = Math.max(headingMatch[1].length, 3);
239
+ const fontSize = { 3: 15, 4: 14, 5: 13, 6: 13 }[level] ?? 14;
240
+ result.push(
241
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
242
+ "p",
243
+ {
244
+ style: { fontWeight: 600, fontSize, margin: "8px 0 4px" },
245
+ children: renderInline(headingMatch[2])
246
+ },
247
+ `h-${result.length}`
248
+ )
249
+ );
250
+ i++;
251
+ continue;
252
+ }
253
+ if (line.trim() === "") {
254
+ i++;
255
+ continue;
256
+ }
257
+ result.push(
258
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
259
+ "span",
260
+ {
261
+ style: { display: "block", marginBottom: 2 },
262
+ children: renderInline(line)
263
+ },
264
+ `p-${result.length}`
265
+ )
266
+ );
267
+ i++;
268
+ }
269
+ return result;
270
+ }
271
+ function renderInline(text) {
272
+ const result = [];
273
+ const pattern = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*]+\*|\[[^\]]+\]\([^)]+\))/g;
274
+ let lastIndex = 0;
275
+ let match;
276
+ while ((match = pattern.exec(text)) !== null) {
277
+ if (match.index > lastIndex) {
278
+ result.push(text.slice(lastIndex, match.index));
279
+ }
280
+ const token = match[0];
281
+ if (token.startsWith("`")) {
282
+ result.push(
283
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: inlineCodeStyle, children: token.slice(1, -1) }, result.length)
284
+ );
285
+ } else if (token.startsWith("**")) {
286
+ result.push(
287
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: token.slice(2, -2) }, result.length)
288
+ );
289
+ } else if (token.startsWith("*")) {
290
+ result.push(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("em", { children: token.slice(1, -1) }, result.length));
291
+ } else if (token.startsWith("[")) {
292
+ const linkMatch = /\[([^\]]+)\]\(([^)]+)\)/.exec(token);
293
+ if (linkMatch) {
294
+ result.push(
295
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
296
+ "a",
297
+ {
298
+ href: linkMatch[2],
299
+ target: "_blank",
300
+ rel: "noopener noreferrer",
301
+ style: linkStyle,
302
+ children: linkMatch[1]
303
+ },
304
+ result.length
305
+ )
306
+ );
307
+ }
308
+ }
309
+ lastIndex = match.index + token.length;
310
+ }
311
+ if (lastIndex < text.length) {
312
+ result.push(text.slice(lastIndex));
313
+ }
314
+ return result;
315
+ }
316
+ var codeBlockStyle = {
317
+ margin: "6px 0",
318
+ padding: "10px 12px",
319
+ borderRadius: 6,
320
+ backgroundColor: "rgba(0,0,0,0.15)",
321
+ fontSize: 12,
322
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
323
+ overflow: "auto",
324
+ whiteSpace: "pre",
325
+ lineHeight: 1.5
326
+ };
327
+ var inlineCodeStyle = {
328
+ padding: "1px 5px",
329
+ borderRadius: 3,
330
+ backgroundColor: "rgba(0,0,0,0.12)",
331
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
332
+ fontSize: "0.9em"
333
+ };
334
+ var linkStyle = {
335
+ color: "var(--ai-me-primary, #6366f1)",
336
+ textDecoration: "underline",
337
+ textUnderlineOffset: "2px"
338
+ };
339
+
340
+ // src/chat.tsx
341
+ var import_jsx_runtime3 = require("react/jsx-runtime");
342
+ var srOnly = {
343
+ position: "absolute",
344
+ width: 1,
345
+ height: 1,
346
+ padding: 0,
347
+ margin: -1,
348
+ overflow: "hidden",
349
+ clip: "rect(0,0,0,0)",
350
+ whiteSpace: "nowrap",
351
+ borderWidth: 0
352
+ };
353
+ function AIMeChat({
354
+ position = "bottom-right",
355
+ theme,
356
+ welcomeMessage = "Hi! I can help you navigate and use this app. What would you like to do?",
357
+ suggestedPrompts,
358
+ defaultOpen = false,
359
+ onToggle
360
+ }) {
361
+ const [open, setOpen] = (0, import_react4.useState)(defaultOpen);
362
+ const messagesEndRef = (0, import_react4.useRef)(null);
363
+ const inputRef = (0, import_react4.useRef)(null);
364
+ const panelRef = (0, import_react4.useRef)(null);
365
+ const triggerRef = (0, import_react4.useRef)(null);
366
+ const {
367
+ messages,
368
+ input,
369
+ handleInputChange,
370
+ handleSubmit,
371
+ status,
372
+ error,
373
+ setInput
374
+ } = useAIMe();
375
+ const titleId = (0, import_react4.useId)();
376
+ const messagesId = (0, import_react4.useId)();
377
+ const isInline = position === "inline";
378
+ const toggleOpen = (0, import_react4.useCallback)(() => {
379
+ const next = !open;
380
+ setOpen(next);
381
+ onToggle?.(next);
382
+ }, [open, onToggle]);
383
+ (0, import_react4.useEffect)(() => {
384
+ function handleKeyDown(e) {
385
+ if ((e.metaKey || e.ctrlKey) && e.key === ".") {
386
+ e.preventDefault();
387
+ toggleOpen();
388
+ }
389
+ }
390
+ window.addEventListener("keydown", handleKeyDown);
391
+ return () => window.removeEventListener("keydown", handleKeyDown);
392
+ }, [toggleOpen]);
393
+ (0, import_react4.useEffect)(() => {
394
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
395
+ }, [messages]);
396
+ (0, import_react4.useEffect)(() => {
397
+ if (open) {
398
+ panelRef.current?.focus();
399
+ setTimeout(() => inputRef.current?.focus(), 0);
400
+ } else {
401
+ triggerRef.current?.focus();
402
+ }
403
+ }, [open]);
404
+ (0, import_react4.useEffect)(() => {
405
+ if (!open || isInline) return;
406
+ function handleKeyDown(e) {
407
+ if (e.key === "Escape") {
408
+ e.preventDefault();
409
+ toggleOpen();
410
+ return;
411
+ }
412
+ if (e.key !== "Tab") return;
413
+ const panel = panelRef.current;
414
+ if (!panel) return;
415
+ const focusable = panel.querySelectorAll(
416
+ 'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
417
+ );
418
+ if (focusable.length === 0) return;
419
+ const first = focusable[0];
420
+ const last = focusable[focusable.length - 1];
421
+ if (e.shiftKey) {
422
+ if (document.activeElement === first) {
423
+ e.preventDefault();
424
+ last.focus();
425
+ }
426
+ } else {
427
+ if (document.activeElement === last) {
428
+ e.preventDefault();
429
+ first.focus();
430
+ }
431
+ }
432
+ }
433
+ window.addEventListener("keydown", handleKeyDown);
434
+ return () => window.removeEventListener("keydown", handleKeyDown);
435
+ }, [open, isInline, toggleOpen]);
436
+ const themeVars = {
437
+ ...defaultThemeVars,
438
+ ...themeToVars(theme)
439
+ };
440
+ const panelStyle = isInline ? {
441
+ ...themeVars,
442
+ width: "100%",
443
+ height: "100%",
444
+ display: "flex",
445
+ flexDirection: "column",
446
+ fontFamily: "var(--ai-me-font)",
447
+ color: "var(--ai-me-text)",
448
+ backgroundColor: "var(--ai-me-bg)",
449
+ borderRadius: "var(--ai-me-radius)",
450
+ border: "1px solid var(--ai-me-border)",
451
+ overflow: "hidden"
452
+ } : {
453
+ ...themeVars,
454
+ position: "fixed",
455
+ bottom: 24,
456
+ ...position === "bottom-right" ? { right: 24 } : { left: 24 },
457
+ width: 380,
458
+ maxHeight: "70vh",
459
+ display: open ? "flex" : "none",
460
+ flexDirection: "column",
461
+ fontFamily: "var(--ai-me-font)",
462
+ color: "var(--ai-me-text)",
463
+ backgroundColor: "var(--ai-me-bg)",
464
+ borderRadius: "var(--ai-me-radius)",
465
+ boxShadow: "var(--ai-me-shadow)",
466
+ border: "1px solid var(--ai-me-border)",
467
+ overflow: "hidden",
468
+ zIndex: 9999
469
+ };
470
+ const triggerStyle = {
471
+ ...themeVars,
472
+ position: "fixed",
473
+ bottom: 24,
474
+ ...position === "bottom-right" ? { right: 24 } : { left: 24 },
475
+ width: 56,
476
+ height: 56,
477
+ borderRadius: "50%",
478
+ backgroundColor: "var(--ai-me-primary)",
479
+ color: "#fff",
480
+ border: "none",
481
+ cursor: "pointer",
482
+ display: isInline || open ? "none" : "flex",
483
+ alignItems: "center",
484
+ justifyContent: "center",
485
+ boxShadow: "var(--ai-me-shadow)",
486
+ fontSize: 24,
487
+ zIndex: 9999
488
+ };
489
+ const isStreaming = status === "submitted" || status === "streaming";
490
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
491
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
492
+ "button",
493
+ {
494
+ ref: triggerRef,
495
+ onClick: toggleOpen,
496
+ style: triggerStyle,
497
+ "aria-label": "Open AI chat",
498
+ "aria-expanded": open,
499
+ "aria-controls": isInline ? void 0 : "ai-me-chat-panel",
500
+ type: "button",
501
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", children: "\u{1F4AC}" })
502
+ }
503
+ ),
504
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
505
+ "div",
506
+ {
507
+ id: "ai-me-chat-panel",
508
+ ref: panelRef,
509
+ style: panelStyle,
510
+ role: "dialog",
511
+ "aria-modal": isInline ? void 0 : "true",
512
+ "aria-labelledby": titleId,
513
+ "aria-busy": isStreaming,
514
+ tabIndex: -1,
515
+ children: [
516
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
517
+ "div",
518
+ {
519
+ style: {
520
+ padding: "12px 16px",
521
+ borderBottom: "1px solid var(--ai-me-border)",
522
+ display: "flex",
523
+ alignItems: "center",
524
+ justifyContent: "space-between",
525
+ backgroundColor: "var(--ai-me-bg-secondary)"
526
+ },
527
+ children: [
528
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { id: titleId, style: { fontWeight: 600, fontSize: 14 }, children: "AI Assistant" }),
529
+ !isInline && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
530
+ "button",
531
+ {
532
+ onClick: toggleOpen,
533
+ style: {
534
+ background: "none",
535
+ border: "none",
536
+ cursor: "pointer",
537
+ fontSize: 18,
538
+ color: "var(--ai-me-text-secondary)",
539
+ padding: 4,
540
+ borderRadius: 4,
541
+ // Visible focus indicator without outline:none
542
+ outline: "2px solid transparent",
543
+ outlineOffset: 2
544
+ },
545
+ onFocus: (e) => {
546
+ e.currentTarget.style.outline = "2px solid var(--ai-me-primary)";
547
+ },
548
+ onBlur: (e) => {
549
+ e.currentTarget.style.outline = "2px solid transparent";
550
+ },
551
+ "aria-label": "Close chat",
552
+ type: "button",
553
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", children: "\u2715" })
554
+ }
555
+ )
556
+ ]
557
+ }
558
+ ),
559
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
560
+ "a",
561
+ {
562
+ href: "#ai-me-chat-input",
563
+ style: {
564
+ ...srOnly
565
+ // Reveal on focus so keyboard users can see it
566
+ },
567
+ onFocus: (e) => {
568
+ Object.assign(e.currentTarget.style, {
569
+ position: "static",
570
+ width: "auto",
571
+ height: "auto",
572
+ padding: "4px 8px",
573
+ margin: 0,
574
+ overflow: "visible",
575
+ clip: "auto",
576
+ whiteSpace: "normal",
577
+ backgroundColor: "var(--ai-me-primary)",
578
+ color: "#fff",
579
+ fontSize: 12,
580
+ borderRadius: 4
581
+ });
582
+ },
583
+ onBlur: (e) => {
584
+ Object.assign(e.currentTarget.style, srOnly);
585
+ },
586
+ children: "Skip to message input"
587
+ }
588
+ ),
589
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
590
+ "div",
591
+ {
592
+ id: messagesId,
593
+ "aria-live": "polite",
594
+ "aria-label": "Conversation",
595
+ "aria-relevant": "additions",
596
+ style: {
597
+ flex: 1,
598
+ overflowY: "auto",
599
+ padding: 16,
600
+ display: "flex",
601
+ flexDirection: "column",
602
+ gap: 12
603
+ },
604
+ children: [
605
+ messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { color: "var(--ai-me-text-secondary)", fontSize: 14 }, children: [
606
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: welcomeMessage }),
607
+ suggestedPrompts && suggestedPrompts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
608
+ "div",
609
+ {
610
+ style: {
611
+ marginTop: 12,
612
+ display: "flex",
613
+ flexDirection: "column",
614
+ gap: 8
615
+ },
616
+ children: [
617
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "0 0 4px", fontSize: 12, fontWeight: 500 }, children: "Suggested questions:" }),
618
+ suggestedPrompts.map((prompt) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
619
+ "button",
620
+ {
621
+ type: "button",
622
+ onClick: () => {
623
+ setInput(prompt);
624
+ inputRef.current?.focus();
625
+ },
626
+ style: {
627
+ padding: "8px 12px",
628
+ border: "1px solid var(--ai-me-border)",
629
+ borderRadius: 8,
630
+ background: "var(--ai-me-bg)",
631
+ cursor: "pointer",
632
+ textAlign: "left",
633
+ fontSize: 13,
634
+ color: "var(--ai-me-text)",
635
+ outline: "2px solid transparent",
636
+ outlineOffset: 2
637
+ },
638
+ onFocus: (e) => {
639
+ e.currentTarget.style.outline = "2px solid var(--ai-me-primary)";
640
+ },
641
+ onBlur: (e) => {
642
+ e.currentTarget.style.outline = "2px solid transparent";
643
+ },
644
+ children: prompt
645
+ },
646
+ prompt
647
+ ))
648
+ ]
649
+ }
650
+ )
651
+ ] }),
652
+ messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
653
+ "div",
654
+ {
655
+ style: {
656
+ alignSelf: m.role === "user" ? "flex-end" : "flex-start",
657
+ maxWidth: "85%",
658
+ padding: "8px 12px",
659
+ borderRadius: 8,
660
+ backgroundColor: m.role === "user" ? "var(--ai-me-primary)" : "var(--ai-me-bg-secondary)",
661
+ color: m.role === "user" ? "#fff" : "var(--ai-me-text)",
662
+ fontSize: 14,
663
+ lineHeight: 1.5,
664
+ whiteSpace: "pre-wrap",
665
+ wordBreak: "break-word"
666
+ },
667
+ children: [
668
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: srOnly, children: m.role === "user" ? "You: " : "Assistant: " }),
669
+ m.parts.map(
670
+ (p, i) => p.type === "text" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: m.role === "assistant" ? renderMarkdown(p.text) : p.text }, i) : null
671
+ )
672
+ ]
673
+ },
674
+ m.id
675
+ )),
676
+ status === "submitted" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
677
+ "div",
678
+ {
679
+ "aria-label": "Assistant is thinking",
680
+ style: {
681
+ alignSelf: "flex-start",
682
+ color: "var(--ai-me-text-secondary)",
683
+ fontSize: 13
684
+ },
685
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", children: "Thinking\u2026" })
686
+ }
687
+ ),
688
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
689
+ "div",
690
+ {
691
+ role: "alert",
692
+ style: {
693
+ padding: "8px 12px",
694
+ borderRadius: 8,
695
+ backgroundColor: "#fef2f2",
696
+ // #dc2626 on #fef2f2 ≈ 5.1:1 — passes AA for normal text
697
+ color: "#dc2626",
698
+ fontSize: 13,
699
+ border: "1px solid #fca5a5"
700
+ },
701
+ children: "Something went wrong. Please try again."
702
+ }
703
+ ),
704
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: messagesEndRef, "aria-hidden": "true" })
705
+ ]
706
+ }
707
+ ),
708
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
709
+ "form",
710
+ {
711
+ onSubmit: handleSubmit,
712
+ style: {
713
+ padding: "12px 16px",
714
+ borderTop: "1px solid var(--ai-me-border)",
715
+ display: "flex",
716
+ gap: 8
717
+ },
718
+ children: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
720
+ "label",
721
+ {
722
+ htmlFor: "ai-me-chat-input",
723
+ style: srOnly,
724
+ children: "Message to AI Assistant"
725
+ }
726
+ ),
727
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
728
+ "input",
729
+ {
730
+ id: "ai-me-chat-input",
731
+ ref: inputRef,
732
+ value: input,
733
+ onChange: handleInputChange,
734
+ placeholder: "Ask anything\u2026",
735
+ disabled: status !== "ready",
736
+ "aria-disabled": status !== "ready",
737
+ style: {
738
+ flex: 1,
739
+ padding: "8px 12px",
740
+ border: "1px solid var(--ai-me-border)",
741
+ borderRadius: 8,
742
+ fontSize: 14,
743
+ fontFamily: "var(--ai-me-font)",
744
+ // Do NOT use outline:none — use outline with transparent color + focus handler
745
+ outline: "2px solid transparent",
746
+ outlineOffset: 2,
747
+ backgroundColor: "var(--ai-me-bg)",
748
+ color: "var(--ai-me-text)"
749
+ },
750
+ onFocus: (e) => {
751
+ e.currentTarget.style.outline = "2px solid var(--ai-me-primary)";
752
+ },
753
+ onBlur: (e) => {
754
+ e.currentTarget.style.outline = "2px solid transparent";
755
+ }
756
+ }
757
+ ),
758
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
759
+ "button",
760
+ {
761
+ type: "submit",
762
+ disabled: status !== "ready" || !input.trim(),
763
+ "aria-disabled": status !== "ready" || !input.trim(),
764
+ style: {
765
+ padding: "8px 16px",
766
+ backgroundColor: "var(--ai-me-primary)",
767
+ color: "#fff",
768
+ border: "none",
769
+ borderRadius: 8,
770
+ cursor: status === "ready" && input.trim() ? "pointer" : "not-allowed",
771
+ fontSize: 14,
772
+ fontFamily: "var(--ai-me-font)",
773
+ opacity: status === "ready" && input.trim() ? 1 : 0.5,
774
+ outline: "2px solid transparent",
775
+ outlineOffset: 2
776
+ },
777
+ onFocus: (e) => {
778
+ e.currentTarget.style.outline = "2px solid var(--ai-me-primary)";
779
+ e.currentTarget.style.outlineOffset = "2px";
780
+ },
781
+ onBlur: (e) => {
782
+ e.currentTarget.style.outline = "2px solid transparent";
783
+ },
784
+ "aria-label": "Send message",
785
+ children: "Send"
786
+ }
787
+ )
788
+ ]
789
+ }
790
+ )
791
+ ]
792
+ }
793
+ )
794
+ ] });
795
+ }
796
+
797
+ // src/command-palette.tsx
798
+ var import_react5 = require("react");
799
+ var import_jsx_runtime4 = require("react/jsx-runtime");
800
+ var defaultCommands = [
801
+ {
802
+ id: "help",
803
+ label: "Ask AI for help",
804
+ description: "Get assistance with any task",
805
+ category: "AI",
806
+ icon: "\u{1F4A1}",
807
+ action: "What can you help me with?"
808
+ },
809
+ {
810
+ id: "list-actions",
811
+ label: "List available actions",
812
+ description: "Show all API actions the AI can perform",
813
+ category: "AI",
814
+ icon: "\u{1F4CB}",
815
+ action: "What actions can you perform? List them all."
816
+ },
817
+ {
818
+ id: "recent-activity",
819
+ label: "Show recent activity",
820
+ description: "Summarize recent actions and changes",
821
+ category: "AI",
822
+ icon: "\u{1F550}",
823
+ action: "What has happened recently? Summarize recent activity."
824
+ }
825
+ ];
826
+ var srOnly2 = {
827
+ position: "absolute",
828
+ width: 1,
829
+ height: 1,
830
+ padding: 0,
831
+ margin: -1,
832
+ overflow: "hidden",
833
+ clip: "rect(0,0,0,0)",
834
+ whiteSpace: "nowrap",
835
+ borderWidth: 0
836
+ };
837
+ function AIMeCommandPalette({
838
+ commands = defaultCommands,
839
+ theme,
840
+ shortcut = { key: "k", meta: true },
841
+ onToggle
842
+ }) {
843
+ const [open, setOpen] = (0, import_react5.useState)(false);
844
+ const [query, setQuery] = (0, import_react5.useState)("");
845
+ const [selectedIndex, setSelectedIndex] = (0, import_react5.useState)(0);
846
+ const inputRef = (0, import_react5.useRef)(null);
847
+ const listRef = (0, import_react5.useRef)(null);
848
+ const dialogRef = (0, import_react5.useRef)(null);
849
+ const previousFocusRef = (0, import_react5.useRef)(null);
850
+ const { sendMessage } = useAIMe();
851
+ const titleId = (0, import_react5.useId)();
852
+ const inputId = (0, import_react5.useId)();
853
+ const toggle = (0, import_react5.useCallback)(
854
+ (next) => {
855
+ setOpen(next);
856
+ setQuery("");
857
+ setSelectedIndex(0);
858
+ onToggle?.(next);
859
+ },
860
+ [onToggle]
861
+ );
862
+ (0, import_react5.useEffect)(() => {
863
+ function handleKeyDown2(e) {
864
+ const metaMatch = shortcut.meta ? e.metaKey : true;
865
+ const ctrlMatch = shortcut.ctrl ? e.ctrlKey : !shortcut.meta ? e.ctrlKey : true;
866
+ if ((metaMatch || ctrlMatch) && e.key === shortcut.key) {
867
+ e.preventDefault();
868
+ if (!open) {
869
+ previousFocusRef.current = document.activeElement;
870
+ }
871
+ toggle(!open);
872
+ }
873
+ }
874
+ window.addEventListener("keydown", handleKeyDown2);
875
+ return () => window.removeEventListener("keydown", handleKeyDown2);
876
+ }, [open, shortcut, toggle]);
877
+ (0, import_react5.useEffect)(() => {
878
+ if (open) {
879
+ setTimeout(() => inputRef.current?.focus(), 0);
880
+ } else {
881
+ if (previousFocusRef.current) {
882
+ previousFocusRef.current.focus();
883
+ previousFocusRef.current = null;
884
+ }
885
+ }
886
+ }, [open]);
887
+ (0, import_react5.useEffect)(() => {
888
+ if (!open) return;
889
+ function handleFocusTrap(e) {
890
+ if (e.key !== "Tab") return;
891
+ const dialog = dialogRef.current;
892
+ if (!dialog) return;
893
+ const focusable = dialog.querySelectorAll(
894
+ 'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
895
+ );
896
+ if (focusable.length === 0) return;
897
+ const first = focusable[0];
898
+ const last = focusable[focusable.length - 1];
899
+ if (e.shiftKey) {
900
+ if (document.activeElement === first) {
901
+ e.preventDefault();
902
+ last.focus();
903
+ }
904
+ } else {
905
+ if (document.activeElement === last) {
906
+ e.preventDefault();
907
+ first.focus();
908
+ }
909
+ }
910
+ }
911
+ window.addEventListener("keydown", handleFocusTrap);
912
+ return () => window.removeEventListener("keydown", handleFocusTrap);
913
+ }, [open]);
914
+ const filtered = query.trim() ? commands.filter(
915
+ (cmd) => cmd.label.toLowerCase().includes(query.toLowerCase()) || cmd.description?.toLowerCase().includes(query.toLowerCase()) || cmd.category?.toLowerCase().includes(query.toLowerCase())
916
+ ) : commands;
917
+ (0, import_react5.useEffect)(() => {
918
+ if (selectedIndex >= filtered.length) {
919
+ setSelectedIndex(Math.max(0, filtered.length - 1));
920
+ }
921
+ }, [filtered.length, selectedIndex]);
922
+ (0, import_react5.useEffect)(() => {
923
+ const list = listRef.current;
924
+ if (!list) return;
925
+ const selected = list.children[selectedIndex];
926
+ selected?.scrollIntoView({ block: "nearest" });
927
+ }, [selectedIndex]);
928
+ function executeCommand(cmd) {
929
+ toggle(false);
930
+ if (typeof cmd.action === "string") {
931
+ sendMessage({ text: cmd.action });
932
+ } else {
933
+ cmd.action();
934
+ }
935
+ }
936
+ function handleKeyDown(e) {
937
+ if (e.key === "ArrowDown") {
938
+ e.preventDefault();
939
+ setSelectedIndex((prev) => Math.min(prev + 1, filtered.length - 1));
940
+ } else if (e.key === "ArrowUp") {
941
+ e.preventDefault();
942
+ setSelectedIndex((prev) => Math.max(prev - 1, 0));
943
+ } else if (e.key === "Enter") {
944
+ e.preventDefault();
945
+ if (filtered[selectedIndex]) {
946
+ executeCommand(filtered[selectedIndex]);
947
+ } else if (query.trim()) {
948
+ toggle(false);
949
+ sendMessage({ text: query });
950
+ }
951
+ } else if (e.key === "Escape") {
952
+ toggle(false);
953
+ }
954
+ }
955
+ if (!open) return null;
956
+ const themeVars = {
957
+ ...defaultThemeVars,
958
+ ...themeToVars(theme)
959
+ };
960
+ const grouped = /* @__PURE__ */ new Map();
961
+ for (const cmd of filtered) {
962
+ const cat = cmd.category ?? "Actions";
963
+ if (!grouped.has(cat)) grouped.set(cat, []);
964
+ grouped.get(cat).push(cmd);
965
+ }
966
+ let flatIndex = 0;
967
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
968
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
969
+ "div",
970
+ {
971
+ onClick: () => toggle(false),
972
+ style: {
973
+ position: "fixed",
974
+ inset: 0,
975
+ backgroundColor: "rgba(0,0,0,0.5)",
976
+ zIndex: 99998
977
+ },
978
+ "aria-hidden": "true"
979
+ }
980
+ ),
981
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
982
+ "div",
983
+ {
984
+ ref: dialogRef,
985
+ style: {
986
+ ...themeVars,
987
+ position: "fixed",
988
+ top: "20%",
989
+ left: "50%",
990
+ transform: "translateX(-50%)",
991
+ width: "min(520px, 90vw)",
992
+ maxHeight: "60vh",
993
+ display: "flex",
994
+ flexDirection: "column",
995
+ backgroundColor: "var(--ai-me-bg)",
996
+ borderRadius: "var(--ai-me-radius)",
997
+ border: "1px solid var(--ai-me-border)",
998
+ boxShadow: "0 24px 48px rgba(0,0,0,0.3)",
999
+ overflow: "hidden",
1000
+ zIndex: 99999,
1001
+ fontFamily: "var(--ai-me-font)",
1002
+ color: "var(--ai-me-text)"
1003
+ },
1004
+ role: "dialog",
1005
+ "aria-modal": "true",
1006
+ "aria-labelledby": titleId,
1007
+ tabIndex: -1,
1008
+ children: [
1009
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { id: titleId, style: srOnly2, children: "Command Palette" }),
1010
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: "12px 16px", borderBottom: "1px solid var(--ai-me-border)" }, children: [
1011
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { htmlFor: inputId, style: srOnly2, children: "Search commands or ask AI" }),
1012
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1013
+ "input",
1014
+ {
1015
+ id: inputId,
1016
+ ref: inputRef,
1017
+ value: query,
1018
+ onChange: (e) => {
1019
+ setQuery(e.target.value);
1020
+ setSelectedIndex(0);
1021
+ },
1022
+ onKeyDown: handleKeyDown,
1023
+ placeholder: "Type a command or ask AI...",
1024
+ style: {
1025
+ width: "100%",
1026
+ padding: "8px 0",
1027
+ border: "none",
1028
+ // Do NOT suppress the outline entirely — use transparent + focus handler
1029
+ outline: "2px solid transparent",
1030
+ outlineOffset: 2,
1031
+ fontSize: 15,
1032
+ fontFamily: "var(--ai-me-font)",
1033
+ backgroundColor: "transparent",
1034
+ color: "var(--ai-me-text)"
1035
+ },
1036
+ onFocus: (e) => {
1037
+ e.currentTarget.style.outline = "2px solid var(--ai-me-primary)";
1038
+ },
1039
+ onBlur: (e) => {
1040
+ e.currentTarget.style.outline = "2px solid transparent";
1041
+ },
1042
+ role: "combobox",
1043
+ "aria-expanded": "true",
1044
+ "aria-autocomplete": "list",
1045
+ "aria-controls": "ai-me-cmd-listbox",
1046
+ "aria-activedescendant": filtered[selectedIndex] ? `cmd-${filtered[selectedIndex].id}` : void 0
1047
+ }
1048
+ )
1049
+ ] }),
1050
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1051
+ "div",
1052
+ {
1053
+ id: "ai-me-cmd-listbox",
1054
+ ref: listRef,
1055
+ style: { overflowY: "auto", padding: "8px 0" },
1056
+ role: "listbox",
1057
+ "aria-label": "Commands",
1058
+ children: [
1059
+ filtered.length === 0 && query.trim() && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1060
+ "div",
1061
+ {
1062
+ role: "option",
1063
+ "aria-selected": "false",
1064
+ style: {
1065
+ padding: "16px",
1066
+ textAlign: "center",
1067
+ fontSize: 13,
1068
+ color: "var(--ai-me-text-secondary)"
1069
+ },
1070
+ children: [
1071
+ "Press Enter to ask AI: \u201C",
1072
+ query,
1073
+ "\u201D"
1074
+ ]
1075
+ }
1076
+ ),
1077
+ Array.from(grouped.entries()).map(([category, items]) => (
1078
+ // role="group" with aria-label for the category heading
1079
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { role: "group", "aria-label": category, children: [
1080
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1081
+ "div",
1082
+ {
1083
+ "aria-hidden": "true",
1084
+ style: {
1085
+ padding: "6px 16px 4px",
1086
+ fontSize: 11,
1087
+ fontWeight: 500,
1088
+ textTransform: "uppercase",
1089
+ letterSpacing: "0.06em",
1090
+ color: "var(--ai-me-text-secondary)"
1091
+ },
1092
+ children: category
1093
+ }
1094
+ ),
1095
+ items.map((cmd) => {
1096
+ const idx = flatIndex++;
1097
+ const isSelected = idx === selectedIndex;
1098
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1099
+ "div",
1100
+ {
1101
+ id: `cmd-${cmd.id}`,
1102
+ role: "option",
1103
+ "aria-selected": isSelected,
1104
+ onClick: () => executeCommand(cmd),
1105
+ onMouseEnter: () => setSelectedIndex(idx),
1106
+ tabIndex: isSelected ? 0 : -1,
1107
+ onKeyDown: (e) => {
1108
+ if (e.key === "Enter" || e.key === " ") {
1109
+ e.preventDefault();
1110
+ executeCommand(cmd);
1111
+ }
1112
+ },
1113
+ style: {
1114
+ padding: "8px 16px",
1115
+ cursor: "pointer",
1116
+ display: "flex",
1117
+ alignItems: "center",
1118
+ gap: 10,
1119
+ backgroundColor: isSelected ? "var(--ai-me-bg-secondary)" : "transparent",
1120
+ outline: "2px solid transparent",
1121
+ outlineOffset: -2
1122
+ },
1123
+ onFocus: (e) => {
1124
+ e.currentTarget.style.outline = "2px solid var(--ai-me-primary)";
1125
+ },
1126
+ onBlur: (e) => {
1127
+ e.currentTarget.style.outline = "2px solid transparent";
1128
+ },
1129
+ children: [
1130
+ cmd.icon && // Icon is decorative — label comes from cmd.label
1131
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", style: { fontSize: 16, flexShrink: 0 }, children: cmd.icon }),
1132
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
1133
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 14, fontWeight: 500 }, children: cmd.label }),
1134
+ cmd.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1135
+ "div",
1136
+ {
1137
+ style: {
1138
+ fontSize: 12,
1139
+ color: "var(--ai-me-text-secondary)",
1140
+ whiteSpace: "nowrap",
1141
+ overflow: "hidden",
1142
+ textOverflow: "ellipsis"
1143
+ },
1144
+ children: cmd.description
1145
+ }
1146
+ )
1147
+ ] })
1148
+ ]
1149
+ },
1150
+ cmd.id
1151
+ );
1152
+ })
1153
+ ] }, category)
1154
+ ))
1155
+ ]
1156
+ }
1157
+ ),
1158
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1159
+ "div",
1160
+ {
1161
+ "aria-hidden": "true",
1162
+ style: {
1163
+ padding: "8px 16px",
1164
+ borderTop: "1px solid var(--ai-me-border)",
1165
+ fontSize: 11,
1166
+ color: "var(--ai-me-text-secondary)",
1167
+ display: "flex",
1168
+ gap: 16
1169
+ },
1170
+ children: [
1171
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u2191\u2193 Navigate" }),
1172
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u21B5 Select" }),
1173
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Esc Close" })
1174
+ ]
1175
+ }
1176
+ )
1177
+ ]
1178
+ }
1179
+ )
1180
+ ] });
1181
+ }
1182
+
1183
+ // src/confirm.tsx
1184
+ var import_react6 = require("react");
1185
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1186
+ function AIMeConfirm({
1187
+ action,
1188
+ description,
1189
+ parameters,
1190
+ onConfirm,
1191
+ onReject
1192
+ }) {
1193
+ const dialogRef = (0, import_react6.useRef)(null);
1194
+ const cancelButtonRef = (0, import_react6.useRef)(null);
1195
+ const titleId = (0, import_react6.useId)();
1196
+ const descriptionId = (0, import_react6.useId)();
1197
+ const handleKeyDown = (0, import_react6.useCallback)(
1198
+ (e) => {
1199
+ if (e.key === "Escape") {
1200
+ e.preventDefault();
1201
+ onReject();
1202
+ return;
1203
+ }
1204
+ if (e.key !== "Tab") return;
1205
+ const dialog = dialogRef.current;
1206
+ if (!dialog) return;
1207
+ const focusable = dialog.querySelectorAll(
1208
+ 'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
1209
+ );
1210
+ if (focusable.length === 0) return;
1211
+ const first = focusable[0];
1212
+ const last = focusable[focusable.length - 1];
1213
+ if (e.shiftKey) {
1214
+ if (document.activeElement === first) {
1215
+ e.preventDefault();
1216
+ last.focus();
1217
+ }
1218
+ } else {
1219
+ if (document.activeElement === last) {
1220
+ e.preventDefault();
1221
+ first.focus();
1222
+ }
1223
+ }
1224
+ },
1225
+ [onReject]
1226
+ );
1227
+ (0, import_react6.useEffect)(() => {
1228
+ const previousFocus = document.activeElement;
1229
+ cancelButtonRef.current?.focus();
1230
+ window.addEventListener("keydown", handleKeyDown);
1231
+ return () => {
1232
+ window.removeEventListener("keydown", handleKeyDown);
1233
+ previousFocus?.focus();
1234
+ };
1235
+ }, [handleKeyDown]);
1236
+ const overlayStyle = {
1237
+ ...defaultThemeVars,
1238
+ position: "fixed",
1239
+ inset: 0,
1240
+ backgroundColor: "rgba(0, 0, 0, 0.4)",
1241
+ display: "flex",
1242
+ alignItems: "center",
1243
+ justifyContent: "center",
1244
+ zIndex: 1e4,
1245
+ fontFamily: "var(--ai-me-font)"
1246
+ };
1247
+ const dialogStyle = {
1248
+ backgroundColor: "var(--ai-me-bg)",
1249
+ borderRadius: "var(--ai-me-radius)",
1250
+ padding: 24,
1251
+ maxWidth: 420,
1252
+ width: "90%",
1253
+ boxShadow: "var(--ai-me-shadow)",
1254
+ color: "var(--ai-me-text)"
1255
+ };
1256
+ const focusStyle = {
1257
+ outline: "2px solid transparent",
1258
+ outlineOffset: 2
1259
+ };
1260
+ function applyFocusRing(el) {
1261
+ el.style.outline = "2px solid var(--ai-me-primary)";
1262
+ el.style.outlineOffset = "2px";
1263
+ }
1264
+ function removeFocusRing(el) {
1265
+ el.style.outline = "2px solid transparent";
1266
+ el.style.outlineOffset = "2px";
1267
+ }
1268
+ return (
1269
+ // Overlay is presentational — role and aria go on the inner dialog
1270
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1271
+ "div",
1272
+ {
1273
+ style: overlayStyle,
1274
+ onClick: (e) => {
1275
+ if (e.target === e.currentTarget) onReject();
1276
+ },
1277
+ "aria-hidden": "false",
1278
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1279
+ "div",
1280
+ {
1281
+ ref: dialogRef,
1282
+ style: dialogStyle,
1283
+ role: "alertdialog",
1284
+ "aria-modal": "true",
1285
+ "aria-labelledby": titleId,
1286
+ "aria-describedby": descriptionId,
1287
+ tabIndex: -1,
1288
+ onClick: (e) => e.stopPropagation(),
1289
+ children: [
1290
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { id: titleId, style: { margin: "0 0 8px", fontSize: 16 }, children: "Confirm Action" }),
1291
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: { margin: "0 0 4px", fontSize: 14, fontWeight: 600 }, children: action }),
1292
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1293
+ "p",
1294
+ {
1295
+ id: descriptionId,
1296
+ style: {
1297
+ margin: "0 0 16px",
1298
+ fontSize: 13,
1299
+ color: "var(--ai-me-text-secondary)"
1300
+ },
1301
+ children: description
1302
+ }
1303
+ ),
1304
+ parameters && Object.keys(parameters).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1305
+ "pre",
1306
+ {
1307
+ style: {
1308
+ margin: "0 0 16px",
1309
+ padding: 12,
1310
+ backgroundColor: "var(--ai-me-bg-secondary)",
1311
+ borderRadius: 8,
1312
+ fontSize: 12,
1313
+ overflow: "auto",
1314
+ maxHeight: 200,
1315
+ border: "1px solid var(--ai-me-border)"
1316
+ },
1317
+ children: JSON.stringify(parameters, null, 2)
1318
+ }
1319
+ ),
1320
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
1321
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1322
+ "button",
1323
+ {
1324
+ ref: cancelButtonRef,
1325
+ type: "button",
1326
+ onClick: onReject,
1327
+ style: {
1328
+ padding: "8px 16px",
1329
+ border: "1px solid var(--ai-me-border)",
1330
+ borderRadius: 8,
1331
+ backgroundColor: "var(--ai-me-bg)",
1332
+ color: "var(--ai-me-text)",
1333
+ cursor: "pointer",
1334
+ fontSize: 14,
1335
+ ...focusStyle
1336
+ },
1337
+ onFocus: (e) => applyFocusRing(e.currentTarget),
1338
+ onBlur: (e) => removeFocusRing(e.currentTarget),
1339
+ children: "Cancel"
1340
+ }
1341
+ ),
1342
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1343
+ "button",
1344
+ {
1345
+ type: "button",
1346
+ onClick: onConfirm,
1347
+ style: {
1348
+ padding: "8px 16px",
1349
+ border: "none",
1350
+ borderRadius: 8,
1351
+ // #fff on var(--ai-me-primary) = #6366f1 → contrast ≈ 4.6:1 (passes AA)
1352
+ backgroundColor: "var(--ai-me-primary)",
1353
+ color: "#fff",
1354
+ cursor: "pointer",
1355
+ fontSize: 14,
1356
+ ...focusStyle
1357
+ },
1358
+ onFocus: (e) => applyFocusRing(e.currentTarget),
1359
+ onBlur: (e) => removeFocusRing(e.currentTarget),
1360
+ children: "Confirm"
1361
+ }
1362
+ )
1363
+ ] })
1364
+ ]
1365
+ }
1366
+ )
1367
+ }
1368
+ )
1369
+ );
1370
+ }
1371
+ // Annotate the CommonJS export names for ESM import in node:
1372
+ 0 && (module.exports = {
1373
+ AIMeChat,
1374
+ AIMeCommandPalette,
1375
+ AIMeConfirm,
1376
+ AIMeProvider,
1377
+ renderMarkdown,
1378
+ useAIMe,
1379
+ useAIMeContext
1380
+ });
1381
+ //# sourceMappingURL=index.cjs.map