@alpaca-editor/core 1.0.3938 → 1.0.3939
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor/ContentTree.js +12 -8
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/ContextMenu.d.ts +1 -1
- package/dist/editor/ContextMenu.js +17 -3
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/FieldActionsOverlay.d.ts +17 -0
- package/dist/editor/FieldActionsOverlay.js +148 -0
- package/dist/editor/FieldActionsOverlay.js.map +1 -0
- package/dist/editor/FieldHistory.d.ts +2 -1
- package/dist/editor/FieldHistory.js +11 -8
- package/dist/editor/FieldHistory.js.map +1 -1
- package/dist/editor/FieldListField.js +14 -17
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/PictureEditor.js +28 -2
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/Titlebar.js +18 -9
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AiTerminal.js +27 -41
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/client/EditorClient.js +48 -18
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +1 -1
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +126 -90
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/menubar/BrowseHistory.js +3 -4
- package/dist/editor/menubar/BrowseHistory.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +37 -9
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js +1 -1
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +98 -2
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/pageModel.d.ts +14 -0
- package/dist/editor/reviews/Comment.js +3 -2
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/services/editService.d.ts +1 -1
- package/dist/editor/services/editService.js +2 -1
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/ui/Icons.js +1 -1
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/ItemList.d.ts +16 -0
- package/dist/editor/ui/ItemList.js +19 -0
- package/dist/editor/ui/ItemList.js.map +1 -0
- package/dist/editor/ui/ItemSearch.js +2 -12
- package/dist/editor/ui/ItemSearch.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.js +1 -1
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +3 -8
- package/package.json +1 -1
- package/src/editor/ContentTree.tsx +15 -12
- package/src/editor/ContextMenu.tsx +20 -2
- package/src/editor/FieldActionsOverlay.tsx +307 -0
- package/src/editor/FieldHistory.tsx +9 -8
- package/src/editor/FieldListField.tsx +29 -29
- package/src/editor/PictureEditor.tsx +66 -1
- package/src/editor/Titlebar.tsx +22 -11
- package/src/editor/ai/AiTerminal.tsx +42 -53
- package/src/editor/client/EditorClient.tsx +62 -18
- package/src/editor/client/editContext.ts +5 -1
- package/src/editor/client/itemsRepository.ts +151 -115
- package/src/editor/menubar/BrowseHistory.tsx +7 -16
- package/src/editor/menubar/PageSelector.tsx +91 -66
- package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +1 -1
- package/src/editor/page-viewer/PageViewerFrame.tsx +143 -0
- package/src/editor/pageModel.ts +12 -0
- package/src/editor/reviews/Comment.tsx +5 -6
- package/src/editor/services/editService.ts +2 -0
- package/src/editor/ui/Icons.tsx +1 -0
- package/src/editor/ui/ItemList.tsx +76 -0
- package/src/editor/ui/ItemSearch.tsx +9 -46
- package/src/editor/ui/SimpleTabs.tsx +1 -1
- package/src/revision.ts +2 -2
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
MouseEventHandler,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
2
8
|
import { useEditContext } from "./client/editContext";
|
|
3
9
|
import { PictureCropper } from "./PictureCropper";
|
|
4
10
|
import { PictureField, PictureRawValue } from "./fieldTypes";
|
|
5
11
|
import { MediaSelectorMode } from "./media-selector/MediaSelector";
|
|
12
|
+
import { loadFieldButtons } from "./services/editService";
|
|
13
|
+
import { FieldButton } from "./pageModel";
|
|
14
|
+
import {
|
|
15
|
+
FieldActionsOverlay,
|
|
16
|
+
FieldActionsOverlayRef,
|
|
17
|
+
} from "./FieldActionsOverlay";
|
|
6
18
|
|
|
7
19
|
export function PictureEditor({
|
|
8
20
|
field,
|
|
@@ -19,8 +31,43 @@ export function PictureEditor({
|
|
|
19
31
|
}) {
|
|
20
32
|
const [showMenu, setShowMenu] = useState(false);
|
|
21
33
|
const [showCropper, setShowCropper] = useState(false);
|
|
34
|
+
const [fieldButtons, setFieldButtons] = useState<FieldButton[]>([]);
|
|
35
|
+
const fieldActionsOverlay = useRef<FieldActionsOverlayRef>(null);
|
|
22
36
|
const editContext = useEditContext();
|
|
23
37
|
|
|
38
|
+
// Load field buttons when component mounts
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const loadButtons = async () => {
|
|
41
|
+
if (field.descriptor) {
|
|
42
|
+
try {
|
|
43
|
+
const buttons = await loadFieldButtons(field.descriptor);
|
|
44
|
+
setFieldButtons(buttons);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.warn("Failed to load field buttons:", error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
loadButtons();
|
|
51
|
+
}, [field.descriptor]);
|
|
52
|
+
|
|
53
|
+
// Field action handlers
|
|
54
|
+
const handleActionClick = useCallback(
|
|
55
|
+
async (action: FieldButton): Promise<void> => {
|
|
56
|
+
editContext?.triggerFieldAction(field.descriptor, action);
|
|
57
|
+
},
|
|
58
|
+
[editContext, field.descriptor],
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const handleParameterizedActionExecute = useCallback(
|
|
62
|
+
async (
|
|
63
|
+
action: FieldButton,
|
|
64
|
+
parameters: Record<string, string>,
|
|
65
|
+
): Promise<void> => {
|
|
66
|
+
editContext?.triggerFieldAction(field.descriptor, action, parameters);
|
|
67
|
+
},
|
|
68
|
+
[editContext, field.descriptor],
|
|
69
|
+
);
|
|
70
|
+
|
|
24
71
|
const variant = field.value?.variants?.find((v) => v.name === variantName);
|
|
25
72
|
const raw = (() => {
|
|
26
73
|
try {
|
|
@@ -174,6 +221,15 @@ export function PictureEditor({
|
|
|
174
221
|
className="min-w-[80px]"
|
|
175
222
|
/>
|
|
176
223
|
)}
|
|
224
|
+
{fieldButtons.length > 0 && (
|
|
225
|
+
<Btn
|
|
226
|
+
label="Actions"
|
|
227
|
+
icon="pi pi-bolt"
|
|
228
|
+
onClick={(e) => fieldActionsOverlay.current?.show(e)}
|
|
229
|
+
testId="field-actions-button"
|
|
230
|
+
className="min-w-[80px]"
|
|
231
|
+
/>
|
|
232
|
+
)}
|
|
177
233
|
</div>
|
|
178
234
|
)}
|
|
179
235
|
{showCropper && (
|
|
@@ -183,6 +239,15 @@ export function PictureEditor({
|
|
|
183
239
|
variantName={variantName}
|
|
184
240
|
/>
|
|
185
241
|
)}
|
|
242
|
+
<FieldActionsOverlay
|
|
243
|
+
ref={fieldActionsOverlay}
|
|
244
|
+
generatorButtons={fieldButtons}
|
|
245
|
+
onActionClick={handleActionClick}
|
|
246
|
+
onParameterizedActionExecute={handleParameterizedActionExecute}
|
|
247
|
+
currentOverlay={editContext?.currentOverlay}
|
|
248
|
+
fieldId={field.id}
|
|
249
|
+
setCurrentOverlay={editContext?.setCurrentOverlay || (() => {})}
|
|
250
|
+
/>
|
|
186
251
|
</div>
|
|
187
252
|
);
|
|
188
253
|
}
|
package/src/editor/Titlebar.tsx
CHANGED
|
@@ -25,21 +25,32 @@ export function Titlebar() {
|
|
|
25
25
|
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
const handleClickOutside = (event: MouseEvent) => {
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
if (!menuOpen) return;
|
|
29
|
+
|
|
30
|
+
const target = event.target as Element;
|
|
31
|
+
const menuElement = menuRef.current;
|
|
32
|
+
const buttonElement = buttonRef.current;
|
|
33
|
+
|
|
34
|
+
// Check if the click is inside the menu or button
|
|
35
|
+
const isInsideMenu = menuElement && menuElement.contains(target);
|
|
36
|
+
const isInsideButton = buttonElement && buttonElement.contains(target);
|
|
37
|
+
|
|
38
|
+
// Check if the click is inside a PrimeReact overlay (which renders in portals)
|
|
39
|
+
const isInsideOverlay = target.closest(".p-overlaypanel") !== null;
|
|
40
|
+
|
|
41
|
+
// Close menu only if click is outside menu, button, and overlays
|
|
42
|
+
if (!isInsideMenu && !isInsideButton && !isInsideOverlay) {
|
|
35
43
|
setMenuOpen(false);
|
|
36
44
|
}
|
|
37
45
|
};
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
if (menuOpen) {
|
|
48
|
+
document.addEventListener("click", handleClickOutside, true);
|
|
49
|
+
|
|
50
|
+
return () => {
|
|
51
|
+
document.removeEventListener("click", handleClickOutside, true);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
43
54
|
}, [menuOpen]);
|
|
44
55
|
|
|
45
56
|
return (
|
|
@@ -121,72 +121,60 @@ export function AiTerminal({
|
|
|
121
121
|
terminalCallback: (text: React.ReactNode, finished: boolean) => void,
|
|
122
122
|
isFinished: boolean,
|
|
123
123
|
) {
|
|
124
|
-
const currentMessages = messagesRef.current;
|
|
125
124
|
const updatedMessages = response.messages;
|
|
126
125
|
|
|
127
|
-
//
|
|
126
|
+
// Replace the conversation history with the authoritative response from AI
|
|
128
127
|
if (updatedMessages && Array.isArray(updatedMessages)) {
|
|
129
|
-
|
|
130
|
-
updatedMessages.forEach((message) => {
|
|
131
|
-
const existingMessageIndex = currentMessages.findIndex(
|
|
132
|
-
(m) => m.id === message.id,
|
|
133
|
-
);
|
|
128
|
+
const formattedMessages = updatedMessages.map((message) => {
|
|
134
129
|
const formattedContent = message.content
|
|
135
130
|
.trim()
|
|
136
131
|
.replaceAll("\n", "<br>")
|
|
137
132
|
?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>");
|
|
138
133
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
formattedContent: formattedContent,
|
|
146
|
-
tool_calls:
|
|
147
|
-
message.tool_calls ||
|
|
148
|
-
currentMessages[existingMessageIndex]?.tool_calls ||
|
|
149
|
-
[],
|
|
150
|
-
};
|
|
151
|
-
} else {
|
|
152
|
-
// Add new message
|
|
153
|
-
currentMessages.push({
|
|
154
|
-
...message,
|
|
155
|
-
content: formattedContent,
|
|
156
|
-
tool_calls: message.tool_calls || [], // Ensure toolCalls exists
|
|
157
|
-
});
|
|
158
|
-
}
|
|
134
|
+
return {
|
|
135
|
+
...message,
|
|
136
|
+
content: message.content,
|
|
137
|
+
formattedContent: formattedContent,
|
|
138
|
+
tool_calls: message.tool_calls || [],
|
|
139
|
+
};
|
|
159
140
|
});
|
|
160
|
-
}
|
|
161
141
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
142
|
+
// Update the messages state with the complete conversation from AI
|
|
143
|
+
setMessages([...formattedMessages]);
|
|
144
|
+
setResponseMessages([...formattedMessages]);
|
|
145
|
+
|
|
146
|
+
terminalCallback(
|
|
147
|
+
<AiResponseMessage
|
|
148
|
+
messages={formattedMessages}
|
|
149
|
+
editOperations={response.editOperations}
|
|
150
|
+
finished={isFinished}
|
|
151
|
+
/>,
|
|
152
|
+
isFinished,
|
|
153
|
+
);
|
|
154
|
+
} else {
|
|
155
|
+
// Fallback: if no messages in response, keep current state
|
|
156
|
+
terminalCallback(
|
|
157
|
+
<AiResponseMessage
|
|
158
|
+
messages={messagesRef.current}
|
|
159
|
+
editOperations={response.editOperations}
|
|
160
|
+
finished={isFinished}
|
|
161
|
+
/>,
|
|
162
|
+
isFinished,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
174
165
|
}
|
|
175
166
|
|
|
176
167
|
async function commandHandler(
|
|
177
168
|
text: string,
|
|
178
169
|
callback: (text: React.ReactNode, finished: boolean) => void,
|
|
179
170
|
) {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
tool_calls: [], // Add empty toolCalls array
|
|
188
|
-
},
|
|
189
|
-
];
|
|
171
|
+
const userMessage = {
|
|
172
|
+
id: Date.now(), // Add unique id
|
|
173
|
+
content: text,
|
|
174
|
+
role: "user",
|
|
175
|
+
name: "user",
|
|
176
|
+
tool_calls: [], // Add empty toolCalls array
|
|
177
|
+
};
|
|
190
178
|
|
|
191
179
|
const context = createAiContext({ editContext });
|
|
192
180
|
|
|
@@ -194,8 +182,8 @@ export function AiTerminal({
|
|
|
194
182
|
const selectedText = editContext?.selectedRange?.text || null;
|
|
195
183
|
if (!activeProfile || !model) return;
|
|
196
184
|
|
|
197
|
-
|
|
198
|
-
|
|
185
|
+
// Build complete message history for API call
|
|
186
|
+
const conversationHistory = [...messagesRef.current, userMessage];
|
|
199
187
|
const messages = [
|
|
200
188
|
...(options?.hiddenSystemPrompt
|
|
201
189
|
? [
|
|
@@ -208,7 +196,7 @@ export function AiTerminal({
|
|
|
208
196
|
},
|
|
209
197
|
]
|
|
210
198
|
: []),
|
|
211
|
-
...
|
|
199
|
+
...conversationHistory,
|
|
212
200
|
];
|
|
213
201
|
|
|
214
202
|
const response = await executePrompt(
|
|
@@ -318,6 +306,7 @@ export function AiTerminal({
|
|
|
318
306
|
ref={terminalRef}
|
|
319
307
|
onReset={() => {
|
|
320
308
|
setMessages([]);
|
|
309
|
+
setResponseMessages([]);
|
|
321
310
|
setResponse(undefined);
|
|
322
311
|
}}
|
|
323
312
|
infobar={
|
|
@@ -701,29 +701,71 @@ export function EditorClient({
|
|
|
701
701
|
}, [user]);
|
|
702
702
|
|
|
703
703
|
useEffect(() => {
|
|
704
|
-
let
|
|
704
|
+
let reconnectTimeout: NodeJS.Timeout | null = null;
|
|
705
|
+
let reconnectAttempts = 0;
|
|
705
706
|
|
|
706
|
-
|
|
707
|
-
socket
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
707
|
+
const connectWebSocket = () => {
|
|
708
|
+
let socket: WebSocket = (globalThis as any).editorSocket;
|
|
709
|
+
|
|
710
|
+
if (
|
|
711
|
+
socket &&
|
|
712
|
+
(socket.readyState === WebSocket.OPEN ||
|
|
713
|
+
socket.readyState === WebSocket.CONNECTING)
|
|
714
|
+
)
|
|
715
|
+
return;
|
|
712
716
|
|
|
713
|
-
|
|
717
|
+
socket = connectSocket(sessionId);
|
|
714
718
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
719
|
+
// Connection opened
|
|
720
|
+
socket.addEventListener("open", () => {
|
|
721
|
+
console.log("Connected!");
|
|
722
|
+
reconnectAttempts = 0; // Reset attempts on successful connection
|
|
723
|
+
sendClientInfo();
|
|
724
|
+
requestQuota();
|
|
725
|
+
//TODO: Load clients
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
// Listen for messages
|
|
729
|
+
socket.addEventListener("message", messageHandler);
|
|
730
|
+
|
|
731
|
+
// Handle connection close
|
|
732
|
+
socket.addEventListener("close", (event) => {
|
|
733
|
+
console.log("WebSocket connection closed:", event.code, event.reason);
|
|
734
|
+
|
|
735
|
+
// Only attempt to reconnect if it wasn't a clean close
|
|
736
|
+
if (event.code !== 1000) {
|
|
737
|
+
// Start with 1 second, increase exponentially up to 30 seconds
|
|
738
|
+
const delay = Math.min(
|
|
739
|
+
1000 * Math.pow(2, Math.min(reconnectAttempts, 5)),
|
|
740
|
+
30000,
|
|
741
|
+
);
|
|
742
|
+
console.log(
|
|
743
|
+
`Attempting to reconnect in ${delay}ms... (attempt ${reconnectAttempts + 1})`,
|
|
744
|
+
);
|
|
745
|
+
|
|
746
|
+
reconnectTimeout = setTimeout(() => {
|
|
747
|
+
reconnectAttempts++;
|
|
748
|
+
connectWebSocket();
|
|
749
|
+
}, delay);
|
|
750
|
+
}
|
|
751
|
+
});
|
|
722
752
|
|
|
723
|
-
|
|
724
|
-
|
|
753
|
+
// Handle connection errors
|
|
754
|
+
socket.addEventListener("error", (error) => {
|
|
755
|
+
console.error("WebSocket error:", error);
|
|
756
|
+
});
|
|
725
757
|
|
|
726
|
-
|
|
758
|
+
(globalThis as any).editorSocket = socket;
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
connectWebSocket();
|
|
762
|
+
|
|
763
|
+
// Cleanup function
|
|
764
|
+
return () => {
|
|
765
|
+
if (reconnectTimeout) {
|
|
766
|
+
clearTimeout(reconnectTimeout);
|
|
767
|
+
}
|
|
768
|
+
};
|
|
727
769
|
}, []);
|
|
728
770
|
|
|
729
771
|
useEffect(() => {
|
|
@@ -1790,6 +1832,7 @@ export function EditorClient({
|
|
|
1790
1832
|
triggerFieldAction: async (
|
|
1791
1833
|
fieldDescriptor: FieldDescriptor,
|
|
1792
1834
|
actionButton: FieldButton,
|
|
1835
|
+
parameters?: Record<string, string>,
|
|
1793
1836
|
) => {
|
|
1794
1837
|
const field = await itemsRepository.getField(fieldDescriptor);
|
|
1795
1838
|
|
|
@@ -1831,6 +1874,7 @@ export function EditorClient({
|
|
|
1831
1874
|
actionButton.action,
|
|
1832
1875
|
editContext!.sessionId,
|
|
1833
1876
|
selectedRange?.text || "",
|
|
1877
|
+
parameters || {},
|
|
1834
1878
|
(data: any) => {
|
|
1835
1879
|
op.message = data.responseText;
|
|
1836
1880
|
setActiveFieldActions((prevFieldActions) => [
|
|
@@ -235,7 +235,11 @@ export type EditContextType = {
|
|
|
235
235
|
unlockField: (field: FieldDescriptor) => void;
|
|
236
236
|
|
|
237
237
|
readonly: boolean;
|
|
238
|
-
triggerFieldAction: (
|
|
238
|
+
triggerFieldAction: (
|
|
239
|
+
field: FieldDescriptor,
|
|
240
|
+
action: FieldButton,
|
|
241
|
+
parameters?: Record<string, string>,
|
|
242
|
+
) => void;
|
|
239
243
|
activeFieldActions: FieldAction[];
|
|
240
244
|
showContextMenu(e: any, menuItems: MenuItem[]): void;
|
|
241
245
|
setCurrentOverlay: React.Dispatch<React.SetStateAction<any>>;
|