@railway/inkwell 0.1.0 → 1.0.0
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/README.md +77 -0
- package/dist/index.cjs +2143 -568
- package/dist/index.d.cts +385 -282
- package/dist/index.d.ts +385 -282
- package/dist/index.js +2142 -571
- package/package.json +12 -8
- package/src/styles.css +474 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,375 +1,478 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, JSX, CSSProperties, RefObject, KeyboardEvent } from 'react';
|
|
2
3
|
import { Plugin } from 'unified';
|
|
3
|
-
import { Awareness } from 'y-protocols/awareness';
|
|
4
|
-
import { XmlText } from 'yjs';
|
|
5
|
-
import { BaseEditor, BaseElement, BaseText } from 'slate';
|
|
6
|
-
import { HistoryEditor } from 'slate-history';
|
|
7
|
-
import { ReactEditor } from 'slate-react';
|
|
8
4
|
|
|
9
|
-
type RehypePlugin
|
|
10
|
-
type RehypePluginConfig
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
interface InkwellEditorProps {
|
|
15
|
-
/**
|
|
16
|
-
* Markdown content string
|
|
17
|
-
*/
|
|
5
|
+
type RehypePlugin = Plugin<any[], any>;
|
|
6
|
+
type RehypePluginConfig = RehypePlugin | [RehypePlugin, ...unknown[]];
|
|
7
|
+
interface InkwellEditorState {
|
|
8
|
+
/** Current source content. Markdown syntax is part of the content. */
|
|
18
9
|
content: string;
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
/** True when the editor has no non-whitespace content. */
|
|
11
|
+
isEmpty: boolean;
|
|
12
|
+
/** True when the Slate editable is focused. */
|
|
13
|
+
isFocused: boolean;
|
|
14
|
+
/** True when user edits are enabled. */
|
|
15
|
+
isEditable: boolean;
|
|
16
|
+
/** Current source content character count. */
|
|
17
|
+
characterCount: number;
|
|
18
|
+
/** Configured character limit, if any. */
|
|
19
|
+
characterLimit?: number;
|
|
20
|
+
/** True when `characterCount` exceeds `characterLimit`. */
|
|
21
|
+
overLimit: boolean;
|
|
22
|
+
}
|
|
23
|
+
interface InkwellEditorFocusOptions {
|
|
24
|
+
/** Where to place the caret after focusing. Defaults to preserving selection. */
|
|
25
|
+
at?: "start" | "end";
|
|
26
|
+
}
|
|
27
|
+
interface InkwellPluginPlaceholder {
|
|
28
|
+
/** Placeholder text shown by Slate while the editor is empty. */
|
|
29
|
+
text: string;
|
|
30
|
+
/** Optional hint prepended to the placeholder text. */
|
|
31
|
+
hint?: string;
|
|
32
|
+
}
|
|
33
|
+
type InkwellContentSelectionOptions = {
|
|
34
|
+
select?: "start" | "end" | "preserve";
|
|
35
|
+
};
|
|
36
|
+
interface InkwellEditorHandle {
|
|
37
|
+
/** Return a snapshot of current editor state. */
|
|
38
|
+
getState: () => InkwellEditorState;
|
|
39
|
+
/** Focus the editor and optionally move the caret. */
|
|
40
|
+
focus: (options?: InkwellEditorFocusOptions) => void;
|
|
41
|
+
/** Replace the document with empty content without calling onChange. */
|
|
42
|
+
clear: (options?: InkwellContentSelectionOptions) => void;
|
|
43
|
+
/** Replace the current document content without calling onChange. */
|
|
44
|
+
setContent: (content: string, options?: InkwellContentSelectionOptions) => void;
|
|
45
|
+
/** Insert content at the current selection. */
|
|
46
|
+
insertContent: (content: string) => void;
|
|
47
|
+
}
|
|
48
|
+
interface InkwellEditorClassNames {
|
|
49
|
+
/** Class added to the root wrapper. */
|
|
50
|
+
root?: string;
|
|
51
|
+
/** Class added to the editable surface. */
|
|
52
|
+
editor?: string;
|
|
53
|
+
}
|
|
54
|
+
interface InkwellEditorStyles {
|
|
55
|
+
/** Inline styles applied to the root wrapper. */
|
|
56
|
+
root?: CSSProperties;
|
|
57
|
+
/** Inline styles applied to the editable surface. */
|
|
58
|
+
editor?: CSSProperties;
|
|
59
|
+
}
|
|
60
|
+
interface InkwellHeadingFeatures {
|
|
61
|
+
h1?: boolean;
|
|
62
|
+
h2?: boolean;
|
|
63
|
+
h3?: boolean;
|
|
64
|
+
h4?: boolean;
|
|
65
|
+
h5?: boolean;
|
|
66
|
+
h6?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/** Controls which Markdown features the editor recognizes. */
|
|
69
|
+
interface InkwellFeatures {
|
|
70
|
+
/** Recognize heading markers. Pass per-level overrides for granular control. */
|
|
71
|
+
headings?: boolean | InkwellHeadingFeatures;
|
|
72
|
+
/** Recognize `> ` as blockquotes. */
|
|
73
|
+
blockquotes?: boolean;
|
|
74
|
+
/** Recognize fenced code blocks. */
|
|
75
|
+
codeBlocks?: boolean;
|
|
76
|
+
/** Recognize standalone image syntax as block images. */
|
|
77
|
+
images?: boolean;
|
|
78
|
+
}
|
|
79
|
+
interface InkwellEditorProps {
|
|
80
|
+
/** Source content string. Markdown syntax is part of the content. */
|
|
81
|
+
content?: string;
|
|
82
|
+
/** Called with source content on every document change. */
|
|
22
83
|
onChange?: (content: string) => void;
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
84
|
+
/** Called with a full editor state snapshot whenever content, focus, or editability changes. */
|
|
85
|
+
onStateChange?: (state: InkwellEditorState) => void;
|
|
86
|
+
/** Additional CSS class for the root wrapper. Alias for `classNames.root`. */
|
|
26
87
|
className?: string;
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
88
|
+
/** Additional CSS classes for editor slots. */
|
|
89
|
+
classNames?: InkwellEditorClassNames;
|
|
90
|
+
/** Inline styles for editor slots. */
|
|
91
|
+
styles?: InkwellEditorStyles;
|
|
92
|
+
/** Placeholder text shown when editor is empty. */
|
|
30
93
|
placeholder?: string;
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
|
|
94
|
+
/** Whether users can edit the document. Defaults to true. */
|
|
95
|
+
editable?: boolean;
|
|
96
|
+
/** Editor plugins. */
|
|
34
97
|
plugins?: InkwellPlugin[];
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Configure which block-level decorations the editor recognizes. All enabled by default.
|
|
41
|
-
*/
|
|
42
|
-
decorations?: InkwellDecorations;
|
|
43
|
-
/**
|
|
44
|
-
* Enable real-time collaborative editing via Yjs
|
|
45
|
-
*/
|
|
46
|
-
collaboration?: CollaborationConfig;
|
|
47
|
-
/**
|
|
48
|
-
* Include the built-in bubble menu plugin (default: true). Pass `false` to
|
|
49
|
-
* disable the built-in toolbar; consumers can still add their own via `plugins`.
|
|
50
|
-
*/
|
|
98
|
+
/** Custom rehype plugins for the syntax highlighting pipeline. */
|
|
99
|
+
rehypePlugins?: RehypePluginConfig[];
|
|
100
|
+
/** Configure which Markdown features the editor recognizes. */
|
|
101
|
+
features?: InkwellFeatures;
|
|
102
|
+
/** Include the built-in bubble menu plugin. Defaults to true. */
|
|
51
103
|
bubbleMenu?: boolean;
|
|
104
|
+
/** Soft character budget. Typing, paste, and inserts past the limit are allowed. */
|
|
105
|
+
characterLimit?: number;
|
|
106
|
+
/** Called on every document change with the current character count and configured limit. */
|
|
107
|
+
onCharacterCount?: (count: number, limit?: number) => void;
|
|
108
|
+
/** When true, Enter submits the editor instead of inserting a newline. */
|
|
109
|
+
submitOnEnter?: boolean;
|
|
110
|
+
/** Called when submitOnEnter handles Enter. */
|
|
111
|
+
onSubmit?: (content: string) => void;
|
|
52
112
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Props for the InkwellRenderer component.
|
|
55
|
-
*/
|
|
56
113
|
interface InkwellRendererProps {
|
|
57
|
-
/**
|
|
58
|
-
* Markdown content string
|
|
59
|
-
*/
|
|
114
|
+
/** Markdown source content string. */
|
|
60
115
|
content: string;
|
|
61
|
-
/**
|
|
62
|
-
* Additional CSS class for the wrapper element
|
|
63
|
-
*/
|
|
116
|
+
/** Additional CSS class for the wrapper element. */
|
|
64
117
|
className?: string;
|
|
65
|
-
/**
|
|
66
|
-
* Custom component overrides for rendered markdown elements
|
|
67
|
-
*/
|
|
118
|
+
/** Custom component overrides for rendered markdown elements. */
|
|
68
119
|
components?: InkwellComponents;
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
120
|
+
/** Custom rehype plugins for the markdown pipeline. */
|
|
121
|
+
rehypePlugins?: RehypePluginConfig[];
|
|
122
|
+
/** Mention patterns to expand in rendered text. */
|
|
123
|
+
mentions?: MentionRenderer[];
|
|
124
|
+
}
|
|
125
|
+
interface ParseMarkdownOptions {
|
|
126
|
+
components?: InkwellComponents;
|
|
127
|
+
rehypePlugins?: RehypePluginConfig[];
|
|
128
|
+
mentions?: MentionRenderer[];
|
|
129
|
+
}
|
|
130
|
+
interface MentionRenderer {
|
|
131
|
+
/** Regular expression applied to text-node content. */
|
|
132
|
+
pattern: RegExp;
|
|
133
|
+
/** Map a match to a React node (rendered in place of the match text). */
|
|
134
|
+
resolve: (match: RegExpExecArray) => ReactNode;
|
|
77
135
|
}
|
|
78
|
-
/**
|
|
79
|
-
* Map of HTML element names to custom React components
|
|
80
|
-
*/
|
|
81
136
|
type InkwellComponents = Partial<{
|
|
82
137
|
[K in keyof JSX.IntrinsicElements]: (props: JSX.IntrinsicElements[K] & {
|
|
83
138
|
children?: ReactNode;
|
|
84
139
|
}) => ReactNode;
|
|
85
140
|
}>;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
* - `"Control+/"` — modifier combo, prevents default
|
|
91
|
-
* - `"@"` — single character, typed into editor (e.g. for mentions)
|
|
92
|
-
*/
|
|
93
|
-
interface PluginTrigger {
|
|
94
|
-
/**
|
|
95
|
-
* Key combo (tinykeys format)
|
|
96
|
-
*/
|
|
141
|
+
type InkwellPluginActivation = {
|
|
142
|
+
type: "always";
|
|
143
|
+
} | {
|
|
144
|
+
type: "trigger";
|
|
97
145
|
key: string;
|
|
146
|
+
} | {
|
|
147
|
+
type: "manual";
|
|
148
|
+
};
|
|
149
|
+
type SubscribeForwardedKey = (listener: (key: string) => void) => () => void;
|
|
150
|
+
interface InkwellPluginEditor {
|
|
151
|
+
getState: () => InkwellEditorState;
|
|
152
|
+
isEmpty: () => boolean;
|
|
153
|
+
focus: (options?: InkwellEditorFocusOptions) => void;
|
|
154
|
+
clear: (options?: InkwellContentSelectionOptions) => void;
|
|
155
|
+
setContent: (content: string, options?: InkwellContentSelectionOptions) => void;
|
|
156
|
+
insertContent: (content: string) => void;
|
|
157
|
+
getContentBeforeCursor: () => string | null;
|
|
158
|
+
getCurrentBlockContent: () => string | null;
|
|
159
|
+
getCurrentBlockContentBeforeCursor: () => string | null;
|
|
160
|
+
replaceCurrentBlockContent: (content: string) => void;
|
|
161
|
+
clearCurrentBlock: () => void;
|
|
162
|
+
wrapSelection: (before: string, after: string) => void;
|
|
163
|
+
insertImage: (image: {
|
|
164
|
+
id?: string;
|
|
165
|
+
url: string;
|
|
166
|
+
alt: string;
|
|
167
|
+
}) => string;
|
|
168
|
+
updateImage: (id: string, image: {
|
|
169
|
+
url?: string;
|
|
170
|
+
alt?: string;
|
|
171
|
+
}) => void;
|
|
172
|
+
removeImage: (id: string) => void;
|
|
98
173
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Props passed to every plugin's render function on every render
|
|
101
|
-
*/
|
|
102
174
|
interface PluginRenderProps {
|
|
103
|
-
/**
|
|
104
|
-
* Whether this plugin's trigger has fired (always true for plugins without triggers)
|
|
105
|
-
*/
|
|
175
|
+
/** Whether this plugin is active. Always-on plugins receive true every render. */
|
|
106
176
|
active: boolean;
|
|
107
|
-
/**
|
|
108
|
-
* Text typed after the trigger fired
|
|
109
|
-
*/
|
|
177
|
+
/** Content typed after the trigger fired. */
|
|
110
178
|
query: string;
|
|
111
|
-
/**
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
onSelect: (text: string) => void;
|
|
115
|
-
/**
|
|
116
|
-
* Deactivate this plugin (resets `active` to false)
|
|
117
|
-
*/
|
|
179
|
+
/** Insert content into the editor at the current cursor position. */
|
|
180
|
+
onSelect: (content: string) => void;
|
|
181
|
+
/** Deactivate this plugin. */
|
|
118
182
|
onDismiss: () => void;
|
|
119
|
-
/**
|
|
120
|
-
* Cursor position when the trigger fired
|
|
121
|
-
*/
|
|
183
|
+
/** Cursor position when the trigger fired. */
|
|
122
184
|
position: {
|
|
123
185
|
top: number;
|
|
124
186
|
left: number;
|
|
125
187
|
};
|
|
126
|
-
/**
|
|
127
|
-
* Ref to the editor's contenteditable element
|
|
128
|
-
*/
|
|
188
|
+
/** Ref to the editable DOM element. */
|
|
129
189
|
editorRef: RefObject<HTMLDivElement | null>;
|
|
130
|
-
/**
|
|
131
|
-
|
|
132
|
-
|
|
190
|
+
/** Narrow editor controller for plugin actions. */
|
|
191
|
+
editor: InkwellPluginEditor;
|
|
192
|
+
/** Wrap the current selection with markdown markers. */
|
|
133
193
|
wrapSelection: (before: string, after: string) => void;
|
|
194
|
+
/** Subscribe to editor-forwarded keystrokes while this plugin is active. */
|
|
195
|
+
subscribeForwardedKey: SubscribeForwardedKey;
|
|
196
|
+
}
|
|
197
|
+
interface PluginInsertDataContext {
|
|
198
|
+
/** Narrow editor controller for plugin actions. */
|
|
199
|
+
editor: InkwellPluginEditor;
|
|
200
|
+
/** Continue with the editor's default paste/drop handling. */
|
|
201
|
+
insertData: (data: DataTransfer) => void;
|
|
134
202
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Context passed to a plugin's `onKeyDown` handler.
|
|
137
|
-
*/
|
|
138
203
|
interface PluginKeyDownContext {
|
|
139
|
-
/**
|
|
140
|
-
|
|
141
|
-
|
|
204
|
+
/** Narrow editor controller for plugin actions. */
|
|
205
|
+
editor: InkwellPluginEditor;
|
|
206
|
+
/** Wrap the current selection with markdown markers. */
|
|
142
207
|
wrapSelection: (before: string, after: string) => void;
|
|
208
|
+
/** Activate the current plugin. */
|
|
209
|
+
activate: (options?: {
|
|
210
|
+
query?: string;
|
|
211
|
+
}) => void;
|
|
212
|
+
/** Dismiss the active plugin. */
|
|
213
|
+
dismiss: () => void;
|
|
143
214
|
}
|
|
144
|
-
/**
|
|
145
|
-
* A Inkwell editor plugin.
|
|
146
|
-
*
|
|
147
|
-
* Every plugin is always rendered. Plugins with a `trigger` receive
|
|
148
|
-
* `active: true` when the trigger fires and `active: false` otherwise.
|
|
149
|
-
* Plugins without a trigger always receive `active: true`.
|
|
150
|
-
*/
|
|
151
215
|
interface InkwellPlugin {
|
|
152
|
-
/**
|
|
153
|
-
* Unique plugin name
|
|
154
|
-
*/
|
|
216
|
+
/** Unique plugin name. */
|
|
155
217
|
name: string;
|
|
156
|
-
/**
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
* `event.preventDefault()` to stop further dispatch for this event.
|
|
168
|
-
*/
|
|
218
|
+
/** Activation behavior. Defaults to `{ type: "always" }`. */
|
|
219
|
+
activation?: InkwellPluginActivation;
|
|
220
|
+
/** Render the plugin UI. Omit for headless plugins. */
|
|
221
|
+
render?: (props: PluginRenderProps) => ReactNode;
|
|
222
|
+
/** Optional dynamic placeholder. */
|
|
223
|
+
getPlaceholder?: (editor: InkwellPluginEditor) => string | InkwellPluginPlaceholder | null;
|
|
224
|
+
/** Optional guard for trigger activation. */
|
|
225
|
+
shouldTrigger?: (event: KeyboardEvent, ctx: PluginKeyDownContext) => boolean;
|
|
226
|
+
/** Optional document-change hook. */
|
|
227
|
+
onEditorChange?: (editor: InkwellPluginEditor) => void;
|
|
228
|
+
/** Optional keydown handler. */
|
|
169
229
|
onKeyDown?: (event: KeyboardEvent, ctx: PluginKeyDownContext) => void;
|
|
230
|
+
/** Optional keydown handler while this plugin is active. */
|
|
231
|
+
onActiveKeyDown?: (event: KeyboardEvent, ctx: PluginKeyDownContext) => false | void;
|
|
232
|
+
/** Optional paste/drop hook. Return true when the data was handled. */
|
|
233
|
+
onInsertData?: (data: DataTransfer, ctx: PluginInsertDataContext) => boolean | void;
|
|
234
|
+
/** Optional one-time setup. */
|
|
235
|
+
setup?: (editor: InkwellPluginEditor) => void | (() => void);
|
|
170
236
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Props passed to each bubble menu item component.
|
|
173
|
-
*/
|
|
174
237
|
interface BubbleMenuItemProps {
|
|
175
|
-
/**
|
|
176
|
-
* Wrap or unwrap the current selection with markdown markers. Toggles if already wrapped.
|
|
177
|
-
*/
|
|
238
|
+
/** Wrap or unwrap the current selection with markdown markers. */
|
|
178
239
|
wrapSelection: (before: string, after: string) => void;
|
|
179
240
|
}
|
|
180
|
-
/**
|
|
181
|
-
* An item in the bubble menu.
|
|
182
|
-
*/
|
|
183
241
|
interface BubbleMenuItem {
|
|
184
|
-
/**
|
|
185
|
-
* Unique key for React reconciliation
|
|
186
|
-
*/
|
|
187
242
|
key: string;
|
|
188
|
-
/**
|
|
189
|
-
* Optional keyboard shortcut (single key, used with Cmd/Ctrl).
|
|
190
|
-
*/
|
|
191
243
|
shortcut?: string;
|
|
192
|
-
/**
|
|
193
|
-
* Action to run when the shortcut fires. Receives wrapSelection.
|
|
194
|
-
*/
|
|
195
244
|
onShortcut?: (wrapSelection: (before: string, after: string) => void) => void;
|
|
196
|
-
/**
|
|
197
|
-
* React component to render in the menu. Receives `wrapSelection`.
|
|
198
|
-
*/
|
|
199
245
|
render: (props: BubbleMenuItemProps) => ReactNode;
|
|
200
246
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Snippet item for the snippets plugin
|
|
203
|
-
*/
|
|
204
247
|
interface Snippet {
|
|
205
|
-
/**
|
|
206
|
-
* Display title (searchable)
|
|
207
|
-
*/
|
|
208
248
|
title: string;
|
|
209
|
-
/**
|
|
210
|
-
* Markdown content to insert
|
|
211
|
-
*/
|
|
212
249
|
content: string;
|
|
213
250
|
}
|
|
251
|
+
|
|
214
252
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
253
|
+
* Public wrapper. Returns `null` during SSR so all hooks in the client
|
|
254
|
+
* component below are always called in the same order on the client.
|
|
217
255
|
*/
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
*
|
|
233
|
-
*/
|
|
234
|
-
|
|
256
|
+
declare const InkwellEditor: react.ForwardRefExoticComponent<InkwellEditorProps & react.RefAttributes<InkwellEditorHandle>>;
|
|
257
|
+
|
|
258
|
+
type AttachmentUploadResult = string | {
|
|
259
|
+
url: string;
|
|
260
|
+
alt?: string;
|
|
261
|
+
[key: string]: unknown;
|
|
262
|
+
};
|
|
263
|
+
interface Attachment {
|
|
264
|
+
url: string;
|
|
265
|
+
filename: string;
|
|
266
|
+
mime: string;
|
|
267
|
+
size: number;
|
|
268
|
+
/**
|
|
269
|
+
* Any extra fields returned from `onUpload` (e.g. a service-side
|
|
270
|
+
* record ID) are forwarded onto the attachment.
|
|
271
|
+
*/
|
|
272
|
+
[key: string]: unknown;
|
|
273
|
+
}
|
|
274
|
+
interface AttachmentsPluginOptions {
|
|
235
275
|
/**
|
|
236
|
-
*
|
|
276
|
+
* Upload a single file and resolve to the public URL, or an object
|
|
277
|
+
* containing the URL plus optional metadata. Rejection triggers
|
|
278
|
+
* `onError`.
|
|
237
279
|
*/
|
|
238
|
-
|
|
280
|
+
onUpload: (file: File) => Promise<AttachmentUploadResult>;
|
|
239
281
|
/**
|
|
240
|
-
*
|
|
282
|
+
* MIME-type filter. Accepts exact matches (`image/png`) and wildcards
|
|
283
|
+
* (`image/*`). Files that don't match pass through untouched.
|
|
241
284
|
*/
|
|
242
|
-
|
|
285
|
+
accept?: string;
|
|
243
286
|
/**
|
|
244
|
-
*
|
|
287
|
+
* Placeholder alt text shown on the inserted image element while an
|
|
288
|
+
* image upload is in flight. Defaults to `"Uploading…"`.
|
|
245
289
|
*/
|
|
246
|
-
|
|
290
|
+
uploadingPlaceholder?: (file: File) => string;
|
|
247
291
|
/**
|
|
248
|
-
*
|
|
292
|
+
* Fired after a non-image file finishes uploading. Use this to track
|
|
293
|
+
* attachments in your own state for message submission, chip UI, etc.
|
|
294
|
+
*
|
|
295
|
+
* Image files (MIME `image/*`) are inserted inline as `<img>` and do
|
|
296
|
+
* NOT fire this callback. If omitted, non-image files are passed
|
|
297
|
+
* through to the editor's default paste/drop handling instead of
|
|
298
|
+
* being silently uploaded and discarded.
|
|
249
299
|
*/
|
|
250
|
-
|
|
300
|
+
onAttachmentAdd?: (attachment: Attachment) => void;
|
|
251
301
|
/**
|
|
252
|
-
*
|
|
302
|
+
* Called when `onUpload` rejects, or when the returned URL fails the
|
|
303
|
+
* URL safety allowlist. For image uploads, the placeholder element is
|
|
304
|
+
* removed before this fires.
|
|
253
305
|
*/
|
|
254
|
-
|
|
306
|
+
onError?: (error: unknown, file: File) => void;
|
|
255
307
|
}
|
|
256
308
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
309
|
+
* Intercepts file paste/drop, uploads via `onUpload`, and either
|
|
310
|
+
* inserts an inline image element (for `image/*` files) or fires
|
|
311
|
+
* `onAttachmentAdd` (for non-image files). Non-image files with no
|
|
312
|
+
* `onAttachmentAdd` callback pass through to the editor's default
|
|
313
|
+
* paste/drop handling.
|
|
262
314
|
*/
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Yjs shared type for the document. Create via `doc.get("content", Y.XmlText)`.
|
|
266
|
-
*/
|
|
267
|
-
sharedType: XmlText;
|
|
268
|
-
/**
|
|
269
|
-
* Awareness instance for remote cursor/presence sharing.
|
|
270
|
-
*/
|
|
271
|
-
awareness: Awareness;
|
|
272
|
-
/**
|
|
273
|
-
* Local user metadata, displayed on remote cursors.
|
|
274
|
-
*/
|
|
275
|
-
user: {
|
|
276
|
-
name: string;
|
|
277
|
-
color: string;
|
|
278
|
-
};
|
|
279
|
-
}
|
|
315
|
+
declare function createAttachmentsPlugin(options: AttachmentsPluginOptions): InkwellPlugin;
|
|
280
316
|
|
|
281
|
-
declare function InkwellEditor$1({ content, onChange, className, placeholder, plugins: userPlugins, rehypePlugins, decorations, collaboration, bubbleMenu, }: InkwellEditorProps): ReactNode;
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Element types in the Inkwell editor
|
|
285
|
-
*/
|
|
286
|
-
type ElementType = "paragraph" | "code-fence" | "code-line" | "blockquote" | "list-item" | "heading";
|
|
287
317
|
/**
|
|
288
|
-
*
|
|
318
|
+
* Default bubble menu items: bold, italic, strikethrough.
|
|
289
319
|
*/
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Unique element identifier. Session-scoped, not persisted in markdown.
|
|
294
|
-
*/
|
|
295
|
-
id: string;
|
|
320
|
+
declare const defaultBubbleMenuItems: BubbleMenuItem[];
|
|
321
|
+
interface BubbleMenuOptions {
|
|
296
322
|
/**
|
|
297
|
-
*
|
|
323
|
+
* Menu items. Defaults to bold, italic, strikethrough.
|
|
298
324
|
*/
|
|
299
|
-
|
|
300
|
-
children: InkwellText[];
|
|
325
|
+
items?: BubbleMenuItem[];
|
|
301
326
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
327
|
+
declare function createBubbleMenuPlugin(options?: BubbleMenuOptions): InkwellPlugin;
|
|
328
|
+
|
|
329
|
+
interface CompletionsPluginOptions {
|
|
330
|
+
/** Unique plugin name. Defaults to `completions`. */
|
|
331
|
+
name?: string;
|
|
332
|
+
/** Completion content to render as placeholder text. Return null when inactive. */
|
|
333
|
+
getCompletion: () => string | null;
|
|
334
|
+
/** Whether to show a loading placeholder while no completion is available. */
|
|
335
|
+
isLoading?: () => boolean;
|
|
336
|
+
/** Text shown while `isLoading()` is true. */
|
|
337
|
+
loadingText?: string;
|
|
338
|
+
/** Hint rendered while the completion placeholder is visible. */
|
|
339
|
+
acceptHint?: string;
|
|
340
|
+
/** Called after Tab accepts and inserts the completion. */
|
|
341
|
+
onAccept?: (completion: string) => void;
|
|
342
|
+
/** Called when Escape or user typing dismisses the current completion. */
|
|
343
|
+
onDismiss?: (completion: string) => void;
|
|
344
|
+
/**
|
|
345
|
+
* Called when an accepted completion is undone back to an empty document.
|
|
346
|
+
* Enabled by default; set `restoreOnUndo` to false to opt out.
|
|
347
|
+
*/
|
|
348
|
+
onRestore?: (completion: string) => void;
|
|
349
|
+
/** Whether undo-to-empty should restore the accepted completion. Defaults to true. */
|
|
350
|
+
restoreOnUndo?: boolean;
|
|
318
351
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
352
|
+
declare function createCompletionsPlugin({ name, getCompletion, isLoading, loadingText, acceptHint, onAccept, onDismiss, onRestore, restoreOnUndo, }: CompletionsPluginOptions): InkwellPlugin;
|
|
353
|
+
|
|
354
|
+
interface EmojiItem {
|
|
355
|
+
/** Emoji glyph inserted into the document. */
|
|
356
|
+
emoji: string;
|
|
357
|
+
/** Primary searchable name, without surrounding colons. */
|
|
358
|
+
name: string;
|
|
359
|
+
/** Optional aliases/shortcodes, without surrounding colons. */
|
|
360
|
+
shortcodes?: string[];
|
|
361
|
+
/** Optional searchable tags. */
|
|
362
|
+
tags?: string[];
|
|
363
|
+
}
|
|
364
|
+
interface EmojiPluginBaseOptions<T extends EmojiItem> {
|
|
365
|
+
name?: string;
|
|
366
|
+
trigger?: string;
|
|
367
|
+
renderItem?: (item: T, active: boolean) => ReactNode;
|
|
368
|
+
emptyMessage?: string;
|
|
329
369
|
}
|
|
370
|
+
type IsExactEmojiItem<T extends EmojiItem> = EmojiItem extends T ? T extends EmojiItem ? true : false : false;
|
|
371
|
+
type EmojiPluginOptions<T extends EmojiItem = EmojiItem> = EmojiPluginBaseOptions<T> & (IsExactEmojiItem<T> extends true ? {
|
|
372
|
+
emojis?: T[];
|
|
373
|
+
search?: (query: string) => Promise<T[]> | T[];
|
|
374
|
+
} : {
|
|
375
|
+
emojis: T[];
|
|
376
|
+
search?: (query: string) => Promise<T[]> | T[];
|
|
377
|
+
} | {
|
|
378
|
+
emojis?: T[];
|
|
379
|
+
search: (query: string) => Promise<T[]> | T[];
|
|
380
|
+
});
|
|
381
|
+
declare const defaultEmojis: EmojiItem[];
|
|
382
|
+
declare function createEmojiPlugin<T extends EmojiItem>(options: EmojiPluginOptions<T>): InkwellPlugin;
|
|
383
|
+
declare function createEmojiPlugin(options?: EmojiPluginOptions): InkwellPlugin;
|
|
330
384
|
|
|
331
385
|
/**
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
* Each line becomes its own element. Block-level patterns (code fences,
|
|
335
|
-
* blockquotes, list items, headings) get their own element types based
|
|
336
|
-
* on the `decorations` config. Everything else is a paragraph. Text content
|
|
337
|
-
* is stored verbatim — visual formatting is handled by decorations at
|
|
338
|
-
* render time, not in the data model.
|
|
386
|
+
* Item shape a mentions plugin operates on. `id` is required so the default
|
|
387
|
+
* reference-marker form (`@<marker>[<id>]`) has something to persist.
|
|
339
388
|
*/
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
389
|
+
interface MentionItem {
|
|
390
|
+
id: string;
|
|
391
|
+
title: string;
|
|
392
|
+
}
|
|
393
|
+
interface MentionsPluginOptions<T extends MentionItem = MentionItem> {
|
|
394
|
+
/** Unique plugin name. Defaults to `mentions`. */
|
|
395
|
+
name?: string;
|
|
396
|
+
/** Key that opens the picker. Defaults to `@`. */
|
|
397
|
+
trigger?: string;
|
|
398
|
+
/**
|
|
399
|
+
* Persisted marker name used when `onSelect` is not provided. Produces
|
|
400
|
+
* `@<marker>[<id>]` in the document. Defaults to `mention`.
|
|
401
|
+
*/
|
|
402
|
+
marker?: string;
|
|
403
|
+
/** Async search callback. Return items matching the query. */
|
|
404
|
+
search: (query: string) => Promise<T[]> | T[];
|
|
405
|
+
/** Render a single item row in the picker. */
|
|
406
|
+
renderItem: (item: T, active: boolean) => ReactNode;
|
|
407
|
+
/**
|
|
408
|
+
* Map a selected item to the string inserted into the document. When
|
|
409
|
+
* omitted, the plugin inserts the marker form `@<marker>[<id>]`.
|
|
410
|
+
*/
|
|
411
|
+
onSelect?: (item: T) => string;
|
|
412
|
+
/** Fallback message when `search` returns no results. */
|
|
413
|
+
emptyMessage?: string;
|
|
414
|
+
}
|
|
344
415
|
/**
|
|
345
|
-
*
|
|
416
|
+
* Generic mentions plugin. Pick items from an async source and insert either
|
|
417
|
+
* the expanded content (via `onSelect`) or a persisted marker `@<marker>[<id>]`.
|
|
346
418
|
*/
|
|
347
|
-
declare
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
419
|
+
declare function createMentionsPlugin<T extends MentionItem = MentionItem>(options: MentionsPluginOptions<T>): InkwellPlugin;
|
|
420
|
+
|
|
421
|
+
interface SlashCommandChoice {
|
|
422
|
+
value: string;
|
|
423
|
+
label: string;
|
|
424
|
+
disabled?: boolean;
|
|
353
425
|
}
|
|
354
|
-
|
|
426
|
+
interface SlashCommandArg {
|
|
427
|
+
name: string;
|
|
428
|
+
description: string;
|
|
429
|
+
choices?: SlashCommandChoice[];
|
|
430
|
+
fetchChoices?: () => Promise<SlashCommandChoice[]>;
|
|
431
|
+
}
|
|
432
|
+
interface SlashCommandItem {
|
|
433
|
+
name: string;
|
|
434
|
+
description: string;
|
|
435
|
+
aliases?: string[];
|
|
436
|
+
arg?: SlashCommandArg;
|
|
437
|
+
disabled?: () => string | false;
|
|
438
|
+
}
|
|
439
|
+
interface SlashCommandExecution {
|
|
440
|
+
name: string;
|
|
441
|
+
args: Record<string, string>;
|
|
442
|
+
raw: string;
|
|
443
|
+
}
|
|
444
|
+
interface SlashCommandsPluginOptions<T extends SlashCommandItem = SlashCommandItem> {
|
|
445
|
+
/** Plugin name. Defaults to `"slash-commands"`. */
|
|
446
|
+
name?: string;
|
|
447
|
+
/** Commands shown in the picker. */
|
|
448
|
+
commands: T[];
|
|
449
|
+
/** Called whenever the menu transitions in and out of the execute phase. */
|
|
450
|
+
onReadyChange?: (ready: boolean) => void;
|
|
451
|
+
/** Called with the structured execution payload when the user presses Enter
|
|
452
|
+
* during the execute phase. */
|
|
453
|
+
onExecute?: (command: SlashCommandExecution) => void;
|
|
454
|
+
/** Fallback message when filtering returns no commands. */
|
|
455
|
+
emptyMessage?: string;
|
|
456
|
+
}
|
|
457
|
+
declare const createSlashCommandsPlugin: <T extends SlashCommandItem>({ name, commands, onReadyChange, onExecute, emptyMessage, }: SlashCommandsPluginOptions<T>) => InkwellPlugin;
|
|
355
458
|
|
|
356
|
-
|
|
459
|
+
interface SnippetsPluginOptions {
|
|
460
|
+
name?: string;
|
|
461
|
+
trigger?: string;
|
|
357
462
|
snippets: Snippet[];
|
|
358
|
-
|
|
359
|
-
}): InkwellPlugin;
|
|
463
|
+
}
|
|
464
|
+
declare function createSnippetsPlugin({ snippets, name, trigger, }: SnippetsPluginOptions): InkwellPlugin;
|
|
360
465
|
|
|
361
466
|
/**
|
|
362
467
|
* Convert an HTML string to a markdown string
|
|
363
468
|
*/
|
|
364
|
-
declare function
|
|
469
|
+
declare function htmlToMarkdown(html: string): string;
|
|
365
470
|
|
|
366
|
-
declare function InkwellRenderer({ content, className, components, rehypePlugins,
|
|
471
|
+
declare function InkwellRenderer({ content, className, components, rehypePlugins, mentions, }: InkwellRendererProps): ReactNode;
|
|
367
472
|
|
|
368
|
-
type RehypePlugin = Plugin<any[], any>;
|
|
369
|
-
type RehypePluginConfig = RehypePlugin | [RehypePlugin, Record<string, unknown>];
|
|
370
473
|
/**
|
|
371
474
|
* Parse a markdown string into React elements synchronously
|
|
372
475
|
*/
|
|
373
|
-
declare function parseMarkdown(
|
|
476
|
+
declare function parseMarkdown(content: string, options?: ParseMarkdownOptions): ReactNode;
|
|
374
477
|
|
|
375
|
-
export { type BubbleMenuItem, type BubbleMenuItemProps, type
|
|
478
|
+
export { type Attachment, type AttachmentUploadResult, type AttachmentsPluginOptions, type BubbleMenuItem, type BubbleMenuItemProps, type BubbleMenuOptions, type CompletionsPluginOptions, type EmojiItem, type EmojiPluginOptions, type InkwellComponents, InkwellEditor, type InkwellEditorClassNames, type InkwellEditorFocusOptions, type InkwellEditorHandle, type InkwellEditorProps, type InkwellEditorState, type InkwellEditorStyles, type InkwellFeatures, type InkwellPlugin, type InkwellPluginActivation, type InkwellPluginEditor, type InkwellPluginPlaceholder, InkwellRenderer, type InkwellRendererProps, type MentionItem, type MentionRenderer, type MentionsPluginOptions, type ParseMarkdownOptions, type PluginInsertDataContext, type PluginKeyDownContext, type PluginRenderProps, type RehypePluginConfig, type SlashCommandArg, type SlashCommandChoice, type SlashCommandExecution, type SlashCommandItem, type SlashCommandsPluginOptions, type Snippet, type SnippetsPluginOptions, type SubscribeForwardedKey, createAttachmentsPlugin, createBubbleMenuPlugin, createCompletionsPlugin, createEmojiPlugin, createMentionsPlugin, createSlashCommandsPlugin, createSnippetsPlugin, defaultBubbleMenuItems, defaultEmojis, htmlToMarkdown, parseMarkdown };
|