@alpaca-editor/core 1.0.4184 → 1.0.4186

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 (77) hide show
  1. package/dist/agents-view/AgentCard.d.ts +12 -0
  2. package/dist/agents-view/AgentCard.js +30 -0
  3. package/dist/agents-view/AgentCard.js.map +1 -0
  4. package/dist/agents-view/AgentsView.d.ts +2 -2
  5. package/dist/agents-view/AgentsView.js +102 -85
  6. package/dist/agents-view/AgentsView.js.map +1 -1
  7. package/dist/agents-view/ProfileAgentsGroup.d.ts +17 -0
  8. package/dist/agents-view/ProfileAgentsGroup.js +13 -0
  9. package/dist/agents-view/ProfileAgentsGroup.js.map +1 -0
  10. package/dist/components/ui/popover.d.ts +3 -3
  11. package/dist/config/config.js +8 -1
  12. package/dist/config/config.js.map +1 -1
  13. package/dist/editor/ConfirmationDialog.js.map +1 -1
  14. package/dist/editor/ImageEditButton.js.map +1 -1
  15. package/dist/editor/ai/AgentProfilesOverview.js +1 -1
  16. package/dist/editor/ai/AgentProfilesOverview.js.map +1 -1
  17. package/dist/editor/ai/Agents.js +6 -2
  18. package/dist/editor/ai/Agents.js.map +1 -1
  19. package/dist/editor/client/EditorShell.js +17 -0
  20. package/dist/editor/client/EditorShell.js.map +1 -1
  21. package/dist/editor/client/ui/EditorChrome.js +1 -0
  22. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  23. package/dist/editor/commands/componentCommands.js +19 -1
  24. package/dist/editor/commands/componentCommands.js.map +1 -1
  25. package/dist/editor/field-types/DateTimeFieldEditor.js +1 -1
  26. package/dist/editor/field-types/DateTimeFieldEditor.js.map +1 -1
  27. package/dist/editor/page-editor-chrome/FrameMenu.js +7 -3
  28. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  29. package/dist/editor/page-viewer/PageViewerFrame.js +7 -28
  30. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  31. package/dist/editor/reviews/Comment.js +56 -4
  32. package/dist/editor/reviews/Comment.js.map +1 -1
  33. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  34. package/dist/editor/reviews/CommentPopover.js +1 -1
  35. package/dist/editor/reviews/CommentPopover.js.map +1 -1
  36. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  37. package/dist/editor/services/agentService.d.ts +19 -0
  38. package/dist/editor/services/agentService.js +39 -0
  39. package/dist/editor/services/agentService.js.map +1 -1
  40. package/dist/editor/services/aiService.d.ts +2 -0
  41. package/dist/editor/services/aiService.js.map +1 -1
  42. package/dist/editor/services/serviceHelper.d.ts +1 -0
  43. package/dist/editor/services/serviceHelper.js +58 -4
  44. package/dist/editor/services/serviceHelper.js.map +1 -1
  45. package/dist/editor/views/ParheliaView.d.ts +5 -0
  46. package/dist/editor/views/ParheliaView.js +136 -0
  47. package/dist/editor/views/ParheliaView.js.map +1 -0
  48. package/dist/revision.d.ts +2 -2
  49. package/dist/revision.js +2 -2
  50. package/dist/styles.css +8 -6
  51. package/dist/tour/default-tour.js +24 -9
  52. package/dist/tour/default-tour.js.map +1 -1
  53. package/package.json +12 -8
  54. package/src/agents-view/AgentCard.tsx +162 -0
  55. package/src/agents-view/AgentsView.tsx +218 -253
  56. package/src/agents-view/ProfileAgentsGroup.tsx +123 -0
  57. package/src/config/config.tsx +10 -1
  58. package/src/editor/ConfirmationDialog.tsx +1 -1
  59. package/src/editor/ImageEditButton.tsx +4 -2
  60. package/src/editor/ai/AgentProfilesOverview.tsx +1 -2
  61. package/src/editor/ai/Agents.tsx +7 -2
  62. package/src/editor/client/EditorShell.tsx +18 -0
  63. package/src/editor/client/ui/EditorChrome.tsx +1 -0
  64. package/src/editor/commands/componentCommands.tsx +19 -1
  65. package/src/editor/field-types/DateTimeFieldEditor.tsx +5 -4
  66. package/src/editor/page-editor-chrome/FrameMenu.tsx +9 -3
  67. package/src/editor/page-viewer/PageViewerFrame.tsx +7 -36
  68. package/src/editor/reviews/Comment.tsx +85 -4
  69. package/src/editor/reviews/CommentDisplayPopover.tsx +2 -2
  70. package/src/editor/reviews/CommentPopover.tsx +2 -2
  71. package/src/editor/reviews/SuggestionDisplayPopover.tsx +1 -1
  72. package/src/editor/services/agentService.ts +77 -0
  73. package/src/editor/services/aiService.ts +4 -0
  74. package/src/editor/services/serviceHelper.ts +92 -28
  75. package/src/editor/views/ParheliaView.tsx +207 -0
  76. package/src/revision.ts +2 -2
  77. package/src/tour/default-tour.tsx +63 -48
@@ -0,0 +1,123 @@
1
+ import React from "react";
2
+ import {
3
+ Agent,
4
+ ProfileAgentsGroup as ProfileGroup,
5
+ } from "../editor/services/agentService";
6
+ import { ChevronDown, ChevronUp } from "lucide-react";
7
+ import { AgentCard } from "./AgentCard";
8
+ import { SecretAgentIcon } from "../editor/ui/Icons";
9
+
10
+ interface ProfileAgentsGroupProps {
11
+ profileGroup: ProfileGroup;
12
+ isExpanded: boolean;
13
+ isLoadingMore: boolean;
14
+ hasMore: boolean;
15
+ onToggle: () => void;
16
+ onLoadMore: () => void;
17
+ onAgentClick: (agent: Agent) => void;
18
+ onResumeAgent: (agent: Agent) => void;
19
+ onCloseAgent: (agentId: string, agentName: string) => void;
20
+ onDeleteAgent: (
21
+ agentId: string,
22
+ agentName: string,
23
+ event: React.MouseEvent,
24
+ ) => void;
25
+ formatDateTime: (dateString: string) => string;
26
+ }
27
+
28
+ export function ProfileAgentsGroup({
29
+ profileGroup,
30
+ isExpanded,
31
+ isLoadingMore,
32
+ hasMore,
33
+ onToggle,
34
+ onLoadMore,
35
+ onAgentClick,
36
+ onResumeAgent,
37
+ onCloseAgent,
38
+ onDeleteAgent,
39
+ formatDateTime,
40
+ }: ProfileAgentsGroupProps) {
41
+ return (
42
+ <div className="rounded-lg border border-gray-200 bg-white shadow-sm">
43
+ {/* Group Header */}
44
+ <button
45
+ onClick={onToggle}
46
+ className="flex w-full items-center gap-3 bg-gray-50 p-3 text-left transition-colors hover:bg-gray-100"
47
+ >
48
+ {/* Profile Icon */}
49
+ <div className="flex-shrink-0">
50
+ {profileGroup.profileSvgIcon ? (
51
+ <div
52
+ className="flex h-6 w-6 items-center justify-center text-gray-500 [&>svg]:h-full [&>svg]:w-full"
53
+ dangerouslySetInnerHTML={{
54
+ __html: profileGroup.profileSvgIcon,
55
+ }}
56
+ />
57
+ ) : (
58
+ <SecretAgentIcon
59
+ size={24}
60
+ strokeWidth={1}
61
+ className="text-gray-500"
62
+ />
63
+ )}
64
+ </div>
65
+
66
+ {/* Profile Name */}
67
+ <div className="min-w-0 flex-1">
68
+ <h3 className="truncate font-medium text-gray-900">
69
+ {profileGroup.profileName}
70
+ </h3>
71
+ </div>
72
+
73
+ {/* Closed Agents Count Badge */}
74
+ <div className="flex-shrink-0 rounded-full bg-gray-200 px-3 py-1 text-xs font-medium text-gray-700">
75
+ {profileGroup.totalClosedCount} closed agent
76
+ {profileGroup.totalClosedCount !== 1 ? "s" : ""}
77
+ </div>
78
+
79
+ {/* Chevron Icon */}
80
+ <div className="flex-shrink-0 text-gray-500">
81
+ {isExpanded ? (
82
+ <ChevronUp className="size-5" strokeWidth={1} />
83
+ ) : (
84
+ <ChevronDown className="size-5" strokeWidth={1} />
85
+ )}
86
+ </div>
87
+ </button>
88
+
89
+ {/* Expandable Content */}
90
+ {isExpanded && (
91
+ <div className="space-y-2 p-3">
92
+ {profileGroup.agents.map((agent) => (
93
+ <AgentCard
94
+ key={agent.id}
95
+ agent={agent}
96
+ onClick={onAgentClick}
97
+ onResume={onResumeAgent}
98
+ onClose={onCloseAgent}
99
+ onDelete={onDeleteAgent}
100
+ formatDateTime={formatDateTime}
101
+ />
102
+ ))}
103
+
104
+ {/* Load More Button */}
105
+ {hasMore && (
106
+ <div className="flex justify-center pt-2">
107
+ <button
108
+ onClick={(e) => {
109
+ e.stopPropagation();
110
+ onLoadMore();
111
+ }}
112
+ disabled={isLoadingMore}
113
+ className="rounded-lg border border-gray-300 bg-white px-6 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50"
114
+ >
115
+ {isLoadingMore ? "Loading..." : "Load More"}
116
+ </button>
117
+ </div>
118
+ )}
119
+ </div>
120
+ )}
121
+ </div>
122
+ );
123
+ }
@@ -71,6 +71,8 @@ import { loadInsertOptions } from "../editor/services/editService";
71
71
  import { getDefaultTourSteps } from "../tour/default-tour";
72
72
  import { Translation } from "../editor/sidebar/Translations";
73
73
  import { EditView } from "../editor/views/EditView";
74
+ import { ParheliaView } from "../editor/views/ParheliaView";
75
+ import { AnimatedSunIcon } from "../editor/ui/Icons";
74
76
  import { InsertMenuTemplate } from "../editor/context-menu/InsertMenu";
75
77
 
76
78
  import { Status } from "../editor/control-center/Status";
@@ -733,8 +735,15 @@ export const getConfiguration = (): EditorConfiguration => {
733
735
  expandTreeNode: "bafb88a1-506a-4671-b47b-1947730d25f6",
734
736
  },
735
737
  },
736
- defaultPinnedViews: ["ai", "content-editor"],
738
+ defaultPinnedViews: ["parhelia", "ai", "content-editor"],
737
739
  views: [
740
+ // {
741
+ // name: "parhelia",
742
+ // title: "Parhelia",
743
+ // icon: <AnimatedSunIcon strokeWidth={1} />,
744
+ // defaultCenterPanelView: <ParheliaView />,
745
+ // hideViewSelector: false,
746
+ // },
738
747
  {
739
748
  name: "splash-screen",
740
749
  title: "Splash Screen",
@@ -68,7 +68,7 @@ const ConfirmationDialog = forwardRef<
68
68
  >
69
69
  <DialogHeader className="space-y-0 px-6 pt-3 pb-2">
70
70
  <DialogTitle className="text-lg font-semibold">
71
- {props?.header || "Confirmation"}
71
+ {props?.header || ("Confirmation" as any)}
72
72
  </DialogTitle>
73
73
  </DialogHeader>
74
74
  <div className="px-6 py-4">
@@ -85,7 +85,9 @@ export function ImageEditButton({
85
85
  return (
86
86
  <DropdownMenu open={showMenu} onOpenChange={setShowMenu}>
87
87
  {triggerButton ? (
88
- <DropdownMenuTrigger asChild>{triggerButton}</DropdownMenuTrigger>
88
+ <DropdownMenuTrigger asChild>
89
+ {triggerButton as any}
90
+ </DropdownMenuTrigger>
89
91
  ) : (
90
92
  defaultTriggerButton
91
93
  )}
@@ -99,7 +101,7 @@ export function ImageEditButton({
99
101
  setShowMenu(false);
100
102
  }}
101
103
  >
102
- {action.icon}
104
+ {action.icon as any}
103
105
  {action.label}
104
106
  </DropdownMenuItem>
105
107
  ))}
@@ -42,7 +42,7 @@ export const AgentProfilesOverview: React.FC<AgentProfilesOverviewProps> = ({
42
42
  <button
43
43
  key={profile.id}
44
44
  onClick={() => onSelectProfile(profile.id)}
45
- className="group flex flex-col gap-3 rounded-lg border border-gray-200 bg-white p-4 text-left transition-all hover:border-blue-300 hover:shadow-md focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 focus:outline-none"
45
+ className="group flex flex-col gap-3 rounded-lg border border-gray-200 bg-white p-4 text-left transition-all hover:border-blue-300 hover:shadow-md"
46
46
  data-testid="agent-profile-card"
47
47
  data-profile-id={profile.id}
48
48
  >
@@ -78,4 +78,3 @@ export const AgentProfilesOverview: React.FC<AgentProfilesOverviewProps> = ({
78
78
  </div>
79
79
  );
80
80
  };
81
-
@@ -289,7 +289,7 @@ export const Agents = React.memo(function Agents({
289
289
  }
290
290
  }
291
291
  if (message.type === "agent:run:start") {
292
- const { agentId, agentName } = message.payload;
292
+ const { agentId, agentName, autoSelect } = message.payload;
293
293
 
294
294
  if (!agentId || !agentName) {
295
295
  console.warn(
@@ -320,7 +320,7 @@ export const Agents = React.memo(function Agents({
320
320
  };
321
321
  return updatedAgents;
322
322
  } else {
323
- // Add new agent to the array (but don't activate it - user may be in a different browser instance)
323
+ // Add new agent to the array
324
324
  const newAgent: Agent = {
325
325
  id: agentId,
326
326
  name: agentName,
@@ -332,6 +332,11 @@ export const Agents = React.memo(function Agents({
332
332
  return [...prevAgents, newAgent];
333
333
  }
334
334
  });
335
+
336
+ // Auto-switch to the spawned agent if requested
337
+ if (autoSelect === true) {
338
+ setActiveAgentIdWithStorage(agentId);
339
+ }
335
340
  } else if (message.type === "agent:info:updated") {
336
341
  const { agentId, agentName, agentDescription } =
337
342
  message.payload || {};
@@ -1518,6 +1518,24 @@ export function EditorShell({
1518
1518
  switchView();
1519
1519
  };
1520
1520
 
1521
+ // Listen for switch-view commands from agents via websocket
1522
+ useEffect(() => {
1523
+ const handleSwitchView = (message: { type: string; payload: any }) => {
1524
+ if (message.type === "switch-view" && message.payload?.viewName) {
1525
+ try {
1526
+ switchView(message.payload.viewName);
1527
+ } catch (error) {
1528
+ console.error("Error switching view:", error);
1529
+ }
1530
+ }
1531
+ };
1532
+
1533
+ const unsubscribe = addSocketMessageListener(handleSwitchView);
1534
+ return () => {
1535
+ unsubscribe();
1536
+ };
1537
+ }, [addSocketMessageListener, switchView]);
1538
+
1521
1539
  const [dialog, setDialog] = useState<ReactNode>(null);
1522
1540
 
1523
1541
  const openDialog: OpenDialog = useCallback(
@@ -50,6 +50,7 @@ export function EditorChrome(props: {
50
50
  showAgentsPanel &&
51
51
  !editContext.currentWizardId &&
52
52
  ![
53
+ "parhelia",
53
54
  "splash-screen",
54
55
  "open-page",
55
56
  "new-page",
@@ -95,6 +95,9 @@ function getInsertCommand(
95
95
  if (!item) return null;
96
96
  if (!item.placeholders || item.placeholders.length === 0) return null;
97
97
  if (editContext.mode === "suggestions") return null;
98
+ // Don't show insert for layout components when showLayoutComponents is disabled
99
+ if (item.layoutId && !editContext.showLayoutComponents) return null;
100
+
98
101
  return {
99
102
  id: "insert",
100
103
  icon: <Plus size={defaultIconSize} />,
@@ -114,6 +117,10 @@ function getDuplicateCommand(
114
117
  if (components.length !== 1) return null;
115
118
  if (editContext.mode === "suggestions") return null;
116
119
 
120
+ const component = components[0];
121
+ // Don't show duplicate for layout components when showLayoutComponents is disabled
122
+ if (component.layoutId && !editContext.showLayoutComponents) return null;
123
+
117
124
  return {
118
125
  id: "duplicate",
119
126
  icon: <Copy size={defaultIconSize} />,
@@ -205,6 +212,9 @@ async function getDesignCommand(
205
212
  if (components.length !== 1 || isPlaceholder(components[0])) return null;
206
213
  const component = components[0];
207
214
  if (!component || !component.datasourceItem) return null;
215
+ // Don't show design for layout components when showLayoutComponents is disabled
216
+ if (component.layoutId && !editContext.showLayoutComponents) return null;
217
+
208
218
  const item = await editContext.itemsRepository.getItem(
209
219
  component.datasourceItem,
210
220
  );
@@ -243,6 +253,9 @@ async function getLinkToMasterCommand(
243
253
  ): Promise<ComponentCommand | null> {
244
254
  if (!components.length) return null;
245
255
  if (editContext.page?.item?.masterLanguages?.length === 0) return null;
256
+ // Don't show for layout components when showLayoutComponents is disabled
257
+ if (components.some((c) => c.layoutId && !editContext.showLayoutComponents))
258
+ return null;
246
259
 
247
260
  return getCheckboxCommand(
248
261
  components,
@@ -304,6 +317,9 @@ async function getInheritChildrenFromMasterCommand(
304
317
  if (editContext.page?.item?.masterLanguages?.length === 0) return null;
305
318
  if (components.find((x) => !x.placeholders || x.placeholders.length === 0))
306
319
  return null;
320
+ // Don't show for layout components when showLayoutComponents is disabled
321
+ if (components.some((c) => c.layoutId && !editContext.showLayoutComponents))
322
+ return null;
307
323
 
308
324
  return getCheckboxCommand(
309
325
  components,
@@ -345,7 +361,9 @@ function getDeleteCommand(
345
361
  ((!isPlaceholder(c) && !c.layoutId) ||
346
362
  c.layoutId === editContext.page?.item.id) &&
347
363
  !isLocked(c, editContext) &&
348
- editContext.page?.item.canWriteItem,
364
+ editContext.page?.item.canWriteItem &&
365
+ // When showLayoutComponents is disabled, layout components should not be editable
366
+ (editContext.showLayoutComponents || !c.layoutId),
349
367
  );
350
368
 
351
369
  if (applicableComponents.length === 0) return null;
@@ -177,9 +177,10 @@ export function DateTimeFieldEditor({
177
177
  <PopoverTrigger asChild>
178
178
  <Button
179
179
  variant="outline"
180
+ size="sm"
180
181
  data-empty={!date}
181
182
  className={cn(
182
- "bg-gray-5 justify-start p-1 text-left text-xs font-normal",
183
+ "bg-gray-5 justify-start px-2 text-left text-xs font-normal",
183
184
  !date && "text-muted-foreground",
184
185
  )}
185
186
  disabled={readOnly}
@@ -205,7 +206,7 @@ export function DateTimeFieldEditor({
205
206
 
206
207
  <div
207
208
  className={cn(
208
- "bg-gray-5 flex items-center justify-start gap-1 px-2 text-left text-xs font-normal",
209
+ "bg-gray-5 flex h-8 items-center justify-start gap-1 rounded-sm px-2 text-left text-xs font-normal",
209
210
  !date && "text-muted-foreground",
210
211
  )}
211
212
  >
@@ -216,7 +217,7 @@ export function DateTimeFieldEditor({
216
217
  <PopoverTrigger asChild>
217
218
  <Button
218
219
  variant="ghost"
219
- className="bg-gray-5 h-8 w-12 p-1 text-xs"
220
+ className="bg-gray-5 h-8 w-12 p-0 text-xs"
220
221
  disabled={readOnly || !date}
221
222
  >
222
223
  {hours.toString().padStart(2, "0")}
@@ -249,7 +250,7 @@ export function DateTimeFieldEditor({
249
250
  <PopoverTrigger asChild>
250
251
  <Button
251
252
  variant="ghost"
252
- className="bg-gray-5 h-7.5 w-12 text-xs"
253
+ className="bg-gray-5 h-8 w-12 p-0 text-xs"
253
254
  disabled={readOnly || !date}
254
255
  >
255
256
  {minutes.toString().padStart(2, "0")}
@@ -186,7 +186,10 @@ export function FrameMenu({
186
186
  loadCommands();
187
187
  }, [component]);
188
188
 
189
- const isDraggable = true; // component.canBeMoved && !component.layoutId;
189
+ // Determine if draggable - layout components should not be draggable when showLayoutComponents is disabled
190
+ const isLayoutReadonlyCheck =
191
+ component.layoutId && !editContext.showLayoutComponents;
192
+ const isDraggable = component.canBeMoved && !isLayoutReadonlyCheck;
190
193
 
191
194
  const commandButtons = commands
192
195
  .filter(
@@ -257,11 +260,14 @@ export function FrameMenu({
257
260
  }
258
261
 
259
262
  const isShared = component.isShared;
260
- const isReadonly = editContext.mode === "preview" || compareView;
261
263
  const isLayout = component.layoutId;
264
+ // Layout components should be read-only when showLayoutComponents is disabled
265
+ const isLayoutReadonly = isLayout && !editContext.showLayoutComponents;
266
+ const isReadonly =
267
+ editContext.mode === "preview" || compareView || isLayoutReadonly;
262
268
 
263
269
  function getColor() {
264
- if (isReadonly) return "readonly";
270
+ if (isReadonly && !isLayoutReadonly) return "readonly";
265
271
  if (editContext?.mode === "suggestions") return "suggestions";
266
272
  if (isLayout) return "layout";
267
273
  if (isShared) return "shared";
@@ -487,18 +487,8 @@ export function PageViewerFrame({
487
487
 
488
488
  let componentId = findNearestEditableComponentId(target as HTMLElement);
489
489
 
490
- // If layout components are hidden, block selection on layout components
491
- if (
492
- componentId &&
493
- editContextRef.current &&
494
- editContextRef.current.page &&
495
- editContextRef.current.showLayoutComponents === false
496
- ) {
497
- const comp = getComponentById(componentId, editContextRef.current.page);
498
- if (comp?.layoutId) {
499
- componentId = undefined;
500
- }
501
- }
490
+ // Layout components can still be selected even when showLayoutComponents is false
491
+ // They will be displayed in read-only mode
502
492
  const currentOverlayName = editContextRef.current?.currentOverlay;
503
493
  const isGeneratorOverlay = !!(
504
494
  currentOverlayName &&
@@ -530,12 +520,7 @@ export function PageViewerFrame({
530
520
  for (const el of all) {
531
521
  const id = el.getAttribute("data-component-id");
532
522
  if (!id) continue;
533
- // Skip layout components when hidden
534
- if (
535
- editContextRef.current?.showLayoutComponents === false &&
536
- getComponentById(id, page)?.layoutId
537
- )
538
- continue;
523
+ // Layout components are now selectable even when showLayoutComponents is false
539
524
  orderedIds.push(id);
540
525
  }
541
526
  }
@@ -688,17 +673,8 @@ export function PageViewerFrame({
688
673
 
689
674
  let componentId = findNearestEditableComponentId(target as HTMLElement);
690
675
 
691
- if (
692
- componentId &&
693
- editContextRef.current &&
694
- editContextRef.current.page &&
695
- editContextRef.current.showLayoutComponents === false
696
- ) {
697
- const comp = getComponentById(componentId, editContextRef.current.page);
698
- if (comp?.layoutId) {
699
- componentId = undefined;
700
- }
701
- }
676
+ // Layout components can now be right-clicked even when showLayoutComponents is false
677
+ // Context menu will show limited/read-only actions
702
678
 
703
679
  if (componentId) {
704
680
  // Only change selection if right-clicking on a component that's not in the current selection
@@ -721,13 +697,8 @@ export function PageViewerFrame({
721
697
  .map((id) => getComponentById(id, pageViewContextRef.current!.page!))
722
698
  .filter((x) => x) as Component[];
723
699
 
724
- // If layout components are hidden, do not show context menu for them
725
- if (
726
- editContextRef.current?.showLayoutComponents === false &&
727
- selectedComponents.some((c) => c.layoutId)
728
- ) {
729
- return;
730
- }
700
+ // Context menu will now show for layout components even when showLayoutComponents is false
701
+ // Commands will be appropriately filtered/disabled
731
702
 
732
703
  const iframeRect = iframe.getBoundingClientRect();
733
704
  const adjustedEvent = new MouseEvent("contextmenu", {
@@ -12,6 +12,7 @@ import { openAiAgentForComment } from "./commentAi";
12
12
  import { useDebouncedCallback } from "use-debounce";
13
13
  import { CommentView } from "./CommentView";
14
14
  import { CommentEditor } from "./CommentEditor";
15
+ import { toast } from "sonner";
15
16
 
16
17
  export function Comment({
17
18
  comment,
@@ -71,8 +72,28 @@ export function Comment({
71
72
  )?.rawValue;
72
73
  }
73
74
 
74
- await createOrUpdateComment(comment);
75
+ const result = await createOrUpdateComment(comment);
76
+
77
+ if (result.type === "error" || result.type === "unauthorized") {
78
+ console.error("Failed to save comment:", result);
79
+ if (result.rawDetails) {
80
+ console.error(
81
+ "Full error details (including stack trace):\n",
82
+ result.rawDetails,
83
+ );
84
+ }
85
+ toast.error(
86
+ result.details || result.summary || "Failed to save comment",
87
+ );
88
+ return;
89
+ }
90
+
75
91
  setIsEditing(false);
92
+ } catch (error) {
93
+ console.error("Failed to save comment:", error);
94
+ toast.error(
95
+ error instanceof Error ? error.message : "Failed to save comment",
96
+ );
76
97
  } finally {
77
98
  setIsSaving(false);
78
99
  }
@@ -91,15 +112,75 @@ export function Comment({
91
112
  };
92
113
 
93
114
  const handleDelete = async () => {
94
- await deleteComment(comment);
115
+ try {
116
+ const result = await deleteComment(comment);
117
+
118
+ if (result.type === "error" || result.type === "unauthorized") {
119
+ console.error("Failed to delete comment:", result);
120
+ if (result.rawDetails) {
121
+ console.error(
122
+ "Full error details (including stack trace):\n",
123
+ result.rawDetails,
124
+ );
125
+ }
126
+ toast.error(
127
+ result.details || result.summary || "Failed to delete comment",
128
+ );
129
+ }
130
+ } catch (error) {
131
+ console.error("Failed to delete comment:", error);
132
+ toast.error(
133
+ error instanceof Error ? error.message : "Failed to delete comment",
134
+ );
135
+ }
95
136
  };
96
137
 
97
138
  const handleResolve = async () => {
98
- await resolveComment(comment);
139
+ try {
140
+ const result = await resolveComment(comment);
141
+
142
+ if (result.type === "error" || result.type === "unauthorized") {
143
+ console.error("Failed to resolve comment:", result);
144
+ if (result.rawDetails) {
145
+ console.error(
146
+ "Full error details (including stack trace):\n",
147
+ result.rawDetails,
148
+ );
149
+ }
150
+ toast.error(
151
+ result.details || result.summary || "Failed to resolve comment",
152
+ );
153
+ }
154
+ } catch (error) {
155
+ console.error("Failed to resolve comment:", error);
156
+ toast.error(
157
+ error instanceof Error ? error.message : "Failed to resolve comment",
158
+ );
159
+ }
99
160
  };
100
161
 
101
162
  const handleUnresolve = async () => {
102
- await unresolveComment(comment);
163
+ try {
164
+ const result = await unresolveComment(comment);
165
+
166
+ if (result.type === "error" || result.type === "unauthorized") {
167
+ console.error("Failed to unresolve comment:", result);
168
+ if (result.rawDetails) {
169
+ console.error(
170
+ "Full error details (including stack trace):\n",
171
+ result.rawDetails,
172
+ );
173
+ }
174
+ toast.error(
175
+ result.details || result.summary || "Failed to unresolve comment",
176
+ );
177
+ }
178
+ } catch (error) {
179
+ console.error("Failed to unresolve comment:", error);
180
+ toast.error(
181
+ error instanceof Error ? error.message : "Failed to unresolve comment",
182
+ );
183
+ }
103
184
  };
104
185
 
105
186
  const handleAiAction = async () =>
@@ -109,9 +109,9 @@ export function CommentDisplayPopover({
109
109
  }}
110
110
  enableIframeClickDetection={true}
111
111
  >
112
- <PopoverTrigger asChild>{children}</PopoverTrigger>
112
+ <PopoverTrigger asChild>{children as any}</PopoverTrigger>
113
113
  <PopoverContent
114
- ref={contentRef}
114
+ ref={contentRef as any}
115
115
  tabIndex={-1}
116
116
  className="w-96 p-4"
117
117
  side="bottom"
@@ -163,11 +163,11 @@ export function CommentPopover({
163
163
  )}
164
164
  {!position && (
165
165
  <PopoverTrigger asChild>
166
- {children ?? <MessageCirclePlus size={16} strokeWidth={1} />}
166
+ {(children ?? <MessageCirclePlus size={16} strokeWidth={1} />) as any}
167
167
  </PopoverTrigger>
168
168
  )}
169
169
  <PopoverContent
170
- ref={contentRef}
170
+ ref={contentRef as any}
171
171
  tabIndex={-1}
172
172
  className="w-96 p-4"
173
173
  data-testid="add-comment-popover"
@@ -265,7 +265,7 @@ export function SuggestionDisplayPopover({
265
265
  }}
266
266
  enableIframeClickDetection={true}
267
267
  >
268
- <PopoverTrigger asChild>{children}</PopoverTrigger>
268
+ <PopoverTrigger asChild>{children as any}</PopoverTrigger>
269
269
  <PopoverContent className="w-96 p-4" side="bottom" align="start">
270
270
  <div className="space-y-3">
271
271
  {/* Header */}