@alpaca-editor/core 1.0.4018 → 1.0.4026

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 (173) hide show
  1. package/dist/components/SimpleLanguageSelector.d.ts +2 -1
  2. package/dist/components/SimpleLanguageSelector.js +9 -2
  3. package/dist/components/SimpleLanguageSelector.js.map +1 -1
  4. package/dist/components/ui/input.js +1 -1
  5. package/dist/components/ui/input.js.map +1 -1
  6. package/dist/components/ui/tooltip.d.ts +3 -1
  7. package/dist/components/ui/tooltip.js +2 -2
  8. package/dist/components/ui/tooltip.js.map +1 -1
  9. package/dist/config/config.js +4 -0
  10. package/dist/config/config.js.map +1 -1
  11. package/dist/config/types.d.ts +2 -0
  12. package/dist/editor/ContentTree.js +1 -1
  13. package/dist/editor/ContentTree.js.map +1 -1
  14. package/dist/editor/ContextMenu.js +26 -0
  15. package/dist/editor/ContextMenu.js.map +1 -1
  16. package/dist/editor/FieldHistory.js +1 -1
  17. package/dist/editor/FieldHistory.js.map +1 -1
  18. package/dist/editor/FieldListField.js +2 -2
  19. package/dist/editor/FieldListField.js.map +1 -1
  20. package/dist/editor/Terminal.js +3 -1
  21. package/dist/editor/Terminal.js.map +1 -1
  22. package/dist/editor/ai/Agents.js +19 -10
  23. package/dist/editor/ai/Agents.js.map +1 -1
  24. package/dist/editor/ai/AiResponseMessage.js +63 -5
  25. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  26. package/dist/editor/ai/AiTerminal.js +7 -7
  27. package/dist/editor/ai/AiTerminal.js.map +1 -1
  28. package/dist/editor/ai/AiToolCall.js +3 -3
  29. package/dist/editor/ai/AiToolCall.js.map +1 -1
  30. package/dist/editor/client/EditorClient.js +5 -1
  31. package/dist/editor/client/EditorClient.js.map +1 -1
  32. package/dist/editor/client/editContext.d.ts +2 -0
  33. package/dist/editor/client/editContext.js.map +1 -1
  34. package/dist/editor/client/operations.d.ts +1 -0
  35. package/dist/editor/client/operations.js +7 -0
  36. package/dist/editor/client/operations.js.map +1 -1
  37. package/dist/editor/commands/componentCommands.js +1 -1
  38. package/dist/editor/commands/componentCommands.js.map +1 -1
  39. package/dist/editor/field-types/ImageFieldEditor.js +1 -1
  40. package/dist/editor/field-types/ImageFieldEditor.js.map +1 -1
  41. package/dist/editor/field-types/MultiLineText.js +1 -1
  42. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  43. package/dist/editor/field-types/PictureFieldEditor.js +1 -1
  44. package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
  45. package/dist/editor/field-types/RawEditor.js +1 -1
  46. package/dist/editor/field-types/RawEditor.js.map +1 -1
  47. package/dist/editor/field-types/RichTextEditorComponent.js +16 -17
  48. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  49. package/dist/editor/field-types/SingleLineText.js +1 -1
  50. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  51. package/dist/editor/field-types/richtext/components/SimpleDropdown.d.ts +18 -0
  52. package/dist/editor/field-types/richtext/components/SimpleDropdown.js +71 -0
  53. package/dist/editor/field-types/richtext/components/SimpleDropdown.js.map +1 -0
  54. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.d.ts +5 -0
  55. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js +359 -0
  56. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js.map +1 -0
  57. package/dist/editor/field-types/richtext/components/SimpleToolbar.d.ts +16 -0
  58. package/dist/editor/field-types/richtext/components/SimpleToolbar.js +181 -0
  59. package/dist/editor/field-types/richtext/components/SimpleToolbar.js.map +1 -0
  60. package/dist/editor/field-types/richtext/components/SimpleToolbarButton.d.ts +9 -0
  61. package/dist/editor/field-types/richtext/components/SimpleToolbarButton.js +14 -0
  62. package/dist/editor/field-types/richtext/components/SimpleToolbarButton.js.map +1 -0
  63. package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +4 -0
  64. package/dist/editor/field-types/richtext/contextMenuFactory.js +193 -0
  65. package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -0
  66. package/dist/editor/field-types/richtext/index.d.ts +6 -5
  67. package/dist/editor/field-types/richtext/index.js +6 -5
  68. package/dist/editor/field-types/richtext/index.js.map +1 -1
  69. package/dist/editor/field-types/richtext/types.d.ts +16 -16
  70. package/dist/editor/field-types/richtext/types.js +84 -84
  71. package/dist/editor/field-types/richtext/types.js.map +1 -1
  72. package/dist/editor/page-editor-chrome/CommentHighlighting.js +1 -1
  73. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  74. package/dist/editor/page-editor-chrome/FrameMenu.js +5 -5
  75. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  76. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +1 -1
  77. package/dist/editor/page-viewer/PageViewerFrame.js +3 -2
  78. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  79. package/dist/editor/services/agentService.d.ts +14 -4
  80. package/dist/editor/services/agentService.js.map +1 -1
  81. package/dist/editor/services/aiService.js +1 -0
  82. package/dist/editor/services/aiService.js.map +1 -1
  83. package/dist/page-wizard/PageWizard.d.ts +2 -0
  84. package/dist/page-wizard/PageWizard.js +6 -13
  85. package/dist/page-wizard/PageWizard.js.map +1 -1
  86. package/dist/page-wizard/WizardSteps.js +3 -1
  87. package/dist/page-wizard/WizardSteps.js.map +1 -1
  88. package/dist/page-wizard/service.d.ts +1 -0
  89. package/dist/page-wizard/service.js +7 -0
  90. package/dist/page-wizard/service.js.map +1 -1
  91. package/dist/page-wizard/steps/ContentStep.js +210 -33
  92. package/dist/page-wizard/steps/ContentStep.js.map +1 -1
  93. package/dist/page-wizard/steps/FindItemsStep.js +11 -3
  94. package/dist/page-wizard/steps/FindItemsStep.js.map +1 -1
  95. package/dist/page-wizard/steps/LayoutStep.js +1 -1
  96. package/dist/page-wizard/steps/LayoutStep.js.map +1 -1
  97. package/dist/page-wizard/steps/MetaDataStep.js +1 -1
  98. package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
  99. package/dist/page-wizard/steps/SchottSelectImagesStep.d.ts +2 -0
  100. package/dist/page-wizard/steps/SchottSelectImagesStep.js +55 -0
  101. package/dist/page-wizard/steps/SchottSelectImagesStep.js.map +1 -0
  102. package/dist/page-wizard/steps/StructureStep.js +20 -5
  103. package/dist/page-wizard/steps/StructureStep.js.map +1 -1
  104. package/dist/page-wizard/steps/TranslateStep.d.ts +2 -0
  105. package/dist/page-wizard/steps/TranslateStep.js +413 -0
  106. package/dist/page-wizard/steps/TranslateStep.js.map +1 -0
  107. package/dist/page-wizard/utils/dataAccessor.d.ts +7 -0
  108. package/dist/page-wizard/utils/dataAccessor.js +76 -0
  109. package/dist/page-wizard/utils/dataAccessor.js.map +1 -1
  110. package/dist/revision.d.ts +2 -2
  111. package/dist/revision.js +2 -2
  112. package/dist/splash-screen/NewPage.js +5 -4
  113. package/dist/splash-screen/NewPage.js.map +1 -1
  114. package/dist/splash-screen/OpenPage.js +2 -1
  115. package/dist/splash-screen/OpenPage.js.map +1 -1
  116. package/dist/splash-screen/RecentPages.js +3 -1
  117. package/dist/splash-screen/RecentPages.js.map +1 -1
  118. package/dist/styles.css +57 -0
  119. package/package.json +5 -4
  120. package/src/components/SimpleLanguageSelector.tsx +11 -1
  121. package/src/components/ui/input.tsx +1 -1
  122. package/src/components/ui/tooltip.tsx +3 -2
  123. package/src/config/config.tsx +4 -0
  124. package/src/config/types.ts +6 -0
  125. package/src/editor/ContentTree.tsx +1 -1
  126. package/src/editor/ContextMenu.tsx +39 -0
  127. package/src/editor/FieldHistory.tsx +1 -1
  128. package/src/editor/FieldListField.tsx +2 -6
  129. package/src/editor/Terminal.tsx +5 -1
  130. package/src/editor/ai/Agents.tsx +27 -16
  131. package/src/editor/ai/AiResponseMessage.tsx +138 -23
  132. package/src/editor/ai/AiTerminal.tsx +43 -29
  133. package/src/editor/ai/AiToolCall.tsx +14 -4
  134. package/src/editor/client/EditorClient.tsx +9 -1
  135. package/src/editor/client/editContext.ts +2 -0
  136. package/src/editor/client/operations.ts +9 -0
  137. package/src/editor/commands/componentCommands.tsx +1 -1
  138. package/src/editor/field-types/ImageFieldEditor.tsx +1 -0
  139. package/src/editor/field-types/MultiLineText.tsx +1 -0
  140. package/src/editor/field-types/PictureFieldEditor.tsx +1 -0
  141. package/src/editor/field-types/RawEditor.tsx +1 -0
  142. package/src/editor/field-types/RichTextEditorComponent.tsx +27 -25
  143. package/src/editor/field-types/SingleLineText.tsx +1 -0
  144. package/src/editor/field-types/richtext/components/SimpleDropdown.tsx +165 -0
  145. package/src/editor/field-types/richtext/components/SimpleRichTextEditor.css +261 -0
  146. package/src/editor/field-types/richtext/components/SimpleRichTextEditor.tsx +505 -0
  147. package/src/editor/field-types/richtext/components/SimpleToolbar.tsx +362 -0
  148. package/src/editor/field-types/richtext/components/SimpleToolbarButton.tsx +36 -0
  149. package/src/editor/field-types/richtext/contextMenuFactory.tsx +264 -0
  150. package/src/editor/field-types/richtext/index.ts +6 -5
  151. package/src/editor/field-types/richtext/types.ts +168 -148
  152. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +1 -1
  153. package/src/editor/page-editor-chrome/FrameMenu.tsx +16 -11
  154. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +1 -1
  155. package/src/editor/page-viewer/PageViewerFrame.tsx +4 -3
  156. package/src/editor/services/agentService.ts +16 -5
  157. package/src/editor/services/aiService.ts +2 -0
  158. package/src/page-wizard/PageWizard.tsx +10 -13
  159. package/src/page-wizard/WizardSteps.tsx +3 -1
  160. package/src/page-wizard/service.ts +11 -0
  161. package/src/page-wizard/steps/ContentStep.tsx +376 -43
  162. package/src/page-wizard/steps/FindItemsStep.tsx +23 -3
  163. package/src/page-wizard/steps/LayoutStep.tsx +1 -1
  164. package/src/page-wizard/steps/MetaDataStep.tsx +1 -1
  165. package/src/page-wizard/steps/SchottSelectImagesStep.tsx +141 -0
  166. package/src/page-wizard/steps/StructureStep.tsx +40 -5
  167. package/src/page-wizard/steps/TranslateStep.tsx +772 -0
  168. package/src/page-wizard/utils/dataAccessor.ts +85 -0
  169. package/src/revision.ts +2 -2
  170. package/src/splash-screen/NewPage.tsx +18 -3
  171. package/src/splash-screen/OpenPage.tsx +14 -1
  172. package/src/splash-screen/RecentPages.tsx +4 -2
  173. package/tsconfig.build.json +1 -0
@@ -1,9 +1,12 @@
1
- import { useState } from "react";
1
+ import { useState, useEffect } from "react";
2
+ import JSONPretty from "react-json-pretty";
2
3
 
3
4
  import { useEditContext } from "../client/editContext";
4
5
  import { EditOperation } from "../../types";
5
6
  import { Message } from "./AiTerminal";
6
7
  import { AiToolCall } from "./AiToolCall";
8
+ import { Popover, PopoverContent, PopoverTrigger } from "../../components/ui/popover";
9
+ import { SimpleTabs, Tab } from "../ui/SimpleTabs";
7
10
 
8
11
  export function AiResponseMessage({
9
12
  messages,
@@ -22,6 +25,23 @@ export function AiResponseMessage({
22
25
  undefined,
23
26
  );
24
27
 
28
+ // Store tool calls to preserve them during streaming
29
+ const [preservedToolCalls, setPreservedToolCalls] = useState<{[messageIndex: number]: any[]}>({});
30
+
31
+ // Update preserved tool calls when messages change
32
+ useEffect(() => {
33
+ const newPreservedToolCalls = {...preservedToolCalls};
34
+
35
+ messages.forEach((message, index) => {
36
+ if (message.tool_calls && message.tool_calls.length > 0) {
37
+ // Store non-empty tool calls
38
+ newPreservedToolCalls[index] = message.tool_calls;
39
+ }
40
+ });
41
+
42
+ setPreservedToolCalls(newPreservedToolCalls);
43
+ }, [messages]);
44
+
25
45
  const reversedEditOperations = [...editOperations].reverse();
26
46
 
27
47
  const canReject =
@@ -32,32 +52,127 @@ export function AiResponseMessage({
32
52
  !editContext.editHistory[index]?.canUndo,
33
53
  ) === undefined;
34
54
 
55
+ // Helper function to render JSON or text
56
+ const renderJsonOrText = (json: string) => {
57
+ try {
58
+ const parsed = JSON.parse(json);
59
+ return (
60
+ <div className="font-mono text-xs whitespace-pre-wrap">
61
+ <pre className="whitespace-pre-wrap break-words">
62
+ {JSON.stringify(parsed, null, 2)}
63
+ </pre>
64
+ </div>
65
+ );
66
+ } catch (e) {
67
+ return <div className="whitespace-pre-wrap font-mono text-xs break-words">{json}</div>;
68
+ }
69
+ };
70
+
71
+ // Helper function to create tabs for a tool call
72
+ const createToolCallTabs = (toolCall: any, result: Message | undefined): Tab[] => {
73
+ const tabs: Tab[] = [
74
+ {
75
+ id: `input-${toolCall.id}`,
76
+ label: "Input",
77
+ content: (
78
+ <div className="max-h-96 overflow-auto p-2">
79
+ <div className="text-sm font-semibold text-gray-700 mb-1">Arguments:</div>
80
+ <div className="text-xs">{renderJsonOrText(toolCall.function.arguments)}</div>
81
+ </div>
82
+ ),
83
+ },
84
+ ];
85
+
86
+ if (result) {
87
+ tabs.push({
88
+ id: `output-${toolCall.id}`,
89
+ label: "Output",
90
+ content: (
91
+ <div className="max-h-96 overflow-auto p-2">
92
+ <div className="text-sm font-semibold text-gray-700 mb-1">Result:</div>
93
+ <div className="text-xs">{renderJsonOrText(result.content || "")}</div>
94
+ </div>
95
+ ),
96
+ });
97
+ }
98
+
99
+ return tabs;
100
+ };
101
+
102
+ // Helper function to create tool call popover content
103
+ const ToolCallPopover = ({ toolCall, result }: { toolCall: any; result: Message | undefined }) => {
104
+ const [activeTab, setActiveTab] = useState(0);
105
+ const tabs = createToolCallTabs(toolCall, result);
106
+
107
+ return (
108
+ <div className="w-96">
109
+ <div className="mb-3 pb-2 border-b border-gray-200">
110
+ <div className="text-sm font-semibold text-gray-800">
111
+ {toolCall.displayName || toolCall.function.name}
112
+ </div>
113
+ </div>
114
+ <SimpleTabs
115
+ tabs={tabs}
116
+ activeTab={activeTab}
117
+ setActiveTab={setActiveTab}
118
+ className="border-b border-gray-200 mb-3"
119
+ tabClassName="text-xs"
120
+ />
121
+ </div>
122
+ );
123
+ };
124
+
35
125
  return (
36
126
  <div>
37
127
  {messages
38
128
  .filter((x) => x.role !== "tool")
39
- .map((message, index) => (
40
- <div key={index}>
41
- <div
42
- dangerouslySetInnerHTML={{
43
- __html: message.formattedContent || message.content || "",
44
- }}
45
- />
46
- {message.tool_calls && message.tool_calls.length > 0 && (
47
- <div className="text-xs text-gray-400">
48
- {message.tool_calls.map((x, index) => (
49
- <AiToolCall
50
- toolCall={x}
51
- key={index}
52
- result={messages.find(
53
- (m) => m.role === "tool" && m.tool_call_id === x.id,
54
- )}
55
- />
56
- ))}
57
- </div>
58
- )}
59
- </div>
60
- ))}
129
+ .map((message, filteredIndex) => {
130
+ // Find the original index of this message in the unfiltered array
131
+ const originalIndex = messages.findIndex(m => m.id === message.id);
132
+ // Use preserved tool calls if current message has empty tool calls
133
+ const toolCalls = (message.tool_calls && message.tool_calls.length > 0)
134
+ ? message.tool_calls
135
+ : (preservedToolCalls[originalIndex] || []);
136
+ return (
137
+ <div key={filteredIndex}>
138
+ <div
139
+ dangerouslySetInnerHTML={{
140
+ __html: message.formattedContent || message.content || "",
141
+ }}
142
+ />
143
+ {toolCalls.length > 0 && (
144
+ <div className="mt-2 flex flex-wrap gap-2">
145
+ {toolCalls.map((toolCall, toolIndex) => {
146
+ return (
147
+ <Popover key={toolIndex} enableIframeClickDetection={false}>
148
+ <PopoverTrigger asChild>
149
+ <div className="inline-flex cursor-pointer items-center gap-1 text-xs text-gray-500 hover:text-gray-700">
150
+ <i className="pi pi-wrench text-xs" />
151
+ <span>
152
+ {toolCall?.displayName || toolCall?.function?.name || 'tool call'}
153
+ </span>
154
+ </div>
155
+ </PopoverTrigger>
156
+ <PopoverContent
157
+ className="p-3"
158
+ align="start"
159
+ side="bottom"
160
+ sideOffset={8}
161
+ onMouseDown={(e) => e.stopPropagation()}
162
+ onClick={(e) => e.stopPropagation()}
163
+ >
164
+ <div className="max-h-[500px] overflow-hidden">
165
+ <ToolCallPopover toolCall={toolCall} result={toolCall.result} />
166
+ </div>
167
+ </PopoverContent>
168
+ </Popover>
169
+ );
170
+ })}
171
+ </div>
172
+ )}
173
+ </div>
174
+ );
175
+ })}
61
176
  {/* <div
62
177
  dangerouslySetInnerHTML={{
63
178
  __html: responseText.replaceAll("\\n", "<br>"),
@@ -44,7 +44,7 @@ export type Message = {
44
44
  name: string;
45
45
  role: string;
46
46
  tool_calls?: ToolCall[];
47
- tool_call_id?: string;
47
+ tool_call_id?: string; // For tool response messages
48
48
  };
49
49
 
50
50
  export type AiContext = {
@@ -75,7 +75,9 @@ export function AiTerminal({
75
75
 
76
76
  if (!editContext) return null;
77
77
 
78
- const [messages, setMessages] = useState<Message[]>(options?.initialMessages || []);
78
+ const [messages, setMessages] = useState<Message[]>(
79
+ options?.initialMessages || [],
80
+ );
79
81
  const [response, setResponse] = useState<Response>();
80
82
  const [model, setModel] = useState<string>();
81
83
  const [prompt, setPrompt] = useState("");
@@ -85,7 +87,9 @@ export function AiTerminal({
85
87
  const [agentId] = useState<string>(() => crypto.randomUUID());
86
88
  const selection = editContext.selection;
87
89
  const terminalRef = useRef<{ submit: () => void }>(null);
88
- const [responseMessages, setResponseMessages] = useState<Message[]>(options?.initialMessages || []);
90
+ const [responseMessages, setResponseMessages] = useState<Message[]>(
91
+ options?.initialMessages || [],
92
+ );
89
93
  const [showSettings, setShowSettings] = useState(false);
90
94
  const settingsRef = useRef<HTMLDivElement>(null);
91
95
 
@@ -125,20 +129,29 @@ export function AiTerminal({
125
129
  }, [responseMessages]);
126
130
 
127
131
  // State for initial messages to pass to Terminal
128
- type TerminalMessage = {text: React.ReactNode, type: "command" | "response"};
129
- const [initialTerminalMessages, setInitialTerminalMessages] = useState<TerminalMessage[] | undefined>(undefined);
132
+ type TerminalMessage = {
133
+ text: React.ReactNode;
134
+ type: "command" | "response";
135
+ };
136
+ const [initialTerminalMessages, setInitialTerminalMessages] = useState<
137
+ TerminalMessage[] | undefined
138
+ >(undefined);
130
139
 
131
- // Effect to set up initial messages for display
140
+ // Effect to set up initial messages for display
132
141
  useEffect(() => {
133
142
  if (options?.initialMessages && options.initialMessages.length > 0) {
134
- console.log("AiTerminal: Loading initial messages", options.initialMessages);
135
-
143
+ console.log(
144
+ "AiTerminal: Loading initial messages",
145
+ options.initialMessages,
146
+ );
147
+
136
148
  // Format the initial messages for display
137
149
  const formattedMessages = options.initialMessages.map((message) => {
138
- const formattedContent = message.content
139
- ?.trim()
140
- ?.replaceAll("\n", "<br>")
141
- ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
150
+ const formattedContent =
151
+ message.content
152
+ ?.trim()
153
+ ?.replaceAll("\n", "<br>")
154
+ ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
142
155
 
143
156
  return {
144
157
  ...message,
@@ -153,7 +166,7 @@ export function AiTerminal({
153
166
  // Update the internal message states
154
167
  setMessages(formattedMessages);
155
168
  setResponseMessages(formattedMessages);
156
-
169
+
157
170
  // Create a response object for internal state
158
171
  const initialResponse: Response = {
159
172
  messages: formattedMessages,
@@ -161,21 +174,21 @@ export function AiTerminal({
161
174
  numInputTokens: 0,
162
175
  numOutputTokens: 0,
163
176
  numCachedTokens: 0,
164
- state: "loaded"
177
+ state: "loaded",
165
178
  };
166
-
179
+
167
180
  console.log("AiTerminal: Setting response", initialResponse);
168
181
  setResponse(initialResponse);
169
-
182
+
170
183
  // Create individual Terminal messages for each conversation message
171
184
  const terminalMessages: TerminalMessage[] = [];
172
-
185
+
173
186
  formattedMessages.forEach((message) => {
174
187
  if (message.role === "user") {
175
188
  // User messages appear as commands
176
189
  terminalMessages.push({
177
190
  type: "command",
178
- text: message.content
191
+ text: message.content,
179
192
  });
180
193
  } else if (message.role === "assistant") {
181
194
  // Assistant messages appear as responses
@@ -187,7 +200,7 @@ export function AiTerminal({
187
200
  __html: message.formattedContent || message.content || "",
188
201
  }}
189
202
  />
190
- )
203
+ ),
191
204
  });
192
205
  }
193
206
  // Add tool calls if present
@@ -196,15 +209,15 @@ export function AiTerminal({
196
209
  terminalMessages.push({
197
210
  type: "response",
198
211
  text: (
199
- <div className="text-xs text-gray-400">
212
+ <div className="text-xs text-gray-1">
200
213
  🔧 {toolCall.displayName}
201
214
  </div>
202
- )
203
- });
215
+ ),
216
+ })
204
217
  });
205
218
  }
206
219
  });
207
-
220
+
208
221
  setInitialTerminalMessages(terminalMessages);
209
222
  }
210
223
  }, [options?.initialMessages]);
@@ -238,10 +251,11 @@ export function AiTerminal({
238
251
  // Replace the conversation history with the authoritative response from AI
239
252
  if (updatedMessages && Array.isArray(updatedMessages)) {
240
253
  const formattedMessages = updatedMessages.map((message) => {
241
- const formattedContent = message.content
242
- ?.trim()
243
- ?.replaceAll("\n", "<br>")
244
- ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
254
+ const formattedContent =
255
+ message.content
256
+ ?.trim()
257
+ ?.replaceAll("\n", "<br>")
258
+ ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
245
259
 
246
260
  return {
247
261
  ...message,
@@ -374,7 +388,7 @@ export function AiTerminal({
374
388
  }}
375
389
  initialMessages={initialTerminalMessages}
376
390
  infobar={
377
- response?.numInputTokens && (
391
+ response?.numInputTokens && response?.numInputTokens > 0 ? (
378
392
  <div
379
393
  className="text-right text-gray-400"
380
394
  style={{ fontSize: "10px" }}
@@ -386,7 +400,7 @@ export function AiTerminal({
386
400
  : ""}
387
401
  {response?.state}
388
402
  </div>
389
- )
403
+ ) : null
390
404
  }
391
405
  prompt={prompt}
392
406
  setPrompt={setPrompt}
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useState } from "react";
2
2
 
3
- import { JsonView, defaultStyles } from "react-json-view-lite";
3
+ import JSONPretty from "react-json-pretty";
4
+
4
5
  import { Message, ToolCall } from "./AiTerminal";
5
6
  import { ProgressSpinner } from "primereact/progressspinner";
6
7
  export function AiToolCall({
@@ -34,12 +35,21 @@ export function AiToolCall({
34
35
  {!result && (
35
36
  <ProgressSpinner style={{ width: "1rem", height: "1rem" }} />
36
37
  )}
37
- {toolCall.displayName}
38
+ {toolCall.displayName || toolCall.function.name}
38
39
  </div>
39
40
  </div>
40
41
  {expanded && (
41
42
  <div className="ml-4">
42
- <div>{renderJsonOrText(toolCall.function.arguments)}</div>
43
+ <div className="mb-2">
44
+ <div className="font-semibold text-gray-600">Arguments:</div>
45
+ <div className="ml-2">{renderJsonOrText(toolCall.function.arguments)}</div>
46
+ </div>
47
+ {result && (
48
+ <div className="mb-2">
49
+ <div className="font-semibold text-gray-600">Result:</div>
50
+ <div className="ml-2">{renderJsonOrText(result.content || "")}</div>
51
+ </div>
52
+ )}
43
53
  {error && (
44
54
  <div className="ml-4 text-red-500">
45
55
  <div className="italic">Error:</div>
@@ -54,7 +64,7 @@ export function AiToolCall({
54
64
 
55
65
  function renderJsonOrText(json: string) {
56
66
  try {
57
- return <JsonView data={JSON.parse(json)} style={defaultStyles} />;
67
+ return <JSONPretty data={JSON.parse(json)} />;
58
68
  } catch (e) {
59
69
  return <div>{json}</div>;
60
70
  }
@@ -248,6 +248,9 @@ export function EditorClient({
248
248
  configuration.editor.views[0]?.name ??
249
249
  "splash-screen",
250
250
  );
251
+ const [previousViewName, setPreviousViewName] = useState<string | undefined>(
252
+ undefined,
253
+ );
251
254
 
252
255
  const [compareMode, setCompareMode] = useState(false);
253
256
  const [compareTo, setCompareTo] = useState<ItemDescriptor>();
@@ -368,7 +371,7 @@ export function EditorClient({
368
371
 
369
372
  useEffect(() => {
370
373
  if (mode === "suggestions") {
371
- setViewName("reviews");
374
+ setViewName("comments");
372
375
  }
373
376
  }, [mode]);
374
377
 
@@ -1681,6 +1684,10 @@ export function EditorClient({
1681
1684
  const result = await currentView.beforeClose(editContext);
1682
1685
  if (!result) return;
1683
1686
  }
1687
+
1688
+ // Track previous view name before switching
1689
+ setPreviousViewName(editContext.viewName);
1690
+
1684
1691
  if (typeof document.startViewTransition === "function") {
1685
1692
  document.startViewTransition(() => {
1686
1693
  flushSync(() => {
@@ -2123,6 +2130,7 @@ export function EditorClient({
2123
2130
  },
2124
2131
  executeCommand,
2125
2132
  viewName,
2133
+ previousViewName,
2126
2134
  switchView,
2127
2135
  compareMode,
2128
2136
  setCompareMode,
@@ -134,6 +134,7 @@ export type EditContextType = {
134
134
  name: string,
135
135
  ) => Promise<ItemDescriptor | undefined>;
136
136
  createVersion: (item: ItemDescriptor) => Promise<void>;
137
+ onFieldBlur: () => void;
137
138
  undoing: boolean;
138
139
  };
139
140
  comments: Comment[];
@@ -154,6 +155,7 @@ export type EditContextType = {
154
155
  view?: EditorView;
155
156
 
156
157
  viewName: string;
158
+ previousViewName?: string;
157
159
  switchView: (
158
160
  viewName: string,
159
161
  options?: { skipConfirmation?: boolean },
@@ -684,6 +684,13 @@ export function getOperationsContext(
684
684
  },
685
685
  [state.itemsRepository],
686
686
  );
687
+
688
+ const onFieldBlur = useCallback(() => {
689
+ // Reset the last operation to force creation of a new operation
690
+ // when the user returns to edit any field after losing focus
691
+ lastOp.current = undefined;
692
+ }, []);
693
+
687
694
  const ops = useMemo(
688
695
  () => ({
689
696
  addComponent,
@@ -704,6 +711,7 @@ export function getOperationsContext(
704
711
  moveItems,
705
712
  copyItems,
706
713
  duplicateItem,
714
+ onFieldBlur,
707
715
  }),
708
716
  [
709
717
  addComponent,
@@ -726,6 +734,7 @@ export function getOperationsContext(
726
734
  state.page,
727
735
  copyItems,
728
736
  duplicateItem,
737
+ onFieldBlur,
729
738
  ],
730
739
  );
731
740
 
@@ -418,7 +418,7 @@ function getCreateCommentCommand(
418
418
  visibilityScopes: ["contextMenu"],
419
419
  execute: async (context: CommandContext<any>) => {
420
420
  context.editContext.addComment();
421
- context.editContext.switchView("reviews");
421
+ context.editContext.switchView("comments");
422
422
  },
423
423
  };
424
424
  }
@@ -58,6 +58,7 @@ export function ImageFieldEditor({
58
58
  refresh: "waitForQuietPeriod",
59
59
  });
60
60
  }}
61
+ onBlur={editContext.operations.onFieldBlur}
61
62
  />
62
63
  </div>
63
64
  </div>
@@ -81,6 +81,7 @@ export function MultiLineText({
81
81
  setValue(e.target.value);
82
82
  updateFieldValue(e.target.value as string);
83
83
  }}
84
+ onBlur={editContextRef.current?.operations.onFieldBlur}
84
85
  />
85
86
  );
86
87
  }
@@ -114,6 +114,7 @@ export function PictureFieldEditor({
114
114
  refresh: "waitForQuietPeriod",
115
115
  });
116
116
  }}
117
+ onBlur={editContext.operations.onFieldBlur}
117
118
  />
118
119
  </div>
119
120
  </div>
@@ -48,6 +48,7 @@ export function RawEditor({
48
48
  debouncedSetFieldvalue(e.target.value as string);
49
49
  // field.value = e.target.value;
50
50
  }}
51
+ onBlur={editContext?.operations.onFieldBlur}
51
52
  />
52
53
  );
53
54
  }
@@ -8,8 +8,8 @@ import {
8
8
  import { useThrottledCallback } from "use-debounce";
9
9
  import { useEffect, useRef, useState, useMemo } from "react";
10
10
 
11
- import { classNames } from "primereact/utils";
12
11
  import { ReactSlate } from "./richtext";
12
+ import { SimpleRichTextEditor } from "./richtext/components/SimpleRichTextEditor";
13
13
  import { RichTextField, RichTextEditorProfile } from "./richtext/types";
14
14
 
15
15
  // Minimal fallback profile for when profile is null or undefined
@@ -17,24 +17,21 @@ const FALLBACK_PROFILE: RichTextEditorProfile = {
17
17
  toolbar: {
18
18
  groups: [
19
19
  {
20
- id: 'basic-formatting',
21
- label: 'Basic Formatting',
22
- display: 'buttons' as const,
20
+ id: "basic-formatting",
21
+ label: "Basic Formatting",
22
+ display: "buttons" as const,
23
23
  showIconsOnly: true,
24
- options: [
25
- ]
24
+ options: [],
26
25
  },
27
26
  {
28
- id: 'basic-format',
29
- label: '',
30
- display: 'dropdown' as const,
27
+ id: "basic-format",
28
+ label: "",
29
+ display: "dropdown" as const,
31
30
  showIconsOnly: false,
32
- options: [
33
- { type: 'block' as const, id: 'no-tag' }
34
- ]
35
- }
36
- ]
37
- }
31
+ options: [{ type: "block" as const, id: "no-tag" }],
32
+ },
33
+ ],
34
+ },
38
35
  };
39
36
 
40
37
  export function RichTextEditorComponent({
@@ -49,11 +46,11 @@ export function RichTextEditorComponent({
49
46
  updateFieldValue?: (value: string) => void;
50
47
  }) {
51
48
  const editContextRef = useEditContextRef();
52
- const modifiedFieldsContext = useModifiedFieldsContext();
49
+ const modifiedFieldsContext = useModifiedFieldsContext();
53
50
  const [focused, setFocused] = useState(false);
54
51
  const [value, setValue] = useState(field.value as string);
55
52
  const editorRef = useRef<HTMLDivElement>(null);
56
-
53
+
57
54
  if (!editContextRef.current) return null;
58
55
 
59
56
  const fieldItem = field.descriptor.item;
@@ -69,11 +66,12 @@ export function RichTextEditorComponent({
69
66
 
70
67
  useEffect(() => {
71
68
  // Only update if the editor is not currently being edited by the user
72
- const isEditorActive = focused && editorRef.current?.contains(document.activeElement);
73
- const newValue = field.isHistoric
74
- ? field.value as string
75
- : modifiedField?.value ?? (field.value as string);
76
-
69
+ const isEditorActive =
70
+ focused && editorRef.current?.contains(document.activeElement);
71
+ const newValue = field.isHistoric
72
+ ? (field.value as string)
73
+ : (modifiedField?.value ?? (field.value as string));
74
+
77
75
  if (!isEditorActive && newValue !== value) {
78
76
  setValue(newValue);
79
77
  }
@@ -95,7 +93,11 @@ export function RichTextEditorComponent({
95
93
 
96
94
  const handleChange = (newValue: string) => {
97
95
  // Skip empty content patterns for both paragraph and no-tag blocks
98
- if ((newValue === "<p><br></p>" || newValue === "<br>" || newValue === "") && !field.value) return;
96
+ if (
97
+ (newValue === "<p><br></p>" || newValue === "<br>" || newValue === "") &&
98
+ !field.value
99
+ )
100
+ return;
99
101
  if (newValue !== value && !readOnly) {
100
102
  setValue(newValue);
101
103
  debouncedSetFieldvalue(newValue);
@@ -104,13 +106,13 @@ export function RichTextEditorComponent({
104
106
 
105
107
  // Use the profile directly or fall back to the minimal profile
106
108
  const editorProfile = profile ?? FALLBACK_PROFILE;
107
-
109
+
108
110
  // const editorProfile = getDebugProfile(); // Debug profile with all options
109
111
  // console.log(editorProfile);
110
112
 
111
113
  return (
112
114
  <div ref={editorRef}>
113
- <ReactSlate
115
+ <SimpleRichTextEditor
114
116
  value={value}
115
117
  onChange={handleChange}
116
118
  onFocus={() => setFocused(true)}
@@ -172,6 +172,7 @@ export function SingleLineText({
172
172
  style={{ width: "100%" }}
173
173
  onChange={handleChange}
174
174
  onSelect={handleSelect}
175
+ onBlur={editContextRef.current?.operations.onFieldBlur}
175
176
  />
176
177
  </div>
177
178
  );