@open-mercato/ui 0.5.1-develop.3036.f02c281f23 → 0.5.1-develop.3045.b4b3320cc2
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +2 -1
- package/__integration__/TC-AI-UI-003-aichat-registry.spec.tsx +204 -0
- package/dist/ai/AiAssistantLauncher.js +596 -0
- package/dist/ai/AiAssistantLauncher.js.map +7 -0
- package/dist/ai/AiChat.js +1092 -0
- package/dist/ai/AiChat.js.map +7 -0
- package/dist/ai/AiChatSessions.js +297 -0
- package/dist/ai/AiChatSessions.js.map +7 -0
- package/dist/ai/AiDock.js +347 -0
- package/dist/ai/AiDock.js.map +7 -0
- package/dist/ai/AiMessageContent.js +369 -0
- package/dist/ai/AiMessageContent.js.map +7 -0
- package/dist/ai/ChatPaneTabs.js +251 -0
- package/dist/ai/ChatPaneTabs.js.map +7 -0
- package/dist/ai/index.js +115 -0
- package/dist/ai/index.js.map +7 -0
- package/dist/ai/parts/ConfirmationCard.js +211 -0
- package/dist/ai/parts/ConfirmationCard.js.map +7 -0
- package/dist/ai/parts/FieldDiffCard.js +119 -0
- package/dist/ai/parts/FieldDiffCard.js.map +7 -0
- package/dist/ai/parts/MutationPreviewCard.js +224 -0
- package/dist/ai/parts/MutationPreviewCard.js.map +7 -0
- package/dist/ai/parts/MutationResultCard.js +240 -0
- package/dist/ai/parts/MutationResultCard.js.map +7 -0
- package/dist/ai/parts/approval-cards-map.js +15 -0
- package/dist/ai/parts/approval-cards-map.js.map +7 -0
- package/dist/ai/parts/index.js +24 -0
- package/dist/ai/parts/index.js.map +7 -0
- package/dist/ai/parts/pending-action-api.js +60 -0
- package/dist/ai/parts/pending-action-api.js.map +7 -0
- package/dist/ai/parts/types.js +1 -0
- package/dist/ai/parts/types.js.map +7 -0
- package/dist/ai/parts/useAiPendingActionPolling.js +126 -0
- package/dist/ai/parts/useAiPendingActionPolling.js.map +7 -0
- package/dist/ai/records/ActivityCard.js +83 -0
- package/dist/ai/records/ActivityCard.js.map +7 -0
- package/dist/ai/records/CompanyCard.js +81 -0
- package/dist/ai/records/CompanyCard.js.map +7 -0
- package/dist/ai/records/DealCard.js +76 -0
- package/dist/ai/records/DealCard.js.map +7 -0
- package/dist/ai/records/PersonCard.js +68 -0
- package/dist/ai/records/PersonCard.js.map +7 -0
- package/dist/ai/records/ProductCard.js +68 -0
- package/dist/ai/records/ProductCard.js.map +7 -0
- package/dist/ai/records/RecordCard.js +29 -0
- package/dist/ai/records/RecordCard.js.map +7 -0
- package/dist/ai/records/RecordCardShell.js +103 -0
- package/dist/ai/records/RecordCardShell.js.map +7 -0
- package/dist/ai/records/index.js +31 -0
- package/dist/ai/records/index.js.map +7 -0
- package/dist/ai/records/registry.js +51 -0
- package/dist/ai/records/registry.js.map +7 -0
- package/dist/ai/records/types.js +1 -0
- package/dist/ai/records/types.js.map +7 -0
- package/dist/ai/ui-part-registry.js +112 -0
- package/dist/ai/ui-part-registry.js.map +7 -0
- package/dist/ai/ui-part-slots.js +14 -0
- package/dist/ai/ui-part-slots.js.map +7 -0
- package/dist/ai/ui-parts/pending-phase3-placeholder.js +35 -0
- package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +7 -0
- package/dist/ai/upload-adapter.js +256 -0
- package/dist/ai/upload-adapter.js.map +7 -0
- package/dist/ai/useAiChat.js +549 -0
- package/dist/ai/useAiChat.js.map +7 -0
- package/dist/ai/useAiChatUpload.js +127 -0
- package/dist/ai/useAiChatUpload.js.map +7 -0
- package/dist/ai/useAiShortcuts.js +43 -0
- package/dist/ai/useAiShortcuts.js.map +7 -0
- package/dist/backend/AppShell.js +8 -4
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/BackendChromeProvider.js +2 -0
- package/dist/backend/BackendChromeProvider.js.map +2 -2
- package/dist/backend/DataTable.js +19 -2
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/FilterBar.js +19 -15
- package/dist/backend/FilterBar.js.map +2 -2
- package/dist/backend/dashboard/DashboardScreen.js +31 -3
- package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
- package/dist/backend/injection/spotIds.js +6 -0
- package/dist/backend/injection/spotIds.js.map +2 -2
- package/dist/backend/notifications/useNotificationEffect.js +38 -2
- package/dist/backend/notifications/useNotificationEffect.js.map +2 -2
- package/dist/index.js +1 -0
- package/dist/index.js.map +2 -2
- package/jest.config.cjs +7 -1
- package/jest.markdown-mock.tsx +7 -0
- package/package.json +10 -4
- package/src/ai/AiAssistantLauncher.tsx +805 -0
- package/src/ai/AiChat.tsx +1483 -0
- package/src/ai/AiChatSessions.tsx +429 -0
- package/src/ai/AiDock.tsx +505 -0
- package/src/ai/AiMessageContent.tsx +515 -0
- package/src/ai/ChatPaneTabs.tsx +310 -0
- package/src/ai/__tests__/AiChat.conversation.test.tsx +160 -0
- package/src/ai/__tests__/AiChat.debug.test.tsx +152 -0
- package/src/ai/__tests__/AiChat.registry.test.tsx +213 -0
- package/src/ai/__tests__/AiChat.test.tsx +257 -0
- package/src/ai/__tests__/AiDock.test.tsx +124 -0
- package/src/ai/__tests__/AiMessageContent.test.ts +111 -0
- package/src/ai/__tests__/ui-part-registry.test.ts +199 -0
- package/src/ai/__tests__/ui-part-slots.test.ts +43 -0
- package/src/ai/__tests__/upload-adapter.test.ts +213 -0
- package/src/ai/__tests__/useAiChatUpload.test.tsx +163 -0
- package/src/ai/__tests__/useAiShortcuts.test.tsx +100 -0
- package/src/ai/index.ts +125 -0
- package/src/ai/parts/ConfirmationCard.tsx +310 -0
- package/src/ai/parts/FieldDiffCard.tsx +173 -0
- package/src/ai/parts/MutationPreviewCard.tsx +302 -0
- package/src/ai/parts/MutationResultCard.tsx +360 -0
- package/src/ai/parts/__tests__/ConfirmationCard.test.tsx +169 -0
- package/src/ai/parts/__tests__/FieldDiffCard.test.tsx +74 -0
- package/src/ai/parts/__tests__/MutationPreviewCard.test.tsx +177 -0
- package/src/ai/parts/__tests__/MutationResultCard.test.tsx +127 -0
- package/src/ai/parts/__tests__/useAiPendingActionPolling.test.tsx +151 -0
- package/src/ai/parts/approval-cards-map.ts +24 -0
- package/src/ai/parts/index.ts +27 -0
- package/src/ai/parts/pending-action-api.ts +123 -0
- package/src/ai/parts/types.ts +84 -0
- package/src/ai/parts/useAiPendingActionPolling.ts +210 -0
- package/src/ai/records/ActivityCard.tsx +102 -0
- package/src/ai/records/CompanyCard.tsx +89 -0
- package/src/ai/records/DealCard.tsx +85 -0
- package/src/ai/records/PersonCard.tsx +77 -0
- package/src/ai/records/ProductCard.tsx +83 -0
- package/src/ai/records/RecordCard.tsx +37 -0
- package/src/ai/records/RecordCardShell.tsx +169 -0
- package/src/ai/records/index.ts +30 -0
- package/src/ai/records/registry.tsx +80 -0
- package/src/ai/records/types.ts +90 -0
- package/src/ai/ui-part-registry.ts +233 -0
- package/src/ai/ui-part-slots.ts +32 -0
- package/src/ai/ui-parts/pending-phase3-placeholder.tsx +50 -0
- package/src/ai/upload-adapter.ts +421 -0
- package/src/ai/useAiChat.ts +865 -0
- package/src/ai/useAiChatUpload.ts +180 -0
- package/src/ai/useAiShortcuts.ts +79 -0
- package/src/backend/AppShell.tsx +12 -5
- package/src/backend/BackendChromeProvider.tsx +2 -0
- package/src/backend/DataTable.tsx +20 -1
- package/src/backend/FilterBar.tsx +26 -13
- package/src/backend/__tests__/BackendChromeProvider.test.tsx +45 -0
- package/src/backend/dashboard/DashboardScreen.tsx +38 -3
- package/src/backend/dashboard/__tests__/DashboardScreen.test.tsx +24 -1
- package/src/backend/injection/spotIds.ts +6 -0
- package/src/backend/notifications/__tests__/useNotificationEffect.test.tsx +77 -0
- package/src/backend/notifications/useNotificationEffect.ts +47 -2
- package/src/index.ts +1 -0
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
AlertTriangle,
|
|
6
|
+
Bot,
|
|
7
|
+
ExternalLink,
|
|
8
|
+
HelpCircle,
|
|
9
|
+
Lightbulb,
|
|
10
|
+
Loader2,
|
|
11
|
+
PanelRightOpen,
|
|
12
|
+
Search,
|
|
13
|
+
Sparkles
|
|
14
|
+
} from "lucide-react";
|
|
15
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
16
|
+
import { cn } from "@open-mercato/shared/lib/utils";
|
|
17
|
+
import { apiCall } from "../backend/utils/apiCall.js";
|
|
18
|
+
import {
|
|
19
|
+
Dialog,
|
|
20
|
+
DialogContent,
|
|
21
|
+
DialogDescription,
|
|
22
|
+
DialogHeader,
|
|
23
|
+
DialogTitle
|
|
24
|
+
} from "../primitives/dialog.js";
|
|
25
|
+
import { Button } from "../primitives/button.js";
|
|
26
|
+
import { IconButton } from "../primitives/icon-button.js";
|
|
27
|
+
import { Kbd, KbdShortcut } from "../primitives/kbd.js";
|
|
28
|
+
import { useAiDock } from "./AiDock.js";
|
|
29
|
+
import { useAiChatSessions } from "./AiChatSessions.js";
|
|
30
|
+
import { ChatPaneTabs } from "./ChatPaneTabs.js";
|
|
31
|
+
const LazyAiChat = React.lazy(async () => {
|
|
32
|
+
const mod = await import("./AiChat.js");
|
|
33
|
+
return { default: mod.AiChat };
|
|
34
|
+
});
|
|
35
|
+
const DEFAULT_AGENTS_ENDPOINT = "/api/ai_assistant/ai/agents";
|
|
36
|
+
const DEFAULT_HEALTH_ENDPOINT = "/api/ai_assistant/health";
|
|
37
|
+
const AI_ASSISTANT_DOCS_URL = "https://docs.openmercato.com/framework/ai-assistant/overview";
|
|
38
|
+
const AI_ASSISTANT_SETTINGS_DOCS_URL = "https://docs.openmercato.com/framework/ai-assistant/settings";
|
|
39
|
+
function isMutationCapable(policy) {
|
|
40
|
+
return policy === "confirm-required" || policy === "destructive-confirm-required";
|
|
41
|
+
}
|
|
42
|
+
function normalizeAgents(payload) {
|
|
43
|
+
if (!payload || !Array.isArray(payload.agents)) return [];
|
|
44
|
+
const result = [];
|
|
45
|
+
for (const raw of payload.agents) {
|
|
46
|
+
if (!raw || typeof raw.id !== "string" || raw.id.length === 0) continue;
|
|
47
|
+
if (typeof raw.label !== "string" || raw.label.length === 0) continue;
|
|
48
|
+
result.push({
|
|
49
|
+
id: raw.id,
|
|
50
|
+
label: raw.label,
|
|
51
|
+
description: typeof raw.description === "string" ? raw.description : null,
|
|
52
|
+
moduleId: typeof raw.moduleId === "string" ? raw.moduleId : null,
|
|
53
|
+
mutationPolicy: typeof raw.mutationPolicy === "string" ? raw.mutationPolicy : null,
|
|
54
|
+
keywords: Array.isArray(raw.keywords) ? raw.keywords.filter((value) => typeof value === "string" && value.length > 0) : [],
|
|
55
|
+
suggestions: Array.isArray(raw.suggestions) ? raw.suggestions.map((suggestion) => {
|
|
56
|
+
if (!suggestion) return null;
|
|
57
|
+
if (typeof suggestion.label !== "string" || typeof suggestion.prompt !== "string") {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (!suggestion.label || !suggestion.prompt) return null;
|
|
61
|
+
return { label: suggestion.label, prompt: suggestion.prompt };
|
|
62
|
+
}).filter((suggestion) => suggestion !== null) : []
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
function matchesQuery(agent, query) {
|
|
68
|
+
if (!query) return true;
|
|
69
|
+
const needle = query.trim().toLowerCase();
|
|
70
|
+
if (!needle) return true;
|
|
71
|
+
if (agent.label.toLowerCase().includes(needle)) return true;
|
|
72
|
+
if (agent.id.toLowerCase().includes(needle)) return true;
|
|
73
|
+
if (agent.description && agent.description.toLowerCase().includes(needle)) return true;
|
|
74
|
+
if (agent.moduleId && agent.moduleId.toLowerCase().includes(needle)) return true;
|
|
75
|
+
if (agent.keywords && agent.keywords.some((keyword) => keyword.toLowerCase().includes(needle))) return true;
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
function isTextEntryTarget(target) {
|
|
79
|
+
if (!target || !(target instanceof HTMLElement)) return false;
|
|
80
|
+
const tag = target.tagName;
|
|
81
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
82
|
+
if (target.isContentEditable) return true;
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
function AiAssistantLauncher({
|
|
86
|
+
variant: _variant = "topbar",
|
|
87
|
+
agentsEndpoint = DEFAULT_AGENTS_ENDPOINT,
|
|
88
|
+
healthEndpoint = DEFAULT_HEALTH_ENDPOINT,
|
|
89
|
+
skipHealthCheck = false,
|
|
90
|
+
disableGlobalShortcut = false,
|
|
91
|
+
className
|
|
92
|
+
}) {
|
|
93
|
+
const t = useT();
|
|
94
|
+
const dock = useAiDock();
|
|
95
|
+
const [healthy, setHealthy] = React.useState(skipHealthCheck ? true : null);
|
|
96
|
+
const [agents, setAgents] = React.useState([]);
|
|
97
|
+
const [agentsLoaded, setAgentsLoaded] = React.useState(false);
|
|
98
|
+
const [agentsError, setAgentsError] = React.useState(null);
|
|
99
|
+
const [aiConfigured, setAiConfigured] = React.useState(null);
|
|
100
|
+
const [pickerOpen, setPickerOpen] = React.useState(false);
|
|
101
|
+
const [activeAgent, setActiveAgent] = React.useState(null);
|
|
102
|
+
const [chatOpen, setChatOpen] = React.useState(false);
|
|
103
|
+
const [query, setQuery] = React.useState("");
|
|
104
|
+
const [highlight, setHighlight] = React.useState(0);
|
|
105
|
+
React.useEffect(() => {
|
|
106
|
+
if (skipHealthCheck) return;
|
|
107
|
+
let cancelled = false;
|
|
108
|
+
apiCall(healthEndpoint, {
|
|
109
|
+
credentials: "same-origin",
|
|
110
|
+
headers: { "x-om-forbidden-redirect": "0", "x-om-unauthorized-redirect": "0" }
|
|
111
|
+
}).then((call) => {
|
|
112
|
+
if (cancelled) return;
|
|
113
|
+
if (!call.ok) {
|
|
114
|
+
setHealthy(true);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const body = call.result;
|
|
118
|
+
if (body && typeof body === "object" && body.healthy === false) {
|
|
119
|
+
setHealthy(false);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
setHealthy(true);
|
|
123
|
+
}).catch(() => {
|
|
124
|
+
if (cancelled) return;
|
|
125
|
+
setHealthy(true);
|
|
126
|
+
});
|
|
127
|
+
return () => {
|
|
128
|
+
cancelled = true;
|
|
129
|
+
};
|
|
130
|
+
}, [healthEndpoint, skipHealthCheck]);
|
|
131
|
+
React.useEffect(() => {
|
|
132
|
+
if (agentsLoaded) return;
|
|
133
|
+
let cancelled = false;
|
|
134
|
+
apiCall(agentsEndpoint, {
|
|
135
|
+
credentials: "same-origin",
|
|
136
|
+
headers: { "x-om-forbidden-redirect": "0", "x-om-unauthorized-redirect": "0" }
|
|
137
|
+
}).then((call) => {
|
|
138
|
+
if (cancelled) return;
|
|
139
|
+
if (!call.ok) {
|
|
140
|
+
setAgents([]);
|
|
141
|
+
setAgentsError(`agents endpoint returned ${call.status}`);
|
|
142
|
+
setAgentsLoaded(true);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (call.result) {
|
|
146
|
+
setAgents(normalizeAgents(call.result));
|
|
147
|
+
setAgentsError(null);
|
|
148
|
+
if (typeof call.result.aiConfigured === "boolean") {
|
|
149
|
+
setAiConfigured(call.result.aiConfigured);
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
setAgents([]);
|
|
153
|
+
setAgentsError("Empty agents response");
|
|
154
|
+
}
|
|
155
|
+
setAgentsLoaded(true);
|
|
156
|
+
}).catch((error) => {
|
|
157
|
+
if (cancelled) return;
|
|
158
|
+
setAgents([]);
|
|
159
|
+
setAgentsError(error instanceof Error ? error.message : String(error));
|
|
160
|
+
setAgentsLoaded(true);
|
|
161
|
+
});
|
|
162
|
+
return () => {
|
|
163
|
+
cancelled = true;
|
|
164
|
+
};
|
|
165
|
+
}, [agentsEndpoint, agentsLoaded]);
|
|
166
|
+
const filteredAgents = React.useMemo(
|
|
167
|
+
() => agents.filter((agent) => matchesQuery(agent, query)),
|
|
168
|
+
[agents, query]
|
|
169
|
+
);
|
|
170
|
+
React.useEffect(() => {
|
|
171
|
+
if (filteredAgents.length === 0) {
|
|
172
|
+
if (highlight !== 0) setHighlight(0);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (highlight >= filteredAgents.length) setHighlight(0);
|
|
176
|
+
}, [filteredAgents, highlight]);
|
|
177
|
+
const openPicker = React.useCallback(() => {
|
|
178
|
+
setQuery("");
|
|
179
|
+
setHighlight(0);
|
|
180
|
+
setPickerOpen(true);
|
|
181
|
+
}, []);
|
|
182
|
+
const handleSelectAgent = React.useCallback((agent) => {
|
|
183
|
+
if (dock.state.assistant?.agent === agent.id) {
|
|
184
|
+
dock.dock(dock.state.assistant);
|
|
185
|
+
setPickerOpen(false);
|
|
186
|
+
setChatOpen(false);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
setActiveAgent(agent);
|
|
190
|
+
setPickerOpen(false);
|
|
191
|
+
setChatOpen(true);
|
|
192
|
+
}, [dock]);
|
|
193
|
+
React.useEffect(() => {
|
|
194
|
+
if (disableGlobalShortcut) return;
|
|
195
|
+
if (typeof window === "undefined") return;
|
|
196
|
+
if (healthy !== true) return;
|
|
197
|
+
if (!agentsLoaded || agents.length === 0) return;
|
|
198
|
+
const listener = (event) => {
|
|
199
|
+
const isModifier = event.metaKey || event.ctrlKey;
|
|
200
|
+
if (!isModifier) return;
|
|
201
|
+
if (event.shiftKey || event.altKey) return;
|
|
202
|
+
if (event.key !== "l" && event.key !== "L") return;
|
|
203
|
+
if (isTextEntryTarget(event.target)) return;
|
|
204
|
+
event.preventDefault();
|
|
205
|
+
openPicker();
|
|
206
|
+
};
|
|
207
|
+
window.addEventListener("keydown", listener);
|
|
208
|
+
return () => window.removeEventListener("keydown", listener);
|
|
209
|
+
}, [agents.length, agentsLoaded, disableGlobalShortcut, healthy, openPicker]);
|
|
210
|
+
const handlePickerKeyDown = React.useCallback(
|
|
211
|
+
(event) => {
|
|
212
|
+
if (aiConfigured === false || filteredAgents.length === 0) {
|
|
213
|
+
if (event.key === "Escape") {
|
|
214
|
+
setPickerOpen(false);
|
|
215
|
+
}
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (event.key === "ArrowDown") {
|
|
219
|
+
event.preventDefault();
|
|
220
|
+
setHighlight((current) => (current + 1) % filteredAgents.length);
|
|
221
|
+
} else if (event.key === "ArrowUp") {
|
|
222
|
+
event.preventDefault();
|
|
223
|
+
setHighlight((current) => (current - 1 + filteredAgents.length) % filteredAgents.length);
|
|
224
|
+
} else if (event.key === "Enter") {
|
|
225
|
+
event.preventDefault();
|
|
226
|
+
const target = filteredAgents[highlight] ?? filteredAgents[0];
|
|
227
|
+
if (target) handleSelectAgent(target);
|
|
228
|
+
} else if (event.key === "Escape") {
|
|
229
|
+
setPickerOpen(false);
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
[aiConfigured, filteredAgents, handleSelectAgent, highlight]
|
|
233
|
+
);
|
|
234
|
+
const triggerLabel = t("ai_assistant.launcher.triggerAriaLabel", "Open AI assistant");
|
|
235
|
+
const dialogTitle = t("ai_assistant.launcher.dialogTitle", "AI assistants");
|
|
236
|
+
const dialogDescription = t(
|
|
237
|
+
"ai_assistant.launcher.dialogDescription",
|
|
238
|
+
"Pick an assistant. Use \u2191/\u2193 to navigate, Enter to launch, Esc to close."
|
|
239
|
+
);
|
|
240
|
+
const placeholder = t("ai_assistant.launcher.searchPlaceholder", "Search assistants...");
|
|
241
|
+
const emptyText = t("ai_assistant.launcher.empty", "No assistants match your search.");
|
|
242
|
+
const noneText = t(
|
|
243
|
+
"ai_assistant.launcher.none",
|
|
244
|
+
"No assistants are available for your account."
|
|
245
|
+
);
|
|
246
|
+
const writesBadge = t("ai_assistant.launcher.writesBadge", "Can write");
|
|
247
|
+
const launcherSuggestions = React.useMemo(
|
|
248
|
+
() => {
|
|
249
|
+
const generic = [
|
|
250
|
+
{
|
|
251
|
+
label: t("ai_assistant.launcher.welcome.suggestion1", "What can you help me with?"),
|
|
252
|
+
prompt: "What can you help me with on this tenant?",
|
|
253
|
+
icon: /* @__PURE__ */ jsx(Sparkles, { className: "size-4" })
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
label: t("ai_assistant.launcher.welcome.suggestion2", "Show what data you can access"),
|
|
257
|
+
prompt: "Describe the data you can read for this tenant \u2014 entities, fields, and limits.",
|
|
258
|
+
icon: /* @__PURE__ */ jsx(Bot, { className: "size-4" })
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
label: t("ai_assistant.launcher.welcome.suggestion3", "Suggest things to try"),
|
|
262
|
+
prompt: "Suggest five concrete questions I could ask you that would surface useful insights for this tenant.",
|
|
263
|
+
icon: /* @__PURE__ */ jsx(Lightbulb, { className: "size-4" })
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
label: t("ai_assistant.launcher.welcome.suggestion4", "How do I use this assistant?"),
|
|
267
|
+
prompt: "Walk me through how to use this assistant: when to ask, what tools you call, and how confirmations work.",
|
|
268
|
+
icon: /* @__PURE__ */ jsx(HelpCircle, { className: "size-4" })
|
|
269
|
+
}
|
|
270
|
+
];
|
|
271
|
+
return [...activeAgent?.suggestions ?? [], ...generic];
|
|
272
|
+
},
|
|
273
|
+
[t, activeAgent]
|
|
274
|
+
);
|
|
275
|
+
const launcherContextItems = React.useMemo(() => [], []);
|
|
276
|
+
const shouldRender = healthy !== false && agentsLoaded && agents.length > 0;
|
|
277
|
+
if (!shouldRender) return null;
|
|
278
|
+
const shortLabel = t("ai_assistant.launcher.triggerLabel", "AI");
|
|
279
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
280
|
+
/* @__PURE__ */ jsxs(
|
|
281
|
+
Button,
|
|
282
|
+
{
|
|
283
|
+
type: "button",
|
|
284
|
+
variant: "ghost",
|
|
285
|
+
size: "sm",
|
|
286
|
+
onClick: openPicker,
|
|
287
|
+
className: cn("hidden sm:inline-flex items-center gap-2", className),
|
|
288
|
+
"data-ai-launcher-trigger": "",
|
|
289
|
+
"aria-label": triggerLabel,
|
|
290
|
+
title: triggerLabel,
|
|
291
|
+
children: [
|
|
292
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "size-4", "aria-hidden": true }),
|
|
293
|
+
/* @__PURE__ */ jsx("span", { children: shortLabel }),
|
|
294
|
+
/* @__PURE__ */ jsx("span", { className: "ml-2 rounded border px-1 text-xs text-muted-foreground", children: "\u2318L" })
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
),
|
|
298
|
+
/* @__PURE__ */ jsx(
|
|
299
|
+
IconButton,
|
|
300
|
+
{
|
|
301
|
+
type: "button",
|
|
302
|
+
variant: "ghost",
|
|
303
|
+
size: "sm",
|
|
304
|
+
className: "sm:hidden",
|
|
305
|
+
onClick: openPicker,
|
|
306
|
+
"aria-label": triggerLabel,
|
|
307
|
+
"data-ai-launcher-trigger-mobile": "",
|
|
308
|
+
children: /* @__PURE__ */ jsx(Sparkles, { className: "size-4", "aria-hidden": true })
|
|
309
|
+
}
|
|
310
|
+
),
|
|
311
|
+
/* @__PURE__ */ jsx(Dialog, { open: pickerOpen, onOpenChange: setPickerOpen, children: /* @__PURE__ */ jsxs(
|
|
312
|
+
DialogContent,
|
|
313
|
+
{
|
|
314
|
+
className: "sm:max-w-lg p-0 gap-0 overflow-hidden",
|
|
315
|
+
"data-ai-launcher-picker": "",
|
|
316
|
+
onKeyDown: handlePickerKeyDown,
|
|
317
|
+
children: [
|
|
318
|
+
/* @__PURE__ */ jsxs(DialogHeader, { className: "px-4 pt-4 pb-2", children: [
|
|
319
|
+
/* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2 text-base", children: [
|
|
320
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "size-4 text-primary", "aria-hidden": true }),
|
|
321
|
+
dialogTitle
|
|
322
|
+
] }),
|
|
323
|
+
/* @__PURE__ */ jsx(DialogDescription, { className: "text-xs", children: dialogDescription })
|
|
324
|
+
] }),
|
|
325
|
+
/* @__PURE__ */ jsx("div", { className: "border-y border-border bg-muted/30 px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
326
|
+
/* @__PURE__ */ jsx(
|
|
327
|
+
Search,
|
|
328
|
+
{
|
|
329
|
+
className: "pointer-events-none absolute left-2 top-1/2 size-4 -translate-y-1/2 text-muted-foreground",
|
|
330
|
+
"aria-hidden": true
|
|
331
|
+
}
|
|
332
|
+
),
|
|
333
|
+
/* @__PURE__ */ jsx(
|
|
334
|
+
"input",
|
|
335
|
+
{
|
|
336
|
+
autoFocus: true,
|
|
337
|
+
type: "text",
|
|
338
|
+
value: query,
|
|
339
|
+
onChange: (event) => {
|
|
340
|
+
setQuery(event.target.value);
|
|
341
|
+
setHighlight(0);
|
|
342
|
+
},
|
|
343
|
+
placeholder,
|
|
344
|
+
className: "w-full rounded-md border border-input bg-background px-8 py-1.5 text-sm outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40",
|
|
345
|
+
"data-ai-launcher-search-input": ""
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
] }) }),
|
|
349
|
+
/* @__PURE__ */ jsx(
|
|
350
|
+
"div",
|
|
351
|
+
{
|
|
352
|
+
className: "max-h-80 overflow-y-auto py-1",
|
|
353
|
+
"data-ai-launcher-list": "",
|
|
354
|
+
role: "listbox",
|
|
355
|
+
"aria-label": dialogTitle,
|
|
356
|
+
children: aiConfigured === false ? /* @__PURE__ */ jsx(AiProviderSetupPanel, { t }) : filteredAgents.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-6 text-center text-xs text-muted-foreground", children: agents.length === 0 ? noneText : emptyText }) : filteredAgents.map((agent, index) => {
|
|
357
|
+
const isActive = index === highlight;
|
|
358
|
+
const writes = isMutationCapable(agent.mutationPolicy);
|
|
359
|
+
return /* @__PURE__ */ jsxs(
|
|
360
|
+
"button",
|
|
361
|
+
{
|
|
362
|
+
type: "button",
|
|
363
|
+
role: "option",
|
|
364
|
+
"aria-selected": isActive,
|
|
365
|
+
onMouseEnter: () => setHighlight(index),
|
|
366
|
+
onClick: () => handleSelectAgent(agent),
|
|
367
|
+
"data-ai-launcher-agent-id": agent.id,
|
|
368
|
+
"data-active": isActive ? "true" : "false",
|
|
369
|
+
className: cn(
|
|
370
|
+
"flex w-full items-start gap-3 px-3 py-2 text-left text-sm transition-colors",
|
|
371
|
+
isActive ? "bg-accent text-accent-foreground" : "hover:bg-accent/60"
|
|
372
|
+
),
|
|
373
|
+
children: [
|
|
374
|
+
/* @__PURE__ */ jsx("span", { className: "mt-0.5 inline-flex size-7 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Sparkles, { className: "size-3.5", "aria-hidden": true }) }),
|
|
375
|
+
/* @__PURE__ */ jsxs("span", { className: "flex-1 min-w-0 space-y-0.5", children: [
|
|
376
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
377
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium leading-tight", children: agent.label }),
|
|
378
|
+
/* @__PURE__ */ jsx(
|
|
379
|
+
"span",
|
|
380
|
+
{
|
|
381
|
+
className: "inline-flex items-center rounded-full border border-border bg-secondary px-1.5 py-0 text-[10px] font-medium uppercase tracking-wide text-secondary-foreground",
|
|
382
|
+
"data-ai-beta-chip": "",
|
|
383
|
+
children: t("ai_assistant.chat.betaChip", "beta")
|
|
384
|
+
}
|
|
385
|
+
),
|
|
386
|
+
writes ? /* @__PURE__ */ jsx(
|
|
387
|
+
"span",
|
|
388
|
+
{
|
|
389
|
+
className: "inline-flex items-center rounded-full border border-border bg-secondary px-1.5 py-0 text-[10px] font-medium text-secondary-foreground",
|
|
390
|
+
"data-ai-launcher-writes": "",
|
|
391
|
+
children: writesBadge
|
|
392
|
+
}
|
|
393
|
+
) : null
|
|
394
|
+
] }),
|
|
395
|
+
agent.description ? /* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-muted-foreground", children: agent.description }) : null,
|
|
396
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate font-mono text-[10px] text-muted-foreground/80", children: agent.id })
|
|
397
|
+
] })
|
|
398
|
+
]
|
|
399
|
+
},
|
|
400
|
+
agent.id
|
|
401
|
+
);
|
|
402
|
+
})
|
|
403
|
+
}
|
|
404
|
+
),
|
|
405
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-t border-border px-3 py-2 text-[11px] text-muted-foreground", children: [
|
|
406
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
407
|
+
/* @__PURE__ */ jsx(KbdShortcut, { keys: ["\u2191", "\u2193"] }),
|
|
408
|
+
" ",
|
|
409
|
+
t("ai_assistant.launcher.hint.navigate", "Navigate"),
|
|
410
|
+
/* @__PURE__ */ jsx("span", { className: "mx-1 text-border", children: "\xB7" }),
|
|
411
|
+
/* @__PURE__ */ jsx(Kbd, { children: "Enter" }),
|
|
412
|
+
" ",
|
|
413
|
+
t("ai_assistant.launcher.hint.launch", "Launch"),
|
|
414
|
+
/* @__PURE__ */ jsx("span", { className: "mx-1 text-border", children: "\xB7" }),
|
|
415
|
+
/* @__PURE__ */ jsx(Kbd, { children: "Esc" }),
|
|
416
|
+
" ",
|
|
417
|
+
t("ai_assistant.launcher.hint.close", "Close")
|
|
418
|
+
] }),
|
|
419
|
+
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline-flex items-center gap-1", children: /* @__PURE__ */ jsx(KbdShortcut, { keys: ["\u2318", "L"] }) })
|
|
420
|
+
] }),
|
|
421
|
+
agentsError ? /* @__PURE__ */ jsxs(
|
|
422
|
+
"div",
|
|
423
|
+
{
|
|
424
|
+
className: "border-t border-status-error-border bg-status-error-bg px-3 py-1.5 text-[11px] text-status-error-foreground",
|
|
425
|
+
"data-ai-launcher-error": "",
|
|
426
|
+
children: [
|
|
427
|
+
/* @__PURE__ */ jsx(Loader2, { className: "mr-1 inline size-3 animate-spin", "aria-hidden": true }),
|
|
428
|
+
agentsError
|
|
429
|
+
]
|
|
430
|
+
}
|
|
431
|
+
) : null
|
|
432
|
+
]
|
|
433
|
+
}
|
|
434
|
+
) }),
|
|
435
|
+
/* @__PURE__ */ jsx(Dialog, { open: chatOpen, onOpenChange: setChatOpen, children: /* @__PURE__ */ jsxs(
|
|
436
|
+
DialogContent,
|
|
437
|
+
{
|
|
438
|
+
className: cn(
|
|
439
|
+
// Mobile: full-screen sheet (matches per-page assistant
|
|
440
|
+
// triggers). Desktop (≥sm): right-anchored side sheet so the
|
|
441
|
+
// chat doesn't appear randomly cropped or off-center.
|
|
442
|
+
// The Dialog primitive applies a centering transform at the
|
|
443
|
+
// sm breakpoint (`sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2
|
|
444
|
+
// sm:-translate-y-1/2 sm:inset-auto`); each piece must be
|
|
445
|
+
// overridden at the same breakpoint or the panel renders half
|
|
446
|
+
// off the viewport on the left.
|
|
447
|
+
"top-0 left-0 right-0 bottom-0 translate-x-0 translate-y-0 max-w-none w-screen h-svh max-h-svh rounded-none",
|
|
448
|
+
"sm:top-0 sm:bottom-0 sm:right-0 sm:left-auto sm:translate-x-0 sm:translate-y-0",
|
|
449
|
+
"sm:max-w-xl sm:w-[36rem] sm:rounded-l-2xl sm:h-screen sm:max-h-screen",
|
|
450
|
+
"flex flex-col gap-3 p-4 z-[70]"
|
|
451
|
+
),
|
|
452
|
+
"data-ai-launcher-sheet": "",
|
|
453
|
+
children: [
|
|
454
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
455
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 pr-8", children: [
|
|
456
|
+
/* @__PURE__ */ jsx(
|
|
457
|
+
IconButton,
|
|
458
|
+
{
|
|
459
|
+
type: "button",
|
|
460
|
+
variant: "ghost",
|
|
461
|
+
size: "sm",
|
|
462
|
+
"aria-label": t("ai_assistant.launcher.sheet.dock", "Dock to side"),
|
|
463
|
+
title: t("ai_assistant.launcher.sheet.dock", "Dock to side"),
|
|
464
|
+
onClick: () => {
|
|
465
|
+
if (!activeAgent) return;
|
|
466
|
+
dock.dock({
|
|
467
|
+
agent: activeAgent.id,
|
|
468
|
+
label: activeAgent.label,
|
|
469
|
+
description: activeAgent.moduleId ?? t("ai_assistant.launcher.dock.subtitle", "AI assistant"),
|
|
470
|
+
pageContext: {},
|
|
471
|
+
placeholder: t(
|
|
472
|
+
"ai_assistant.launcher.composerPlaceholder",
|
|
473
|
+
"Ask anything\u2026"
|
|
474
|
+
),
|
|
475
|
+
suggestions: launcherSuggestions,
|
|
476
|
+
contextItems: launcherContextItems,
|
|
477
|
+
welcomeTitle: activeAgent.label,
|
|
478
|
+
welcomeDescription: activeAgent.description ?? t(
|
|
479
|
+
"ai_assistant.launcher.welcome.fallback",
|
|
480
|
+
"How can I help?"
|
|
481
|
+
)
|
|
482
|
+
});
|
|
483
|
+
setChatOpen(false);
|
|
484
|
+
},
|
|
485
|
+
"data-ai-launcher-dock": "",
|
|
486
|
+
className: "hidden lg:inline-flex shrink-0",
|
|
487
|
+
children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "size-4", "aria-hidden": true })
|
|
488
|
+
}
|
|
489
|
+
),
|
|
490
|
+
/* @__PURE__ */ jsxs(DialogTitle, { className: "flex-1 min-w-0 flex items-center gap-2", children: [
|
|
491
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "size-4 text-primary shrink-0", "aria-hidden": true }),
|
|
492
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: activeAgent?.label ?? dialogTitle }),
|
|
493
|
+
/* @__PURE__ */ jsx(
|
|
494
|
+
"span",
|
|
495
|
+
{
|
|
496
|
+
className: "inline-flex shrink-0 items-center rounded-full border border-border bg-secondary px-1.5 py-0 text-[10px] font-medium uppercase tracking-wide text-secondary-foreground",
|
|
497
|
+
"data-ai-beta-chip": "",
|
|
498
|
+
children: t("ai_assistant.chat.betaChip", "beta")
|
|
499
|
+
}
|
|
500
|
+
)
|
|
501
|
+
] })
|
|
502
|
+
] }),
|
|
503
|
+
activeAgent?.description ? /* @__PURE__ */ jsx(DialogDescription, { children: activeAgent.description }) : null
|
|
504
|
+
] }),
|
|
505
|
+
activeAgent ? /* @__PURE__ */ jsx(
|
|
506
|
+
LauncherChatBody,
|
|
507
|
+
{
|
|
508
|
+
activeAgent,
|
|
509
|
+
suggestions: launcherSuggestions,
|
|
510
|
+
contextItems: launcherContextItems,
|
|
511
|
+
welcomeFallback: t(
|
|
512
|
+
"ai_assistant.launcher.welcome.fallback",
|
|
513
|
+
"How can I help?"
|
|
514
|
+
),
|
|
515
|
+
placeholder: t(
|
|
516
|
+
"ai_assistant.launcher.composerPlaceholder",
|
|
517
|
+
"Ask anything\u2026"
|
|
518
|
+
)
|
|
519
|
+
}
|
|
520
|
+
) : null
|
|
521
|
+
]
|
|
522
|
+
}
|
|
523
|
+
) })
|
|
524
|
+
] });
|
|
525
|
+
}
|
|
526
|
+
function LauncherChatBody({
|
|
527
|
+
activeAgent,
|
|
528
|
+
suggestions,
|
|
529
|
+
contextItems,
|
|
530
|
+
welcomeFallback,
|
|
531
|
+
placeholder
|
|
532
|
+
}) {
|
|
533
|
+
const sessions = useAiChatSessions();
|
|
534
|
+
const session = sessions.getActiveSession(activeAgent.id);
|
|
535
|
+
React.useEffect(() => {
|
|
536
|
+
if (!session) sessions.ensureSession(activeAgent.id);
|
|
537
|
+
}, [activeAgent.id, session, sessions]);
|
|
538
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
539
|
+
/* @__PURE__ */ jsx(ChatPaneTabs, { agentId: activeAgent.id, className: "border-b" }),
|
|
540
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", "data-ai-launcher-chat-container": "", children: session ? /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
541
|
+
LazyAiChat,
|
|
542
|
+
{
|
|
543
|
+
agent: activeAgent.id,
|
|
544
|
+
conversationId: session.conversationId,
|
|
545
|
+
pageContext: {},
|
|
546
|
+
className: "h-full",
|
|
547
|
+
placeholder,
|
|
548
|
+
suggestions,
|
|
549
|
+
contextItems,
|
|
550
|
+
welcomeTitle: activeAgent.label,
|
|
551
|
+
welcomeDescription: activeAgent.description ?? welcomeFallback
|
|
552
|
+
},
|
|
553
|
+
session.id
|
|
554
|
+
) }) : null })
|
|
555
|
+
] });
|
|
556
|
+
}
|
|
557
|
+
var AiAssistantLauncher_default = AiAssistantLauncher;
|
|
558
|
+
function AiProviderSetupPanel({ t }) {
|
|
559
|
+
return /* @__PURE__ */ jsx("div", { className: "px-4 py-5", "data-ai-launcher-provider-setup": "", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-status-warning-border bg-status-warning-bg p-4 text-status-warning-text", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
560
|
+
/* @__PURE__ */ jsx("span", { className: "mt-0.5 inline-flex size-8 shrink-0 items-center justify-center rounded-full bg-background/70 text-status-warning-icon", children: /* @__PURE__ */ jsx(AlertTriangle, { className: "size-4", "aria-hidden": true }) }),
|
|
561
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-3", children: [
|
|
562
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
563
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.launcher.setup.title", "Configure an AI provider to use assistants") }),
|
|
564
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs leading-5 text-status-warning-text/90", children: t(
|
|
565
|
+
"ai_assistant.launcher.setup.body",
|
|
566
|
+
"AI assistants are installed, but no provider key is configured. Set OPENCODE_PROVIDER and one matching API key in your .env file, then restart the app."
|
|
567
|
+
) })
|
|
568
|
+
] }),
|
|
569
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-status-warning-border/70 bg-background/80 p-3 font-mono text-[11px] leading-5 text-foreground", children: [
|
|
570
|
+
/* @__PURE__ */ jsx("div", { children: "OPENCODE_PROVIDER=anthropic" }),
|
|
571
|
+
/* @__PURE__ */ jsx("div", { children: "ANTHROPIC_API_KEY=..." }),
|
|
572
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 text-muted-foreground", children: "# or" }),
|
|
573
|
+
/* @__PURE__ */ jsx("div", { children: "OPENCODE_PROVIDER=openai" }),
|
|
574
|
+
/* @__PURE__ */ jsx("div", { children: "OPENAI_API_KEY=..." }),
|
|
575
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 text-muted-foreground", children: "# or" }),
|
|
576
|
+
/* @__PURE__ */ jsx("div", { children: "OPENCODE_PROVIDER=google" }),
|
|
577
|
+
/* @__PURE__ */ jsx("div", { children: "GOOGLE_GENERATIVE_AI_API_KEY=..." })
|
|
578
|
+
] }),
|
|
579
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
580
|
+
/* @__PURE__ */ jsx(Button, { asChild: true, size: "sm", variant: "outline", children: /* @__PURE__ */ jsxs("a", { href: AI_ASSISTANT_DOCS_URL, target: "_blank", rel: "noreferrer", children: [
|
|
581
|
+
t("ai_assistant.launcher.setup.docs", "AI assistant docs"),
|
|
582
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "ml-1 size-3", "aria-hidden": true })
|
|
583
|
+
] }) }),
|
|
584
|
+
/* @__PURE__ */ jsx(Button, { asChild: true, size: "sm", variant: "ghost", children: /* @__PURE__ */ jsxs("a", { href: AI_ASSISTANT_SETTINGS_DOCS_URL, target: "_blank", rel: "noreferrer", children: [
|
|
585
|
+
t("ai_assistant.launcher.setup.settingsDocs", "Provider settings"),
|
|
586
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "ml-1 size-3", "aria-hidden": true })
|
|
587
|
+
] }) })
|
|
588
|
+
] })
|
|
589
|
+
] })
|
|
590
|
+
] }) }) });
|
|
591
|
+
}
|
|
592
|
+
export {
|
|
593
|
+
AiAssistantLauncher,
|
|
594
|
+
AiAssistantLauncher_default as default
|
|
595
|
+
};
|
|
596
|
+
//# sourceMappingURL=AiAssistantLauncher.js.map
|