@pubwave/editor 0.1.1-alpha.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.
@@ -0,0 +1,1339 @@
1
+ import { default as default_2 } from 'react';
2
+ import { Editor } from '@tiptap/react';
3
+ import { Editor as Editor_2 } from '@tiptap/core';
4
+ import { Extension } from '@tiptap/core';
5
+ import { ForwardRefExoticComponent } from 'react';
6
+ import { JSONContent } from '@tiptap/core';
7
+ import { Node as Node_2 } from '@tiptap/pm/model';
8
+ import { Plugin as Plugin_2 } from '@tiptap/pm/state';
9
+ import { PluginKey } from '@tiptap/pm/state';
10
+ import { RefAttributes } from 'react';
11
+ import { SuggestionKeyDownProps } from '@tiptap/suggestion';
12
+
13
+ /**
14
+ * Action result for feedback
15
+ */
16
+ export declare interface ActionResult {
17
+ success: boolean;
18
+ message?: string;
19
+ }
20
+
21
+ declare interface BlockExtensionsConfig {
22
+ /**
23
+ * Heading levels to support
24
+ * @default [1, 2, 3]
25
+ */
26
+ headingLevels?: (1 | 2 | 3 | 4 | 5 | 6)[];
27
+ /**
28
+ * Image upload configuration
29
+ */
30
+ imageUpload?: ImageUploadConfig;
31
+ }
32
+
33
+ /**
34
+ * BlockHandle Component - Notion-style block controls
35
+ */
36
+ export declare function BlockHandle({ editor }: BlockHandleProps): default_2.ReactElement | null;
37
+
38
+ export declare interface BlockHandleProps {
39
+ /** The Tiptap editor instance */
40
+ editor: Editor_2;
41
+ }
42
+
43
+ /**
44
+ * Supported block types in the editor
45
+ */
46
+ export declare type BlockType = 'paragraph' | 'heading' | 'bulletList' | 'orderedList' | 'taskList' | 'blockquote' | 'codeBlock' | 'horizontalRule' | 'image';
47
+
48
+ /**
49
+ * BubbleToolbar - Selection-driven floating toolbar
50
+ */
51
+ export declare function BubbleToolbar({ editor, className, children, onVisibilityChange, }: BubbleToolbarProps): default_2.ReactElement | null;
52
+
53
+ export declare interface BubbleToolbarProps {
54
+ /** The Tiptap editor instance */
55
+ editor: Editor_2;
56
+ /** Additional CSS class names */
57
+ className?: string;
58
+ /** Custom toolbar content (renders default if not provided) */
59
+ children?: default_2.ReactNode;
60
+ /** Callback when toolbar visibility changes */
61
+ onVisibilityChange?: (visible: boolean) => void;
62
+ }
63
+
64
+ /**
65
+ * Cancel a drag operation
66
+ */
67
+ export declare function cancelDrag(editor: Editor_2): void;
68
+
69
+ /**
70
+ * Check if DOM APIs are available
71
+ * Use this before accessing window, document, or other browser APIs
72
+ */
73
+ export declare const canUseDOM: boolean;
74
+
75
+ /**
76
+ * Clear all formatting from the selection
77
+ */
78
+ export declare function clearFormatting(editor: Editor_2): boolean;
79
+
80
+ /**
81
+ * Utility function to combine class names
82
+ * Filters out falsy values and joins with space
83
+ */
84
+ export declare function cn(...classes: (string | boolean | undefined | null)[]): string;
85
+
86
+ /**
87
+ * Action factory for creating toolbar action handlers
88
+ * Returns a function that can be used as onClick handler
89
+ */
90
+ export declare function createActionHandler(editor: Editor_2, action: (editor: Editor_2) => ActionResult, onResult?: (result: ActionResult) => void): () => void;
91
+
92
+ /**
93
+ * Auto-scroll Utility
94
+ *
95
+ * Enables automatic scrolling when dragging near viewport edges.
96
+ * Essential for long documents where blocks need to be moved across visible area.
97
+ *
98
+ * Key behaviors:
99
+ * - Smooth scrolling that accelerates near edges
100
+ * - Works with both window and container scrolling
101
+ * - Cleans up properly when drag ends
102
+ */
103
+ /**
104
+ * Create an auto-scroll controller for drag operations
105
+ */
106
+ export declare function createAutoScroller(scrollContainer?: HTMLElement | null): {
107
+ start: () => void;
108
+ update: (clientY: number) => void;
109
+ stop: () => void;
110
+ };
111
+
112
+ /**
113
+ * Create the DnD ProseMirror plugin
114
+ */
115
+ export declare function createDndPlugin(editor?: Editor_2): Plugin_2<DndState>;
116
+
117
+ /**
118
+ * Create a new editor instance
119
+ *
120
+ * This factory function creates a configured Tiptap editor with all necessary
121
+ * extensions and settings based on the provided configuration.
122
+ *
123
+ * @param config - Editor configuration options
124
+ * @returns A configured Tiptap Editor instance
125
+ * @throws Error if called in a non-browser environment without proper guards
126
+ */
127
+ export declare function createEditor(config?: EditorConfig): Editor_2;
128
+
129
+ /**
130
+ * Create the core extension set for the editor
131
+ *
132
+ * This composes all block, mark, and feature extensions into a single array.
133
+ */
134
+ export declare function createExtensions(config?: ExtensionConfig): Extension[];
135
+
136
+ /**
137
+ * Create a focus trap within the editor UI
138
+ *
139
+ * Useful for modals/dialogs that should trap focus
140
+ */
141
+ export declare function createFocusTrap(container: HTMLElement): {
142
+ activate: () => void;
143
+ deactivate: () => void;
144
+ };
145
+
146
+ /**
147
+ * Create the Slash Commands Extension
148
+ */
149
+ export declare function createSlashCommandsExtension(commands?: SlashCommand[], imageUploadConfig?: ImageUploadConfig, locale?: EditorLocale_2): Extension<any, any>;
150
+
151
+ /**
152
+ * Create a toast ID
153
+ */
154
+ export declare function createToastId(): string;
155
+
156
+ /**
157
+ * Default slash commands (without image upload config, uses base64)
158
+ * Note: This is deprecated, use createDefaultSlashCommands with locale instead
159
+ */
160
+ export declare const defaultSlashCommands: SlashCommand[];
161
+
162
+ export declare const defaultTokens = "\n:root {\n /* Colors - Light theme */\n --pubwave-color-background: #ffffff;\n --pubwave-color-surface: #ffffff;\n --pubwave-color-text: #1a1a1a;\n --pubwave-color-text-muted: #6b7280;\n --pubwave-color-border: #e5e7eb;\n --pubwave-color-border-light: #f3f4f6;\n --pubwave-color-hover: rgba(0, 0, 0, 0.03);\n --pubwave-color-focus: rgba(0, 0, 0, 0.05);\n --pubwave-color-selection: #3b82f6;\n --pubwave-color-selection-bg: rgba(59, 130, 246, 0.1);\n --pubwave-color-primary: #3b82f6;\n --pubwave-color-primary-faded: rgba(59, 130, 246, 0.1);\n --pubwave-color-drop-indicator: #3b82f6;\n --pubwave-color-drop-target: rgba(59, 130, 246, 0.05);\n --pubwave-color-error: #ef4444;\n\n /* Spacing */\n --pubwave-spacing-xs: 0.25rem;\n --pubwave-spacing-sm: 0.5rem;\n --pubwave-spacing-md: 1rem;\n --pubwave-spacing-lg: 1.5rem;\n --pubwave-spacing-xl: 2rem;\n --pubwave-block-padding-x: 0;\n --pubwave-block-padding-y: 0.25rem;\n --pubwave-block-margin-y: 0.25rem;\n --pubwave-toolbar-gap: 0.25rem;\n\n /* Typography */\n --pubwave-font-family: inherit;\n --pubwave-font-size-base: 1rem;\n --pubwave-font-size-small: 0.875rem;\n --pubwave-line-height-base: 1.625;\n --pubwave-line-height-heading: 1.25;\n --pubwave-font-weight-normal: 400;\n --pubwave-font-weight-medium: 500;\n --pubwave-font-weight-bold: 700;\n --pubwave-font-weight-heading: 600;\n\n /* Borders */\n --pubwave-border-radius-sm: 0.25rem;\n --pubwave-border-radius: 0.375rem;\n --pubwave-border-radius-large: 0.5rem;\n\n /* Shadows */\n --pubwave-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n --pubwave-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);\n --pubwave-shadow-toolbar: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);\n --pubwave-shadow-popup: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n\n /* Transitions */\n --pubwave-transition-fast: 0.1s ease;\n --pubwave-transition-normal: 0.15s ease;\n\n /* Z-index layers */\n --pubwave-z-drag-preview: 100;\n --pubwave-z-toolbar: 50;\n --pubwave-z-dropdown: 60;\n}\n\n/* Dark theme (if prefers-color-scheme is dark) */\n@media (prefers-color-scheme: dark) {\n :root {\n --pubwave-color-background: #1a1a1a;\n --pubwave-color-surface: #262626;\n --pubwave-color-text: #e5e7eb;\n --pubwave-color-text-muted: #9ca3af;\n --pubwave-color-border: #374151;\n --pubwave-color-border-light: #4b5563;\n --pubwave-color-hover: rgba(255, 255, 255, 0.05);\n --pubwave-color-focus: rgba(255, 255, 255, 0.08);\n --pubwave-color-selection: #60a5fa;\n --pubwave-color-selection-bg: rgba(96, 165, 250, 0.15);\n --pubwave-color-primary: #60a5fa;\n --pubwave-color-primary-faded: rgba(96, 165, 250, 0.15);\n --pubwave-color-drop-indicator: #60a5fa;\n --pubwave-color-drop-target: rgba(96, 165, 250, 0.08);\n --pubwave-color-error: #f87171;\n }\n}\n";
163
+
164
+ /**
165
+ * Default toolbar actions
166
+ */
167
+ export declare const defaultToolbarActions: ToolbarActionDescriptor[];
168
+
169
+ /**
170
+ * Destroy an editor instance and clean up resources
171
+ *
172
+ * @param editor - The editor instance to destroy
173
+ */
174
+ export declare function destroyEditor(editor: Editor_2 | null): void;
175
+
176
+ /**
177
+ * Plugin key for accessing DnD state
178
+ */
179
+ export declare const dndPluginKey: PluginKey<any>;
180
+
181
+ /**
182
+ * DnD state stored in plugin
183
+ */
184
+ export declare interface DndState {
185
+ /** Block currently being dragged */
186
+ draggingBlockId: string | null;
187
+ /** Position of the block being dragged */
188
+ draggingBlockPos: number | null;
189
+ /** Target position for drop (null if no valid target) */
190
+ dropTargetPos: number | null;
191
+ /** Whether drop is before or after the target */
192
+ dropPosition: 'before' | 'after' | null;
193
+ /** Whether drag was cancelled */
194
+ cancelled: boolean;
195
+ }
196
+
197
+ /**
198
+ * DragHandle - Block drag affordance
199
+ */
200
+ export declare function DragHandle({ blockId, isHovered, isDragging, onDragStart, onDragEnd, className, }: DragHandleProps): default_2.ReactElement;
201
+
202
+ export declare interface DragHandleProps {
203
+ /** Block ID for drag identification */
204
+ blockId: string;
205
+ /** Whether the block is currently hovered */
206
+ isHovered: boolean;
207
+ /** Whether a drag is currently in progress */
208
+ isDragging?: boolean;
209
+ /** Callback when drag starts */
210
+ onDragStart?: (blockId: string, event: default_2.DragEvent) => void;
211
+ /** Callback when drag ends */
212
+ onDragEnd?: (blockId: string, event: default_2.DragEvent) => void;
213
+ /** Additional CSS class names */
214
+ className?: string;
215
+ }
216
+
217
+ /**
218
+ * DropIndicator - Visual drop target indicator
219
+ */
220
+ export declare function DropIndicator({ visible, position, className, }: DropIndicatorProps): default_2.ReactElement;
221
+
222
+ export declare interface DropIndicatorProps {
223
+ /** Whether the indicator is visible */
224
+ visible: boolean;
225
+ /** Position relative to target block */
226
+ position: 'before' | 'after';
227
+ /** Additional CSS class names */
228
+ className?: string;
229
+ }
230
+
231
+ /**
232
+ * Public API exposed by the editor
233
+ */
234
+ export declare interface EditorAPI {
235
+ /**
236
+ * Get the current editor state
237
+ */
238
+ getState: () => EditorState;
239
+ /**
240
+ * Get the content as JSON
241
+ */
242
+ getJSON: () => JSONContent;
243
+ /**
244
+ * Get the content as HTML
245
+ */
246
+ getHTML: () => string;
247
+ /**
248
+ * Get the content as plain text
249
+ */
250
+ getText: () => string;
251
+ /**
252
+ * Set new content
253
+ */
254
+ setContent: (content: JSONContent) => void;
255
+ /**
256
+ * Clear all content
257
+ */
258
+ clearContent: () => void;
259
+ /**
260
+ * Set editable state
261
+ */
262
+ setEditable: (editable: boolean) => void;
263
+ /**
264
+ * Focus the editor
265
+ */
266
+ focus: (position?: 'start' | 'end') => void;
267
+ /**
268
+ * Blur the editor
269
+ */
270
+ blur: () => void;
271
+ /**
272
+ * Toggle bold mark
273
+ */
274
+ toggleBold: () => void;
275
+ /**
276
+ * Toggle italic mark
277
+ */
278
+ toggleItalic: () => void;
279
+ /**
280
+ * Set or remove a link
281
+ */
282
+ setLink: (href: string | null) => void;
283
+ /**
284
+ * Destroy the editor instance
285
+ */
286
+ destroy: () => void;
287
+ /**
288
+ * Access the underlying Tiptap editor instance
289
+ * Use with caution - this is an escape hatch
290
+ */
291
+ readonly tiptapEditor: Editor | null;
292
+ }
293
+
294
+ /**
295
+ * Configuration options for creating an editor instance
296
+ */
297
+ export declare interface EditorConfig {
298
+ /**
299
+ * Initial content in Tiptap JSON format
300
+ */
301
+ content?: JSONContent;
302
+ /**
303
+ * Whether the editor is in read-only mode
304
+ * When true, all editing affordances are hidden
305
+ * @default false
306
+ */
307
+ editable?: boolean;
308
+ /**
309
+ * Placeholder text shown when the editor is empty
310
+ */
311
+ placeholder?: string;
312
+ /**
313
+ * Theme configuration for styling
314
+ */
315
+ theme?: EditorTheme;
316
+ /**
317
+ * Enable autofocus on mount
318
+ * - true: Focus at the end
319
+ * - 'start': Focus at the beginning
320
+ * - 'end': Focus at the end
321
+ * - false: No autofocus
322
+ * @default false
323
+ */
324
+ autofocus?: boolean | 'start' | 'end';
325
+ /**
326
+ * Image upload configuration
327
+ * If not provided, images will be converted to base64 by default
328
+ */
329
+ imageUpload?: ImageUploadConfig;
330
+ /**
331
+ * Callback fired when the editor content changes
332
+ */
333
+ onUpdate?: (props: {
334
+ editor: Editor;
335
+ }) => void;
336
+ /**
337
+ * Callback fired when the selection changes
338
+ */
339
+ onSelectionUpdate?: (props: {
340
+ editor: Editor;
341
+ }) => void;
342
+ /**
343
+ * Callback fired when the editor gains focus
344
+ */
345
+ onFocus?: (props: {
346
+ editor: Editor;
347
+ }) => void;
348
+ /**
349
+ * Callback fired when the editor loses focus
350
+ */
351
+ onBlur?: (props: {
352
+ editor: Editor;
353
+ }) => void;
354
+ /**
355
+ * Callback fired when the editor is ready
356
+ */
357
+ onCreate?: (props: {
358
+ editor: Editor;
359
+ }) => void;
360
+ /**
361
+ * Callback fired before the editor is destroyed
362
+ */
363
+ onDestroy?: () => void;
364
+ }
365
+
366
+ /**
367
+ * Theme configuration using CSS custom property tokens
368
+ * All colors must be CSS custom properties, never hard-coded values
369
+ */
370
+ /**
371
+ * Locale for internationalization
372
+ * @default 'en'
373
+ */
374
+ export declare type EditorLocale = 'en' | 'zh' | 'zh-CN' | 'ja' | 'ko' | 'fr' | 'de' | 'es' | 'pt';
375
+
376
+ declare interface EditorLocale_2 {
377
+ slashMenu: {
378
+ groups: {
379
+ basic: string;
380
+ list: string;
381
+ media: string;
382
+ advanced: string;
383
+ };
384
+ commands: {
385
+ paragraph: {
386
+ title: string;
387
+ description: string;
388
+ };
389
+ heading1: {
390
+ title: string;
391
+ description: string;
392
+ };
393
+ heading2: {
394
+ title: string;
395
+ description: string;
396
+ };
397
+ heading3: {
398
+ title: string;
399
+ description: string;
400
+ };
401
+ bulletList: {
402
+ title: string;
403
+ description: string;
404
+ };
405
+ orderedList: {
406
+ title: string;
407
+ description: string;
408
+ };
409
+ taskList: {
410
+ title: string;
411
+ description: string;
412
+ };
413
+ image: {
414
+ title: string;
415
+ description: string;
416
+ };
417
+ blockquote: {
418
+ title: string;
419
+ description: string;
420
+ };
421
+ codeBlock: {
422
+ title: string;
423
+ description: string;
424
+ };
425
+ horizontalRule: {
426
+ title: string;
427
+ description: string;
428
+ };
429
+ };
430
+ };
431
+ toolbar: {
432
+ bold: string;
433
+ italic: string;
434
+ underline: string;
435
+ strike: string;
436
+ code: string;
437
+ link: string;
438
+ turnInto: string;
439
+ colorPicker: string;
440
+ textColor: string;
441
+ backgroundColor: string;
442
+ recentlyUsed: string;
443
+ openLink: string;
444
+ removeLink: string;
445
+ blockTypes: {
446
+ text: string;
447
+ heading1: string;
448
+ heading2: string;
449
+ heading3: string;
450
+ bulletedList: string;
451
+ numberedList: string;
452
+ };
453
+ };
454
+ aria: {
455
+ editor: string;
456
+ editorContent: string;
457
+ toolbar: string;
458
+ boldButton: string;
459
+ italicButton: string;
460
+ underlineButton: string;
461
+ strikeButton: string;
462
+ codeButton: string;
463
+ linkButton: string;
464
+ turnIntoButton: string;
465
+ colorPickerButton: string;
466
+ headingButton: string;
467
+ paragraphButton: string;
468
+ dragHandle: string;
469
+ dropIndicator: string;
470
+ blockMenu: string;
471
+ addBlock: string;
472
+ deleteBlock: string;
473
+ linkDialog: string;
474
+ linkUrlInput: string;
475
+ linkSaveButton: string;
476
+ linkRemoveButton: string;
477
+ linkCancelButton: string;
478
+ addBlockBelow: string;
479
+ dragToMove: string;
480
+ openLinkInNewTab: string;
481
+ };
482
+ placeholder: string;
483
+ linkPlaceholder: string;
484
+ }
485
+
486
+ /**
487
+ * Complete editor state exposed to consumers
488
+ */
489
+ export declare interface EditorState {
490
+ /**
491
+ * Current selection state
492
+ */
493
+ selection: SelectionState;
494
+ /**
495
+ * Current mark state for the selection
496
+ */
497
+ marks: MarkState;
498
+ /**
499
+ * Whether the editor is currently focused
500
+ */
501
+ isFocused: boolean;
502
+ /**
503
+ * Whether the editor is in read-only mode
504
+ */
505
+ isEditable: boolean;
506
+ /**
507
+ * Whether the editor content is empty
508
+ */
509
+ isEmpty: boolean;
510
+ /**
511
+ * Current character count
512
+ */
513
+ characterCount: number;
514
+ }
515
+
516
+ export declare interface EditorTheme {
517
+ /**
518
+ * CSS class name prefix for styling hooks
519
+ * @default 'pubwave-editor'
520
+ */
521
+ classNamePrefix?: string;
522
+ /**
523
+ * Locale for internationalization
524
+ * @default 'en'
525
+ */
526
+ locale?: EditorLocale;
527
+ /**
528
+ * Additional CSS classes to apply to the editor container
529
+ */
530
+ containerClassName?: string;
531
+ /**
532
+ * Additional CSS classes to apply to the content area
533
+ */
534
+ contentClassName?: string;
535
+ /**
536
+ * Color theme configuration
537
+ * Colors will be applied as CSS custom properties to the editor container
538
+ * If not provided, default theme colors will be used
539
+ */
540
+ colors?: {
541
+ /**
542
+ * Editor background color
543
+ */
544
+ background?: string;
545
+ /**
546
+ * Primary text color
547
+ */
548
+ text?: string;
549
+ /**
550
+ * Muted/secondary text color (e.g., for placeholders)
551
+ */
552
+ textMuted?: string;
553
+ /**
554
+ * Border color
555
+ */
556
+ border?: string;
557
+ /**
558
+ * Primary color (for links, buttons, etc.)
559
+ */
560
+ primary?: string;
561
+ /**
562
+ * Link color (if not provided, will use primary color)
563
+ * Useful for dark themes where a brighter link color is needed for visibility
564
+ */
565
+ linkColor?: string;
566
+ };
567
+ /**
568
+ * Background image configuration
569
+ * Can be a URL string or CSS background-image value
570
+ * If provided, will be applied as the editor container background image
571
+ * Background image will be layered on top of background color
572
+ */
573
+ backgroundImage?: string;
574
+ /**
575
+ * Background image options
576
+ * Controls how the background image is displayed
577
+ */
578
+ backgroundImageOptions?: {
579
+ /**
580
+ * Background image repeat behavior
581
+ * @default 'no-repeat'
582
+ */
583
+ repeat?: 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';
584
+ /**
585
+ * Background image position
586
+ * @default 'center'
587
+ */
588
+ position?: string;
589
+ /**
590
+ * Background image size
591
+ * @default 'cover'
592
+ */
593
+ size?: 'cover' | 'contain' | string;
594
+ /**
595
+ * Background image attachment
596
+ * @default 'scroll'
597
+ */
598
+ attachment?: 'scroll' | 'fixed' | 'local';
599
+ };
600
+ }
601
+
602
+ /**
603
+ * Configuration for creating the extension set
604
+ */
605
+ declare interface ExtensionConfig extends BlockExtensionsConfig, MarkExtensionsConfig {
606
+ /**
607
+ * Placeholder text for empty editor
608
+ */
609
+ placeholder?: string;
610
+ /**
611
+ * Undo/redo history depth
612
+ * @default 100
613
+ */
614
+ historyDepth?: number;
615
+ /**
616
+ * Enable slash commands menu
617
+ * @default true
618
+ */
619
+ enableSlashCommands?: boolean;
620
+ /**
621
+ * Custom slash commands (merged with defaults)
622
+ */
623
+ slashCommands?: SlashCommand[];
624
+ /**
625
+ * Image upload configuration
626
+ */
627
+ imageUpload?: ImageUploadConfig;
628
+ /**
629
+ * Locale for internationalization
630
+ */
631
+ locale?: EditorLocale;
632
+ }
633
+
634
+ /**
635
+ * Find top-level block node at a given document position
636
+ * Returns the direct child of doc that contains the position
637
+ */
638
+ export declare function findBlockAtPos(doc: Node_2, pos: number): {
639
+ node: Node_2;
640
+ pos: number;
641
+ } | null;
642
+
643
+ /**
644
+ * Focus the editor after a click event
645
+ *
646
+ * Handles edge cases where clicking on toolbar/UI elements
647
+ * should restore focus to the editor
648
+ */
649
+ export declare function focusAfterClick(editor: Editor_2, event: React.MouseEvent): void;
650
+
651
+ /**
652
+ * Focus the editor at a specific position
653
+ */
654
+ export declare function focusAt(editor: Editor_2, position: FocusPosition): void;
655
+
656
+ /**
657
+ * Focus the editor
658
+ */
659
+ export declare function focusEditor(editor: Editor_2, position?: 'start' | 'end' | 'all' | number): boolean;
660
+
661
+ /**
662
+ * Focus position options
663
+ */
664
+ export declare type FocusPosition = 'start' | 'end' | 'preserve' | number;
665
+
666
+ /**
667
+ * Get the current link href if selection is inside a link
668
+ */
669
+ export declare function getActiveLinkHref(editor: Editor_2): string | null;
670
+
671
+ /**
672
+ * Get current marks at the selection
673
+ */
674
+ export declare function getActiveMarks(editor: Editor_2): string[];
675
+
676
+ /**
677
+ * Get the current DnD state from an editor
678
+ */
679
+ export declare function getDndState(editor: Editor_2): DndState;
680
+
681
+ /**
682
+ * Get selection coordinates for positioning UI elements (like toolbar)
683
+ * Returns the bounding box of the current selection
684
+ */
685
+ export declare function getSelectionBounds(editor: Editor_2): {
686
+ top: number;
687
+ left: number;
688
+ right: number;
689
+ bottom: number;
690
+ width: number;
691
+ height: number;
692
+ } | null;
693
+
694
+ /**
695
+ * Get the current selection state from the editor
696
+ *
697
+ * This is the primary function for determining toolbar visibility.
698
+ * Per constitution: toolbar MUST NOT be visible when selection is empty.
699
+ */
700
+ export declare function getSelectionState(editor: Editor_2): SelectionState_2;
701
+
702
+ /**
703
+ * Handle focus for selection clear
704
+ *
705
+ * When selection is cleared (e.g., clicking in empty space),
706
+ * ensure cursor is placed at a sensible position
707
+ */
708
+ export declare function handleSelectionClear(editor: Editor_2): void;
709
+
710
+ /**
711
+ * Image upload configuration
712
+ */
713
+ declare interface ImageUploadConfig {
714
+ /**
715
+ * Custom image upload handler
716
+ * If provided, images will be uploaded using this function
717
+ * If not provided, images will be converted to base64 (default)
718
+ */
719
+ handler?: ImageUploadHandler;
720
+ /**
721
+ * Maximum file size in bytes
722
+ * @default 10MB (10 * 1024 * 1024)
723
+ */
724
+ maxSize?: number;
725
+ /**
726
+ * Accepted file types
727
+ * @default ['image/*']
728
+ */
729
+ accept?: string[];
730
+ }
731
+
732
+ /**
733
+ * Image upload handler function
734
+ * Should return a Promise that resolves to the image URL
735
+ * If the function throws or rejects, the editor will fall back to base64
736
+ */
737
+ declare type ImageUploadHandler = (file: File) => Promise<string>;
738
+
739
+ /**
740
+ * Check if the editor is editable (inverse of read-only)
741
+ */
742
+ export declare function isEditable(editor: Editor_2 | null): boolean;
743
+
744
+ /**
745
+ * Check if an editor instance is in a valid state
746
+ *
747
+ * @param editor - The editor instance to check
748
+ * @returns true if the editor exists and is not destroyed
749
+ */
750
+ export declare function isEditorValid(editor: Editor_2 | null): editor is Editor_2;
751
+
752
+ /**
753
+ * Check if a mark is currently active
754
+ */
755
+ export declare function isMarkActive(editor: Editor_2, markType: string): boolean;
756
+
757
+ /**
758
+ * Check if a node type is currently active
759
+ */
760
+ export declare function isNodeActive(editor: Editor_2, nodeType: string, attrs?: Record<string, unknown>): boolean;
761
+
762
+ /**
763
+ * Check if the editor is in read-only mode
764
+ */
765
+ export declare function isReadOnly(editor: Editor_2 | null): boolean;
766
+
767
+ /**
768
+ * Check if the selection is empty (cursor only)
769
+ *
770
+ * This is the key check for toolbar visibility.
771
+ * Returns true when toolbar should be HIDDEN.
772
+ */
773
+ export declare function isSelectionEmpty(editor: Editor_2): boolean;
774
+
775
+ /**
776
+ * Check if code is running in a server-side environment
777
+ */
778
+ export declare const isSSR: boolean;
779
+
780
+ /**
781
+ * Keep focus within the editor
782
+ *
783
+ * Prevents focus from escaping during common operations
784
+ */
785
+ export declare function maintainFocus(editor: Editor_2): () => void;
786
+
787
+ declare interface MarkExtensionsConfig {
788
+ /**
789
+ * Whether links should open in a new tab
790
+ * @default true
791
+ */
792
+ linkOpenInNewTab?: boolean;
793
+ /**
794
+ * Whether to open links on click (within editor)
795
+ * @default true - links open on click
796
+ */
797
+ linkOpenOnClick?: boolean;
798
+ }
799
+
800
+ /**
801
+ * Mark state for the current selection
802
+ */
803
+ export declare interface MarkState {
804
+ /**
805
+ * Whether bold is active at the current selection
806
+ */
807
+ isBold: boolean;
808
+ /**
809
+ * Whether italic is active at the current selection
810
+ */
811
+ isItalic: boolean;
812
+ /**
813
+ * Whether a link is active at the current selection
814
+ */
815
+ isLink: boolean;
816
+ /**
817
+ * The href of the active link, if any
818
+ */
819
+ linkHref?: string;
820
+ }
821
+
822
+ /**
823
+ * Supported mark types for inline formatting
824
+ */
825
+ export declare type MarkType = 'bold' | 'italic' | 'underline' | 'strike' | 'code' | 'link';
826
+
827
+ /**
828
+ * Move a block from one position to another
829
+ */
830
+ export declare function moveBlock(editor: Editor_2, fromPos: number, toPos: number, position: 'before' | 'after'): boolean;
831
+
832
+ /**
833
+ * PubwaveEditor Component
834
+ *
835
+ * A Notion-level block editor with:
836
+ * - Premium writing experience
837
+ * - Selection-only formatting toolbar
838
+ * - Block drag & drop
839
+ * - Full theme customization via CSS tokens
840
+ */
841
+ export declare const PubwaveEditor: ForwardRefExoticComponent<PubwaveEditorProps & RefAttributes<EditorAPI | null>>;
842
+
843
+ /**
844
+ * Props for the PubwaveEditor component
845
+ */
846
+ declare interface PubwaveEditorProps {
847
+ /**
848
+ * Initial content in Tiptap JSON format
849
+ */
850
+ content?: JSONContent;
851
+ /**
852
+ * Whether the editor is in read-only mode
853
+ * @default false
854
+ */
855
+ editable?: boolean;
856
+ /**
857
+ * Placeholder text shown when the editor is empty
858
+ */
859
+ placeholder?: string;
860
+ /**
861
+ * Theme configuration
862
+ */
863
+ theme?: EditorTheme;
864
+ /**
865
+ * Enable autofocus on mount
866
+ * @default false
867
+ */
868
+ autofocus?: boolean | 'start' | 'end';
869
+ /**
870
+ * Callback when content changes
871
+ */
872
+ onChange?: (content: JSONContent) => void;
873
+ /**
874
+ * Callback when selection changes
875
+ */
876
+ onSelectionChange?: () => void;
877
+ /**
878
+ * Callback when editor gains focus
879
+ */
880
+ onFocus?: () => void;
881
+ /**
882
+ * Callback when editor loses focus
883
+ */
884
+ onBlur?: () => void;
885
+ /**
886
+ * Callback when editor is ready
887
+ */
888
+ onReady?: (api: EditorAPI) => void;
889
+ /**
890
+ * Image upload configuration
891
+ * If not provided, images will be converted to base64 by default
892
+ */
893
+ imageUpload?: ImageUploadConfig;
894
+ /**
895
+ * Additional CSS class for the container
896
+ */
897
+ className?: string;
898
+ /**
899
+ * Editor container width
900
+ * Can be a CSS value like '100%', '1200px', '90vw', etc.
901
+ * @default '100%'
902
+ */
903
+ width?: string;
904
+ /**
905
+ * Test ID for testing
906
+ */
907
+ 'data-testid'?: string;
908
+ }
909
+
910
+ /**
911
+ * Redo the last undone action
912
+ */
913
+ export declare function redo(editor: Editor_2): boolean;
914
+
915
+ /**
916
+ * Restore focus after a DOM operation
917
+ *
918
+ * Use this after operations that might cause focus loss:
919
+ * - Modal/dialog interactions
920
+ * - Toolbar button clicks
921
+ * - Context menu actions
922
+ */
923
+ export declare function restoreFocus(editor: Editor_2): void;
924
+
925
+ export declare function restoreSelection(editor: Editor_2, savedSelection: SavedSelection | null): void;
926
+
927
+ /**
928
+ * Save and restore selection state
929
+ *
930
+ * Useful for operations that need to temporarily change selection
931
+ * then restore it (e.g., inserting content at cursor)
932
+ */
933
+ export declare interface SavedSelection {
934
+ from: number;
935
+ to: number;
936
+ empty: boolean;
937
+ }
938
+
939
+ export declare function saveSelection(editor: Editor_2): SavedSelection | null;
940
+
941
+ /**
942
+ * Select all content
943
+ */
944
+ export declare function selectAll(editor: Editor_2): boolean;
945
+
946
+ /**
947
+ * Current selection state derived from the editor
948
+ */
949
+ export declare interface SelectionState {
950
+ /**
951
+ * Whether there is a non-empty text selection
952
+ */
953
+ hasSelection: boolean;
954
+ /**
955
+ * Whether the selection spans multiple blocks
956
+ */
957
+ isMultiBlock: boolean;
958
+ /**
959
+ * Start position of the selection
960
+ */
961
+ from: number;
962
+ /**
963
+ * End position of the selection
964
+ */
965
+ to: number;
966
+ /**
967
+ * Whether the selection is empty (cursor only)
968
+ */
969
+ isEmpty: boolean;
970
+ }
971
+
972
+ /**
973
+ * Selection state information
974
+ */
975
+ declare interface SelectionState_2 {
976
+ /**
977
+ * Whether there is any selection at all
978
+ */
979
+ hasSelection: boolean;
980
+ /**
981
+ * Whether the selection is empty (cursor only, no text selected)
982
+ */
983
+ isEmpty: boolean;
984
+ /**
985
+ * Whether the selection spans actual text content
986
+ */
987
+ hasTextContent: boolean;
988
+ /**
989
+ * Whether this is a node selection (e.g., selected image or block)
990
+ */
991
+ isNodeSelection: boolean;
992
+ /**
993
+ * The selected text, if any
994
+ */
995
+ selectedText: string;
996
+ /**
997
+ * Start position of the selection
998
+ */
999
+ from: number;
1000
+ /**
1001
+ * End position of the selection
1002
+ */
1003
+ to: number;
1004
+ /**
1005
+ * Whether bold mark is active in selection
1006
+ */
1007
+ isBold: boolean;
1008
+ /**
1009
+ * Whether italic mark is active in selection
1010
+ */
1011
+ isItalic: boolean;
1012
+ /**
1013
+ * Whether underline mark is active in selection
1014
+ */
1015
+ isUnderline: boolean;
1016
+ /**
1017
+ * Whether strike mark is active in selection
1018
+ */
1019
+ isStrike: boolean;
1020
+ /**
1021
+ * Whether code mark is active in selection
1022
+ */
1023
+ isCode: boolean;
1024
+ /**
1025
+ * Whether the selection has a link
1026
+ */
1027
+ hasLink: boolean;
1028
+ /**
1029
+ * Active link href if any
1030
+ */
1031
+ linkHref: string | null;
1032
+ }
1033
+
1034
+ /**
1035
+ * Set the current block to a heading
1036
+ */
1037
+ export declare function setHeading(editor: Editor_2, level: 1 | 2 | 3 | 4 | 5 | 6): boolean;
1038
+
1039
+ /**
1040
+ * Set a link on the current selection
1041
+ */
1042
+ export declare function setLink(editor: Editor_2, href: string): boolean;
1043
+
1044
+ /**
1045
+ * Set the current block to a paragraph
1046
+ */
1047
+ export declare function setParagraph(editor: Editor_2): boolean;
1048
+
1049
+ /**
1050
+ * Guard function for any interactive element
1051
+ * Returns true if interactive elements should be enabled
1052
+ */
1053
+ export declare function shouldAllowInteraction(editor: Editor_2 | null): boolean;
1054
+
1055
+ /**
1056
+ * Guard function for toolbar visibility
1057
+ * Returns true if toolbar should potentially be visible
1058
+ * (Still depends on selection state)
1059
+ */
1060
+ export declare function shouldAllowToolbar(editor: Editor_2 | null): boolean;
1061
+
1062
+ /**
1063
+ * Guard function for UI components
1064
+ * Returns true if the component should be rendered (editable mode)
1065
+ */
1066
+ export declare function shouldRenderEditUI(editor: Editor_2 | null): boolean;
1067
+
1068
+ /**
1069
+ * Guard function for drag handles
1070
+ * Returns true if drag handles should be visible
1071
+ */
1072
+ export declare function shouldShowDragHandle(editor: Editor_2 | null): boolean;
1073
+
1074
+ /**
1075
+ * Check if selection should trigger toolbar visibility
1076
+ *
1077
+ * Per constitution: toolbar appears ONLY on non-empty text selection.
1078
+ * - Cursor only: NO toolbar
1079
+ * - Text selected: YES toolbar
1080
+ * - Node selected (image, etc.): Context-dependent
1081
+ */
1082
+ export declare function shouldShowToolbar(editor: Editor_2): boolean;
1083
+
1084
+ /**
1085
+ * Command item definition
1086
+ */
1087
+ export declare interface SlashCommand {
1088
+ id: string;
1089
+ title: string;
1090
+ description: string;
1091
+ icon: default_2.ReactNode;
1092
+ aliases?: string[];
1093
+ group: 'basic' | 'list' | 'media' | 'advanced';
1094
+ action: (editor: Editor_2) => void;
1095
+ }
1096
+
1097
+ /**
1098
+ * Slash Menu List Component
1099
+ */
1100
+ export declare const SlashMenuList: default_2.ForwardRefExoticComponent<SlashMenuListProps & default_2.RefAttributes<{
1101
+ onKeyDown: (props: SuggestionKeyDownProps) => boolean;
1102
+ }>>;
1103
+
1104
+ /**
1105
+ * Slash Menu Component Props
1106
+ */
1107
+ declare interface SlashMenuListProps {
1108
+ items: SlashCommand[];
1109
+ command: (item: SlashCommand) => void;
1110
+ editor: Editor_2;
1111
+ query?: string;
1112
+ groupLabels?: Record<string, string>;
1113
+ }
1114
+
1115
+ /**
1116
+ * Start a drag operation
1117
+ */
1118
+ export declare function startDrag(editor: Editor_2, blockId: string, pos: number): void;
1119
+
1120
+ /**
1121
+ * Supported block types
1122
+ */
1123
+ export declare const SUPPORTED_BLOCKS: readonly BlockType[];
1124
+
1125
+ /**
1126
+ * Supported mark types
1127
+ */
1128
+ export declare const SUPPORTED_MARKS: readonly MarkType[];
1129
+
1130
+ /**
1131
+ * Toast - Transient notification component
1132
+ */
1133
+ export declare function Toast({ message, type, duration, onDismiss, className, }: ToastProps): default_2.ReactElement;
1134
+
1135
+ export declare function ToastContainer({ toasts, onDismiss, }: ToastContainerProps): default_2.ReactElement;
1136
+
1137
+ export declare interface ToastContainerProps {
1138
+ toasts: ToastItem[];
1139
+ onDismiss: (id: string) => void;
1140
+ }
1141
+
1142
+ /**
1143
+ * Toast container for managing multiple toasts
1144
+ */
1145
+ export declare interface ToastItem {
1146
+ id: string;
1147
+ message: string;
1148
+ type?: 'info' | 'success' | 'error' | 'warning';
1149
+ duration?: number;
1150
+ }
1151
+
1152
+ export declare interface ToastProps {
1153
+ /** Toast message content */
1154
+ message: string;
1155
+ /** Toast type for styling */
1156
+ type?: 'info' | 'success' | 'error' | 'warning';
1157
+ /** Auto-dismiss duration in ms (0 = no auto-dismiss) */
1158
+ duration?: number;
1159
+ /** Callback when toast is dismissed */
1160
+ onDismiss?: () => void;
1161
+ /** Additional CSS class names */
1162
+ className?: string;
1163
+ }
1164
+
1165
+ /**
1166
+ * Toggle bold formatting on the current selection
1167
+ */
1168
+ export declare function toggleBold(editor: Editor_2): boolean;
1169
+
1170
+ /**
1171
+ * Toggle heading for the current block
1172
+ */
1173
+ export declare function toggleHeading(editor: Editor_2, level: 1 | 2 | 3 | 4 | 5 | 6): boolean;
1174
+
1175
+ /**
1176
+ * Toggle italic formatting on the current selection
1177
+ */
1178
+ export declare function toggleItalic(editor: Editor_2): boolean;
1179
+
1180
+ /**
1181
+ * CSS Token definitions
1182
+ * These map to CSS custom properties that host applications can override.
1183
+ */
1184
+ export declare const tokens: {
1185
+ readonly colors: {
1186
+ readonly background: "var(--pubwave-color-background)";
1187
+ readonly surface: "var(--pubwave-color-surface)";
1188
+ readonly text: "var(--pubwave-color-text)";
1189
+ readonly textMuted: "var(--pubwave-color-text-muted)";
1190
+ readonly border: "var(--pubwave-color-border)";
1191
+ readonly borderLight: "var(--pubwave-color-border-light)";
1192
+ readonly hover: "var(--pubwave-color-hover)";
1193
+ readonly focus: "var(--pubwave-color-focus)";
1194
+ readonly selection: "var(--pubwave-color-selection)";
1195
+ readonly selectionBg: "var(--pubwave-color-selection-bg)";
1196
+ readonly primary: "var(--pubwave-color-primary)";
1197
+ readonly primaryFaded: "var(--pubwave-color-primary-faded)";
1198
+ readonly dropIndicator: "var(--pubwave-color-drop-indicator)";
1199
+ readonly dropTarget: "var(--pubwave-color-drop-target)";
1200
+ readonly error: "var(--pubwave-color-error)";
1201
+ };
1202
+ readonly spacing: {
1203
+ readonly xs: "var(--pubwave-spacing-xs)";
1204
+ readonly sm: "var(--pubwave-spacing-sm)";
1205
+ readonly md: "var(--pubwave-spacing-md)";
1206
+ readonly lg: "var(--pubwave-spacing-lg)";
1207
+ readonly xl: "var(--pubwave-spacing-xl)";
1208
+ readonly blockPaddingX: "var(--pubwave-block-padding-x)";
1209
+ readonly blockPaddingY: "var(--pubwave-block-padding-y)";
1210
+ readonly blockMarginY: "var(--pubwave-block-margin-y)";
1211
+ readonly toolbarGap: "var(--pubwave-toolbar-gap)";
1212
+ };
1213
+ readonly typography: {
1214
+ readonly fontFamily: "var(--pubwave-font-family)";
1215
+ readonly fontSizeBase: "var(--pubwave-font-size-base)";
1216
+ readonly fontSizeSmall: "var(--pubwave-font-size-small)";
1217
+ readonly lineHeightBase: "var(--pubwave-line-height-base)";
1218
+ readonly lineHeightHeading: "var(--pubwave-line-height-heading)";
1219
+ readonly fontWeightNormal: "var(--pubwave-font-weight-normal)";
1220
+ readonly fontWeightMedium: "var(--pubwave-font-weight-medium)";
1221
+ readonly fontWeightBold: "var(--pubwave-font-weight-bold)";
1222
+ readonly fontWeightHeading: "var(--pubwave-font-weight-heading)";
1223
+ };
1224
+ readonly borderRadius: {
1225
+ readonly sm: "var(--pubwave-border-radius-sm)";
1226
+ readonly md: "var(--pubwave-border-radius)";
1227
+ readonly lg: "var(--pubwave-border-radius-large)";
1228
+ };
1229
+ readonly shadow: {
1230
+ readonly sm: "var(--pubwave-shadow-sm)";
1231
+ readonly md: "var(--pubwave-shadow-md)";
1232
+ readonly toolbar: "var(--pubwave-shadow-toolbar)";
1233
+ readonly popup: "var(--pubwave-shadow-popup)";
1234
+ };
1235
+ readonly transition: {
1236
+ readonly fast: "var(--pubwave-transition-fast)";
1237
+ readonly normal: "var(--pubwave-transition-normal)";
1238
+ };
1239
+ readonly zIndex: {
1240
+ readonly dragPreview: 100;
1241
+ readonly dragHandle: 40;
1242
+ readonly toolbar: 50;
1243
+ readonly dropdown: 60;
1244
+ };
1245
+ };
1246
+
1247
+ /**
1248
+ * Toolbar action descriptors for dynamic toolbar generation
1249
+ */
1250
+ export declare interface ToolbarActionDescriptor {
1251
+ id: string;
1252
+ label: string;
1253
+ shortcut?: string;
1254
+ icon: string;
1255
+ action: (editor: Editor_2) => ActionResult;
1256
+ isActive: (editor: Editor_2) => boolean;
1257
+ isDisabled?: (editor: Editor_2) => boolean;
1258
+ }
1259
+
1260
+ /**
1261
+ * Toolbar position calculated relative to viewport
1262
+ */
1263
+ export declare interface ToolbarPosition {
1264
+ top: number;
1265
+ left: number;
1266
+ visible: boolean;
1267
+ }
1268
+
1269
+ /**
1270
+ * Remove link from selection
1271
+ */
1272
+ export declare function toolbarRemoveLink(editor: Editor_2): ActionResult;
1273
+
1274
+ /**
1275
+ * Set heading level
1276
+ */
1277
+ export declare function toolbarSetHeading(editor: Editor_2, level: 1 | 2 | 3): ActionResult;
1278
+
1279
+ /**
1280
+ * Set a link on the selection
1281
+ */
1282
+ export declare function toolbarSetLink(editor: Editor_2, href: string): ActionResult;
1283
+
1284
+ /**
1285
+ * Set paragraph (remove heading)
1286
+ */
1287
+ export declare function toolbarSetParagraph(editor: Editor_2): ActionResult;
1288
+
1289
+ /**
1290
+ * Toggle bold mark on selection
1291
+ */
1292
+ export declare function toolbarToggleBold(editor: Editor_2): ActionResult;
1293
+
1294
+ /**
1295
+ * Toggle italic mark on selection
1296
+ */
1297
+ export declare function toolbarToggleItalic(editor: Editor_2): ActionResult;
1298
+
1299
+ /**
1300
+ * Toggle link - prompts for URL if adding, removes if already linked
1301
+ */
1302
+ export declare function toolbarToggleLink(editor: Editor_2, promptFn?: (message: string) => string | null): ActionResult;
1303
+
1304
+ /**
1305
+ * Undo the last action
1306
+ */
1307
+ export declare function undo(editor: Editor_2): boolean;
1308
+
1309
+ /**
1310
+ * Remove link from the current selection
1311
+ */
1312
+ export declare function unsetLink(editor: Editor_2): boolean;
1313
+
1314
+ /**
1315
+ * React hook for auto-scroll during drag operations
1316
+ */
1317
+ export declare function useAutoScroll(scrollContainer?: HTMLElement | null): {
1318
+ startAutoScroll: () => void;
1319
+ updateAutoScroll: (event: {
1320
+ clientY: number;
1321
+ }) => void;
1322
+ stopAutoScroll: () => void;
1323
+ };
1324
+
1325
+ export { }
1326
+
1327
+
1328
+ declare module '@tiptap/core' {
1329
+ interface Commands<ReturnType> {
1330
+ textColor: {
1331
+ setColor: (color: string) => ReturnType;
1332
+ unsetColor: () => ReturnType;
1333
+ };
1334
+ backgroundColor: {
1335
+ setBackgroundColor: (backgroundColor: string) => ReturnType;
1336
+ unsetBackgroundColor: () => ReturnType;
1337
+ };
1338
+ }
1339
+ }