@alpaca-editor/core 1.0.3939 → 1.0.3941
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/components/ActionButton.d.ts +1 -0
- package/dist/components/ActionButton.js +2 -2
- package/dist/components/ActionButton.js.map +1 -1
- package/dist/editor/FieldActionsOverlay.d.ts +1 -0
- package/dist/editor/FieldActionsOverlay.js +2 -11
- package/dist/editor/FieldActionsOverlay.js.map +1 -1
- package/dist/editor/PictureCropper.js +65 -23
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/PictureEditor.js +16 -2
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/Titlebar.js +1 -1
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/GhostWriter.js +21 -2
- package/dist/editor/ai/GhostWriter.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +7 -3
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/media-selector/MediaFolderBrowser.d.ts +5 -0
- package/dist/editor/media-selector/MediaFolderBrowser.js +77 -0
- package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -0
- package/dist/editor/media-selector/MediaSelector.js +1 -1
- package/dist/editor/media-selector/MediaSelector.js.map +1 -1
- package/dist/editor/media-selector/Thumbnails.js +2 -2
- package/dist/editor/media-selector/index.d.ts +8 -0
- package/dist/editor/media-selector/index.js +9 -0
- package/dist/editor/media-selector/index.js.map +1 -0
- package/dist/editor/menubar/BrowseHistory.js +1 -1
- package/dist/editor/menubar/PageSelector.js +39 -15
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +37 -11
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/reviews/Comment.js +1 -1
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/services/aiService.js +0 -1
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.js +3 -4
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.d.ts +1 -0
- package/dist/editor/ui/SimpleTabs.js +3 -3
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/editor/ui/Splitter.js +61 -6
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/views/MediaFolderEditView.d.ts +4 -0
- package/dist/editor/views/MediaFolderEditView.js +40 -0
- package/dist/editor/views/MediaFolderEditView.js.map +1 -0
- package/dist/editor/views/SingleEditView.js +9 -1
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +61 -5
- package/package.json +8 -2
- package/.prettierrc +0 -3
- package/build.css +0 -3
- package/components.json +0 -21
- package/eslint.config.mjs +0 -4
- package/images/bg-shape-black.webp +0 -0
- package/images/wizard-bg.png +0 -0
- package/images/wizard-tour.png +0 -0
- package/images/wizard.png +0 -0
- package/src/client-components/api.ts +0 -6
- package/src/client-components/index.ts +0 -19
- package/src/components/ActionButton.tsx +0 -41
- package/src/components/Error.tsx +0 -57
- package/src/components/ui/CardConnector.tsx +0 -56
- package/src/components/ui/button.tsx +0 -62
- package/src/components/ui/card.tsx +0 -372
- package/src/components/ui/context-menu.tsx +0 -250
- package/src/config/config.tsx +0 -917
- package/src/config/types.ts +0 -286
- package/src/editor/ComponentInfo.tsx +0 -90
- package/src/editor/ConfirmationDialog.tsx +0 -103
- package/src/editor/ContentTree.tsx +0 -730
- package/src/editor/ContextMenu.tsx +0 -230
- package/src/editor/Editor.tsx +0 -90
- package/src/editor/EditorWarning.tsx +0 -34
- package/src/editor/EditorWarnings.tsx +0 -33
- package/src/editor/FieldActionsOverlay.tsx +0 -307
- package/src/editor/FieldEditorPopup.tsx +0 -65
- package/src/editor/FieldHistory.tsx +0 -75
- package/src/editor/FieldList.tsx +0 -190
- package/src/editor/FieldListField.tsx +0 -391
- package/src/editor/FieldListFieldWithFallbacks.tsx +0 -217
- package/src/editor/FloatingToolbar.tsx +0 -163
- package/src/editor/ImageEditor.tsx +0 -128
- package/src/editor/ItemInfo.tsx +0 -90
- package/src/editor/LinkEditorDialog.tsx +0 -196
- package/src/editor/MainLayout.tsx +0 -95
- package/src/editor/MobileLayout.tsx +0 -68
- package/src/editor/NewEditorClient.tsx +0 -11
- package/src/editor/PictureCropper.tsx +0 -503
- package/src/editor/PictureEditor.tsx +0 -277
- package/src/editor/PictureEditorDialog.tsx +0 -381
- package/src/editor/PublishDialog.ignore +0 -74
- package/src/editor/ScrollingContentTree.tsx +0 -67
- package/src/editor/Terminal.tsx +0 -227
- package/src/editor/Titlebar.tsx +0 -104
- package/src/editor/ai/AiPopup.tsx +0 -59
- package/src/editor/ai/AiResponseMessage.tsx +0 -106
- package/src/editor/ai/AiTerminal.tsx +0 -503
- package/src/editor/ai/AiToolCall.tsx +0 -61
- package/src/editor/ai/EditorAiTerminal.tsx +0 -20
- package/src/editor/ai/GhostWriter.tsx +0 -432
- package/src/editor/ai/aiPageModel.ts +0 -108
- package/src/editor/ai/editorAiContext.ts +0 -18
- package/src/editor/client/AboutDialog.tsx +0 -44
- package/src/editor/client/EditorClient.tsx +0 -2241
- package/src/editor/client/GenericDialog.tsx +0 -50
- package/src/editor/client/editContext.ts +0 -416
- package/src/editor/client/helpers.ts +0 -44
- package/src/editor/client/itemsRepository.ts +0 -574
- package/src/editor/client/operations.ts +0 -768
- package/src/editor/client/pageModelBuilder.ts +0 -219
- package/src/editor/commands/commands.ts +0 -22
- package/src/editor/commands/componentCommands.tsx +0 -424
- package/src/editor/commands/createVersionCommand.ts +0 -33
- package/src/editor/commands/deleteVersionCommand.ts +0 -71
- package/src/editor/commands/itemCommands.tsx +0 -351
- package/src/editor/commands/localizeItem/LocalizeItemDialog.tsx +0 -201
- package/src/editor/commands/localizeItem/LocalizeItemUtils.ts +0 -27
- package/src/editor/commands/undo.ts +0 -39
- package/src/editor/component-designer/ComponentDesigner.tsx +0 -70
- package/src/editor/component-designer/ComponentDesignerAiTerminal.tsx +0 -11
- package/src/editor/component-designer/ComponentDesignerMenu.tsx +0 -91
- package/src/editor/component-designer/ComponentEditor.tsx +0 -97
- package/src/editor/component-designer/ComponentRenderingCodeEditor.tsx +0 -31
- package/src/editor/component-designer/ComponentRenderingEditor.tsx +0 -104
- package/src/editor/component-designer/ComponentsDropdown.tsx +0 -39
- package/src/editor/component-designer/PlaceholdersEditor.tsx +0 -179
- package/src/editor/component-designer/RenderingsDropdown.tsx +0 -36
- package/src/editor/component-designer/TemplateEditor.tsx +0 -236
- package/src/editor/component-designer/aiContext.ts +0 -23
- package/src/editor/componentTreeHelper.tsx +0 -116
- package/src/editor/context-menu/CopyMoveMenu.tsx +0 -103
- package/src/editor/context-menu/InsertMenu.tsx +0 -347
- package/src/editor/control-center/About.tsx +0 -342
- package/src/editor/control-center/ControlCenterMenu.tsx +0 -76
- package/src/editor/control-center/IndexOverview.tsx +0 -50
- package/src/editor/control-center/IndexSettings.tsx +0 -266
- package/src/editor/control-center/Info.tsx +0 -104
- package/src/editor/control-center/QuotaInfo.tsx +0 -301
- package/src/editor/control-center/Status.tsx +0 -113
- package/src/editor/control-center/WebSocketMessages.tsx +0 -155
- package/src/editor/editor-warnings/ItemLocked.tsx +0 -63
- package/src/editor/editor-warnings/NoLanguageWriteAccess.tsx +0 -22
- package/src/editor/editor-warnings/NoWorkflowWriteAccess.tsx +0 -23
- package/src/editor/editor-warnings/NoWriteAccess.tsx +0 -16
- package/src/editor/editor-warnings/ValidationErrors.tsx +0 -54
- package/src/editor/field-types/AttachmentEditor.tsx +0 -9
- package/src/editor/field-types/CheckboxEditor.tsx +0 -47
- package/src/editor/field-types/DropLinkEditor.tsx +0 -80
- package/src/editor/field-types/DropListEditor.tsx +0 -84
- package/src/editor/field-types/ImageFieldEditor.tsx +0 -65
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +0 -117
- package/src/editor/field-types/LinkFieldEditor.tsx +0 -85
- package/src/editor/field-types/MultiLineText.tsx +0 -82
- package/src/editor/field-types/PictureFieldEditor.tsx +0 -121
- package/src/editor/field-types/RawEditor.tsx +0 -53
- package/src/editor/field-types/ReactQuill.tsx +0 -580
- package/src/editor/field-types/RichTextEditor.tsx +0 -22
- package/src/editor/field-types/RichTextEditorComponent.tsx +0 -127
- package/src/editor/field-types/SingleLineText.tsx +0 -174
- package/src/editor/field-types/TreeListEditor.tsx +0 -261
- package/src/editor/fieldTypes.ts +0 -140
- package/src/editor/media-selector/AiImageSearch.tsx +0 -185
- package/src/editor/media-selector/AiImageSearchPrompt.tsx +0 -94
- package/src/editor/media-selector/MediaSelector.tsx +0 -42
- package/src/editor/media-selector/Preview.tsx +0 -14
- package/src/editor/media-selector/Thumbnails.tsx +0 -48
- package/src/editor/media-selector/TreeSelector.tsx +0 -292
- package/src/editor/media-selector/UploadZone.tsx +0 -137
- package/src/editor/menubar/ActionsMenu.tsx +0 -94
- package/src/editor/menubar/ActiveUsers.tsx +0 -17
- package/src/editor/menubar/ApproveAndPublish.tsx +0 -18
- package/src/editor/menubar/BrowseHistory.tsx +0 -28
- package/src/editor/menubar/ItemLanguageVersion.tsx +0 -76
- package/src/editor/menubar/LanguageSelector.tsx +0 -226
- package/src/editor/menubar/Menu.tsx +0 -83
- package/src/editor/menubar/NavButtons.tsx +0 -74
- package/src/editor/menubar/PageSelector.tsx +0 -166
- package/src/editor/menubar/PageViewerControls.tsx +0 -120
- package/src/editor/menubar/PreviewSecondaryControls.tsx +0 -18
- package/src/editor/menubar/SecondaryControls.tsx +0 -45
- package/src/editor/menubar/Separator.tsx +0 -12
- package/src/editor/menubar/SiteInfo.tsx +0 -53
- package/src/editor/menubar/User.tsx +0 -27
- package/src/editor/menubar/VersionSelector.tsx +0 -142
- package/src/editor/page-editor-chrome/CommentHighlighting.tsx +0 -307
- package/src/editor/page-editor-chrome/CommentHighlightings.tsx +0 -35
- package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +0 -59
- package/src/editor/page-editor-chrome/FieldActionIndicators.tsx +0 -23
- package/src/editor/page-editor-chrome/FieldEditedIndicator.tsx +0 -64
- package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +0 -35
- package/src/editor/page-editor-chrome/FrameMenu.tsx +0 -338
- package/src/editor/page-editor-chrome/FrameMenus.tsx +0 -48
- package/src/editor/page-editor-chrome/InlineEditor.tsx +0 -765
- package/src/editor/page-editor-chrome/LockedFieldIndicator.tsx +0 -61
- package/src/editor/page-editor-chrome/NoLayout.tsx +0 -36
- package/src/editor/page-editor-chrome/PageEditorChrome.tsx +0 -122
- package/src/editor/page-editor-chrome/PictureEditorOverlay.tsx +0 -161
- package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +0 -169
- package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +0 -315
- package/src/editor/page-editor-chrome/SuggestionHighlighting.tsx +0 -300
- package/src/editor/page-editor-chrome/SuggestionHighlightings.tsx +0 -40
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +0 -791
- package/src/editor/page-viewer/DeviceToolbar.tsx +0 -70
- package/src/editor/page-viewer/EditorForm.tsx +0 -258
- package/src/editor/page-viewer/MiniMap.tsx +0 -362
- package/src/editor/page-viewer/PageViewer.tsx +0 -169
- package/src/editor/page-viewer/PageViewerFrame.tsx +0 -1022
- package/src/editor/page-viewer/pageModelSkeletonBuilder.ts +0 -412
- package/src/editor/page-viewer/pageViewContext.ts +0 -186
- package/src/editor/pageModel.ts +0 -220
- package/src/editor/picture-shared.tsx +0 -53
- package/src/editor/reviews/Comment.tsx +0 -307
- package/src/editor/reviews/Comments.tsx +0 -125
- package/src/editor/reviews/DiffView.tsx +0 -109
- package/src/editor/reviews/PreviewInfo.tsx +0 -35
- package/src/editor/reviews/Reviews.tsx +0 -280
- package/src/editor/reviews/SuggestedEdit.tsx +0 -316
- package/src/editor/reviews/reviewCommands.tsx +0 -47
- package/src/editor/reviews/useReviews.tsx +0 -70
- package/src/editor/services/aiService.ts +0 -174
- package/src/editor/services/componentDesignerService.ts +0 -151
- package/src/editor/services/contentService.ts +0 -180
- package/src/editor/services/editService.ts +0 -488
- package/src/editor/services/indexService.ts +0 -24
- package/src/editor/services/reviewsService.ts +0 -53
- package/src/editor/services/serviceHelper.ts +0 -95
- package/src/editor/services/suggestedEditsService.ts +0 -39
- package/src/editor/services/systemService.ts +0 -5
- package/src/editor/services/translationService.ts +0 -21
- package/src/editor/services-server/api.ts +0 -150
- package/src/editor/services-server/graphQL.ts +0 -106
- package/src/editor/sidebar/ComponentPalette.tsx +0 -161
- package/src/editor/sidebar/ComponentTree.tsx +0 -548
- package/src/editor/sidebar/ComponentTree2.tsxx +0 -490
- package/src/editor/sidebar/Debug.tsx +0 -111
- package/src/editor/sidebar/DictionaryEditor.tsx +0 -261
- package/src/editor/sidebar/EditHistory.tsx +0 -134
- package/src/editor/sidebar/GraphQL.tsx +0 -164
- package/src/editor/sidebar/Insert.tsx +0 -35
- package/src/editor/sidebar/MainContentTree.tsx +0 -102
- package/src/editor/sidebar/Performance.tsx +0 -53
- package/src/editor/sidebar/Sessions.tsx +0 -35
- package/src/editor/sidebar/Sidebar.tsx +0 -20
- package/src/editor/sidebar/SidebarView.tsx +0 -152
- package/src/editor/sidebar/Translations.tsx +0 -295
- package/src/editor/sidebar/Validation.tsx +0 -102
- package/src/editor/sidebar/ViewSelector.tsx +0 -60
- package/src/editor/sidebar/Workbox.tsx +0 -209
- package/src/editor/ui/CenteredMessage.tsx +0 -7
- package/src/editor/ui/CopyMoveTargetSelectorDialog.tsx +0 -81
- package/src/editor/ui/CopyToClipboardButton.tsx +0 -24
- package/src/editor/ui/DialogButtons.tsx +0 -11
- package/src/editor/ui/Icons.tsx +0 -709
- package/src/editor/ui/ItemList.tsx +0 -76
- package/src/editor/ui/ItemNameDialogNew.tsx +0 -118
- package/src/editor/ui/ItemSearch.tsx +0 -153
- package/src/editor/ui/PerfectTree.tsx +0 -571
- package/src/editor/ui/Section.tsx +0 -35
- package/src/editor/ui/SimpleIconButton.tsx +0 -54
- package/src/editor/ui/SimpleMenu.tsx +0 -40
- package/src/editor/ui/SimpleTable.tsx +0 -60
- package/src/editor/ui/SimpleTabs.tsx +0 -55
- package/src/editor/ui/SimpleToolbar.tsx +0 -7
- package/src/editor/ui/Spinner.tsx +0 -9
- package/src/editor/ui/Splitter.tsx +0 -314
- package/src/editor/ui/StackedPanels.tsx +0 -134
- package/src/editor/ui/Toolbar.tsx +0 -7
- package/src/editor/utils/id-helper.ts +0 -3
- package/src/editor/utils/insertOptions.ts +0 -69
- package/src/editor/utils/itemutils.ts +0 -29
- package/src/editor/utils/useMemoDebug.ts +0 -28
- package/src/editor/utils.ts +0 -486
- package/src/editor/views/CompareView.tsx +0 -245
- package/src/editor/views/EditView.tsx +0 -27
- package/src/editor/views/ItemEditor.tsx +0 -58
- package/src/editor/views/SingleEditView.tsx +0 -46
- package/src/fonts/Geist-Black.woff2 +0 -0
- package/src/fonts/Geist-Bold.woff2 +0 -0
- package/src/fonts/Geist-ExtraBold.woff2 +0 -0
- package/src/fonts/Geist-ExtraLight.woff2 +0 -0
- package/src/fonts/Geist-Light.woff2 +0 -0
- package/src/fonts/Geist-Medium.woff2 +0 -0
- package/src/fonts/Geist-Regular.woff2 +0 -0
- package/src/fonts/Geist-SemiBold.woff2 +0 -0
- package/src/fonts/Geist-Thin.woff2 +0 -0
- package/src/fonts/Geist[wght].woff2 +0 -0
- package/src/fonts/index.ts +0 -10
- package/src/index.ts +0 -23
- package/src/lib/safelist.tsx +0 -16
- package/src/lib/utils.ts +0 -6
- package/src/page-wizard/PageWizard.tsx +0 -139
- package/src/page-wizard/WizardBox.tsx +0 -4
- package/src/page-wizard/WizardBoxConnector.tsx +0 -56
- package/src/page-wizard/WizardSteps.tsx +0 -458
- package/src/page-wizard/service.ts +0 -35
- package/src/page-wizard/startPageWizardCommand.ts +0 -26
- package/src/page-wizard/steps/BuildPageStep.tsx +0 -259
- package/src/page-wizard/steps/CollectStep.tsx +0 -296
- package/src/page-wizard/steps/ComponentTypesSelector.tsx +0 -454
- package/src/page-wizard/steps/Components.tsx +0 -193
- package/src/page-wizard/steps/ContentStep.tsx +0 -890
- package/src/page-wizard/steps/EditButton.tsx +0 -34
- package/src/page-wizard/steps/FieldEditor.tsx +0 -102
- package/src/page-wizard/steps/Generate.tsx +0 -60
- package/src/page-wizard/steps/ImagesStep.tsx +0 -382
- package/src/page-wizard/steps/LayoutStep.tsx +0 -227
- package/src/page-wizard/steps/MetaDataStep.tsx +0 -173
- package/src/page-wizard/steps/SelectStep.tsx +0 -281
- package/src/page-wizard/steps/schema.ts +0 -180
- package/src/page-wizard/steps/usePageCreator.ts +0 -325
- package/src/page-wizard/usePageWizard.ts +0 -79
- package/src/revision.ts +0 -2
- package/src/splash-screen/NewPage.tsx +0 -294
- package/src/splash-screen/OpenPage.tsx +0 -113
- package/src/splash-screen/RecentPages.tsx +0 -123
- package/src/splash-screen/SectionHeadline.tsx +0 -21
- package/src/splash-screen/SplashScreen.tsx +0 -195
- package/src/tour/Tour.tsx +0 -566
- package/src/tour/default-tour.tsx +0 -301
- package/src/tour/preview-tour.tsx +0 -128
- package/src/types.ts +0 -335
- package/styles.css +0 -765
- package/tsconfig.build.json +0 -31
- package/tsconfig.json +0 -14
|
@@ -1,765 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from "react";
|
|
2
|
-
import {
|
|
3
|
-
useEditContext,
|
|
4
|
-
useModifiedFieldsContext,
|
|
5
|
-
useEditContextRef,
|
|
6
|
-
} from "../client/editContext";
|
|
7
|
-
import { useThrottledCallback } from "use-debounce";
|
|
8
|
-
import { getFieldDescriptorFromElement, hasFieldLock } from "../utils";
|
|
9
|
-
import { PageViewContext } from "../page-viewer/pageViewContext";
|
|
10
|
-
import { applyPatch, diffWords } from "diff";
|
|
11
|
-
import { createPatch } from "diff";
|
|
12
|
-
// import { useDebouncedCallback } from "use-debounce";
|
|
13
|
-
// import { executePrompt } from "../services/aiService";
|
|
14
|
-
import { useInlineAiCompletion } from "./useInlineAICompletion";
|
|
15
|
-
|
|
16
|
-
export function InlineEditor({
|
|
17
|
-
pageViewContext,
|
|
18
|
-
compareView,
|
|
19
|
-
}: {
|
|
20
|
-
pageViewContext: PageViewContext;
|
|
21
|
-
compareView: boolean;
|
|
22
|
-
}) {
|
|
23
|
-
const context = useEditContext();
|
|
24
|
-
const contextRef = useEditContextRef();
|
|
25
|
-
const modifiedFieldsContext = useModifiedFieldsContext();
|
|
26
|
-
const [cursorSpanId] = useState(
|
|
27
|
-
() => `cursor-indicator-${Math.random().toString(36).substring(2, 9)}`,
|
|
28
|
-
);
|
|
29
|
-
const isUpdatingRef = useRef(false);
|
|
30
|
-
if (!context) return;
|
|
31
|
-
|
|
32
|
-
const mutationObserverRef = useRef<MutationObserver | null>(null);
|
|
33
|
-
const selectionChangeListenerRef = useRef<() => void | null>(null);
|
|
34
|
-
|
|
35
|
-
const refreshCompletion = useInlineAiCompletion({
|
|
36
|
-
pageViewContext,
|
|
37
|
-
cursorSpanId,
|
|
38
|
-
isUpdatingRef,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const isTextFieldType = (fieldType: string) => {
|
|
42
|
-
const fieldTypeLower = fieldType.toLowerCase();
|
|
43
|
-
return (
|
|
44
|
-
fieldTypeLower === "single-line text" ||
|
|
45
|
-
fieldTypeLower === "multi-line text" ||
|
|
46
|
-
fieldTypeLower === "rich text"
|
|
47
|
-
);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const debouncedSetFieldvalue = useThrottledCallback(
|
|
51
|
-
(value, fieldId, fieldName, itemId, language, version) => {
|
|
52
|
-
// Don't include our cursor indicator in the saved value
|
|
53
|
-
const iframeDocument =
|
|
54
|
-
pageViewContext.editorIframeRef.current?.contentWindow?.document;
|
|
55
|
-
const element = context.inlineEditingFieldElement;
|
|
56
|
-
const isRichText = element?.getAttribute("data-is-richtext") === "true";
|
|
57
|
-
|
|
58
|
-
// Clone element to avoid modifying the original
|
|
59
|
-
if (element && iframeDocument) {
|
|
60
|
-
const clone = element.cloneNode(true) as HTMLElement;
|
|
61
|
-
const cursorElem = clone.querySelector(`#${cursorSpanId}`);
|
|
62
|
-
if (cursorElem) {
|
|
63
|
-
cursorElem.parentNode?.removeChild(cursorElem);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Remove any zero-width spaces we might have added
|
|
67
|
-
const textNodes = Array.from(clone.childNodes).filter(
|
|
68
|
-
(node) =>
|
|
69
|
-
node.nodeType === Node.TEXT_NODE && node.textContent === "\u200B",
|
|
70
|
-
);
|
|
71
|
-
textNodes.forEach((node) => node.parentNode?.removeChild(node));
|
|
72
|
-
|
|
73
|
-
value = isRichText ? clone.innerHTML : clone.innerText;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const modifiedFieldValue = modifiedFieldsContext?.modifiedFields.find(
|
|
77
|
-
(x) =>
|
|
78
|
-
x.fieldId === fieldId &&
|
|
79
|
-
x.item.id === itemId &&
|
|
80
|
-
x.item.language === language &&
|
|
81
|
-
x.item.version === version,
|
|
82
|
-
)?.value;
|
|
83
|
-
|
|
84
|
-
if (modifiedFieldValue === value) return;
|
|
85
|
-
|
|
86
|
-
contextRef.current?.operations.editField({
|
|
87
|
-
field: {
|
|
88
|
-
fieldId,
|
|
89
|
-
fieldName: fieldName ?? undefined,
|
|
90
|
-
item: {
|
|
91
|
-
id: itemId,
|
|
92
|
-
language,
|
|
93
|
-
version,
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
refresh: "none",
|
|
97
|
-
value: value,
|
|
98
|
-
});
|
|
99
|
-
},
|
|
100
|
-
context.configuration.debounceFieldEditsInterval,
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
useEffect(() => {
|
|
104
|
-
if (!context || compareView || context.mode === "preview") return;
|
|
105
|
-
const element = context.inlineEditingFieldElement;
|
|
106
|
-
|
|
107
|
-
const editableElements =
|
|
108
|
-
pageViewContext.editorIframeRef.current?.contentWindow?.document.querySelectorAll(
|
|
109
|
-
'[contenteditable="true"]',
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
editableElements?.forEach((element) => {
|
|
113
|
-
if (element !== context.inlineEditingFieldElement)
|
|
114
|
-
element.setAttribute("contenteditable", "false");
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
if (element) {
|
|
118
|
-
// Clean up any existing cursor indicators
|
|
119
|
-
const iframeDocument =
|
|
120
|
-
pageViewContext.editorIframeRef.current?.contentWindow?.document;
|
|
121
|
-
const existingCursors = iframeDocument?.querySelectorAll(
|
|
122
|
-
'[data-cursor-indicator="true"]',
|
|
123
|
-
);
|
|
124
|
-
existingCursors?.forEach((el) => el.parentNode?.removeChild(el));
|
|
125
|
-
|
|
126
|
-
const fieldId = element.getAttribute("data-fieldid");
|
|
127
|
-
const fieldName = element.getAttribute("data-fieldname");
|
|
128
|
-
const itemId = element.getAttribute("data-itemid");
|
|
129
|
-
const language = element.getAttribute("data-language");
|
|
130
|
-
const versionText = element.getAttribute("data-version");
|
|
131
|
-
const version = versionText ? parseInt(versionText) : undefined;
|
|
132
|
-
const isRichText = element.getAttribute("data-is-richtext") === "true";
|
|
133
|
-
|
|
134
|
-
if (!fieldId || !itemId || !language || !version) return;
|
|
135
|
-
|
|
136
|
-
const fieldDescriptor = getFieldDescriptorFromElement(element);
|
|
137
|
-
|
|
138
|
-
const hasFielLock =
|
|
139
|
-
context.mode === "suggestions" ||
|
|
140
|
-
hasFieldLock(fieldDescriptor, context);
|
|
141
|
-
|
|
142
|
-
if (!hasFielLock) return;
|
|
143
|
-
|
|
144
|
-
element.setAttribute("contenteditable", "true");
|
|
145
|
-
|
|
146
|
-
const iframeWindow =
|
|
147
|
-
pageViewContext.editorIframeRef.current?.contentWindow;
|
|
148
|
-
|
|
149
|
-
// Setup a more robust mutation observer
|
|
150
|
-
mutationObserverRef.current = new MutationObserver((mutations) => {
|
|
151
|
-
// If we're already processing an update, don't trigger another one
|
|
152
|
-
|
|
153
|
-
if (isUpdatingRef.current) return;
|
|
154
|
-
|
|
155
|
-
// Check if mutations only affect our cursor span
|
|
156
|
-
const hasRealChanges = mutations.some((mutation) => {
|
|
157
|
-
// Skip our cursor span
|
|
158
|
-
if (
|
|
159
|
-
mutation.target.nodeType === Node.ELEMENT_NODE &&
|
|
160
|
-
(mutation.target as Element).id === cursorSpanId
|
|
161
|
-
) {
|
|
162
|
-
console.log("skipping cursor span");
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Skip mutations where target is our span
|
|
167
|
-
if (
|
|
168
|
-
mutation.target.nodeType === Node.ELEMENT_NODE &&
|
|
169
|
-
(mutation.target as Element).getAttribute &&
|
|
170
|
-
(mutation.target as Element).getAttribute(
|
|
171
|
-
"data-cursor-indicator",
|
|
172
|
-
) === "true"
|
|
173
|
-
) {
|
|
174
|
-
console.log("skipping cursor span 2");
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Skip if all added/removed nodes are just our cursor span
|
|
179
|
-
if (mutation.type === "childList") {
|
|
180
|
-
const nonCursorChanges = Array.from(mutation.addedNodes)
|
|
181
|
-
.concat(Array.from(mutation.removedNodes))
|
|
182
|
-
.filter((node) => {
|
|
183
|
-
return (
|
|
184
|
-
node.nodeType !== Node.ELEMENT_NODE ||
|
|
185
|
-
!(node as Element).id ||
|
|
186
|
-
(node as Element).id !== cursorSpanId
|
|
187
|
-
);
|
|
188
|
-
});
|
|
189
|
-
return nonCursorChanges.length > 0;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return true;
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
if (hasRealChanges) {
|
|
196
|
-
debouncedSetFieldvalue(
|
|
197
|
-
isRichText ? element.innerHTML : element.innerText,
|
|
198
|
-
fieldId,
|
|
199
|
-
fieldName,
|
|
200
|
-
itemId,
|
|
201
|
-
language,
|
|
202
|
-
version,
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Always update cursor position after any mutation,
|
|
207
|
-
// but delay to let the mutation processing complete
|
|
208
|
-
// if (!isUpdatingRef.current) {
|
|
209
|
-
// setTimeout(updateCursorSpan, 0);
|
|
210
|
-
// // updateCompletion();
|
|
211
|
-
// }
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
setTimeout(() => {
|
|
215
|
-
element.focus();
|
|
216
|
-
}, 50);
|
|
217
|
-
|
|
218
|
-
// Initial cursor placement
|
|
219
|
-
// setTimeout(updateCursorSpan, 10);
|
|
220
|
-
|
|
221
|
-
// Add listener for selection changes
|
|
222
|
-
if (iframeWindow) {
|
|
223
|
-
const selectionChangeHandler = () => {
|
|
224
|
-
// Only update if we're not already updating
|
|
225
|
-
// if (!isUpdatingRef.current) {
|
|
226
|
-
// setTimeout(updateCursorSpan, 0);
|
|
227
|
-
// }
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
iframeWindow.document.addEventListener(
|
|
231
|
-
"selectionchange",
|
|
232
|
-
selectionChangeHandler,
|
|
233
|
-
);
|
|
234
|
-
selectionChangeListenerRef.current = () => {
|
|
235
|
-
iframeWindow.document.removeEventListener(
|
|
236
|
-
"selectionchange",
|
|
237
|
-
selectionChangeHandler,
|
|
238
|
-
);
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
mutationObserverRef.current.observe(element, {
|
|
242
|
-
childList: true, // observe direct children changes
|
|
243
|
-
subtree: true, // observe all descendants changes
|
|
244
|
-
characterData: true, // observe text changes
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// Cleanup
|
|
248
|
-
return () => {
|
|
249
|
-
if (mutationObserverRef.current) {
|
|
250
|
-
mutationObserverRef.current.disconnect();
|
|
251
|
-
mutationObserverRef.current = null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (selectionChangeListenerRef.current) {
|
|
255
|
-
selectionChangeListenerRef.current();
|
|
256
|
-
selectionChangeListenerRef.current = null;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// iframeWindow.document.removeEventListener("keydown", keyHandler);
|
|
260
|
-
|
|
261
|
-
// Clean up any cursor indicators on unmount
|
|
262
|
-
const cursorElement = iframeDocument?.getElementById(cursorSpanId);
|
|
263
|
-
if (cursorElement) {
|
|
264
|
-
cursorElement.parentNode?.removeChild(cursorElement);
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
async function updateFocusedFieldContent() {
|
|
271
|
-
const element = context?.inlineEditingFieldElement;
|
|
272
|
-
if (!element) return;
|
|
273
|
-
|
|
274
|
-
const savedPosition = saveCaretPosition(element);
|
|
275
|
-
|
|
276
|
-
const start = savedPosition?.offset || 0;
|
|
277
|
-
let contentWasUpdated = false;
|
|
278
|
-
|
|
279
|
-
const fieldId = element.getAttribute("data-fieldid");
|
|
280
|
-
const itemId = element.getAttribute("data-itemid");
|
|
281
|
-
const language = element.getAttribute("data-language");
|
|
282
|
-
const versionStr = element.getAttribute("data-version");
|
|
283
|
-
if (!fieldId || !itemId || !language || !versionStr) return;
|
|
284
|
-
|
|
285
|
-
const version = parseInt(versionStr, 10);
|
|
286
|
-
const descriptor = { id: itemId, language, version };
|
|
287
|
-
|
|
288
|
-
// Retrieve the current field value from the repository.
|
|
289
|
-
const loadedItem = await context.itemsRepository.getItem(descriptor);
|
|
290
|
-
if (!loadedItem) return;
|
|
291
|
-
// Get the baseline value from the repository.
|
|
292
|
-
const repositoryField = loadedItem.fields.find(
|
|
293
|
-
(f: any) => f.id === fieldId,
|
|
294
|
-
);
|
|
295
|
-
let baseValue = repositoryField ? repositoryField.rawValue || "" : "";
|
|
296
|
-
|
|
297
|
-
// Override with the modified field value if it exists.
|
|
298
|
-
const modField = modifiedFieldsContext?.modifiedFields.find(
|
|
299
|
-
(mod: any) =>
|
|
300
|
-
mod.fieldId === fieldId &&
|
|
301
|
-
mod.item.id === itemId &&
|
|
302
|
-
mod.item.language === language &&
|
|
303
|
-
mod.item.version === version,
|
|
304
|
-
);
|
|
305
|
-
if (modField) {
|
|
306
|
-
baseValue = modField.value || "";
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// If suggestions mode is active, merge all suggestions for this field.
|
|
310
|
-
|
|
311
|
-
if (context.mode === "suggestions") {
|
|
312
|
-
const fieldSuggestions = context.suggestedEdits.filter(
|
|
313
|
-
(s: any) =>
|
|
314
|
-
s.fieldId === fieldId &&
|
|
315
|
-
s.itemId === itemId &&
|
|
316
|
-
s.mainItemLanguage === language &&
|
|
317
|
-
s.mainItemVersion === version,
|
|
318
|
-
);
|
|
319
|
-
// Sort suggestions in chronological order (oldest first).
|
|
320
|
-
fieldSuggestions.sort(
|
|
321
|
-
(a: any, b: any) =>
|
|
322
|
-
new Date(a.created).getTime() - new Date(b.created).getTime(),
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
let mergedValue = baseValue;
|
|
326
|
-
for (const suggestion of fieldSuggestions) {
|
|
327
|
-
const patch = createPatch(
|
|
328
|
-
"field",
|
|
329
|
-
suggestion.oldValue,
|
|
330
|
-
suggestion.newValue,
|
|
331
|
-
);
|
|
332
|
-
const patchedCandidate = applyPatch(mergedValue, patch);
|
|
333
|
-
if (
|
|
334
|
-
patchedCandidate !== false &&
|
|
335
|
-
typeof patchedCandidate === "string"
|
|
336
|
-
) {
|
|
337
|
-
mergedValue = patchedCandidate;
|
|
338
|
-
} else {
|
|
339
|
-
mergedValue = suggestion.newValue;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
// Update focused field element with the merged plain text value.
|
|
343
|
-
|
|
344
|
-
element.innerHTML = mergedValue;
|
|
345
|
-
contentWasUpdated = true;
|
|
346
|
-
} else {
|
|
347
|
-
if (element.innerHTML !== baseValue) {
|
|
348
|
-
// If suggestions mode is off, update with the base value.
|
|
349
|
-
element.innerHTML = baseValue;
|
|
350
|
-
contentWasUpdated = true;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (contentWasUpdated) {
|
|
355
|
-
setTimeout(() => {
|
|
356
|
-
restoreCaretPosition(element, start);
|
|
357
|
-
}, 0);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
setTimeout(() => {
|
|
362
|
-
updateFocusedFieldContent();
|
|
363
|
-
}, 50);
|
|
364
|
-
|
|
365
|
-
return () => {
|
|
366
|
-
if (mutationObserverRef.current) {
|
|
367
|
-
mutationObserverRef.current.disconnect();
|
|
368
|
-
mutationObserverRef.current = null;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (selectionChangeListenerRef.current) {
|
|
372
|
-
selectionChangeListenerRef.current();
|
|
373
|
-
selectionChangeListenerRef.current = null;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Clean up any cursor indicators on unmount
|
|
377
|
-
const iframeDocument =
|
|
378
|
-
pageViewContext.editorIframeRef.current?.contentWindow?.document;
|
|
379
|
-
const cursorElement = iframeDocument?.getElementById(cursorSpanId);
|
|
380
|
-
if (cursorElement) {
|
|
381
|
-
cursorElement.parentNode?.removeChild(cursorElement);
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
}, [context?.inlineEditingFieldElement]);
|
|
385
|
-
|
|
386
|
-
function saveCaretPosition(
|
|
387
|
-
editableElement: HTMLElement,
|
|
388
|
-
): { node: Node; offset: number } | null {
|
|
389
|
-
const selection =
|
|
390
|
-
pageViewContext.editorIframeRef.current?.contentWindow?.getSelection();
|
|
391
|
-
if (
|
|
392
|
-
!selection ||
|
|
393
|
-
selection.rangeCount === 0 ||
|
|
394
|
-
!editableElement.contains(selection.anchorNode)
|
|
395
|
-
) {
|
|
396
|
-
return null;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Get the current range
|
|
400
|
-
const range = selection.getRangeAt(0);
|
|
401
|
-
const startNode = range.startContainer;
|
|
402
|
-
const startOffset = range.startOffset;
|
|
403
|
-
|
|
404
|
-
// Calculate absolute position from the beginning of the element
|
|
405
|
-
let absoluteOffset = 0;
|
|
406
|
-
|
|
407
|
-
// Function to calculate text length before a node
|
|
408
|
-
function getTextLengthBefore(node: Node): number {
|
|
409
|
-
let length = 0;
|
|
410
|
-
|
|
411
|
-
// Process all nodes before this one in current parent
|
|
412
|
-
let currentNode = node.parentNode?.firstChild;
|
|
413
|
-
while (currentNode && currentNode !== node) {
|
|
414
|
-
length += currentNode.textContent?.length || 0;
|
|
415
|
-
currentNode = currentNode.nextSibling;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// If the node's parent isn't the editable element, add lengths from higher levels
|
|
419
|
-
if (
|
|
420
|
-
node.parentNode &&
|
|
421
|
-
node.parentNode !== editableElement &&
|
|
422
|
-
node.parentNode.parentNode
|
|
423
|
-
) {
|
|
424
|
-
length += getTextLengthBefore(node.parentNode);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
return length;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// If we're in a text node directly under the editable element
|
|
431
|
-
if (startNode.nodeType === Node.TEXT_NODE) {
|
|
432
|
-
absoluteOffset = getTextLengthBefore(startNode) + startOffset;
|
|
433
|
-
} else {
|
|
434
|
-
// Handle cases where selection is on an element node
|
|
435
|
-
absoluteOffset = getTextLengthBefore(startNode);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
return { node: startNode, offset: absoluteOffset };
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function restoreCaretPosition(element: HTMLElement, position: number) {
|
|
442
|
-
if (position === undefined || position === null) return;
|
|
443
|
-
|
|
444
|
-
const selection =
|
|
445
|
-
pageViewContext.editorIframeRef.current?.contentWindow?.getSelection();
|
|
446
|
-
if (!selection) return;
|
|
447
|
-
|
|
448
|
-
selection.removeAllRanges();
|
|
449
|
-
|
|
450
|
-
// Find the appropriate text node and offset
|
|
451
|
-
const currentNode = element.firstChild;
|
|
452
|
-
let currentPos = 0;
|
|
453
|
-
|
|
454
|
-
// Helper function to find the right node and position
|
|
455
|
-
function findNodeAtPosition(
|
|
456
|
-
node: Node | null,
|
|
457
|
-
targetPos: number,
|
|
458
|
-
): { node: Node; offset: number } | null {
|
|
459
|
-
while (node) {
|
|
460
|
-
// Text node case
|
|
461
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
462
|
-
const length = node.textContent?.length || 0;
|
|
463
|
-
if (currentPos + length >= targetPos) {
|
|
464
|
-
return { node, offset: targetPos - currentPos };
|
|
465
|
-
}
|
|
466
|
-
currentPos += length;
|
|
467
|
-
}
|
|
468
|
-
// Element node case - check its children
|
|
469
|
-
else if (node.nodeType === Node.ELEMENT_NODE && node.hasChildNodes()) {
|
|
470
|
-
const result = findNodeAtPosition(node.firstChild, targetPos);
|
|
471
|
-
if (result) return result;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
node = node.nextSibling;
|
|
475
|
-
}
|
|
476
|
-
return null;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const result = findNodeAtPosition(currentNode, position);
|
|
480
|
-
|
|
481
|
-
// If we found a valid node, set the range
|
|
482
|
-
if (result) {
|
|
483
|
-
const range = new Range();
|
|
484
|
-
range.setStart(result.node, result.offset);
|
|
485
|
-
range.setEnd(result.node, result.offset);
|
|
486
|
-
selection.addRange(range);
|
|
487
|
-
} else if (element.firstChild) {
|
|
488
|
-
// Fallback to end of content if position is too large
|
|
489
|
-
const lastNode = element.lastChild;
|
|
490
|
-
if (lastNode && lastNode.nodeType === Node.TEXT_NODE) {
|
|
491
|
-
const range = new Range();
|
|
492
|
-
range.setStart(lastNode, lastNode.textContent?.length || 0);
|
|
493
|
-
range.setEnd(lastNode, lastNode.textContent?.length || 0);
|
|
494
|
-
selection.addRange(range);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
useEffect(() => {
|
|
500
|
-
const updateFieldsWithSuggestions = async () => {
|
|
501
|
-
const iframeWindow =
|
|
502
|
-
pageViewContext.editorIframeRef.current?.contentWindow;
|
|
503
|
-
if (!iframeWindow) return;
|
|
504
|
-
const doc = iframeWindow.document;
|
|
505
|
-
|
|
506
|
-
// First, process modified fields directly (from updateFields functionality)
|
|
507
|
-
modifiedFieldsContext?.modifiedFields.forEach((field) => {
|
|
508
|
-
const elements = doc.querySelectorAll(
|
|
509
|
-
`[data-fieldid="${field.fieldId}"][data-itemid="${field.item.id}"][data-language="${field.item.language}"][data-version="${field.item.version}"`,
|
|
510
|
-
);
|
|
511
|
-
|
|
512
|
-
elements?.forEach(async (element) => {
|
|
513
|
-
if (element && element !== context?.inlineEditingFieldElement) {
|
|
514
|
-
const realField = await context.itemsRepository.getField(field);
|
|
515
|
-
const fieldType = realField?.type;
|
|
516
|
-
|
|
517
|
-
if (fieldType && isTextFieldType(fieldType)) {
|
|
518
|
-
element.innerHTML = field?.value ? (field.value as string) : "";
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
if (context.mode !== "suggestions" && !context.showSuggestedEdits) return;
|
|
525
|
-
|
|
526
|
-
// Now proceed with the original updateFieldsWithSuggestions logic
|
|
527
|
-
// Query all field elements by data attributes.
|
|
528
|
-
const allFieldElements = doc.querySelectorAll(
|
|
529
|
-
"[data-fieldid][data-itemid][data-language][data-version]",
|
|
530
|
-
);
|
|
531
|
-
|
|
532
|
-
const fieldItems = await context.itemsRepository.getItems(
|
|
533
|
-
Array.from(allFieldElements)
|
|
534
|
-
.map((element: Element) => {
|
|
535
|
-
const fieldId = element.getAttribute("data-fieldid");
|
|
536
|
-
return fieldId
|
|
537
|
-
? {
|
|
538
|
-
id: fieldId,
|
|
539
|
-
language: "en",
|
|
540
|
-
version: 0,
|
|
541
|
-
}
|
|
542
|
-
: null;
|
|
543
|
-
})
|
|
544
|
-
.filter(
|
|
545
|
-
(item): item is { id: string; language: string; version: number } =>
|
|
546
|
-
item !== null,
|
|
547
|
-
),
|
|
548
|
-
);
|
|
549
|
-
|
|
550
|
-
const validFields = fieldItems.filter((item) => {
|
|
551
|
-
const typeField = item.fields.find((f: any) => f.name === "Type");
|
|
552
|
-
if (!typeField) return false;
|
|
553
|
-
const typeValue = typeField.value;
|
|
554
|
-
return typeValue && isTextFieldType(typeValue as string);
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
const validFieldElements = Array.from(allFieldElements).filter(
|
|
558
|
-
(element) =>
|
|
559
|
-
validFields.some(
|
|
560
|
-
(item) => element.getAttribute("data-fieldid") === item.id,
|
|
561
|
-
),
|
|
562
|
-
);
|
|
563
|
-
|
|
564
|
-
validFields.forEach(async (item, index) => {
|
|
565
|
-
const fieldElement = validFieldElements[index];
|
|
566
|
-
if (!fieldElement) return;
|
|
567
|
-
|
|
568
|
-
// Do not update if this field is currently focused.
|
|
569
|
-
if (fieldElement === context.inlineEditingFieldElement) {
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
const fieldId = fieldElement.getAttribute("data-fieldid");
|
|
574
|
-
const itemId = fieldElement.getAttribute("data-itemid");
|
|
575
|
-
const language = fieldElement.getAttribute("data-language");
|
|
576
|
-
const versionStr = fieldElement.getAttribute("data-version");
|
|
577
|
-
if (!fieldId || !itemId || !language || !versionStr) return;
|
|
578
|
-
const version = parseInt(versionStr, 10);
|
|
579
|
-
|
|
580
|
-
// Build an item descriptor.
|
|
581
|
-
const descriptor = { id: itemId, language, version };
|
|
582
|
-
|
|
583
|
-
// Fetch the current item from the repository.
|
|
584
|
-
const loadedItem = await context.itemsRepository.getItem(descriptor);
|
|
585
|
-
if (!loadedItem) return;
|
|
586
|
-
|
|
587
|
-
// Get the baseline from repository.
|
|
588
|
-
const repositoryField = loadedItem.fields.find(
|
|
589
|
-
(f: any) => f.id === fieldId,
|
|
590
|
-
);
|
|
591
|
-
let originalValue = repositoryField
|
|
592
|
-
? repositoryField.rawValue || ""
|
|
593
|
-
: "";
|
|
594
|
-
|
|
595
|
-
// If the field is modified locally, use that value.
|
|
596
|
-
const modField = modifiedFieldsContext?.modifiedFields.find(
|
|
597
|
-
(mod: any) =>
|
|
598
|
-
mod.fieldId === fieldId &&
|
|
599
|
-
mod.item.id === itemId &&
|
|
600
|
-
mod.item.language === language &&
|
|
601
|
-
mod.item.version === version,
|
|
602
|
-
);
|
|
603
|
-
if (modField) {
|
|
604
|
-
originalValue = modField.value || "";
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// If showSuggestedEdits is false, update with the base value.
|
|
608
|
-
if (!context.showSuggestedEdits && context.mode !== "suggestions") {
|
|
609
|
-
fieldElement.innerHTML = originalValue;
|
|
610
|
-
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// Otherwise, gather all suggestions for this field.
|
|
615
|
-
const fieldSuggestions = context.suggestedEdits.filter(
|
|
616
|
-
(s: any) =>
|
|
617
|
-
s.status === "Pending" &&
|
|
618
|
-
s.fieldId === fieldId &&
|
|
619
|
-
s.itemId === itemId &&
|
|
620
|
-
s.mainItemLanguage === language &&
|
|
621
|
-
s.mainItemId === pageViewContext.pageItemDescriptor?.id &&
|
|
622
|
-
s.mainItemVersion === version,
|
|
623
|
-
);
|
|
624
|
-
|
|
625
|
-
if (!fieldSuggestions.length) return;
|
|
626
|
-
|
|
627
|
-
// Sort suggestions in chronological order (oldest first).
|
|
628
|
-
fieldSuggestions.sort(
|
|
629
|
-
(a: any, b: any) =>
|
|
630
|
-
new Date(a.created).getTime() - new Date(b.created).getTime(),
|
|
631
|
-
);
|
|
632
|
-
|
|
633
|
-
// Apply suggestions sequentially to generate the merged value.
|
|
634
|
-
let mergedValue = originalValue;
|
|
635
|
-
let couldApplyPatch = true;
|
|
636
|
-
|
|
637
|
-
for (const suggestion of fieldSuggestions) {
|
|
638
|
-
// Compute a patch from the suggestion's baseline to its intended new value.
|
|
639
|
-
const patch = createPatch(
|
|
640
|
-
"field",
|
|
641
|
-
suggestion.oldValue,
|
|
642
|
-
suggestion.newValue,
|
|
643
|
-
);
|
|
644
|
-
|
|
645
|
-
const patchedCandidate = applyPatch(mergedValue, patch);
|
|
646
|
-
|
|
647
|
-
if (
|
|
648
|
-
patchedCandidate !== false &&
|
|
649
|
-
typeof patchedCandidate === "string"
|
|
650
|
-
) {
|
|
651
|
-
mergedValue = patchedCandidate;
|
|
652
|
-
} else {
|
|
653
|
-
couldApplyPatch = false;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
if (!couldApplyPatch)
|
|
658
|
-
mergedValue = fieldSuggestions[0]?.newValue || originalValue;
|
|
659
|
-
|
|
660
|
-
// If showSuggestedEditsDiff is false, show only the merged text
|
|
661
|
-
if (!context.showSuggestedEditsDiff) {
|
|
662
|
-
fieldElement.innerHTML = mergedValue;
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Compute a word-based diff between originalValue and mergedValue.
|
|
667
|
-
const diffParts = diffWords(originalValue, mergedValue);
|
|
668
|
-
|
|
669
|
-
// Build HTML markup from the diff:
|
|
670
|
-
let diffHTML = "";
|
|
671
|
-
diffParts.forEach((part: any) => {
|
|
672
|
-
let style = "";
|
|
673
|
-
if (part.added) {
|
|
674
|
-
style = "color: green;";
|
|
675
|
-
} else if (part.removed) {
|
|
676
|
-
style = "color: red; text-decoration: line-through;";
|
|
677
|
-
} else {
|
|
678
|
-
style = "color: gray;";
|
|
679
|
-
}
|
|
680
|
-
// Escape any HTML in part.value if needed.
|
|
681
|
-
diffHTML += `<span style="${style}">${part.value}</span>`;
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
// Update the element's innerHTML with the diff markup.
|
|
685
|
-
fieldElement.innerHTML = diffHTML;
|
|
686
|
-
});
|
|
687
|
-
};
|
|
688
|
-
|
|
689
|
-
updateFieldsWithSuggestions();
|
|
690
|
-
}, [
|
|
691
|
-
context.mode,
|
|
692
|
-
modifiedFieldsContext?.modifiedFields,
|
|
693
|
-
context?.itemsRepository.revision,
|
|
694
|
-
context?.inlineEditingFieldElement,
|
|
695
|
-
context?.showSuggestedEdits,
|
|
696
|
-
context?.suggestedEdits,
|
|
697
|
-
pageViewContext.pageItemDescriptor,
|
|
698
|
-
context.pageView.page,
|
|
699
|
-
context.showSuggestedEditsDiff,
|
|
700
|
-
context.showSuggestedEdits,
|
|
701
|
-
]);
|
|
702
|
-
|
|
703
|
-
useEffect(() => {
|
|
704
|
-
if (!context || compareView || context.mode === "preview") return;
|
|
705
|
-
|
|
706
|
-
const iframeDoc =
|
|
707
|
-
pageViewContext.editorIframeRef.current?.contentWindow?.document;
|
|
708
|
-
if (!iframeDoc) return;
|
|
709
|
-
|
|
710
|
-
// Query all field elements
|
|
711
|
-
const allFieldEls = Array.from(
|
|
712
|
-
iframeDoc.querySelectorAll<HTMLElement>(
|
|
713
|
-
"[data-fieldid][data-itemid][data-language][data-version]",
|
|
714
|
-
),
|
|
715
|
-
);
|
|
716
|
-
|
|
717
|
-
allFieldEls.forEach(async (el) => {
|
|
718
|
-
if (context.mode === "suggestions" || context.showSuggestedEdits) return;
|
|
719
|
-
// don't stomp on the one that's live‑editing
|
|
720
|
-
if (el === context.inlineEditingFieldElement) return;
|
|
721
|
-
|
|
722
|
-
const fieldId = el.getAttribute("data-fieldid")!;
|
|
723
|
-
|
|
724
|
-
const realField = await context.itemsRepository.getItem({
|
|
725
|
-
id: fieldId,
|
|
726
|
-
language: "en",
|
|
727
|
-
version: 0,
|
|
728
|
-
});
|
|
729
|
-
const fieldType = realField?.fields.find((f) => f.name === "Type")?.value;
|
|
730
|
-
|
|
731
|
-
if (!fieldType || !isTextFieldType(fieldType as string)) {
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
const itemId = el.getAttribute("data-itemid")!;
|
|
736
|
-
const language = el.getAttribute("data-language")!;
|
|
737
|
-
const version = parseInt(el.getAttribute("data-version")!, 10);
|
|
738
|
-
|
|
739
|
-
// lookup baseline
|
|
740
|
-
const loaded = await context.itemsRepository.getItem({
|
|
741
|
-
id: itemId,
|
|
742
|
-
language,
|
|
743
|
-
version,
|
|
744
|
-
});
|
|
745
|
-
const repoF = loaded?.fields.find((f) => f.id === fieldId);
|
|
746
|
-
let value = repoF?.rawValue ?? "";
|
|
747
|
-
|
|
748
|
-
// override with any local edit
|
|
749
|
-
const mod = modifiedFieldsContext?.modifiedFields.find(
|
|
750
|
-
(m) =>
|
|
751
|
-
m.fieldId === fieldId &&
|
|
752
|
-
m.item.id === itemId &&
|
|
753
|
-
m.item.language === language &&
|
|
754
|
-
m.item.version === version,
|
|
755
|
-
);
|
|
756
|
-
|
|
757
|
-
if (mod) value = mod.value as string;
|
|
758
|
-
|
|
759
|
-
// write it in
|
|
760
|
-
el.innerHTML = value;
|
|
761
|
-
});
|
|
762
|
-
}, [context.mode, context.showSuggestedEdits]);
|
|
763
|
-
|
|
764
|
-
return null;
|
|
765
|
-
}
|