@alpaca-editor/core 1.0.4111 → 1.0.4114

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 (36) hide show
  1. package/dist/components/ui/context-menu.js +51 -21
  2. package/dist/components/ui/context-menu.js.map +1 -1
  3. package/dist/config/config.js +2 -2
  4. package/dist/config/config.js.map +1 -1
  5. package/dist/editor/ContextMenu.js +1 -1
  6. package/dist/editor/ContextMenu.js.map +1 -1
  7. package/dist/editor/client/EditorShell.js +2 -5
  8. package/dist/editor/client/EditorShell.js.map +1 -1
  9. package/dist/editor/client/hooks/useGlobalEditorKeyDown.d.ts +1 -0
  10. package/dist/editor/client/hooks/useGlobalEditorKeyDown.js +12 -0
  11. package/dist/editor/client/hooks/useGlobalEditorKeyDown.js.map +1 -0
  12. package/dist/editor/reviews/CommentView.js +5 -3
  13. package/dist/editor/reviews/CommentView.js.map +1 -1
  14. package/dist/editor/sidebar/SidebarView.js +9 -3
  15. package/dist/editor/sidebar/SidebarView.js.map +1 -1
  16. package/dist/editor/ui/Splitter.js +81 -10
  17. package/dist/editor/ui/Splitter.js.map +1 -1
  18. package/dist/editor/utils/keyboardNavigation.js +1 -0
  19. package/dist/editor/utils/keyboardNavigation.js.map +1 -1
  20. package/dist/revision.d.ts +2 -2
  21. package/dist/revision.js +2 -2
  22. package/package.json +1 -1
  23. package/src/components/ui/context-menu.tsx +59 -22
  24. package/src/config/config.tsx +2 -2
  25. package/src/editor/ContextMenu.tsx +0 -2
  26. package/src/editor/client/EditorShell.tsx +3 -5
  27. package/src/editor/client/hooks/useGlobalEditorKeyDown.ts +11 -0
  28. package/src/editor/reviews/CommentView.tsx +34 -10
  29. package/src/editor/sidebar/SidebarView.tsx +8 -3
  30. package/src/editor/ui/Splitter.tsx +95 -10
  31. package/src/editor/utils/keyboardNavigation.ts +1 -0
  32. package/src/revision.ts +2 -2
  33. package/dist/editor/client/hooks/useGlobalEditorEvents.d.ts +0 -4
  34. package/dist/editor/client/hooks/useGlobalEditorEvents.js +0 -25
  35. package/dist/editor/client/hooks/useGlobalEditorEvents.js.map +0 -1
  36. package/src/editor/client/hooks/useGlobalEditorEvents.ts +0 -30
package/dist/revision.js CHANGED
@@ -1,3 +1,3 @@
1
- export const version = "1.0.4111";
2
- export const buildDate = "2025-09-25 09:06:17";
1
+ export const version = "1.0.4114";
2
+ export const buildDate = "2025-09-25 14:12:59";
3
3
  //# sourceMappingURL=revision.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alpaca-editor/core",
3
- "version": "1.0.4111",
3
+ "version": "1.0.4114",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -64,10 +64,13 @@ function ContextMenu({ children }: { children: React.ReactNode }) {
64
64
  window.addEventListener("keydown", handleKeyDown, true);
65
65
  // Use bubble phase so capture-phase handlers inside the menu can stop propagation
66
66
  window.addEventListener("pointerdown", handlePointerDown as any);
67
+ // Also listen for click events so synthetic clicks (e.g., forwarded from iframes) close the menu
68
+ window.addEventListener("click", handlePointerDown as any);
67
69
  window.addEventListener("contextmenu", handlePointerDown as any);
68
70
  return () => {
69
71
  window.removeEventListener("keydown", handleKeyDown, true);
70
72
  window.removeEventListener("pointerdown", handlePointerDown as any);
73
+ window.removeEventListener("click", handlePointerDown as any);
71
74
  window.removeEventListener("contextmenu", handlePointerDown as any);
72
75
  };
73
76
  }, [isOpen, close]);
@@ -161,13 +164,15 @@ function ContextMenuContent({
161
164
  }: React.HTMLAttributes<HTMLDivElement>) {
162
165
  const { isOpen, position, close, registerContainer, unregisterContainer } =
163
166
  useContextMenuRoot();
164
- const contentRef = React.useRef<HTMLDivElement | null>(null);
165
167
 
166
- React.useEffect(() => {
167
- const el = contentRef.current;
168
- registerContainer(el);
169
- return () => unregisterContainer(el);
170
- }, [registerContainer, unregisterContainer]);
168
+ const contentRefCallback = React.useCallback(
169
+ (el: HTMLDivElement | null) => {
170
+ if (el) {
171
+ registerContainer(el);
172
+ }
173
+ },
174
+ [registerContainer],
175
+ );
171
176
 
172
177
  if (!isOpen || !position) return null;
173
178
 
@@ -177,7 +182,7 @@ function ContextMenuContent({
177
182
 
178
183
  const node = (
179
184
  <div
180
- ref={contentRef}
185
+ ref={contentRefCallback}
181
186
  data-slot="context-menu-content"
182
187
  data-testid="context-menu-content"
183
188
  className={cn(
@@ -220,12 +225,42 @@ function ContextMenuItem({
220
225
  }: ItemCommonProps) {
221
226
  const { close } = useContextMenuRoot();
222
227
  const disabled = rest.disabled ?? false;
228
+ const executedRef = React.useRef(false);
223
229
 
224
- const handle = (e: React.MouseEvent<HTMLDivElement>) => {
230
+ const performAction = (e: React.MouseEvent<HTMLDivElement>) => {
225
231
  if (disabled) return;
226
- if (onClick) onClick(e);
227
- if (onSelect) onSelect(e);
228
- close();
232
+ if (executedRef.current) return;
233
+ executedRef.current = true;
234
+ try {
235
+ if (onClick) onClick(e);
236
+ if (onSelect) onSelect(e);
237
+ close();
238
+ } finally {
239
+ // Reset in next tick so a subsequent distinct click can trigger again
240
+ setTimeout(() => {
241
+ executedRef.current = false;
242
+ }, 0);
243
+ }
244
+ };
245
+
246
+ const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
247
+ // Trigger on primary-button press for snappier UX and to avoid external handlers interfering
248
+ if (e.button === 0) {
249
+ performAction(e);
250
+ }
251
+ };
252
+
253
+ const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
254
+ // Fallback in case mousedown didn't fire
255
+ performAction(e);
256
+ };
257
+
258
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
259
+ if (e.key === "Enter" || e.key === " ") {
260
+ e.preventDefault();
261
+ // Cast to any to reuse performAction signature
262
+ performAction(e as unknown as React.MouseEvent<HTMLDivElement>);
263
+ }
229
264
  };
230
265
  return (
231
266
  <div
@@ -239,7 +274,9 @@ function ContextMenuItem({
239
274
  "hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-xs outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
240
275
  className,
241
276
  )}
242
- onClick={handle}
277
+ onMouseDown={handleMouseDown}
278
+ onClick={handleClick}
279
+ onKeyDown={handleKeyDown}
243
280
  {...rest}
244
281
  />
245
282
  );
@@ -466,13 +503,21 @@ function ContextMenuSubContent({
466
503
  }: React.HTMLAttributes<HTMLDivElement>) {
467
504
  const { isOpen, setOpen, triggerRef } = useSubmenu();
468
505
  const { registerContainer, unregisterContainer } = useContextMenuRoot();
469
- const ref = React.useRef<HTMLDivElement | null>(null);
470
506
 
471
507
  const [coords, setCoords] = React.useState<{ left: number; top: number }>({
472
508
  left: 0,
473
509
  top: 0,
474
510
  });
475
511
 
512
+ const subContentRefCallback = React.useCallback(
513
+ (el: HTMLDivElement | null) => {
514
+ if (el) {
515
+ registerContainer(el);
516
+ }
517
+ },
518
+ [registerContainer],
519
+ );
520
+
476
521
  React.useEffect(() => {
477
522
  const trig = triggerRef.current;
478
523
  if (!trig) return;
@@ -480,17 +525,11 @@ function ContextMenuSubContent({
480
525
  setCoords({ left: rect.right, top: rect.top });
481
526
  }, [triggerRef.current, isOpen]);
482
527
 
483
- React.useEffect(() => {
484
- const el = ref.current;
485
- registerContainer(el);
486
- return () => unregisterContainer(el);
487
- }, [registerContainer, unregisterContainer]);
488
-
489
528
  if (!isOpen) return null;
490
529
 
491
530
  const node = (
492
531
  <div
493
- ref={ref}
532
+ ref={subContentRefCallback}
494
533
  data-slot="context-menu-sub-content"
495
534
  data-testid="context-menu-sub-content"
496
535
  className={cn(
@@ -500,8 +539,6 @@ function ContextMenuSubContent({
500
539
  style={{ position: "fixed", left: coords.left, top: coords.top }}
501
540
  onMouseLeave={() => setOpen(false)}
502
541
  onMouseEnter={() => setOpen(true)}
503
- onMouseDownCapture={(e) => e.stopPropagation()}
504
- onClick={(e) => e.stopPropagation()}
505
542
  >
506
543
  {children}
507
544
  </div>
@@ -577,14 +577,14 @@ export const getConfiguration = (): EditorConfiguration => {
577
577
  name: "comments",
578
578
  title: "Comments & Suggestions",
579
579
  content: <Comments />,
580
- initialSize: 100,
580
+ initialSize: 65,
581
581
  noOverflow: true,
582
582
  },
583
583
  {
584
584
  name: "latest-feedback",
585
585
  title: "Latest Feedback",
586
586
  content: <LatestFeedback />,
587
- initialSize: 50,
587
+ initialSize: 35,
588
588
  },
589
589
  ],
590
590
  },
@@ -380,8 +380,6 @@ export const EditContextMenu = forwardRef<
380
380
  }
381
381
  // default Radix behavior closes menu on item click
382
382
  }}
383
- onMouseDownCapture={(e) => e.stopPropagation()}
384
- onMouseUpCapture={(e) => e.stopPropagation()}
385
383
  disabled={item.disabled}
386
384
  className={cn("cursor-pointer", item.className)}
387
385
  >
@@ -137,11 +137,12 @@ import { useQuota } from "./hooks/useQuota";
137
137
  import { useEditorUrlSync } from "./hooks/useEditorUrlSync";
138
138
 
139
139
  import { useMediaQuery } from "./hooks/useMediaQuery";
140
- import { useGlobalEditorEvents } from "./hooks/useGlobalEditorEvents";
140
+
141
141
  import { FullscreenControls } from "./ui/FullscreenControls";
142
142
  import { EditorChrome } from "./ui/EditorChrome";
143
143
  import { useWorkbox } from "./hooks/useWorkbox";
144
144
  import { useMediaSelector } from "./hooks/useMediaSelector";
145
+ import { useGlobalEditorKeyDown } from "./hooks/useGlobalEditorKeyDown";
145
146
 
146
147
  export type FieldAction = {
147
148
  field: FieldDescriptor;
@@ -1525,10 +1526,7 @@ export function EditorShell({
1525
1526
  executeCommand,
1526
1527
  });
1527
1528
 
1528
- useGlobalEditorEvents({
1529
- onKeyDown: handleKeyDown,
1530
- onWindowClick: () => contextMenuRef.current?.close({}),
1531
- });
1529
+ useGlobalEditorKeyDown(handleKeyDown);
1532
1530
 
1533
1531
  useEffect(() => {
1534
1532
  const handleGlobalBlur = () => {
@@ -0,0 +1,11 @@
1
+ import { useEffect } from "react";
2
+
3
+ export function useGlobalEditorKeyDown(onKeyDown: (ev: KeyboardEvent) => void) {
4
+ useEffect(() => {
5
+ if (typeof window === "undefined") return;
6
+ window.addEventListener("keydown", onKeyDown, true);
7
+ return () => {
8
+ window.removeEventListener("keydown", onKeyDown, true);
9
+ };
10
+ }, [onKeyDown]);
11
+ }
@@ -86,23 +86,47 @@ export function CommentView({
86
86
  const componentName = isComponentDifferent ? comment.itemName : undefined;
87
87
  const fieldName = comment.fieldName;
88
88
 
89
- const segments = [pageName, componentName, fieldName].filter(
90
- (x): x is string => !!x && x.length > 0,
91
- );
89
+ const hasPageName = !!pageName && pageName.length > 0;
90
+ const hasComponentName = !!componentName && componentName.length > 0;
91
+ const hasFieldName = !!fieldName && fieldName.length > 0;
92
92
 
93
- if (segments.length === 0) return null;
93
+ if (!hasPageName && !hasComponentName && !hasFieldName) return null;
94
94
 
95
95
  return (
96
96
  <div
97
97
  className={`${compact ? "mt-2" : "mt-3"} flex items-center border-t pt-${compact ? "2" : "3"} text-xs`}
98
98
  data-testid="comment-context-info"
99
99
  >
100
- {segments.map((seg, idx) => (
101
- <React.Fragment key={idx}>
102
- {idx > 0 && <div className="text-2xs mx-2 text-gray-500">&gt;</div>}
103
- <div className="text-2xs text-gray-500">{seg}</div>
104
- </React.Fragment>
105
- ))}
100
+ {hasPageName && (
101
+ <div
102
+ className="text-2xs text-gray-500"
103
+ data-testid="comment-page-name"
104
+ >
105
+ {pageName}
106
+ </div>
107
+ )}
108
+ {hasPageName && (hasComponentName || hasFieldName) && (
109
+ <div className="text-2xs mx-2 text-gray-500">&gt;</div>
110
+ )}
111
+ {hasComponentName && (
112
+ <div
113
+ className="text-2xs text-gray-500"
114
+ data-testid="comment-component-name"
115
+ >
116
+ {componentName}
117
+ </div>
118
+ )}
119
+ {hasComponentName && hasFieldName && (
120
+ <div className="text-2xs mx-2 text-gray-500">&gt;</div>
121
+ )}
122
+ {hasFieldName && (
123
+ <div
124
+ className="text-2xs text-gray-500"
125
+ data-testid="comment-field-name"
126
+ >
127
+ {fieldName}
128
+ </div>
129
+ )}
106
130
  </div>
107
131
  );
108
132
  };
@@ -93,8 +93,10 @@ export function SidebarView({
93
93
  // Multiple panels - use existing Splitter component
94
94
  const splitterPanels: SplitterPanel[] = resolvedPanels.map(
95
95
  (panel, index) => ({
96
- name: `panel-${index}`,
97
- defaultSize: index === 0 ? panel.initialSize || 300 : "auto",
96
+ // Use stable names so persisted sizes map correctly per panel
97
+ name: panel.name || `panel-${index}`,
98
+ // Respect each panel's initialSize (treat as proportional weight)
99
+ defaultSize: panel.initialSize,
98
100
  content: (
99
101
  <div className="flex h-full flex-col bg-white">
100
102
  {getHeader(panel, index)}
@@ -117,7 +119,10 @@ export function SidebarView({
117
119
  <Splitter
118
120
  panels={splitterPanels}
119
121
  direction="vertical"
120
- localStorageKey={`sidebar-${sidebar.panels.length}-panels`}
122
+ // Key per view and panel composition to avoid cross-view collisions
123
+ localStorageKey={`sidebar-${editContext.viewName}-${splitterPanels
124
+ .map((p) => p.name)
125
+ .join("-")}`}
121
126
  className={cn(detached ? "p-2" : "", !active ? "hidden" : "")}
122
127
  />
123
128
  );
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, useEffect } from "react";
1
+ import React, { useState, useRef, useEffect, useLayoutEffect } from "react";
2
2
  import { flushSync } from "react-dom";
3
3
  import { cn } from "../../lib/utils";
4
4
  export type SplitterPanel = {
@@ -68,19 +68,104 @@ export const Splitter: React.FC<SplitterProps> = ({
68
68
 
69
69
  const [isResizing, setIsResizing] = useState(false);
70
70
 
71
- useEffect(() => {
72
- if (!panelSizes && splitterRef.current) {
73
- const initialSizes: PanelSizes = {};
71
+ useLayoutEffect(() => {
72
+ if (panelSizes || !splitterRef.current) return;
74
73
 
75
- panels.forEach((panel) => {
76
- initialSizes[panel.name] = panel.defaultSize;
77
- });
74
+ const container = splitterRef.current;
75
+ const isHorizontal = direction === "horizontal";
76
+
77
+ const computeAndSet = (containerSize: number) => {
78
+ const visiblePanels = panels.filter((p) => !p.hidden);
79
+ if (visiblePanels.length === 0) return;
80
+
81
+ const minPanelSize = 50;
82
+
83
+ const fixedPanels = visiblePanels.filter(
84
+ (p) => typeof p.defaultSize === "number",
85
+ );
86
+ const autoPanels = visiblePanels.filter((p) => p.defaultSize === "auto");
87
+
88
+ const sumFixed = fixedPanels.reduce((sum, p) => {
89
+ const size = p.defaultSize as number;
90
+ return sum + (typeof size === "number" ? size : 0);
91
+ }, 0);
78
92
 
79
- setPanelSizes(initialSizes);
93
+ const updated: PanelSizes = {};
94
+
95
+ if (autoPanels.length === 0) {
96
+ const scale =
97
+ containerSize > 0 && sumFixed > 0 ? containerSize / sumFixed : 1;
98
+ visiblePanels.forEach((p) => {
99
+ updated[p.name] = Math.max(
100
+ minPanelSize,
101
+ Math.round((p.defaultSize as number) * scale),
102
+ );
103
+ });
104
+ } else if (containerSize > 0) {
105
+ const minAutoTotal = autoPanels.length * minPanelSize;
106
+ const remainder = containerSize - sumFixed;
107
+
108
+ if (remainder >= minAutoTotal) {
109
+ const per = Math.floor(remainder / autoPanels.length);
110
+ fixedPanels.forEach((p) => {
111
+ updated[p.name] = p.defaultSize as number;
112
+ });
113
+ autoPanels.forEach((p) => {
114
+ updated[p.name] = per;
115
+ });
116
+ } else {
117
+ const availableForFixed = Math.max(0, containerSize - minAutoTotal);
118
+ const scale = sumFixed > 0 ? availableForFixed / sumFixed : 0;
119
+
120
+ fixedPanels.forEach((p) => {
121
+ updated[p.name] = Math.max(
122
+ minPanelSize,
123
+ Math.round((p.defaultSize as number) * scale),
124
+ );
125
+ });
126
+ autoPanels.forEach((p) => {
127
+ updated[p.name] = minPanelSize;
128
+ });
129
+ }
130
+ } else {
131
+ // Fallback when container size is 0
132
+ fixedPanels.forEach((p) => {
133
+ updated[p.name] = p.defaultSize as number;
134
+ });
135
+ autoPanels.forEach((p) => {
136
+ updated[p.name] = minPanelSize;
137
+ });
138
+ }
139
+
140
+ setPanelSizes(updated);
141
+ };
142
+
143
+ const initialSize = isHorizontal
144
+ ? container.clientWidth
145
+ : container.clientHeight;
146
+
147
+ if (initialSize > 0) {
148
+ computeAndSet(initialSize);
149
+ return;
80
150
  }
81
- }, [panels]);
82
151
 
83
- // No container size tracking needed with fractional sizing
152
+ const ro = new ResizeObserver((entries) => {
153
+ for (const entry of entries) {
154
+ const size = isHorizontal
155
+ ? entry.contentRect.width
156
+ : entry.contentRect.height;
157
+ if (size > 0) {
158
+ computeAndSet(size);
159
+ ro.disconnect();
160
+ break;
161
+ }
162
+ }
163
+ });
164
+ ro.observe(container);
165
+ return () => {
166
+ ro.disconnect();
167
+ };
168
+ }, [panels, panelSizes, direction]);
84
169
 
85
170
  useEffect(() => {
86
171
  if (panelSizes) {
@@ -49,6 +49,7 @@ export function useKeyboardNavigation(deps: KeyboardNavigationDependencies) {
49
49
  await operations.redo();
50
50
  }
51
51
  if (event.ctrlKey && event.key === "F11") {
52
+ console.log("F11 pressed");
52
53
  event.preventDefault();
53
54
  if (!configuration.forceFullscreen) {
54
55
  pageViewContext.setFullscreen(false);
package/src/revision.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = "1.0.4111";
2
- export const buildDate = "2025-09-25 09:06:17";
1
+ export const version = "1.0.4114";
2
+ export const buildDate = "2025-09-25 14:12:59";
@@ -1,4 +0,0 @@
1
- export declare function useGlobalEditorEvents(params: {
2
- onKeyDown: (ev: KeyboardEvent) => void;
3
- onWindowClick: () => void;
4
- }): void;
@@ -1,25 +0,0 @@
1
- import { useEffect } from "react";
2
- export function useGlobalEditorEvents(params) {
3
- const { onKeyDown, onWindowClick } = params;
4
- useEffect(() => {
5
- if (typeof window === "undefined")
6
- return;
7
- window.addEventListener("keydown", onKeyDown, true);
8
- const onClickCapture = (ev) => {
9
- const target = ev.target;
10
- // Ignore clicks inside context menu UI; let the menu manage its own lifecycle
11
- if (target &&
12
- (target.closest('[data-slot^="context-menu-"]') ||
13
- target.closest('[data-testid^="context-menu-" ]'))) {
14
- return;
15
- }
16
- onWindowClick();
17
- };
18
- window.addEventListener("click", onClickCapture, true);
19
- return () => {
20
- window.removeEventListener("keydown", onKeyDown, true);
21
- window.removeEventListener("click", onClickCapture, true);
22
- };
23
- }, [onKeyDown, onWindowClick]);
24
- }
25
- //# sourceMappingURL=useGlobalEditorEvents.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useGlobalEditorEvents.js","sourceRoot":"","sources":["../../../../src/editor/client/hooks/useGlobalEditorEvents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,MAAM,UAAU,qBAAqB,CAAC,MAGrC;IACC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC;IAE5C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,cAAc,GAAG,CAAC,EAAc,EAAE,EAAE;YACxC,MAAM,MAAM,GAAG,EAAE,CAAC,MAA4B,CAAC;YAC/C,8EAA8E;YAC9E,IACE,MAAM;gBACN,CAAC,MAAM,CAAC,OAAO,CAAC,8BAA8B,CAAC;oBAC7C,MAAM,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,EACpD,CAAC;gBACD,OAAO;YACT,CAAC;YACD,aAAa,EAAE,CAAC;QAClB,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;QACvD,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YACvD,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;QAC5D,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC;AACjC,CAAC"}
@@ -1,30 +0,0 @@
1
- import { useEffect } from "react";
2
-
3
- export function useGlobalEditorEvents(params: {
4
- onKeyDown: (ev: KeyboardEvent) => void;
5
- onWindowClick: () => void;
6
- }) {
7
- const { onKeyDown, onWindowClick } = params;
8
-
9
- useEffect(() => {
10
- if (typeof window === "undefined") return;
11
- window.addEventListener("keydown", onKeyDown, true);
12
- const onClickCapture = (ev: MouseEvent) => {
13
- const target = ev.target as HTMLElement | null;
14
- // Ignore clicks inside context menu UI; let the menu manage its own lifecycle
15
- if (
16
- target &&
17
- (target.closest('[data-slot^="context-menu-"]') ||
18
- target.closest('[data-testid^="context-menu-" ]'))
19
- ) {
20
- return;
21
- }
22
- onWindowClick();
23
- };
24
- window.addEventListener("click", onClickCapture, true);
25
- return () => {
26
- window.removeEventListener("keydown", onKeyDown, true);
27
- window.removeEventListener("click", onClickCapture, true);
28
- };
29
- }, [onKeyDown, onWindowClick]);
30
- }