@ai-matrx/context-menu 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js CHANGED
@@ -382,11 +382,21 @@ var CONTEXT_REGION_TRIGGER_ATTRS = {
382
382
  };
383
383
 
384
384
  // src/engine/utils/table-at-target.ts
385
- import { shapeTextOfNode } from "@ai-matrx/records-ui/table-shape";
385
+ var shapeReader = null;
386
+ var shapeLoad = null;
387
+ function preloadTableShape() {
388
+ shapeLoad ??= import("@ai-matrx/records-ui/table-shape").then((m) => {
389
+ shapeReader = m.shapeTextOfNode;
390
+ }).catch((error) => {
391
+ shapeLoad = null;
392
+ console.error("[ContextMenuV3] could not load the table shape reader", error);
393
+ });
394
+ return shapeLoad;
395
+ }
386
396
  function tableTextAtTarget(target) {
387
397
  const table = target?.closest("table");
388
- if (!table) return null;
389
- const text = shapeTextOfNode(table.cloneNode(true));
398
+ if (!table || !shapeReader) return null;
399
+ const text = shapeReader(table.cloneNode(true));
390
400
  return text.trim() ? text : null;
391
401
  }
392
402
 
@@ -525,7 +535,6 @@ function useContextMenuHostBindings() {
525
535
 
526
536
  // src/engine/ContextMenuV3.tsx
527
537
  import { selectAllShortcutsArray } from "@ai-matrx/chat/agents/redux/agent-shortcuts/selectors";
528
- import { fetchUnifiedMenu } from "@ai-matrx/chat/agents/redux/agent-shortcuts/thunks";
529
538
  import { jsx, jsxs } from "react/jsx-runtime";
530
539
  var NON_TEXT_INPUT_TYPES = /* @__PURE__ */ new Set([
531
540
  "button",
@@ -597,17 +606,7 @@ function warmMenuContent() {
597
606
  }
598
607
  return menuContentSlot().warm ?? Promise.resolve();
599
608
  }
600
- function useWarmMenuContentWhenIdle() {
601
- useEffect(() => {
602
- if (menuContentSlot().warm) return;
603
- if (typeof window.requestIdleCallback === "function") {
604
- const id2 = window.requestIdleCallback(() => void warmMenuContent(), { timeout: 4e3 });
605
- return () => window.cancelIdleCallback(id2);
606
- }
607
- const id = window.setTimeout(() => void warmMenuContent(), 1500);
608
- return () => window.clearTimeout(id);
609
- }, []);
610
- }
609
+ var menuEverOpened = false;
611
610
  function ShortcutKeyRunner(props) {
612
611
  const mod = useMenuContentModule();
613
612
  return mod ? /* @__PURE__ */ jsx(mod.ShortcutKeyRunner, { ...props }) : null;
@@ -722,7 +721,8 @@ function ContextMenuV3({
722
721
  const store = React.useContext(ReactReduxContext)?.store;
723
722
  const [menuPoint, setMenuPoint] = useState({ x: 0, y: 0 });
724
723
  const [openSeq, setOpenSeq] = useState(0);
725
- useWarmMenuContentWhenIdle();
724
+ const [engaged, setEngaged] = useState(false);
725
+ const engage = () => setEngaged(true);
726
726
  const capturedSelection = useRef(null);
727
727
  const selectionLocked = useRef(false);
728
728
  const lastMousePos = useRef(null);
@@ -753,7 +753,7 @@ function ContextMenuV3({
753
753
  onTextInsertAfter,
754
754
  getApplicationScope
755
755
  }) : null;
756
- const widgetHandleId = useOptionalWidgetHandle(widgetHandle);
756
+ const widgetHandleId = useOptionalWidgetHandle(engaged ? widgetHandle : null);
757
757
  const getEffectiveContextData = () => mergeResolvedContextData(
758
758
  contextData,
759
759
  resolvedContext
@@ -761,6 +761,7 @@ function ContextMenuV3({
761
761
  const surfaceSpokeAboutEntity = !!resolvedContext && types_exports.CONTEXT_MENU_ENTITY_KEY in resolvedContext;
762
762
  const effectiveEntity = surfaceSpokeAboutEntity ? resolveEffectiveEntity(entity, resolvedContext) : sniffedEntity ?? entity;
763
763
  useEffect(() => {
764
+ if (!engaged) return;
764
765
  const handleSelection = () => {
765
766
  if (selectionLocked.current) return;
766
767
  const owner = selectionOwnerRef.current;
@@ -777,18 +778,15 @@ function ContextMenuV3({
777
778
  };
778
779
  document.addEventListener("selectionchange", handleSelection);
779
780
  return () => document.removeEventListener("selectionchange", handleSelection);
780
- }, []);
781
- useEffect(() => {
782
- const handleMouseMove = (e) => {
783
- lastMousePos.current = { x: e.clientX, y: e.clientY };
784
- };
785
- document.addEventListener("mousemove", handleMouseMove, { passive: true });
786
- return () => document.removeEventListener("mousemove", handleMouseMove);
787
- }, []);
781
+ }, [engaged]);
782
+ const rememberPointer = (e) => {
783
+ lastMousePos.current = { x: e.clientX, y: e.clientY };
784
+ };
788
785
  const handleMouseDown = (e) => {
789
786
  if (suppressed) return;
790
787
  if (reachedThroughPortal(e)) return;
791
788
  if (e.button !== 2) return;
789
+ void warmMenuContent();
792
790
  if (!isEditable && yieldsToNativeTextMenu(e.target)) return;
793
791
  const target = e.target;
794
792
  resolvePerTargetContext(target);
@@ -804,6 +802,9 @@ function ContextMenuV3({
804
802
  }
805
803
  };
806
804
  const resolvePerTargetContext = (target) => {
805
+ menuEverOpened = true;
806
+ engage();
807
+ if (target?.closest("table")) void preloadTableShape();
807
808
  const rowHit = resolveTableRowItem(target);
808
809
  const rowMenu = rowHit?.menu ?? null;
809
810
  setResolvedItem(rowHit?.item ?? null);
@@ -944,11 +945,14 @@ function ContextMenuV3({
944
945
  setDropdownOpen(true);
945
946
  };
946
947
  const selectionHost = { kind: "context-menu-selection", open: openFromSelection };
947
- useSelectionZone(selectionOwner, {
948
- // A suppressed menu (the text is streaming) keeps the toolbar away until it settles.
949
- suppress: suppressed,
950
- host: { [CONTEXT_MENU_SELECTION_HOST_KEY]: selectionHost }
951
- });
948
+ useSelectionZone(
949
+ selectionOwner,
950
+ engaged ? {
951
+ // A suppressed menu (the text is streaming) keeps the toolbar away until it settles.
952
+ suppress: suppressed,
953
+ host: { [CONTEXT_MENU_SELECTION_HOST_KEY]: selectionHost }
954
+ } : null
955
+ );
952
956
  const clearLongPress = () => {
953
957
  if (longPressTimer.current !== null) {
954
958
  clearTimeout(longPressTimer.current);
@@ -1084,6 +1088,8 @@ function ContextMenuV3({
1084
1088
  const reg = paletteRegistry();
1085
1089
  const now = Date.now();
1086
1090
  if (e.type === "pointerdown") reg.pointerDown = { element: e.target, at: now };
1091
+ if (e.type === "pointerdown" || e.type === "focusin" || e.type === "focus") engage();
1092
+ if (e.type === "pointerover" || e.type === "pointerdown") rememberPointer(e);
1087
1093
  reg.seq += 1;
1088
1094
  reg[kind] = { id: paletteIdRef.current, element: e.target, seq: reg.seq, at: now };
1089
1095
  };
@@ -1104,6 +1110,7 @@ function ContextMenuV3({
1104
1110
  const native = e.nativeEvent;
1105
1111
  if (native[KEY_RUN_CLAIMED] || e.defaultPrevented || suppressed || e.repeat) return;
1106
1112
  if (reachedThroughPortal(e)) return;
1113
+ if (!menuEverOpened) return;
1107
1114
  if (!store || !e.altKey && !e.ctrlKey && !e.metaKey) return;
1108
1115
  const matchOf = (event) => findComboMatch(
1109
1116
  event,
@@ -1139,7 +1146,7 @@ function ContextMenuV3({
1139
1146
  const loadShortcuts = () => {
1140
1147
  if (!store || !isEditable || shortcutsLoaded.current) return null;
1141
1148
  if (!shortcutsLoad.current) {
1142
- shortcutsLoad.current = store.dispatch(fetchUnifiedMenu({ scope, scopeId })).catch(() => void 0).finally(() => {
1149
+ shortcutsLoad.current = import("@ai-matrx/chat/agents/redux/agent-shortcuts/thunks").then(({ fetchUnifiedMenu: fetchUnifiedMenu2 }) => store.dispatch(fetchUnifiedMenu2({ scope, scopeId }))).catch(() => void 0).finally(() => {
1143
1150
  shortcutsLoaded.current = true;
1144
1151
  });
1145
1152
  }
@@ -1158,6 +1165,7 @@ function ContextMenuV3({
1158
1165
  "data-content-source": contentSource ? contentSourceKey(contentSource) : void 0,
1159
1166
  onContextMenuCapture: (e) => {
1160
1167
  if (reachedThroughPortal(e)) return;
1168
+ void warmMenuContent();
1161
1169
  if (!isEditable && yieldsToNativeTextMenu(e.target))
1162
1170
  NATIVE_TEXT_YIELD.set(e.nativeEvent, e.currentTarget);
1163
1171
  },
@@ -1796,6 +1804,17 @@ function toast2(props) {
1796
1804
  else notify.message(message, options);
1797
1805
  }
1798
1806
 
1807
+ // src/engine/utils/ensure-block-template.ts
1808
+ import { supabase } from "@ai-matrx/chat/host/db";
1809
+ import { sklActions } from "@ai-matrx/chat/host/ui-slots";
1810
+ async function ensureBlockTemplate(blockId, dispatch) {
1811
+ const { data, error } = await supabase.schema("skill").from("render_definition").select("template").eq("id", blockId).single();
1812
+ if (error) throw new Error(error.message);
1813
+ const template = data?.template ?? "";
1814
+ dispatch(sklActions.renderDefinitionsMerged([{ id: blockId, template }]));
1815
+ return template;
1816
+ }
1817
+
1799
1818
  // src/engine/hooks/useContextMenuActions.ts
1800
1819
  import { selectOrganizationId } from "@ai-matrx/chat/host/org";
1801
1820
  import { useAgentLauncher } from "@ai-matrx/chat/agents/hooks/useAgentLauncher";
@@ -1932,7 +1951,7 @@ import {
1932
1951
  import { useMemo, useState as useState4, useCallback as useCallback2 } from "react";
1933
1952
  import { useAppDispatch, useAppSelector } from "@ai-matrx/chat/store/hooks";
1934
1953
  import {
1935
- fetchUnifiedMenu as fetchUnifiedMenu2,
1954
+ fetchUnifiedMenu,
1936
1955
  unifiedMenuLoadedKey
1937
1956
  } from "@ai-matrx/chat/agents/redux/agent-shortcuts/thunks";
1938
1957
 
@@ -2102,7 +2121,7 @@ function useUnifiedAgentContextMenu(args) {
2102
2121
  try {
2103
2122
  setError(null);
2104
2123
  await (0, menu_deadline_exports.withMenuDeadline)(
2105
- dispatch(fetchUnifiedMenu2({ scope, scopeId })).unwrap(),
2124
+ dispatch(fetchUnifiedMenu({ scope, scopeId })).unwrap(),
2106
2125
  "Loading the menu's AI actions"
2107
2126
  );
2108
2127
  } catch (err) {
@@ -2995,12 +3014,25 @@ function useContextMenuActions(props) {
2995
3014
  };
2996
3015
  const insertTargets = { editorId, getTextarea, insertAtCaret, onTextReplace };
2997
3016
  const handleContentBlockInsert = (entry) => {
2998
- const template = entry.template;
2999
- if (insertIntoEditor(insertTargets, template)) {
3000
- onContentInserted?.();
3001
- return;
3002
- }
3003
- void kitCopyText(template).then((copied) => {
3017
+ void (async () => {
3018
+ let template = entry.template;
3019
+ if (!template) {
3020
+ try {
3021
+ template = await ensureBlockTemplate(entry.id, dispatch);
3022
+ } catch (error) {
3023
+ toast2({
3024
+ title: "Couldn't load that block",
3025
+ description: error instanceof Error ? error.message : "Try again.",
3026
+ variant: "destructive"
3027
+ });
3028
+ return;
3029
+ }
3030
+ }
3031
+ if (insertIntoEditor(insertTargets, template)) {
3032
+ onContentInserted?.();
3033
+ return;
3034
+ }
3035
+ const copied = await kitCopyText(template);
3004
3036
  if (copied) {
3005
3037
  toast2({
3006
3038
  title: "Copied to clipboard",
@@ -3009,7 +3041,7 @@ function useContextMenuActions(props) {
3009
3041
  } else {
3010
3042
  getContextMenuHost().showManualCopy({ text: template });
3011
3043
  }
3012
- });
3044
+ })();
3013
3045
  };
3014
3046
  const handleEntrySelect = (entry) => {
3015
3047
  if (entry.entryType === "agent_shortcut") void handleShortcutExecute(entry);