@alpaca-editor/core 1.0.4073 → 1.0.4074
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/Editor.js +3 -3
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/client/{EditorClient.d.ts → EditorShell.d.ts} +1 -19
- package/dist/editor/client/{EditorClient.js → EditorShell.js} +55 -572
- package/dist/editor/client/EditorShell.js.map +1 -0
- package/dist/editor/client/editContext.d.ts +2 -1
- package/dist/editor/client/hooks/useEditorUrlSync.d.ts +18 -0
- package/dist/editor/client/hooks/useEditorUrlSync.js +56 -0
- package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -0
- package/dist/editor/client/hooks/useEditorWebSocket.d.ts +11 -0
- package/dist/editor/client/hooks/useEditorWebSocket.js +70 -0
- package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -0
- package/dist/editor/client/hooks/useGlobalEditorEvents.d.ts +4 -0
- package/dist/editor/client/hooks/useGlobalEditorEvents.js +15 -0
- package/dist/editor/client/hooks/useGlobalEditorEvents.js.map +1 -0
- package/dist/editor/client/hooks/useMediaQuery.d.ts +1 -0
- package/dist/editor/client/hooks/useMediaQuery.js +19 -0
- package/dist/editor/client/hooks/useMediaQuery.js.map +1 -0
- package/dist/editor/client/hooks/useMediaSelector.d.ts +12 -0
- package/dist/editor/client/hooks/useMediaSelector.js +30 -0
- package/dist/editor/client/hooks/useMediaSelector.js.map +1 -0
- package/dist/editor/client/hooks/useQuota.d.ts +29 -0
- package/dist/editor/client/hooks/useQuota.js +53 -0
- package/dist/editor/client/hooks/useQuota.js.map +1 -0
- package/dist/editor/client/hooks/useSocketMessageHandler.d.ts +34 -0
- package/dist/editor/client/hooks/useSocketMessageHandler.js +191 -0
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -0
- package/dist/editor/client/hooks/useWorkbox.d.ts +9 -0
- package/dist/editor/client/hooks/useWorkbox.js +53 -0
- package/dist/editor/client/hooks/useWorkbox.js.map +1 -0
- package/dist/editor/client/ui/EditorChrome.d.ts +12 -0
- package/dist/editor/client/ui/EditorChrome.js +23 -0
- package/dist/editor/client/ui/EditorChrome.js.map +1 -0
- package/dist/editor/client/ui/FullscreenControls.d.ts +7 -0
- package/dist/editor/client/ui/FullscreenControls.js +21 -0
- package/dist/editor/client/ui/FullscreenControls.js.map +1 -0
- package/dist/editor/control-center/WebSocketMessages.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldActionIndicator.d.ts +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/package.json +1 -1
- package/src/editor/Editor.tsx +14 -5
- package/src/editor/client/{EditorClient.tsx → EditorShell.tsx} +78 -789
- package/src/editor/client/editContext.ts +3 -3
- package/src/editor/client/hooks/useEditorUrlSync.ts +83 -0
- package/src/editor/client/hooks/useEditorWebSocket.ts +101 -0
- package/src/editor/client/hooks/useGlobalEditorEvents.ts +18 -0
- package/src/editor/client/hooks/useMediaQuery.ts +21 -0
- package/src/editor/client/hooks/useMediaSelector.ts +48 -0
- package/src/editor/client/hooks/useQuota.ts +81 -0
- package/src/editor/client/hooks/useSocketMessageHandler.ts +285 -0
- package/src/editor/client/hooks/useWorkbox.ts +67 -0
- package/src/editor/client/ui/EditorChrome.tsx +76 -0
- package/src/editor/client/ui/FullscreenControls.tsx +58 -0
- package/src/editor/control-center/WebSocketMessages.tsx +1 -2
- package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +1 -1
- package/src/revision.ts +2 -2
- package/dist/editor/client/EditorClient.js.map +0 -1
|
@@ -8,21 +8,19 @@ import { useRouter, useSearchParams, usePathname } from "next/navigation";
|
|
|
8
8
|
import { findComponent, getComponentById } from "../componentTreeHelper";
|
|
9
9
|
import { getOperationsContext } from "./operations";
|
|
10
10
|
import { handleErrorResult } from "./helpers";
|
|
11
|
-
import { executeFieldAction as executeFieldServerAction,
|
|
11
|
+
import { executeFieldAction as executeFieldServerAction, getEditHistory, releaseFieldLocks, validateItems, } from "../services/editService";
|
|
12
12
|
import "primeicons/primeicons.css";
|
|
13
13
|
import "primereact/resources/themes/md-light-indigo/theme.css";
|
|
14
14
|
import "react-json-view-lite/dist/index.css";
|
|
15
15
|
import { MediaSelector, } from "../media-selector/MediaSelector";
|
|
16
16
|
import { getComponentCommands } from "../commands/componentCommands";
|
|
17
|
-
import { getLanguagesAndVersions,
|
|
17
|
+
import { getLanguagesAndVersions, } from "../services/contentService";
|
|
18
18
|
import ConfirmationDialog from "../ConfirmationDialog";
|
|
19
|
-
import
|
|
20
|
-
import { getItemDescriptor, useEventListenerExt } from "../utils";
|
|
19
|
+
import { getItemDescriptor } from "../utils";
|
|
21
20
|
import { EditContextMenu } from "../ContextMenu";
|
|
22
21
|
import { FieldEditorPopup } from "../FieldEditorPopup";
|
|
23
22
|
import { EditorFormPopup, } from "../page-viewer/EditorFormPopup";
|
|
24
23
|
import { post } from "../services/serviceHelper";
|
|
25
|
-
import { SidebarView } from "../sidebar/SidebarView";
|
|
26
24
|
import { PageViewerFrame } from "../page-viewer/PageViewerFrame";
|
|
27
25
|
import { useItemsRepository } from "./itemsRepository";
|
|
28
26
|
import { Spinner } from "../ui/Spinner";
|
|
@@ -38,10 +36,15 @@ import uuid from "react-uuid";
|
|
|
38
36
|
import { flushSync } from "react-dom";
|
|
39
37
|
import { getSuggestedEdits } from "../services/suggestedEditsService";
|
|
40
38
|
import { usePageWizard } from "../../page-wizard/usePageWizard";
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
|
|
39
|
+
import { useQuota } from "./hooks/useQuota";
|
|
40
|
+
import { useEditorUrlSync } from "./hooks/useEditorUrlSync";
|
|
41
|
+
import { useMediaQuery } from "./hooks/useMediaQuery";
|
|
42
|
+
import { useGlobalEditorEvents } from "./hooks/useGlobalEditorEvents";
|
|
43
|
+
import { FullscreenControls } from "./ui/FullscreenControls";
|
|
44
|
+
import { EditorChrome } from "./ui/EditorChrome";
|
|
45
|
+
import { useWorkbox } from "./hooks/useWorkbox";
|
|
46
|
+
import { useMediaSelector } from "./hooks/useMediaSelector";
|
|
47
|
+
export function EditorShell({ configuration, className, item: loadItemDescriptor, sessionId, userInfo, userPreferences, editorSettings, setUserPreferences, children, }) {
|
|
45
48
|
const router = useRouter();
|
|
46
49
|
const pathname = usePathname();
|
|
47
50
|
const searchParams = useSearchParams();
|
|
@@ -50,10 +53,7 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
50
53
|
const [refreshCompletedFlag, setRefreshCompletedFlag] = useState(false);
|
|
51
54
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
52
55
|
const [dragObject, setDragObject] = useState();
|
|
53
|
-
|
|
54
|
-
const [mediaSelectorVisible, setMediaSelectorVisible] = useState(false);
|
|
55
|
-
const [mediaSelectorMode, setMediaSelectorMode] = useState("images");
|
|
56
|
-
const [selectedMediaIdPath, setSelectedMediaIdPath] = useState("");
|
|
56
|
+
// media selection handled by useMediaSelector hook
|
|
57
57
|
const [scrollIntoView, setScrollIntoView] = useState();
|
|
58
58
|
const confirmationDialogRef = useRef(null);
|
|
59
59
|
const contextMenuRef = useRef(null);
|
|
@@ -167,7 +167,7 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
167
167
|
const [centerPanelView, setCenterPanelView] = useState();
|
|
168
168
|
const [timings, setTimings] = useState({});
|
|
169
169
|
const [revision, setRevision] = useState();
|
|
170
|
-
|
|
170
|
+
// moved into useWorkbox
|
|
171
171
|
const [isTourActive, setIsTourActive] = useState(false);
|
|
172
172
|
const [mode, setMode] = useState("edit");
|
|
173
173
|
const [statusMessage, setStatusMessage] = useState("");
|
|
@@ -177,7 +177,9 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
177
177
|
const [showAgentsPanel, setShowAgentsPanel] = useState(userPreferences.showAgentsPanel ?? false);
|
|
178
178
|
const [activeEditorTab, setActiveEditorTab] = useState(null);
|
|
179
179
|
const [showLayoutComponents, setShowLayoutComponents] = useState(userPreferences.showLayoutComponents ?? false);
|
|
180
|
-
const
|
|
180
|
+
const { quotaInfo, setQuotaInfo, isQuotaExceeded, getQuotaWarningMessage } = useQuota({
|
|
181
|
+
showError: ({ summary, details }) => showErrorToast({ summary, details }),
|
|
182
|
+
});
|
|
181
183
|
const pageWizard = usePageWizard();
|
|
182
184
|
const [webSocketMessages, setWebSocketMessages] = useState([]);
|
|
183
185
|
const [favorites, setFavorites] = useState([]);
|
|
@@ -213,7 +215,6 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
213
215
|
configuration,
|
|
214
216
|
});
|
|
215
217
|
const socketMessageListeners = useRef(new Set());
|
|
216
|
-
const socketInstanceRef = useRef(null);
|
|
217
218
|
const addSocketMessageListener = useCallback((callback) => {
|
|
218
219
|
socketMessageListeners.current.add(callback);
|
|
219
220
|
return () => socketMessageListeners.current.delete(callback);
|
|
@@ -283,241 +284,7 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
283
284
|
setShowLayoutComponents(newValue);
|
|
284
285
|
setUserPreferences({ showLayoutComponents: newValue });
|
|
285
286
|
}, [showLayoutComponents, setShowLayoutComponents, setUserPreferences]);
|
|
286
|
-
|
|
287
|
-
if (!event.data.startsWith("{"))
|
|
288
|
-
return;
|
|
289
|
-
const message = JSON.parse(event.data);
|
|
290
|
-
// Track all WebSocket messages for debugging/monitoring
|
|
291
|
-
try {
|
|
292
|
-
const webSocketMessage = {
|
|
293
|
-
id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
|
|
294
|
-
timestamp: new Date().toISOString(),
|
|
295
|
-
type: message.type || "unknown",
|
|
296
|
-
payload: message.payload,
|
|
297
|
-
rawMessage: JSON.stringify(message, null, 2),
|
|
298
|
-
};
|
|
299
|
-
setWebSocketMessages((prev) => {
|
|
300
|
-
const updated = [webSocketMessage, ...prev];
|
|
301
|
-
// Keep only the latest 1000 messages
|
|
302
|
-
return updated.slice(0, 1000);
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
catch (error) {
|
|
306
|
-
console.error("Error tracking WebSocket message:", error);
|
|
307
|
-
}
|
|
308
|
-
if (message.type === "active-sessions") {
|
|
309
|
-
setActiveSessions((prev) => {
|
|
310
|
-
// Ensure payload is an array and contains valid session objects
|
|
311
|
-
if (!Array.isArray(message.payload)) {
|
|
312
|
-
console.warn("❌ Active sessions payload is not an array:", message.payload);
|
|
313
|
-
globalThis.__alpacaActiveSessions = prev;
|
|
314
|
-
return prev; // keep previous instead of clearing to avoid losing user during HMR blips
|
|
315
|
-
}
|
|
316
|
-
// Filter out any invalid session objects
|
|
317
|
-
const validSessions = message.payload.filter((session) => session &&
|
|
318
|
-
typeof session === "object" &&
|
|
319
|
-
session.sessionId &&
|
|
320
|
-
session.user);
|
|
321
|
-
if (validSessions.length !== message.payload.length) {
|
|
322
|
-
console.warn("❌ Some sessions were filtered out due to invalid data:", {
|
|
323
|
-
original: message.payload.length,
|
|
324
|
-
valid: validSessions.length,
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
// If server reports empty list, keep previous to avoid transient blanking during HMR
|
|
328
|
-
if (validSessions.length === 0 && prev.length > 0) {
|
|
329
|
-
console.warn("⚠️ Received empty active sessions; preserving previous list to avoid losing state");
|
|
330
|
-
globalThis.__alpacaActiveSessions = prev;
|
|
331
|
-
return prev;
|
|
332
|
-
}
|
|
333
|
-
globalThis.__alpacaActiveSessions = validSessions;
|
|
334
|
-
return validSessions;
|
|
335
|
-
});
|
|
336
|
-
// Detect if the current session is missing from the list and self-heal
|
|
337
|
-
try {
|
|
338
|
-
const payload = Array.isArray(message.payload)
|
|
339
|
-
? message.payload
|
|
340
|
-
: [];
|
|
341
|
-
const hasMySession = payload.some((s) => s && s.sessionId === sessionId);
|
|
342
|
-
if (!hasMySession) {
|
|
343
|
-
console.warn("⚠️ Current session missing from active sessions. Re-sending client-info to recover...");
|
|
344
|
-
setTimeout(() => {
|
|
345
|
-
sendClientInfo();
|
|
346
|
-
}, 300);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
catch (e) {
|
|
350
|
-
console.warn("Failed to verify active sessions for current session!", e);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
if (message.type === "item-deleted") {
|
|
354
|
-
itemsRepository.onItemsDeleted([
|
|
355
|
-
{ item: message.payload.item, parentId: message.payload.parentId },
|
|
356
|
-
]);
|
|
357
|
-
if (message.payload.item.id === currentItemDescriptor?.id) {
|
|
358
|
-
console.log("Load", message.payload.parentId);
|
|
359
|
-
loadItem({
|
|
360
|
-
id: message.payload.parentId,
|
|
361
|
-
language: currentItemDescriptor?.language ?? "en",
|
|
362
|
-
version: 0,
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
if (message.type === "item-changed") {
|
|
367
|
-
await itemsRepository.refreshItems([message.payload.item]);
|
|
368
|
-
if (message.payload.item.id === currentItemDescriptor?.id)
|
|
369
|
-
loadItemVersions();
|
|
370
|
-
}
|
|
371
|
-
if (message.type === "item-version-added") {
|
|
372
|
-
if (currentItemDescriptorRef.current) {
|
|
373
|
-
if (currentItemDescriptorRef.current.id === message.payload.item.id)
|
|
374
|
-
await loadItemVersions();
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
if (message.type === "comment-updated") {
|
|
378
|
-
setComments((x) => {
|
|
379
|
-
const newComments = [...x];
|
|
380
|
-
const index = newComments.findIndex((c) => c.id === message.payload.comment.id);
|
|
381
|
-
if (index !== -1)
|
|
382
|
-
newComments[index] = message.payload.comment;
|
|
383
|
-
else
|
|
384
|
-
newComments.push(message.payload.comment);
|
|
385
|
-
return newComments;
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
if (message.type === "comment-deleted") {
|
|
389
|
-
setComments((x) => {
|
|
390
|
-
return x.filter((c) => c.id !== message.payload.commentId);
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
if (message.type === "suggested-edit-updated") {
|
|
394
|
-
setSuggestedEdits((x) => {
|
|
395
|
-
const index = x.findIndex((s) => s.id === message.payload.suggestedEdit.id);
|
|
396
|
-
if (index !== -1)
|
|
397
|
-
x[index] = message.payload.suggestedEdit;
|
|
398
|
-
else
|
|
399
|
-
x.push(message.payload.suggestedEdit);
|
|
400
|
-
return x;
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
if (message.type === "suggested-edit-deleted") {
|
|
404
|
-
setSuggestedEdits((x) => {
|
|
405
|
-
return x.filter((s) => s.id !== message.payload.id);
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
if (message.type === "executing-field-action") {
|
|
409
|
-
setActiveFieldActions((x) => {
|
|
410
|
-
const payload = message.payload;
|
|
411
|
-
const fieldId = payload.fieldId;
|
|
412
|
-
const item = payload.item;
|
|
413
|
-
const status = payload.status;
|
|
414
|
-
const msg = payload.message;
|
|
415
|
-
const label = payload.label;
|
|
416
|
-
// Map backend status to FieldAction state
|
|
417
|
-
let state;
|
|
418
|
-
switch (status?.toLowerCase()) {
|
|
419
|
-
case "completed":
|
|
420
|
-
case "success":
|
|
421
|
-
state = "success";
|
|
422
|
-
break;
|
|
423
|
-
case "failed":
|
|
424
|
-
case "error":
|
|
425
|
-
state = "error";
|
|
426
|
-
break;
|
|
427
|
-
default:
|
|
428
|
-
state = "running";
|
|
429
|
-
break;
|
|
430
|
-
}
|
|
431
|
-
// Check if action already exists
|
|
432
|
-
const existingActionIndex = x.findIndex((action) => action.field.fieldId === fieldId &&
|
|
433
|
-
action.field.item.id === item.id &&
|
|
434
|
-
action.field.item.language === item.language &&
|
|
435
|
-
action.field.item.version === item.version);
|
|
436
|
-
if (existingActionIndex !== -1) {
|
|
437
|
-
// Update existing action
|
|
438
|
-
const newActions = [...x];
|
|
439
|
-
newActions[existingActionIndex].state = state;
|
|
440
|
-
newActions[existingActionIndex].message = msg;
|
|
441
|
-
return newActions;
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
// Insert new action
|
|
445
|
-
const fieldDescriptor = {
|
|
446
|
-
fieldId: fieldId,
|
|
447
|
-
item: item,
|
|
448
|
-
};
|
|
449
|
-
const newAction = {
|
|
450
|
-
field: fieldDescriptor,
|
|
451
|
-
state,
|
|
452
|
-
message: msg,
|
|
453
|
-
label: label,
|
|
454
|
-
};
|
|
455
|
-
// console.log(newAction);
|
|
456
|
-
return [...x, newAction];
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
if (message.type === "update-quota") {
|
|
461
|
-
setQuotaInfo(message.payload);
|
|
462
|
-
}
|
|
463
|
-
if (message.type === "agent-started") {
|
|
464
|
-
// Agent started message will be handled by individual components that subscribe
|
|
465
|
-
// The payload should contain { agentId: string, agentName: string }
|
|
466
|
-
}
|
|
467
|
-
if (message.type === "load-item") {
|
|
468
|
-
const itemDescriptor = message.payload;
|
|
469
|
-
if (itemDescriptor) {
|
|
470
|
-
loadItem(itemDescriptor, { skipViewChange: true });
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
if (message.type === "edit-operation") {
|
|
474
|
-
const op = message.payload;
|
|
475
|
-
if (op.type === "edit-field") {
|
|
476
|
-
const editFieldOperation = op;
|
|
477
|
-
const field = await itemsRepository.getField({
|
|
478
|
-
item: {
|
|
479
|
-
...editFieldOperation.mainItem,
|
|
480
|
-
id: editFieldOperation.itemId,
|
|
481
|
-
},
|
|
482
|
-
fieldId: editFieldOperation.fieldId,
|
|
483
|
-
});
|
|
484
|
-
if (!field ||
|
|
485
|
-
(field.type !== "single-line text" &&
|
|
486
|
-
field.type !== "multi-line text" &&
|
|
487
|
-
field.type !== "rich text")) {
|
|
488
|
-
itemsRepository.refreshItems([
|
|
489
|
-
{
|
|
490
|
-
...editFieldOperation.mainItem,
|
|
491
|
-
id: editFieldOperation.itemId,
|
|
492
|
-
},
|
|
493
|
-
]);
|
|
494
|
-
requestRefresh("immediate");
|
|
495
|
-
}
|
|
496
|
-
//TODO: field value changes that require rerender
|
|
497
|
-
else
|
|
498
|
-
itemsRepository.updateFieldValue({
|
|
499
|
-
fieldId: editFieldOperation.fieldId,
|
|
500
|
-
item: {
|
|
501
|
-
...editFieldOperation.mainItem,
|
|
502
|
-
id: editFieldOperation.itemId,
|
|
503
|
-
},
|
|
504
|
-
}, editFieldOperation.user ?? { name: "unknown", ai: false }, false, editFieldOperation.undone
|
|
505
|
-
? editFieldOperation.oldValue
|
|
506
|
-
: editFieldOperation.value);
|
|
507
|
-
}
|
|
508
|
-
else {
|
|
509
|
-
requestRefresh("immediate");
|
|
510
|
-
}
|
|
511
|
-
if (op.mainItem &&
|
|
512
|
-
op.mainItem.id === currentItemRef.current?.descriptor.id &&
|
|
513
|
-
op.mainItem.language ===
|
|
514
|
-
currentItemRef.current?.descriptor.language &&
|
|
515
|
-
op.mainItem.version === currentItemRef.current?.descriptor.version) {
|
|
516
|
-
loadHistory(op.mainItem);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
socketMessageListeners.current.forEach((listener) => listener(message));
|
|
520
|
-
}, [currentItemDescriptorRef, addRecentEdit]);
|
|
287
|
+
// messageHandler is defined after loadItem/loadHistory declarations to avoid temporal dead zones
|
|
521
288
|
const user = useMemo(() => activeSessions.find((x) => x.sessionId === sessionId)?.user ||
|
|
522
289
|
userInfo.user, [activeSessions, sessionId, userInfo.user]);
|
|
523
290
|
// Self-heal if our session disappears (e.g., after HMR)
|
|
@@ -549,11 +316,9 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
549
316
|
else {
|
|
550
317
|
// Force a reconnect to refresh presence after several failed nudges
|
|
551
318
|
try {
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
catch {
|
|
555
|
-
// Ignore errors while attempting to recover presence
|
|
319
|
+
globalThis.editorSocket?.close(4000, "recover-presence");
|
|
556
320
|
}
|
|
321
|
+
catch { }
|
|
557
322
|
}
|
|
558
323
|
}, delay);
|
|
559
324
|
return () => {
|
|
@@ -647,25 +412,8 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
647
412
|
clearInterval(urlCheckInterval);
|
|
648
413
|
};
|
|
649
414
|
}, []);
|
|
650
|
-
// Custom hook for responsive design
|
|
651
|
-
const useMediaQuery = (query) => {
|
|
652
|
-
const [matches, setMatches] = useState(false);
|
|
653
|
-
useEffect(() => {
|
|
654
|
-
const media = window.matchMedia(query);
|
|
655
|
-
const updateMatch = () => setMatches(media.matches);
|
|
656
|
-
// Set initial value
|
|
657
|
-
updateMatch();
|
|
658
|
-
// Listen for changes
|
|
659
|
-
media.addEventListener("change", updateMatch);
|
|
660
|
-
// Cleanup
|
|
661
|
-
return () => {
|
|
662
|
-
media.removeEventListener("change", updateMatch);
|
|
663
|
-
};
|
|
664
|
-
}, [query]);
|
|
665
|
-
return matches;
|
|
666
|
-
};
|
|
667
|
-
// Detect mobile screens (max-width: 768px)
|
|
668
415
|
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
416
|
+
const media = useMediaSelector();
|
|
669
417
|
useEffect(() => {
|
|
670
418
|
// Suppress auto-start tour when `noTour` query parameter is present
|
|
671
419
|
if (searchParams.get("noTour") !== null) {
|
|
@@ -679,117 +427,8 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
679
427
|
localStorage.setItem(key, "true");
|
|
680
428
|
}
|
|
681
429
|
}, [user]);
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
let reconnectAttempts = 0;
|
|
685
|
-
const connectWebSocket = () => {
|
|
686
|
-
let socket = globalThis.editorSocket;
|
|
687
|
-
const needsNewSocket = !socket ||
|
|
688
|
-
socket.readyState === WebSocket.CLOSING ||
|
|
689
|
-
socket.readyState === WebSocket.CLOSED;
|
|
690
|
-
if (needsNewSocket) {
|
|
691
|
-
socket = connectSocket(sessionId);
|
|
692
|
-
// Connection opened
|
|
693
|
-
socket.addEventListener("open", () => {
|
|
694
|
-
console.log("Connected!");
|
|
695
|
-
reconnectAttempts = 0; // Reset attempts on successful connection
|
|
696
|
-
sendClientInfo();
|
|
697
|
-
requestQuota();
|
|
698
|
-
//TODO: Load clients
|
|
699
|
-
});
|
|
700
|
-
// Handle connection close
|
|
701
|
-
socket.addEventListener("close", (event) => {
|
|
702
|
-
// WebSocket connection closed
|
|
703
|
-
// Only attempt to reconnect if it wasn't a clean close
|
|
704
|
-
if (event.code !== 1000) {
|
|
705
|
-
// Start with 1 second, increase exponentially up to 30 seconds
|
|
706
|
-
const delay = Math.min(1000 * Math.pow(2, Math.min(reconnectAttempts, 5)), 30000);
|
|
707
|
-
// Attempting to reconnect with backoff
|
|
708
|
-
reconnectTimeout = setTimeout(() => {
|
|
709
|
-
reconnectAttempts++;
|
|
710
|
-
connectWebSocket();
|
|
711
|
-
}, delay);
|
|
712
|
-
}
|
|
713
|
-
});
|
|
714
|
-
// Handle connection errors
|
|
715
|
-
socket.addEventListener("error", (error) => {
|
|
716
|
-
console.error("WebSocket error:", error);
|
|
717
|
-
});
|
|
718
|
-
globalThis.editorSocket = socket;
|
|
719
|
-
}
|
|
720
|
-
// Always ensure this instance is listening to messages
|
|
721
|
-
if (socket) {
|
|
722
|
-
socket.addEventListener("message", messageHandler);
|
|
723
|
-
socketInstanceRef.current = socket;
|
|
724
|
-
// If we attached to an already-open socket, resend client info to refresh presence
|
|
725
|
-
if (socket.readyState === WebSocket.OPEN) {
|
|
726
|
-
sendClientInfo();
|
|
727
|
-
requestQuota();
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
};
|
|
731
|
-
connectWebSocket();
|
|
732
|
-
// Cleanup function
|
|
733
|
-
return () => {
|
|
734
|
-
if (reconnectTimeout) {
|
|
735
|
-
clearTimeout(reconnectTimeout);
|
|
736
|
-
}
|
|
737
|
-
if (socketInstanceRef.current) {
|
|
738
|
-
socketInstanceRef.current.removeEventListener("message", messageHandler);
|
|
739
|
-
socketInstanceRef.current = null;
|
|
740
|
-
}
|
|
741
|
-
};
|
|
742
|
-
}, []);
|
|
743
|
-
// Handle initial state setup from URL (only on initial load)
|
|
744
|
-
useEffect(() => {
|
|
745
|
-
if (!isInitialLoad)
|
|
746
|
-
return;
|
|
747
|
-
const itemid = searchParams.get("itemid");
|
|
748
|
-
const wizardId = searchParams.get("wizardid");
|
|
749
|
-
const urlView = searchParams.get("view");
|
|
750
|
-
// Handle wizard ID from URL first (before view)
|
|
751
|
-
if (wizardId) {
|
|
752
|
-
setCurrentWizardId(wizardId);
|
|
753
|
-
}
|
|
754
|
-
if (urlView) {
|
|
755
|
-
setViewName(urlView);
|
|
756
|
-
}
|
|
757
|
-
else if (!itemid) {
|
|
758
|
-
setViewName("splash-screen");
|
|
759
|
-
}
|
|
760
|
-
}, [searchParams, isInitialLoad]);
|
|
761
|
-
// Handle initial compare mode from URL (only on initial load)
|
|
762
|
-
useEffect(() => {
|
|
763
|
-
if (!isInitialLoad)
|
|
764
|
-
return;
|
|
765
|
-
if (searchParams.has("compare")) {
|
|
766
|
-
const compareValue = searchParams.get("compare") === "true";
|
|
767
|
-
setCompareMode(compareValue);
|
|
768
|
-
}
|
|
769
|
-
}, [searchParams, pathname, isInitialLoad]);
|
|
770
|
-
// Handle initial item loading from URL and loadItemDescriptor
|
|
771
|
-
useEffect(() => {
|
|
772
|
-
// Use URL params only on initial load, otherwise respect loadItemDescriptor prop
|
|
773
|
-
const itemid = isInitialLoad ? searchParams.get("itemid") : null;
|
|
774
|
-
const itemId = cleanId(loadItemDescriptor?.id ?? itemid ?? undefined);
|
|
775
|
-
const language = loadItemDescriptor?.language ??
|
|
776
|
-
(isInitialLoad
|
|
777
|
-
? (searchParams.get("lang") ?? searchParams.get("language"))
|
|
778
|
-
: null);
|
|
779
|
-
const version = loadItemDescriptor?.version ??
|
|
780
|
-
(isInitialLoad && searchParams.has("version")
|
|
781
|
-
? parseInt(searchParams.get("version"))
|
|
782
|
-
: 0);
|
|
783
|
-
if (!itemId || !language)
|
|
784
|
-
return;
|
|
785
|
-
// Skip loading if the item is already loaded with the same parameters
|
|
786
|
-
if (currentItemDescriptor?.id === itemId &&
|
|
787
|
-
currentItemDescriptor?.language === language &&
|
|
788
|
-
(!version || currentItemDescriptor?.version === version)) {
|
|
789
|
-
return;
|
|
790
|
-
}
|
|
791
|
-
loadItem({ id: itemId, language, version });
|
|
792
|
-
}, [loadItemDescriptor, searchParams, currentItemDescriptor, isInitialLoad]);
|
|
430
|
+
// WebSocket initialization is performed after messageHandler is defined
|
|
431
|
+
// Defer URL sync until loadItem is defined below
|
|
793
432
|
// Mark end of initial load phase
|
|
794
433
|
useEffect(() => {
|
|
795
434
|
if (isInitialLoad) {
|
|
@@ -889,28 +528,7 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
889
528
|
if (!isLoading)
|
|
890
529
|
setInserting(undefined);
|
|
891
530
|
}, [page, viewName, revision]);
|
|
892
|
-
|
|
893
|
-
// Handle fullscreen on initial load only
|
|
894
|
-
if (isInitialLoad &&
|
|
895
|
-
(searchParams.get("fullscreen") || configuration.forceFullscreen)) {
|
|
896
|
-
pageViewContext.setFullscreen(true);
|
|
897
|
-
}
|
|
898
|
-
const handleMessage = (event) => {
|
|
899
|
-
if (event.data.action === "refresh") {
|
|
900
|
-
requestRefresh("immediate");
|
|
901
|
-
}
|
|
902
|
-
};
|
|
903
|
-
window.addEventListener("message", handleMessage);
|
|
904
|
-
return () => {
|
|
905
|
-
window.removeEventListener("message", handleMessage);
|
|
906
|
-
};
|
|
907
|
-
}, [
|
|
908
|
-
isInitialLoad,
|
|
909
|
-
searchParams,
|
|
910
|
-
pathname,
|
|
911
|
-
pageViewContext,
|
|
912
|
-
configuration.forceFullscreen,
|
|
913
|
-
]);
|
|
531
|
+
// moved below to ensure requestRefresh is declared first
|
|
914
532
|
const loadHistory = useDebouncedCallback(async (item) => {
|
|
915
533
|
const result = await getEditHistory(item);
|
|
916
534
|
if (handleErrorResult(result, ui, state))
|
|
@@ -923,6 +541,7 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
923
541
|
return;
|
|
924
542
|
setEditHistory(result.data || []);
|
|
925
543
|
}, []);
|
|
544
|
+
// defined below after loadItem/loadItemVersions/requestRefresh
|
|
926
545
|
const requestRefresh = useCallback((mode) => {
|
|
927
546
|
const refreshTimer = globalThis.editorRefreshTimer;
|
|
928
547
|
const doRefresh = () => {
|
|
@@ -1118,6 +737,20 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
1118
737
|
loadHistory,
|
|
1119
738
|
addToBrowseHistory,
|
|
1120
739
|
]);
|
|
740
|
+
useEditorUrlSync({
|
|
741
|
+
isInitialLoad,
|
|
742
|
+
setIsInitialLoad,
|
|
743
|
+
searchParams,
|
|
744
|
+
pathname,
|
|
745
|
+
setCurrentWizardId,
|
|
746
|
+
setViewName,
|
|
747
|
+
setCompareMode,
|
|
748
|
+
loadItem,
|
|
749
|
+
currentItemDescriptor,
|
|
750
|
+
loadItemDescriptor,
|
|
751
|
+
routerPush: (url) => router.push(url, { scroll: false }),
|
|
752
|
+
});
|
|
753
|
+
// initialized below after messageHandler
|
|
1121
754
|
useEffect(() => {
|
|
1122
755
|
if (pageViewContext.fullscreen &&
|
|
1123
756
|
!searchParams.get("fullscreen") &&
|
|
@@ -1222,78 +855,12 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
1222
855
|
confirmationDialogRef,
|
|
1223
856
|
onOperationExecuted,
|
|
1224
857
|
}), [showErrorToast, confirmationDialogRef, onOperationExecuted]);
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
setMediaResolver(() => resolve);
|
|
1232
|
-
});
|
|
1233
|
-
}, []);
|
|
1234
|
-
const onMediaSelect = useCallback((mediaUrl) => {
|
|
1235
|
-
mediaResolver?.(mediaUrl);
|
|
1236
|
-
setMediaSelectorVisible(false);
|
|
1237
|
-
setMediaResolver(undefined);
|
|
1238
|
-
}, [mediaResolver]);
|
|
1239
|
-
useEffect(() => {
|
|
1240
|
-
if (!workboxItems || workboxItems.length === 0)
|
|
1241
|
-
return;
|
|
1242
|
-
const itemsToValidate = workboxItems.map((x) => x.item);
|
|
1243
|
-
validate(itemsToValidate);
|
|
1244
|
-
}, [workboxItems]);
|
|
1245
|
-
async function loadWorkbox(items) {
|
|
1246
|
-
if (!items.length) {
|
|
1247
|
-
setWorkboxItems([]);
|
|
1248
|
-
return;
|
|
1249
|
-
}
|
|
1250
|
-
const workbox = await getWorkbox(items.map((x) => getItemDescriptor(x)));
|
|
1251
|
-
const workboxItems = workbox.data || [];
|
|
1252
|
-
const sortedWorkboxItems = workboxItems.sort((a, b) => {
|
|
1253
|
-
if (a.isPublished === b.isPublished)
|
|
1254
|
-
return ((b.workflowCommands?.length || 0) - (a.workflowCommands?.length || 0));
|
|
1255
|
-
return !a.isPublished || !a.isPublishable ? -1 : 1;
|
|
1256
|
-
});
|
|
1257
|
-
setWorkboxItems(sortedWorkboxItems);
|
|
1258
|
-
}
|
|
1259
|
-
const loadWorkboxDebounced = useDebouncedCallback((items) => loadWorkbox(items), 5000);
|
|
1260
|
-
useEffect(() => {
|
|
1261
|
-
const items = [];
|
|
1262
|
-
if (editContext.contentEditorItem) {
|
|
1263
|
-
items.push(editContext.contentEditorItem.descriptor);
|
|
1264
|
-
}
|
|
1265
|
-
if (editContext.page) {
|
|
1266
|
-
collectAllItems(editContext.page.rootComponent, items);
|
|
1267
|
-
}
|
|
1268
|
-
loadWorkboxDebounced(items.filter((x) => x));
|
|
1269
|
-
}, [page, contentEditorItem]);
|
|
1270
|
-
function collectAllItems(component, items) {
|
|
1271
|
-
component.placeholders.forEach((x) => {
|
|
1272
|
-
x.components.forEach((y) => {
|
|
1273
|
-
if (y.isShared && y.datasourceItem) {
|
|
1274
|
-
items.push(y.datasourceItem);
|
|
1275
|
-
}
|
|
1276
|
-
//TODO: Add picture fields
|
|
1277
|
-
// y.datasourceItem?.fields.forEach((z) => {
|
|
1278
|
-
// if (z.type === "picture") {
|
|
1279
|
-
// const picture = z.value as PictureValue;
|
|
1280
|
-
// if (picture.variants) {
|
|
1281
|
-
// picture.variants.forEach((v) => {
|
|
1282
|
-
// if (v.mediaId) {
|
|
1283
|
-
// items.push({
|
|
1284
|
-
// id: v.mediaId,
|
|
1285
|
-
// language: y.datasourceItem!.descriptor.language,
|
|
1286
|
-
// version: 0,
|
|
1287
|
-
// });
|
|
1288
|
-
// }
|
|
1289
|
-
// });
|
|
1290
|
-
// }
|
|
1291
|
-
// }
|
|
1292
|
-
// });
|
|
1293
|
-
collectAllItems(y, items);
|
|
1294
|
-
});
|
|
1295
|
-
});
|
|
1296
|
-
}
|
|
858
|
+
// moved to useMediaSelector
|
|
859
|
+
const { workboxItems } = useWorkbox({
|
|
860
|
+
page,
|
|
861
|
+
contentEditorItem,
|
|
862
|
+
validate,
|
|
863
|
+
});
|
|
1297
864
|
const switchView = (viewName, options) => {
|
|
1298
865
|
async function switchView() {
|
|
1299
866
|
if (currentView?.beforeClose && !options?.skipConfirmation) {
|
|
@@ -1375,12 +942,10 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
1375
942
|
showErrorToast,
|
|
1376
943
|
executeCommand,
|
|
1377
944
|
});
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
contextMenuRef.current?.close({});
|
|
1383
|
-
}, window, true);
|
|
945
|
+
useGlobalEditorEvents({
|
|
946
|
+
onKeyDown: handleKeyDown,
|
|
947
|
+
onWindowClick: () => contextMenuRef.current?.close({}),
|
|
948
|
+
});
|
|
1384
949
|
useEffect(() => {
|
|
1385
950
|
if (mode === "suggestions") {
|
|
1386
951
|
setShowSuggestedEdits(true);
|
|
@@ -1406,57 +971,7 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
1406
971
|
router.push(newUrl, { scroll: false });
|
|
1407
972
|
}, [router]);
|
|
1408
973
|
// Quota checking functions
|
|
1409
|
-
|
|
1410
|
-
if (!quotaInfo)
|
|
1411
|
-
return false;
|
|
1412
|
-
const { usage, limits } = quotaInfo;
|
|
1413
|
-
// Check absolute limits
|
|
1414
|
-
if (limits.totalTokens > 0 && usage.totalTokens >= limits.totalTokens)
|
|
1415
|
-
return true;
|
|
1416
|
-
if (limits.totalImages > 0 && usage.totalImages >= limits.totalImages)
|
|
1417
|
-
return true;
|
|
1418
|
-
// For now, we're only checking absolute limits as daily/monthly would require server-side logic
|
|
1419
|
-
// You can extend this to check daily/monthly limits if the server provides that information
|
|
1420
|
-
return false;
|
|
1421
|
-
}, [quotaInfo]);
|
|
1422
|
-
const getQuotaWarningMessage = useCallback(() => {
|
|
1423
|
-
if (!quotaInfo)
|
|
1424
|
-
return null;
|
|
1425
|
-
const { usage, limits } = quotaInfo;
|
|
1426
|
-
const warnings = [];
|
|
1427
|
-
// Check tokens
|
|
1428
|
-
if (limits.totalTokens > 0) {
|
|
1429
|
-
const tokenPercentage = (usage.totalTokens / limits.totalTokens) * 100;
|
|
1430
|
-
if (tokenPercentage >= 100) {
|
|
1431
|
-
warnings.push(`Token limit exceeded (${usage.totalTokens}/${limits.totalTokens})`);
|
|
1432
|
-
}
|
|
1433
|
-
else if (tokenPercentage >= 90) {
|
|
1434
|
-
warnings.push(`Token usage high: ${Math.round(tokenPercentage)}% (${usage.totalTokens}/${limits.totalTokens})`);
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
// Check images
|
|
1438
|
-
if (limits.totalImages > 0) {
|
|
1439
|
-
const imagePercentage = (usage.totalImages / limits.totalImages) * 100;
|
|
1440
|
-
if (imagePercentage >= 100) {
|
|
1441
|
-
warnings.push(`Image limit exceeded (${usage.totalImages}/${limits.totalImages})`);
|
|
1442
|
-
}
|
|
1443
|
-
else if (imagePercentage >= 90) {
|
|
1444
|
-
warnings.push(`Image usage high: ${Math.round(imagePercentage)}% (${usage.totalImages}/${limits.totalImages})`);
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
return warnings.length > 0 ? warnings.join(", ") : null;
|
|
1448
|
-
}, [quotaInfo]);
|
|
1449
|
-
// Show warning when quota is exceeded
|
|
1450
|
-
useEffect(() => {
|
|
1451
|
-
const warningMessage = getQuotaWarningMessage();
|
|
1452
|
-
if (warningMessage) {
|
|
1453
|
-
const isExceeded = isQuotaExceeded();
|
|
1454
|
-
showErrorToast({
|
|
1455
|
-
summary: isExceeded ? "AI Quota Exceeded" : "AI Quota Warning",
|
|
1456
|
-
details: warningMessage,
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
}, [quotaInfo, getQuotaWarningMessage, isQuotaExceeded, showErrorToast]);
|
|
974
|
+
// moved into hook
|
|
1460
975
|
// Calculate visible views separately to avoid circular dependency
|
|
1461
976
|
const visibleViews = useMemo(() => {
|
|
1462
977
|
const allViews = configuration.editor.views
|
|
@@ -1519,7 +1034,7 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
1519
1034
|
const oldUrl = `${pathname}?${searchParams.toString()}`;
|
|
1520
1035
|
router.push(newUrl, { scroll: false });
|
|
1521
1036
|
},
|
|
1522
|
-
selectMedia,
|
|
1037
|
+
selectMedia: media.selectMedia,
|
|
1523
1038
|
showToast: (message) => {
|
|
1524
1039
|
toast(message);
|
|
1525
1040
|
},
|
|
@@ -1843,7 +1358,6 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
1843
1358
|
searchParams,
|
|
1844
1359
|
pathname,
|
|
1845
1360
|
router,
|
|
1846
|
-
selectMedia,
|
|
1847
1361
|
scrollIntoView,
|
|
1848
1362
|
focusedField,
|
|
1849
1363
|
renderedFields,
|
|
@@ -2170,42 +1684,11 @@ export function EditorClient({ configuration, className, item: loadItemDescripto
|
|
|
2170
1684
|
}, [currentItemDescriptor]);
|
|
2171
1685
|
if (!currentView)
|
|
2172
1686
|
return null;
|
|
2173
|
-
const editorUi = pageViewContext.fullscreen ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "fixed inset-0 flex", children: [_jsx(PageViewerFrame, { compareView: compareMode, pageViewContext: pageViewContext }),
|
|
2174
|
-
const currentDevice = pageViewContext.device;
|
|
2175
|
-
if (currentDevice === "desktop") {
|
|
2176
|
-
// Switch to mobile (first mobile device from configuration)
|
|
2177
|
-
const firstMobileDevice = configuration.devices[0];
|
|
2178
|
-
if (firstMobileDevice) {
|
|
2179
|
-
pageViewContext.setDevice(firstMobileDevice.name);
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
else {
|
|
2183
|
-
// Switch to desktop
|
|
2184
|
-
pageViewContext.setDevice("desktop");
|
|
2185
|
-
}
|
|
2186
|
-
}, className: "flex h-10 w-10 cursor-pointer items-center justify-center rounded-full bg-black/20 text-white backdrop-blur-sm transition-colors duration-200 hover:bg-black/40", "aria-label": pageViewContext.device === "desktop"
|
|
2187
|
-
? "Switch to mobile view"
|
|
2188
|
-
: "Switch to desktop view", title: pageViewContext.device === "desktop"
|
|
2189
|
-
? "Switch to mobile view"
|
|
2190
|
-
: "Switch to desktop view", "data-testid": "fullscreen-device-toggle", children: pageViewContext.device === "desktop" ? (_jsx(Smartphone, { className: "h-5 w-5" })) : (_jsx(Monitor, { className: "h-5 w-5" })) }), !configuration.forceFullscreen && (_jsx("button", { onClick: () => pageViewContext.setFullscreen(false), className: "flex h-10 w-10 cursor-pointer items-center justify-center rounded-full bg-black/20 text-white backdrop-blur-sm transition-colors duration-200 hover:bg-black/40", "aria-label": "Exit fullscreen", title: "Return to normal view", "data-testid": "fullscreen-exit-button", children: _jsx(Shrink, { className: "h-5 w-5" }) }))] })] }), _jsx(EditorFormPopup, { ref: editorFormPopupRef, pageViewContext: pageViewContext }), showFullscreenHint && !configuration.forceFullscreen && (_jsx("div", { className: "fixed inset-0", onMouseMoveCapture: () => {
|
|
1687
|
+
const editorUi = pageViewContext.fullscreen ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "fixed inset-0 flex", children: [_jsx(PageViewerFrame, { compareView: compareMode, pageViewContext: pageViewContext }), _jsx(FullscreenControls, { device: pageViewContext.device, setDevice: (d) => pageViewContext.setDevice(d), canExit: !configuration.forceFullscreen, onExit: () => pageViewContext.setFullscreen(false), firstMobileDeviceName: configuration.devices[0]?.name })] }), _jsx(EditorFormPopup, { ref: editorFormPopupRef, pageViewContext: pageViewContext }), showFullscreenHint && !configuration.forceFullscreen && (_jsx("div", { className: "fixed inset-0", onMouseMoveCapture: () => {
|
|
2191
1688
|
setTimeout(() => {
|
|
2192
1689
|
setShowFullscreenHint(false);
|
|
2193
1690
|
}, 600);
|
|
2194
|
-
}, "data-testid": "fullscreen-hint-overlay", children: _jsx("div", { className: "fixed top-3 left-1/2 -translate-x-1/2 transform rounded-sm bg-gray-200 p-12", children: "Press Ctrl + F11 to exit fullscreen mode" }) }))] })) : (_jsxs(_Fragment, { children: [_jsx(
|
|
2195
|
-
|
|
2196
|
-
showComponentNavigator && (_jsx(SidebarView, { sidebar: currentView.rightSidebar, editContext: editContext, active: true, detached: true, onClose: () => handleSetShowComponentNavigator(false) })), rightSidebarTitle: currentView.rightSidebar?.title, farRightSidebar: showAgentsPanel &&
|
|
2197
|
-
!["splash-screen", "open-page", "new-page"].includes(viewName) && (_jsx(SidebarView, { sidebar: {
|
|
2198
|
-
title: "Agents",
|
|
2199
|
-
panels: [
|
|
2200
|
-
{
|
|
2201
|
-
name: "agents",
|
|
2202
|
-
title: "Agents",
|
|
2203
|
-
content: _jsx(Agents, {}),
|
|
2204
|
-
initialSize: 70,
|
|
2205
|
-
noOverflow: true,
|
|
2206
|
-
},
|
|
2207
|
-
],
|
|
2208
|
-
}, editContext: editContext, active: true, detached: true, paddingRight: true, onClose: () => handleSetShowAgentsPanel(false) })), farRightSidebarTitle: "AGENTS" }), isTourActive && _jsx(Tour, { tourStopCallback: () => setIsTourActive(false) })] }));
|
|
2209
|
-
return (_jsx("div", { className: `editor h-full`, children: _jsx(OperationsContextProvider, { value: operationsContext.context, children: _jsx(FieldsEditContextProvider, { value: fieldsEditContext, children: _jsxs(EditContextProvider, { value: editContext, children: [editContext.isRefreshing && (_jsx("div", { className: "pointer-events-none fixed right-0 bottom-0 flex h-24 w-24 items-center justify-center text-gray-600 opacity-50 select-none", children: _jsx(Spinner, {}) })), children || editorUi, dialog, _jsx(Toaster, { position: "top-center" }), " ", _jsx(ConfirmationDialog, { ref: confirmationDialogRef }), _jsx(EditContextMenu, { ref: contextMenuRef }), mediaSelectorVisible && (_jsx(MediaSelector, { language: editContext.currentItemDescriptor.language, visible: mediaSelectorVisible, onHide: () => setMediaSelectorVisible(false), onMediaSelected: onMediaSelect, selectedIdPath: selectedMediaIdPath, mode: mediaSelectorMode })), _jsx(FieldEditorPopup, { ref: fieldEditorPopupRef }), _jsx(EditorFormPopup, { ref: editorFormPopupRef, pageViewContext: pageViewContext })] }) }) }) }));
|
|
1691
|
+
}, "data-testid": "fullscreen-hint-overlay", children: _jsx("div", { className: "fixed top-3 left-1/2 -translate-x-1/2 transform rounded-sm bg-gray-200 p-12", children: "Press Ctrl + F11 to exit fullscreen mode" }) }))] })) : (_jsxs(_Fragment, { children: [_jsx(EditorChrome, { className: className, currentView: currentView, centerPanelView: centerPanelView, editContext: editContext, showComponentNavigator: showComponentNavigator, handleSetShowComponentNavigator: handleSetShowComponentNavigator, showAgentsPanel: showAgentsPanel, handleSetShowAgentsPanel: handleSetShowAgentsPanel, viewName: viewName }), isTourActive && _jsx(Tour, { tourStopCallback: () => setIsTourActive(false) })] }));
|
|
1692
|
+
return (_jsx("div", { className: `editor h-full`, children: _jsx(OperationsContextProvider, { value: operationsContext.context, children: _jsx(FieldsEditContextProvider, { value: fieldsEditContext, children: _jsxs(EditContextProvider, { value: editContext, children: [editContext.isRefreshing && (_jsx("div", { className: "pointer-events-none fixed right-0 bottom-0 flex h-24 w-24 items-center justify-center text-gray-600 opacity-50 select-none", children: _jsx(Spinner, {}) })), children || editorUi, dialog, _jsx(Toaster, { position: "top-center" }), " ", _jsx(ConfirmationDialog, { ref: confirmationDialogRef }), _jsx(EditContextMenu, { ref: contextMenuRef }), media.mediaSelectorVisible && (_jsx(MediaSelector, { language: editContext.currentItemDescriptor.language, visible: media.mediaSelectorVisible, onHide: () => media.setMediaSelectorVisible(false), onMediaSelected: media.onMediaSelect, selectedIdPath: media.selectedMediaIdPath, mode: media.mediaSelectorMode })), _jsx(FieldEditorPopup, { ref: fieldEditorPopupRef }), _jsx(EditorFormPopup, { ref: editorFormPopupRef, pageViewContext: pageViewContext })] }) }) }) }));
|
|
2210
1693
|
}
|
|
2211
|
-
//# sourceMappingURL=
|
|
1694
|
+
//# sourceMappingURL=EditorShell.js.map
|