@aglyn/plugins-mui 1.0.0-beta.143
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/LICENSE +201 -0
- package/README.md +22 -0
- package/package.json +47 -0
- package/src/index.d.ts +21 -0
- package/src/index.js +22 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/accordion.d.ts +96 -0
- package/src/lib/components/accordion.js +392 -0
- package/src/lib/components/accordion.js.map +1 -0
- package/src/lib/components/app-bar.d.ts +32 -0
- package/src/lib/components/app-bar.js +86 -0
- package/src/lib/components/app-bar.js.map +1 -0
- package/src/lib/components/blocks.d.ts +46 -0
- package/src/lib/components/blocks.js +1007 -0
- package/src/lib/components/blocks.js.map +1 -0
- package/src/lib/components/box.d.ts +47 -0
- package/src/lib/components/box.js +125 -0
- package/src/lib/components/box.js.map +1 -0
- package/src/lib/components/breadcrumbs.d.ts +44 -0
- package/src/lib/components/breadcrumbs.js +172 -0
- package/src/lib/components/breadcrumbs.js.map +1 -0
- package/src/lib/components/button.d.ts +89 -0
- package/src/lib/components/button.js +272 -0
- package/src/lib/components/button.js.map +1 -0
- package/src/lib/components/card.d.ts +61 -0
- package/src/lib/components/card.js +336 -0
- package/src/lib/components/card.js.map +1 -0
- package/src/lib/components/collection.d.ts +514 -0
- package/src/lib/components/collection.js +2999 -0
- package/src/lib/components/collection.js.map +1 -0
- package/src/lib/components/container.d.ts +42 -0
- package/src/lib/components/container.js +169 -0
- package/src/lib/components/container.js.map +1 -0
- package/src/lib/components/custom-html.d.ts +78 -0
- package/src/lib/components/custom-html.js +219 -0
- package/src/lib/components/custom-html.js.map +1 -0
- package/src/lib/components/data-table.d.ts +57 -0
- package/src/lib/components/data-table.js +216 -0
- package/src/lib/components/data-table.js.map +1 -0
- package/src/lib/components/document-root.d.ts +77 -0
- package/src/lib/components/document-root.js +118 -0
- package/src/lib/components/document-root.js.map +1 -0
- package/src/lib/components/drawer.d.ts +93 -0
- package/src/lib/components/drawer.js +566 -0
- package/src/lib/components/drawer.js.map +1 -0
- package/src/lib/components/function-controls.d.ts +66 -0
- package/src/lib/components/function-controls.js +207 -0
- package/src/lib/components/function-controls.js.map +1 -0
- package/src/lib/components/function-scope.d.ts +110 -0
- package/src/lib/components/function-scope.js +478 -0
- package/src/lib/components/function-scope.js.map +1 -0
- package/src/lib/components/function-widget.d.ts +75 -0
- package/src/lib/components/function-widget.js +307 -0
- package/src/lib/components/function-widget.js.map +1 -0
- package/src/lib/components/grid.d.ts +91 -0
- package/src/lib/components/grid.js +322 -0
- package/src/lib/components/grid.js.map +1 -0
- package/src/lib/components/icon.d.ts +54 -0
- package/src/lib/components/icon.js +163 -0
- package/src/lib/components/icon.js.map +1 -0
- package/src/lib/components/image-list.d.ts +58 -0
- package/src/lib/components/image-list.js +336 -0
- package/src/lib/components/image-list.js.map +1 -0
- package/src/lib/components/image.d.ts +154 -0
- package/src/lib/components/image.js +451 -0
- package/src/lib/components/image.js.map +1 -0
- package/src/lib/components/inline-text.d.ts +74 -0
- package/src/lib/components/inline-text.js +407 -0
- package/src/lib/components/inline-text.js.map +1 -0
- package/src/lib/components/language-switcher.d.ts +35 -0
- package/src/lib/components/language-switcher.js +179 -0
- package/src/lib/components/language-switcher.js.map +1 -0
- package/src/lib/components/layout-slot.d.ts +68 -0
- package/src/lib/components/layout-slot.js +192 -0
- package/src/lib/components/layout-slot.js.map +1 -0
- package/src/lib/components/link-box-accessible-name.d.ts +79 -0
- package/src/lib/components/link-box-accessible-name.js +158 -0
- package/src/lib/components/link-box-accessible-name.js.map +1 -0
- package/src/lib/components/link-box.d.ts +66 -0
- package/src/lib/components/link-box.js +212 -0
- package/src/lib/components/link-box.js.map +1 -0
- package/src/lib/components/list-item-text.d.ts +22 -0
- package/src/lib/components/list-item-text.js +86 -0
- package/src/lib/components/list-item-text.js.map +1 -0
- package/src/lib/components/list-item.d.ts +32 -0
- package/src/lib/components/list-item.js +127 -0
- package/src/lib/components/list-item.js.map +1 -0
- package/src/lib/components/list.d.ts +34 -0
- package/src/lib/components/list.js +108 -0
- package/src/lib/components/list.js.map +1 -0
- package/src/lib/components/markdown.d.ts +129 -0
- package/src/lib/components/markdown.js +853 -0
- package/src/lib/components/markdown.js.map +1 -0
- package/src/lib/components/nav-menu.d.ts +68 -0
- package/src/lib/components/nav-menu.js +683 -0
- package/src/lib/components/nav-menu.js.map +1 -0
- package/src/lib/components/pagination.d.ts +59 -0
- package/src/lib/components/pagination.js +243 -0
- package/src/lib/components/pagination.js.map +1 -0
- package/src/lib/components/paper.d.ts +47 -0
- package/src/lib/components/paper.js +202 -0
- package/src/lib/components/paper.js.map +1 -0
- package/src/lib/components/plugin-frame.d.ts +64 -0
- package/src/lib/components/plugin-frame.js +335 -0
- package/src/lib/components/plugin-frame.js.map +1 -0
- package/src/lib/components/plugin.d.ts +95 -0
- package/src/lib/components/plugin.js +304 -0
- package/src/lib/components/plugin.js.map +1 -0
- package/src/lib/components/product.d.ts +40 -0
- package/src/lib/components/product.js +287 -0
- package/src/lib/components/product.js.map +1 -0
- package/src/lib/components/reusable-instance.d.ts +57 -0
- package/src/lib/components/reusable-instance.js +126 -0
- package/src/lib/components/reusable-instance.js.map +1 -0
- package/src/lib/components/screen-link.d.ts +85 -0
- package/src/lib/components/screen-link.js +324 -0
- package/src/lib/components/screen-link.js.map +1 -0
- package/src/lib/components/search-box.d.ts +41 -0
- package/src/lib/components/search-box.js +125 -0
- package/src/lib/components/search-box.js.map +1 -0
- package/src/lib/components/section.d.ts +56 -0
- package/src/lib/components/section.js +132 -0
- package/src/lib/components/section.js.map +1 -0
- package/src/lib/components/stack.d.ts +55 -0
- package/src/lib/components/stack.js +309 -0
- package/src/lib/components/stack.js.map +1 -0
- package/src/lib/components/tabs.d.ts +288 -0
- package/src/lib/components/tabs.js +788 -0
- package/src/lib/components/tabs.js.map +1 -0
- package/src/lib/components/theme-mode-switcher.d.ts +38 -0
- package/src/lib/components/theme-mode-switcher.js +168 -0
- package/src/lib/components/theme-mode-switcher.js.map +1 -0
- package/src/lib/components/toolbar.d.ts +37 -0
- package/src/lib/components/toolbar.js +117 -0
- package/src/lib/components/toolbar.js.map +1 -0
- package/src/lib/components/typography.d.ts +33 -0
- package/src/lib/components/typography.js +433 -0
- package/src/lib/components/typography.js.map +1 -0
- package/src/lib/components/video-lightbox-controls.d.ts +131 -0
- package/src/lib/components/video-lightbox-controls.js +536 -0
- package/src/lib/components/video-lightbox-controls.js.map +1 -0
- package/src/lib/components/video-lightbox.d.ts +60 -0
- package/src/lib/components/video-lightbox.js +247 -0
- package/src/lib/components/video-lightbox.js.map +1 -0
- package/src/lib/components/video-playback-beacon.d.ts +80 -0
- package/src/lib/components/video-playback-beacon.js +128 -0
- package/src/lib/components/video-playback-beacon.js.map +1 -0
- package/src/lib/components/video-player-frame.d.ts +58 -0
- package/src/lib/components/video-player-frame.js +51 -0
- package/src/lib/components/video-player-frame.js.map +1 -0
- package/src/lib/components/video.d.ts +220 -0
- package/src/lib/components/video.js +874 -0
- package/src/lib/components/video.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +17 -0
- package/src/lib/constants/bundle-common.js +18 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/constants/field-presets.d.ts +93 -0
- package/src/lib/constants/field-presets.js +248 -0
- package/src/lib/constants/field-presets.js.map +1 -0
- package/src/lib/plugin.d.ts +80 -0
- package/src/lib/plugin.js +599 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/utils/author-html-label.d.ts +73 -0
- package/src/lib/utils/author-html-label.js +89 -0
- package/src/lib/utils/author-html-label.js.map +1 -0
- package/src/lib/utils/drop-cleared-props.d.ts +18 -0
- package/src/lib/utils/drop-cleared-props.js +74 -0
- package/src/lib/utils/drop-cleared-props.js.map +1 -0
- package/src/lib/utils/element-picker.d.ts +99 -0
- package/src/lib/utils/element-picker.js +140 -0
- package/src/lib/utils/element-picker.js.map +1 -0
- package/src/lib/utils/generate-preset-id.d.ts +19 -0
- package/src/lib/utils/generate-preset-id.js +25 -0
- package/src/lib/utils/generate-preset-id.js.map +1 -0
- package/src/lib/utils/link-target-props.d.ts +53 -0
- package/src/lib/utils/link-target-props.js +53 -0
- package/src/lib/utils/link-target-props.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/plugin-frame.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport Box from '@mui/material/Box'\nimport type { SxProps } from '@mui/material/styles'\nimport {\n forwardRef,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\n/**\n * Host-side sandboxed plugin runtime (AGL-45), per AGL-43 §2. Renders an\n * installed executable plugin inside a cross-origin sandboxed iframe and\n * talks to it only through the capability-scoped postMessage bridge — the\n * plugin never runs in the console/tenant realm, so it has no ambient\n * access to host Firestore, auth, storage, or the DOM. Renders a safe\n * placeholder (never the plugin) whenever it can't run safely: no plugin\n * origin configured, the origin isn't cross-origin to this app, or the\n * version is revoked (kill switch).\n *\n * SANDBOX NOTE: `allow-scripts allow-same-origin` is correct HERE and only\n * here because `src` is a DEDICATED cross-origin (`pluginOrigin`). For a\n * cross-origin frame, `allow-same-origin` grants the frame ITS OWN\n * (plugin) origin's privileges — not the parent's — so it stays walled off\n * from the app while still being able to load its own bundle and honor the\n * plugin-origin CSP's `connect-src`. The combination is only unsafe for a\n * SAME-origin frame; `isCrossOrigin` below refuses that case.\n *\n * SERVER RENDER: the origin comparison needs `window.location.origin`, so a\n * server render cannot make it and neither render that has to agree with the\n * server makes it. Both emit the frame with no `src` — the plugin URL is\n * attached on the render after mount, once the origin has actually been\n * checked. That keeps the server HTML and the first client render identical\n * whatever `pluginOrigin` is set to, and it keeps an unverified origin out of\n * the markup a browser would start loading before any of this code runs.\n */\nexport interface PluginFrameProps {\n /** Dedicated plugin origin, e.g. `https://plugins.aglyn.app`. */\n pluginOrigin?: string\n /** Host id, for scoping host-mediated fetch (AGL-191). */\n hostId?: string\n listingId: string\n /**\n * Which DECLARED element of the plugin to render (AGL-1031).\n *\n * A FRAME prop, never a member of `pluginProps`: those are filtered against\n * the manifest allowlist, so an element id routed through them would be\n * dropped for every plugin that had not thought to declare `elementId` as a\n * settable prop — silently, and only for declared elements. It is host\n * metadata like `version` and `sha256`, and travels with them.\n */\n elementId?: string\n version: string\n sha256: string\n /** Manifest capabilities (event allowlist, declared size). */\n capabilities?: Aglyn.PluginCapabilities\n /** Props to pass in; filtered to the manifest allowlist before sending. */\n pluginProps?: Record<string, unknown>\n /** True when the pinned version is revoked (from the revocations doc). */\n revoked?: boolean\n scheme?: 'light' | 'dark'\n title?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed\n * into an attribute — recomposed below so the Styles panel's values are\n * merged rather than replaced (AGL-1284).\n */\n sx?: SxProps\n}\n\ntype FrameState = 'loading' | 'ready' | 'error'\n\n/**\n * Does `pluginOrigin` resolve to an origin other than the app's own?\n *\n * True is the only answer that lets the frame render: the sandbox pair in\n * the SANDBOX NOTE is containment exactly while the frame's origin differs\n * from the page's, and is a no-op when it doesn't.\n *\n * Browser-only, and unavoidably so. The app's origin is\n * `window.location.origin` and no constant stands in for it — a site answers\n * on both its platform address and any custom domain attached to it, and the\n * console, a preview, a self-host container and a dev server each run this\n * under a different one. Callers ask only once mounted; the `catch` refuses\n * an unparsable origin, and refuses a call made without a `window` for the\n * same reason — an origin nothing compared is not an origin anything cleared.\n */\nfunction isCrossOrigin(pluginOrigin: string): boolean {\n try {\n return new URL(pluginOrigin).origin !== window.location.origin\n } catch {\n return false\n }\n}\n\ninterface PlaceholderProps {\n message: string\n height?: number\n}\n\nconst Placeholder = forwardRef<HTMLDivElement, PlaceholderProps>(\n ({ message, height }, ref) => (\n <Box\n ref={ref}\n sx={{\n minHeight: height ?? 120,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontSize: 13,\n fontFamily: 'system-ui, sans-serif',\n p: 2,\n textAlign: 'center',\n }}\n >\n {message}\n </Box>\n ),\n)\nPlaceholder.displayName = 'PluginFramePlaceholder'\n\nconst PluginFrame = forwardRef<HTMLIFrameElement, PluginFrameProps>(\n (props, ref) => {\n const {\n pluginOrigin,\n hostId,\n listingId,\n elementId,\n version,\n sha256,\n capabilities,\n pluginProps,\n revoked,\n scheme,\n title,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const frameRef = useRef<HTMLIFrameElement | null>(null)\n const [state, setState] = useState<FrameState>('loading')\n const [height, setHeight] = useState<number>(\n capabilities?.size?.height ?? 120,\n )\n /**\n * Whether this render is running in a browser, where `isCrossOrigin` can\n * be answered.\n *\n * Every other input here is a prop or a build-time env and so reads the\n * same on both sides; the origin comparison is the one that doesn't, and\n * a render that branches on it disagrees with the server for any\n * `pluginOrigin` that is same-origin to the app. Deferring it to the\n * render after mount costs nothing visible: the frame is hidden until the\n * guest posts `ready`, and the listener that hears `ready` is registered\n * in an effect, so no plugin has ever painted before this point either.\n */\n const [mounted, setMounted] = useState(false)\n useEffect(() => setMounted(true), [])\n\n const allowedEvents = capabilities?.events\n const allowedProps = capabilities?.props\n const filteredProps = useMemo(\n () => Aglyn.filterPluginProps(pluginProps, allowedProps),\n [pluginProps, allowedProps],\n )\n // Stable dependency for the props-update effect.\n const filteredPropsKey = useMemo(\n () => JSON.stringify(filteredProps),\n [filteredProps],\n )\n\n /**\n * Three answers, not two: `null` while the check is still unanswerable,\n * `false` once the browser has refused the origin. Collapsing them would\n * put the refusal placeholder in the server HTML of every correctly\n * configured site, which is the same mismatch pointing the other way.\n */\n const crossOrigin: boolean | null =\n mounted && pluginOrigin ? isCrossOrigin(pluginOrigin) : null\n\n const originUsable = !revoked && crossOrigin === true\n\n // Undefined, never `''`: an empty `src` is still an `src`, and the frame\n // must carry no address at all until the origin behind it has cleared.\n const src = useMemo(() => {\n if (!originUsable) return undefined\n const url = new URL('/load', String(pluginOrigin))\n url.searchParams.set('listing', listingId)\n url.searchParams.set('v', version)\n url.searchParams.set('sha', sha256)\n if (elementId) url.searchParams.set('element', elementId)\n // The framing host (AGL-884): the origin resolves this to the site's\n // VERIFIED custom domain and widens frame-ancestors to exactly that —\n // never to caller-supplied origins.\n if (hostId) url.searchParams.set('host', hostId)\n return url.toString()\n }, [originUsable, pluginOrigin, hostId, listingId, elementId, version, sha256])\n\n // Bridge: validate every inbound message by origin + source + schema,\n // then honor resize / surface events / flag errors. Never eval or graft\n // anything from the frame — only sized output and named events.\n useEffect(() => {\n if (!originUsable) return\n const expectedOrigin = new URL(String(pluginOrigin)).origin\n const handleMessage = (event: MessageEvent) => {\n if (event.origin !== expectedOrigin) return\n if (event.source !== frameRef.current?.contentWindow) return\n const message = Aglyn.parseGuestMessage(event.data, allowedEvents)\n if (!message) return\n switch (message.type) {\n case 'ready':\n setState('ready')\n frameRef.current?.contentWindow?.postMessage(\n {\n type: 'init',\n v: Aglyn.PLUGIN_BRIDGE_VERSION,\n props: filteredProps,\n ...(scheme ? { scheme } : {}),\n },\n expectedOrigin,\n )\n break\n case 'resize':\n setHeight(Math.max(24, message.height))\n break\n case 'event':\n Aglyn.emitter?.emit?.(`plugin:${listingId}:${message.name}`, {\n listingId,\n name: message.name,\n payload: message.payload,\n })\n break\n case 'fetch-request': {\n // Host-mediated fetch (AGL-191): proxy through the host API,\n // which re-checks the manifest allowlist server-side. Reply on\n // the same id; a disallowed/failed call resolves ok:false.\n const requestId = message.id\n const respond = (payload: {\n ok: boolean\n status: number\n body?: string\n error?: string\n }) =>\n frameRef.current?.contentWindow?.postMessage(\n { type: 'fetch-response', v: Aglyn.PLUGIN_BRIDGE_VERSION, id: requestId, ...payload },\n expectedOrigin,\n )\n if (!hostId) {\n respond({ ok: false, status: 0, error: 'No host context' })\n break\n }\n void fetch('/api/plugins/fetch', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n listingId,\n url: message.url,\n method: message.method,\n body: message.body,\n }),\n })\n .then((response) => response.json())\n .then((payload) =>\n respond({\n ok: Boolean(payload?.ok),\n status: Number(payload?.status ?? 0),\n body: payload?.body,\n error: payload?.error,\n }),\n )\n .catch(() =>\n respond({ ok: false, status: 0, error: 'Fetch failed' }),\n )\n break\n }\n case 'error':\n setState('error')\n break\n }\n }\n window.addEventListener('message', handleMessage)\n return () => window.removeEventListener('message', handleMessage)\n // filteredProps is re-sent by the effect below; init uses the latest.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [originUsable, pluginOrigin, hostId, listingId, allowedEvents, scheme, src])\n\n // Push prop updates to an already-ready frame.\n useEffect(() => {\n if (state !== 'ready' || !originUsable) return\n const expectedOrigin = new URL(String(pluginOrigin)).origin\n frameRef.current?.contentWindow?.postMessage(\n {\n type: 'props',\n v: Aglyn.PLUGIN_BRIDGE_VERSION,\n props: filteredProps,\n },\n expectedOrigin,\n )\n // filteredPropsKey captures the serialized props identity.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [filteredPropsKey, state, originUsable, pluginOrigin])\n\n if (revoked) {\n return (\n <Placeholder message=\"This plugin has been disabled by the platform.\" />\n )\n }\n if (!pluginOrigin) {\n return (\n <Placeholder message=\"Plugins are not enabled on this deployment.\" />\n )\n }\n // A refusal the browser actually made. `null` — not yet checked — falls\n // through to the frame below, which carries no `src` until it clears.\n if (crossOrigin === false) {\n return (\n <Placeholder message=\"Plugin cannot be loaded safely here.\" />\n )\n }\n\n return (\n <Box sx={{ position: 'relative', width: '100%' }}>\n {state === 'error' ? (\n <Placeholder message=\"This plugin failed to load.\" height={height} />\n ) : (\n <Box\n component=\"iframe\"\n ref={(node: HTMLIFrameElement | null) => {\n frameRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n src={src}\n title={title ?? 'Plugin'}\n loading=\"lazy\"\n onError={() => setState('error')}\n {...rest}\n // AFTER the spread, deliberately (AGL-2484). See SANDBOX NOTE —\n // this pair is safe only because `src` is a dedicated\n // cross-origin, and JSX resolves a duplicate attribute by taking\n // the LAST one, so listing them before `{...rest}` made the\n // frame's only containment overridable by whatever a caller\n // happened to spread in. No caller passes either today and\n // `PluginFrameProps` declares neither; the props here do arrive\n // from renderers that forward author-controlled bags, which is\n // how \"no caller does this\" stops being true.\n sandbox=\"allow-scripts allow-same-origin\"\n referrerPolicy=\"no-referrer\"\n // MERGE, never replace (AGL-1284). The spread in the old literal\n // LOOKED like a merge and merged nothing — it folded in a local\n // conditional, not the node's `sx`, so every style an author set\n // on a plugin element was discarded. Order matters: the frame's\n // own box goes first so the author can override it, and the\n // load-time hide goes LAST so an authored `visibility` can never\n // reveal a frame that has not finished loading.\n sx={[\n { width: '100%', height, border: 0, display: 'block' },\n ...nodeSx,\n ...(state === 'loading' ? [{ visibility: 'hidden' }] : []),\n ]}\n />\n )}\n </Box>\n )\n },\n)\nPluginFrame.displayName = 'AglynPluginFrame'\n\nexport { PluginFrame }\nexport default PluginFrame\n"],"names":["Aglyn","Box","forwardRef","useEffect","useMemo","useRef","useState","isCrossOrigin","pluginOrigin","URL","origin","window","location","Placeholder","message","height","ref","sx","minHeight","display","alignItems","justifyContent","border","borderColor","borderRadius","color","fontSize","fontFamily","p","textAlign","displayName","PluginFrame","props","capabilities","hostId","listingId","elementId","version","sha256","pluginProps","revoked","scheme","title","rest","nodeSx","Array","isArray","frameRef","state","setState","setHeight","size","mounted","setMounted","allowedEvents","events","allowedProps","filteredProps","filterPluginProps","filteredPropsKey","JSON","stringify","crossOrigin","originUsable","src","undefined","url","String","searchParams","set","toString","expectedOrigin","handleMessage","event","source","current","contentWindow","parseGuestMessage","data","type","postMessage","v","PLUGIN_BRIDGE_VERSION","Math","max","emitter","emit","name","payload","requestId","id","respond","ok","status","error","fetch","method","headers","body","then","response","json","Boolean","Number","catch","addEventListener","removeEventListener","position","width","component","node","loading","onError","sandbox","referrerPolicy","visibility"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;;AAEA,YAAYA,WAAW,eAAc;AACrC,OAAOC,SAAS,oBAAmB;AAEnC,SACEC,UAAU,EACVC,SAAS,EACTC,OAAO,EACPC,MAAM,EACNC,QAAQ,QACH,QAAO;AAgEd;;;;;;;;;;;;;;CAcC,GACD,SAASC,cAAcC,YAAoB;IACzC,IAAI;QACF,OAAO,IAAIC,IAAID,cAAcE,MAAM,KAAKC,OAAOC,QAAQ,CAACF,MAAM;IAChE,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAOA,MAAMG,4BAAcX,WAClB,CAAC,EAAEY,OAAO,EAAEC,MAAM,EAAE,EAAEC,oBACtB,KAACf;QACCe,KAAKA;QACLC,IAAI;YACFC,SAAS,EAAEH,iBAAAA,SAAU;YACrBI,SAAS;YACTC,YAAY;YACZC,gBAAgB;YAChBC,QAAQ;YACRC,aAAa;YACbC,cAAc;YACdC,OAAO;YACPC,UAAU;YACVC,YAAY;YACZC,GAAG;YACHC,WAAW;QACb;kBAECf;;AAILD,YAAYiB,WAAW,GAAG;AAE1B,MAAMC,4BAAc7B,WAClB,CAAC8B,OAAOhB;;QAoBJiB;IAnBF,MAAM,EACJzB,YAAY,EACZ0B,MAAM,EACNC,SAAS,EACTC,SAAS,EACTC,OAAO,EACPC,MAAM,EACNL,YAAY,EACZM,WAAW,EACXC,OAAO,EACPC,MAAM,EACNC,KAAK,EAEN,GAAGV,OADCW,wCACDX;;;;;;;;;;;;;IACJ,yEAAyE;IACzE,MAAMY,SAASC,MAAMC,OAAO,CAACd,KAAK,CAAC,KAAK,IAAIA,KAAK,CAAC,KAAK,GAAG;QAACA,KAAK,CAAC,KAAK;KAAC;IACvE,MAAMe,WAAW1C,OAAiC;IAClD,MAAM,CAAC2C,OAAOC,SAAS,GAAG3C,SAAqB;IAC/C,MAAM,CAACS,QAAQmC,UAAU,GAAG5C,iBAC1B2B,iCAAAA,qBAAAA,aAAckB,IAAI,qBAAlBlB,mBAAoBlB,MAAM,mBAAI;IAEhC;;;;;;;;;;;KAWC,GACD,MAAM,CAACqC,SAASC,WAAW,GAAG/C,SAAS;IACvCH,UAAU,IAAMkD,WAAW,OAAO,EAAE;IAEpC,MAAMC,gBAAgBrB,gCAAAA,aAAcsB,MAAM;IAC1C,MAAMC,eAAevB,gCAAAA,aAAcD,KAAK;IACxC,MAAMyB,gBAAgBrD,QACpB,IAAMJ,MAAM0D,iBAAiB,CAACnB,aAAaiB,eAC3C;QAACjB;QAAaiB;KAAa;IAE7B,iDAAiD;IACjD,MAAMG,mBAAmBvD,QACvB,IAAMwD,KAAKC,SAAS,CAACJ,gBACrB;QAACA;KAAc;IAGjB;;;;;KAKC,GACD,MAAMK,cACJV,WAAW5C,eAAeD,cAAcC,gBAAgB;IAE1D,MAAMuD,eAAe,CAACvB,WAAWsB,gBAAgB;IAEjD,yEAAyE;IACzE,uEAAuE;IACvE,MAAME,MAAM5D,QAAQ;QAClB,IAAI,CAAC2D,cAAc,OAAOE;QAC1B,MAAMC,MAAM,IAAIzD,IAAI,SAAS0D,OAAO3D;QACpC0D,IAAIE,YAAY,CAACC,GAAG,CAAC,WAAWlC;QAChC+B,IAAIE,YAAY,CAACC,GAAG,CAAC,KAAKhC;QAC1B6B,IAAIE,YAAY,CAACC,GAAG,CAAC,OAAO/B;QAC5B,IAAIF,WAAW8B,IAAIE,YAAY,CAACC,GAAG,CAAC,WAAWjC;QAC/C,qEAAqE;QACrE,sEAAsE;QACtE,oCAAoC;QACpC,IAAIF,QAAQgC,IAAIE,YAAY,CAACC,GAAG,CAAC,QAAQnC;QACzC,OAAOgC,IAAII,QAAQ;IACrB,GAAG;QAACP;QAAcvD;QAAc0B;QAAQC;QAAWC;QAAWC;QAASC;KAAO;IAE9E,sEAAsE;IACtE,wEAAwE;IACxE,gEAAgE;IAChEnC,UAAU;QACR,IAAI,CAAC4D,cAAc;QACnB,MAAMQ,iBAAiB,IAAI9D,IAAI0D,OAAO3D,eAAeE,MAAM;QAC3D,MAAM8D,gBAAgB,CAACC;gBAEA1B;YADrB,IAAI0B,MAAM/D,MAAM,KAAK6D,gBAAgB;YACrC,IAAIE,MAAMC,MAAM,OAAK3B,oBAAAA,SAAS4B,OAAO,qBAAhB5B,kBAAkB6B,aAAa,GAAE;YACtD,MAAM9D,UAAUd,MAAM6E,iBAAiB,CAACJ,MAAMK,IAAI,EAAExB;YACpD,IAAI,CAACxC,SAAS;YACd,OAAQA,QAAQiE,IAAI;gBAClB,KAAK;wBAEHhC,iCAAAA;oBADAE,SAAS;qBACTF,qBAAAA,SAAS4B,OAAO,sBAAhB5B,kCAAAA,mBAAkB6B,aAAa,qBAA/B7B,gCAAiCiC,WAAW,CAC1C;wBACED,MAAM;wBACNE,GAAGjF,MAAMkF,qBAAqB;wBAC9BlD,OAAOyB;uBACHhB,SAAS;wBAAEA;oBAAO,IAAI,CAAC,IAE7B8B;oBAEF;gBACF,KAAK;oBACHrB,UAAUiC,KAAKC,GAAG,CAAC,IAAItE,QAAQC,MAAM;oBACrC;gBACF,KAAK;wBACHf,qBAAAA;qBAAAA,iBAAAA,MAAMqF,OAAO,sBAAbrF,sBAAAA,eAAesF,IAAI,qBAAnBtF,yBAAAA,gBAAsB,CAAC,OAAO,EAAEmC,UAAU,CAAC,EAAErB,QAAQyE,IAAI,EAAE,EAAE;wBAC3DpD;wBACAoD,MAAMzE,QAAQyE,IAAI;wBAClBC,SAAS1E,QAAQ0E,OAAO;oBAC1B;oBACA;gBACF,KAAK;oBAAiB;wBACpB,6DAA6D;wBAC7D,+DAA+D;wBAC/D,2DAA2D;wBAC3D,MAAMC,YAAY3E,QAAQ4E,EAAE;wBAC5B,MAAMC,UAAU,CAACH;gCAMfzC,iCAAAA;oCAAAA,oBAAAA,SAAS4B,OAAO,sBAAhB5B,kCAAAA,kBAAkB6B,aAAa,qBAA/B7B,gCAAiCiC,WAAW,CAC1C;gCAAED,MAAM;gCAAkBE,GAAGjF,MAAMkF,qBAAqB;gCAAEQ,IAAID;+BAAcD,UAC5EjB;;wBAEJ,IAAI,CAACrC,QAAQ;4BACXyD,QAAQ;gCAAEC,IAAI;gCAAOC,QAAQ;gCAAGC,OAAO;4BAAkB;4BACzD;wBACF;wBACA,KAAKC,MAAM,sBAAsB;4BAC/BC,QAAQ;4BACRC,SAAS;gCAAE,gBAAgB;4BAAmB;4BAC9CC,MAAMtC,KAAKC,SAAS,CAAC;gCACnB3B;gCACAC;gCACA+B,KAAKpD,QAAQoD,GAAG;gCAChB8B,QAAQlF,QAAQkF,MAAM;gCACtBE,MAAMpF,QAAQoF,IAAI;4BACpB;wBACF,GACGC,IAAI,CAAC,CAACC,WAAaA,SAASC,IAAI,IAChCF,IAAI,CAAC,CAACX;;mCACLG,QAAQ;gCACNC,IAAIU,QAAQd,2BAAAA,QAASI,EAAE;gCACvBC,QAAQU,eAAOf,2BAAAA,QAASK,MAAM,mBAAI;gCAClCK,IAAI,EAAEV,2BAAAA,QAASU,IAAI;gCACnBJ,KAAK,EAAEN,2BAAAA,QAASM,KAAK;4BACvB;2BAEDU,KAAK,CAAC,IACLb,QAAQ;gCAAEC,IAAI;gCAAOC,QAAQ;gCAAGC,OAAO;4BAAe;wBAE1D;oBACF;gBACA,KAAK;oBACH7C,SAAS;oBACT;YACJ;QACF;QACAtC,OAAO8F,gBAAgB,CAAC,WAAWjC;QACnC,OAAO,IAAM7D,OAAO+F,mBAAmB,CAAC,WAAWlC;IACnD,sEAAsE;IACtE,uDAAuD;IACzD,GAAG;QAACT;QAAcvD;QAAc0B;QAAQC;QAAWmB;QAAeb;QAAQuB;KAAI;IAE9E,+CAA+C;IAC/C7D,UAAU;YAGR4C,iCAAAA;QAFA,IAAIC,UAAU,WAAW,CAACe,cAAc;QACxC,MAAMQ,iBAAiB,IAAI9D,IAAI0D,OAAO3D,eAAeE,MAAM;SAC3DqC,oBAAAA,SAAS4B,OAAO,sBAAhB5B,kCAAAA,kBAAkB6B,aAAa,qBAA/B7B,gCAAiCiC,WAAW,CAC1C;YACED,MAAM;YACNE,GAAGjF,MAAMkF,qBAAqB;YAC9BlD,OAAOyB;QACT,GACAc;IAEF,2DAA2D;IAC3D,uDAAuD;IACzD,GAAG;QAACZ;QAAkBX;QAAOe;QAAcvD;KAAa;IAExD,IAAIgC,SAAS;QACX,qBACE,KAAC3B;YAAYC,SAAQ;;IAEzB;IACA,IAAI,CAACN,cAAc;QACjB,qBACE,KAACK;YAAYC,SAAQ;;IAEzB;IACA,wEAAwE;IACxE,sEAAsE;IACtE,IAAIgD,gBAAgB,OAAO;QACzB,qBACE,KAACjD;YAAYC,SAAQ;;IAEzB;IAEA,qBACE,KAACb;QAAIgB,IAAI;YAAE0F,UAAU;YAAYC,OAAO;QAAO;kBAC5C5D,UAAU,wBACT,KAACnC;YAAYC,SAAQ;YAA8BC,QAAQA;2BAE3D,KAACd;YACC4G,WAAU;YACV7F,KAAK,CAAC8F;gBACJ/D,SAAS4B,OAAO,GAAGmC;gBACnB,IAAI,OAAO9F,QAAQ,YAAYA,IAAI8F;qBAC9B,IAAI9F,KAAKA,IAAI2D,OAAO,GAAGmC;YAC9B;YACA9C,KAAKA;YACLtB,KAAK,EAAEA,gBAAAA,QAAS;YAChBqE,SAAQ;YACRC,SAAS,IAAM/D,SAAS;WACpBN;YACJ,gEAAgE;YAChE,sDAAsD;YACtD,iEAAiE;YACjE,4DAA4D;YAC5D,4DAA4D;YAC5D,2DAA2D;YAC3D,gEAAgE;YAChE,+DAA+D;YAC/D,8CAA8C;YAC9CsE,SAAQ;YACRC,gBAAe;YACf,iEAAiE;YACjE,gEAAgE;YAChE,iEAAiE;YACjE,gEAAgE;YAChE,4DAA4D;YAC5D,iEAAiE;YACjE,gDAAgD;YAChDjG,IAAI;gBACF;oBAAE2F,OAAO;oBAAQ7F;oBAAQO,QAAQ;oBAAGH,SAAS;gBAAQ;mBAClDyB;mBACCI,UAAU,YAAY;oBAAC;wBAAEmE,YAAY;oBAAS;iBAAE,GAAG,EAAE;aAC1D;;;AAKX;AAEFpF,YAAYD,WAAW,GAAG;AAE1B,SAASC,WAAW,GAAE;AACtB,eAAeA,YAAW"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as Aglyn from '@aglyn/aglyn';
|
|
2
|
+
export declare const ID: Aglyn.ComponentId;
|
|
3
|
+
export interface MarketplacePluginProps {
|
|
4
|
+
/** Installed listing id; the only prop authors set. */
|
|
5
|
+
listingId?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Which DECLARED element of the plugin this node is (AGL-1031). Set by the
|
|
8
|
+
* palette preset; absent for the generic Plugin element. Passed through to
|
|
9
|
+
* the sandboxed frame as an ordinary prop, so one node type and one compose
|
|
10
|
+
* path serve both.
|
|
11
|
+
*/
|
|
12
|
+
elementId?: string;
|
|
13
|
+
/** Injected at compose by `attachPluginInstalls` (AGL-45). */
|
|
14
|
+
version?: string;
|
|
15
|
+
sha256?: string;
|
|
16
|
+
capabilities?: Aglyn.PluginCapabilities;
|
|
17
|
+
revoked?: boolean;
|
|
18
|
+
/** Extra props forwarded to the plugin (filtered to its allowlist). */
|
|
19
|
+
pluginProps?: Record<string, unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* Per-placement plugin settings as JSON (AGL-192), authored in the
|
|
22
|
+
* element panel. Parsed into `pluginProps` and filtered to the
|
|
23
|
+
* manifest's declared props before it reaches the plugin.
|
|
24
|
+
*/
|
|
25
|
+
pluginPropsJson?: string;
|
|
26
|
+
}
|
|
27
|
+
/** Parses the authored settings JSON; invalid input yields no props. */
|
|
28
|
+
export declare function parsePluginPropsJson(raw: string | undefined): Record<string, unknown> | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* What the inert placeholder says, and — more to the point — what it refuses
|
|
31
|
+
* to say (AGL-1029).
|
|
32
|
+
*
|
|
33
|
+
* It used to read "not installed on this site" for EVERY plugin with a listing
|
|
34
|
+
* id, because it inferred installation from `version`/`sha256`: fields the
|
|
35
|
+
* tenant compose pass injects and the canvas never has. Plugins do not execute
|
|
36
|
+
* in the editor by design, so their absence there means nothing about the pin,
|
|
37
|
+
* and the first thing an author saw after installing was a message telling them
|
|
38
|
+
* the install had failed.
|
|
39
|
+
*
|
|
40
|
+
* The claim is only made when it has been earned. The editor publishes the
|
|
41
|
+
* installs it reads (`setKnownPluginInstalls`); a listing id missing from that
|
|
42
|
+
* list really is not installed. Where nothing published a list — a preview —
|
|
43
|
+
* the placeholder states what is true of the placement instead and asserts
|
|
44
|
+
* nothing about installation. A published page draws no placeholder at all.
|
|
45
|
+
*/
|
|
46
|
+
export declare function placeholderText(listingId: string | undefined): string;
|
|
47
|
+
/**
|
|
48
|
+
* Marketplace plugin element (AGL-45): a placement for an installed executable
|
|
49
|
+
* plugin. The saved node carries only `listingId`; the tenant compose pass
|
|
50
|
+
* (`attachPluginInstalls`) injects the pinned version/sha256/capabilities +
|
|
51
|
+
* kill-switch state, and this renders them through the sandboxed
|
|
52
|
+
* `PluginFrame`. Without a resolved install (the editor canvas, or an
|
|
53
|
+
* uninstalled listing) it shows an inert placeholder — plugins never
|
|
54
|
+
* execute in the editor. The plugin origin is a NEXT_PUBLIC env so it's
|
|
55
|
+
* available in the browser bundle; unset = plugins disabled (placeholder).
|
|
56
|
+
*/
|
|
57
|
+
declare const MarketplacePlugin: import("react").ForwardRefExoticComponent<MarketplacePluginProps & import("react").RefAttributes<HTMLElement>>;
|
|
58
|
+
export declare const schema: Aglyn.ComponentSchema<MarketplacePluginProps>;
|
|
59
|
+
export declare const presets: Aglyn.PresetSchema[];
|
|
60
|
+
/** The besigner drawer category installed plugins register under (AGL-190). */
|
|
61
|
+
export declare const PLUGIN_DRAWER_CATEGORY = "Marketplace";
|
|
62
|
+
export interface PluginInstallLike {
|
|
63
|
+
listingId?: string;
|
|
64
|
+
$id?: string;
|
|
65
|
+
displayName?: string;
|
|
66
|
+
pluginId?: string;
|
|
67
|
+
manifest?: {
|
|
68
|
+
name?: string;
|
|
69
|
+
restrictParent?: string[];
|
|
70
|
+
restrictChildren?: string[];
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Builds a besigner preset for an installed plugin (AGL-190): a named,
|
|
75
|
+
* draggable drawer entry that drops a `marketplacePlugin` node with the
|
|
76
|
+
* listing id pre-pinned, so editors never hand-type ids. Reuses the single
|
|
77
|
+
* `marketplacePlugin` renderer — no per-plugin component registration. The
|
|
78
|
+
* manifest's lineal rules ride on the node data for later enforcement.
|
|
79
|
+
* Returns null for an install without a resolvable listing id.
|
|
80
|
+
*/
|
|
81
|
+
export declare function muiPluginInstallToPreset(install: PluginInstallLike): Aglyn.PresetSchema | null;
|
|
82
|
+
/**
|
|
83
|
+
* Every drawer preset an installed plugin contributes (AGL-1031).
|
|
84
|
+
*
|
|
85
|
+
* The generic Plugin element, plus one per element the PINNED version
|
|
86
|
+
* declares. Declared elements save as the same node with an `elementId`
|
|
87
|
+
* alongside the listing id — the issue's preferred answer, and the one that
|
|
88
|
+
* keeps a single compose path and a single sandbox. What changes is the
|
|
89
|
+
* palette entry and the label, not what executes.
|
|
90
|
+
*
|
|
91
|
+
* Resolved from the pin, so an element appears only where the plugin is
|
|
92
|
+
* installed and disappears with a revoked or downgraded version.
|
|
93
|
+
*/
|
|
94
|
+
export declare function muiPluginInstallToPresets(install: PluginInstallLike): Aglyn.PresetSchema[];
|
|
95
|
+
export default MarketplacePlugin;
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
19
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
20
|
+
import * as Aglyn from "@aglyn/aglyn";
|
|
21
|
+
import { MdiIcons } from "@aglyn/shared-data-mdi";
|
|
22
|
+
import { mdiPuzzle } from "@aglyn/shared-data-mdi";
|
|
23
|
+
import Box from "@mui/material/Box";
|
|
24
|
+
import { forwardRef, useContext } from "react";
|
|
25
|
+
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
26
|
+
import { generatePresetId } from "../utils/generate-preset-id.js";
|
|
27
|
+
import { PluginFrame } from "./plugin-frame.js";
|
|
28
|
+
// Component id is persisted in screen documents; never rename.
|
|
29
|
+
export const ID = Aglyn.PLUGIN_COMPONENT_ID;
|
|
30
|
+
/** Parses the authored settings JSON; invalid input yields no props. */ export function parsePluginPropsJson(raw) {
|
|
31
|
+
if (!raw || !raw.trim()) return undefined;
|
|
32
|
+
try {
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : undefined;
|
|
35
|
+
} catch (unused) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* What the inert placeholder says, and — more to the point — what it refuses
|
|
41
|
+
* to say (AGL-1029).
|
|
42
|
+
*
|
|
43
|
+
* It used to read "not installed on this site" for EVERY plugin with a listing
|
|
44
|
+
* id, because it inferred installation from `version`/`sha256`: fields the
|
|
45
|
+
* tenant compose pass injects and the canvas never has. Plugins do not execute
|
|
46
|
+
* in the editor by design, so their absence there means nothing about the pin,
|
|
47
|
+
* and the first thing an author saw after installing was a message telling them
|
|
48
|
+
* the install had failed.
|
|
49
|
+
*
|
|
50
|
+
* The claim is only made when it has been earned. The editor publishes the
|
|
51
|
+
* installs it reads (`setKnownPluginInstalls`); a listing id missing from that
|
|
52
|
+
* list really is not installed. Where nothing published a list — a preview —
|
|
53
|
+
* the placeholder states what is true of the placement instead and asserts
|
|
54
|
+
* nothing about installation. A published page draws no placeholder at all.
|
|
55
|
+
*/ export function placeholderText(listingId) {
|
|
56
|
+
if (!listingId) return 'Plugin — pick an installed plugin';
|
|
57
|
+
const known = Aglyn.getKnownPluginInstall(listingId);
|
|
58
|
+
if (known) {
|
|
59
|
+
const name = known.displayName || 'Plugin';
|
|
60
|
+
return known.scope === 'org' ? `${name} — installed org-wide; renders on the published site` : `${name} — renders on the published site`;
|
|
61
|
+
}
|
|
62
|
+
return Aglyn.hasKnownPluginInstalls() ? 'Plugin — not installed on this site' : 'Plugin — renders on the published site';
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Marketplace plugin element (AGL-45): a placement for an installed executable
|
|
66
|
+
* plugin. The saved node carries only `listingId`; the tenant compose pass
|
|
67
|
+
* (`attachPluginInstalls`) injects the pinned version/sha256/capabilities +
|
|
68
|
+
* kill-switch state, and this renders them through the sandboxed
|
|
69
|
+
* `PluginFrame`. Without a resolved install (the editor canvas, or an
|
|
70
|
+
* uninstalled listing) it shows an inert placeholder — plugins never
|
|
71
|
+
* execute in the editor. The plugin origin is a NEXT_PUBLIC env so it's
|
|
72
|
+
* available in the browser bundle; unset = plugins disabled (placeholder).
|
|
73
|
+
*/ const MarketplacePlugin = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
74
|
+
var _parsePluginPropsJson;
|
|
75
|
+
const { listingId, elementId, version, sha256, capabilities, revoked, pluginProps, pluginPropsJson } = props, rest = _object_without_properties_loose(props, [
|
|
76
|
+
"listingId",
|
|
77
|
+
"elementId",
|
|
78
|
+
"version",
|
|
79
|
+
"sha256",
|
|
80
|
+
"capabilities",
|
|
81
|
+
"revoked",
|
|
82
|
+
"pluginProps",
|
|
83
|
+
"pluginPropsJson"
|
|
84
|
+
]);
|
|
85
|
+
// Node styles ride the renderer-merged sx; recompose (stack.ts pattern).
|
|
86
|
+
const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [
|
|
87
|
+
props['sx']
|
|
88
|
+
];
|
|
89
|
+
// Merge authored settings (AGL-192) under any compose-injected props;
|
|
90
|
+
// PluginFrame filters the result to the manifest allowlist.
|
|
91
|
+
const resolvedPluginProps = _extends({}, pluginProps != null ? pluginProps : {}, (_parsePluginPropsJson = parsePluginPropsJson(pluginPropsJson)) != null ? _parsePluginPropsJson : {});
|
|
92
|
+
const pluginOrigin = typeof process !== 'undefined' ? // form reads undefined in the browser.
|
|
93
|
+
process.env.NEXT_PUBLIC_PLUGIN_ORIGIN : undefined;
|
|
94
|
+
// Host id for host-mediated fetch (AGL-191); empty in the editor.
|
|
95
|
+
const { hostId } = Aglyn.useSite();
|
|
96
|
+
const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext);
|
|
97
|
+
// No resolved install (editor canvas / uninstalled): inert placeholder,
|
|
98
|
+
// which speaks to the author, so a published page renders the bare
|
|
99
|
+
// element instead.
|
|
100
|
+
if (!listingId || !version || !sha256) {
|
|
101
|
+
if (!suppressNavigation) return /*#__PURE__*/ _jsx(Box, _extends({
|
|
102
|
+
ref: ref
|
|
103
|
+
}, rest));
|
|
104
|
+
return /*#__PURE__*/ _jsx(Box, _extends({
|
|
105
|
+
ref: ref
|
|
106
|
+
}, rest, {
|
|
107
|
+
sx: [
|
|
108
|
+
{
|
|
109
|
+
minHeight: 120,
|
|
110
|
+
display: 'flex',
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
justifyContent: 'center',
|
|
113
|
+
border: '1px dashed',
|
|
114
|
+
borderColor: 'divider',
|
|
115
|
+
borderRadius: 1,
|
|
116
|
+
color: 'text.secondary',
|
|
117
|
+
fontSize: 13,
|
|
118
|
+
fontFamily: 'system-ui, sans-serif',
|
|
119
|
+
p: 2,
|
|
120
|
+
textAlign: 'center'
|
|
121
|
+
},
|
|
122
|
+
...nodeSx
|
|
123
|
+
],
|
|
124
|
+
children: placeholderText(listingId)
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
return /*#__PURE__*/ _jsx(PluginFrame, {
|
|
128
|
+
ref: ref,
|
|
129
|
+
pluginOrigin: pluginOrigin,
|
|
130
|
+
hostId: hostId,
|
|
131
|
+
listingId: listingId,
|
|
132
|
+
elementId: elementId,
|
|
133
|
+
version: version,
|
|
134
|
+
sha256: sha256,
|
|
135
|
+
capabilities: capabilities,
|
|
136
|
+
pluginProps: resolvedPluginProps,
|
|
137
|
+
revoked: revoked
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
MarketplacePlugin.displayName = 'AglynMarketplacePlugin';
|
|
141
|
+
export const schema = {
|
|
142
|
+
$id: ID,
|
|
143
|
+
pluginId: BUNDLE_ID,
|
|
144
|
+
displayName: 'Plugin',
|
|
145
|
+
description: 'Where an installed marketplace plugin renders, in its own sandbox.',
|
|
146
|
+
category: Aglyn.ComponentCategory.DATA_DISPLAY,
|
|
147
|
+
icon: {
|
|
148
|
+
path: mdiPuzzle.path,
|
|
149
|
+
sx: {
|
|
150
|
+
color: '#5e35b1'
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
flags: {
|
|
154
|
+
selfClosing: Aglyn.FEATURE_FLAG.ENABLED
|
|
155
|
+
},
|
|
156
|
+
attributes: [
|
|
157
|
+
{
|
|
158
|
+
name: 'listingId',
|
|
159
|
+
// A picker over what is actually installed (AGL-1030), not a typed
|
|
160
|
+
// document id: a mistyped character used to produce an element that
|
|
161
|
+
// looked broken with no clue why. The old helper text also pointed at
|
|
162
|
+
// "Manage → Plugins", a path AGL-1011 removed (AGL-1029).
|
|
163
|
+
description: 'Which installed plugin to place here. The sandboxed plugin renders ' + 'in an isolated iframe region on the published site.',
|
|
164
|
+
component: Aglyn.FieldComponentType.PLUGIN_SELECT,
|
|
165
|
+
label: 'Plugin'
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'pluginPropsJson',
|
|
169
|
+
description: 'Only what this plugin declares in its manifest is offered here, so ' + 'what you set is what it receives. "Edit as JSON" covers anything a ' + 'declaration does not.',
|
|
170
|
+
// Was a raw JSON textarea (AGL-1049): authors guessed key names, and a
|
|
171
|
+
// typo did not fail — `filterPluginProps` silently dropped it.
|
|
172
|
+
component: Aglyn.FieldComponentType.PLUGIN_SETTINGS,
|
|
173
|
+
label: 'Plugin settings'
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
};
|
|
177
|
+
export const presets = [
|
|
178
|
+
{
|
|
179
|
+
$id: generatePresetId(ID),
|
|
180
|
+
type: Aglyn.NodeType.PRESET,
|
|
181
|
+
displayName: 'Plugin',
|
|
182
|
+
pluginId: BUNDLE_ID,
|
|
183
|
+
description: 'Sandboxed marketplace plugin region',
|
|
184
|
+
category: Aglyn.ComponentCategory.DATA_DISPLAY,
|
|
185
|
+
icon: {
|
|
186
|
+
path: mdiPuzzle.path,
|
|
187
|
+
sx: {
|
|
188
|
+
color: '#5e35b1'
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
data: {
|
|
192
|
+
$id: null,
|
|
193
|
+
componentId: ID,
|
|
194
|
+
pluginId: BUNDLE_ID,
|
|
195
|
+
props: {}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
];
|
|
199
|
+
/** The besigner drawer category installed plugins register under (AGL-190). */ export const PLUGIN_DRAWER_CATEGORY = 'Marketplace';
|
|
200
|
+
/**
|
|
201
|
+
* Builds a besigner preset for an installed plugin (AGL-190): a named,
|
|
202
|
+
* draggable drawer entry that drops a `marketplacePlugin` node with the
|
|
203
|
+
* listing id pre-pinned, so editors never hand-type ids. Reuses the single
|
|
204
|
+
* `marketplacePlugin` renderer — no per-plugin component registration. The
|
|
205
|
+
* manifest's lineal rules ride on the node data for later enforcement.
|
|
206
|
+
* Returns null for an install without a resolvable listing id.
|
|
207
|
+
*/ export function muiPluginInstallToPreset(install) {
|
|
208
|
+
var _install_listingId;
|
|
209
|
+
var _install_manifest, _install_manifest1, _install_manifest2;
|
|
210
|
+
const listingId = (_install_listingId = install.listingId) != null ? _install_listingId : install.$id;
|
|
211
|
+
if (!listingId) return null;
|
|
212
|
+
const name = install.displayName || ((_install_manifest = install.manifest) == null ? void 0 : _install_manifest.name) || 'Marketplace plugin';
|
|
213
|
+
return {
|
|
214
|
+
$id: `plugin__${listingId}`,
|
|
215
|
+
type: Aglyn.NodeType.PRESET,
|
|
216
|
+
displayName: name,
|
|
217
|
+
pluginId: BUNDLE_ID,
|
|
218
|
+
description: 'Installed marketplace plugin',
|
|
219
|
+
category: PLUGIN_DRAWER_CATEGORY,
|
|
220
|
+
icon: {
|
|
221
|
+
path: mdiPuzzle.path,
|
|
222
|
+
sx: {
|
|
223
|
+
color: '#5e35b1'
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
data: _extends({
|
|
227
|
+
$id: null,
|
|
228
|
+
componentId: ID,
|
|
229
|
+
pluginId: BUNDLE_ID,
|
|
230
|
+
props: {
|
|
231
|
+
listingId
|
|
232
|
+
}
|
|
233
|
+
}, ((_install_manifest1 = install.manifest) == null ? void 0 : _install_manifest1.restrictParent) ? {
|
|
234
|
+
restrictParent: install.manifest.restrictParent
|
|
235
|
+
} : {}, ((_install_manifest2 = install.manifest) == null ? void 0 : _install_manifest2.restrictChildren) ? {
|
|
236
|
+
restrictChildren: install.manifest.restrictChildren
|
|
237
|
+
} : {})
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Every drawer preset an installed plugin contributes (AGL-1031).
|
|
242
|
+
*
|
|
243
|
+
* The generic Plugin element, plus one per element the PINNED version
|
|
244
|
+
* declares. Declared elements save as the same node with an `elementId`
|
|
245
|
+
* alongside the listing id — the issue's preferred answer, and the one that
|
|
246
|
+
* keeps a single compose path and a single sandbox. What changes is the
|
|
247
|
+
* palette entry and the label, not what executes.
|
|
248
|
+
*
|
|
249
|
+
* Resolved from the pin, so an element appears only where the plugin is
|
|
250
|
+
* installed and disappears with a revoked or downgraded version.
|
|
251
|
+
*/ export function muiPluginInstallToPresets(install) {
|
|
252
|
+
var _install_listingId;
|
|
253
|
+
var _install_manifest;
|
|
254
|
+
const generic = muiPluginInstallToPreset(install);
|
|
255
|
+
const presets = generic ? [
|
|
256
|
+
generic
|
|
257
|
+
] : [];
|
|
258
|
+
const listingId = (_install_listingId = install.listingId) != null ? _install_listingId : install.$id;
|
|
259
|
+
if (!listingId) return presets;
|
|
260
|
+
for (const element of Aglyn.resolvePluginElements({
|
|
261
|
+
listingId,
|
|
262
|
+
capabilities: (_install_manifest = install.manifest) == null ? void 0 : _install_manifest.capabilities,
|
|
263
|
+
manifest: install.manifest
|
|
264
|
+
})){
|
|
265
|
+
var _element_description, _ref;
|
|
266
|
+
var _install_manifest1, _install_manifest2;
|
|
267
|
+
// The declared icon is an mdi NAME; look it up in the set the host already
|
|
268
|
+
// ships. An unresolved name falls back to the puzzle mark rather than
|
|
269
|
+
// rendering nothing — the entry is still placeable, which matters more
|
|
270
|
+
// than the glyph.
|
|
271
|
+
const declared = element.icon ? MdiIcons.get(element.icon) : undefined;
|
|
272
|
+
presets.push({
|
|
273
|
+
$id: `plugin__${listingId}__${element.elementId}`,
|
|
274
|
+
type: Aglyn.NodeType.PRESET,
|
|
275
|
+
displayName: element.displayName,
|
|
276
|
+
pluginId: BUNDLE_ID,
|
|
277
|
+
description: (_element_description = element.description) != null ? _element_description : 'Installed marketplace plugin',
|
|
278
|
+
category: element.category,
|
|
279
|
+
icon: {
|
|
280
|
+
path: (_ref = declared == null ? void 0 : declared.path) != null ? _ref : mdiPuzzle.path,
|
|
281
|
+
sx: {
|
|
282
|
+
color: '#5e35b1'
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
data: _extends({
|
|
286
|
+
$id: null,
|
|
287
|
+
componentId: ID,
|
|
288
|
+
pluginId: BUNDLE_ID,
|
|
289
|
+
props: {
|
|
290
|
+
listingId,
|
|
291
|
+
elementId: element.elementId
|
|
292
|
+
}
|
|
293
|
+
}, ((_install_manifest1 = install.manifest) == null ? void 0 : _install_manifest1.restrictParent) ? {
|
|
294
|
+
restrictParent: install.manifest.restrictParent
|
|
295
|
+
} : {}, ((_install_manifest2 = install.manifest) == null ? void 0 : _install_manifest2.restrictChildren) ? {
|
|
296
|
+
restrictChildren: install.manifest.restrictChildren
|
|
297
|
+
} : {})
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return presets;
|
|
301
|
+
}
|
|
302
|
+
export default MarketplacePlugin;
|
|
303
|
+
|
|
304
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/plugin.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { MdiIcons } from '@aglyn/shared-data-mdi'\nimport { registerPluginInstallPresetMapper } from '@aglyn/aglyn'\nimport { mdiPuzzle } from '@aglyn/shared-data-mdi'\nimport Box from '@mui/material/Box'\nimport { forwardRef, useContext } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\nimport { PluginFrame } from './plugin-frame'\n\n// Component id is persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = Aglyn.PLUGIN_COMPONENT_ID\n\nexport interface MarketplacePluginProps {\n /** Installed listing id; the only prop authors set. */\n listingId?: string\n /**\n * Which DECLARED element of the plugin this node is (AGL-1031). Set by the\n * palette preset; absent for the generic Plugin element. Passed through to\n * the sandboxed frame as an ordinary prop, so one node type and one compose\n * path serve both.\n */\n elementId?: string\n /** Injected at compose by `attachPluginInstalls` (AGL-45). */\n version?: string\n sha256?: string\n capabilities?: Aglyn.PluginCapabilities\n revoked?: boolean\n /** Extra props forwarded to the plugin (filtered to its allowlist). */\n pluginProps?: Record<string, unknown>\n /**\n * Per-placement plugin settings as JSON (AGL-192), authored in the\n * element panel. Parsed into `pluginProps` and filtered to the\n * manifest's declared props before it reaches the plugin.\n */\n pluginPropsJson?: string\n}\n\n/** Parses the authored settings JSON; invalid input yields no props. */\nexport function parsePluginPropsJson(\n raw: string | undefined,\n): Record<string, unknown> | undefined {\n if (!raw || !raw.trim()) return undefined\n try {\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * What the inert placeholder says, and — more to the point — what it refuses\n * to say (AGL-1029).\n *\n * It used to read \"not installed on this site\" for EVERY plugin with a listing\n * id, because it inferred installation from `version`/`sha256`: fields the\n * tenant compose pass injects and the canvas never has. Plugins do not execute\n * in the editor by design, so their absence there means nothing about the pin,\n * and the first thing an author saw after installing was a message telling them\n * the install had failed.\n *\n * The claim is only made when it has been earned. The editor publishes the\n * installs it reads (`setKnownPluginInstalls`); a listing id missing from that\n * list really is not installed. Where nothing published a list — a preview —\n * the placeholder states what is true of the placement instead and asserts\n * nothing about installation. A published page draws no placeholder at all.\n */\nexport function placeholderText(listingId: string | undefined): string {\n if (!listingId) return 'Plugin — pick an installed plugin'\n const known = Aglyn.getKnownPluginInstall(listingId)\n if (known) {\n const name = known.displayName || 'Plugin'\n return known.scope === 'org'\n ? `${name} — installed org-wide; renders on the published site`\n : `${name} — renders on the published site`\n }\n return Aglyn.hasKnownPluginInstalls()\n ? 'Plugin — not installed on this site'\n : 'Plugin — renders on the published site'\n}\n\n/**\n * Marketplace plugin element (AGL-45): a placement for an installed executable\n * plugin. The saved node carries only `listingId`; the tenant compose pass\n * (`attachPluginInstalls`) injects the pinned version/sha256/capabilities +\n * kill-switch state, and this renders them through the sandboxed\n * `PluginFrame`. Without a resolved install (the editor canvas, or an\n * uninstalled listing) it shows an inert placeholder — plugins never\n * execute in the editor. The plugin origin is a NEXT_PUBLIC env so it's\n * available in the browser bundle; unset = plugins disabled (placeholder).\n */\nconst MarketplacePlugin = forwardRef<HTMLElement, MarketplacePluginProps>(\n (props, ref) => {\n const {\n listingId,\n elementId,\n version,\n sha256,\n capabilities,\n revoked,\n pluginProps,\n pluginPropsJson,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n // Merge authored settings (AGL-192) under any compose-injected props;\n // PluginFrame filters the result to the manifest allowlist.\n const resolvedPluginProps = {\n ...(pluginProps ?? {}),\n ...(parsePluginPropsJson(pluginPropsJson) ?? {}),\n }\n const pluginOrigin =\n typeof process !== 'undefined'\n ? // Dot notation (AGL-2037) — a client component; the bracket\n // form reads undefined in the browser.\n process.env.NEXT_PUBLIC_PLUGIN_ORIGIN\n : undefined\n // Host id for host-mediated fetch (AGL-191); empty in the editor.\n const { hostId } = Aglyn.useSite()\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n\n // No resolved install (editor canvas / uninstalled): inert placeholder,\n // which speaks to the author, so a published page renders the bare\n // element instead.\n if (!listingId || !version || !sha256) {\n if (!suppressNavigation) return <Box ref={ref as any} {...rest} />\n return (\n <Box\n ref={ref as any}\n {...rest}\n sx={[\n {\n minHeight: 120,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontSize: 13,\n fontFamily: 'system-ui, sans-serif',\n p: 2,\n textAlign: 'center',\n },\n ...nodeSx,\n ]}\n >\n {placeholderText(listingId)}\n </Box>\n )\n }\n\n return (\n <PluginFrame\n ref={ref as any}\n pluginOrigin={pluginOrigin}\n hostId={hostId}\n listingId={listingId}\n elementId={elementId}\n version={version}\n sha256={sha256}\n capabilities={capabilities}\n pluginProps={resolvedPluginProps}\n revoked={revoked}\n />\n )\n },\n)\nMarketplacePlugin.displayName = 'AglynMarketplacePlugin'\n\nexport const schema: Aglyn.ComponentSchema<MarketplacePluginProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Plugin',\n description:\n 'Where an installed marketplace plugin renders, in its own sandbox.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPuzzle.path, sx: { color: '#5e35b1' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'listingId',\n // A picker over what is actually installed (AGL-1030), not a typed\n // document id: a mistyped character used to produce an element that\n // looked broken with no clue why. The old helper text also pointed at\n // \"Manage → Plugins\", a path AGL-1011 removed (AGL-1029).\n description:\n 'Which installed plugin to place here. The sandboxed plugin renders ' +\n 'in an isolated iframe region on the published site.',\n component: Aglyn.FieldComponentType.PLUGIN_SELECT,\n label: 'Plugin',\n },\n {\n name: 'pluginPropsJson',\n description:\n 'Only what this plugin declares in its manifest is offered here, so ' +\n 'what you set is what it receives. \"Edit as JSON\" covers anything a ' +\n 'declaration does not.',\n // Was a raw JSON textarea (AGL-1049): authors guessed key names, and a\n // typo did not fail — `filterPluginProps` silently dropped it.\n component: Aglyn.FieldComponentType.PLUGIN_SETTINGS,\n label: 'Plugin settings',\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Plugin',\n pluginId: BUNDLE_ID,\n description: 'Sandboxed marketplace plugin region',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPuzzle.path, sx: { color: '#5e35b1' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n]\n\n/** The besigner drawer category installed plugins register under (AGL-190). */\nexport const PLUGIN_DRAWER_CATEGORY = 'Marketplace'\n\nexport interface PluginInstallLike {\n listingId?: string\n $id?: string\n displayName?: string\n pluginId?: string\n manifest?: {\n name?: string\n restrictParent?: string[]\n restrictChildren?: string[]\n }\n}\n\n/**\n * Builds a besigner preset for an installed plugin (AGL-190): a named,\n * draggable drawer entry that drops a `marketplacePlugin` node with the\n * listing id pre-pinned, so editors never hand-type ids. Reuses the single\n * `marketplacePlugin` renderer — no per-plugin component registration. The\n * manifest's lineal rules ride on the node data for later enforcement.\n * Returns null for an install without a resolvable listing id.\n */\nexport function muiPluginInstallToPreset(\n install: PluginInstallLike,\n): Aglyn.PresetSchema | null {\n const listingId = install.listingId ?? install.$id\n if (!listingId) return null\n const name =\n install.displayName || install.manifest?.name || 'Marketplace plugin'\n return {\n $id: `plugin__${listingId}`,\n type: Aglyn.NodeType.PRESET,\n displayName: name,\n pluginId: BUNDLE_ID,\n description: 'Installed marketplace plugin',\n category: PLUGIN_DRAWER_CATEGORY,\n icon: { path: mdiPuzzle.path, sx: { color: '#5e35b1' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { listingId },\n ...(install.manifest?.restrictParent\n ? { restrictParent: install.manifest.restrictParent }\n : {}),\n ...(install.manifest?.restrictChildren\n ? { restrictChildren: install.manifest.restrictChildren }\n : {}),\n } as any,\n }\n}\n\n/**\n * Every drawer preset an installed plugin contributes (AGL-1031).\n *\n * The generic Plugin element, plus one per element the PINNED version\n * declares. Declared elements save as the same node with an `elementId`\n * alongside the listing id — the issue's preferred answer, and the one that\n * keeps a single compose path and a single sandbox. What changes is the\n * palette entry and the label, not what executes.\n *\n * Resolved from the pin, so an element appears only where the plugin is\n * installed and disappears with a revoked or downgraded version.\n */\nexport function muiPluginInstallToPresets(\n install: PluginInstallLike,\n): Aglyn.PresetSchema[] {\n const generic = muiPluginInstallToPreset(install)\n const presets = generic ? [generic] : []\n const listingId = install.listingId ?? install.$id\n if (!listingId) return presets\n\n for (const element of Aglyn.resolvePluginElements({\n listingId,\n capabilities: (install as any).manifest?.capabilities,\n manifest: (install as any).manifest,\n })) {\n // The declared icon is an mdi NAME; look it up in the set the host already\n // ships. An unresolved name falls back to the puzzle mark rather than\n // rendering nothing — the entry is still placeable, which matters more\n // than the glyph.\n const declared = element.icon ? MdiIcons.get(element.icon as never) : undefined\n presets.push({\n $id: `plugin__${listingId}__${element.elementId}`,\n type: Aglyn.NodeType.PRESET,\n displayName: element.displayName,\n pluginId: BUNDLE_ID,\n description: element.description ?? 'Installed marketplace plugin',\n category: element.category,\n icon: {\n path: (declared as { path?: string } | undefined)?.path ?? mdiPuzzle.path,\n sx: { color: '#5e35b1' },\n },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { listingId, elementId: element.elementId },\n ...(install.manifest?.restrictParent\n ? { restrictParent: install.manifest.restrictParent }\n : {}),\n ...(install.manifest?.restrictChildren\n ? { restrictChildren: install.manifest.restrictChildren }\n : {}),\n } as any,\n })\n }\n return presets\n}\n\nexport default MarketplacePlugin\n"],"names":["Aglyn","MdiIcons","mdiPuzzle","Box","forwardRef","useContext","BUNDLE_ID","generatePresetId","PluginFrame","ID","PLUGIN_COMPONENT_ID","parsePluginPropsJson","raw","trim","undefined","parsed","JSON","parse","Array","isArray","placeholderText","listingId","known","getKnownPluginInstall","name","displayName","scope","hasKnownPluginInstalls","MarketplacePlugin","props","ref","elementId","version","sha256","capabilities","revoked","pluginProps","pluginPropsJson","rest","nodeSx","resolvedPluginProps","pluginOrigin","process","env","NEXT_PUBLIC_PLUGIN_ORIGIN","hostId","useSite","suppressNavigation","ScreenLinkContext","sx","minHeight","display","alignItems","justifyContent","border","borderColor","borderRadius","color","fontSize","fontFamily","p","textAlign","schema","$id","pluginId","description","category","ComponentCategory","DATA_DISPLAY","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","component","FieldComponentType","PLUGIN_SELECT","label","PLUGIN_SETTINGS","presets","type","NodeType","PRESET","data","componentId","PLUGIN_DRAWER_CATEGORY","muiPluginInstallToPreset","install","manifest","restrictParent","restrictChildren","muiPluginInstallToPresets","generic","element","resolvePluginElements","declared","get","push"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import * as Aglyn from '@aglyn/aglyn';
|
|
18
|
+
export declare const ID: Aglyn.ComponentId;
|
|
19
|
+
export interface ProductProps {
|
|
20
|
+
/** Id of the product doc under hosts/{hostId}/products. */
|
|
21
|
+
productId?: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
/** Display price; the charge always uses the product doc's price. */
|
|
24
|
+
priceUsd?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
imageUrl?: string;
|
|
27
|
+
buyLabel?: string;
|
|
28
|
+
/** Shows a coupon-code input above the buy button (AGL-96). */
|
|
29
|
+
showCoupon?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Commerce Starter product block (AGL-90): displays from its own props,
|
|
33
|
+
* but Buy posts {hostId, productId} to the tenant's /api/commerce/checkout,
|
|
34
|
+
* which prices from the server-side product doc — canvas props can't forge
|
|
35
|
+
* a charge. Inert without SiteContext (besigner canvas/preview).
|
|
36
|
+
*/
|
|
37
|
+
declare const Product: import("react").ForwardRefExoticComponent<ProductProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
38
|
+
export declare const schema: Aglyn.ComponentSchema<ProductProps>;
|
|
39
|
+
export declare const presets: Aglyn.PresetSchema[];
|
|
40
|
+
export default Product;
|