@tessera-editor/core 0.1.0 → 0.2.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/dist/index.d.ts +137 -173
- package/dist/index.js +307 -405
- package/dist/index.js.map +1 -1
- package/dist/tessera.css +132 -120
- package/package.json +1 -1
- package/src/__tests__/host-config.test.ts +115 -0
- package/src/__tests__/linkedit.test.ts +98 -0
- package/src/__tests__/table-guard.test.ts +155 -0
- package/src/__tests__/v11.test.ts +1 -50
- package/src/extensions/context-menu.ts +7 -2
- package/src/extensions/shortcuts.ts +8 -6
- package/src/extensions/slash.ts +43 -25
- package/src/i18n.ts +26 -33
- package/src/index.ts +10 -13
- package/src/linkedit.ts +66 -0
- package/src/markdown.ts +0 -1
- package/src/nodes/table.ts +130 -0
- package/src/preset.ts +65 -26
- package/src/services.ts +1 -22
- package/src/styles/tessera.css +132 -120
- package/src/diff.ts +0 -150
- package/src/extensions/history.ts +0 -133
- package/src/marks/placeholder.ts +0 -63
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as _tiptap_core from '@tiptap/core';
|
|
2
|
-
import { Node, Mark, Extension, Editor, Range,
|
|
2
|
+
import { Node, Mark, Extension, Editor, Range, Extensions, JSONContent } from '@tiptap/core';
|
|
3
|
+
export { Editor, JSONContent } from '@tiptap/core';
|
|
3
4
|
import * as _tiptap_extension_table from '@tiptap/extension-table';
|
|
4
5
|
export { TableRow as AiTableRow } from '@tiptap/extension-table';
|
|
5
6
|
import * as _tiptap_pm_model from '@tiptap/pm/model';
|
|
@@ -94,6 +95,8 @@ declare module '@tiptap/core' {
|
|
|
94
95
|
setColumnType: (index: number, kind: TableColumnKind) => ReturnType;
|
|
95
96
|
/** Stable-sort body rows by column `index`. */
|
|
96
97
|
sortTableByColumn: (index: number, direction: 'asc' | 'desc') => ReturnType;
|
|
98
|
+
/** Strip stray hidden text from every typed (non-text) cell. */
|
|
99
|
+
normalizeTypedCells: () => ReturnType;
|
|
97
100
|
};
|
|
98
101
|
}
|
|
99
102
|
}
|
|
@@ -182,24 +185,6 @@ declare function listCommentRanges(state: _tiptap_pm_state.EditorState): {
|
|
|
182
185
|
}[];
|
|
183
186
|
declare const CommentCommands: Extension<any, any>;
|
|
184
187
|
|
|
185
|
-
/**
|
|
186
|
-
* Placeholder mark (v1.1, Slite's 占位符): dashed-underline token marking a
|
|
187
|
-
* spot to fill in later — text / person / date / doc-link. ⌘⌥P toggles it on
|
|
188
|
-
* a selection; slash items insert ready-made tokens.
|
|
189
|
-
*/
|
|
190
|
-
type PlaceholderKind = 'text' | 'person' | 'date' | 'doc';
|
|
191
|
-
declare const PlaceholderMark: Mark<any, any>;
|
|
192
|
-
declare module '@tiptap/core' {
|
|
193
|
-
interface Commands<ReturnType> {
|
|
194
|
-
tesseraPlaceholder: {
|
|
195
|
-
togglePlaceholderMark: (kind?: PlaceholderKind) => ReturnType;
|
|
196
|
-
/** Insert a placeholder token at the caret (selection replaced). */
|
|
197
|
-
insertPlaceholderToken: (kind: PlaceholderKind, label: string) => ReturnType;
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
declare const PlaceholderCommands: Extension<any, any>;
|
|
202
|
-
|
|
203
188
|
/**
|
|
204
189
|
* Tessera-specific input rules beyond the StarterKit defaults:
|
|
205
190
|
* `[] ` + typing → task list (mirrors Slite's trigger)
|
|
@@ -229,7 +214,6 @@ declare module '@tiptap/core' {
|
|
|
229
214
|
'tessera:findPanel': Record<string, never>;
|
|
230
215
|
'tessera:insertImage': Record<string, never>;
|
|
231
216
|
'tessera:askPanel': Record<string, never>;
|
|
232
|
-
'tessera:historyPanel': Record<string, never>;
|
|
233
217
|
'tessera:commentPanel': Record<string, never>;
|
|
234
218
|
}
|
|
235
219
|
}
|
|
@@ -327,6 +311,8 @@ declare const tesseraMessages: {
|
|
|
327
311
|
readonly highlightNone: "无高亮";
|
|
328
312
|
readonly linkPlaceholder: "链接地址…";
|
|
329
313
|
readonly linkApply: "应用";
|
|
314
|
+
readonly linkSave: "保存";
|
|
315
|
+
readonly linkTextPlaceholder: "显示文字…";
|
|
330
316
|
readonly linkRemove: "移除链接";
|
|
331
317
|
readonly linkOpen: "打开";
|
|
332
318
|
readonly emptyLineExpand: "展开全部块";
|
|
@@ -342,6 +328,10 @@ declare const tesseraMessages: {
|
|
|
342
328
|
readonly imageAlignLeft: "左对齐";
|
|
343
329
|
readonly imageAlignCenter: "居中";
|
|
344
330
|
readonly imageAlignFull: "全宽";
|
|
331
|
+
readonly imageZoomIn: "放大";
|
|
332
|
+
readonly imageZoomOut: "缩小";
|
|
333
|
+
readonly imageZoomReset: "重置";
|
|
334
|
+
readonly imageZoomHint: "Ctrl+滚轮缩放 · 双击图片复位";
|
|
345
335
|
readonly aiTitle: "AI";
|
|
346
336
|
readonly aiAskPlaceholder: "针对本文档提问…";
|
|
347
337
|
readonly aiSend: "发送";
|
|
@@ -378,16 +368,6 @@ declare const tesseraMessages: {
|
|
|
378
368
|
readonly tableCopyCsv: "复制为 CSV";
|
|
379
369
|
readonly tableToggleHeader: "切换表头";
|
|
380
370
|
readonly cellEmpty: "空";
|
|
381
|
-
readonly itemHistory: "版本历史";
|
|
382
|
-
readonly historyTitle: "版本历史";
|
|
383
|
-
readonly historyEmpty: "暂无快照(编辑后空闲自动保存,或手动捕获)";
|
|
384
|
-
readonly historyCapture: "捕获快照";
|
|
385
|
-
readonly historyRestore: "恢复此版本";
|
|
386
|
-
readonly historyCurrent: "当前";
|
|
387
|
-
readonly historyDiffAdded: "新增";
|
|
388
|
-
readonly historyDiffRemoved: "删除";
|
|
389
|
-
readonly historyDiffChanged: "修改";
|
|
390
|
-
readonly historyConfirmRestore: "恢复到该版本?当前内容将被替换(可撤销)";
|
|
391
371
|
readonly tooltipCommentV11: "评论";
|
|
392
372
|
readonly commentTitle: "评论";
|
|
393
373
|
readonly commentEmpty: "暂无评论";
|
|
@@ -407,9 +387,6 @@ declare const tesseraMessages: {
|
|
|
407
387
|
readonly embedInvalid: "无效链接";
|
|
408
388
|
readonly embedOpen: "打开原链接";
|
|
409
389
|
readonly tocEmpty: "暂无标题——添加 H1–H4 后自动出现";
|
|
410
|
-
readonly placeholderText: "待补充";
|
|
411
|
-
readonly placeholderPerson: "待填人";
|
|
412
|
-
readonly placeholderDate: "待填日期";
|
|
413
390
|
readonly menuCopyAnchor: "复制锚链接";
|
|
414
391
|
readonly menuCopyBlockId: "复制块 ID";
|
|
415
392
|
readonly menuDeleteBlock: "删除此块";
|
|
@@ -467,6 +444,8 @@ declare const tesseraMessages: {
|
|
|
467
444
|
readonly highlightNone: "No highlight";
|
|
468
445
|
readonly linkPlaceholder: "Link URL…";
|
|
469
446
|
readonly linkApply: "Apply";
|
|
447
|
+
readonly linkSave: "Save";
|
|
448
|
+
readonly linkTextPlaceholder: "Link text…";
|
|
470
449
|
readonly linkRemove: "Remove link";
|
|
471
450
|
readonly linkOpen: "Open";
|
|
472
451
|
readonly emptyLineExpand: "Show all blocks";
|
|
@@ -482,6 +461,10 @@ declare const tesseraMessages: {
|
|
|
482
461
|
readonly imageAlignLeft: "Align left";
|
|
483
462
|
readonly imageAlignCenter: "Center";
|
|
484
463
|
readonly imageAlignFull: "Full width";
|
|
464
|
+
readonly imageZoomIn: "Zoom in";
|
|
465
|
+
readonly imageZoomOut: "Zoom out";
|
|
466
|
+
readonly imageZoomReset: "Reset zoom";
|
|
467
|
+
readonly imageZoomHint: "Ctrl+scroll to zoom · double-click to reset";
|
|
485
468
|
readonly aiTitle: "AI";
|
|
486
469
|
readonly aiAskPlaceholder: "Ask about this doc…";
|
|
487
470
|
readonly aiSend: "Send";
|
|
@@ -518,16 +501,6 @@ declare const tesseraMessages: {
|
|
|
518
501
|
readonly tableCopyCsv: "Copy as CSV";
|
|
519
502
|
readonly tableToggleHeader: "Toggle header row";
|
|
520
503
|
readonly cellEmpty: "Empty";
|
|
521
|
-
readonly itemHistory: "Version history";
|
|
522
|
-
readonly historyTitle: "Version history";
|
|
523
|
-
readonly historyEmpty: "No snapshots yet (auto-captured when idle, or capture manually)";
|
|
524
|
-
readonly historyCapture: "Capture snapshot";
|
|
525
|
-
readonly historyRestore: "Restore this version";
|
|
526
|
-
readonly historyCurrent: "Current";
|
|
527
|
-
readonly historyDiffAdded: "Added";
|
|
528
|
-
readonly historyDiffRemoved: "Removed";
|
|
529
|
-
readonly historyDiffChanged: "Changed";
|
|
530
|
-
readonly historyConfirmRestore: "Restore this version? Current content will be replaced (undoable)";
|
|
531
504
|
readonly tooltipCommentV11: "Comment";
|
|
532
505
|
readonly commentTitle: "Comments";
|
|
533
506
|
readonly commentEmpty: "No comments yet";
|
|
@@ -547,17 +520,16 @@ declare const tesseraMessages: {
|
|
|
547
520
|
readonly embedInvalid: "Invalid URL";
|
|
548
521
|
readonly embedOpen: "Open original";
|
|
549
522
|
readonly tocEmpty: "No headings yet — add H1–H4 and they appear here";
|
|
550
|
-
readonly placeholderText: "to fill in";
|
|
551
|
-
readonly placeholderPerson: "assignee";
|
|
552
|
-
readonly placeholderDate: "due date";
|
|
553
523
|
readonly menuCopyAnchor: "Copy anchor link";
|
|
554
524
|
readonly menuCopyBlockId: "Copy block ID";
|
|
555
525
|
readonly menuDeleteBlock: "Delete block";
|
|
556
526
|
};
|
|
557
527
|
};
|
|
558
528
|
type TesseraMessageKey = keyof typeof tesseraMessages['zh-CN'];
|
|
529
|
+
/** Host-side overrides of individual UI strings (merged over the dictionary). */
|
|
530
|
+
type TesseraMessageOverrides = Partial<Record<TesseraMessageKey, string>>;
|
|
559
531
|
type TesseraTranslator = (key: TesseraMessageKey) => string;
|
|
560
|
-
declare function createTesseraT(locale?: TesseraLocale): TesseraTranslator;
|
|
532
|
+
declare function createTesseraT(locale?: TesseraLocale, overrides?: TesseraMessageOverrides): TesseraTranslator;
|
|
561
533
|
|
|
562
534
|
/**
|
|
563
535
|
* Slash menu (acceptance §1): framework-neutral extension built on
|
|
@@ -587,7 +559,17 @@ interface SlashMenuOptions {
|
|
|
587
559
|
}) => SlashMenuItem[];
|
|
588
560
|
includeAiItems?: boolean;
|
|
589
561
|
render?: SlashRenderFactory;
|
|
590
|
-
|
|
562
|
+
/**
|
|
563
|
+
* Host block policy (mirrors `TesseraPresetOptions.excludeBlocks`): drop
|
|
564
|
+
* default items whose node type is excluded, so the menu never offers a
|
|
565
|
+
* block the editor cannot represent.
|
|
566
|
+
*/
|
|
567
|
+
excludeItems?: string[];
|
|
568
|
+
}
|
|
569
|
+
/** Slash item id → the node type it produces. */
|
|
570
|
+
declare function slashItemNodeName(item: SlashMenuItem): string;
|
|
571
|
+
/** Drop default items whose node type is excluded by the host. */
|
|
572
|
+
declare function filterSlashItems(items: SlashMenuItem[], excludeBlocks?: string[]): SlashMenuItem[];
|
|
591
573
|
/** Default block items — mirrors the Slite slash palette for the M1 scope. */
|
|
592
574
|
declare function defaultSlashItems(t: TesseraTranslator): SlashMenuItem[];
|
|
593
575
|
declare const SlashMenu: Extension<SlashMenuOptions, any>;
|
|
@@ -619,102 +601,6 @@ interface EmojiMenuOptions {
|
|
|
619
601
|
}
|
|
620
602
|
declare const EmojiMenu: Extension<EmojiMenuOptions, any>;
|
|
621
603
|
|
|
622
|
-
/**
|
|
623
|
-
* Injected services (ADR-0001 family): the component family never performs
|
|
624
|
-
* network or storage I/O itself. Hosts provide implementations through the
|
|
625
|
-
* binding; the editor reads them from `editor.storage.tesseraServices`.
|
|
626
|
-
*/
|
|
627
|
-
interface UploadedAsset {
|
|
628
|
-
url: string;
|
|
629
|
-
name?: string;
|
|
630
|
-
mime?: string;
|
|
631
|
-
}
|
|
632
|
-
interface UploadService {
|
|
633
|
-
uploadImage(file: File | Blob): Promise<UploadedAsset>;
|
|
634
|
-
uploadFile?(file: File | Blob): Promise<UploadedAsset>;
|
|
635
|
-
}
|
|
636
|
-
/** v1.1: persistent version history storage (snapshots keyed by time). */
|
|
637
|
-
interface DocSnapshot {
|
|
638
|
-
id: string;
|
|
639
|
-
ts: number;
|
|
640
|
-
doc: JSONContent;
|
|
641
|
-
label?: string;
|
|
642
|
-
}
|
|
643
|
-
interface StorageService {
|
|
644
|
-
saveSnapshot(snapshot: DocSnapshot): Promise<void>;
|
|
645
|
-
listSnapshots(): Promise<DocSnapshot[]>;
|
|
646
|
-
deleteSnapshot?(id: string): Promise<void>;
|
|
647
|
-
}
|
|
648
|
-
/** v1.1: inline comments. */
|
|
649
|
-
interface CommentEntry {
|
|
650
|
-
id: string;
|
|
651
|
-
authorId: string;
|
|
652
|
-
authorName: string;
|
|
653
|
-
text: string;
|
|
654
|
-
ts: number;
|
|
655
|
-
}
|
|
656
|
-
interface CommentThread {
|
|
657
|
-
id: string;
|
|
658
|
-
quote: string;
|
|
659
|
-
resolved: boolean;
|
|
660
|
-
createdAt: number;
|
|
661
|
-
entries: CommentEntry[];
|
|
662
|
-
}
|
|
663
|
-
interface CommentStore {
|
|
664
|
-
list(): Promise<CommentThread[]>;
|
|
665
|
-
upsert(thread: CommentThread): Promise<void>;
|
|
666
|
-
remove(id: string): Promise<void>;
|
|
667
|
-
}
|
|
668
|
-
/** v1.1: who is editing (author of comments / history labels). */
|
|
669
|
-
interface IdentityService {
|
|
670
|
-
getCurrentUser(): {
|
|
671
|
-
id: string;
|
|
672
|
-
name: string;
|
|
673
|
-
} | null;
|
|
674
|
-
}
|
|
675
|
-
interface TesseraServicesStorage {
|
|
676
|
-
upload?: UploadService;
|
|
677
|
-
storage?: StorageService;
|
|
678
|
-
comments?: CommentStore;
|
|
679
|
-
identity?: IdentityService;
|
|
680
|
-
}
|
|
681
|
-
declare const TesseraServices: Extension<any, any>;
|
|
682
|
-
type ServicesEditor = {
|
|
683
|
-
storage: unknown;
|
|
684
|
-
};
|
|
685
|
-
declare function getUploadService(editor: ServicesEditor): UploadService | undefined;
|
|
686
|
-
declare function getStorageService(editor: ServicesEditor): StorageService | undefined;
|
|
687
|
-
declare function getCommentStore(editor: ServicesEditor): CommentStore | undefined;
|
|
688
|
-
declare function getIdentityService(editor: ServicesEditor): IdentityService | undefined;
|
|
689
|
-
|
|
690
|
-
/**
|
|
691
|
-
* Version history capture (v1.1): snapshots the canonical JSON into the
|
|
692
|
-
* injected StorageService. Auto-capture fires after `idleMs` of inactivity
|
|
693
|
-
* (Slite: 5 minutes); manual capture is always available as a command.
|
|
694
|
-
*/
|
|
695
|
-
interface HistorySnapshotOptions {
|
|
696
|
-
/** idle window before auto-capture (default 5 minutes) */
|
|
697
|
-
idleMs?: number;
|
|
698
|
-
/** minimum ms between two auto-captures (default 60s) */
|
|
699
|
-
minIntervalMs?: number;
|
|
700
|
-
/** snapshot label source */
|
|
701
|
-
label?: () => string | undefined;
|
|
702
|
-
}
|
|
703
|
-
declare module '@tiptap/core' {
|
|
704
|
-
interface Commands<ReturnType> {
|
|
705
|
-
tesseraHistory: {
|
|
706
|
-
captureSnapshot: (label?: string) => ReturnType;
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
|
-
interface EditorEvents {
|
|
710
|
-
'tessera:snapshotSaved': {
|
|
711
|
-
snapshot: DocSnapshot;
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
declare const historyKey: PluginKey<any>;
|
|
716
|
-
declare const TesseraHistory: Extension<HistorySnapshotOptions, any>;
|
|
717
|
-
|
|
718
604
|
/**
|
|
719
605
|
* Block context menu (v1.1): right-click a block → `tessera:blockMenu` event.
|
|
720
606
|
* The binding renders the menu (copy anchor link / copy block id / delete;
|
|
@@ -815,18 +701,122 @@ declare function deleteBlockById(editor: Editor, id: string): boolean;
|
|
|
815
701
|
/** Anchor URL for a block id (`#block-<id>` on the current page). */
|
|
816
702
|
declare function blockAnchorUrl(blockId: string): string;
|
|
817
703
|
|
|
704
|
+
/**
|
|
705
|
+
* Link click-to-edit: clicking a link opens a panel with its display text
|
|
706
|
+
* and href, and offers unlink (degrade to plain text). The doc semantics
|
|
707
|
+
* live here so React and Vue bindings behave identically.
|
|
708
|
+
*/
|
|
709
|
+
interface TesseraLinkRange {
|
|
710
|
+
from: number;
|
|
711
|
+
to: number;
|
|
712
|
+
text: string;
|
|
713
|
+
href: string;
|
|
714
|
+
}
|
|
715
|
+
/** Resolve the link mark range under/ending at `pos` (defaults to the caret). */
|
|
716
|
+
declare function findLinkRange(editor: Editor, pos?: number): TesseraLinkRange | null;
|
|
717
|
+
/**
|
|
718
|
+
* Save an edit in ONE transaction (single undo step): update href, and when
|
|
719
|
+
* the display text changed replace the range content while keeping the
|
|
720
|
+
* text's other marks. An empty href degrades to unlink.
|
|
721
|
+
*/
|
|
722
|
+
declare function saveLinkRange(editor: Editor, range: TesseraLinkRange, next: {
|
|
723
|
+
text: string;
|
|
724
|
+
href: string;
|
|
725
|
+
}): void;
|
|
726
|
+
/** Strip the link mark, keeping the display text as plain text. */
|
|
727
|
+
declare function removeLinkRange(editor: Editor, range: TesseraLinkRange): void;
|
|
728
|
+
|
|
818
729
|
interface TesseraPresetOptions {
|
|
819
730
|
locale?: TesseraLocale;
|
|
820
|
-
/**
|
|
821
|
-
|
|
731
|
+
/** Empty-paragraph placeholder text (default: the i18n `placeholderEmpty` string). */
|
|
732
|
+
placeholder?: string;
|
|
733
|
+
/** Per-key overrides of the built-in UI dictionary (slash items, tooltips…). */
|
|
734
|
+
messages?: TesseraMessageOverrides;
|
|
735
|
+
/**
|
|
736
|
+
* Host block policy: top-level block types to exclude entirely. An excluded
|
|
737
|
+
* node is dropped from the extension preset, the slash menu, UniqueID's id
|
|
738
|
+
* list and the keyboard shortcuts that produce it — the editor cannot create
|
|
739
|
+
* it and rejects its JSON on parse. Recognized names: `hint`, `collapsible`,
|
|
740
|
+
* `embedBlock`, `tocBlock`, `horizontalRule`, `table`, `taskList`,
|
|
741
|
+
* `imageBlock`.
|
|
742
|
+
*/
|
|
743
|
+
excludeBlocks?: string[];
|
|
822
744
|
}
|
|
823
745
|
/** Node types that receive stable block IDs (write-back protocol basis). */
|
|
824
746
|
declare const ID_BLOCK_TYPES: string[];
|
|
747
|
+
/**
|
|
748
|
+
* Every top-level block type the FULL preset can emit into the canonical JSON.
|
|
749
|
+
* Hosts gating saves server-side (a block whitelist) should align their list
|
|
750
|
+
* with this one; `excludeBlocks` trims the editor to match a narrower policy.
|
|
751
|
+
* (Nested containers — listItem/taskItem/tableRow — are not listed: they only
|
|
752
|
+
* appear inside their parents.)
|
|
753
|
+
*/
|
|
754
|
+
declare const SUPPORTED_BLOCK_TYPES: readonly ["paragraph", "heading", "bulletList", "orderedList", "taskList", "blockquote", "codeBlock", "horizontalRule", "imageBlock", "table", "hint", "collapsible", "embedBlock", "tocBlock"];
|
|
825
755
|
/**
|
|
826
756
|
* The Tessera extension preset: framework-agnostic, UI-free. The binding layers
|
|
827
757
|
* rendering (slash menu renderer, node views) on top of this.
|
|
828
758
|
*/
|
|
829
759
|
declare function createTesseraExtensions(options?: TesseraPresetOptions): Extensions;
|
|
760
|
+
/**
|
|
761
|
+
* ProseMirror schema for the preset without instantiating an editor — the
|
|
762
|
+
* headless entry for hosts that only need format conversion (markdown ⇄ JSON).
|
|
763
|
+
* Honors `excludeBlocks` so conversion always agrees with the mounted editor.
|
|
764
|
+
*/
|
|
765
|
+
declare function createTesseraSchema(options?: TesseraPresetOptions): Schema;
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Injected services (ADR-0001 family): the component family never performs
|
|
769
|
+
* network or storage I/O itself. Hosts provide implementations through the
|
|
770
|
+
* binding; the editor reads them from `editor.storage.tesseraServices`.
|
|
771
|
+
*/
|
|
772
|
+
interface UploadedAsset {
|
|
773
|
+
url: string;
|
|
774
|
+
name?: string;
|
|
775
|
+
mime?: string;
|
|
776
|
+
}
|
|
777
|
+
interface UploadService {
|
|
778
|
+
uploadImage(file: File | Blob): Promise<UploadedAsset>;
|
|
779
|
+
uploadFile?(file: File | Blob): Promise<UploadedAsset>;
|
|
780
|
+
}
|
|
781
|
+
/** v1.1: inline comments. */
|
|
782
|
+
interface CommentEntry {
|
|
783
|
+
id: string;
|
|
784
|
+
authorId: string;
|
|
785
|
+
authorName: string;
|
|
786
|
+
text: string;
|
|
787
|
+
ts: number;
|
|
788
|
+
}
|
|
789
|
+
interface CommentThread {
|
|
790
|
+
id: string;
|
|
791
|
+
quote: string;
|
|
792
|
+
resolved: boolean;
|
|
793
|
+
createdAt: number;
|
|
794
|
+
entries: CommentEntry[];
|
|
795
|
+
}
|
|
796
|
+
interface CommentStore {
|
|
797
|
+
list(): Promise<CommentThread[]>;
|
|
798
|
+
upsert(thread: CommentThread): Promise<void>;
|
|
799
|
+
remove(id: string): Promise<void>;
|
|
800
|
+
}
|
|
801
|
+
/** v1.1: who is editing (comment authorship). */
|
|
802
|
+
interface IdentityService {
|
|
803
|
+
getCurrentUser(): {
|
|
804
|
+
id: string;
|
|
805
|
+
name: string;
|
|
806
|
+
} | null;
|
|
807
|
+
}
|
|
808
|
+
interface TesseraServicesStorage {
|
|
809
|
+
upload?: UploadService;
|
|
810
|
+
comments?: CommentStore;
|
|
811
|
+
identity?: IdentityService;
|
|
812
|
+
}
|
|
813
|
+
declare const TesseraServices: Extension<any, any>;
|
|
814
|
+
type ServicesEditor = {
|
|
815
|
+
storage: unknown;
|
|
816
|
+
};
|
|
817
|
+
declare function getUploadService(editor: ServicesEditor): UploadService | undefined;
|
|
818
|
+
declare function getCommentStore(editor: ServicesEditor): CommentStore | undefined;
|
|
819
|
+
declare function getIdentityService(editor: ServicesEditor): IdentityService | undefined;
|
|
830
820
|
|
|
831
821
|
declare function createMarkdownSerializer(schema: Schema): MarkdownSerializer;
|
|
832
822
|
/** Canonical JSON (or doc node) → Markdown. */
|
|
@@ -836,32 +826,6 @@ declare function markdownToDoc(markdown: string, schema: Schema): JSONContent;
|
|
|
836
826
|
/** Strip volatile/whitespace-irrelevant fields for round-trip comparisons. */
|
|
837
827
|
declare function stableJson(json: JSONContent): JSONContent;
|
|
838
828
|
|
|
839
|
-
/**
|
|
840
|
-
* Document diff (v1.1 history panel): block-level via stable ids (LCS on the
|
|
841
|
-
* id sequence) + word-level inside changed text blocks. Pure utility — the
|
|
842
|
-
* panel renders it.
|
|
843
|
-
*/
|
|
844
|
-
interface WordDiffPart {
|
|
845
|
-
text: string;
|
|
846
|
-
type: 'same' | 'add' | 'del';
|
|
847
|
-
}
|
|
848
|
-
interface BlockDiffEntry {
|
|
849
|
-
kind: 'added' | 'removed' | 'changed' | 'unchanged';
|
|
850
|
-
id?: string;
|
|
851
|
-
before?: JSONContent;
|
|
852
|
-
after?: JSONContent;
|
|
853
|
-
wordDiff?: WordDiffPart[];
|
|
854
|
-
}
|
|
855
|
-
/** Classic LCS word diff with a size guard. */
|
|
856
|
-
declare function wordDiff(beforeText: string, afterText: string): WordDiffPart[];
|
|
857
|
-
declare function diffDocs(before: JSONContent, after: JSONContent): BlockDiffEntry[];
|
|
858
|
-
/** Compact summary for the panel header. */
|
|
859
|
-
declare function diffSummary(entries: BlockDiffEntry[]): {
|
|
860
|
-
added: number;
|
|
861
|
-
removed: number;
|
|
862
|
-
changed: number;
|
|
863
|
-
};
|
|
864
|
-
|
|
865
829
|
/**
|
|
866
830
|
* Write-back protocol (ADR-0001 / product-definition §4): AI and host code
|
|
867
831
|
* mutate the document by stable block IDs — never whole-doc rewrites.
|
|
@@ -900,4 +864,4 @@ declare function removeBlocks(editor: Editor, ids: string[]): boolean;
|
|
|
900
864
|
/** Read a block (and its subtree) by id as canonical JSON. */
|
|
901
865
|
declare function getBlockJson(editor: Editor, id: string): JSONContent | null;
|
|
902
866
|
|
|
903
|
-
export { AiAttribution, type AiAttributionOptions, AiTable, AiTableCell, AiTableHeader, type AppendBlocksOptions, BlockContextMenu, type
|
|
867
|
+
export { AiAttribution, type AiAttributionOptions, AiTable, AiTableCell, AiTableHeader, type AppendBlocksOptions, BlockContextMenu, type BlockHandle, Collapsible, CollapsibleContent, type CollapsibleOptions, CollapsibleSummary, CommentCommands, type CommentEntry, CommentMark, type CommentStore, type CommentThread, EMOJI_ITEMS, EmbedBlock, type EmojiItem, EmojiMenu, type EmojiMenuOptions, type FindMatch, type FindReplaceState, Hint, type HintOptions, type HintVariant, ID_BLOCK_TYPES, type IdentityService, type ImageAlign, ImageBlock, type ImageBlockOptions, type ModifyRangeOptions, SUPPORTED_BLOCK_TYPES, SlashMenu, type SlashMenuItem, type SlashMenuOptions, type SlashRenderFactory, TABLE_COLUMN_KINDS, type TableColumnKind, TesseraFindReplace, TesseraGallery, TesseraInputRules, type TesseraLinkRange, type TesseraLocale, type TesseraMessageKey, type TesseraMessageOverrides, TesseraMetrics, type TesseraMetricsSnapshot, type TesseraPresetOptions, TesseraServices, type TesseraServicesStorage, TesseraShortcuts, type TesseraTranslator, TesseraWordPaste, TocBlock, type UploadService, type UploadedAsset, appendBlocks, blockAnchorUrl, cleanWordHtml, createMarkdownSerializer, createTesseraExtensions, createTesseraSchema, createTesseraT, defaultSlashItems, deleteBlockById, docToMarkdown, filterEmojiItems, filterSlashItems, findBlockPosById, findGalleryRuns, findLinkRange, findReplaceKey, galleryKey, getBlockJson, getCommentStore, getIdentityService, getTesseraMetrics, getTopLevelBlocks, getUploadService, isWordHtml, listCommentRanges, markdownToDoc, measureTesseraMetrics, modifyRange, normalizeTypes, removeBlocks, removeLinkRange, saveLinkRange, slashItemNodeName, stableJson, tableNodeToCsv, tableToCsvAt, tesseraMessages };
|