@sanity/block-insert-picker 0.0.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +150 -43
- package/dist/index.d.ts +385 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1195 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -7
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sanity.io
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,45 +1,152 @@
|
|
|
1
1
|
# @sanity/block-insert-picker
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
3
|
+
Slash-command block-insert picker and markdown input rules for Portable Text fields in Sanity Studio.
|
|
4
|
+
|
|
5
|
+
Type `/` at the start of a line (or press `Cmd`/`Ctrl` + `/` anywhere) to open a caret-anchored menu of every object block the field's schema allows — searchable by title, trigger, and alias keywords, grouped into sections, keyboard-first. Selecting an item resolves the member type's initial value, inserts the block as a single undo step, and opens it for editing. Optional markdown input rules give the heaviest block types an even faster path, for example ` ```ts ` + space straight into a code block.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @sanity/block-insert-picker
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires `sanity` v5.6 or later.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Attach the picker to a Portable Text array type via `components.portableText.plugins`. Zero config is the intended call — the picker detects the array it is mounted on and derives its items from that array's members, so it only ever offers blocks the field accepts:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import {blockInsertPicker} from '@sanity/block-insert-picker'
|
|
21
|
+
import {defineType} from 'sanity'
|
|
22
|
+
|
|
23
|
+
export const content = defineType({
|
|
24
|
+
name: 'content',
|
|
25
|
+
type: 'array',
|
|
26
|
+
of: [{type: 'block'}, {type: 'callout'}, {type: 'codeBlock'}],
|
|
27
|
+
components: {
|
|
28
|
+
portableText: {
|
|
29
|
+
plugins: blockInsertPicker(),
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Titles, icons, and descriptions come from each member's schema type (with the same fallbacks Studio's built-in insert menu uses), and detection handles aliased members (`{type: 'image', name: 'photo'}`) and inline-declared members with full fidelity.
|
|
36
|
+
|
|
37
|
+
When an array derives zero items — its only member is the text block — and no `inputRules` are configured, the picker disables itself: no behaviors register and `/` inserts plain text (with a `console.warn` in dev builds). Items appended via `resolveItems` (or configured `inputRules`) re-enable it, so a custom-command-only picker on a text-only field works.
|
|
38
|
+
|
|
39
|
+
### Curating items
|
|
40
|
+
|
|
41
|
+
The optional `items` metadata curates the derived items — slash triggers, alias keywords, section grouping, badges, per-item visibility, and rank (array order). Entries match a member's name or any name in its resolved type chain, and a curated `description` wins over the schema type's:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
blockInsertPicker({
|
|
45
|
+
items: [
|
|
46
|
+
{
|
|
47
|
+
type: 'codeBlock',
|
|
48
|
+
trigger: '/code',
|
|
49
|
+
keywords: ['snippet', 'syntax'],
|
|
50
|
+
group: 'Code',
|
|
51
|
+
description: 'Syntax-highlighted code sample',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'callout',
|
|
55
|
+
trigger: '/callout',
|
|
56
|
+
keywords: ['note', 'warning', 'tip'],
|
|
57
|
+
group: 'Callouts',
|
|
58
|
+
},
|
|
59
|
+
{type: 'legacyEmbed', hidden: true},
|
|
60
|
+
],
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Members without an entry still get picker items, appended in schema order. Entries whose `type` matches no member log a dev-mode warning.
|
|
65
|
+
|
|
66
|
+
### Presets for well-known blocks
|
|
67
|
+
|
|
68
|
+
Members whose type resolves to a well-known name — `image`, `file`, `code` (`@sanity/code-input`), `table`, `color`, `latex`, `mux.video`, `sanity.video` — get default triggers and search keywords out of the box. Presets never add items, never group, and always lose to your `items` metadata. Opt out with `presets: false`.
|
|
69
|
+
|
|
70
|
+
### Markdown input rules
|
|
71
|
+
|
|
72
|
+
`inputRules` adds typed transforms that replace markdown-style text with an inserted block — one undo step, and the block opens for editing just like a picker insert. Rules are opt-in; two factories cover the common cases:
|
|
73
|
+
|
|
74
|
+
````ts
|
|
75
|
+
import {blockInsertPicker, blockquoteRule, codeFenceRule} from '@sanity/block-insert-picker'
|
|
76
|
+
|
|
77
|
+
blockInsertPicker({
|
|
78
|
+
inputRules: [
|
|
79
|
+
// "```lang" + space inserts a code block with the language resolved
|
|
80
|
+
codeFenceRule({
|
|
81
|
+
blockType: 'codeBlock',
|
|
82
|
+
defaultLanguage: 'typescript',
|
|
83
|
+
languages: [
|
|
84
|
+
{value: 'typescript', aliases: ['ts'], filename: 'index.ts'},
|
|
85
|
+
{value: 'javascript', aliases: ['js'], filename: 'index.js'},
|
|
86
|
+
],
|
|
87
|
+
createValue: ({language, filename}) => ({
|
|
88
|
+
language,
|
|
89
|
+
...(filename ? {filename} : {}),
|
|
90
|
+
}),
|
|
91
|
+
}),
|
|
92
|
+
// "> " at the start of a block inserts a quote/callout
|
|
93
|
+
blockquoteRule({
|
|
94
|
+
blockType: 'callout',
|
|
95
|
+
createValue: () => ({tone: 'info'}),
|
|
96
|
+
}),
|
|
97
|
+
],
|
|
98
|
+
})
|
|
99
|
+
````
|
|
100
|
+
|
|
101
|
+
`createValue` builds the inserted block's fields synchronously from the resolved fence language (and optional tab filename), shaped for **your** schema. Each rule's `blockType` resolves against the array's members by name or resolved type chain (aliased members included; the inserted `_type` is always the member name); rules matching nothing are ignored. For fully custom transforms, pass a `{pattern, blockType, buildValue}` config directly. A ready-made `wellKnownInputRules` bundle covers `@sanity/code-input`'s `code` type: `inputRules: [...wellKnownInputRules]`.
|
|
102
|
+
|
|
103
|
+
### Reacting to inserts
|
|
104
|
+
|
|
105
|
+
`onInsert` fires after every successful insert, from either path:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
blockInsertPicker({
|
|
109
|
+
onInsert: ({blockKey, blockType, via, mode, query}) => {
|
|
110
|
+
// via is 'picker' or 'inputRule'; mode and query are picker-only
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Escape hatches
|
|
116
|
+
|
|
117
|
+
For anything the declarative options don't cover:
|
|
118
|
+
|
|
119
|
+
- **`resolveItems`** — a function over the fully-presented item list: reorder, remove, relabel, or append items. Appended items can carry a `custom` action that runs your code instead of inserting a block:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
blockInsertPicker({
|
|
123
|
+
resolveItems: (items, {schemaType}) => [
|
|
124
|
+
...items,
|
|
125
|
+
{
|
|
126
|
+
id: 'ai-draft',
|
|
127
|
+
title: 'Draft with AI',
|
|
128
|
+
action: {type: 'custom', onSelect: ({editor}) => startDraft(editor)},
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- **`filter`** — replaces the built-in matching (case-insensitive substring over title, keywords, and description, plus trigger prefixes). It receives the bare query in both modes; the slash-mode `/` prefix is stripped.
|
|
135
|
+
- **`labels`** — overrides any user-facing string in the picker chrome (header, empty state, footer hints, error toast), for reworded or translated UI.
|
|
136
|
+
- **`shortcut: false`** — disables the `Cmd`/`Ctrl` + `/` shortcut; `openOnInsert: false` — keeps inserted blocks closed (also available per item via metadata).
|
|
137
|
+
- **`BlockInsertPicker` / `MarkdownInputRules`** — the underlying components, exported for hosts composing their own `components.portableText.plugins` chain, along with the pure `derivePickerItems` and `filterPickerItems` utilities.
|
|
138
|
+
- **`arrayTypeName`** — names the array type explicitly, for environments where the member-schema context is unavailable. Normally unnecessary.
|
|
139
|
+
|
|
140
|
+
## Keyboard reference
|
|
141
|
+
|
|
142
|
+
| Key | Action |
|
|
143
|
+
| ------------------ | -------------------------------------------------- |
|
|
144
|
+
| `/` at line start | Open the picker; keep typing to filter |
|
|
145
|
+
| `Cmd`/`Ctrl` + `/` | Open the picker anywhere; type to filter privately |
|
|
146
|
+
| `↑` / `↓` | Move the highlight |
|
|
147
|
+
| `Enter` | Insert the highlighted block |
|
|
148
|
+
| `Escape` | Dismiss |
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
[MIT](LICENSE) © Sanity.io
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { Editor } from "@portabletext/editor";
|
|
2
|
+
import { ComponentType } from "react";
|
|
3
|
+
import { ArraySchemaType, ObjectSchemaType, PortableTextPluginsProps } from "sanity";
|
|
4
|
+
import "@portabletext/plugin-input-rule";
|
|
5
|
+
/**
|
|
6
|
+
* Every user-facing string in the picker chrome, centralized so hosts can
|
|
7
|
+
* translate or reword them via the `labels` option (the same pattern
|
|
8
|
+
* `@sanity/insert-menu` uses for its label overrides). Item titles and
|
|
9
|
+
* descriptions are schema/metadata content, not chrome — localize those
|
|
10
|
+
* through `items` or `resolveItems` instead.
|
|
11
|
+
*/
|
|
12
|
+
type BlockInsertPickerLabels = {
|
|
13
|
+
/** Popover header; also the listbox aria-label. */
|
|
14
|
+
title: string;
|
|
15
|
+
/** Empty state; `{query}` is replaced with the typed filter text. */
|
|
16
|
+
noMatches: string;
|
|
17
|
+
/**
|
|
18
|
+
* Screen-reader position announcement for the highlighted row; `{index}`
|
|
19
|
+
* and `{count}` are replaced with the 1-based position and list length.
|
|
20
|
+
*/
|
|
21
|
+
positionAnnouncement: string;
|
|
22
|
+
/**
|
|
23
|
+
* Section header for items without a `group`, shown only when grouped
|
|
24
|
+
* sections are also present.
|
|
25
|
+
*/
|
|
26
|
+
ungroupedSection: string;
|
|
27
|
+
/** Footer legend verbs. */
|
|
28
|
+
footerNavigate: string;
|
|
29
|
+
footerInsert: string;
|
|
30
|
+
footerDismiss: string;
|
|
31
|
+
footerAnywhere: string;
|
|
32
|
+
/** Toast title when resolving a block's initial value fails. */
|
|
33
|
+
insertError: string;
|
|
34
|
+
/**
|
|
35
|
+
* Toast description when the block the picker was anchored to is deleted
|
|
36
|
+
* before the insert completes.
|
|
37
|
+
*/
|
|
38
|
+
insertAnchorRemoved: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Emitted after a block lands in the document, whether via picker selection
|
|
42
|
+
* or a markdown input rule. The public seam for hosts that need to react to
|
|
43
|
+
* inserts (analytics, custom focus handling) without reaching into Studio
|
|
44
|
+
* internals the way the built-in open-on-insert does (see openBlockOnInsert).
|
|
45
|
+
*/
|
|
46
|
+
type PickerInsertEvent = {
|
|
47
|
+
blockKey: string;
|
|
48
|
+
blockType: string;
|
|
49
|
+
via: 'inputRule' | 'picker';
|
|
50
|
+
/** How the picker was opened; only present when `via` is `'picker'`. */
|
|
51
|
+
mode?: PickerMode;
|
|
52
|
+
/** The filter query at select time; only present when `via` is `'picker'`. */
|
|
53
|
+
query?: string;
|
|
54
|
+
};
|
|
55
|
+
type PickerItem = {
|
|
56
|
+
id: string;
|
|
57
|
+
trigger?: string;
|
|
58
|
+
title: string;
|
|
59
|
+
/**
|
|
60
|
+
* One-line explanation shown under the title. Curated metadata wins over
|
|
61
|
+
* the member schema type's own `description` — curation is the more
|
|
62
|
+
* specific intent (see resolveItemPresentation).
|
|
63
|
+
*/
|
|
64
|
+
description?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Category label used to partition the menu into sections. Items without a
|
|
67
|
+
* group render in a single unlabelled list (e.g. a curated `items` prop).
|
|
68
|
+
*/
|
|
69
|
+
group?: string;
|
|
70
|
+
icon?: ComponentType;
|
|
71
|
+
keywords?: readonly string[];
|
|
72
|
+
/** Short keyboard-style hint rendered at the row's trailing edge. */
|
|
73
|
+
badge?: string;
|
|
74
|
+
/** Per-item override of the plugin-level `openOnInsert` default. */
|
|
75
|
+
openOnInsert?: boolean;
|
|
76
|
+
action: PickerItemAction;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* What selecting an item does. An open union: further named actions (for
|
|
80
|
+
* example setting a text style) can be added without a breaking change.
|
|
81
|
+
* `custom` items never insert anything themselves — the host's `onSelect`
|
|
82
|
+
* runs with the editor after the picker closes and the typed query text
|
|
83
|
+
* (slash mode) has been cleaned up.
|
|
84
|
+
*/
|
|
85
|
+
type PickerItemAction = {
|
|
86
|
+
type: 'custom';
|
|
87
|
+
onSelect: (context: PickerActionContext) => void;
|
|
88
|
+
} | {
|
|
89
|
+
type: 'insertBlock';
|
|
90
|
+
blockType: string;
|
|
91
|
+
};
|
|
92
|
+
/** Handed to a `custom` item's `onSelect` when the item is chosen. */
|
|
93
|
+
interface PickerActionContext {
|
|
94
|
+
editor: Editor;
|
|
95
|
+
closePicker: () => void;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Host-supplied curation for schema-derived picker items, keyed by member
|
|
99
|
+
* type name (falling back to a name anywhere in the member's resolved type
|
|
100
|
+
* chain, so `{type: 'image', name: 'photo'}` matches both `photo` and
|
|
101
|
+
* `image` — the member name wins when both have entries): slash trigger,
|
|
102
|
+
* alias keywords, section grouping, badge, visibility, per-item
|
|
103
|
+
* open-on-insert, and description. Array position is the sort rank — members
|
|
104
|
+
* without an entry still get picker items, appended after the ranked ones in
|
|
105
|
+
* schema order.
|
|
106
|
+
*/
|
|
107
|
+
type PickerItemMetadata = {
|
|
108
|
+
type: string;
|
|
109
|
+
trigger?: string;
|
|
110
|
+
keywords?: readonly string[];
|
|
111
|
+
group?: string;
|
|
112
|
+
description?: string;
|
|
113
|
+
/** Short keyboard-style hint rendered at the row's trailing edge. */
|
|
114
|
+
badge?: string;
|
|
115
|
+
/** Removes the derived item from the picker without touching the schema. */
|
|
116
|
+
hidden?: boolean;
|
|
117
|
+
/** Per-item override of the plugin-level `openOnInsert` default. */
|
|
118
|
+
openOnInsert?: boolean;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* The schema surroundings picker items derive from: the Portable Text array
|
|
122
|
+
* type itself and its insertable (non-text-block) member types. Sourced from
|
|
123
|
+
* Studio's member-schema context when available, or resolved from an
|
|
124
|
+
* explicit `arrayTypeName` otherwise (see memberSchemaTypes.ts).
|
|
125
|
+
*/
|
|
126
|
+
interface PickerItemsContext {
|
|
127
|
+
schemaType: ArraySchemaType;
|
|
128
|
+
memberTypes: readonly ObjectSchemaType[];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Programmatic escape hatch over the derived item list: reorder, remove,
|
|
132
|
+
* relabel, or append items (including `custom`-action items) after schema
|
|
133
|
+
* derivation, presets, and `items` metadata have been applied.
|
|
134
|
+
*/
|
|
135
|
+
type PickerItemsResolver = (items: readonly PickerItem[], context: PickerItemsContext) => readonly PickerItem[];
|
|
136
|
+
type PickerMode = 'shortcut' | 'slash';
|
|
137
|
+
type BlockInsertPickerProps = {
|
|
138
|
+
/**
|
|
139
|
+
* Per-member curation (trigger, keywords, group, description, badge,
|
|
140
|
+
* hidden, openOnInsert) merged into the items derived from the array's
|
|
141
|
+
* members; array order is the rank. Members without an entry still get
|
|
142
|
+
* items, in schema order.
|
|
143
|
+
*/
|
|
144
|
+
items?: readonly PickerItemMetadata[];
|
|
145
|
+
/**
|
|
146
|
+
* Escape hatch: names the portable-text array type to resolve items from
|
|
147
|
+
* when Studio's member-schema context is unavailable. With the context
|
|
148
|
+
* present (any supported sanity version) this is ignored — the containing
|
|
149
|
+
* array and its members are detected automatically.
|
|
150
|
+
*/
|
|
151
|
+
arrayTypeName?: string;
|
|
152
|
+
/**
|
|
153
|
+
* Programmatic seam over the derived-and-curated item list: reorder,
|
|
154
|
+
* remove, relabel, or append items (including `custom`-action items).
|
|
155
|
+
*/
|
|
156
|
+
resolveItems?: PickerItemsResolver;
|
|
157
|
+
/**
|
|
158
|
+
* Replaces the built-in matching (see filterPickerItems). Receives the
|
|
159
|
+
* bare query in both modes — the slash-mode "/" prefix is stripped.
|
|
160
|
+
*/
|
|
161
|
+
filter?: (items: readonly PickerItem[], query: string) => readonly PickerItem[];
|
|
162
|
+
/** Overrides for the picker chrome's user-facing strings. */
|
|
163
|
+
labels?: Partial<BlockInsertPickerLabels>;
|
|
164
|
+
/** Notified after each successful insert, alongside open-on-insert. */
|
|
165
|
+
onInsert?: (event: PickerInsertEvent) => void;
|
|
166
|
+
/**
|
|
167
|
+
* Whether an inserted block opens for editing (default true). Items can
|
|
168
|
+
* override per member via their metadata's `openOnInsert`.
|
|
169
|
+
*/
|
|
170
|
+
openOnInsert?: boolean;
|
|
171
|
+
/** Whether Cmd/Ctrl+/ opens the picker (default true). */
|
|
172
|
+
shortcut?: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* Whether well-known block types (image, file, code, table, ...) get
|
|
175
|
+
* default triggers and keywords when the host's `items` doesn't cover
|
|
176
|
+
* them (default true). See standardBlockPresets.
|
|
177
|
+
*/
|
|
178
|
+
presets?: boolean;
|
|
179
|
+
};
|
|
180
|
+
declare function BlockInsertPicker({ arrayTypeName, filter, items, labels: labelOverrides, onInsert, openOnInsert, presets, resolveItems, shortcut }: BlockInsertPickerProps): import("react").JSX.Element | null;
|
|
181
|
+
/**
|
|
182
|
+
* Derives picker items from a portable-text array's insertable member types
|
|
183
|
+
* (see PickerItemsContext), so the picker automatically covers every object
|
|
184
|
+
* block the schema allows. The optional metadata list curates triggers,
|
|
185
|
+
* keywords, grouping, badges, visibility, and descriptions per member type;
|
|
186
|
+
* its array order is the sort rank (see PickerItemMetadata). Titles and
|
|
187
|
+
* icons intentionally stay unresolved here: BlockInsertPicker resolves
|
|
188
|
+
* presentation from the member schema type, which carries member-level
|
|
189
|
+
* config (a member-specific icon, a per-member title override).
|
|
190
|
+
*/
|
|
191
|
+
declare function derivePickerItems(context: PickerItemsContext, metadata?: readonly PickerItemMetadata[]): PickerItem[];
|
|
192
|
+
declare function filterPickerItems(items: readonly PickerItem[], query: string): readonly PickerItem[];
|
|
193
|
+
/**
|
|
194
|
+
* Generic machinery for markdown-style input rules that replace typed text
|
|
195
|
+
* with an inserted object block, complementing the slash/⌘+/ picker with a
|
|
196
|
+
* keyboard-only path that mirrors what writers already type in markdown.
|
|
197
|
+
*
|
|
198
|
+
* The rules run through `@portabletext/plugin-input-rule` (see
|
|
199
|
+
* `markdownInputRules.tsx`), which applies the delete + insert as a single
|
|
200
|
+
* behavior action set — so each transform is one undo step.
|
|
201
|
+
*
|
|
202
|
+
* Block values are built synchronously via each config's `buildValue` rather
|
|
203
|
+
* than resolved from the schema's async initial values: the insert has to
|
|
204
|
+
* happen inside the rule's action set for that single-undo guarantee to hold,
|
|
205
|
+
* so anything the writer would otherwise have to fill in (language, filename,
|
|
206
|
+
* callout type) is seeded from the host's rule config.
|
|
207
|
+
*/
|
|
208
|
+
type KeyGenerator = () => string;
|
|
209
|
+
/**
|
|
210
|
+
* One markdown input rule: when `pattern` matches the block text, the match
|
|
211
|
+
* is deleted and a block of `blockType` is inserted in its place, carrying
|
|
212
|
+
* the fields `buildValue` returns. `buildValue` must stay synchronous so the
|
|
213
|
+
* delete + insert land in one action set (one undo step).
|
|
214
|
+
*/
|
|
215
|
+
type MarkdownInputRuleConfig = {
|
|
216
|
+
pattern: RegExp;
|
|
217
|
+
blockType: string;
|
|
218
|
+
buildValue: (context: {
|
|
219
|
+
matchText: string;
|
|
220
|
+
keyGenerator: KeyGenerator;
|
|
221
|
+
}) => Record<string, unknown>;
|
|
222
|
+
};
|
|
223
|
+
declare const CODE_FENCE_PATTERN: RegExp;
|
|
224
|
+
declare const BLOCKQUOTE_PATTERN: RegExp;
|
|
225
|
+
/**
|
|
226
|
+
* A row of a code-fence language table: the stored `value`, the markdown
|
|
227
|
+
* fence tokens that should resolve to it, and an optional tab filename.
|
|
228
|
+
*/
|
|
229
|
+
type LanguageEntry = {
|
|
230
|
+
value: string;
|
|
231
|
+
aliases?: readonly string[];
|
|
232
|
+
filename?: string;
|
|
233
|
+
};
|
|
234
|
+
/** Sugar: a blockquote rule ("> " at the start of a block) for `blockType`. */
|
|
235
|
+
declare function blockquoteRule(options: {
|
|
236
|
+
blockType: string;
|
|
237
|
+
createValue?: (context: {
|
|
238
|
+
keyGenerator: KeyGenerator;
|
|
239
|
+
}) => Record<string, unknown>;
|
|
240
|
+
}): MarkdownInputRuleConfig;
|
|
241
|
+
/**
|
|
242
|
+
* Sugar: a code-fence rule ("```lang␣") for `blockType`. The language table
|
|
243
|
+
* resolves fence tokens (aliases included) to the values the block's language
|
|
244
|
+
* selector stores, plus an optional filename; tokens outside the table pass
|
|
245
|
+
* through as typed so nothing the writer means is lost. `createValue` turns
|
|
246
|
+
* the resolved language and filename into the block's fields.
|
|
247
|
+
*/
|
|
248
|
+
declare function codeFenceRule(options: {
|
|
249
|
+
blockType: string;
|
|
250
|
+
languages?: readonly LanguageEntry[];
|
|
251
|
+
defaultLanguage?: string;
|
|
252
|
+
createValue: (context: {
|
|
253
|
+
filename: string | undefined;
|
|
254
|
+
keyGenerator: KeyGenerator;
|
|
255
|
+
language: string | undefined;
|
|
256
|
+
}) => Record<string, unknown>;
|
|
257
|
+
}): MarkdownInputRuleConfig;
|
|
258
|
+
/** Strips the leading "```" fence and surrounding whitespace from a match. */
|
|
259
|
+
declare function fenceLanguageFromMatch(matchText: string): string;
|
|
260
|
+
/**
|
|
261
|
+
* Resolves a typed fence token against a language table: a canonical value,
|
|
262
|
+
* an alias (`py`→python, `yml`→yaml), or — for anything the table doesn't
|
|
263
|
+
* know — the token as typed. An empty token yields `defaultLanguage`.
|
|
264
|
+
*/
|
|
265
|
+
declare function normalizeFenceLanguage(raw: string, languages: readonly LanguageEntry[], defaultLanguage?: string): string | undefined;
|
|
266
|
+
type MarkdownInputRulesProps = {
|
|
267
|
+
/**
|
|
268
|
+
* Escape hatch: names the portable-text array type when Studio's
|
|
269
|
+
* member-schema context is unavailable (see BlockInsertPickerProps).
|
|
270
|
+
* Rules are only enabled for block types the array actually allows.
|
|
271
|
+
*/
|
|
272
|
+
arrayTypeName?: string;
|
|
273
|
+
/** The markdown transforms to enable (see inputRules.ts). */
|
|
274
|
+
rules: readonly MarkdownInputRuleConfig[];
|
|
275
|
+
/** Notified after each successful insert, alongside open-on-insert. */
|
|
276
|
+
onInsert?: (event: PickerInsertEvent) => void;
|
|
277
|
+
/** Whether an inserted block opens for editing (default true). */
|
|
278
|
+
openOnInsert?: boolean;
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* Mounts markdown input rules on the PTE and opens each inserted block for
|
|
282
|
+
* editing once its member item mounts — the same open-on-insert behavior the
|
|
283
|
+
* picker uses, so a fenced code block lands ready to type into.
|
|
284
|
+
*/
|
|
285
|
+
declare function MarkdownInputRules({ arrayTypeName, onInsert, openOnInsert, rules }: MarkdownInputRulesProps): null;
|
|
286
|
+
interface BlockInsertPickerConfig {
|
|
287
|
+
/**
|
|
288
|
+
* Per-member curation (slash trigger, alias keywords, section grouping,
|
|
289
|
+
* description, badge, visibility, per-item open-on-insert) merged into the
|
|
290
|
+
* items derived from the array's members; array order is the sort rank.
|
|
291
|
+
* Members without an entry still get picker items, appended in schema
|
|
292
|
+
* order. Entries whose `type` matches no member warn in dev mode.
|
|
293
|
+
*/
|
|
294
|
+
items?: readonly PickerItemMetadata[];
|
|
295
|
+
/**
|
|
296
|
+
* Programmatic seam over the derived item list — reorder, remove, relabel,
|
|
297
|
+
* or append items (including `custom`-action items) — running after schema
|
|
298
|
+
* derivation, presets, and `items` metadata.
|
|
299
|
+
*/
|
|
300
|
+
resolveItems?: PickerItemsResolver;
|
|
301
|
+
/**
|
|
302
|
+
* Markdown-style transforms (for example from the {@link codeFenceRule} and
|
|
303
|
+
* {@link blockquoteRule} factories) that replace typed text with an
|
|
304
|
+
* inserted block, as one undo step. Opt-in only; rules targeting block
|
|
305
|
+
* types the array does not allow are ignored.
|
|
306
|
+
*/
|
|
307
|
+
inputRules?: readonly MarkdownInputRuleConfig[];
|
|
308
|
+
/**
|
|
309
|
+
* Replaces the built-in matching (case-insensitive substring over title,
|
|
310
|
+
* keywords, and description, plus trigger prefixes). Receives the bare
|
|
311
|
+
* query in both modes — the slash-mode "/" prefix is stripped.
|
|
312
|
+
*/
|
|
313
|
+
filter?: (items: readonly PickerItem[], query: string) => readonly PickerItem[];
|
|
314
|
+
/** Overrides for the picker chrome's user-facing strings. */
|
|
315
|
+
labels?: Partial<BlockInsertPickerLabels>;
|
|
316
|
+
/**
|
|
317
|
+
* Notified after each successful insert — picker selection or input rule —
|
|
318
|
+
* alongside the built-in open-for-editing behavior.
|
|
319
|
+
*/
|
|
320
|
+
onInsert?: (event: PickerInsertEvent) => void;
|
|
321
|
+
/**
|
|
322
|
+
* Whether an inserted block opens for editing (default true). Items can
|
|
323
|
+
* override per member via their metadata's `openOnInsert`.
|
|
324
|
+
*/
|
|
325
|
+
openOnInsert?: boolean;
|
|
326
|
+
/** Whether Cmd/Ctrl+/ opens the picker (default true). */
|
|
327
|
+
shortcut?: boolean;
|
|
328
|
+
/**
|
|
329
|
+
* Whether well-known block types (image, file, code, table, ...) get
|
|
330
|
+
* default triggers and keywords when `items` doesn't cover them
|
|
331
|
+
* (default true). See {@link standardBlockPresets}.
|
|
332
|
+
*/
|
|
333
|
+
presets?: boolean;
|
|
334
|
+
/**
|
|
335
|
+
* Escape hatch: names the portable-text array type to resolve items
|
|
336
|
+
* against when Studio's member-schema context is unavailable. Normally
|
|
337
|
+
* unnecessary — the containing array and its members are detected
|
|
338
|
+
* automatically from where the plugin is mounted.
|
|
339
|
+
*/
|
|
340
|
+
arrayTypeName?: string;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Creates the Portable Text editor plugins component for the block-insert
|
|
344
|
+
* picker: a slash-command / Cmd+/ menu of the blocks the host array allows,
|
|
345
|
+
* plus optional markdown input rules. Zero-config is the intended call —
|
|
346
|
+
* items derive from the array the plugin is mounted on. Attach it to the
|
|
347
|
+
* array type's `components.portableText.plugins`:
|
|
348
|
+
*
|
|
349
|
+
* ```ts
|
|
350
|
+
* defineType({
|
|
351
|
+
* name: 'content',
|
|
352
|
+
* type: 'array',
|
|
353
|
+
* of: [{type: 'block'}, {type: 'callout'}],
|
|
354
|
+
* components: {
|
|
355
|
+
* portableText: {
|
|
356
|
+
* plugins: blockInsertPicker(),
|
|
357
|
+
* },
|
|
358
|
+
* },
|
|
359
|
+
* })
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
declare function blockInsertPicker(config?: BlockInsertPickerConfig): ComponentType<PortableTextPluginsProps>;
|
|
363
|
+
/**
|
|
364
|
+
* Default triggers and search keywords for block types with fixed, official
|
|
365
|
+
* names: Studio's intrinsic types (`image`, `file`), first-party plugin
|
|
366
|
+
* types (`code`, `table`, `color`, `latex`, `mux.video`), and the Media
|
|
367
|
+
* Library's `sanity.video`. Presets are deliberately presentation-only —
|
|
368
|
+
* they never add items (a type must actually be a member of the array),
|
|
369
|
+
* never set groups (partial grouping would strand unmatched items in an
|
|
370
|
+
* "Other" bucket), never affect rank, and always lose to a host's `items`
|
|
371
|
+
* entry for the same type. Opt out entirely with `presets: false`.
|
|
372
|
+
*/
|
|
373
|
+
declare const standardBlockPresets: readonly PickerItemMetadata[];
|
|
374
|
+
/**
|
|
375
|
+
* Opt-in input rules for well-known block types — spread into `inputRules`
|
|
376
|
+
* (`inputRules: [...wellKnownInputRules]`) rather than applied by default.
|
|
377
|
+
* Today: a code fence for `@sanity/code-input`'s `code` type that stores the
|
|
378
|
+
* typed fence token as the language. Like all rules, each resolves against
|
|
379
|
+
* the array's members by name or resolved type chain (so an aliased
|
|
380
|
+
* `{type: 'code', name: 'snippet'}` member still gets the fence, inserted
|
|
381
|
+
* with the member's `_type`) and is ignored when nothing matches.
|
|
382
|
+
*/
|
|
383
|
+
declare const wellKnownInputRules: readonly MarkdownInputRuleConfig[];
|
|
384
|
+
export { BLOCKQUOTE_PATTERN, BlockInsertPicker, type BlockInsertPickerConfig, type BlockInsertPickerLabels, type BlockInsertPickerProps, CODE_FENCE_PATTERN, type KeyGenerator, type LanguageEntry, type MarkdownInputRuleConfig, MarkdownInputRules, type MarkdownInputRulesProps, type PickerActionContext, type PickerInsertEvent, type PickerItem, type PickerItemAction, type PickerItemMetadata, type PickerItemsContext, type PickerItemsResolver, type PickerMode, blockInsertPicker, blockquoteRule, codeFenceRule, derivePickerItems, fenceLanguageFromMatch, filterPickerItems, normalizeFenceLanguage, standardBlockPresets, wellKnownInputRules };
|
|
385
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/labels.ts","../src/types.ts","../src/blockInsertPicker.tsx","../src/deriveItems.ts","../src/filterItems.ts","../src/inputRules.ts","../src/markdownInputRules.tsx","../src/plugin.tsx","../src/presets.ts"],"mappings":";;;;;;;;;;;KAOY;;EAEV;;EAEA;;;;;EAKA;;;;;EAKA;;EAEA;EACA;EACA;EACA;;EAEA;;;;;EAKA;;;;;;;;KCvBU;EACV;EACA;EACA;;EAEA,OAAO;;EAEP;;KAWU;EACV;EACA;EACA;;;;;;EAMA;;;;;EAKA;EACA,OAAO;EACP;;EAEA;;EAEA;EACA,QAAQ;;;;;;;;;KAUE;EACP;EAAgB,WAAW,SAAS;;EACpC;EAAqB;;;UAGT;EACf,QAAQ;EACR;;;;;;;;;;;;KAaU;EACV;EACA;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;;;;;;;;UASe;EACf,YAAY;EACZ,sBAAsB;;;;;;;KAQZ,uBACV,gBAAgB,cAChB,SAAS,gCACG;KAEF;KChFA;;;;;;;EAOV,iBAAiB;;;;;;;EAOjB;;;;;EAKA,eAAe;;;;;EAKf,UAAU,gBAAgB,cAAc,2BAA2B;;EAEnE,SAAS,QAAQ;;EAEjB,YAAY,OAAO;;;;;EAKnB;;EAEA;;;;;;EAMA;;iBAGc,oBACd,eACA,QACA,OACA,QAAQ,gBACR,UACA,cACA,SACA,cACA,YACC,yCAAsB,IAAA;;;;;;;;;;;iBCrDT,kBACd,SAAS,oBACT,oBAAmB,uBAClB;iBCpCa,kBACd,gBAAgB,cAChB,yBACU;;;;;;;;;;;;;;;;KCeA;;;;;;;KAQA;EACV,SAAS;EACT;EACA,aAAa;IAAU;IAAmB,cAAc;QAAkB;;cAgC/D,oBAAkB;cAClB,oBAAkB;;;;;KAMnB;EACV;EACA;EACA;;;iBAIc,eAAe;EAC7B;EACA,eAAe;IAAU,cAAc;QAAkB;IACvD;;;;;;;;iBAeY,cAAc;EAC5B;EACA,qBAAqB;EACrB;EACA,cAAc;IACZ;IACA,cAAc;IACd;QACI;IACJ;;iBAoBY,uBAAuB;;;;;;iBASvB,uBACd,aACA,oBAAoB,iBACpB;KC9HU;;;;;;EAMV;;EAEA,gBAAgB;;EAEhB,YAAY,OAAO;;EAEnB;;;;;;;iBAQc,qBACd,eACA,UACA,cACA,SACC;UC1Bc;;;;;;;;EAQf,iBAAiB;;;;;;EAMjB,eAAe;;;;;;;EAOf,sBAAsB;;;;;;EAMtB,UAAU,gBAAgB,cAAc,2BAA2B;;EAEnE,SAAS,QAAQ;;;;;EAKjB,YAAY,OAAO;;;;;EAKnB;;EAEA;;;;;;EAMA;;;;;;;EAOA;;;;;;;;;;;;;;;;;;;;;;iBAuBc,kBACd,SAAQ,0BACP,cAAc;;;;;;;;;;;cC1EJ,+BAA+B;;;;;;;;;;cAoB/B,8BAA8B"}
|