@sanity/block-insert-picker
1.0.121.0.13
dist/index.js.map~
dist/index.js.mapModified+1−1
Index: package/dist/index.js.map
===================================================================
--- package/dist/index.js.map
+++ package/dist/index.js.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"index.js","names":["useMemo<PickerItemsContext | null>(() => {\n if (memberSchemaTypes) {\n return {\n memberTypes: memberSchemaTypes.blockObjects,\n schemaType: memberSchemaTypes.portableText,\n }\n }\n const arrayType = arrayTypeName ? schema.get(arrayTypeName) : undefined\n if (!arrayType || arrayType.jsonType !== 'array') return null\n return {\n memberTypes: arrayType.of.filter(\n (member): member is ObjectSchemaType =>\n member.jsonType === 'object' && !isTextBlockMember(member),\n ),\n schemaType: arrayType,\n }\n }, [arrayTypeName, memberSchemaTypes, schema])","{\n memberTypes: memberSchemaTypes.blockObjects,\n schemaType: memberSchemaTypes.portableText,\n }","blockObjects","portableText","(member): member is ObjectSchemaType =>\n member.jsonType === 'object' && !isTextBlockMember(member)","_c","_temp","() => {\n const key = pendingKeyRef.current\n if (!key) return\n const item = memberItems.find((m) => m.member.key === key)\n if (item) {\n onPathOpen(item.member.item.path)\n onSetPathCollapsed(item.member.item.path, false)\n pendingKeyRef.current = null\n }\n }","[memberItems, onPathOpen, onSetPathCollapsed]","(blockKey: string) => {\n pendingKeyRef.current = blockKey\n }","useCallback((blockKey: string) => {\n pendingKeyRef.current = blockKey\n }, [])","_c","unhandled","{keys, label}: {keys: string[]; label: string}","<Hotkeys fontSize={0} keys={keys} />","<Text muted size={0}>\n {label}\n </Text>","<Flex align=\"center\" gap={2}>\n <Hotkeys fontSize={0} keys={keys} />\n <Text muted size={0}>\n {label}\n </Text>\n </Flex>","_c","{\n arrayTypeName,\n onInsert,\n openOnInsert = true,\n rules,\n}: MarkdownInputRulesProps","openOnInsert = true","itemsContext?.memberTypes ?? []","memberTypes","rules.flatMap((rule) => {\n const member =\n memberTypes.find((candidate) => candidate.name === rule.blockType) ??\n memberTypes.find((candidate) => typeNameChain(candidate).includes(rule.blockType))\n if (!member) return []\n return [rule.blockType === member.name ? rule : {...rule, blockType: member.name}]\n })","(rule) => {\n const member =\n memberTypes.find((candidate) => candidate.name === rule.blockType) ??\n memberTypes.find((candidate) => typeNameChain(candidate).includes(rule.blockType))\n if (!member) return []\n return [rule.blockType === member.name ? rule : {...rule, blockType: member.name}]\n }","candidate","useMemo(() => {\n const memberTypes = itemsContext?.memberTypes ?? []\n return rules.flatMap((rule) => {\n const member =\n memberTypes.find((candidate) => candidate.name === rule.blockType) ??\n memberTypes.find((candidate) => typeNameChain(candidate).includes(rule.blockType))\n if (!member) return []\n return [rule.blockType === member.name ? rule : {...rule, blockType: member.name}]\n })\n }, [itemsContext, rules])","() => {}","() => {\n onInsertedRef.current = (block) => {\n if (openOnInsert) openBlockOnInsert(block._key)\n onInsert?.({\n blockKey: block._key,\n blockType: block._type,\n via: 'inputRule',\n })\n }\n }","() => {\n const inputRules = createMarkdownInputRules({\n // resolvedRules are already narrowed to insertable members.\n allowedBlockTypes: new Set(resolvedRules.map((rule) => rule.blockType)),\n // The generator is read per insert so keys stay unique across a session.\n keyGenerator: () => editor.getSnapshot().context.keyGenerator(),\n onInserted: (block) => onInsertedRef.current(block),\n rules: resolvedRules,\n })\n if (inputRules.length === 0) return undefined\n return editor.registerBehavior({\n behavior: defineInputRuleBehavior({rules: inputRules}),\n })\n }","[editor, resolvedRules]","(rule) => rule.blockType","block","rule","_c","_Fragment","\"NODE_ENV\""],"sources":["../src/deriveItems.ts","../src/filterItems.ts","../src/groupItems.ts","../src/insertBehavior.ts","../src/insertPickerItem.ts","../src/labels.ts","../src/memberSchemaTypes.ts","../src/openBlockOnInsert.ts","../src/pickerBehavior.ts","../src/pickerReducer.ts","../src/inputRules.ts","../src/presets.ts","../src/resolveItemPresentation.ts","../src/blockInsertPicker.tsx","../src/markdownInputRules.tsx","../src/plugin.tsx"],"sourcesContent":["import type {PickerItem, PickerItemMetadata, PickerItemsContext} from './types'\n\n/**\n * The member's own name plus every name up its compiled `type` chain, most\n * specific first: `{type: 'image', name: 'photo'}` yields `['photo',\n * 'image', ...]`. Metadata and presets match against this chain so curation\n * can target either the member name or the underlying type name.\n */\nexport function typeNameChain(memberType: {\n name: string\n type?: {name: string; type?: unknown} | undefined\n}): string[] {\n const names: string[] = []\n let current: {name: string; type?: unknown} | undefined = memberType\n while (current && typeof current.name === 'string') {\n names.push(current.name)\n current =\n current.type && typeof current.type === 'object' && 'name' in current.type\n ? // oxlint-disable-next-line no-unsafe-type-assertion\n (current.type as {name: string; type?: unknown})\n : undefined\n }\n return names\n}\n\n/**\n * Derives picker items from a portable-text array's insertable member types\n * (see PickerItemsContext), so the picker automatically covers every object\n * block the schema allows. The optional metadata list curates triggers,\n * keywords, grouping, badges, visibility, and descriptions per member type;\n * its array order is the sort rank (see PickerItemMetadata). Titles and\n * icons intentionally stay unresolved here: BlockInsertPicker resolves\n * presentation from the member schema type, which carries member-level\n * config (a member-specific icon, a per-member title override).\n */\nexport function derivePickerItems(\n context: PickerItemsContext,\n metadata: readonly PickerItemMetadata[] = [],\n): PickerItem[] {\n return context.memberTypes\n .map((memberType, schemaIndex) => {\n const chain = typeNameChain(memberType)\n // The member's own name wins over a resolved-chain match, so an entry\n // for `photo` beats an entry for `image` on `{type: 'image', name:\n // 'photo'}` — but whichever matches, its array position is the rank.\n const nameRank = metadata.findIndex((entry) => entry.type === memberType.name)\n const rank =\n nameRank === -1 ? metadata.findIndex((entry) => chain.includes(entry.type)) : nameRank\n const entry = rank === -1 ? undefined : metadata[rank]\n return {\n entry,\n name: memberType.name,\n rank: rank === -1 ? Number.MAX_SAFE_INTEGER : rank,\n schemaIndex,\n }\n })\n .filter(({entry}) => !entry?.hidden)\n .sort((a, b) => a.rank - b.rank || a.schemaIndex - b.schemaIndex)\n .map(({entry, name}) => ({\n action: {blockType: name, type: 'insertBlock' as const},\n badge: entry?.badge,\n description: entry?.description,\n group: entry?.group,\n id: name,\n keywords: entry?.keywords,\n openOnInsert: entry?.openOnInsert,\n title: '',\n trigger: entry?.trigger,\n }))\n}\n\n/**\n * Metadata entries whose `type` matches neither a member name nor any name\n * in a member's resolved type chain — almost always a typo. Surfaced as a\n * dev-mode warning by BlockInsertPicker.\n */\nexport function unknownMetadataTypes(\n context: PickerItemsContext,\n metadata: readonly PickerItemMetadata[],\n): string[] {\n const known = new Set(context.memberTypes.flatMap((memberType) => typeNameChain(memberType)))\n return metadata.map((entry) => entry.type).filter((type) => !known.has(type))\n}\n","import type {PickerItem} from './types'\n\nexport function filterPickerItems(\n items: readonly PickerItem[],\n query: string,\n): readonly PickerItem[] {\n if (query === '' || query === '/') return items\n\n const queryLower = query.toLowerCase()\n // Slash-mode queries always carry the leading \"/\", which titles and\n // keywords never contain — strip it before substring matching.\n const bareQuery = queryLower.startsWith('/') ? queryLower.slice(1) : queryLower\n\n return items.filter((item) => {\n // Triggers match with the slash stripped from both sides so that bare\n // shortcut-mode queries (\"cta\") reach them, not just slash-mode ones.\n const bareTrigger = item.trigger?.toLowerCase().replace(/^\\//, '')\n if (bareTrigger && bareQuery !== '' && bareTrigger.startsWith(bareQuery)) return true\n if (bareQuery === '') return false\n if (item.title.toLowerCase().includes(bareQuery)) return true\n if (item.description?.toLowerCase().includes(bareQuery)) return true\n if (item.keywords?.some((kw) => kw.toLowerCase().includes(bareQuery))) return true\n return false\n })\n}\n","import type {PickerItem} from './types'\n\ntype PickerSection = {\n /** Category label, or null for items that carry no group. */\n group: null | string\n items: readonly PickerItem[]\n}\n\n/**\n * Flattens sections back into the single index space that keyboard navigation,\n * selection, and scroll-into-view all share. Rendering the sections and\n * indexing this array must stay derived from the same `groupPickerItems` call\n * so Enter inserts exactly the highlighted row.\n */\nexport function flattenSections(sections: readonly PickerSection[]): readonly PickerItem[] {\n return sections.flatMap((section) => section.items)\n}\n\n/**\n * Partitions items into sections by their `group`, preserving the incoming\n * order within each section and ordering the sections by first appearance.\n *\n * Because the caller feeds rank-ordered items (see derivePickerItems), the\n * first-appearance rule places the section holding the most-used block first,\n * without a separate ordering constant to drift out of sync. Items without a\n * group collapse into a single `group: null` section (e.g. a curated `items`\n * prop), so the flat/ungrouped case stays a no-op reordering.\n */\nexport function groupPickerItems(items: readonly PickerItem[]): PickerSection[] {\n const order: (null | string)[] = []\n const byGroup = new Map<null | string, PickerItem[]>()\n for (const item of items) {\n const key = item.group ?? null\n let bucket = byGroup.get(key)\n if (!bucket) {\n bucket = []\n byGroup.set(key, bucket)\n order.push(key)\n }\n bucket.push(item)\n }\n return order.map((key) => ({group: key, items: byGroup.get(key)!}))\n}\n","import type {Editor, EditorSelection, EditorSnapshot} from '@portabletext/editor'\nimport {defineBehavior, raise} from '@portabletext/editor/behaviors'\nimport {blockOffsetsToSelection, getTextBlockText, isTextBlock} from '@portabletext/editor/utils'\n\nimport type {PickerMode} from './types'\n\ntype InsertGuardResult = {\n anchor: EditorSelection\n block: InsertPickerItemPayload['block']\n cleanup: EditorSelection\n placement: 'after' | 'auto'\n}\n\ntype InsertPickerItemPayload = {\n anchorBlockKey: string\n block: {_key: string; _type: string} & Record<string, unknown>\n mode: PickerMode\n query: string\n}\n\n/**\n * Performs the picker insertion as a single behavior action set so the whole\n * operation is one undo step, and cleans up the typed \"/query\" text with\n * delete.text over exactly that range (never delete.block) so text that\n * already existed in the anchor block can't be destroyed: the caret can sit\n * at offset 0 of a populated block and still open the picker.\n */\nexport function createInsertBehavior() {\n return defineBehavior<\n InsertPickerItemPayload,\n 'custom.blockInsertPicker.insert',\n InsertGuardResult\n >({\n actions: [\n (_, {anchor, block, cleanup, placement}) => [\n ...(cleanup ? [raise({at: cleanup, type: 'delete.text'})] : []),\n raise({\n block,\n placement,\n select: 'start',\n type: 'insert.block',\n // Pin the insert to the anchor block: the live selection may have\n // moved (e.g. input arriving during the select-time initial-value\n // await) and placement resolves relative to `at` when provided.\n ...(anchor ? {at: anchor} : {}),\n }),\n ],\n ],\n guard: ({event, snapshot}) => {\n const {anchorBlockKey, block, mode} = event\n const anchorBlockExists = snapshot.context.value.some(\n (candidate) => candidate._key === anchorBlockKey,\n )\n const anchor = anchorBlockExists\n ? blockOffsetsToSelection({\n offsets: {\n anchor: {offset: 0, path: [{_key: anchorBlockKey}]},\n focus: {offset: 0, path: [{_key: anchorBlockKey}]},\n },\n snapshot,\n })\n : null\n return {\n anchor,\n block,\n cleanup: resolveQueryCleanup(event, snapshot),\n placement: mode === 'slash' ? ('auto' as const) : ('after' as const),\n }\n },\n on: 'custom.blockInsertPicker.insert',\n })\n}\n\nexport function sendInsertPickerItem(editor: Editor, payload: InsertPickerItemPayload): void {\n editor.send({type: 'custom.blockInsertPicker.insert', ...payload})\n}\n\ntype CleanupQueryPayload = Omit<InsertPickerItemPayload, 'block'>\n\n/**\n * The query-cleanup half of the insert behavior on its own, for `custom`\n * picker actions that don't insert a block: the typed \"/query\" text still\n * leaves the document before the host's `onSelect` runs, under the same\n * only-if-unchanged guard as a real insert.\n */\nexport function createCleanupQueryBehavior() {\n return defineBehavior<\n CleanupQueryPayload,\n 'custom.blockInsertPicker.cleanupQuery',\n NonNullable<EditorSelection>\n >({\n actions: [(_, cleanup) => [raise({at: cleanup, type: 'delete.text'})]],\n guard: ({event, snapshot}) => resolveQueryCleanup(event, snapshot) ?? false,\n on: 'custom.blockInsertPicker.cleanupQuery',\n })\n}\n\nexport function sendCleanupQuery(editor: Editor, payload: CleanupQueryPayload): void {\n editor.send({type: 'custom.blockInsertPicker.cleanupQuery', ...payload})\n}\n\n/**\n * The selection spanning the typed \"/query\" text in the anchor block, when it\n * is safe to delete. Only slash mode leaves query text in the document, and\n * only when the anchor block still starts with the typed query; if it changed\n * underneath us (collaborator edit, uncaptured caret movement) the action is\n * still the user's intent, but nothing may be deleted.\n */\nfunction resolveQueryCleanup(\n event: {anchorBlockKey: string; mode: PickerMode; query: string},\n snapshot: EditorSnapshot,\n): EditorSelection {\n const {anchorBlockKey, mode, query} = event\n if (mode !== 'slash' || query.length === 0) return null\n const anchorBlock = snapshot.context.value.find((candidate) => candidate._key === anchorBlockKey)\n if (\n !anchorBlock ||\n !isTextBlock({schema: snapshot.context.schema}, anchorBlock) ||\n !getTextBlockText(anchorBlock).startsWith(query)\n ) {\n return null\n }\n return blockOffsetsToSelection({\n offsets: {\n anchor: {offset: 0, path: [{_key: anchorBlockKey}]},\n focus: {offset: query.length, path: [{_key: anchorBlockKey}]},\n },\n snapshot,\n })\n}\n","import type {Editor} from '@portabletext/editor'\n\nimport {sendInsertPickerItem} from './insertBehavior'\nimport type {PickerMode} from './types'\n\ntype InsertContext = {\n /** The `_type` of the block to insert — an `insertBlock` action's target. */\n blockType: string\n mode: PickerMode\n editor: Editor\n anchorBlockKey: string\n query: string\n keyGenerator: () => string\n initialValue?: Record<string, unknown>\n onInsertedKey?: (key: string) => void\n}\n\nexport function insertPickerItem(ctx: InsertContext): void {\n const blockKey = ctx.keyGenerator()\n // One custom event; the registered insert behavior performs the query\n // cleanup and the insertion in a single action set (single undo step).\n sendInsertPickerItem(ctx.editor, {\n anchorBlockKey: ctx.anchorBlockKey,\n block: {\n // Spread the resolved initial value first so the generated `_key` and\n // the target `_type` always win: Sanity's initial-value resolver sets\n // `_type` on object values, and a schema's initialValue could carry\n // stray meta keys. In this flow the resolved `_type` matches\n // `ctx.blockType` by construction, but ordering it last keeps the\n // insert correct even when that assumption doesn't hold.\n ...ctx.initialValue,\n _key: blockKey,\n _type: ctx.blockType,\n },\n mode: ctx.mode,\n query: ctx.query,\n })\n ctx.onInsertedKey?.(blockKey)\n}\n","/**\n * Every user-facing string in the picker chrome, centralized so hosts can\n * translate or reword them via the `labels` option (the same pattern\n * `@sanity/insert-menu` uses for its label overrides). Item titles and\n * descriptions are schema/metadata content, not chrome — localize those\n * through `items` or `resolveItems` instead.\n */\nexport type BlockInsertPickerLabels = {\n /** Popover header; also the listbox aria-label. */\n title: string\n /** Empty state; `{query}` is replaced with the typed filter text. */\n noMatches: string\n /**\n * Screen-reader position announcement for the highlighted row; `{index}`\n * and `{count}` are replaced with the 1-based position and list length.\n */\n positionAnnouncement: string\n /**\n * Section header for items without a `group`, shown only when grouped\n * sections are also present.\n */\n ungroupedSection: string\n /** Footer legend verbs. */\n footerNavigate: string\n footerInsert: string\n footerDismiss: string\n footerAnywhere: string\n /** Toast title when resolving a block's initial value fails. */\n insertError: string\n /**\n * Toast description when the block the picker was anchored to is deleted\n * before the insert completes.\n */\n insertAnchorRemoved: string\n}\n\nconst DEFAULT_LABELS: BlockInsertPickerLabels = {\n footerAnywhere: 'Anywhere',\n footerDismiss: 'Dismiss',\n footerInsert: 'Insert',\n footerNavigate: 'Navigate',\n insertAnchorRemoved: 'The block you were inserting into was removed',\n insertError: 'Could not insert block',\n noMatches: 'No matches for \"{query}\"',\n positionAnnouncement: '{index} of {count}',\n title: 'Insert block',\n ungroupedSection: 'Other blocks',\n}\n\nexport function resolveLabels(\n overrides: Partial<BlockInsertPickerLabels> | undefined,\n): BlockInsertPickerLabels {\n return overrides ? {...DEFAULT_LABELS, ...overrides} : DEFAULT_LABELS\n}\n\n/** Fills the `{query}` placeholder in the no-matches label. */\nexport function formatNoMatches(labels: BlockInsertPickerLabels, query: string): string {\n // Function replacement: a query containing replacement patterns (\"$&\")\n // must land verbatim, not be interpreted by String.replace.\n return labels.noMatches.replace('{query}', () => query)\n}\n\n/** Fills `{index}`/`{count}` in the screen-reader position announcement. */\nexport function formatPosition(\n labels: BlockInsertPickerLabels,\n index: number,\n count: number,\n): string {\n return labels.positionAnnouncement\n .replace('{index}', () => String(index))\n .replace('{count}', () => String(count))\n}\n","import {type ContextType, useContext, useMemo} from 'react'\nimport {type ObjectSchemaType, useSchema} from 'sanity'\nimport {PortableTextMemberSchemaTypesContext} from 'sanity/_singletons'\n\nimport type {PickerItemsContext} from './types'\n\n/**\n * Resolves the Portable Text array type and its insertable member types from\n * where this plugin renders — inside the PT input's `EditorProvider`.\n *\n * Primary source: Studio's own member-schema context, which the PT input\n * provides above the plugins slot. It carries the containing array type and\n * its non-text-block members as full Studio schema types, so aliased members\n * (`{type: 'image', name: 'photo'}`), inline-declared members, and arrays\n * defined inline on a field all resolve with member-level fidelity and no\n * configuration. Fallback: an explicit `arrayTypeName` looked up in the\n * workspace schema — the escape hatch for environments where the context is\n * unavailable. With neither, callers get `null` and must render nothing\n * (never an empty picker).\n *\n * Together with openBlockOnInsert.ts, this module is one of exactly two\n * importers of `sanity/_singletons` — an internal subpath with no stability\n * guarantee (the context exists since sanity 5.6.0, which the peer range\n * pins). The dependency stays quarantined here so it can be swapped for a\n * public API (an exported `usePortableTextMemberSchemaTypes`, or a\n * `schemaType` on `PortableTextPluginsProps`) in one place.\n */\n\n// The context's value type lives in @portabletext/sanity-bridge, which\n// consumer studios cannot resolve directly (it is sanity's own dependency) —\n// extract it from the context instead of importing the bridge.\ntype MemberSchemaTypes = NonNullable<ContextType<typeof PortableTextMemberSchemaTypesContext>>\n\nexport function usePickerItemsContext(arrayTypeName?: string): PickerItemsContext | null {\n const memberSchemaTypes: MemberSchemaTypes | null = useContext(\n PortableTextMemberSchemaTypesContext,\n )\n const schema = useSchema()\n\n return useMemo<PickerItemsContext | null>(() => {\n if (memberSchemaTypes) {\n return {\n memberTypes: memberSchemaTypes.blockObjects,\n schemaType: memberSchemaTypes.portableText,\n }\n }\n const arrayType = arrayTypeName ? schema.get(arrayTypeName) : undefined\n if (!arrayType || arrayType.jsonType !== 'array') return null\n return {\n memberTypes: arrayType.of.filter(\n (member): member is ObjectSchemaType =>\n member.jsonType === 'object' && !isTextBlockMember(member),\n ),\n schemaType: arrayType,\n }\n }, [arrayTypeName, memberSchemaTypes, schema])\n}\n\n/**\n * Whether a member resolves to the built-in text block: walk the compiled\n * `type` chain to its root and compare the root's name — the same algorithm\n * Studio's schema bridge uses to bucketize `of`. A literal `name === 'block'`\n * check would miss aliased text blocks (`{name: 'myBlock', type: 'block'}`)\n * and wrongly offer them as object inserts.\n */\nfunction isTextBlockMember(memberType: {\n name: string\n type?: {name: string; type?: unknown} | undefined\n}): boolean {\n let current: {name: string; type?: unknown} = memberType\n while (current.type && typeof current.type === 'object' && 'name' in current.type) {\n // oxlint-disable-next-line no-unsafe-type-assertion\n current = current.type as {name: string; type?: unknown}\n }\n return current.name === 'block'\n}\n","import {useCallback, useContext, useEffect, useRef} from 'react'\nimport {useFormCallbacks} from 'sanity'\nimport {PortableTextMemberItemsContext} from 'sanity/_singletons'\n\n/**\n * Opens a just-inserted block's editing UI (dialog/popover per the member's\n * config) once its member item mounts. An insert lands synchronously, but the\n * member item only exists on a later render — so callers schedule the key and\n * the effect opens it when it appears.\n *\n * Together with memberSchemaTypes.ts, this module is one of exactly two\n * importers of `sanity/_singletons` in this plugin: the subpath is internal\n * with no stability guarantee, so each dependency stays quarantined where it\n * can be swapped for a public API in one place. Consumers that need to react\n * to inserts without this internal should use the `onInsert` seam on the\n * plugin components instead.\n */\nexport function useOpenBlockOnInsert(): (blockKey: string) => void {\n const memberItems = useContext(PortableTextMemberItemsContext)\n const {onPathOpen, onSetPathCollapsed} = useFormCallbacks()\n const pendingKeyRef = useRef<null | string>(null)\n\n useEffect(() => {\n const key = pendingKeyRef.current\n if (!key) return\n const item = memberItems.find((m) => m.member.key === key)\n if (item) {\n onPathOpen(item.member.item.path)\n onSetPathCollapsed(item.member.item.path, false)\n pendingKeyRef.current = null\n }\n }, [memberItems, onPathOpen, onSetPathCollapsed])\n\n // Stable identity: callers hold this in effect dependency arrays, so it must\n // keep a constant reference — an unstable return would re-run (and re-register\n // behaviors in) every effect that lists it, on every render. The empty-dep\n // `useCallback` states that contract explicitly rather than relying on the\n // React Compiler to infer it.\n return useCallback((blockKey: string) => {\n pendingKeyRef.current = blockKey\n }, [])\n}\n","import {defineBehavior, effect, forward} from '@portabletext/editor/behaviors'\nimport type {BehaviorEvent, BehaviorGuard} from '@portabletext/editor/behaviors'\nimport {\n getBlockTextBefore,\n getFirstBlock,\n getFocusBlock,\n getFocusTextBlock,\n isSelectionCollapsed,\n} from '@portabletext/editor/selectors'\n\nimport type {PickerIntent, PickerState} from './types'\n\ntype GuardResult = {\n // true = block the native/default editor handling; false = allow it to proceed\n blockEvent: boolean\n intent: PickerIntent\n}\n\ntype PickerBehaviorRefs = {\n onIntent: (intent: PickerIntent) => void\n getState: () => PickerState\n /**\n * Whether Cmd/Ctrl+/ may open the picker. Read per keystroke (not captured\n * at registration) so the host toggling `shortcut` doesn't force a\n * re-registration, which would re-sort the editor's behavior chain.\n */\n isShortcutEnabled: () => boolean\n}\n\nexport function createPickerBehavior({getState, isShortcutEnabled, onIntent}: PickerBehaviorRefs) {\n const guard: BehaviorGuard<BehaviorEvent, GuardResult> = ({event, snapshot}) => {\n const state = getState()\n\n // Picker is closed — only the two open triggers can fire.\n if (state.mode === 'closed') {\n if (event.type === 'insert.text' && event.text === '/') {\n const focus = getFocusTextBlock(snapshot)\n if (!focus || !isSelectionCollapsed(snapshot) || getBlockTextBefore(snapshot) !== '') {\n return false\n }\n return {\n blockEvent: false,\n intent: {\n anchorBlockKey: focus.node._key,\n mode: 'slash',\n query: '/',\n type: 'open',\n } satisfies PickerIntent,\n }\n }\n if (\n event.type === 'keyboard.keydown' &&\n event.originEvent.key === '/' &&\n (event.originEvent.metaKey || event.originEvent.ctrlKey) &&\n isShortcutEnabled()\n ) {\n // For the shortcut mode, anchor to the focused block whether it is a\n // text block or a block object (image, callout); fall back to the\n // first block only when there is no selection at all (e.g. when\n // triggered via a toolbar button before the editor is DOM-focused).\n const focus = getFocusBlock(snapshot)\n const fallback = getFirstBlock(snapshot)\n const anchorBlock = focus ?? fallback\n if (!anchorBlock) return false\n return {\n blockEvent: true,\n intent: {\n anchorBlockKey: anchorBlock.node._key,\n mode: 'shortcut',\n query: '',\n type: 'open',\n } satisfies PickerIntent,\n }\n }\n return false\n }\n\n // Picker is open — capture editing/navigation events.\n if (event.type === 'insert.text') {\n if (state.mode === 'slash') {\n if (event.text === ' ') {\n // Space while open: close picker, let the space insert proceed.\n return {blockEvent: false, intent: {type: 'close'}}\n }\n // Character typed while in slash mode: update query; let the editor\n // insert the character into the document as well.\n return {\n blockEvent: false,\n intent: {query: state.query + event.text, type: 'updateQuery'},\n }\n }\n // Shortcut-mode type-to-filter: the query is picker-only state that\n // never entered the document, so capture the character (spaces\n // included — titles contain them) and block the insertion.\n return {\n blockEvent: true,\n intent: {query: state.query + event.text, type: 'updateQuery'},\n }\n }\n\n if (event.type === 'delete.backward') {\n if (state.mode === 'shortcut') {\n // Backspace edits the picker-only query, never the document.\n if (state.query.length === 0) {\n return {blockEvent: true, intent: {type: 'close'}}\n }\n return {\n blockEvent: true,\n intent: {\n // Word/line deletes clear the whole query rather than desyncing.\n query: event.unit === 'character' ? state.query.slice(0, -1) : '',\n type: 'updateQuery',\n },\n }\n }\n // Slash mode: the deletion proceeds in the document, so shrink the\n // query in sync. Deleting the opening \"/\" itself closes, and word/line\n // deletes can outrun the query — close and let them proceed.\n if (event.unit === 'character' && state.query.length > 1) {\n return {\n blockEvent: false,\n intent: {query: state.query.slice(0, -1), type: 'updateQuery'},\n }\n }\n return {blockEvent: false, intent: {type: 'close'}}\n }\n\n if (event.type === 'delete.forward') {\n // Forward delete edits content ahead of the caret, not the query;\n // close and let the deletion proceed in the editor normally.\n return {blockEvent: false, intent: {type: 'close'}}\n }\n\n if (event.type === 'keyboard.keydown') {\n const key = event.originEvent.key\n if (key === 'ArrowDown') {\n return {blockEvent: true, intent: {delta: 1, type: 'navigate'}}\n }\n if (key === 'ArrowUp') {\n return {blockEvent: true, intent: {delta: -1, type: 'navigate'}}\n }\n if (key === 'Enter') {\n return {blockEvent: true, intent: {type: 'select'}}\n }\n if (key === 'Escape') {\n return {blockEvent: true, intent: {type: 'close'}}\n }\n if (\n key === 'ArrowLeft' ||\n key === 'ArrowRight' ||\n key === 'Home' ||\n key === 'End' ||\n key === 'PageUp' ||\n key === 'PageDown'\n ) {\n // The caret is moving off the typed query; close and let the caret\n // move normally so the picker's query can't desync from the document.\n return {blockEvent: false, intent: {type: 'close'}}\n }\n }\n\n return false\n }\n\n return defineBehavior({\n actions: [\n (payload, guardResponse) => {\n // Actions only run when the guard returned a value, so guardResponse\n // is the GuardResult the guard produced.\n const {blockEvent, intent} = guardResponse\n const sideEffect = effect(() => onIntent(intent))\n if (blockEvent) {\n // Stopping propagation: only run the side effect, no forward.\n return [sideEffect]\n }\n // Non-blocking: run side effect and forward the original event so\n // PTE's default handling still executes.\n return [sideEffect, forward(payload.event)]\n },\n ],\n guard,\n on: '*',\n })\n}\n","import type {PickerIntent, PickerState} from './types'\n\nexport function pickerReducer(state: PickerState, intent: PickerIntent): PickerState {\n switch (intent.type) {\n case 'close':\n case 'select':\n return {mode: 'closed'}\n case 'navigate':\n if (state.mode === 'closed') return state\n return {\n ...state,\n highlightedIndex: state.highlightedIndex + intent.delta,\n }\n case 'open':\n if (state.mode !== 'closed') return state\n return {\n anchorBlockKey: intent.anchorBlockKey,\n highlightedIndex: 0,\n mode: intent.mode,\n query: intent.query,\n }\n case 'setHighlightedIndex':\n if (state.mode === 'closed') return state\n // Like `navigate`, the reducer does not clamp — the caller (which knows\n // the filtered list length) is responsible for staying in range.\n return {...state, highlightedIndex: intent.index}\n case 'updateQuery':\n if (state.mode === 'closed') return state\n return {...state, highlightedIndex: 0, query: intent.query}\n default: {\n // All intents are handled above; this arm satisfies consistent-return\n // while keeping the omission of a future variant a compile error.\n const unhandled: never = intent\n return unhandled\n }\n }\n}\n","import {effect, raise} from '@portabletext/editor/behaviors'\nimport {blockOffsetsToSelection} from '@portabletext/editor/utils'\nimport {defineInputRule, type InputRule} from '@portabletext/plugin-input-rule'\n\n/**\n * Generic machinery for markdown-style input rules that replace typed text\n * with an inserted object block, complementing the slash/⌘+/ picker with a\n * keyboard-only path that mirrors what writers already type in markdown.\n *\n * The rules run through `@portabletext/plugin-input-rule` (see\n * `markdownInputRules.tsx`), which applies the delete + insert as a single\n * behavior action set — so each transform is one undo step.\n *\n * Block values are built synchronously via each config's `buildValue` rather\n * than resolved from the schema's async initial values: the insert has to\n * happen inside the rule's action set for that single-undo guarantee to hold,\n * so anything the writer would otherwise have to fill in (language, filename,\n * callout type) is seeded from the host's rule config.\n */\n\nexport type KeyGenerator = () => string\n\n/**\n * One markdown input rule: when `pattern` matches the block text, the match\n * is deleted and a block of `blockType` is inserted in its place, carrying\n * the fields `buildValue` returns. `buildValue` must stay synchronous so the\n * delete + insert land in one action set (one undo step).\n */\nexport type MarkdownInputRuleConfig = {\n pattern: RegExp\n blockType: string\n buildValue: (context: {matchText: string; keyGenerator: KeyGenerator}) => Record<string, unknown>\n}\n\ntype BuildRulesDeps = {\n keyGenerator: KeyGenerator\n /** Called with the inserted block so the caller can open it for editing. */\n onInserted: (block: InsertableBlock) => void\n}\n\ntype InsertableBlock = {_key: string; _type: string} & Record<string, unknown>\n\n/**\n * Builds InputRules for the configs whose block type the host array actually\n * allows, so a rule can't insert a block the field would reject.\n */\nexport function createMarkdownInputRules({\n allowedBlockTypes,\n keyGenerator,\n onInserted,\n rules,\n}: BuildRulesDeps & {\n allowedBlockTypes: ReadonlySet<string>\n rules: readonly MarkdownInputRuleConfig[]\n}): InputRule[] {\n return rules\n .filter((config) => allowedBlockTypes.has(config.blockType))\n .map((config) => createInsertRule(config, keyGenerator, onInserted))\n}\n\n// The plugin matches its regex against the block text AFTER the pending\n// insertion. The trailing \"\\s\" is the space the writer types to commit the\n// fence/quote; it is part of the match and gets deleted along with it.\nexport const CODE_FENCE_PATTERN = /^`{3}\\S*\\s$/\nexport const BLOCKQUOTE_PATTERN = /^>\\s$/\n\n/**\n * A row of a code-fence language table: the stored `value`, the markdown\n * fence tokens that should resolve to it, and an optional tab filename.\n */\nexport type LanguageEntry = {\n value: string\n aliases?: readonly string[]\n filename?: string\n}\n\n/** Sugar: a blockquote rule (\"> \" at the start of a block) for `blockType`. */\nexport function blockquoteRule(options: {\n blockType: string\n createValue?: (context: {keyGenerator: KeyGenerator}) => Record<string, unknown>\n}): MarkdownInputRuleConfig {\n return {\n blockType: options.blockType,\n buildValue: ({keyGenerator}) => options.createValue?.({keyGenerator}) ?? {},\n pattern: BLOCKQUOTE_PATTERN,\n }\n}\n\n/**\n * Sugar: a code-fence rule (\"```lang␣\") for `blockType`. The language table\n * resolves fence tokens (aliases included) to the values the block's language\n * selector stores, plus an optional filename; tokens outside the table pass\n * through as typed so nothing the writer means is lost. `createValue` turns\n * the resolved language and filename into the block's fields.\n */\nexport function codeFenceRule(options: {\n blockType: string\n languages?: readonly LanguageEntry[]\n defaultLanguage?: string\n createValue: (context: {\n filename: string | undefined\n keyGenerator: KeyGenerator\n language: string | undefined\n }) => Record<string, unknown>\n}): MarkdownInputRuleConfig {\n const languages = options.languages ?? []\n return {\n blockType: options.blockType,\n buildValue: ({keyGenerator, matchText}) => {\n const language = normalizeFenceLanguage(\n fenceLanguageFromMatch(matchText),\n languages,\n options.defaultLanguage,\n )\n const filename = language\n ? languages.find((entry) => entry.value === language)?.filename\n : undefined\n return options.createValue({filename, keyGenerator, language})\n },\n pattern: CODE_FENCE_PATTERN,\n }\n}\n\n/** Strips the leading \"```\" fence and surrounding whitespace from a match. */\nexport function fenceLanguageFromMatch(matchText: string): string {\n return matchText.replace(/^`{3}/, '').trim()\n}\n\n/**\n * Resolves a typed fence token against a language table: a canonical value,\n * an alias (`py`→python, `yml`→yaml), or — for anything the table doesn't\n * know — the token as typed. An empty token yields `defaultLanguage`.\n */\nexport function normalizeFenceLanguage(\n raw: string,\n languages: readonly LanguageEntry[],\n defaultLanguage?: string,\n): string | undefined {\n const token = raw.trim()\n const lower = token.toLowerCase()\n if (!lower) return defaultLanguage\n // Match case-insensitively on both sides so a table with mixed-case values\n // or aliases still resolves (and yields its canonical `value`, casing\n // preserved) rather than falling through to the typed token.\n for (const {aliases, value} of languages) {\n if (value.toLowerCase() === lower || aliases?.some((alias) => alias.toLowerCase() === lower))\n return value\n }\n // Unknown token: pass it through as typed (case preserved) per the contract\n // above — not the lowercased form used for matching.\n return token\n}\n\nfunction createInsertRule(\n config: MarkdownInputRuleConfig,\n keyGenerator: KeyGenerator,\n onInserted: (block: InsertableBlock) => void,\n): InputRule {\n return defineInputRule({\n actions: [\n ({event, snapshot}) => {\n const match = event.matches[0]\n if (!match) return []\n const block: InsertableBlock = {\n // Spread the host-built fields first so the generated `_key` and the\n // resolved `_type` always win (mirrors insertPickerItem): a stray\n // `_key`/`_type` from buildValue can't hijack the block identity.\n ...config.buildValue({keyGenerator, matchText: match.text}),\n _key: keyGenerator(),\n _type: config.blockType,\n }\n // `match.selection` is clamped to the text that existed *before* the\n // triggering space, so it stops short of that space and would leave it\n // orphaned as a stray paragraph next to the inserted block.\n // `match.targetOffsets` spans the full match (space included); convert\n // it to a selection so the whole fence/quote is removed cleanly.\n const at =\n blockOffsetsToSelection({\n offsets: {\n anchor: match.targetOffsets.anchor,\n focus: match.targetOffsets.focus,\n },\n snapshot,\n }) ?? match.selection\n return [\n // Remove the typed fence/quote text, then insert the block in its\n // place. Both raises land in one action set (one undo step).\n raise({at, type: 'delete.text'}),\n raise({\n block,\n placement: 'auto',\n select: 'start',\n type: 'insert.block',\n }),\n effect(() => onInserted(block)),\n ]\n },\n ],\n on: config.pattern,\n })\n}\n","import {typeNameChain} from './deriveItems'\nimport {codeFenceRule, type MarkdownInputRuleConfig} from './inputRules'\nimport type {PickerItem, PickerItemMetadata, PickerItemsContext} from './types'\n\n/**\n * Default triggers and search keywords for block types with fixed, official\n * names: Studio's intrinsic types (`image`, `file`), first-party plugin\n * types (`code`, `table`, `color`, `latex`, `mux.video`), and the Media\n * Library's `sanity.video`. Presets are deliberately presentation-only —\n * they never add items (a type must actually be a member of the array),\n * never set groups (partial grouping would strand unmatched items in an\n * \"Other\" bucket), never affect rank, and always lose to a host's `items`\n * entry for the same type. Opt out entirely with `presets: false`.\n */\nexport const standardBlockPresets: readonly PickerItemMetadata[] = [\n {keywords: ['photo', 'picture', 'figure', 'media'], trigger: '/image', type: 'image'},\n {keywords: ['attachment', 'download', 'document'], trigger: '/file', type: 'file'},\n {keywords: ['snippet', 'syntax', 'fence', 'pre'], trigger: '/code', type: 'code'},\n {keywords: ['grid', 'rows', 'columns', 'spreadsheet'], trigger: '/table', type: 'table'},\n {keywords: ['swatch', 'palette'], trigger: '/color', type: 'color'},\n {keywords: ['formula', 'equation', 'math', 'tex', 'katex'], trigger: '/latex', type: 'latex'},\n {keywords: ['video', 'player', 'mux'], trigger: '/video', type: 'mux.video'},\n {keywords: ['video', 'player', 'media'], trigger: '/video', type: 'sanity.video'},\n]\n\n/**\n * Opt-in input rules for well-known block types — spread into `inputRules`\n * (`inputRules: [...wellKnownInputRules]`) rather than applied by default.\n * Today: a code fence for `@sanity/code-input`'s `code` type that stores the\n * typed fence token as the language. Like all rules, each resolves against\n * the array's members by name or resolved type chain (so an aliased\n * `{type: 'code', name: 'snippet'}` member still gets the fence, inserted\n * with the member's `_type`) and is ignored when nothing matches.\n */\nexport const wellKnownInputRules: readonly MarkdownInputRuleConfig[] = [\n codeFenceRule({\n blockType: 'code',\n createValue: ({language}) => (language ? {language} : {}),\n }),\n]\n\n/**\n * Fills preset triggers and keywords into derived items that don't already\n * have them, matching presets against each member's resolved type chain so\n * `{type: 'image', name: 'photo'}` still gets the image preset. Host `items`\n * metadata has already been folded in by derivePickerItems, so anything it\n * set wins by construction.\n */\nexport function applyPresetMetadata(\n items: readonly PickerItem[],\n context: PickerItemsContext,\n presets: readonly PickerItemMetadata[] = standardBlockPresets,\n): PickerItem[] {\n const presetByType = new Map(presets.map((preset) => [preset.type, preset]))\n return items.map((item) => {\n if (item.action.type !== 'insertBlock') return item\n if (item.trigger !== undefined && item.keywords !== undefined) return item\n const blockType = item.action.blockType\n const memberType = context.memberTypes.find((candidate) => candidate.name === blockType)\n if (!memberType) return item\n const preset = typeNameChain(memberType)\n .map((name) => presetByType.get(name))\n .find(Boolean)\n if (!preset) return item\n return {\n ...item,\n keywords: item.keywords ?? preset.keywords,\n trigger: item.trigger ?? preset.trigger,\n }\n })\n}\n","import {BlockElementIcon} from '@sanity/icons/BlockElement'\nimport type {ComponentType} from 'react'\n\nimport type {PickerItem} from './types'\n\ntype ItemPresentation = {\n title: string\n icon: ComponentType\n description: string | undefined\n}\n\ntype SchemaTypeLike = {\n name: string\n title?: string\n description?: string\n icon?: ComponentType\n type?: SchemaTypeLike\n /** Reference types carry their targets here; core reads `to[0].icon`. */\n to?: ReadonlyArray<{icon?: ComponentType}>\n}\n\n/**\n * Resolves what a picker row shows for an item, following the same fallback\n * chains Studio's built-in insert menu uses: icon from the member type, its\n * parent type, a reference target, then the generic block icon; title from\n * the member type or its capitalized name. The curated description (from\n * `items` metadata) wins over the schema type's own — curation is the more\n * specific intent.\n */\nexport function resolveItemPresentation(\n item: PickerItem,\n schemaType: SchemaTypeLike | undefined,\n): ItemPresentation {\n if (!schemaType) {\n return {\n description: item.description,\n icon: item.icon ?? BlockElementIcon,\n title: item.title || (item.action.type === 'insertBlock' ? item.action.blockType : item.id),\n }\n }\n return {\n description: item.description ?? schemaType.description,\n icon: schemaType.icon ?? schemaType.type?.icon ?? schemaType.to?.[0]?.icon ?? BlockElementIcon,\n title: schemaType.title ?? upperFirst(schemaType.name),\n }\n}\n\n// Matches core's lodash `upperFirst`: capitalize the first character, leave\n// the rest untouched (`codeBlock` -> `CodeBlock`, not `Codeblock`).\nfunction upperFirst(value: string): string {\n return value.charAt(0).toUpperCase() + value.slice(1)\n}\n","import {useEditor} from '@portabletext/editor'\nimport {Box, Card, Flex, Hotkeys, Stack, Text} from '@sanity/ui'\nimport {Popover} from '@sanity/ui/popover'\nimport {useToast} from '@sanity/ui/toast'\nimport type {CSSProperties} from 'react'\nimport {useEffect, useId, useLayoutEffect, useMemo, useReducer, useRef, useState} from 'react'\nimport {useResolveInitialValueForType, useSchema} from 'sanity'\nimport type {SchemaType} from 'sanity'\n\nimport {derivePickerItems, unknownMetadataTypes} from './deriveItems'\nimport {filterPickerItems} from './filterItems'\nimport {flattenSections, groupPickerItems} from './groupItems'\nimport {createCleanupQueryBehavior, createInsertBehavior, sendCleanupQuery} from './insertBehavior'\nimport {insertPickerItem} from './insertPickerItem'\nimport {\n type BlockInsertPickerLabels,\n formatNoMatches,\n formatPosition,\n resolveLabels,\n} from './labels'\nimport {usePickerItemsContext} from './memberSchemaTypes'\nimport {useOpenBlockOnInsert} from './openBlockOnInsert'\nimport {createPickerBehavior} from './pickerBehavior'\nimport {pickerReducer} from './pickerReducer'\nimport {applyPresetMetadata} from './presets'\nimport {resolveItemPresentation} from './resolveItemPresentation'\nimport type {\n PickerInsertEvent,\n PickerIntent,\n PickerItem,\n PickerItemMetadata,\n PickerItemsContext,\n PickerItemsResolver,\n PickerState,\n} from './types'\n\nexport type BlockInsertPickerProps = {\n /**\n * Per-member curation (trigger, keywords, group, description, badge,\n * hidden, openOnInsert) merged into the items derived from the array's\n * members; array order is the rank. Members without an entry still get\n * items, in schema order.\n */\n items?: readonly PickerItemMetadata[]\n /**\n * Escape hatch: names the portable-text array type to resolve items from\n * when Studio's member-schema context is unavailable. With the context\n * present (any supported sanity version) this is ignored — the containing\n * array and its members are detected automatically.\n */\n arrayTypeName?: string\n /**\n * Programmatic seam over the derived-and-curated item list: reorder,\n * remove, relabel, or append items (including `custom`-action items).\n */\n resolveItems?: PickerItemsResolver\n /**\n * Replaces the built-in matching (see filterPickerItems). Receives the\n * bare query in both modes — the slash-mode \"/\" prefix is stripped.\n */\n filter?: (items: readonly PickerItem[], query: string) => readonly PickerItem[]\n /** Overrides for the picker chrome's user-facing strings. */\n labels?: Partial<BlockInsertPickerLabels>\n /** Notified after each successful insert, alongside open-on-insert. */\n onInsert?: (event: PickerInsertEvent) => void\n /**\n * Whether an inserted block opens for editing (default true). Items can\n * override per member via their metadata's `openOnInsert`.\n */\n openOnInsert?: boolean\n /** Whether Cmd/Ctrl+/ opens the picker (default true). */\n shortcut?: boolean\n /**\n * Whether well-known block types (image, file, code, table, ...) get\n * default triggers and keywords when the host's `items` doesn't cover\n * them (default true). See standardBlockPresets.\n */\n presets?: boolean\n}\n\nexport function BlockInsertPicker({\n arrayTypeName,\n filter,\n items,\n labels: labelOverrides,\n onInsert,\n openOnInsert = true,\n presets = true,\n resolveItems,\n shortcut = true,\n}: BlockInsertPickerProps) {\n const editor = useEditor()\n const openBlockOnInsert = useOpenBlockOnInsert()\n const itemsContext = usePickerItemsContext(arrayTypeName)\n const schema = useSchema()\n const resolveInitialValueForType = useResolveInitialValueForType()\n const toast = useToast()\n const labels = useMemo(() => resolveLabels(labelOverrides), [labelOverrides])\n // Instance-scoped so ARIA option ids stay unique when several PTE fields\n // (each with their own picker) mount on the same document.\n const instanceId = useId()\n const listboxId = `${instanceId}-listbox`\n const optionId = (id: string) => `${instanceId}-option-${id}`\n\n // Items derive from the array's member types (via Studio's member-schema\n // context, or the arrayTypeName fallback), curated by the metadata in\n // `items` and decorated by presets. Titles and icons are then enriched\n // ONCE from the member type — not the global type of the same name — so\n // member-level config wins: a member that redefines a core type's name\n // shows its own icon and title rather than the global type's. Enriching\n // before filtering keeps query matching aligned with what the popover\n // displays, and `resolveItems` runs last over the fully-presented list so\n // hosts relabel or append against what would actually render.\n const pickerItems = useMemo(() => {\n if (!itemsContext) return []\n const derived = presets\n ? applyPresetMetadata(derivePickerItems(itemsContext, items), itemsContext)\n : derivePickerItems(itemsContext, items)\n const presented = derived.map((item) => {\n if (item.action.type !== 'insertBlock') return item\n const schemaType = memberTypeFor(itemsContext, schema, item.action.blockType)\n const presentation = resolveItemPresentation(item, schemaType)\n // The derived items are fresh objects owned by this memo, so in-place\n // assignment is safe (and satisfies oxc's no-map-spread).\n return Object.assign(item, {\n description: presentation.description,\n icon: presentation.icon,\n title: presentation.title,\n })\n })\n return resolveItems ? [...resolveItems(presented, itemsContext)] : presented\n }, [items, itemsContext, presets, resolveItems, schema])\n\n const hasItems = pickerItems.length > 0\n\n // Dev-mode configuration diagnostics, once per mount per category: a\n // silent picker (no context, or nothing derivable) and typo'd metadata\n // keys are the two misconfigurations schema-derived items can't surface\n // on their own.\n const warnedRef = useRef<{context?: boolean; empty?: boolean; unknown?: boolean}>({})\n useEffect(() => {\n if (process.env['NODE_ENV'] === 'production') return\n const warned = warnedRef.current\n if (!itemsContext && !warned.context) {\n warned.context = true\n console.warn(\n '[@sanity/block-insert-picker] No Portable Text member schema context found and no ' +\n '`arrayTypeName` resolved to an array type — the picker is disabled for this field.',\n )\n return\n }\n if (itemsContext && items && !warned.unknown) {\n const unknown = unknownMetadataTypes(itemsContext, items)\n if (unknown.length > 0) {\n warned.unknown = true\n console.warn(\n `[@sanity/block-insert-picker] \\`items\\` metadata references types that are not ` +\n `members of this array: ${unknown.join(', ')}`,\n )\n }\n }\n if (itemsContext && !hasItems && !warned.empty) {\n warned.empty = true\n console.warn(\n '[@sanity/block-insert-picker] No insertable items: the array has no non-text-block ' +\n 'members (and `resolveItems` added none) — the picker is disabled for this field.',\n )\n }\n }, [hasItems, items, itemsContext])\n\n const [state, dispatch] = useReducer(pickerReducer, {mode: 'closed'})\n\n // stateRef gives behavior callbacks synchronous access to the latest state\n // without needing to re-register the behavior on every state change.\n // useLayoutEffect (vs useEffect) ensures the ref is in sync before paint,\n // closing a stale-read window for chained behavior events within a tick.\n const stateRef = useRef<PickerState>(state)\n useLayoutEffect(() => {\n stateRef.current = state\n })\n\n // The shortcut flag is read through a ref for the same reason: the guard\n // consults it per keystroke, so toggling it must not re-register (and\n // re-sort) the behavior chain.\n const shortcutRef = useRef(shortcut)\n useLayoutEffect(() => {\n shortcutRef.current = shortcut\n })\n\n const open = state.mode !== 'closed'\n const query = open ? state.query : ''\n const mode = open ? state.mode : null\n\n const filterItems = filter ?? filterPickerItems\n // The filter seam receives the query without the slash-mode \"/\" prefix, so\n // a custom `filter` sees the same bare text in both modes. The raw query\n // keeps driving the empty-state label, the shortcut echo, and onInsert.\n const filterQuery = mode === 'slash' ? bareQuery(query) : query\n\n // Sections drive the rendered layout (group headers); `filtered` is the same\n // items flattened into the single index space navigation and selection share.\n const sections = useMemo(\n () => groupPickerItems(filterItems(pickerItems, filterQuery)),\n [filterItems, filterQuery, pickerItems],\n )\n const filtered = useMemo(() => flattenSections(sections), [sections])\n // Rows resolve their flat index positionally (section offset + row index),\n // never by item id: a resolveItems-appended item may reuse a derived id,\n // and an id-keyed map would point both rows at one index.\n const sectionOffsets = useMemo(() => {\n const offsets: number[] = []\n let total = 0\n for (const section of sections) {\n offsets.push(total)\n total += section.items.length\n }\n return offsets\n }, [sections])\n // A lone section carries no useful category label (e.g. a curated `items`\n // prop, or a filter that matched a single group) — skip the header chrome.\n const showGroupHeaders = sections.length > 1\n\n // Guards the async select path: a second select arriving while a slow\n // initial-value resolution is in flight must not double-insert.\n const insertInFlightRef = useRef(false)\n\n // Plain functions below (no useCallback): the React Compiler memoizes\n // them, and its lint rule rejects hand-written dependency arrays here.\n // Core selection logic: given a resolved item index into the filtered list for\n // `current` state, perform the insertion. Extracted so both keyboard-Enter\n // (via handleIntent) and mouse-click (via selectItemAtIndex) share the same\n // path without reading a potentially-stale highlightedIndex from stateRef.\n // The initial value resolves at select time (mirroring Studio's own toolbar\n // handleInsertBlock flow) so the first select in a session can't race an\n // eager cache, and resolution errors surface instead of being swallowed.\n const insertItemAtIndex = async (\n current: Exclude<PickerState, {mode: 'closed'}>,\n idx: number,\n ) => {\n if (insertInFlightRef.current) {\n // A prior select is still resolving; bail to avoid a double-insert. Still\n // close, because the user may have reopened the picker mid-flight and a\n // stuck-open menu that swallows this Enter looks broken.\n dispatch({type: 'close'})\n return\n }\n insertInFlightRef.current = true\n try {\n // Index into the grouped display order (not the raw rank-flat order)\n // so the row the user sees highlighted is the one that gets inserted.\n const visible = flattenSections(\n groupPickerItems(\n filterItems(\n pickerItems,\n current.mode === 'slash' ? bareQuery(current.query) : current.query,\n ),\n ),\n )\n const clamped = Math.max(0, Math.min(visible.length - 1, idx))\n const chosen = visible[clamped]\n // Close immediately for a snappy response; the insert continues from\n // the captured `current` state.\n dispatch({type: 'close'})\n if (!chosen) return\n if (chosen.action.type === 'custom') {\n // No block to insert: clean up the typed \"/query\" text (slash mode)\n // and hand the editor to the host's action.\n sendCleanupQuery(editor, {\n anchorBlockKey: current.anchorBlockKey,\n mode: current.mode,\n query: current.query,\n })\n chosen.action.onSelect({\n closePicker: () => dispatch({type: 'close'}),\n editor,\n })\n return\n }\n const blockType = chosen.action.blockType\n let initialValue: Record<string, unknown> | undefined\n const schemaType = memberTypeFor(itemsContext, schema, blockType)\n if (schemaType) {\n initialValue = (await resolveInitialValueForType(schemaType, {})) ?? undefined\n }\n // Re-take the snapshot after the await; the document may have changed.\n // A null selection is fine: the insert behavior anchors the block via\n // `at` from anchorBlockKey, so a blur during a slow resolution (or a\n // shortcut-mode open with no selection) must not abort the insert.\n const snapshot = editor.getSnapshot()\n // Confirm the anchor block still exists in the document; if it was\n // deleted while the picker was open we have nothing to anchor against.\n const anchorStillExists = snapshot.context.value.some(\n (b) => b._key === current.anchorBlockKey,\n )\n if (!anchorStillExists) {\n toast.push({\n closable: true,\n description: labels.insertAnchorRemoved,\n status: 'warning',\n title: labels.insertError,\n })\n return\n }\n insertPickerItem({\n anchorBlockKey: current.anchorBlockKey,\n blockType,\n editor,\n initialValue,\n keyGenerator: () => snapshot.context.keyGenerator(),\n mode: current.mode,\n onInsertedKey: (key) => {\n if (chosen.openOnInsert ?? openOnInsert) openBlockOnInsert(key)\n onInsert?.({\n blockKey: key,\n blockType,\n mode: current.mode,\n query: current.query,\n via: 'picker',\n })\n },\n query: current.query,\n })\n } catch (error) {\n // insertItemAtIndex is fire-and-forget (`void insertItemAtIndex(...)`),\n // so anything thrown here — initial-value resolution, a host `filter`,\n // a custom action's onSelect, a host onInsert — would otherwise become\n // an unhandled rejection with no user feedback.\n toast.push({\n closable: true,\n description: error instanceof Error ? error.message : String(error),\n status: 'error',\n title: labels.insertError,\n })\n // oxlint-disable-next-line react/todo\n } finally {\n insertInFlightRef.current = false\n }\n }\n\n // Rows call this from onClick with their own index rather than reading\n // highlightedIndex: the onMouseMove highlight update for the row under the\n // cursor may not have flushed yet, so passing idx avoids inserting the\n // previously-highlighted row.\n const selectItemAtIndex = (idx: number) => {\n const current = stateRef.current\n if (current.mode === 'closed') return\n void insertItemAtIndex(current, idx)\n }\n\n const handleIntent = (intent: PickerIntent) => {\n if (intent.type === 'navigate') {\n // The reducer deliberately doesn't clamp (it can't know the filtered\n // list length), so resolve the move here. Clamping the base index too\n // self-heals any stored value that has drifted out of range — an\n // unclamped store makes reversed arrow presses appear \"dead\" while a\n // hidden out-of-bounds index counts back into range.\n const current = stateRef.current\n if (current.mode === 'closed') return\n const lastIndex = filtered.length - 1\n const base = Math.max(0, Math.min(lastIndex, current.highlightedIndex))\n const next = Math.max(0, Math.min(lastIndex, base + intent.delta))\n dispatch({index: next, type: 'setHighlightedIndex'})\n return\n }\n if (intent.type !== 'select') {\n dispatch(intent)\n return\n }\n // Resolve the currently-highlighted item against the filtered list.\n const current = stateRef.current\n if (current.mode === 'closed') return\n void insertItemAtIndex(current, current.highlightedIndex)\n }\n\n // handleIntentRef lets the behavior always call the latest handleIntent\n // without needing to re-register the behavior when items changes.\n // useLayoutEffect for the same stale-read-window reason as stateRef above.\n const handleIntentRef = useRef<(intent: PickerIntent) => void>(handleIntent)\n useLayoutEffect(() => {\n handleIntentRef.current = handleIntent\n })\n\n // Register behaviors imperatively so their lifecycle follows this\n // component's — and only while there is anything to offer: with zero\n // items the picker must never open (no empty popover, no swallowed \"/\").\n // The picker behavior watches typing/keys; the insert behavior performs\n // the atomic cleanup + insert when a custom.blockInsertPicker.insert\n // event arrives; the cleanup behavior serves `custom` item actions.\n useEffect(() => {\n if (!hasItems) return undefined\n const behavior = createPickerBehavior({\n getState: () => stateRef.current,\n isShortcutEnabled: () => shortcutRef.current,\n onIntent: (intent) => handleIntentRef.current(intent),\n })\n const unregisterPicker = editor.registerBehavior({behavior})\n const unregisterInsert = editor.registerBehavior({\n behavior: createInsertBehavior(),\n })\n const unregisterCleanup = editor.registerBehavior({\n behavior: createCleanupQueryBehavior(),\n })\n return () => {\n unregisterPicker()\n unregisterInsert()\n unregisterCleanup()\n }\n }, [editor, hasItems])\n\n // If the item list empties while the picker is open (a resolveItems change,\n // a schema hot-reload), the behaviors above unregister — close the popover\n // rather than leaving a zombie that no behavior can drive.\n useEffect(() => {\n if (!hasItems && stateRef.current.mode !== 'closed') dispatch({type: 'close'})\n }, [hasItems])\n\n // --- UI ---\n\n const highlightedIndex = open\n ? Math.max(0, Math.min(filtered.length - 1, state.highlightedIndex))\n : 0\n\n // Announced to screen readers via a polite live region: the focus stays in\n // the editor, so aria-selected alone would never be read aloud. Recomputing\n // this on every highlight/filter change lets assistive tech track the list.\n const highlightedItem = open ? filtered[highlightedIndex] : undefined\n const announcement = highlightedItem\n ? [\n highlightedItem.title,\n highlightedItem.description,\n formatPosition(labels, highlightedIndex + 1, filtered.length),\n ]\n .filter(Boolean)\n .join(', ')\n : ''\n\n // Capture the caret DOMRect once when the picker opens and hand Popover a\n // virtual reference element wrapping it. The rect\n // is intentionally pinned at the opening position — re-capturing on every\n // `state` change (e.g. each keystroke that updates `query`) would make the\n // popover drift right as the caret advances through typed characters.\n // editor.dom is the supported anchoring API; a collapsed caret in an empty\n // block can yield an all-zero rect in some browsers, so fall back to the\n // block element's rect in that case.\n const [cursorRect, setCursorRect] = useState<DOMRect | null>(null)\n\n useLayoutEffect(() => {\n if (!open) {\n // oxlint-disable-next-line react/set-state-in-effect\n setCursorRect(null)\n return\n }\n const snapshot = editor.getSnapshot()\n const rect = editor.dom.getSelectionRect(snapshot)\n if (rect && (rect.x || rect.y || rect.width || rect.height)) {\n setCursorRect(rect)\n return\n }\n const blockRect = editor.dom.getStartBlockElement(snapshot)?.getBoundingClientRect()\n if (blockRect) setCursorRect(blockRect)\n }, [editor, open])\n\n // Close on any pointerdown outside the popover (clicking into another block\n // must not leave a zombie picker that later intercepts Enter), and when\n // focus leaves the editor entirely (e.g. Tab). @sanity/ui's Popover has no\n // onClickOutside prop — that lives on Menu/Dialog — so listen manually.\n const popoverContentRef = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!open) return undefined\n const handlePointerDown = (event: PointerEvent) => {\n const target = event.target\n if (target instanceof Node && popoverContentRef.current?.contains(target)) return\n dispatch({type: 'close'})\n }\n // getEditorElement is typed as Element, whose addEventListener overload\n // hands the listener a plain Event; narrow to FocusEvent inside instead\n // of asserting the element type.\n const editorElement = editor.dom.getEditorElement()\n const handleFocusOut = (event: Event) => {\n const next = event instanceof FocusEvent ? event.relatedTarget : null\n if (\n next instanceof Node &&\n (editorElement?.contains(next) || popoverContentRef.current?.contains(next))\n )\n return\n dispatch({type: 'close'})\n }\n document.addEventListener('pointerdown', handlePointerDown, true)\n editorElement?.addEventListener('focusout', handleFocusOut)\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown, true)\n editorElement?.removeEventListener('focusout', handleFocusOut)\n }\n }, [editor, open])\n\n // Keep the highlighted row visible inside the scrolling list.\n const highlightedRowRef = useRef<HTMLDivElement | null>(null)\n\n // Hover may only move the highlight on genuine pointer movement. When\n // keyboard navigation scrolls the list (scrollIntoView below), rows slide\n // under a stationary cursor and the browser fires mouseenter — and, in some\n // engines, a coordinate-identical mousemove — on whichever row lands there.\n // Honoring those synthetic events hijacks the keyboard highlight back to\n // wherever the mouse happens to rest. Track the last seen coordinates and\n // require them to change before hover wins; the first event only seeds.\n const lastPointerRef = useRef<null | {x: number; y: number}>(null)\n\n const highlightOnPointerMove = (idx: number, x: number, y: number) => {\n const last = lastPointerRef.current\n lastPointerRef.current = {x, y}\n if (!last || (last.x === x && last.y === y)) return\n const current = stateRef.current\n if (current.mode !== 'closed' && idx !== current.highlightedIndex) {\n dispatch({index: idx, type: 'setHighlightedIndex'})\n }\n }\n\n useEffect(() => {\n highlightedRowRef.current?.scrollIntoView({block: 'nearest'})\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n }, [highlightedIndex])\n\n // Stable virtual element derived from the captured rect — keeps Popover\n // positioning stable even as `state` changes while the picker is open.\n const cursorElement = useMemo(() => {\n if (!cursorRect) return null\n // Popover types referenceElement as HTMLElement, but its positioning\n // only ever reads getBoundingClientRect — the standard virtual-element\n // escape hatch for anchoring to a caret rect.\n return {\n getBoundingClientRect: () => cursorRect,\n // oxlint-disable-next-line no-unsafe-type-assertion\n } as unknown as HTMLElement\n }, [cursorRect])\n\n if (!open) return null\n\n if (filtered.length === 0) {\n return (\n <Popover\n constrainSize\n content={\n <Card\n aria-live=\"polite\"\n onMouseDown={preventFocusSteal}\n padding={3}\n radius={2}\n ref={popoverContentRef}\n // Card renders (and refs) a div; an <output> would change the\n // element type the outside-pointerdown ref relies on.\n // oxlint-disable-next-line prefer-tag-over-role\n role=\"status\"\n >\n <Text muted size={1}>\n {formatNoMatches(labels, query)}\n </Text>\n </Card>\n }\n fallbackPlacements={['top-start']}\n open\n placement=\"bottom-start\"\n portal\n preventOverflow\n referenceElement={cursorElement}\n />\n )\n }\n\n return (\n <Popover\n constrainSize\n content={\n <Card\n onMouseDown={preventFocusSteal}\n padding={1}\n radius={2}\n ref={popoverContentRef}\n style={{\n display: 'flex',\n flexDirection: 'column',\n maxHeight: '100%',\n minWidth: 300,\n }}\n >\n {/* Focus stays in the editor, so aria-selected on rows is never read\n aloud on its own. This polite region carries the open state and\n each highlight change to assistive tech. */}\n <output aria-live=\"polite\" style={SR_ONLY}>\n {announcement}\n </output>\n <Flex align=\"center\" flex=\"none\" gap={2} padding={2}>\n <Box flex={1}>\n <Text\n muted\n size={0}\n style={{letterSpacing: '0.05em', textTransform: 'uppercase'}}\n weight=\"medium\"\n >\n {labels.title}\n </Text>\n </Box>\n {mode === 'shortcut' && query !== '' ? (\n // Shortcut-mode typing never reaches the document, so echo the\n // captured query here — otherwise keystrokes appear to vanish.\n <Text muted size={1} textOverflow=\"ellipsis\">\n {query}\n </Text>\n ) : null}\n </Flex>\n <Box\n aria-label={labels.title}\n id={listboxId}\n // A custom ARIA listbox: <select>/<datalist> cannot host rich\n // rows (icons, descriptions, hotkey chips) or grouped sections.\n // oxlint-disable-next-line prefer-tag-over-role\n role=\"listbox\"\n style={{flex: 1, maxHeight: 320, minHeight: 0, overflowY: 'auto'}}\n >\n {sections.map((section, sectionIndex) => {\n const sectionLabel = section.group ?? labels.ungroupedSection\n // Key on the raw group (namespaced), not the display label: a\n // curated group named the same as `ungroupedSection` would\n // otherwise share a React key with the null-group section.\n const sectionKey = section.group === null ? 'ungrouped' : `group:${section.group}`\n const rows = section.items.map((item, rowIndex) => {\n const idx = (sectionOffsets[sectionIndex] ?? 0) + rowIndex\n const Icon = item.icon\n const isHighlighted = idx === highlightedIndex\n return (\n <Card\n aria-selected={isHighlighted}\n id={optionId(String(idx))}\n key={`${item.id}-${idx}`}\n onClick={() => selectItemAtIndex(idx)}\n onMouseMove={(event) =>\n highlightOnPointerMove(idx, event.clientX, event.clientY)\n }\n padding={2}\n radius={1}\n ref={isHighlighted ? highlightedRowRef : undefined}\n // A native <option> only works inside <select>; these are\n // ARIA options in a custom listbox rendered from Cards.\n // oxlint-disable-next-line prefer-tag-over-role\n role=\"option\"\n style={{cursor: 'pointer'}}\n // Focusable in principle (never via Tab): DOM focus stays\n // in the editor and the live region does the announcing.\n tabIndex={-1}\n tone={isHighlighted ? 'primary' : 'default'}\n >\n <Flex align=\"center\" gap={3}>\n <Box\n style={{\n alignItems: 'center',\n display: 'flex',\n height: 22,\n justifyContent: 'center',\n width: 22,\n }}\n >\n {Icon ? <Icon /> : null}\n </Box>\n <Stack flex={1} gap={2}>\n <Text size={1} textOverflow=\"ellipsis\">\n {item.title}\n </Text>\n {item.description ? (\n <Text muted size={0} textOverflow=\"ellipsis\">\n {item.description}\n </Text>\n ) : null}\n </Stack>\n {item.badge ? (\n <Box flex=\"none\">\n <Text muted size={0}>\n {item.badge}\n </Text>\n </Box>\n ) : null}\n {item.trigger ? (\n <Box flex=\"none\">\n <Hotkeys fontSize={0} keys={[item.trigger]} />\n </Box>\n ) : null}\n </Flex>\n </Card>\n )\n })\n if (!showGroupHeaders) {\n return (\n <Stack key={sectionKey} gap={1}>\n {rows}\n </Stack>\n )\n }\n return (\n <Box\n aria-label={sectionLabel}\n key={sectionKey}\n paddingTop={sectionIndex === 0 ? 0 : 2}\n // Grouped options inside a listbox: role=\"group\" is the\n // valid ARIA structure here; the tags the rule suggests\n // (fieldset, address, …) are not permitted in a listbox.\n // oxlint-disable-next-line prefer-tag-over-role\n role=\"group\"\n >\n <Box paddingBottom={1} paddingTop={2} paddingX={2}>\n <Text\n muted\n size={0}\n style={{\n letterSpacing: '0.05em',\n textTransform: 'uppercase',\n }}\n weight=\"medium\"\n >\n {sectionLabel}\n </Text>\n </Box>\n <Stack gap={1}>{rows}</Stack>\n </Box>\n )\n })}\n </Box>\n <Card borderTop flex=\"none\" padding={2} radius={0}>\n <Flex align=\"center\" gap={3} wrap=\"wrap\">\n <FooterHint keys={['↑', '↓']} label={labels.footerNavigate} />\n <FooterHint keys={['↵']} label={labels.footerInsert} />\n <FooterHint keys={['Esc']} label={labels.footerDismiss} />\n {shortcut ? (\n <FooterHint keys={[OPEN_MODIFIER, '/']} label={labels.footerAnywhere} />\n ) : null}\n </Flex>\n </Card>\n </Card>\n }\n fallbackPlacements={['top-start']}\n open\n placement=\"bottom-start\"\n portal\n preventOverflow\n referenceElement={cursorElement}\n />\n )\n}\n\n// A single key legend in the teaching footer: the key chips plus a short verb.\nfunction FooterHint({keys, label}: {keys: string[]; label: string}) {\n return (\n <Flex align=\"center\" gap={2}>\n <Hotkeys fontSize={0} keys={keys} />\n <Text muted size={0}>\n {label}\n </Text>\n </Flex>\n )\n}\n\n// Module-level (not a component closure) so useMemo dependency arrays don't\n// need a function that the react-hooks lint flags as changing every render.\n// Member types resolve from the picker's items context first (member-level\n// config wins), then from the global type of the same name — the same\n// fallback the previous arrayTypeName-only lookup used.\nfunction memberTypeFor(\n itemsContext: PickerItemsContext | null,\n schema: {get(name: string): SchemaType | undefined},\n blockType: string,\n): SchemaType | undefined {\n const member = itemsContext?.memberTypes.find((candidate) => candidate.name === blockType)\n return member ?? schema.get(blockType)\n}\n\n// Slash-mode queries carry the leading \"/\" the writer typed; the filter seam\n// works on the bare text (see filterQuery above).\nfunction bareQuery(query: string): string {\n return query.startsWith('/') ? query.slice(1) : query\n}\n\n// Interacting with any popover surface (header, padding, the scroll\n// container's scrollbar) must not steal DOM focus from the editor: the blur\n// would trip the focusout close listener and dismiss the picker mid-use.\nfunction preventFocusSteal(event: {preventDefault: () => void}) {\n event.preventDefault()\n}\n\n// Visually hidden but present in the accessibility tree — used for the live\n// region so announcements don't disturb the popover layout.\nconst SR_ONLY: CSSProperties = {\n border: 0,\n clipPath: 'inset(50%)',\n height: 1,\n margin: -1,\n overflow: 'hidden',\n padding: 0,\n position: 'absolute',\n whiteSpace: 'nowrap',\n width: 1,\n}\n\n// The Cmd+/ shortcut symbol matches the platform: ⌘ on Apple, Ctrl elsewhere.\n// navigator.platform is deprecated but still the most reliable signal here;\n// fall back to the user agent, then to Ctrl when neither is available.\nconst IS_APPLE_PLATFORM =\n typeof navigator !== 'undefined' &&\n /mac|iphone|ipad|ipod/i.test(navigator.platform || navigator.userAgent || '')\nconst OPEN_MODIFIER = IS_APPLE_PLATFORM ? '⌘' : 'Ctrl'\n","import {useEditor} from '@portabletext/editor'\nimport {defineInputRuleBehavior} from '@portabletext/plugin-input-rule'\nimport {useEffect, useLayoutEffect, useMemo, useRef} from 'react'\n\nimport {typeNameChain} from './deriveItems'\nimport {createMarkdownInputRules, type MarkdownInputRuleConfig} from './inputRules'\nimport {usePickerItemsContext} from './memberSchemaTypes'\nimport {useOpenBlockOnInsert} from './openBlockOnInsert'\nimport type {PickerInsertEvent} from './types'\n\nexport type MarkdownInputRulesProps = {\n /**\n * Escape hatch: names the portable-text array type when Studio's\n * member-schema context is unavailable (see BlockInsertPickerProps).\n * Rules are only enabled for block types the array actually allows.\n */\n arrayTypeName?: string\n /** The markdown transforms to enable (see inputRules.ts). */\n rules: readonly MarkdownInputRuleConfig[]\n /** Notified after each successful insert, alongside open-on-insert. */\n onInsert?: (event: PickerInsertEvent) => void\n /** Whether an inserted block opens for editing (default true). */\n openOnInsert?: boolean\n}\n\n/**\n * Mounts markdown input rules on the PTE and opens each inserted block for\n * editing once its member item mounts — the same open-on-insert behavior the\n * picker uses, so a fenced code block lands ready to type into.\n */\nexport function MarkdownInputRules({\n arrayTypeName,\n onInsert,\n openOnInsert = true,\n rules,\n}: MarkdownInputRulesProps) {\n const editor = useEditor()\n const itemsContext = usePickerItemsContext(arrayTypeName)\n const openBlockOnInsert = useOpenBlockOnInsert()\n\n // Rules resolve against the array's insertable members with the same\n // precedence as picker item metadata: a rule's blockType matches a member\n // by name first, then by any name in the member's resolved type chain — so\n // wellKnownInputRules' `code` fence also serves `{type: 'code', name:\n // 'snippet'}`, with the inserted `_type` rewritten to the member name the\n // array actually accepts. Rules matching nothing are dropped here, which\n // also keeps `block` itself (and aliased text blocks) unreachable.\n const resolvedRules = useMemo(() => {\n const memberTypes = itemsContext?.memberTypes ?? []\n return rules.flatMap((rule) => {\n const member =\n memberTypes.find((candidate) => candidate.name === rule.blockType) ??\n memberTypes.find((candidate) => typeNameChain(candidate).includes(rule.blockType))\n if (!member) return []\n return [rule.blockType === member.name ? rule : {...rule, blockType: member.name}]\n })\n }, [itemsContext, rules])\n\n // The insert callbacks are routed through a ref (same pattern as\n // BlockInsertPicker's handleIntentRef) so the registration effect below\n // stays keyed on the rules/schema/editor only: re-registering a behavior\n // is not free (it tears down and re-sorts the editor's behavior chain, and\n // moves this behavior after later-registered ones), so a host passing an\n // inline onInsert must not cause per-render churn.\n const onInsertedRef = useRef<(block: {_key: string; _type: string}) => void>(() => {})\n useLayoutEffect(() => {\n onInsertedRef.current = (block) => {\n if (openOnInsert) openBlockOnInsert(block._key)\n onInsert?.({\n blockKey: block._key,\n blockType: block._type,\n via: 'inputRule',\n })\n }\n })\n\n // Register the input-rule behavior imperatively rather than through the\n // <InputRulePlugin>\n // component: the rule closures then live inside an effect and can schedule\n // the open-on-insert without hitting the React Compiler's \"no refs during\n // render\" rule. Each transform is still a single undo step (the plugin\n // applies the delete + insert as one behavior action set).\n useEffect(() => {\n const inputRules = createMarkdownInputRules({\n // resolvedRules are already narrowed to insertable members.\n allowedBlockTypes: new Set(resolvedRules.map((rule) => rule.blockType)),\n // The generator is read per insert so keys stay unique across a session.\n keyGenerator: () => editor.getSnapshot().context.keyGenerator(),\n onInserted: (block) => onInsertedRef.current(block),\n rules: resolvedRules,\n })\n if (inputRules.length === 0) return undefined\n return editor.registerBehavior({\n behavior: defineInputRuleBehavior({rules: inputRules}),\n })\n }, [editor, resolvedRules])\n\n return null\n}\n","import type {ComponentType} from 'react'\nimport type {PortableTextPluginsProps} from 'sanity'\n\nimport {BlockInsertPicker} from './blockInsertPicker'\nimport type {MarkdownInputRuleConfig} from './inputRules'\nimport type {BlockInsertPickerLabels} from './labels'\nimport {MarkdownInputRules} from './markdownInputRules'\nimport type {PickerInsertEvent, PickerItem, PickerItemMetadata, PickerItemsResolver} from './types'\n\nexport interface BlockInsertPickerConfig {\n /**\n * Per-member curation (slash trigger, alias keywords, section grouping,\n * description, badge, visibility, per-item open-on-insert) merged into the\n * items derived from the array's members; array order is the sort rank.\n * Members without an entry still get picker items, appended in schema\n * order. Entries whose `type` matches no member warn in dev mode.\n */\n items?: readonly PickerItemMetadata[]\n /**\n * Programmatic seam over the derived item list — reorder, remove, relabel,\n * or append items (including `custom`-action items) — running after schema\n * derivation, presets, and `items` metadata.\n */\n resolveItems?: PickerItemsResolver\n /**\n * Markdown-style transforms (for example from the {@link codeFenceRule} and\n * {@link blockquoteRule} factories) that replace typed text with an\n * inserted block, as one undo step. Opt-in only; rules targeting block\n * types the array does not allow are ignored.\n */\n inputRules?: readonly MarkdownInputRuleConfig[]\n /**\n * Replaces the built-in matching (case-insensitive substring over title,\n * keywords, and description, plus trigger prefixes). Receives the bare\n * query in both modes — the slash-mode \"/\" prefix is stripped.\n */\n filter?: (items: readonly PickerItem[], query: string) => readonly PickerItem[]\n /** Overrides for the picker chrome's user-facing strings. */\n labels?: Partial<BlockInsertPickerLabels>\n /**\n * Notified after each successful insert — picker selection or input rule —\n * alongside the built-in open-for-editing behavior.\n */\n onInsert?: (event: PickerInsertEvent) => void\n /**\n * Whether an inserted block opens for editing (default true). Items can\n * override per member via their metadata's `openOnInsert`.\n */\n openOnInsert?: boolean\n /** Whether Cmd/Ctrl+/ opens the picker (default true). */\n shortcut?: boolean\n /**\n * Whether well-known block types (image, file, code, table, ...) get\n * default triggers and keywords when `items` doesn't cover them\n * (default true). See {@link standardBlockPresets}.\n */\n presets?: boolean\n /**\n * Escape hatch: names the portable-text array type to resolve items\n * against when Studio's member-schema context is unavailable. Normally\n * unnecessary — the containing array and its members are detected\n * automatically from where the plugin is mounted.\n */\n arrayTypeName?: string\n}\n\n/**\n * Creates the Portable Text editor plugins component for the block-insert\n * picker: a slash-command / Cmd+/ menu of the blocks the host array allows,\n * plus optional markdown input rules. Zero-config is the intended call —\n * items derive from the array the plugin is mounted on. Attach it to the\n * array type's `components.portableText.plugins`:\n *\n * ```ts\n * defineType({\n * name: 'content',\n * type: 'array',\n * of: [{type: 'block'}, {type: 'callout'}],\n * components: {\n * portableText: {\n * plugins: blockInsertPicker(),\n * },\n * },\n * })\n * ```\n */\nexport function blockInsertPicker(\n config: BlockInsertPickerConfig = {},\n): ComponentType<PortableTextPluginsProps> {\n const {\n arrayTypeName,\n filter,\n inputRules,\n items,\n labels,\n onInsert,\n openOnInsert,\n presets,\n resolveItems,\n shortcut,\n } = config\n return function BlockInsertPickerPlugins(props: PortableTextPluginsProps) {\n return (\n <>\n {props.renderDefault(props)}\n {inputRules?.length ? (\n <MarkdownInputRules\n arrayTypeName={arrayTypeName}\n onInsert={onInsert}\n openOnInsert={openOnInsert}\n rules={inputRules}\n />\n ) : null}\n <BlockInsertPicker\n arrayTypeName={arrayTypeName}\n filter={filter}\n items={items}\n labels={labels}\n onInsert={onInsert}\n openOnInsert={openOnInsert}\n presets={presets}\n resolveItems={resolveItems}\n shortcut={shortcut}\n />\n </>\n )\n }\n}\n"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"index.js","names":["useMemo<PickerItemsContext | null>(() => {\n if (memberSchemaTypes) {\n return {\n memberTypes: memberSchemaTypes.blockObjects,\n schemaType: memberSchemaTypes.portableText,\n }\n }\n const arrayType = arrayTypeName ? schema.get(arrayTypeName) : undefined\n if (!arrayType || arrayType.jsonType !== 'array') return null\n return {\n memberTypes: arrayType.of.filter(\n (member): member is ObjectSchemaType =>\n member.jsonType === 'object' && !isTextBlockMember(member),\n ),\n schemaType: arrayType,\n }\n }, [arrayTypeName, memberSchemaTypes, schema])","{\n memberTypes: memberSchemaTypes.blockObjects,\n schemaType: memberSchemaTypes.portableText,\n }","blockObjects","portableText","(member): member is ObjectSchemaType =>\n member.jsonType === 'object' && !isTextBlockMember(member)","_c","_temp","() => {\n const key = pendingKeyRef.current\n if (!key) return\n const item = memberItems.find((m) => m.member.key === key)\n if (item) {\n onPathOpen(item.member.item.path)\n onSetPathCollapsed(item.member.item.path, false)\n pendingKeyRef.current = null\n }\n }","[memberItems, onPathOpen, onSetPathCollapsed]","(blockKey: string) => {\n pendingKeyRef.current = blockKey\n }","useCallback((blockKey: string) => {\n pendingKeyRef.current = blockKey\n }, [])","_c","unhandled","{keys, label}: {keys: string[]; label: string}","<Hotkeys fontSize={0} keys={keys} />","<Text muted size={0}>\n {label}\n </Text>","<Flex align=\"center\" gap={2}>\n <Hotkeys fontSize={0} keys={keys} />\n <Text muted size={0}>\n {label}\n </Text>\n </Flex>","_c","{\n arrayTypeName,\n onInsert,\n openOnInsert = true,\n rules,\n}: MarkdownInputRulesProps","openOnInsert = true","itemsContext?.memberTypes ?? []","memberTypes","rules.flatMap((rule) => {\n const member =\n memberTypes.find((candidate) => candidate.name === rule.blockType) ??\n memberTypes.find((candidate) => typeNameChain(candidate).includes(rule.blockType))\n if (!member) return []\n return [rule.blockType === member.name ? rule : {...rule, blockType: member.name}]\n })","(rule) => {\n const member =\n memberTypes.find((candidate) => candidate.name === rule.blockType) ??\n memberTypes.find((candidate) => typeNameChain(candidate).includes(rule.blockType))\n if (!member) return []\n return [rule.blockType === member.name ? rule : {...rule, blockType: member.name}]\n }","candidate","useMemo(() => {\n const memberTypes = itemsContext?.memberTypes ?? []\n return rules.flatMap((rule) => {\n const member =\n memberTypes.find((candidate) => candidate.name === rule.blockType) ??\n memberTypes.find((candidate) => typeNameChain(candidate).includes(rule.blockType))\n if (!member) return []\n return [rule.blockType === member.name ? rule : {...rule, blockType: member.name}]\n })\n }, [itemsContext, rules])","() => {}","() => {\n onInsertedRef.current = (block) => {\n if (openOnInsert) openBlockOnInsert(block._key)\n onInsert?.({\n blockKey: block._key,\n blockType: block._type,\n via: 'inputRule',\n })\n }\n }","() => {\n const inputRules = createMarkdownInputRules({\n // resolvedRules are already narrowed to insertable members.\n allowedBlockTypes: new Set(resolvedRules.map((rule) => rule.blockType)),\n // The generator is read per insert so keys stay unique across a session.\n keyGenerator: () => editor.getSnapshot().context.keyGenerator(),\n onInserted: (block) => onInsertedRef.current(block),\n rules: resolvedRules,\n })\n if (inputRules.length === 0) return undefined\n return editor.registerBehavior({\n behavior: defineInputRuleBehavior({rules: inputRules}),\n })\n }","[editor, resolvedRules]","(rule) => rule.blockType","block","rule","_c","_Fragment","\"NODE_ENV\""],"sources":["../src/deriveItems.ts","../src/filterItems.ts","../src/groupItems.ts","../src/insertBehavior.ts","../src/insertPickerItem.ts","../src/labels.ts","../src/memberSchemaTypes.ts","../src/openBlockOnInsert.ts","../src/pickerBehavior.ts","../src/pickerReducer.ts","../src/inputRules.ts","../src/presets.ts","../src/resolveItemPresentation.ts","../src/blockInsertPicker.tsx","../src/markdownInputRules.tsx","../src/plugin.tsx"],"sourcesContent":["import type {PickerItem, PickerItemMetadata, PickerItemsContext} from './types'\n\n/**\n * The member's own name plus every name up its compiled `type` chain, most\n * specific first: `{type: 'image', name: 'photo'}` yields `['photo',\n * 'image', ...]`. Metadata and presets match against this chain so curation\n * can target either the member name or the underlying type name.\n */\nexport function typeNameChain(memberType: {\n name: string\n type?: {name: string; type?: unknown} | undefined\n}): string[] {\n const names: string[] = []\n let current: {name: string; type?: unknown} | undefined = memberType\n while (current && typeof current.name === 'string') {\n names.push(current.name)\n current =\n current.type && typeof current.type === 'object' && 'name' in current.type\n ? // oxlint-disable-next-line no-unsafe-type-assertion\n (current.type as {name: string; type?: unknown})\n : undefined\n }\n return names\n}\n\n/**\n * Derives picker items from a portable-text array's insertable member types\n * (see PickerItemsContext), so the picker automatically covers every object\n * block the schema allows. The optional metadata list curates triggers,\n * keywords, grouping, badges, visibility, and descriptions per member type;\n * its array order is the sort rank (see PickerItemMetadata). Titles and\n * icons intentionally stay unresolved here: BlockInsertPicker resolves\n * presentation from the member schema type, which carries member-level\n * config (a member-specific icon, a per-member title override).\n */\nexport function derivePickerItems(\n context: PickerItemsContext,\n metadata: readonly PickerItemMetadata[] = [],\n): PickerItem[] {\n return context.memberTypes\n .map((memberType, schemaIndex) => {\n const chain = typeNameChain(memberType)\n // The member's own name wins over a resolved-chain match, so an entry\n // for `photo` beats an entry for `image` on `{type: 'image', name:\n // 'photo'}` — but whichever matches, its array position is the rank.\n const nameRank = metadata.findIndex((entry) => entry.type === memberType.name)\n const rank =\n nameRank === -1 ? metadata.findIndex((entry) => chain.includes(entry.type)) : nameRank\n const entry = rank === -1 ? undefined : metadata[rank]\n return {\n entry,\n name: memberType.name,\n rank: rank === -1 ? Number.MAX_SAFE_INTEGER : rank,\n schemaIndex,\n }\n })\n .filter(({entry}) => !entry?.hidden)\n .sort((a, b) => a.rank - b.rank || a.schemaIndex - b.schemaIndex)\n .map(({entry, name}) => ({\n action: {blockType: name, type: 'insertBlock' as const},\n badge: entry?.badge,\n description: entry?.description,\n group: entry?.group,\n id: name,\n keywords: entry?.keywords,\n openOnInsert: entry?.openOnInsert,\n title: '',\n trigger: entry?.trigger,\n }))\n}\n\n/**\n * Metadata entries whose `type` matches neither a member name nor any name\n * in a member's resolved type chain — almost always a typo. Surfaced as a\n * dev-mode warning by BlockInsertPicker.\n */\nexport function unknownMetadataTypes(\n context: PickerItemsContext,\n metadata: readonly PickerItemMetadata[],\n): string[] {\n const known = new Set(context.memberTypes.flatMap((memberType) => typeNameChain(memberType)))\n return metadata.map((entry) => entry.type).filter((type) => !known.has(type))\n}\n","import type {PickerItem} from './types'\n\nexport function filterPickerItems(\n items: readonly PickerItem[],\n query: string,\n): readonly PickerItem[] {\n if (query === '' || query === '/') return items\n\n const queryLower = query.toLowerCase()\n // Slash-mode queries always carry the leading \"/\", which titles and\n // keywords never contain — strip it before substring matching.\n const bareQuery = queryLower.startsWith('/') ? queryLower.slice(1) : queryLower\n\n return items.filter((item) => {\n // Triggers match with the slash stripped from both sides so that bare\n // shortcut-mode queries (\"cta\") reach them, not just slash-mode ones.\n const bareTrigger = item.trigger?.toLowerCase().replace(/^\\//, '')\n if (bareTrigger && bareQuery !== '' && bareTrigger.startsWith(bareQuery)) return true\n if (bareQuery === '') return false\n if (item.title.toLowerCase().includes(bareQuery)) return true\n if (item.description?.toLowerCase().includes(bareQuery)) return true\n if (item.keywords?.some((kw) => kw.toLowerCase().includes(bareQuery))) return true\n return false\n })\n}\n","import type {PickerItem} from './types'\n\ntype PickerSection = {\n /** Category label, or null for items that carry no group. */\n group: null | string\n items: readonly PickerItem[]\n}\n\n/**\n * Flattens sections back into the single index space that keyboard navigation,\n * selection, and scroll-into-view all share. Rendering the sections and\n * indexing this array must stay derived from the same `groupPickerItems` call\n * so Enter inserts exactly the highlighted row.\n */\nexport function flattenSections(sections: readonly PickerSection[]): readonly PickerItem[] {\n return sections.flatMap((section) => section.items)\n}\n\n/**\n * Partitions items into sections by their `group`, preserving the incoming\n * order within each section and ordering the sections by first appearance.\n *\n * Because the caller feeds rank-ordered items (see derivePickerItems), the\n * first-appearance rule places the section holding the most-used block first,\n * without a separate ordering constant to drift out of sync. Items without a\n * group collapse into a single `group: null` section (e.g. a curated `items`\n * prop), so the flat/ungrouped case stays a no-op reordering.\n */\nexport function groupPickerItems(items: readonly PickerItem[]): PickerSection[] {\n const order: (null | string)[] = []\n const byGroup = new Map<null | string, PickerItem[]>()\n for (const item of items) {\n const key = item.group ?? null\n let bucket = byGroup.get(key)\n if (!bucket) {\n bucket = []\n byGroup.set(key, bucket)\n order.push(key)\n }\n bucket.push(item)\n }\n return order.map((key) => ({group: key, items: byGroup.get(key)!}))\n}\n","import type {Editor, EditorSelection, EditorSnapshot} from '@portabletext/editor'\nimport {defineBehavior, raise} from '@portabletext/editor/behaviors'\nimport {blockOffsetsToSelection, getTextBlockText, isTextBlock} from '@portabletext/editor/utils'\n\nimport type {PickerMode} from './types'\n\ntype InsertGuardResult = {\n anchor: EditorSelection\n block: InsertPickerItemPayload['block']\n cleanup: EditorSelection\n placement: 'after' | 'auto'\n}\n\ntype InsertPickerItemPayload = {\n anchorBlockKey: string\n block: {_key: string; _type: string} & Record<string, unknown>\n mode: PickerMode\n query: string\n}\n\n/**\n * Performs the picker insertion as a single behavior action set so the whole\n * operation is one undo step, and cleans up the typed \"/query\" text with\n * delete.text over exactly that range (never delete.block) so text that\n * already existed in the anchor block can't be destroyed: the caret can sit\n * at offset 0 of a populated block and still open the picker.\n */\nexport function createInsertBehavior() {\n return defineBehavior<\n InsertPickerItemPayload,\n 'custom.blockInsertPicker.insert',\n InsertGuardResult\n >({\n actions: [\n (_, {anchor, block, cleanup, placement}) => [\n ...(cleanup ? [raise({at: cleanup, type: 'delete.text'})] : []),\n raise({\n block,\n placement,\n select: 'start',\n type: 'insert.block',\n // Pin the insert to the anchor block: the live selection may have\n // moved (e.g. input arriving during the select-time initial-value\n // await) and placement resolves relative to `at` when provided.\n ...(anchor ? {at: anchor} : {}),\n }),\n ],\n ],\n guard: ({event, snapshot}) => {\n const {anchorBlockKey, block, mode} = event\n const anchorBlockExists = snapshot.context.value.some(\n (candidate) => candidate._key === anchorBlockKey,\n )\n const anchor = anchorBlockExists\n ? blockOffsetsToSelection({\n offsets: {\n anchor: {offset: 0, path: [{_key: anchorBlockKey}]},\n focus: {offset: 0, path: [{_key: anchorBlockKey}]},\n },\n snapshot,\n })\n : null\n return {\n anchor,\n block,\n cleanup: resolveQueryCleanup(event, snapshot),\n placement: mode === 'slash' ? ('auto' as const) : ('after' as const),\n }\n },\n on: 'custom.blockInsertPicker.insert',\n })\n}\n\nexport function sendInsertPickerItem(editor: Editor, payload: InsertPickerItemPayload): void {\n editor.send({type: 'custom.blockInsertPicker.insert', ...payload})\n}\n\ntype CleanupQueryPayload = Omit<InsertPickerItemPayload, 'block'>\n\n/**\n * The query-cleanup half of the insert behavior on its own, for `custom`\n * picker actions that don't insert a block: the typed \"/query\" text still\n * leaves the document before the host's `onSelect` runs, under the same\n * only-if-unchanged guard as a real insert.\n */\nexport function createCleanupQueryBehavior() {\n return defineBehavior<\n CleanupQueryPayload,\n 'custom.blockInsertPicker.cleanupQuery',\n NonNullable<EditorSelection>\n >({\n actions: [(_, cleanup) => [raise({at: cleanup, type: 'delete.text'})]],\n guard: ({event, snapshot}) => resolveQueryCleanup(event, snapshot) ?? false,\n on: 'custom.blockInsertPicker.cleanupQuery',\n })\n}\n\nexport function sendCleanupQuery(editor: Editor, payload: CleanupQueryPayload): void {\n editor.send({type: 'custom.blockInsertPicker.cleanupQuery', ...payload})\n}\n\n/**\n * The selection spanning the typed \"/query\" text in the anchor block, when it\n * is safe to delete. Only slash mode leaves query text in the document, and\n * only when the anchor block still starts with the typed query; if it changed\n * underneath us (collaborator edit, uncaptured caret movement) the action is\n * still the user's intent, but nothing may be deleted.\n */\nfunction resolveQueryCleanup(\n event: {anchorBlockKey: string; mode: PickerMode; query: string},\n snapshot: EditorSnapshot,\n): EditorSelection {\n const {anchorBlockKey, mode, query} = event\n if (mode !== 'slash' || query.length === 0) return null\n const anchorBlock = snapshot.context.value.find((candidate) => candidate._key === anchorBlockKey)\n if (\n !anchorBlock ||\n !isTextBlock({schema: snapshot.context.schema}, anchorBlock) ||\n !getTextBlockText(anchorBlock).startsWith(query)\n ) {\n return null\n }\n return blockOffsetsToSelection({\n offsets: {\n anchor: {offset: 0, path: [{_key: anchorBlockKey}]},\n focus: {offset: query.length, path: [{_key: anchorBlockKey}]},\n },\n snapshot,\n })\n}\n","import type {Editor} from '@portabletext/editor'\n\nimport {sendInsertPickerItem} from './insertBehavior'\nimport type {PickerMode} from './types'\n\ntype InsertContext = {\n /** The `_type` of the block to insert — an `insertBlock` action's target. */\n blockType: string\n mode: PickerMode\n editor: Editor\n anchorBlockKey: string\n query: string\n keyGenerator: () => string\n initialValue?: Record<string, unknown>\n onInsertedKey?: (key: string) => void\n}\n\nexport function insertPickerItem(ctx: InsertContext): void {\n const blockKey = ctx.keyGenerator()\n // One custom event; the registered insert behavior performs the query\n // cleanup and the insertion in a single action set (single undo step).\n sendInsertPickerItem(ctx.editor, {\n anchorBlockKey: ctx.anchorBlockKey,\n block: {\n // Spread the resolved initial value first so the generated `_key` and\n // the target `_type` always win: Sanity's initial-value resolver sets\n // `_type` on object values, and a schema's initialValue could carry\n // stray meta keys. In this flow the resolved `_type` matches\n // `ctx.blockType` by construction, but ordering it last keeps the\n // insert correct even when that assumption doesn't hold.\n ...ctx.initialValue,\n _key: blockKey,\n _type: ctx.blockType,\n },\n mode: ctx.mode,\n query: ctx.query,\n })\n ctx.onInsertedKey?.(blockKey)\n}\n","/**\n * Every user-facing string in the picker chrome, centralized so hosts can\n * translate or reword them via the `labels` option (the same pattern\n * `@sanity/insert-menu` uses for its label overrides). Item titles and\n * descriptions are schema/metadata content, not chrome — localize those\n * through `items` or `resolveItems` instead.\n */\nexport type BlockInsertPickerLabels = {\n /** Popover header; also the listbox aria-label. */\n title: string\n /** Empty state; `{query}` is replaced with the typed filter text. */\n noMatches: string\n /**\n * Screen-reader position announcement for the highlighted row; `{index}`\n * and `{count}` are replaced with the 1-based position and list length.\n */\n positionAnnouncement: string\n /**\n * Section header for items without a `group`, shown only when grouped\n * sections are also present.\n */\n ungroupedSection: string\n /** Footer legend verbs. */\n footerNavigate: string\n footerInsert: string\n footerDismiss: string\n footerAnywhere: string\n /** Toast title when resolving a block's initial value fails. */\n insertError: string\n /**\n * Toast description when the block the picker was anchored to is deleted\n * before the insert completes.\n */\n insertAnchorRemoved: string\n}\n\nconst DEFAULT_LABELS: BlockInsertPickerLabels = {\n footerAnywhere: 'Anywhere',\n footerDismiss: 'Dismiss',\n footerInsert: 'Insert',\n footerNavigate: 'Navigate',\n insertAnchorRemoved: 'The block you were inserting into was removed',\n insertError: 'Could not insert block',\n noMatches: 'No matches for \"{query}\"',\n positionAnnouncement: '{index} of {count}',\n title: 'Insert block',\n ungroupedSection: 'Other blocks',\n}\n\nexport function resolveLabels(\n overrides: Partial<BlockInsertPickerLabels> | undefined,\n): BlockInsertPickerLabels {\n return overrides ? {...DEFAULT_LABELS, ...overrides} : DEFAULT_LABELS\n}\n\n/** Fills the `{query}` placeholder in the no-matches label. */\nexport function formatNoMatches(labels: BlockInsertPickerLabels, query: string): string {\n // Function replacement: a query containing replacement patterns (\"$&\")\n // must land verbatim, not be interpreted by String.replace.\n return labels.noMatches.replace('{query}', () => query)\n}\n\n/** Fills `{index}`/`{count}` in the screen-reader position announcement. */\nexport function formatPosition(\n labels: BlockInsertPickerLabels,\n index: number,\n count: number,\n): string {\n return labels.positionAnnouncement\n .replace('{index}', () => String(index))\n .replace('{count}', () => String(count))\n}\n","import {type ContextType, useContext, useMemo} from 'react'\nimport {type ObjectSchemaType, useSchema} from 'sanity'\nimport {PortableTextMemberSchemaTypesContext} from 'sanity/_singletons'\n\nimport type {PickerItemsContext} from './types'\n\n/**\n * Resolves the Portable Text array type and its insertable member types from\n * where this plugin renders — inside the PT input's `EditorProvider`.\n *\n * Primary source: Studio's own member-schema context, which the PT input\n * provides above the plugins slot. It carries the containing array type and\n * its non-text-block members as full Studio schema types, so aliased members\n * (`{type: 'image', name: 'photo'}`), inline-declared members, and arrays\n * defined inline on a field all resolve with member-level fidelity and no\n * configuration. Fallback: an explicit `arrayTypeName` looked up in the\n * workspace schema — the escape hatch for environments where the context is\n * unavailable. With neither, callers get `null` and must render nothing\n * (never an empty picker).\n *\n * Together with openBlockOnInsert.ts, this module is one of exactly two\n * importers of `sanity/_singletons` — an internal subpath with no stability\n * guarantee (the context exists since sanity 5.6.0, which the peer range\n * pins). The dependency stays quarantined here so it can be swapped for a\n * public API (an exported `usePortableTextMemberSchemaTypes`, or a\n * `schemaType` on `PortableTextPluginsProps`) in one place.\n */\n\n// The context's value type lives in @portabletext/sanity-bridge, which\n// consumer studios cannot resolve directly (it is sanity's own dependency) —\n// extract it from the context instead of importing the bridge.\ntype MemberSchemaTypes = NonNullable<ContextType<typeof PortableTextMemberSchemaTypesContext>>\n\nexport function usePickerItemsContext(arrayTypeName?: string): PickerItemsContext | null {\n const memberSchemaTypes: MemberSchemaTypes | null = useContext(\n PortableTextMemberSchemaTypesContext,\n )\n const schema = useSchema()\n\n return useMemo<PickerItemsContext | null>(() => {\n if (memberSchemaTypes) {\n return {\n memberTypes: memberSchemaTypes.blockObjects,\n schemaType: memberSchemaTypes.portableText,\n }\n }\n const arrayType = arrayTypeName ? schema.get(arrayTypeName) : undefined\n if (!arrayType || arrayType.jsonType !== 'array') return null\n return {\n memberTypes: arrayType.of.filter(\n (member): member is ObjectSchemaType =>\n member.jsonType === 'object' && !isTextBlockMember(member),\n ),\n schemaType: arrayType,\n }\n }, [arrayTypeName, memberSchemaTypes, schema])\n}\n\n/**\n * Whether a member resolves to the built-in text block: walk the compiled\n * `type` chain to its root and compare the root's name — the same algorithm\n * Studio's schema bridge uses to bucketize `of`. A literal `name === 'block'`\n * check would miss aliased text blocks (`{name: 'myBlock', type: 'block'}`)\n * and wrongly offer them as object inserts.\n */\nfunction isTextBlockMember(memberType: {\n name: string\n type?: {name: string; type?: unknown} | undefined\n}): boolean {\n let current: {name: string; type?: unknown} = memberType\n while (current.type && typeof current.type === 'object' && 'name' in current.type) {\n // oxlint-disable-next-line no-unsafe-type-assertion\n current = current.type as {name: string; type?: unknown}\n }\n return current.name === 'block'\n}\n","import {useCallback, useContext, useEffect, useRef} from 'react'\nimport {useFormCallbacks} from 'sanity'\nimport {PortableTextMemberItemsContext} from 'sanity/_singletons'\n\n/**\n * Opens a just-inserted block's editing UI (dialog/popover per the member's\n * config) once its member item mounts. An insert lands synchronously, but the\n * member item only exists on a later render — so callers schedule the key and\n * the effect opens it when it appears.\n *\n * Together with memberSchemaTypes.ts, this module is one of exactly two\n * importers of `sanity/_singletons` in this plugin: the subpath is internal\n * with no stability guarantee, so each dependency stays quarantined where it\n * can be swapped for a public API in one place. Consumers that need to react\n * to inserts without this internal should use the `onInsert` seam on the\n * plugin components instead.\n */\nexport function useOpenBlockOnInsert(): (blockKey: string) => void {\n const memberItems = useContext(PortableTextMemberItemsContext)\n const {onPathOpen, onSetPathCollapsed} = useFormCallbacks()\n const pendingKeyRef = useRef<null | string>(null)\n\n useEffect(() => {\n const key = pendingKeyRef.current\n if (!key) return\n const item = memberItems.find((m) => m.member.key === key)\n if (item) {\n onPathOpen(item.member.item.path)\n onSetPathCollapsed(item.member.item.path, false)\n pendingKeyRef.current = null\n }\n }, [memberItems, onPathOpen, onSetPathCollapsed])\n\n // Stable identity: callers hold this in effect dependency arrays, so it must\n // keep a constant reference — an unstable return would re-run (and re-register\n // behaviors in) every effect that lists it, on every render. The empty-dep\n // `useCallback` states that contract explicitly rather than relying on the\n // React Compiler to infer it.\n return useCallback((blockKey: string) => {\n pendingKeyRef.current = blockKey\n }, [])\n}\n","import {defineBehavior, effect, forward} from '@portabletext/editor/behaviors'\nimport type {BehaviorEvent, BehaviorGuard} from '@portabletext/editor/behaviors'\nimport {\n getBlockTextBefore,\n getFirstBlock,\n getFocusBlock,\n getFocusTextBlock,\n isSelectionCollapsed,\n} from '@portabletext/editor/selectors'\n\nimport type {PickerIntent, PickerState} from './types'\n\ntype GuardResult = {\n // true = block the native/default editor handling; false = allow it to proceed\n blockEvent: boolean\n intent: PickerIntent\n}\n\ntype PickerBehaviorRefs = {\n onIntent: (intent: PickerIntent) => void\n getState: () => PickerState\n /**\n * Whether Cmd/Ctrl+/ may open the picker. Read per keystroke (not captured\n * at registration) so the host toggling `shortcut` doesn't force a\n * re-registration, which would re-sort the editor's behavior chain.\n */\n isShortcutEnabled: () => boolean\n}\n\nexport function createPickerBehavior({getState, isShortcutEnabled, onIntent}: PickerBehaviorRefs) {\n const guard: BehaviorGuard<BehaviorEvent, GuardResult> = ({event, snapshot}) => {\n const state = getState()\n\n // Picker is closed — only the two open triggers can fire.\n if (state.mode === 'closed') {\n if (event.type === 'insert.text' && event.text === '/') {\n const focus = getFocusTextBlock(snapshot)\n if (!focus || !isSelectionCollapsed(snapshot) || getBlockTextBefore(snapshot) !== '') {\n return false\n }\n return {\n blockEvent: false,\n intent: {\n anchorBlockKey: focus.node._key,\n mode: 'slash',\n query: '/',\n type: 'open',\n } satisfies PickerIntent,\n }\n }\n if (\n event.type === 'keyboard.keydown' &&\n event.originEvent.key === '/' &&\n (event.originEvent.metaKey || event.originEvent.ctrlKey) &&\n isShortcutEnabled()\n ) {\n // For the shortcut mode, anchor to the focused block whether it is a\n // text block or a block object (image, callout); fall back to the\n // first block only when there is no selection at all (e.g. when\n // triggered via a toolbar button before the editor is DOM-focused).\n const focus = getFocusBlock(snapshot)\n const fallback = getFirstBlock(snapshot)\n const anchorBlock = focus ?? fallback\n if (!anchorBlock) return false\n return {\n blockEvent: true,\n intent: {\n anchorBlockKey: anchorBlock.node._key,\n mode: 'shortcut',\n query: '',\n type: 'open',\n } satisfies PickerIntent,\n }\n }\n return false\n }\n\n // Picker is open — capture editing/navigation events.\n if (event.type === 'insert.text') {\n if (state.mode === 'slash') {\n if (event.text === ' ') {\n // Space while open: close picker, let the space insert proceed.\n return {blockEvent: false, intent: {type: 'close'}}\n }\n // Character typed while in slash mode: update query; let the editor\n // insert the character into the document as well.\n return {\n blockEvent: false,\n intent: {query: state.query + event.text, type: 'updateQuery'},\n }\n }\n // Shortcut-mode type-to-filter: the query is picker-only state that\n // never entered the document, so capture the character (spaces\n // included — titles contain them) and block the insertion.\n return {\n blockEvent: true,\n intent: {query: state.query + event.text, type: 'updateQuery'},\n }\n }\n\n if (event.type === 'delete.backward') {\n if (state.mode === 'shortcut') {\n // Backspace edits the picker-only query, never the document.\n if (state.query.length === 0) {\n return {blockEvent: true, intent: {type: 'close'}}\n }\n return {\n blockEvent: true,\n intent: {\n // Word/line deletes clear the whole query rather than desyncing.\n query: event.unit === 'character' ? state.query.slice(0, -1) : '',\n type: 'updateQuery',\n },\n }\n }\n // Slash mode: the deletion proceeds in the document, so shrink the\n // query in sync. Deleting the opening \"/\" itself closes, and word/line\n // deletes can outrun the query — close and let them proceed.\n if (event.unit === 'character' && state.query.length > 1) {\n return {\n blockEvent: false,\n intent: {query: state.query.slice(0, -1), type: 'updateQuery'},\n }\n }\n return {blockEvent: false, intent: {type: 'close'}}\n }\n\n if (event.type === 'delete.forward') {\n // Forward delete edits content ahead of the caret, not the query;\n // close and let the deletion proceed in the editor normally.\n return {blockEvent: false, intent: {type: 'close'}}\n }\n\n if (event.type === 'keyboard.keydown') {\n const key = event.originEvent.key\n if (key === 'ArrowDown') {\n return {blockEvent: true, intent: {delta: 1, type: 'navigate'}}\n }\n if (key === 'ArrowUp') {\n return {blockEvent: true, intent: {delta: -1, type: 'navigate'}}\n }\n if (key === 'Enter') {\n return {blockEvent: true, intent: {type: 'select'}}\n }\n if (key === 'Escape') {\n return {blockEvent: true, intent: {type: 'close'}}\n }\n if (\n key === 'ArrowLeft' ||\n key === 'ArrowRight' ||\n key === 'Home' ||\n key === 'End' ||\n key === 'PageUp' ||\n key === 'PageDown'\n ) {\n // The caret is moving off the typed query; close and let the caret\n // move normally so the picker's query can't desync from the document.\n return {blockEvent: false, intent: {type: 'close'}}\n }\n }\n\n return false\n }\n\n return defineBehavior({\n actions: [\n (payload, guardResponse) => {\n // Actions only run when the guard returned a value, so guardResponse\n // is the GuardResult the guard produced.\n const {blockEvent, intent} = guardResponse\n const sideEffect = effect(() => onIntent(intent))\n if (blockEvent) {\n // Stopping propagation: only run the side effect, no forward.\n return [sideEffect]\n }\n // Non-blocking: run side effect and forward the original event so\n // PTE's default handling still executes.\n return [sideEffect, forward(payload.event)]\n },\n ],\n guard,\n on: '*',\n })\n}\n","import type {PickerIntent, PickerState} from './types'\n\nexport function pickerReducer(state: PickerState, intent: PickerIntent): PickerState {\n switch (intent.type) {\n case 'close':\n case 'select':\n return {mode: 'closed'}\n case 'navigate':\n if (state.mode === 'closed') return state\n return {\n ...state,\n highlightedIndex: state.highlightedIndex + intent.delta,\n }\n case 'open':\n if (state.mode !== 'closed') return state\n return {\n anchorBlockKey: intent.anchorBlockKey,\n highlightedIndex: 0,\n mode: intent.mode,\n query: intent.query,\n }\n case 'setHighlightedIndex':\n if (state.mode === 'closed') return state\n // Like `navigate`, the reducer does not clamp — the caller (which knows\n // the filtered list length) is responsible for staying in range.\n return {...state, highlightedIndex: intent.index}\n case 'updateQuery':\n if (state.mode === 'closed') return state\n return {...state, highlightedIndex: 0, query: intent.query}\n default: {\n // All intents are handled above; this arm satisfies consistent-return\n // while keeping the omission of a future variant a compile error.\n const unhandled: never = intent\n return unhandled\n }\n }\n}\n","import {effect, raise} from '@portabletext/editor/behaviors'\nimport {blockOffsetsToSelection} from '@portabletext/editor/utils'\nimport {defineInputRule, type InputRule} from '@portabletext/plugin-input-rule'\n\n/**\n * Generic machinery for markdown-style input rules that replace typed text\n * with an inserted object block, complementing the slash/⌘+/ picker with a\n * keyboard-only path that mirrors what writers already type in markdown.\n *\n * The rules run through `@portabletext/plugin-input-rule` (see\n * `markdownInputRules.tsx`), which applies the delete + insert as a single\n * behavior action set — so each transform is one undo step.\n *\n * Block values are built synchronously via each config's `buildValue` rather\n * than resolved from the schema's async initial values: the insert has to\n * happen inside the rule's action set for that single-undo guarantee to hold,\n * so anything the writer would otherwise have to fill in (language, filename,\n * callout type) is seeded from the host's rule config.\n */\n\nexport type KeyGenerator = () => string\n\n/**\n * One markdown input rule: when `pattern` matches the block text, the match\n * is deleted and a block of `blockType` is inserted in its place, carrying\n * the fields `buildValue` returns. `buildValue` must stay synchronous so the\n * delete + insert land in one action set (one undo step).\n */\nexport type MarkdownInputRuleConfig = {\n pattern: RegExp\n blockType: string\n buildValue: (context: {matchText: string; keyGenerator: KeyGenerator}) => Record<string, unknown>\n}\n\ntype BuildRulesDeps = {\n keyGenerator: KeyGenerator\n /** Called with the inserted block so the caller can open it for editing. */\n onInserted: (block: InsertableBlock) => void\n}\n\ntype InsertableBlock = {_key: string; _type: string} & Record<string, unknown>\n\n/**\n * Builds InputRules for the configs whose block type the host array actually\n * allows, so a rule can't insert a block the field would reject.\n */\nexport function createMarkdownInputRules({\n allowedBlockTypes,\n keyGenerator,\n onInserted,\n rules,\n}: BuildRulesDeps & {\n allowedBlockTypes: ReadonlySet<string>\n rules: readonly MarkdownInputRuleConfig[]\n}): InputRule[] {\n return rules\n .filter((config) => allowedBlockTypes.has(config.blockType))\n .map((config) => createInsertRule(config, keyGenerator, onInserted))\n}\n\n// The plugin matches its regex against the block text AFTER the pending\n// insertion. The trailing \"\\s\" is the space the writer types to commit the\n// fence/quote; it is part of the match and gets deleted along with it.\nexport const CODE_FENCE_PATTERN = /^`{3}\\S*\\s$/\nexport const BLOCKQUOTE_PATTERN = /^>\\s$/\n\n/**\n * A row of a code-fence language table: the stored `value`, the markdown\n * fence tokens that should resolve to it, and an optional tab filename.\n */\nexport type LanguageEntry = {\n value: string\n aliases?: readonly string[]\n filename?: string\n}\n\n/** Sugar: a blockquote rule (\"> \" at the start of a block) for `blockType`. */\nexport function blockquoteRule(options: {\n blockType: string\n createValue?: (context: {keyGenerator: KeyGenerator}) => Record<string, unknown>\n}): MarkdownInputRuleConfig {\n return {\n blockType: options.blockType,\n buildValue: ({keyGenerator}) => options.createValue?.({keyGenerator}) ?? {},\n pattern: BLOCKQUOTE_PATTERN,\n }\n}\n\n/**\n * Sugar: a code-fence rule (\"```lang␣\") for `blockType`. The language table\n * resolves fence tokens (aliases included) to the values the block's language\n * selector stores, plus an optional filename; tokens outside the table pass\n * through as typed so nothing the writer means is lost. `createValue` turns\n * the resolved language and filename into the block's fields.\n */\nexport function codeFenceRule(options: {\n blockType: string\n languages?: readonly LanguageEntry[]\n defaultLanguage?: string\n createValue: (context: {\n filename: string | undefined\n keyGenerator: KeyGenerator\n language: string | undefined\n }) => Record<string, unknown>\n}): MarkdownInputRuleConfig {\n const languages = options.languages ?? []\n return {\n blockType: options.blockType,\n buildValue: ({keyGenerator, matchText}) => {\n const language = normalizeFenceLanguage(\n fenceLanguageFromMatch(matchText),\n languages,\n options.defaultLanguage,\n )\n const filename = language\n ? languages.find((entry) => entry.value === language)?.filename\n : undefined\n return options.createValue({filename, keyGenerator, language})\n },\n pattern: CODE_FENCE_PATTERN,\n }\n}\n\n/** Strips the leading \"```\" fence and surrounding whitespace from a match. */\nexport function fenceLanguageFromMatch(matchText: string): string {\n return matchText.replace(/^`{3}/, '').trim()\n}\n\n/**\n * Resolves a typed fence token against a language table: a canonical value,\n * an alias (`py`→python, `yml`→yaml), or — for anything the table doesn't\n * know — the token as typed. An empty token yields `defaultLanguage`.\n */\nexport function normalizeFenceLanguage(\n raw: string,\n languages: readonly LanguageEntry[],\n defaultLanguage?: string,\n): string | undefined {\n const token = raw.trim()\n const lower = token.toLowerCase()\n if (!lower) return defaultLanguage\n // Match case-insensitively on both sides so a table with mixed-case values\n // or aliases still resolves (and yields its canonical `value`, casing\n // preserved) rather than falling through to the typed token.\n for (const {aliases, value} of languages) {\n if (value.toLowerCase() === lower || aliases?.some((alias) => alias.toLowerCase() === lower))\n return value\n }\n // Unknown token: pass it through as typed (case preserved) per the contract\n // above — not the lowercased form used for matching.\n return token\n}\n\nfunction createInsertRule(\n config: MarkdownInputRuleConfig,\n keyGenerator: KeyGenerator,\n onInserted: (block: InsertableBlock) => void,\n): InputRule {\n return defineInputRule({\n actions: [\n ({event, snapshot}) => {\n const match = event.matches[0]\n if (!match) return []\n const block: InsertableBlock = {\n // Spread the host-built fields first so the generated `_key` and the\n // resolved `_type` always win (mirrors insertPickerItem): a stray\n // `_key`/`_type` from buildValue can't hijack the block identity.\n ...config.buildValue({keyGenerator, matchText: match.text}),\n _key: keyGenerator(),\n _type: config.blockType,\n }\n // `match.selection` is clamped to the text that existed *before* the\n // triggering space, so it stops short of that space and would leave it\n // orphaned as a stray paragraph next to the inserted block.\n // `match.targetOffsets` spans the full match (space included); convert\n // it to a selection so the whole fence/quote is removed cleanly.\n const at =\n blockOffsetsToSelection({\n offsets: {\n anchor: match.targetOffsets.anchor,\n focus: match.targetOffsets.focus,\n },\n snapshot,\n }) ?? match.selection\n return [\n // Remove the typed fence/quote text, then insert the block in its\n // place. Both raises land in one action set (one undo step).\n raise({at, type: 'delete.text'}),\n raise({\n block,\n placement: 'auto',\n select: 'start',\n type: 'insert.block',\n }),\n effect(() => onInserted(block)),\n ]\n },\n ],\n on: config.pattern,\n })\n}\n","import {typeNameChain} from './deriveItems'\nimport {codeFenceRule, type MarkdownInputRuleConfig} from './inputRules'\nimport type {PickerItem, PickerItemMetadata, PickerItemsContext} from './types'\n\n/**\n * Default triggers and search keywords for block types with fixed, official\n * names: Studio's intrinsic types (`image`, `file`), first-party plugin\n * types (`code`, `table`, `color`, `latex`, `mux.video`), and the Media\n * Library's `sanity.video`. Presets are deliberately presentation-only —\n * they never add items (a type must actually be a member of the array),\n * never set groups (partial grouping would strand unmatched items in an\n * \"Other\" bucket), never affect rank, and always lose to a host's `items`\n * entry for the same type. Opt out entirely with `presets: false`.\n */\nexport const standardBlockPresets: readonly PickerItemMetadata[] = [\n {keywords: ['photo', 'picture', 'figure', 'media'], trigger: '/image', type: 'image'},\n {keywords: ['attachment', 'download', 'document'], trigger: '/file', type: 'file'},\n {keywords: ['snippet', 'syntax', 'fence', 'pre'], trigger: '/code', type: 'code'},\n {keywords: ['grid', 'rows', 'columns', 'spreadsheet'], trigger: '/table', type: 'table'},\n {keywords: ['swatch', 'palette'], trigger: '/color', type: 'color'},\n {keywords: ['formula', 'equation', 'math', 'tex', 'katex'], trigger: '/latex', type: 'latex'},\n {keywords: ['video', 'player', 'mux'], trigger: '/video', type: 'mux.video'},\n {keywords: ['video', 'player', 'media'], trigger: '/video', type: 'sanity.video'},\n]\n\n/**\n * Opt-in input rules for well-known block types — spread into `inputRules`\n * (`inputRules: [...wellKnownInputRules]`) rather than applied by default.\n * Today: a code fence for `@sanity/code-input`'s `code` type that stores the\n * typed fence token as the language. Like all rules, each resolves against\n * the array's members by name or resolved type chain (so an aliased\n * `{type: 'code', name: 'snippet'}` member still gets the fence, inserted\n * with the member's `_type`) and is ignored when nothing matches.\n */\nexport const wellKnownInputRules: readonly MarkdownInputRuleConfig[] = [\n codeFenceRule({\n blockType: 'code',\n createValue: ({language}) => (language ? {language} : {}),\n }),\n]\n\n/**\n * Fills preset triggers and keywords into derived items that don't already\n * have them, matching presets against each member's resolved type chain so\n * `{type: 'image', name: 'photo'}` still gets the image preset. Host `items`\n * metadata has already been folded in by derivePickerItems, so anything it\n * set wins by construction.\n */\nexport function applyPresetMetadata(\n items: readonly PickerItem[],\n context: PickerItemsContext,\n presets: readonly PickerItemMetadata[] = standardBlockPresets,\n): PickerItem[] {\n const presetByType = new Map(presets.map((preset) => [preset.type, preset]))\n return items.map((item) => {\n if (item.action.type !== 'insertBlock') return item\n if (item.trigger !== undefined && item.keywords !== undefined) return item\n const blockType = item.action.blockType\n const memberType = context.memberTypes.find((candidate) => candidate.name === blockType)\n if (!memberType) return item\n const preset = typeNameChain(memberType)\n .map((name) => presetByType.get(name))\n .find(Boolean)\n if (!preset) return item\n return {\n ...item,\n keywords: item.keywords ?? preset.keywords,\n trigger: item.trigger ?? preset.trigger,\n }\n })\n}\n","import {BlockElementIcon} from '@sanity/icons/BlockElement'\nimport type {ComponentType} from 'react'\n\nimport type {PickerItem} from './types'\n\ntype ItemPresentation = {\n title: string\n icon: ComponentType\n description: string | undefined\n}\n\ntype SchemaTypeLike = {\n name: string\n title?: string\n description?: string\n icon?: ComponentType\n type?: SchemaTypeLike\n /** Reference types carry their targets here; core reads `to[0].icon`. */\n to?: ReadonlyArray<{icon?: ComponentType}>\n}\n\n/**\n * Resolves what a picker row shows for an item, following the same fallback\n * chains Studio's built-in insert menu uses: icon from the member type, its\n * parent type, a reference target, then the generic block icon; title from\n * the member type or its capitalized name. The curated description (from\n * `items` metadata) wins over the schema type's own — curation is the more\n * specific intent.\n */\nexport function resolveItemPresentation(\n item: PickerItem,\n schemaType: SchemaTypeLike | undefined,\n): ItemPresentation {\n if (!schemaType) {\n return {\n description: item.description,\n icon: item.icon ?? BlockElementIcon,\n title: item.title || (item.action.type === 'insertBlock' ? item.action.blockType : item.id),\n }\n }\n return {\n description: item.description ?? schemaType.description,\n icon: schemaType.icon ?? schemaType.type?.icon ?? schemaType.to?.[0]?.icon ?? BlockElementIcon,\n title: schemaType.title ?? upperFirst(schemaType.name),\n }\n}\n\n// Matches core's lodash `upperFirst`: capitalize the first character, leave\n// the rest untouched (`codeBlock` -> `CodeBlock`, not `Codeblock`).\nfunction upperFirst(value: string): string {\n return value.charAt(0).toUpperCase() + value.slice(1)\n}\n","import {useEditor} from '@portabletext/editor'\nimport {Box, Card, Flex, Hotkeys, Stack, Text} from '@sanity/ui'\nimport {Popover} from '@sanity/ui/popover'\nimport {useToast} from '@sanity/ui/toast'\nimport type {CSSProperties} from 'react'\nimport {useEffect, useId, useLayoutEffect, useMemo, useReducer, useRef, useState} from 'react'\nimport {useResolveInitialValueForType, useSchema} from 'sanity'\nimport type {SchemaType} from 'sanity'\n\nimport {derivePickerItems, unknownMetadataTypes} from './deriveItems'\nimport {filterPickerItems} from './filterItems'\nimport {flattenSections, groupPickerItems} from './groupItems'\nimport {createCleanupQueryBehavior, createInsertBehavior, sendCleanupQuery} from './insertBehavior'\nimport {insertPickerItem} from './insertPickerItem'\nimport {\n type BlockInsertPickerLabels,\n formatNoMatches,\n formatPosition,\n resolveLabels,\n} from './labels'\nimport {usePickerItemsContext} from './memberSchemaTypes'\nimport {useOpenBlockOnInsert} from './openBlockOnInsert'\nimport {createPickerBehavior} from './pickerBehavior'\nimport {pickerReducer} from './pickerReducer'\nimport {applyPresetMetadata} from './presets'\nimport {resolveItemPresentation} from './resolveItemPresentation'\nimport type {\n PickerInsertEvent,\n PickerIntent,\n PickerItem,\n PickerItemMetadata,\n PickerItemsContext,\n PickerItemsResolver,\n PickerState,\n} from './types'\n\nexport type BlockInsertPickerProps = {\n /**\n * Per-member curation (trigger, keywords, group, description, badge,\n * hidden, openOnInsert) merged into the items derived from the array's\n * members; array order is the rank. Members without an entry still get\n * items, in schema order.\n */\n items?: readonly PickerItemMetadata[]\n /**\n * Escape hatch: names the portable-text array type to resolve items from\n * when Studio's member-schema context is unavailable. With the context\n * present (any supported sanity version) this is ignored — the containing\n * array and its members are detected automatically.\n */\n arrayTypeName?: string\n /**\n * Programmatic seam over the derived-and-curated item list: reorder,\n * remove, relabel, or append items (including `custom`-action items).\n */\n resolveItems?: PickerItemsResolver\n /**\n * Replaces the built-in matching (see filterPickerItems). Receives the\n * bare query in both modes — the slash-mode \"/\" prefix is stripped.\n */\n filter?: (items: readonly PickerItem[], query: string) => readonly PickerItem[]\n /** Overrides for the picker chrome's user-facing strings. */\n labels?: Partial<BlockInsertPickerLabels>\n /** Notified after each successful insert, alongside open-on-insert. */\n onInsert?: (event: PickerInsertEvent) => void\n /**\n * Whether an inserted block opens for editing (default true). Items can\n * override per member via their metadata's `openOnInsert`.\n */\n openOnInsert?: boolean\n /** Whether Cmd/Ctrl+/ opens the picker (default true). */\n shortcut?: boolean\n /**\n * Whether well-known block types (image, file, code, table, ...) get\n * default triggers and keywords when the host's `items` doesn't cover\n * them (default true). See standardBlockPresets.\n */\n presets?: boolean\n}\n\nexport function BlockInsertPicker({\n arrayTypeName,\n filter,\n items,\n labels: labelOverrides,\n onInsert,\n openOnInsert = true,\n presets = true,\n resolveItems,\n shortcut = true,\n}: BlockInsertPickerProps) {\n const editor = useEditor()\n const openBlockOnInsert = useOpenBlockOnInsert()\n const itemsContext = usePickerItemsContext(arrayTypeName)\n const schema = useSchema()\n const resolveInitialValueForType = useResolveInitialValueForType()\n const toast = useToast()\n const labels = useMemo(() => resolveLabels(labelOverrides), [labelOverrides])\n // Instance-scoped so ARIA option ids stay unique when several PTE fields\n // (each with their own picker) mount on the same document.\n const instanceId = useId()\n const listboxId = `${instanceId}-listbox`\n const optionId = (id: string) => `${instanceId}-option-${id}`\n\n // Items derive from the array's member types (via Studio's member-schema\n // context, or the arrayTypeName fallback), curated by the metadata in\n // `items` and decorated by presets. Titles and icons are then enriched\n // ONCE from the member type — not the global type of the same name — so\n // member-level config wins: a member that redefines a core type's name\n // shows its own icon and title rather than the global type's. Enriching\n // before filtering keeps query matching aligned with what the popover\n // displays, and `resolveItems` runs last over the fully-presented list so\n // hosts relabel or append against what would actually render.\n const pickerItems = useMemo(() => {\n if (!itemsContext) return []\n const derived = presets\n ? applyPresetMetadata(derivePickerItems(itemsContext, items), itemsContext)\n : derivePickerItems(itemsContext, items)\n const presented = derived.map((item) => {\n if (item.action.type !== 'insertBlock') return item\n const schemaType = memberTypeFor(itemsContext, schema, item.action.blockType)\n const presentation = resolveItemPresentation(item, schemaType)\n // The derived items are fresh objects owned by this memo, so in-place\n // assignment is safe (and satisfies oxc's no-map-spread).\n return Object.assign(item, {\n description: presentation.description,\n icon: presentation.icon,\n title: presentation.title,\n })\n })\n return resolveItems ? [...resolveItems(presented, itemsContext)] : presented\n }, [items, itemsContext, presets, resolveItems, schema])\n\n const hasItems = pickerItems.length > 0\n\n // Dev-mode configuration diagnostics, once per mount per category: a\n // silent picker (no context, or nothing derivable) and typo'd metadata\n // keys are the two misconfigurations schema-derived items can't surface\n // on their own.\n const warnedRef = useRef<{context?: boolean; empty?: boolean; unknown?: boolean}>({})\n useEffect(() => {\n if (process.env['NODE_ENV'] === 'production') return\n const warned = warnedRef.current\n if (!itemsContext && !warned.context) {\n warned.context = true\n console.warn(\n '[@sanity/block-insert-picker] No Portable Text member schema context found and no ' +\n '`arrayTypeName` resolved to an array type — the picker is disabled for this field.',\n )\n return\n }\n if (itemsContext && items && !warned.unknown) {\n const unknown = unknownMetadataTypes(itemsContext, items)\n if (unknown.length > 0) {\n warned.unknown = true\n console.warn(\n `[@sanity/block-insert-picker] \\`items\\` metadata references types that are not ` +\n `members of this array: ${unknown.join(', ')}`,\n )\n }\n }\n if (itemsContext && !hasItems && !warned.empty) {\n warned.empty = true\n console.warn(\n '[@sanity/block-insert-picker] No insertable items: the array has no non-text-block ' +\n 'members (and `resolveItems` added none) — the picker is disabled for this field.',\n )\n }\n }, [hasItems, items, itemsContext])\n\n const [state, dispatch] = useReducer(pickerReducer, {mode: 'closed'})\n\n // stateRef gives behavior callbacks synchronous access to the latest state\n // without needing to re-register the behavior on every state change.\n // useLayoutEffect (vs useEffect) ensures the ref is in sync before paint,\n // closing a stale-read window for chained behavior events within a tick.\n const stateRef = useRef<PickerState>(state)\n useLayoutEffect(() => {\n stateRef.current = state\n })\n\n // The shortcut flag is read through a ref for the same reason: the guard\n // consults it per keystroke, so toggling it must not re-register (and\n // re-sort) the behavior chain.\n const shortcutRef = useRef(shortcut)\n useLayoutEffect(() => {\n shortcutRef.current = shortcut\n })\n\n const open = state.mode !== 'closed'\n const query = open ? state.query : ''\n const mode = open ? state.mode : null\n\n const filterItems = filter ?? filterPickerItems\n // The filter seam receives the query without the slash-mode \"/\" prefix, so\n // a custom `filter` sees the same bare text in both modes. The raw query\n // keeps driving the empty-state label, the shortcut echo, and onInsert.\n const filterQuery = mode === 'slash' ? bareQuery(query) : query\n\n // Sections drive the rendered layout (group headers); `filtered` is the same\n // items flattened into the single index space navigation and selection share.\n const sections = useMemo(\n () => groupPickerItems(filterItems(pickerItems, filterQuery)),\n [filterItems, filterQuery, pickerItems],\n )\n const filtered = useMemo(() => flattenSections(sections), [sections])\n // Rows resolve their flat index positionally (section offset + row index),\n // never by item id: a resolveItems-appended item may reuse a derived id,\n // and an id-keyed map would point both rows at one index.\n const sectionOffsets = useMemo(() => {\n const offsets: number[] = []\n let total = 0\n for (const section of sections) {\n offsets.push(total)\n total += section.items.length\n }\n return offsets\n }, [sections])\n // A lone section carries no useful category label (e.g. a curated `items`\n // prop, or a filter that matched a single group) — skip the header chrome.\n const showGroupHeaders = sections.length > 1\n\n // Guards the async select path: a second select arriving while a slow\n // initial-value resolution is in flight must not double-insert.\n const insertInFlightRef = useRef(false)\n\n // Plain functions below (no useCallback): the React Compiler memoizes\n // them, and its lint rule rejects hand-written dependency arrays here.\n // Core selection logic: given a resolved item index into the filtered list for\n // `current` state, perform the insertion. Extracted so both keyboard-Enter\n // (via handleIntent) and mouse-click (via selectItemAtIndex) share the same\n // path without reading a potentially-stale highlightedIndex from stateRef.\n // The initial value resolves at select time (mirroring Studio's own toolbar\n // handleInsertBlock flow) so the first select in a session can't race an\n // eager cache, and resolution errors surface instead of being swallowed.\n const insertItemAtIndex = async (\n current: Exclude<PickerState, {mode: 'closed'}>,\n idx: number,\n ) => {\n if (insertInFlightRef.current) {\n // A prior select is still resolving; bail to avoid a double-insert. Still\n // close, because the user may have reopened the picker mid-flight and a\n // stuck-open menu that swallows this Enter looks broken.\n dispatch({type: 'close'})\n return\n }\n insertInFlightRef.current = true\n try {\n // Index into the grouped display order (not the raw rank-flat order)\n // so the row the user sees highlighted is the one that gets inserted.\n const visible = flattenSections(\n groupPickerItems(\n filterItems(\n pickerItems,\n current.mode === 'slash' ? bareQuery(current.query) : current.query,\n ),\n ),\n )\n const clamped = Math.max(0, Math.min(visible.length - 1, idx))\n const chosen = visible[clamped]\n // Close immediately for a snappy response; the insert continues from\n // the captured `current` state.\n dispatch({type: 'close'})\n if (!chosen) return\n if (chosen.action.type === 'custom') {\n // No block to insert: clean up the typed \"/query\" text (slash mode)\n // and hand the editor to the host's action.\n sendCleanupQuery(editor, {\n anchorBlockKey: current.anchorBlockKey,\n mode: current.mode,\n query: current.query,\n })\n chosen.action.onSelect({\n closePicker: () => dispatch({type: 'close'}),\n editor,\n })\n return\n }\n const blockType = chosen.action.blockType\n let initialValue: Record<string, unknown> | undefined\n const schemaType = memberTypeFor(itemsContext, schema, blockType)\n if (schemaType) {\n initialValue = (await resolveInitialValueForType(schemaType, {})) ?? undefined\n }\n // Re-take the snapshot after the await; the document may have changed.\n // A null selection is fine: the insert behavior anchors the block via\n // `at` from anchorBlockKey, so a blur during a slow resolution (or a\n // shortcut-mode open with no selection) must not abort the insert.\n const snapshot = editor.getSnapshot()\n // Confirm the anchor block still exists in the document; if it was\n // deleted while the picker was open we have nothing to anchor against.\n const anchorStillExists = snapshot.context.value.some(\n (b) => b._key === current.anchorBlockKey,\n )\n if (!anchorStillExists) {\n toast.push({\n closable: true,\n description: labels.insertAnchorRemoved,\n status: 'warning',\n title: labels.insertError,\n })\n return\n }\n insertPickerItem({\n anchorBlockKey: current.anchorBlockKey,\n blockType,\n editor,\n initialValue,\n keyGenerator: () => snapshot.context.keyGenerator(),\n mode: current.mode,\n onInsertedKey: (key) => {\n if (chosen.openOnInsert ?? openOnInsert) openBlockOnInsert(key)\n onInsert?.({\n blockKey: key,\n blockType,\n mode: current.mode,\n query: current.query,\n via: 'picker',\n })\n },\n query: current.query,\n })\n } catch (error) {\n // insertItemAtIndex is fire-and-forget (`void insertItemAtIndex(...)`),\n // so anything thrown here — initial-value resolution, a host `filter`,\n // a custom action's onSelect, a host onInsert — would otherwise become\n // an unhandled rejection with no user feedback.\n toast.push({\n closable: true,\n description: error instanceof Error ? error.message : String(error),\n status: 'error',\n title: labels.insertError,\n })\n // oxlint-disable-next-line react/todo\n } finally {\n insertInFlightRef.current = false\n }\n }\n\n // Rows call this from onClick with their own index rather than reading\n // highlightedIndex: the onMouseMove highlight update for the row under the\n // cursor may not have flushed yet, so passing idx avoids inserting the\n // previously-highlighted row.\n const selectItemAtIndex = (idx: number) => {\n const current = stateRef.current\n if (current.mode === 'closed') return\n void insertItemAtIndex(current, idx)\n }\n\n const handleIntent = (intent: PickerIntent) => {\n if (intent.type === 'navigate') {\n // The reducer deliberately doesn't clamp (it can't know the filtered\n // list length), so resolve the move here. Clamping the base index too\n // self-heals any stored value that has drifted out of range — an\n // unclamped store makes reversed arrow presses appear \"dead\" while a\n // hidden out-of-bounds index counts back into range.\n const current = stateRef.current\n if (current.mode === 'closed') return\n const lastIndex = filtered.length - 1\n const base = Math.max(0, Math.min(lastIndex, current.highlightedIndex))\n const next = Math.max(0, Math.min(lastIndex, base + intent.delta))\n dispatch({index: next, type: 'setHighlightedIndex'})\n return\n }\n if (intent.type !== 'select') {\n dispatch(intent)\n return\n }\n // Resolve the currently-highlighted item against the filtered list.\n const current = stateRef.current\n if (current.mode === 'closed') return\n void insertItemAtIndex(current, current.highlightedIndex)\n }\n\n // handleIntentRef lets the behavior always call the latest handleIntent\n // without needing to re-register the behavior when items changes.\n // useLayoutEffect for the same stale-read-window reason as stateRef above.\n const handleIntentRef = useRef<(intent: PickerIntent) => void>(handleIntent)\n useLayoutEffect(() => {\n handleIntentRef.current = handleIntent\n })\n\n // Register behaviors imperatively so their lifecycle follows this\n // component's — and only while there is anything to offer: with zero\n // items the picker must never open (no empty popover, no swallowed \"/\").\n // The picker behavior watches typing/keys; the insert behavior performs\n // the atomic cleanup + insert when a custom.blockInsertPicker.insert\n // event arrives; the cleanup behavior serves `custom` item actions.\n useEffect(() => {\n if (!hasItems) return undefined\n const behavior = createPickerBehavior({\n getState: () => stateRef.current,\n isShortcutEnabled: () => shortcutRef.current,\n onIntent: (intent) => handleIntentRef.current(intent),\n })\n const unregisterPicker = editor.registerBehavior({behavior})\n const unregisterInsert = editor.registerBehavior({\n behavior: createInsertBehavior(),\n })\n const unregisterCleanup = editor.registerBehavior({\n behavior: createCleanupQueryBehavior(),\n })\n return () => {\n unregisterPicker()\n unregisterInsert()\n unregisterCleanup()\n }\n }, [editor, hasItems])\n\n // If the item list empties while the picker is open (a resolveItems change,\n // a schema hot-reload), the behaviors above unregister — close the popover\n // rather than leaving a zombie that no behavior can drive.\n useEffect(() => {\n if (!hasItems && stateRef.current.mode !== 'closed') dispatch({type: 'close'})\n }, [hasItems])\n\n // --- UI ---\n\n const highlightedIndex = open\n ? Math.max(0, Math.min(filtered.length - 1, state.highlightedIndex))\n : 0\n\n // Announced to screen readers via a polite live region: the focus stays in\n // the editor, so aria-selected alone would never be read aloud. Recomputing\n // this on every highlight/filter change lets assistive tech track the list.\n const highlightedItem = open ? filtered[highlightedIndex] : undefined\n const announcement = highlightedItem\n ? [\n highlightedItem.title,\n highlightedItem.description,\n formatPosition(labels, highlightedIndex + 1, filtered.length),\n ]\n .filter(Boolean)\n .join(', ')\n : ''\n\n // Capture the caret DOMRect once when the picker opens and hand Popover a\n // virtual reference element wrapping it. The rect\n // is intentionally pinned at the opening position — re-capturing on every\n // `state` change (e.g. each keystroke that updates `query`) would make the\n // popover drift right as the caret advances through typed characters.\n // editor.dom is the supported anchoring API; a collapsed caret in an empty\n // block can yield an all-zero rect in some browsers, so fall back to the\n // block element's rect in that case.\n const [cursorRect, setCursorRect] = useState<DOMRect | null>(null)\n\n useLayoutEffect(() => {\n if (!open) {\n // oxlint-disable-next-line react/set-state-in-effect\n setCursorRect(null)\n return\n }\n const snapshot = editor.getSnapshot()\n const rect = editor.dom.getSelectionRect(snapshot)\n if (rect && (rect.x || rect.y || rect.width || rect.height)) {\n setCursorRect(rect)\n return\n }\n const blockRect = editor.dom.getStartBlockElement(snapshot)?.getBoundingClientRect()\n if (blockRect) setCursorRect(blockRect)\n }, [editor, open])\n\n // Close on any pointerdown outside the popover (clicking into another block\n // must not leave a zombie picker that later intercepts Enter), and when\n // focus leaves the editor entirely (e.g. Tab). @sanity/ui's Popover has no\n // onClickOutside prop — that lives on Menu/Dialog — so listen manually.\n const popoverContentRef = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!open) return undefined\n const handlePointerDown = (event: PointerEvent) => {\n const target = event.target\n if (target instanceof Node && popoverContentRef.current?.contains(target)) return\n dispatch({type: 'close'})\n }\n // getEditorElement is typed as Element, whose addEventListener overload\n // hands the listener a plain Event; narrow to FocusEvent inside instead\n // of asserting the element type.\n const editorElement = editor.dom.getEditorElement()\n const handleFocusOut = (event: Event) => {\n const next = event instanceof FocusEvent ? event.relatedTarget : null\n if (\n next instanceof Node &&\n (editorElement?.contains(next) || popoverContentRef.current?.contains(next))\n )\n return\n dispatch({type: 'close'})\n }\n document.addEventListener('pointerdown', handlePointerDown, true)\n editorElement?.addEventListener('focusout', handleFocusOut)\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown, true)\n editorElement?.removeEventListener('focusout', handleFocusOut)\n }\n }, [editor, open])\n\n // Keep the highlighted row visible inside the scrolling list.\n const highlightedRowRef = useRef<HTMLDivElement | null>(null)\n\n // Hover may only move the highlight on genuine pointer movement. When\n // keyboard navigation scrolls the list (scrollIntoView below), rows slide\n // under a stationary cursor and the browser fires mouseenter — and, in some\n // engines, a coordinate-identical mousemove — on whichever row lands there.\n // Honoring those synthetic events hijacks the keyboard highlight back to\n // wherever the mouse happens to rest. Track the last seen coordinates and\n // require them to change before hover wins; the first event only seeds.\n const lastPointerRef = useRef<null | {x: number; y: number}>(null)\n\n const highlightOnPointerMove = (idx: number, x: number, y: number) => {\n const last = lastPointerRef.current\n lastPointerRef.current = {x, y}\n if (!last || (last.x === x && last.y === y)) return\n const current = stateRef.current\n if (current.mode !== 'closed' && idx !== current.highlightedIndex) {\n dispatch({index: idx, type: 'setHighlightedIndex'})\n }\n }\n\n useEffect(() => {\n highlightedRowRef.current?.scrollIntoView({block: 'nearest'})\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n }, [highlightedIndex])\n\n // Stable virtual element derived from the captured rect — keeps Popover\n // positioning stable even as `state` changes while the picker is open.\n const cursorElement = useMemo(() => {\n if (!cursorRect) return null\n // Popover types referenceElement as HTMLElement, but its positioning\n // only ever reads getBoundingClientRect — the standard virtual-element\n // escape hatch for anchoring to a caret rect.\n return {\n getBoundingClientRect: () => cursorRect,\n // oxlint-disable-next-line no-unsafe-type-assertion\n } as unknown as HTMLElement\n }, [cursorRect])\n\n if (!open) return null\n\n if (filtered.length === 0) {\n return (\n <Popover\n constrainSize\n content={\n <Card\n aria-live=\"polite\"\n onMouseDown={preventFocusSteal}\n padding={3}\n radius={2}\n ref={popoverContentRef}\n // Card renders (and refs) a div; an <output> would change the\n // element type the outside-pointerdown ref relies on.\n // oxlint-disable-next-line prefer-tag-over-role\n role=\"status\"\n >\n <Text muted size={1}>\n {formatNoMatches(labels, query)}\n </Text>\n </Card>\n }\n fallbackPlacements={['top-start']}\n open\n placement=\"bottom-start\"\n portal\n preventOverflow\n referenceElement={cursorElement}\n />\n )\n }\n\n return (\n <Popover\n constrainSize\n content={\n <Card\n onMouseDown={preventFocusSteal}\n padding={1}\n radius={2}\n ref={popoverContentRef}\n style={{\n display: 'flex',\n flexDirection: 'column',\n maxHeight: '100%',\n minWidth: 300,\n }}\n >\n {/* Focus stays in the editor, so aria-selected on rows is never read\n aloud on its own. This polite region carries the open state and\n each highlight change to assistive tech. */}\n <output aria-live=\"polite\" style={SR_ONLY}>\n {announcement}\n </output>\n <Flex align=\"center\" flex=\"none\" gap={2} padding={2}>\n <Box flex={1}>\n <Text\n muted\n size={0}\n style={{letterSpacing: '0.05em', textTransform: 'uppercase'}}\n weight=\"medium\"\n >\n {labels.title}\n </Text>\n </Box>\n {mode === 'shortcut' && query !== '' ? (\n // Shortcut-mode typing never reaches the document, so echo the\n // captured query here — otherwise keystrokes appear to vanish.\n <Text muted size={1} textOverflow=\"ellipsis\">\n {query}\n </Text>\n ) : null}\n </Flex>\n <Box\n aria-label={labels.title}\n id={listboxId}\n // A custom ARIA listbox: <select>/<datalist> cannot host rich\n // rows (icons, descriptions, hotkey chips) or grouped sections.\n // oxlint-disable-next-line prefer-tag-over-role\n role=\"listbox\"\n style={{flex: 1, maxHeight: 320, minHeight: 0, overflowY: 'auto'}}\n >\n {sections.map((section, sectionIndex) => {\n const sectionLabel = section.group ?? labels.ungroupedSection\n // Key on the raw group (namespaced), not the display label: a\n // curated group named the same as `ungroupedSection` would\n // otherwise share a React key with the null-group section.\n const sectionKey = section.group === null ? 'ungrouped' : `group:${section.group}`\n const rows = section.items.map((item, rowIndex) => {\n const idx = (sectionOffsets[sectionIndex] ?? 0) + rowIndex\n const Icon = item.icon\n const isHighlighted = idx === highlightedIndex\n return (\n <Card\n aria-selected={isHighlighted}\n id={optionId(String(idx))}\n key={`${item.id}-${idx}`}\n onClick={() => selectItemAtIndex(idx)}\n onMouseMove={(event) =>\n highlightOnPointerMove(idx, event.clientX, event.clientY)\n }\n padding={2}\n radius={1}\n ref={isHighlighted ? highlightedRowRef : undefined}\n // A native <option> only works inside <select>; these are\n // ARIA options in a custom listbox rendered from Cards.\n // oxlint-disable-next-line prefer-tag-over-role\n role=\"option\"\n style={{cursor: 'pointer'}}\n // Focusable in principle (never via Tab): DOM focus stays\n // in the editor and the live region does the announcing.\n tabIndex={-1}\n tone={isHighlighted ? 'primary' : 'default'}\n >\n <Flex align=\"center\" gap={3}>\n <Box\n style={{\n alignItems: 'center',\n display: 'flex',\n height: 22,\n justifyContent: 'center',\n width: 22,\n }}\n >\n {Icon ? <Icon /> : null}\n </Box>\n <Stack flex={1} gap={2}>\n <Text size={1} textOverflow=\"ellipsis\">\n {item.title}\n </Text>\n {item.description ? (\n <Text muted size={0} textOverflow=\"ellipsis\">\n {item.description}\n </Text>\n ) : null}\n </Stack>\n {item.badge ? (\n <Box flex=\"none\">\n <Text muted size={0}>\n {item.badge}\n </Text>\n </Box>\n ) : null}\n {item.trigger ? (\n <Box flex=\"none\">\n <Hotkeys fontSize={0} keys={[item.trigger]} />\n </Box>\n ) : null}\n </Flex>\n </Card>\n )\n })\n if (!showGroupHeaders) {\n return (\n <Stack key={sectionKey} gap={1}>\n {rows}\n </Stack>\n )\n }\n return (\n <Box\n aria-label={sectionLabel}\n key={sectionKey}\n paddingTop={sectionIndex === 0 ? 0 : 2}\n // Grouped options inside a listbox: role=\"group\" is the\n // valid ARIA structure here; the tags the rule suggests\n // (fieldset, address, …) are not permitted in a listbox.\n // oxlint-disable-next-line prefer-tag-over-role\n role=\"group\"\n >\n <Box paddingBottom={1} paddingTop={2} paddingX={2}>\n <Text\n muted\n size={0}\n style={{\n letterSpacing: '0.05em',\n textTransform: 'uppercase',\n }}\n weight=\"medium\"\n >\n {sectionLabel}\n </Text>\n </Box>\n <Stack gap={1}>{rows}</Stack>\n </Box>\n )\n })}\n </Box>\n <Card borderTop flex=\"none\" padding={2} radius={0}>\n <Flex align=\"center\" gap={3} wrap=\"wrap\">\n <FooterHint keys={['↑', '↓']} label={labels.footerNavigate} />\n <FooterHint keys={['↵']} label={labels.footerInsert} />\n <FooterHint keys={['Esc']} label={labels.footerDismiss} />\n {shortcut ? (\n <FooterHint keys={[OPEN_MODIFIER, '/']} label={labels.footerAnywhere} />\n ) : null}\n </Flex>\n </Card>\n </Card>\n }\n fallbackPlacements={['top-start']}\n open\n placement=\"bottom-start\"\n portal\n preventOverflow\n referenceElement={cursorElement}\n />\n )\n}\n\n// A single key legend in the teaching footer: the key chips plus a short verb.\nfunction FooterHint({keys, label}: {keys: string[]; label: string}) {\n return (\n <Flex align=\"center\" gap={2}>\n <Hotkeys fontSize={0} keys={keys} />\n <Text muted size={0}>\n {label}\n </Text>\n </Flex>\n )\n}\n\n// Module-level (not a component closure) so useMemo dependency arrays don't\n// need a function that the react-hooks lint flags as changing every render.\n// Member types resolve from the picker's items context first (member-level\n// config wins), then from the global type of the same name — the same\n// fallback the previous arrayTypeName-only lookup used.\nfunction memberTypeFor(\n itemsContext: PickerItemsContext | null,\n schema: {get(name: string): SchemaType | undefined},\n blockType: string,\n): SchemaType | undefined {\n const member = itemsContext?.memberTypes.find((candidate) => candidate.name === blockType)\n return member ?? schema.get(blockType)\n}\n\n// Slash-mode queries carry the leading \"/\" the writer typed; the filter seam\n// works on the bare text (see filterQuery above).\nfunction bareQuery(query: string): string {\n return query.startsWith('/') ? query.slice(1) : query\n}\n\n// Interacting with any popover surface (header, padding, the scroll\n// container's scrollbar) must not steal DOM focus from the editor: the blur\n// would trip the focusout close listener and dismiss the picker mid-use.\nfunction preventFocusSteal(event: {preventDefault: () => void}) {\n event.preventDefault()\n}\n\n// Visually hidden but present in the accessibility tree — used for the live\n// region so announcements don't disturb the popover layout.\nconst SR_ONLY: CSSProperties = {\n border: 0,\n clipPath: 'inset(50%)',\n height: 1,\n margin: -1,\n overflow: 'hidden',\n padding: 0,\n position: 'absolute',\n whiteSpace: 'nowrap',\n width: 1,\n}\n\n// The Cmd+/ shortcut symbol matches the platform: ⌘ on Apple, Ctrl elsewhere.\n// navigator.platform is deprecated but still the most reliable signal here;\n// fall back to the user agent, then to Ctrl when neither is available.\nconst IS_APPLE_PLATFORM =\n typeof navigator !== 'undefined' &&\n /mac|iphone|ipad|ipod/i.test(navigator.platform || navigator.userAgent || '')\nconst OPEN_MODIFIER = IS_APPLE_PLATFORM ? '⌘' : 'Ctrl'\n","import {useEditor} from '@portabletext/editor'\nimport {defineInputRuleBehavior} from '@portabletext/plugin-input-rule'\nimport {useEffect, useLayoutEffect, useMemo, useRef} from 'react'\n\nimport {typeNameChain} from './deriveItems'\nimport {createMarkdownInputRules, type MarkdownInputRuleConfig} from './inputRules'\nimport {usePickerItemsContext} from './memberSchemaTypes'\nimport {useOpenBlockOnInsert} from './openBlockOnInsert'\nimport type {PickerInsertEvent} from './types'\n\nexport type MarkdownInputRulesProps = {\n /**\n * Escape hatch: names the portable-text array type when Studio's\n * member-schema context is unavailable (see BlockInsertPickerProps).\n * Rules are only enabled for block types the array actually allows.\n */\n arrayTypeName?: string\n /** The markdown transforms to enable (see inputRules.ts). */\n rules: readonly MarkdownInputRuleConfig[]\n /** Notified after each successful insert, alongside open-on-insert. */\n onInsert?: (event: PickerInsertEvent) => void\n /** Whether an inserted block opens for editing (default true). */\n openOnInsert?: boolean\n}\n\n/**\n * Mounts markdown input rules on the PTE and opens each inserted block for\n * editing once its member item mounts — the same open-on-insert behavior the\n * picker uses, so a fenced code block lands ready to type into.\n */\nexport function MarkdownInputRules({\n arrayTypeName,\n onInsert,\n openOnInsert = true,\n rules,\n}: MarkdownInputRulesProps) {\n const editor = useEditor()\n const itemsContext = usePickerItemsContext(arrayTypeName)\n const openBlockOnInsert = useOpenBlockOnInsert()\n\n // Rules resolve against the array's insertable members with the same\n // precedence as picker item metadata: a rule's blockType matches a member\n // by name first, then by any name in the member's resolved type chain — so\n // wellKnownInputRules' `code` fence also serves `{type: 'code', name:\n // 'snippet'}`, with the inserted `_type` rewritten to the member name the\n // array actually accepts. Rules matching nothing are dropped here, which\n // also keeps `block` itself (and aliased text blocks) unreachable.\n const resolvedRules = useMemo(() => {\n const memberTypes = itemsContext?.memberTypes ?? []\n return rules.flatMap((rule) => {\n const member =\n memberTypes.find((candidate) => candidate.name === rule.blockType) ??\n memberTypes.find((candidate) => typeNameChain(candidate).includes(rule.blockType))\n if (!member) return []\n return [rule.blockType === member.name ? rule : {...rule, blockType: member.name}]\n })\n }, [itemsContext, rules])\n\n // The insert callbacks are routed through a ref (same pattern as\n // BlockInsertPicker's handleIntentRef) so the registration effect below\n // stays keyed on the rules/schema/editor only: re-registering a behavior\n // is not free (it tears down and re-sorts the editor's behavior chain, and\n // moves this behavior after later-registered ones), so a host passing an\n // inline onInsert must not cause per-render churn.\n const onInsertedRef = useRef<(block: {_key: string; _type: string}) => void>(() => {})\n useLayoutEffect(() => {\n onInsertedRef.current = (block) => {\n if (openOnInsert) openBlockOnInsert(block._key)\n onInsert?.({\n blockKey: block._key,\n blockType: block._type,\n via: 'inputRule',\n })\n }\n })\n\n // Register the input-rule behavior imperatively rather than through the\n // <InputRulePlugin>\n // component: the rule closures then live inside an effect and can schedule\n // the open-on-insert without hitting the React Compiler's \"no refs during\n // render\" rule. Each transform is still a single undo step (the plugin\n // applies the delete + insert as one behavior action set).\n useEffect(() => {\n const inputRules = createMarkdownInputRules({\n // resolvedRules are already narrowed to insertable members.\n allowedBlockTypes: new Set(resolvedRules.map((rule) => rule.blockType)),\n // The generator is read per insert so keys stay unique across a session.\n keyGenerator: () => editor.getSnapshot().context.keyGenerator(),\n onInserted: (block) => onInsertedRef.current(block),\n rules: resolvedRules,\n })\n if (inputRules.length === 0) return undefined\n return editor.registerBehavior({\n behavior: defineInputRuleBehavior({rules: inputRules}),\n })\n }, [editor, resolvedRules])\n\n return null\n}\n","import type {ComponentType} from 'react'\nimport type {PortableTextPluginsProps} from 'sanity'\n\nimport {BlockInsertPicker} from './blockInsertPicker'\nimport type {MarkdownInputRuleConfig} from './inputRules'\nimport type {BlockInsertPickerLabels} from './labels'\nimport {MarkdownInputRules} from './markdownInputRules'\nimport type {PickerInsertEvent, PickerItem, PickerItemMetadata, PickerItemsResolver} from './types'\n\nexport interface BlockInsertPickerConfig {\n /**\n * Per-member curation (slash trigger, alias keywords, section grouping,\n * description, badge, visibility, per-item open-on-insert) merged into the\n * items derived from the array's members; array order is the sort rank.\n * Members without an entry still get picker items, appended in schema\n * order. Entries whose `type` matches no member warn in dev mode.\n */\n items?: readonly PickerItemMetadata[]\n /**\n * Programmatic seam over the derived item list — reorder, remove, relabel,\n * or append items (including `custom`-action items) — running after schema\n * derivation, presets, and `items` metadata.\n */\n resolveItems?: PickerItemsResolver\n /**\n * Markdown-style transforms (for example from the {@link codeFenceRule} and\n * {@link blockquoteRule} factories) that replace typed text with an\n * inserted block, as one undo step. Opt-in only; rules targeting block\n * types the array does not allow are ignored.\n */\n inputRules?: readonly MarkdownInputRuleConfig[]\n /**\n * Replaces the built-in matching (case-insensitive substring over title,\n * keywords, and description, plus trigger prefixes). Receives the bare\n * query in both modes — the slash-mode \"/\" prefix is stripped.\n */\n filter?: (items: readonly PickerItem[], query: string) => readonly PickerItem[]\n /** Overrides for the picker chrome's user-facing strings. */\n labels?: Partial<BlockInsertPickerLabels>\n /**\n * Notified after each successful insert — picker selection or input rule —\n * alongside the built-in open-for-editing behavior.\n */\n onInsert?: (event: PickerInsertEvent) => void\n /**\n * Whether an inserted block opens for editing (default true). Items can\n * override per member via their metadata's `openOnInsert`.\n */\n openOnInsert?: boolean\n /** Whether Cmd/Ctrl+/ opens the picker (default true). */\n shortcut?: boolean\n /**\n * Whether well-known block types (image, file, code, table, ...) get\n * default triggers and keywords when `items` doesn't cover them\n * (default true). See {@link standardBlockPresets}.\n */\n presets?: boolean\n /**\n * Escape hatch: names the portable-text array type to resolve items\n * against when Studio's member-schema context is unavailable. Normally\n * unnecessary — the containing array and its members are detected\n * automatically from where the plugin is mounted.\n */\n arrayTypeName?: string\n}\n\n/**\n * Creates the Portable Text editor plugins component for the block-insert\n * picker: a slash-command / Cmd+/ menu of the blocks the host array allows,\n * plus optional markdown input rules. Zero-config is the intended call —\n * items derive from the array the plugin is mounted on. Attach it to the\n * array type's `components.portableText.plugins`:\n *\n * ```ts\n * defineType({\n * name: 'content',\n * type: 'array',\n * of: [{type: 'block'}, {type: 'callout'}],\n * components: {\n * portableText: {\n * plugins: blockInsertPicker(),\n * },\n * },\n * })\n * ```\n */\nexport function blockInsertPicker(\n config: BlockInsertPickerConfig = {},\n): ComponentType<PortableTextPluginsProps> {\n const {\n arrayTypeName,\n filter,\n inputRules,\n items,\n labels,\n onInsert,\n openOnInsert,\n presets,\n resolveItems,\n shortcut,\n } = config\n return function BlockInsertPickerPlugins(props: PortableTextPluginsProps) {\n return (\n <>\n {props.renderDefault(props)}\n {inputRules?.length ? (\n <MarkdownInputRules\n arrayTypeName={arrayTypeName}\n onInsert={onInsert}\n openOnInsert={openOnInsert}\n rules={inputRules}\n />\n ) : null}\n <BlockInsertPicker\n arrayTypeName={arrayTypeName}\n filter={filter}\n items={items}\n labels={labels}\n onInsert={onInsert}\n openOnInsert={openOnInsert}\n presets={presets}\n resolveItems={resolveItems}\n shortcut={shortcut}\n />\n </>\n )\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAQA,SAAgB,cAAc,YAGjB;CACX,IAAM,QAAkB,CAAC,GACrB,UAAsD;CAC1D,OAAO,WAAW,OAAO,QAAQ,QAAS,WAExC,AADA,MAAM,KAAK,QAAQ,IAAI,GACvB,UACE,QAAQ,QAAQ,OAAO,QAAQ,QAAS,YAAY,UAAU,QAAQ,OAEjE,QAAQ,OACT,KAAA;CAER,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,kBACd,SACA,WAA0C,CAAC,GAC7B;CACd,OAAO,QAAQ,YACZ,KAAK,YAAY,gBAAgB;EAChC,IAAM,QAAQ,cAAc,UAAU,GAIhC,WAAW,SAAS,WAAW,UAAU,MAAM,SAAS,WAAW,IAAI,GACvE,OACJ,aAAa,KAAK,SAAS,WAAW,UAAU,MAAM,SAAS,MAAM,IAAI,CAAC,IAAI;EAEhF,OAAO;GACL,OAFY,SAAS,KAAK,KAAA,IAAY,SAAS;GAG/C,MAAM,WAAW;GACjB,MAAM,SAAS,mBAA+B;GAC9C;EACF;CACF,CAAC,CAAC,CACD,QAAQ,EAAC,YAAW,CAAC,OAAO,MAAM,CAAC,CACnC,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,CAAC,CAChE,KAAK,EAAC,OAAO,YAAW;EACvB,QAAQ;GAAC,WAAW;GAAM,MAAM;EAAsB;EACtD,OAAO,OAAO;EACd,aAAa,OAAO;EACpB,OAAO,OAAO;EACd,IAAI;EACJ,UAAU,OAAO;EACjB,cAAc,OAAO;EACrB,OAAO;EACP,SAAS,OAAO;CAClB,EAAE;AACN;;;;;;AAOA,SAAgB,qBACd,SACA,UACU;CACV,IAAM,QAAQ,IAAI,IAAI,QAAQ,YAAY,SAAS,eAAe,cAAc,UAAU,CAAC,CAAC;CAC5F,OAAO,SAAS,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,QAAQ,SAAS,CAAC,MAAM,IAAI,IAAI,CAAC;AAC9E;AChFA,SAAgB,kBACd,OACA,OACuB;CACvB,IAAI,UAAU,MAAM,UAAU,KAAK,OAAO;CAE1C,IAAM,aAAa,MAAM,YAAY,GAG/B,YAAY,WAAW,WAAW,GAAG,IAAI,WAAW,MAAM,CAAC,IAAI;CAErE,OAAO,MAAM,QAAQ,SAAS;EAG5B,IAAM,cAAc,KAAK,SAAS,YAAY,CAAC,CAAC,QAAQ,OAAO,EAAE;EAMjE,OALI,eAAe,cAAc,MAAM,YAAY,WAAW,SAAS,IAAU,KAC7E,cAAc,MAGlB,GAFI,KAAK,MAAM,YAAY,CAAC,CAAC,SAAS,SAAS,KAC3C,KAAK,aAAa,YAAY,CAAC,CAAC,SAAS,SAAS,KAClD,KAAK,UAAU,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,SAAS,SAAS,CAAC;CAEtE,CAAC;AACH;;;;;;;ACVA,SAAgB,gBAAgB,UAA2D;CACzF,OAAO,SAAS,SAAS,YAAY,QAAQ,KAAK;AACpD;;;;;;;;;;;AAYA,SAAgB,iBAAiB,OAA+C;CAC9E,IAAM,QAA2B,CAAC,GAC5B,0BAAU,IAAI,IAAiC;CACrD,KAAK,IAAM,QAAQ,OAAO;EACxB,IAAM,MAAM,KAAK,SAAS,MACtB,SAAS,QAAQ,IAAI,GAAG;EAM5B,AALK,WACH,SAAS,CAAC,GACV,QAAQ,IAAI,KAAK,MAAM,GACvB,MAAM,KAAK,GAAG,IAEhB,OAAO,KAAK,IAAI;CAClB;CACA,OAAO,MAAM,KAAK,SAAS;EAAC,OAAO;EAAK,OAAO,QAAQ,IAAI,GAAG;CAAE,EAAE;AACpE;;;;;;;;ACfA,SAAgB,uBAAuB;CACrC,OAAO,eAIL;EACA,SAAS,EACN,GAAG,EAAC,QAAQ,OAAO,SAAS,gBAAe,CAC1C,GAAI,UAAU,CAAC,MAAM;GAAC,IAAI;GAAS,MAAM;EAAa,CAAC,CAAC,IAAI,CAAC,GAC7D,MAAM;GACJ;GACA;GACA,QAAQ;GACR,MAAM;GAIN,GAAI,SAAS,EAAC,IAAI,OAAM,IAAI,CAAC;EAC/B,CAAC,CACH,CACF;EACA,QAAQ,EAAC,OAAO,eAAc;GAC5B,IAAM,EAAC,gBAAgB,OAAO,SAAQ;GAatC,OAAO;IACL,QAbwB,SAAS,QAAQ,MAAM,MAC9C,cAAc,UAAU,SAAS,cAErB,IACX,wBAAwB;KACtB,SAAS;MACP,QAAQ;OAAC,QAAQ;OAAG,MAAM,CAAC,EAAC,MAAM,eAAc,CAAC;MAAC;MAClD,OAAO;OAAC,QAAQ;OAAG,MAAM,CAAC,EAAC,MAAM,eAAc,CAAC;MAAC;KACnD;KACA;IACF,CAAC,IACD;IAGF;IACA,SAAS,oBAAoB,OAAO,QAAQ;IAC5C,WAAW,SAAS,UAAW,SAAoB;GACrD;EACF;EACA,IAAI;CACN,CAAC;AACH;AAEA,SAAgB,qBAAqB,QAAgB,SAAwC;CAC3F,OAAO,KAAK;EAAC,MAAM;EAAmC,GAAG;CAAO,CAAC;AACnE;;;;;;;AAUA,SAAgB,6BAA6B;CAC3C,OAAO,eAIL;EACA,SAAS,EAAE,GAAG,YAAY,CAAC,MAAM;GAAC,IAAI;GAAS,MAAM;EAAa,CAAC,CAAC,CAAC;EACrE,QAAQ,EAAC,OAAO,eAAc,oBAAoB,OAAO,QAAQ,KAAK;EACtE,IAAI;CACN,CAAC;AACH;AAEA,SAAgB,iBAAiB,QAAgB,SAAoC;CACnF,OAAO,KAAK;EAAC,MAAM;EAAyC,GAAG;CAAO,CAAC;AACzE;;;;;;;;AASA,SAAS,oBACP,OACA,UACiB;CACjB,IAAM,EAAC,gBAAgB,MAAM,UAAS;CACtC,IAAI,SAAS,WAAW,MAAM,WAAW,GAAG,OAAO;CACnD,IAAM,cAAc,SAAS,QAAQ,MAAM,MAAM,cAAc,UAAU,SAAS,cAAc;CAQhG,OANE,CAAC,eACD,CAAC,YAAY,EAAC,QAAQ,SAAS,QAAQ,OAAM,GAAG,WAAW,KAC3D,CAAC,iBAAiB,WAAW,CAAC,CAAC,WAAW,KAAK,IAExC,OAEF,wBAAwB;EAC7B,SAAS;GACP,QAAQ;IAAC,QAAQ;IAAG,MAAM,CAAC,EAAC,MAAM,eAAc,CAAC;GAAC;GAClD,OAAO;IAAC,QAAQ,MAAM;IAAQ,MAAM,CAAC,EAAC,MAAM,eAAc,CAAC;GAAC;EAC9D;EACA;CACF,CAAC;AACH;AChHA,SAAgB,iBAAiB,KAA0B;CACzD,IAAM,WAAW,IAAI,aAAa;CAmBlC,AAhBA,qBAAqB,IAAI,QAAQ;EAC/B,gBAAgB,IAAI;EACpB,OAAO;GAOL,GAAG,IAAI;GACP,MAAM;GACN,OAAO,IAAI;EACb;EACA,MAAM,IAAI;EACV,OAAO,IAAI;CACb,CAAC,GACD,IAAI,gBAAgB,QAAQ;AAC9B;ACFA,MAAM,iBAA0C;CAC9C,gBAAgB;CAChB,eAAe;CACf,cAAc;CACd,gBAAgB;CAChB,qBAAqB;CACrB,aAAa;CACb,WAAW;CACX,sBAAsB;CACtB,OAAO;CACP,kBAAkB;AACpB;AAEA,SAAgB,cACd,WACyB;CACzB,OAAO,YAAY;EAAC,GAAG;EAAgB,GAAG;CAAS,IAAI;AACzD;;AAGA,SAAgB,gBAAgB,QAAiC,OAAuB;CAGtF,OAAO,OAAO,UAAU,QAAQ,iBAAiB,KAAK;AACxD;;AAGA,SAAgB,eACd,QACA,OACA,OACQ;CACR,OAAO,OAAO,qBACX,QAAQ,iBAAiB,OAAO,KAAK,CAAC,CAAC,CACvC,QAAQ,iBAAiB,OAAO,KAAK,CAAC;AAC3C;ACtCA,SAAgB,sBAAsB,eAAmD;eACjF,oBAA8C,WAClD,oCACF,GACM,SAAS,UAAU,GAElBA;CAOa,IAAA,EAAA,OAAA,iBAAA,EAAA,OANd,qBAAA,EAAA,OAM8B,QAAA;;GANlC,IAAI,mBAAmB;IACdC,IAAAA;IAAP,AACiCC,EAAAA,OAAAA,kBAAAA,gBAAAA,EAAAA,OACDC,kBAAAA,gBAFzB,KAAA;KACL,aAAa,kBAAkB;KAC/B,YAAY,kBAAkB;IAChC,GAFiCD,EAAAA,KAAAA,kBAAAA,cACDC,EAAAA,KAAAA,kBAAAA,sCAFhC,KAAOF;IAAP,MAAA;GAIF;GACA,IAAM,YAAY,gBAAgB,OAAO,IAAI,aAAa,IAAI,KAAA;GAC9D,IAAI,CAAC,aAAa,UAAU,aAAa,SAAA;IAAS,KAAO;IAAP,MAAA;;GAClD,KAAO;IACL,aAAa,UAAU,GAAG,OACxBG,OAEF;IACA,YAAY;GACd;;EARkB,EAAA,KAAA,eANd,EAAA,KAAA,mBAM8B,EAAA,KAAA;;CAPpC,OAAOJ;AAiBT;AANQ,SAAAM,QAAC,QAAA;CACC,OAAA,OAAO,aAAa,YAAY,CAAC,kBAAkB,MAAM;;;;;;;;;AAcnE,SAAS,kBAAkB,YAGf;CACV,IAAI,UAA0C;CAC9C,OAAO,QAAQ,QAAQ,OAAO,QAAQ,QAAS,YAAY,UAAU,QAAQ,OAE3E,UAAU,QAAQ;CAEpB,OAAO,QAAQ,SAAS;AAC1B;;;;;;;;;;;;;;AC1DA,SAAgB,uBAAmD;eAC3D,cAAc,WAAW,8BAA8B,GACvD,EAAC,YAAY,uBAAsB,iBAAiB,GACpD,gBAAgB,OAAsB,IAAI,GAEtCC,IASPC;CATH,AAGe,EAAA,OAAA,eAAA,EAAA,OAEX,cAAA,EAAA,OACA,sBANM,WAAM;EACd,IAAM,MAAM,cAAc;EAC1B,IAAI,CAAC,KAAK;EACV,IAAM,OAAO,YAAY,MAAM,MAAM,EAAE,OAAO,QAAQ,GAAG;EACzD,AAAI,SACF,WAAW,KAAK,OAAO,KAAK,IAAI,GAChC,mBAAmB,KAAK,OAAO,KAAK,MAAM,EAAK,GAC/C,cAAc,UAAU;CAE5B,GAAG,KAAA;EAAC;EAAa;EAAY;CAAkB,GANhC,EAAA,KAAA,aAEX,EAAA,KAAA,YACA,EAAA,KAAA,oEANJ,UAAUD,IASPC,EAA6C;CAO7BC,IAAAA;CAAnB,2DAAoB,MAAA,aAAqB;EACvC,cAAc,UAAU;CAC1B,2BAFOC;AAGT;ACZA,SAAgB,qBAAqB,EAAC,UAAU,mBAAmB,YAA+B;CAChG,IAAM,SAAoD,EAAC,OAAO,eAAc;EAC9E,IAAM,QAAQ,SAAS;EAGvB,IAAI,MAAM,SAAS,UAAU;GAC3B,IAAI,MAAM,SAAS,iBAAiB,MAAM,SAAS,KAAK;IACtD,IAAM,QAAQ,kBAAkB,QAAQ;IAIxC,OAHI,CAAC,SAAS,CAAC,qBAAqB,QAAQ,KAAK,mBAAmB,QAAQ,MAAM,KACzE,KAEF;KACL,YAAY;KACZ,QAAQ;MACN,gBAAgB,MAAM,KAAK;MAC3B,MAAM;MACN,OAAO;MACP,MAAM;KACR;IACF;GACF;GACA,IACE,MAAM,SAAS,sBACf,MAAM,YAAY,QAAQ,QACzB,MAAM,YAAY,WAAW,MAAM,YAAY,YAChD,kBAAkB,GAClB;IAKA,IAAM,QAAQ,cAAc,QAAQ,GAC9B,WAAW,cAAc,QAAQ,GACjC,cAAc,SAAS;IAE7B,OADK,cACE;KACL,YAAY;KACZ,QAAQ;MACN,gBAAgB,YAAY,KAAK;MACjC,MAAM;MACN,OAAO;MACP,MAAM;KACR;IACF,IATyB;GAU3B;GACA,OAAO;EACT;EAGA,IAAI,MAAM,SAAS,eAgBjB,OAfI,MAAM,SAAS,UACb,MAAM,SAAS,MAEV;GAAC,YAAY;GAAO,QAAQ,EAAC,MAAM,QAAO;EAAC,IAI7C;GACL,YAAY;GACZ,QAAQ;IAAC,OAAO,MAAM,QAAQ,MAAM;IAAM,MAAM;GAAa;EAC/D,IAKK;GACL,YAAY;GACZ,QAAQ;IAAC,OAAO,MAAM,QAAQ,MAAM;IAAM,MAAM;GAAa;EAC/D;EAGF,IAAI,MAAM,SAAS,mBAwBjB,OAvBI,MAAM,SAAS,aAEb,MAAM,MAAM,WAAW,IAClB;GAAC,YAAY;GAAM,QAAQ,EAAC,MAAM,QAAO;EAAC,IAE5C;GACL,YAAY;GACZ,QAAQ;IAEN,OAAO,MAAM,SAAS,cAAc,MAAM,MAAM,MAAM,GAAG,EAAE,IAAI;IAC/D,MAAM;GACR;EACF,IAKE,MAAM,SAAS,eAAe,MAAM,MAAM,SAAS,IAC9C;GACL,YAAY;GACZ,QAAQ;IAAC,OAAO,MAAM,MAAM,MAAM,GAAG,EAAE;IAAG,MAAM;GAAa;EAC/D,IAEK;GAAC,YAAY;GAAO,QAAQ,EAAC,MAAM,QAAO;EAAC;EAGpD,IAAI,MAAM,SAAS,kBAGjB,OAAO;GAAC,YAAY;GAAO,QAAQ,EAAC,MAAM,QAAO;EAAC;EAGpD,IAAI,MAAM,SAAS,oBAAoB;GACrC,IAAM,MAAM,MAAM,YAAY;GAC9B,IAAI,QAAQ,aACV,OAAO;IAAC,YAAY;IAAM,QAAQ;KAAC,OAAO;KAAG,MAAM;IAAU;GAAC;GAEhE,IAAI,QAAQ,WACV,OAAO;IAAC,YAAY;IAAM,QAAQ;KAAC,OAAO;KAAI,MAAM;IAAU;GAAC;GAEjE,IAAI,QAAQ,SACV,OAAO;IAAC,YAAY;IAAM,QAAQ,EAAC,MAAM,SAAQ;GAAC;GAEpD,IAAI,QAAQ,UACV,OAAO;IAAC,YAAY;IAAM,QAAQ,EAAC,MAAM,QAAO;GAAC;GAEnD,IACE,QAAQ,eACR,QAAQ,gBACR,QAAQ,UACR,QAAQ,SACR,QAAQ,YACR,QAAQ,YAIR,OAAO;IAAC,YAAY;IAAO,QAAQ,EAAC,MAAM,QAAO;GAAC;EAEtD;EAEA,OAAO;CACT;CAEA,OAAO,eAAe;EACpB,SAAS,EACN,SAAS,kBAAkB;GAG1B,IAAM,EAAC,YAAY,WAAU,eACvB,aAAa,aAAa,SAAS,MAAM,CAAC;GAOhD,OANI,aAEK,CAAC,UAAU,IAIb,CAAC,YAAY,QAAQ,QAAQ,KAAK,CAAC;EAC5C,CACF;EACA;EACA,IAAI;CACN,CAAC;AACH;ACrLA,SAAgB,cAAc,OAAoB,QAAmC;CACnF,QAAQ,OAAO,MAAf;EACE,KAAK;EACL,KAAK,UACH,OAAO,EAAC,MAAM,SAAQ;EACxB,KAAK,YAEH,OADI,MAAM,SAAS,WAAiB,QAC7B;GACL,GAAG;GACH,kBAAkB,MAAM,mBAAmB,OAAO;EACpD;EACF,KAAK,QAEH,OADI,MAAM,SAAS,WACZ;GACL,gBAAgB,OAAO;GACvB,kBAAkB;GAClB,MAAM,OAAO;GACb,OAAO,OAAO;EAChB,IANoC;EAOtC,KAAK,uBAIH,OAHI,MAAM,SAAS,WAAiB,QAG7B;GAAC,GAAG;GAAO,kBAAkB,OAAO;EAAK;EAClD,KAAK,eAEH,OADI,MAAM,SAAS,WAAiB,QAC7B;GAAC,GAAG;GAAO,kBAAkB;GAAG,OAAO,OAAO;EAAK;EAC5D,SAIE,OAAOE;CAEX;AACF;;;;;ACUA,SAAgB,yBAAyB,EACvC,mBACA,cACA,YACA,SAIc;CACd,OAAO,MACJ,QAAQ,WAAW,kBAAkB,IAAI,OAAO,SAAS,CAAC,CAAC,CAC3D,KAAK,WAAW,iBAAiB,QAAQ,cAAc,UAAU,CAAC;AACvE;AAKA,MAAa,qBAAqB,eACrB,qBAAqB;;AAalC,SAAgB,eAAe,SAGH;CAC1B,OAAO;EACL,WAAW,QAAQ;EACnB,aAAa,EAAC,mBAAkB,QAAQ,cAAc,EAAC,aAAY,CAAC,KAAK,CAAC;EAC1E,SAAS;CACX;AACF;;;;;;;;AASA,SAAgB,cAAc,SASF;CAC1B,IAAM,YAAY,QAAQ,aAAa,CAAC;CACxC,OAAO;EACL,WAAW,QAAQ;EACnB,aAAa,EAAC,cAAc,gBAAe;GACzC,IAAM,WAAW,uBACf,uBAAuB,SAAS,GAChC,WACA,QAAQ,eACV,GACM,WAAW,WACb,UAAU,MAAM,UAAU,MAAM,UAAU,QAAQ,CAAC,EAAE,WACrD,KAAA;GACJ,OAAO,QAAQ,YAAY;IAAC;IAAU;IAAc;GAAQ,CAAC;EAC/D;EACA,SAAS;CACX;AACF;;AAGA,SAAgB,uBAAuB,WAA2B;CAChE,OAAO,UAAU,QAAQ,SAAS,EAAE,CAAC,CAAC,KAAK;AAC7C;;;;;;AAOA,SAAgB,uBACd,KACA,WACA,iBACoB;CACpB,IAAM,QAAQ,IAAI,KAAK,GACjB,QAAQ,MAAM,YAAY;CAChC,IAAI,CAAC,OAAO,OAAO;CAInB,KAAK,IAAM,EAAC,SAAS,WAAU,WAC7B,IAAI,MAAM,YAAY,MAAM,SAAS,SAAS,MAAM,UAAU,MAAM,YAAY,MAAM,KAAK,GACzF,OAAO;CAIX,OAAO;AACT;AAEA,SAAS,iBACP,QACA,cACA,YACW;CACX,OAAO,gBAAgB;EACrB,SAAS,EACN,EAAC,OAAO,eAAc;GACrB,IAAM,QAAQ,MAAM,QAAQ;GAC5B,IAAI,CAAC,OAAO,OAAO,CAAC;GACpB,IAAM,QAAyB;IAI7B,GAAG,OAAO,WAAW;KAAC;KAAc,WAAW,MAAM;IAAI,CAAC;IAC1D,MAAM,aAAa;IACnB,OAAO,OAAO;GAChB,GAMM,KACJ,wBAAwB;IACtB,SAAS;KACP,QAAQ,MAAM,cAAc;KAC5B,OAAO,MAAM,cAAc;IAC7B;IACA;GACF,CAAC,KAAK,MAAM;GACd,OAAO;IAGL,MAAM;KAAC;KAAI,MAAM;IAAa,CAAC;IAC/B,MAAM;KACJ;KACA,WAAW;KACX,QAAQ;KACR,MAAM;IACR,CAAC;IACD,aAAa,WAAW,KAAK,CAAC;GAChC;EACF,CACF;EACA,IAAI,OAAO;CACb,CAAC;AACH;;;;;;;;;;;AC1LA,MAAa,uBAAsD;CACjE;EAAC,UAAU;GAAC;GAAS;GAAW;GAAU;EAAO;EAAG,SAAS;EAAU,MAAM;CAAO;CACpF;EAAC,UAAU;GAAC;GAAc;GAAY;EAAU;EAAG,SAAS;EAAS,MAAM;CAAM;CACjF;EAAC,UAAU;GAAC;GAAW;GAAU;GAAS;EAAK;EAAG,SAAS;EAAS,MAAM;CAAM;CAChF;EAAC,UAAU;GAAC;GAAQ;GAAQ;GAAW;EAAa;EAAG,SAAS;EAAU,MAAM;CAAO;CACvF;EAAC,UAAU,CAAC,UAAU,SAAS;EAAG,SAAS;EAAU,MAAM;CAAO;CAClE;EAAC,UAAU;GAAC;GAAW;GAAY;GAAQ;GAAO;EAAO;EAAG,SAAS;EAAU,MAAM;CAAO;CAC5F;EAAC,UAAU;GAAC;GAAS;GAAU;EAAK;EAAG,SAAS;EAAU,MAAM;CAAW;CAC3E;EAAC,UAAU;GAAC;GAAS;GAAU;EAAO;EAAG,SAAS;EAAU,MAAM;CAAc;AAClF,GAWa,sBAA0D,CACrE,cAAc;CACZ,WAAW;CACX,cAAc,EAAC,eAAe,WAAW,EAAC,SAAQ,IAAI,CAAC;AACzD,CAAC,CACH;;;;;;;;AASA,SAAgB,oBACd,OACA,SACA,UAAyC,sBAC3B;CACd,IAAM,eAAe,IAAI,IAAI,QAAQ,KAAK,WAAW,CAAC,OAAO,MAAM,MAAM,CAAC,CAAC;CAC3E,OAAO,MAAM,KAAK,SAAS;EAEzB,IADI,KAAK,OAAO,SAAS,iBACrB,KAAK,YAAY,KAAA,KAAa,KAAK,aAAa,KAAA,GAAW,OAAO;EACtE,IAAM,YAAY,KAAK,OAAO,WACxB,aAAa,QAAQ,YAAY,MAAM,cAAc,UAAU,SAAS,SAAS;EACvF,IAAI,CAAC,YAAY,OAAO;EACxB,IAAM,SAAS,cAAc,UAAU,CAAC,CACrC,KAAK,SAAS,aAAa,IAAI,IAAI,CAAC,CAAC,CACrC,KAAK,OAAO;EAEf,OADK,SACE;GACL,GAAG;GACH,UAAU,KAAK,YAAY,OAAO;GAClC,SAAS,KAAK,WAAW,OAAO;EAClC,IALoB;CAMtB,CAAC;AACH;;;;;;;;;ACzCA,SAAgB,wBACd,MACA,YACkB;CAQlB,OAPK,aAOE;EACL,aAAa,KAAK,eAAe,WAAW;EAC5C,MAAM,WAAW,QAAQ,WAAW,MAAM,QAAQ,WAAW,KAAK,EAAE,EAAE,QAAQ;EAC9E,OAAO,WAAW,SAAS,WAAW,WAAW,IAAI;CACvD,IAVS;EACL,aAAa,KAAK;EAClB,MAAM,KAAK,QAAQ;EACnB,OAAO,KAAK,UAAU,KAAK,OAAO,SAAS,gBAAgB,KAAK,OAAO,YAAY,KAAK;CAC1F;AAOJ;AAIA,SAAS,WAAW,OAAuB;CACzC,OAAO,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,MAAM,MAAM,CAAC;AACtD;AC6BA,SAAgB,kBAAkB,EAChC,eACA,QACA,OACA,QAAQ,gBACR,UACA,eAAe,IACf,UAAU,IACV,cACA,WAAW,MACc;CACzB,IAAM,SAAS,UAAU,GACnB,oBAAoB,qBAAqB,GACzC,eAAe,sBAAsB,aAAa,GAClD,SAAS,UAAU,GACnB,6BAA6B,8BAA8B,GAC3D,QAAQ,SAAS,GACjB,SAAS,cAAc,cAAc,cAAc,GAAG,CAAC,cAAc,CAAC,GAGtE,aAAa,MAAM,GACnB,YAAY,GAAG,WAAW,WAC1B,YAAY,OAAe,GAAG,WAAW,UAAU,MAWnD,cAAc,cAAc;EAChC,IAAI,CAAC,cAAc,OAAO,CAAC;EAI3B,IAAM,aAHU,UACZ,oBAAoB,kBAAkB,cAAc,KAAK,GAAG,YAAY,IACxE,kBAAkB,cAAc,KAAK,EAAA,CACf,KAAK,SAAS;GACtC,IAAI,KAAK,OAAO,SAAS,eAAe,OAAO;GAE/C,IAAM,eAAe,wBAAwB,MAD1B,cAAc,cAAc,QAAQ,KAAK,OAAO,SAChB,CAAU;GAG7D,OAAO,OAAO,OAAO,MAAM;IACzB,aAAa,aAAa;IAC1B,MAAM,aAAa;IACnB,OAAO,aAAa;GACtB,CAAC;EACH,CAAC;EACD,OAAO,eAAe,CAAC,GAAG,aAAa,WAAW,YAAY,CAAC,IAAI;CACrE,GAAG;EAAC;EAAO;EAAc;EAAS;EAAc;CAAM,CAAC,GAEjD,WAAW,YAAY,SAAS,GAMhC,YAAY,OAAgE,CAAC,CAAC;CACpF,gBAAgB;EACd,IAAI,QAAQ,IAAIuB,aAAgB,cAAc;EAC9C,IAAM,SAAS,UAAU;EACzB,IAAI,CAAC,gBAAgB,CAAC,OAAO,SAE3B,AADA,OAAO,UAAU,IACjB,QAAQ,KACN,sKAEF;OAGF;OAAI,gBAAgB,SAAS,CAAC,OAAO,SAAS;IAC5C,IAAM,UAAU,qBAAqB,cAAc,KAAK;IACxD,AAAI,QAAQ,SAAS,MACnB,OAAO,UAAU,IACjB,QAAQ,KACN,yGAC4B,QAAQ,KAAK,IAAI,GAC/C;GAEJ;GACA,AAAI,gBAAgB,CAAC,YAAY,CAAC,OAAO,UACvC,OAAO,QAAQ,IACf,QAAQ,KACN,qKAEF;EANF;CAQF,GAAG;EAAC;EAAU;EAAO;CAAY,CAAC;CAElC,IAAM,CAAC,OAAO,YAAY,WAAW,eAAe,EAAC,MAAM,SAAQ,CAAC,GAM9D,WAAW,OAAoB,KAAK;CAC1C,sBAAsB;EACpB,SAAS,UAAU;CACrB,CAAC;CAKD,IAAM,cAAc,OAAO,QAAQ;CACnC,sBAAsB;EACpB,YAAY,UAAU;CACxB,CAAC;CAED,IAAM,OAAO,MAAM,SAAS,UACtB,QAAQ,OAAO,MAAM,QAAQ,IAC7B,OAAO,OAAO,MAAM,OAAO,MAE3B,cAAc,UAAU,mBAIxB,cAAc,SAAS,UAAU,UAAU,KAAK,IAAI,OAIpD,WAAW,cACT,iBAAiB,YAAY,aAAa,WAAW,CAAC,GAC5D;EAAC;EAAa;EAAa;CAAW,CACxC,GACM,WAAW,cAAc,gBAAgB,QAAQ,GAAG,CAAC,QAAQ,CAAC,GAI9D,iBAAiB,cAAc;EACnC,IAAM,UAAoB,CAAC,GACvB,QAAQ;EACZ,KAAK,IAAM,WAAW,UAEpB,AADA,QAAQ,KAAK,KAAK,GAClB,SAAS,QAAQ,MAAM;EAEzB,OAAO;CACT,GAAG,CAAC,QAAQ,CAAC,GAGP,mBAAmB,SAAS,SAAS,GAIrC,oBAAoB,OAAO,EAAK,GAWhC,oBAAoB,OACxB,SACA,QACG;EACH,IAAI,kBAAkB,SAIpB,SAAS,EAAC,MAAM,QAAO,CAAC;OAG1B;qBAAkB,UAAU;GAC5B,IAAI;IAGF,IAAM,UAAU,gBACd,iBACE,YACE,aACA,QAAQ,SAAS,UAAU,UAAU,QAAQ,KAAK,IAAI,QAAQ,KAChE,CACF,CACF,GAEM,SAAS,QADC,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,SAAS,GAAG,GAAG,CACrC;IAIvB,IADA,SAAS,EAAC,MAAM,QAAO,CAAC,GACpB,CAAC,QAAQ;IACb,IAAI,OAAO,OAAO,SAAS,UAAU;KAQnC,AALA,iBAAiB,QAAQ;MACvB,gBAAgB,QAAQ;MACxB,MAAM,QAAQ;MACd,OAAO,QAAQ;KACjB,CAAC,GACD,OAAO,OAAO,SAAS;MACrB,mBAAmB,SAAS,EAAC,MAAM,QAAO,CAAC;MAC3C;KACF,CAAC;KACD;IACF;IACA,IAAM,YAAY,OAAO,OAAO,WAC5B,cACE,aAAa,cAAc,cAAc,QAAQ,SAAS;IAChE,AAAI,eACF,eAAgB,MAAM,2BAA2B,YAAY,CAAC,CAAC,KAAM,KAAA;IAMvE,IAAM,WAAW,OAAO,YAAY;IAMpC,IAAI,CAHsB,SAAS,QAAQ,MAAM,MAC9C,MAAM,EAAE,SAAS,QAAQ,cAEvB,GAAmB;KACtB,MAAM,KAAK;MACT,UAAU;MACV,aAAa,OAAO;MACpB,QAAQ;MACR,OAAO,OAAO;KAChB,CAAC;KACD;IACF;IACA,iBAAiB;KACf,gBAAgB,QAAQ;KACxB;KACA;KACA;KACA,oBAAoB,SAAS,QAAQ,aAAa;KAClD,MAAM,QAAQ;KACd,gBAAgB,QAAQ;MAEtB,CADI,OAAO,gBAAgB,iBAAc,kBAAkB,GAAG,GAC9D,WAAW;OACT,UAAU;OACV;OACA,MAAM,QAAQ;OACd,OAAO,QAAQ;OACf,KAAK;MACP,CAAC;KACH;KACA,OAAO,QAAQ;IACjB,CAAC;GACH,SAAS,OAAO;IAKd,MAAM,KAAK;KACT,UAAU;KACV,aAAa,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;KAClE,QAAQ;KACR,OAAO,OAAO;IAChB,CAAC;GAEH,UAAU;IACR,kBAAkB,UAAU;GAC9B;EA1F4B;CA2F9B,GAMM,qBAAqB,QAAgB;EACzC,IAAM,UAAU,SAAS;EACzB,AAAI,QAAQ,SAAS,YACrB,kBAAuB,SAAS,GAAG;CACrC,GAEM,gBAAgB,WAAyB;EAC7C,IAAI,OAAO,SAAS,YAAY;GAM9B,IAAM,UAAU,SAAS;GACzB,IAAI,QAAQ,SAAS,UAAU;GAC/B,IAAM,YAAY,SAAS,SAAS,GAC9B,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,WAAW,QAAQ,gBAAgB,CAAC,GAChE,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,WAAW,OAAO,OAAO,KAAK,CAAC;GACjE,SAAS;IAAC,OAAO;IAAM,MAAM;GAAqB,CAAC;GACnD;EACF;EACA,IAAI,OAAO,SAAS,UAAU;GAC5B,SAAS,MAAM;GACf;EACF;EAEA,IAAM,UAAU,SAAS;EACzB,AAAI,QAAQ,SAAS,YACrB,kBAAuB,SAAS,QAAQ,gBAAgB;CAC1D,GAKM,kBAAkB,OAAuC,YAAY;CAmC3E,AAlCA,sBAAsB;EACpB,gBAAgB,UAAU;CAC5B,CAAC,GAQD,gBAAgB;EACd,IAAI,CAAC,UAAU;EACf,IAAM,WAAW,qBAAqB;GACpC,gBAAgB,SAAS;GACzB,yBAAyB,YAAY;GACrC,WAAW,WAAW,gBAAgB,QAAQ,MAAM;EACtD,CAAC,GACK,mBAAmB,OAAO,iBAAiB,EAAC,SAAQ,CAAC,GACrD,mBAAmB,OAAO,iBAAiB,EAC/C,UAAU,qBAAqB,EACjC,CAAC,GACK,oBAAoB,OAAO,iBAAiB,EAChD,UAAU,2BAA2B,EACvC,CAAC;EACD,aAAa;GAGX,AAFA,iBAAiB,GACjB,iBAAiB,GACjB,kBAAkB;EACpB;CACF,GAAG,CAAC,QAAQ,QAAQ,CAAC,GAKrB,gBAAgB;EACd,AAAI,CAAC,YAAY,SAAS,QAAQ,SAAS,YAAU,SAAS,EAAC,MAAM,QAAO,CAAC;CAC/E,GAAG,CAAC,QAAQ,CAAC;CAIb,IAAM,mBAAmB,OACrB,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,SAAS,GAAG,MAAM,gBAAgB,CAAC,IACjE,GAKE,kBAAkB,OAAO,SAAS,oBAAoB,KAAA,GACtD,eAAe,kBACjB;EACE,gBAAgB;EAChB,gBAAgB;EAChB,eAAe,QAAQ,mBAAmB,GAAG,SAAS,MAAM;CAC9D,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,IAAI,IACZ,IAUE,CAAC,YAAY,iBAAiB,SAAyB,IAAI;CAEjE,sBAAsB;EACpB,IAAI,CAAC,MAAM;GAET,cAAc,IAAI;GAClB;EACF;EACA,IAAM,WAAW,OAAO,YAAY,GAC9B,OAAO,OAAO,IAAI,iBAAiB,QAAQ;EACjD,IAAI,SAAS,KAAK,KAAK,KAAK,KAAK,KAAK,SAAS,KAAK,SAAS;GAC3D,cAAc,IAAI;GAClB;EACF;EACA,IAAM,YAAY,OAAO,IAAI,qBAAqB,QAAQ,CAAC,EAAE,sBAAsB;EACnF,AAAI,aAAW,cAAc,SAAS;CACxC,GAAG,CAAC,QAAQ,IAAI,CAAC;CAMjB,IAAM,oBAAoB,OAA8B,IAAI;CAE5D,gBAAgB;EACd,IAAI,CAAC,MAAM;EACX,IAAM,qBAAqB,UAAwB;GACjD,IAAM,SAAS,MAAM;GACrB,AAAI,kBAAkB,QAAQ,kBAAkB,SAAS,SAAS,MAAM,KACxE,SAAS,EAAC,MAAM,QAAO,CAAC;EAC1B,GAIM,gBAAgB,OAAO,IAAI,iBAAiB,GAC5C,kBAAkB,UAAiB;GACvC,IAAM,OAAO,iBAAiB,aAAa,MAAM,gBAAgB;GACjE,AACE,gBAAgB,SACf,eAAe,SAAS,IAAI,KAAK,kBAAkB,SAAS,SAAS,IAAI,MAG5E,SAAS,EAAC,MAAM,QAAO,CAAC;EAC1B;EAGA,OAFA,SAAS,iBAAiB,eAAe,mBAAmB,EAAI,GAChE,eAAe,iBAAiB,YAAY,cAAc,SAC7C;GAEX,AADA,SAAS,oBAAoB,eAAe,mBAAmB,EAAI,GACnE,eAAe,oBAAoB,YAAY,cAAc;EAC/D;CACF,GAAG,CAAC,QAAQ,IAAI,CAAC;CAGjB,IAAM,oBAAoB,OAA8B,IAAI,GAStD,iBAAiB,OAAsC,IAAI,GAE3D,0BAA0B,KAAa,GAAW,MAAc;EACpE,IAAM,OAAO,eAAe;EAE5B,IADA,eAAe,UAAU;GAAC;GAAG;EAAC,GAC1B,CAAC,QAAS,KAAK,MAAM,KAAK,KAAK,MAAM,GAAI;EAC7C,IAAM,UAAU,SAAS;EACzB,AAAI,QAAQ,SAAS,YAAY,QAAQ,QAAQ,oBAC/C,SAAS;GAAC,OAAO;GAAK,MAAM;EAAqB,CAAC;CAEtD;CAEA,gBAAgB;EACd,kBAAkB,SAAS,eAAe,EAAC,OAAO,UAAS,CAAC;CAE9D,GAAG,CAAC,gBAAgB,CAAC;CAIrB,IAAM,gBAAgB,cACf,aAIE,EACL,6BAA6B,WAE/B,IAPwB,MAQvB,CAAC,UAAU,CAAC;CAmCf,OAjCK,OAED,SAAS,WAAW,IAEpB,oBAAC,SAAD;EACE,eAAA;EACA,SACE,oBAAC,MAAD;GACE,aAAU;GACV,aAAa;GACb,SAAS;GACT,QAAQ;GACR,KAAK;GAIL,MAAK;GAEL,UAAA,oBAAC,MAAD;IAAM,OAAA;IAAM,MAAM;IACf,UAAA,gBAAgB,QAAQ,KAAK;GAC1B,CAAA;EACF,CAAA;EAER,oBAAoB,CAAC,WAAW;EAChC,MAAA;EACA,WAAU;EACV,QAAA;EACA,iBAAA;EACA,kBAAkB;CACnB,CAAA,IAKH,oBAAC,SAAD;EACE,eAAA;EACA,SACE,qBAAC,MAAD;GACE,aAAa;GACb,SAAS;GACT,QAAQ;GACR,KAAK;GACL,OAAO;IACL,SAAS;IACT,eAAe;IACf,WAAW;IACX,UAAU;GACZ;GAVF,UAAA;IAeE,oBAAC,UAAD;KAAQ,aAAU;KAAS,OAAO;KAC/B,UAAA;IACK,CAAA;IACR,qBAAC,MAAD;KAAM,OAAM;KAAS,MAAK;KAAO,KAAK;KAAG,SAAS;KAAlD,UAAA,CACE,oBAAC,KAAD;MAAK,MAAM;MACT,UAAA,oBAAC,MAAD;OACE,OAAA;OACA,MAAM;OACN,OAAO;QAAC,eAAe;QAAU,eAAe;OAAW;OAC3D,QAAO;OAEN,UAAA,OAAO;MACJ,CAAA;KACH,CAAA,GACJ,SAAS,cAAc,UAAU,KAGhC,oBAAC,MAAD;MAAM,OAAA;MAAM,MAAM;MAAG,cAAa;MAC/B,UAAA;KACG,CAAA,IACJ,IACA;;IACN,oBAAC,KAAD;KACE,cAAY,OAAO;KACnB,IAAI;KAIJ,MAAK;KACL,OAAO;MAAC,MAAM;MAAG,WAAW;MAAK,WAAW;MAAG,WAAW;KAAM;KAE/D,UAAA,SAAS,KAAK,SAAS,iBAAiB;MACvC,IAAM,eAAe,QAAQ,SAAS,OAAO,kBAIvC,aAAa,QAAQ,UAAU,OAAO,cAAc,SAAS,QAAQ,SACrE,OAAO,QAAQ,MAAM,KAAK,MAAM,aAAa;OACjD,IAAM,OAAO,eAAe,iBAAiB,KAAK,UAC5C,OAAO,KAAK,MACZ,gBAAgB,QAAQ;OAC9B,OACE,oBAAC,MAAD;QACE,iBAAe;QACf,IAAI,SAAS,OAAO,GAAG,CAAC;QAExB,eAAe,kBAAkB,GAAG;QACpC,cAAc,UACZ,uBAAuB,KAAK,MAAM,SAAS,MAAM,OAAO;QAE1D,SAAS;QACT,QAAQ;QACR,KAAK,gBAAgB,oBAAoB,KAAA;QAIzC,MAAK;QACL,OAAO,EAAC,QAAQ,UAAS;QAGzB,UAAU;QACV,MAAM,gBAAgB,YAAY;QAElC,UAAA,qBAAC,MAAD;SAAM,OAAM;SAAS,KAAK;SAA1B,UAAA;UACE,oBAAC,KAAD;WACE,OAAO;YACL,YAAY;YACZ,SAAS;YACT,QAAQ;YACR,gBAAgB;YAChB,OAAO;WACT;WAEC,UAAA,OAAO,oBAAC,MAAD,CAAO,CAAA,IAAI;UAChB,CAAA;UACL,qBAAC,OAAD;WAAO,MAAM;WAAG,KAAK;WAArB,UAAA,CACE,oBAAC,MAAD;YAAM,MAAM;YAAG,cAAa;YACzB,UAAA,KAAK;WACF,CAAA,GACL,KAAK,cACJ,oBAAC,MAAD;YAAM,OAAA;YAAM,MAAM;YAAG,cAAa;YAC/B,UAAA,KAAK;WACF,CAAA,IACJ,IACC;;UACN,KAAK,QACJ,oBAAC,KAAD;WAAK,MAAK;WACR,UAAA,oBAAC,MAAD;YAAM,OAAA;YAAM,MAAM;YACf,UAAA,KAAK;WACF,CAAA;UACH,CAAA,IACH;UACH,KAAK,UACJ,oBAAC,KAAD;WAAK,MAAK;WACR,UAAA,oBAAC,SAAD;YAAS,UAAU;YAAG,MAAM,CAAC,KAAK,OAAO;WAAI,CAAA;UAC1C,CAAA,IACH;SACA;;OACF,GArDC,GAAG,KAAK,GAAG,GAAG,KAqDf;MAEV,CAAC;MAQD,OAPK,mBAQH,qBAAC,KAAD;OACE,cAAY;OAEZ,YAAY,iBAAiB,IAAI,IAAI;OAKrC,MAAK;OARP,UAAA,CAUE,oBAAC,KAAD;QAAK,eAAe;QAAG,YAAY;QAAG,UAAU;QAC9C,UAAA,oBAAC,MAAD;SACE,OAAA;SACA,MAAM;SACN,OAAO;UACL,eAAe;UACf,eAAe;SACjB;SACA,QAAO;SAEN,UAAA;QACG,CAAA;OACH,CAAA,GACL,oBAAC,OAAD;QAAO,KAAK;QAAI,UAAA;OAAY,CAAA,CACzB;MAtBE,GAAA,UAsBF,IA9BH,oBAAC,OAAD;OAAwB,KAAK;OAC1B,UAAA;MACI,GAFK,UAEL;KA8Bb,CAAC;IACE,CAAA;IACL,oBAAC,MAAD;KAAM,WAAA;KAAU,MAAK;KAAO,SAAS;KAAG,QAAQ;KAC9C,UAAA,qBAAC,MAAD;MAAM,OAAM;MAAS,KAAK;MAAG,MAAK;MAAlC,UAAA;OACE,oBAAC,YAAD;QAAY,MAAM,CAAC,KAAK,GAAG;QAAG,OAAO,OAAO;OAAiB,CAAA;OAC7D,oBAAC,YAAD;QAAY,MAAM,CAAC,GAAG;QAAG,OAAO,OAAO;OAAe,CAAA;OACtD,oBAAC,YAAD;QAAY,MAAM,CAAC,KAAK;QAAG,OAAO,OAAO;OAAgB,CAAA;OACxD,WACC,oBAAC,YAAD;QAAY,MAAM,CAAC,eAAe,GAAG;QAAG,OAAO,OAAO;OAAiB,CAAA,IACrE;MACA;;IACF,CAAA;GACF;;EAER,oBAAoB,CAAC,WAAW;EAChC,MAAA;EACA,WAAU;EACV,QAAA;EACA,iBAAA;EACA,kBAAkB;CACnB,CAAA,IA/Me;AAiNpB;AAGA,SAAS,WAAA,IAA2D;eAAhD,EAAC,MAAM,UAAPtB,IAGdC;CAA4B,AAAA,EAAA,OAAA,oBAA5B,KAAA,oBAAC,SAAD;EAAS,UAAU;EAAS;CAAO,CAAA,GAAP,EAAA,KAAA;CAC5BC,IAAAA;CACG,AAAA,EAAA,OAAA,qBADH,KAAA,oBAAC,MAAD;EAAM,OAAA;EAAM,MAAM;EACf,UAAA;CACG,CAAA,GADH,EAAA,KAAA;CAHLC,IAAAA;CADF,OAEIF,EAAAA,OAAAA,MAAAA,EAAAA,OACAC,MAFF,KAAA,qBAAC,MAAD;EAAM,OAAM;EAAS,KAAK;EAA1B,UAAA,CACED,IACAC,EAGI;KAJJD,EAAAA,KAAAA,IACAC,EAAAA,KAAAA,4BAFFC;AAOJ;AAOA,SAAS,cACP,cACA,QACA,WACwB;CAExB,OADe,cAAc,YAAY,MAAM,cAAc,UAAU,SAAS,SAAS,KACxE,OAAO,IAAI,SAAS;AACvC;AAIA,SAAS,UAAU,OAAuB;CACxC,OAAO,MAAM,WAAW,GAAG,IAAI,MAAM,MAAM,CAAC,IAAI;AAClD;AAKA,SAAS,kBAAkB,OAAqC;CAC9D,MAAM,eAAe;AACvB;AAIA,MAAM,UAAyB;CAC7B,QAAQ;CACR,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,SAAS;CACT,UAAU;CACV,YAAY;CACZ,OAAO;AACT,GAQM,gBAFJ,OAAO,YAAc,OACrB,wBAAwB,KAAK,UAAU,YAAY,UAAU,aAAa,EAAE,IACpC,MAAM;;;;;;ACxwBhD,SAAgB,mBAAA,IAKY;gBALO,EACjC,eACA,UACA,cAAA,IACA,UAJiCE,IAGjC,eAAA,OAAA,KAAA,KAAAC,IAGM,SAAS,UAAU,GACnB,eAAe,sBAAsB,aAAa,GAClD,oBAAoB,qBAAqB,GAUzBC;CAAcC,AAAAA,EAAAA,OAAAA,cAAAA,2BAAd,KAAA,cAAc,eAAe,CAAC,GAAhBA,EAAAA,KAAAA,cAAAA;CAAlC,IAAM,cAAcD,IACbE;CAEH,IAAA,EAAA,OAAA,eAAA,EAAA,OAFG,OAAA;EAAcC,IAAAA;EAEjB,EAAA,OAAA,2BAFiB,MAAC,SAAS;GAC7B,IAAM,SACJ,YAAY,MAAM,gBAAcC,YAAU,SAAS,KAAK,SAAS,KACjE,YAAY,MAAM,cAAc,cAAc,SAAS,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC;GAEnF,OADK,SACE,CAAC,KAAK,cAAc,OAAO,OAAO,OAAO;IAAC,GAAG;IAAM,WAAW,OAAO;GAAI,CAAC,IAD7D,CAAC;EAEvB,GAJI,EAAA,KAAA,yBAFG,KAAA,MAAM,QAAQD,EAMpB,GAJG,EAAA,KAAA,aAFG,EAAA,KAAA;;CAFT,IAAM,gBAAgBE,IAiBhB,gBAAgB,OAAuDC,KAAQ,GACrEC;CAAhB,AAGI,EAAA,OAAA,YAAA,EAAA,OADkB,qBAAA,EAAA,OAAd,gBAFQ,WAAM;EACpB,cAAc,WAAW,UAAU;GAEjC,AADI,gBAAc,kBAAkB,MAAM,IAAI,GAC9C,WAAW;IACT,UAAU,MAAM;IAChB,WAAW,MAAM;IACjB,KAAK;GACP,CAAC;EACH;CACF,GANI,EAAA,KAAA,UADkB,EAAA,KAAA,mBAAd,EAAA,KAAA,wCAFR,gBAAgBA,EASf;CAQSC,IAAAA,IAaPC;CAEH,OAVwB,EAAA,QAAA,UAAA,EAAA,QAFO,iBAHrB,WAAM;EACd,IAAM,aAAa,yBAAyB;GAE1C,mBAAmB,IAAI,IAAI,cAAc,IAAIC,MAAwB,CAAC;GAEtE,oBAAoB,OAAO,YAAY,CAAC,CAAC,QAAQ,aAAa;GAC9D,aAAa,YAAU,cAAc,QAAQC,OAAK;GAClD,OAAO;EACT,CAAC;EACD,IAAI,WAAW,WAAW,GAC1B,OAAO,OAAO,iBAAiB,EAC7B,UAAU,wBAAwB,EAAC,OAAO,WAAU,CAAC,EACvD,CAAC;CACH,GAAG,KAAA,CAAC,QAAQ,aAAa,GARD,EAAA,MAAA,QAFO,EAAA,MAAA,mEAH/B,UAAUH,IAaPC,EAAuB,GAEnB;AACT;AAbmD,SAAA,OAAC,QAAA;CAASG,OAAAA,OAAK;;AArBa,SAAA,QAAM,CAAC;;;;;;;;;;;;;;;;;;;;;ACsBtF,SAAgB,kBACd,SAAkC,CAAC,GACM;CACzC,IAAM,EACJ,eACA,QACA,YACA,OACA,QACA,UACA,cACA,SACA,cACA,aACE;CACJ,OAAO,SAAS,yBAAyB,OAAiC;EACxE,OACE,qBAAAE,UAAA,EAAA,UAAA;GACG,MAAM,cAAc,KAAK;GACzB,YAAY,SACX,oBAAC,oBAAD;IACiB;IACL;IACI;IACd,OAAO;GACR,CAAA,IACC;GACJ,oBAAC,mBAAD;IACiB;IACP;IACD;IACC;IACE;IACI;IACL;IACK;IACJ;GACX,CAAA;EACD,EAAA,CAAA;CAEN;AACF"}
\ No newline at end of file