@mvriu5/payload-ai 1.2.0 → 1.3.2
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/README.md +42 -0
- package/dist/components/Icons.d.ts +0 -8
- package/dist/components/Icons.js +4 -216
- package/dist/components/{ActionToast.d.ts → action-toast/ActionToast.d.ts} +1 -2
- package/dist/components/{ActionToast.js → action-toast/ActionToast.js} +25 -34
- package/dist/components/{ActionToast.module.css → action-toast/ActionToast.module.css} +0 -79
- package/dist/components/ai-input/AIInput.js +433 -0
- package/dist/components/{AIInput.module.css → ai-input/AIInput.module.css} +111 -43
- package/dist/components/ai-input/badge.d.ts +14 -0
- package/dist/components/ai-input/badge.js +85 -0
- package/dist/components/{AuditLogList.d.ts → audit-log-list/AuditLogList.d.ts} +4 -8
- package/dist/components/{AuditLogList.js → audit-log-list/AuditLogList.js} +49 -21
- package/dist/components/{AuditLogList.module.css → audit-log-list/AuditLogList.module.css} +11 -65
- package/dist/components/dashboard/Dashboard.d.ts +2 -0
- package/dist/components/dashboard/Dashboard.js +15 -0
- package/dist/components/dashboard/Dashboard.module.css +13 -0
- package/dist/components/{DiffDialog.d.ts → diff-dialog/DiffDialog.d.ts} +2 -2
- package/dist/components/{DiffDialog.js → diff-dialog/DiffDialog.js} +200 -189
- package/dist/components/{DiffDialog.module.css → diff-dialog/DiffDialog.module.css} +17 -35
- package/dist/components/hooks/useAIChatStream.d.ts +39 -0
- package/dist/components/hooks/useAIChatStream.js +217 -0
- package/dist/components/hooks/useAISettings.js +26 -25
- package/dist/components/hooks/useAuditLog.d.ts +11 -0
- package/dist/components/hooks/useAuditLog.js +41 -0
- package/dist/components/hooks/useDocumentMentionSuggestions.d.ts +1 -1
- package/dist/components/hooks/useDocumentMentionSuggestions.js +31 -27
- package/dist/components/hooks/useMentions.d.ts +58 -0
- package/dist/components/hooks/useMentions.js +276 -0
- package/dist/components/hooks/usePluginConfig.d.ts +52 -0
- package/dist/components/hooks/usePluginConfig.js +20 -0
- package/dist/components/{MentionPopover.d.ts → mention-popover/MentionPopover.d.ts} +2 -4
- package/dist/components/{MentionPopover.js → mention-popover/MentionPopover.js} +1 -8
- package/dist/components/{MentionPopover.module.css → mention-popover/MentionPopover.module.css} +1 -1
- package/dist/exports/client.d.ts +2 -2
- package/dist/exports/client.js +2 -2
- package/dist/handlers/applyActionHandler.js +27 -7
- package/dist/handlers/chatHandler.js +302 -58
- package/dist/handlers/mediaUploadHandler.d.ts +7 -0
- package/dist/handlers/mediaUploadHandler.js +99 -0
- package/dist/handlers/mentionSuggestionHandler.js +16 -14
- package/dist/handlers/proposalDiffHandler.js +41 -29
- package/dist/index.d.ts +6 -0
- package/dist/index.js +39 -7
- package/dist/payload/collectionPermissions.js +3 -1
- package/dist/payload/normalizeData.d.ts +0 -6
- package/dist/payload/normalizeData.js +25 -13
- package/dist/payload/proposalData.js +4 -1
- package/dist/payload/schemaContext.js +98 -43
- package/package.json +6 -6
- package/dist/components/AIInput.js +0 -896
- /package/dist/components/{AIInput.d.ts → ai-input/AIInput.d.ts} +0 -0
|
@@ -1,896 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useConfig } from "@payloadcms/ui";
|
|
4
|
-
import { formatAdminURL } from "payload/shared";
|
|
5
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
-
import { getResolvedAIModelConfig } from "../ai/providerOptions.js";
|
|
7
|
-
import { getSerializableLabel, isInternalCollection } from "../payload/shared.js";
|
|
8
|
-
import { ActionToast } from "./ActionToast.js";
|
|
9
|
-
import styles from "./AIInput.module.css";
|
|
10
|
-
import { MentionPopover } from "./MentionPopover.js";
|
|
11
|
-
import { ClaudeIcon, GoogleGeminiIcon, MistralAiIcon, OpenaiIcon, OpenrouterIcon, Send } from "./Icons.js";
|
|
12
|
-
import { RecentChangesList } from "./AuditLogList.js";
|
|
13
|
-
import { useAISettings } from "./hooks/useAISettings.js";
|
|
14
|
-
import { useDocumentMentionSuggestions } from "./hooks/useDocumentMentionSuggestions.js";
|
|
15
|
-
const collectBlockOptions = ({ fields, parent })=>{
|
|
16
|
-
const options = [];
|
|
17
|
-
for (const field of fields){
|
|
18
|
-
if (field.type === "blocks" && field.blocks) {
|
|
19
|
-
for (const block of field.blocks){
|
|
20
|
-
options.push({
|
|
21
|
-
label: getSerializableLabel(block.labels?.singular, block.slug),
|
|
22
|
-
parent,
|
|
23
|
-
slug: block.slug,
|
|
24
|
-
type: "block"
|
|
25
|
-
});
|
|
26
|
-
options.push(...collectBlockOptions({
|
|
27
|
-
fields: block.fields || [],
|
|
28
|
-
parent: `${parent}/${block.slug}`
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (field.fields) {
|
|
33
|
-
options.push(...collectBlockOptions({
|
|
34
|
-
fields: field.fields,
|
|
35
|
-
parent
|
|
36
|
-
}));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return options;
|
|
40
|
-
};
|
|
41
|
-
const getProviderIcon = (provider)=>{
|
|
42
|
-
const iconProps = {
|
|
43
|
-
"aria-hidden": true,
|
|
44
|
-
className: styles.selectProviderIcon
|
|
45
|
-
};
|
|
46
|
-
switch(provider){
|
|
47
|
-
case "claude":
|
|
48
|
-
return /*#__PURE__*/ _jsx(ClaudeIcon, {
|
|
49
|
-
...iconProps
|
|
50
|
-
});
|
|
51
|
-
case "google":
|
|
52
|
-
return /*#__PURE__*/ _jsx(GoogleGeminiIcon, {
|
|
53
|
-
...iconProps
|
|
54
|
-
});
|
|
55
|
-
case "mistral":
|
|
56
|
-
return /*#__PURE__*/ _jsx(MistralAiIcon, {
|
|
57
|
-
...iconProps
|
|
58
|
-
});
|
|
59
|
-
case "openai":
|
|
60
|
-
return /*#__PURE__*/ _jsx(OpenaiIcon, {
|
|
61
|
-
...iconProps
|
|
62
|
-
});
|
|
63
|
-
case "openrouter":
|
|
64
|
-
return /*#__PURE__*/ _jsx(OpenrouterIcon, {
|
|
65
|
-
...iconProps
|
|
66
|
-
});
|
|
67
|
-
default:
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
const parseSSEEvent = (chunk)=>{
|
|
72
|
-
const lines = chunk.split("\n");
|
|
73
|
-
let eventName = "";
|
|
74
|
-
const dataLines = [];
|
|
75
|
-
for (const line of lines){
|
|
76
|
-
if (line.startsWith("event:")) {
|
|
77
|
-
eventName = line.slice(6).trim();
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
if (line.startsWith("data:")) {
|
|
81
|
-
dataLines.push(line.slice(5).trim());
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (!eventName) return null;
|
|
85
|
-
try {
|
|
86
|
-
const data = JSON.parse(dataLines.join("\n"));
|
|
87
|
-
if (eventName !== "text" && eventName !== "proposals" && eventName !== "error" && eventName !== "done" && eventName !== "debug") {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
data,
|
|
92
|
-
event: eventName
|
|
93
|
-
};
|
|
94
|
-
} catch {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
const sanitizeResponseText = (value)=>value.replace(/\*\*/g, "");
|
|
99
|
-
const getDebugReasonLabel = (reason)=>{
|
|
100
|
-
switch(reason){
|
|
101
|
-
case "model_did_not_call_tool":
|
|
102
|
-
return "Model did not create a proposal tool call.";
|
|
103
|
-
case "proposal_created":
|
|
104
|
-
return "Proposal created.";
|
|
105
|
-
case "tool_validation_failed":
|
|
106
|
-
return "Tool validation failed before a proposal could be created.";
|
|
107
|
-
case "write_intent_without_tool_call":
|
|
108
|
-
return "The selected model did not produce the required proposal tool call for this content change.";
|
|
109
|
-
default:
|
|
110
|
-
return "Unknown";
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
const getApplyDebugReasonLabel = (reason)=>{
|
|
114
|
-
switch(reason){
|
|
115
|
-
case "unauthorized":
|
|
116
|
-
return "Request was not authorized.";
|
|
117
|
-
case "missing_proposal":
|
|
118
|
-
return "No proposal was submitted to apply.";
|
|
119
|
-
case "invalid_signature":
|
|
120
|
-
return "Proposal signature was invalid or expired.";
|
|
121
|
-
case "invalid_proposal_shape":
|
|
122
|
-
return "Proposal shape was invalid.";
|
|
123
|
-
case "sensitive_data_in_data":
|
|
124
|
-
case "sensitive_data_in_localized_data":
|
|
125
|
-
return "Proposal contained sensitive data.";
|
|
126
|
-
case "unknown_global":
|
|
127
|
-
return "Target global was not found.";
|
|
128
|
-
case "unknown_or_disallowed_collection":
|
|
129
|
-
return "Target collection is unknown or not allowed.";
|
|
130
|
-
case "invalid_collection_write_shape":
|
|
131
|
-
return "Proposal data does not match the target collection schema.";
|
|
132
|
-
case "invalid_global_write_shape":
|
|
133
|
-
return "Proposal data does not match the target global schema.";
|
|
134
|
-
case "localized_create_without_locales":
|
|
135
|
-
return "Localized create proposal had no locale entries.";
|
|
136
|
-
case "missing_auth_password":
|
|
137
|
-
return "Auth create proposal was missing a password.";
|
|
138
|
-
case "missing_auth_email":
|
|
139
|
-
return "Auth create proposal was missing an email.";
|
|
140
|
-
case "payload_operation_failed":
|
|
141
|
-
return "Payload rejected the write operation.";
|
|
142
|
-
default:
|
|
143
|
-
return "Unknown";
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
const getChatDebugMessage = (debugInfo)=>{
|
|
147
|
-
if (debugInfo.toolFailures?.length) {
|
|
148
|
-
return debugInfo.toolFailures[0]?.message || getDebugReasonLabel(debugInfo.reason);
|
|
149
|
-
}
|
|
150
|
-
return getDebugReasonLabel(debugInfo.reason);
|
|
151
|
-
};
|
|
152
|
-
const isMentionBoundary = (character)=>{
|
|
153
|
-
return character === undefined || /\s/.test(character);
|
|
154
|
-
};
|
|
155
|
-
const getActiveMentionRange = (valueBeforeCaret)=>{
|
|
156
|
-
const caretPosition = valueBeforeCaret.length;
|
|
157
|
-
for(let index = valueBeforeCaret.length - 1; index >= 0; index -= 1){
|
|
158
|
-
const character = valueBeforeCaret[index];
|
|
159
|
-
if (character === "@") {
|
|
160
|
-
const previousCharacter = valueBeforeCaret[index - 1];
|
|
161
|
-
const query = valueBeforeCaret.slice(index + 1);
|
|
162
|
-
if (!isMentionBoundary(previousCharacter)) return null;
|
|
163
|
-
if (!/^[\w-]*$/.test(query)) return null;
|
|
164
|
-
return {
|
|
165
|
-
query,
|
|
166
|
-
range: {
|
|
167
|
-
end: caretPosition,
|
|
168
|
-
start: index
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
if (isMentionBoundary(character)) break;
|
|
173
|
-
}
|
|
174
|
-
return null;
|
|
175
|
-
};
|
|
176
|
-
const svgNamespace = "http://www.w3.org/2000/svg";
|
|
177
|
-
const auditLogCollectionSlug = "payload-ai-auditlog";
|
|
178
|
-
const responseOnlyToastCooldownMs = 10000;
|
|
179
|
-
const appendSvgPath = (svg, d)=>{
|
|
180
|
-
const path = document.createElementNS(svgNamespace, "path");
|
|
181
|
-
path.setAttribute("d", d);
|
|
182
|
-
svg.append(path);
|
|
183
|
-
};
|
|
184
|
-
const createBadgeIcon = (type)=>{
|
|
185
|
-
if (type === "locale") return null;
|
|
186
|
-
const svg = document.createElementNS(svgNamespace, "svg");
|
|
187
|
-
svg.setAttribute("aria-hidden", "true");
|
|
188
|
-
svg.setAttribute("class", styles.badgeIcon);
|
|
189
|
-
svg.setAttribute("fill", "none");
|
|
190
|
-
svg.setAttribute("stroke", "currentColor");
|
|
191
|
-
svg.setAttribute("stroke-linecap", "round");
|
|
192
|
-
svg.setAttribute("stroke-linejoin", "round");
|
|
193
|
-
svg.setAttribute("stroke-width", "2");
|
|
194
|
-
svg.setAttribute("viewBox", "0 0 24 24");
|
|
195
|
-
if (type === "collection") {
|
|
196
|
-
appendSvgPath(svg, "M5 4h4l3 3h7a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-11a2 2 0 0 1 2 -2");
|
|
197
|
-
}
|
|
198
|
-
if (type === "doc") {
|
|
199
|
-
appendSvgPath(svg, "M14 3v4a1 1 0 0 0 1 1h4");
|
|
200
|
-
appendSvgPath(svg, "M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2");
|
|
201
|
-
}
|
|
202
|
-
if (type === "global") {
|
|
203
|
-
appendSvgPath(svg, "M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0");
|
|
204
|
-
appendSvgPath(svg, "M3.6 9h16.8");
|
|
205
|
-
appendSvgPath(svg, "M3.6 15h16.8");
|
|
206
|
-
appendSvgPath(svg, "M11.5 3a17 17 0 0 0 0 18");
|
|
207
|
-
appendSvgPath(svg, "M12.5 3a17 17 0 0 1 0 18");
|
|
208
|
-
}
|
|
209
|
-
if (type === "block") {
|
|
210
|
-
appendSvgPath(svg, "M14 4a1 1 0 0 1 1 -1h5a1 1 0 0 1 1 1v5a1 1 0 0 1 -1 1h-5a1 1 0 0 1 -1 -1l0 -5");
|
|
211
|
-
appendSvgPath(svg, "M3 14h12a2 2 0 0 1 2 2v3a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-10a2 2 0 0 1 2 -2h3a2 2 0 0 1 2 2v12");
|
|
212
|
-
}
|
|
213
|
-
return svg;
|
|
214
|
-
};
|
|
215
|
-
const createBadgePrefix = (suggestion)=>{
|
|
216
|
-
const prefix = document.createElement("span");
|
|
217
|
-
const icon = createBadgeIcon(suggestion.type);
|
|
218
|
-
prefix.className = styles.prefix;
|
|
219
|
-
if (icon) {
|
|
220
|
-
prefix.append(icon);
|
|
221
|
-
}
|
|
222
|
-
if (suggestion.type === "doc") {
|
|
223
|
-
prefix.append(document.createTextNode(`${suggestion.collection || "document"}:`));
|
|
224
|
-
} else if (suggestion.type === "locale") {
|
|
225
|
-
prefix.textContent = "locale:";
|
|
226
|
-
}
|
|
227
|
-
return prefix;
|
|
228
|
-
};
|
|
229
|
-
const AIInput = ()=>{
|
|
230
|
-
const { config } = useConfig();
|
|
231
|
-
const editorRef = useRef(null);
|
|
232
|
-
const mentionPopoverRef = useRef(null);
|
|
233
|
-
const [prompt, setPrompt] = useState("");
|
|
234
|
-
const configuredModels = config.admin?.custom?.payloadAiPlugin?.models;
|
|
235
|
-
const aiModelConfig = useMemo(()=>getResolvedAIModelConfig(configuredModels), [
|
|
236
|
-
configuredModels
|
|
237
|
-
]);
|
|
238
|
-
const { selectedModel, setSelectedModel, settingsProvider } = useAISettings({
|
|
239
|
-
adminUserSlug: config.admin?.user,
|
|
240
|
-
apiRoute: config.routes.api,
|
|
241
|
-
defaultModels: aiModelConfig.defaults
|
|
242
|
-
});
|
|
243
|
-
const [mentionQuery, setMentionQuery] = useState("");
|
|
244
|
-
const [mentionRange, setMentionRange] = useState(null);
|
|
245
|
-
const [mentionPopoverPosition, setMentionPopoverPosition] = useState(null);
|
|
246
|
-
const [mentions, setMentions] = useState([]);
|
|
247
|
-
const [appliedProposalIndexes, setAppliedProposalIndexes] = useState([]);
|
|
248
|
-
const [response, setResponse] = useState("");
|
|
249
|
-
const [tokenUsage, setTokenUsage] = useState(null);
|
|
250
|
-
const [chatDebugInfo, setChatDebugInfo] = useState(null);
|
|
251
|
-
const [applyDebugInfo, setApplyDebugInfo] = useState(null);
|
|
252
|
-
const [error, setError] = useState("");
|
|
253
|
-
const [proposals, setProposals] = useState([]);
|
|
254
|
-
const [appliedChanges, setAppliedChanges] = useState([]);
|
|
255
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
256
|
-
const [isApplying, setIsApplying] = useState(false);
|
|
257
|
-
const enabledCollectionSlugs = config.admin?.custom?.payloadAiPlugin?.collectionSlugs;
|
|
258
|
-
const enabledCollectionSlugSet = useMemo(()=>enabledCollectionSlugs ? new Set(enabledCollectionSlugs) : null, [
|
|
259
|
-
enabledCollectionSlugs
|
|
260
|
-
]);
|
|
261
|
-
const isCollectionMentionEnabled = (slug)=>!enabledCollectionSlugSet || enabledCollectionSlugSet.has(slug);
|
|
262
|
-
const recentChangesEndpoint = useMemo(()=>formatAdminURL({
|
|
263
|
-
apiRoute: config.routes.api,
|
|
264
|
-
path: "/ai-audit-log"
|
|
265
|
-
}), [
|
|
266
|
-
config.routes.api
|
|
267
|
-
]);
|
|
268
|
-
const allChangesURL = useMemo(()=>`${config.routes.admin || "/admin"}/collections/${auditLogCollectionSlug}`, [
|
|
269
|
-
config.routes.admin
|
|
270
|
-
]);
|
|
271
|
-
const loadRecentChanges = useCallback(async ()=>{
|
|
272
|
-
const res = await fetch(recentChangesEndpoint);
|
|
273
|
-
const result = await res.json().catch(()=>null);
|
|
274
|
-
if (res.ok && result?.changes) {
|
|
275
|
-
setAppliedChanges(result.changes.slice(0, 10));
|
|
276
|
-
}
|
|
277
|
-
}, [
|
|
278
|
-
recentChangesEndpoint
|
|
279
|
-
]);
|
|
280
|
-
useEffect(()=>{
|
|
281
|
-
void loadRecentChanges().catch(()=>undefined);
|
|
282
|
-
}, [
|
|
283
|
-
loadRecentChanges
|
|
284
|
-
]);
|
|
285
|
-
useEffect(()=>{
|
|
286
|
-
if (isLoading || error || proposals.length > 0 || !response) return;
|
|
287
|
-
const timeout = window.setTimeout(()=>{
|
|
288
|
-
setResponse("");
|
|
289
|
-
clearInput();
|
|
290
|
-
}, responseOnlyToastCooldownMs);
|
|
291
|
-
return ()=>window.clearTimeout(timeout);
|
|
292
|
-
}, [
|
|
293
|
-
error,
|
|
294
|
-
isLoading,
|
|
295
|
-
proposals.length,
|
|
296
|
-
response
|
|
297
|
-
]);
|
|
298
|
-
const collections = config.collections.filter((collection)=>!isInternalCollection(collection.slug)).filter((collection)=>isCollectionMentionEnabled(collection.slug)).map((collection)=>({
|
|
299
|
-
label: getSerializableLabel(collection.labels?.singular, collection.slug),
|
|
300
|
-
slug: collection.slug,
|
|
301
|
-
type: "collection"
|
|
302
|
-
}));
|
|
303
|
-
const globals = config.globals?.map((global)=>({
|
|
304
|
-
label: getSerializableLabel(global.label, global.slug),
|
|
305
|
-
slug: global.slug,
|
|
306
|
-
type: "global"
|
|
307
|
-
})) || [];
|
|
308
|
-
const localizationConfig = config.localization;
|
|
309
|
-
const localesConfig = localizationConfig?.locales ?? [];
|
|
310
|
-
const locales = localesConfig.flatMap((locale)=>{
|
|
311
|
-
if (typeof locale === "string") {
|
|
312
|
-
return [
|
|
313
|
-
{
|
|
314
|
-
isDefault: locale === localizationConfig?.defaultLocale,
|
|
315
|
-
label: locale,
|
|
316
|
-
slug: locale,
|
|
317
|
-
type: "locale"
|
|
318
|
-
}
|
|
319
|
-
];
|
|
320
|
-
}
|
|
321
|
-
if (!locale || typeof locale !== "object") return [];
|
|
322
|
-
const slug = typeof locale.code === "string" ? locale.code : typeof locale.label === "string" ? locale.label : null;
|
|
323
|
-
if (!slug) return [];
|
|
324
|
-
return [
|
|
325
|
-
{
|
|
326
|
-
isDefault: slug === localizationConfig?.defaultLocale,
|
|
327
|
-
label: getSerializableLabel(locale.label, slug),
|
|
328
|
-
slug,
|
|
329
|
-
type: "locale"
|
|
330
|
-
}
|
|
331
|
-
];
|
|
332
|
-
});
|
|
333
|
-
const blocks = [
|
|
334
|
-
...config.collections.filter((collection)=>isCollectionMentionEnabled(collection.slug)).flatMap((collection)=>collectBlockOptions({
|
|
335
|
-
fields: collection.fields,
|
|
336
|
-
parent: collection.slug
|
|
337
|
-
})),
|
|
338
|
-
...config.globals?.flatMap((global)=>collectBlockOptions({
|
|
339
|
-
fields: global.fields,
|
|
340
|
-
parent: global.slug
|
|
341
|
-
})) || []
|
|
342
|
-
];
|
|
343
|
-
const mentionOptions = [
|
|
344
|
-
...collections,
|
|
345
|
-
...globals,
|
|
346
|
-
...blocks,
|
|
347
|
-
...locales
|
|
348
|
-
];
|
|
349
|
-
const normalizedMentionQuery = mentionQuery.toLowerCase();
|
|
350
|
-
const filteredCollections = collections.filter((collection)=>collection.slug.toLowerCase().includes(normalizedMentionQuery) || collection.label.toLowerCase().includes(normalizedMentionQuery));
|
|
351
|
-
const filteredMentionOptions = mentionOptions.filter((option)=>option.slug.toLowerCase().includes(normalizedMentionQuery) || option.label.toLowerCase().includes(normalizedMentionQuery));
|
|
352
|
-
const documentSuggestionCollection = filteredCollections.length === 1 ? filteredCollections[0]?.slug : null;
|
|
353
|
-
const { documentSuggestions, resetDocumentSuggestions } = useDocumentMentionSuggestions({
|
|
354
|
-
apiRoute: config.routes.api,
|
|
355
|
-
documentSuggestionCollection,
|
|
356
|
-
mentionQuery,
|
|
357
|
-
mentionRange
|
|
358
|
-
});
|
|
359
|
-
const mentionSuggestions = [
|
|
360
|
-
...filteredMentionOptions,
|
|
361
|
-
...documentSuggestions
|
|
362
|
-
];
|
|
363
|
-
const shouldShowChatDebugInfo = Boolean(chatDebugInfo) && (Boolean(error) || chatDebugInfo?.reason !== "proposal_created");
|
|
364
|
-
const shouldShowApplyDebugInfo = Boolean(applyDebugInfo) && Boolean(error);
|
|
365
|
-
const actionToastDescription = response;
|
|
366
|
-
const getTextBeforeCaret = (element)=>{
|
|
367
|
-
const selection = window.getSelection();
|
|
368
|
-
if (!selection || selection.rangeCount === 0) return "";
|
|
369
|
-
const range = selection.getRangeAt(0);
|
|
370
|
-
const clonedRange = range.cloneRange();
|
|
371
|
-
clonedRange.selectNodeContents(element);
|
|
372
|
-
clonedRange.setEnd(range.endContainer, range.endOffset);
|
|
373
|
-
return clonedRange.toString();
|
|
374
|
-
};
|
|
375
|
-
const getTextNodeAtOffset = (element, offset)=>{
|
|
376
|
-
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
|
|
377
|
-
let currentOffset = 0;
|
|
378
|
-
let node = walker.nextNode();
|
|
379
|
-
while(node){
|
|
380
|
-
const nextOffset = currentOffset + (node.textContent?.length || 0);
|
|
381
|
-
if (offset <= nextOffset) {
|
|
382
|
-
return {
|
|
383
|
-
node,
|
|
384
|
-
offset: offset - currentOffset
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
currentOffset = nextOffset;
|
|
388
|
-
node = walker.nextNode();
|
|
389
|
-
}
|
|
390
|
-
const textNode = document.createTextNode("");
|
|
391
|
-
element.append(textNode);
|
|
392
|
-
return {
|
|
393
|
-
node: textNode,
|
|
394
|
-
offset: 0
|
|
395
|
-
};
|
|
396
|
-
};
|
|
397
|
-
const updateMentionPopoverPosition = useCallback((range)=>{
|
|
398
|
-
const editor = editorRef.current;
|
|
399
|
-
const popover = mentionPopoverRef.current;
|
|
400
|
-
if (!editor || !popover || !range) {
|
|
401
|
-
setMentionPopoverPosition(null);
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
const startPosition = getTextNodeAtOffset(editor, range.start);
|
|
405
|
-
const anchorRange = document.createRange();
|
|
406
|
-
anchorRange.setStart(startPosition.node, startPosition.offset);
|
|
407
|
-
anchorRange.setEnd(startPosition.node, startPosition.offset);
|
|
408
|
-
const editorRect = editor.getBoundingClientRect();
|
|
409
|
-
const anchorRect = anchorRange.getBoundingClientRect();
|
|
410
|
-
const fallbackLeft = Math.max(0, anchorRect.left - editorRect.left + 12);
|
|
411
|
-
const fallbackTop = Math.max(0, anchorRect.bottom - editorRect.top + 20);
|
|
412
|
-
const popoverWidth = popover.offsetWidth || 260;
|
|
413
|
-
const maxLeft = Math.max(0, editor.clientWidth - popoverWidth);
|
|
414
|
-
setMentionPopoverPosition({
|
|
415
|
-
left: Math.min(fallbackLeft, maxLeft),
|
|
416
|
-
top: fallbackTop
|
|
417
|
-
});
|
|
418
|
-
}, []);
|
|
419
|
-
const replaceTextRangeWithBadge = ({ badge, editor, end, start })=>{
|
|
420
|
-
const startPosition = getTextNodeAtOffset(editor, start);
|
|
421
|
-
const endPosition = getTextNodeAtOffset(editor, end);
|
|
422
|
-
const range = document.createRange();
|
|
423
|
-
const trailingSpace = document.createTextNode(" ");
|
|
424
|
-
range.setStart(startPosition.node, startPosition.offset);
|
|
425
|
-
range.setEnd(endPosition.node, endPosition.offset);
|
|
426
|
-
range.deleteContents();
|
|
427
|
-
range.insertNode(trailingSpace);
|
|
428
|
-
range.insertNode(badge);
|
|
429
|
-
const selection = window.getSelection();
|
|
430
|
-
const caretRange = document.createRange();
|
|
431
|
-
caretRange.setStartAfter(trailingSpace);
|
|
432
|
-
caretRange.collapse(true);
|
|
433
|
-
selection?.removeAllRanges();
|
|
434
|
-
selection?.addRange(caretRange);
|
|
435
|
-
};
|
|
436
|
-
const updateMentionState = (valueBeforeCaret)=>{
|
|
437
|
-
const activeMention = getActiveMentionRange(valueBeforeCaret);
|
|
438
|
-
if (!activeMention) {
|
|
439
|
-
setMentionQuery("");
|
|
440
|
-
setMentionRange(null);
|
|
441
|
-
setMentionPopoverPosition(null);
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
setMentionQuery(activeMention.query);
|
|
445
|
-
setMentionRange(activeMention.range);
|
|
446
|
-
};
|
|
447
|
-
useEffect(()=>{
|
|
448
|
-
if (!mentionRange) {
|
|
449
|
-
setMentionPopoverPosition(null);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
updateMentionPopoverPosition(mentionRange);
|
|
453
|
-
}, [
|
|
454
|
-
mentionRange,
|
|
455
|
-
mentionSuggestions.length,
|
|
456
|
-
updateMentionPopoverPosition
|
|
457
|
-
]);
|
|
458
|
-
useEffect(()=>{
|
|
459
|
-
if (!mentionRange) return;
|
|
460
|
-
const updatePosition = ()=>updateMentionPopoverPosition(mentionRange);
|
|
461
|
-
window.addEventListener("resize", updatePosition);
|
|
462
|
-
window.addEventListener("scroll", updatePosition, true);
|
|
463
|
-
return ()=>{
|
|
464
|
-
window.removeEventListener("resize", updatePosition);
|
|
465
|
-
window.removeEventListener("scroll", updatePosition, true);
|
|
466
|
-
};
|
|
467
|
-
}, [
|
|
468
|
-
mentionRange,
|
|
469
|
-
updateMentionPopoverPosition
|
|
470
|
-
]);
|
|
471
|
-
const clearInput = ()=>{
|
|
472
|
-
setPrompt("");
|
|
473
|
-
setMentions([]);
|
|
474
|
-
if (editorRef.current) editorRef.current.textContent = "";
|
|
475
|
-
};
|
|
476
|
-
const getProposalViewURL = (proposal)=>{
|
|
477
|
-
const adminRoute = config.routes.admin || "/admin";
|
|
478
|
-
if (proposal.action === "updateGlobal" && proposal.slug) {
|
|
479
|
-
return `${adminRoute}/globals/${proposal.slug}`;
|
|
480
|
-
}
|
|
481
|
-
if (proposal.collection && proposal.id) {
|
|
482
|
-
return `${adminRoute}/collections/${proposal.collection}/${proposal.id}`;
|
|
483
|
-
}
|
|
484
|
-
return null;
|
|
485
|
-
};
|
|
486
|
-
const insertMention = (suggestion)=>{
|
|
487
|
-
const editor = editorRef.current;
|
|
488
|
-
if (!mentionRange || !editor) return;
|
|
489
|
-
const currentValue = editor.innerText;
|
|
490
|
-
const beforeMention = currentValue.slice(0, mentionRange.start);
|
|
491
|
-
const afterMention = currentValue.slice(mentionRange.end);
|
|
492
|
-
const badgeType = suggestion.type === "doc" ? "document" : suggestion.type;
|
|
493
|
-
const badgePrefix = `${badgeType}:`;
|
|
494
|
-
const badgeText = `${badgePrefix} ${suggestion.label}`;
|
|
495
|
-
const badge = document.createElement("span");
|
|
496
|
-
badge.className = [
|
|
497
|
-
styles.badge,
|
|
498
|
-
styles[suggestion.type],
|
|
499
|
-
styles.inlineBadge
|
|
500
|
-
].join(" ");
|
|
501
|
-
badge.contentEditable = "false";
|
|
502
|
-
badge.append(createBadgePrefix(suggestion), Object.assign(document.createElement("span"), {
|
|
503
|
-
className: styles.name,
|
|
504
|
-
textContent: suggestion.label
|
|
505
|
-
}));
|
|
506
|
-
replaceTextRangeWithBadge({
|
|
507
|
-
badge,
|
|
508
|
-
editor,
|
|
509
|
-
end: mentionRange.end,
|
|
510
|
-
start: mentionRange.start
|
|
511
|
-
});
|
|
512
|
-
editor.focus();
|
|
513
|
-
setPrompt(`${beforeMention}${badgeText} ${afterMention}`);
|
|
514
|
-
setMentions((currentMentions)=>{
|
|
515
|
-
const mentionExists = currentMentions.some((mention)=>mention.type === suggestion.type && mention.slug === suggestion.slug && mention.parent === suggestion.parent && mention.collection === suggestion.collection && mention.id === suggestion.id);
|
|
516
|
-
if (mentionExists) return currentMentions;
|
|
517
|
-
return [
|
|
518
|
-
...currentMentions,
|
|
519
|
-
suggestion
|
|
520
|
-
];
|
|
521
|
-
});
|
|
522
|
-
setMentionQuery("");
|
|
523
|
-
setMentionRange(null);
|
|
524
|
-
setMentionPopoverPosition(null);
|
|
525
|
-
resetDocumentSuggestions();
|
|
526
|
-
};
|
|
527
|
-
const handleSubmit = async ()=>{
|
|
528
|
-
const trimmedPrompt = prompt.trim();
|
|
529
|
-
if (!trimmedPrompt) return;
|
|
530
|
-
setIsLoading(true);
|
|
531
|
-
setAppliedProposalIndexes([]);
|
|
532
|
-
setError("");
|
|
533
|
-
setProposals([]);
|
|
534
|
-
setResponse("");
|
|
535
|
-
setTokenUsage(null);
|
|
536
|
-
setChatDebugInfo(null);
|
|
537
|
-
setApplyDebugInfo(null);
|
|
538
|
-
try {
|
|
539
|
-
const res = await fetch(formatAdminURL({
|
|
540
|
-
apiRoute: config.routes.api,
|
|
541
|
-
path: "/ai-chat"
|
|
542
|
-
}), {
|
|
543
|
-
body: JSON.stringify({
|
|
544
|
-
mentions,
|
|
545
|
-
model: selectedModel,
|
|
546
|
-
prompt: trimmedPrompt
|
|
547
|
-
}),
|
|
548
|
-
headers: {
|
|
549
|
-
"Content-Type": "application/json"
|
|
550
|
-
},
|
|
551
|
-
method: "POST"
|
|
552
|
-
});
|
|
553
|
-
if (!res.ok) {
|
|
554
|
-
const result = await res.json().catch(()=>null);
|
|
555
|
-
throw new Error(result?.error || "AI request failed");
|
|
556
|
-
}
|
|
557
|
-
if (!res.body) {
|
|
558
|
-
throw new Error("AI response stream is unavailable");
|
|
559
|
-
}
|
|
560
|
-
const reader = res.body.getReader();
|
|
561
|
-
const decoder = new TextDecoder();
|
|
562
|
-
let buffer = "";
|
|
563
|
-
let finalDebugInfo = null;
|
|
564
|
-
let receivedProposals = [];
|
|
565
|
-
let receivedText = "";
|
|
566
|
-
let receivedVisibleText = "";
|
|
567
|
-
while(true){
|
|
568
|
-
const { done, value } = await reader.read();
|
|
569
|
-
if (done) break;
|
|
570
|
-
buffer += decoder.decode(value, {
|
|
571
|
-
stream: true
|
|
572
|
-
});
|
|
573
|
-
const chunks = buffer.split("\n\n");
|
|
574
|
-
buffer = chunks.pop() || "";
|
|
575
|
-
for (const chunk of chunks){
|
|
576
|
-
const event = parseSSEEvent(chunk);
|
|
577
|
-
if (!event) continue;
|
|
578
|
-
if (event.event === "text") {
|
|
579
|
-
if (event.data.delta) {
|
|
580
|
-
const nextDelta = sanitizeResponseText(event.data.delta || "");
|
|
581
|
-
receivedText += nextDelta;
|
|
582
|
-
receivedVisibleText += nextDelta.replace(/\s+/g, "");
|
|
583
|
-
setResponse((current)=>current + nextDelta);
|
|
584
|
-
}
|
|
585
|
-
continue;
|
|
586
|
-
}
|
|
587
|
-
if (event.event === "proposals") {
|
|
588
|
-
receivedProposals = event.data.proposals || [];
|
|
589
|
-
setProposals(receivedProposals);
|
|
590
|
-
setTokenUsage(event.data.usage || null);
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
593
|
-
if (event.event === "debug") {
|
|
594
|
-
finalDebugInfo = event.data;
|
|
595
|
-
setChatDebugInfo(event.data);
|
|
596
|
-
if ((event.data.proposalCount || 0) === 0 && !receivedVisibleText) {
|
|
597
|
-
// Show specific tool validation message if present, otherwise a generic no‑action message
|
|
598
|
-
if (event.data.reason === "tool_validation_failed") {
|
|
599
|
-
const msg = getChatDebugMessage(event.data);
|
|
600
|
-
setResponse(msg);
|
|
601
|
-
// Auto‑clear after the standard toast timeout
|
|
602
|
-
window.setTimeout(()=>setResponse(""), responseOnlyToastCooldownMs);
|
|
603
|
-
} else {
|
|
604
|
-
setResponse("No action needed");
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
continue;
|
|
608
|
-
}
|
|
609
|
-
if (event.event === "error") {
|
|
610
|
-
throw new Error(event.data.error || "AI request failed");
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
const finalEvent = buffer.trim() ? parseSSEEvent(buffer.trim()) : null;
|
|
615
|
-
if (finalEvent?.event === "proposals") {
|
|
616
|
-
receivedProposals = finalEvent.data.proposals || [];
|
|
617
|
-
setProposals(receivedProposals);
|
|
618
|
-
setTokenUsage(finalEvent.data.usage || null);
|
|
619
|
-
}
|
|
620
|
-
if (finalEvent?.event === "debug") {
|
|
621
|
-
finalDebugInfo = finalEvent.data;
|
|
622
|
-
setChatDebugInfo(finalEvent.data);
|
|
623
|
-
if ((finalEvent.data.proposalCount || 0) === 0 && !receivedVisibleText) {
|
|
624
|
-
// Same logic as above for the final event
|
|
625
|
-
if (finalEvent.data.reason === "tool_validation_failed") {
|
|
626
|
-
setResponse(getChatDebugMessage(finalEvent.data));
|
|
627
|
-
} else {
|
|
628
|
-
setResponse("No action needed");
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
if (finalEvent?.event === "error") {
|
|
633
|
-
throw new Error(finalEvent.data.error || "AI request failed");
|
|
634
|
-
}
|
|
635
|
-
if (receivedProposals.length === 0) {
|
|
636
|
-
if (finalDebugInfo) {
|
|
637
|
-
const debugMessage = getChatDebugMessage(finalDebugInfo);
|
|
638
|
-
const isMeaningfulVisibleText = receivedVisibleText.length >= 12;
|
|
639
|
-
const trimmedReceivedText = receivedText.trim();
|
|
640
|
-
if (!isMeaningfulVisibleText || trimmedReceivedText.length < 12) {
|
|
641
|
-
setResponse(debugMessage);
|
|
642
|
-
} else {
|
|
643
|
-
setResponse((current)=>current.trim() || debugMessage);
|
|
644
|
-
}
|
|
645
|
-
} else {
|
|
646
|
-
// Fallback when no debug info is provided (e.g., tool validation failures without a debug event)
|
|
647
|
-
setResponse("No action needed");
|
|
648
|
-
}
|
|
649
|
-
clearInput();
|
|
650
|
-
}
|
|
651
|
-
} catch (err) {
|
|
652
|
-
setProposals([]);
|
|
653
|
-
setResponse("");
|
|
654
|
-
setTokenUsage(null);
|
|
655
|
-
setApplyDebugInfo(null);
|
|
656
|
-
setError(err instanceof Error ? err.message : "AI request failed");
|
|
657
|
-
} finally{
|
|
658
|
-
setIsLoading(false);
|
|
659
|
-
}
|
|
660
|
-
};
|
|
661
|
-
const handleApplyProposal = async (proposal)=>{
|
|
662
|
-
setIsApplying(true);
|
|
663
|
-
setError("");
|
|
664
|
-
try {
|
|
665
|
-
const res = await fetch(formatAdminURL({
|
|
666
|
-
apiRoute: config.routes.api,
|
|
667
|
-
path: "/ai-apply-action"
|
|
668
|
-
}), {
|
|
669
|
-
body: JSON.stringify({
|
|
670
|
-
aiResponse: response,
|
|
671
|
-
prompt,
|
|
672
|
-
proposal,
|
|
673
|
-
tokenUsage
|
|
674
|
-
}),
|
|
675
|
-
headers: {
|
|
676
|
-
"Content-Type": "application/json"
|
|
677
|
-
},
|
|
678
|
-
method: "POST"
|
|
679
|
-
});
|
|
680
|
-
const result = await res.json();
|
|
681
|
-
if (!res.ok) {
|
|
682
|
-
setProposals([]);
|
|
683
|
-
setResponse("");
|
|
684
|
-
setApplyDebugInfo(result.debug || null);
|
|
685
|
-
throw new Error(result.error || "Could not apply proposal");
|
|
686
|
-
}
|
|
687
|
-
setAppliedProposalIndexes([]);
|
|
688
|
-
setError("");
|
|
689
|
-
setProposals([]);
|
|
690
|
-
setResponse("");
|
|
691
|
-
setTokenUsage(null);
|
|
692
|
-
setChatDebugInfo(null);
|
|
693
|
-
setApplyDebugInfo(null);
|
|
694
|
-
if (result.change) {
|
|
695
|
-
setAppliedChanges((current)=>[
|
|
696
|
-
result.change,
|
|
697
|
-
...current
|
|
698
|
-
].slice(0, 10));
|
|
699
|
-
}
|
|
700
|
-
void loadRecentChanges().catch(()=>undefined);
|
|
701
|
-
clearInput();
|
|
702
|
-
} catch (err) {
|
|
703
|
-
setError(err instanceof Error ? err.message : "Could not apply proposal");
|
|
704
|
-
} finally{
|
|
705
|
-
setIsApplying(false);
|
|
706
|
-
}
|
|
707
|
-
};
|
|
708
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
709
|
-
className: styles.chatLayout,
|
|
710
|
-
children: [
|
|
711
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
712
|
-
className: styles.chat,
|
|
713
|
-
children: [
|
|
714
|
-
/*#__PURE__*/ _jsx("div", {
|
|
715
|
-
className: styles.chatHeader,
|
|
716
|
-
children: /*#__PURE__*/ _jsxs("div", {
|
|
717
|
-
children: [
|
|
718
|
-
/*#__PURE__*/ _jsx("h2", {
|
|
719
|
-
className: styles.chatTitle,
|
|
720
|
-
children: "AI Assistant"
|
|
721
|
-
}),
|
|
722
|
-
/*#__PURE__*/ _jsx("p", {
|
|
723
|
-
className: styles.chatDescription,
|
|
724
|
-
children: "Ask AI to draft, improve, or analyze content."
|
|
725
|
-
})
|
|
726
|
-
]
|
|
727
|
-
})
|
|
728
|
-
}),
|
|
729
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
730
|
-
className: styles.chatInputRow,
|
|
731
|
-
children: [
|
|
732
|
-
/*#__PURE__*/ _jsx("div", {
|
|
733
|
-
className: styles.chatInputSurface,
|
|
734
|
-
children: /*#__PURE__*/ _jsx("div", {
|
|
735
|
-
className: styles.chatInput,
|
|
736
|
-
contentEditable: true,
|
|
737
|
-
"data-placeholder": "Ask AI...",
|
|
738
|
-
onInput: (event)=>{
|
|
739
|
-
const value = event.currentTarget.innerText;
|
|
740
|
-
setPrompt(value);
|
|
741
|
-
if (!value.trim()) {
|
|
742
|
-
setMentions([]);
|
|
743
|
-
}
|
|
744
|
-
updateMentionState(getTextBeforeCaret(event.currentTarget));
|
|
745
|
-
},
|
|
746
|
-
onKeyDown: (event)=>{
|
|
747
|
-
if (event.key === "ArrowDown" && mentionRange && mentionSuggestions.length > 0) {
|
|
748
|
-
const firstOption = mentionPopoverRef.current?.querySelector("button");
|
|
749
|
-
if (firstOption) {
|
|
750
|
-
event.preventDefault();
|
|
751
|
-
firstOption.focus();
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
if (event.key === "Enter" && !event.shiftKey) {
|
|
756
|
-
event.preventDefault();
|
|
757
|
-
void handleSubmit();
|
|
758
|
-
}
|
|
759
|
-
},
|
|
760
|
-
ref: editorRef,
|
|
761
|
-
role: "textbox",
|
|
762
|
-
suppressContentEditableWarning: true
|
|
763
|
-
})
|
|
764
|
-
}),
|
|
765
|
-
mentionRange && /*#__PURE__*/ _jsx(MentionPopover, {
|
|
766
|
-
containerRef: mentionPopoverRef,
|
|
767
|
-
onSelect: insertMention,
|
|
768
|
-
style: mentionPopoverPosition ? {
|
|
769
|
-
left: `${mentionPopoverPosition.left}px`,
|
|
770
|
-
top: `${mentionPopoverPosition.top}px`
|
|
771
|
-
} : undefined,
|
|
772
|
-
suggestions: mentionSuggestions
|
|
773
|
-
})
|
|
774
|
-
]
|
|
775
|
-
}),
|
|
776
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
777
|
-
className: styles.chatActionsRow,
|
|
778
|
-
children: [
|
|
779
|
-
/*#__PURE__*/ _jsx("div", {
|
|
780
|
-
className: styles.settings,
|
|
781
|
-
children: /*#__PURE__*/ _jsxs("label", {
|
|
782
|
-
className: styles.setting,
|
|
783
|
-
children: [
|
|
784
|
-
/*#__PURE__*/ _jsx("span", {
|
|
785
|
-
className: styles.settingLabel,
|
|
786
|
-
children: "Model"
|
|
787
|
-
}),
|
|
788
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
789
|
-
className: styles.selectWrapper,
|
|
790
|
-
children: [
|
|
791
|
-
getProviderIcon(settingsProvider),
|
|
792
|
-
/*#__PURE__*/ _jsxs("select", {
|
|
793
|
-
className: styles.select,
|
|
794
|
-
disabled: !settingsProvider,
|
|
795
|
-
onChange: (event)=>setSelectedModel(event.target.value),
|
|
796
|
-
value: selectedModel,
|
|
797
|
-
children: [
|
|
798
|
-
!settingsProvider && /*#__PURE__*/ _jsx("option", {
|
|
799
|
-
value: "",
|
|
800
|
-
children: "Select provider in account settings"
|
|
801
|
-
}),
|
|
802
|
-
settingsProvider && aiModelConfig.providers[settingsProvider].map((model)=>/*#__PURE__*/ _jsx("option", {
|
|
803
|
-
value: model.value,
|
|
804
|
-
children: model.label
|
|
805
|
-
}, model.value))
|
|
806
|
-
]
|
|
807
|
-
})
|
|
808
|
-
]
|
|
809
|
-
})
|
|
810
|
-
]
|
|
811
|
-
})
|
|
812
|
-
}),
|
|
813
|
-
/*#__PURE__*/ _jsxs("button", {
|
|
814
|
-
className: styles.chatButton,
|
|
815
|
-
disabled: !prompt.trim() || !settingsProvider || !selectedModel || isLoading || Boolean(error) || Boolean(actionToastDescription) || proposals.length > 0,
|
|
816
|
-
onClick: ()=>void handleSubmit(),
|
|
817
|
-
type: "button",
|
|
818
|
-
children: [
|
|
819
|
-
/*#__PURE__*/ _jsx(Send, {
|
|
820
|
-
width: 14,
|
|
821
|
-
height: 14
|
|
822
|
-
}),
|
|
823
|
-
isLoading ? "Sending..." : "Send"
|
|
824
|
-
]
|
|
825
|
-
})
|
|
826
|
-
]
|
|
827
|
-
}),
|
|
828
|
-
/*#__PURE__*/ _jsx(ActionToast, {
|
|
829
|
-
apiRoute: config.routes.api,
|
|
830
|
-
appliedProposalIndexes: appliedProposalIndexes,
|
|
831
|
-
description: actionToastDescription,
|
|
832
|
-
error: error,
|
|
833
|
-
getViewURL: getProposalViewURL,
|
|
834
|
-
isApplying: isApplying,
|
|
835
|
-
onDismiss: ()=>{
|
|
836
|
-
setAppliedProposalIndexes([]);
|
|
837
|
-
setError("");
|
|
838
|
-
setProposals([]);
|
|
839
|
-
setResponse("");
|
|
840
|
-
setTokenUsage(null);
|
|
841
|
-
setChatDebugInfo(null);
|
|
842
|
-
setApplyDebugInfo(null);
|
|
843
|
-
clearInput();
|
|
844
|
-
},
|
|
845
|
-
onDismissError: ()=>{
|
|
846
|
-
setError("");
|
|
847
|
-
},
|
|
848
|
-
onApply: (proposal, _index)=>void handleApplyProposal(proposal),
|
|
849
|
-
proposals: proposals,
|
|
850
|
-
prompt: prompt,
|
|
851
|
-
tokenUsage: tokenUsage
|
|
852
|
-
}),
|
|
853
|
-
shouldShowApplyDebugInfo && applyDebugInfo && /*#__PURE__*/ _jsxs("div", {
|
|
854
|
-
className: styles.debugInfo,
|
|
855
|
-
children: [
|
|
856
|
-
/*#__PURE__*/ _jsx("strong", {
|
|
857
|
-
children: "Apply debug"
|
|
858
|
-
}),
|
|
859
|
-
/*#__PURE__*/ _jsx("br", {}),
|
|
860
|
-
"Reason: ",
|
|
861
|
-
getApplyDebugReasonLabel(applyDebugInfo.reason),
|
|
862
|
-
/*#__PURE__*/ _jsx("br", {}),
|
|
863
|
-
"Phase: ",
|
|
864
|
-
applyDebugInfo.phase,
|
|
865
|
-
/*#__PURE__*/ _jsx("br", {}),
|
|
866
|
-
"Target: ",
|
|
867
|
-
applyDebugInfo.collection || applyDebugInfo.slug || "unknown",
|
|
868
|
-
applyDebugInfo.id ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
869
|
-
children: [
|
|
870
|
-
/*#__PURE__*/ _jsx("br", {}),
|
|
871
|
-
"ID: ",
|
|
872
|
-
applyDebugInfo.id
|
|
873
|
-
]
|
|
874
|
-
}) : null,
|
|
875
|
-
applyDebugInfo.details ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
876
|
-
children: [
|
|
877
|
-
/*#__PURE__*/ _jsx("br", {}),
|
|
878
|
-
"Details:",
|
|
879
|
-
/*#__PURE__*/ _jsx("pre", {
|
|
880
|
-
className: styles.debugDetails,
|
|
881
|
-
children: JSON.stringify(applyDebugInfo.details, null, 2)
|
|
882
|
-
})
|
|
883
|
-
]
|
|
884
|
-
}) : null
|
|
885
|
-
]
|
|
886
|
-
})
|
|
887
|
-
]
|
|
888
|
-
}),
|
|
889
|
-
/*#__PURE__*/ _jsx(RecentChangesList, {
|
|
890
|
-
allChangesURL: allChangesURL,
|
|
891
|
-
changes: appliedChanges
|
|
892
|
-
})
|
|
893
|
-
]
|
|
894
|
-
});
|
|
895
|
-
};
|
|
896
|
-
export default AIInput;
|