@ai-matrx/alchemy 0.11.0 → 0.12.0

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 (76) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +1 -1
  3. package/dist/actions.cjs +1 -1
  4. package/dist/actions.cjs.map +1 -1
  5. package/dist/actions.d.cts +72 -1
  6. package/dist/actions.d.ts +72 -1
  7. package/dist/actions.js +1 -1
  8. package/dist/actions.js.map +1 -1
  9. package/dist/checks.cjs +1 -1
  10. package/dist/checks.cjs.map +1 -1
  11. package/dist/checks.d.cts +8 -0
  12. package/dist/checks.d.ts +8 -0
  13. package/dist/checks.js +1 -1
  14. package/dist/checks.js.map +1 -1
  15. package/dist/core.d.cts +8 -0
  16. package/dist/core.d.ts +8 -0
  17. package/dist/declare/schema.json +3 -0
  18. package/dist/declare.d.cts +8 -0
  19. package/dist/declare.d.ts +8 -0
  20. package/dist/index.d.cts +8 -0
  21. package/dist/index.d.ts +8 -0
  22. package/dist/menu.d.cts +29 -0
  23. package/dist/menu.d.ts +29 -0
  24. package/dist/operate.d.cts +8 -0
  25. package/dist/operate.d.ts +8 -0
  26. package/dist/ports.d.cts +8 -0
  27. package/dist/ports.d.ts +8 -0
  28. package/dist/react-bar.cjs +1 -1
  29. package/dist/react-bar.cjs.map +1 -1
  30. package/dist/react-bar.d.cts +8 -0
  31. package/dist/react-bar.d.ts +8 -0
  32. package/dist/react-bar.js +1 -1
  33. package/dist/react-bar.js.map +1 -1
  34. package/dist/react-host.cjs +1 -1
  35. package/dist/react-host.cjs.map +1 -1
  36. package/dist/react-host.d.cts +42 -1
  37. package/dist/react-host.d.ts +42 -1
  38. package/dist/react-host.js +1 -1
  39. package/dist/react-host.js.map +1 -1
  40. package/dist/react-menu.cjs +1 -1
  41. package/dist/react-menu.cjs.map +1 -1
  42. package/dist/react-menu.d.cts +8 -0
  43. package/dist/react-menu.d.ts +8 -0
  44. package/dist/react-menu.js +1 -1
  45. package/dist/react-menu.js.map +1 -1
  46. package/dist/react-overflow.cjs +1 -1
  47. package/dist/react-overflow.cjs.map +1 -1
  48. package/dist/react-overflow.d.cts +29 -0
  49. package/dist/react-overflow.d.ts +29 -0
  50. package/dist/react-overflow.js +1 -1
  51. package/dist/react-overflow.js.map +1 -1
  52. package/dist/react-palette.cjs +1 -1
  53. package/dist/react-palette.cjs.map +1 -1
  54. package/dist/react-palette.d.cts +8 -0
  55. package/dist/react-palette.d.ts +8 -0
  56. package/dist/react-palette.js +1 -1
  57. package/dist/react-palette.js.map +1 -1
  58. package/dist/react-selection.cjs +1 -1
  59. package/dist/react-selection.cjs.map +1 -1
  60. package/dist/react-selection.d.cts +8 -0
  61. package/dist/react-selection.d.ts +8 -0
  62. package/dist/react-selection.js +1 -1
  63. package/dist/react-selection.js.map +1 -1
  64. package/dist/react-sheet.cjs +1 -1
  65. package/dist/react-sheet.cjs.map +1 -1
  66. package/dist/react-sheet.d.cts +8 -0
  67. package/dist/react-sheet.d.ts +8 -0
  68. package/dist/react-sheet.js +1 -1
  69. package/dist/react-sheet.js.map +1 -1
  70. package/dist/surface.cjs +1 -1
  71. package/dist/surface.cjs.map +1 -1
  72. package/dist/surface.d.cts +12 -3
  73. package/dist/surface.d.ts +12 -3
  74. package/dist/surface.js +1 -1
  75. package/dist/surface.js.map +1 -1
  76. package/package.json +3 -3
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- "use strict";var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target,mod)),__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var react_host_exports={};__export(react_host_exports,{ActionMenuTrigger:()=>ActionMenuTrigger,AlchemyActionsProvider:()=>AlchemyActionsProvider,useAlchemyActions:()=>useAlchemyActions,useRegisterActionProvider:()=>useRegisterActionProvider});module.exports=__toCommonJS(react_host_exports);var React2=__toESM(require("react"),1);var UNIVERSAL_VERBS=["copy","cut","paste","undo","redo","find"];var DuplicateActionError=class extends Error{actionId;providers;constructor(actionId,first,second){super(first===second?`Action "${actionId}" is yielded twice by provider "${first}". Ids are unique; the second was refused.`:`Action "${actionId}" is registered by provider "${first}" and again by "${second}". Ids are unique; "${second}"'s copy was refused.`),this.name="DuplicateActionError",this.actionId=actionId,this.providers=[first,second]}},DuplicateProviderError=class extends Error{providerId;constructor(providerId){super(`Action provider "${providerId}" is already registered. Unregister it first; nothing was overwritten.`),this.name="DuplicateProviderError",this.providerId=providerId}},VERBS=new Set(UNIVERSAL_VERBS);function capture(ports,error,detail){try{ports.diagnostics.capture(error,{area:"actions",detail})}catch{}}function computeEligibility(action,target,options){let restrict=options.restrict;if(target.excludedActionIds.has(action.id))return{status:"absent"};if(restrict?.exclude?.includes(action.id))return{status:"absent"};if(restrict?.categories&&!restrict.categories.includes(action.category))return{status:"absent"};if(action.writes&&action.writes.length>0){if(target.readOnly)return{status:"absent"};let writable=new Set(target.writable.map(w=>w.name));if(!action.writes.some(name=>writable.has(name)))return{status:"absent"}}if(action.requiresAuth&&!target.auth.authenticated&&!options.authResumable)return{status:"absent"};let own=action.eligible(target);return own.status==="unavailable"?own.sentence.trim()?own:{status:"absent"}:own.status!=="unavailable-verb"?own:action.category!=="clipboard"||!VERBS.has(own.verb)?{status:"contract-break",reason:`Action "${action.id}" answered unavailable-verb but only the six universal verbs (category "clipboard") may grey; it is absent instead.`}:own.sentence.trim()?own:{status:"contract-break",reason:`Universal verb "${action.id}" greyed without a sentence; a greyed verb must say why.`}}function createActionRegistry(options){let ports=options.ports,entries=new Map,listeners=new Set,notify=()=>{for(let l of[...listeners])l()};return{register(provider){if(entries.has(provider.id))throw new DuplicateProviderError(provider.id);let declared=provider.declaredIds?.()??[],own=new Set;for(let id of declared){if(own.has(id))throw new DuplicateActionError(id,provider.id,provider.id);own.add(id);for(let[otherId,other]of entries)if(other.declared.includes(id))throw new DuplicateActionError(id,otherId,provider.id)}let entry={provider,declared};return entries.set(provider.id,entry),notify(),()=>{entries.get(provider.id)===entry&&(entries.delete(provider.id),notify())}},async resolve(target,restrict){let authResumable=!!ports.auth,seen=new Map,out=[];for(let[providerId,{provider}]of[...entries]){let actions;try{actions=await provider.actions(target)}catch(error){capture(ports,error,{providerId,stage:"actions"});continue}for(let raw of actions){let first=seen.get(raw.id);if(first!==void 0){capture(ports,new DuplicateActionError(raw.id,first,providerId),{actionId:raw.id});continue}seen.set(raw.id,providerId);let action=raw.provider===providerId?raw:{...raw,provider:providerId},eligibility;try{eligibility=computeEligibility(action,target,{restrict:restrict??{},authResumable})}catch(error){capture(ports,error,{actionId:action.id,stage:"eligible"});continue}if(eligibility.status==="contract-break"){capture(ports,new Error(eligibility.reason),{actionId:action.id,stage:"eligible"});continue}eligibility.status!=="absent"&&out.push({action,eligibility})}}return out},providers(){return[...entries.keys()]},subscribe(listener){return listeners.add(listener),()=>{listeners.delete(listener)}}}}var React=__toESM(require("react"),1),KEY=Symbol.for("ai-matrx.alchemy.actions-context"),AlchemyActionsContext=globalThis[KEY]??=React.createContext(null);function useAlchemyActions(){let value=React.useContext(AlchemyActionsContext);if(!value)throw new Error("Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).");return value}var import_jsx_runtime=require("react/jsx-runtime");function AlchemyActionsProvider({ports,registry,children}){let own=React2.useMemo(()=>registry??createActionRegistry({ports}),[registry,ports]),value=React2.useMemo(()=>({registry:own,ports}),[own,ports]);return(0,import_jsx_runtime.jsx)(AlchemyActionsContext.Provider,{value,children})}function useRegisterActionProvider(provider){let{registry}=useAlchemyActions();React2.useEffect(()=>provider?registry.register(provider):void 0,[registry,provider])}var LONG_PRESS_MS=480;function isEditable(el){return el?el instanceof HTMLTextAreaElement?!0:el instanceof HTMLInputElement?!["checkbox","radio","button","submit","range","color","file"].includes(el.type):el.isContentEditable===!0:!1}function mobileViewport(){return typeof window>"u"?!1:window.matchMedia?.("(max-width: 767px)").matches??!1}function ActionMenuTrigger({resolveTarget,children,arrangement,density,mobile,editable=!1,loadMenu=()=>import("@ai-matrx/alchemy/react/menu"),loadSheet=()=>import("@ai-matrx/alchemy/react/sheet"),...options}){let[state,setState]=React2.useState(null),[open,setOpen]=React2.useState(!1),[Menu,setMenu]=React2.useState(null),[Sheet,setSheet]=React2.useState(null),press=React2.useRef(null),openAt=React2.useCallback((element,x,y,touch)=>{let editable2=isEditable(element)||isEditable(document.activeElement),selectionText=window.getSelection?.()?.toString()??"",target=resolveTarget({element,selectionText,editable:editable2});if(!target)return!1;let sheet=mobile??(touch||mobileViewport());return setState({target,point:{x,y},sheet}),setOpen(!0),sheet&&!Sheet&&loadSheet().then(m=>setSheet(()=>m.ActionSheet)),!sheet&&!Menu&&loadMenu().then(m=>setMenu(()=>m.ContextMenuPanel)),!0},[resolveTarget,mobile,Sheet,Menu,loadSheet,loadMenu]),child=React2.Children.only(children),props=child.props,compose=(own,theirs)=>e=>{typeof theirs=="function"&&theirs(e),own(e)},trigger=React2.cloneElement(child,{"data-alchemy-trigger":"context",onContextMenu:compose(e=>{if(e.defaultPrevented)return;let el=e.target;isEditable(el)&&!editable||openAt(el,e.clientX,e.clientY,!1)&&e.preventDefault()},props.onContextMenu),onTouchStart:compose(e=>{let t=e.touches[0];if(!t)return;let el=e.target;if(isEditable(el)&&!editable)return;let timer=setTimeout(()=>{press.current=null,openAt(el,t.clientX,t.clientY,!0)},LONG_PRESS_MS);press.current={timer,x:t.clientX,y:t.clientY},e.stopPropagation()},props.onTouchStart),onTouchMove:compose(e=>{let t=e.touches[0],p=press.current;p&&t&&Math.hypot(t.clientX-p.x,t.clientY-p.y)>8&&(clearTimeout(p.timer),press.current=null)},props.onTouchMove),onTouchEnd:compose(()=>{press.current&&clearTimeout(press.current.timer),press.current=null},props.onTouchEnd)});return(0,import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment,{children:[trigger,state&&!state.sheet&&Menu?(0,import_jsx_runtime.jsx)(Menu,{...options,target:state.target,point:state.point,open,onOpenChange:setOpen,...arrangement?{arrangement}:{},...density?{density}:{}}):null,state&&state.sheet&&Sheet?(0,import_jsx_runtime.jsx)(Sheet,{...options,target:state.target,open,onOpenChange:setOpen}):null]})}
2
+ "use strict";var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target,mod)),__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var react_host_exports={};__export(react_host_exports,{ActionMenuTrigger:()=>ActionMenuTrigger,AlchemyActionsProvider:()=>AlchemyActionsProvider,useAlchemyActions:()=>useAlchemyActions,useOptionalAlchemyActions:()=>useOptionalAlchemyActions,useRegisterActionProvider:()=>useRegisterActionProvider});module.exports=__toCommonJS(react_host_exports);var React2=__toESM(require("react"),1);var DEFAULT_SENSITIVITY=Object.freeze({exportable:!0,classification:"ordinary",includedByDefault:!0});function resolveSensitivity(value){return{exportable:value.sensitivity?.exportable??DEFAULT_SENSITIVITY.exportable,classification:value.sensitivity?.classification??DEFAULT_SENSITIVITY.classification,includedByDefault:value.sensitivity?.includedByDefault??DEFAULT_SENSITIVITY.includedByDefault}}function qualifyValueKey(itemType,name){return itemType?`${itemType}.${name}`:name}function domainOf(surfaceName){let slash=surfaceName.indexOf("/");return slash>0?surfaceName.slice(0,slash):surfaceName}function createClickTarget(input={}){let declaration=input.declaration??null,item=input.item??null,valueKeys=new Set,restricted=!1,writable=[];if(declaration){for(let value of declaration.values){valueKeys.add(value.name);let s=resolveSensitivity(value);(s.classification!=="ordinary"||!s.exportable)&&(restricted=!0)}let itemType=item?declaration.itemTypes?.find(t=>t.name===item.itemType):void 0;if(itemType){for(let value of itemType.values){valueKeys.add(qualifyValueKey(itemType.name,value.name));let s=resolveSensitivity(value);(s.classification!=="ordinary"||!s.exportable)&&(restricted=!0)}for(let t of itemType.writeTargets??[])writable.push(t)}for(let t of declaration.writeTargets??[])(!t.itemType||t.itemType===item?.itemType)&&writable.push(t)}let readOnly=input.readOnly??item?.readOnly??!1,surfaceName=input.surface?.surfaceName??declaration?.surfaceName??null,scopeChain=input.scopeChain??(surfaceName?["global",domainOf(surfaceName),surfaceName]:["global"]);return{surface:input.surface??null,declaration,valueKeys,scopeChain,excludedActionIds:new Set(input.excludedActionIds??[]),item,level:input.level??item?.level??null,selection:input.selection??null,payloadKinds:input.payloadKinds??[],writable:readOnly?[]:input.writable??writable,readOnly,restricted,organizationId:input.organizationId??null,auth:{authenticated:input.auth?.authenticated??!1,admin:input.auth?.admin??!1,creator:input.auth?.creator??!1},host:input.host}}var UNIVERSAL_VERBS=["copy","cut","paste","undo","redo","find"];var DuplicateActionError=class extends Error{actionId;providers;constructor(actionId,first,second){super(first===second?`Action "${actionId}" is yielded twice by provider "${first}". Ids are unique; the second was refused.`:`Action "${actionId}" is registered by provider "${first}" and again by "${second}". Ids are unique; "${second}"'s copy was refused.`),this.name="DuplicateActionError",this.actionId=actionId,this.providers=[first,second]}},DuplicateProviderError=class extends Error{providerId;constructor(providerId){super(`Action provider "${providerId}" is already registered. Unregister it first; nothing was overwritten.`),this.name="DuplicateProviderError",this.providerId=providerId}},VERBS=new Set(UNIVERSAL_VERBS);function capture(ports,error,detail){try{ports.diagnostics.capture(error,{area:"actions",detail})}catch{}}function computeEligibility(action,target,options){let restrict=options.restrict;if(!isMenuAction(action))return{status:"absent"};if(target.excludedActionIds.has(action.id))return{status:"absent"};if(restrict?.exclude?.includes(action.id))return{status:"absent"};if(restrict?.categories&&!restrict.categories.includes(action.category))return{status:"absent"};if(action.writes&&action.writes.length>0){if(target.readOnly)return{status:"absent"};let writable=new Set(target.writable.map(w=>w.name));if(!action.writes.some(name=>writable.has(name)))return{status:"absent"}}if(action.requiresAuth&&!target.auth.authenticated&&!options.authResumable)return{status:"absent"};let own=action.eligible(target);return own.status==="unavailable"?own.sentence.trim()?own:{status:"absent"}:own.status!=="unavailable-verb"?own:action.category!=="clipboard"||!VERBS.has(own.verb)?{status:"contract-break",reason:`Action "${action.id}" answered unavailable-verb but only the six universal verbs (category "clipboard") may grey; it is absent instead.`}:own.sentence.trim()?own:{status:"contract-break",reason:`Universal verb "${action.id}" greyed without a sentence; a greyed verb must say why.`}}function isMenuAction(action){return!action.surfaces||action.surfaces.includes("menu")}function createActionRegistry(options){let ports=options.ports,entries=new Map,listeners=new Set,notify=()=>{for(let l of[...listeners])l()};return{register(provider){if(entries.has(provider.id))throw new DuplicateProviderError(provider.id);let declared=provider.declaredIds?.()??[],own=new Set;for(let id of declared){if(own.has(id))throw new DuplicateActionError(id,provider.id,provider.id);own.add(id);for(let[otherId,other]of entries)if(other.declared.includes(id))throw new DuplicateActionError(id,otherId,provider.id)}let entry={provider,declared};return entries.set(provider.id,entry),notify(),()=>{entries.get(provider.id)===entry&&(entries.delete(provider.id),notify())}},async resolve(target,restrict){let authResumable=!!ports.auth,seen=new Map,out=[];for(let[providerId,{provider}]of[...entries]){let actions;try{actions=await provider.actions(target)}catch(error){capture(ports,error,{providerId,stage:"actions"});continue}for(let raw of actions){let first=seen.get(raw.id);if(first!==void 0){capture(ports,new DuplicateActionError(raw.id,first,providerId),{actionId:raw.id});continue}seen.set(raw.id,providerId);let action=raw.provider===providerId?raw:{...raw,provider:providerId},eligibility;try{eligibility=computeEligibility(action,target,{restrict:restrict??{},authResumable})}catch(error){capture(ports,error,{actionId:action.id,stage:"eligible"});continue}if(eligibility.status==="contract-break"){capture(ports,new Error(eligibility.reason),{actionId:action.id,stage:"eligible"});continue}eligibility.status!=="absent"&&out.push({action,eligibility})}}return out},providers(){return[...entries.keys()]},async get(id,target){let at=target??createClickTarget(),all=[...entries],declaring=all.filter(([,e])=>e.declared.includes(id)),order=declaring.length>0?declaring:all;for(let[providerId,{provider}]of order){let actions;try{actions=await provider.actions(at)}catch(error){capture(ports,error,{providerId,stage:"actions",actionId:id});continue}let found=actions.find(a=>a.id===id);if(found)return found.provider===providerId?found:{...found,provider:providerId}}return null},subscribe(listener){return listeners.add(listener),()=>{listeners.delete(listener)}}}}var React=__toESM(require("react"),1),KEY=Symbol.for("ai-matrx.alchemy.actions-context"),AlchemyActionsContext=globalThis[KEY]??=React.createContext(null);function useAlchemyActions(){let value=React.useContext(AlchemyActionsContext);if(!value)throw new Error("Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).");return value}function useOptionalAlchemyActions(){return React.useContext(AlchemyActionsContext)}var import_jsx_runtime=require("react/jsx-runtime");function AlchemyActionsProvider({ports,registry,children}){let own=React2.useMemo(()=>registry??createActionRegistry({ports}),[registry,ports]),value=React2.useMemo(()=>({registry:own,ports}),[own,ports]);return(0,import_jsx_runtime.jsx)(AlchemyActionsContext.Provider,{value,children})}function useRegisterActionProvider(provider){let{registry}=useAlchemyActions();React2.useEffect(()=>provider?registry.register(provider):void 0,[registry,provider])}var LONG_PRESS_MS=480;function isEditable(el){return el?el instanceof HTMLTextAreaElement?!0:el instanceof HTMLInputElement?!["checkbox","radio","button","submit","range","color","file"].includes(el.type):el.isContentEditable===!0:!1}function mobileViewport(){return typeof window>"u"?!1:window.matchMedia?.("(max-width: 767px)").matches??!1}function ActionMenuTrigger({resolveTarget,children,arrangement,density,mobile,editable=!1,loadMenu=()=>import("@ai-matrx/alchemy/react/menu"),loadSheet=()=>import("@ai-matrx/alchemy/react/sheet"),...options}){let[state,setState]=React2.useState(null),[open,setOpen]=React2.useState(!1),[Menu,setMenu]=React2.useState(null),[Sheet,setSheet]=React2.useState(null),press=React2.useRef(null),openAt=React2.useCallback((element,x,y,touch)=>{let editable2=isEditable(element)||isEditable(document.activeElement),selectionText=window.getSelection?.()?.toString()??"",target=resolveTarget({element,selectionText,editable:editable2});if(!target)return!1;let sheet=mobile??(touch||mobileViewport());return setState({target,point:{x,y},sheet}),setOpen(!0),sheet&&!Sheet&&loadSheet().then(m=>setSheet(()=>m.ActionSheet)),!sheet&&!Menu&&loadMenu().then(m=>setMenu(()=>m.ContextMenuPanel)),!0},[resolveTarget,mobile,Sheet,Menu,loadSheet,loadMenu]),child=React2.Children.only(children),props=child.props,compose=(own,theirs)=>e=>{typeof theirs=="function"&&theirs(e),own(e)},trigger=React2.cloneElement(child,{"data-alchemy-trigger":"context",onContextMenu:compose(e=>{if(e.defaultPrevented)return;let el=e.target;isEditable(el)&&!editable||openAt(el,e.clientX,e.clientY,!1)&&e.preventDefault()},props.onContextMenu),onTouchStart:compose(e=>{let t=e.touches[0];if(!t)return;let el=e.target;if(isEditable(el)&&!editable)return;let timer=setTimeout(()=>{press.current=null,openAt(el,t.clientX,t.clientY,!0)},LONG_PRESS_MS);press.current={timer,x:t.clientX,y:t.clientY},e.stopPropagation()},props.onTouchStart),onTouchMove:compose(e=>{let t=e.touches[0],p=press.current;p&&t&&Math.hypot(t.clientX-p.x,t.clientY-p.y)>8&&(clearTimeout(p.timer),press.current=null)},props.onTouchMove),onTouchEnd:compose(()=>{press.current&&clearTimeout(press.current.timer),press.current=null},props.onTouchEnd)});return(0,import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment,{children:[trigger,state&&!state.sheet&&Menu?(0,import_jsx_runtime.jsx)(Menu,{...options,target:state.target,point:state.point,open,onOpenChange:setOpen,...arrangement?{arrangement}:{},...density?{density}:{}}):null,state&&state.sheet&&Sheet?(0,import_jsx_runtime.jsx)(Sheet,{...options,target:state.target,open,onOpenChange:setOpen}):null]})}
3
3
  //# sourceMappingURL=react-host.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react-host.ts","../src/react/host.tsx","../src/actions/click-target.ts","../src/actions/registry.ts","../src/react/context.ts"],"sourcesContent":["/** `@ai-matrx/alchemy/react/host` — T0 shell: provider, provider registration, gesture trigger. */\nexport {\n ActionMenuTrigger,\n AlchemyActionsProvider,\n useAlchemyActions,\n useRegisterActionProvider,\n type ActionMenuTriggerProps,\n type AlchemyActionsProviderProps,\n type AlchemyActionsValue,\n} from \"./react/host\";\n","\"use client\";\n/**\n * `@ai-matrx/alchemy/react/host` — T0, the shell every mount pays for.\n *\n * Provider (registry + ports), provider registration, and the gesture shell\n * (right-click on desktop, long-press on touch). It imports NO model, NO\n * layout, NO renderer: the context menu (`/react/menu`, T1) and the mobile\n * sheet (`/react/sheet`, T1m) load by dynamic import on first open only.\n */\nimport * as React from \"react\";\nimport { createActionRegistry } from \"../actions/registry\";\nimport type { ActionProvider, ActionRegistry, ClickTarget } from \"../actions/types\";\nimport type { AlchemyHostPorts } from \"../ports\";\nimport { AlchemyActionsContext, useAlchemyActions, type AlchemyActionsValue } from \"./context\";\nimport type { MenuEngineOptions } from \"./engine\";\n\nexport { useAlchemyActions, type AlchemyActionsValue };\n\nexport interface AlchemyActionsProviderProps {\n ports: AlchemyHostPorts;\n /** Share one registry across providers; default: one per provider mount. */\n registry?: ActionRegistry;\n children: React.ReactNode;\n}\n\nexport function AlchemyActionsProvider({ ports, registry, children }: AlchemyActionsProviderProps): React.ReactElement {\n const own = React.useMemo(() => registry ?? createActionRegistry({ ports }), [registry, ports]);\n const value = React.useMemo(() => ({ registry: own, ports }), [own, ports]);\n return <AlchemyActionsContext.Provider value={value}>{children}</AlchemyActionsContext.Provider>;\n}\n\n/** Register a provider for the lifetime of the calling component. Duplicate ids throw (refused). */\nexport function useRegisterActionProvider(provider: ActionProvider | null): void {\n const { registry } = useAlchemyActions();\n React.useEffect(() => (provider ? registry.register(provider) : undefined), [registry, provider]);\n}\n\ntype MenuModule = typeof import(\"./menu\");\ntype SheetModule = typeof import(\"./sheet\");\n\nexport interface ActionMenuTriggerProps extends MenuEngineOptions {\n /** Build the target at gesture time (selection captured then). */\n resolveTarget(gesture: { element: Element; selectionText: string; editable: boolean }): ClickTarget | null;\n children: React.ReactElement;\n arrangement?: \"classic\" | \"tiered\" | \"command\";\n density?: \"comfortable\" | \"compact\";\n /** Force a renderer; default: sheet below 768px or on coarse pointers. */\n mobile?: boolean;\n /**\n * The wrapped surface is an editor that wires text mutation into the menu,\n * so the menu takes its right-click. Otherwise a live text field keeps its\n * native menu (Paste, spellcheck, autofill).\n */\n editable?: boolean;\n /** Override the lazy loaders (tests, custom bundlers). */\n loadMenu?: () => Promise<Pick<MenuModule, \"ContextMenuPanel\">>;\n loadSheet?: () => Promise<Pick<SheetModule, \"ActionSheet\">>;\n}\n\nconst LONG_PRESS_MS = 480;\n\nfunction isEditable(el: Element | null): boolean {\n if (!el) return false;\n if (el instanceof HTMLTextAreaElement) return true;\n if (el instanceof HTMLInputElement) return ![\"checkbox\", \"radio\", \"button\", \"submit\", \"range\", \"color\", \"file\"].includes(el.type);\n return (el as HTMLElement).isContentEditable === true;\n}\n\nfunction mobileViewport(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia?.(\"(max-width: 767px)\").matches ?? false;\n}\n\nexport function ActionMenuTrigger({\n resolveTarget,\n children,\n arrangement,\n density,\n mobile,\n editable = false,\n loadMenu = () => import(\"@ai-matrx/alchemy/react/menu\"),\n loadSheet = () => import(\"@ai-matrx/alchemy/react/sheet\"),\n ...options\n}: ActionMenuTriggerProps): React.ReactElement {\n const [state, setState] = React.useState<{ target: ClickTarget; point: { x: number; y: number }; sheet: boolean } | null>(null);\n const [open, setOpen] = React.useState(false);\n const [Menu, setMenu] = React.useState<MenuModule[\"ContextMenuPanel\"] | null>(null);\n const [Sheet, setSheet] = React.useState<SheetModule[\"ActionSheet\"] | null>(null);\n const press = React.useRef<{ timer: ReturnType<typeof setTimeout>; x: number; y: number } | null>(null);\n\n const openAt = React.useCallback(\n (element: Element, x: number, y: number, touch: boolean) => {\n const editable = isEditable(element) || isEditable(document.activeElement);\n const selectionText = window.getSelection?.()?.toString() ?? \"\";\n const target = resolveTarget({ element, selectionText, editable });\n if (!target) return false;\n const sheet = mobile ?? (touch || mobileViewport());\n setState({ target, point: { x, y }, sheet });\n setOpen(true);\n if (sheet && !Sheet) void loadSheet().then((m) => setSheet(() => m.ActionSheet));\n if (!sheet && !Menu) void loadMenu().then((m) => setMenu(() => m.ContextMenuPanel));\n return true;\n },\n [resolveTarget, mobile, Sheet, Menu, loadSheet, loadMenu],\n );\n\n const child = React.Children.only(children) as React.ReactElement<Record<string, unknown>>;\n const props = child.props;\n const compose =\n <E,>(own: (e: E) => void, theirs: unknown) =>\n (e: E) => {\n if (typeof theirs === \"function\") (theirs as (e: E) => void)(e);\n own(e);\n };\n\n const trigger = React.cloneElement(child, {\n \"data-alchemy-trigger\": \"context\",\n onContextMenu: compose((e: React.MouseEvent) => {\n if (e.defaultPrevented) return; // an inner menu owns the gesture\n const el = e.target as Element;\n if (isEditable(el) && !editable) return; // never steal a live text field's native menu\n if (openAt(el, e.clientX, e.clientY, false)) e.preventDefault();\n }, props.onContextMenu),\n onTouchStart: compose((e: React.TouchEvent) => {\n const t = e.touches[0];\n if (!t) return;\n const el = e.target as Element;\n if (isEditable(el) && !editable) return;\n const timer = setTimeout(() => {\n press.current = null;\n openAt(el, t.clientX, t.clientY, true);\n }, LONG_PRESS_MS);\n press.current = { timer, x: t.clientX, y: t.clientY };\n e.stopPropagation();\n }, props.onTouchStart),\n onTouchMove: compose((e: React.TouchEvent) => {\n const t = e.touches[0];\n const p = press.current;\n if (p && t && Math.hypot(t.clientX - p.x, t.clientY - p.y) > 8) {\n clearTimeout(p.timer);\n press.current = null;\n }\n }, props.onTouchMove),\n onTouchEnd: compose(() => {\n if (press.current) clearTimeout(press.current.timer);\n press.current = null;\n }, props.onTouchEnd),\n });\n\n return (\n <>\n {trigger}\n {state && !state.sheet && Menu ? (\n <Menu\n {...options}\n target={state.target}\n point={state.point}\n open={open}\n onOpenChange={setOpen}\n {...(arrangement ? { arrangement } : {})}\n {...(density ? { density } : {})}\n />\n ) : null}\n {state && state.sheet && Sheet ? <Sheet {...options} target={state.target} open={open} onOpenChange={setOpen} /> : null}\n </>\n );\n}\n","/**\n * The click target (CONTRACT §2.4) in its own module: `/ports` references it,\n * and `/ports` must stay free of every other package (types only).\n */\nimport type { QualifiedValueKey, ResolvedItem, ResolvedSurfaceDeclaration, WriteTarget } from \"../declare/types\";\n\n/** The six universal verbs — the ONLY rows that may grey (R1 c). */\nexport const UNIVERSAL_VERBS = [\"copy\", \"cut\", \"paste\", \"undo\", \"redo\", \"find\"] as const;\nexport type UniversalVerb = (typeof UNIVERSAL_VERBS)[number];\n\n/** The transfer Payload's kinds (`actions/types.ts` asserts equality at compile time). */\nexport type PayloadKind = \"text\" | \"markdown\" | \"json\" | \"rows\" | \"registered\";\n\n/** What eligibility is computed from: everything declared at the click target. */\nexport interface ClickTarget {\n surface: { instanceId: string; surfaceName: string } | null;\n /** The resolved declaration (inheritance applied). Eligibility decides by KEY existence over it. */\n declaration: ResolvedSurfaceDeclaration | null;\n /** Declared value keys with a read path here (screen names + qualified item keys). Key existence, not population. */\n valueKeys: ReadonlySet<QualifiedValueKey>;\n /** Scope chain, most general first: [\"global\", \"<domain>\", \"<surfaceName>\"]. */\n scopeChain: readonly string[];\n /** The surface's explicit exclusions (the one override). */\n excludedActionIds: ReadonlySet<string>;\n item: ResolvedItem | null;\n level: string | null;\n /** Offsets + the live range handle, editable or not. */\n selection: {\n text: string;\n type: \"editable\" | \"non-editable\";\n start: number;\n end: number;\n /** Opaque host handle (DOM Range / element) used to restore or stream into the selection. */\n handle?: unknown;\n } | null;\n /** Payload kinds a capture of this target would produce (from declared value types/kinds). */\n payloadKinds: readonly PayloadKind[];\n /** Declared targets writable on this target right now. */\n writable: readonly WriteTarget[];\n readOnly: boolean;\n /** True when any declared value here is secret/credential or non-exportable. */\n restricted: boolean;\n organizationId: string | null;\n auth: { authenticated: boolean; admin: boolean; creator: boolean };\n /**\n * Host-owned context a provider reads to act (the rich-document source, the\n * editable field's callbacks). Opaque to the package; never crosses into the\n * model. Providers that need it narrow it themselves.\n */\n host?: unknown;\n}\n\n","/**\n * The one action registry (CONTRACT §2.4).\n *\n * - Duplicate ids are REFUSED, never overwritten (rich-document's registry\n * silently overwrote — R12). A duplicate known at registration throws; one\n * that only appears when a provider yields it is dropped and captured.\n * - Eligibility is one pure pass over the resolved click target, under\n * \"restrict, never relax\": a host may remove ids or categories, never add.\n * - R1: an action that does not apply is ABSENT. Only the six universal verbs\n * (category \"clipboard\") may answer `unavailable-verb`, with a sentence; any\n * other action answering it is a contract break → absent + diagnostics.\n */\nimport type { AlchemyHostPorts, Json } from \"../ports\";\nimport {\n UNIVERSAL_VERBS,\n type Action,\n type ActionProvider,\n type ActionRegistry,\n type ClickTarget,\n type Eligibility,\n type ResolveRestriction,\n type ResolvedAction,\n} from \"./types\";\n\nexport class DuplicateActionError extends Error {\n readonly actionId: string;\n readonly providers: readonly [string, string];\n constructor(actionId: string, first: string, second: string) {\n super(\n first === second\n ? `Action \"${actionId}\" is yielded twice by provider \"${first}\". Ids are unique; the second was refused.`\n : `Action \"${actionId}\" is registered by provider \"${first}\" and again by \"${second}\". Ids are unique; \"${second}\"'s copy was refused.`,\n );\n this.name = \"DuplicateActionError\";\n this.actionId = actionId;\n this.providers = [first, second];\n }\n}\n\nexport class DuplicateProviderError extends Error {\n readonly providerId: string;\n constructor(providerId: string) {\n super(`Action provider \"${providerId}\" is already registered. Unregister it first; nothing was overwritten.`);\n this.name = \"DuplicateProviderError\";\n this.providerId = providerId;\n }\n}\n\nexport interface ActionRegistryOptions {\n /** `diagnostics` is required (nothing fails silently); `auth` decides whether requiresAuth actions reach a signed-out person. */\n ports: Pick<AlchemyHostPorts, \"diagnostics\" | \"auth\">;\n}\n\nconst VERBS = new Set<string>(UNIVERSAL_VERBS);\n\nfunction capture(ports: ActionRegistryOptions[\"ports\"], error: unknown, detail: Json): void {\n try {\n ports.diagnostics.capture(error, { area: \"actions\", detail });\n } catch {\n // A broken diagnostics port must never take the menu down with it.\n }\n}\n\n/**\n * Eligibility of one action at one target — the ONE pure pass (CONTRACT §1.6).\n * Exported for providers' tests; hosts call `registry.resolve`.\n */\nexport function computeEligibility(\n action: Action,\n target: ClickTarget,\n options: { restrict?: ResolveRestriction; authResumable: boolean },\n): Eligibility | { status: \"contract-break\"; reason: string } {\n const restrict = options.restrict;\n if (target.excludedActionIds.has(action.id)) return { status: \"absent\" };\n if (restrict?.exclude?.includes(action.id)) return { status: \"absent\" };\n if (restrict?.categories && !restrict.categories.includes(action.category)) return { status: \"absent\" };\n if (action.writes && action.writes.length > 0) {\n // A read-only target offers nothing that would change the record.\n if (target.readOnly) return { status: \"absent\" };\n const writable = new Set(target.writable.map((w) => w.name));\n if (!action.writes.some((name) => writable.has(name))) return { status: \"absent\" };\n }\n if (action.requiresAuth && !target.auth.authenticated && !options.authResumable) {\n // Sign-in-then-resume is the capability; without the auth port it is absent (N8).\n return { status: \"absent\" };\n }\n const own = action.eligible(target);\n if (own.status === \"unavailable\") {\n // A greyed row must say why; without a sentence it is absent.\n return own.sentence.trim() ? own : { status: \"absent\" };\n }\n if (own.status !== \"unavailable-verb\") return own;\n if (action.category !== \"clipboard\" || !VERBS.has(own.verb)) {\n return {\n status: \"contract-break\",\n reason: `Action \"${action.id}\" answered unavailable-verb but only the six universal verbs (category \"clipboard\") may grey; it is absent instead.`,\n };\n }\n if (!own.sentence.trim()) {\n return {\n status: \"contract-break\",\n reason: `Universal verb \"${action.id}\" greyed without a sentence; a greyed verb must say why.`,\n };\n }\n return own;\n}\n\ninterface Entry {\n provider: ActionProvider;\n declared: readonly string[];\n}\n\nexport function createActionRegistry(options: ActionRegistryOptions): ActionRegistry {\n const ports = options.ports;\n const entries = new Map<string, Entry>();\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const l of [...listeners]) l();\n };\n\n return {\n register(provider) {\n if (entries.has(provider.id)) throw new DuplicateProviderError(provider.id);\n const declared = provider.declaredIds?.() ?? [];\n const own = new Set<string>();\n for (const id of declared) {\n if (own.has(id)) throw new DuplicateActionError(id, provider.id, provider.id);\n own.add(id);\n for (const [otherId, other] of entries) {\n if (other.declared.includes(id)) throw new DuplicateActionError(id, otherId, provider.id);\n }\n }\n const entry: Entry = { provider, declared };\n entries.set(provider.id, entry);\n notify();\n return () => {\n if (entries.get(provider.id) === entry) {\n entries.delete(provider.id);\n notify();\n }\n };\n },\n\n async resolve(target, restrict) {\n const authResumable = Boolean(ports.auth);\n const seen = new Map<string, string>();\n const out: ResolvedAction[] = [];\n for (const [providerId, { provider }] of [...entries]) {\n let actions: readonly Action[];\n try {\n actions = await provider.actions(target);\n } catch (error) {\n capture(ports, error, { providerId, stage: \"actions\" });\n continue;\n }\n for (const raw of actions) {\n const first = seen.get(raw.id);\n if (first !== undefined) {\n capture(ports, new DuplicateActionError(raw.id, first, providerId), { actionId: raw.id });\n continue;\n }\n seen.set(raw.id, providerId);\n const action: Action = raw.provider === providerId ? raw : { ...raw, provider: providerId };\n let eligibility: ReturnType<typeof computeEligibility>;\n try {\n eligibility = computeEligibility(action, target, { restrict: restrict ?? {}, authResumable });\n } catch (error) {\n capture(ports, error, { actionId: action.id, stage: \"eligible\" });\n continue;\n }\n if (eligibility.status === \"contract-break\") {\n capture(ports, new Error(eligibility.reason), { actionId: action.id, stage: \"eligible\" });\n continue;\n }\n if (eligibility.status === \"absent\") continue;\n out.push({ action, eligibility });\n }\n }\n return out;\n },\n\n providers() {\n return [...entries.keys()];\n },\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\n/** A provider over a fixed list; its ids are declared up front so duplicates fail at registration. */\nexport function staticActionProvider(\n id: string,\n actions: readonly Action[],\n tier: ActionProvider[\"tier\"] = \"T0\",\n): ActionProvider {\n const list = actions.map((a) => (a.provider === id ? a : { ...a, provider: id }));\n return {\n id,\n tier,\n declaredIds: () => list.map((a) => a.id),\n actions: () => list,\n };\n}\n\n/** Always-available eligibility, for actions with no rule of their own. */\nexport const alwaysEligible = (): Eligibility => ({ status: \"available\" });\n","/**\n * The shared React context (T0-safe: imports only React and types). Shared\n * through globalThis so every per-layout bundle reads the same provider.\n */\nimport * as React from \"react\";\nimport type { ActionRegistry } from \"../actions/types\";\nimport type { AlchemyHostPorts } from \"../ports\";\n\nexport interface AlchemyActionsValue {\n registry: ActionRegistry;\n ports: AlchemyHostPorts;\n}\n\nconst KEY = Symbol.for(\"ai-matrx.alchemy.actions-context\");\ntype Holder = { [KEY]?: React.Context<AlchemyActionsValue | null> };\nexport const AlchemyActionsContext: React.Context<AlchemyActionsValue | null> =\n ((globalThis as Holder)[KEY] ??= React.createContext<AlchemyActionsValue | null>(null));\n\nexport function useAlchemyActions(): AlchemyActionsValue {\n const value = React.useContext(AlchemyActionsContext);\n if (!value) throw new Error(\"Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).\");\n return value;\n}\n"],"mappings":"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","names":["React","editable"]}
1
+ {"version":3,"sources":["../src/react-host.ts","../src/react/host.tsx","../src/declare/sensitivity.ts","../src/declare/keys.ts","../src/actions/target.ts","../src/actions/click-target.ts","../src/actions/registry.ts","../src/react/context.ts"],"sourcesContent":["/** `@ai-matrx/alchemy/react/host` — T0 shell: provider, provider registration, gesture trigger. */\nexport {\n ActionMenuTrigger,\n AlchemyActionsProvider,\n useAlchemyActions,\n useOptionalAlchemyActions,\n useRegisterActionProvider,\n type ActionMenuTriggerProps,\n type AlchemyActionsProviderProps,\n type AlchemyActionsValue,\n} from \"./react/host\";\n","\"use client\";\n/**\n * `@ai-matrx/alchemy/react/host` — T0, the shell every mount pays for.\n *\n * Provider (registry + ports), provider registration, and the gesture shell\n * (right-click on desktop, long-press on touch). It imports NO model, NO\n * layout, NO renderer: the context menu (`/react/menu`, T1) and the mobile\n * sheet (`/react/sheet`, T1m) load by dynamic import on first open only.\n */\nimport * as React from \"react\";\nimport { createActionRegistry } from \"../actions/registry\";\nimport type { ActionProvider, ActionRegistry, ClickTarget } from \"../actions/types\";\nimport type { AlchemyHostPorts } from \"../ports\";\nimport { AlchemyActionsContext, useAlchemyActions, useOptionalAlchemyActions, type AlchemyActionsValue } from \"./context\";\nimport type { MenuEngineOptions } from \"./engine\";\n\nexport { useAlchemyActions, useOptionalAlchemyActions, type AlchemyActionsValue };\n\nexport interface AlchemyActionsProviderProps {\n ports: AlchemyHostPorts;\n /** Share one registry across providers; default: one per provider mount. */\n registry?: ActionRegistry;\n children: React.ReactNode;\n}\n\nexport function AlchemyActionsProvider({ ports, registry, children }: AlchemyActionsProviderProps): React.ReactElement {\n const own = React.useMemo(() => registry ?? createActionRegistry({ ports }), [registry, ports]);\n const value = React.useMemo(() => ({ registry: own, ports }), [own, ports]);\n return <AlchemyActionsContext.Provider value={value}>{children}</AlchemyActionsContext.Provider>;\n}\n\n/** Register a provider for the lifetime of the calling component. Duplicate ids throw (refused). */\nexport function useRegisterActionProvider(provider: ActionProvider | null): void {\n const { registry } = useAlchemyActions();\n React.useEffect(() => (provider ? registry.register(provider) : undefined), [registry, provider]);\n}\n\ntype MenuModule = typeof import(\"./menu\");\ntype SheetModule = typeof import(\"./sheet\");\n\nexport interface ActionMenuTriggerProps extends MenuEngineOptions {\n /** Build the target at gesture time (selection captured then). */\n resolveTarget(gesture: { element: Element; selectionText: string; editable: boolean }): ClickTarget | null;\n children: React.ReactElement;\n arrangement?: \"classic\" | \"tiered\" | \"command\";\n density?: \"comfortable\" | \"compact\";\n /** Force a renderer; default: sheet below 768px or on coarse pointers. */\n mobile?: boolean;\n /**\n * The wrapped surface is an editor that wires text mutation into the menu,\n * so the menu takes its right-click. Otherwise a live text field keeps its\n * native menu (Paste, spellcheck, autofill).\n */\n editable?: boolean;\n /** Override the lazy loaders (tests, custom bundlers). */\n loadMenu?: () => Promise<Pick<MenuModule, \"ContextMenuPanel\">>;\n loadSheet?: () => Promise<Pick<SheetModule, \"ActionSheet\">>;\n}\n\nconst LONG_PRESS_MS = 480;\n\nfunction isEditable(el: Element | null): boolean {\n if (!el) return false;\n if (el instanceof HTMLTextAreaElement) return true;\n if (el instanceof HTMLInputElement) return ![\"checkbox\", \"radio\", \"button\", \"submit\", \"range\", \"color\", \"file\"].includes(el.type);\n return (el as HTMLElement).isContentEditable === true;\n}\n\nfunction mobileViewport(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia?.(\"(max-width: 767px)\").matches ?? false;\n}\n\nexport function ActionMenuTrigger({\n resolveTarget,\n children,\n arrangement,\n density,\n mobile,\n editable = false,\n loadMenu = () => import(\"@ai-matrx/alchemy/react/menu\"),\n loadSheet = () => import(\"@ai-matrx/alchemy/react/sheet\"),\n ...options\n}: ActionMenuTriggerProps): React.ReactElement {\n const [state, setState] = React.useState<{ target: ClickTarget; point: { x: number; y: number }; sheet: boolean } | null>(null);\n const [open, setOpen] = React.useState(false);\n const [Menu, setMenu] = React.useState<MenuModule[\"ContextMenuPanel\"] | null>(null);\n const [Sheet, setSheet] = React.useState<SheetModule[\"ActionSheet\"] | null>(null);\n const press = React.useRef<{ timer: ReturnType<typeof setTimeout>; x: number; y: number } | null>(null);\n\n const openAt = React.useCallback(\n (element: Element, x: number, y: number, touch: boolean) => {\n const editable = isEditable(element) || isEditable(document.activeElement);\n const selectionText = window.getSelection?.()?.toString() ?? \"\";\n const target = resolveTarget({ element, selectionText, editable });\n if (!target) return false;\n const sheet = mobile ?? (touch || mobileViewport());\n setState({ target, point: { x, y }, sheet });\n setOpen(true);\n if (sheet && !Sheet) void loadSheet().then((m) => setSheet(() => m.ActionSheet));\n if (!sheet && !Menu) void loadMenu().then((m) => setMenu(() => m.ContextMenuPanel));\n return true;\n },\n [resolveTarget, mobile, Sheet, Menu, loadSheet, loadMenu],\n );\n\n const child = React.Children.only(children) as React.ReactElement<Record<string, unknown>>;\n const props = child.props;\n const compose =\n <E,>(own: (e: E) => void, theirs: unknown) =>\n (e: E) => {\n if (typeof theirs === \"function\") (theirs as (e: E) => void)(e);\n own(e);\n };\n\n const trigger = React.cloneElement(child, {\n \"data-alchemy-trigger\": \"context\",\n onContextMenu: compose((e: React.MouseEvent) => {\n if (e.defaultPrevented) return; // an inner menu owns the gesture\n const el = e.target as Element;\n if (isEditable(el) && !editable) return; // never steal a live text field's native menu\n if (openAt(el, e.clientX, e.clientY, false)) e.preventDefault();\n }, props.onContextMenu),\n onTouchStart: compose((e: React.TouchEvent) => {\n const t = e.touches[0];\n if (!t) return;\n const el = e.target as Element;\n if (isEditable(el) && !editable) return;\n const timer = setTimeout(() => {\n press.current = null;\n openAt(el, t.clientX, t.clientY, true);\n }, LONG_PRESS_MS);\n press.current = { timer, x: t.clientX, y: t.clientY };\n e.stopPropagation();\n }, props.onTouchStart),\n onTouchMove: compose((e: React.TouchEvent) => {\n const t = e.touches[0];\n const p = press.current;\n if (p && t && Math.hypot(t.clientX - p.x, t.clientY - p.y) > 8) {\n clearTimeout(p.timer);\n press.current = null;\n }\n }, props.onTouchMove),\n onTouchEnd: compose(() => {\n if (press.current) clearTimeout(press.current.timer);\n press.current = null;\n }, props.onTouchEnd),\n });\n\n return (\n <>\n {trigger}\n {state && !state.sheet && Menu ? (\n <Menu\n {...options}\n target={state.target}\n point={state.point}\n open={open}\n onOpenChange={setOpen}\n {...(arrangement ? { arrangement } : {})}\n {...(density ? { density } : {})}\n />\n ) : null}\n {state && state.sheet && Sheet ? <Sheet {...options} target={state.target} open={open} onOpenChange={setOpen} /> : null}\n </>\n );\n}\n","/**\n * Sensitivity defaults and resolution (CONTRACT §2.1). Its own module so the\n * lightweight entries (`/actions`, the T0 React host) can project without\n * pulling the declaration registry.\n */\nimport type { DeclaredValue, Sensitivity } from \"./types\";\n\nexport const DEFAULT_SENSITIVITY: Sensitivity = Object.freeze({\n exportable: true,\n classification: \"ordinary\",\n includedByDefault: true,\n});\n\nexport function resolveSensitivity(value: Pick<DeclaredValue, \"sensitivity\">): Sensitivity {\n return {\n exportable: value.sensitivity?.exportable ?? DEFAULT_SENSITIVITY.exportable,\n classification: value.sensitivity?.classification ?? DEFAULT_SENSITIVITY.classification,\n includedByDefault: value.sensitivity?.includedByDefault ?? DEFAULT_SENSITIVITY.includedByDefault,\n };\n}\n","import type { QualifiedValueKey } from \"./types\";\n\n/** Every value, write-target, item-type and group name (the DB CHECK rule). */\nexport const DECLARATION_NAME_RE = /^[a-z][a-z0-9_]*$/;\n\n/** \"<client>/<local>\" — the surface naming convention. */\nexport const SURFACE_NAME_RE = /^[a-z][a-z0-9-]*\\/[a-z0-9-/]+$/;\n\n/** A bare screen value (`status`) or an item value (`table_row.status`). */\nexport const QUALIFIED_VALUE_KEY_RE = /^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)?$/;\n\nexport interface ParsedValueKey {\n /** \"\" for a screen-level value. */\n itemType: string;\n name: string;\n}\n\n/** Split a qualified key; null when it is not a valid bare or qualified key. */\nexport function parseQualifiedValueKey(key: string): ParsedValueKey | null {\n if (!QUALIFIED_VALUE_KEY_RE.test(key)) return null;\n const dot = key.indexOf(\".\");\n if (dot === -1) return { itemType: \"\", name: key };\n return { itemType: key.slice(0, dot), name: key.slice(dot + 1) };\n}\n\nexport function qualifyValueKey(itemType: string, name: string): QualifiedValueKey {\n return itemType ? `${itemType}.${name}` : name;\n}\n\n/** The client that owns a surface: its name prefix. */\nexport function surfaceClientOf(surfaceName: string): string {\n return surfaceName.split(\"/\")[0] ?? \"\";\n}\n","/**\n * Building a ClickTarget from what is declared at the click (CONTRACT §2.4).\n * Key existence, never value population: a value key is present when the\n * declaration gives it a read path here.\n */\nimport { resolveSensitivity } from \"../declare/sensitivity\";\nimport { qualifyValueKey } from \"../declare/keys\";\nimport type { ResolvedItem, ResolvedSurfaceDeclaration, WriteTarget } from \"../declare/types\";\nimport type { ClickTarget } from \"./types\";\n\nexport interface ClickTargetInput {\n surface?: ClickTarget[\"surface\"];\n declaration?: ResolvedSurfaceDeclaration | null;\n item?: ResolvedItem | null;\n level?: string | null;\n selection?: ClickTarget[\"selection\"];\n payloadKinds?: ClickTarget[\"payloadKinds\"];\n /** Defaults to the item's readOnly (or false). */\n readOnly?: boolean;\n /** Extra exclusions on top of the declaration's own. */\n excludedActionIds?: Iterable<string>;\n scopeChain?: readonly string[];\n organizationId?: string | null;\n auth?: Partial<ClickTarget[\"auth\"]>;\n /** Write targets the host can serve right now; defaults to every declared target for the screen/item. */\n writable?: readonly WriteTarget[];\n host?: unknown;\n}\n\nfunction domainOf(surfaceName: string): string {\n const slash = surfaceName.indexOf(\"/\");\n return slash > 0 ? surfaceName.slice(0, slash) : surfaceName;\n}\n\nexport function createClickTarget(input: ClickTargetInput = {}): ClickTarget {\n const declaration = input.declaration ?? null;\n const item = input.item ?? null;\n const valueKeys = new Set<string>();\n let restricted = false;\n const writable: WriteTarget[] = [];\n if (declaration) {\n for (const value of declaration.values) {\n valueKeys.add(value.name);\n const s = resolveSensitivity(value);\n if (s.classification !== \"ordinary\" || !s.exportable) restricted = true;\n }\n const itemType = item ? declaration.itemTypes?.find((t) => t.name === item.itemType) : undefined;\n if (itemType) {\n for (const value of itemType.values) {\n valueKeys.add(qualifyValueKey(itemType.name, value.name));\n const s = resolveSensitivity(value);\n if (s.classification !== \"ordinary\" || !s.exportable) restricted = true;\n }\n for (const t of itemType.writeTargets ?? []) writable.push(t);\n }\n for (const t of declaration.writeTargets ?? []) {\n if (!t.itemType || t.itemType === item?.itemType) writable.push(t);\n }\n }\n const readOnly = input.readOnly ?? item?.readOnly ?? false;\n const surfaceName = input.surface?.surfaceName ?? declaration?.surfaceName ?? null;\n const scopeChain = input.scopeChain ?? (surfaceName ? [\"global\", domainOf(surfaceName), surfaceName] : [\"global\"]);\n return {\n surface: input.surface ?? null,\n declaration,\n valueKeys,\n scopeChain,\n excludedActionIds: new Set(input.excludedActionIds ?? []),\n item,\n level: input.level ?? item?.level ?? null,\n selection: input.selection ?? null,\n payloadKinds: input.payloadKinds ?? [],\n writable: readOnly ? [] : (input.writable ?? writable),\n readOnly,\n restricted,\n organizationId: input.organizationId ?? null,\n auth: {\n authenticated: input.auth?.authenticated ?? false,\n admin: input.auth?.admin ?? false,\n creator: input.auth?.creator ?? false,\n },\n host: input.host,\n };\n}\n","/**\n * The click target (CONTRACT §2.4) in its own module: `/ports` references it,\n * and `/ports` must stay free of every other package (types only).\n */\nimport type { QualifiedValueKey, ResolvedItem, ResolvedSurfaceDeclaration, WriteTarget } from \"../declare/types\";\n\n/** The six universal verbs — the ONLY rows that may grey (R1 c). */\nexport const UNIVERSAL_VERBS = [\"copy\", \"cut\", \"paste\", \"undo\", \"redo\", \"find\"] as const;\nexport type UniversalVerb = (typeof UNIVERSAL_VERBS)[number];\n\n/** The transfer Payload's kinds (`actions/types.ts` asserts equality at compile time). */\nexport type PayloadKind = \"text\" | \"markdown\" | \"json\" | \"rows\" | \"registered\";\n\n/** What eligibility is computed from: everything declared at the click target. */\nexport interface ClickTarget {\n surface: { instanceId: string; surfaceName: string } | null;\n /** The resolved declaration (inheritance applied). Eligibility decides by KEY existence over it. */\n declaration: ResolvedSurfaceDeclaration | null;\n /** Declared value keys with a read path here (screen names + qualified item keys). Key existence, not population. */\n valueKeys: ReadonlySet<QualifiedValueKey>;\n /** Scope chain, most general first: [\"global\", \"<domain>\", \"<surfaceName>\"]. */\n scopeChain: readonly string[];\n /** The surface's explicit exclusions (the one override). */\n excludedActionIds: ReadonlySet<string>;\n item: ResolvedItem | null;\n level: string | null;\n /** Offsets + the live range handle, editable or not. */\n selection: {\n text: string;\n type: \"editable\" | \"non-editable\";\n start: number;\n end: number;\n /** Opaque host handle (DOM Range / element) used to restore or stream into the selection. */\n handle?: unknown;\n } | null;\n /** Payload kinds a capture of this target would produce (from declared value types/kinds). */\n payloadKinds: readonly PayloadKind[];\n /** Declared targets writable on this target right now. */\n writable: readonly WriteTarget[];\n readOnly: boolean;\n /** True when any declared value here is secret/credential or non-exportable. */\n restricted: boolean;\n organizationId: string | null;\n auth: { authenticated: boolean; admin: boolean; creator: boolean };\n /**\n * Host-owned context a provider reads to act (the rich-document source, the\n * editable field's callbacks). Opaque to the package; never crosses into the\n * model. Providers that need it narrow it themselves.\n */\n host?: unknown;\n}\n\n","/**\n * The one action registry (CONTRACT §2.4).\n *\n * - Duplicate ids are REFUSED, never overwritten (rich-document's registry\n * silently overwrote — R12). A duplicate known at registration throws; one\n * that only appears when a provider yields it is dropped and captured.\n * - Eligibility is one pure pass over the resolved click target, under\n * \"restrict, never relax\": a host may remove ids or categories, never add.\n * - R1: an action that does not apply is ABSENT. Only the six universal verbs\n * (category \"clipboard\") may answer `unavailable-verb`, with a sentence; any\n * other action answering it is a contract break → absent + diagnostics.\n */\nimport type { AlchemyHostPorts, Json } from \"../ports\";\nimport { createClickTarget } from \"./target\";\nimport {\n UNIVERSAL_VERBS,\n type Action,\n type ActionProvider,\n type ActionRegistry,\n type ClickTarget,\n type Eligibility,\n type ResolveRestriction,\n type ResolvedAction,\n} from \"./types\";\n\nexport class DuplicateActionError extends Error {\n readonly actionId: string;\n readonly providers: readonly [string, string];\n constructor(actionId: string, first: string, second: string) {\n super(\n first === second\n ? `Action \"${actionId}\" is yielded twice by provider \"${first}\". Ids are unique; the second was refused.`\n : `Action \"${actionId}\" is registered by provider \"${first}\" and again by \"${second}\". Ids are unique; \"${second}\"'s copy was refused.`,\n );\n this.name = \"DuplicateActionError\";\n this.actionId = actionId;\n this.providers = [first, second];\n }\n}\n\nexport class DuplicateProviderError extends Error {\n readonly providerId: string;\n constructor(providerId: string) {\n super(`Action provider \"${providerId}\" is already registered. Unregister it first; nothing was overwritten.`);\n this.name = \"DuplicateProviderError\";\n this.providerId = providerId;\n }\n}\n\nexport interface ActionRegistryOptions {\n /** `diagnostics` is required (nothing fails silently); `auth` decides whether requiresAuth actions reach a signed-out person. */\n ports: Pick<AlchemyHostPorts, \"diagnostics\" | \"auth\">;\n}\n\nconst VERBS = new Set<string>(UNIVERSAL_VERBS);\n\nfunction capture(ports: ActionRegistryOptions[\"ports\"], error: unknown, detail: Json): void {\n try {\n ports.diagnostics.capture(error, { area: \"actions\", detail });\n } catch {\n // A broken diagnostics port must never take the menu down with it.\n }\n}\n\n/**\n * Eligibility of one action at one target — the ONE pure pass (CONTRACT §1.6).\n * Exported for providers' tests; hosts call `registry.resolve`.\n */\nexport function computeEligibility(\n action: Action,\n target: ClickTarget,\n options: { restrict?: ResolveRestriction; authResumable: boolean },\n): Eligibility | { status: \"contract-break\"; reason: string } {\n const restrict = options.restrict;\n // A programmatic-only Action (surfaces without \"menu\") is never a menu row; its own `eligible` is not asked.\n if (!isMenuAction(action)) return { status: \"absent\" };\n if (target.excludedActionIds.has(action.id)) return { status: \"absent\" };\n if (restrict?.exclude?.includes(action.id)) return { status: \"absent\" };\n if (restrict?.categories && !restrict.categories.includes(action.category)) return { status: \"absent\" };\n if (action.writes && action.writes.length > 0) {\n // A read-only target offers nothing that would change the record.\n if (target.readOnly) return { status: \"absent\" };\n const writable = new Set(target.writable.map((w) => w.name));\n if (!action.writes.some((name) => writable.has(name))) return { status: \"absent\" };\n }\n if (action.requiresAuth && !target.auth.authenticated && !options.authResumable) {\n // Sign-in-then-resume is the capability; without the auth port it is absent (N8).\n return { status: \"absent\" };\n }\n const own = action.eligible(target);\n if (own.status === \"unavailable\") {\n // A greyed row must say why; without a sentence it is absent.\n return own.sentence.trim() ? own : { status: \"absent\" };\n }\n if (own.status !== \"unavailable-verb\") return own;\n if (action.category !== \"clipboard\" || !VERBS.has(own.verb)) {\n return {\n status: \"contract-break\",\n reason: `Action \"${action.id}\" answered unavailable-verb but only the six universal verbs (category \"clipboard\") may grey; it is absent instead.`,\n };\n }\n if (!own.sentence.trim()) {\n return {\n status: \"contract-break\",\n reason: `Universal verb \"${action.id}\" greyed without a sentence; a greyed verb must say why.`,\n };\n }\n return own;\n}\n\n/** True unless the Action declares `surfaces` without \"menu\" (default: a menu Action). */\nexport function isMenuAction(action: Pick<Action, \"surfaces\">): boolean {\n return !action.surfaces || action.surfaces.includes(\"menu\");\n}\n\ninterface Entry {\n provider: ActionProvider;\n declared: readonly string[];\n}\n\nexport function createActionRegistry(options: ActionRegistryOptions): ActionRegistry {\n const ports = options.ports;\n const entries = new Map<string, Entry>();\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const l of [...listeners]) l();\n };\n\n return {\n register(provider) {\n if (entries.has(provider.id)) throw new DuplicateProviderError(provider.id);\n const declared = provider.declaredIds?.() ?? [];\n const own = new Set<string>();\n for (const id of declared) {\n if (own.has(id)) throw new DuplicateActionError(id, provider.id, provider.id);\n own.add(id);\n for (const [otherId, other] of entries) {\n if (other.declared.includes(id)) throw new DuplicateActionError(id, otherId, provider.id);\n }\n }\n const entry: Entry = { provider, declared };\n entries.set(provider.id, entry);\n notify();\n return () => {\n if (entries.get(provider.id) === entry) {\n entries.delete(provider.id);\n notify();\n }\n };\n },\n\n async resolve(target, restrict) {\n const authResumable = Boolean(ports.auth);\n const seen = new Map<string, string>();\n const out: ResolvedAction[] = [];\n for (const [providerId, { provider }] of [...entries]) {\n let actions: readonly Action[];\n try {\n actions = await provider.actions(target);\n } catch (error) {\n capture(ports, error, { providerId, stage: \"actions\" });\n continue;\n }\n for (const raw of actions) {\n const first = seen.get(raw.id);\n if (first !== undefined) {\n capture(ports, new DuplicateActionError(raw.id, first, providerId), { actionId: raw.id });\n continue;\n }\n seen.set(raw.id, providerId);\n const action: Action = raw.provider === providerId ? raw : { ...raw, provider: providerId };\n let eligibility: ReturnType<typeof computeEligibility>;\n try {\n eligibility = computeEligibility(action, target, { restrict: restrict ?? {}, authResumable });\n } catch (error) {\n capture(ports, error, { actionId: action.id, stage: \"eligible\" });\n continue;\n }\n if (eligibility.status === \"contract-break\") {\n capture(ports, new Error(eligibility.reason), { actionId: action.id, stage: \"eligible\" });\n continue;\n }\n if (eligibility.status === \"absent\") continue;\n out.push({ action, eligibility });\n }\n }\n return out;\n },\n\n providers() {\n return [...entries.keys()];\n },\n\n async get(id, target) {\n const at = target ?? createClickTarget();\n const all = [...entries];\n const declaring = all.filter(([, e]) => e.declared.includes(id));\n // Declared ids are unique across providers (refused at registration), so at most one declares it.\n // Nobody declared it: fall back to asking every provider (T1 providers yield ids only on resolve).\n const order = declaring.length > 0 ? declaring : all;\n for (const [providerId, { provider }] of order) {\n let actions: readonly Action[];\n try {\n actions = await provider.actions(at);\n } catch (error) {\n capture(ports, error, { providerId, stage: \"actions\", actionId: id });\n continue;\n }\n const found = actions.find((a) => a.id === id);\n if (found) return found.provider === providerId ? found : { ...found, provider: providerId };\n }\n return null;\n },\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\n/** A provider over a fixed list; its ids are declared up front so duplicates fail at registration. */\nexport function staticActionProvider(\n id: string,\n actions: readonly Action[],\n tier: ActionProvider[\"tier\"] = \"T0\",\n): ActionProvider {\n const list = actions.map((a) => (a.provider === id ? a : { ...a, provider: id }));\n return {\n id,\n tier,\n declaredIds: () => list.map((a) => a.id),\n actions: () => list,\n };\n}\n\n/** Always-available eligibility, for actions with no rule of their own. */\nexport const alwaysEligible = (): Eligibility => ({ status: \"available\" });\n","/**\n * The shared React context (T0-safe: imports only React and types). Shared\n * through globalThis so every per-layout bundle reads the same provider.\n */\nimport * as React from \"react\";\nimport type { ActionRegistry } from \"../actions/types\";\nimport type { AlchemyHostPorts } from \"../ports\";\n\nexport interface AlchemyActionsValue {\n registry: ActionRegistry;\n ports: AlchemyHostPorts;\n}\n\nconst KEY = Symbol.for(\"ai-matrx.alchemy.actions-context\");\ntype Holder = { [KEY]?: React.Context<AlchemyActionsValue | null> };\nexport const AlchemyActionsContext: React.Context<AlchemyActionsValue | null> =\n ((globalThis as Holder)[KEY] ??= React.createContext<AlchemyActionsValue | null>(null));\n\nexport function useAlchemyActions(): AlchemyActionsValue {\n const value = React.useContext(AlchemyActionsContext);\n if (!value) throw new Error(\"Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).\");\n return value;\n}\n\n/**\n * The registry + ports when rendered inside `<AlchemyActionsProvider>`, else null. For callers that\n * must never crash a render outside the host (a kind component, an assist chip, a bare test render):\n * they answer honestly (\"actions aren't available here\") instead of throwing.\n */\nexport function useOptionalAlchemyActions(): AlchemyActionsValue | null {\n return React.useContext(AlchemyActionsContext);\n}\n"],"mappings":"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","names":["React","editable"]}
@@ -122,6 +122,14 @@ interface SurfaceDeclaration {
122
122
  urlPattern?: string;
123
123
  inheritsFrom?: string;
124
124
  intro?: string;
125
+ /**
126
+ * The moment in plain words (`ui_surface.situation`, AP-6): a short paragraph
127
+ * with blanks `{value}`, `{value.path}` or `{value|words when missing}` bound to
128
+ * this surface's declared values and the request's `user`. The server renders
129
+ * it on every agent turn and puts it FIRST (`<situation>`). Omitted: the server
130
+ * builds an automatic sentence from the value labels.
131
+ */
132
+ situation?: string;
125
133
  overlayId?: string;
126
134
  values: readonly DeclaredValue[];
127
135
  groups?: readonly ValueGroup[];
@@ -890,9 +898,25 @@ interface ActionSection {
890
898
  */
891
899
  kind?: "group" | "target";
892
900
  }
901
+ /**
902
+ * Where an Action can be reached. "menu" (the default): menus, bars, palettes and sheets — it runs
903
+ * through `run(target, ctx)`. "invoke": code runs it BY ID through `invokeAction` — it runs through
904
+ * `invoke(input, ctx)`. An Action whose `surfaces` omits "menu" is never resolved into a menu, so it
905
+ * needs no eligibility of its own (`invocableAction` builds one).
906
+ */
907
+ type ActionSurface = "menu" | "invoke";
908
+ /** What a programmatic invocation receives: the host's ports and a capability-scoped context the caller binds. */
909
+ interface InvocationContext {
910
+ ports: AlchemyHostPorts;
911
+ /** Opaque to the package: the capabilities the caller hands this one Action (a launcher, the acting user). */
912
+ context: unknown;
913
+ signal: AbortSignal;
914
+ }
893
915
  interface Action {
894
916
  /** Globally unique. A duplicate is refused (never overwritten). */
895
917
  id: string;
918
+ /** Where it is reachable. Default ["menu"]. ["invoke"] = programmatic only: absent from every menu. */
919
+ surfaces?: readonly ActionSurface[];
896
920
  /** The provider that registered it — filled by the registry. */
897
921
  provider?: string;
898
922
  /** Resolved through I18nPort.t(labelKey ?? id, label). */
@@ -952,6 +976,11 @@ interface Action {
952
976
  /** Shown centred when `expand` resolves empty. */
953
977
  emptyLabel?: string;
954
978
  run(target: ClickTarget, context: ActionRunContext): Promise<TransferOutcome | void> | TransferOutcome | void;
979
+ /**
980
+ * The typed invocation channel (programmatic Actions): input in, a never-thrown result envelope out.
981
+ * Called only by `invokeAction`; a throw here is captured there and answered `action_failed`.
982
+ */
983
+ invoke?(input: unknown, context: InvocationContext): Promise<ActionResult<unknown>> | ActionResult<unknown>;
955
984
  }
956
985
  /** One removed read, recorded (kit content-transfer's omission shape). */
957
986
  interface ReadOmission {
@@ -1013,6 +1042,12 @@ interface ActionRegistry {
1013
1042
  resolve(target: ClickTarget, restrict?: ResolveRestriction): Promise<readonly ResolvedAction[]>;
1014
1043
  /** Registered provider ids, in registration order. */
1015
1044
  providers(): readonly string[];
1045
+ /**
1046
+ * One Action by id, menu-reachable or not, or null. Asks only the provider that declared the id
1047
+ * (`declaredIds`); falls back to every provider when no declaration names it. Never throws: a
1048
+ * provider that throws is captured and skipped. `target` defaults to an empty click target.
1049
+ */
1050
+ get(id: string, target?: ClickTarget): Promise<Action | null>;
1016
1051
  /** Change notification (a provider registered or unregistered). */
1017
1052
  subscribe(listener: () => void): () => void;
1018
1053
  }
@@ -1196,6 +1231,12 @@ interface AlchemyActionsValue {
1196
1231
  ports: AlchemyHostPorts;
1197
1232
  }
1198
1233
  declare function useAlchemyActions(): AlchemyActionsValue;
1234
+ /**
1235
+ * The registry + ports when rendered inside `<AlchemyActionsProvider>`, else null. For callers that
1236
+ * must never crash a render outside the host (a kind component, an assist chip, a bare test render):
1237
+ * they answer honestly ("actions aren't available here") instead of throwing.
1238
+ */
1239
+ declare function useOptionalAlchemyActions(): AlchemyActionsValue | null;
1199
1240
 
1200
1241
  interface AlchemyActionsProviderProps {
1201
1242
  ports: AlchemyHostPorts;
@@ -1232,4 +1273,4 @@ interface ActionMenuTriggerProps extends MenuEngineOptions {
1232
1273
  }
1233
1274
  declare function ActionMenuTrigger({ resolveTarget, children, arrangement, density, mobile, editable, loadMenu, loadSheet, ...options }: ActionMenuTriggerProps): React.ReactElement;
1234
1275
 
1235
- export { ActionMenuTrigger, type ActionMenuTriggerProps, AlchemyActionsProvider, type AlchemyActionsProviderProps, type AlchemyActionsValue, useAlchemyActions, useRegisterActionProvider };
1276
+ export { ActionMenuTrigger, type ActionMenuTriggerProps, AlchemyActionsProvider, type AlchemyActionsProviderProps, type AlchemyActionsValue, useAlchemyActions, useOptionalAlchemyActions, useRegisterActionProvider };
@@ -122,6 +122,14 @@ interface SurfaceDeclaration {
122
122
  urlPattern?: string;
123
123
  inheritsFrom?: string;
124
124
  intro?: string;
125
+ /**
126
+ * The moment in plain words (`ui_surface.situation`, AP-6): a short paragraph
127
+ * with blanks `{value}`, `{value.path}` or `{value|words when missing}` bound to
128
+ * this surface's declared values and the request's `user`. The server renders
129
+ * it on every agent turn and puts it FIRST (`<situation>`). Omitted: the server
130
+ * builds an automatic sentence from the value labels.
131
+ */
132
+ situation?: string;
125
133
  overlayId?: string;
126
134
  values: readonly DeclaredValue[];
127
135
  groups?: readonly ValueGroup[];
@@ -890,9 +898,25 @@ interface ActionSection {
890
898
  */
891
899
  kind?: "group" | "target";
892
900
  }
901
+ /**
902
+ * Where an Action can be reached. "menu" (the default): menus, bars, palettes and sheets — it runs
903
+ * through `run(target, ctx)`. "invoke": code runs it BY ID through `invokeAction` — it runs through
904
+ * `invoke(input, ctx)`. An Action whose `surfaces` omits "menu" is never resolved into a menu, so it
905
+ * needs no eligibility of its own (`invocableAction` builds one).
906
+ */
907
+ type ActionSurface = "menu" | "invoke";
908
+ /** What a programmatic invocation receives: the host's ports and a capability-scoped context the caller binds. */
909
+ interface InvocationContext {
910
+ ports: AlchemyHostPorts;
911
+ /** Opaque to the package: the capabilities the caller hands this one Action (a launcher, the acting user). */
912
+ context: unknown;
913
+ signal: AbortSignal;
914
+ }
893
915
  interface Action {
894
916
  /** Globally unique. A duplicate is refused (never overwritten). */
895
917
  id: string;
918
+ /** Where it is reachable. Default ["menu"]. ["invoke"] = programmatic only: absent from every menu. */
919
+ surfaces?: readonly ActionSurface[];
896
920
  /** The provider that registered it — filled by the registry. */
897
921
  provider?: string;
898
922
  /** Resolved through I18nPort.t(labelKey ?? id, label). */
@@ -952,6 +976,11 @@ interface Action {
952
976
  /** Shown centred when `expand` resolves empty. */
953
977
  emptyLabel?: string;
954
978
  run(target: ClickTarget, context: ActionRunContext): Promise<TransferOutcome | void> | TransferOutcome | void;
979
+ /**
980
+ * The typed invocation channel (programmatic Actions): input in, a never-thrown result envelope out.
981
+ * Called only by `invokeAction`; a throw here is captured there and answered `action_failed`.
982
+ */
983
+ invoke?(input: unknown, context: InvocationContext): Promise<ActionResult<unknown>> | ActionResult<unknown>;
955
984
  }
956
985
  /** One removed read, recorded (kit content-transfer's omission shape). */
957
986
  interface ReadOmission {
@@ -1013,6 +1042,12 @@ interface ActionRegistry {
1013
1042
  resolve(target: ClickTarget, restrict?: ResolveRestriction): Promise<readonly ResolvedAction[]>;
1014
1043
  /** Registered provider ids, in registration order. */
1015
1044
  providers(): readonly string[];
1045
+ /**
1046
+ * One Action by id, menu-reachable or not, or null. Asks only the provider that declared the id
1047
+ * (`declaredIds`); falls back to every provider when no declaration names it. Never throws: a
1048
+ * provider that throws is captured and skipped. `target` defaults to an empty click target.
1049
+ */
1050
+ get(id: string, target?: ClickTarget): Promise<Action | null>;
1016
1051
  /** Change notification (a provider registered or unregistered). */
1017
1052
  subscribe(listener: () => void): () => void;
1018
1053
  }
@@ -1196,6 +1231,12 @@ interface AlchemyActionsValue {
1196
1231
  ports: AlchemyHostPorts;
1197
1232
  }
1198
1233
  declare function useAlchemyActions(): AlchemyActionsValue;
1234
+ /**
1235
+ * The registry + ports when rendered inside `<AlchemyActionsProvider>`, else null. For callers that
1236
+ * must never crash a render outside the host (a kind component, an assist chip, a bare test render):
1237
+ * they answer honestly ("actions aren't available here") instead of throwing.
1238
+ */
1239
+ declare function useOptionalAlchemyActions(): AlchemyActionsValue | null;
1199
1240
 
1200
1241
  interface AlchemyActionsProviderProps {
1201
1242
  ports: AlchemyHostPorts;
@@ -1232,4 +1273,4 @@ interface ActionMenuTriggerProps extends MenuEngineOptions {
1232
1273
  }
1233
1274
  declare function ActionMenuTrigger({ resolveTarget, children, arrangement, density, mobile, editable, loadMenu, loadSheet, ...options }: ActionMenuTriggerProps): React.ReactElement;
1234
1275
 
1235
- export { ActionMenuTrigger, type ActionMenuTriggerProps, AlchemyActionsProvider, type AlchemyActionsProviderProps, type AlchemyActionsValue, useAlchemyActions, useRegisterActionProvider };
1276
+ export { ActionMenuTrigger, type ActionMenuTriggerProps, AlchemyActionsProvider, type AlchemyActionsProviderProps, type AlchemyActionsValue, useAlchemyActions, useOptionalAlchemyActions, useRegisterActionProvider };
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- import*as React2 from"react";var UNIVERSAL_VERBS=["copy","cut","paste","undo","redo","find"];var DuplicateActionError=class extends Error{actionId;providers;constructor(actionId,first,second){super(first===second?`Action "${actionId}" is yielded twice by provider "${first}". Ids are unique; the second was refused.`:`Action "${actionId}" is registered by provider "${first}" and again by "${second}". Ids are unique; "${second}"'s copy was refused.`),this.name="DuplicateActionError",this.actionId=actionId,this.providers=[first,second]}},DuplicateProviderError=class extends Error{providerId;constructor(providerId){super(`Action provider "${providerId}" is already registered. Unregister it first; nothing was overwritten.`),this.name="DuplicateProviderError",this.providerId=providerId}},VERBS=new Set(UNIVERSAL_VERBS);function capture(ports,error,detail){try{ports.diagnostics.capture(error,{area:"actions",detail})}catch{}}function computeEligibility(action,target,options){let restrict=options.restrict;if(target.excludedActionIds.has(action.id))return{status:"absent"};if(restrict?.exclude?.includes(action.id))return{status:"absent"};if(restrict?.categories&&!restrict.categories.includes(action.category))return{status:"absent"};if(action.writes&&action.writes.length>0){if(target.readOnly)return{status:"absent"};let writable=new Set(target.writable.map(w=>w.name));if(!action.writes.some(name=>writable.has(name)))return{status:"absent"}}if(action.requiresAuth&&!target.auth.authenticated&&!options.authResumable)return{status:"absent"};let own=action.eligible(target);return own.status==="unavailable"?own.sentence.trim()?own:{status:"absent"}:own.status!=="unavailable-verb"?own:action.category!=="clipboard"||!VERBS.has(own.verb)?{status:"contract-break",reason:`Action "${action.id}" answered unavailable-verb but only the six universal verbs (category "clipboard") may grey; it is absent instead.`}:own.sentence.trim()?own:{status:"contract-break",reason:`Universal verb "${action.id}" greyed without a sentence; a greyed verb must say why.`}}function createActionRegistry(options){let ports=options.ports,entries=new Map,listeners=new Set,notify=()=>{for(let l of[...listeners])l()};return{register(provider){if(entries.has(provider.id))throw new DuplicateProviderError(provider.id);let declared=provider.declaredIds?.()??[],own=new Set;for(let id of declared){if(own.has(id))throw new DuplicateActionError(id,provider.id,provider.id);own.add(id);for(let[otherId,other]of entries)if(other.declared.includes(id))throw new DuplicateActionError(id,otherId,provider.id)}let entry={provider,declared};return entries.set(provider.id,entry),notify(),()=>{entries.get(provider.id)===entry&&(entries.delete(provider.id),notify())}},async resolve(target,restrict){let authResumable=!!ports.auth,seen=new Map,out=[];for(let[providerId,{provider}]of[...entries]){let actions;try{actions=await provider.actions(target)}catch(error){capture(ports,error,{providerId,stage:"actions"});continue}for(let raw of actions){let first=seen.get(raw.id);if(first!==void 0){capture(ports,new DuplicateActionError(raw.id,first,providerId),{actionId:raw.id});continue}seen.set(raw.id,providerId);let action=raw.provider===providerId?raw:{...raw,provider:providerId},eligibility;try{eligibility=computeEligibility(action,target,{restrict:restrict??{},authResumable})}catch(error){capture(ports,error,{actionId:action.id,stage:"eligible"});continue}if(eligibility.status==="contract-break"){capture(ports,new Error(eligibility.reason),{actionId:action.id,stage:"eligible"});continue}eligibility.status!=="absent"&&out.push({action,eligibility})}}return out},providers(){return[...entries.keys()]},subscribe(listener){return listeners.add(listener),()=>{listeners.delete(listener)}}}}import*as React from"react";var KEY=Symbol.for("ai-matrx.alchemy.actions-context"),AlchemyActionsContext=globalThis[KEY]??=React.createContext(null);function useAlchemyActions(){let value=React.useContext(AlchemyActionsContext);if(!value)throw new Error("Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).");return value}import{Fragment,jsx,jsxs}from"react/jsx-runtime";function AlchemyActionsProvider({ports,registry,children}){let own=React2.useMemo(()=>registry??createActionRegistry({ports}),[registry,ports]),value=React2.useMemo(()=>({registry:own,ports}),[own,ports]);return jsx(AlchemyActionsContext.Provider,{value,children})}function useRegisterActionProvider(provider){let{registry}=useAlchemyActions();React2.useEffect(()=>provider?registry.register(provider):void 0,[registry,provider])}var LONG_PRESS_MS=480;function isEditable(el){return el?el instanceof HTMLTextAreaElement?!0:el instanceof HTMLInputElement?!["checkbox","radio","button","submit","range","color","file"].includes(el.type):el.isContentEditable===!0:!1}function mobileViewport(){return typeof window>"u"?!1:window.matchMedia?.("(max-width: 767px)").matches??!1}function ActionMenuTrigger({resolveTarget,children,arrangement,density,mobile,editable=!1,loadMenu=()=>import("@ai-matrx/alchemy/react/menu"),loadSheet=()=>import("@ai-matrx/alchemy/react/sheet"),...options}){let[state,setState]=React2.useState(null),[open,setOpen]=React2.useState(!1),[Menu,setMenu]=React2.useState(null),[Sheet,setSheet]=React2.useState(null),press=React2.useRef(null),openAt=React2.useCallback((element,x,y,touch)=>{let editable2=isEditable(element)||isEditable(document.activeElement),selectionText=window.getSelection?.()?.toString()??"",target=resolveTarget({element,selectionText,editable:editable2});if(!target)return!1;let sheet=mobile??(touch||mobileViewport());return setState({target,point:{x,y},sheet}),setOpen(!0),sheet&&!Sheet&&loadSheet().then(m=>setSheet(()=>m.ActionSheet)),!sheet&&!Menu&&loadMenu().then(m=>setMenu(()=>m.ContextMenuPanel)),!0},[resolveTarget,mobile,Sheet,Menu,loadSheet,loadMenu]),child=React2.Children.only(children),props=child.props,compose=(own,theirs)=>e=>{typeof theirs=="function"&&theirs(e),own(e)},trigger=React2.cloneElement(child,{"data-alchemy-trigger":"context",onContextMenu:compose(e=>{if(e.defaultPrevented)return;let el=e.target;isEditable(el)&&!editable||openAt(el,e.clientX,e.clientY,!1)&&e.preventDefault()},props.onContextMenu),onTouchStart:compose(e=>{let t=e.touches[0];if(!t)return;let el=e.target;if(isEditable(el)&&!editable)return;let timer=setTimeout(()=>{press.current=null,openAt(el,t.clientX,t.clientY,!0)},LONG_PRESS_MS);press.current={timer,x:t.clientX,y:t.clientY},e.stopPropagation()},props.onTouchStart),onTouchMove:compose(e=>{let t=e.touches[0],p=press.current;p&&t&&Math.hypot(t.clientX-p.x,t.clientY-p.y)>8&&(clearTimeout(p.timer),press.current=null)},props.onTouchMove),onTouchEnd:compose(()=>{press.current&&clearTimeout(press.current.timer),press.current=null},props.onTouchEnd)});return jsxs(Fragment,{children:[trigger,state&&!state.sheet&&Menu?jsx(Menu,{...options,target:state.target,point:state.point,open,onOpenChange:setOpen,...arrangement?{arrangement}:{},...density?{density}:{}}):null,state&&state.sheet&&Sheet?jsx(Sheet,{...options,target:state.target,open,onOpenChange:setOpen}):null]})}export{ActionMenuTrigger,AlchemyActionsProvider,useAlchemyActions,useRegisterActionProvider};
2
+ import*as React2 from"react";var DEFAULT_SENSITIVITY=Object.freeze({exportable:!0,classification:"ordinary",includedByDefault:!0});function resolveSensitivity(value){return{exportable:value.sensitivity?.exportable??DEFAULT_SENSITIVITY.exportable,classification:value.sensitivity?.classification??DEFAULT_SENSITIVITY.classification,includedByDefault:value.sensitivity?.includedByDefault??DEFAULT_SENSITIVITY.includedByDefault}}function qualifyValueKey(itemType,name){return itemType?`${itemType}.${name}`:name}function domainOf(surfaceName){let slash=surfaceName.indexOf("/");return slash>0?surfaceName.slice(0,slash):surfaceName}function createClickTarget(input={}){let declaration=input.declaration??null,item=input.item??null,valueKeys=new Set,restricted=!1,writable=[];if(declaration){for(let value of declaration.values){valueKeys.add(value.name);let s=resolveSensitivity(value);(s.classification!=="ordinary"||!s.exportable)&&(restricted=!0)}let itemType=item?declaration.itemTypes?.find(t=>t.name===item.itemType):void 0;if(itemType){for(let value of itemType.values){valueKeys.add(qualifyValueKey(itemType.name,value.name));let s=resolveSensitivity(value);(s.classification!=="ordinary"||!s.exportable)&&(restricted=!0)}for(let t of itemType.writeTargets??[])writable.push(t)}for(let t of declaration.writeTargets??[])(!t.itemType||t.itemType===item?.itemType)&&writable.push(t)}let readOnly=input.readOnly??item?.readOnly??!1,surfaceName=input.surface?.surfaceName??declaration?.surfaceName??null,scopeChain=input.scopeChain??(surfaceName?["global",domainOf(surfaceName),surfaceName]:["global"]);return{surface:input.surface??null,declaration,valueKeys,scopeChain,excludedActionIds:new Set(input.excludedActionIds??[]),item,level:input.level??item?.level??null,selection:input.selection??null,payloadKinds:input.payloadKinds??[],writable:readOnly?[]:input.writable??writable,readOnly,restricted,organizationId:input.organizationId??null,auth:{authenticated:input.auth?.authenticated??!1,admin:input.auth?.admin??!1,creator:input.auth?.creator??!1},host:input.host}}var UNIVERSAL_VERBS=["copy","cut","paste","undo","redo","find"];var DuplicateActionError=class extends Error{actionId;providers;constructor(actionId,first,second){super(first===second?`Action "${actionId}" is yielded twice by provider "${first}". Ids are unique; the second was refused.`:`Action "${actionId}" is registered by provider "${first}" and again by "${second}". Ids are unique; "${second}"'s copy was refused.`),this.name="DuplicateActionError",this.actionId=actionId,this.providers=[first,second]}},DuplicateProviderError=class extends Error{providerId;constructor(providerId){super(`Action provider "${providerId}" is already registered. Unregister it first; nothing was overwritten.`),this.name="DuplicateProviderError",this.providerId=providerId}},VERBS=new Set(UNIVERSAL_VERBS);function capture(ports,error,detail){try{ports.diagnostics.capture(error,{area:"actions",detail})}catch{}}function computeEligibility(action,target,options){let restrict=options.restrict;if(!isMenuAction(action))return{status:"absent"};if(target.excludedActionIds.has(action.id))return{status:"absent"};if(restrict?.exclude?.includes(action.id))return{status:"absent"};if(restrict?.categories&&!restrict.categories.includes(action.category))return{status:"absent"};if(action.writes&&action.writes.length>0){if(target.readOnly)return{status:"absent"};let writable=new Set(target.writable.map(w=>w.name));if(!action.writes.some(name=>writable.has(name)))return{status:"absent"}}if(action.requiresAuth&&!target.auth.authenticated&&!options.authResumable)return{status:"absent"};let own=action.eligible(target);return own.status==="unavailable"?own.sentence.trim()?own:{status:"absent"}:own.status!=="unavailable-verb"?own:action.category!=="clipboard"||!VERBS.has(own.verb)?{status:"contract-break",reason:`Action "${action.id}" answered unavailable-verb but only the six universal verbs (category "clipboard") may grey; it is absent instead.`}:own.sentence.trim()?own:{status:"contract-break",reason:`Universal verb "${action.id}" greyed without a sentence; a greyed verb must say why.`}}function isMenuAction(action){return!action.surfaces||action.surfaces.includes("menu")}function createActionRegistry(options){let ports=options.ports,entries=new Map,listeners=new Set,notify=()=>{for(let l of[...listeners])l()};return{register(provider){if(entries.has(provider.id))throw new DuplicateProviderError(provider.id);let declared=provider.declaredIds?.()??[],own=new Set;for(let id of declared){if(own.has(id))throw new DuplicateActionError(id,provider.id,provider.id);own.add(id);for(let[otherId,other]of entries)if(other.declared.includes(id))throw new DuplicateActionError(id,otherId,provider.id)}let entry={provider,declared};return entries.set(provider.id,entry),notify(),()=>{entries.get(provider.id)===entry&&(entries.delete(provider.id),notify())}},async resolve(target,restrict){let authResumable=!!ports.auth,seen=new Map,out=[];for(let[providerId,{provider}]of[...entries]){let actions;try{actions=await provider.actions(target)}catch(error){capture(ports,error,{providerId,stage:"actions"});continue}for(let raw of actions){let first=seen.get(raw.id);if(first!==void 0){capture(ports,new DuplicateActionError(raw.id,first,providerId),{actionId:raw.id});continue}seen.set(raw.id,providerId);let action=raw.provider===providerId?raw:{...raw,provider:providerId},eligibility;try{eligibility=computeEligibility(action,target,{restrict:restrict??{},authResumable})}catch(error){capture(ports,error,{actionId:action.id,stage:"eligible"});continue}if(eligibility.status==="contract-break"){capture(ports,new Error(eligibility.reason),{actionId:action.id,stage:"eligible"});continue}eligibility.status!=="absent"&&out.push({action,eligibility})}}return out},providers(){return[...entries.keys()]},async get(id,target){let at=target??createClickTarget(),all=[...entries],declaring=all.filter(([,e])=>e.declared.includes(id)),order=declaring.length>0?declaring:all;for(let[providerId,{provider}]of order){let actions;try{actions=await provider.actions(at)}catch(error){capture(ports,error,{providerId,stage:"actions",actionId:id});continue}let found=actions.find(a=>a.id===id);if(found)return found.provider===providerId?found:{...found,provider:providerId}}return null},subscribe(listener){return listeners.add(listener),()=>{listeners.delete(listener)}}}}import*as React from"react";var KEY=Symbol.for("ai-matrx.alchemy.actions-context"),AlchemyActionsContext=globalThis[KEY]??=React.createContext(null);function useAlchemyActions(){let value=React.useContext(AlchemyActionsContext);if(!value)throw new Error("Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).");return value}function useOptionalAlchemyActions(){return React.useContext(AlchemyActionsContext)}import{Fragment,jsx,jsxs}from"react/jsx-runtime";function AlchemyActionsProvider({ports,registry,children}){let own=React2.useMemo(()=>registry??createActionRegistry({ports}),[registry,ports]),value=React2.useMemo(()=>({registry:own,ports}),[own,ports]);return jsx(AlchemyActionsContext.Provider,{value,children})}function useRegisterActionProvider(provider){let{registry}=useAlchemyActions();React2.useEffect(()=>provider?registry.register(provider):void 0,[registry,provider])}var LONG_PRESS_MS=480;function isEditable(el){return el?el instanceof HTMLTextAreaElement?!0:el instanceof HTMLInputElement?!["checkbox","radio","button","submit","range","color","file"].includes(el.type):el.isContentEditable===!0:!1}function mobileViewport(){return typeof window>"u"?!1:window.matchMedia?.("(max-width: 767px)").matches??!1}function ActionMenuTrigger({resolveTarget,children,arrangement,density,mobile,editable=!1,loadMenu=()=>import("@ai-matrx/alchemy/react/menu"),loadSheet=()=>import("@ai-matrx/alchemy/react/sheet"),...options}){let[state,setState]=React2.useState(null),[open,setOpen]=React2.useState(!1),[Menu,setMenu]=React2.useState(null),[Sheet,setSheet]=React2.useState(null),press=React2.useRef(null),openAt=React2.useCallback((element,x,y,touch)=>{let editable2=isEditable(element)||isEditable(document.activeElement),selectionText=window.getSelection?.()?.toString()??"",target=resolveTarget({element,selectionText,editable:editable2});if(!target)return!1;let sheet=mobile??(touch||mobileViewport());return setState({target,point:{x,y},sheet}),setOpen(!0),sheet&&!Sheet&&loadSheet().then(m=>setSheet(()=>m.ActionSheet)),!sheet&&!Menu&&loadMenu().then(m=>setMenu(()=>m.ContextMenuPanel)),!0},[resolveTarget,mobile,Sheet,Menu,loadSheet,loadMenu]),child=React2.Children.only(children),props=child.props,compose=(own,theirs)=>e=>{typeof theirs=="function"&&theirs(e),own(e)},trigger=React2.cloneElement(child,{"data-alchemy-trigger":"context",onContextMenu:compose(e=>{if(e.defaultPrevented)return;let el=e.target;isEditable(el)&&!editable||openAt(el,e.clientX,e.clientY,!1)&&e.preventDefault()},props.onContextMenu),onTouchStart:compose(e=>{let t=e.touches[0];if(!t)return;let el=e.target;if(isEditable(el)&&!editable)return;let timer=setTimeout(()=>{press.current=null,openAt(el,t.clientX,t.clientY,!0)},LONG_PRESS_MS);press.current={timer,x:t.clientX,y:t.clientY},e.stopPropagation()},props.onTouchStart),onTouchMove:compose(e=>{let t=e.touches[0],p=press.current;p&&t&&Math.hypot(t.clientX-p.x,t.clientY-p.y)>8&&(clearTimeout(p.timer),press.current=null)},props.onTouchMove),onTouchEnd:compose(()=>{press.current&&clearTimeout(press.current.timer),press.current=null},props.onTouchEnd)});return jsxs(Fragment,{children:[trigger,state&&!state.sheet&&Menu?jsx(Menu,{...options,target:state.target,point:state.point,open,onOpenChange:setOpen,...arrangement?{arrangement}:{},...density?{density}:{}}):null,state&&state.sheet&&Sheet?jsx(Sheet,{...options,target:state.target,open,onOpenChange:setOpen}):null]})}export{ActionMenuTrigger,AlchemyActionsProvider,useAlchemyActions,useOptionalAlchemyActions,useRegisterActionProvider};
3
3
  //# sourceMappingURL=react-host.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react/host.tsx","../src/actions/click-target.ts","../src/actions/registry.ts","../src/react/context.ts"],"sourcesContent":["\"use client\";\n/**\n * `@ai-matrx/alchemy/react/host` — T0, the shell every mount pays for.\n *\n * Provider (registry + ports), provider registration, and the gesture shell\n * (right-click on desktop, long-press on touch). It imports NO model, NO\n * layout, NO renderer: the context menu (`/react/menu`, T1) and the mobile\n * sheet (`/react/sheet`, T1m) load by dynamic import on first open only.\n */\nimport * as React from \"react\";\nimport { createActionRegistry } from \"../actions/registry\";\nimport type { ActionProvider, ActionRegistry, ClickTarget } from \"../actions/types\";\nimport type { AlchemyHostPorts } from \"../ports\";\nimport { AlchemyActionsContext, useAlchemyActions, type AlchemyActionsValue } from \"./context\";\nimport type { MenuEngineOptions } from \"./engine\";\n\nexport { useAlchemyActions, type AlchemyActionsValue };\n\nexport interface AlchemyActionsProviderProps {\n ports: AlchemyHostPorts;\n /** Share one registry across providers; default: one per provider mount. */\n registry?: ActionRegistry;\n children: React.ReactNode;\n}\n\nexport function AlchemyActionsProvider({ ports, registry, children }: AlchemyActionsProviderProps): React.ReactElement {\n const own = React.useMemo(() => registry ?? createActionRegistry({ ports }), [registry, ports]);\n const value = React.useMemo(() => ({ registry: own, ports }), [own, ports]);\n return <AlchemyActionsContext.Provider value={value}>{children}</AlchemyActionsContext.Provider>;\n}\n\n/** Register a provider for the lifetime of the calling component. Duplicate ids throw (refused). */\nexport function useRegisterActionProvider(provider: ActionProvider | null): void {\n const { registry } = useAlchemyActions();\n React.useEffect(() => (provider ? registry.register(provider) : undefined), [registry, provider]);\n}\n\ntype MenuModule = typeof import(\"./menu\");\ntype SheetModule = typeof import(\"./sheet\");\n\nexport interface ActionMenuTriggerProps extends MenuEngineOptions {\n /** Build the target at gesture time (selection captured then). */\n resolveTarget(gesture: { element: Element; selectionText: string; editable: boolean }): ClickTarget | null;\n children: React.ReactElement;\n arrangement?: \"classic\" | \"tiered\" | \"command\";\n density?: \"comfortable\" | \"compact\";\n /** Force a renderer; default: sheet below 768px or on coarse pointers. */\n mobile?: boolean;\n /**\n * The wrapped surface is an editor that wires text mutation into the menu,\n * so the menu takes its right-click. Otherwise a live text field keeps its\n * native menu (Paste, spellcheck, autofill).\n */\n editable?: boolean;\n /** Override the lazy loaders (tests, custom bundlers). */\n loadMenu?: () => Promise<Pick<MenuModule, \"ContextMenuPanel\">>;\n loadSheet?: () => Promise<Pick<SheetModule, \"ActionSheet\">>;\n}\n\nconst LONG_PRESS_MS = 480;\n\nfunction isEditable(el: Element | null): boolean {\n if (!el) return false;\n if (el instanceof HTMLTextAreaElement) return true;\n if (el instanceof HTMLInputElement) return ![\"checkbox\", \"radio\", \"button\", \"submit\", \"range\", \"color\", \"file\"].includes(el.type);\n return (el as HTMLElement).isContentEditable === true;\n}\n\nfunction mobileViewport(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia?.(\"(max-width: 767px)\").matches ?? false;\n}\n\nexport function ActionMenuTrigger({\n resolveTarget,\n children,\n arrangement,\n density,\n mobile,\n editable = false,\n loadMenu = () => import(\"@ai-matrx/alchemy/react/menu\"),\n loadSheet = () => import(\"@ai-matrx/alchemy/react/sheet\"),\n ...options\n}: ActionMenuTriggerProps): React.ReactElement {\n const [state, setState] = React.useState<{ target: ClickTarget; point: { x: number; y: number }; sheet: boolean } | null>(null);\n const [open, setOpen] = React.useState(false);\n const [Menu, setMenu] = React.useState<MenuModule[\"ContextMenuPanel\"] | null>(null);\n const [Sheet, setSheet] = React.useState<SheetModule[\"ActionSheet\"] | null>(null);\n const press = React.useRef<{ timer: ReturnType<typeof setTimeout>; x: number; y: number } | null>(null);\n\n const openAt = React.useCallback(\n (element: Element, x: number, y: number, touch: boolean) => {\n const editable = isEditable(element) || isEditable(document.activeElement);\n const selectionText = window.getSelection?.()?.toString() ?? \"\";\n const target = resolveTarget({ element, selectionText, editable });\n if (!target) return false;\n const sheet = mobile ?? (touch || mobileViewport());\n setState({ target, point: { x, y }, sheet });\n setOpen(true);\n if (sheet && !Sheet) void loadSheet().then((m) => setSheet(() => m.ActionSheet));\n if (!sheet && !Menu) void loadMenu().then((m) => setMenu(() => m.ContextMenuPanel));\n return true;\n },\n [resolveTarget, mobile, Sheet, Menu, loadSheet, loadMenu],\n );\n\n const child = React.Children.only(children) as React.ReactElement<Record<string, unknown>>;\n const props = child.props;\n const compose =\n <E,>(own: (e: E) => void, theirs: unknown) =>\n (e: E) => {\n if (typeof theirs === \"function\") (theirs as (e: E) => void)(e);\n own(e);\n };\n\n const trigger = React.cloneElement(child, {\n \"data-alchemy-trigger\": \"context\",\n onContextMenu: compose((e: React.MouseEvent) => {\n if (e.defaultPrevented) return; // an inner menu owns the gesture\n const el = e.target as Element;\n if (isEditable(el) && !editable) return; // never steal a live text field's native menu\n if (openAt(el, e.clientX, e.clientY, false)) e.preventDefault();\n }, props.onContextMenu),\n onTouchStart: compose((e: React.TouchEvent) => {\n const t = e.touches[0];\n if (!t) return;\n const el = e.target as Element;\n if (isEditable(el) && !editable) return;\n const timer = setTimeout(() => {\n press.current = null;\n openAt(el, t.clientX, t.clientY, true);\n }, LONG_PRESS_MS);\n press.current = { timer, x: t.clientX, y: t.clientY };\n e.stopPropagation();\n }, props.onTouchStart),\n onTouchMove: compose((e: React.TouchEvent) => {\n const t = e.touches[0];\n const p = press.current;\n if (p && t && Math.hypot(t.clientX - p.x, t.clientY - p.y) > 8) {\n clearTimeout(p.timer);\n press.current = null;\n }\n }, props.onTouchMove),\n onTouchEnd: compose(() => {\n if (press.current) clearTimeout(press.current.timer);\n press.current = null;\n }, props.onTouchEnd),\n });\n\n return (\n <>\n {trigger}\n {state && !state.sheet && Menu ? (\n <Menu\n {...options}\n target={state.target}\n point={state.point}\n open={open}\n onOpenChange={setOpen}\n {...(arrangement ? { arrangement } : {})}\n {...(density ? { density } : {})}\n />\n ) : null}\n {state && state.sheet && Sheet ? <Sheet {...options} target={state.target} open={open} onOpenChange={setOpen} /> : null}\n </>\n );\n}\n","/**\n * The click target (CONTRACT §2.4) in its own module: `/ports` references it,\n * and `/ports` must stay free of every other package (types only).\n */\nimport type { QualifiedValueKey, ResolvedItem, ResolvedSurfaceDeclaration, WriteTarget } from \"../declare/types\";\n\n/** The six universal verbs — the ONLY rows that may grey (R1 c). */\nexport const UNIVERSAL_VERBS = [\"copy\", \"cut\", \"paste\", \"undo\", \"redo\", \"find\"] as const;\nexport type UniversalVerb = (typeof UNIVERSAL_VERBS)[number];\n\n/** The transfer Payload's kinds (`actions/types.ts` asserts equality at compile time). */\nexport type PayloadKind = \"text\" | \"markdown\" | \"json\" | \"rows\" | \"registered\";\n\n/** What eligibility is computed from: everything declared at the click target. */\nexport interface ClickTarget {\n surface: { instanceId: string; surfaceName: string } | null;\n /** The resolved declaration (inheritance applied). Eligibility decides by KEY existence over it. */\n declaration: ResolvedSurfaceDeclaration | null;\n /** Declared value keys with a read path here (screen names + qualified item keys). Key existence, not population. */\n valueKeys: ReadonlySet<QualifiedValueKey>;\n /** Scope chain, most general first: [\"global\", \"<domain>\", \"<surfaceName>\"]. */\n scopeChain: readonly string[];\n /** The surface's explicit exclusions (the one override). */\n excludedActionIds: ReadonlySet<string>;\n item: ResolvedItem | null;\n level: string | null;\n /** Offsets + the live range handle, editable or not. */\n selection: {\n text: string;\n type: \"editable\" | \"non-editable\";\n start: number;\n end: number;\n /** Opaque host handle (DOM Range / element) used to restore or stream into the selection. */\n handle?: unknown;\n } | null;\n /** Payload kinds a capture of this target would produce (from declared value types/kinds). */\n payloadKinds: readonly PayloadKind[];\n /** Declared targets writable on this target right now. */\n writable: readonly WriteTarget[];\n readOnly: boolean;\n /** True when any declared value here is secret/credential or non-exportable. */\n restricted: boolean;\n organizationId: string | null;\n auth: { authenticated: boolean; admin: boolean; creator: boolean };\n /**\n * Host-owned context a provider reads to act (the rich-document source, the\n * editable field's callbacks). Opaque to the package; never crosses into the\n * model. Providers that need it narrow it themselves.\n */\n host?: unknown;\n}\n\n","/**\n * The one action registry (CONTRACT §2.4).\n *\n * - Duplicate ids are REFUSED, never overwritten (rich-document's registry\n * silently overwrote — R12). A duplicate known at registration throws; one\n * that only appears when a provider yields it is dropped and captured.\n * - Eligibility is one pure pass over the resolved click target, under\n * \"restrict, never relax\": a host may remove ids or categories, never add.\n * - R1: an action that does not apply is ABSENT. Only the six universal verbs\n * (category \"clipboard\") may answer `unavailable-verb`, with a sentence; any\n * other action answering it is a contract break → absent + diagnostics.\n */\nimport type { AlchemyHostPorts, Json } from \"../ports\";\nimport {\n UNIVERSAL_VERBS,\n type Action,\n type ActionProvider,\n type ActionRegistry,\n type ClickTarget,\n type Eligibility,\n type ResolveRestriction,\n type ResolvedAction,\n} from \"./types\";\n\nexport class DuplicateActionError extends Error {\n readonly actionId: string;\n readonly providers: readonly [string, string];\n constructor(actionId: string, first: string, second: string) {\n super(\n first === second\n ? `Action \"${actionId}\" is yielded twice by provider \"${first}\". Ids are unique; the second was refused.`\n : `Action \"${actionId}\" is registered by provider \"${first}\" and again by \"${second}\". Ids are unique; \"${second}\"'s copy was refused.`,\n );\n this.name = \"DuplicateActionError\";\n this.actionId = actionId;\n this.providers = [first, second];\n }\n}\n\nexport class DuplicateProviderError extends Error {\n readonly providerId: string;\n constructor(providerId: string) {\n super(`Action provider \"${providerId}\" is already registered. Unregister it first; nothing was overwritten.`);\n this.name = \"DuplicateProviderError\";\n this.providerId = providerId;\n }\n}\n\nexport interface ActionRegistryOptions {\n /** `diagnostics` is required (nothing fails silently); `auth` decides whether requiresAuth actions reach a signed-out person. */\n ports: Pick<AlchemyHostPorts, \"diagnostics\" | \"auth\">;\n}\n\nconst VERBS = new Set<string>(UNIVERSAL_VERBS);\n\nfunction capture(ports: ActionRegistryOptions[\"ports\"], error: unknown, detail: Json): void {\n try {\n ports.diagnostics.capture(error, { area: \"actions\", detail });\n } catch {\n // A broken diagnostics port must never take the menu down with it.\n }\n}\n\n/**\n * Eligibility of one action at one target — the ONE pure pass (CONTRACT §1.6).\n * Exported for providers' tests; hosts call `registry.resolve`.\n */\nexport function computeEligibility(\n action: Action,\n target: ClickTarget,\n options: { restrict?: ResolveRestriction; authResumable: boolean },\n): Eligibility | { status: \"contract-break\"; reason: string } {\n const restrict = options.restrict;\n if (target.excludedActionIds.has(action.id)) return { status: \"absent\" };\n if (restrict?.exclude?.includes(action.id)) return { status: \"absent\" };\n if (restrict?.categories && !restrict.categories.includes(action.category)) return { status: \"absent\" };\n if (action.writes && action.writes.length > 0) {\n // A read-only target offers nothing that would change the record.\n if (target.readOnly) return { status: \"absent\" };\n const writable = new Set(target.writable.map((w) => w.name));\n if (!action.writes.some((name) => writable.has(name))) return { status: \"absent\" };\n }\n if (action.requiresAuth && !target.auth.authenticated && !options.authResumable) {\n // Sign-in-then-resume is the capability; without the auth port it is absent (N8).\n return { status: \"absent\" };\n }\n const own = action.eligible(target);\n if (own.status === \"unavailable\") {\n // A greyed row must say why; without a sentence it is absent.\n return own.sentence.trim() ? own : { status: \"absent\" };\n }\n if (own.status !== \"unavailable-verb\") return own;\n if (action.category !== \"clipboard\" || !VERBS.has(own.verb)) {\n return {\n status: \"contract-break\",\n reason: `Action \"${action.id}\" answered unavailable-verb but only the six universal verbs (category \"clipboard\") may grey; it is absent instead.`,\n };\n }\n if (!own.sentence.trim()) {\n return {\n status: \"contract-break\",\n reason: `Universal verb \"${action.id}\" greyed without a sentence; a greyed verb must say why.`,\n };\n }\n return own;\n}\n\ninterface Entry {\n provider: ActionProvider;\n declared: readonly string[];\n}\n\nexport function createActionRegistry(options: ActionRegistryOptions): ActionRegistry {\n const ports = options.ports;\n const entries = new Map<string, Entry>();\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const l of [...listeners]) l();\n };\n\n return {\n register(provider) {\n if (entries.has(provider.id)) throw new DuplicateProviderError(provider.id);\n const declared = provider.declaredIds?.() ?? [];\n const own = new Set<string>();\n for (const id of declared) {\n if (own.has(id)) throw new DuplicateActionError(id, provider.id, provider.id);\n own.add(id);\n for (const [otherId, other] of entries) {\n if (other.declared.includes(id)) throw new DuplicateActionError(id, otherId, provider.id);\n }\n }\n const entry: Entry = { provider, declared };\n entries.set(provider.id, entry);\n notify();\n return () => {\n if (entries.get(provider.id) === entry) {\n entries.delete(provider.id);\n notify();\n }\n };\n },\n\n async resolve(target, restrict) {\n const authResumable = Boolean(ports.auth);\n const seen = new Map<string, string>();\n const out: ResolvedAction[] = [];\n for (const [providerId, { provider }] of [...entries]) {\n let actions: readonly Action[];\n try {\n actions = await provider.actions(target);\n } catch (error) {\n capture(ports, error, { providerId, stage: \"actions\" });\n continue;\n }\n for (const raw of actions) {\n const first = seen.get(raw.id);\n if (first !== undefined) {\n capture(ports, new DuplicateActionError(raw.id, first, providerId), { actionId: raw.id });\n continue;\n }\n seen.set(raw.id, providerId);\n const action: Action = raw.provider === providerId ? raw : { ...raw, provider: providerId };\n let eligibility: ReturnType<typeof computeEligibility>;\n try {\n eligibility = computeEligibility(action, target, { restrict: restrict ?? {}, authResumable });\n } catch (error) {\n capture(ports, error, { actionId: action.id, stage: \"eligible\" });\n continue;\n }\n if (eligibility.status === \"contract-break\") {\n capture(ports, new Error(eligibility.reason), { actionId: action.id, stage: \"eligible\" });\n continue;\n }\n if (eligibility.status === \"absent\") continue;\n out.push({ action, eligibility });\n }\n }\n return out;\n },\n\n providers() {\n return [...entries.keys()];\n },\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\n/** A provider over a fixed list; its ids are declared up front so duplicates fail at registration. */\nexport function staticActionProvider(\n id: string,\n actions: readonly Action[],\n tier: ActionProvider[\"tier\"] = \"T0\",\n): ActionProvider {\n const list = actions.map((a) => (a.provider === id ? a : { ...a, provider: id }));\n return {\n id,\n tier,\n declaredIds: () => list.map((a) => a.id),\n actions: () => list,\n };\n}\n\n/** Always-available eligibility, for actions with no rule of their own. */\nexport const alwaysEligible = (): Eligibility => ({ status: \"available\" });\n","/**\n * The shared React context (T0-safe: imports only React and types). Shared\n * through globalThis so every per-layout bundle reads the same provider.\n */\nimport * as React from \"react\";\nimport type { ActionRegistry } from \"../actions/types\";\nimport type { AlchemyHostPorts } from \"../ports\";\n\nexport interface AlchemyActionsValue {\n registry: ActionRegistry;\n ports: AlchemyHostPorts;\n}\n\nconst KEY = Symbol.for(\"ai-matrx.alchemy.actions-context\");\ntype Holder = { [KEY]?: React.Context<AlchemyActionsValue | null> };\nexport const AlchemyActionsContext: React.Context<AlchemyActionsValue | null> =\n ((globalThis as Holder)[KEY] ??= React.createContext<AlchemyActionsValue | null>(null));\n\nexport function useAlchemyActions(): AlchemyActionsValue {\n const value = React.useContext(AlchemyActionsContext);\n if (!value) throw new Error(\"Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).\");\n return value;\n}\n"],"mappings":"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","names":["React","editable"]}
1
+ {"version":3,"sources":["../src/react/host.tsx","../src/declare/sensitivity.ts","../src/declare/keys.ts","../src/actions/target.ts","../src/actions/click-target.ts","../src/actions/registry.ts","../src/react/context.ts"],"sourcesContent":["\"use client\";\n/**\n * `@ai-matrx/alchemy/react/host` — T0, the shell every mount pays for.\n *\n * Provider (registry + ports), provider registration, and the gesture shell\n * (right-click on desktop, long-press on touch). It imports NO model, NO\n * layout, NO renderer: the context menu (`/react/menu`, T1) and the mobile\n * sheet (`/react/sheet`, T1m) load by dynamic import on first open only.\n */\nimport * as React from \"react\";\nimport { createActionRegistry } from \"../actions/registry\";\nimport type { ActionProvider, ActionRegistry, ClickTarget } from \"../actions/types\";\nimport type { AlchemyHostPorts } from \"../ports\";\nimport { AlchemyActionsContext, useAlchemyActions, useOptionalAlchemyActions, type AlchemyActionsValue } from \"./context\";\nimport type { MenuEngineOptions } from \"./engine\";\n\nexport { useAlchemyActions, useOptionalAlchemyActions, type AlchemyActionsValue };\n\nexport interface AlchemyActionsProviderProps {\n ports: AlchemyHostPorts;\n /** Share one registry across providers; default: one per provider mount. */\n registry?: ActionRegistry;\n children: React.ReactNode;\n}\n\nexport function AlchemyActionsProvider({ ports, registry, children }: AlchemyActionsProviderProps): React.ReactElement {\n const own = React.useMemo(() => registry ?? createActionRegistry({ ports }), [registry, ports]);\n const value = React.useMemo(() => ({ registry: own, ports }), [own, ports]);\n return <AlchemyActionsContext.Provider value={value}>{children}</AlchemyActionsContext.Provider>;\n}\n\n/** Register a provider for the lifetime of the calling component. Duplicate ids throw (refused). */\nexport function useRegisterActionProvider(provider: ActionProvider | null): void {\n const { registry } = useAlchemyActions();\n React.useEffect(() => (provider ? registry.register(provider) : undefined), [registry, provider]);\n}\n\ntype MenuModule = typeof import(\"./menu\");\ntype SheetModule = typeof import(\"./sheet\");\n\nexport interface ActionMenuTriggerProps extends MenuEngineOptions {\n /** Build the target at gesture time (selection captured then). */\n resolveTarget(gesture: { element: Element; selectionText: string; editable: boolean }): ClickTarget | null;\n children: React.ReactElement;\n arrangement?: \"classic\" | \"tiered\" | \"command\";\n density?: \"comfortable\" | \"compact\";\n /** Force a renderer; default: sheet below 768px or on coarse pointers. */\n mobile?: boolean;\n /**\n * The wrapped surface is an editor that wires text mutation into the menu,\n * so the menu takes its right-click. Otherwise a live text field keeps its\n * native menu (Paste, spellcheck, autofill).\n */\n editable?: boolean;\n /** Override the lazy loaders (tests, custom bundlers). */\n loadMenu?: () => Promise<Pick<MenuModule, \"ContextMenuPanel\">>;\n loadSheet?: () => Promise<Pick<SheetModule, \"ActionSheet\">>;\n}\n\nconst LONG_PRESS_MS = 480;\n\nfunction isEditable(el: Element | null): boolean {\n if (!el) return false;\n if (el instanceof HTMLTextAreaElement) return true;\n if (el instanceof HTMLInputElement) return ![\"checkbox\", \"radio\", \"button\", \"submit\", \"range\", \"color\", \"file\"].includes(el.type);\n return (el as HTMLElement).isContentEditable === true;\n}\n\nfunction mobileViewport(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia?.(\"(max-width: 767px)\").matches ?? false;\n}\n\nexport function ActionMenuTrigger({\n resolveTarget,\n children,\n arrangement,\n density,\n mobile,\n editable = false,\n loadMenu = () => import(\"@ai-matrx/alchemy/react/menu\"),\n loadSheet = () => import(\"@ai-matrx/alchemy/react/sheet\"),\n ...options\n}: ActionMenuTriggerProps): React.ReactElement {\n const [state, setState] = React.useState<{ target: ClickTarget; point: { x: number; y: number }; sheet: boolean } | null>(null);\n const [open, setOpen] = React.useState(false);\n const [Menu, setMenu] = React.useState<MenuModule[\"ContextMenuPanel\"] | null>(null);\n const [Sheet, setSheet] = React.useState<SheetModule[\"ActionSheet\"] | null>(null);\n const press = React.useRef<{ timer: ReturnType<typeof setTimeout>; x: number; y: number } | null>(null);\n\n const openAt = React.useCallback(\n (element: Element, x: number, y: number, touch: boolean) => {\n const editable = isEditable(element) || isEditable(document.activeElement);\n const selectionText = window.getSelection?.()?.toString() ?? \"\";\n const target = resolveTarget({ element, selectionText, editable });\n if (!target) return false;\n const sheet = mobile ?? (touch || mobileViewport());\n setState({ target, point: { x, y }, sheet });\n setOpen(true);\n if (sheet && !Sheet) void loadSheet().then((m) => setSheet(() => m.ActionSheet));\n if (!sheet && !Menu) void loadMenu().then((m) => setMenu(() => m.ContextMenuPanel));\n return true;\n },\n [resolveTarget, mobile, Sheet, Menu, loadSheet, loadMenu],\n );\n\n const child = React.Children.only(children) as React.ReactElement<Record<string, unknown>>;\n const props = child.props;\n const compose =\n <E,>(own: (e: E) => void, theirs: unknown) =>\n (e: E) => {\n if (typeof theirs === \"function\") (theirs as (e: E) => void)(e);\n own(e);\n };\n\n const trigger = React.cloneElement(child, {\n \"data-alchemy-trigger\": \"context\",\n onContextMenu: compose((e: React.MouseEvent) => {\n if (e.defaultPrevented) return; // an inner menu owns the gesture\n const el = e.target as Element;\n if (isEditable(el) && !editable) return; // never steal a live text field's native menu\n if (openAt(el, e.clientX, e.clientY, false)) e.preventDefault();\n }, props.onContextMenu),\n onTouchStart: compose((e: React.TouchEvent) => {\n const t = e.touches[0];\n if (!t) return;\n const el = e.target as Element;\n if (isEditable(el) && !editable) return;\n const timer = setTimeout(() => {\n press.current = null;\n openAt(el, t.clientX, t.clientY, true);\n }, LONG_PRESS_MS);\n press.current = { timer, x: t.clientX, y: t.clientY };\n e.stopPropagation();\n }, props.onTouchStart),\n onTouchMove: compose((e: React.TouchEvent) => {\n const t = e.touches[0];\n const p = press.current;\n if (p && t && Math.hypot(t.clientX - p.x, t.clientY - p.y) > 8) {\n clearTimeout(p.timer);\n press.current = null;\n }\n }, props.onTouchMove),\n onTouchEnd: compose(() => {\n if (press.current) clearTimeout(press.current.timer);\n press.current = null;\n }, props.onTouchEnd),\n });\n\n return (\n <>\n {trigger}\n {state && !state.sheet && Menu ? (\n <Menu\n {...options}\n target={state.target}\n point={state.point}\n open={open}\n onOpenChange={setOpen}\n {...(arrangement ? { arrangement } : {})}\n {...(density ? { density } : {})}\n />\n ) : null}\n {state && state.sheet && Sheet ? <Sheet {...options} target={state.target} open={open} onOpenChange={setOpen} /> : null}\n </>\n );\n}\n","/**\n * Sensitivity defaults and resolution (CONTRACT §2.1). Its own module so the\n * lightweight entries (`/actions`, the T0 React host) can project without\n * pulling the declaration registry.\n */\nimport type { DeclaredValue, Sensitivity } from \"./types\";\n\nexport const DEFAULT_SENSITIVITY: Sensitivity = Object.freeze({\n exportable: true,\n classification: \"ordinary\",\n includedByDefault: true,\n});\n\nexport function resolveSensitivity(value: Pick<DeclaredValue, \"sensitivity\">): Sensitivity {\n return {\n exportable: value.sensitivity?.exportable ?? DEFAULT_SENSITIVITY.exportable,\n classification: value.sensitivity?.classification ?? DEFAULT_SENSITIVITY.classification,\n includedByDefault: value.sensitivity?.includedByDefault ?? DEFAULT_SENSITIVITY.includedByDefault,\n };\n}\n","import type { QualifiedValueKey } from \"./types\";\n\n/** Every value, write-target, item-type and group name (the DB CHECK rule). */\nexport const DECLARATION_NAME_RE = /^[a-z][a-z0-9_]*$/;\n\n/** \"<client>/<local>\" — the surface naming convention. */\nexport const SURFACE_NAME_RE = /^[a-z][a-z0-9-]*\\/[a-z0-9-/]+$/;\n\n/** A bare screen value (`status`) or an item value (`table_row.status`). */\nexport const QUALIFIED_VALUE_KEY_RE = /^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)?$/;\n\nexport interface ParsedValueKey {\n /** \"\" for a screen-level value. */\n itemType: string;\n name: string;\n}\n\n/** Split a qualified key; null when it is not a valid bare or qualified key. */\nexport function parseQualifiedValueKey(key: string): ParsedValueKey | null {\n if (!QUALIFIED_VALUE_KEY_RE.test(key)) return null;\n const dot = key.indexOf(\".\");\n if (dot === -1) return { itemType: \"\", name: key };\n return { itemType: key.slice(0, dot), name: key.slice(dot + 1) };\n}\n\nexport function qualifyValueKey(itemType: string, name: string): QualifiedValueKey {\n return itemType ? `${itemType}.${name}` : name;\n}\n\n/** The client that owns a surface: its name prefix. */\nexport function surfaceClientOf(surfaceName: string): string {\n return surfaceName.split(\"/\")[0] ?? \"\";\n}\n","/**\n * Building a ClickTarget from what is declared at the click (CONTRACT §2.4).\n * Key existence, never value population: a value key is present when the\n * declaration gives it a read path here.\n */\nimport { resolveSensitivity } from \"../declare/sensitivity\";\nimport { qualifyValueKey } from \"../declare/keys\";\nimport type { ResolvedItem, ResolvedSurfaceDeclaration, WriteTarget } from \"../declare/types\";\nimport type { ClickTarget } from \"./types\";\n\nexport interface ClickTargetInput {\n surface?: ClickTarget[\"surface\"];\n declaration?: ResolvedSurfaceDeclaration | null;\n item?: ResolvedItem | null;\n level?: string | null;\n selection?: ClickTarget[\"selection\"];\n payloadKinds?: ClickTarget[\"payloadKinds\"];\n /** Defaults to the item's readOnly (or false). */\n readOnly?: boolean;\n /** Extra exclusions on top of the declaration's own. */\n excludedActionIds?: Iterable<string>;\n scopeChain?: readonly string[];\n organizationId?: string | null;\n auth?: Partial<ClickTarget[\"auth\"]>;\n /** Write targets the host can serve right now; defaults to every declared target for the screen/item. */\n writable?: readonly WriteTarget[];\n host?: unknown;\n}\n\nfunction domainOf(surfaceName: string): string {\n const slash = surfaceName.indexOf(\"/\");\n return slash > 0 ? surfaceName.slice(0, slash) : surfaceName;\n}\n\nexport function createClickTarget(input: ClickTargetInput = {}): ClickTarget {\n const declaration = input.declaration ?? null;\n const item = input.item ?? null;\n const valueKeys = new Set<string>();\n let restricted = false;\n const writable: WriteTarget[] = [];\n if (declaration) {\n for (const value of declaration.values) {\n valueKeys.add(value.name);\n const s = resolveSensitivity(value);\n if (s.classification !== \"ordinary\" || !s.exportable) restricted = true;\n }\n const itemType = item ? declaration.itemTypes?.find((t) => t.name === item.itemType) : undefined;\n if (itemType) {\n for (const value of itemType.values) {\n valueKeys.add(qualifyValueKey(itemType.name, value.name));\n const s = resolveSensitivity(value);\n if (s.classification !== \"ordinary\" || !s.exportable) restricted = true;\n }\n for (const t of itemType.writeTargets ?? []) writable.push(t);\n }\n for (const t of declaration.writeTargets ?? []) {\n if (!t.itemType || t.itemType === item?.itemType) writable.push(t);\n }\n }\n const readOnly = input.readOnly ?? item?.readOnly ?? false;\n const surfaceName = input.surface?.surfaceName ?? declaration?.surfaceName ?? null;\n const scopeChain = input.scopeChain ?? (surfaceName ? [\"global\", domainOf(surfaceName), surfaceName] : [\"global\"]);\n return {\n surface: input.surface ?? null,\n declaration,\n valueKeys,\n scopeChain,\n excludedActionIds: new Set(input.excludedActionIds ?? []),\n item,\n level: input.level ?? item?.level ?? null,\n selection: input.selection ?? null,\n payloadKinds: input.payloadKinds ?? [],\n writable: readOnly ? [] : (input.writable ?? writable),\n readOnly,\n restricted,\n organizationId: input.organizationId ?? null,\n auth: {\n authenticated: input.auth?.authenticated ?? false,\n admin: input.auth?.admin ?? false,\n creator: input.auth?.creator ?? false,\n },\n host: input.host,\n };\n}\n","/**\n * The click target (CONTRACT §2.4) in its own module: `/ports` references it,\n * and `/ports` must stay free of every other package (types only).\n */\nimport type { QualifiedValueKey, ResolvedItem, ResolvedSurfaceDeclaration, WriteTarget } from \"../declare/types\";\n\n/** The six universal verbs — the ONLY rows that may grey (R1 c). */\nexport const UNIVERSAL_VERBS = [\"copy\", \"cut\", \"paste\", \"undo\", \"redo\", \"find\"] as const;\nexport type UniversalVerb = (typeof UNIVERSAL_VERBS)[number];\n\n/** The transfer Payload's kinds (`actions/types.ts` asserts equality at compile time). */\nexport type PayloadKind = \"text\" | \"markdown\" | \"json\" | \"rows\" | \"registered\";\n\n/** What eligibility is computed from: everything declared at the click target. */\nexport interface ClickTarget {\n surface: { instanceId: string; surfaceName: string } | null;\n /** The resolved declaration (inheritance applied). Eligibility decides by KEY existence over it. */\n declaration: ResolvedSurfaceDeclaration | null;\n /** Declared value keys with a read path here (screen names + qualified item keys). Key existence, not population. */\n valueKeys: ReadonlySet<QualifiedValueKey>;\n /** Scope chain, most general first: [\"global\", \"<domain>\", \"<surfaceName>\"]. */\n scopeChain: readonly string[];\n /** The surface's explicit exclusions (the one override). */\n excludedActionIds: ReadonlySet<string>;\n item: ResolvedItem | null;\n level: string | null;\n /** Offsets + the live range handle, editable or not. */\n selection: {\n text: string;\n type: \"editable\" | \"non-editable\";\n start: number;\n end: number;\n /** Opaque host handle (DOM Range / element) used to restore or stream into the selection. */\n handle?: unknown;\n } | null;\n /** Payload kinds a capture of this target would produce (from declared value types/kinds). */\n payloadKinds: readonly PayloadKind[];\n /** Declared targets writable on this target right now. */\n writable: readonly WriteTarget[];\n readOnly: boolean;\n /** True when any declared value here is secret/credential or non-exportable. */\n restricted: boolean;\n organizationId: string | null;\n auth: { authenticated: boolean; admin: boolean; creator: boolean };\n /**\n * Host-owned context a provider reads to act (the rich-document source, the\n * editable field's callbacks). Opaque to the package; never crosses into the\n * model. Providers that need it narrow it themselves.\n */\n host?: unknown;\n}\n\n","/**\n * The one action registry (CONTRACT §2.4).\n *\n * - Duplicate ids are REFUSED, never overwritten (rich-document's registry\n * silently overwrote — R12). A duplicate known at registration throws; one\n * that only appears when a provider yields it is dropped and captured.\n * - Eligibility is one pure pass over the resolved click target, under\n * \"restrict, never relax\": a host may remove ids or categories, never add.\n * - R1: an action that does not apply is ABSENT. Only the six universal verbs\n * (category \"clipboard\") may answer `unavailable-verb`, with a sentence; any\n * other action answering it is a contract break → absent + diagnostics.\n */\nimport type { AlchemyHostPorts, Json } from \"../ports\";\nimport { createClickTarget } from \"./target\";\nimport {\n UNIVERSAL_VERBS,\n type Action,\n type ActionProvider,\n type ActionRegistry,\n type ClickTarget,\n type Eligibility,\n type ResolveRestriction,\n type ResolvedAction,\n} from \"./types\";\n\nexport class DuplicateActionError extends Error {\n readonly actionId: string;\n readonly providers: readonly [string, string];\n constructor(actionId: string, first: string, second: string) {\n super(\n first === second\n ? `Action \"${actionId}\" is yielded twice by provider \"${first}\". Ids are unique; the second was refused.`\n : `Action \"${actionId}\" is registered by provider \"${first}\" and again by \"${second}\". Ids are unique; \"${second}\"'s copy was refused.`,\n );\n this.name = \"DuplicateActionError\";\n this.actionId = actionId;\n this.providers = [first, second];\n }\n}\n\nexport class DuplicateProviderError extends Error {\n readonly providerId: string;\n constructor(providerId: string) {\n super(`Action provider \"${providerId}\" is already registered. Unregister it first; nothing was overwritten.`);\n this.name = \"DuplicateProviderError\";\n this.providerId = providerId;\n }\n}\n\nexport interface ActionRegistryOptions {\n /** `diagnostics` is required (nothing fails silently); `auth` decides whether requiresAuth actions reach a signed-out person. */\n ports: Pick<AlchemyHostPorts, \"diagnostics\" | \"auth\">;\n}\n\nconst VERBS = new Set<string>(UNIVERSAL_VERBS);\n\nfunction capture(ports: ActionRegistryOptions[\"ports\"], error: unknown, detail: Json): void {\n try {\n ports.diagnostics.capture(error, { area: \"actions\", detail });\n } catch {\n // A broken diagnostics port must never take the menu down with it.\n }\n}\n\n/**\n * Eligibility of one action at one target — the ONE pure pass (CONTRACT §1.6).\n * Exported for providers' tests; hosts call `registry.resolve`.\n */\nexport function computeEligibility(\n action: Action,\n target: ClickTarget,\n options: { restrict?: ResolveRestriction; authResumable: boolean },\n): Eligibility | { status: \"contract-break\"; reason: string } {\n const restrict = options.restrict;\n // A programmatic-only Action (surfaces without \"menu\") is never a menu row; its own `eligible` is not asked.\n if (!isMenuAction(action)) return { status: \"absent\" };\n if (target.excludedActionIds.has(action.id)) return { status: \"absent\" };\n if (restrict?.exclude?.includes(action.id)) return { status: \"absent\" };\n if (restrict?.categories && !restrict.categories.includes(action.category)) return { status: \"absent\" };\n if (action.writes && action.writes.length > 0) {\n // A read-only target offers nothing that would change the record.\n if (target.readOnly) return { status: \"absent\" };\n const writable = new Set(target.writable.map((w) => w.name));\n if (!action.writes.some((name) => writable.has(name))) return { status: \"absent\" };\n }\n if (action.requiresAuth && !target.auth.authenticated && !options.authResumable) {\n // Sign-in-then-resume is the capability; without the auth port it is absent (N8).\n return { status: \"absent\" };\n }\n const own = action.eligible(target);\n if (own.status === \"unavailable\") {\n // A greyed row must say why; without a sentence it is absent.\n return own.sentence.trim() ? own : { status: \"absent\" };\n }\n if (own.status !== \"unavailable-verb\") return own;\n if (action.category !== \"clipboard\" || !VERBS.has(own.verb)) {\n return {\n status: \"contract-break\",\n reason: `Action \"${action.id}\" answered unavailable-verb but only the six universal verbs (category \"clipboard\") may grey; it is absent instead.`,\n };\n }\n if (!own.sentence.trim()) {\n return {\n status: \"contract-break\",\n reason: `Universal verb \"${action.id}\" greyed without a sentence; a greyed verb must say why.`,\n };\n }\n return own;\n}\n\n/** True unless the Action declares `surfaces` without \"menu\" (default: a menu Action). */\nexport function isMenuAction(action: Pick<Action, \"surfaces\">): boolean {\n return !action.surfaces || action.surfaces.includes(\"menu\");\n}\n\ninterface Entry {\n provider: ActionProvider;\n declared: readonly string[];\n}\n\nexport function createActionRegistry(options: ActionRegistryOptions): ActionRegistry {\n const ports = options.ports;\n const entries = new Map<string, Entry>();\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const l of [...listeners]) l();\n };\n\n return {\n register(provider) {\n if (entries.has(provider.id)) throw new DuplicateProviderError(provider.id);\n const declared = provider.declaredIds?.() ?? [];\n const own = new Set<string>();\n for (const id of declared) {\n if (own.has(id)) throw new DuplicateActionError(id, provider.id, provider.id);\n own.add(id);\n for (const [otherId, other] of entries) {\n if (other.declared.includes(id)) throw new DuplicateActionError(id, otherId, provider.id);\n }\n }\n const entry: Entry = { provider, declared };\n entries.set(provider.id, entry);\n notify();\n return () => {\n if (entries.get(provider.id) === entry) {\n entries.delete(provider.id);\n notify();\n }\n };\n },\n\n async resolve(target, restrict) {\n const authResumable = Boolean(ports.auth);\n const seen = new Map<string, string>();\n const out: ResolvedAction[] = [];\n for (const [providerId, { provider }] of [...entries]) {\n let actions: readonly Action[];\n try {\n actions = await provider.actions(target);\n } catch (error) {\n capture(ports, error, { providerId, stage: \"actions\" });\n continue;\n }\n for (const raw of actions) {\n const first = seen.get(raw.id);\n if (first !== undefined) {\n capture(ports, new DuplicateActionError(raw.id, first, providerId), { actionId: raw.id });\n continue;\n }\n seen.set(raw.id, providerId);\n const action: Action = raw.provider === providerId ? raw : { ...raw, provider: providerId };\n let eligibility: ReturnType<typeof computeEligibility>;\n try {\n eligibility = computeEligibility(action, target, { restrict: restrict ?? {}, authResumable });\n } catch (error) {\n capture(ports, error, { actionId: action.id, stage: \"eligible\" });\n continue;\n }\n if (eligibility.status === \"contract-break\") {\n capture(ports, new Error(eligibility.reason), { actionId: action.id, stage: \"eligible\" });\n continue;\n }\n if (eligibility.status === \"absent\") continue;\n out.push({ action, eligibility });\n }\n }\n return out;\n },\n\n providers() {\n return [...entries.keys()];\n },\n\n async get(id, target) {\n const at = target ?? createClickTarget();\n const all = [...entries];\n const declaring = all.filter(([, e]) => e.declared.includes(id));\n // Declared ids are unique across providers (refused at registration), so at most one declares it.\n // Nobody declared it: fall back to asking every provider (T1 providers yield ids only on resolve).\n const order = declaring.length > 0 ? declaring : all;\n for (const [providerId, { provider }] of order) {\n let actions: readonly Action[];\n try {\n actions = await provider.actions(at);\n } catch (error) {\n capture(ports, error, { providerId, stage: \"actions\", actionId: id });\n continue;\n }\n const found = actions.find((a) => a.id === id);\n if (found) return found.provider === providerId ? found : { ...found, provider: providerId };\n }\n return null;\n },\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\n/** A provider over a fixed list; its ids are declared up front so duplicates fail at registration. */\nexport function staticActionProvider(\n id: string,\n actions: readonly Action[],\n tier: ActionProvider[\"tier\"] = \"T0\",\n): ActionProvider {\n const list = actions.map((a) => (a.provider === id ? a : { ...a, provider: id }));\n return {\n id,\n tier,\n declaredIds: () => list.map((a) => a.id),\n actions: () => list,\n };\n}\n\n/** Always-available eligibility, for actions with no rule of their own. */\nexport const alwaysEligible = (): Eligibility => ({ status: \"available\" });\n","/**\n * The shared React context (T0-safe: imports only React and types). Shared\n * through globalThis so every per-layout bundle reads the same provider.\n */\nimport * as React from \"react\";\nimport type { ActionRegistry } from \"../actions/types\";\nimport type { AlchemyHostPorts } from \"../ports\";\n\nexport interface AlchemyActionsValue {\n registry: ActionRegistry;\n ports: AlchemyHostPorts;\n}\n\nconst KEY = Symbol.for(\"ai-matrx.alchemy.actions-context\");\ntype Holder = { [KEY]?: React.Context<AlchemyActionsValue | null> };\nexport const AlchemyActionsContext: React.Context<AlchemyActionsValue | null> =\n ((globalThis as Holder)[KEY] ??= React.createContext<AlchemyActionsValue | null>(null));\n\nexport function useAlchemyActions(): AlchemyActionsValue {\n const value = React.useContext(AlchemyActionsContext);\n if (!value) throw new Error(\"Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).\");\n return value;\n}\n\n/**\n * The registry + ports when rendered inside `<AlchemyActionsProvider>`, else null. For callers that\n * must never crash a render outside the host (a kind component, an assist chip, a bare test render):\n * they answer honestly (\"actions aren't available here\") instead of throwing.\n */\nexport function useOptionalAlchemyActions(): AlchemyActionsValue | null {\n return React.useContext(AlchemyActionsContext);\n}\n"],"mappings":"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","names":["React","editable"]}