wendang 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.
Files changed (98) hide show
  1. package/LICENSE +43 -2
  2. package/NOTICE.md +1 -1
  3. package/README.md +120 -52
  4. package/dist/cjs/document.js +1 -19
  5. package/dist/cjs/index.js +1 -25
  6. package/dist/cjs/mindmap.js +1 -22
  7. package/dist/cjs/schema.js +1 -11
  8. package/dist/cjs/shared/anchor.js +1 -201
  9. package/dist/cjs/shared/callout.js +1 -64
  10. package/dist/cjs/shared/code-block.js +1 -33
  11. package/dist/cjs/shared/code-languages.js +1 -51
  12. package/dist/cjs/shared/doc-compatibility.js +1 -23
  13. package/dist/cjs/shared/doc-text.js +1 -52
  14. package/dist/cjs/shared/document-colors.js +1 -61
  15. package/dist/cjs/shared/headings.js +1 -0
  16. package/dist/cjs/shared/inline-comments.js +1 -179
  17. package/dist/cjs/shared/mind-map-folding.js +1 -14
  18. package/dist/cjs/shared/mind-map-phase2.js +1 -133
  19. package/dist/cjs/shared/mind-map.js +1 -445
  20. package/dist/cjs/shared/mind-topic-text.js +1 -57
  21. package/dist/cjs/shared/node-catalog.js +1 -62
  22. package/dist/cjs/shared/presence.js +1 -141
  23. package/dist/cjs/shared/safe-href.js +1 -36
  24. package/dist/cjs/shared/table-cell-format.js +1 -18
  25. package/dist/cjs/shared/table-layout.js +1 -28
  26. package/dist/cjs/shared/types.js +1 -2
  27. package/dist/cjs/shared/video-embed.js +1 -102
  28. package/dist/cjs/shared/workbook.js +1 -152
  29. package/dist/cjs/spreadsheet.js +1 -17
  30. package/dist/cjs/types.js +1 -17
  31. package/dist/cjs/validation/callout.js +1 -17
  32. package/dist/cjs/validation/doc-node-whitelist.js +1 -71
  33. package/dist/cjs/validation/error.js +1 -17
  34. package/dist/cjs/validation/mind-map.js +1 -129
  35. package/dist/cjs/validation/schema-version.js +1 -5
  36. package/dist/cjs/validation/schema.js +1 -376
  37. package/dist/cjs/validation/video-embed.js +1 -17
  38. package/dist/cjs/validation/workbook.js +1 -17
  39. package/dist/esm/document.js +1 -3
  40. package/dist/esm/index.js +1 -3
  41. package/dist/esm/mindmap.js +1 -4
  42. package/dist/esm/schema.js +1 -3
  43. package/dist/esm/shared/anchor.js +1 -192
  44. package/dist/esm/shared/callout.js +1 -56
  45. package/dist/esm/shared/code-block.js +1 -25
  46. package/dist/esm/shared/code-languages.js +1 -46
  47. package/dist/esm/shared/doc-compatibility.js +1 -20
  48. package/dist/esm/shared/doc-text.js +1 -48
  49. package/dist/esm/shared/document-colors.js +1 -56
  50. package/dist/esm/shared/headings.js +1 -0
  51. package/dist/esm/shared/inline-comments.js +1 -173
  52. package/dist/esm/shared/mind-map-folding.js +1 -10
  53. package/dist/esm/shared/mind-map-phase2.js +1 -121
  54. package/dist/esm/shared/mind-map.js +1 -427
  55. package/dist/esm/shared/mind-topic-text.js +1 -49
  56. package/dist/esm/shared/node-catalog.js +1 -57
  57. package/dist/esm/shared/presence.js +1 -130
  58. package/dist/esm/shared/safe-href.js +1 -32
  59. package/dist/esm/shared/table-cell-format.js +1 -14
  60. package/dist/esm/shared/table-layout.js +1 -23
  61. package/dist/esm/shared/types.js +1 -1
  62. package/dist/esm/shared/video-embed.js +1 -96
  63. package/dist/esm/shared/workbook.js +1 -144
  64. package/dist/esm/spreadsheet.js +1 -1
  65. package/dist/esm/types.js +1 -1
  66. package/dist/esm/validation/callout.js +1 -1
  67. package/dist/esm/validation/doc-node-whitelist.js +1 -68
  68. package/dist/esm/validation/error.js +1 -13
  69. package/dist/esm/validation/mind-map.js +1 -125
  70. package/dist/esm/validation/schema-version.js +1 -2
  71. package/dist/esm/validation/schema.js +1 -372
  72. package/dist/esm/validation/video-embed.js +1 -1
  73. package/dist/esm/validation/workbook.js +1 -1
  74. package/dist/types/document.d.ts +1 -0
  75. package/dist/types/shared/anchor.d.ts +0 -55
  76. package/dist/types/shared/callout.d.ts +39 -2
  77. package/dist/types/shared/code-block.d.ts +0 -1
  78. package/dist/types/shared/code-languages.d.ts +0 -19
  79. package/dist/types/shared/doc-text.d.ts +0 -14
  80. package/dist/types/shared/document-colors.d.ts +0 -4
  81. package/dist/types/shared/headings.d.ts +7 -0
  82. package/dist/types/shared/inline-comments.d.ts +0 -42
  83. package/dist/types/shared/mind-map-folding.d.ts +0 -2
  84. package/dist/types/shared/mind-map.d.ts +0 -4
  85. package/dist/types/shared/mind-topic-text.d.ts +0 -2
  86. package/dist/types/shared/node-catalog.d.ts +0 -17
  87. package/dist/types/shared/presence.d.ts +0 -51
  88. package/dist/types/shared/safe-href.d.ts +0 -1
  89. package/dist/types/shared/table-cell-format.d.ts +0 -1
  90. package/dist/types/shared/table-layout.d.ts +0 -3
  91. package/dist/types/shared/types.d.ts +0 -4
  92. package/dist/types/shared/video-embed.d.ts +9 -2
  93. package/dist/types/shared/workbook.d.ts +0 -2
  94. package/dist/types/validation/doc-node-whitelist.d.ts +0 -19
  95. package/dist/types/validation/mind-map.d.ts +0 -2
  96. package/dist/types/validation/schema-version.d.ts +0 -1
  97. package/dist/types/validation/schema.d.ts +0 -34
  98. package/package.json +9 -9
@@ -1,8 +1,46 @@
1
- /** Document attributes only; shared with server validation by a source parity test. */
2
1
  export declare const CALLOUT_TONES: readonly ["info", "success", "warning", "danger"];
3
2
  export declare const CALLOUT_ICONS: readonly ["💡", "ℹ️", "✅", "⚠️", "❗", "📌", "🎯", "💬", "📖", "📝", "🚀", "🔔", "🔎", "⭐", "🌅", "🛠️", "📅", "🔒", "❤️", "🎉", "❓", "📎", "🏁", "🔥"];
4
3
  export declare const CALLOUT_COLORS: readonly ["gray", "red", "orange", "yellow", "green", "blue", "purple"];
5
4
  export declare const CALLOUT_FILLS: readonly ["gray", "red", "orange", "yellow", "green", "blue", "purple", ...string[]];
5
+ export declare const CALLOUT_SWATCH_FALLBACKS: {
6
+ readonly textColor: {
7
+ readonly gray: "#646a73";
8
+ readonly red: "#d92c2c";
9
+ readonly orange: "#b95d0b";
10
+ readonly yellow: "#8a6c0a";
11
+ readonly green: "#237b35";
12
+ readonly blue: "#1971c2";
13
+ readonly purple: "#7048e8";
14
+ };
15
+ readonly borderColor: {
16
+ readonly gray: "#c7cbd1";
17
+ readonly red: "#f0a2a2";
18
+ readonly orange: "#ffc078";
19
+ readonly yellow: "#e3ca71";
20
+ readonly green: "#91d4a0";
21
+ readonly blue: "#9ac5ef";
22
+ readonly purple: "#c0a4ed";
23
+ };
24
+ readonly backgroundColor: {
25
+ readonly gray: "#f1f3f5";
26
+ readonly red: "#ffe3e3";
27
+ readonly orange: "#fff0db";
28
+ readonly yellow: "#fff3bf";
29
+ readonly green: "#d3f9d8";
30
+ readonly blue: "#d0ebff";
31
+ readonly purple: "#e5dbff";
32
+ readonly 'gray-strong': "#ced4da";
33
+ readonly 'red-strong': "#ffc9c9";
34
+ readonly 'orange-strong': "#ffd8a8";
35
+ readonly 'yellow-strong': "#ffec99";
36
+ readonly 'green-strong': "#b2f2bb";
37
+ readonly 'blue-strong': "#a5d8ff";
38
+ readonly 'purple-strong': "#d0bfff";
39
+ };
40
+ };
41
+ export type CalloutSwatchKey = keyof typeof CALLOUT_SWATCH_FALLBACKS;
42
+ export declare function calloutColorToken(key: CalloutSwatchKey, value: string): string;
43
+ export declare function calloutSwatchStyle(key: CalloutSwatchKey, value: string | null): Record<string, string>;
6
44
  export interface CalloutAttrs {
7
45
  tone?: string | null;
8
46
  icon?: string | null;
@@ -19,7 +57,6 @@ export declare function effectiveCallout(attrs?: CalloutAttrs): {
19
57
  backgroundColor: string;
20
58
  };
21
59
  export declare function calloutDOMAttributes(attrs?: CalloutAttrs): Record<string, string>;
22
- /** Optional Markdown metadata never contains free CSS or HTML. */
23
60
  export declare function calloutMetadata(attrs: CalloutAttrs): string;
24
61
  export declare function readCalloutMetadata(text: string): {
25
62
  attrs: CalloutAttrs;
@@ -1,4 +1,3 @@
1
- /** Presentation metadata only; code, whitespace and the document schema remain unchanged. */
2
1
  export declare const CODE_THEMES: readonly [{
3
2
  readonly value: "auto";
4
3
  readonly label: "跟随页面";
@@ -1,26 +1,7 @@
1
- /**
2
- * 代码块语言目录(**纯数据,无编辑器依赖**)。
3
- *
4
- * 为什么放 shared:编辑端(语言选择器)与只读端(语言标签)都要用;
5
- * 若放在 editor/ 下,只读端 import 它就会把编辑器模块拉进阅读路径,
6
- * 违反读写分离(与 doc-schema-version.ts 的取舍同理)。
7
- *
8
- * `value` 即写入正文 `codeBlock.language` 的值,也是 Markdown 围栏的语言标记
9
- * (```` ```js ````),因此导出/导入可无损往返(服务端序列化器与解析器均已支持)。
10
- * `label` 仅供界面展示。
11
- */
12
1
  export interface CodeLanguage {
13
2
  value: string;
14
3
  label: string;
15
4
  }
16
- /** 空字符串表示「纯文本 / 不指定」。 */
17
5
  export declare const CODE_LANGUAGES: CodeLanguage[];
18
- /**
19
- * 取语言的展示标签。
20
- *
21
- * 未知语言(如用户从 Markdown 导入的少见围栏标记)直接显示原值——
22
- * 不隐藏、不改写,避免"我明明标了语言却看不到"。
23
- */
24
6
  export declare function codeLanguageLabel(value: string | null | undefined): string;
25
- /** 是否为目录内已知语言(用于选择器回显:未知值时追加一个临时选项)。 */
26
7
  export declare function isKnownCodeLanguage(value: string): boolean;
@@ -1,21 +1,7 @@
1
- /**
2
- * 从 ProseMirror 文档提取用于锚点定位的块级纯文本。
3
- *
4
- * 与 anchor.ts 配合:锚点用「在扁平文本中的字符偏移」表达,
5
- * 这里负责把文档结构摊平,并保留「块 → 字符区间」的映射,
6
- * 以便把定位结果映射回具体的块(前端据此在对应块上加高亮)。
7
- *
8
- * 与后端 DocSchemaService.extractPlainText 保持一致的遍历顺序(先文本、后子节点),
9
- * 否则同一段内容在提交锚点与解析锚点时会得到不同的字符偏移。
10
- */
11
1
  import type { ProseMirrorNode } from './types.js';
12
2
  export interface BlockText {
13
- /** 块在文档顶层 content 中的索引 */
14
3
  index: number;
15
- /** 该块的纯文本 */
16
4
  text: string;
17
5
  }
18
- /** 提取顶层块(含嵌套块)的纯文本。列表会递归取出每一项。 */
19
6
  export declare function extractBlockTexts(doc: ProseMirrorNode | null | undefined): BlockText[];
20
- /** 取节点的全部文本(含后代)。 */
21
7
  export declare function plainTextOf(node: ProseMirrorNode): string;
@@ -1,7 +1,3 @@
1
- /**
2
- * 文档文字与高亮的受限调色板。存储值保持原始 hex,显示色通过主题变量切换;
3
- * 阅读路径复用此模块,不引入 Tiptap。历史色值均保留,复制时 data-color 可往返。
4
- */
5
1
  export declare const TEXT_COLORS: readonly [{
6
2
  readonly name: "默认";
7
3
  readonly value: null;
@@ -0,0 +1,7 @@
1
+ import type { ProseMirrorNode } from './types.js';
2
+ export interface DocumentHeading {
3
+ level: number;
4
+ text: string;
5
+ order: number;
6
+ }
7
+ export declare function extractHeadings(doc: ProseMirrorNode | null | undefined): DocumentHeading[];
@@ -1,71 +1,29 @@
1
1
  import type { CommentItem, ProseMirrorNode } from './types.js';
2
- /**
3
- * 把行内评论锚点解析为**文本节点内的字符区间**,供渲染器加高亮。
4
- *
5
- * ## 为什么不用「块索引」
6
- *
7
- * 直觉做法是记录「第几个段落 + 段内偏移」。但文档结构是嵌套的
8
- * (列表套列表、引用里含段落、表格单元格…),"第几块"这种扁平索引
9
- * 很难与渲染器的实际遍历对应,映射错一位高亮就会错位。
10
- *
11
- * ## 采用方案:文本节点序号 + 节点内偏移
12
- *
13
- * 把文档里所有**文本节点**按深度优先顺序编号(第 0 个、第 1 个…),
14
- * 锚点记录「第几个文本节点 + 节点内起止」。
15
- *
16
- * 关键在于:锚点解析与渲染器**使用完全相同的遍历顺序**
17
- * (都是深度优先、先文本后子节点),因此序号天然一致,不需要维护额外映射。
18
- * 这个顺序与后端 `DocSchemaService.extractPlainText` 也保持一致。
19
- *
20
- * ## 三种状态必须区分显示
21
- * - `exact`:原文仍在,正常高亮;
22
- * - `shifted`:原文被改动,仅靠上下文降级定位,零宽落点并提示;
23
- * - 失联:无法定位,**不给正文加高亮**,只在评论区列出。
24
- * 宁可显示「有评论已失联」,也不要把评论挂到无关文字上。
25
- */
26
2
  export type InlineMarkState = 'exact' | 'shifted';
27
3
  export interface InlineMark {
28
4
  commentId: string;
29
- /** 文本节点序号(深度优先顺序,从 0 开始) */
30
5
  textIndex: number;
31
- /** 该文本节点内的字符区间(含头不含尾);shifted 时 from === to */
32
6
  from: number;
33
7
  to: number;
34
8
  state: InlineMarkState;
35
- /** 该评论是否有回复(供高亮配色区分) */
36
9
  hasReplies: boolean;
37
10
  }
38
11
  export interface ResolvedInlineComments {
39
12
  marks: InlineMark[];
40
- /** 无法定位的评论(内容已被删除或改动过大) */
41
13
  orphanCommentIds: string[];
42
- /** 按文本节点序号分组,渲染时 O(1) 查询 */
43
14
  byTextIndex: Map<number, InlineMark[]>;
44
15
  }
45
- /**
46
- * 把文档摊平为「每个文本节点一段」,并给出每段在扁平文本中的偏移。
47
- *
48
- * **遍历顺序必须与 DocRenderer 一致**(深度优先、先 text 后 content),
49
- * 否则文本节点序号会错位。二者的一致性由测试覆盖。
50
- */
51
16
  export interface TextRun {
52
- /** 文本节点序号 */
53
17
  index: number;
54
18
  text: string;
55
- /** 在扁平文本中的起始偏移 */
56
19
  flatStart: number;
57
- /** 在扁平文本中的结束偏移(不含) */
58
20
  flatEnd: number;
59
21
  }
60
22
  export declare function buildTextRuns(doc: ProseMirrorNode | null | undefined): TextRun[];
61
- /** 文档的扁平文本(与 buildTextRuns 的偏移体系一致)。 */
62
23
  export declare function buildFlatText(doc: ProseMirrorNode | null | undefined): string;
63
- /** 仅有锚点的评论才参与定位(页级评论不属于行内)。 */
64
24
  export declare function resolveInlineComments(doc: ProseMirrorNode | null | undefined, comments: CommentItem[]): ResolvedInlineComments;
65
- /** 响应式封装。 */
66
25
  export interface TextPiece {
67
26
  text: string;
68
- /** 覆盖该片段的标记(可能多个:不同评论指向重叠文字) */
69
27
  marks: InlineMark[];
70
28
  }
71
29
  export declare function splitTextByMarks(text: string, marks: InlineMark[]): TextPiece[];
@@ -1,5 +1,3 @@
1
1
  import { type MindMapModel } from './mind-map.js';
2
- /** Root/leaf/stale entries have no local fold affordance and must not hide a whole map. */
3
2
  export declare function normalizeMindFolds(model: MindMapModel, ids: Iterable<string>): Set<string>;
4
- /** Keep every first-level branch visible, collapsing only those with descendants. */
5
3
  export declare function firstLevelMindFolds(model: MindMapModel): Set<string>;
@@ -1,4 +1,3 @@
1
- /** Engine-independent document format. Topic text remains ordinary ProseMirror text. */
2
1
  export declare const MIND_MAP_VERSION = 3;
3
2
  export declare const MIND_MAP_MAX_TOPICS = 500;
4
3
  export declare const MIND_MAP_MAX_TEXT = 2000;
@@ -98,13 +97,10 @@ export interface MindMapDrawing {
98
97
  export declare const mindMapId: () => `${string}-${string}-${string}-${string}-${string}`;
99
98
  export declare function createMindMap(layout?: MindMapLayout): MindMapJson;
100
99
  export declare function readMindMap(node: MindMapJson | null | undefined): MindMapModel | null;
101
- /** Missing parents and concurrent cycles keep every surviving topic, deterministically. */
102
100
  export declare function mindMapTree(model: MindMapModel): MindTopic[];
103
101
  export declare function mindMapDescendants(model: MindMapModel, id: string): Set<string>;
104
102
  export declare function canMoveMindTopic(model: MindMapModel, id: string, parentId: string): boolean;
105
- /** Only first-level topics own a side; all descendants inherit their branch direction. */
106
103
  export declare function mindMapBranchSides(model: MindMapModel): Map<string, -1 | 1>;
107
- /** Shared by the live drop preview and the single document transaction on release. */
108
104
  export declare function projectMindSideMove(model: MindMapModel, id: string, side: -1 | 1, beforeId?: string): MindMapModel | null;
109
105
  export declare function textDifference(before: string, after: string): {
110
106
  from: number;
@@ -5,9 +5,7 @@ export declare const MIND_TOPIC_FONT = "-apple-system, BlinkMacSystemFont, \"Seg
5
5
  export type MindTextMeasure = (text: string) => number;
6
6
  declare let factory: ((fontSize: number, weight: number) => MindTextMeasure | null) | undefined;
7
7
  export declare function setMindTextMeasureFactory(value: typeof factory): void;
8
- /** Real browser font metrics; non-DOM exports/tests keep a conservative text-only fallback. */
9
8
  export declare function mindTextMeasure(fontSize?: number, weight?: number): MindTextMeasure;
10
- /** Keep explicit empty lines and emoji clusters; presentation wrapping never edits the source. */
11
9
  export declare function wrapMindText(text: string, width: number, measure: MindTextMeasure): string[];
12
10
  export declare function mindTopicTextSize(text: string, fontSize?: number, root?: boolean, description?: string, bold?: boolean, minimumWidth?: number): {
13
11
  width: number;
@@ -1,30 +1,13 @@
1
- /**
2
- * 节点目录:编辑器的 nodeView 与只读渲染规则**共用的单一真源**
3
- * (对齐《Wiki好用技术方案》1.1)。
4
- *
5
- * 这里只放纯数据(无 Vue、无 Tiptap 依赖),因此阅读端可以只引它 + Renderer,
6
- * 而完全不加载编辑器代码;编辑器端再基于同一份目录生成扩展。
7
- *
8
- * 新增节点时必须同时更新:
9
- * 1) 本目录 2) 服务端 doc-schema.service.ts 白名单 3) 提升 schema 版本
10
- * 否则旧客户端会因「遇到不支持节点」而被服务端拒写(这是预期行为,避免静默丢内容)。
11
- */
12
1
  export type NodeKind = 'block' | 'inline' | 'text' | 'atom';
13
2
  export interface NodeSpecMeta {
14
3
  type: string;
15
4
  kind: NodeKind;
16
- /** 能否出现在只读正文的顶层容器 */
17
5
  topLevel?: boolean;
18
- /** 是否为原子节点(无 content,如分隔线、图片) */
19
6
  atom?: boolean;
20
- /** 供 UI 展示与 slash 菜单使用 */
21
7
  label: string;
22
- /** 是否仅编辑态可用(如占位) */
23
8
  editorOnly?: boolean;
24
9
  }
25
10
  export declare const NODE_CATALOG: NodeSpecMeta[];
26
11
  export declare function nodeMeta(type: string | undefined): NodeSpecMeta | undefined;
27
- /** 只读渲染遇到未知节点时使用的降级提示(不静默丢弃,向用户明示)。 */
28
12
  export declare const UNKNOWN_NODE_LABEL = "\u6B64\u5185\u5BB9\u7531\u66F4\u9AD8\u7248\u672C\u7684\u7F16\u8F91\u5668\u521B\u5EFA\uFF0C\u5F53\u524D\u7248\u672C\u65E0\u6CD5\u663E\u793A";
29
- /** 标题级别 → HTML 标签 */
30
13
  export declare function headingTag(level: unknown): string;
@@ -1,69 +1,18 @@
1
- /**
2
- * 协同在线状态(presence)的纯逻辑(可单测,不依赖 DOM / Yjs / 网络)。
3
- *
4
- * ## 为什么单独抽出来
5
- *
6
- * 在线状态有两个容易做错、且出错后**不报错、只是显示不对**的地方:
7
- * 1) 颜色分配:同一用户在不同客户端必须得到**相同颜色**(否则"那个蓝色光标是谁"
8
- * 无法对应),而颜色若只按"连接顺序"分,B 客户端上同一个人的颜色就会变。
9
- * 因此颜色必须由**用户身份**稳定推导,不能依赖顺序或随机。
10
- * 2) 对比度:名字气泡上的文字要与背景色对比可读;简单地对 6 个色相取固定亮度,
11
- * 在浅色模式下会有几个色相偏亮、白字看不清。
12
- * 这些都能用纯函数锁定,因此放在这里。
13
- */
14
- /** 在线用户条目(用于头像/名字展示)。 */
15
1
  export interface PresenceUser {
16
2
  userId: string;
17
3
  displayName: string;
18
- /** 稳定的身份色(hex) */
19
4
  color: string;
20
5
  }
21
- /**
22
- * 稳定的用户色板。
23
- *
24
- * 选取标准不是"好看",而是**所有颜色配白字都达到 WCAG AA(≥4.5:1)**——
25
- * 这样光标/名字气泡永远是"彩色底 + 白字",视觉一致,也不会出现浅底白字看不清。
26
- * (初版用了偏亮的绿 `#1a9d5a`(3.49:1)与橙 `#d97706`(3.19:1),配白字或深字
27
- * 都不到 AA;这里换成更深的一档,由单测锁定对比度。)
28
- */
29
6
  export declare const PRESENCE_PALETTE: readonly ["#2f6feb", "#127a45", "#b45309", "#c026d3", "#dc2626", "#0e7490", "#7c3aed", "#9a4a08"];
30
- /**
31
- * 由用户 id 稳定映射到调色板颜色。
32
- *
33
- * 用 FNV-1a 哈希而非 simple sum:简单的字符求和会让 `user1`/`user2`
34
- * 这类相邻 id 落到同一颜色,而实际用户 id 常是顺序生成的。
35
- */
36
7
  export declare function colorForUser(userId: string): string;
37
- /** FNV-1a 32 位哈希 → 取模。 */
38
8
  export declare function hashIndex(input: string, buckets: number): number;
39
- /** 名字首字(用于头像占位)。中文取首字,英文取首字母大写。 */
40
9
  export declare function initialOf(displayName: string): string;
41
- /**
42
- * 汇总在线用户:去重(同一用户多标签页只显示一次)、排除自己、稳定排序。
43
- *
44
- * 稳定性很重要:若按 Map 遍历顺序输出,光标/头像列表会随消息到达顺序抖动。
45
- * 这里按 displayName 排序,保证界面上顺序稳定。
46
- */
47
10
  export declare function collectPresence(states: Array<{
48
11
  userId: string;
49
12
  displayName: string;
50
13
  }>, selfUserId: string | null): PresenceUser[];
51
- /**
52
- * 相对亮度(WCAG),用于决定光标/气泡上该用白字还是深字。
53
- * 返回 0(黑)~1(白)。
54
- */
55
14
  export declare function relativeLuminance(hex: string): number;
56
- /** 前景深色的基准(与 :root 的 --color-text 接近)。 */
57
15
  export declare const DARK_TEXT = "#1f2937";
58
- /** WCAG 对比度。 */
59
16
  export declare function contrastRatio(a: string, b: string): number;
60
- /**
61
- * 该背景色上应使用的前景色。
62
- *
63
- * 实现为「比较白字与深字各自的对比度,取更高的那个」,而不是用一个亮度阈值判断——
64
- * 阈值是脆弱的:初版用 0.179 作分界,`#2f6feb`(亮度 0.180)被判为用深字,
65
- * 但实际白字对比度 4.57 优于深字 3.21,选错了。(由单测发现。)
66
- */
67
17
  export declare function readableTextColor(hex: string): '#ffffff' | typeof DARK_TEXT;
68
- /** 解析 #rgb / #rrggbb;非法返回 null。 */
69
18
  export declare function parseHex(hex: string): [number, number, number] | null;
@@ -1,3 +1,2 @@
1
1
  export declare function isSafeHref(raw: unknown): boolean;
2
- /** 渲染用:安全则原样返回,否则返回 '#'。 */
3
2
  export declare function safeHref(raw: unknown): string;
@@ -1,4 +1,3 @@
1
- /** 固定单元格底色,不接受任意 CSS;读写端共用,服务端同步白名单。 */
2
1
  export declare const CELL_BACKGROUNDS: readonly [{
3
2
  readonly name: "默认";
4
3
  readonly value: null;
@@ -1,7 +1,4 @@
1
1
  import type { ProseMirrorNode } from './types.js';
2
- /** 自动列宽保留可读空间;窄屏在表格内部滚动,不把长文本压成单字列。 */
3
2
  export declare const TABLE_CELL_MIN_WIDTH = 120;
4
- /** 首行合并单元格的 colwidth 数组包含多个视觉列,必须逐列展开。 */
5
3
  export declare function tableColumnWidths(table: ProseMirrorNode): number[];
6
- /** 未设置宽度的列与编辑器 cellMinWidth 共用同一默认值;保留历史显式列宽。 */
7
4
  export declare function tableMinWidth(widths: number[]): number | undefined;
@@ -17,7 +17,6 @@ export interface CommentAnchor {
17
17
  prefix: string | null;
18
18
  suffix: string | null;
19
19
  start: number | null;
20
- /** 锚点创建时的已发布版本号 */
21
20
  seq: number | null;
22
21
  }
23
22
  export interface CommentItem {
@@ -25,18 +24,15 @@ export interface CommentItem {
25
24
  pageId: string;
26
25
  parentId: string | null;
27
26
  body: string;
28
- /** 行内评论的锚点;页级评论为 null */
29
27
  anchor?: CommentAnchor | null;
30
28
  authorId: string | null;
31
29
  authorName: string;
32
30
  deleted: boolean;
33
31
  canDelete: boolean;
34
32
  canEdit?: boolean;
35
- /** 讨论是否已解决(仅顶层评论有意义) */
36
33
  resolved: boolean;
37
34
  resolvedByName: string | null;
38
35
  resolvedAt: string | null;
39
- /** 当前用户是否可标记/取消解决 */
40
36
  canResolve: boolean;
41
37
  createdAt: string;
42
38
  updatedAt: string;
@@ -1,11 +1,18 @@
1
- /** 纯数据协议;服务端同名文件必须保持一致,由契约测试检查。 */
1
+ export type VideoProvider = 'youtube' | 'bilibili' | 'vimeo' | 'youku' | 'tencent' | 'acfun';
2
2
  export interface VideoEmbedData {
3
- provider: 'youtube' | 'bilibili';
3
+ provider: VideoProvider;
4
4
  videoId: string;
5
5
  page: number;
6
6
  startSeconds: number;
7
7
  }
8
+ export declare const VIDEO_PROVIDER_LABELS: Record<VideoProvider, string>;
8
9
  export declare function normalizeVideoEmbed(raw: unknown): VideoEmbedData | null;
10
+ export type VideoUrlIssue = 'empty' | 'invalid' | 'unsupported' | 'short-link' | 'missing-id';
11
+ export interface VideoUrlInspection {
12
+ data: VideoEmbedData | null;
13
+ issue: VideoUrlIssue | null;
14
+ }
15
+ export declare function inspectVideoUrl(value: string): VideoUrlInspection;
9
16
  export declare function parseVideoUrl(value: string): VideoEmbedData | null;
10
17
  export declare function videoSourceUrl(data: VideoEmbedData): string;
11
18
  export declare function videoPlayerUrl(data: VideoEmbedData): string;
@@ -1,4 +1,3 @@
1
- /** Neutral workbook wire format. Keep in sync with web/src/shared/workbook.ts. */
2
1
  export interface WorkbookCell {
3
2
  v?: string | number | boolean | null;
4
3
  f?: string;
@@ -50,7 +49,6 @@ export declare const WORKBOOK_LIMITS: {
50
49
  };
51
50
  export declare function isWorkbookDocument(value: unknown): value is WorkbookDocument;
52
51
  export declare function emptyWorkbook(id: string): WorkbookDocument;
53
- /** Validate the complete snapshot without silently dropping supported engine attributes. */
54
52
  export declare function validateWorkbook(raw: unknown): WorkbookDocument;
55
53
  export declare function workbookCellText(cell: WorkbookCell | null | undefined): string;
56
54
  export declare function workbookText(doc: WorkbookDocument): string;
@@ -1,21 +1,2 @@
1
- /**
2
- * 文档节点白名单(**纯数据,无依赖**)。
3
- *
4
- * 为什么单独成文件:白名单需要被三处引用——
5
- * 1) 服务端校验(doc-schema.service.ts)
6
- * 2) 一致性测试(与前端 node-catalog 交叉核对)
7
- * 3) 导出序列化器的防漂移测试
8
- * 若把它放在 doc-schema.service.ts 里,测试导入会连带加载 Nest 依赖,
9
- * 导致 spec 在测试环境下加载失败(实测:整个测试文件被静默跳过)。
10
- *
11
- * 因此这里只放数据,不放逻辑,任何一方都能安全引用。
12
- *
13
- * 新增节点时必须同时更新:
14
- * 1) 本文件
15
- * 2) 前端 web/src/shared/node-catalog.ts
16
- * 3) 导出序列化器 markdown-serializer.ts 的处理分支
17
- * 三处一致性由测试自动核对(读取真实文件,而非维护测试内副本)。
18
- */
19
1
  export declare const ALLOWED_NODES: Set<string>;
20
- /** 允许的标记类型。 */
21
2
  export declare const ALLOWED_MARKS: Set<string>;
@@ -1,4 +1,3 @@
1
- /** Kept independent of Nest and of the editing engine: usable by validation and export. */
2
1
  interface JsonNode {
3
2
  type?: string;
4
3
  attrs?: Record<string, unknown>;
@@ -15,7 +14,6 @@ interface Topic {
15
14
  attachmentId?: string;
16
15
  }
17
16
  export declare function validateMindMap(node: JsonNode): void;
18
- /** Same deterministic tree projection as the reader; orphan/cycle conflicts never discard text. */
19
17
  export declare function mindMapOutline(node: JsonNode): Array<Topic & {
20
18
  depth: number;
21
19
  }>;
@@ -1,2 +1 @@
1
- /** 协同握手与正文写入共用,不依赖 Nest 服务。 */
2
1
  export declare const CONTENT_SCHEMA_VERSION = 10;
@@ -1,49 +1,15 @@
1
1
  import { ALLOWED_NODES } from './doc-node-whitelist.js';
2
2
  export { ALLOWED_NODES };
3
- /**
4
- * 文档 schema 校验与派生提取(M0 定稿,见实施计划 4.1)。
5
- *
6
- * 职责:
7
- * 1) 校验客户端提交的 ProseMirror JSON 是否只含白名单节点/标记 → 否则**拒绝写入**,
8
- * 避免旧客户端遇到未知节点的文档时覆盖成空(对齐主方案第 3 节)。
9
- * 2) 提取派生数据:正文引用的页面 ID(用于反向链接)、附件 ID。
10
- *
11
- * schemaVersion 升级策略:新增节点或旧客户端无法保留的持久属性时提升 SCHEMA_VERSION,并在此登记。
12
- * 旧版本客户端读到更高版本文档时由前端拒编辑(本服务不影响读取)。
13
- */
14
- /**
15
- * 正文格式版本。
16
- *
17
- * 变更记录:
18
- * - 1:M0 初始版本(段落/标题/列表/引用/代码/表格/图片/页面引用/提及/公式)
19
- * - 2:新增 `fileAttachment`(视频或文件卡片)与 `callout`(高亮块)
20
- * - 3:新增块级格式属性 `textAlign`/`indent`(段落与标题)与行内样式标记
21
- * `textStyle`(文字颜色)/`highlight`(背景高亮);同时补上此前遗漏的
22
- * `paragraph` 属性条目与表格 `colwidth`(列宽)
23
- * - 4:新增分栏节点 `columns`/`column`(等宽,无属性)
24
- * - 5:新增平台视频节点 `videoEmbed`
25
- * - 6:新增结构化思维导图 `mindMap` / `mindMapTopic`
26
- * - 7:导图描述、备注、关系节点、受限主题样式与图片引用;标题支持行内标记
27
- * - 8:思维导图分支方向 side,防止旧编辑器重写时丢失左右布局
28
- * - 9:高亮块图标与独立文字、边框、填充颜色
29
- * - 10:独立工作簿根格式 workbook(不进入 ProseMirror)
30
- *
31
- * 提升它的意义:客户端读到**更高版本**的文档时可以选择拒编辑(而非静默丢内容),
32
- * 因此只要引入了新的节点类型就必须登记在此,不能只改白名单。
33
- */
34
3
  export declare const SCHEMA_VERSION = 10;
35
4
  export interface ValidatedDoc {
36
5
  doc: Record<string, unknown>;
37
6
  schemaVersion: number;
38
7
  linkTargetIds: string[];
39
8
  attachmentIds: string[];
40
- /** 正文中 @ 提及的用户 ID(去重),用于通知扇出 */
41
9
  mentionUserIds: string[];
42
10
  }
43
11
  export declare class ContentSchemaService {
44
12
  validate(raw: unknown, schemaVersion: number): ValidatedDoc;
45
- /** 仅允许 http/https/mailto 与站内相对链接,阻断 javascript: 等危险协议。 */
46
13
  private isSafeHref;
47
- /** 纯文本投影:供 M0 搜索与摘要使用(不含 HTML,避免 XSS 面)。 */
48
14
  extractPlainText(doc: unknown): string;
49
15
  }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "wendang",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "文档、工作表和思维导图的内容协议与校验核心。",
5
5
  "type": "module",
6
- "license": "UNLICENSED",
6
+ "license": "SEE LICENSE IN LICENSE",
7
7
  "main": "./dist/cjs/index.js",
8
8
  "module": "./dist/esm/index.js",
9
9
  "types": "./dist/types/index.d.ts",
@@ -74,6 +74,11 @@
74
74
  "engines": {
75
75
  "node": ">=20.19"
76
76
  },
77
+ "scripts": {
78
+ "build": "node ../../scripts/build-core.mjs",
79
+ "dev": "node ../../scripts/build-core.mjs --watch",
80
+ "typecheck": "tsc --noEmit"
81
+ },
77
82
  "publishConfig": {
78
83
  "access": "public",
79
84
  "registry": "https://registry.npmjs.org/"
@@ -84,13 +89,8 @@
84
89
  "url": "git+https://gitee.com/ztes/wendang.git",
85
90
  "directory": "packages/core"
86
91
  },
87
- "homepage": "https://gitee.com/ztes/wendang",
92
+ "homepage": "https://doc.yjmf.com",
88
93
  "bugs": {
89
94
  "url": "https://gitee.com/ztes/wendang/issues"
90
- },
91
- "scripts": {
92
- "build": "node ../../scripts/build-core.mjs",
93
- "dev": "node ../../scripts/build-core.mjs --watch",
94
- "typecheck": "tsc --noEmit"
95
95
  }
96
- }
96
+ }