@matops/editor 0.1.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,1140 @@
1
+ import { ClassValue } from 'clsx';
2
+ import { DecorationSet } from '@tiptap/pm/view';
3
+ import { default as default_2 } from 'react';
4
+ import { Dispatch } from 'react';
5
+ import { Doc } from 'yjs';
6
+ import { Editor as Editor_2 } from '@tiptap/core';
7
+ import { Extension } from '@tiptap/core';
8
+ import { Extensions } from '@tiptap/core';
9
+ import { JSONContent } from '@tiptap/core';
10
+ import { JSX as JSX_2 } from 'react/jsx-runtime';
11
+ import { Node as Node_2 } from '@tiptap/core';
12
+ import { PluginKey } from '@tiptap/pm/state';
13
+ import { ReactNode } from 'react';
14
+ import { RefObject } from 'react';
15
+ import { SetStateAction } from 'react';
16
+
17
+ export declare interface AttachmentItem {
18
+ id: string;
19
+ name: string;
20
+ size: number;
21
+ mimeType: string;
22
+ url: string;
23
+ }
24
+
25
+ export declare interface AttachmentsAttrs {
26
+ items: string;
27
+ }
28
+
29
+ export declare interface AttachmentsDocAttr {
30
+ items: string;
31
+ }
32
+
33
+ /**
34
+ * AttachmentsGuard — stub extension for the `"attachments"` feature.
35
+ * Previously guarded the attachments content node; now a no-op since
36
+ * attachments data lives in doc.attrs and is rendered as a React panel.
37
+ */
38
+ export declare const AttachmentsGuard: Extension<any, any>;
39
+
40
+ export declare const AttachmentsNode: Node_2<any, any>;
41
+
42
+ /**
43
+ * AttachmentsPanel — React panel (Panel 3) for document attachments.
44
+ *
45
+ * Reads from `editor.state.doc.attrs.attachments` (an AttachmentsDocAttr or null).
46
+ * Writes via `editor.commands.setAttachmentsAttr(items)`.
47
+ *
48
+ * Structure (mirrors the old AttachmentsSectionView layout exactly):
49
+ * ────── [separator-icon mirror] ────── ← static, reads from doc.attrs.separatorIcon
50
+ * 📎 Attachments [+ Add attachment]
51
+ * [ file list ]
52
+ *
53
+ * No ProseMirror NodeView — this is pure React rendered below <EditorContent>.
54
+ *
55
+ * All drag events call e.stopPropagation() so files dropped here don't also
56
+ * get picked up by the editor's useImageUpload handler.
57
+ */
58
+ export declare function AttachmentsPanel(): JSX_2.Element | null;
59
+
60
+ /**
61
+ * BlockFloatingMenu
62
+ *
63
+ * A compact "+" trigger button on empty lines.
64
+ * Click to expand into an icon-only pill of available commands.
65
+ * Hover each icon to see the label as a tooltip.
66
+ */
67
+ export declare function BlockFloatingMenu(): JSX_2.Element | null;
68
+
69
+ /**
70
+ * CharacterCountBar
71
+ *
72
+ * A slim footer bar rendered below the editor content area.
73
+ * Shows: words, characters, paragraphs, and estimated reading time.
74
+ *
75
+ * IMPORTANT: useEditorUpdate() is called so this component re-renders on
76
+ * every editor transaction — without it, stats would only refresh on
77
+ * React prop changes (i.e. never, since the editor ref is stable).
78
+ */
79
+ export declare function CharacterCountBar({ wordLimit, charLimit, }: CharacterCountBarProps): JSX_2.Element | null;
80
+
81
+ declare interface CharacterCountBarProps {
82
+ /** Optional word limit — shows a progress indicator when set */
83
+ wordLimit?: number;
84
+ /** Optional character limit */
85
+ charLimit?: number;
86
+ }
87
+
88
+ /**
89
+ * Merge Tailwind CSS classes safely.
90
+ * Combines clsx (conditional classes) with tailwind-merge (deduplication).
91
+ *
92
+ * @example
93
+ * cn("p-4", isActive && "bg-blue-500", "p-2")
94
+ * // → "bg-blue-500 p-2" (p-4 deduplicated by tailwind-merge)
95
+ */
96
+ export declare function cn(...inputs: ClassValue[]): string;
97
+
98
+ export declare interface CollaborationProvider {
99
+ document: Doc;
100
+ user?: CollaborationUser;
101
+ }
102
+
103
+ export declare interface CollaborationUser {
104
+ name: string;
105
+ color: string;
106
+ avatar?: string;
107
+ }
108
+
109
+ export declare function computeCropStyle(crop: CropRect, objectPosition?: string): React.CSSProperties;
110
+
111
+ export declare function computeCropStyleStr(crop: CropRect, objectPosition?: string): string;
112
+
113
+ /**
114
+ * Registers (or re-registers) the "tables" feature with the given options.
115
+ *
116
+ * Calling this function multiple times is intentional when you need custom
117
+ * configuration — the registry warns on overwrite, which is expected.
118
+ *
119
+ * @param options - Partial options to override TABLE_DEFAULTS.
120
+ */
121
+ export declare function configureTablesFeature(options?: TableFeatureOptions): void;
122
+
123
+ export declare type ContentWidth = (typeof ContentWidths)[keyof typeof ContentWidths];
124
+
125
+ export declare const ContentWidths: {
126
+ readonly full: "full";
127
+ readonly normal: "normal";
128
+ readonly compact: "compact";
129
+ };
130
+
131
+ export declare interface CoverImageAttrs {
132
+ src: string | null;
133
+ alt: string;
134
+ objectPosition: string;
135
+ cropX: number;
136
+ cropY: number;
137
+ cropWidth: number;
138
+ cropHeight: number;
139
+ }
140
+
141
+ export declare interface CoverImageDocAttr {
142
+ src: string | null;
143
+ alt: string;
144
+ objectPosition: string;
145
+ cropX: number;
146
+ cropY: number;
147
+ cropWidth: number;
148
+ cropHeight: number;
149
+ }
150
+
151
+ /**
152
+ * CoverImageGuard — stub extension for the `"cover-image"` feature.
153
+ * Previously guarded the cover-image content node; now a no-op since
154
+ * cover-image data lives in doc.attrs.
155
+ */
156
+ export declare const CoverImageGuard: Extension<any, any>;
157
+
158
+ export declare const CoverImageNode: Node_2<any, any>;
159
+
160
+ /**
161
+ * CoverImagePanel — React panel (Panel 1) for the cover/banner image.
162
+ *
163
+ * Reads from `editor.state.doc.attrs.coverImage` (a CoverImageDocAttr or null).
164
+ * Writes via `editor.commands.setCoverImageAttr(...)`.
165
+ *
166
+ * No ProseMirror NodeView — this is pure React rendered above <EditorContent>.
167
+ *
168
+ * Crop coordinates are percentages (0–100) stored in doc.attrs.coverImage.
169
+ */
170
+ export declare function CoverImagePanel(): JSX_2.Element | null;
171
+
172
+ /** @deprecated Use `createDocContent()` instead. */
173
+ export declare function createAttachmentsNode(items?: AttachmentItem[]): JSONContent;
174
+
175
+ /**
176
+ * @deprecated Use `createDocContent()` instead.
177
+ * Returns a `cover-image` content node for internal Tiptap use.
178
+ */
179
+ export declare function createCoverImageNode(src: string, options?: Partial<Omit<CoverImageAttrs, "src">>): JSONContent;
180
+
181
+ /**
182
+ * Build a complete Tiptap document JSONContent in **external format**.
183
+ *
184
+ * Structural metadata (cover image, title, separator icon, attachments) is
185
+ * stored in `doc.attrs`. Only body content goes in `content[]`.
186
+ *
187
+ * Pass the result as `initialContent` to `<Editor>` — it reads doc.attrs and
188
+ * injects the appropriate content nodes internally.
189
+ *
190
+ * @example
191
+ * createDocContent({
192
+ * coverSrc: "https://cdn.example.com/cover.jpg",
193
+ * title: "Getting Started",
194
+ * body: [{ type: "paragraph", content: [{ type: "text", text: "Hello!" }] }],
195
+ * })
196
+ */
197
+ export declare function createDocContent(options?: DocContentOptions): JSONContent;
198
+
199
+ /** @deprecated Use `createDocContent()` instead. */
200
+ export declare function createEmptyCoverImageNode(): JSONContent;
201
+
202
+ /** @deprecated Use `createDocContent()` instead. */
203
+ export declare function createEmptySeparatorIconNode(): JSONContent;
204
+
205
+ /** @deprecated Use `createDocContent()` instead. */
206
+ export declare function createSeparatorIconNode(src: string, options?: Partial<Omit<SeparatorIconAttrs, "src">>): JSONContent;
207
+
208
+ /** @deprecated Use `createDocContent()` instead. */
209
+ export declare function createTitleNode(text?: string, options?: Partial<TitleAttrs>): JSONContent;
210
+
211
+ export declare function CropModal({ src, initialCrop, aspectRatio, onConfirm, onCancel, }: CropModalProps): default_2.ReactPortal;
212
+
213
+ declare interface CropModalProps {
214
+ src: string;
215
+ initialCrop?: CropRect;
216
+ aspectRatio?: number;
217
+ onConfirm: (crop: CropRect) => void;
218
+ onCancel: () => void;
219
+ }
220
+
221
+ export declare interface CropRect {
222
+ x: number;
223
+ y: number;
224
+ width: number;
225
+ height: number;
226
+ }
227
+
228
+ export declare function cropToAttrs(crop: CropRect): {
229
+ cropX: number;
230
+ cropY: number;
231
+ cropWidth: number;
232
+ cropHeight: number;
233
+ };
234
+
235
+ export declare const DEFAULT_CONTENT_WIDTH: ContentWidth;
236
+
237
+ export declare const DEFAULT_CROP: CropRect;
238
+
239
+ /**
240
+ * DefaultSeparatorIcon — the quill SVG used as the fallback badge icon
241
+ * in TitleSeparator, SeparatorIconView, and EditorViewer.
242
+ * Single source of truth — no more duplication across three files.
243
+ */
244
+ export declare const DefaultSeparatorIcon: ({ size }: IconProps) => JSX_2.Element;
245
+
246
+ export declare interface DocAttrs {
247
+ contentWidth?: ContentWidth;
248
+ coverImage?: CoverImageDocAttr | null;
249
+ title?: TitleDocAttr | null;
250
+ separatorIcon?: SeparatorIconDocAttr | null;
251
+ attachments?: AttachmentsDocAttr | null;
252
+ }
253
+
254
+ export declare interface DocContentOptions {
255
+ /** Cover image URL. Omit to skip the `coverImage` attr entirely. */
256
+ coverSrc?: string;
257
+ coverOptions?: Partial<Omit<CoverImageDocAttr, "src">>;
258
+ /**
259
+ * Document title text. Pass `""` to include the title with no text
260
+ * (shows placeholder in the editor). Omit to skip entirely.
261
+ */
262
+ title?: string;
263
+ titleOptions?: Partial<TitleDocAttr>;
264
+ /**
265
+ * Separator icon URL. Only applied when `title` is also present.
266
+ */
267
+ separatorIconSrc?: string;
268
+ /** Body content nodes. Defaults to a single empty paragraph. */
269
+ body?: JSONContent[];
270
+ /**
271
+ * Attachments section.
272
+ * - `true` → empty section
273
+ * - `AttachmentItem[]` → pre-populated
274
+ * - omit / `false` → disabled
275
+ */
276
+ attachments?: boolean | AttachmentItem[];
277
+ layout?: DocLayout;
278
+ }
279
+
280
+ export declare interface DocLayout {
281
+ contentWidth?: ContentWidth;
282
+ }
283
+
284
+ export declare interface DropdownPosition {
285
+ top: number;
286
+ left: number;
287
+ }
288
+
289
+ declare interface EditorContextValue {
290
+ editor: Editor_2 | null;
291
+ features: EditorFeature[];
292
+ featureSet: Set<EditorFeature>;
293
+ theme: EditorTheme;
294
+ editable: boolean;
295
+ onFileUploaded?: (file: File) => Promise<string>;
296
+ separatorIcon?: ReactNode;
297
+ }
298
+
299
+ export declare type EditorFeature = (typeof EditorFeatures)[keyof typeof EditorFeatures];
300
+
301
+ export declare const EditorFeatures: {
302
+ readonly formatting: "formatting";
303
+ readonly code: "code";
304
+ readonly media: "media";
305
+ readonly tables: "tables";
306
+ readonly taskList: "task-list";
307
+ readonly layout: "layout";
308
+ readonly collaboration: "collaboration";
309
+ readonly history: "history";
310
+ readonly slashCommand: "slash-command";
311
+ readonly placeholder: "placeholder";
312
+ readonly characterCount: "character-count";
313
+ readonly findReplace: "find-replace";
314
+ readonly coverImage: "cover-image";
315
+ readonly pageHeader: "page-header";
316
+ readonly attachments: "attachments";
317
+ };
318
+
319
+ export declare interface EditorProps {
320
+ features?: EditorFeature[];
321
+ initialContent?: JSONContent;
322
+ theme?: EditorTheme;
323
+ editable?: boolean;
324
+ placeholder?: string;
325
+ collaborationProvider?: CollaborationProvider;
326
+ className?: string;
327
+ onChange?: (json: JSONContent) => void;
328
+ onContentChange?: (html: string, json: JSONContent) => void;
329
+ onPublish?: (content: JSONContent) => void;
330
+ onFileUploaded?: (file: File) => Promise<string>;
331
+ onEditorReady?: (editor: Editor_2) => void;
332
+ separatorIcon?: ReactNode;
333
+ }
334
+
335
+ export declare function EditorProvider({ children, editor, features, theme, editable, onFileUploaded, separatorIcon, }: EditorProviderProps): JSX_2.Element;
336
+
337
+ declare interface EditorProviderProps {
338
+ children: ReactNode;
339
+ editor: Editor_2 | null;
340
+ features: EditorFeature[];
341
+ theme: EditorTheme;
342
+ editable: boolean;
343
+ onFileUploaded?: (file: File) => Promise<string>;
344
+ separatorIcon?: ReactNode;
345
+ }
346
+
347
+ export declare type EditorTheme = (typeof EditorThemes)[keyof typeof EditorThemes];
348
+
349
+ export declare const EditorThemes: {
350
+ readonly light: "light";
351
+ readonly dark: "dark";
352
+ };
353
+
354
+ export declare function EditorToolbar(): JSX_2.Element | null;
355
+
356
+ export declare function EditorToolbarWithActions({ onOpenFind, onOpenExport, onOpenShortcuts, previewMode, onTogglePreview, }?: EditorToolbarWithActionsProps): JSX_2.Element | null;
357
+
358
+ declare interface EditorToolbarWithActionsProps {
359
+ onOpenFind?: () => void;
360
+ onOpenExport?: () => void;
361
+ onOpenShortcuts?: () => void;
362
+ /** Whether preview (read-only viewer) mode is currently active */
363
+ previewMode?: boolean;
364
+ /** Called when the user clicks the preview toggle pill */
365
+ onTogglePreview?: () => void;
366
+ }
367
+
368
+ declare interface EditorViewerProps {
369
+ content: JSONContent;
370
+ theme?: EditorTheme;
371
+ className?: string;
372
+ features?: EditorFeature[];
373
+ separatorIcon?: ReactNode;
374
+ showStats?: boolean;
375
+ }
376
+ export { EditorViewerProps }
377
+ export { EditorViewerProps as MatopsViewerProps }
378
+
379
+ export declare function ExportPanel({ open, onClose }: ExportPanelProps): JSX_2.Element | null;
380
+
381
+ declare interface ExportPanelProps {
382
+ open: boolean;
383
+ onClose: () => void;
384
+ }
385
+
386
+ /**
387
+ * Extract the attachment list. Reads `doc.attrs.attachments.items` first,
388
+ * then falls back to the legacy `attachments` content node.
389
+ */
390
+ export declare function extractAttachments(doc: JSONContent): AttachmentItem[];
391
+
392
+ /**
393
+ * Extract `contentWidth`. Returns `DEFAULT_CONTENT_WIDTH` if absent or invalid.
394
+ */
395
+ export declare function extractContentWidth(doc: JSONContent): ContentWidth;
396
+
397
+ /**
398
+ * Extract the full cover-image attr object.
399
+ * Returns `null` if no cover image attr is present.
400
+ */
401
+ export declare function extractCoverImageAttr(doc: JSONContent): CoverImageDocAttr | null;
402
+
403
+ /**
404
+ * Extract the cover image src. Reads `doc.attrs.coverImage.src` first,
405
+ * then falls back to the legacy `cover-image` content node.
406
+ */
407
+ export declare function extractCoverSrc(doc: JSONContent): string | null;
408
+
409
+ /**
410
+ * Extract the separator icon src. Reads `doc.attrs.separatorIcon.src` first,
411
+ * then falls back to the legacy `separator-icon` content node.
412
+ */
413
+ export declare function extractSeparatorIconSrc(doc: JSONContent): string | null;
414
+
415
+ /**
416
+ * Extract the title text. Reads `doc.attrs.title.text` first,
417
+ * then falls back to the legacy `title` content node.
418
+ */
419
+ export declare function extractTitle(doc: JSONContent): string;
420
+
421
+ /**
422
+ * Extract the full title attr object.
423
+ * Returns `null` if no title attr is present.
424
+ */
425
+ export declare function extractTitleAttr(doc: JSONContent): TitleDocAttr | null;
426
+
427
+ export declare interface FeatureDefinition {
428
+ name: EditorFeature;
429
+ extensions: Extensions;
430
+ dependencies?: EditorFeature[];
431
+ enabled?: (activeFeatures: EditorFeature[]) => boolean;
432
+ }
433
+
434
+ declare class FeatureRegistry {
435
+ private readonly registry;
436
+ register(definition: FeatureDefinition): void;
437
+ resolve(requestedFeatures: EditorFeature[]): ResolvedEditorConfig;
438
+ has(name: EditorFeature): boolean;
439
+ get(name: EditorFeature): FeatureDefinition | undefined;
440
+ list(): EditorFeature[];
441
+ get size(): number;
442
+ unregister(name: EditorFeature): void;
443
+ clear(): void;
444
+ private resolveDependencies;
445
+ private filterEnabled;
446
+ private collectExtensions;
447
+ }
448
+
449
+ export declare const featureRegistry: FeatureRegistry;
450
+
451
+ /**
452
+ * FindReplacePanel
453
+ *
454
+ * UI for the find-and-replace feature. Works with SearchExtension
455
+ * (extensions/plugins/searchPlugin.ts) which manages all ProseMirror
456
+ * decoration state.
457
+ *
458
+ * Optimisation notes vs original:
459
+ * - Search (query + caseSensitive) and navigation (currentIndex) are
460
+ * dispatched separately so changing the active match doesn't re-run the
461
+ * full text search.
462
+ * - Plugin state is read immediately after each dispatch (synchronous in
463
+ * ProseMirror) so results are always consistent.
464
+ */
465
+ export declare function FindReplacePanel({ open, onClose }: FindReplacePanelProps): JSX_2.Element | null;
466
+
467
+ declare interface FindReplacePanelProps {
468
+ open: boolean;
469
+ onClose: () => void;
470
+ }
471
+
472
+ export declare function FormattingBubbleMenu(): JSX_2.Element | null;
473
+
474
+ /**
475
+ * Returns the `attachments` attr from a live editor's document.
476
+ * Returns `null` if the attr is absent or null.
477
+ */
478
+ export declare function getAttachmentsAttr(editor: Editor_2 | null): AttachmentsDocAttr | null;
479
+
480
+ /**
481
+ * Returns the `contentWidth` stored in a live Tiptap editor's document,
482
+ * falling back to `DEFAULT_CONTENT_WIDTH` if the attr is missing or invalid.
483
+ *
484
+ * @example
485
+ * const width = getContentWidth(editor);
486
+ * // → "normal" | "compact" | "full"
487
+ */
488
+ export declare function getContentWidth(editor: Editor_2 | null): ContentWidth;
489
+
490
+ /**
491
+ * Returns the `coverImage` attr from a live editor's document.
492
+ * Returns `null` if the attr is absent or null.
493
+ */
494
+ export declare function getCoverImageAttr(editor: Editor_2 | null): CoverImageDocAttr | null;
495
+
496
+ /**
497
+ * Returns the `separatorIcon` attr from a live editor's document.
498
+ * Returns `null` if the attr is absent or null.
499
+ */
500
+ export declare function getSeparatorIconAttr(editor: Editor_2 | null): SeparatorIconDocAttr | null;
501
+
502
+ /**
503
+ * Returns the `title` attr from a live editor's document.
504
+ * Returns `null` if the attr is absent or null.
505
+ */
506
+ export declare function getTitleAttr(editor: Editor_2 | null): TitleDocAttr | null;
507
+
508
+ /**
509
+ * @matops/editor — Icon Library
510
+ *
511
+ * Single source of truth for all inline SVG icons used across the editor UI.
512
+ * No external icon library dependency — keeps bundle lean and icons consistent.
513
+ *
514
+ * Naming convention: <PurposeName>Icon
515
+ * All icons default to 14×14 unless a specific size is needed by their context.
516
+ */
517
+ declare interface IconProps {
518
+ size?: number;
519
+ }
520
+
521
+ /**
522
+ * ImageBubbleMenu
523
+ *
524
+ * Appears when an image is selected. Provides:
525
+ * - Text alignment (left / center / right)
526
+ * - Alt text editing via prompt
527
+ * - Delete image
528
+ *
529
+ * Uses `pluginKey="imageMenu"` to coexist with text + link bubble menus.
530
+ */
531
+ export declare function ImageBubbleMenu(): JSX_2.Element | null;
532
+
533
+ export declare function isDefaultCrop(crop: CropRect): boolean;
534
+
535
+ /**
536
+ * KeyboardShortcutsModal
537
+ *
538
+ * Feature-aware keyboard shortcut reference.
539
+ * Only shows shortcuts whose `feature` is currently active.
540
+ * Closes on Escape or overlay click.
541
+ *
542
+ * Triggered by: `?` key (via useShortcutsModal) or toolbar Help button.
543
+ */
544
+ export declare function KeyboardShortcutsModal({ open, onClose, }: KeyboardShortcutsModalProps): JSX_2.Element | null;
545
+
546
+ declare interface KeyboardShortcutsModalProps {
547
+ open: boolean;
548
+ onClose: () => void;
549
+ }
550
+
551
+ /**
552
+ * LinkPopover
553
+ *
554
+ * Appears when the cursor is inside a link node.
555
+ * Two modes:
556
+ * - Preview: shows URL with edit / open / unlink buttons
557
+ * - Edit: inline input field to change the URL
558
+ *
559
+ * Uses `pluginKey="linkMenu"` to coexist with the text formatting BubbleMenu.
560
+ */
561
+ export declare function LinkPopover(): JSX_2.Element | null;
562
+
563
+ export declare const MatopsDocument: Node_2<any, any>;
564
+
565
+ declare function MatopsEditor(props: EditorProps): JSX_2.Element;
566
+ export { MatopsEditor as Editor }
567
+ export { MatopsEditor }
568
+ export default MatopsEditor;
569
+
570
+ declare function MatopsViewer({ content, theme, className, separatorIcon, showStats, }: EditorViewerProps): JSX_2.Element;
571
+ export { MatopsViewer as EditorViewer }
572
+ export { MatopsViewer }
573
+
574
+ export declare function MenuButton({ onClick, isActive, isDisabled, isDanger, title, className, children, }: MenuButtonProps): JSX_2.Element;
575
+
576
+ /**
577
+ * MenuButton — shared button primitive for all floating menus.
578
+ *
579
+ * Uses `onMouseDown` + `e.preventDefault()` to prevent the editor from
580
+ * losing focus when a menu button is pressed.
581
+ */
582
+ declare interface MenuButtonProps {
583
+ onClick: () => void;
584
+ isActive?: boolean;
585
+ isDisabled?: boolean;
586
+ isDanger?: boolean;
587
+ title: string;
588
+ className?: string;
589
+ children: default_2.ReactNode;
590
+ }
591
+
592
+ export declare function MenuDivider(): JSX_2.Element;
593
+
594
+ /**
595
+ * Migrate a **legacy** document (structural nodes in `content[]`) to the
596
+ * **external** format (structural metadata in `doc.attrs`).
597
+ *
598
+ * Safe to call on already-migrated documents — returns them unchanged.
599
+ * Alias of `toExternalFormat`.
600
+ */
601
+ export declare const migrateToDocAttrs: typeof toExternalFormat;
602
+
603
+ export declare function ModalDialog({ children, className, label, }: {
604
+ children: default_2.ReactNode;
605
+ className?: string;
606
+ label?: string;
607
+ }): JSX_2.Element;
608
+
609
+ export declare function ModalHeader({ title, onClose, }: {
610
+ title: string;
611
+ onClose: () => void;
612
+ }): JSX_2.Element;
613
+
614
+ export declare function ModalOverlay({ onClose, children, }: {
615
+ onClose: () => void;
616
+ children: default_2.ReactNode;
617
+ }): default_2.ReactPortal;
618
+
619
+ /**
620
+ * PageHeaderGuard — stub extension for the `"page-header"` feature.
621
+ * Previously guarded title + separator-icon content nodes; now a no-op since
622
+ * those sections live in doc.attrs and are rendered as React panels.
623
+ */
624
+ export declare const PageHeaderGuard: Extension<any, any>;
625
+
626
+ export declare function parseAttachments(raw: string | null | undefined): AttachmentItem[];
627
+
628
+ export declare function readCropFromAttrs(attrs: unknown): CropRect;
629
+
630
+ /**
631
+ * Rebuild a Tiptap document in **internal format** (structural nodes in `content[]`)
632
+ * from any input format: external (doc.attrs), legacy (structural content nodes),
633
+ * or internal (already has content nodes).
634
+ *
635
+ * Called by `Editor.tsx` when features change so that Tiptap always receives a
636
+ * document whose `content[]` contains the correct set of structural nodes for the
637
+ * active feature set.
638
+ *
639
+ * @param current - Latest live document JSON (any format).
640
+ * @param hasCoverImage - Whether `"cover-image"` feature is active.
641
+ * @param hasPageHeader - Whether `"page-header"` feature is active.
642
+ * @param hasAttachments - Whether `"attachments"` feature is active.
643
+ * @param fallbackContent - Used as a secondary source when structural data is
644
+ * absent from `current`. Defaults to `current`.
645
+ */
646
+ export declare function rebuildContent(current: JSONContent, hasCoverImage: boolean, hasPageHeader: boolean, hasAttachments: boolean, fallbackContent?: JSONContent): JSONContent;
647
+
648
+ export declare function registerFeature(definition: FeatureDefinition): void;
649
+
650
+ declare interface ResolvedEditorConfig {
651
+ resolvedFeatures: EditorFeature[];
652
+ extensions: Extensions;
653
+ }
654
+
655
+ export declare const SearchExtension: Extension<any, any>;
656
+
657
+ export declare interface SearchMatch {
658
+ from: number;
659
+ to: number;
660
+ }
661
+
662
+ export declare const searchPluginKey: PluginKey<SearchPluginState>;
663
+
664
+ declare interface SearchPluginState {
665
+ query: string;
666
+ caseSensitive: boolean;
667
+ decorations: DecorationSet;
668
+ results: SearchMatch[];
669
+ currentIndex: number;
670
+ }
671
+
672
+ export declare interface SeparatorIconAttrs {
673
+ src: string | null;
674
+ alt: string;
675
+ cropX: number;
676
+ cropY: number;
677
+ cropWidth: number;
678
+ cropHeight: number;
679
+ }
680
+
681
+ export declare interface SeparatorIconDocAttr {
682
+ src: string | null;
683
+ alt: string;
684
+ cropX: number;
685
+ cropY: number;
686
+ cropWidth: number;
687
+ cropHeight: number;
688
+ }
689
+
690
+ export declare const SeparatorIconNode: Node_2<any, any>;
691
+
692
+ /**
693
+ * SeparatorIconPanel — React panel (Panel 1) for the title/body separator.
694
+ *
695
+ * Reads from `editor.state.doc.attrs.separatorIcon` (a SeparatorIconDocAttr or null).
696
+ * Writes via `editor.commands.setSeparatorIconAttr(...)`.
697
+ *
698
+ * Renders: ────── [badge] ──────
699
+ *
700
+ * Icon resolution priority:
701
+ * 1. doc.attrs.separatorIcon.src + crop → uploaded image, cropped
702
+ * 2. separatorIcon prop (ReactNode from EditorProvider)
703
+ * 3. Default quill SVG
704
+ *
705
+ * No ProseMirror NodeView — this is pure React rendered above <EditorContent>.
706
+ */
707
+ export declare function SeparatorIconPanel(): JSX_2.Element | null;
708
+
709
+ export declare function serializeAttachments(items: AttachmentItem[]): string;
710
+
711
+ export declare function SlashMenu(): JSX_2.Element | null;
712
+
713
+ /**
714
+ * Live defaults consumed by the table insert command in commandRegistry.ts
715
+ * and by the toolbar / slash-menu integrations.
716
+ *
717
+ * Do NOT mutate this object directly — use configureTablesFeature() instead
718
+ * so that the feature registration is re-run with the updated options.
719
+ */
720
+ export declare const TABLE_DEFAULTS: Required<TableFeatureOptions>;
721
+
722
+ /**
723
+ * TableContextMenu
724
+ *
725
+ * A BubbleMenu that appears whenever the cursor is inside a table.
726
+ * Uses `pluginKey="tableMenu"` to coexist with other BubbleMenus —
727
+ * FormattingBubbleMenu suppresses itself when `e.isActive("table")` so
728
+ * these two menus never overlap.
729
+ *
730
+ * Sections (always): Row | Column | Cell | Table
731
+ * Section (conditional): Format — shown only when text is selected inside
732
+ * a cell (from !== to), giving users bold/italic/etc. without needing to
733
+ * dismiss the table menu first.
734
+ *
735
+ * v3: imports from @tiptap/react/menus, uses `options` (not `tippyOptions`).
736
+ */
737
+ export declare function TableContextMenu(): JSX_2.Element | null;
738
+
739
+ /**
740
+ * @feature tables
741
+ *
742
+ * Provides rich table support built on top of @tiptap/extension-table's
743
+ * TableKit bundle (Table + TableRow + TableHeader + TableCell).
744
+ *
745
+ * ── What this feature provides ────────────────────────────────────────
746
+ *
747
+ * Extensions (Tiptap)
748
+ * • Table — the table node with column-resize handles
749
+ * • TableRow — <tr> node
750
+ * • TableHeader — <th> node with default header styling
751
+ * • TableCell — <td> node
752
+ *
753
+ * UI (activated automatically when this feature is resolved)
754
+ * • Toolbar button — "Insert Table" icon in the toolbar block strip
755
+ * • Slash command — /table inserts a default-sized table
756
+ * • TableContextMenu — BubbleMenu that appears inside a table with
757
+ * Row / Column / Cell / Table action sections
758
+ *
759
+ * Keyboard shortcuts (built into Tiptap table extensions)
760
+ * • Tab — move to next cell (creates new row at end)
761
+ * • Shift+Tab — move to previous cell
762
+ *
763
+ * ── Enabled vs disabled ───────────────────────────────────────────────
764
+ *
765
+ * Enabled → TableKit is loaded. All of the above are active. The toolbar
766
+ * table button and /table slash command both insert a table with
767
+ * TABLE_DEFAULTS rows, cols, and withHeaderRow settings.
768
+ *
769
+ * Disabled → No table schema. Existing table nodes in a document will not
770
+ * parse correctly and content may be lost. If you need read-only
771
+ * table rendering without the editing UI, keep this feature on
772
+ * and set `editable={false}` on the editor instead.
773
+ *
774
+ * ── Custom configuration ──────────────────────────────────────────────
775
+ *
776
+ * Call configureTablesFeature(options) BEFORE featureBootstrap runs to
777
+ * override the defaults. Typically done in your app entry point:
778
+ *
779
+ * import { configureTablesFeature } from "@matops/editor/extensions/features/tablesFeature";
780
+ *
781
+ * configureTablesFeature({
782
+ * resizable: true,
783
+ * cellMinWidth: 80,
784
+ * defaultRows: 4,
785
+ * defaultCols: 4,
786
+ * withHeaderRow: true,
787
+ * });
788
+ *
789
+ * featureBootstrap.ts still re-imports this file — the registration is
790
+ * idempotent (it warns and overwrites on duplicate) so calling
791
+ * configureTablesFeature() first is safe and the correct approach.
792
+ *
793
+ * ── Dependencies ─────────────────────────────────────────────────────
794
+ * Requires "formatting" because TableCell content is paragraph-based and
795
+ * needs the paragraph + text extensions already registered.
796
+ */
797
+ /**
798
+ * Options for the "tables" feature.
799
+ *
800
+ * All fields are optional — unset fields fall back to TABLE_DEFAULTS.
801
+ */
802
+ export declare interface TableFeatureOptions {
803
+ /**
804
+ * Allow column widths to be dragged to resize.
805
+ * @default true
806
+ */
807
+ resizable?: boolean;
808
+ /**
809
+ * Minimum width in pixels for every table cell.
810
+ * Prevents columns from collapsing to zero when resizing.
811
+ * @default 100
812
+ */
813
+ cellMinWidth?: number;
814
+ /**
815
+ * Whether the last column can be resized.
816
+ * Set false when the table fills the full content width and the last
817
+ * column should stretch to absorb any remaining space.
818
+ * @default true
819
+ */
820
+ lastColumnResizable?: boolean;
821
+ /**
822
+ * Number of rows inserted when a new table is created via the toolbar
823
+ * button or the /table slash command.
824
+ * @default 3
825
+ */
826
+ defaultRows?: number;
827
+ /**
828
+ * Number of columns inserted when a new table is created.
829
+ * @default 3
830
+ */
831
+ defaultCols?: number;
832
+ /**
833
+ * Whether newly inserted tables include a styled header row.
834
+ * @default true
835
+ */
836
+ withHeaderRow?: boolean;
837
+ }
838
+
839
+ export declare interface TitleAttrs {
840
+ placeholder: string;
841
+ }
842
+
843
+ export declare interface TitleDocAttr {
844
+ text: string;
845
+ placeholder: string;
846
+ }
847
+
848
+ export declare const TitleNode: Node_2<any, any>;
849
+
850
+ /**
851
+ * TitlePanel — React panel (Panel 1) for the document title.
852
+ *
853
+ * Reads from `editor.state.doc.attrs.title` (a TitleDocAttr or null).
854
+ * Writes via `editor.commands.setTitleAttr({ text })`.
855
+ *
856
+ * Uses a contentEditable div so the user gets native text-editing behaviour
857
+ * (cursor, selection, copy/paste). The doc.attrs.title.text is the source of
858
+ * truth; the div is kept in sync via a ref and only triggers a command when
859
+ * the user actually changes the text (avoids re-render loops).
860
+ *
861
+ * No ProseMirror NodeView — this is pure React rendered above <EditorContent>.
862
+ */
863
+ export declare function TitlePanel(): JSX_2.Element | null;
864
+
865
+ export declare function TitleSeparator({ icon, hasImageIcon }: TitleSeparatorProps): JSX_2.Element;
866
+
867
+ declare interface TitleSeparatorProps {
868
+ /** Custom icon to render in the centre badge. Defaults to the quill SVG. */
869
+ icon?: ReactNode;
870
+ /**
871
+ * Set to true when the icon is an uploaded image that may use position:absolute
872
+ * crop styles. Adds `overflow:hidden` + `position:relative` to the badge span
873
+ * so the cropped image is clipped correctly.
874
+ */
875
+ hasImageIcon?: boolean;
876
+ }
877
+
878
+ /**
879
+ * Convert a Tiptap **internal** document (structural nodes in `content[]`) to
880
+ * **external** format (structural metadata in `doc.attrs`, body-only `content[]`).
881
+ *
882
+ * This is called inside `useEditorInstance` before firing `onChange` so that
883
+ * consumers always receive the canonical external format regardless of how
884
+ * Tiptap stores the document internally.
885
+ *
886
+ * Safe to call on documents already in external format — returns them unchanged.
887
+ */
888
+ export declare function toExternalFormat(doc: JSONContent): JSONContent;
889
+
890
+ /**
891
+ * Convert a Tiptap JSONContent node tree to Markdown.
892
+ *
893
+ * Supports all standard nodes plus the page-header nodes:
894
+ * - `cover-image` → HTML img tag (no native Markdown equivalent)
895
+ * - `title` → # heading (H1)
896
+ *
897
+ * @param node - The JSONContent node to serialize
898
+ * @param depth - Indentation depth for nested lists (internal)
899
+ */
900
+ export declare function toMarkdown(node: JSONContent, depth?: number): string;
901
+
902
+ export declare const UploadIcon: ({ size }: IconProps) => JSX_2.Element;
903
+
904
+ /**
905
+ * useDropdown
906
+ *
907
+ * Shared hook for portal-based dropdown menus used in EditorToolbar and
908
+ * FormattingBubbleMenu. Handles:
909
+ * - Open / close state
910
+ * - Post-render position measurement (via useLayoutEffect)
911
+ * - Viewport clamping so the menu never overflows the screen
912
+ * - Close on outside click and scroll
913
+ *
914
+ * The menu is initially rendered off-screen (left: -9999) and becomes
915
+ * visible only after its real dimensions are measured.
916
+ *
917
+ * @example
918
+ * const { open, visible, dropPos, triggerRef, menuRef, toggle, close } =
919
+ * useDropdown({ offset: 6 });
920
+ */
921
+ export declare function useDropdown({ offset }?: UseDropdownOptions): UseDropdownResult;
922
+
923
+ declare interface UseDropdownOptions {
924
+ /** Extra vertical gap between trigger and menu (px). Default 4. */
925
+ offset?: number;
926
+ }
927
+
928
+ declare interface UseDropdownResult {
929
+ open: boolean;
930
+ visible: boolean;
931
+ dropPos: DropdownPosition;
932
+ triggerRef: RefObject<HTMLButtonElement>;
933
+ menuRef: RefObject<HTMLDivElement>;
934
+ toggle: () => void;
935
+ close: () => void;
936
+ }
937
+
938
+ /**
939
+ * Returns the live Tiptap editor instance, or null while initializing.
940
+ */
941
+ export declare const useEditor: () => Editor_2 | null;
942
+
943
+ /**
944
+ * Returns the raw EditorContext value.
945
+ * Use the focused hooks in editorHooks.ts for most cases — this is
946
+ * only needed when accessing onFileUploaded or separatorIcon directly.
947
+ */
948
+ export declare function useEditorContext(): EditorContextValue;
949
+
950
+ /**
951
+ * useEditorInstance
952
+ *
953
+ * Manages the full Tiptap editor lifecycle for the <Editor> component.
954
+ *
955
+ * Design decisions:
956
+ * - `buildEditorConfig` is called once on mount and stored in a ref. The
957
+ * feature list and placeholder are intentionally not reactive — changing
958
+ * them after mount would require destroying and recreating the entire editor.
959
+ * - Callback props (onChange, onContentChange, onEditorReady) are stored in
960
+ * refs so they can be updated without recreating the editor instance.
961
+ * - `editable` is the only prop synced reactively via editor.setEditable().
962
+ */
963
+ export declare function useEditorInstance({ features, initialContent, editable, placeholder, collaborationProvider, onChange, onContentChange, onEditorReady, }: UseEditorInstanceOptions): UseEditorInstanceResult;
964
+
965
+ /**
966
+ * Subset of EditorProps consumed by this hook.
967
+ * Uses Pick so adding a prop to EditorProps automatically makes it available here.
968
+ *
969
+ * NOTE: "placeholder" is intentionally included even though the Placeholder
970
+ * extension is configured at build time (not Tiptap's internal placeholder).
971
+ * It must flow through here → buildEditorConfig → Placeholder.configure().
972
+ */
973
+ declare type UseEditorInstanceOptions = Pick<EditorProps, "features" | "initialContent" | "editable" | "placeholder" | "collaborationProvider" | "onChange" | "onContentChange" | "onEditorReady">;
974
+
975
+ declare interface UseEditorInstanceResult {
976
+ editor: Editor_2 | null;
977
+ resolvedFeatures: EditorFeature[];
978
+ }
979
+
980
+ /**
981
+ * useEditorKeyboard
982
+ *
983
+ * Central hub for all global keyboard shortcuts that live outside the
984
+ * Tiptap extension system. Tiptap handles its own shortcuts (bold, italic, etc.)
985
+ * internally — this hook only manages shortcuts that need React state or that
986
+ * trigger UI panels/modals.
987
+ *
988
+ * Shortcuts managed here:
989
+ * - Ctrl/Cmd + Shift + P → onPublish (save/publish document)
990
+ * - Ctrl/Cmd + F → open Find & Replace panel
991
+ *
992
+ * To add a new shortcut:
993
+ * 1. Add its handler inside the `handler` function below
994
+ * 2. Add it to KeyboardShortcutsModal.tsx SHORTCUTS data array
995
+ *
996
+ * Shortcuts NOT managed here:
997
+ * - `?` → KeyboardShortcutsModal (managed by useShortcutsModal)
998
+ * - Formatting shortcuts (Bold, Italic…) → handled by Tiptap extensions
999
+ * - Table navigation (Tab) → handled by Tiptap table extension
1000
+ * - Slash command (/) → handled by SlashMenu
1001
+ * - Escape → handled locally in each panel/modal component
1002
+ */
1003
+ export declare function useEditorKeyboard({ editor, editable, resolvedFeatures, onPublish, onOpenFind, }: UseEditorKeyboardOptions): void;
1004
+
1005
+ declare interface UseEditorKeyboardOptions {
1006
+ editor: Editor_2 | null;
1007
+ editable: boolean;
1008
+ resolvedFeatures: EditorFeature[];
1009
+ onPublish?: (content: JSONContent) => void;
1010
+ onOpenFind?: () => void;
1011
+ }
1012
+
1013
+ /**
1014
+ * Subscribes to a derived slice of editor state.
1015
+ * Re-renders only when the selected value changes (by reference equality).
1016
+ *
1017
+ * @example
1018
+ * const isBold = useEditorState(ed => ed?.isActive("bold") ?? false);
1019
+ */
1020
+ export declare function useEditorState<T>(selector: (editor: Editor_2 | null) => T): T;
1021
+
1022
+ /**
1023
+ * Returns the current editor theme.
1024
+ */
1025
+ export declare function useEditorTheme(): EditorTheme;
1026
+
1027
+ /**
1028
+ * Forces a re-render on every editor transaction.
1029
+ * Use when you need UI to stay in sync with every state change.
1030
+ */
1031
+ export declare function useEditorUpdate(): void;
1032
+
1033
+ /**
1034
+ * Returns true if a single feature is active in the current editor instance.
1035
+ */
1036
+ export declare function useFeature(feature: EditorFeature): boolean;
1037
+
1038
+ /**
1039
+ * Returns a map of all feature flags as named booleans.
1040
+ * Prefer this over multiple `useFeature` calls in the same component.
1041
+ */
1042
+ export declare function useFeatures(): {
1043
+ hasFormatting: boolean;
1044
+ hasCode: boolean;
1045
+ hasMedia: boolean;
1046
+ hasTables: boolean;
1047
+ hasTaskList: boolean;
1048
+ hasCollaboration: boolean;
1049
+ hasHistory: boolean;
1050
+ hasPlaceholder: boolean;
1051
+ hasCharCount: boolean;
1052
+ hasFindReplace: boolean;
1053
+ hasCoverImage: boolean;
1054
+ hasPageHeader: boolean;
1055
+ hasAttachments: boolean;
1056
+ };
1057
+
1058
+ export declare function useImageUpload({ editor, editable, onFileUploaded, containerRef, }: UseImageUploadOptions): void;
1059
+
1060
+ declare interface UseImageUploadOptions {
1061
+ editor: Editor_2 | null;
1062
+ editable: boolean;
1063
+ onFileUploaded?: (file: File) => Promise<string>;
1064
+ containerRef?: React.RefObject<HTMLElement>;
1065
+ }
1066
+
1067
+ /**
1068
+ * useShortcutsModal
1069
+ *
1070
+ * Manages open/close state for the KeyboardShortcutsModal.
1071
+ * Binds the `?` key globally to toggle the modal, but only when
1072
+ * the active element is not a text input or textarea (to avoid
1073
+ * interfering with typing "?" in form fields or in the editor itself).
1074
+ *
1075
+ * Usage in parent:
1076
+ * ```ts
1077
+ * const { open, onClose, setOpen } = useShortcutsModal();
1078
+ * <KeyboardShortcutsModal open={open} onClose={onClose} />
1079
+ * ```
1080
+ */
1081
+ export declare function useShortcutsModal(): {
1082
+ open: boolean;
1083
+ setOpen: Dispatch<SetStateAction<boolean>>;
1084
+ onClose: () => void;
1085
+ };
1086
+
1087
+ export { }
1088
+
1089
+
1090
+ /** Augment Tiptap's Commands interface so TypeScript knows about all doc-attr setters. */
1091
+ declare module "@tiptap/core" {
1092
+ interface Commands<ReturnType> {
1093
+ matopsDocument: {
1094
+ /**
1095
+ * Update the document-level `contentWidth` layout attribute.
1096
+ *
1097
+ * @example
1098
+ * editor.commands.setContentWidth("compact")
1099
+ */
1100
+ setContentWidth: (width: MatopsContentWidth) => ReturnType;
1101
+ /**
1102
+ * Set or clear the cover-image doc attr.
1103
+ *
1104
+ * Pass a partial `CoverImageDocAttr` to merge into the existing value,
1105
+ * or `null` to remove the cover image (keeps the attr key with null value
1106
+ * so the feature knows the slot exists).
1107
+ *
1108
+ * @example
1109
+ * editor.commands.setCoverImageAttr({ src: "https://cdn.example.com/banner.jpg" })
1110
+ * editor.commands.setCoverImageAttr(null) // clear image
1111
+ */
1112
+ setCoverImageAttr: (value: Partial<CoverImageDocAttr> | null) => ReturnType;
1113
+ /**
1114
+ * Update the title text and/or placeholder stored in the doc attr.
1115
+ *
1116
+ * @example
1117
+ * editor.commands.setTitleAttr({ text: "My Document" })
1118
+ * editor.commands.setTitleAttr({ text: "Draft", placeholder: "Untitled" })
1119
+ */
1120
+ setTitleAttr: (value: Partial<TitleDocAttr> | null) => ReturnType;
1121
+ /**
1122
+ * Set or clear the separator-icon doc attr.
1123
+ *
1124
+ * @example
1125
+ * editor.commands.setSeparatorIconAttr({ src: "https://cdn.example.com/logo.png" })
1126
+ * editor.commands.setSeparatorIconAttr(null) // revert to default icon
1127
+ */
1128
+ setSeparatorIconAttr: (value: Partial<SeparatorIconDocAttr> | null) => ReturnType;
1129
+ /**
1130
+ * Replace the attachments list stored in the doc attr.
1131
+ *
1132
+ * @example
1133
+ * editor.commands.setAttachmentsAttr([
1134
+ * { id: "a1", name: "brief.pdf", size: 51200, mimeType: "application/pdf", url: "https://…" }
1135
+ * ])
1136
+ */
1137
+ setAttachmentsAttr: (items: AttachmentItem[]) => ReturnType;
1138
+ };
1139
+ }
1140
+ }