@alpaca-editor/core 1.0.3938 → 1.0.3939

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 (75) hide show
  1. package/dist/editor/ContentTree.js +12 -8
  2. package/dist/editor/ContentTree.js.map +1 -1
  3. package/dist/editor/ContextMenu.d.ts +1 -1
  4. package/dist/editor/ContextMenu.js +17 -3
  5. package/dist/editor/ContextMenu.js.map +1 -1
  6. package/dist/editor/FieldActionsOverlay.d.ts +17 -0
  7. package/dist/editor/FieldActionsOverlay.js +148 -0
  8. package/dist/editor/FieldActionsOverlay.js.map +1 -0
  9. package/dist/editor/FieldHistory.d.ts +2 -1
  10. package/dist/editor/FieldHistory.js +11 -8
  11. package/dist/editor/FieldHistory.js.map +1 -1
  12. package/dist/editor/FieldListField.js +14 -17
  13. package/dist/editor/FieldListField.js.map +1 -1
  14. package/dist/editor/PictureEditor.js +28 -2
  15. package/dist/editor/PictureEditor.js.map +1 -1
  16. package/dist/editor/Titlebar.js +18 -9
  17. package/dist/editor/Titlebar.js.map +1 -1
  18. package/dist/editor/ai/AiTerminal.js +27 -41
  19. package/dist/editor/ai/AiTerminal.js.map +1 -1
  20. package/dist/editor/client/EditorClient.js +48 -18
  21. package/dist/editor/client/EditorClient.js.map +1 -1
  22. package/dist/editor/client/editContext.d.ts +1 -1
  23. package/dist/editor/client/editContext.js.map +1 -1
  24. package/dist/editor/client/itemsRepository.js +126 -90
  25. package/dist/editor/client/itemsRepository.js.map +1 -1
  26. package/dist/editor/menubar/BrowseHistory.js +3 -4
  27. package/dist/editor/menubar/BrowseHistory.js.map +1 -1
  28. package/dist/editor/menubar/PageSelector.js +37 -9
  29. package/dist/editor/menubar/PageSelector.js.map +1 -1
  30. package/dist/editor/page-editor-chrome/FieldActionIndicator.js +1 -1
  31. package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -1
  32. package/dist/editor/page-viewer/PageViewerFrame.js +98 -2
  33. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  34. package/dist/editor/pageModel.d.ts +14 -0
  35. package/dist/editor/reviews/Comment.js +3 -2
  36. package/dist/editor/reviews/Comment.js.map +1 -1
  37. package/dist/editor/services/editService.d.ts +1 -1
  38. package/dist/editor/services/editService.js +2 -1
  39. package/dist/editor/services/editService.js.map +1 -1
  40. package/dist/editor/ui/Icons.js +1 -1
  41. package/dist/editor/ui/Icons.js.map +1 -1
  42. package/dist/editor/ui/ItemList.d.ts +16 -0
  43. package/dist/editor/ui/ItemList.js +19 -0
  44. package/dist/editor/ui/ItemList.js.map +1 -0
  45. package/dist/editor/ui/ItemSearch.js +2 -12
  46. package/dist/editor/ui/ItemSearch.js.map +1 -1
  47. package/dist/editor/ui/SimpleTabs.js +1 -1
  48. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  49. package/dist/revision.d.ts +2 -2
  50. package/dist/revision.js +2 -2
  51. package/dist/styles.css +3 -8
  52. package/package.json +1 -1
  53. package/src/editor/ContentTree.tsx +15 -12
  54. package/src/editor/ContextMenu.tsx +20 -2
  55. package/src/editor/FieldActionsOverlay.tsx +307 -0
  56. package/src/editor/FieldHistory.tsx +9 -8
  57. package/src/editor/FieldListField.tsx +29 -29
  58. package/src/editor/PictureEditor.tsx +66 -1
  59. package/src/editor/Titlebar.tsx +22 -11
  60. package/src/editor/ai/AiTerminal.tsx +42 -53
  61. package/src/editor/client/EditorClient.tsx +62 -18
  62. package/src/editor/client/editContext.ts +5 -1
  63. package/src/editor/client/itemsRepository.ts +151 -115
  64. package/src/editor/menubar/BrowseHistory.tsx +7 -16
  65. package/src/editor/menubar/PageSelector.tsx +91 -66
  66. package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +1 -1
  67. package/src/editor/page-viewer/PageViewerFrame.tsx +143 -0
  68. package/src/editor/pageModel.ts +12 -0
  69. package/src/editor/reviews/Comment.tsx +5 -6
  70. package/src/editor/services/editService.ts +2 -0
  71. package/src/editor/ui/Icons.tsx +1 -0
  72. package/src/editor/ui/ItemList.tsx +76 -0
  73. package/src/editor/ui/ItemSearch.tsx +9 -46
  74. package/src/editor/ui/SimpleTabs.tsx +1 -1
  75. package/src/revision.ts +2 -2
@@ -23,6 +23,11 @@ import { showComponentContextMenu } from "../ContextMenu";
23
23
  import { loadFieldButtons } from "../services/editService";
24
24
  import { usePathname } from "next/navigation";
25
25
  import { EditorWarnings } from "../EditorWarnings";
26
+ import {
27
+ FieldActionsOverlay,
28
+ FieldActionsOverlayRef,
29
+ } from "../FieldActionsOverlay";
30
+ import { FieldButton, FieldDescriptor } from "../pageModel";
26
31
 
27
32
  import { Component, ItemDescriptor } from "../pageModel";
28
33
  import { NoLayout } from "../page-editor-chrome/NoLayout";
@@ -61,6 +66,29 @@ export function PageViewerFrame({
61
66
  const [showSpinner, setShowSpinner] = useState(false);
62
67
  const [scroll, setScroll] = useState(0);
63
68
  const [showMiniMap, setShowMiniMap] = useState(false);
69
+ const fieldActionsOverlay = useRef<FieldActionsOverlayRef>(null);
70
+ const [contextMenuFieldButtons, setContextMenuFieldButtons] = useState<
71
+ FieldButton[]
72
+ >([]);
73
+ const [contextMenuField, setContextMenuField] = useState<
74
+ FieldDescriptor | undefined
75
+ >();
76
+ const [contextMenuPosition, setContextMenuPosition] = useState<
77
+ { x: number; y: number } | undefined
78
+ >();
79
+ const [preSelectedAction, setPreSelectedAction] = useState<
80
+ FieldButton | undefined
81
+ >();
82
+
83
+ // Clear preSelectedAction when overlay is closed
84
+ useEffect(() => {
85
+ if (
86
+ editContext?.currentOverlay !==
87
+ `${contextMenuField?.fieldId || ""}_generators`
88
+ ) {
89
+ setPreSelectedAction(undefined);
90
+ }
91
+ }, [editContext?.currentOverlay, contextMenuField?.fieldId]);
64
92
 
65
93
  const zoom = pageViewContext.zoom;
66
94
  const blockBlurEventRef = useRef(0);
@@ -68,6 +96,25 @@ export function PageViewerFrame({
68
96
  ItemDescriptor | undefined
69
97
  >(undefined);
70
98
 
99
+ // Field action handlers for the overlay
100
+ const handleActionClick = async (action: FieldButton): Promise<void> => {
101
+ if (contextMenuField) {
102
+ editContext?.triggerFieldAction(contextMenuField, action);
103
+ }
104
+ };
105
+
106
+ const handleParameterizedActionExecute = async (
107
+ action: FieldButton,
108
+ parameters: Record<string, string>,
109
+ ): Promise<void> => {
110
+ if (contextMenuField) {
111
+ editContext?.triggerFieldAction(contextMenuField, action, parameters);
112
+ }
113
+ };
114
+
115
+ // Note: handleParameterizedActionFromContextMenu is now created inline in handleContextMenu
116
+ // to avoid React state timing issues with contextMenuPosition
117
+
71
118
  const pageItemDescriptor = pageViewContext.pageItemDescriptor;
72
119
 
73
120
  useEffect(() => {
@@ -505,17 +552,103 @@ export function PageViewerFrame({
505
552
  clientY: event.clientY + iframeRect.y,
506
553
  });
507
554
 
555
+ // Store the original context menu position for overlay positioning
556
+ const menuPosition = {
557
+ x: event.clientX + iframeRect.x,
558
+ y: event.clientY + iframeRect.y,
559
+ };
560
+ setContextMenuPosition(menuPosition);
561
+
562
+ console.log("Original event:", event);
563
+ console.log("Adjusted event:", adjustedEvent);
564
+ console.log("Menu position:", menuPosition);
565
+
508
566
  const field = fieldElement
509
567
  ? getFieldDescriptorFromElement(fieldElement)
510
568
  : undefined;
511
569
 
512
570
  const fieldButtons = field ? await loadFieldButtons(field) : [];
513
571
 
572
+ // Create a handler that has access to the current menu position
573
+ const handleParameterizedActionWithPosition = (
574
+ field: FieldDescriptor,
575
+ action: FieldButton,
576
+ allFieldButtons: FieldButton[],
577
+ event: any,
578
+ ) => {
579
+ setContextMenuField(field);
580
+ setContextMenuFieldButtons([action]);
581
+ setPreSelectedAction(action);
582
+
583
+ // Create a new MouseEvent with the captured position
584
+ const syntheticEvent = new MouseEvent("click", {
585
+ bubbles: true,
586
+ cancelable: true,
587
+ view: window,
588
+ clientX: menuPosition.x,
589
+ clientY: menuPosition.y,
590
+ screenX: menuPosition.x,
591
+ screenY: menuPosition.y,
592
+ });
593
+
594
+ // Add target property to the event for proper positioning
595
+ Object.defineProperty(syntheticEvent, "target", {
596
+ value: document.body,
597
+ enumerable: true,
598
+ });
599
+ console.log("About to show overlay with position:", {
600
+ menuPosition,
601
+ syntheticEvent,
602
+ preSelectedAction: action,
603
+ });
604
+
605
+ // Add a small delay to ensure context menu has closed
606
+ setTimeout(() => {
607
+ console.log("Showing overlay with position:", {
608
+ menuPosition,
609
+ syntheticEvent,
610
+ preSelectedAction: action,
611
+ });
612
+
613
+ // Create a temporary element at the exact position for better positioning
614
+ const tempElement = document.createElement("div");
615
+ tempElement.style.position = "fixed";
616
+ tempElement.style.left = menuPosition.x + "px";
617
+ tempElement.style.top = menuPosition.y + "px";
618
+ tempElement.style.width = "1px";
619
+ tempElement.style.height = "1px";
620
+ tempElement.style.visibility = "hidden";
621
+ tempElement.style.pointerEvents = "none";
622
+ document.body.appendChild(tempElement);
623
+
624
+ // Create event targeting the positioned element
625
+ const positionedEvent = new MouseEvent("click", {
626
+ bubbles: true,
627
+ cancelable: true,
628
+ view: window,
629
+ clientX: menuPosition.x,
630
+ clientY: menuPosition.y,
631
+ });
632
+ Object.defineProperty(positionedEvent, "target", {
633
+ value: tempElement,
634
+ enumerable: true,
635
+ });
636
+
637
+ fieldActionsOverlay.current?.show(positionedEvent, action);
638
+
639
+ // Clean up the temporary element after overlay is shown
640
+ setTimeout(() => {
641
+ document.body.removeChild(tempElement);
642
+ }, 1000);
643
+ }, 100);
644
+ };
645
+
514
646
  const showMenu = await showComponentContextMenu(
515
647
  selectedComponents,
516
648
  editContextRef.current!,
517
649
  field,
518
650
  fieldButtons,
651
+ handleParameterizedActionWithPosition,
519
652
  );
520
653
  if (showMenu) showMenu(adjustedEvent);
521
654
  };
@@ -734,6 +867,16 @@ export function PageViewerFrame({
734
867
  </>
735
868
  )}
736
869
  </div>
870
+ <FieldActionsOverlay
871
+ ref={fieldActionsOverlay}
872
+ generatorButtons={contextMenuFieldButtons}
873
+ onActionClick={handleActionClick}
874
+ onParameterizedActionExecute={handleParameterizedActionExecute}
875
+ currentOverlay={editContext?.currentOverlay}
876
+ fieldId={contextMenuField?.fieldId || ""}
877
+ setCurrentOverlay={editContext?.setCurrentOverlay || (() => {})}
878
+ preSelectedAction={preSelectedAction}
879
+ />
737
880
  </div>
738
881
  );
739
882
  }
@@ -74,6 +74,17 @@ export type ItemRef = {
74
74
  name?: string;
75
75
  };
76
76
 
77
+ export type FieldButtonParameter = {
78
+ id: string;
79
+ label: string;
80
+ placeholder?: string;
81
+ type?: "text" | "textarea" | "number" | "select";
82
+ required?: boolean;
83
+ defaultValue?: string;
84
+ options?: { value: string; label: string }[]; // for select type
85
+ rows?: number; // for textarea type
86
+ };
87
+
77
88
  export type FieldButton = {
78
89
  id: string;
79
90
  label: string;
@@ -81,6 +92,7 @@ export type FieldButton = {
81
92
  action?: string;
82
93
  icon?: string;
83
94
  isGenerator: boolean;
95
+ parameters?: FieldButtonParameter[];
84
96
  };
85
97
 
86
98
  export type LockSession = {
@@ -13,6 +13,7 @@ import { SimpleIconButton } from "../ui/SimpleIconButton";
13
13
  import { OverlayPanel } from "primereact/overlaypanel";
14
14
  import { ProgressSpinner } from "primereact/progressspinner";
15
15
  import { useDebouncedCallback } from "use-debounce";
16
+ import { ActionButton } from "../../components/ActionButton";
16
17
 
17
18
  export function Comment({ comment }: { comment: CommentType }) {
18
19
  const editContext = useEditContext();
@@ -257,8 +258,7 @@ export function Comment({ comment }: { comment: CommentType }) {
257
258
  )}
258
259
  {!isSaving && (
259
260
  <div className="mt-1 flex justify-end gap-2">
260
- <Button
261
- variant="outline"
261
+ <ActionButton
262
262
  onClick={() => {
263
263
  if (comment.isNew) {
264
264
  if (editContext?.setComments) {
@@ -272,11 +272,10 @@ export function Comment({ comment }: { comment: CommentType }) {
272
272
  }}
273
273
  >
274
274
  Cancel
275
- </Button>
275
+ </ActionButton>
276
276
 
277
- <Button
277
+ <ActionButton
278
278
  className="tour-submit-comment-button"
279
- variant="outline"
280
279
  onClick={async () => {
281
280
  comment.text = commentText;
282
281
  setIsSaving(true);
@@ -299,7 +298,7 @@ export function Comment({ comment }: { comment: CommentType }) {
299
298
  }}
300
299
  >
301
300
  {comment.isNew ? "Comment" : "Save"}
302
- </Button>
301
+ </ActionButton>
303
302
  </div>
304
303
  )}
305
304
  {renderContextInfo()}
@@ -184,6 +184,7 @@ export async function executeFieldAction(
184
184
  action: string,
185
185
  sessionId: string,
186
186
  selectedText: string,
187
+ parameters: Record<string, string>,
187
188
  callback: (data: any) => void,
188
189
  ): Promise<ExecutionResult<any>> {
189
190
  const response = await fetch(
@@ -197,6 +198,7 @@ export async function executeFieldAction(
197
198
  action,
198
199
  selectedText,
199
200
  sessionId,
201
+ parameters,
200
202
  }),
201
203
  credentials: "include",
202
204
  headers: {
@@ -5,6 +5,7 @@ export function WizardIcon({ className }: { className?: string }) {
5
5
  height="24"
6
6
  viewBox="0 0 24 24"
7
7
  fill="none"
8
+ className={className}
8
9
  xmlns="http://www.w3.org/2000/svg"
9
10
  >
10
11
  <path
@@ -0,0 +1,76 @@
1
+ import { ResultItem } from "./ItemSearch";
2
+ import { ItemDescriptor } from "../pageModel";
3
+
4
+ interface ItemListProps {
5
+ items: ResultItem[];
6
+ onItemHover?: (item: ResultItem | undefined) => void;
7
+ onItemClick: (
8
+ item: ItemDescriptor & { path?: string; idPath?: string; icon?: string },
9
+ ) => void;
10
+ maxItems?: number;
11
+ className?: string;
12
+ showPath?: boolean;
13
+ }
14
+
15
+ export const ItemList: React.FC<ItemListProps> = ({
16
+ items,
17
+ onItemHover,
18
+ onItemClick,
19
+ maxItems = 6,
20
+ className = "",
21
+ showPath = true,
22
+ }) => {
23
+ const filteredItems = items.filter((x) => x.language).slice(0, maxItems);
24
+
25
+ const handleItemClick = (item: ResultItem) => {
26
+ onItemClick({
27
+ id: item.id,
28
+ language: item.language,
29
+ version: 0,
30
+ path: item.path,
31
+ icon: item.icon,
32
+ idPath: item.idPath,
33
+ });
34
+ };
35
+
36
+ if (filteredItems.length === 0) {
37
+ return null;
38
+ }
39
+
40
+ return (
41
+ <div className={`flex flex-1 flex-col gap-1 overflow-y-auto ${className}`}>
42
+ {filteredItems.map((item) => (
43
+ <div
44
+ key={item.id + item.language}
45
+ className="flex cursor-pointer items-start gap-2 rounded p-1 text-xs hover:bg-gray-50"
46
+ onMouseEnter={() => onItemHover?.(item)}
47
+ onMouseLeave={() => onItemHover?.(undefined)}
48
+ onClick={() => handleItemClick(item)}
49
+ >
50
+ {item.icon && (
51
+ <img
52
+ src={item.icon}
53
+ width="16"
54
+ height="16"
55
+ className="mt-0.5 flex-shrink-0"
56
+ />
57
+ )}
58
+
59
+ <div className="flex min-w-0 flex-1 flex-col gap-0.5">
60
+ <div className="truncate font-medium text-gray-900">
61
+ {item.name}
62
+ <span className="text-2xs ml-1 text-gray-400">
63
+ ({item.language})
64
+ </span>
65
+ </div>
66
+ {showPath && (
67
+ <div className="text-2xs break-words text-gray-500">
68
+ {item.path}
69
+ </div>
70
+ )}
71
+ </div>
72
+ </div>
73
+ ))}
74
+ </div>
75
+ );
76
+ };
@@ -6,6 +6,7 @@ import { useDebouncedCallback } from "use-debounce";
6
6
  import { ProgressSpinner } from "primereact/progressspinner";
7
7
  import { ItemDescriptor } from "../pageModel";
8
8
  import { getItemDescriptor } from "../utils";
9
+ import { ItemList } from "./ItemList";
9
10
 
10
11
  export type ResultItem = {
11
12
  id: string;
@@ -112,7 +113,7 @@ export const ItemSearch: React.FC<SearchProps> = ({
112
113
  <div className="relative">
113
114
  <div className="flex items-center">
114
115
  <InputText
115
- className="w-full pr-8 text-sm" // Add padding to the right to make space for the spinner
116
+ className="w-full rounded border border-gray-300 px-2 py-1 pr-6 text-xs focus:border-blue-400 focus:ring-1 focus:ring-blue-400 focus:outline-none"
116
117
  onChange={(e) => setQuery(e.target.value)}
117
118
  value={query}
118
119
  ref={inputRef}
@@ -134,51 +135,13 @@ export const ItemSearch: React.FC<SearchProps> = ({
134
135
  </div>
135
136
  </div>
136
137
  {results.length > 0 && (
137
- <div
138
- className={`mt-1 flex flex-1 flex-col gap-1 overflow-y-auto ${resultClassName}`}
139
- >
140
- {results
141
- .filter((x) => x.language)
142
- .slice(0, 6)
143
- .map((x) => (
144
- <div
145
- key={x.id + x.language}
146
- className="flex cursor-pointer items-start gap-2 rounded p-2 text-xs hover:bg-gray-50"
147
- onMouseEnter={() => setHoveredItem?.(x)}
148
- onMouseLeave={() => setHoveredItem?.(undefined)}
149
- onClick={() =>
150
- itemSelected({
151
- id: x.id,
152
- language: x.language,
153
- version: 0,
154
- path: x.path,
155
- icon: x.icon,
156
- idPath: x.idPath,
157
- })
158
- }
159
- >
160
- {x.icon && (
161
- <img
162
- src={x.icon}
163
- width="16"
164
- height="16"
165
- className="mt-0.5 flex-shrink-0"
166
- />
167
- )}
168
- <div className="flex min-w-0 flex-1 flex-col gap-0.5">
169
- <div className="truncate font-medium text-gray-900">
170
- {x.name}
171
- <span className="text-2xs ml-1 text-gray-400">
172
- ({x.language})
173
- </span>
174
- </div>
175
- <div className="text-2xs truncate text-gray-500">
176
- {x.path}
177
- </div>
178
- </div>
179
- </div>
180
- ))}
181
- </div>
138
+ <ItemList
139
+ items={results}
140
+ onItemHover={setHoveredItem}
141
+ onItemClick={itemSelected}
142
+ maxItems={6}
143
+ className={`mt-1 ${resultClassName}`}
144
+ />
182
145
  )}
183
146
  {results.length === 0 && !loading && query && (
184
147
  <div className="mt-1 text-xs text-gray-500">No results found</div>
@@ -30,7 +30,7 @@ export function SimpleTabs({
30
30
 
31
31
  return (
32
32
  <>
33
- <div className={twMerge("flex gap-4 pb-1 md:pb-3", className)}>
33
+ <div className={twMerge("flex gap-4 pb-1", className)}>
34
34
  {tabs.map((tab, index) => (
35
35
  <button
36
36
  id={tab.id}
package/src/revision.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = "1.0.3938";
2
- export const buildDate = "2025-06-06 15:49:56";
1
+ export const version = "1.0.3939";
2
+ export const buildDate = "2025-06-07 12:23:08";