@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.
Files changed (220) hide show
  1. package/dist/agents-view/AgentsSidebar.js +1 -1
  2. package/dist/agents-view/AgentsSidebar.js.map +1 -1
  3. package/dist/agents-view/AgentsTitlebar.d.ts +1 -1
  4. package/dist/agents-view/AgentsTitlebar.js +3 -6
  5. package/dist/agents-view/AgentsTitlebar.js.map +1 -1
  6. package/dist/agents-view/AgentsView.d.ts +2 -2
  7. package/dist/agents-view/AgentsView.js +2 -2
  8. package/dist/agents-view/AgentsView.js.map +1 -1
  9. package/dist/agents-view/AgentsWorkspaceView.js +1 -12
  10. package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
  11. package/dist/agents-view/CreateAgentView.d.ts +1 -1
  12. package/dist/agents-view/CreateAgentView.js +1 -1
  13. package/dist/agents-view/DateAgentsGroup.js +12 -1
  14. package/dist/agents-view/DateAgentsGroup.js.map +1 -1
  15. package/dist/agents-view/ProfileAgentsGroup.js +16 -4
  16. package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
  17. package/dist/components/ui/checkbox.js +1 -1
  18. package/dist/components/ui/checkbox.js.map +1 -1
  19. package/dist/components/ui/context-menu.d.ts +2 -1
  20. package/dist/components/ui/context-menu.js +4 -1
  21. package/dist/components/ui/context-menu.js.map +1 -1
  22. package/dist/components/ui/input.js +2 -2
  23. package/dist/components/ui/input.js.map +1 -1
  24. package/dist/components/ui/select.js +1 -1
  25. package/dist/components/ui/select.js.map +1 -1
  26. package/dist/components/ui/textarea.js +2 -2
  27. package/dist/components/ui/textarea.js.map +1 -1
  28. package/dist/config/config.js +92 -14
  29. package/dist/config/config.js.map +1 -1
  30. package/dist/config/types.d.ts +7 -0
  31. package/dist/config/types.js.map +1 -1
  32. package/dist/editor/ContextMenu.d.ts +1 -0
  33. package/dist/editor/ContextMenu.js +4 -4
  34. package/dist/editor/ContextMenu.js.map +1 -1
  35. package/dist/editor/FieldHistory.d.ts +2 -1
  36. package/dist/editor/FieldHistory.js +13 -12
  37. package/dist/editor/FieldHistory.js.map +1 -1
  38. package/dist/editor/FieldListField.js +4 -18
  39. package/dist/editor/FieldListField.js.map +1 -1
  40. package/dist/editor/LinkEditorDialog.d.ts +9 -2
  41. package/dist/editor/LinkEditorDialog.js +174 -70
  42. package/dist/editor/LinkEditorDialog.js.map +1 -1
  43. package/dist/editor/MainLayout.js +49 -6
  44. package/dist/editor/MainLayout.js.map +1 -1
  45. package/dist/editor/MobileLayout.js +33 -1
  46. package/dist/editor/MobileLayout.js.map +1 -1
  47. package/dist/editor/PictureCropper.js +45 -28
  48. package/dist/editor/PictureCropper.js.map +1 -1
  49. package/dist/editor/ai/AgentProfileSelector.js +7 -7
  50. package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
  51. package/dist/editor/ai/Agents.js +19 -5
  52. package/dist/editor/ai/Agents.js.map +1 -1
  53. package/dist/editor/ai/InlineAiDialog.d.ts +1 -0
  54. package/dist/editor/ai/InlineAiDialog.js +239 -198
  55. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  56. package/dist/editor/ai/InlineAiTextEditTooltip.d.ts +8 -0
  57. package/dist/editor/ai/InlineAiTextEditTooltip.js +10 -0
  58. package/dist/editor/ai/InlineAiTextEditTooltip.js.map +1 -0
  59. package/dist/editor/ai/InlineAiTrigger.js +187 -20
  60. package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
  61. package/dist/editor/ai/inlineAiTextEditLabels.d.ts +2 -0
  62. package/dist/editor/ai/inlineAiTextEditLabels.js +8 -0
  63. package/dist/editor/ai/inlineAiTextEditLabels.js.map +1 -0
  64. package/dist/editor/ai/prepareInlineAiTextSelection.d.ts +5 -0
  65. package/dist/editor/ai/prepareInlineAiTextSelection.js +86 -0
  66. package/dist/editor/ai/prepareInlineAiTextSelection.js.map +1 -0
  67. package/dist/editor/ai/terminal/components/AgentSettingsPopover.js +1 -1
  68. package/dist/editor/ai/terminal/components/AgentSettingsPopover.js.map +1 -1
  69. package/dist/editor/ai/terminal/components/AiResponseMessage.js +9 -9
  70. package/dist/editor/ai/terminal/components/AiResponseMessage.js.map +1 -1
  71. package/dist/editor/ai/terminal/components/ToolCallDisplay.js +3 -1
  72. package/dist/editor/ai/terminal/components/ToolCallDisplay.js.map +1 -1
  73. package/dist/editor/ai/useActiveAgentConversation.d.ts +3 -0
  74. package/dist/editor/ai/useActiveAgentConversation.js +32 -0
  75. package/dist/editor/ai/useActiveAgentConversation.js.map +1 -0
  76. package/dist/editor/ai/useInlineAiPosition.d.ts +9 -1
  77. package/dist/editor/ai/useInlineAiPosition.js +10 -19
  78. package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
  79. package/dist/editor/client/EditorShell.js +23 -4
  80. package/dist/editor/client/EditorShell.js.map +1 -1
  81. package/dist/editor/client/editContext.d.ts +25 -0
  82. package/dist/editor/client/editContext.js.map +1 -1
  83. package/dist/editor/client/fieldModificationStore.d.ts +1 -0
  84. package/dist/editor/client/fieldModificationStore.js +7 -2
  85. package/dist/editor/client/fieldModificationStore.js.map +1 -1
  86. package/dist/editor/client/itemsRepository.js +3 -1
  87. package/dist/editor/client/itemsRepository.js.map +1 -1
  88. package/dist/editor/client/operations.js +26 -4
  89. package/dist/editor/client/operations.js.map +1 -1
  90. package/dist/editor/client/ui/EditorChrome.js +1 -1
  91. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  92. package/dist/editor/commands/componentCommands.d.ts +3 -1
  93. package/dist/editor/commands/componentCommands.js +8 -3
  94. package/dist/editor/commands/componentCommands.js.map +1 -1
  95. package/dist/editor/commands/handlers/uiActionHandlers.js +5 -1
  96. package/dist/editor/commands/handlers/uiActionHandlers.js.map +1 -1
  97. package/dist/editor/editor-warnings/FinalWorkflowStateReadOnly.js +5 -0
  98. package/dist/editor/editor-warnings/FinalWorkflowStateReadOnly.js.map +1 -1
  99. package/dist/editor/editor-warnings/ItemLocked.js +6 -3
  100. package/dist/editor/editor-warnings/ItemLocked.js.map +1 -1
  101. package/dist/editor/field-types/DateFieldEditor.js +1 -1
  102. package/dist/editor/field-types/DateFieldEditor.js.map +1 -1
  103. package/dist/editor/field-types/DateTimeFieldEditor.js +1 -1
  104. package/dist/editor/field-types/DateTimeFieldEditor.js.map +1 -1
  105. package/dist/editor/field-types/DropLinkEditor.js +1 -1
  106. package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
  107. package/dist/editor/field-types/DropListEditor.js +1 -1
  108. package/dist/editor/field-types/DropListEditor.js.map +1 -1
  109. package/dist/editor/field-types/ImageFieldEditor.js +1 -1
  110. package/dist/editor/field-types/ImageFieldEditor.js.map +1 -1
  111. package/dist/editor/field-types/InternalLinkFieldEditor.js +1 -1
  112. package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
  113. package/dist/editor/field-types/LinkFieldEditor.js +15 -3
  114. package/dist/editor/field-types/LinkFieldEditor.js.map +1 -1
  115. package/dist/editor/field-types/MultiLineText.js +1 -1
  116. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  117. package/dist/editor/field-types/NameValueListEditor.js +1 -1
  118. package/dist/editor/field-types/NameValueListEditor.js.map +1 -1
  119. package/dist/editor/field-types/PictureFieldEditor.js +2 -2
  120. package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
  121. package/dist/editor/field-types/RawEditor.js +1 -1
  122. package/dist/editor/field-types/RawEditor.js.map +1 -1
  123. package/dist/editor/field-types/RichTextEditorComponent.js +18 -36
  124. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  125. package/dist/editor/field-types/SingleLineText.js +1 -1
  126. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  127. package/dist/editor/field-types/TreeListEditor.js +1 -1
  128. package/dist/editor/field-types/TreeListEditor.js.map +1 -1
  129. package/dist/editor/field-types/richtext/bridgeRichTextProfile.d.ts +21 -0
  130. package/dist/editor/field-types/richtext/bridgeRichTextProfile.js +96 -0
  131. package/dist/editor/field-types/richtext/bridgeRichTextProfile.js.map +1 -0
  132. package/dist/editor/field-types/richtext/components/ReactSlate.css +44 -6
  133. package/dist/editor/field-types/richtext/components/ReactSlate.js +185 -36
  134. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  135. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.css +5 -2
  136. package/dist/editor/field-types/richtext/components/SimpleToolbar.js +5 -4
  137. package/dist/editor/field-types/richtext/components/SimpleToolbar.js.map +1 -1
  138. package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +2 -14
  139. package/dist/editor/field-types/richtext/contextMenuFactory.js +4 -232
  140. package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
  141. package/dist/editor/field-types/richtext/richTextToolbarIcons.d.ts +7 -0
  142. package/dist/editor/field-types/richtext/richTextToolbarIcons.js +49 -0
  143. package/dist/editor/field-types/richtext/richTextToolbarIcons.js.map +1 -0
  144. package/dist/editor/field-types/richtext/utils/conversion.js +23 -2
  145. package/dist/editor/field-types/richtext/utils/conversion.js.map +1 -1
  146. package/dist/editor/fieldTypes.d.ts +2 -0
  147. package/dist/editor/media-selector/MediaFolderBrowser.d.ts +2 -1
  148. package/dist/editor/media-selector/MediaFolderBrowser.js +19 -9
  149. package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
  150. package/dist/editor/media-selector/TreeSelector.js +15 -9
  151. package/dist/editor/media-selector/TreeSelector.js.map +1 -1
  152. package/dist/editor/media-selector/UploadZone.d.ts +2 -1
  153. package/dist/editor/media-selector/UploadZone.js +21 -9
  154. package/dist/editor/media-selector/UploadZone.js.map +1 -1
  155. package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +10 -0
  156. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +607 -85
  157. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
  158. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +3 -1
  159. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  160. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.d.ts +3 -1
  161. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js +357 -73
  162. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js.map +1 -1
  163. package/dist/editor/page-editor-chrome/PageEditorChrome.js +1 -1
  164. package/dist/editor/page-editor-chrome/PageEditorChrome.js.map +1 -1
  165. package/dist/editor/page-editor-chrome/bridgeInlineFormatToolbarLayout.d.ts +24 -0
  166. package/dist/editor/page-editor-chrome/bridgeInlineFormatToolbarLayout.js +89 -0
  167. package/dist/editor/page-editor-chrome/bridgeInlineFormatToolbarLayout.js.map +1 -0
  168. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.d.ts +1 -1
  169. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js +7 -1
  170. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js.map +1 -1
  171. package/dist/editor/page-viewer/PageViewer.js +23 -4
  172. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  173. package/dist/editor/page-viewer/PageViewerFrame.js +157 -2
  174. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  175. package/dist/editor/reviews/Comment.js +2 -2
  176. package/dist/editor/reviews/Comment.js.map +1 -1
  177. package/dist/editor/reviews/CommentDisplayPopover.js +2 -1
  178. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  179. package/dist/editor/reviews/CommentEditor.d.ts +1 -0
  180. package/dist/editor/reviews/CommentEditor.js +3 -2
  181. package/dist/editor/reviews/CommentEditor.js.map +1 -1
  182. package/dist/editor/reviews/CommentPopover.js +2 -2
  183. package/dist/editor/reviews/CommentPopover.js.map +1 -1
  184. package/dist/editor/reviews/Comments.js +5 -4
  185. package/dist/editor/reviews/Comments.js.map +1 -1
  186. package/dist/editor/reviews/FeedbackCard.js +5 -7
  187. package/dist/editor/reviews/FeedbackCard.js.map +1 -1
  188. package/dist/editor/reviews/SuggestionCommentThread.js +3 -3
  189. package/dist/editor/reviews/SuggestionCommentThread.js.map +1 -1
  190. package/dist/editor/services/contentService.d.ts +1 -0
  191. package/dist/editor/services/contentService.js.map +1 -1
  192. package/dist/editor/sidebar/Validation.js +4 -1
  193. package/dist/editor/sidebar/Validation.js.map +1 -1
  194. package/dist/editor/template-wizard/TemplateStructureInlineEditor.js +1 -1
  195. package/dist/editor/template-wizard/TemplateStructureInlineEditor.js.map +1 -1
  196. package/dist/editor/ui/SimpleIconButton.js +8 -2
  197. package/dist/editor/ui/SimpleIconButton.js.map +1 -1
  198. package/dist/editor/ui/Splitter.d.ts +1 -0
  199. package/dist/editor/ui/Splitter.js +12 -2
  200. package/dist/editor/ui/Splitter.js.map +1 -1
  201. package/dist/editor/ui/animationSettle.d.ts +32 -0
  202. package/dist/editor/ui/animationSettle.js +85 -0
  203. package/dist/editor/ui/animationSettle.js.map +1 -0
  204. package/dist/editor/utils/expandSelectionAtCaret.d.ts +15 -0
  205. package/dist/editor/utils/expandSelectionAtCaret.js +183 -0
  206. package/dist/editor/utils/expandSelectionAtCaret.js.map +1 -0
  207. package/dist/editor/views/MediaFolderEditView.js +1 -1
  208. package/dist/editor/views/MediaFolderEditView.js.map +1 -1
  209. package/dist/revision.d.ts +2 -2
  210. package/dist/revision.js +2 -2
  211. package/dist/splash-screen/DialogWrappers.js +2 -2
  212. package/dist/splash-screen/DialogWrappers.js.map +1 -1
  213. package/dist/task-board/components/TaskDetailPanel.js +2 -1
  214. package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
  215. package/dist/task-board/views/DependencyGraphView.d.ts +42 -1
  216. package/dist/task-board/views/DependencyGraphView.js +94 -0
  217. package/dist/task-board/views/DependencyGraphView.js.map +1 -1
  218. package/dist/types.d.ts +1 -0
  219. package/package.json +1 -1
  220. package/styles.css +59 -12
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useCallback, useRef, useEffect, useLayoutEffect, useMemo, } from "react";
4
- import { Sparkles, Check, X, SendHorizonal, Wand2, RotateCcw, Maximize2, History, Cpu, RotateCw, StopCircle, Bug, } from "lucide-react";
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) && ids.length > 0 ? ids : null;
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 AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION = `Return ONLY valid JSON (no markdown, no code fences, no extra text).
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 { error: "AI response is missing `replacementText` or `error`." };
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.length ? selected : availableModels.slice(0, 1);
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 (validStored?.length) {
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 && selectedModelIds.length > 0) {
442
+ if (profileId && activeProfile) {
366
443
  setStoredModelIds(profileId, selectedModelIds);
367
444
  }
368
- }, [profileId, selectedModelIds]);
369
- // Focus input on mount
370
- useEffect(() => {
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" && run.replacementText?.length);
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: parsed.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
- ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
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
- ${AI_TEXT_EDIT_RESPONSE_FORMAT_INSTRUCTION}`;
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
- const toggleLarge = useCallback(() => {
846
- setIsLarge((prev) => !prev);
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-highlight-10 text-highlight-100 italic"), children: centerText }));
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-highlight-10 text-highlight-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: "..." })] }))] }));
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
- if (state === "preview") {
865
- handleTryAgain();
866
- }
867
- else {
868
- handleClose();
869
- }
982
+ handleClose();
870
983
  }
871
- }, [state, handleTryAgain, handleClose]);
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
- // Clamp the dialog position so it never renders outside the viewport.
882
- // This matters because `position` is based on selection coordinates which can be near the viewport edge.
883
- useLayoutEffect(() => {
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: clampedPosition.x,
937
- top: clampedPosition.y,
938
- zIndex: 95,
999
+ left: position.x,
1000
+ top: position.y,
1001
+ zIndex: 1100,
939
1002
  }
940
- : undefined, onKeyDown: handleKeyDown, children: [_jsxs("div", { className: "from-highlight-100/5 to-highlight-100/5 border-border-default flex flex-shrink-0 items-center gap-2.5 border-b bg-gradient-to-r px-4 py-3", children: [_jsx("div", { className: "bg-highlight-100 shadow-highlight-100 flex h-6 w-6 items-center justify-center rounded-lg shadow-sm", children: _jsx(Sparkles, { className: "h-3.5 w-3.5 text-white" }) }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "text-neutral-grey-100 text-sm leading-tight font-semibold", children: "AI Text Edit" }), conversationHistory.length > 0 && (_jsxs("span", { className: "text-neutral-grey-50 text-[10px] font-medium", children: [conversationHistory.length, " ", conversationHistory.length === 1 ? "turn" : "turns", " in history"] }))] }), _jsxs("div", { className: "ml-auto flex items-center gap-1", children: [_jsx("button", { onClick: toggleLarge, className: cn("hover:bg-neutral-grey-5 group rounded-lg p-1.5 transition-all duration-200", isLarge &&
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-1.5 rounded-lg px-2 py-1.5 text-xs font-medium transition-all duration-200", "border-border-default text-neutral-grey-50 border bg-white", "hover:border-highlight-100 hover:text-highlight-100 hover:bg-highlight-10", "shadow-sm hover:shadow-md active:scale-95", isMatch &&
954
- "border-highlight-100 bg-highlight-10 text-highlight-100 ring-highlight-100 ring-2", isPartialMatch &&
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-3.5 w-3.5 shrink-0 items-center justify-center [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
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
- }) })] }), _jsxs("form", { onSubmit: (e) => {
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
- }, className: "group relative", children: [_jsx(Textarea, { ref: inputRef, value: instruction, onChange: (e) => setInstruction(e.target.value), onKeyDown: handleTextareaKeyDown, placeholder: conversationHistory.length > 0
969
- ? "How can I improve this further?"
970
- : "Describe the change you want...", className: cn("border-border-default w-full rounded-2xl border py-3 pr-14 pl-3 text-xs", "focus:border-highlight-100 focus:ring-highlight-100/10 focus:ring-4", "bg-neutral-grey-5/30 resize-none transition-all duration-200 outline-none", isLarge ? "min-h-[200px]" : "min-h-[100px]") }), _jsxs("div", { className: "absolute right-2 bottom-2 flex items-center gap-1", children: [hasMultipleModels && (_jsxs(Popover, { open: modelPopoverOpen, onOpenChange: setModelPopoverOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: cn("flex h-7 w-7 items-center justify-center rounded-lg p-0 transition-colors duration-200", "text-neutral-grey-50 hover:text-highlight-100 hover:bg-highlight-10", modelPopoverOpen &&
971
- "text-highlight-100 bg-highlight-10"), title: selectedModelSummary
972
- ? `Models: ${selectedModelSummary}`
973
- : "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-neutral-grey-50 text-[10px] 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)
974
- ? "bg-highlight-10 text-highlight-100 font-medium"
975
- : "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)
976
- ? "border-highlight-100 bg-highlight-100 text-white"
977
- : "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
978
- ? "bg-highlight-100 hover:bg-highlight-100 text-white"
979
- : "bg-neutral-grey-5 text-neutral-grey-50 cursor-not-allowed"), title: "Send (Enter)", children: _jsx(SendHorizonal, { className: "h-3.5 w-3.5" }) })] })] }), conversationHistory.length > 0 && (_jsxs("div", { className: "flex items-center justify-between pt-1", children: [_jsxs("div", { className: "text-neutral-grey-50 flex items-center gap-1.5 text-[10px] font-medium", children: [_jsx(History, { className: "h-3 w-3" }), "Conversation active"] }), _jsxs("button", { onClick: handleStartOver, className: "text-highlight-100 hover:text-highlight-100 flex items-center gap-1 text-[10px] font-bold tracking-tight transition-colors", children: [_jsx(RotateCcw, { className: "h-3 w-3" }), "Clear History"] })] })), 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-lg 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: [Object.keys(modelRuns).length > 0 && (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "text-neutral-grey-50 flex items-center gap-1.5 text-[10px] font-bold tracking-wider", children: [_jsx("div", { className: "bg-highlight-100 h-1.5 w-1.5 rounded-full" }), Object.values(modelRuns).some((r) => isActiveModelRunStatus(r.status))
980
- ? "Responses (live)"
981
- : "Model responses"] }), _jsx("div", { className: "flex gap-3 overflow-x-auto pb-1", children: Object.values(modelRuns).map((run) => (_jsxs("div", { className: cn("flex min-w-[280px] flex-1 flex-col gap-2 rounded-xl border p-3", run.status === "done" &&
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)) : (_jsx("span", { className: "text-neutral-grey-50 italic", children: "Waiting for response\u2026" }));
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
- run.replacementText != null &&
996
- renderTextWithContext(run.replacementText, true), run.status === "error" && (_jsx("div", { className: "text-feedback-red", children: run.error || "Something went wrong." }))] }), run.status === "done" && run.replacementText != null && (_jsxs(Button, { size: "sm", onClick: () => handleUseRunResult(run.replacementText), className: "bg-highlight-100 hover:bg-highlight-100 mt-2 w-full rounded-lg font-semibold text-white", children: [_jsx(Check, { className: "mr-1.5 h-4 w-4" }), "Use this"] }))] }, run.agentId))) })] })), _jsxs("form", { onSubmit: (e) => {
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
- }, className: "group relative", children: [_jsx(Textarea, { ref: inputRef, value: instruction, onChange: (e) => setInstruction(e.target.value), onKeyDown: handleTextareaKeyDown, placeholder: "Not quite right? Ask for a change...", className: cn("border-border-default min-h-[80px] w-full rounded-2xl border py-3 pr-14 pl-3 text-xs", "focus:border-highlight-100 focus:ring-highlight-100/10 focus:ring-4", "bg-neutral-grey-5/30 resize-none transition-all duration-200 outline-none") }), _jsxs("div", { className: "absolute right-2 bottom-2 flex items-center gap-1", children: [hasMultipleModels && (_jsxs(Popover, { open: modelPopoverOpen, onOpenChange: setModelPopoverOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: cn("flex h-7 w-7 items-center justify-center rounded-lg p-0 transition-colors duration-200", "text-neutral-grey-50 hover:text-highlight-100 hover:bg-highlight-10", modelPopoverOpen &&
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