tiptap-markdown-react 0.9.6 → 0.10.1

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.
@@ -8,3 +8,6 @@ import { SourceRef } from './citationUtils';
8
8
  * 单块仍挂则剥标记当段落。某一处脏输出不至于整次插入失败。
9
9
  */
10
10
  export declare function insertMarkdown(editor: Editor, markdown: string, sources?: SourceRef[]): void;
11
+ /** 围栏代码整块留下,其余按空行切。 */
12
+ export declare function splitTopLevelBlocks(markdown: string): string[];
13
+ export declare function stripToPlainText(markdown: string): string;
@@ -0,0 +1,36 @@
1
+ import { Editor } from '@tiptap/react';
2
+ /**
3
+ * 取区间对应的 markdown,并给出它在 `editor.getMarkdown()` 输出里的精确偏移。
4
+ *
5
+ * 为什么需要它:后端改卡走的是「在正文里逐字找到这段,替换」(`replace_in_text` +
6
+ * `count=1`),所以发过去的那段必须是**存库正文的逐字子串**;调用方还要知道它在整篇里
7
+ * 的偏移,才能从存库正文取前后文(`ref`)并界定「这次改了哪一段」。官方 OSS 只有整篇
8
+ * `Editor.getMarkdown()`;官方 Pro 的 `getMarkdownRange` 只返回字符串、不给偏移,也不做
9
+ * 块扩展。
10
+ *
11
+ * **默认扩块**(`expandToBlocks: true`):把区间扩展到与它相交的顶层块的首尾。一是序列化
12
+ * 稳定(半句话里跨着加粗,片段序列化会在断口插入多余的 `**`),二是**只有块对齐时偏移才是
13
+ * 算出来的,而不是猜出来的**。
14
+ *
15
+ * 偏移怎么来的:把 doc 的顶层块逐个序列化、用 `\n\n` 拼起来,等于整篇序列化(有测试钉住
16
+ * 这条不变量),于是「前 i 块拼接的长度」就是第 i 块的起点偏移。这条不变量若在某篇文档上
17
+ * 不成立,退回按内容在整篇里定位;定位不到或出现多处就返回 `null`——宁可让调用方知道
18
+ * 拿不到偏移,也不要给一个错的。
19
+ */
20
+ export interface MarkdownRange {
21
+ /** 区间对应的 markdown 片段(扩块后)。 */
22
+ markdown: string;
23
+ /** 片段在 `editor.getMarkdown()` 输出里的起始偏移。 */
24
+ from: number;
25
+ /** 结束偏移(不含)。恒有 `to - from === markdown.length`。 */
26
+ to: number;
27
+ }
28
+ export interface GetMarkdownForRangeOptions {
29
+ /**
30
+ * 是否把区间扩展到与它相交的顶层块(段落 / 表格 / 列表 / 图表 …)的首尾。
31
+ * 默认 `true`:调用方要「存库正文的逐字子串 + 精确偏移」时应该保持默认。
32
+ * 设 `false` 按原区间取,此时偏移依赖内容定位,可能返回 `null`。
33
+ */
34
+ expandToBlocks?: boolean;
35
+ }
36
+ export declare function getMarkdownForRange(editor: Editor, from: number, to: number, options?: GetMarkdownForRangeOptions): MarkdownRange | null;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 把一段正文规范成「编辑器读进来再吐出去」的那种写法。
3
+ *
4
+ * **为什么需要它**:编辑器加载 markdown 时要先解析成文档结构,而 markdown 里有些东西是
5
+ * 排版装饰、解析时就丢了(表格列宽的空格、注释 JSON 里的空格、数值尾零 …)。所以
6
+ * 「存库正文」和「编辑器序列化结果」常常**逐字不同**——这与用户改没改无关,加载那一刻就
7
+ * 发生了。而后端改卡走的是「在正文里逐字找到这段再替换」,两边写法不一致就会搜不到。
8
+ * 宿主在要动正文之前(例如圈选改写提交前)先用本函数比对/对齐一次即可。
9
+ *
10
+ * **已知会被规范化的结构**(都是排版层面,内容语义不变):
11
+ *
12
+ * | 结构 | 存库可能是 | 规范化后 |
13
+ * |------|-----------|---------|
14
+ * | GFM 表格 | `\| 收入 \| 100 \|` | 按列内容宽度重排对齐(多/少空格) |
15
+ * | 图表注释 JSON | `<!-- {"chartType": "column"} -->` | `<!-- {"chartType":"column"} -->`(去空格,键序按解析结果) |
16
+ * | **图表区**的数值 | `132.0` | `132`(图表把表解析成 payload,数值走 number 再写回) |
17
+ * | 纯空白的正文 | `" \n "` | `""` |
18
+ *
19
+ * 注意第三行**只作用于图表区**:普通 GFM 表格走 marked 的表格解析,单元格是文本,
20
+ * `1,234.50` 这种写法会原样保留(有测试钉住)。
21
+ *
22
+ * 反过来,这些**不该**被改动,同样有测试钉住:标题/段落/加粗斜体/列表/引用、代码块、
23
+ * 公式、图片、脚注 `[^n]`、图表仍是「注释 + 表」的作者形态(不会变成 ```tmr-chart 围栏)。
24
+ *
25
+ * 注意它**不使用** `enrichMarkdownCitations`:脚注的 `[^n]` 由 CitationRef 自带的
26
+ * tokenizer 解析,url/title 只存在于节点属性里、不进 markdown,所以规范化不需要 sources。
27
+ */
28
+ export declare function normalizeMarkdown(markdown: string): string;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import { PluginKey } from '@tiptap/pm/state';
3
+ import { DecorationSet } from '@tiptap/pm/view';
4
+ import { Editor } from '@tiptap/react';
5
+ /**
6
+ * 「这段正在被 AI 改写」的高亮。
7
+ *
8
+ * 用 **decoration** 而不是 mark:两者都不会漏进 markdown,都能盖住表格,但 decoration
9
+ * 不需要往 schema 里加标记、也不参与 undo 历史,而且**原子节点(图表 / 图片)也能盖**
10
+ * ——mark 只作用于行内内容,盖不住 `atom: true` 的块级节点。
11
+ *
12
+ * 状态由宿主通过 `pendingAnchors` 推入(提交圈选改写时给上,回填或冲突时清空)。
13
+ * 文档被编辑时装饰会跟着映射(在原文中间插字,高亮跟着变长;在前面插,整体后移)。
14
+ */
15
+ export interface PendingAnchor {
16
+ /** 宿主自己的标识(例如一次圈选请求的 request_id)。 */
17
+ id: string;
18
+ /** doc 坐标区间;通常是与选区相交的顶层块的首尾(见 getMarkdownForRange)。 */
19
+ ranges: Array<{
20
+ from: number;
21
+ to: number;
22
+ }>;
23
+ }
24
+ export interface PendingAnchorState {
25
+ decorations: DecorationSet;
26
+ }
27
+ /** 用 transaction meta 推入锚点:`tr.setMeta(PENDING_ANCHOR_META, anchors)`。 */
28
+ export declare const PENDING_ANCHOR_META = "tmr:pendingAnchor";
29
+ export declare const pendingAnchorPluginKey: PluginKey<PendingAnchorState>;
30
+ /** 装饰上的 class;点击命中时宿主用 `[data-pending-id]` 取回 id。 */
31
+ export declare const PENDING_ANCHOR_CLASS = "tmr-pending-anchor";
32
+ export declare const pendingAnchorExtension: Extension<any, any>;
33
+ /** 更新待改写高亮(传空数组即清空)。不进 undo 历史。 */
34
+ export declare function setPendingAnchors(editor: Editor, anchors: PendingAnchor[]): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ import { Editor } from '@tiptap/react';
2
+ import { SourceRef } from './citationUtils';
3
+ export interface ReplaceRangeOptions {
4
+ /** 脚注来源:把 `[^n]` enrich 成带 url/title 的 citation-ref(与 `insertMarkdown` 同一套)。 */
5
+ sources?: SourceRef[];
6
+ }
7
+ /**
8
+ * 用一段 markdown **替换**一个区间。给「后台回填」用——AI 在别处改完,把结果写回编辑器。
9
+ *
10
+ * 与 `insertMarkdown` 的三处刻意差异(都在注释里点明,改的时候别抹平):
11
+ *
12
+ * 1. **不抢焦点**。`insertMarkdown` 是「用户主动插入」,链式带了 `.focus()`;后台回填时
13
+ * 用户可能正在别处打字,抢焦点等于打断他。所以这里直接调 `insertContentAt`。
14
+ * 2. **按区间替换**,不是往光标处插(`insertContentAt` 收 `{from,to}` 时后者即替换)。
15
+ * 3. **进 undo 历史**。`insertContentAt` 本身是一个 transaction,撤销一步就回去;
16
+ * 与评论锚定的 `addToHistory: false` 相反——正文改动必须能撤。
17
+ *
18
+ * 解析失败(AI 的脏输出过不了 schema:嵌套 bold、块级图进 listItem …)时按块降级,
19
+ * 与 `insertMarkdown` 同一套策略。注意降级路径是**逐块多次 transaction**,撤销要多按几下;
20
+ * 只在整段失败时才走。
21
+ *
22
+ * @returns 是否改动了文档。空 markdown 返回 `false`(不静默删掉用户的内容)。
23
+ */
24
+ export declare function replaceRangeWithMarkdown(editor: Editor, range: {
25
+ from: number;
26
+ to: number;
27
+ }, markdown: string, options?: ReplaceRangeOptions): boolean;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import { Selection } from '@tiptap/pm/state';
2
+ /**
3
+ * 选区类型——给宿主判断「用户选的是什么」用。
4
+ *
5
+ * - `text`:普通文字选区(含 AllSelection)
6
+ * - `cell`:表格里的单元格选区(`CellSelection`,拖过表格时就是它)
7
+ * - `node`:整节点选区(`NodeSelection`)。图表 / 图片 / 代码块这类 `atom` 节点只能这样被选中
8
+ * ——它们在编辑器里渲染成图或块,用户没法用拖选文字的方式选中
9
+ */
10
+ export type SelectionKind = 'text' | 'cell' | 'node';
11
+ export declare function selectionKind(selection: Selection): SelectionKind;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiptap-markdown-react",
3
- "version": "0.9.6",
3
+ "version": "0.10.1",
4
4
  "description": "A batteries-included, self-styled Markdown WYSIWYG editor and reader suite built on Tiptap v3. Markdown in/out, SSR/static rendering, table of contents, themeable via CSS variables, zero Ant Design.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -81,6 +81,7 @@
81
81
  "@tiptap/extension-task-item": "3.27.1",
82
82
  "@tiptap/extension-task-list": "3.27.1",
83
83
  "@tiptap/extension-text-style": "3.27.1",
84
+ "@tiptap/extensions": "3.27.1",
84
85
  "@tiptap/markdown": "3.27.1",
85
86
  "@tiptap/pm": "3.27.1",
86
87
  "@tiptap/react": "3.27.1",