@parhelia/core 0.1.12881 → 0.1.12883
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/agents-view/AgentsSidebar.js +1 -1
- package/dist/agents-view/AgentsSidebar.js.map +1 -1
- package/dist/agents-view/AgentsTitlebar.d.ts +1 -1
- package/dist/agents-view/AgentsTitlebar.js +3 -6
- package/dist/agents-view/AgentsTitlebar.js.map +1 -1
- package/dist/agents-view/AgentsView.d.ts +2 -2
- package/dist/agents-view/AgentsView.js +2 -2
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/agents-view/AgentsWorkspaceView.js +1 -12
- package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
- package/dist/agents-view/CreateAgentView.d.ts +1 -1
- package/dist/agents-view/CreateAgentView.js +1 -1
- package/dist/agents-view/DateAgentsGroup.js +12 -1
- package/dist/agents-view/DateAgentsGroup.js.map +1 -1
- package/dist/agents-view/ProfileAgentsGroup.js +16 -4
- package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/checkbox.js.map +1 -1
- package/dist/components/ui/context-menu.d.ts +2 -1
- package/dist/components/ui/context-menu.js +4 -1
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/input.js +2 -2
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/textarea.js +2 -2
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/config/config.js +92 -14
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +7 -0
- package/dist/config/types.js.map +1 -1
- package/dist/editor/ContextMenu.d.ts +1 -0
- package/dist/editor/ContextMenu.js +4 -4
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/FieldHistory.d.ts +2 -1
- package/dist/editor/FieldHistory.js +13 -12
- package/dist/editor/FieldHistory.js.map +1 -1
- package/dist/editor/FieldListField.js +4 -18
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/LinkEditorDialog.d.ts +9 -2
- package/dist/editor/LinkEditorDialog.js +174 -70
- package/dist/editor/LinkEditorDialog.js.map +1 -1
- package/dist/editor/MainLayout.js +49 -6
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/MobileLayout.js +33 -1
- package/dist/editor/MobileLayout.js.map +1 -1
- package/dist/editor/PictureCropper.js +45 -28
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/ai/AgentProfileSelector.js +7 -7
- package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
- package/dist/editor/ai/Agents.js +19 -5
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/InlineAiDialog.d.ts +1 -0
- package/dist/editor/ai/InlineAiDialog.js +239 -198
- package/dist/editor/ai/InlineAiDialog.js.map +1 -1
- package/dist/editor/ai/InlineAiTextEditTooltip.d.ts +8 -0
- package/dist/editor/ai/InlineAiTextEditTooltip.js +10 -0
- package/dist/editor/ai/InlineAiTextEditTooltip.js.map +1 -0
- package/dist/editor/ai/InlineAiTrigger.js +187 -20
- package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
- package/dist/editor/ai/inlineAiTextEditLabels.d.ts +2 -0
- package/dist/editor/ai/inlineAiTextEditLabels.js +8 -0
- package/dist/editor/ai/inlineAiTextEditLabels.js.map +1 -0
- package/dist/editor/ai/prepareInlineAiTextSelection.d.ts +5 -0
- package/dist/editor/ai/prepareInlineAiTextSelection.js +86 -0
- package/dist/editor/ai/prepareInlineAiTextSelection.js.map +1 -0
- package/dist/editor/ai/terminal/components/AgentSettingsPopover.js +1 -1
- package/dist/editor/ai/terminal/components/AgentSettingsPopover.js.map +1 -1
- package/dist/editor/ai/terminal/components/AiResponseMessage.js +9 -9
- package/dist/editor/ai/terminal/components/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/terminal/components/ToolCallDisplay.js +3 -1
- package/dist/editor/ai/terminal/components/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/useActiveAgentConversation.d.ts +3 -0
- package/dist/editor/ai/useActiveAgentConversation.js +32 -0
- package/dist/editor/ai/useActiveAgentConversation.js.map +1 -0
- package/dist/editor/ai/useInlineAiPosition.d.ts +9 -1
- package/dist/editor/ai/useInlineAiPosition.js +10 -19
- package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
- package/dist/editor/client/EditorShell.js +23 -4
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +25 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/fieldModificationStore.d.ts +1 -0
- package/dist/editor/client/fieldModificationStore.js +7 -2
- package/dist/editor/client/fieldModificationStore.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +3 -1
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.js +26 -4
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/client/ui/EditorChrome.js +1 -1
- package/dist/editor/client/ui/EditorChrome.js.map +1 -1
- package/dist/editor/commands/componentCommands.d.ts +3 -1
- package/dist/editor/commands/componentCommands.js +8 -3
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/handlers/uiActionHandlers.js +5 -1
- package/dist/editor/commands/handlers/uiActionHandlers.js.map +1 -1
- package/dist/editor/editor-warnings/FinalWorkflowStateReadOnly.js +5 -0
- package/dist/editor/editor-warnings/FinalWorkflowStateReadOnly.js.map +1 -1
- package/dist/editor/editor-warnings/ItemLocked.js +6 -3
- package/dist/editor/editor-warnings/ItemLocked.js.map +1 -1
- package/dist/editor/field-types/DateFieldEditor.js +1 -1
- package/dist/editor/field-types/DateFieldEditor.js.map +1 -1
- package/dist/editor/field-types/DateTimeFieldEditor.js +1 -1
- package/dist/editor/field-types/DateTimeFieldEditor.js.map +1 -1
- package/dist/editor/field-types/DropLinkEditor.js +1 -1
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
- package/dist/editor/field-types/DropListEditor.js +1 -1
- package/dist/editor/field-types/DropListEditor.js.map +1 -1
- package/dist/editor/field-types/ImageFieldEditor.js +1 -1
- package/dist/editor/field-types/ImageFieldEditor.js.map +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/LinkFieldEditor.js +15 -3
- package/dist/editor/field-types/LinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +1 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/NameValueListEditor.js +1 -1
- package/dist/editor/field-types/NameValueListEditor.js.map +1 -1
- package/dist/editor/field-types/PictureFieldEditor.js +2 -2
- package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
- package/dist/editor/field-types/RawEditor.js +1 -1
- package/dist/editor/field-types/RawEditor.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +18 -36
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +1 -1
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/field-types/TreeListEditor.js +1 -1
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/bridgeRichTextProfile.d.ts +21 -0
- package/dist/editor/field-types/richtext/bridgeRichTextProfile.js +96 -0
- package/dist/editor/field-types/richtext/bridgeRichTextProfile.js.map +1 -0
- package/dist/editor/field-types/richtext/components/ReactSlate.css +44 -6
- package/dist/editor/field-types/richtext/components/ReactSlate.js +185 -36
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.css +5 -2
- package/dist/editor/field-types/richtext/components/SimpleToolbar.js +5 -4
- package/dist/editor/field-types/richtext/components/SimpleToolbar.js.map +1 -1
- package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +2 -14
- package/dist/editor/field-types/richtext/contextMenuFactory.js +4 -232
- package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
- package/dist/editor/field-types/richtext/richTextToolbarIcons.d.ts +7 -0
- package/dist/editor/field-types/richtext/richTextToolbarIcons.js +49 -0
- package/dist/editor/field-types/richtext/richTextToolbarIcons.js.map +1 -0
- package/dist/editor/field-types/richtext/utils/conversion.js +23 -2
- package/dist/editor/field-types/richtext/utils/conversion.js.map +1 -1
- package/dist/editor/fieldTypes.d.ts +2 -0
- package/dist/editor/media-selector/MediaFolderBrowser.d.ts +2 -1
- package/dist/editor/media-selector/MediaFolderBrowser.js +19 -9
- package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
- package/dist/editor/media-selector/TreeSelector.js +15 -9
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/media-selector/UploadZone.d.ts +2 -1
- package/dist/editor/media-selector/UploadZone.js +21 -9
- package/dist/editor/media-selector/UploadZone.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +10 -0
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +607 -85
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +3 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.d.ts +3 -1
- package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js +357 -73
- package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js.map +1 -1
- package/dist/editor/page-editor-chrome/PageEditorChrome.js +1 -1
- package/dist/editor/page-editor-chrome/PageEditorChrome.js.map +1 -1
- package/dist/editor/page-editor-chrome/bridgeInlineFormatToolbarLayout.d.ts +24 -0
- package/dist/editor/page-editor-chrome/bridgeInlineFormatToolbarLayout.js +89 -0
- package/dist/editor/page-editor-chrome/bridgeInlineFormatToolbarLayout.js.map +1 -0
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.d.ts +1 -1
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js +7 -1
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +23 -4
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +157 -2
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/reviews/Comment.js +2 -2
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +2 -1
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/CommentEditor.d.ts +1 -0
- package/dist/editor/reviews/CommentEditor.js +3 -2
- package/dist/editor/reviews/CommentEditor.js.map +1 -1
- package/dist/editor/reviews/CommentPopover.js +2 -2
- package/dist/editor/reviews/CommentPopover.js.map +1 -1
- package/dist/editor/reviews/Comments.js +5 -4
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/reviews/FeedbackCard.js +5 -7
- package/dist/editor/reviews/FeedbackCard.js.map +1 -1
- package/dist/editor/reviews/SuggestionCommentThread.js +3 -3
- package/dist/editor/reviews/SuggestionCommentThread.js.map +1 -1
- package/dist/editor/services/contentService.d.ts +1 -0
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/sidebar/Validation.js +4 -1
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/editor/template-wizard/TemplateStructureInlineEditor.js +1 -1
- package/dist/editor/template-wizard/TemplateStructureInlineEditor.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.js +8 -2
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/editor/ui/Splitter.d.ts +1 -0
- package/dist/editor/ui/Splitter.js +12 -2
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/ui/animationSettle.d.ts +32 -0
- package/dist/editor/ui/animationSettle.js +85 -0
- package/dist/editor/ui/animationSettle.js.map +1 -0
- package/dist/editor/utils/expandSelectionAtCaret.d.ts +15 -0
- package/dist/editor/utils/expandSelectionAtCaret.js +183 -0
- package/dist/editor/utils/expandSelectionAtCaret.js.map +1 -0
- package/dist/editor/views/MediaFolderEditView.js +1 -1
- package/dist/editor/views/MediaFolderEditView.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/DialogWrappers.js +2 -2
- package/dist/splash-screen/DialogWrappers.js.map +1 -1
- package/dist/task-board/components/TaskDetailPanel.js +2 -1
- package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
- package/dist/task-board/views/DependencyGraphView.d.ts +42 -1
- package/dist/task-board/views/DependencyGraphView.js +94 -0
- package/dist/task-board/views/DependencyGraphView.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
- package/styles.css +59 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx,
|
|
3
|
-
import { useState, useCallback, useRef, useEffect,
|
|
4
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useCallback, useRef, useEffect, useMemo, } from "react";
|
|
4
|
+
import { Check, X, ArrowUp, ChevronDown, ChevronLeft, Lightbulb, LoaderCircle, } from "lucide-react";
|
|
5
5
|
import { Button } from "../../components/ui/button";
|
|
6
6
|
import { Badge } from "../../components/ui/badge";
|
|
7
7
|
import { Textarea } from "../../components/ui/textarea";
|
|
@@ -15,9 +15,11 @@ import { subscribeAgent as subscribeAgentRegistry } from "../services/agentSubsc
|
|
|
15
15
|
import { getAiTextEditPrompts, getEditorSettings, } from "../services/systemService";
|
|
16
16
|
import { loadAiProfiles } from "../services/aiService";
|
|
17
17
|
import { localStorageService } from "../services/localStorageService";
|
|
18
|
+
import { diffWords } from "diff";
|
|
18
19
|
// Default Profile ID for the Inline Text Editor profile
|
|
19
20
|
const DEFAULT_INLINE_TEXT_EDITOR_PROFILE_ID = "8e53c90c-2eac-476b-b03d-d0c2cfbbcd8a";
|
|
20
21
|
const AI_TEXT_EDIT_MODELS_STORAGE_KEY = "aiTextEdit.selectedModelIds";
|
|
22
|
+
const START_AGENT_TIMEOUT_MS = 15_000;
|
|
21
23
|
/** Normalize profile ID to a stable key (lowercase, no braces) so storage works regardless of API casing. */
|
|
22
24
|
function normalizeProfileIdForStorage(profileId) {
|
|
23
25
|
return profileId.replace(/^\{|\}$/g, "").toLowerCase();
|
|
@@ -38,7 +40,7 @@ function getStoredModelIds(profileId) {
|
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
|
-
return Array.isArray(ids)
|
|
43
|
+
return Array.isArray(ids) ? ids : null;
|
|
42
44
|
}
|
|
43
45
|
catch {
|
|
44
46
|
return null;
|
|
@@ -68,10 +70,26 @@ function setStoredModelIds(profileId, modelIds) {
|
|
|
68
70
|
function isActiveModelRunStatus(status) {
|
|
69
71
|
return (status === "pending" || status === "thinking" || status === "streaming");
|
|
70
72
|
}
|
|
71
|
-
const
|
|
73
|
+
const AI_TEXT_EDIT_SINGLE_RESPONSE_FORMAT_INSTRUCTION = `Return ONLY valid JSON (no markdown, no code fences, no extra text).
|
|
72
74
|
Use exactly one of these shapes:
|
|
73
75
|
{"replacementText":"<the modified text>"}
|
|
74
76
|
{"error":"<short explanation of why you cannot complete this request>"}`;
|
|
77
|
+
function normalizeAiTextEditResultCount(resultCount) {
|
|
78
|
+
return typeof resultCount === "number" && Number.isFinite(resultCount)
|
|
79
|
+
? Math.max(1, Math.min(20, Math.floor(resultCount)))
|
|
80
|
+
: 1;
|
|
81
|
+
}
|
|
82
|
+
function getAiTextEditResponseFormatInstruction(resultCount) {
|
|
83
|
+
const normalizedResultCount = normalizeAiTextEditResultCount(resultCount);
|
|
84
|
+
if (normalizedResultCount <= 1) {
|
|
85
|
+
return AI_TEXT_EDIT_SINGLE_RESPONSE_FORMAT_INSTRUCTION;
|
|
86
|
+
}
|
|
87
|
+
return `Return ONLY valid JSON (no markdown, no code fences, no extra text).
|
|
88
|
+
Use exactly one of these shapes:
|
|
89
|
+
{"replacementTexts":["<result 1>","<result 2>"]}
|
|
90
|
+
{"error":"<short explanation of why you cannot complete this request>"}
|
|
91
|
+
Return exactly ${normalizedResultCount} results in replacementTexts. Each array item must be a complete standalone replacement/insert text. Do not include numbering or bullets inside the values.`;
|
|
92
|
+
}
|
|
75
93
|
function extractJsonObjectCandidates(input) {
|
|
76
94
|
const candidates = [];
|
|
77
95
|
for (let start = input.indexOf("{"); start >= 0; start = input.indexOf("{", start + 1)) {
|
|
@@ -173,6 +191,12 @@ function coerceAiTextEditResponse(parsed) {
|
|
|
173
191
|
if (typeof parsedObj.replacementText === "string") {
|
|
174
192
|
return { replacementText: parsedObj.replacementText };
|
|
175
193
|
}
|
|
194
|
+
if (Array.isArray(parsedObj.replacementTexts)) {
|
|
195
|
+
const replacementTexts = parsedObj.replacementTexts.filter((value) => typeof value === "string");
|
|
196
|
+
if (replacementTexts.length > 0) {
|
|
197
|
+
return { replacementTexts };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
176
200
|
return null;
|
|
177
201
|
}
|
|
178
202
|
function tryParseAiTextEditJson(json) {
|
|
@@ -204,7 +228,9 @@ export function parseAiTextEditResponse(raw) {
|
|
|
204
228
|
if (!parsed || typeof parsed !== "object") {
|
|
205
229
|
return { error: "AI returned an invalid response format." };
|
|
206
230
|
}
|
|
207
|
-
return {
|
|
231
|
+
return {
|
|
232
|
+
error: "AI response is missing `replacementText`, `replacementTexts`, or `error`.",
|
|
233
|
+
};
|
|
208
234
|
}
|
|
209
235
|
catch {
|
|
210
236
|
// Fall through to candidate extraction for mixed prose/JSON responses.
|
|
@@ -247,11 +273,69 @@ function extractAgentErrorMessage(message) {
|
|
|
247
273
|
}
|
|
248
274
|
return null;
|
|
249
275
|
}
|
|
276
|
+
function withTimeout(promise, timeoutMs, timeoutMessage) {
|
|
277
|
+
return new Promise((resolve, reject) => {
|
|
278
|
+
const timeoutId = window.setTimeout(() => {
|
|
279
|
+
reject(new Error(timeoutMessage));
|
|
280
|
+
}, timeoutMs);
|
|
281
|
+
promise.then((value) => {
|
|
282
|
+
window.clearTimeout(timeoutId);
|
|
283
|
+
resolve(value);
|
|
284
|
+
}, (error) => {
|
|
285
|
+
window.clearTimeout(timeoutId);
|
|
286
|
+
reject(error);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Shared instruction box: a rounded composer with the send button inside the
|
|
292
|
+
* textarea shell and the model selector below it.
|
|
293
|
+
*/
|
|
294
|
+
function InstructionComposer({ inputRef, value, onChange, onSubmit, onKeyDown, placeholder, minHeightClass, submitTitle, canSubmit, models, hasMultipleModels, modelPopoverOpen, onModelPopoverOpenChange, selectedModelIds, selectedModelsCount, selectedModelSummary, onToggleModel, onFocusChange, }) {
|
|
295
|
+
const formRef = useRef(null);
|
|
296
|
+
const [focused, setFocused] = useState(false);
|
|
297
|
+
// Single line at rest; grows to its full height while focused or holding text.
|
|
298
|
+
const isExpanded = focused || value.trim().length > 0;
|
|
299
|
+
// Report focus up so the popover width follows the custom prompt field.
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
onFocusChange?.(focused);
|
|
302
|
+
}, [focused, onFocusChange]);
|
|
303
|
+
useEffect(() => {
|
|
304
|
+
return () => onFocusChange?.(false);
|
|
305
|
+
}, [onFocusChange]);
|
|
306
|
+
// Collapse again once focus leaves the composer entirely. Interactions with the
|
|
307
|
+
// footer controls or the (portaled) model popover keep it expanded.
|
|
308
|
+
const handleBlur = () => {
|
|
309
|
+
window.setTimeout(() => {
|
|
310
|
+
const active = document.activeElement;
|
|
311
|
+
if (formRef.current?.contains(active ?? null) ||
|
|
312
|
+
(active instanceof Element &&
|
|
313
|
+
active.closest("[data-radix-popper-content-wrapper]"))) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
setFocused(false);
|
|
317
|
+
}, 0);
|
|
318
|
+
};
|
|
319
|
+
return (_jsx("form", { ref: formRef, onSubmit: (event) => {
|
|
320
|
+
event.preventDefault();
|
|
321
|
+
onSubmit();
|
|
322
|
+
}, className: "w-full", children: _jsx("div", { className: "flex w-full items-start gap-2", children: _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [_jsxs("div", { className: "border-border-default focus-within:border-neutral-grey-100 focus-within:ring-neutral-grey-100/10 bg-neutral-grey-5/30 relative min-w-0 overflow-hidden rounded-[12px] border transition-colors duration-200 focus-within:ring-4", children: [_jsx(Textarea, { ref: inputRef, value: value, onChange: (event) => onChange(event.target.value), onKeyDown: onKeyDown, onFocus: () => setFocused(true), onBlur: handleBlur, placeholder: placeholder, rows: 1, className: cn("w-full resize-none rounded-none border-0 bg-transparent px-2 pr-11 text-xs shadow-none transition-colors duration-200 outline-none focus-visible:border-0 focus-visible:shadow-none", isExpanded
|
|
323
|
+
? `${minHeightClass} pt-3 pb-10 leading-normal`
|
|
324
|
+
: "h-8 min-h-0 py-0 leading-8") }), _jsx("button", { type: "submit", disabled: !canSubmit, className: cn("absolute right-1.5 flex h-6 w-6 items-center justify-center rounded-[4px] p-0 transition-colors duration-200", isExpanded ? "bottom-1.5" : "top-1", canSubmit
|
|
325
|
+
? "bg-neutral-grey-100 hover:bg-neutral-grey-100/90 text-white"
|
|
326
|
+
: "bg-neutral-grey-5 text-neutral-grey-50 cursor-not-allowed"), title: submitTitle, children: _jsx(ArrowUp, { className: "h-4 w-4" }) })] }), hasMultipleModels && (_jsx("div", { className: "flex items-center overflow-hidden", children: _jsxs(Popover, { open: modelPopoverOpen, onOpenChange: onModelPopoverOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: cn("inline-flex items-center gap-1 rounded-[4px] px-2 py-1.5 text-[11px] font-medium transition-colors duration-200", "text-neutral-grey-50 hover:text-neutral-grey-100 hover:bg-neutral-grey-5", modelPopoverOpen &&
|
|
327
|
+
"text-neutral-grey-100 bg-neutral-grey-5"), title: selectedModelSummary
|
|
328
|
+
? `Models: ${selectedModelSummary}`
|
|
329
|
+
: "Choose models", children: [_jsx("span", { className: "max-w-[150px] truncate", children: selectedModelSummary ?? "Choose models" }), _jsx(ChevronDown, { className: "h-3 w-3" })] }) }), _jsxs(PopoverContent, { className: "w-56 p-1.5", align: "start", sideOffset: 8, children: [_jsxs("div", { className: "badge-pad flex items-center justify-between", children: [_jsx("span", { className: "text-neutral-grey-50 text-[10px] font-semibold tracking-wider", children: "Models" }), _jsxs("span", { className: "text-neutral-grey-100 text-[10px] font-medium", children: [selectedModelsCount, " selected"] })] }), _jsx("div", { className: "space-y-0.5", children: models.map((model) => (_jsxs("button", { type: "button", onClick: () => onToggleModel(model.id), className: cn("flex w-full items-center gap-2 rounded-[4px] px-2 py-1.5 text-left text-xs transition-colors", selectedModelIds.includes(model.id)
|
|
330
|
+
? "bg-neutral-grey-5 text-neutral-grey-100"
|
|
331
|
+
: "text-neutral-grey-50 hover:bg-neutral-grey-5"), children: [_jsx("span", { className: cn("flex h-3.5 w-3.5 items-center justify-center rounded border", selectedModelIds.includes(model.id)
|
|
332
|
+
? "border-neutral-grey-100 bg-neutral-grey-100 text-white"
|
|
333
|
+
: "border-border-default bg-white text-transparent"), children: _jsx(Check, { className: "h-3 w-3" }) }), model.name] }, model.id))) })] })] }) }))] }) }) }));
|
|
334
|
+
}
|
|
250
335
|
export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter, onAccept, onClose, position, className, }) {
|
|
251
336
|
const editContext = useEditContext();
|
|
252
337
|
const [state, setState] = useState("input");
|
|
253
338
|
const [instruction, setInstruction] = useState("");
|
|
254
|
-
const [result, setResult] = useState("");
|
|
255
339
|
const [error, setError] = useState(null);
|
|
256
340
|
const [conversationHistory, setConversationHistory] = useState([]);
|
|
257
341
|
const [quickActions, setQuickActions] = useState([]);
|
|
@@ -261,8 +345,6 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
261
345
|
const [modelRuns, setModelRuns] = useState({});
|
|
262
346
|
const inputRef = useRef(null);
|
|
263
347
|
const dialogRef = useRef(null);
|
|
264
|
-
const [isLarge, setIsLarge] = useState(false);
|
|
265
|
-
const [clampedPosition, setClampedPosition] = useState(position);
|
|
266
348
|
// Agent state for the current multi-model run
|
|
267
349
|
const activeAgentIdsRef = useRef(new Set());
|
|
268
350
|
const unsubscribeRef = useRef(null);
|
|
@@ -271,6 +353,8 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
271
353
|
const lastParsedRawRef = useRef({});
|
|
272
354
|
// Model popover state
|
|
273
355
|
const [modelPopoverOpen, setModelPopoverOpen] = useState(false);
|
|
356
|
+
// Whether the custom-prompt composer is focused, so the popover can grow with it.
|
|
357
|
+
const [promptFocused, setPromptFocused] = useState(false);
|
|
274
358
|
// ALT+letter quick select state
|
|
275
359
|
const [altSearchBuffer, setAltSearchBuffer] = useState("");
|
|
276
360
|
const [isAltPressed, setIsAltPressed] = useState(false);
|
|
@@ -286,7 +370,7 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
286
370
|
const selected = selectedModelIds
|
|
287
371
|
.map((id) => byId.get(id))
|
|
288
372
|
.filter(Boolean);
|
|
289
|
-
return selected
|
|
373
|
+
return selected;
|
|
290
374
|
}, [selectedModelIds, availableModels]);
|
|
291
375
|
const selectedModelSummary = useMemo(() => {
|
|
292
376
|
if (!selectedModels.length)
|
|
@@ -295,16 +379,6 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
295
379
|
return selectedModels[0]?.name || null;
|
|
296
380
|
return `${selectedModels.length} models selected`;
|
|
297
381
|
}, [selectedModels]);
|
|
298
|
-
const successfulRuns = useMemo(() => Object.values(modelRuns).filter((run) => run.status === "done" &&
|
|
299
|
-
typeof run.replacementText === "string" &&
|
|
300
|
-
run.replacementText.length > 0), [modelRuns]);
|
|
301
|
-
const hasMultipleResults = successfulRuns.length > 1;
|
|
302
|
-
// Automatically switch to larger mode when showing more than one response
|
|
303
|
-
useEffect(() => {
|
|
304
|
-
if (state === "preview" && hasMultipleResults) {
|
|
305
|
-
setIsLarge(true);
|
|
306
|
-
}
|
|
307
|
-
}, [state, hasMultipleResults]);
|
|
308
382
|
useEffect(() => {
|
|
309
383
|
const loadSettings = async () => {
|
|
310
384
|
try {
|
|
@@ -343,7 +417,10 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
343
417
|
const defaultModelId = profile.defaultModelId || profile.models?.[0]?.id;
|
|
344
418
|
const stored = getStoredModelIds(profileId);
|
|
345
419
|
const validStored = stored?.filter((id) => availableModelIds.has(id));
|
|
346
|
-
if (
|
|
420
|
+
if (stored && stored.length === 0) {
|
|
421
|
+
setSelectedModelIds([]);
|
|
422
|
+
}
|
|
423
|
+
else if (validStored?.length) {
|
|
347
424
|
setSelectedModelIds(validStored);
|
|
348
425
|
}
|
|
349
426
|
else if (defaultModelId) {
|
|
@@ -362,16 +439,12 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
362
439
|
}, [modelRuns]);
|
|
363
440
|
// Persist model selection to localStorage when it or profileId changes
|
|
364
441
|
useEffect(() => {
|
|
365
|
-
if (profileId &&
|
|
442
|
+
if (profileId && activeProfile) {
|
|
366
443
|
setStoredModelIds(profileId, selectedModelIds);
|
|
367
444
|
}
|
|
368
|
-
}, [profileId, selectedModelIds]);
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
if (inputRef.current) {
|
|
372
|
-
inputRef.current.focus();
|
|
373
|
-
}
|
|
374
|
-
}, []);
|
|
445
|
+
}, [profileId, activeProfile, selectedModelIds]);
|
|
446
|
+
// The custom-prompt composer starts collapsed (single line) and expands on
|
|
447
|
+
// focus, so the input is intentionally not auto-focused on mount.
|
|
375
448
|
const evaluateRunCompletion = useCallback((runs) => {
|
|
376
449
|
const values = Object.values(runs);
|
|
377
450
|
if (!values.length)
|
|
@@ -379,19 +452,13 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
379
452
|
const allTerminal = values.every((run) => run.status === "done" || run.status === "error");
|
|
380
453
|
if (!allTerminal)
|
|
381
454
|
return;
|
|
382
|
-
const doneRuns = values.filter((run) => run.status === "done" &&
|
|
455
|
+
const doneRuns = values.filter((run) => run.status === "done" &&
|
|
456
|
+
(run.replacementTexts?.length || run.replacementText?.length));
|
|
383
457
|
if (doneRuns.length === 0) {
|
|
384
458
|
setError("All selected models failed. See each model’s card for details. You can retry or change your instruction.");
|
|
385
|
-
setResult("");
|
|
386
459
|
// Stay in preview so the user sees per-model error cards and can Retry/Discard
|
|
387
460
|
return;
|
|
388
461
|
}
|
|
389
|
-
if (doneRuns.length === 1) {
|
|
390
|
-
setResult(doneRuns[0]?.replacementText ?? "");
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
setResult("");
|
|
394
|
-
}
|
|
395
462
|
setError(null);
|
|
396
463
|
// State is already "preview" – we show results side-by-side as they stream in
|
|
397
464
|
}, []);
|
|
@@ -415,13 +482,6 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
415
482
|
return;
|
|
416
483
|
subscriptionReleasesRef.current.set(agentId, subscribeAgentRegistry(agentId));
|
|
417
484
|
}, []);
|
|
418
|
-
const releaseAgentSubscription = useCallback((agentId) => {
|
|
419
|
-
const release = subscriptionReleasesRef.current.get(agentId);
|
|
420
|
-
if (release) {
|
|
421
|
-
release();
|
|
422
|
-
subscriptionReleasesRef.current.delete(agentId);
|
|
423
|
-
}
|
|
424
|
-
}, []);
|
|
425
485
|
const unsubscribeAllAgents = useCallback(() => {
|
|
426
486
|
for (const release of subscriptionReleasesRef.current.values()) {
|
|
427
487
|
release();
|
|
@@ -436,22 +496,6 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
436
496
|
latestRawContentRef.current = {};
|
|
437
497
|
lastParsedRawRef.current = {};
|
|
438
498
|
}, []);
|
|
439
|
-
const handleStopModelRun = useCallback((agentId) => {
|
|
440
|
-
activeAgentIdsRef.current.delete(agentId);
|
|
441
|
-
delete latestRawContentRef.current[agentId];
|
|
442
|
-
delete lastParsedRawRef.current[agentId];
|
|
443
|
-
releaseAgentSubscription(agentId);
|
|
444
|
-
closeAgent(agentId).catch(() => { });
|
|
445
|
-
updateModelRun(agentId, (current) => ({
|
|
446
|
-
...current,
|
|
447
|
-
status: "error",
|
|
448
|
-
error: "Stopped by user",
|
|
449
|
-
}));
|
|
450
|
-
}, [updateModelRun, releaseAgentSubscription]);
|
|
451
|
-
const handleOpenAgentInSidePanel = useCallback((agentId) => {
|
|
452
|
-
editContext?.openSidebar?.("agents-panel");
|
|
453
|
-
window.dispatchEvent(new CustomEvent("editor:openAgent", { detail: { agentId } }));
|
|
454
|
-
}, [editContext]);
|
|
455
499
|
// Cleanup on unmount
|
|
456
500
|
useEffect(() => {
|
|
457
501
|
return () => {
|
|
@@ -568,10 +612,13 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
568
612
|
}));
|
|
569
613
|
}
|
|
570
614
|
else {
|
|
615
|
+
const replacementTexts = parsed.replacementTexts ??
|
|
616
|
+
(parsed.replacementText != null ? [parsed.replacementText] : []);
|
|
571
617
|
updateModelRun(messageAgentId, (run) => ({
|
|
572
618
|
...run,
|
|
573
619
|
status: "done",
|
|
574
|
-
replacementText:
|
|
620
|
+
replacementText: replacementTexts[0] ?? "",
|
|
621
|
+
replacementTexts,
|
|
575
622
|
error: undefined,
|
|
576
623
|
}));
|
|
577
624
|
}
|
|
@@ -580,7 +627,7 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
580
627
|
unsubscribeRef.current =
|
|
581
628
|
editContext.addSocketMessageListener(handleMessage);
|
|
582
629
|
}, [editContext, updateModelRun]);
|
|
583
|
-
const executeAiModification = useCallback(async (customInstruction) => {
|
|
630
|
+
const executeAiModification = useCallback(async (customInstruction, resultCount = 1) => {
|
|
584
631
|
if (!editContext)
|
|
585
632
|
return;
|
|
586
633
|
if (!selectedModels.length) {
|
|
@@ -588,7 +635,6 @@ export function InlineAiDialog({ selectedText = "", contextBefore, contextAfter,
|
|
|
588
635
|
return;
|
|
589
636
|
}
|
|
590
637
|
setError(null);
|
|
591
|
-
setResult("");
|
|
592
638
|
setModelRuns({});
|
|
593
639
|
modelRunsRef.current = {};
|
|
594
640
|
latestRawContentRef.current = {};
|
|
@@ -605,6 +651,8 @@ After: "${contextAfter || ""}"
|
|
|
605
651
|
|
|
606
652
|
`
|
|
607
653
|
: "";
|
|
654
|
+
const responseFormatInstruction = getAiTextEditResponseFormatInstruction(resultCount);
|
|
655
|
+
const requestedResultCount = normalizeAiTextEditResultCount(resultCount);
|
|
608
656
|
let message;
|
|
609
657
|
if (selectedText) {
|
|
610
658
|
message = `${contextInfo}I need you to modify ONLY the following selected portion of the text:
|
|
@@ -617,7 +665,7 @@ ${selectedText}
|
|
|
617
665
|
${customInstruction}
|
|
618
666
|
|
|
619
667
|
Return ONLY the modified version of the selected text, not the entire field.
|
|
620
|
-
${
|
|
668
|
+
${responseFormatInstruction}`;
|
|
621
669
|
}
|
|
622
670
|
else {
|
|
623
671
|
message = `${contextInfo}I need you to generate text at the cursor position.
|
|
@@ -625,7 +673,7 @@ ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
|
|
|
625
673
|
${customInstruction}
|
|
626
674
|
|
|
627
675
|
Return ONLY the text to insert at the cursor position, not the entire field.
|
|
628
|
-
${
|
|
676
|
+
${responseFormatInstruction}`;
|
|
629
677
|
}
|
|
630
678
|
setConversationHistory((prev) => [...prev, customInstruction]);
|
|
631
679
|
let agentContext = undefined;
|
|
@@ -672,6 +720,7 @@ ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
|
|
|
672
720
|
agentId,
|
|
673
721
|
modelId: model.id,
|
|
674
722
|
modelName: model.name,
|
|
723
|
+
requestedResultCount,
|
|
675
724
|
status: "pending",
|
|
676
725
|
rawContent: "",
|
|
677
726
|
};
|
|
@@ -682,7 +731,7 @@ ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
|
|
|
682
731
|
await Promise.all(Object.values(initialRuns).map(async (run) => {
|
|
683
732
|
subscribeAgentInline(run.agentId);
|
|
684
733
|
try {
|
|
685
|
-
await startAgent({
|
|
734
|
+
await withTimeout(startAgent({
|
|
686
735
|
agentId: run.agentId,
|
|
687
736
|
message,
|
|
688
737
|
sessionId: editContext.sessionId,
|
|
@@ -690,6 +739,11 @@ ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
|
|
|
690
739
|
mode: "autonomous",
|
|
691
740
|
model: run.modelId,
|
|
692
741
|
context: agentContext,
|
|
742
|
+
}), START_AGENT_TIMEOUT_MS, `Timed out starting ${run.modelName}. Please try again.`);
|
|
743
|
+
updateModelRun(run.agentId, (current) => {
|
|
744
|
+
if (current.status !== "pending")
|
|
745
|
+
return current;
|
|
746
|
+
return { ...current, status: "thinking" };
|
|
693
747
|
});
|
|
694
748
|
}
|
|
695
749
|
catch (runErr) {
|
|
@@ -724,7 +778,7 @@ ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
|
|
|
724
778
|
updateModelRun,
|
|
725
779
|
]);
|
|
726
780
|
const handleQuickAction = useCallback((action) => {
|
|
727
|
-
executeAiModification(action.instruction);
|
|
781
|
+
executeAiModification(action.instruction, action.resultCount);
|
|
728
782
|
}, [executeAiModification]);
|
|
729
783
|
// ALT+letter quick select for quick actions
|
|
730
784
|
useEffect(() => {
|
|
@@ -803,72 +857,131 @@ ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
|
|
|
803
857
|
return;
|
|
804
858
|
onAccept(replacementText);
|
|
805
859
|
}, [onAccept]);
|
|
860
|
+
const handleModelBadgeClick = useCallback((event, agentId) => {
|
|
861
|
+
if (!event.ctrlKey)
|
|
862
|
+
return;
|
|
863
|
+
event.preventDefault();
|
|
864
|
+
event.stopPropagation();
|
|
865
|
+
if (editContext?.isMobile) {
|
|
866
|
+
editContext.openSidebar("agents-panel");
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
editContext?.setShowAgentsPanel(true);
|
|
870
|
+
}
|
|
871
|
+
const dispatchOpenAgent = () => {
|
|
872
|
+
window.dispatchEvent(new CustomEvent("editor:openAgent", {
|
|
873
|
+
detail: { agentId },
|
|
874
|
+
}));
|
|
875
|
+
};
|
|
876
|
+
if (editContext?.isMobile) {
|
|
877
|
+
window.setTimeout(dispatchOpenAgent, 500);
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
dispatchOpenAgent();
|
|
881
|
+
}
|
|
882
|
+
}, [editContext]);
|
|
806
883
|
const toggleModelSelection = useCallback((modelId) => {
|
|
807
884
|
setSelectedModelIds((prev) => {
|
|
808
885
|
if (prev.includes(modelId)) {
|
|
809
|
-
if (prev.length === 1) {
|
|
810
|
-
return prev;
|
|
811
|
-
}
|
|
812
886
|
return prev.filter((id) => id !== modelId);
|
|
813
887
|
}
|
|
814
888
|
return [...prev, modelId];
|
|
815
889
|
});
|
|
816
890
|
}, []);
|
|
817
891
|
const handleTryAgain = useCallback(() => {
|
|
818
|
-
// Go back to input state to refine
|
|
819
|
-
setState("input");
|
|
820
|
-
setResult("");
|
|
821
|
-
}, []);
|
|
822
|
-
const handleRetry = useCallback(() => {
|
|
823
|
-
const lastInstruction = conversationHistory[conversationHistory.length - 1];
|
|
824
|
-
if (lastInstruction) {
|
|
825
|
-
executeAiModification(lastInstruction);
|
|
826
|
-
}
|
|
827
|
-
}, [conversationHistory, executeAiModification]);
|
|
828
|
-
const handleStartOver = useCallback(() => {
|
|
829
892
|
unsubscribeAllAgents();
|
|
830
893
|
closeAllActiveAgents();
|
|
894
|
+
setState("input");
|
|
895
|
+
setInstruction("");
|
|
896
|
+
setError(null);
|
|
897
|
+
setConversationHistory([]);
|
|
831
898
|
setModelRuns({});
|
|
832
899
|
modelRunsRef.current = {};
|
|
833
900
|
latestRawContentRef.current = {};
|
|
834
901
|
lastParsedRawRef.current = {};
|
|
835
|
-
setState("input");
|
|
836
|
-
setResult("");
|
|
837
|
-
setConversationHistory([]);
|
|
838
|
-
setError(null);
|
|
839
902
|
}, [unsubscribeAllAgents, closeAllActiveAgents]);
|
|
840
903
|
const handleClose = useCallback(() => {
|
|
841
904
|
unsubscribeAllAgents();
|
|
842
905
|
closeAllActiveAgents();
|
|
843
906
|
onClose();
|
|
844
907
|
}, [unsubscribeAllAgents, closeAllActiveAgents, onClose]);
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
908
|
+
useEffect(() => {
|
|
909
|
+
const handleEscapeKey = (event) => {
|
|
910
|
+
if (event.key !== "Escape")
|
|
911
|
+
return;
|
|
912
|
+
event.preventDefault();
|
|
913
|
+
event.stopPropagation();
|
|
914
|
+
handleClose();
|
|
915
|
+
};
|
|
916
|
+
document.addEventListener("keydown", handleEscapeKey, true);
|
|
917
|
+
return () => {
|
|
918
|
+
document.removeEventListener("keydown", handleEscapeKey, true);
|
|
919
|
+
};
|
|
920
|
+
}, [handleClose]);
|
|
848
921
|
const renderTextWithContext = (centerText, isResult = false) => {
|
|
849
922
|
const hasContext = contextBefore || contextAfter;
|
|
850
923
|
// No context available - just show the selected text
|
|
851
924
|
if (!hasContext) {
|
|
852
925
|
return (_jsx("span", { className: cn("rounded px-0.5 font-medium", isResult
|
|
853
926
|
? "bg-feedback-green-light text-feedback-green"
|
|
854
|
-
: "bg-
|
|
927
|
+
: "bg-neutral-grey-5 text-neutral-grey-100 italic"), children: centerText }));
|
|
855
928
|
}
|
|
856
929
|
// Show context with ellipsis indicators
|
|
857
930
|
return (_jsxs("div", { className: "text-xs leading-relaxed whitespace-pre-wrap", children: [contextBefore && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-neutral-grey-50", children: "..." }), _jsx("span", { className: "text-neutral-grey-50", children: contextBefore })] })), _jsx("span", { className: cn("mx-0.5 rounded px-0.5 font-medium", isResult
|
|
858
931
|
? "bg-feedback-green-light text-feedback-green"
|
|
859
|
-
: "bg-
|
|
932
|
+
: "bg-neutral-grey-5 text-neutral-grey-100 italic"), children: centerText }), contextAfter && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-neutral-grey-50", children: contextAfter }), _jsx("span", { className: "text-neutral-grey-50", children: "..." })] }))] }));
|
|
933
|
+
};
|
|
934
|
+
const renderSuggestionPreview = (replacementText) => {
|
|
935
|
+
const hasContext = contextBefore || contextAfter;
|
|
936
|
+
const hasOriginalSelection = selectedText.trim().length > 0;
|
|
937
|
+
const diff = hasOriginalSelection
|
|
938
|
+
? diffWords(selectedText, replacementText)
|
|
939
|
+
: [];
|
|
940
|
+
const suggestedTextClass = "bg-feedback-green-light text-feedback-green rounded px-0.5";
|
|
941
|
+
return (_jsxs("div", { className: "font-mono text-sm whitespace-pre-wrap", children: [hasContext && contextBefore && (_jsxs("span", { className: "text-neutral-grey-50", children: [_jsx("span", { children: "..." }), _jsx("span", { children: contextBefore })] })), hasOriginalSelection ? (diff.map((part, index) => {
|
|
942
|
+
if (part.added) {
|
|
943
|
+
return (_jsx("span", { className: suggestedTextClass, children: part.value }, index));
|
|
944
|
+
}
|
|
945
|
+
if (part.removed) {
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
return (_jsx("span", { className: suggestedTextClass, children: part.value }, index));
|
|
949
|
+
})) : (_jsx("span", { className: suggestedTextClass, children: replacementText })), hasContext && contextAfter && (_jsxs("span", { className: "text-neutral-grey-50", children: [_jsx("span", { children: contextAfter }), _jsx("span", { children: "..." })] }))] }));
|
|
860
950
|
};
|
|
951
|
+
const modelRunValues = useMemo(() => Object.values(modelRuns), [modelRuns]);
|
|
952
|
+
const useCompactResultList = useMemo(() => modelRunValues.some((run) => run.requestedResultCount > 1 ||
|
|
953
|
+
(run.replacementTexts?.length ?? 0) > 1), [modelRunValues]);
|
|
954
|
+
const compactSuggestions = useMemo(() => {
|
|
955
|
+
const seenSuggestions = new Set();
|
|
956
|
+
return modelRunValues.flatMap((run) => {
|
|
957
|
+
const replacementTexts = run.replacementTexts ??
|
|
958
|
+
(run.replacementText != null ? [run.replacementText] : []);
|
|
959
|
+
return replacementTexts
|
|
960
|
+
.filter((replacementText) => {
|
|
961
|
+
const dedupeKey = replacementText
|
|
962
|
+
.trim()
|
|
963
|
+
.replace(/\s+/g, " ")
|
|
964
|
+
.toLocaleLowerCase();
|
|
965
|
+
if (!dedupeKey || seenSuggestions.has(dedupeKey)) {
|
|
966
|
+
return false;
|
|
967
|
+
}
|
|
968
|
+
seenSuggestions.add(dedupeKey);
|
|
969
|
+
return true;
|
|
970
|
+
})
|
|
971
|
+
.map((replacementText, index) => ({
|
|
972
|
+
id: `${run.agentId}-${index}`,
|
|
973
|
+
agentId: run.agentId,
|
|
974
|
+
modelName: run.modelName,
|
|
975
|
+
text: replacementText,
|
|
976
|
+
}));
|
|
977
|
+
});
|
|
978
|
+
}, [modelRunValues]);
|
|
861
979
|
const handleKeyDown = useCallback((e) => {
|
|
862
980
|
if (e.key === "Escape") {
|
|
863
981
|
e.preventDefault();
|
|
864
|
-
|
|
865
|
-
handleTryAgain();
|
|
866
|
-
}
|
|
867
|
-
else {
|
|
868
|
-
handleClose();
|
|
869
|
-
}
|
|
982
|
+
handleClose();
|
|
870
983
|
}
|
|
871
|
-
}, [
|
|
984
|
+
}, [handleClose]);
|
|
872
985
|
const handleTextareaKeyDown = useCallback((e) => {
|
|
873
986
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
874
987
|
e.preventDefault();
|
|
@@ -878,67 +991,16 @@ ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
|
|
|
878
991
|
}
|
|
879
992
|
}
|
|
880
993
|
}, [instruction, executeAiModification]);
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
if (!position) {
|
|
885
|
-
if (clampedPosition !== undefined)
|
|
886
|
-
setClampedPosition(undefined);
|
|
887
|
-
return;
|
|
888
|
-
}
|
|
889
|
-
const margin = 12;
|
|
890
|
-
const vw = window.innerWidth || 0;
|
|
891
|
-
const vh = window.innerHeight || 0;
|
|
892
|
-
// Use ResizeObserver to track the actual size of the dialog as it changes
|
|
893
|
-
const updatePosition = () => {
|
|
894
|
-
const el = dialogRef.current;
|
|
895
|
-
if (!el)
|
|
896
|
-
return;
|
|
897
|
-
const width = el.offsetWidth || 0;
|
|
898
|
-
const height = el.offsetHeight || 0;
|
|
899
|
-
if (!width || !height || !vw || !vh) {
|
|
900
|
-
const next = { x: position.x, y: position.y };
|
|
901
|
-
if (clampedPosition?.x !== next.x || clampedPosition?.y !== next.y)
|
|
902
|
-
setClampedPosition(next);
|
|
903
|
-
return;
|
|
904
|
-
}
|
|
905
|
-
const x = Math.min(Math.max(margin, position.x), Math.max(margin, vw - width - margin));
|
|
906
|
-
const y = Math.min(Math.max(margin, position.y), Math.max(margin, vh - height - margin));
|
|
907
|
-
if (clampedPosition?.x !== x || clampedPosition?.y !== y) {
|
|
908
|
-
setClampedPosition({ x, y });
|
|
909
|
-
}
|
|
910
|
-
};
|
|
911
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
912
|
-
updatePosition();
|
|
913
|
-
});
|
|
914
|
-
if (dialogRef.current) {
|
|
915
|
-
resizeObserver.observe(dialogRef.current);
|
|
916
|
-
}
|
|
917
|
-
// Initial update
|
|
918
|
-
updatePosition();
|
|
919
|
-
return () => {
|
|
920
|
-
resizeObserver.disconnect();
|
|
921
|
-
};
|
|
922
|
-
// Re-clamp when the content changes (height changes) or when the requested position changes.
|
|
923
|
-
}, [
|
|
924
|
-
position?.x,
|
|
925
|
-
position?.y,
|
|
926
|
-
state,
|
|
927
|
-
result,
|
|
928
|
-
selectedText,
|
|
929
|
-
isLarge,
|
|
930
|
-
modelRuns,
|
|
931
|
-
hasMultipleResults,
|
|
932
|
-
]);
|
|
933
|
-
return (_jsxs("div", { ref: dialogRef, className: cn("border-border-default agent-inline-dialog flex max-h-[80vh] flex-col overflow-hidden rounded-xl border bg-white/95 shadow-[0_10px_40px_color-mix(in_srgb,var(--color-neutral-grey-100)_10%,transparent)] backdrop-blur-sm transition-all duration-300 ease-in-out", isLarge ? "w-[800px]" : "w-[420px]", "animate-in fade-in-0 zoom-in-95 duration-200 ease-out", className), style: clampedPosition
|
|
994
|
+
return (_jsxs("div", { ref: dialogRef, className: cn("border-border-default agent-inline-dialog flex max-h-[80vh] flex-col overflow-hidden rounded-2xl border bg-white/95 shadow-lg backdrop-blur-sm transition-all duration-300 ease-in-out [&_button]:rounded-[4px]",
|
|
995
|
+
// Popover grows while the custom prompt field is focused or results are shown.
|
|
996
|
+
promptFocused || state === "preview" ? "w-[390px]" : "w-[190px]", "animate-in fade-in-0 zoom-in-95 duration-200 ease-out", className), style: position
|
|
934
997
|
? {
|
|
935
998
|
position: "fixed",
|
|
936
|
-
left:
|
|
937
|
-
top:
|
|
938
|
-
zIndex:
|
|
999
|
+
left: position.x,
|
|
1000
|
+
top: position.y,
|
|
1001
|
+
zIndex: 1100,
|
|
939
1002
|
}
|
|
940
|
-
: undefined, onKeyDown: handleKeyDown, children: [_jsxs("div", { className: "
|
|
941
|
-
"bg-highlight-10 text-highlight-100 hover:bg-highlight-10"), title: isLarge ? "Make smaller" : "Make larger", children: _jsx(Maximize2, { className: cn("text-neutral-grey-50 group-hover:text-neutral-grey-50 h-4 w-4", isLarge && "text-highlight-100") }) }), _jsx(CloseButton, { onClick: handleClose, label: "Close" })] })] }), selectedText && (_jsxs("div", { className: "bg-neutral-grey-5/50 border-border-default flex-shrink-0 border-b px-4 py-3", children: [_jsxs("div", { className: "text-neutral-grey-50 mb-2 flex items-center gap-1.5 text-[10px] font-bold tracking-wider", children: [_jsx("div", { className: "bg-neutral-grey-10 h-1.5 w-1.5 rounded-full" }), "Selected Content"] }), _jsxs("div", { className: "relative max-h-48 overflow-y-auto whitespace-pre-wrap transition-all duration-300", children: [_jsx("div", { className: "bg-highlight-10 absolute top-0 bottom-0 left-0 w-0.5 rounded-full" }), _jsx("div", { className: "pl-3", children: renderTextWithContext(selectedText) })] })] })), _jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children: [state === "input" && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [isAltPressed && (_jsxs("div", { className: "text-highlight-100 bg-highlight-10 animate-in fade-in flex items-center gap-2 rounded-lg px-2 py-1.5 text-xs duration-150", children: [_jsx("kbd", { className: "badge-pad-sm bg-highlight-10 rounded font-mono text-[10px] font-bold", children: "ALT" }), _jsx("span", { className: "font-medium", children: altSearchBuffer ? (_jsxs(_Fragment, { children: ["Type to select:", " ", _jsx("span", { className: "font-bold", children: altSearchBuffer })] })) : ("Type first letters of an action...") })] })), _jsx("div", { className: "flex flex-wrap gap-2", children: quickActions.map((action) => {
|
|
1003
|
+
: undefined, onKeyDown: handleKeyDown, children: [state === "preview" && (_jsxs("div", { className: "border-border-default flex flex-shrink-0 items-center gap-2.5 border-b px-3 py-3", children: [_jsx("button", { onClick: handleTryAgain, className: "hover:bg-neutral-grey-5 -ml-1.5 rounded-[4px] p-1.5 transition-colors", title: "Back", "aria-label": "Back", children: _jsx(ChevronLeft, { className: "text-neutral-grey-50 h-4 w-4" }) }), _jsx("span", { className: "text-neutral-grey-100 text-sm leading-tight font-semibold", children: "Choose a result" }), _jsx("div", { className: "ml-auto flex items-center gap-1", children: _jsx(CloseButton, { onClick: handleClose, label: "Close" }) })] })), _jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto p-3", children: [state === "input" && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [isAltPressed && (_jsxs("div", { className: "text-neutral-grey-100 bg-neutral-grey-5 animate-in fade-in flex items-center gap-2 rounded-xl px-2 py-1.5 text-xs duration-150", children: [_jsx("kbd", { className: "badge-pad-sm bg-neutral-grey-5 rounded font-mono text-[10px] font-bold", children: "ALT" }), _jsx("span", { className: "font-medium", children: altSearchBuffer ? (_jsxs(_Fragment, { children: ["Type to select:", " ", _jsx("span", { className: "font-bold", children: altSearchBuffer })] })) : ("Type first letters of an action...") })] })), _jsx("div", { className: "flex flex-col", children: quickActions.map((action) => {
|
|
942
1004
|
const hasSvgIcon = action.icon?.trim() &&
|
|
943
1005
|
(action.icon.trim().startsWith("<svg") ||
|
|
944
1006
|
action.icon.trim().startsWith("<?xml"));
|
|
@@ -950,65 +1012,44 @@ ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
|
|
|
950
1012
|
altSearchBuffer &&
|
|
951
1013
|
labelLower.includes(altSearchBuffer) &&
|
|
952
1014
|
!isMatch;
|
|
953
|
-
return (_jsxs("button", { onClick: () => handleQuickAction(action), className: cn("flex items-center gap-
|
|
954
|
-
"
|
|
955
|
-
"border-highlight-100 bg-highlight-10/50", isAltPressed &&
|
|
1015
|
+
return (_jsxs("button", { onClick: () => handleQuickAction(action), className: cn("flex w-full items-center gap-2.5 rounded-[4px] px-2 py-2 text-left text-xs transition-colors duration-150", "text-neutral-grey-100 hover:text-neutral-grey-100 hover:bg-neutral-grey-5", isMatch &&
|
|
1016
|
+
"bg-neutral-grey-5 text-neutral-grey-100 ring-neutral-grey-100 ring-2", isPartialMatch && "bg-neutral-grey-5/50", isAltPressed &&
|
|
956
1017
|
!isMatch &&
|
|
957
1018
|
!isPartialMatch &&
|
|
958
|
-
"opacity-50"), children: [hasSvgIcon && (_jsx("div", { className: "flex h-
|
|
1019
|
+
"opacity-50"), children: [hasSvgIcon && (_jsx("div", { className: "text-neutral-grey-50 flex h-4 w-4 shrink-0 items-center justify-start [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
|
|
959
1020
|
__html: sanitizeSvg(action.icon.trim()),
|
|
960
1021
|
} })), isAltPressed && altSearchBuffer ? (_jsxs("span", { children: [_jsx("span", { className: cn(isMatch &&
|
|
961
1022
|
"font-bold underline underline-offset-2"), children: action.label.slice(0, altSearchBuffer.length) }), action.label.slice(altSearchBuffer.length)] })) : (action.label)] }, action.id));
|
|
962
|
-
}) })] }),
|
|
963
|
-
e.preventDefault();
|
|
1023
|
+
}) })] }), _jsx(InstructionComposer, { inputRef: inputRef, value: instruction, onChange: setInstruction, onSubmit: () => {
|
|
964
1024
|
if (instruction.trim()) {
|
|
965
1025
|
executeAiModification(instruction.trim());
|
|
966
1026
|
setInstruction("");
|
|
967
1027
|
}
|
|
968
|
-
},
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
"border-feedback-green bg-feedback-green-light/40", run.status === "error" &&
|
|
983
|
-
"border-feedback-red bg-feedback-red-light/30", isActiveModelRunStatus(run.status) &&
|
|
984
|
-
"border-highlight-100 bg-highlight-10/30"), children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("div", { className: "text-neutral-grey-100 truncate text-xs font-semibold", children: run.modelName }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => handleOpenAgentInSidePanel(run.agentId), className: "text-neutral-grey-50 hover:text-highlight-100 hover:bg-highlight-10 rounded p-1 transition-colors", title: "Open agent in side panel", children: _jsx(Bug, { className: "h-3 w-3" }) }), isActiveModelRunStatus(run.status) && (_jsx("button", { type: "button", onClick: () => handleStopModelRun(run.agentId), className: "text-neutral-grey-50 hover:text-feedback-red hover:bg-feedback-red-light rounded-lg p-1.5 transition-colors", title: "Stop this model", children: _jsx(StopCircle, { className: "h-4 w-4" }) })), _jsxs(Badge, { variant: "outline", className: cn(run.status === "done" &&
|
|
985
|
-
"border-feedback-green text-feedback-green", run.status === "error" &&
|
|
986
|
-
"border-feedback-red text-feedback-red", isActiveModelRunStatus(run.status) &&
|
|
987
|
-
"border-highlight-100 text-highlight-100"), children: [run.status === "pending" && "Starting", run.status === "thinking" && "Thinking", run.status === "streaming" && "Streaming…", run.status === "done" && "Ready", run.status === "error" && "Error"] })] })] }), _jsxs("div", { className: cn("min-h-[80px] overflow-y-auto rounded-lg border p-2 text-xs whitespace-pre-wrap", run.status === "done" &&
|
|
988
|
-
"border-feedback-green bg-white/80", run.status === "error" &&
|
|
989
|
-
"border-feedback-red text-feedback-red bg-white/80", isActiveModelRunStatus(run.status) &&
|
|
990
|
-
"border-highlight-100 bg-white/60"), style: { maxHeight: isLarge ? "420px" : "200px" }, children: [run.status === "pending" && (_jsx("span", { className: "text-neutral-grey-50 italic", children: "Starting\u2026" })), run.status === "thinking" && (_jsx("span", { className: "text-neutral-grey-50 italic", children: "Thinking..." })), run.status === "streaming" &&
|
|
1028
|
+
}, onKeyDown: handleTextareaKeyDown, placeholder: conversationHistory.length > 0
|
|
1029
|
+
? "How can I improve this further?"
|
|
1030
|
+
: "Describe...", minHeightClass: "min-h-[92px]", submitTitle: "Send (Enter)", canSubmit: !!instruction.trim() && selectedModels.length > 0, models: availableModels, hasMultipleModels: hasMultipleModels, modelPopoverOpen: modelPopoverOpen, onModelPopoverOpenChange: setModelPopoverOpen, selectedModelIds: selectedModelIds, selectedModelsCount: selectedModels.length, selectedModelSummary: selectedModelSummary, onToggleModel: toggleModelSelection, onFocusChange: setPromptFocused }), error && (_jsxs("div", { className: "text-feedback-red bg-feedback-red-light border-feedback-red animate-in fade-in slide-in-from-top-1 flex items-start gap-2 rounded-xl border px-3 py-2 text-xs duration-200", children: [_jsx(X, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }), _jsx("span", { children: error })] }))] })), state === "preview" && (_jsxs("div", { className: "animate-in fade-in slide-in-from-bottom-2 space-y-4 duration-300", children: [modelRunValues.length > 0 &&
|
|
1031
|
+
(useCompactResultList ? (_jsxs("div", { className: "border-border-default overflow-hidden rounded-lg border bg-white", children: [compactSuggestions.length > 0 ? (compactSuggestions.map((suggestion) => (_jsxs("button", { type: "button", onClick: () => handleUseRunResult(suggestion.text), className: "hover:bg-neutral-grey-5 border-border-default flex h-8 w-full items-center gap-2 border-b px-3 text-left text-xs last:border-b-0", title: suggestion.text, children: [_jsx("span", { className: "text-neutral-grey-100 min-w-0 flex-1 truncate", children: suggestion.text }), selectedModels.length > 1 && (_jsx("span", { className: "text-neutral-grey-50 max-w-[96px] shrink-0 truncate text-[10px]", title: suggestion.modelName, children: suggestion.modelName }))] }, suggestion.id)))) : (_jsxs("div", { className: "text-neutral-grey-50 flex min-h-9 items-center gap-1.5 px-3 text-xs italic", children: [modelRunValues.some((run) => isActiveModelRunStatus(run.status)) && (_jsx(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" })), modelRunValues.some((run) => isActiveModelRunStatus(run.status))
|
|
1032
|
+
? "Waiting for suggestions..."
|
|
1033
|
+
: "No suggestions returned."] })), compactSuggestions.length === 0 &&
|
|
1034
|
+
modelRunValues
|
|
1035
|
+
.filter((run) => run.status === "error")
|
|
1036
|
+
.map((run) => (_jsx("div", { className: "text-feedback-red border-border-default border-t px-3 py-2 text-xs", children: run.error || `${run.modelName} failed.` }, `${run.agentId}-error`)))] })) : (_jsx("div", { className: "flex flex-col gap-4", children: modelRunValues.map((run) => {
|
|
1037
|
+
const replacementTexts = run.replacementTexts ??
|
|
1038
|
+
(run.replacementText != null
|
|
1039
|
+
? [run.replacementText]
|
|
1040
|
+
: []);
|
|
1041
|
+
return (_jsxs("div", { className: cn("border-border-default flex flex-col gap-4 rounded-lg border bg-white p-4", run.status === "error" && "border-feedback-red"), children: [_jsxs("div", { className: "text-neutral-grey-100 flex items-center gap-2 text-xs leading-none font-semibold", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(Lightbulb, { className: "text-neutral-grey-100 h-5 w-5 shrink-0" }), _jsx("span", { children: "Suggestion" })] }), selectedModels.length > 1 && (_jsx(Badge, { asChild: true, variant: "secondary", size: "sm", className: "ml-auto max-w-[180px] cursor-default truncate", children: _jsx("button", { type: "button", onClick: (event) => handleModelBadgeClick(event, run.agentId), title: `${run.modelName} (Ctrl+click to open agent chat)`, children: run.modelName }) }))] }), _jsxs("div", { className: "max-h-[220px] min-h-[44px] overflow-y-auto", children: [run.status === "pending" && (_jsxs("span", { className: "text-neutral-grey-50 inline-flex items-center gap-1.5 text-xs italic", children: [_jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), "Starting..."] })), run.status === "thinking" && (_jsxs("span", { className: "text-neutral-grey-50 inline-flex items-center gap-1.5 text-xs italic", children: [_jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), "Thinking..."] })), run.status === "streaming" &&
|
|
991
1042
|
(() => {
|
|
992
1043
|
const preview = extractStreamingPreview(run.rawContent ?? "");
|
|
993
|
-
return preview ? (renderTextWithContext(preview, false)) : (
|
|
1044
|
+
return preview ? (_jsx("div", { className: "font-mono text-sm leading-7 whitespace-pre-wrap", children: renderTextWithContext(preview, false) })) : (_jsxs("span", { className: "text-neutral-grey-50 inline-flex items-center gap-1.5 text-xs italic", children: [_jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), "Waiting for response..."] }));
|
|
994
1045
|
})(), run.status === "done" &&
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
e.preventDefault();
|
|
1046
|
+
replacementTexts.length > 0 && (_jsx("div", { className: "flex flex-col gap-4", children: replacementTexts.map((replacementText, index) => (_jsxs("div", { className: "border-border-default flex flex-col gap-3 border-b pb-4 last:border-b-0 last:pb-0", children: [replacementTexts.length > 1 && (_jsxs("div", { className: "text-neutral-grey-50 text-[11px] font-semibold", children: ["Suggestion ", index + 1] })), renderSuggestionPreview(replacementText), _jsxs(Button, { size: "sm", onClick: () => handleUseRunResult(replacementText), className: "bg-neutral-grey-10 hover:bg-neutral-grey-15 text-neutral-grey-100 self-start rounded-[4px] px-4 py-2 text-sm font-semibold shadow-none", children: [_jsx(Check, { className: "mr-1.5 h-4 w-4" }), "Apply"] })] }, `${run.agentId}-${index}`))) })), run.status === "error" && (_jsx("div", { className: "text-feedback-red text-sm", children: run.error || "Something went wrong." }))] })] }, run.agentId));
|
|
1047
|
+
}) }))), _jsx(InstructionComposer, { inputRef: inputRef, value: instruction, onChange: setInstruction, onSubmit: () => {
|
|
998
1048
|
if (instruction.trim()) {
|
|
999
1049
|
executeAiModification(instruction.trim());
|
|
1000
1050
|
setInstruction("");
|
|
1001
1051
|
}
|
|
1002
|
-
},
|
|
1003
|
-
"text-highlight-100 bg-highlight-10"), title: selectedModelSummary
|
|
1004
|
-
? `Models: ${selectedModelSummary}`
|
|
1005
|
-
: "Select models", children: _jsx(Cpu, { className: "h-3.5 w-3.5" }) }) }), _jsxs(PopoverContent, { className: "w-56 p-1.5", align: "end", sideOffset: 8, children: [_jsxs("div", { className: "badge-pad flex items-center justify-between", children: [_jsx("span", { className: "text-2xs text-neutral-grey-50 font-semibold tracking-wider", children: "Models" }), _jsxs("span", { className: "text-highlight-100 text-[10px] font-medium", children: [selectedModels.length, " selected"] })] }), _jsx("div", { className: "space-y-0.5", children: availableModels.map((model) => (_jsxs("button", { type: "button", onClick: () => toggleModelSelection(model.id), className: cn("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs transition-colors", selectedModelIds.includes(model.id)
|
|
1006
|
-
? "bg-highlight-10 text-highlight-100 font-medium"
|
|
1007
|
-
: "text-neutral-grey-50 hover:bg-neutral-grey-5"), children: [_jsx("span", { className: cn("flex h-3.5 w-3.5 items-center justify-center rounded border", selectedModelIds.includes(model.id)
|
|
1008
|
-
? "border-highlight-100 bg-highlight-100 text-white"
|
|
1009
|
-
: "border-border-default bg-white text-transparent"), children: _jsx(Check, { className: "h-3 w-3" }) }), model.name] }, model.id))) })] })] })), _jsx("button", { type: "submit", disabled: !instruction.trim() || selectedModels.length === 0, className: cn("flex h-7 w-7 items-center justify-center rounded-lg p-0 transition-colors duration-200", instruction.trim() && selectedModels.length > 0
|
|
1010
|
-
? "bg-highlight-100 hover:bg-highlight-100 text-white"
|
|
1011
|
-
: "bg-neutral-grey-5 text-neutral-grey-50 cursor-not-allowed"), title: "Refine (Enter)", children: _jsx(Wand2, { className: "h-3.5 w-3.5" }) })] })] }), _jsxs("div", { className: "flex gap-2.5 pt-1", children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: handleStartOver, className: "text-neutral-grey-50 hover:text-neutral-grey-100 hover:bg-neutral-grey-5 flex-1 rounded-xl font-medium", children: [_jsx(RotateCcw, { className: "mr-2 h-4 w-4" }), "Discard"] }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: handleRetry, disabled: conversationHistory.length === 0, className: "text-highlight-100 hover:text-highlight-100 hover:bg-highlight-10 flex-1 rounded-xl font-medium disabled:pointer-events-none disabled:opacity-50", title: "Run the same request again", children: [_jsx(RotateCw, { className: "mr-2 h-4 w-4" }), "Retry"] })] })] }))] })] }));
|
|
1052
|
+
}, onKeyDown: handleTextareaKeyDown, placeholder: "Not quite right? Ask for a change...", minHeightClass: "min-h-[92px]", submitTitle: "Refine (Enter)", canSubmit: !!instruction.trim() && selectedModels.length > 0, models: availableModels, hasMultipleModels: hasMultipleModels, modelPopoverOpen: modelPopoverOpen, onModelPopoverOpenChange: setModelPopoverOpen, selectedModelIds: selectedModelIds, selectedModelsCount: selectedModels.length, selectedModelSummary: selectedModelSummary, onToggleModel: toggleModelSelection, onFocusChange: setPromptFocused })] }))] })] }));
|
|
1012
1053
|
}
|
|
1013
1054
|
export default InlineAiDialog;
|
|
1014
1055
|
//# sourceMappingURL=InlineAiDialog.js.map
|