@alpaca-editor/core 1.0.3937 → 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/FieldListFieldWithFallbacks.js +10 -5
- package/dist/editor/FieldListFieldWithFallbacks.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 -3
- 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/page-wizard/WizardSteps.js +10 -0
- package/dist/page-wizard/WizardSteps.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/FieldListFieldWithFallbacks.tsx +11 -5
- 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 -2
- 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/page-wizard/WizardSteps.tsx +13 -0
- package/src/revision.ts +2 -2
|
@@ -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>>;
|
|
@@ -82,52 +82,59 @@ export function useItemsRepository(
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
const fetchPromise = (async () => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
!prev?.find(
|
|
115
|
-
(x) =>
|
|
116
|
-
x.id === descriptor.id &&
|
|
117
|
-
x.language === descriptor.language &&
|
|
118
|
-
x.version === descriptor.version,
|
|
119
|
-
)
|
|
120
|
-
) {
|
|
121
|
-
fallbackDependencyMap.current.set(
|
|
85
|
+
try {
|
|
86
|
+
const fetchedItems = await fetchItems(itemsToFetch);
|
|
87
|
+
|
|
88
|
+
if (!fetchedItems) return;
|
|
89
|
+
|
|
90
|
+
fetchedItems.forEach((item, index) => {
|
|
91
|
+
if (!item) return;
|
|
92
|
+
const descriptor = getItemDescriptor(item);
|
|
93
|
+
const itemToStore = {
|
|
94
|
+
...item,
|
|
95
|
+
descriptor,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const primaryKey = generateKey(item);
|
|
99
|
+
itemsMap.current.set(primaryKey, itemToStore);
|
|
100
|
+
|
|
101
|
+
// Only store version 0 if the current item isn't already version 0
|
|
102
|
+
if (itemsToFetch[index]?.version === 0 && item.version !== 0) {
|
|
103
|
+
const versionZeroKey = generateKey(itemsToFetch[index]);
|
|
104
|
+
itemsMap.current.set(versionZeroKey, itemToStore);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (item.fields) {
|
|
108
|
+
item.fields.forEach((field) => {
|
|
109
|
+
if (field.fallbackChain) {
|
|
110
|
+
const descriptor = getItemDescriptor(item);
|
|
111
|
+
field.fallbackChain.forEach((fallback) => {
|
|
112
|
+
const prev = fallbackDependencyMap.current.get(
|
|
122
113
|
getItemIdLanguageKey(fallback.item),
|
|
123
|
-
[...(prev || []), descriptor],
|
|
124
114
|
);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
!prev?.find(
|
|
118
|
+
(x) =>
|
|
119
|
+
x.id === descriptor.id &&
|
|
120
|
+
x.language === descriptor.language &&
|
|
121
|
+
x.version === descriptor.version,
|
|
122
|
+
)
|
|
123
|
+
) {
|
|
124
|
+
fallbackDependencyMap.current.set(
|
|
125
|
+
getItemIdLanguageKey(fallback.item),
|
|
126
|
+
[...(prev || []), descriptor],
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error("Error fetching items:", error);
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
131
138
|
})();
|
|
132
139
|
|
|
133
140
|
// Store the promise for each item being fetched
|
|
@@ -145,7 +152,7 @@ export function useItemsRepository(
|
|
|
145
152
|
});
|
|
146
153
|
}
|
|
147
154
|
},
|
|
148
|
-
[
|
|
155
|
+
[],
|
|
149
156
|
);
|
|
150
157
|
|
|
151
158
|
const getItems = useCallback(
|
|
@@ -168,55 +175,7 @@ export function useItemsRepository(
|
|
|
168
175
|
.map((x) => map.current.get(generateKey(x)))
|
|
169
176
|
.filter((x): x is FullItem => x !== undefined);
|
|
170
177
|
},
|
|
171
|
-
[
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
const getItemsStubs = useCallback(
|
|
175
|
-
async (descriptors: ItemDescriptor[]): Promise<ItemStub[]> => {
|
|
176
|
-
const keys = descriptors.map((x) => ({
|
|
177
|
-
key: generateKey(x),
|
|
178
|
-
descriptor: x,
|
|
179
|
-
}));
|
|
180
|
-
|
|
181
|
-
// First try to get stubs from full items if available
|
|
182
|
-
const result: ItemStub[] = [];
|
|
183
|
-
const itemsToFetch: ItemDescriptor[] = [];
|
|
184
|
-
|
|
185
|
-
for (const { key, descriptor } of keys) {
|
|
186
|
-
// If we have the full item, use it
|
|
187
|
-
const fullItem = itemsMap.current.get(key);
|
|
188
|
-
if (fullItem) {
|
|
189
|
-
result.push(fullItem);
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// If we have a cached stub, use it
|
|
194
|
-
const cachedStub = stubsMap.current.get(key);
|
|
195
|
-
if (cachedStub) {
|
|
196
|
-
result.push(cachedStub);
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Otherwise, fetch it
|
|
201
|
-
itemsToFetch.push(descriptor);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (itemsToFetch.length > 0) {
|
|
205
|
-
await fetchStubsFromServer(itemsToFetch);
|
|
206
|
-
|
|
207
|
-
// Add fetched stubs to the result
|
|
208
|
-
for (const descriptor of itemsToFetch) {
|
|
209
|
-
const key = generateKey(descriptor);
|
|
210
|
-
const stub = stubsMap.current.get(key);
|
|
211
|
-
if (stub) {
|
|
212
|
-
result.push(stub);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return result;
|
|
218
|
-
},
|
|
219
|
-
[itemsMap.current, stubsMap.current],
|
|
178
|
+
[fetchItemsFromServer],
|
|
220
179
|
);
|
|
221
180
|
|
|
222
181
|
const fetchStubsFromServer = useCallback(
|
|
@@ -242,19 +201,26 @@ export function useItemsRepository(
|
|
|
242
201
|
}
|
|
243
202
|
|
|
244
203
|
const fetchPromise = (async () => {
|
|
245
|
-
|
|
204
|
+
try {
|
|
205
|
+
const fetchedStubs = await fetchItemStubs(itemsToFetch);
|
|
246
206
|
|
|
247
|
-
|
|
207
|
+
if (!fetchedStubs) return;
|
|
248
208
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
209
|
+
fetchedStubs.forEach((stub, index) => {
|
|
210
|
+
if (!stub) return;
|
|
211
|
+
const primaryKey = generateKey(stub);
|
|
212
|
+
stubsMap.current.set(primaryKey, stub);
|
|
252
213
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
214
|
+
// Only store version 0 if the current stub isn't already version 0
|
|
215
|
+
if (itemsToFetch[index]?.version === 0 && stub.version !== 0) {
|
|
216
|
+
const versionZeroKey = generateKey(itemsToFetch[index]);
|
|
217
|
+
stubsMap.current.set(versionZeroKey, stub);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error("Error fetching stubs:", error);
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
258
224
|
})();
|
|
259
225
|
|
|
260
226
|
// Store the promise for each item being fetched
|
|
@@ -272,15 +238,55 @@ export function useItemsRepository(
|
|
|
272
238
|
});
|
|
273
239
|
}
|
|
274
240
|
},
|
|
275
|
-
[
|
|
241
|
+
[],
|
|
276
242
|
);
|
|
277
243
|
|
|
278
|
-
const
|
|
279
|
-
async (
|
|
280
|
-
const
|
|
281
|
-
|
|
244
|
+
const getItemsStubs = useCallback(
|
|
245
|
+
async (descriptors: ItemDescriptor[]): Promise<ItemStub[]> => {
|
|
246
|
+
const keys = descriptors.map((x) => ({
|
|
247
|
+
key: generateKey(x),
|
|
248
|
+
descriptor: x,
|
|
249
|
+
}));
|
|
250
|
+
|
|
251
|
+
// First try to get stubs from full items if available
|
|
252
|
+
const result: ItemStub[] = [];
|
|
253
|
+
const itemsToFetch: ItemDescriptor[] = [];
|
|
254
|
+
|
|
255
|
+
for (const { key, descriptor } of keys) {
|
|
256
|
+
// If we have the full item, use it
|
|
257
|
+
const fullItem = itemsMap.current.get(key);
|
|
258
|
+
if (fullItem) {
|
|
259
|
+
result.push(fullItem);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// If we have a cached stub, use it
|
|
264
|
+
const cachedStub = stubsMap.current.get(key);
|
|
265
|
+
if (cachedStub) {
|
|
266
|
+
result.push(cachedStub);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Otherwise, fetch it
|
|
271
|
+
itemsToFetch.push(descriptor);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (itemsToFetch.length > 0) {
|
|
275
|
+
await fetchStubsFromServer(itemsToFetch);
|
|
276
|
+
|
|
277
|
+
// Add fetched stubs to the result
|
|
278
|
+
for (const descriptor of itemsToFetch) {
|
|
279
|
+
const key = generateKey(descriptor);
|
|
280
|
+
const stub = stubsMap.current.get(key);
|
|
281
|
+
if (stub) {
|
|
282
|
+
result.push(stub);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return result;
|
|
282
288
|
},
|
|
283
|
-
[
|
|
289
|
+
[fetchStubsFromServer],
|
|
284
290
|
);
|
|
285
291
|
|
|
286
292
|
const getItem = useCallback(
|
|
@@ -291,6 +297,19 @@ export function useItemsRepository(
|
|
|
291
297
|
[getItems],
|
|
292
298
|
);
|
|
293
299
|
|
|
300
|
+
const getField = useCallback(
|
|
301
|
+
async (fieldDescriptor: FieldDescriptor): Promise<Field | undefined> => {
|
|
302
|
+
try {
|
|
303
|
+
const item = await getItem(fieldDescriptor.item);
|
|
304
|
+
return item?.fields.find((x) => x.id === fieldDescriptor.fieldId);
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error("Error getting field:", error);
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
[getItem],
|
|
311
|
+
);
|
|
312
|
+
|
|
294
313
|
const updateFieldValue = useCallback(
|
|
295
314
|
async (
|
|
296
315
|
field: FieldDescriptor,
|
|
@@ -305,7 +324,21 @@ export function useItemsRepository(
|
|
|
305
324
|
const fieldToUpdate = item.fields.find((x) => x.id === field.fieldId);
|
|
306
325
|
if (!fieldToUpdate) return;
|
|
307
326
|
|
|
308
|
-
|
|
327
|
+
// Update the field value in the item copy
|
|
328
|
+
const updatedItem = {
|
|
329
|
+
...item,
|
|
330
|
+
fields: item.fields.map((f) =>
|
|
331
|
+
f.id === field.fieldId
|
|
332
|
+
? {
|
|
333
|
+
...f,
|
|
334
|
+
rawValue: rawValue !== undefined ? rawValue : String(value),
|
|
335
|
+
value,
|
|
336
|
+
}
|
|
337
|
+
: f,
|
|
338
|
+
),
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
itemsMap.current.set(generateKey(field.item), updatedItem);
|
|
309
342
|
|
|
310
343
|
setModifiedFields((prevModifiedFields) => {
|
|
311
344
|
const val = rawValue !== undefined ? rawValue : value;
|
|
@@ -353,7 +386,7 @@ export function useItemsRepository(
|
|
|
353
386
|
|
|
354
387
|
// setRevision((prev) => prev + 1);
|
|
355
388
|
},
|
|
356
|
-
[
|
|
389
|
+
[getItem, setModifiedFields, setLastEditedFields],
|
|
357
390
|
);
|
|
358
391
|
|
|
359
392
|
const refreshItems = useCallback(
|
|
@@ -426,7 +459,7 @@ export function useItemsRepository(
|
|
|
426
459
|
items.map((x) => ({ item: x, action: "update" })),
|
|
427
460
|
);
|
|
428
461
|
},
|
|
429
|
-
[setRevision,
|
|
462
|
+
[setRevision, fetchItemsFromServer, setModifiedFields],
|
|
430
463
|
);
|
|
431
464
|
|
|
432
465
|
const onFieldSaved = useCallback(
|
|
@@ -453,7 +486,7 @@ export function useItemsRepository(
|
|
|
453
486
|
}
|
|
454
487
|
triggerItemsChangedEvent([{ item: field.item, action: "update" }]);
|
|
455
488
|
},
|
|
456
|
-
[setModifiedFields],
|
|
489
|
+
[setModifiedFields, refreshItems],
|
|
457
490
|
);
|
|
458
491
|
|
|
459
492
|
const onItemsDeleted = useCallback(
|
|
@@ -473,7 +506,7 @@ export function useItemsRepository(
|
|
|
473
506
|
);
|
|
474
507
|
setRevision((prev) => prev + 1);
|
|
475
508
|
},
|
|
476
|
-
[
|
|
509
|
+
[setRevision],
|
|
477
510
|
);
|
|
478
511
|
|
|
479
512
|
const subscribeItemsChanged = useCallback(
|
|
@@ -524,7 +557,10 @@ export function useItemsRepository(
|
|
|
524
557
|
getField,
|
|
525
558
|
revision,
|
|
526
559
|
updateFieldValue,
|
|
560
|
+
refreshItems,
|
|
561
|
+
onFieldSaved,
|
|
527
562
|
onItemsDeleted,
|
|
563
|
+
subscribeItemsChanged,
|
|
528
564
|
clear,
|
|
529
565
|
]);
|
|
530
566
|
|
|
@@ -2,6 +2,7 @@ import { useEditContext } from "../client/editContext";
|
|
|
2
2
|
|
|
3
3
|
import { ItemDescriptor } from "../pageModel";
|
|
4
4
|
import { ResultItem } from "../ui/ItemSearch";
|
|
5
|
+
import { ItemList } from "../ui/ItemList";
|
|
5
6
|
|
|
6
7
|
export function BrowseHistory({
|
|
7
8
|
setHoveredItem,
|
|
@@ -17,21 +18,11 @@ export function BrowseHistory({
|
|
|
17
18
|
const history = editContext.browseHistory;
|
|
18
19
|
|
|
19
20
|
return (
|
|
20
|
-
<
|
|
21
|
-
{history
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
className="text-xs cursor-pointer hover:text-gray-400 flex gap-1"
|
|
27
|
-
onMouseEnter={() => setHoveredItem(x)}
|
|
28
|
-
onMouseLeave={() => setHoveredItem(undefined)}
|
|
29
|
-
onClick={() => itemSelected(x)}
|
|
30
|
-
>
|
|
31
|
-
{x.icon && <img src={x.icon} width="16" height="16" />} {x.path}{" "}
|
|
32
|
-
<span className="text-500">({x.language})</span>
|
|
33
|
-
</div>
|
|
34
|
-
))}
|
|
35
|
-
</div>
|
|
21
|
+
<ItemList
|
|
22
|
+
items={history}
|
|
23
|
+
onItemHover={setHoveredItem}
|
|
24
|
+
onItemClick={itemSelected}
|
|
25
|
+
maxItems={12}
|
|
26
|
+
/>
|
|
36
27
|
);
|
|
37
28
|
}
|