@tessera-editor/core 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.
- package/LICENSE +21 -0
- package/dist/index.d.ts +903 -0
- package/dist/index.js +2538 -0
- package/dist/index.js.map +1 -0
- package/dist/tessera.css +1491 -0
- package/package.json +72 -0
- package/src/__tests__/markdown.test.ts +138 -0
- package/src/__tests__/v11.test.ts +184 -0
- package/src/__tests__/v112.test.ts +184 -0
- package/src/__tests__/writeback.test.ts +123 -0
- package/src/blockmenu.ts +38 -0
- package/src/diff.ts +150 -0
- package/src/extensions/context-menu.ts +55 -0
- package/src/extensions/emoji.ts +148 -0
- package/src/extensions/find-replace.ts +229 -0
- package/src/extensions/gallery.ts +111 -0
- package/src/extensions/history.ts +133 -0
- package/src/extensions/input-rules.ts +34 -0
- package/src/extensions/metrics.ts +61 -0
- package/src/extensions/shortcuts.ts +118 -0
- package/src/extensions/slash.ts +272 -0
- package/src/extensions/word-paste.ts +25 -0
- package/src/i18n.ts +320 -0
- package/src/index.ts +80 -0
- package/src/markdown.ts +260 -0
- package/src/marks/ai.ts +55 -0
- package/src/marks/comment.ts +137 -0
- package/src/marks/placeholder.ts +63 -0
- package/src/nodes/collapsible.ts +171 -0
- package/src/nodes/embed.ts +55 -0
- package/src/nodes/hint.ts +70 -0
- package/src/nodes/image.ts +77 -0
- package/src/nodes/table.ts +286 -0
- package/src/nodes/toc.ts +38 -0
- package/src/preset.ts +117 -0
- package/src/services.ts +103 -0
- package/src/styles/tessera.css +1491 -0
- package/src/wordpaste.ts +56 -0
- package/src/writeback.ts +156 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,903 @@
|
|
|
1
|
+
import * as _tiptap_core from '@tiptap/core';
|
|
2
|
+
import { Node, Mark, Extension, Editor, Range, JSONContent, Extensions } from '@tiptap/core';
|
|
3
|
+
import * as _tiptap_extension_table from '@tiptap/extension-table';
|
|
4
|
+
export { TableRow as AiTableRow } from '@tiptap/extension-table';
|
|
5
|
+
import * as _tiptap_pm_model from '@tiptap/pm/model';
|
|
6
|
+
import { Node as Node$1, Schema } from '@tiptap/pm/model';
|
|
7
|
+
import * as _tiptap_pm_state from '@tiptap/pm/state';
|
|
8
|
+
import { PluginKey } from '@tiptap/pm/state';
|
|
9
|
+
import { SuggestionOptions, SuggestionProps, SuggestionKeyDownProps } from '@tiptap/suggestion';
|
|
10
|
+
import { MarkdownSerializer } from 'prosemirror-markdown';
|
|
11
|
+
|
|
12
|
+
type HintVariant = 'info' | 'success' | 'warning' | 'danger' | 'neutral';
|
|
13
|
+
interface HintOptions {
|
|
14
|
+
HTMLAttributes: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
declare module '@tiptap/core' {
|
|
17
|
+
interface Commands<ReturnType> {
|
|
18
|
+
hint: {
|
|
19
|
+
/** Wrap the current block into a hint container (input rule: `!! ` + space). */
|
|
20
|
+
setHint: (variant?: HintVariant) => ReturnType;
|
|
21
|
+
toggleHint: () => ReturnType;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Slite-style Hint block: a callout container that holds any block content.
|
|
27
|
+
* Trigger: type `!!` followed by a space at the start of a line.
|
|
28
|
+
*/
|
|
29
|
+
declare const Hint: Node<HintOptions, any>;
|
|
30
|
+
|
|
31
|
+
interface CollapsibleOptions {
|
|
32
|
+
HTMLAttributes: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
declare module '@tiptap/core' {
|
|
35
|
+
interface Commands<ReturnType> {
|
|
36
|
+
collapsible: {
|
|
37
|
+
/** Replace the current empty paragraph with a collapsible block (input rule: `>>` + space). */
|
|
38
|
+
insertCollapsible: () => ReturnType;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Slite-style Collapsible block: always-visible summary line + collapsible content,
|
|
44
|
+
* nested blocks allowed. Trigger: type `>>` followed by a space on an empty line.
|
|
45
|
+
*/
|
|
46
|
+
declare const Collapsible: Node<CollapsibleOptions, any>;
|
|
47
|
+
declare const CollapsibleSummary: Node<any, any>;
|
|
48
|
+
declare const CollapsibleContent: Node<any, any>;
|
|
49
|
+
|
|
50
|
+
type ImageAlign = 'left' | 'center' | 'full';
|
|
51
|
+
interface ImageBlockOptions {
|
|
52
|
+
HTMLAttributes: Record<string, unknown>;
|
|
53
|
+
}
|
|
54
|
+
declare module '@tiptap/core' {
|
|
55
|
+
interface Commands<ReturnType> {
|
|
56
|
+
imageBlock: {
|
|
57
|
+
setImage: (options: {
|
|
58
|
+
src: string;
|
|
59
|
+
alt?: string;
|
|
60
|
+
title?: string;
|
|
61
|
+
width?: number;
|
|
62
|
+
align?: ImageAlign;
|
|
63
|
+
}) => ReturnType;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Block-level image with width + alignment attributes. Binary data goes
|
|
69
|
+
* through the injected UploadService; this node only stores the resulting URL.
|
|
70
|
+
*/
|
|
71
|
+
declare const ImageBlock: Node<ImageBlockOptions, any>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Typed-column table (v1.1 quality focus — Slite's worst-reviewed feature is
|
|
75
|
+
* our opportunity). Column kinds live on the table node (`types`, indexed by
|
|
76
|
+
* column position); cell values for non-text kinds live on the cell attr
|
|
77
|
+
* `value`. Text columns keep rich inline content in the cell itself.
|
|
78
|
+
*
|
|
79
|
+
* Deliberately NOT supported (per Slite teardown §3.6): cell merging and
|
|
80
|
+
* in-cell calculations.
|
|
81
|
+
*/
|
|
82
|
+
type TableColumnKind = 'text' | 'checkbox' | 'select' | 'multiSelect' | 'number' | 'date' | 'link';
|
|
83
|
+
declare const TABLE_COLUMN_KINDS: TableColumnKind[];
|
|
84
|
+
declare module '@tiptap/core' {
|
|
85
|
+
interface Commands<ReturnType> {
|
|
86
|
+
tesseraTable: {
|
|
87
|
+
/** Insert a typed table. Kinds default to `text` for every column. */
|
|
88
|
+
insertTableTyped: (options?: {
|
|
89
|
+
rows?: number;
|
|
90
|
+
cols?: number;
|
|
91
|
+
withHeaderRow?: boolean;
|
|
92
|
+
}) => ReturnType;
|
|
93
|
+
/** Change the kind of column `index` (0-based). */
|
|
94
|
+
setColumnType: (index: number, kind: TableColumnKind) => ReturnType;
|
|
95
|
+
/** Stable-sort body rows by column `index`. */
|
|
96
|
+
sortTableByColumn: (index: number, direction: 'asc' | 'desc') => ReturnType;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** CSV of the table containing the caret (utility, not a command). */
|
|
101
|
+
declare function tableToCsvAt(editor: _tiptap_core.Editor): string | null;
|
|
102
|
+
declare function normalizeTypes(types: unknown, cols: number): TableColumnKind[];
|
|
103
|
+
/** Serialize a table node to CSV (text form of typed values). */
|
|
104
|
+
declare function tableNodeToCsv(table: Node$1): string;
|
|
105
|
+
declare const AiTable: _tiptap_core.Node<_tiptap_extension_table.TableOptions, any>;
|
|
106
|
+
|
|
107
|
+
declare const AiTableCell: _tiptap_core.Node<_tiptap_extension_table.TableCellOptions, any>;
|
|
108
|
+
declare const AiTableHeader: _tiptap_core.Node<_tiptap_extension_table.TableHeaderOptions, any>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Sandboxed iframe embed (v1.1): default sandbox="" — NO scripts, NO
|
|
112
|
+
* same-origin. Hosts may opt into `allowScripts` per instance for app-like
|
|
113
|
+
* embeds; allow-same-origin is never combined with allow-scripts.
|
|
114
|
+
*/
|
|
115
|
+
declare const EmbedBlock: Node<any, any>;
|
|
116
|
+
declare module '@tiptap/core' {
|
|
117
|
+
interface Commands<ReturnType> {
|
|
118
|
+
embedBlock: {
|
|
119
|
+
insertEmbed: (attrs: {
|
|
120
|
+
src: string;
|
|
121
|
+
height?: number;
|
|
122
|
+
allowScripts?: boolean;
|
|
123
|
+
}) => ReturnType;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Table-of-contents block (v1.1, Slite /outline): renders the document
|
|
130
|
+
* outline (H1–H4) with anchor jumps. The list itself is derived state — the
|
|
131
|
+
* node stores nothing, so it can never go stale in the canonical JSON.
|
|
132
|
+
*/
|
|
133
|
+
declare const TocBlock: Node<any, any>;
|
|
134
|
+
declare module '@tiptap/core' {
|
|
135
|
+
interface Commands<ReturnType> {
|
|
136
|
+
tocBlock: {
|
|
137
|
+
insertToc: () => ReturnType;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Attribution mark for AI-written content — the Slite-style "human vs Agent"
|
|
144
|
+
* provenance signal. Lifecycle:
|
|
145
|
+
*
|
|
146
|
+
* pending: true → suggestion awaiting Accept / Reject (styled prominently)
|
|
147
|
+
* pending: false → accepted AI content (subtle permanent attribution)
|
|
148
|
+
*
|
|
149
|
+
* Reject deletes the marked range; Accept flips `pending` and stamps metadata.
|
|
150
|
+
*/
|
|
151
|
+
interface AiAttributionOptions {
|
|
152
|
+
HTMLAttributes: Record<string, unknown>;
|
|
153
|
+
}
|
|
154
|
+
declare const AiAttribution: Mark<AiAttributionOptions, any>;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Inline comment mark (v1.1): anchors a comment thread to a text range.
|
|
158
|
+
* Thread data lives in the injected CommentStore; the mark only carries the
|
|
159
|
+
* thread id + resolved state so the doc stays host-persistable.
|
|
160
|
+
*/
|
|
161
|
+
declare const CommentMark: Mark<any, any>;
|
|
162
|
+
declare module '@tiptap/core' {
|
|
163
|
+
interface Commands<ReturnType> {
|
|
164
|
+
tesseraComment: {
|
|
165
|
+
/** Attach a comment thread id to the current selection. */
|
|
166
|
+
addCommentThread: (threadId: string) => ReturnType;
|
|
167
|
+
/** Flip resolved for every mark of `threadId`. */
|
|
168
|
+
setCommentResolved: (threadId: string, resolved: boolean) => ReturnType;
|
|
169
|
+
/** Remove all marks of `threadId` (thread deleted). */
|
|
170
|
+
removeCommentThread: (threadId: string) => ReturnType;
|
|
171
|
+
/** Select the next comment range from the caret (wraps around). */
|
|
172
|
+
focusNextCommentThread: () => ReturnType;
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** All comment thread ranges in document order. */
|
|
177
|
+
declare function listCommentRanges(state: _tiptap_pm_state.EditorState): {
|
|
178
|
+
threadId: string;
|
|
179
|
+
from: number;
|
|
180
|
+
to: number;
|
|
181
|
+
resolved: boolean;
|
|
182
|
+
}[];
|
|
183
|
+
declare const CommentCommands: Extension<any, any>;
|
|
184
|
+
|
|
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
|
+
/**
|
|
204
|
+
* Tessera-specific input rules beyond the StarterKit defaults:
|
|
205
|
+
* `[] ` + typing → task list (mirrors Slite's trigger)
|
|
206
|
+
* `::text::` → highlight mark
|
|
207
|
+
*/
|
|
208
|
+
declare const TesseraInputRules: Extension<any, any>;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The Tessera keyboard map (acceptance checklist §3). Panel-opening shortcuts
|
|
212
|
+
* emit `tessera:*` events that the binding's UI layer listens to, keeping this
|
|
213
|
+
* extension UI-free.
|
|
214
|
+
*/
|
|
215
|
+
declare module '@tiptap/core' {
|
|
216
|
+
interface Commands<ReturnType> {
|
|
217
|
+
tesseraShortcuts: {
|
|
218
|
+
/** Move the top-level block containing the caret up one position. */
|
|
219
|
+
moveBlockUp: () => ReturnType;
|
|
220
|
+
/** Move the top-level block containing the caret down one position. */
|
|
221
|
+
moveBlockDown: () => ReturnType;
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
interface EditorEvents {
|
|
225
|
+
'tessera:formatPanel': {
|
|
226
|
+
kind: 'color' | 'highlight';
|
|
227
|
+
};
|
|
228
|
+
'tessera:linkPanel': Record<string, never>;
|
|
229
|
+
'tessera:findPanel': Record<string, never>;
|
|
230
|
+
'tessera:insertImage': Record<string, never>;
|
|
231
|
+
'tessera:askPanel': Record<string, never>;
|
|
232
|
+
'tessera:historyPanel': Record<string, never>;
|
|
233
|
+
'tessera:commentPanel': Record<string, never>;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
declare const TesseraShortcuts: Extension<any, any>;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Find & replace (acceptance §3: ⌘F). The core plugin owns match computation
|
|
240
|
+
* and decorations; the binding renders the panel and calls these commands.
|
|
241
|
+
*/
|
|
242
|
+
interface FindMatch {
|
|
243
|
+
from: number;
|
|
244
|
+
to: number;
|
|
245
|
+
}
|
|
246
|
+
interface FindReplaceState {
|
|
247
|
+
query: string;
|
|
248
|
+
matches: FindMatch[];
|
|
249
|
+
active: number;
|
|
250
|
+
visible: boolean;
|
|
251
|
+
}
|
|
252
|
+
interface FindReplaceStorage extends FindReplaceState {
|
|
253
|
+
}
|
|
254
|
+
declare const findReplaceKey: PluginKey<FindReplaceState>;
|
|
255
|
+
declare module '@tiptap/core' {
|
|
256
|
+
interface Commands<ReturnType> {
|
|
257
|
+
findReplace: {
|
|
258
|
+
openFindPanel: () => ReturnType;
|
|
259
|
+
closeFindPanel: () => ReturnType;
|
|
260
|
+
setFindQuery: (query: string) => ReturnType;
|
|
261
|
+
findNext: () => ReturnType;
|
|
262
|
+
findPrev: () => ReturnType;
|
|
263
|
+
replaceCurrent: (replacement: string) => ReturnType;
|
|
264
|
+
replaceAll: (replacement: string) => ReturnType;
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
declare const TesseraFindReplace: Extension<Record<string, never>, FindReplaceStorage>;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Tessera i18n: zh-CN / en-US dictionaries for every UI string the component
|
|
272
|
+
* family renders (menus, toolbars, panels, AI actions). Hosts may extend with
|
|
273
|
+
* additional locales by supplying messages to the binding.
|
|
274
|
+
*/
|
|
275
|
+
type TesseraLocale = 'zh-CN' | 'en-US';
|
|
276
|
+
declare const tesseraMessages: {
|
|
277
|
+
readonly 'zh-CN': {
|
|
278
|
+
readonly placeholderEmpty: "输入 / 唤起菜单,或直接开始书写…";
|
|
279
|
+
readonly groupBasic: "基础块";
|
|
280
|
+
readonly groupAdvanced: "高级块";
|
|
281
|
+
readonly groupAi: "AI";
|
|
282
|
+
readonly itemText: "文本";
|
|
283
|
+
readonly itemTextDesc: "普通段落";
|
|
284
|
+
readonly itemH1: "标题 1";
|
|
285
|
+
readonly itemH2: "标题 2";
|
|
286
|
+
readonly itemH3: "标题 3";
|
|
287
|
+
readonly itemH4: "标题 4";
|
|
288
|
+
readonly itemBullet: "无序列表";
|
|
289
|
+
readonly itemBulletDesc: "简单的项目符号列表";
|
|
290
|
+
readonly itemOrdered: "有序列表";
|
|
291
|
+
readonly itemOrderedDesc: "带编号的列表";
|
|
292
|
+
readonly itemTask: "任务清单";
|
|
293
|
+
readonly itemTaskDesc: "用复选框跟踪待办";
|
|
294
|
+
readonly itemQuote: "引用";
|
|
295
|
+
readonly itemQuoteDesc: "引用一段文字";
|
|
296
|
+
readonly itemCode: "代码块";
|
|
297
|
+
readonly itemCodeDesc: "带语法高亮的代码";
|
|
298
|
+
readonly itemDivider: "分割线";
|
|
299
|
+
readonly itemDividerDesc: "水平分割线";
|
|
300
|
+
readonly itemHint: "提示块";
|
|
301
|
+
readonly itemHintDesc: "醒目的信息/警告容器(!! + 空格)";
|
|
302
|
+
readonly itemCollapsible: "折叠块";
|
|
303
|
+
readonly itemCollapsibleDesc: "可折叠的内容区(>> + 空格)";
|
|
304
|
+
readonly itemImage: "图片";
|
|
305
|
+
readonly itemImageDesc: "上传或粘贴图片";
|
|
306
|
+
readonly itemImageUrl: "图片链接";
|
|
307
|
+
readonly itemSummarize: "AI 摘要";
|
|
308
|
+
readonly itemSummarizeDesc: "为整篇文档生成 TL;DR";
|
|
309
|
+
readonly itemAsk: "AI 问答";
|
|
310
|
+
readonly itemAskDesc: "针对当前文档提问";
|
|
311
|
+
readonly itemImprove: "AI 改写";
|
|
312
|
+
readonly itemImproveDesc: "改写选中的文字";
|
|
313
|
+
readonly tooltipBold: "加粗";
|
|
314
|
+
readonly tooltipItalic: "斜体";
|
|
315
|
+
readonly tooltipUnderline: "下划线";
|
|
316
|
+
readonly tooltipStrike: "删除线";
|
|
317
|
+
readonly tooltipCode: "行内代码";
|
|
318
|
+
readonly tooltipColor: "文字颜色";
|
|
319
|
+
readonly tooltipHighlight: "高亮";
|
|
320
|
+
readonly tooltipLink: "链接";
|
|
321
|
+
readonly tooltipComment: "评论(v1.x)";
|
|
322
|
+
readonly tooltipTurnCollapsible: "转为折叠块";
|
|
323
|
+
readonly tooltipMore: "更多";
|
|
324
|
+
readonly tooltipImprove: "AI 改写";
|
|
325
|
+
readonly tooltipCopyMarkdown: "复制为 Markdown";
|
|
326
|
+
readonly colorDefault: "默认";
|
|
327
|
+
readonly highlightNone: "无高亮";
|
|
328
|
+
readonly linkPlaceholder: "链接地址…";
|
|
329
|
+
readonly linkApply: "应用";
|
|
330
|
+
readonly linkRemove: "移除链接";
|
|
331
|
+
readonly linkOpen: "打开";
|
|
332
|
+
readonly emptyLineExpand: "展开全部块";
|
|
333
|
+
readonly findPlaceholder: "查找…";
|
|
334
|
+
readonly replacePlaceholder: "替换为…";
|
|
335
|
+
readonly findNext: "下一个";
|
|
336
|
+
readonly findPrev: "上一个";
|
|
337
|
+
readonly replaceOne: "替换";
|
|
338
|
+
readonly replaceAll: "全部替换";
|
|
339
|
+
readonly findCount: (n: number) => string;
|
|
340
|
+
readonly findClose: "关闭";
|
|
341
|
+
readonly imageUploadFailed: "图片上传失败";
|
|
342
|
+
readonly imageAlignLeft: "左对齐";
|
|
343
|
+
readonly imageAlignCenter: "居中";
|
|
344
|
+
readonly imageAlignFull: "全宽";
|
|
345
|
+
readonly aiTitle: "AI";
|
|
346
|
+
readonly aiAskPlaceholder: "针对本文档提问…";
|
|
347
|
+
readonly aiSend: "发送";
|
|
348
|
+
readonly aiThinking: "思考中…";
|
|
349
|
+
readonly aiAccept: "接受";
|
|
350
|
+
readonly aiReject: "拒绝";
|
|
351
|
+
readonly aiImprovePrompt: "改写要求(可选)…";
|
|
352
|
+
readonly aiImproveRun: "执行";
|
|
353
|
+
readonly aiStreaming: "生成中…";
|
|
354
|
+
readonly aiRuntimeMissing: "未注入 AI Runtime,此操作不可用";
|
|
355
|
+
readonly itemTable: "表格";
|
|
356
|
+
readonly itemTableDesc: "带类型化列的结构化表格(⌘⌥S)";
|
|
357
|
+
readonly itemTableSimple: "无表头表格";
|
|
358
|
+
readonly itemTableSimpleDesc: "纯文本简易布局表(⌘⌥T)";
|
|
359
|
+
readonly colTypeText: "文本";
|
|
360
|
+
readonly colTypeCheckbox: "复选框";
|
|
361
|
+
readonly colTypeSelect: "单选标签";
|
|
362
|
+
readonly colTypeMultiSelect: "多选标签";
|
|
363
|
+
readonly colTypeNumber: "数字";
|
|
364
|
+
readonly colTypeDate: "日期";
|
|
365
|
+
readonly colTypeLink: "链接";
|
|
366
|
+
readonly colMenuTitle: "列操作";
|
|
367
|
+
readonly colMenuSortAsc: "升序排序";
|
|
368
|
+
readonly colMenuSortDesc: "降序排序";
|
|
369
|
+
readonly colMenuInsertLeft: "左侧插入列";
|
|
370
|
+
readonly colMenuInsertRight: "右侧插入列";
|
|
371
|
+
readonly colMenuDelete: "删除此列";
|
|
372
|
+
readonly colMenuType: "列类型";
|
|
373
|
+
readonly rowMenuTitle: "行操作";
|
|
374
|
+
readonly rowMenuInsertAbove: "上方插入行";
|
|
375
|
+
readonly rowMenuInsertBelow: "下方插入行";
|
|
376
|
+
readonly rowMenuDelete: "删除此行";
|
|
377
|
+
readonly tableDelete: "删除表格";
|
|
378
|
+
readonly tableCopyCsv: "复制为 CSV";
|
|
379
|
+
readonly tableToggleHeader: "切换表头";
|
|
380
|
+
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
|
+
readonly tooltipCommentV11: "评论";
|
|
392
|
+
readonly commentTitle: "评论";
|
|
393
|
+
readonly commentEmpty: "暂无评论";
|
|
394
|
+
readonly commentPlaceholder: "写下评论…(Shift+Enter 换行)";
|
|
395
|
+
readonly commentSend: "发送";
|
|
396
|
+
readonly commentResolve: "解决";
|
|
397
|
+
readonly commentReopen: "重新打开";
|
|
398
|
+
readonly commentDelete: "删除";
|
|
399
|
+
readonly commentResolvedBadge: "已解决";
|
|
400
|
+
readonly commentCount: (n: number) => string;
|
|
401
|
+
readonly itemEmbed: "嵌入";
|
|
402
|
+
readonly itemEmbedDesc: "嵌入外部网页(iframe 沙箱)";
|
|
403
|
+
readonly itemToc: "目录";
|
|
404
|
+
readonly itemTocDesc: "自动生成全文大纲(H1–H4)";
|
|
405
|
+
readonly embedPlaceholder: "粘贴要嵌入的链接…";
|
|
406
|
+
readonly embedApply: "嵌入";
|
|
407
|
+
readonly embedInvalid: "无效链接";
|
|
408
|
+
readonly embedOpen: "打开原链接";
|
|
409
|
+
readonly tocEmpty: "暂无标题——添加 H1–H4 后自动出现";
|
|
410
|
+
readonly placeholderText: "待补充";
|
|
411
|
+
readonly placeholderPerson: "待填人";
|
|
412
|
+
readonly placeholderDate: "待填日期";
|
|
413
|
+
readonly menuCopyAnchor: "复制锚链接";
|
|
414
|
+
readonly menuCopyBlockId: "复制块 ID";
|
|
415
|
+
readonly menuDeleteBlock: "删除此块";
|
|
416
|
+
};
|
|
417
|
+
readonly 'en-US': {
|
|
418
|
+
readonly placeholderEmpty: "Type / for blocks, or just start writing…";
|
|
419
|
+
readonly groupBasic: "Basic blocks";
|
|
420
|
+
readonly groupAdvanced: "Advanced blocks";
|
|
421
|
+
readonly groupAi: "AI";
|
|
422
|
+
readonly itemText: "Text";
|
|
423
|
+
readonly itemTextDesc: "Plain paragraph";
|
|
424
|
+
readonly itemH1: "Heading 1";
|
|
425
|
+
readonly itemH2: "Heading 2";
|
|
426
|
+
readonly itemH3: "Heading 3";
|
|
427
|
+
readonly itemH4: "Heading 4";
|
|
428
|
+
readonly itemBullet: "Bullet list";
|
|
429
|
+
readonly itemBulletDesc: "Simple bulleted list";
|
|
430
|
+
readonly itemOrdered: "Numbered list";
|
|
431
|
+
readonly itemOrderedDesc: "List with numbering";
|
|
432
|
+
readonly itemTask: "Task list";
|
|
433
|
+
readonly itemTaskDesc: "Track to-dos with checkboxes";
|
|
434
|
+
readonly itemQuote: "Quote";
|
|
435
|
+
readonly itemQuoteDesc: "Quote a passage";
|
|
436
|
+
readonly itemCode: "Code block";
|
|
437
|
+
readonly itemCodeDesc: "Code with syntax highlighting";
|
|
438
|
+
readonly itemDivider: "Divider";
|
|
439
|
+
readonly itemDividerDesc: "Horizontal rule";
|
|
440
|
+
readonly itemHint: "Hint";
|
|
441
|
+
readonly itemHintDesc: "Callout container (!! + space)";
|
|
442
|
+
readonly itemCollapsible: "Collapsible";
|
|
443
|
+
readonly itemCollapsibleDesc: "Foldable section (>> + space)";
|
|
444
|
+
readonly itemImage: "Image";
|
|
445
|
+
readonly itemImageDesc: "Upload or paste an image";
|
|
446
|
+
readonly itemImageUrl: "Image URL";
|
|
447
|
+
readonly itemSummarize: "AI Summarize";
|
|
448
|
+
readonly itemSummarizeDesc: "Generate a TL;DR for this doc";
|
|
449
|
+
readonly itemAsk: "AI Ask";
|
|
450
|
+
readonly itemAskDesc: "Ask questions about this doc";
|
|
451
|
+
readonly itemImprove: "AI Improve";
|
|
452
|
+
readonly itemImproveDesc: "Rewrite the selection";
|
|
453
|
+
readonly tooltipBold: "Bold";
|
|
454
|
+
readonly tooltipItalic: "Italic";
|
|
455
|
+
readonly tooltipUnderline: "Underline";
|
|
456
|
+
readonly tooltipStrike: "Strikethrough";
|
|
457
|
+
readonly tooltipCode: "Inline code";
|
|
458
|
+
readonly tooltipColor: "Text color";
|
|
459
|
+
readonly tooltipHighlight: "Highlight";
|
|
460
|
+
readonly tooltipLink: "Link";
|
|
461
|
+
readonly tooltipComment: "Comment (v1.x)";
|
|
462
|
+
readonly tooltipTurnCollapsible: "Turn into collapsible";
|
|
463
|
+
readonly tooltipMore: "More";
|
|
464
|
+
readonly tooltipImprove: "AI Improve";
|
|
465
|
+
readonly tooltipCopyMarkdown: "Copy as Markdown";
|
|
466
|
+
readonly colorDefault: "Default";
|
|
467
|
+
readonly highlightNone: "No highlight";
|
|
468
|
+
readonly linkPlaceholder: "Link URL…";
|
|
469
|
+
readonly linkApply: "Apply";
|
|
470
|
+
readonly linkRemove: "Remove link";
|
|
471
|
+
readonly linkOpen: "Open";
|
|
472
|
+
readonly emptyLineExpand: "Show all blocks";
|
|
473
|
+
readonly findPlaceholder: "Find…";
|
|
474
|
+
readonly replacePlaceholder: "Replace with…";
|
|
475
|
+
readonly findNext: "Next";
|
|
476
|
+
readonly findPrev: "Prev";
|
|
477
|
+
readonly replaceOne: "Replace";
|
|
478
|
+
readonly replaceAll: "Replace all";
|
|
479
|
+
readonly findCount: (n: number) => string;
|
|
480
|
+
readonly findClose: "Close";
|
|
481
|
+
readonly imageUploadFailed: "Image upload failed";
|
|
482
|
+
readonly imageAlignLeft: "Align left";
|
|
483
|
+
readonly imageAlignCenter: "Center";
|
|
484
|
+
readonly imageAlignFull: "Full width";
|
|
485
|
+
readonly aiTitle: "AI";
|
|
486
|
+
readonly aiAskPlaceholder: "Ask about this doc…";
|
|
487
|
+
readonly aiSend: "Send";
|
|
488
|
+
readonly aiThinking: "Thinking…";
|
|
489
|
+
readonly aiAccept: "Accept";
|
|
490
|
+
readonly aiReject: "Reject";
|
|
491
|
+
readonly aiImprovePrompt: "Instruction (optional)…";
|
|
492
|
+
readonly aiImproveRun: "Run";
|
|
493
|
+
readonly aiStreaming: "Generating…";
|
|
494
|
+
readonly aiRuntimeMissing: "No AI Runtime injected; action unavailable";
|
|
495
|
+
readonly itemTable: "Table";
|
|
496
|
+
readonly itemTableDesc: "Structured table with typed columns (⌘⌥S)";
|
|
497
|
+
readonly itemTableSimple: "Table (no header)";
|
|
498
|
+
readonly itemTableSimpleDesc: "Plain layout table without header (⌘⌥T)";
|
|
499
|
+
readonly colTypeText: "Text";
|
|
500
|
+
readonly colTypeCheckbox: "Checkbox";
|
|
501
|
+
readonly colTypeSelect: "Select tag";
|
|
502
|
+
readonly colTypeMultiSelect: "Multi-select";
|
|
503
|
+
readonly colTypeNumber: "Number";
|
|
504
|
+
readonly colTypeDate: "Date";
|
|
505
|
+
readonly colTypeLink: "Link";
|
|
506
|
+
readonly colMenuTitle: "Column";
|
|
507
|
+
readonly colMenuSortAsc: "Sort ascending";
|
|
508
|
+
readonly colMenuSortDesc: "Sort descending";
|
|
509
|
+
readonly colMenuInsertLeft: "Insert column left";
|
|
510
|
+
readonly colMenuInsertRight: "Insert column right";
|
|
511
|
+
readonly colMenuDelete: "Delete column";
|
|
512
|
+
readonly colMenuType: "Column type";
|
|
513
|
+
readonly rowMenuTitle: "Row";
|
|
514
|
+
readonly rowMenuInsertAbove: "Insert row above";
|
|
515
|
+
readonly rowMenuInsertBelow: "Insert row below";
|
|
516
|
+
readonly rowMenuDelete: "Delete row";
|
|
517
|
+
readonly tableDelete: "Delete table";
|
|
518
|
+
readonly tableCopyCsv: "Copy as CSV";
|
|
519
|
+
readonly tableToggleHeader: "Toggle header row";
|
|
520
|
+
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
|
+
readonly tooltipCommentV11: "Comment";
|
|
532
|
+
readonly commentTitle: "Comments";
|
|
533
|
+
readonly commentEmpty: "No comments yet";
|
|
534
|
+
readonly commentPlaceholder: "Write a comment… (Shift+Enter for newline)";
|
|
535
|
+
readonly commentSend: "Send";
|
|
536
|
+
readonly commentResolve: "Resolve";
|
|
537
|
+
readonly commentReopen: "Reopen";
|
|
538
|
+
readonly commentDelete: "Delete";
|
|
539
|
+
readonly commentResolvedBadge: "Resolved";
|
|
540
|
+
readonly commentCount: (n: number) => string;
|
|
541
|
+
readonly itemEmbed: "Embed";
|
|
542
|
+
readonly itemEmbedDesc: "Embed an external page (sandboxed iframe)";
|
|
543
|
+
readonly itemToc: "Table of contents";
|
|
544
|
+
readonly itemTocDesc: "Auto outline from H1–H4";
|
|
545
|
+
readonly embedPlaceholder: "Paste a link to embed…";
|
|
546
|
+
readonly embedApply: "Embed";
|
|
547
|
+
readonly embedInvalid: "Invalid URL";
|
|
548
|
+
readonly embedOpen: "Open original";
|
|
549
|
+
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
|
+
readonly menuCopyAnchor: "Copy anchor link";
|
|
554
|
+
readonly menuCopyBlockId: "Copy block ID";
|
|
555
|
+
readonly menuDeleteBlock: "Delete block";
|
|
556
|
+
};
|
|
557
|
+
};
|
|
558
|
+
type TesseraMessageKey = keyof typeof tesseraMessages['zh-CN'];
|
|
559
|
+
type TesseraTranslator = (key: TesseraMessageKey) => string;
|
|
560
|
+
declare function createTesseraT(locale?: TesseraLocale): TesseraTranslator;
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Slash menu (acceptance §1): framework-neutral extension built on
|
|
564
|
+
* @tiptap/suggestion. The binding injects a `render` implementation; items are
|
|
565
|
+
* data (title/description/shortcut/command) so React or Vue can render them.
|
|
566
|
+
*/
|
|
567
|
+
interface SlashMenuItem {
|
|
568
|
+
id: string;
|
|
569
|
+
group: 'basic' | 'advanced' | 'ai';
|
|
570
|
+
title: string;
|
|
571
|
+
description?: string;
|
|
572
|
+
/** display-only hint, e.g. "⌘⇧9" */
|
|
573
|
+
shortcut?: string;
|
|
574
|
+
keywords?: string[];
|
|
575
|
+
command: (props: {
|
|
576
|
+
editor: Editor;
|
|
577
|
+
range: Range;
|
|
578
|
+
}) => void;
|
|
579
|
+
}
|
|
580
|
+
type SlashRenderFactory = SuggestionOptions<SlashMenuItem>['render'];
|
|
581
|
+
interface SlashMenuOptions {
|
|
582
|
+
locale: TesseraLocale;
|
|
583
|
+
/** extra host items (e.g. AI actions registered by @tessera-editor/ai) */
|
|
584
|
+
extraItems?: (ctx: {
|
|
585
|
+
editor: Editor;
|
|
586
|
+
t: TesseraTranslator;
|
|
587
|
+
}) => SlashMenuItem[];
|
|
588
|
+
includeAiItems?: boolean;
|
|
589
|
+
render?: SlashRenderFactory;
|
|
590
|
+
}
|
|
591
|
+
/** Default block items — mirrors the Slite slash palette for the M1 scope. */
|
|
592
|
+
declare function defaultSlashItems(t: TesseraTranslator): SlashMenuItem[];
|
|
593
|
+
declare const SlashMenu: Extension<SlashMenuOptions, any>;
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Emoji picker (acceptance §2, v1.1): typing `:` anywhere opens a picker
|
|
597
|
+
* filtered by name/keywords; Return / click inserts the emoji and closes.
|
|
598
|
+
* Runs on @tiptap/suggestion like the slash menu — IME composition guard
|
|
599
|
+
* comes for free (suggestion ignores composing transactions).
|
|
600
|
+
*/
|
|
601
|
+
interface EmojiItem {
|
|
602
|
+
char: string;
|
|
603
|
+
name: string;
|
|
604
|
+
keywords: string[];
|
|
605
|
+
}
|
|
606
|
+
/** Curated common set; hosts may append via `extraItems`. */
|
|
607
|
+
declare const EMOJI_ITEMS: EmojiItem[];
|
|
608
|
+
declare function filterEmojiItems(query: string, items?: EmojiItem[]): EmojiItem[];
|
|
609
|
+
interface EmojiMenuOptions {
|
|
610
|
+
/** UI renderer provided by a binding (same contract as the slash menu). */
|
|
611
|
+
render?: () => {
|
|
612
|
+
onStart: (props: SuggestionProps<EmojiItem>) => void;
|
|
613
|
+
onUpdate: (props: SuggestionProps<EmojiItem>) => void;
|
|
614
|
+
onExit: (props: SuggestionProps<EmojiItem>) => void;
|
|
615
|
+
onKeyDown?: (props: SuggestionKeyDownProps) => boolean;
|
|
616
|
+
};
|
|
617
|
+
/** Hosts may append their own emoji entries. */
|
|
618
|
+
extraItems?: () => EmojiItem[];
|
|
619
|
+
}
|
|
620
|
+
declare const EmojiMenu: Extension<EmojiMenuOptions, any>;
|
|
621
|
+
|
|
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
|
+
/**
|
|
719
|
+
* Block context menu (v1.1): right-click a block → `tessera:blockMenu` event.
|
|
720
|
+
* The binding renders the menu (copy anchor link / copy block id / delete;
|
|
721
|
+
* row & column ops when the target is a table).
|
|
722
|
+
*/
|
|
723
|
+
declare const BlockContextMenu: Extension<any, any>;
|
|
724
|
+
declare module '@tiptap/core' {
|
|
725
|
+
interface EditorEvents {
|
|
726
|
+
'tessera:blockMenu': {
|
|
727
|
+
blockId: string;
|
|
728
|
+
blockType: string;
|
|
729
|
+
clientX: number;
|
|
730
|
+
clientY: number;
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Image gallery (acceptance §6, v1.1): runs of two or more consecutive
|
|
737
|
+
* top-level image blocks are flagged with data-gallery attributes so the
|
|
738
|
+
* theme can lay them out three per row (CSS: inline-block thirds).
|
|
739
|
+
* Purely presentational — the document format is unchanged (still
|
|
740
|
+
* sibling imageBlock nodes), so Markdown/JSON round-trips are unaffected.
|
|
741
|
+
*/
|
|
742
|
+
declare const galleryKey: PluginKey<any>;
|
|
743
|
+
interface GalleryRun {
|
|
744
|
+
from: number;
|
|
745
|
+
to: number;
|
|
746
|
+
size: number;
|
|
747
|
+
}
|
|
748
|
+
/** Shared run detection so tests and the plugin agree on the definition. */
|
|
749
|
+
declare function findGalleryRuns(doc: Node$1): GalleryRun[];
|
|
750
|
+
declare const TesseraGallery: Extension<any, any>;
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Performance metrics (acceptance §8, v1.1): long-document observability.
|
|
754
|
+
* The architecture keeps per-block NodeViews and lazy decoration passes in
|
|
755
|
+
* place; this exposes the numbers a host needs to watch (block/word counts,
|
|
756
|
+
* heavy-node counts, markdown serialize cost) so virtualization work can be
|
|
757
|
+
* measured rather than guessed. Usage: `getTesseraMetrics(editor)`.
|
|
758
|
+
*/
|
|
759
|
+
interface TesseraMetricsSnapshot {
|
|
760
|
+
/** Top-level blocks in the document. */
|
|
761
|
+
blocks: number;
|
|
762
|
+
/** Whitespace-split word count of the document text. */
|
|
763
|
+
words: number;
|
|
764
|
+
/** imageBlock nodes. */
|
|
765
|
+
images: number;
|
|
766
|
+
/** aiTable nodes. */
|
|
767
|
+
tables: number;
|
|
768
|
+
/** NodeView wrapper elements currently mounted. */
|
|
769
|
+
mountedNodeViews: number;
|
|
770
|
+
/** Milliseconds for a full Markdown serialization of the current doc. */
|
|
771
|
+
serializeMs: number;
|
|
772
|
+
}
|
|
773
|
+
declare function measureTesseraMetrics(editor: Editor): TesseraMetricsSnapshot;
|
|
774
|
+
declare const TesseraMetrics: Extension<any, any>;
|
|
775
|
+
declare function getTesseraMetrics(editor: Editor): TesseraMetricsSnapshot;
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Word-source paste cleaning (acceptance §9, v1.1): when the pasted HTML is
|
|
779
|
+
* detected as Word export, the rules table in wordpaste.ts runs before the
|
|
780
|
+
* schema-based parse. Non-Word pastes pass through untouched.
|
|
781
|
+
*/
|
|
782
|
+
declare const TesseraWordPaste: Extension<any, any>;
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Word paste cleaning (acceptance §9, v1.1).
|
|
786
|
+
*
|
|
787
|
+
* Word export HTML is full of Office plumbing that ProseMirror's default
|
|
788
|
+
* schema-based cleaning handles badly (mso styles leak as text, namespace
|
|
789
|
+
* tags become unknown nodes, empty paragraphs multiply). When pasted HTML
|
|
790
|
+
* is detected as Word source, these rules run BEFORE PM parses it:
|
|
791
|
+
*
|
|
792
|
+
* R1 drop comments incl. `<!--[if ...]>...<![endif]-->` conditionals
|
|
793
|
+
* R2 drop <style>/<script>/<meta>/<link> blocks and tags
|
|
794
|
+
* R3 strip Office namespace tags entirely (<o:p>, <w:*, <st1:*>, <v:*>)
|
|
795
|
+
* R4 drop class / style / lang attributes (visual plumbing; bold/italic/
|
|
796
|
+
* underline survive via <b>/<i>/<u> tags which PM keeps)
|
|
797
|
+
* R5 drop Word list markers ("l" bullets in mso-list spans are removed
|
|
798
|
+
* with R3/R4; list paragraphs become plain paragraphs — semantic list
|
|
799
|
+
* reconstruction is explicitly out of scope for v1.1)
|
|
800
|
+
* R6 collapses to a normal space
|
|
801
|
+
* R7 empty paragraphs (<p></p>, <p><br></p>, whitespace-only) are removed
|
|
802
|
+
*/
|
|
803
|
+
declare function isWordHtml(html: string): boolean;
|
|
804
|
+
declare function cleanWordHtml(html: string): string;
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Block menu helpers shared by the right-click context menu and the drag
|
|
808
|
+
* handle menu (acceptance §5). Pure functions so both bindings — and the
|
|
809
|
+
* tests — operate on the same logic.
|
|
810
|
+
*/
|
|
811
|
+
/** Resolves the document position of a top-level block by its stable id. */
|
|
812
|
+
declare function findBlockPosById(editor: Editor, id: string): number | null;
|
|
813
|
+
/** Removes a top-level block by its stable id (single transaction). */
|
|
814
|
+
declare function deleteBlockById(editor: Editor, id: string): boolean;
|
|
815
|
+
/** Anchor URL for a block id (`#block-<id>` on the current page). */
|
|
816
|
+
declare function blockAnchorUrl(blockId: string): string;
|
|
817
|
+
|
|
818
|
+
interface TesseraPresetOptions {
|
|
819
|
+
locale?: TesseraLocale;
|
|
820
|
+
/** v1.1 history auto-capture idle window (playground uses a short one) */
|
|
821
|
+
historyIdleMs?: number;
|
|
822
|
+
}
|
|
823
|
+
/** Node types that receive stable block IDs (write-back protocol basis). */
|
|
824
|
+
declare const ID_BLOCK_TYPES: string[];
|
|
825
|
+
/**
|
|
826
|
+
* The Tessera extension preset: framework-agnostic, UI-free. The binding layers
|
|
827
|
+
* rendering (slash menu renderer, node views) on top of this.
|
|
828
|
+
*/
|
|
829
|
+
declare function createTesseraExtensions(options?: TesseraPresetOptions): Extensions;
|
|
830
|
+
|
|
831
|
+
declare function createMarkdownSerializer(schema: Schema): MarkdownSerializer;
|
|
832
|
+
/** Canonical JSON (or doc node) → Markdown. */
|
|
833
|
+
declare function docToMarkdown(doc: Node$1 | JSONContent, schema: Schema): string;
|
|
834
|
+
/** Markdown → canonical JSON (authoritative format). Requires DOM (browser/jsdom). */
|
|
835
|
+
declare function markdownToDoc(markdown: string, schema: Schema): JSONContent;
|
|
836
|
+
/** Strip volatile/whitespace-irrelevant fields for round-trip comparisons. */
|
|
837
|
+
declare function stableJson(json: JSONContent): JSONContent;
|
|
838
|
+
|
|
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
|
+
/**
|
|
866
|
+
* Write-back protocol (ADR-0001 / product-definition §4): AI and host code
|
|
867
|
+
* mutate the document by stable block IDs — never whole-doc rewrites.
|
|
868
|
+
* Semantics mirror SliteML's modifyRange / appendBlocks / removeBlocks.
|
|
869
|
+
*/
|
|
870
|
+
interface BlockHandle {
|
|
871
|
+
/** stable block id (UniqueID extension); null when the block predates ids */
|
|
872
|
+
id: string | null;
|
|
873
|
+
type: string;
|
|
874
|
+
pos: number;
|
|
875
|
+
node: _tiptap_pm_model.Node;
|
|
876
|
+
}
|
|
877
|
+
/** Top-level blocks with their ids, in document order. */
|
|
878
|
+
declare function getTopLevelBlocks(editor: Editor): BlockHandle[];
|
|
879
|
+
interface ModifyRangeOptions {
|
|
880
|
+
/** first block to replace (default: first block in doc) */
|
|
881
|
+
fromId?: string;
|
|
882
|
+
/** last block to replace, inclusive (default: fromId) */
|
|
883
|
+
toId?: string;
|
|
884
|
+
content: JSONContent[];
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Replace the inclusive block range [fromId … toId] with `content`.
|
|
888
|
+
* One transaction → one undo step.
|
|
889
|
+
*/
|
|
890
|
+
declare function modifyRange(editor: Editor, options: ModifyRangeOptions): boolean;
|
|
891
|
+
interface AppendBlocksOptions {
|
|
892
|
+
/** insert after this block (default: end of document) */
|
|
893
|
+
afterId?: string;
|
|
894
|
+
content: JSONContent[];
|
|
895
|
+
}
|
|
896
|
+
/** Append blocks after `afterId` (or at the document end). */
|
|
897
|
+
declare function appendBlocks(editor: Editor, options: AppendBlocksOptions): boolean;
|
|
898
|
+
/** Remove blocks by ids. One transaction → one undo step. */
|
|
899
|
+
declare function removeBlocks(editor: Editor, ids: string[]): boolean;
|
|
900
|
+
/** Read a block (and its subtree) by id as canonical JSON. */
|
|
901
|
+
declare function getBlockJson(editor: Editor, id: string): JSONContent | null;
|
|
902
|
+
|
|
903
|
+
export { AiAttribution, type AiAttributionOptions, AiTable, AiTableCell, AiTableHeader, type AppendBlocksOptions, BlockContextMenu, type BlockDiffEntry, type BlockHandle, Collapsible, CollapsibleContent, type CollapsibleOptions, CollapsibleSummary, CommentCommands, type CommentEntry, CommentMark, type CommentStore, type CommentThread, type DocSnapshot, EMOJI_ITEMS, EmbedBlock, type EmojiItem, EmojiMenu, type EmojiMenuOptions, type FindMatch, type FindReplaceState, Hint, type HintOptions, type HintVariant, type HistorySnapshotOptions, ID_BLOCK_TYPES, type IdentityService, type ImageAlign, ImageBlock, type ImageBlockOptions, type ModifyRangeOptions, PlaceholderCommands, type PlaceholderKind, PlaceholderMark, SlashMenu, type SlashMenuItem, type SlashMenuOptions, type SlashRenderFactory, type StorageService, TABLE_COLUMN_KINDS, type TableColumnKind, TesseraFindReplace, TesseraGallery, TesseraHistory, TesseraInputRules, type TesseraLocale, type TesseraMessageKey, TesseraMetrics, type TesseraMetricsSnapshot, type TesseraPresetOptions, TesseraServices, type TesseraServicesStorage, TesseraShortcuts, type TesseraTranslator, TesseraWordPaste, TocBlock, type UploadService, type UploadedAsset, type WordDiffPart, appendBlocks, blockAnchorUrl, cleanWordHtml, createMarkdownSerializer, createTesseraExtensions, createTesseraT, defaultSlashItems, deleteBlockById, diffDocs, diffSummary, docToMarkdown, filterEmojiItems, findBlockPosById, findGalleryRuns, findReplaceKey, galleryKey, getBlockJson, getCommentStore, getIdentityService, getStorageService, getTesseraMetrics, getTopLevelBlocks, getUploadService, historyKey, isWordHtml, listCommentRanges, markdownToDoc, measureTesseraMetrics, modifyRange, normalizeTypes, removeBlocks, stableJson, tableNodeToCsv, tableToCsvAt, tesseraMessages, wordDiff };
|