@sanity/block-insert-picker
1.0.41.0.5
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":["PickerItem","PickerItemMetadata","PickerItemsContext","typeNameChain","memberType","name","type","names","current","push","undefined","derivePickerItems","context","metadata","memberTypes","map","schemaIndex","chain","nameRank","findIndex","entry","rank","includes","Number","MAX_SAFE_INTEGER","filter","hidden","sort","a","b","action","blockType","const","badge","description","group","id","keywords","openOnInsert","title","trigger","unknownMetadataTypes","known","Set","flatMap","has","PickerItem","filterPickerItems","items","query","queryLower","toLowerCase","bareQuery","startsWith","slice","filter","item","bareTrigger","trigger","replace","title","includes","description","keywords","some","kw","PickerItem","PickerSection","group","items","flattenSections","sections","flatMap","section","groupPickerItems","order","byGroup","Map","item","key","bucket","get","set","push","map","Editor","EditorSelection","EditorSnapshot","defineBehavior","raise","blockOffsetsToSelection","getTextBlockText","isTextBlock","PickerMode","InsertGuardResult","anchor","block","InsertPickerItemPayload","cleanup","placement","anchorBlockKey","_key","_type","Record","mode","query","createInsertBehavior","actions","_","at","type","select","guard","event","snapshot","anchorBlockExists","context","value","some","candidate","offsets","offset","path","focus","resolveQueryCleanup","const","on","sendInsertPickerItem","editor","payload","send","CleanupQueryPayload","Omit","createCleanupQueryBehavior","NonNullable","sendCleanupQuery","length","anchorBlock","find","schema","startsWith","Editor","sendInsertPickerItem","PickerMode","InsertContext","blockType","mode","editor","anchorBlockKey","query","keyGenerator","initialValue","Record","onInsertedKey","key","insertPickerItem","ctx","blockKey","block","_key","_type","BlockInsertPickerLabels","title","noMatches","positionAnnouncement","ungroupedSection","footerNavigate","footerInsert","footerDismiss","footerAnywhere","insertError","insertAnchorRemoved","DEFAULT_LABELS","resolveLabels","overrides","Partial","formatNoMatches","labels","query","replace","formatPosition","index","count","String","ContextType","useContext","useMemo","ObjectSchemaType","useSchema","PortableTextMemberSchemaTypesContext","PickerItemsContext","MemberSchemaTypes","NonNullable","usePickerItemsContext","arrayTypeName","$","_c","memberSchemaTypes","schema","t0","bb0","t1","blockObjects","portableText","memberTypes","schemaType","arrayType","get","undefined","jsonType","of","filter","_temp","member","isTextBlockMember","memberType","name","type","current","useCallback","useContext","useEffect","useRef","useFormCallbacks","PortableTextMemberItemsContext","useOpenBlockOnInsert","$","_c","memberItems","onPathOpen","onSetPathCollapsed","pendingKeyRef","t0","t1","key","current","item","find","m","member","path","t2","Symbol","for","blockKey","defineBehavior","effect","forward","BehaviorEvent","BehaviorGuard","getBlockTextBefore","getFirstBlock","getFocusBlock","getFocusTextBlock","isSelectionCollapsed","PickerIntent","PickerState","GuardResult","blockEvent","intent","PickerBehaviorRefs","onIntent","getState","isShortcutEnabled","createPickerBehavior","guard","event","snapshot","state","mode","type","text","focus","anchorBlockKey","node","_key","query","originEvent","key","metaKey","ctrlKey","fallback","anchorBlock","length","unit","slice","delta","actions","payload","guardResponse","sideEffect","on","PickerIntent","PickerState","pickerReducer","state","intent","type","mode","highlightedIndex","delta","anchorBlockKey","query","index","unhandled","effect","raise","blockOffsetsToSelection","defineInputRule","InputRule","KeyGenerator","MarkdownInputRuleConfig","pattern","RegExp","blockType","buildValue","context","matchText","keyGenerator","Record","BuildRulesDeps","onInserted","block","InsertableBlock","_key","_type","createMarkdownInputRules","allowedBlockTypes","rules","ReadonlySet","filter","config","has","map","createInsertRule","CODE_FENCE_PATTERN","BLOCKQUOTE_PATTERN","LanguageEntry","value","aliases","filename","blockquoteRule","options","createValue","codeFenceRule","languages","defaultLanguage","language","normalizeFenceLanguage","fenceLanguageFromMatch","find","entry","undefined","replace","trim","raw","token","lower","toLowerCase","some","alias","actions","event","snapshot","match","matches","text","at","offsets","anchor","targetOffsets","focus","selection","type","placement","select","on","typeNameChain","codeFenceRule","MarkdownInputRuleConfig","PickerItem","PickerItemMetadata","PickerItemsContext","standardBlockPresets","keywords","trigger","type","wellKnownInputRules","blockType","createValue","language","applyPresetMetadata","items","context","presets","presetByType","Map","map","preset","item","action","undefined","memberType","memberTypes","find","candidate","name","get","Boolean","BlockElementIcon","ComponentType","PickerItem","ItemPresentation","title","icon","description","SchemaTypeLike","name","type","to","ReadonlyArray","resolveItemPresentation","item","schemaType","action","blockType","id","upperFirst","value","charAt","toUpperCase","slice","useEditor","Box","Card","Flex","Hotkeys","Popover","Stack","Text","useToast","CSSProperties","useEffect","useId","useLayoutEffect","useMemo","useReducer","useRef","useState","useResolveInitialValueForType","useSchema","SchemaType","derivePickerItems","unknownMetadataTypes","filterPickerItems","flattenSections","groupPickerItems","createCleanupQueryBehavior","createInsertBehavior","sendCleanupQuery","insertPickerItem","BlockInsertPickerLabels","formatNoMatches","formatPosition","resolveLabels","usePickerItemsContext","useOpenBlockOnInsert","createPickerBehavior","pickerReducer","applyPresetMetadata","resolveItemPresentation","PickerInsertEvent","PickerIntent","PickerItem","PickerItemMetadata","PickerItemsContext","PickerItemsResolver","PickerState","BlockInsertPickerProps","items","arrayTypeName","resolveItems","filter","query","labels","Partial","onInsert","event","openOnInsert","shortcut","presets","BlockInsertPicker","labelOverrides","editor","openBlockOnInsert","itemsContext","schema","resolveInitialValueForType","toast","instanceId","listboxId","optionId","id","pickerItems","derived","presented","map","item","action","type","schemaType","memberTypeFor","blockType","presentation","Object","assign","description","icon","title","hasItems","length","warnedRef","context","empty","unknown","process","env","warned","current","console","warn","join","state","dispatch","mode","stateRef","shortcutRef","open","filterItems","filterQuery","bareQuery","sections","filtered","sectionOffsets","offsets","total","section","push","showGroupHeaders","insertInFlightRef","insertItemAtIndex","Exclude","idx","visible","clamped","Math","max","min","chosen","anchorBlockKey","onSelect","closePicker","initialValue","Record","undefined","snapshot","getSnapshot","anchorStillExists","value","some","b","_key","closable","insertAnchorRemoved","status","insertError","keyGenerator","onInsertedKey","key","blockKey","via","error","Error","message","String","selectItemAtIndex","handleIntent","intent","lastIndex","base","highlightedIndex","next","delta","index","handleIntentRef","behavior","getState","isShortcutEnabled","onIntent","unregisterPicker","registerBehavior","unregisterInsert","unregisterCleanup","highlightedItem","announcement","Boolean","cursorRect","setCursorRect","DOMRect","rect","dom","getSelectionRect","x","y","width","height","blockRect","getStartBlockElement","getBoundingClientRect","popoverContentRef","HTMLDivElement","handlePointerDown","PointerEvent","target","Node","contains","editorElement","getEditorElement","handleFocusOut","Event","FocusEvent","relatedTarget","document","addEventListener","removeEventListener","highlightedRowRef","lastPointerRef","highlightOnPointerMove","last","scrollIntoView","block","cursorElement","HTMLElement","preventFocusSteal","display","flexDirection","maxHeight","minWidth","SR_ONLY","letterSpacing","textTransform","flex","minHeight","overflowY","sectionIndex","sectionLabel","group","ungroupedSection","sectionKey","rows","rowIndex","Icon","isHighlighted","clientX","clientY","cursor","alignItems","justifyContent","badge","trigger","footerNavigate","footerInsert","footerDismiss","OPEN_MODIFIER","footerAnywhere","FooterHint","t0","$","_c","keys","label","t1","t2","t3","get","name","member","memberTypes","find","candidate","startsWith","slice","preventDefault","border","clipPath","margin","overflow","padding","position","whiteSpace","IS_APPLE_PLATFORM","navigator","test","platform","userAgent","useEditor","defineInputRuleBehavior","useEffect","useLayoutEffect","useMemo","useRef","typeNameChain","createMarkdownInputRules","MarkdownInputRuleConfig","usePickerItemsContext","useOpenBlockOnInsert","PickerInsertEvent","MarkdownInputRulesProps","arrayTypeName","rules","onInsert","event","openOnInsert","MarkdownInputRules","t0","$","_c","t1","undefined","editor","itemsContext","openBlockOnInsert","t2","memberTypes","t3","t4","rule","member","find","candidate_0","candidate","name","blockType","includes","flatMap","resolvedRules","onInsertedRef","_temp","current","block","_key","blockKey","_type","via","t5","t6","inputRules","allowedBlockTypes","Set","map","_temp2","keyGenerator","getSnapshot","context","onInserted","block_0","length","registerBehavior","behavior","rule_0","ComponentType","PortableTextPluginsProps","BlockInsertPicker","MarkdownInputRuleConfig","BlockInsertPickerLabels","MarkdownInputRules","PickerInsertEvent","PickerItem","PickerItemMetadata","PickerItemsResolver","BlockInsertPickerConfig","items","resolveItems","inputRules","filter","query","labels","Partial","onInsert","event","openOnInsert","shortcut","presets","arrayTypeName","blockInsertPicker","config","BlockInsertPickerPlugins","props","renderDefault","length"],"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(() => {\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, Popover, Stack, Text, useToast} from '@sanity/ui'\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 } 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/react-compiler\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 }, [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 // oxlint-disable-next-line no-unsafe-type-assertion\n return {\n getBoundingClientRect: () => cursorRect,\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":["PickerItem","PickerItemMetadata","PickerItemsContext","typeNameChain","memberType","name","type","names","current","push","undefined","derivePickerItems","context","metadata","memberTypes","map","schemaIndex","chain","nameRank","findIndex","entry","rank","includes","Number","MAX_SAFE_INTEGER","filter","hidden","sort","a","b","action","blockType","const","badge","description","group","id","keywords","openOnInsert","title","trigger","unknownMetadataTypes","known","Set","flatMap","has","PickerItem","filterPickerItems","items","query","queryLower","toLowerCase","bareQuery","startsWith","slice","filter","item","bareTrigger","trigger","replace","title","includes","description","keywords","some","kw","PickerItem","PickerSection","group","items","flattenSections","sections","flatMap","section","groupPickerItems","order","byGroup","Map","item","key","bucket","get","set","push","map","Editor","EditorSelection","EditorSnapshot","defineBehavior","raise","blockOffsetsToSelection","getTextBlockText","isTextBlock","PickerMode","InsertGuardResult","anchor","block","InsertPickerItemPayload","cleanup","placement","anchorBlockKey","_key","_type","Record","mode","query","createInsertBehavior","actions","_","at","type","select","guard","event","snapshot","anchorBlockExists","context","value","some","candidate","offsets","offset","path","focus","resolveQueryCleanup","const","on","sendInsertPickerItem","editor","payload","send","CleanupQueryPayload","Omit","createCleanupQueryBehavior","NonNullable","sendCleanupQuery","length","anchorBlock","find","schema","startsWith","Editor","sendInsertPickerItem","PickerMode","InsertContext","blockType","mode","editor","anchorBlockKey","query","keyGenerator","initialValue","Record","onInsertedKey","key","insertPickerItem","ctx","blockKey","block","_key","_type","BlockInsertPickerLabels","title","noMatches","positionAnnouncement","ungroupedSection","footerNavigate","footerInsert","footerDismiss","footerAnywhere","insertError","insertAnchorRemoved","DEFAULT_LABELS","resolveLabels","overrides","Partial","formatNoMatches","labels","query","replace","formatPosition","index","count","String","ContextType","useContext","useMemo","ObjectSchemaType","useSchema","PortableTextMemberSchemaTypesContext","PickerItemsContext","MemberSchemaTypes","NonNullable","usePickerItemsContext","arrayTypeName","$","_c","memberSchemaTypes","schema","t0","bb0","t1","blockObjects","portableText","memberTypes","schemaType","arrayType","get","undefined","jsonType","of","filter","_temp","member","isTextBlockMember","memberType","name","type","current","useCallback","useContext","useEffect","useRef","useFormCallbacks","PortableTextMemberItemsContext","useOpenBlockOnInsert","$","_c","memberItems","onPathOpen","onSetPathCollapsed","pendingKeyRef","t0","t1","key","current","item","find","m","member","path","t2","Symbol","for","blockKey","defineBehavior","effect","forward","BehaviorEvent","BehaviorGuard","getBlockTextBefore","getFirstBlock","getFocusBlock","getFocusTextBlock","isSelectionCollapsed","PickerIntent","PickerState","GuardResult","blockEvent","intent","PickerBehaviorRefs","onIntent","getState","isShortcutEnabled","createPickerBehavior","guard","event","snapshot","state","mode","type","text","focus","anchorBlockKey","node","_key","query","originEvent","key","metaKey","ctrlKey","fallback","anchorBlock","length","unit","slice","delta","actions","payload","guardResponse","sideEffect","on","PickerIntent","PickerState","pickerReducer","state","intent","type","mode","highlightedIndex","delta","anchorBlockKey","query","index","unhandled","effect","raise","blockOffsetsToSelection","defineInputRule","InputRule","KeyGenerator","MarkdownInputRuleConfig","pattern","RegExp","blockType","buildValue","context","matchText","keyGenerator","Record","BuildRulesDeps","onInserted","block","InsertableBlock","_key","_type","createMarkdownInputRules","allowedBlockTypes","rules","ReadonlySet","filter","config","has","map","createInsertRule","CODE_FENCE_PATTERN","BLOCKQUOTE_PATTERN","LanguageEntry","value","aliases","filename","blockquoteRule","options","createValue","codeFenceRule","languages","defaultLanguage","language","normalizeFenceLanguage","fenceLanguageFromMatch","find","entry","undefined","replace","trim","raw","token","lower","toLowerCase","some","alias","actions","event","snapshot","match","matches","text","at","offsets","anchor","targetOffsets","focus","selection","type","placement","select","on","typeNameChain","codeFenceRule","MarkdownInputRuleConfig","PickerItem","PickerItemMetadata","PickerItemsContext","standardBlockPresets","keywords","trigger","type","wellKnownInputRules","blockType","createValue","language","applyPresetMetadata","items","context","presets","presetByType","Map","map","preset","item","action","undefined","memberType","memberTypes","find","candidate","name","get","Boolean","BlockElementIcon","ComponentType","PickerItem","ItemPresentation","title","icon","description","SchemaTypeLike","name","type","to","ReadonlyArray","resolveItemPresentation","item","schemaType","action","blockType","id","upperFirst","value","charAt","toUpperCase","slice","useEditor","Box","Card","Flex","Hotkeys","Stack","Text","Popover","useToast","CSSProperties","useEffect","useId","useLayoutEffect","useMemo","useReducer","useRef","useState","useResolveInitialValueForType","useSchema","SchemaType","derivePickerItems","unknownMetadataTypes","filterPickerItems","flattenSections","groupPickerItems","createCleanupQueryBehavior","createInsertBehavior","sendCleanupQuery","insertPickerItem","BlockInsertPickerLabels","formatNoMatches","formatPosition","resolveLabels","usePickerItemsContext","useOpenBlockOnInsert","createPickerBehavior","pickerReducer","applyPresetMetadata","resolveItemPresentation","PickerInsertEvent","PickerIntent","PickerItem","PickerItemMetadata","PickerItemsContext","PickerItemsResolver","PickerState","BlockInsertPickerProps","items","arrayTypeName","resolveItems","filter","query","labels","Partial","onInsert","event","openOnInsert","shortcut","presets","BlockInsertPicker","labelOverrides","editor","openBlockOnInsert","itemsContext","schema","resolveInitialValueForType","toast","instanceId","listboxId","optionId","id","pickerItems","derived","presented","map","item","action","type","schemaType","memberTypeFor","blockType","presentation","Object","assign","description","icon","title","hasItems","length","warnedRef","context","empty","unknown","process","env","warned","current","console","warn","join","state","dispatch","mode","stateRef","shortcutRef","open","filterItems","filterQuery","bareQuery","sections","filtered","sectionOffsets","offsets","total","section","push","showGroupHeaders","insertInFlightRef","insertItemAtIndex","Exclude","idx","visible","clamped","Math","max","min","chosen","anchorBlockKey","onSelect","closePicker","initialValue","Record","undefined","snapshot","getSnapshot","anchorStillExists","value","some","b","_key","closable","insertAnchorRemoved","status","insertError","keyGenerator","onInsertedKey","key","blockKey","via","error","Error","message","String","selectItemAtIndex","handleIntent","intent","lastIndex","base","highlightedIndex","next","delta","index","handleIntentRef","behavior","getState","isShortcutEnabled","onIntent","unregisterPicker","registerBehavior","unregisterInsert","unregisterCleanup","highlightedItem","announcement","Boolean","cursorRect","setCursorRect","DOMRect","rect","dom","getSelectionRect","x","y","width","height","blockRect","getStartBlockElement","getBoundingClientRect","popoverContentRef","HTMLDivElement","handlePointerDown","PointerEvent","target","Node","contains","editorElement","getEditorElement","handleFocusOut","Event","FocusEvent","relatedTarget","document","addEventListener","removeEventListener","highlightedRowRef","lastPointerRef","highlightOnPointerMove","last","scrollIntoView","block","cursorElement","HTMLElement","preventFocusSteal","display","flexDirection","maxHeight","minWidth","SR_ONLY","letterSpacing","textTransform","flex","minHeight","overflowY","sectionIndex","sectionLabel","group","ungroupedSection","sectionKey","rows","rowIndex","Icon","isHighlighted","clientX","clientY","cursor","alignItems","justifyContent","badge","trigger","footerNavigate","footerInsert","footerDismiss","OPEN_MODIFIER","footerAnywhere","FooterHint","t0","$","_c","keys","label","t1","t2","t3","get","name","member","memberTypes","find","candidate","startsWith","slice","preventDefault","border","clipPath","margin","overflow","padding","position","whiteSpace","IS_APPLE_PLATFORM","navigator","test","platform","userAgent","useEditor","defineInputRuleBehavior","useEffect","useLayoutEffect","useMemo","useRef","typeNameChain","createMarkdownInputRules","MarkdownInputRuleConfig","usePickerItemsContext","useOpenBlockOnInsert","PickerInsertEvent","MarkdownInputRulesProps","arrayTypeName","rules","onInsert","event","openOnInsert","MarkdownInputRules","t0","$","_c","t1","undefined","editor","itemsContext","openBlockOnInsert","t2","memberTypes","t3","t4","rule","member","find","candidate_0","candidate","name","blockType","includes","flatMap","resolvedRules","onInsertedRef","_temp","current","block","_key","blockKey","_type","via","t5","t6","inputRules","allowedBlockTypes","Set","map","_temp2","keyGenerator","getSnapshot","context","onInserted","block_0","length","registerBehavior","behavior","rule_0","ComponentType","PortableTextPluginsProps","BlockInsertPicker","MarkdownInputRuleConfig","BlockInsertPickerLabels","MarkdownInputRules","PickerInsertEvent","PickerItem","PickerItemMetadata","PickerItemsResolver","BlockInsertPickerConfig","items","resolveItems","inputRules","filter","query","labels","Partial","onInsert","event","openOnInsert","shortcut","presets","arrayTypeName","blockInsertPicker","config","BlockInsertPickerPlugins","props","renderDefault","length"],"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(() => {\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 } 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/react-compiler\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 }, [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 // oxlint-disable-next-line no-unsafe-type-assertion\n return {\n getBoundingClientRect: () => cursorRect,\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