@alpaca-editor/core 1.0.4069 → 1.0.4073
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/index.d.ts +2 -0
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/ui/select.d.ts +17 -0
- package/dist/components/ui/select.js +32 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/FieldListFieldWithFallbacks.js +8 -7
- package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +43 -6
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.js +93 -45
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/client/EditorClient.js +44 -26
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +10 -6
- package/dist/editor/client/editContext.js +12 -3
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +1 -2
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +2 -8
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/editor-warnings/ValidationErrors.js +5 -2
- package/dist/editor/editor-warnings/ValidationErrors.js.map +1 -1
- package/dist/editor/field-types/DropLinkEditor.js +12 -24
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
- package/dist/editor/field-types/DropListEditor.js +14 -25
- package/dist/editor/field-types/DropListEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +9 -9
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +18 -17
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/page-editor-chrome/CommentHighlighting.js +4 -3
- package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js +3 -2
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js +2 -2
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js.map +1 -1
- package/dist/editor/page-editor-chrome/InlineEditor.js +15 -14
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
- package/dist/editor/page-editor-chrome/SuggestionHighlighting.js +4 -3
- package/dist/editor/page-editor-chrome/SuggestionHighlighting.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +17 -13
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +3 -3
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +14 -12
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/reviews/Comment.js +3 -2
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentPopover.js +6 -5
- package/dist/editor/reviews/CommentPopover.js.map +1 -1
- package/dist/editor/reviews/SuggestedEdit.js +6 -5
- package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
- package/dist/editor/reviews/SuggestionDisplayPopover.js +3 -2
- package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
- package/dist/editor/sidebar/DictionaryEditor.js +5 -5
- package/dist/editor/sidebar/DictionaryEditor.js.map +1 -1
- package/dist/editor/sidebar/EditHistory.js +9 -4
- package/dist/editor/sidebar/EditHistory.js.map +1 -1
- package/dist/editor/sidebar/Validation.js +3 -2
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +4 -3
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +3 -0
- package/package.json +1 -1
- package/src/components/index.ts +2 -0
- package/src/components/ui/select.tsx +134 -0
- package/src/components/ui/textarea.tsx +1 -1
- package/src/editor/FieldListField.tsx +1 -1
- package/src/editor/FieldListFieldWithFallbacks.tsx +15 -7
- package/src/editor/ai/AgentTerminal.tsx +44 -6
- package/src/editor/ai/AiResponseMessage.tsx +125 -67
- package/src/editor/client/EditorClient.tsx +52 -30
- package/src/editor/client/editContext.ts +29 -10
- package/src/editor/client/itemsRepository.ts +1 -2
- package/src/editor/commands/componentCommands.tsx +3 -10
- package/src/editor/editor-warnings/ValidationErrors.tsx +11 -7
- package/src/editor/field-types/DropLinkEditor.tsx +21 -31
- package/src/editor/field-types/DropListEditor.tsx +28 -35
- package/src/editor/field-types/MultiLineText.tsx +11 -12
- package/src/editor/field-types/RichTextEditorComponent.tsx +4 -8
- package/src/editor/field-types/SingleLineText.tsx +21 -19
- package/src/editor/page-editor-chrome/CommentHighlighting.tsx +4 -6
- package/src/editor/page-editor-chrome/FieldEditedIndicator.tsx +7 -2
- package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +2 -2
- package/src/editor/page-editor-chrome/InlineEditor.tsx +20 -14
- package/src/editor/page-editor-chrome/SuggestionHighlighting.tsx +4 -6
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +17 -13
- package/src/editor/page-viewer/PageViewer.tsx +3 -6
- package/src/editor/page-viewer/PageViewerFrame.tsx +21 -12
- package/src/editor/reviews/Comment.tsx +3 -2
- package/src/editor/reviews/CommentPopover.tsx +10 -5
- package/src/editor/reviews/SuggestedEdit.tsx +6 -5
- package/src/editor/reviews/SuggestionDisplayPopover.tsx +3 -2
- package/src/editor/sidebar/DictionaryEditor.tsx +10 -13
- package/src/editor/sidebar/EditHistory.tsx +11 -4
- package/src/editor/sidebar/Validation.tsx +3 -2
- package/src/page-wizard/steps/ContentStep.tsx +4 -3
- package/src/revision.ts +2 -2
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Dropdown, DropdownChangeEvent } from "primereact/dropdown";
|
|
4
|
-
|
|
5
3
|
import { useEditContext } from "../client/editContext";
|
|
6
|
-
import { useEffect, useState } from "react";
|
|
4
|
+
import { useEffect, useState, useCallback, useMemo } from "react";
|
|
7
5
|
|
|
8
6
|
import { getLookupSources } from "../services/editService";
|
|
9
7
|
import { TextField } from "../fieldTypes";
|
|
10
8
|
import { ItemIdAndName } from "../pageModel";
|
|
9
|
+
import { Select, SelectOption } from "../../components";
|
|
11
10
|
|
|
12
11
|
export function DropListEditor({
|
|
13
12
|
field,
|
|
@@ -29,7 +28,7 @@ export function DropListEditor({
|
|
|
29
28
|
onLazyLoad();
|
|
30
29
|
}, [field.descriptor]);
|
|
31
30
|
|
|
32
|
-
const onLazyLoad = async () => {
|
|
31
|
+
const onLazyLoad = useCallback(async () => {
|
|
33
32
|
setLazyLoading(true);
|
|
34
33
|
const options = await getLookupSources(field, editContext.sessionId);
|
|
35
34
|
if (field.value && !options.find((o) => o.name === field.value)) {
|
|
@@ -41,44 +40,38 @@ export function DropListEditor({
|
|
|
41
40
|
}
|
|
42
41
|
setLazyItems(options);
|
|
43
42
|
setLazyLoading(false);
|
|
44
|
-
};
|
|
43
|
+
}, [field, editContext.sessionId]);
|
|
44
|
+
|
|
45
|
+
// Convert ItemIdAndName[] to SelectOption[] using name for both value and label - memoized
|
|
46
|
+
const selectOptions: SelectOption[] = useMemo(() =>
|
|
47
|
+
lazyItems.map((item) => ({
|
|
48
|
+
value: item.name,
|
|
49
|
+
label: item.name,
|
|
50
|
+
})), [lazyItems]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Find the current value or show unknown value - memoized
|
|
54
|
+
const currentValue = useMemo(() =>
|
|
55
|
+
lazyItems.find((o) => o.name === field.value)?.name || field.value,
|
|
56
|
+
[lazyItems, field.value]
|
|
57
|
+
);
|
|
45
58
|
|
|
46
59
|
return (
|
|
47
|
-
<
|
|
48
|
-
value={
|
|
49
|
-
|
|
50
|
-
"[Unknown: " + field.value + "]"
|
|
51
|
-
}
|
|
52
|
-
disabled={readOnly}
|
|
53
|
-
onChange={(e: DropdownChangeEvent) => {
|
|
60
|
+
<Select
|
|
61
|
+
value={currentValue}
|
|
62
|
+
onValueChange={(value) => {
|
|
54
63
|
editContext?.operations.editField({
|
|
55
64
|
field: field.descriptor,
|
|
56
|
-
rawValue:
|
|
65
|
+
rawValue: value,
|
|
57
66
|
});
|
|
58
67
|
}}
|
|
59
|
-
options={
|
|
60
|
-
optionLabel="name"
|
|
61
|
-
optionValue="name"
|
|
68
|
+
options={selectOptions}
|
|
62
69
|
placeholder="Select"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// showLoader: true,
|
|
69
|
-
loading: lazyLoading,
|
|
70
|
-
//delay: 250,
|
|
71
|
-
// loadingTemplate: (options) => {
|
|
72
|
-
// return (
|
|
73
|
-
// <div
|
|
74
|
-
// className="flex align-items-center p-2"
|
|
75
|
-
// style={{ height: "38px" }}
|
|
76
|
-
// >
|
|
77
|
-
// <Skeleton width={options.even ? "60%" : "50%"} height="1rem" />
|
|
78
|
-
// </div>
|
|
79
|
-
// );
|
|
80
|
-
// },
|
|
81
|
-
}}
|
|
70
|
+
disabled={readOnly}
|
|
71
|
+
className="md:w-14rem w-full"
|
|
72
|
+
loading={lazyLoading}
|
|
73
|
+
onLazyLoad={onLazyLoad}
|
|
74
|
+
emptyMessage="No options available"
|
|
82
75
|
/>
|
|
83
76
|
);
|
|
84
77
|
}
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { InputTextarea } from "primereact/inputtextarea";
|
|
4
4
|
import {
|
|
5
|
-
useEditContext,
|
|
6
5
|
useEditContextRef,
|
|
7
|
-
|
|
6
|
+
useFieldsEditContextRef,
|
|
8
7
|
} from "../client/editContext";
|
|
9
8
|
import { useFieldModification } from "../client/fieldModificationStore";
|
|
10
9
|
|
|
@@ -22,7 +21,7 @@ export function MultiLineText({
|
|
|
22
21
|
updateFieldValue: (value: string) => void;
|
|
23
22
|
}) {
|
|
24
23
|
const editContextRef = useEditContextRef();
|
|
25
|
-
const
|
|
24
|
+
const fieldsContextRef = useFieldsEditContextRef();
|
|
26
25
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
27
26
|
|
|
28
27
|
const fieldItem = field.descriptor.item;
|
|
@@ -31,9 +30,9 @@ export function MultiLineText({
|
|
|
31
30
|
// Field-specific subscription - only rerenders when THIS field changes
|
|
32
31
|
const { modifiedField } = useFieldModification(
|
|
33
32
|
field.id,
|
|
34
|
-
fieldItem.id,
|
|
33
|
+
fieldItem.id,
|
|
35
34
|
fieldItem.language,
|
|
36
|
-
fieldItem.version
|
|
35
|
+
fieldItem.version,
|
|
37
36
|
);
|
|
38
37
|
|
|
39
38
|
useEffect(() => {
|
|
@@ -49,13 +48,13 @@ export function MultiLineText({
|
|
|
49
48
|
|
|
50
49
|
useEffect(() => {
|
|
51
50
|
setTimeout(() => {
|
|
51
|
+
const focusedField = fieldsContextRef.current?.focusedField;
|
|
52
52
|
if (
|
|
53
|
-
!
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
editContextRef.current?.focusedField?.item.version === fieldItem.version
|
|
53
|
+
!fieldsContextRef.current?.inlineEditingFieldElement &&
|
|
54
|
+
focusedField?.fieldId === field.id &&
|
|
55
|
+
focusedField?.item.id === fieldItem.id &&
|
|
56
|
+
focusedField?.item.language === fieldItem.language &&
|
|
57
|
+
focusedField?.item.version === fieldItem.version
|
|
59
58
|
) {
|
|
60
59
|
// Only focus if no other element currently has focus (e.g., popover buttons)
|
|
61
60
|
if (
|
|
@@ -66,7 +65,7 @@ export function MultiLineText({
|
|
|
66
65
|
}
|
|
67
66
|
}
|
|
68
67
|
}, 500);
|
|
69
|
-
}, [
|
|
68
|
+
}, [fieldsContextRef.current?.focusedField]);
|
|
70
69
|
|
|
71
70
|
return (
|
|
72
71
|
<InputTextarea
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
useEditContextRef,
|
|
5
|
-
useModifiedFieldsContext,
|
|
6
|
-
} from "../client/editContext";
|
|
3
|
+
import { useEditContextRef, useFieldsEditContext } from "../client/editContext";
|
|
7
4
|
import { useFieldModification } from "../client/fieldModificationStore";
|
|
8
5
|
|
|
9
6
|
import { useThrottledCallback } from "use-debounce";
|
|
@@ -22,8 +19,7 @@ const FALLBACK_PROFILE: RichTextEditorProfile = {
|
|
|
22
19
|
label: "Basic Formatting",
|
|
23
20
|
display: "buttons" as const,
|
|
24
21
|
showIconsOnly: true,
|
|
25
|
-
options: [
|
|
26
|
-
],
|
|
22
|
+
options: [],
|
|
27
23
|
},
|
|
28
24
|
],
|
|
29
25
|
},
|
|
@@ -53,9 +49,9 @@ export function RichTextEditorComponent({
|
|
|
53
49
|
// Field-specific subscription - only rerenders when THIS field changes
|
|
54
50
|
const { modifiedField } = useFieldModification(
|
|
55
51
|
field.id,
|
|
56
|
-
fieldItem.id,
|
|
52
|
+
fieldItem.id,
|
|
57
53
|
fieldItem.language,
|
|
58
|
-
fieldItem.version
|
|
54
|
+
fieldItem.version,
|
|
59
55
|
);
|
|
60
56
|
|
|
61
57
|
useEffect(() => {
|
|
@@ -4,7 +4,8 @@ import { InputText } from "primereact/inputtext";
|
|
|
4
4
|
import {
|
|
5
5
|
useEditContext,
|
|
6
6
|
useEditContextRef,
|
|
7
|
-
|
|
7
|
+
useFieldsEditContext,
|
|
8
|
+
useFieldsEditContextRef,
|
|
8
9
|
SelectionRange,
|
|
9
10
|
} from "../client/editContext";
|
|
10
11
|
import { useFieldModification } from "../client/fieldModificationStore";
|
|
@@ -25,6 +26,7 @@ export function SingleLineText({
|
|
|
25
26
|
}) {
|
|
26
27
|
const editContextRef = useEditContextRef();
|
|
27
28
|
const editContext = useEditContext();
|
|
29
|
+
const fieldsContextRef = useFieldsEditContextRef();
|
|
28
30
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
29
31
|
// const [selectionStart, setSelectionStart] = useState(0);
|
|
30
32
|
// const [selectionEnd, setSelectionEnd] = useState(0);
|
|
@@ -36,9 +38,9 @@ export function SingleLineText({
|
|
|
36
38
|
// Field-specific subscription - only rerenders when THIS field changes
|
|
37
39
|
const { modifiedField } = useFieldModification(
|
|
38
40
|
field.id,
|
|
39
|
-
fieldItem.id,
|
|
41
|
+
fieldItem.id,
|
|
40
42
|
fieldItem.language,
|
|
41
|
-
fieldItem.version
|
|
43
|
+
fieldItem.version,
|
|
42
44
|
);
|
|
43
45
|
|
|
44
46
|
useEffect(() => {
|
|
@@ -79,22 +81,22 @@ export function SingleLineText({
|
|
|
79
81
|
|
|
80
82
|
if (
|
|
81
83
|
range.text !== editContext?.selectedRange?.text ||
|
|
82
|
-
(range.text && range.fieldId !== editContext?.selectedRange
|
|
83
|
-
range.itemId !== editContext
|
|
84
|
+
(range.text && range.fieldId !== editContext?.selectedRange?.fieldId) ||
|
|
85
|
+
range.itemId !== editContext?.selectedRange?.itemId
|
|
84
86
|
) {
|
|
85
87
|
editContextRef.current?.setSelectedRange(range);
|
|
86
88
|
}
|
|
87
89
|
};
|
|
88
90
|
|
|
89
91
|
useEffect(() => {
|
|
92
|
+
const focusedField = fieldsContextRef.current?.focusedField;
|
|
90
93
|
if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
editContextRef.current?.focusedField?.item.version === fieldItem.version
|
|
94
|
+
focusedField?.fieldId === field.id &&
|
|
95
|
+
focusedField?.item.id === fieldItem.id &&
|
|
96
|
+
focusedField?.item.language === fieldItem.language &&
|
|
97
|
+
focusedField?.item.version === fieldItem.version
|
|
96
98
|
) {
|
|
97
|
-
const range = editContextRef.current
|
|
99
|
+
const range = editContextRef.current?.selectedRange;
|
|
98
100
|
if (range && inputRef.current) {
|
|
99
101
|
inputRef.current.setSelectionRange(range.startOffset, range.endOffset);
|
|
100
102
|
// setSelectionStart(range.startOffset);
|
|
@@ -134,13 +136,13 @@ export function SingleLineText({
|
|
|
134
136
|
|
|
135
137
|
useEffect(() => {
|
|
136
138
|
setTimeout(() => {
|
|
139
|
+
const focusedField = fieldsContextRef.current?.focusedField;
|
|
137
140
|
if (
|
|
138
|
-
!
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
editContextRef.current?.focusedField?.item.version === fieldItem.version
|
|
141
|
+
!fieldsContextRef.current?.inlineEditingFieldElement &&
|
|
142
|
+
focusedField?.fieldId === field.id &&
|
|
143
|
+
focusedField?.item.id === fieldItem.id &&
|
|
144
|
+
focusedField?.item.language === fieldItem.language &&
|
|
145
|
+
focusedField?.item.version === fieldItem.version
|
|
144
146
|
) {
|
|
145
147
|
// Only focus if no other element currently has focus (e.g., popover buttons)
|
|
146
148
|
if (
|
|
@@ -151,7 +153,7 @@ export function SingleLineText({
|
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
155
|
}, 500);
|
|
154
|
-
}, [
|
|
156
|
+
}, [fieldsContextRef.current?.focusedField]);
|
|
155
157
|
|
|
156
158
|
const customSource = (field as any)?.customProperties?.source as
|
|
157
159
|
| string
|
|
@@ -185,7 +187,7 @@ export function SingleLineText({
|
|
|
185
187
|
<button
|
|
186
188
|
type="button"
|
|
187
189
|
title="Re-render"
|
|
188
|
-
className="absolute
|
|
190
|
+
className="absolute top-1/2 right-1 -translate-y-1/2 rounded p-1 text-gray-600 hover:text-gray-900"
|
|
189
191
|
onClick={() => editContext?.requestRefresh("immediate")}
|
|
190
192
|
>
|
|
191
193
|
<RefreshCw className="h-4 w-4" strokeWidth={1} />
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useEditContext,
|
|
3
|
-
useModifiedFieldsContext,
|
|
4
|
-
} from "../client/editContext";
|
|
1
|
+
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
5
2
|
import { findComponentRect, findFieldElement } from "../utils";
|
|
6
3
|
import { Comment } from "../../types";
|
|
7
4
|
import { useState } from "react";
|
|
@@ -21,7 +18,7 @@ export function CommentHighlighting({
|
|
|
21
18
|
}) {
|
|
22
19
|
const editContext = useEditContext();
|
|
23
20
|
const isSelected = comment.id === editContext?.selectedComment?.id;
|
|
24
|
-
const modifiedFields =
|
|
21
|
+
const modifiedFields = useFieldsEditContext();
|
|
25
22
|
|
|
26
23
|
const [range, setRange] = useState<number[]>([
|
|
27
24
|
comment.rangeStart || 0,
|
|
@@ -198,7 +195,8 @@ export function CommentHighlighting({
|
|
|
198
195
|
editContext?.setScrollIntoView(comment.itemId);
|
|
199
196
|
editContext?.select([comment.itemId]);
|
|
200
197
|
if (comment.fieldId) {
|
|
201
|
-
|
|
198
|
+
const fieldsContext = modifiedFields; // alias for readability
|
|
199
|
+
fieldsContext?.setFocusedField(
|
|
202
200
|
{
|
|
203
201
|
fieldId: comment.fieldId,
|
|
204
202
|
item: {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
EditedField,
|
|
3
|
+
useEditContext,
|
|
4
|
+
useFieldsEditContext,
|
|
5
|
+
} from "../client/editContext";
|
|
2
6
|
import { PageViewContext } from "../page-viewer/pageViewContext";
|
|
3
7
|
|
|
4
8
|
export function FieldEditedIndicator({
|
|
@@ -13,6 +17,7 @@ export function FieldEditedIndicator({
|
|
|
13
17
|
scroll?: number;
|
|
14
18
|
}) {
|
|
15
19
|
const editContext = useEditContext();
|
|
20
|
+
const fieldsContext = useFieldsEditContext();
|
|
16
21
|
if (!editContext) return null;
|
|
17
22
|
|
|
18
23
|
const fieldElements =
|
|
@@ -24,7 +29,7 @@ export function FieldEditedIndicator({
|
|
|
24
29
|
return (
|
|
25
30
|
<>
|
|
26
31
|
{[...fieldElements]
|
|
27
|
-
.filter((x) => x !=
|
|
32
|
+
.filter((x) => x != fieldsContext?.inlineEditingFieldElement)
|
|
28
33
|
.map((element) => (
|
|
29
34
|
<SingleFieldEditedIndicator
|
|
30
35
|
element={element}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEditContext,
|
|
1
|
+
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
2
2
|
import { PageViewContext } from "../page-viewer/pageViewContext";
|
|
3
3
|
import { FieldEditedIndicator } from "./FieldEditedIndicator";
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@ export function FieldEditedIndicators({
|
|
|
12
12
|
scroll?: number;
|
|
13
13
|
}) {
|
|
14
14
|
const editContext = useEditContext();
|
|
15
|
-
const modifiedFieldsContext =
|
|
15
|
+
const modifiedFieldsContext = useFieldsEditContext();
|
|
16
16
|
|
|
17
17
|
if (!editContext || !modifiedFieldsContext) return null;
|
|
18
18
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
useEditContext,
|
|
4
|
-
|
|
4
|
+
useFieldsEditContext,
|
|
5
5
|
useEditContextRef,
|
|
6
6
|
} from "../client/editContext";
|
|
7
7
|
import { useThrottledCallback } from "use-debounce";
|
|
@@ -22,7 +22,7 @@ export function InlineEditor({
|
|
|
22
22
|
}) {
|
|
23
23
|
const context = useEditContext();
|
|
24
24
|
const contextRef = useEditContextRef();
|
|
25
|
-
const modifiedFieldsContext =
|
|
25
|
+
const modifiedFieldsContext = useFieldsEditContext();
|
|
26
26
|
const [cursorSpanId] = useState(
|
|
27
27
|
() => `cursor-indicator-${Math.random().toString(36).substring(2, 9)}`,
|
|
28
28
|
);
|
|
@@ -52,7 +52,7 @@ export function InlineEditor({
|
|
|
52
52
|
// Don't include our cursor indicator in the saved value
|
|
53
53
|
const iframeDocument =
|
|
54
54
|
pageViewContext.editorIframe?.contentWindow?.document;
|
|
55
|
-
const element =
|
|
55
|
+
const element = modifiedFieldsContext?.inlineEditingFieldElement;
|
|
56
56
|
const isRichText = element?.getAttribute("data-is-richtext") === "true";
|
|
57
57
|
|
|
58
58
|
// Clone element to avoid modifying the original
|
|
@@ -102,7 +102,7 @@ export function InlineEditor({
|
|
|
102
102
|
|
|
103
103
|
useEffect(() => {
|
|
104
104
|
if (!context || compareView || context.mode === "preview") return;
|
|
105
|
-
const element =
|
|
105
|
+
const element = modifiedFieldsContext?.inlineEditingFieldElement;
|
|
106
106
|
|
|
107
107
|
const editableElements =
|
|
108
108
|
pageViewContext.editorIframe?.contentWindow?.document.querySelectorAll(
|
|
@@ -110,7 +110,7 @@ export function InlineEditor({
|
|
|
110
110
|
);
|
|
111
111
|
|
|
112
112
|
editableElements?.forEach((element) => {
|
|
113
|
-
if (element !==
|
|
113
|
+
if (element !== modifiedFieldsContext?.inlineEditingFieldElement)
|
|
114
114
|
element.setAttribute("contenteditable", "false");
|
|
115
115
|
});
|
|
116
116
|
|
|
@@ -267,7 +267,7 @@ export function InlineEditor({
|
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
async function updateFocusedFieldContent() {
|
|
270
|
-
const element =
|
|
270
|
+
const element = modifiedFieldsContext?.inlineEditingFieldElement;
|
|
271
271
|
if (!element) return;
|
|
272
272
|
|
|
273
273
|
const savedPosition = saveCaretPosition(element);
|
|
@@ -285,7 +285,8 @@ export function InlineEditor({
|
|
|
285
285
|
const descriptor = { id: itemId, language, version };
|
|
286
286
|
|
|
287
287
|
// Retrieve the current field value from the repository.
|
|
288
|
-
const loadedItem =
|
|
288
|
+
const loadedItem =
|
|
289
|
+
await contextRef.current?.itemsRepository.getItem(descriptor);
|
|
289
290
|
if (!loadedItem) return;
|
|
290
291
|
// Get the baseline value from the repository.
|
|
291
292
|
const repositoryField = loadedItem.fields.find(
|
|
@@ -307,8 +308,10 @@ export function InlineEditor({
|
|
|
307
308
|
|
|
308
309
|
// If suggestions mode is active, merge all suggestions for this field.
|
|
309
310
|
|
|
310
|
-
if (
|
|
311
|
-
const fieldSuggestions =
|
|
311
|
+
if (contextRef.current?.mode === "suggestions") {
|
|
312
|
+
const fieldSuggestions = (
|
|
313
|
+
contextRef.current?.suggestedEdits ?? []
|
|
314
|
+
).filter(
|
|
312
315
|
(s: any) =>
|
|
313
316
|
s.fieldId === fieldId &&
|
|
314
317
|
s.itemId === itemId &&
|
|
@@ -380,7 +383,7 @@ export function InlineEditor({
|
|
|
380
383
|
cursorElement.parentNode?.removeChild(cursorElement);
|
|
381
384
|
}
|
|
382
385
|
};
|
|
383
|
-
}, [
|
|
386
|
+
}, [modifiedFieldsContext?.inlineEditingFieldElement]);
|
|
384
387
|
|
|
385
388
|
function saveCaretPosition(
|
|
386
389
|
editableElement: HTMLElement,
|
|
@@ -508,7 +511,10 @@ export function InlineEditor({
|
|
|
508
511
|
);
|
|
509
512
|
|
|
510
513
|
elements?.forEach(async (element) => {
|
|
511
|
-
if (
|
|
514
|
+
if (
|
|
515
|
+
element &&
|
|
516
|
+
element !== modifiedFieldsContext?.inlineEditingFieldElement
|
|
517
|
+
) {
|
|
512
518
|
const realField = await context.itemsRepository.getField(field);
|
|
513
519
|
const fieldType = realField?.type;
|
|
514
520
|
|
|
@@ -564,7 +570,7 @@ export function InlineEditor({
|
|
|
564
570
|
if (!fieldElement) return;
|
|
565
571
|
|
|
566
572
|
// Do not update if this field is currently focused.
|
|
567
|
-
if (fieldElement ===
|
|
573
|
+
if (fieldElement === modifiedFieldsContext?.inlineEditingFieldElement) {
|
|
568
574
|
return;
|
|
569
575
|
}
|
|
570
576
|
|
|
@@ -689,7 +695,7 @@ export function InlineEditor({
|
|
|
689
695
|
context.mode,
|
|
690
696
|
modifiedFieldsContext?.modifiedFields,
|
|
691
697
|
context?.itemsRepository.revision,
|
|
692
|
-
|
|
698
|
+
modifiedFieldsContext?.inlineEditingFieldElement,
|
|
693
699
|
context?.showSuggestedEdits,
|
|
694
700
|
context?.suggestedEdits,
|
|
695
701
|
pageViewContext.pageItemDescriptor,
|
|
@@ -715,7 +721,7 @@ export function InlineEditor({
|
|
|
715
721
|
// don't stomp on the one that's live‑editing (only in non-preview modes)
|
|
716
722
|
if (
|
|
717
723
|
context.mode !== "preview" &&
|
|
718
|
-
el ===
|
|
724
|
+
el === modifiedFieldsContext?.inlineEditingFieldElement
|
|
719
725
|
)
|
|
720
726
|
return;
|
|
721
727
|
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useEditContext,
|
|
3
|
-
useModifiedFieldsContext,
|
|
4
|
-
} from "../client/editContext";
|
|
1
|
+
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
5
2
|
import { findFieldElement } from "../utils";
|
|
6
3
|
import { SuggestedEdit } from "../../types";
|
|
7
4
|
import { useState } from "react";
|
|
@@ -21,7 +18,7 @@ export function SuggestionHighlighting({
|
|
|
21
18
|
iframe: HTMLIFrameElement;
|
|
22
19
|
}) {
|
|
23
20
|
const editContext = useEditContext();
|
|
24
|
-
const modifiedFields =
|
|
21
|
+
const modifiedFields = useFieldsEditContext();
|
|
25
22
|
const [ranges, setRanges] = useState<Array<[number, number]>>([]);
|
|
26
23
|
const [textContent, setTextContent] = useState<string>("");
|
|
27
24
|
|
|
@@ -227,7 +224,8 @@ export function SuggestionHighlighting({
|
|
|
227
224
|
)}
|
|
228
225
|
onClick={() => {
|
|
229
226
|
editContext.select([suggestion.itemId]);
|
|
230
|
-
|
|
227
|
+
const fieldsContext = modifiedFields; // alias to use setter
|
|
228
|
+
fieldsContext?.setFocusedField(
|
|
231
229
|
{
|
|
232
230
|
item: {
|
|
233
231
|
id: suggestion.itemId,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "react";
|
|
9
9
|
import { useDebouncedCallback } from "use-debounce";
|
|
10
10
|
import { PageViewContext } from "../page-viewer/pageViewContext";
|
|
11
|
-
import { useEditContext } from "../client/editContext";
|
|
11
|
+
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
12
12
|
import { executePrompt } from "../services/aiService";
|
|
13
13
|
import {
|
|
14
14
|
generatePageContext,
|
|
@@ -26,6 +26,7 @@ export function useInlineAiCompletion({
|
|
|
26
26
|
isUpdatingRef: MutableRefObject<boolean>;
|
|
27
27
|
}) {
|
|
28
28
|
const editContext = useEditContext();
|
|
29
|
+
const fieldsContext = useFieldsEditContext();
|
|
29
30
|
const [currentCompletion, setCurrentCompletion] = useState<string | null>(
|
|
30
31
|
null,
|
|
31
32
|
);
|
|
@@ -61,7 +62,7 @@ export function useInlineAiCompletion({
|
|
|
61
62
|
try {
|
|
62
63
|
const iframeWindow = pageViewContext.editorIframe?.contentWindow;
|
|
63
64
|
const iframeDocument = iframeWindow?.document;
|
|
64
|
-
const editableElement =
|
|
65
|
+
const editableElement = fieldsContext?.inlineEditingFieldElement;
|
|
65
66
|
|
|
66
67
|
if (!iframeWindow || !iframeDocument || !editableElement) return;
|
|
67
68
|
|
|
@@ -396,7 +397,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
396
397
|
// Debounced AI call: recompute the sentence, call getCompletion, and extract only the "tail" for the ghost text
|
|
397
398
|
const getCompletionDebounced = useDebouncedCallback(
|
|
398
399
|
async (isManualTrigger = false) => {
|
|
399
|
-
const el =
|
|
400
|
+
const el = fieldsContext?.inlineEditingFieldElement;
|
|
400
401
|
if (!el) return;
|
|
401
402
|
|
|
402
403
|
// 1) Recompute the exact sentence at this moment
|
|
@@ -426,7 +427,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
426
427
|
|
|
427
428
|
// Manual completion trigger (non-debounced for immediate response)
|
|
428
429
|
const getCompletionManual = async () => {
|
|
429
|
-
const el =
|
|
430
|
+
const el = fieldsContext?.inlineEditingFieldElement;
|
|
430
431
|
if (!el) return;
|
|
431
432
|
|
|
432
433
|
// 1) Recompute the exact sentence at this moment
|
|
@@ -556,7 +557,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
556
557
|
// On every input: either reuse the existing suggestion or fire a new one
|
|
557
558
|
const handleInput = useCallback(
|
|
558
559
|
(e: KeyboardEvent) => {
|
|
559
|
-
const el =
|
|
560
|
+
const el = fieldsContext?.inlineEditingFieldElement;
|
|
560
561
|
if (!el) return;
|
|
561
562
|
|
|
562
563
|
// Clear completion when Escape is pressed
|
|
@@ -624,7 +625,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
624
625
|
|
|
625
626
|
// Check if cursor is at the end of the text
|
|
626
627
|
const isAtEnd = () => {
|
|
627
|
-
const el =
|
|
628
|
+
const el = fieldsContext?.inlineEditingFieldElement;
|
|
628
629
|
if (!el) return false;
|
|
629
630
|
|
|
630
631
|
const iframeWindow = pageViewContext.editorIframe?.contentWindow;
|
|
@@ -663,7 +664,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
663
664
|
}
|
|
664
665
|
},
|
|
665
666
|
[
|
|
666
|
-
|
|
667
|
+
fieldsContext?.inlineEditingFieldElement,
|
|
667
668
|
currentCompletion,
|
|
668
669
|
getCompletionDebounced,
|
|
669
670
|
getCompletionManual,
|
|
@@ -673,12 +674,12 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
673
674
|
// Wire up the input listener
|
|
674
675
|
useEffect(() => {
|
|
675
676
|
if (!editContext?.enableCompletions) return;
|
|
676
|
-
const el =
|
|
677
|
+
const el = fieldsContext?.inlineEditingFieldElement;
|
|
677
678
|
if (!el) return;
|
|
678
679
|
el.addEventListener("keydown", handleInput);
|
|
679
680
|
return () => el.removeEventListener("keydown", handleInput);
|
|
680
681
|
}, [
|
|
681
|
-
|
|
682
|
+
fieldsContext?.inlineEditingFieldElement,
|
|
682
683
|
handleInput,
|
|
683
684
|
editContext?.enableCompletions,
|
|
684
685
|
]);
|
|
@@ -686,7 +687,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
686
687
|
// Add mouse click handler to update cursor span position
|
|
687
688
|
useEffect(() => {
|
|
688
689
|
if (!editContext?.enableCompletions) return;
|
|
689
|
-
const el =
|
|
690
|
+
const el = fieldsContext?.inlineEditingFieldElement;
|
|
690
691
|
if (!el) return;
|
|
691
692
|
|
|
692
693
|
const handleMouseUp = () => {
|
|
@@ -703,7 +704,10 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
703
704
|
|
|
704
705
|
el.addEventListener("mouseup", handleMouseUp);
|
|
705
706
|
return () => el.removeEventListener("mouseup", handleMouseUp);
|
|
706
|
-
}, [
|
|
707
|
+
}, [
|
|
708
|
+
fieldsContext?.inlineEditingFieldElement,
|
|
709
|
+
editContext?.enableCompletions,
|
|
710
|
+
]);
|
|
707
711
|
|
|
708
712
|
// Clean up abort controller on unmount
|
|
709
713
|
useEffect(() => {
|
|
@@ -728,7 +732,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
728
732
|
if (
|
|
729
733
|
!iframeWindow ||
|
|
730
734
|
!iframeDocument ||
|
|
731
|
-
!
|
|
735
|
+
!fieldsContext?.inlineEditingFieldElement
|
|
732
736
|
)
|
|
733
737
|
return;
|
|
734
738
|
|
|
@@ -739,7 +743,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
739
743
|
const completionToApply = cursorSpan.textContent || "";
|
|
740
744
|
if (!completionToApply) return;
|
|
741
745
|
|
|
742
|
-
const element =
|
|
746
|
+
const element = fieldsContext?.inlineEditingFieldElement;
|
|
743
747
|
|
|
744
748
|
// Get the current selection position
|
|
745
749
|
const selection = iframeWindow.getSelection();
|
|
@@ -3,10 +3,7 @@ import { EditorForm } from "./EditorForm";
|
|
|
3
3
|
import { PageViewerFrame } from "./PageViewerFrame";
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
5
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
6
|
-
import {
|
|
7
|
-
useEditContext,
|
|
8
|
-
useModifiedFieldsContext,
|
|
9
|
-
} from "../client/editContext";
|
|
6
|
+
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
10
7
|
import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
11
8
|
import { cn } from "../../lib/utils";
|
|
12
9
|
import { Splitter, SplitterPanel } from "../ui/Splitter";
|
|
@@ -29,7 +26,7 @@ export function PageViewer({
|
|
|
29
26
|
noMargins?: boolean;
|
|
30
27
|
}) {
|
|
31
28
|
const editContext = useEditContext();
|
|
32
|
-
const modifiedFieldsContext =
|
|
29
|
+
const modifiedFieldsContext = useFieldsEditContext();
|
|
33
30
|
|
|
34
31
|
const [followEdits, setFollowEdits] = useState(followEditsDefault);
|
|
35
32
|
const [formEditorCollapsed, setFormEditorCollapsed] = useState(false);
|
|
@@ -50,7 +47,7 @@ export function PageViewer({
|
|
|
50
47
|
modifiedFieldsContext?.recentEdits &&
|
|
51
48
|
modifiedFieldsContext.recentEdits.length > 0
|
|
52
49
|
) {
|
|
53
|
-
if (
|
|
50
|
+
if (modifiedFieldsContext?.inlineEditingFieldElement) return;
|
|
54
51
|
|
|
55
52
|
const lastEdit =
|
|
56
53
|
modifiedFieldsContext.recentEdits[
|