@alpaca-editor/core 1.0.4040 → 1.0.4042

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.
Files changed (30) hide show
  1. package/dist/components/ui/textarea.d.ts +1 -1
  2. package/dist/components/ui/textarea.js +5 -3
  3. package/dist/components/ui/textarea.js.map +1 -1
  4. package/dist/editor/commands/componentCommands.js +18 -5
  5. package/dist/editor/commands/componentCommands.js.map +1 -1
  6. package/dist/editor/page-editor-chrome/CommentHighlighting.js +30 -10
  7. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  8. package/dist/editor/page-editor-chrome/SuggestionHighlighting.js +17 -13
  9. package/dist/editor/page-editor-chrome/SuggestionHighlighting.js.map +1 -1
  10. package/dist/editor/reviews/CommentDisplayPopover.d.ts +9 -0
  11. package/dist/editor/reviews/CommentDisplayPopover.js +101 -0
  12. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -0
  13. package/dist/editor/reviews/CommentPopover.d.ts +15 -0
  14. package/dist/editor/reviews/CommentPopover.js +151 -0
  15. package/dist/editor/reviews/CommentPopover.js.map +1 -0
  16. package/dist/editor/reviews/SuggestionDisplayPopover.d.ts +9 -0
  17. package/dist/editor/reviews/SuggestionDisplayPopover.js +186 -0
  18. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -0
  19. package/dist/revision.d.ts +2 -2
  20. package/dist/revision.js +2 -2
  21. package/dist/styles.css +3 -0
  22. package/package.json +1 -1
  23. package/src/components/ui/textarea.tsx +7 -2
  24. package/src/editor/commands/componentCommands.tsx +16 -4
  25. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +49 -18
  26. package/src/editor/page-editor-chrome/SuggestionHighlighting.tsx +36 -26
  27. package/src/editor/reviews/CommentDisplayPopover.tsx +326 -0
  28. package/src/editor/reviews/CommentPopover.tsx +249 -0
  29. package/src/editor/reviews/SuggestionDisplayPopover.tsx +410 -0
  30. package/src/revision.ts +2 -2
@@ -0,0 +1,151 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useEffect, useLayoutEffect, useState } from "react";
5
+ import uuid from "react-uuid";
6
+ import { Send, MessageCirclePlus } from "lucide-react";
7
+ import { useEditContext } from "../client/editContext";
8
+ import { createOrUpdateComment } from "../services/reviewsService";
9
+ import { Popover, PopoverContent, PopoverTrigger, PopoverAnchor, } from "../../components/ui/popover";
10
+ import { Button } from "../../components/ui/button";
11
+ import { Textarea } from "../../components/ui/textarea";
12
+ import * as ReactDOM from "react-dom/client";
13
+ export function CommentPopover({ children, editContext: externalEditContext, position, onClose, }) {
14
+ const [isOpen, setIsOpen] = useState(!!position);
15
+ const [text, setText] = useState("");
16
+ const textareaRef = React.useRef(null);
17
+ const [saving, setSaving] = useState(false);
18
+ const contextFromHook = useEditContext();
19
+ const editContext = externalEditContext || contextFromHook;
20
+ useEffect(() => {
21
+ if (position)
22
+ setIsOpen(true);
23
+ }, [position?.x, position?.y]);
24
+ const handleSubmit = async () => {
25
+ if (!editContext || !text.trim() || saving)
26
+ return;
27
+ const descriptor = editContext.contentEditorItem?.descriptor;
28
+ if (!descriptor)
29
+ return;
30
+ const itemId = editContext.focusedField?.item.id ||
31
+ (editContext.selection.length > 0
32
+ ? editContext.selection[0]
33
+ : undefined) ||
34
+ descriptor.id;
35
+ if (!itemId)
36
+ return;
37
+ const language = descriptor.language;
38
+ const version = descriptor.version;
39
+ const getFieldName = async () => {
40
+ if (!editContext.focusedField)
41
+ return undefined;
42
+ const field = await editContext.itemsRepository.getField(editContext.focusedField);
43
+ return field?.name;
44
+ };
45
+ const getItemName = async () => {
46
+ const item = await editContext.itemsRepository.getItem({
47
+ id: itemId,
48
+ language,
49
+ version,
50
+ });
51
+ return item?.name;
52
+ };
53
+ setSaving(true);
54
+ try {
55
+ const newComment = {
56
+ id: uuid(),
57
+ isNew: false,
58
+ itemId,
59
+ itemName: await getItemName(),
60
+ fieldId: editContext.focusedField?.fieldId,
61
+ fieldName: await getFieldName(),
62
+ mainItemId: descriptor.id,
63
+ language,
64
+ version,
65
+ position: 0,
66
+ rangeStart: editContext.selectedRange?.startOffset || 0,
67
+ rangeEnd: editContext.selectedRange?.endOffset || 0,
68
+ author: editContext.user?.name,
69
+ authorDisplayName: editContext.user?.displayName,
70
+ date: new Date().toISOString(),
71
+ text: text.trim(),
72
+ };
73
+ editContext.setComments([newComment, ...editContext.comments]);
74
+ editContext.setSelectedComment(newComment);
75
+ try {
76
+ const item = await editContext.itemsRepository.getItem({
77
+ id: itemId,
78
+ language,
79
+ version,
80
+ });
81
+ if (item && newComment.fieldId) {
82
+ newComment.fieldValue = item.fields.find((f) => f.id === newComment.fieldId)?.rawValue;
83
+ }
84
+ }
85
+ catch { }
86
+ await createOrUpdateComment(newComment);
87
+ editContext.loadComments();
88
+ }
89
+ finally {
90
+ setSaving(false);
91
+ setIsOpen(false);
92
+ setText("");
93
+ onClose?.();
94
+ }
95
+ };
96
+ const handleKeyDown = (e) => {
97
+ if (e.key === "Enter" && !e.shiftKey) {
98
+ e.preventDefault();
99
+ handleSubmit();
100
+ }
101
+ };
102
+ useLayoutEffect(() => {
103
+ if (isOpen) {
104
+ // Single focus attempt after the popover is fully mounted
105
+ const focusTextarea = () => {
106
+ const el = textareaRef.current;
107
+ if (el) {
108
+ el.focus();
109
+ try {
110
+ const len = el.value.length;
111
+ el.setSelectionRange(len, len);
112
+ }
113
+ catch { }
114
+ }
115
+ };
116
+ // Use a longer delay to ensure popover is fully rendered, especially for dynamically created popovers
117
+ const timeoutId = setTimeout(focusTextarea, 300);
118
+ return () => clearTimeout(timeoutId);
119
+ }
120
+ }, [isOpen]);
121
+ const assignTextareaRef = (el) => {
122
+ textareaRef.current = el;
123
+ };
124
+ return (_jsxs(Popover, { open: isOpen, onOpenChange: (open) => {
125
+ setIsOpen(open);
126
+ if (!open)
127
+ onClose?.();
128
+ }, enableIframeClickDetection: false, modal: true, children: [position && (_jsx("div", { style: {
129
+ position: "fixed",
130
+ left: position.x,
131
+ top: position.y,
132
+ width: 1,
133
+ height: 1,
134
+ pointerEvents: "none",
135
+ }, children: _jsx(PopoverAnchor, {}) })), !position && (_jsx(PopoverTrigger, { asChild: true, children: children ?? _jsx(MessageCirclePlus, { size: 16, strokeWidth: 1 }) })), _jsx(PopoverContent, { className: "w-96 p-4", side: "bottom", align: "start", onPointerDownOutside: (e) => e.preventDefault(), onInteractOutside: (e) => e.preventDefault(), onFocusOutside: (e) => e.preventDefault(), onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "font-medium", children: "Add Comment" }), _jsx(Textarea, { ref: assignTextareaRef, value: text, onChange: (e) => setText(e.target.value), onKeyDown: handleKeyDown, placeholder: "Write your comment...", className: "mt-2 resize-none", rows: 3, autoFocus: true }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => {
136
+ setIsOpen(false);
137
+ onClose?.();
138
+ }, disabled: saving, children: "Cancel" }), _jsxs(Button, { size: "sm", onClick: handleSubmit, disabled: !text.trim() || saving, className: "flex items-center gap-1", children: [_jsx(Send, { size: 14, strokeWidth: 1 }), saving ? "Saving..." : "Add Comment"] })] })] }) })] }));
139
+ }
140
+ export function showCommentPopoverAt(position, editContext) {
141
+ const container = document.createElement("div");
142
+ document.body.appendChild(container);
143
+ const root = ReactDOM.createRoot(container);
144
+ const handleClose = () => {
145
+ root.unmount();
146
+ if (container.parentNode)
147
+ container.parentNode.removeChild(container);
148
+ };
149
+ root.render(_jsx(CommentPopover, { editContext: editContext, position: position, onClose: handleClose }));
150
+ }
151
+ //# sourceMappingURL=CommentPopover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentPopover.js","sourceRoot":"","sources":["../../../src/editor/reviews/CommentPopover.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import * as React from "react";
2
+ import { SuggestedEdit } from "../../types";
3
+ interface SuggestionDisplayPopoverProps {
4
+ suggestion: SuggestedEdit;
5
+ children: React.ReactNode;
6
+ onSuggestionUpdated?: () => void;
7
+ }
8
+ export declare function SuggestionDisplayPopover({ suggestion, children, onSuggestionUpdated, }: SuggestionDisplayPopoverProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,186 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useState } from "react";
5
+ import { useEditContext } from "../client/editContext";
6
+ import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
7
+ import { formatDate } from "../utils";
8
+ import { Button } from "../../components/ui/button";
9
+ import { deleteSuggestedEdit, createOrUpdateSuggestedEdit, } from "../services/suggestedEditsService";
10
+ import { Check, Trash2, Brush, GalleryVertical, } from "lucide-react";
11
+ import { DiffView } from "./DiffView";
12
+ import { SimpleIconButton } from "../ui/SimpleIconButton";
13
+ import { createPatch, applyPatch } from "diff";
14
+ import { cn } from "../../lib/utils";
15
+ export function SuggestionDisplayPopover({ suggestion, children, onSuggestionUpdated, }) {
16
+ const editContext = useEditContext();
17
+ const [isOpen, setIsOpen] = useState(false);
18
+ const [deleteConfirm, setDeleteConfirm] = useState(false);
19
+ const [isApplying, setIsApplying] = useState(false);
20
+ const [item, setItem] = useState(null);
21
+ const [patchPossible, setPatchPossible] = useState(true);
22
+ const [patchWarning, setPatchWarning] = useState("");
23
+ const [ignoreFormatting, setIgnoreFormatting] = useState(true);
24
+ const [clipUnchanged, setClipUnchanged] = useState(true);
25
+ const canDelete = suggestion.author === editContext?.user?.name &&
26
+ suggestion.status !== "applied";
27
+ const canApply = editContext?.mode === "edit" &&
28
+ suggestion.status !== "applied" &&
29
+ !editContext?.readonly;
30
+ // Load the full item when popover opens
31
+ React.useEffect(() => {
32
+ if (isOpen && editContext?.itemsRepository && suggestion.itemId) {
33
+ editContext.itemsRepository
34
+ .getItem({
35
+ id: suggestion.itemId,
36
+ language: suggestion.mainItemLanguage,
37
+ version: suggestion.mainItemVersion,
38
+ })
39
+ .then((loadedItem) => {
40
+ setItem(loadedItem);
41
+ checkAndComputePatch(loadedItem);
42
+ })
43
+ .catch((err) => {
44
+ console.error("Error loading item:", err);
45
+ });
46
+ }
47
+ }, [isOpen, suggestion, editContext?.itemsRepository]);
48
+ // Check if the patch can be applied cleanly
49
+ const checkAndComputePatch = async (loadedItem) => {
50
+ const field = loadedItem?.fields?.find((f) => f.id === suggestion.fieldId);
51
+ if (!field)
52
+ return;
53
+ const currentValue = field.rawValue || "";
54
+ const patch = createPatch("field", suggestion.oldValue, suggestion.newValue);
55
+ const patchedCandidate = applyPatch(currentValue, patch);
56
+ if (patchedCandidate === false || typeof patchedCandidate !== "string") {
57
+ setPatchPossible(false);
58
+ setPatchWarning("Patch cannot be applied cleanly to current field value.");
59
+ }
60
+ else {
61
+ setPatchPossible(true);
62
+ setPatchWarning("");
63
+ }
64
+ };
65
+ const handleDelete = async () => {
66
+ if (!deleteConfirm) {
67
+ setDeleteConfirm(true);
68
+ return;
69
+ }
70
+ await deleteSuggestedEdit(suggestion);
71
+ setIsOpen(false);
72
+ onSuggestionUpdated?.();
73
+ };
74
+ const handleApplyPatch = async () => {
75
+ if (!patchPossible || !editContext || isApplying)
76
+ return;
77
+ setIsApplying(true);
78
+ try {
79
+ // Recalculate the patch immediately before applying
80
+ const field = item?.fields?.find((f) => f.id === suggestion.fieldId);
81
+ if (!field)
82
+ return;
83
+ const currentValue = field.rawValue || "";
84
+ const patch = createPatch("field", suggestion.oldValue, suggestion.newValue);
85
+ const patchedCandidate = applyPatch(currentValue, patch);
86
+ if (patchedCandidate === false || typeof patchedCandidate !== "string") {
87
+ setPatchWarning("Patch cannot be applied cleanly to current field value.");
88
+ return;
89
+ }
90
+ await editContext.operations.editField({
91
+ field: {
92
+ fieldId: suggestion.fieldId,
93
+ item: {
94
+ id: suggestion.itemId,
95
+ language: suggestion.mainItemLanguage,
96
+ version: suggestion.mainItemVersion,
97
+ },
98
+ },
99
+ value: patchedCandidate,
100
+ rawValue: patchedCandidate,
101
+ refresh: "immediate",
102
+ });
103
+ // Update the suggestion status to "applied"
104
+ const updatedSuggestion = { ...suggestion, status: "applied" };
105
+ await createOrUpdateSuggestedEdit(updatedSuggestion);
106
+ onSuggestionUpdated?.();
107
+ setIsOpen(false);
108
+ }
109
+ finally {
110
+ setIsApplying(false);
111
+ }
112
+ };
113
+ const handleReplaceCompletely = async () => {
114
+ if (!editContext || isApplying)
115
+ return;
116
+ setIsApplying(true);
117
+ try {
118
+ await editContext.operations.editField({
119
+ field: {
120
+ fieldId: suggestion.fieldId,
121
+ item: {
122
+ id: suggestion.itemId,
123
+ language: suggestion.mainItemLanguage,
124
+ version: suggestion.mainItemVersion,
125
+ },
126
+ },
127
+ value: suggestion.newValue,
128
+ rawValue: suggestion.newValue,
129
+ refresh: "immediate",
130
+ });
131
+ const updatedSuggestion = { ...suggestion, status: "applied" };
132
+ await createOrUpdateSuggestedEdit(updatedSuggestion);
133
+ onSuggestionUpdated?.();
134
+ setIsOpen(false);
135
+ }
136
+ finally {
137
+ setIsApplying(false);
138
+ }
139
+ };
140
+ const renderContextInfo = () => {
141
+ const itemName = item ? item.name : null;
142
+ const fieldName = item && item.fields
143
+ ? item.fields.find((f) => f.id === suggestion.fieldId)?.name
144
+ : null;
145
+ if (!itemName && !fieldName)
146
+ return null;
147
+ return (_jsxs("div", { className: "mt-3 flex items-center border-t pt-3 text-xs", children: [itemName && _jsx("div", { className: "text-xs text-gray-500", children: itemName }), fieldName && itemName && (_jsx("div", { className: "mx-2 text-xs text-gray-500", children: ">" })), fieldName && _jsx("div", { className: "text-xs text-gray-500", children: fieldName })] }));
148
+ };
149
+ const renderDiffToggleButtons = () => {
150
+ return (_jsxs("div", { className: "mb-2 flex gap-2", children: [_jsx(SimpleIconButton, { icon: _jsx(Brush, { size: 14, className: "p-0.5", strokeWidth: 1 }), label: "Ignore Formatting", onClick: () => setIgnoreFormatting((prev) => !prev), className: cn("text-gray-500", ignoreFormatting ? "bg-gray-200" : "") }), _jsx(SimpleIconButton, { icon: _jsx(GalleryVertical, { size: 14, className: "p-0.5", strokeWidth: 1 }), label: "Clip", onClick: () => setClipUnchanged((prev) => !prev), className: cn("text-gray-500", clipUnchanged ? "bg-gray-200" : "") })] }));
151
+ };
152
+ return (_jsxs(Popover, { open: isOpen, onOpenChange: (open) => {
153
+ setIsOpen(open);
154
+ if (!open) {
155
+ setDeleteConfirm(false);
156
+ setPatchWarning("");
157
+ }
158
+ }, enableIframeClickDetection: true, children: [_jsx(PopoverTrigger, { asChild: true, children: children }), _jsx(PopoverContent, { className: "w-96 p-4", side: "bottom", align: "start", children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-semibold text-gray-900", children: suggestion.authorDisplayName || suggestion.author }), _jsx("div", { className: "text-xs text-gray-500", children: suggestion.created
159
+ ? formatDate(new Date(suggestion.created))
160
+ : "" })] }), _jsxs("div", { className: "flex items-center gap-1", children: [canApply && patchPossible && suggestion.status !== "applied" && (_jsx(Button, { variant: "ghost", size: "sm", onClick: handleApplyPatch, disabled: isApplying, className: "h-8 w-8 p-0 text-green-600", children: _jsx(Check, { size: 14, strokeWidth: 1 }) })), suggestion.status === "applied" && (_jsx("div", { className: "flex h-8 w-8 items-center justify-center", children: _jsx(Check, { size: 14, strokeWidth: 1, className: "text-green-500" }) })), canDelete && (_jsx(Button, { variant: "ghost", size: "sm", onClick: handleDelete, className: `h-8 w-8 p-0 ${deleteConfirm ? "text-red-500" : "text-gray-400"}`, children: _jsx(Trash2, { size: 14, strokeWidth: 1 }) }))] })] }), suggestion.status === "applied" && (_jsxs("div", { className: "text-xs font-medium text-green-600", children: ["\u2713 Applied", " ", suggestion.updatedBy ? `by ${suggestion.updatedBy}` : "", suggestion.updated
161
+ ? ` on ${formatDate(new Date(suggestion.updated))}`
162
+ : ""] })), renderDiffToggleButtons(), _jsxs("div", { className: "text-sm", children: [_jsx(DiffView, { oldText: suggestion.oldValue, newText: suggestion.newValue, ignoreFormatting: ignoreFormatting, clipUnchanged: clipUnchanged, clipThreshold: 50, clipContext: 10 }), canApply && patchWarning && (_jsxs("div", { className: "mt-2 text-xs text-red-500", children: [patchWarning, _jsx("button", { className: "ml-2 cursor-pointer underline hover:text-red-700", onClick: handleReplaceCompletely, disabled: isApplying, children: "Click here to replace the field content completely." })] }))] }), renderContextInfo(), deleteConfirm && (_jsxs("div", { className: "border-t pt-3", children: [_jsx("div", { className: "mb-2 text-sm text-red-600", children: "Are you sure you want to delete this suggestion?" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setDeleteConfirm(false), children: "Cancel" }), _jsx(Button, { variant: "destructive", size: "sm", onClick: handleDelete, children: "Delete" })] })] })), canApply && !patchPossible && suggestion.status !== "applied" && (_jsx("div", { className: "border-t pt-3", children: _jsx(Button, { size: "sm", onClick: handleReplaceCompletely, disabled: isApplying, className: "w-full", children: isApplying ? "Applying..." : "Replace Field Content" }) })), !deleteConfirm && (_jsx("div", { className: "border-t pt-3", children: _jsx(Button, { variant: "outline", size: "sm", onClick: () => {
163
+ setIsOpen(false);
164
+ // Exit fullscreen mode if active
165
+ if (editContext?.pageView?.fullscreen) {
166
+ editContext.pageView.setFullscreen(false);
167
+ }
168
+ // Focus on the field and select the item
169
+ if (suggestion.fieldId) {
170
+ editContext?.setFocusedField({
171
+ fieldId: suggestion.fieldId,
172
+ item: {
173
+ id: suggestion.itemId,
174
+ language: suggestion.mainItemLanguage,
175
+ version: suggestion.mainItemVersion,
176
+ },
177
+ }, false);
178
+ editContext?.select?.([suggestion.itemId]);
179
+ }
180
+ // Switch to comments view
181
+ if (editContext?.switchView) {
182
+ editContext.switchView("comments");
183
+ }
184
+ }, className: "w-full", children: "View in Comments Panel" }) }))] }) })] }));
185
+ }
186
+ //# sourceMappingURL=SuggestionDisplayPopover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SuggestionDisplayPopover.js","sourceRoot":"","sources":["../../../src/editor/reviews/SuggestionDisplayPopover.tsx"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export declare const version = "1.0.4040";
2
- export declare const buildDate = "2025-08-12 01:54:07";
1
+ export declare const version = "1.0.4042";
2
+ export declare const buildDate = "2025-08-13 09:49:23";
package/dist/revision.js CHANGED
@@ -1,3 +1,3 @@
1
- export const version = "1.0.4040";
2
- export const buildDate = "2025-08-12 01:54:07";
1
+ export const version = "1.0.4042";
2
+ export const buildDate = "2025-08-13 09:49:23";
3
3
  //# sourceMappingURL=revision.js.map
package/dist/styles.css CHANGED
@@ -769,6 +769,9 @@
769
769
  .min-h-\[70px\] {
770
770
  min-height: 70px;
771
771
  }
772
+ .min-h-\[80px\] {
773
+ min-height: 80px;
774
+ }
772
775
  .min-h-\[100px\] {
773
776
  min-height: 100px;
774
777
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alpaca-editor/core",
3
- "version": "1.0.4040",
3
+ "version": "1.0.4042",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -2,9 +2,13 @@ import * as React from "react";
2
2
 
3
3
  import { cn } from "../../lib/utils";
4
4
 
5
- function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
5
+ const Textarea = React.forwardRef<
6
+ HTMLTextAreaElement,
7
+ React.ComponentProps<"textarea">
8
+ >(({ className, ...props }, ref) => {
6
9
  return (
7
10
  <textarea
11
+ ref={ref}
8
12
  data-slot="textarea"
9
13
  className={cn(
10
14
  "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
@@ -13,6 +17,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
13
17
  {...props}
14
18
  />
15
19
  );
16
- }
20
+ });
21
+ Textarea.displayName = "Textarea";
17
22
 
18
23
  export { Textarea };
@@ -25,6 +25,7 @@ import {
25
25
  TriangleAlert,
26
26
  } from "lucide-react";
27
27
  import { AiPromptPopover } from "../ai/AiPromptPopover";
28
+ import { showCommentPopoverAt } from "../reviews/CommentPopover";
28
29
 
29
30
  export type ComponentCommandData = CommandData & {
30
31
  components: Component[];
@@ -72,7 +73,7 @@ export async function getComponentCommands(
72
73
  ) as Component[];
73
74
 
74
75
  const commands = [
75
- await getCreateCommentCommand(components),
76
+ await getCreateCommentCommand(components, editContext),
76
77
  await getInsertCommand(components, editContext),
77
78
  await getDeleteCommand(components, editContext),
78
79
  await getDuplicateCommand(components, editContext),
@@ -413,18 +414,29 @@ function deleteComponents(
413
414
 
414
415
  function getCreateCommentCommand(
415
416
  components: Component[],
417
+ editContext: EditContextType,
416
418
  ): ComponentCommand | null {
417
419
  if (components.length !== 1 || isPlaceholder(components[0])) return null;
418
420
 
419
421
  return {
420
422
  id: "addComment",
421
423
  label: "Add Comment",
422
- icon: <MessageCirclePlus size={14} />,
424
+ icon: <MessageCirclePlus size={14} strokeWidth={1} />,
423
425
  disabled: () => false,
424
426
  visibilityScopes: ["contextMenu"],
425
427
  execute: async (context: CommandContext<any>) => {
426
- context.editContext.addComment();
427
- context.editContext.switchView("comments");
428
+ const ev = context.event as any;
429
+ if (ev?.preventDefault) ev.preventDefault();
430
+ if (ev?.stopPropagation) ev.stopPropagation();
431
+ if (ev?.nativeEvent?.stopImmediatePropagation)
432
+ ev.nativeEvent.stopImmediatePropagation();
433
+ const x = ev?.clientX ?? ev?.pageX ?? window.innerWidth / 2;
434
+ const y = ev?.clientY ?? ev?.pageY ?? window.innerHeight / 2;
435
+ setTimeout(() => {
436
+ requestAnimationFrame(() => {
437
+ showCommentPopoverAt({ x, y }, context.editContext);
438
+ });
439
+ }, 0);
428
440
  },
429
441
  };
430
442
  }
@@ -8,7 +8,7 @@ import { useState } from "react";
8
8
  import { useEffect } from "react";
9
9
  import { classNames } from "primereact/utils";
10
10
  import { CommentIcon } from "../ui/Icons";
11
- import { getComponentById } from "../componentTreeHelper";
11
+ import { CommentDisplayPopover } from "../reviews/CommentDisplayPopover";
12
12
 
13
13
  export function CommentHighlighting({
14
14
  comment,
@@ -20,6 +20,7 @@ export function CommentHighlighting({
20
20
  iframe: HTMLIFrameElement;
21
21
  }) {
22
22
  const editContext = useEditContext();
23
+ const isSelected = comment.id === editContext?.selectedComment?.id;
23
24
  const modifiedFields = useModifiedFieldsContext();
24
25
 
25
26
  const [range, setRange] = useState<number[]>([
@@ -176,22 +177,50 @@ export function CommentHighlighting({
176
177
  }}
177
178
  >
178
179
  {index === 0 && (
179
- <div
180
- className={classNames(
181
- "pointer-events-auto absolute h-5 w-5 cursor-pointer",
182
- rect.top * scale < 18 ? "top-0" : "top-[-18px]",
183
- (rect.left + rect.width) * scale > iframeRect.width - 18
184
- ? "right-0"
185
- : "right-[-18px]",
186
- )}
187
- onClick={() => {
188
- editContext.switchView("comments");
189
- editContext?.select([comment.itemId]);
190
- editContext?.setSelectedComment(comment);
180
+ <CommentDisplayPopover
181
+ comment={comment}
182
+ onCommentUpdated={() => {
183
+ // Reload comments to reflect any changes
184
+ editContext?.loadComments?.();
191
185
  }}
192
186
  >
193
- <CommentIcon className="text-blue-500 hover:text-blue-600" />
194
- </div>
187
+ <div
188
+ className={classNames(
189
+ "pointer-events-auto absolute h-5 w-5 cursor-pointer",
190
+ rect.top * scale < 18 ? "top-0" : "top-[-18px]",
191
+ (rect.left + rect.width) * scale > iframeRect.width - 18
192
+ ? "right-0"
193
+ : "right-[-18px]",
194
+ )}
195
+ onClick={() => {
196
+ // Set this comment as selected
197
+ editContext?.setSelectedComment(comment);
198
+ editContext?.setScrollIntoView(comment.itemId);
199
+ editContext?.select([comment.itemId]);
200
+ if (comment.fieldId) {
201
+ editContext?.setFocusedField(
202
+ {
203
+ fieldId: comment.fieldId,
204
+ item: {
205
+ id: comment.itemId,
206
+ language: comment.language,
207
+ version: comment.version,
208
+ },
209
+ },
210
+ false,
211
+ );
212
+ }
213
+ }}
214
+ >
215
+ <CommentIcon
216
+ className={
217
+ isSelected
218
+ ? "text-red-700 hover:text-red-800"
219
+ : "text-blue-500 hover:text-blue-600"
220
+ }
221
+ />
222
+ </div>
223
+ </CommentDisplayPopover>
195
224
  )}
196
225
  {isTextRange && (
197
226
  <div
@@ -199,9 +228,11 @@ export function CommentHighlighting({
199
228
  style={{
200
229
  width: "100%",
201
230
  height: "100%",
202
- backgroundColor: comment.isResolved
203
- ? "rgba(0, 255, 0, 0.45)"
204
- : "rgba(255, 0, 0, 0.45)",
231
+ backgroundColor: isSelected
232
+ ? "rgba(255, 0, 0, 1)" // Darker red for selected
233
+ : comment.isResolved
234
+ ? "rgba(0, 255, 0, 0.45)" // Green for resolved
235
+ : "rgba(255, 0, 0, 0.45)", // Red for unresolved
205
236
  }}
206
237
  ></div>
207
238
  )}