tiptop-editor 1.2.0 → 1.4.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 CHANGED
@@ -43,7 +43,7 @@ export function Editor() {
43
43
  }
44
44
  ```
45
45
 
46
- `editorOptions` accepts the same options as `useEditor` from `@tiptap/react`, except `extensions`, which is managed internally by the package.
46
+ `editorOptions` accepts the same options as `useEditor` from `@tiptap/react`, except `extensions`, which is managed internally by the package. To add your own extensions, use `editorOptions.extraExtensions`.
47
47
 
48
48
  ## Editor Ref and Events
49
49
 
@@ -79,6 +79,99 @@ Available ref methods:
79
79
  - `off(event, callback?)`
80
80
  - `once(event, callback)`
81
81
 
82
+ ## Extending the Editor
83
+
84
+ The package now supports two extension points:
85
+
86
+ - `editorOptions.extraExtensions`
87
+ Appends custom Tiptap extensions after the built-in set.
88
+ - `slots`
89
+ Lets you inject custom React UI around the editor and inside the selection menus.
90
+
91
+ ### Add custom Tiptap extensions
92
+
93
+ ```tsx
94
+ import { Extension } from '@tiptap/core'
95
+ import { TiptopEditor } from 'tiptop-editor'
96
+
97
+ const MyExtension = Extension.create({
98
+ name: 'myExtension',
99
+ })
100
+
101
+ export function EditorWithExtraExtensions() {
102
+ return (
103
+ <TiptopEditor
104
+ editorOptions={{
105
+ immediatelyRender: false,
106
+ extraExtensions: [MyExtension],
107
+ }}
108
+ />
109
+ )
110
+ }
111
+ ```
112
+
113
+ `extraExtensions` is additive only. If you pass an extension with the same name as one of the built-in extensions, the editor will warn in the console and show a toast because duplicate extension names can lead to unstable behavior.
114
+
115
+ ### Add custom UI with slots
116
+
117
+ Supported slots:
118
+
119
+ - `editorTop`
120
+ - `editorBottom`
121
+ - `selectionMenuPrepend`
122
+ - `selectionMenuAppend`
123
+ - `tableMenuPrepend`
124
+ - `tableMenuAppend`
125
+
126
+ Each slot accepts either:
127
+
128
+ - a React node
129
+ - a render function receiving `{ editor }`
130
+
131
+ ```tsx
132
+ <TiptopEditor
133
+ slots={{
134
+ editorTop: ({ editor }) => (
135
+ <button onClick={() => editor.chain().focus().insertContent('<p>Draft</p>').run()}>
136
+ Insert draft
137
+ </button>
138
+ ),
139
+ }}
140
+ />
141
+ ```
142
+
143
+ ### Use the editor context hook
144
+
145
+ For slotted components, you can consume the current editor instance through `useTiptopEditor()` instead of passing `editor` down manually.
146
+
147
+ ```tsx
148
+ import { TiptopEditor, useTiptopEditor } from 'tiptop-editor'
149
+
150
+ function AiToolbar() {
151
+ const editor = useTiptopEditor()
152
+
153
+ if (!editor) {
154
+ return null
155
+ }
156
+
157
+ return (
158
+ <button onClick={() => editor.chain().focus().insertContent('<p>AI draft</p>').run()}>
159
+ Insert draft
160
+ </button>
161
+ )
162
+ }
163
+
164
+ export function EditorWithSlots() {
165
+ return (
166
+ <TiptopEditor
167
+ slots={{
168
+ editorTop: <AiToolbar />,
169
+ }}
170
+ />
171
+ )
172
+ }
173
+ ```
174
+
82
175
  ## Custom Editor UI Options
83
176
 
84
177
  `TiptopEditor` also supports a few package-specific options inside `editorOptions`:
@@ -97,6 +190,8 @@ Available ref methods:
97
190
  Disables the default HeroUI `Card` wrapper and removes the editor's built-in padding. Use this when you want the editor to live inside your own container/layout.
98
191
  - `showDragHandle`
99
192
  Controls whether the block drag handle is rendered. Default: `true`.
193
+ - `extraExtensions`
194
+ Appends custom Tiptap extensions after the built-in editor set.
100
195
 
101
196
  ## Built-in Extensions
102
197
 
@@ -242,7 +337,7 @@ Example:
242
337
  ## Notes
243
338
 
244
339
  - If you use SSR, keep `immediatelyRender: false`.
245
- - The package manages the editor extensions internally, so custom `extensions` are intentionally not accepted yet.
340
+ - The package manages the built-in editor extensions internally. Use `editorOptions.extraExtensions` to append your own feature extensions.
246
341
 
247
342
  ## Feedback
248
343
 
@@ -1,4 +1,4 @@
1
1
  import { default as React } from 'react';
2
2
  import { TextSelectionMenuProps } from '../../types';
3
- declare const _default: React.MemoExoticComponent<({ editor }: TextSelectionMenuProps) => import("react/jsx-runtime").JSX.Element>;
3
+ declare const _default: React.MemoExoticComponent<({ editor, prepend, append }: TextSelectionMenuProps) => import("react/jsx-runtime").JSX.Element>;
4
4
  export default _default;
@@ -14,3 +14,4 @@ export default meta;
14
14
  type Story = StoryObj<typeof meta>;
15
15
  export declare const Default: Story;
16
16
  export declare const Frameless: Story;
17
+ export declare const WithSlots: Story;
@@ -0,0 +1,3 @@
1
+ import { Editor } from '@tiptap/react';
2
+ export declare const TiptopEditorContext: import('react').Context<Editor | null>;
3
+ export declare const useTiptopEditor: () => Editor | null;
@@ -0,0 +1,8 @@
1
+ import { Extensions } from '@tiptap/core';
2
+ import { ImageUploadResponseResolver } from '../../types';
3
+ interface CreateDefaultExtensionsOptions {
4
+ imgUploadUrl?: string;
5
+ imgUploadResponseKey?: ImageUploadResponseResolver;
6
+ }
7
+ export declare const createDefaultExtensions: ({ imgUploadUrl, imgUploadResponseKey, }: CreateDefaultExtensionsOptions) => Extensions;
8
+ export {};
@@ -0,0 +1,4 @@
1
+ import { Editor } from '@tiptap/react';
2
+ import { ReactNode } from 'react';
3
+ import { TiptopEditorSlot } from '../../types';
4
+ export declare const renderTiptopSlot: (slot: TiptopEditorSlot | undefined, editor: Editor | null) => ReactNode;
@@ -0,0 +1,2 @@
1
+ import { Extensions } from '@tiptap/core';
2
+ export declare const useDuplicateExtensionWarnings: (baseExtensions: Extensions, extraExtensions: Extensions) => void;
package/dist/helpers.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Editor } from '@tiptap/core';
2
2
  import { EditorState } from '@tiptap/pm/state';
3
- import { SlashCommandGroupCommandsProps } from './types';
3
+ import { DocumentMap, SlashCommandGroupCommandsProps, TargetedUpdate } from './types';
4
4
  import { Node } from '@tiptap/pm/model';
5
5
  export declare const isTextSelected: (editor: Editor) => boolean;
6
6
  export declare const hasTextNodeInSelection: (editor: Editor) => boolean;
@@ -38,3 +38,31 @@ export declare const getUploaderAtPos: (state: Editor["state"], pos: number) =>
38
38
  depth: number;
39
39
  node: Node;
40
40
  } | undefined;
41
+ /**
42
+ * Returns a structured snapshot of every top-level block in the editor.
43
+ * Send this to your AI model so it can reference nodes and words by index.
44
+ */
45
+ export declare const getDocumentMap: (editor: Editor) => DocumentMap;
46
+ /**
47
+ * Applies a single targeted content update.
48
+ *
49
+ * @example
50
+ * // Translate the first word of the second paragraph
51
+ * applyTargetedUpdate(editor, { nodeIndex: 1, wordIndex: 0, replacement: 'Bonjour' })
52
+ */
53
+ export declare const applyTargetedUpdate: (editor: Editor, update: TargetedUpdate) => boolean;
54
+ /**
55
+ * Applies multiple targeted updates in a single atomic ProseMirror transaction.
56
+ *
57
+ * All positions are resolved against the document state *before* any mutations,
58
+ * then applied from the bottom of the document upward so that earlier replacements
59
+ * never shift the absolute positions of later ones.
60
+ *
61
+ * @example
62
+ * // Bold-ify two specific words in one go
63
+ * applyTargetedUpdates(editor, [
64
+ * { nodeIndex: 0, wordIndex: 2, replacement: [{ type: 'text', text: 'foo', marks: [{ type: 'bold' }] }] },
65
+ * { nodeIndex: 2, wordRange: [0, 1], replacement: 'Hello world' },
66
+ * ])
67
+ */
68
+ export declare const applyTargetedUpdates: (editor: Editor, updates: TargetedUpdate[]) => boolean;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export { default as TiptopEditor } from './components/editor/TiptopEditor';
2
+ export { TiptopEditorContext, useTiptopEditor } from './components/editor/TiptopEditorContext';
3
+ export { getDocumentMap, applyTargetedUpdate, applyTargetedUpdates } from './helpers';
2
4
  export * from './types';