@ai-matrx/alchemy 0.8.57 → 0.8.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/dist/actions.cjs.map +1 -1
- package/dist/actions.d.cts +5 -0
- package/dist/actions.d.ts +5 -0
- package/dist/actions.js.map +1 -1
- package/dist/menu.cjs +1 -1
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.d.cts +7 -0
- package/dist/menu.d.ts +7 -0
- package/dist/menu.js +1 -1
- package/dist/menu.js.map +1 -1
- package/dist/react-bar.cjs +2 -2
- package/dist/react-bar.cjs.map +1 -1
- package/dist/react-bar.d.cts +7 -2
- package/dist/react-bar.d.ts +7 -2
- package/dist/react-bar.js +2 -2
- package/dist/react-bar.js.map +1 -1
- package/dist/react-host.d.cts +27 -3
- package/dist/react-host.d.ts +27 -3
- package/dist/react-menu.cjs +2 -2
- package/dist/react-menu.cjs.map +1 -1
- package/dist/react-menu.d.cts +23 -4
- package/dist/react-menu.d.ts +23 -4
- package/dist/react-menu.js +2 -2
- package/dist/react-menu.js.map +1 -1
- package/dist/react-overflow.cjs +2 -2
- package/dist/react-overflow.cjs.map +1 -1
- package/dist/react-overflow.d.cts +14 -2
- package/dist/react-overflow.d.ts +14 -2
- package/dist/react-overflow.js +2 -2
- package/dist/react-overflow.js.map +1 -1
- package/dist/react-palette.cjs +2 -2
- package/dist/react-palette.cjs.map +1 -1
- package/dist/react-palette.d.cts +7 -2
- package/dist/react-palette.d.ts +7 -2
- package/dist/react-palette.js +2 -2
- package/dist/react-palette.js.map +1 -1
- package/dist/react-selection.cjs +2 -2
- package/dist/react-selection.cjs.map +1 -1
- package/dist/react-selection.d.cts +7 -2
- package/dist/react-selection.d.ts +7 -2
- package/dist/react-selection.js +2 -2
- package/dist/react-selection.js.map +1 -1
- package/dist/react-sheet.cjs +2 -2
- package/dist/react-sheet.cjs.map +1 -1
- package/dist/react-sheet.d.cts +7 -2
- package/dist/react-sheet.d.ts +7 -2
- package/dist/react-sheet.js +2 -2
- package/dist/react-sheet.js.map +1 -1
- package/package.json +2 -2
package/dist/react-bar.d.cts
CHANGED
|
@@ -334,8 +334,13 @@ interface MenuEngineOptions {
|
|
|
334
334
|
/** The field's name, when the content is a field's text (see buildMenuModel). */
|
|
335
335
|
contentLabel?: string | null;
|
|
336
336
|
contentFingerprint?: string;
|
|
337
|
-
/**
|
|
338
|
-
|
|
337
|
+
/**
|
|
338
|
+
* Called after a node ran (close the menu, announce). `keepOpen` is true for a row that reloads
|
|
339
|
+
* the menu it sits in (`Action.keepsMenuOpen`): a layout never closes on it.
|
|
340
|
+
*/
|
|
341
|
+
onRan?: (nodeId: string, result: RunResult, ran?: {
|
|
342
|
+
keepOpen: boolean;
|
|
343
|
+
}) => void;
|
|
339
344
|
/** Resolve only while true (renderers resolve on open). Default true. */
|
|
340
345
|
active?: boolean;
|
|
341
346
|
/**
|
package/dist/react-bar.d.ts
CHANGED
|
@@ -334,8 +334,13 @@ interface MenuEngineOptions {
|
|
|
334
334
|
/** The field's name, when the content is a field's text (see buildMenuModel). */
|
|
335
335
|
contentLabel?: string | null;
|
|
336
336
|
contentFingerprint?: string;
|
|
337
|
-
/**
|
|
338
|
-
|
|
337
|
+
/**
|
|
338
|
+
* Called after a node ran (close the menu, announce). `keepOpen` is true for a row that reloads
|
|
339
|
+
* the menu it sits in (`Action.keepsMenuOpen`): a layout never closes on it.
|
|
340
|
+
*/
|
|
341
|
+
onRan?: (nodeId: string, result: RunResult, ran?: {
|
|
342
|
+
keepOpen: boolean;
|
|
343
|
+
}) => void;
|
|
339
344
|
/** Resolve only while true (renderers resolve on open). Default true. */
|
|
340
345
|
active?: boolean;
|
|
341
346
|
/**
|
package/dist/react-bar.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import*as React4 from"react";import{DropdownMenu,DropdownMenuContent,DropdownMenuTrigger,TooltipProvider}from"@ai-matrx/design-system";function sectionLabel(section){return section.label??null}function fold(section){return{kind:"submenu",id:`${section.id}:fold`,label:section.label,...section.icon!==void 0?{icon:section.icon}:{},children:section.nodes}}function rowCount(nodes){return nodes.filter(n=>n.kind!=="separator"&&n.kind!=="label").length}function placementOf(node){return"placement"in node&&node.placement?node.placement:"overflow"}function foldNamed(sections){return sections.map(s=>s.label&&!s.primary&&rowCount(s.nodes)>3?{id:s.id,group:s.group,nodes:[fold({...s,label:s.label})]}:s)}function bar(model,_options={}){let primary=[],overflowSections=[];for(let s of model.sections){let rest=[];for(let n of s.nodes){if(n.kind==="separator"||n.kind==="label"){rest.push(n);continue}let p=placementOf(n);(p==="primary"||p==="both")&&primary.push(n),(p==="overflow"||p==="both")&&rest.push(n)}rowCount(rest)>0&&overflowSections.push({...s,nodes:rest})}return{primary,overflow:foldNamed(overflowSections)}}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 toJson(value){if(!(value===void 0||typeof value=="function"||typeof value=="symbol")){if(value===null||typeof value=="string"||typeof value=="boolean")return value;if(typeof value=="number")return Number.isFinite(value)?value:null;if(typeof value=="bigint")return value.toString();if(value instanceof Date)return value.toISOString();if(Array.isArray(value))return value.map(v=>toJson(v)??null);if(typeof value=="object"){let out={};for(let[k,v]of Object.entries(value)){let j=toJson(v);j!==void 0&&(out[k]=j)}return out}}}function projectValues(raw,declared,pathPrefix=""){let byName=new Map(declared.map(d=>[d.name,d])),values={},omissions=[];for(let[name,value]of Object.entries(raw)){let path=pathPrefix?`${pathPrefix}.${name}`:name,decl=byName.get(name);if(!decl){omissions.push({path,reason:"filtered"});continue}let sensitivity=resolveSensitivity(decl);if(sensitivity.classification!=="ordinary"){omissions.push({path,reason:"excluded",classification:sensitivity.classification});continue}if(!sensitivity.exportable){omissions.push({path,reason:"excluded"});continue}let json=toJson(value);json!==void 0&&(values[name]=json)}return{values,omissions}}function createProjectedReads(source){return{async values(signal){if(!source.surface||!source.declaration)return{values:{},omissions:[]};let raw=await source.surface.readValues(signal),screenValues=source.declaration.values;return projectValues(raw,screenValues)},async item(signal){let item=source.item;if(!item||!source.declaration)return null;let type=source.declaration.itemTypes?.find(t=>t.name===item.itemType);if(!type)return{values:{},omissions:[{path:item.itemType,reason:"filtered"}]};let raw=await item.readValues(signal);return projectValues(raw,type.values,item.itemType)},async live(signal){return source.surface?.readLive?source.surface.readLive(signal):{}}}}async function runAction(action,target,options){let{ports}=options;if(action.requiresAuth&&!target.auth.authenticated)return ports.auth?(ports.auth.signInThenResume({actionId:action.id,contentFingerprint:options.contentFingerprint??action.id}),{status:"auth-pending"}):(ports.diagnostics.capture(new Error(`requiresAuth action "${action.id}" ran signed out with no auth port`),{area:"actions",detail:{actionId:action.id}}),{status:"failed",error:new Error("sign-in unavailable")});let reads=createProjectedReads(options.source??{surface:null,declaration:target.declaration,item:null}),signal=options.signal??new AbortController().signal;try{return{status:"ran",outcome:await action.run(target,{reads,ports,signal})}}catch(error){ports.diagnostics.capture(error,{area:"actions",detail:{actionId:action.id,stage:"run"}});let label=typeof action.label=="string"?action.label:action.id;return ports.notify?.error(`${label} did not finish.`,"Try again; if it keeps failing, report it from the feedback button."),{status:"failed",error}}}var APPROVED_HEADINGS=new Set(["Save","Copy as","Export","Edit","Creator tools","Admin","App","Compare","History"]),CONTAINER_WORD=/^(more|more…|more\.\.\.|other|others|misc|miscellaneous|additional|extra|extras|actions|options|advanced|stuff|everything else)$/i,TARGET_SHAPE=/^(Cell|Row|Column|Table|Range|Selection|Record) · .+$/;function isContainerWord(label){return CONTAINER_WORD.test(label.trim())}function isApprovedHeading(label,kind="group",approved=APPROVED_HEADINGS){let l=label.trim();return!l||isContainerWord(l)?!1:approved.has(l)?!0:kind==="target"||TARGET_SHAPE.test(l)}function defaultHeadingPolicy(){return globalThis.process?.env?.NODE_ENV==="production"?"inline":"throw"}var UnapprovedHeadingError=class extends Error{heading;constructor(heading,where){super(`Menu heading "${heading}" (${where}) is not an approved heading. Remedy: show these rows inline \u2014 declare no section (or kind "target" for the clicked thing's own label) \u2014 or ask Arman to add the name to APPROVED_HEADINGS in @ai-matrx/alchemy/menu. A generic container word ("More\u2026", "Other") is never approved.`),this.name="UnapprovedHeadingError",this.heading=heading}};var CATEGORY_GROUP={clipboard:"clipboard",copy:"transfer",export:"transfer",save:"document",share:"document",edit:"editable",history:"history",ai:"ai",ask:"ai",listen:"tools",study:"tools",app:"tools",feedback:"quick",creator:"admin",admin:"admin","surface-info":"surface-info"},GROUP_ORDER=["clipboard","editable","transfer","document","history","ai","tools","quick","surface","admin","surface-info"],CATEGORY_ORDER=["clipboard","edit","copy","export","save","share","history","ask","ai","listen","study","app","feedback","creator","admin","surface-info"];function translate(i18n,key,fallback){if(!i18n)return fallback;try{return i18n.t(key,fallback)}catch{return fallback}}function actionToNode(resolved,target,options={}){let{action,eligibility}=resolved,rawLabel=typeof action.label=="function"?action.label(target):action.label,label=translate(options.i18n,action.labelKey??action.id,rawLabel),base={id:action.id,actionId:action.id,...action.icon!==void 0?{icon:action.icon}:{},...action.placement!==void 0?{placement:action.placement}:{}};if(action.expand)return{...base,kind:"submenu",label,lazy:!0,loading:!1,...action.pending?.(target)?{pending:!0}:{},children:[],...action.emptyLabel!==void 0?{emptyLabel:action.emptyLabel}:{}};if(action.href!==void 0){let href=typeof action.href=="function"?action.href(target):action.href;return{...base,kind:"link",label,href}}if(action.toggle)return{...base,kind:"checkbox",label,checked:action.pressed?.(target)??!1,...action.description!==void 0?{description:action.description}:{}};let state=action.stateIcon?.(target)??null,pressed=action.pressed?.(target),hint=action.hint!==void 0?translate(options.i18n,action.hintKey??`${action.id}.hint`,action.hint):void 0;return{...base,kind:"item",label,...action.iconTone!==void 0?{iconTone:action.iconTone}:{},...state?{stateIcon:state}:{},...action.description!==void 0?{description:action.description}:{},...hint!==void 0?{hint}:{},...pressed!==void 0?{pressed}:{},...action.destructive?{destructive:!0}:{},...action.preserveSelection?{preserveSelection:!0}:{},...eligibility.status==="unavailable-verb"||eligibility.status==="unavailable"?{unavailable:{sentence:eligibility.sentence}}:{}}}function buildMenuModel(target,resolved,options={}){let policy=options.headingPolicy??defaultHeadingPolicy();resolved=resolved.map(r=>{let sec=r.action.section;if(r.action.expand&&isContainerWord(typeof r.action.label=="string"?r.action.label:"")&&policy==="throw")throw new UnapprovedHeadingError(r.action.label,`submenu of ${r.action.id}`);if(!sec)return r;let kind=sec.kind??(sec.primary?"target":"group");if(isApprovedHeading(sec.label,kind))return r;if(policy==="throw")throw new UnapprovedHeadingError(sec.label,`section of ${r.action.id}`);let{section:_dropped,...rest}=r.action;return{...r,action:rest}});let ordered=resolved.map((r,index)=>({r,index})).sort((a,b)=>{let ca=CATEGORY_ORDER.indexOf(a.r.action.category),cb=CATEGORY_ORDER.indexOf(b.r.action.category);if(ca!==cb)return ca-cb;let oa=a.r.action.order??0,ob=b.r.action.order??0;return oa!==ob?oa-ob:a.index-b.index}).map(x=>x.r),sections=new Map,roles={},explicit=new Map;resolved.forEach((r,index)=>{let sec=r.action.section;if(!sec)return;let entry=explicit.get(sec.id)??{group:CATEGORY_GROUP[r.action.category],rows:[]};entry.rows.push({r,index}),explicit.set(sec.id,entry)});let placed=new Set;for(let r of ordered){let sec=r.action.section;if(sec&&placed.has(sec.id))continue;let group=sec?explicit.get(sec.id)?.group??CATEGORY_GROUP[r.action.category]:CATEGORY_GROUP[r.action.category],sectionId=sec?`section:${sec.id}`:group,section=sections.get(sectionId);section||(section={id:sectionId,group,nodes:[],...sec?{label:translate(options.i18n,sec.labelKey??`section.${sec.id}`,sec.label)}:{},...sec?.icon!==void 0?{icon:sec.icon}:{},...sec?.primary?{primary:!0}:{},...sec&&(sec.kind??(sec.primary?"target":"group"))==="target"?{target:!0}:{}},sections.set(sectionId,section));let rows=sec?(explicit.get(sec.id)?.rows??[]).sort((x,y)=>(x.r.action.order??0)-(y.r.action.order??0)||x.index-y.index).map(x=>x.r):[r];sec&&placed.add(sec.id);for(let row of rows)row.action.startsGroup&§ion.nodes.length>0&§ion.nodes.push({kind:"separator",id:`${row.action.id}:group`}),section.nodes.push(actionToNode(row,target,options)),row.action.verb&&(roles[row.action.verb]=row.action.id)}let list=[...sections.values()].sort((a,b)=>!!a.primary!=!!b.primary?a.primary?-1:1:GROUP_ORDER.indexOf(a.group)-GROUP_ORDER.indexOf(b.group)),selected=target.selection?.text?.trim(),content=options.content?.trim(),fieldName=options.contentLabel?.trim();return{header:selected?{label:"Selected",text:target.selection?.text??""}:fieldName?{label:fieldName,text:options.content??""}:content?{label:"Content",text:options.content??""}:null,sections:list,roles}}function withGroupDividers(children,nodes){let out=[];return children.forEach((child,i)=>{let node=nodes[i];node&&(i>0&&(child.startsGroup||(children[i-1].section?.id??null)!==(child.section?.id??null))&&out.push({kind:"separator",id:`${child.id}:group`}),out.push(node))}),out}function withExpandedChildren(model,nodeId,children){let visit=nodes=>nodes.map(n=>n.kind!=="submenu"?n:n.id===nodeId?children==="loading"?{...n,loading:!0}:{...n,lazy:!1,loading:!1,pending:!1,children}:{...n,children:visit(n.children)});return{...model,sections:model.sections.map(s=>({...s,nodes:visit(s.nodes)}))}}function findNode(nodes,id){for(let n of nodes){if(n.id===id)return n;if(n.kind==="submenu"){let hit=findNode(n.children,id);if(hit)return hit}}return null}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 isLatestExpansion(capturedTargetEpoch,currentTargetEpoch,capturedNodeSequence,currentNodeSequence){return capturedTargetEpoch===currentTargetEpoch&&capturedNodeSequence===currentNodeSequence}import{jsx}from"react/jsx-runtime";function nodeInModel(model,id){for(let s of model.sections){let hit=findNode(s.nodes,id);if(hit)return hit}return null}function hasLoadedRows(model,id){let n=nodeInModel(model,id);return!!(n&&n.kind==="submenu"&&!n.lazy&&!n.loading)}function loadedChildren(model,id){let n=nodeInModel(model,id);return n&&n.kind==="submenu"&&!n.lazy?n.children:[]}function carryOpened(prev,next,opened){return next.map(n=>{if(n.kind!=="submenu")return n;let old=prev.find(o=>o.id===n.id);return!old||old.kind!=="submenu"||old.lazy?n:n.lazy?opened.has(n.id)?{...n,lazy:!1,loading:!1,pending:!1,children:old.children}:n:{...n,children:carryOpened(old.children,n.children,opened)}})}function carryOpenedModel(prev,next,opened){if(opened.size===0)return next;let prevNodes=prev.sections.flatMap(s=>s.nodes);return{...next,sections:next.sections.map(s=>({...s,nodes:carryOpened(prevNodes,s.nodes,opened)}))}}function useMenuEngine(target,options={}){let{registry,ports}=useAlchemyActions(),[resolved,setResolved]=React2.useState([]),[model,setModel]=React2.useState(null),[tick,setTick]=React2.useState(0),expanded=React2.useRef(new Map),opened=React2.useRef(new Set),expandSeq=React2.useRef(new Map),targetEpoch=React2.useRef(0),currentTarget=React2.useRef(target);currentTarget.current!==target&&(currentTarget.current=target,targetEpoch.current+=1);let active=options.active??!0,optionsRef=React2.useRef(options);optionsRef.current=options,React2.useEffect(()=>registry.subscribe(()=>setTick(t=>t+1)),[registry]);let expandAction=React2.useCallback(async(action,nodeId)=>{if(!target||!action.expand)return;let epoch=targetEpoch.current,seq=(expandSeq.current.get(nodeId)??0)+1;expandSeq.current.set(nodeId,seq);let latest=()=>isLatestExpansion(epoch,targetEpoch.current,seq,expandSeq.current.get(nodeId));setModel(m=>m&&!hasLoadedRows(m,nodeId)?withExpandedChildren(m,nodeId,"loading"):m);try{let children=await action.expand(target,new AbortController().signal);if(!latest())return;let nodes=withGroupDividers(children,children.map(child=>(expanded.current.set(child.id,child),actionToNode({action:child,eligibility:{status:"available"}},target,ports.i18n?{i18n:ports.i18n}:{}))));setModel(m=>m&&withExpandedChildren(m,nodeId,carryOpened(loadedChildren(m,nodeId),nodes,opened.current)));for(let child of children)child.expand&&opened.current.has(child.id)&&expandAction(child,child.id)}catch(error){if(!latest())return;ports.diagnostics.capture(error,{area:"menu",detail:{nodeId,stage:"expand"}}),setModel(m=>m&&withExpandedChildren(m,nodeId,[]))}},[target,ports]);React2.useEffect(()=>{expanded.current.clear(),opened.current.clear()},[target]),React2.useEffect(()=>{if(!target||!active)return;let live=!0;return registry.resolve(target,optionsRef.current.restrict).then(list=>{if(!live)return;setResolved(list);let built=buildMenuModel(target,list,{...ports.i18n?{i18n:ports.i18n}:{},content:optionsRef.current.content??null,contentLabel:optionsRef.current.contentLabel??null});setModel(prev=>prev?carryOpenedModel(prev,built,opened.current):built);for(let{action}of list)opened.current.has(action.id)&&action.expand&&expandAction(action,action.id)}),()=>{live=!1}},[registry,ports,target,active,tick,options.revision,expandAction]);let resolvedRef=React2.useRef(resolved);resolvedRef.current=resolved;let subscribedIds=resolved.map(r=>r.action.id).join(`
|
|
3
|
-
`);React2.useEffect(()=>{if(!target)return;let subscribing=!0,onChange=()=>{subscribing||setTick(t=>t+1)},offs=resolvedRef.current.map(r=>r.action.subscribe?.(onChange,target)).filter(off=>typeof off=="function");return subscribing=!1,()=>offs.forEach(off=>off())},[subscribedIds,target]);let findAction=React2.useCallback(id=>resolved.find(r=>r.action.id===id)?.action??expanded.current.get(id)??void 0,[resolved]),run=React2.useCallback(async nodeId=>{if(!target||!model)return null;let node=model.sections.map(s=>findNode(s.nodes,nodeId)).find(Boolean)??null,action=findAction(node&&"actionId"in node&&node.actionId?node.actionId:nodeId);if(!action)return null;let o=optionsRef.current,result=await runAction(action,target,{ports,...o.source?{source:o.source}:{},...o.contentFingerprint?{contentFingerprint:o.contentFingerprint}:{}});return o.onRan?.(nodeId,result),result},[target,model,findAction,ports]),expand=React2.useCallback(async nodeId=>{let action=findAction(nodeId);!target||!action?.expand||(opened.current.add(nodeId),await expandAction(action,nodeId))},[findAction,target,expandAction]),icon=React2.useCallback((key,tone)=>{if(!key||!ports.icons)return null;let Resolved=ports.icons.resolve(key);return Resolved?jsx(Resolved,{className:`h-4 w-4 shrink-0 ${tone??""}`,"aria-hidden":!0}):null},[ports.icons]),explain=React2.useCallback(sentence=>{ports.notify?ports.notify.info(sentence):ports.diagnostics.capture(new Error(`No notify port to explain: ${sentence}`),{area:"menu",detail:{sentence}})},[ports]);return{model,resolved,run,expand,icon,explain}}import*as React3 from"react";import{DropdownMenuCheckboxItem,DropdownMenuItem,DropdownMenuLabel,DropdownMenuSeparator,DropdownMenuSub,DropdownMenuSubContent,DropdownMenuSubTrigger,Tooltip,TooltipContent,TooltipTrigger}from"@ai-matrx/design-system";import{Fragment as Fragment2,jsx as jsx2,jsxs}from"react/jsx-runtime";function NodeList({nodes,engine}){return jsx2(Fragment2,{children:nodes.map(node=>jsx2(MenuNodeView,{node,engine},node.id))})}function SectionList({sections,engine}){return jsx2(Fragment2,{children:sections.map((section,index)=>jsxs(React3.Fragment,{children:[index>0?jsx2(DropdownMenuSeparator,{}):null,sectionLabel(section)?jsx2(DropdownMenuLabel,{className:"text-xs font-medium text-muted-foreground",children:sectionLabel(section)}):null,jsx2(NodeList,{nodes:section.nodes,engine})]},section.id))})}function iconOf(node,engine){if(node.kind!=="item")return"icon"in node?engine.icon(node.icon):null;if(node.stateIcon){let glyph=engine.icon(node.stateIcon.icon);return node.stateIcon.spin?jsx2("span",{className:"inline-flex animate-spin",children:glyph}):glyph}if(!("icon"in node))return null;let tone=node.pressed===!1?void 0:node.iconTone;return engine.icon(node.icon,tone)}function MenuNodeView({node,engine}){switch(node.kind){case"separator":return jsx2(DropdownMenuSeparator,{});case"label":return jsx2(DropdownMenuLabel,{className:"text-xs font-medium text-muted-foreground",children:node.label});case"submenu":return jsx2(SubmenuView,{node,engine});case"checkbox":return jsxs(DropdownMenuCheckboxItem,{"data-alchemy-node":node.id,checked:node.checked,onSelect:()=>{engine.run(node.id)},className:"gap-2",children:[iconOf(node,engine),jsx2("span",{className:"truncate",children:node.label})]});case"link":return jsx2(DropdownMenuItem,{asChild:!0,"data-alchemy-node":node.id,children:jsxs("a",{href:node.href,target:node.target,className:"gap-2",children:[iconOf(node,engine),jsx2("span",{className:"truncate",children:node.label})]})});case"item":return jsx2(LeafItem,{node,engine})}}function SubmenuView({node,engine}){let triggerRef=React3.useRef(null);return jsxs(DropdownMenuSub,{onOpenChange:open=>{open&&node.lazy&&!node.loading&&engine.expand(node.id)},children:[jsxs(DropdownMenuSubTrigger,{ref:triggerRef,"data-alchemy-node":node.id,className:"gap-2","aria-busy":node.pending?!0:void 0,children:[iconOf(node,engine),jsx2("span",{className:"truncate",children:node.label}),node.pending?jsx2("span",{className:"ml-auto text-xs text-muted-foreground",children:"Loading\u2026"}):null]}),jsx2(DropdownMenuSubContent,{"data-alchemy-submenu":node.id,onFocusOutside:event=>{let to=event.target,trigger=triggerRef.current;to instanceof Element&&trigger&&to.getAttribute("role")==="menu"&&to.contains(trigger)&&event.preventDefault()},className:"w-72 max-w-[calc(100vw-1rem)] max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:node.loading?jsx2(DropdownMenuLabel,{className:"text-xs text-muted-foreground",children:"Loading\u2026"}):node.children.length===0&&!node.lazy?jsx2(DropdownMenuLabel,{className:"text-xs text-muted-foreground",children:node.emptyLabel??"Nothing here yet"}):jsx2(NodeList,{nodes:node.children,engine})})]})}function LeafItem({node,engine}){let row=jsxs(DropdownMenuItem,{"data-alchemy-node":node.id,"aria-disabled":node.unavailable?!0:void 0,"aria-pressed":node.pressed,onSelect:event=>{if(node.unavailable){event.preventDefault();return}engine.run(node.id)},className:`gap-2 ${node.unavailable?"opacity-50":""} ${node.destructive?"text-destructive":""}`,children:[iconOf(node,engine),jsxs("span",{className:"flex min-w-0 flex-1 flex-col",children:[jsx2("span",{className:"truncate",children:node.label}),node.description?jsx2("span",{className:"truncate text-xs text-muted-foreground",children:node.description}):null]}),node.hint?jsx2("span",{className:"ml-auto text-xs text-muted-foreground",children:node.hint}):null]});return node.unavailable?jsxs(Tooltip,{children:[jsx2(TooltipTrigger,{asChild:!0,children:row}),jsx2(TooltipContent,{children:node.unavailable.sentence})]}):row}function LeafButton({node,engine,showLabel}){if(node.kind!=="item"&&node.kind!=="checkbox"&&node.kind!=="link")return null;let unavailable=node.kind==="item"?node.unavailable:void 0,pressed=node.kind==="item"?node.pressed:node.kind==="checkbox"?node.checked:void 0,glyph=iconOf(node,engine),button=jsxs("button",{type:"button","data-alchemy-node":node.id,"data-matrx-button":"","data-tap-floor":"","data-tap-floor-square":"","aria-label":node.label,"aria-disabled":unavailable?!0:void 0,"aria-pressed":pressed,onMouseDown:e=>{node.kind==="item"&&node.preserveSelection&&e.preventDefault()},onClick:()=>{if(unavailable){engine.explain(unavailable.sentence);return}if(node.kind==="link"){window.location.assign(node.href);return}engine.run(node.id)},className:`inline-flex h-7 min-w-7 pointer-coarse:h-11 pointer-coarse:min-w-11 shrink-0 items-center justify-center gap-1.5 rounded-md px-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground ${unavailable?"cursor-default opacity-50 hover:bg-transparent":""} ${pressed?"text-foreground":""}`,children:[glyph,showLabel||!glyph?jsx2("span",{className:"whitespace-nowrap",children:node.label}):null]});return jsxs(Tooltip,{children:[jsx2(TooltipTrigger,{asChild:!0,children:button}),jsx2(TooltipContent,{children:unavailable?unavailable.sentence:node.label})]})}import{Fragment as Fragment3,jsx as jsx3,jsxs as jsxs2}from"react/jsx-runtime";function isNarrow(width){return typeof window>"u"?!1:window.matchMedia?.(`(max-width: ${width-1}px)`).matches??!1}var TRIGGER_CLASS="inline-flex h-7 min-w-7 pointer-coarse:h-11 pointer-coarse:min-w-11 items-center justify-center gap-1 rounded-md px-1.5 text-muted-foreground hover:bg-accent hover:text-foreground";function PrimarySubmenu({node,engine}){return jsxs2(DropdownMenu,{onOpenChange:open=>{open&&node.lazy&&!node.loading&&engine.expand(node.id)},children:[jsx3(DropdownMenuTrigger,{asChild:!0,children:jsx3("button",{type:"button","aria-label":node.label,"data-alchemy-node":node.id,className:TRIGGER_CLASS,children:engine.icon(node.icon)??jsx3("span",{className:"text-sm",children:node.label})})}),jsx3(DropdownMenuContent,{align:"start",className:"w-64 max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:node.loading?jsx3("div",{className:"px-2 py-1.5 text-xs text-muted-foreground",children:"Loading\u2026"}):jsx3(NodeList,{nodes:node.children,engine})})]})}function ActionBar({target,mini=!1,className,hideOverflow=!1,sheetBelow=768,loadSheet=()=>import("@ai-matrx/alchemy/react/sheet"),...options}){let engine=useMenuEngine(target,options),[sheetOpen,setSheetOpen]=React4.useState(!1),[Sheet,setSheet]=React4.useState(null);if(!engine.model)return null;let arranged=bar(engine.model,{mini});return arranged.primary.length===0&&arranged.overflow.length===0?null:jsx3(TooltipProvider,{children:jsxs2("div",{role:"toolbar","aria-label":"Actions","data-alchemy-layout":"bar",className:`flex items-center ${mini?"gap-0":"gap-0.5"} ${className??""}`,children:[arranged.primary.map(node=>node.kind==="submenu"?jsx3(PrimarySubmenu,{node,engine},node.id):jsx3(LeafButton,{node,engine,showLabel:!1},node.id)),hideOverflow||arranged.overflow.length===0?null:isNarrow(sheetBelow)?jsxs2(Fragment3,{children:[jsx3("button",{type:"button","aria-label":"More actions","data-alchemy-trigger":"overflow",className:TRIGGER_CLASS,onClick:()=>{setSheetOpen(!0),Sheet||loadSheet().then(m=>setSheet(()=>m.ActionSheet))},children:jsx3("span",{"aria-hidden":!0,className:"text-base leading-none",children:"\u22EF"})}),Sheet?jsx3(Sheet,{...options,target,open:sheetOpen,onOpenChange:setSheetOpen}):null]}):jsxs2(DropdownMenu,{children:[jsx3(DropdownMenuTrigger,{asChild:!0,children:jsx3("button",{type:"button","aria-label":"More actions","data-alchemy-trigger":"overflow",className:TRIGGER_CLASS,children:jsx3("span",{"aria-hidden":!0,className:"text-base leading-none",children:"\u22EF"})})}),jsx3(DropdownMenuContent,{align:"end",className:"w-64 max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:jsx3(SectionList,{sections:arranged.overflow,engine})})]})]})})}export{ActionBar};
|
|
2
|
+
import*as React5 from"react";import{DropdownMenu,DropdownMenuContent,DropdownMenuTrigger,TooltipProvider}from"@ai-matrx/design-system";function sectionLabel(section){return section.label??null}function fold(section){return{kind:"submenu",id:`${section.id}:fold`,label:section.label,...section.icon!==void 0?{icon:section.icon}:{},children:section.nodes}}function rowCount(nodes){return nodes.filter(n=>n.kind!=="separator"&&n.kind!=="label").length}function placementOf(node){return"placement"in node&&node.placement?node.placement:"overflow"}function foldNamed(sections){return sections.map(s=>s.label&&!s.primary&&rowCount(s.nodes)>3?{id:s.id,group:s.group,nodes:[fold({...s,label:s.label})]}:s)}function bar(model,_options={}){let primary=[],overflowSections=[];for(let s of model.sections){let rest=[];for(let n of s.nodes){if(n.kind==="separator"||n.kind==="label"){rest.push(n);continue}let p=placementOf(n);(p==="primary"||p==="both")&&primary.push(n),(p==="overflow"||p==="both")&&rest.push(n)}rowCount(rest)>0&&overflowSections.push({...s,nodes:rest})}return{primary,overflow:foldNamed(overflowSections)}}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 toJson(value){if(!(value===void 0||typeof value=="function"||typeof value=="symbol")){if(value===null||typeof value=="string"||typeof value=="boolean")return value;if(typeof value=="number")return Number.isFinite(value)?value:null;if(typeof value=="bigint")return value.toString();if(value instanceof Date)return value.toISOString();if(Array.isArray(value))return value.map(v=>toJson(v)??null);if(typeof value=="object"){let out={};for(let[k,v]of Object.entries(value)){let j=toJson(v);j!==void 0&&(out[k]=j)}return out}}}function projectValues(raw,declared,pathPrefix=""){let byName=new Map(declared.map(d=>[d.name,d])),values={},omissions=[];for(let[name,value]of Object.entries(raw)){let path=pathPrefix?`${pathPrefix}.${name}`:name,decl=byName.get(name);if(!decl){omissions.push({path,reason:"filtered"});continue}let sensitivity=resolveSensitivity(decl);if(sensitivity.classification!=="ordinary"){omissions.push({path,reason:"excluded",classification:sensitivity.classification});continue}if(!sensitivity.exportable){omissions.push({path,reason:"excluded"});continue}let json=toJson(value);json!==void 0&&(values[name]=json)}return{values,omissions}}function createProjectedReads(source){return{async values(signal){if(!source.surface||!source.declaration)return{values:{},omissions:[]};let raw=await source.surface.readValues(signal),screenValues=source.declaration.values;return projectValues(raw,screenValues)},async item(signal){let item=source.item;if(!item||!source.declaration)return null;let type=source.declaration.itemTypes?.find(t=>t.name===item.itemType);if(!type)return{values:{},omissions:[{path:item.itemType,reason:"filtered"}]};let raw=await item.readValues(signal);return projectValues(raw,type.values,item.itemType)},async live(signal){return source.surface?.readLive?source.surface.readLive(signal):{}}}}async function runAction(action,target,options){let{ports}=options;if(action.requiresAuth&&!target.auth.authenticated)return ports.auth?(ports.auth.signInThenResume({actionId:action.id,contentFingerprint:options.contentFingerprint??action.id}),{status:"auth-pending"}):(ports.diagnostics.capture(new Error(`requiresAuth action "${action.id}" ran signed out with no auth port`),{area:"actions",detail:{actionId:action.id}}),{status:"failed",error:new Error("sign-in unavailable")});let reads=createProjectedReads(options.source??{surface:null,declaration:target.declaration,item:null}),signal=options.signal??new AbortController().signal;try{return{status:"ran",outcome:await action.run(target,{reads,ports,signal})}}catch(error){ports.diagnostics.capture(error,{area:"actions",detail:{actionId:action.id,stage:"run"}});let label=typeof action.label=="string"?action.label:action.id;return ports.notify?.error(`${label} did not finish.`,"Try again; if it keeps failing, report it from the feedback button."),{status:"failed",error}}}var APPROVED_HEADINGS=new Set(["Save","Copy as","Export","Edit","Creator tools","Admin","App","Compare","History"]),CONTAINER_WORD=/^(more|more…|more\.\.\.|other|others|misc|miscellaneous|additional|extra|extras|actions|options|advanced|stuff|everything else)$/i,TARGET_SHAPE=/^(Cell|Row|Column|Table|Range|Selection|Record) · .+$/;function isContainerWord(label){return CONTAINER_WORD.test(label.trim())}function isApprovedHeading(label,kind="group",approved=APPROVED_HEADINGS){let l=label.trim();return!l||isContainerWord(l)?!1:approved.has(l)?!0:kind==="target"||TARGET_SHAPE.test(l)}function defaultHeadingPolicy(){return globalThis.process?.env?.NODE_ENV==="production"?"inline":"throw"}var UnapprovedHeadingError=class extends Error{heading;constructor(heading,where){super(`Menu heading "${heading}" (${where}) is not an approved heading. Remedy: show these rows inline \u2014 declare no section (or kind "target" for the clicked thing's own label) \u2014 or ask Arman to add the name to APPROVED_HEADINGS in @ai-matrx/alchemy/menu. A generic container word ("More\u2026", "Other") is never approved.`),this.name="UnapprovedHeadingError",this.heading=heading}};var CATEGORY_GROUP={clipboard:"clipboard",copy:"transfer",export:"transfer",save:"document",share:"document",edit:"editable",history:"history",ai:"ai",ask:"ai",listen:"tools",study:"tools",app:"tools",feedback:"quick",creator:"admin",admin:"admin","surface-info":"surface-info"},GROUP_ORDER=["clipboard","editable","transfer","document","history","ai","tools","quick","surface","admin","surface-info"],CATEGORY_ORDER=["clipboard","edit","copy","export","save","share","history","ask","ai","listen","study","app","feedback","creator","admin","surface-info"];function translate(i18n,key,fallback){if(!i18n)return fallback;try{return i18n.t(key,fallback)}catch{return fallback}}function actionToNode(resolved,target,options={}){let{action,eligibility}=resolved,rawLabel=typeof action.label=="function"?action.label(target):action.label,label=translate(options.i18n,action.labelKey??action.id,rawLabel),base={id:action.id,actionId:action.id,...action.icon!==void 0?{icon:action.icon}:{},...action.placement!==void 0?{placement:action.placement}:{}};if(action.expand)return{...base,kind:"submenu",label,lazy:!0,loading:!1,...action.pending?.(target)?{pending:!0}:{},children:[],...action.emptyLabel!==void 0?{emptyLabel:action.emptyLabel}:{}};if(action.href!==void 0){let href=typeof action.href=="function"?action.href(target):action.href;return{...base,kind:"link",label,href}}if(action.toggle)return{...base,kind:"checkbox",label,checked:action.pressed?.(target)??!1,...action.description!==void 0?{description:action.description}:{}};let state=action.stateIcon?.(target)??null,pressed=action.pressed?.(target),hint=action.hint!==void 0?translate(options.i18n,action.hintKey??`${action.id}.hint`,action.hint):void 0;return{...base,kind:"item",label,...action.iconTone!==void 0?{iconTone:action.iconTone}:{},...state?{stateIcon:state}:{},...action.description!==void 0?{description:action.description}:{},...hint!==void 0?{hint}:{},...pressed!==void 0?{pressed}:{},...action.destructive?{destructive:!0}:{},...action.preserveSelection?{preserveSelection:!0}:{},...action.keepsMenuOpen?{keepsMenuOpen:!0}:{},...eligibility.status==="unavailable-verb"||eligibility.status==="unavailable"?{unavailable:{sentence:eligibility.sentence}}:{}}}function buildMenuModel(target,resolved,options={}){let policy=options.headingPolicy??defaultHeadingPolicy();resolved=resolved.map(r=>{let sec=r.action.section;if(r.action.expand&&isContainerWord(typeof r.action.label=="string"?r.action.label:"")&&policy==="throw")throw new UnapprovedHeadingError(r.action.label,`submenu of ${r.action.id}`);if(!sec)return r;let kind=sec.kind??(sec.primary?"target":"group");if(isApprovedHeading(sec.label,kind))return r;if(policy==="throw")throw new UnapprovedHeadingError(sec.label,`section of ${r.action.id}`);let{section:_dropped,...rest}=r.action;return{...r,action:rest}});let ordered=resolved.map((r,index)=>({r,index})).sort((a,b)=>{let ca=CATEGORY_ORDER.indexOf(a.r.action.category),cb=CATEGORY_ORDER.indexOf(b.r.action.category);if(ca!==cb)return ca-cb;let oa=a.r.action.order??0,ob=b.r.action.order??0;return oa!==ob?oa-ob:a.index-b.index}).map(x=>x.r),sections=new Map,roles={},explicit=new Map;resolved.forEach((r,index)=>{let sec=r.action.section;if(!sec)return;let entry=explicit.get(sec.id)??{group:CATEGORY_GROUP[r.action.category],rows:[]};entry.rows.push({r,index}),explicit.set(sec.id,entry)});let placed=new Set;for(let r of ordered){let sec=r.action.section;if(sec&&placed.has(sec.id))continue;let group=sec?explicit.get(sec.id)?.group??CATEGORY_GROUP[r.action.category]:CATEGORY_GROUP[r.action.category],sectionId=sec?`section:${sec.id}`:group,section=sections.get(sectionId);section||(section={id:sectionId,group,nodes:[],...sec?{label:translate(options.i18n,sec.labelKey??`section.${sec.id}`,sec.label)}:{},...sec?.icon!==void 0?{icon:sec.icon}:{},...sec?.primary?{primary:!0}:{},...sec&&(sec.kind??(sec.primary?"target":"group"))==="target"?{target:!0}:{}},sections.set(sectionId,section));let rows=sec?(explicit.get(sec.id)?.rows??[]).sort((x,y)=>(x.r.action.order??0)-(y.r.action.order??0)||x.index-y.index).map(x=>x.r):[r];sec&&placed.add(sec.id);for(let row of rows)row.action.startsGroup&§ion.nodes.length>0&§ion.nodes.push({kind:"separator",id:`${row.action.id}:group`}),section.nodes.push(actionToNode(row,target,options)),row.action.verb&&(roles[row.action.verb]=row.action.id)}let list=[...sections.values()].sort((a,b)=>!!a.primary!=!!b.primary?a.primary?-1:1:GROUP_ORDER.indexOf(a.group)-GROUP_ORDER.indexOf(b.group)),selected=target.selection?.text?.trim(),content=options.content?.trim(),fieldName=options.contentLabel?.trim();return{header:selected?{label:"Selected",text:target.selection?.text??""}:fieldName?{label:fieldName,text:options.content??""}:content?{label:"Content",text:options.content??""}:null,sections:list,roles}}function withGroupDividers(children,nodes){let out=[];return children.forEach((child,i)=>{let node=nodes[i];node&&(i>0&&(child.startsGroup||(children[i-1].section?.id??null)!==(child.section?.id??null))&&out.push({kind:"separator",id:`${child.id}:group`}),out.push(node))}),out}function withExpandedChildren(model,nodeId,children){let visit=nodes=>nodes.map(n=>n.kind!=="submenu"?n:n.id===nodeId?children==="loading"?{...n,loading:!0}:{...n,lazy:!1,loading:!1,pending:!1,children}:{...n,children:visit(n.children)});return{...model,sections:model.sections.map(s=>({...s,nodes:visit(s.nodes)}))}}function findNode(nodes,id){for(let n of nodes){if(n.id===id)return n;if(n.kind==="submenu"){let hit=findNode(n.children,id);if(hit)return hit}}return null}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 isLatestExpansion(capturedTargetEpoch,currentTargetEpoch,capturedNodeSequence,currentNodeSequence){return capturedTargetEpoch===currentTargetEpoch&&capturedNodeSequence===currentNodeSequence}import{jsx}from"react/jsx-runtime";function nodeInModel(model,id){for(let s of model.sections){let hit=findNode(s.nodes,id);if(hit)return hit}return null}function hasLoadedRows(model,id){let n=nodeInModel(model,id);return!!(n&&n.kind==="submenu"&&!n.lazy&&!n.loading)}function loadedChildren(model,id){let n=nodeInModel(model,id);return n&&n.kind==="submenu"&&!n.lazy?n.children:[]}function carryOpened(prev,next,opened){return next.map(n=>{if(n.kind!=="submenu")return n;let old=prev.find(o=>o.id===n.id);return!old||old.kind!=="submenu"||old.lazy?n:n.lazy?opened.has(n.id)?{...n,lazy:!1,loading:!1,pending:!1,children:old.children}:n:{...n,children:carryOpened(old.children,n.children,opened)}})}function carryOpenedModel(prev,next,opened){if(opened.size===0)return next;let prevNodes=prev.sections.flatMap(s=>s.nodes);return{...next,sections:next.sections.map(s=>({...s,nodes:carryOpened(prevNodes,s.nodes,opened)}))}}function useMenuEngine(target,options={}){let{registry,ports}=useAlchemyActions(),[resolved,setResolved]=React2.useState([]),[model,setModel]=React2.useState(null),[tick,setTick]=React2.useState(0),expanded=React2.useRef(new Map),opened=React2.useRef(new Set),expandSeq=React2.useRef(new Map),targetEpoch=React2.useRef(0),currentTarget=React2.useRef(target);currentTarget.current!==target&&(currentTarget.current=target,targetEpoch.current+=1);let active=options.active??!0,optionsRef=React2.useRef(options);optionsRef.current=options,React2.useEffect(()=>registry.subscribe(()=>setTick(t=>t+1)),[registry]);let expandAction=React2.useCallback(async(action,nodeId)=>{if(!target||!action.expand)return;let epoch=targetEpoch.current,seq=(expandSeq.current.get(nodeId)??0)+1;expandSeq.current.set(nodeId,seq);let latest=()=>isLatestExpansion(epoch,targetEpoch.current,seq,expandSeq.current.get(nodeId));setModel(m=>m&&!hasLoadedRows(m,nodeId)?withExpandedChildren(m,nodeId,"loading"):m);try{let children=await action.expand(target,new AbortController().signal);if(!latest())return;let nodes=withGroupDividers(children,children.map(child=>(expanded.current.set(child.id,child),actionToNode({action:child,eligibility:{status:"available"}},target,ports.i18n?{i18n:ports.i18n}:{}))));setModel(m=>m&&withExpandedChildren(m,nodeId,carryOpened(loadedChildren(m,nodeId),nodes,opened.current)));for(let child of children)child.expand&&opened.current.has(child.id)&&expandAction(child,child.id)}catch(error){if(!latest())return;ports.diagnostics.capture(error,{area:"menu",detail:{nodeId,stage:"expand"}}),setModel(m=>m&&withExpandedChildren(m,nodeId,[]))}},[target,ports]);React2.useEffect(()=>{expanded.current.clear(),opened.current.clear()},[target]),React2.useEffect(()=>{if(!target||!active)return;let live=!0;return registry.resolve(target,optionsRef.current.restrict).then(list=>{if(!live)return;setResolved(list);let built=buildMenuModel(target,list,{...ports.i18n?{i18n:ports.i18n}:{},content:optionsRef.current.content??null,contentLabel:optionsRef.current.contentLabel??null});setModel(prev=>prev?carryOpenedModel(prev,built,opened.current):built);for(let{action}of list)opened.current.has(action.id)&&action.expand&&expandAction(action,action.id)}),()=>{live=!1}},[registry,ports,target,active,tick,options.revision,expandAction]);let resolvedRef=React2.useRef(resolved);resolvedRef.current=resolved;let subscribedIds=resolved.map(r=>r.action.id).join(`
|
|
3
|
+
`);React2.useEffect(()=>{if(!target)return;let subscribing=!0,onChange=()=>{subscribing||setTick(t=>t+1)},offs=resolvedRef.current.map(r=>r.action.subscribe?.(onChange,target)).filter(off=>typeof off=="function");return subscribing=!1,()=>offs.forEach(off=>off())},[subscribedIds,target]);let findAction=React2.useCallback(id=>resolved.find(r=>r.action.id===id)?.action??expanded.current.get(id)??void 0,[resolved]),run=React2.useCallback(async nodeId=>{if(!target||!model)return null;let node=model.sections.map(s=>findNode(s.nodes,nodeId)).find(Boolean)??null,action=findAction(node&&"actionId"in node&&node.actionId?node.actionId:nodeId);if(!action)return null;let o=optionsRef.current,result=await runAction(action,target,{ports,...o.source?{source:o.source}:{},...o.contentFingerprint?{contentFingerprint:o.contentFingerprint}:{}});return o.onRan?.(nodeId,result,{keepOpen:!!action.keepsMenuOpen}),result},[target,model,findAction,ports]),expand=React2.useCallback(async nodeId=>{let action=findAction(nodeId);!target||!action?.expand||(opened.current.add(nodeId),await expandAction(action,nodeId))},[findAction,target,expandAction]),icon=React2.useCallback((key,tone)=>{if(!key||!ports.icons)return null;let Resolved=ports.icons.resolve(key);return Resolved?jsx(Resolved,{className:`h-4 w-4 shrink-0 ${tone??""}`,"aria-hidden":!0}):null},[ports.icons]),explain=React2.useCallback(sentence=>{ports.notify?ports.notify.info(sentence):ports.diagnostics.capture(new Error(`No notify port to explain: ${sentence}`),{area:"menu",detail:{sentence}})},[ports]);return{model,resolved,run,expand,icon,explain}}import*as React4 from"react";import{DropdownMenuCheckboxItem,DropdownMenuItem,DropdownMenuLabel,DropdownMenuSeparator,DropdownMenuSub,DropdownMenuSubContent,DropdownMenuSubTrigger,Tooltip,TooltipContent,TooltipTrigger}from"@ai-matrx/design-system";import*as React3 from"react";var MenuSizeContext=React3.createContext("standard");function useMenuSize(){return React3.useContext(MenuSizeContext)}var MENU_SIZE_CLASSES={standard:{panel:"w-72",row:"gap-2",strip:"flex items-center gap-0.5 px-1 py-1",stripButton:"h-7 min-w-7 px-1.5",label:"text-xs",filter:"h-7 text-sm",hint:"text-xs"},large:{panel:"w-[22.5rem] [&_svg]:size-[18px]",row:"gap-3 min-h-9 px-3 text-[15px]",strip:"flex items-center justify-between gap-1 px-2 py-1.5",stripButton:"h-9 min-w-9 px-2",label:"text-[13px]",filter:"h-9 text-[15px]",hint:"text-[13px]"}};import{Fragment as Fragment2,jsx as jsx2,jsxs}from"react/jsx-runtime";function NodeList({nodes,engine}){return jsx2(Fragment2,{children:nodes.map(node=>jsx2(MenuNodeView,{node,engine},node.id))})}function SectionList({sections,engine}){let size=MENU_SIZE_CLASSES[useMenuSize()];return jsx2(Fragment2,{children:sections.map((section,index)=>jsxs(React4.Fragment,{children:[index>0?jsx2(DropdownMenuSeparator,{}):null,sectionLabel(section)?jsx2(DropdownMenuLabel,{className:`${size.label} font-medium text-muted-foreground`,children:sectionLabel(section)}):null,jsx2(NodeList,{nodes:section.nodes,engine})]},section.id))})}function iconOf(node,engine){if(node.kind!=="item")return"icon"in node?engine.icon(node.icon):null;if(node.stateIcon){let glyph=engine.icon(node.stateIcon.icon);return node.stateIcon.spin?jsx2("span",{className:"inline-flex animate-spin",children:glyph}):glyph}if(!("icon"in node))return null;let tone=node.pressed===!1?void 0:node.iconTone;return engine.icon(node.icon,tone)}function MenuNodeView({node,engine}){let size=MENU_SIZE_CLASSES[useMenuSize()];switch(node.kind){case"separator":return jsx2(DropdownMenuSeparator,{});case"label":return jsx2(DropdownMenuLabel,{className:`${size.label} font-medium text-muted-foreground`,children:node.label});case"submenu":return jsx2(SubmenuView,{node,engine});case"checkbox":return jsxs(DropdownMenuCheckboxItem,{"data-alchemy-node":node.id,checked:node.checked,onSelect:()=>{engine.run(node.id)},className:size.row,children:[iconOf(node,engine),jsx2("span",{className:"truncate",children:node.label})]});case"link":return jsx2(DropdownMenuItem,{asChild:!0,"data-alchemy-node":node.id,children:jsxs("a",{href:node.href,target:node.target,className:size.row,children:[iconOf(node,engine),jsx2("span",{className:"truncate",children:node.label})]})});case"item":return jsx2(LeafItem,{node,engine})}}function SubmenuView({node,engine}){let triggerRef=React4.useRef(null),size=MENU_SIZE_CLASSES[useMenuSize()];return jsxs(DropdownMenuSub,{onOpenChange:open=>{open&&node.lazy&&!node.loading&&engine.expand(node.id)},children:[jsxs(DropdownMenuSubTrigger,{ref:triggerRef,"data-alchemy-node":node.id,className:size.row,"aria-busy":node.pending?!0:void 0,children:[iconOf(node,engine),jsx2("span",{className:"truncate",children:node.label}),node.pending?jsx2("span",{className:"ml-auto text-xs text-muted-foreground",children:"Loading\u2026"}):null]}),jsx2(DropdownMenuSubContent,{"data-alchemy-submenu":node.id,onFocusOutside:event=>{let to=event.target,trigger=triggerRef.current;to instanceof Element&&trigger&&to.getAttribute("role")==="menu"&&to.contains(trigger)&&event.preventDefault()},className:`${size.panel} max-w-[calc(100vw-1rem)] max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto`,children:node.loading?jsx2(DropdownMenuLabel,{className:"text-xs text-muted-foreground",children:"Loading\u2026"}):node.children.length===0&&!node.lazy?jsx2(DropdownMenuLabel,{className:"text-xs text-muted-foreground",children:node.emptyLabel??"Nothing here yet"}):jsx2(NodeList,{nodes:node.children,engine})})]})}function LeafItem({node,engine}){let size=MENU_SIZE_CLASSES[useMenuSize()],row=jsxs(DropdownMenuItem,{"data-alchemy-node":node.id,"aria-disabled":node.unavailable?!0:void 0,"aria-pressed":node.pressed,onSelect:event=>{if(node.unavailable){event.preventDefault();return}node.keepsMenuOpen&&event.preventDefault(),engine.run(node.id)},className:`${size.row} ${node.unavailable?"opacity-50":""} ${node.destructive?"text-destructive":""}`,children:[iconOf(node,engine),jsxs("span",{className:"flex min-w-0 flex-1 flex-col",children:[jsx2("span",{className:"truncate",children:node.label}),node.description?jsx2("span",{className:"truncate text-xs text-muted-foreground",children:node.description}):null]}),node.hint?jsx2("span",{className:`ml-auto ${size.hint} text-muted-foreground`,children:node.hint}):null]});return node.unavailable?jsxs(Tooltip,{children:[jsx2(TooltipTrigger,{asChild:!0,children:row}),jsx2(TooltipContent,{children:node.unavailable.sentence})]}):row}function LeafButton({node,engine,showLabel}){if(node.kind!=="item"&&node.kind!=="checkbox"&&node.kind!=="link")return null;let unavailable=node.kind==="item"?node.unavailable:void 0,pressed=node.kind==="item"?node.pressed:node.kind==="checkbox"?node.checked:void 0,glyph=iconOf(node,engine),size=MENU_SIZE_CLASSES[useMenuSize()],destructive=node.kind==="item"&&node.destructive,button=jsxs("button",{type:"button","data-alchemy-node":node.id,"data-matrx-button":"","data-tap-floor":"","data-tap-floor-square":"","aria-label":node.label,"aria-disabled":unavailable?!0:void 0,"aria-pressed":pressed,onMouseDown:e=>{node.kind==="item"&&node.preserveSelection&&e.preventDefault()},onClick:()=>{if(unavailable){engine.explain(unavailable.sentence);return}if(node.kind==="link"){window.location.assign(node.href);return}engine.run(node.id)},className:`inline-flex ${size.stripButton} pointer-coarse:h-11 pointer-coarse:min-w-11 shrink-0 items-center justify-center gap-1.5 rounded-md text-sm ${destructive?"text-destructive hover:bg-destructive/10":"text-muted-foreground hover:bg-accent hover:text-foreground"} ${unavailable?"cursor-default opacity-50 hover:bg-transparent":""} ${pressed?"text-foreground":""}`,children:[glyph,showLabel||!glyph?jsx2("span",{className:"whitespace-nowrap",children:node.label}):null]});return jsxs(Tooltip,{children:[jsx2(TooltipTrigger,{asChild:!0,children:button}),jsx2(TooltipContent,{children:unavailable?unavailable.sentence:node.label})]})}import{Fragment as Fragment3,jsx as jsx3,jsxs as jsxs2}from"react/jsx-runtime";function isNarrow(width){return typeof window>"u"?!1:window.matchMedia?.(`(max-width: ${width-1}px)`).matches??!1}var TRIGGER_CLASS="inline-flex h-7 min-w-7 pointer-coarse:h-11 pointer-coarse:min-w-11 items-center justify-center gap-1 rounded-md px-1.5 text-muted-foreground hover:bg-accent hover:text-foreground";function PrimarySubmenu({node,engine}){return jsxs2(DropdownMenu,{onOpenChange:open=>{open&&node.lazy&&!node.loading&&engine.expand(node.id)},children:[jsx3(DropdownMenuTrigger,{asChild:!0,children:jsx3("button",{type:"button","aria-label":node.label,"data-alchemy-node":node.id,className:TRIGGER_CLASS,children:engine.icon(node.icon)??jsx3("span",{className:"text-sm",children:node.label})})}),jsx3(DropdownMenuContent,{align:"start",className:"w-64 max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:node.loading?jsx3("div",{className:"px-2 py-1.5 text-xs text-muted-foreground",children:"Loading\u2026"}):jsx3(NodeList,{nodes:node.children,engine})})]})}function ActionBar({target,mini=!1,className,hideOverflow=!1,sheetBelow=768,loadSheet=()=>import("@ai-matrx/alchemy/react/sheet"),...options}){let engine=useMenuEngine(target,options),[sheetOpen,setSheetOpen]=React5.useState(!1),[Sheet,setSheet]=React5.useState(null);if(!engine.model)return null;let arranged=bar(engine.model,{mini});return arranged.primary.length===0&&arranged.overflow.length===0?null:jsx3(TooltipProvider,{children:jsxs2("div",{role:"toolbar","aria-label":"Actions","data-alchemy-layout":"bar",className:`flex items-center ${mini?"gap-0":"gap-0.5"} ${className??""}`,children:[arranged.primary.map(node=>node.kind==="submenu"?jsx3(PrimarySubmenu,{node,engine},node.id):jsx3(LeafButton,{node,engine,showLabel:!1},node.id)),hideOverflow||arranged.overflow.length===0?null:isNarrow(sheetBelow)?jsxs2(Fragment3,{children:[jsx3("button",{type:"button","aria-label":"More actions","data-alchemy-trigger":"overflow",className:TRIGGER_CLASS,onClick:()=>{setSheetOpen(!0),Sheet||loadSheet().then(m=>setSheet(()=>m.ActionSheet))},children:jsx3("span",{"aria-hidden":!0,className:"text-base leading-none",children:"\u22EF"})}),Sheet?jsx3(Sheet,{...options,target,open:sheetOpen,onOpenChange:setSheetOpen}):null]}):jsxs2(DropdownMenu,{children:[jsx3(DropdownMenuTrigger,{asChild:!0,children:jsx3("button",{type:"button","aria-label":"More actions","data-alchemy-trigger":"overflow",className:TRIGGER_CLASS,children:jsx3("span",{"aria-hidden":!0,className:"text-base leading-none",children:"\u22EF"})})}),jsx3(DropdownMenuContent,{align:"end",className:"w-64 max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:jsx3(SectionList,{sections:arranged.overflow,engine})})]})]})})}export{ActionBar};
|
|
4
4
|
//# sourceMappingURL=react-bar.js.map
|