remnote-bridge 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.
Files changed (135) hide show
  1. package/dist/cli/commands/connect.d.ts +12 -0
  2. package/dist/cli/commands/connect.js +124 -0
  3. package/dist/cli/commands/disconnect.d.ts +11 -0
  4. package/dist/cli/commands/disconnect.js +100 -0
  5. package/dist/cli/commands/edit-rem.d.ts +13 -0
  6. package/dist/cli/commands/edit-rem.js +83 -0
  7. package/dist/cli/commands/edit-tree.d.ts +14 -0
  8. package/dist/cli/commands/edit-tree.js +67 -0
  9. package/dist/cli/commands/health.d.ts +12 -0
  10. package/dist/cli/commands/health.js +100 -0
  11. package/dist/cli/commands/install-skill.d.ts +6 -0
  12. package/dist/cli/commands/install-skill.js +39 -0
  13. package/dist/cli/commands/read-context.d.ts +20 -0
  14. package/dist/cli/commands/read-context.js +77 -0
  15. package/dist/cli/commands/read-globe.d.ts +16 -0
  16. package/dist/cli/commands/read-globe.js +60 -0
  17. package/dist/cli/commands/read-rem.d.ts +16 -0
  18. package/dist/cli/commands/read-rem.js +80 -0
  19. package/dist/cli/commands/read-tree.d.ts +17 -0
  20. package/dist/cli/commands/read-tree.js +85 -0
  21. package/dist/cli/commands/search.d.ts +12 -0
  22. package/dist/cli/commands/search.js +65 -0
  23. package/dist/cli/config.d.ts +55 -0
  24. package/dist/cli/config.js +139 -0
  25. package/dist/cli/daemon/daemon.d.ts +11 -0
  26. package/dist/cli/daemon/daemon.js +186 -0
  27. package/dist/cli/daemon/dev-server.d.ts +26 -0
  28. package/dist/cli/daemon/dev-server.js +81 -0
  29. package/dist/cli/daemon/pid.d.ts +34 -0
  30. package/dist/cli/daemon/pid.js +67 -0
  31. package/dist/cli/daemon/send-request.d.ts +24 -0
  32. package/dist/cli/daemon/send-request.js +92 -0
  33. package/dist/cli/handlers/context-read-handler.d.ts +18 -0
  34. package/dist/cli/handlers/context-read-handler.js +24 -0
  35. package/dist/cli/handlers/edit-handler.d.ts +30 -0
  36. package/dist/cli/handlers/edit-handler.js +133 -0
  37. package/dist/cli/handlers/globe-read-handler.d.ts +17 -0
  38. package/dist/cli/handlers/globe-read-handler.js +23 -0
  39. package/dist/cli/handlers/read-handler.d.ts +16 -0
  40. package/dist/cli/handlers/read-handler.js +78 -0
  41. package/dist/cli/handlers/rem-cache.d.ts +19 -0
  42. package/dist/cli/handlers/rem-cache.js +63 -0
  43. package/dist/cli/handlers/tree-edit-handler.d.ts +30 -0
  44. package/dist/cli/handlers/tree-edit-handler.js +188 -0
  45. package/dist/cli/handlers/tree-parser.d.ts +95 -0
  46. package/dist/cli/handlers/tree-parser.js +506 -0
  47. package/dist/cli/handlers/tree-read-handler.d.ts +28 -0
  48. package/dist/cli/handlers/tree-read-handler.js +53 -0
  49. package/dist/cli/main.d.ts +7 -0
  50. package/dist/cli/main.js +300 -0
  51. package/dist/cli/protocol.d.ts +39 -0
  52. package/dist/cli/protocol.js +35 -0
  53. package/dist/cli/server/config-server.d.ts +26 -0
  54. package/dist/cli/server/config-server.js +363 -0
  55. package/dist/cli/server/ws-server.d.ts +68 -0
  56. package/dist/cli/server/ws-server.js +335 -0
  57. package/dist/cli/utils/output.d.ts +11 -0
  58. package/dist/cli/utils/output.js +13 -0
  59. package/dist/mcp/daemon-client.d.ts +31 -0
  60. package/dist/mcp/daemon-client.js +99 -0
  61. package/dist/mcp/index.d.ts +7 -0
  62. package/dist/mcp/index.js +68 -0
  63. package/dist/mcp/instructions.d.ts +1 -0
  64. package/dist/mcp/instructions.js +249 -0
  65. package/dist/mcp/resources/edit-tree-guide.d.ts +1 -0
  66. package/dist/mcp/resources/edit-tree-guide.js +197 -0
  67. package/dist/mcp/resources/error-reference.d.ts +1 -0
  68. package/dist/mcp/resources/error-reference.js +132 -0
  69. package/dist/mcp/resources/outline-format.d.ts +1 -0
  70. package/dist/mcp/resources/outline-format.js +104 -0
  71. package/dist/mcp/resources/rem-object-fields.d.ts +1 -0
  72. package/dist/mcp/resources/rem-object-fields.js +331 -0
  73. package/dist/mcp/resources/separator-flashcard.d.ts +1 -0
  74. package/dist/mcp/resources/separator-flashcard.js +120 -0
  75. package/dist/mcp/tools/edit-tools.d.ts +5 -0
  76. package/dist/mcp/tools/edit-tools.js +47 -0
  77. package/dist/mcp/tools/infra-tools.d.ts +5 -0
  78. package/dist/mcp/tools/infra-tools.js +43 -0
  79. package/dist/mcp/tools/read-tools.d.ts +5 -0
  80. package/dist/mcp/tools/read-tools.js +195 -0
  81. package/dist/mcp/types.d.ts +12 -0
  82. package/dist/mcp/types.js +4 -0
  83. package/docs/instruction/connect.md +158 -0
  84. package/docs/instruction/disconnect.md +146 -0
  85. package/docs/instruction/edit-rem.md +509 -0
  86. package/docs/instruction/edit-tree.md +419 -0
  87. package/docs/instruction/health.md +159 -0
  88. package/docs/instruction/overall.md +751 -0
  89. package/docs/instruction/read-context.md +353 -0
  90. package/docs/instruction/read-globe.md +206 -0
  91. package/docs/instruction/read-rem.md +476 -0
  92. package/docs/instruction/read-tree.md +428 -0
  93. package/docs/instruction/search.md +196 -0
  94. package/package.json +41 -0
  95. package/remnote-plugin/package.json +48 -0
  96. package/remnote-plugin/postcss.config.js +5 -0
  97. package/remnote-plugin/public/bridge-icon.svg +8 -0
  98. package/remnote-plugin/public/manifest.json +22 -0
  99. package/remnote-plugin/src/bridge/message-router.ts +57 -0
  100. package/remnote-plugin/src/bridge/websocket-client.ts +245 -0
  101. package/remnote-plugin/src/index.css +1 -0
  102. package/remnote-plugin/src/services/breadcrumb.ts +26 -0
  103. package/remnote-plugin/src/services/create-rem.ts +59 -0
  104. package/remnote-plugin/src/services/delete-rem.ts +29 -0
  105. package/remnote-plugin/src/services/index.ts +16 -0
  106. package/remnote-plugin/src/services/move-rem.ts +39 -0
  107. package/remnote-plugin/src/services/powerup-filter.ts +31 -0
  108. package/remnote-plugin/src/services/read-context.ts +368 -0
  109. package/remnote-plugin/src/services/read-globe.ts +197 -0
  110. package/remnote-plugin/src/services/read-rem.ts +284 -0
  111. package/remnote-plugin/src/services/read-tree.ts +222 -0
  112. package/remnote-plugin/src/services/rem-builder.ts +124 -0
  113. package/remnote-plugin/src/services/reorder-children.ts +61 -0
  114. package/remnote-plugin/src/services/search.ts +56 -0
  115. package/remnote-plugin/src/services/write-rem-fields.ts +254 -0
  116. package/remnote-plugin/src/settings.ts +12 -0
  117. package/remnote-plugin/src/style.css +45 -0
  118. package/remnote-plugin/src/test-scripts/AGENTS.md +46 -0
  119. package/remnote-plugin/src/test-scripts/test-actions.ts +230 -0
  120. package/remnote-plugin/src/test-scripts/test-powerup-rendering.ts +722 -0
  121. package/remnote-plugin/src/test-scripts/test-rem-type-mapping.ts +283 -0
  122. package/remnote-plugin/src/test-scripts/test-richtext-builder.ts +207 -0
  123. package/remnote-plugin/src/test-scripts/test-richtext-matrix.ts +332 -0
  124. package/remnote-plugin/src/test-scripts/test-richtext-remaining.ts +245 -0
  125. package/remnote-plugin/src/test-scripts/test-rw-fields.ts +399 -0
  126. package/remnote-plugin/src/types.ts +419 -0
  127. package/remnote-plugin/src/utils/elision.ts +45 -0
  128. package/remnote-plugin/src/utils/index.ts +10 -0
  129. package/remnote-plugin/src/utils/tree-serializer.ts +269 -0
  130. package/remnote-plugin/src/widgets/bridge_widget.tsx +170 -0
  131. package/remnote-plugin/src/widgets/index.tsx +82 -0
  132. package/remnote-plugin/tailwind.config.js +7 -0
  133. package/remnote-plugin/tsconfig.json +21 -0
  134. package/remnote-plugin/webpack.config.js +125 -0
  135. package/skill/SKILL.md +428 -0
@@ -0,0 +1,419 @@
1
+ /**
2
+ * RemObject — 本项目定义的 Rem 数据对象
3
+ *
4
+ * 包含 SDK Rem 类所有可获取的信息。
5
+ * 由 plugin services 层组装,通过 WS 传给 CLI,最终交给 AI Agent 消费。
6
+ *
7
+ * 设计原则:
8
+ * - 所有字段均来自 SDK 可获取的数据,无猜测
9
+ * - type 和 isDocument 是两个独立维度,不合并
10
+ * - 不包含任何 Flashcard/Card 相关字段
11
+ * - 关联关系只存 ID,不嵌套对象(避免循环引用和体积膨胀)
12
+ * - 需要参数的方法(如 isCollapsed(portalId))不纳入,留给按需查询
13
+ *
14
+ * Powerup 渲染机制(2026-03-05 实验验证,详见 docs/powerup-rendering/README.md):
15
+ * RemNote 的许多"格式设置"SDK 方法(setFontSize、setHighlightColor、setIsCode 等)
16
+ * 底层并非修改 Rem 自身字段,而是通过三层 Powerup 机制实现:
17
+ * 第 1 层:向 Rem 注入一个系统 Powerup Tag(控制渲染开关)
18
+ * 第 2 层:创建隐藏的 descriptor 子 Rem(携带参数值,如颜色、大小)
19
+ * 第 3 层:Powerup Tag 定义 Rem(模板,定义可接受的参数插槽)
20
+ * 这些隐藏的 Tag 和子 Rem 在 UI 中不可见,但通过 SDK 可读出。
21
+ * 以下每个 [RW] 字段的注释标注了其底层是 Powerup 机制还是纯字段修改
22
+ *
23
+ * 读写标注:
24
+ * - [RW] = 可读可写(SDK 有对应的 setter/add/remove 方法)
25
+ * - [R] = 只读,默认输出(SDK 仅有 getter)
26
+ * - [R-F] = 只读,仅 --full 模式输出(低频 / 细粒度 / 可由其他字段推导)
27
+ *
28
+ * 输出模式(CLI --full 选项):
29
+ * - 默认模式:输出 [RW] + [R] 字段(34 个),覆盖 Agent 常用场景
30
+ * - --full 模式:额外输出 [R-F] 字段(+17 个),用于调试或深度分析
31
+ *
32
+ * 实测标注(2026-03-03 在 RemNote UI 中逐字段截图观察):
33
+ * - ✅ 已实测 = 在真实 RemNote 环境中创建独立 Rem 并截图观察视觉行为
34
+ * - 每个 [RW] 字段的 JSDoc 记录了实际 UI 行为(非仅 PASS/FAIL)
35
+ */
36
+
37
+ // ─── RichText ────────────────────────────────────────────────
38
+
39
+ /**
40
+ * RichText 元素 — 直接透传 SDK 的 RichTextElementInterface
41
+ *
42
+ * 每个元素为 string(纯文本)或带 i 字段的格式化对象:
43
+ * i="m" → 带格式文本(bold/italic/underline/code/highlight/cloze/url)
44
+ * i="q" → Rem 引用(含 _id、aliasId?)
45
+ * i="i" → 图片(url/width/height)
46
+ * i="g" → 全局名称
47
+ * i="fi" → 闪卡图标
48
+ * 以及 audio、latex、plugin、annotation、card delimiter、add icon 等
49
+ */
50
+ export type RichTextElement = string | Record<string, unknown>;
51
+ export type RichText = RichTextElement[];
52
+
53
+ // ─── 枚举 / 字面量类型 ──────────────────────────────────────
54
+
55
+ /** Rem 类型(对应 SDK RemType 枚举) */
56
+ export type RemTypeValue = 'concept' | 'descriptor' | 'portal' | 'default';
57
+
58
+ /** 标题大小 */
59
+ export type FontSize = 'H1' | 'H2' | 'H3';
60
+
61
+ /** 待办状态 */
62
+ export type TodoStatus = 'Finished' | 'Unfinished';
63
+
64
+ /**
65
+ * 高亮颜色(对应 SDK RemColor 枚举,除 undefined=0 外的全部值)
66
+ * 注意:SDK setHighlightColor() 类型签名只列了前 6 种,但实测 Gray/Brown/Pink 也可读出
67
+ */
68
+ export type HighlightColor = 'Red' | 'Orange' | 'Yellow' | 'Green' | 'Blue' | 'Purple' | 'Gray' | 'Brown' | 'Pink';
69
+
70
+ /** Portal 子类型(仅 type === 'portal' 时有值) */
71
+ export type PortalType = 'portal' | 'embedded_queue' | 'scaffold' | 'search_portal';
72
+
73
+ /** 练习方向 */
74
+ export type PracticeDirection = 'forward' | 'backward' | 'both' | 'none';
75
+
76
+ /**
77
+ * 属性类型(对应 SDK PropertyType 枚举)
78
+ * 当 Rem 作为 tag/powerup 的属性时,标识该属性的数据类型
79
+ */
80
+ export type PropertyTypeValue =
81
+ | 'text'
82
+ | 'number'
83
+ | 'date'
84
+ | 'checkbox'
85
+ | 'single_select'
86
+ | 'multi_select'
87
+ | 'url'
88
+ | 'image'
89
+ | 'title'
90
+ | 'definition'
91
+ | 'created_at'
92
+ | 'last_updated'
93
+ | 0; // IMPLICIT_TEXT
94
+
95
+ // ─── RemObject 主体 ─────────────────────────────────────────
96
+
97
+ export interface RemObject {
98
+
99
+ // ══════════════════════════════════════════════════════════
100
+ // 核心标识
101
+ // ══════════════════════════════════════════════════════════
102
+
103
+ /** [R] Rem 唯一 ID。SDK 直接属性 _id */
104
+ id: string;
105
+
106
+ // ══════════════════════════════════════════════════════════
107
+ // 内容
108
+ // ══════════════════════════════════════════════════════════
109
+
110
+ /**
111
+ * [RW] ✅ 正面文本(RichText 数组)。SDK: text / setText()
112
+ * UI 行为:文本内容立即更新显示,无格式副作用
113
+ */
114
+ text: RichText;
115
+ /**
116
+ * [RW] ✅ 背面文本。SDK: backText / setBackText()
117
+ * UI 行为:设值后 Rem 显示为 "正面文本 → 背面文本" 格式(箭头分隔符)
118
+ * 默认 null(无背面);设值即产生闪卡正反面结构
119
+ * 写入语义:null → setBackText([])(SDK 接受 undefined | RichTextInterface,空数组清除背面)
120
+ * 底层机制:纯字段修改,不涉及 Powerup Tag 注入或隐藏子 Rem
121
+ */
122
+ backText: RichText | null;
123
+
124
+ // ══════════════════════════════════════════════════════════
125
+ // 类型系统(两个独立维度)
126
+ // ══════════════════════════════════════════════════════════
127
+
128
+ /**
129
+ * [RW] ✅ Rem 类型。SDK: type, getType() / setType(SetRemType)
130
+ * UI 行为:CONCEPT → 文字变粗体;DESCRIPTOR → 保持正常字重(与默认无视觉差异)
131
+ * SetRemType 不含 PORTAL(6),Portal 只能通过 createPortal() 创建
132
+ * 底层机制:纯字段修改,不涉及 Powerup Tag 注入或隐藏子 Rem
133
+ */
134
+ type: RemTypeValue;
135
+ /**
136
+ * [RW] ✅ 是否作为独立文档页面打开。SDK: isDocument() / setIsDocument()
137
+ * UI 行为:bullet (•) 变为文档页面图标(小方块),Rem 可作为独立页面打开
138
+ * 独立于 type,CONCEPT Rem 可以同时是 Document
139
+ * 底层机制:Powerup — 注入"文档" Tag + 自动创建 [Status];;[Draft] descriptor 子 Rem
140
+ */
141
+ isDocument: boolean;
142
+
143
+ // ══════════════════════════════════════════════════════════
144
+ // 结构
145
+ // ══════════════════════════════════════════════════════════
146
+
147
+ /**
148
+ * [RW] ✅ 父 Rem ID。null 表示顶级。SDK: parent / setParent(parent, position?)
149
+ * UI 行为:Rem 从原位置消失,出现在新父级的子列表中
150
+ */
151
+ parent: string | null;
152
+ /** [R] 子 Rem ID 有序数组。SDK 直接属性 children(修改子的 parent 间接改变) */
153
+ children: string[];
154
+
155
+ // ══════════════════════════════════════════════════════════
156
+ // 格式 / 显示
157
+ // ══════════════════════════════════════════════════════════
158
+
159
+ /**
160
+ * [RW] ✅ 标题大小。SDK: getFontSize() / setFontSize()
161
+ * UI 行为:H1 → 超大粗体;H2 → 大粗体(略小于 H1);H3 → 中粗体
162
+ * 默认 null(普通大小);setFontSize(undefined) 恢复
163
+ * 底层机制:Powerup — 注入"标题" Tag + 创建 [Size];;[H1/H2/H3] descriptor 子 Rem
164
+ */
165
+ fontSize: FontSize | null;
166
+ /**
167
+ * [RW] ✅ 高亮颜色。SDK: getHighlightColor() / setHighlightColor()
168
+ * UI 行为:整行背景变为对应颜色(Red→粉红、Blue→浅蓝),bullet 也着色
169
+ * 默认 null(无高亮)
170
+ * SDK 注意:setHighlightColor() 只能设置颜色,不能清除(null/undefined 均被拒绝)
171
+ * 清除高亮通过 removePowerup('h') 从底层移除高亮 Tag 实现
172
+ * 底层机制:Powerup — 注入"高亮" Tag (TBOrcFVvsbb3nqzaV) + 创建 [Color];;[Red/Blue/...] descriptor 子 Rem
173
+ */
174
+ highlightColor: HighlightColor | null;
175
+
176
+ // ══════════════════════════════════════════════════════════
177
+ // 状态标记 — 所有 is*() 布尔方法
178
+ // ══════════════════════════════════════════════════════════
179
+
180
+ /**
181
+ * [RW] ✅ 是否待办。SDK: isTodo() / setIsTodo()
182
+ * UI 行为:文本前出现空心 checkbox(☐);副作用:todoStatus 自动初始化为 "Unfinished"
183
+ * 底层机制:Powerup — 注入"待办" Tag + 自动创建 [Status];;[Unfinished] descriptor 子 Rem
184
+ */
185
+ isTodo: boolean;
186
+ /**
187
+ * [RW] ✅ 待办完成状态。SDK: getTodoStatus() / setTodoStatus()
188
+ * UI 行为:Finished → checkbox 变蓝色已勾选(☑)+ 文本加删除线
189
+ * 前提:需先 setIsTodo(true),否则无意义
190
+ * 写入语义:null → 跳过(清除 todo 状态应通过 isTodo=false 实现,SDK 不接受 null)
191
+ */
192
+ todoStatus: TodoStatus | null;
193
+ /**
194
+ * [RW] ✅ 是否代码块。SDK: isCode() / setIsCode()
195
+ * UI 行为:Rem 变为代码块容器——等宽字体、灰色背景、块级缩进
196
+ * 底层机制:Powerup — 注入"代码" Tag,无参数子 Rem
197
+ */
198
+ isCode: boolean;
199
+ /**
200
+ * [RW] ✅ 是否引用块。SDK: isQuote() / setIsQuote()
201
+ * UI 行为:左侧出现灰色竖线 + 行背景变浅灰(经典 blockquote 样式)
202
+ * 底层机制:Powerup — 注入"引用" Tag,无参数子 Rem
203
+ */
204
+ isQuote: boolean;
205
+ /**
206
+ * [RW] ✅ 是否列表项。SDK: isListItem() / setIsListItem()
207
+ * UI 行为:bullet (•) 变为数字编号 "1."(有序列表样式)
208
+ * 底层机制:Powerup — 注入"列表项" Tag,无参数子 Rem
209
+ */
210
+ isListItem: boolean;
211
+ /**
212
+ * [RW] ✅ 是否卡片项。SDK: isCardItem() / setIsCardItem()
213
+ * UI:无明显变化。功能:标记 Rem 以卡片样式显示(类似看板布局),
214
+ * 而非默认项目符号列表,在 RemNote 的 Card View 中生效
215
+ * 底层机制:Powerup — 注入"卡片条目" Tag (MultiLineCard),无参数子 Rem
216
+ */
217
+ isCardItem: boolean;
218
+ /** [R] 是否表格。SDK: isTable()(无 setIsTable,只有 setTableFilter) */
219
+ isTable: boolean;
220
+ /**
221
+ * [RW] ✅ 是否 Powerup 插槽。SDK: isSlot() / setIsSlot()
222
+ * UI:bullet 变为方形图标(☐)。功能:标记 Rem 为 Powerup 的数据插槽(slot),
223
+ * Powerup 注册时通过 slots 配置定义,用于存储键值对数据(值为 RichText)。
224
+ * 通过 getPowerupProperty(code, slot) / setPowerupProperty() 读写
225
+ * 底层机制:Powerup — 注入"模板插槽" Tag (vD8KGEg5dkj9bzkRn),无参数子 Rem
226
+ * 重要发现:setIsSlot() 与 setIsProperty() 底层完全相同——注入同一个 Tag,
227
+ * 结果 isSlot=true 且 isProperty=true。两个名字是同一概念的不同视角:
228
+ * "Slot" 用于 Powerup 语境,"Property" 用于表格/Tag 语境
229
+ */
230
+ isSlot: boolean;
231
+ /**
232
+ * [RW] ✅ 是否 Tag 属性(表格列)。SDK: isProperty() / setIsProperty()
233
+ * UI:bullet 变为方形图标(☐,与 isSlot 相同)。功能:标记 Rem 为父级 Tag 的
234
+ * 结构化属性列,可通过 getPropertyType() 指定数据类型(text/number/date/checkbox/
235
+ * single_select/multi_select/url/image 等),通过 getTagPropertyValue(propertyId) /
236
+ * setTagPropertyValue() 读写单元格值。是 RemNote 表格系统的核心机制
237
+ * 底层机制:与 isSlot 完全相同 — 注入同一个"模板插槽" Tag (vD8KGEg5dkj9bzkRn)
238
+ */
239
+ isProperty: boolean;
240
+ /** [R-F] 是否 Powerup。SDK: isPowerup()(写入用 addPowerup/removePowerup,参数化)。Powerup 系统标识 */
241
+ isPowerup: boolean;
242
+ /** [R-F] 是否 Powerup 枚举。SDK: isPowerupEnum()。Powerup 细分类型 */
243
+ isPowerupEnum: boolean;
244
+ /** [R-F] 是否 Powerup 属性。SDK: isPowerupProperty()。Powerup 细分类型 */
245
+ isPowerupProperty: boolean;
246
+ /** [R-F] 是否 Powerup 属性列表项。SDK: isPowerupPropertyListItem()。Powerup 细分类型 */
247
+ isPowerupPropertyListItem: boolean;
248
+ /** [R-F] 是否 Powerup 插槽。SDK: isPowerupSlot()。Powerup 细分类型 */
249
+ isPowerupSlot: boolean;
250
+
251
+ // ══════════════════════════════════════════════════════════
252
+ // Portal 专用
253
+ // ══════════════════════════════════════════════════════════
254
+
255
+ /** [R] Portal 子类型。仅 type === 'portal' 时有值。SDK: getPortalType() */
256
+ portalType: PortalType | null;
257
+ /** [R] Portal 直接包含的 Rem ID 数组。SDK: getPortalDirectlyIncludedRem() */
258
+ portalDirectlyIncludedRem: string[];
259
+
260
+ // ══════════════════════════════════════════════════════════
261
+ // 属性类型(当此 Rem 是 tag/powerup 的属性时)
262
+ // ══════════════════════════════════════════════════════════
263
+
264
+ /** [R] 属性数据类型。SDK: getPropertyType() */
265
+ propertyType: PropertyTypeValue | null;
266
+
267
+ // ══════════════════════════════════════════════════════════
268
+ // 练习设置(Rem 本身的配置,非 Card 操控)
269
+ // ══════════════════════════════════════════════════════════
270
+
271
+ /**
272
+ * [RW] ✅ 是否启用间隔重复练习。SDK: getEnablePractice() / setEnablePractice()
273
+ * UI:无明显变化。功能:为 true 时,RemNote 根据 Rem 的 text/backText 结构
274
+ * 自动生成闪卡并纳入间隔重复调度。setType(CONCEPT) 可能自动置为 true
275
+ * 底层机制:纯字段修改,不涉及 Powerup Tag 注入或隐藏子 Rem
276
+ */
277
+ enablePractice: boolean;
278
+ /**
279
+ * [RW] ✅ 闪卡练习方向。SDK: getPracticeDirection() / setPracticeDirection()
280
+ * UI:无明显变化。功能:控制闪卡生成方向——forward=正面→背面,
281
+ * backward=背面→正面,both=双向生成,none=不生成闪卡
282
+ * 底层机制:纯字段修改,不涉及 Powerup Tag 注入或隐藏子 Rem
283
+ */
284
+ practiceDirection: PracticeDirection;
285
+
286
+ // ══════════════════════════════════════════════════════════
287
+ // 关联 — 直接关系(ID 数组)
288
+ // ══════════════════════════════════════════════════════════
289
+
290
+ /**
291
+ * [RW] ✅ 标签 Rem ID 数组。SDK: getTagRems() / addTag() / removeTag()
292
+ * UI 行为:行右侧出现标签徽章(圆角矩形,显示标签名 + × 删除按钮)
293
+ * setType(CONCEPT) 等操作可能自动添加系统标签
294
+ * 注意:系统 Powerup Tag 会混入此数组(如 setIsCode 注入的"代码" Tag),
295
+ * 可通过 isPowerup 属性区分用户标签和系统 Powerup Tag
296
+ */
297
+ tags: string[];
298
+ /**
299
+ * [RW] ✅ 来源 Rem ID 数组。SDK: getSources() / addSource() / removeSource()
300
+ * UI 行为:Rem 下方出现来源引用子元素(灰色圆角框,显示来源 Rem 名 + ↗ 图标)
301
+ */
302
+ sources: string[];
303
+ /**
304
+ * [R] 别名 Rem ID 数组。SDK: getAliases()
305
+ * 写入接口 getOrCreateAliasWithText(text) 需要文本参数(非 ID),与 RemObject 的 ID 数组形式不匹配。
306
+ * v1 标记为只读,后续可提供独立命令 `add-alias <remId> <text>`
307
+ */
308
+ aliases: string[];
309
+
310
+ // ══════════════════════════════════════════════════════════
311
+ // 关联 — 引用关系(ID 数组)
312
+ // ══════════════════════════════════════════════════════════
313
+
314
+ /** [R] 本 Rem 引用的其他 Rem ID 数组。SDK: remsBeingReferenced() */
315
+ remsBeingReferenced: string[];
316
+ /** [R-F] 本 Rem 深层引用的 Rem ID 数组。SDK: deepRemsBeingReferenced()。可由 remsBeingReferenced 递归获取 */
317
+ deepRemsBeingReferenced: string[];
318
+ /** [R] 引用本 Rem 的 Rem ID 数组(反向链接)。SDK: remsReferencingThis() */
319
+ remsReferencingThis: string[];
320
+
321
+ // ══════════════════════════════════════════════════════════
322
+ // 关联 — 标签体系(ID 数组)
323
+ // ══════════════════════════════════════════════════════════
324
+
325
+ /** [R] 被本 Rem 标记的 Rem ID 数组(本 Rem 作为 tag 时)。SDK: taggedRem() */
326
+ taggedRem: string[];
327
+ /** [R-F] 祖先标签 Rem ID 数组。SDK: ancestorTagRem()。标签继承链,低频 */
328
+ ancestorTagRem: string[];
329
+ /** [R-F] 后代标签 Rem ID 数组。SDK: descendantTagRem()。标签继承链,低频 */
330
+ descendantTagRem: string[];
331
+
332
+ // ══════════════════════════════════════════════════════════
333
+ // 关联 — 层级遍历(ID 数组)
334
+ // ══════════════════════════════════════════════════════════
335
+
336
+ /** [R] 所有后代 Rem ID 数组。SDK: getDescendants() */
337
+ descendants: string[];
338
+ /** [R] 兄弟 Rem ID 数组。SDK: siblingRem() */
339
+ siblingRem: string[];
340
+ /** [R-F] 包含的 Portal 和文档 Rem ID 数组。SDK: portalsAndDocumentsIn()。使用场景有限 */
341
+ portalsAndDocumentsIn: string[];
342
+ /** [R-F] 文档/Portal 中所有 Rem ID 数组。SDK: allRemInDocumentOrPortal()。可能数据量大 */
343
+ allRemInDocumentOrPortal: string[];
344
+ /** [R-F] 文件夹队列中的 Rem ID 数组。SDK: allRemInFolderQueue()。场景有限 */
345
+ allRemInFolderQueue: string[];
346
+
347
+ // ══════════════════════════════════════════════════════════
348
+ // 位置 / 统计
349
+ // ══════════════════════════════════════════════════════════
350
+
351
+ /**
352
+ * [RW] ✅ 在兄弟间的位置(0 起始)。SDK: positionAmongstSiblings() / setParent(parent, position)
353
+ * UI 行为:Rem 在父级子列表中的显示位置改变(测试:A→B→C 变为 B→C→A)
354
+ * position 超过实际数量会被钳位到末尾;位置相对于父 Rem 的全部 children
355
+ */
356
+ positionAmongstSiblings: number | null;
357
+ /** [R-F] 搜索中被选次数。SDK: timesSelectedInSearch()。统计数据,低频 */
358
+ timesSelectedInSearch: number;
359
+ /** [R-F] 上次移动时间(毫秒时间戳)。SDK: getLastTimeMovedTo()。过于细粒度 */
360
+ lastTimeMovedTo: number;
361
+ /** [R-F] Schema 版本号。SDK: getSchemaVersion()。内部版本号 */
362
+ schemaVersion: number;
363
+
364
+ // ══════════════════════════════════════════════════════════
365
+ // 队列视图
366
+ // ══════════════════════════════════════════════════════════
367
+
368
+ /** [R-F] 嵌入式队列视图模式。SDK: embeddedQueueViewMode()。场景有限 */
369
+ embeddedQueueViewMode: boolean;
370
+
371
+ // ══════════════════════════════════════════════════════════
372
+ // 元数据 / 时间戳
373
+ // ══════════════════════════════════════════════════════════
374
+
375
+ /** [R] 创建时间(毫秒时间戳)。SDK 直接属性 createdAt */
376
+ createdAt: number;
377
+ /** [R] 最后更新时间(毫秒时间戳)。SDK 直接属性 updatedAt */
378
+ updatedAt: number;
379
+ /** [R-F] 本地最后更新时间(毫秒时间戳)。SDK 直接属性 localUpdatedAt。与 updatedAt 重叠 */
380
+ localUpdatedAt: number;
381
+ /** [R-F] 上次练习时间(毫秒时间戳)。SDK: getLastPracticed()。间隔重复内部数据 */
382
+ lastPracticed: number;
383
+ }
384
+
385
+ // ══════════════════════════════════════════════════════════════
386
+ // 以下 SDK 方法需要参数,不纳入 RemObject 静态字段,
387
+ // 留给按需查询接口:
388
+ //
389
+ // [RW] isCollapsed(portalId) → bool / setIsCollapsed(bool, portalId)
390
+ // [R] positionAmongstVisibleSiblings(portalId?) → number
391
+ // [R] visibleSiblingRem(portalId?) → Rem[]
392
+ // [RW] hasPowerup(code) → bool / addPowerup(code) / removePowerup(code)
393
+ // [RW] getPowerupProperty(code, slot) / setPowerupProperty(code, slot, value)
394
+ // [RW] getPowerupPropertyAsRichText(code, slot) / setPowerupProperty(code, slot, value)
395
+ // [R] getPowerupPropertyAsRem(code, slot) → Rem
396
+ // [RW] getTagPropertyValue(propertyId) → RT / setTagPropertyValue(propertyId, value)
397
+ // [R] getTagPropertyAsRem(propertyId) → Rem
398
+ // [RW] getOrCreateAliasWithText(aliasText) → Rem(含副作用,创建+读取)
399
+ //
400
+ // 以下 SDK 方法是纯动作/副作用,不对应任何可读字段:
401
+ // (2026-03-03 在 RemNote UI 中逐个截图观察,初态→终态对比)
402
+ //
403
+ // [W] ✅ remove() — UI:Rem 从文档中完全消失
404
+ // [W] ✅ indent(portal?) — UI:Rem 变为上方兄弟的子级(缩进一层,出现左竖线)
405
+ // [W] ❌ outdent(portal?) — UI:无可见变化(Rem 仍为子级)。可能需要编辑器焦点上下文
406
+ // [W] ✅ merge(remId) — UI:被合并 Rem 消失,目标 Rem 保留原始文本(文本未追加)
407
+ // [W] mergeAndSetAlias(rem) — 未测试(与 merge 类似 + 设别名)
408
+ // [W] ✅ addToPortal(portal) — UI:原始 Rem 保留;Portal 内出现引用副本(紫色左边框)
409
+ // [W] removeFromPortal(portal) — 未测试(addToPortal 的逆操作)
410
+ // [W] ❌ collapse(portal) — UI:无可见变化(子级仍然可见)。可能需要 portalId 或编辑器上下文
411
+ // [W] ❌ expand(portal, recurse) — UI:无可见变化(collapse 未生效故 expand 也无效果)
412
+ // [W] setTableFilter(filter) — 未测试(需要表格 Rem)
413
+ // [W] openRemAsPage() — 未测试(会导致页面导航)
414
+ // [W] openRemInContext() — 未测试(会导致页面导航)
415
+ // [W] scrollToReaderHighlight() — 未测试(需要 Reader 上下文)
416
+ // [W] copyReferenceToClipboard() — 未测试(剪贴板操作,无 UI 变化)
417
+ // [W] copyPortalReferenceToClipboard() — 未测试(同上)
418
+ // [W] copyTagReferenceToClipboard() — 未测试(同上)
419
+ // ══════════════════════════════════════════════════════════════
@@ -0,0 +1,45 @@
1
+ /**
2
+ * elision.ts — 省略引擎辅助函数
3
+ *
4
+ * 纯函数、无副作用、不依赖其他层。
5
+ * 被 read-tree、read-globe、read-context 共享。
6
+ */
7
+
8
+ /** 省略描述信息 */
9
+ export interface ElidedInfo {
10
+ count: number;
11
+ parentId: string;
12
+ rangeFrom: number;
13
+ rangeTo: number;
14
+ totalSiblings: number;
15
+ }
16
+
17
+ /**
18
+ * 裁剪 siblings:当 children 数量超过 maxSiblings 时,
19
+ * 保留前 70% + 后 30%,中间省略。
20
+ *
21
+ * @returns visible 索引数组 + 可选的省略信息
22
+ */
23
+ export function sliceSiblings(
24
+ totalCount: number,
25
+ maxSiblings: number,
26
+ parentId: string,
27
+ ): { visibleIndices: { head: number; tail: number } | null; elided: ElidedInfo | null } {
28
+ if (totalCount <= maxSiblings) {
29
+ return { visibleIndices: null, elided: null };
30
+ }
31
+
32
+ const head = Math.ceil(maxSiblings * 0.7);
33
+ const tail = Math.floor(maxSiblings * 0.3);
34
+
35
+ return {
36
+ visibleIndices: { head, tail },
37
+ elided: {
38
+ count: totalCount - head - tail,
39
+ parentId,
40
+ rangeFrom: head,
41
+ rangeTo: totalCount - tail - 1,
42
+ totalSiblings: totalCount,
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Utils 层 — 共享辅助工具(无状态纯函数)
3
+ *
4
+ * 被 services 层单向依赖,不依赖任何其他业务层。
5
+ *
6
+ * 待实现:
7
+ * - rich-text.ts → 富文本解析(extractText 等)
8
+ * - content-renderer.ts → 内容渲染(Markdown / Structured)
9
+ * - rem-classifier.ts → Rem 分类、别名、父级解析
10
+ */