remnote-bridge 0.1.1 → 0.1.3

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.
@@ -25,21 +25,21 @@ RemObject 是本项目对 RemNote Rem 的标准化表示,包含 51 个字段
25
25
 
26
26
  | 字段 | 类型 | 权限 | 说明 |
27
27
  |------|------|:----:|------|
28
- | \\\`text\\\` | \\\`RichText\\\` | RW | 正面文本(RichText 数组) |
29
- | \\\`backText\\\` | \\\`RichText \\| null\\\` | RW | 背面文本。null=无背面;设值即产生闪卡正反面结构 |
28
+ | \\\`text\\\` | \\\`RichText\\\` | RW | 正面文本(RichText 数组)。UI:文本内容立即更新显示 |
29
+ | \\\`backText\\\` | \\\`RichText \\| null\\\` | RW | 背面文本。null=无背面;设值即产生闪卡正反面结构。UI:显示为"正面 → 背面"箭头分隔格式 |
30
30
 
31
31
  ## 类型系统
32
32
 
33
33
  | 字段 | 类型 | 权限 | 说明 |
34
34
  |------|------|:----:|------|
35
35
  | \\\`type\\\` | \\\`RemTypeValue\\\` | RW | \\\`concept\\\` / \\\`descriptor\\\` / \\\`default\\\` / \\\`portal\\\` |
36
- | \\\`isDocument\\\` | \\\`boolean\\\` | RW | 是否作为独立文档页面。独立于 type |
36
+ | \\\`isDocument\\\` | \\\`boolean\\\` | RW | 是否作为独立文档页面。独立于 type。UI:bullet(•)变为文档图标,可独立打开 |
37
37
 
38
38
  ## 结构
39
39
 
40
40
  | 字段 | 类型 | 权限 | 说明 |
41
41
  |------|------|:----:|------|
42
- | \\\`parent\\\` | \\\`string \\| null\\\` | RW | 父 Rem ID。null=顶级 |
42
+ | \\\`parent\\\` | \\\`string \\| null\\\` | RW | 父 Rem ID。null=顶级。UI:Rem 从原位置消失,出现在新父级下 |
43
43
  | \\\`children\\\` | \\\`string[]\\\` | R | 子 Rem ID 有序数组 |
44
44
 
45
45
  ## 格式 / 显示
@@ -47,21 +47,21 @@ RemObject 是本项目对 RemNote Rem 的标准化表示,包含 51 个字段
47
47
  | 字段 | 类型 | 权限 | 说明 |
48
48
  |------|------|:----:|------|
49
49
  | \\\`fontSize\\\` | \\\`FontSize \\| null\\\` | RW | 标题大小:\\\`H1\\\` / \\\`H2\\\` / \\\`H3\\\`。null=普通 |
50
- | \\\`highlightColor\\\` | \\\`HighlightColor \\| null\\\` | RW | 高亮颜色(9 种)。null=无高亮 |
50
+ | \\\`highlightColor\\\` | \\\`HighlightColor \\| null\\\` | RW | 高亮颜色(9 种)。null=无高亮。UI:整行背景变为对应颜色,bullet 也着色 |
51
51
 
52
52
  ## 状态标记
53
53
 
54
54
  | 字段 | 类型 | 权限 | 说明 |
55
55
  |------|------|:----:|------|
56
- | \\\`isTodo\\\` | \\\`boolean\\\` | RW | 是否待办。设为 true 时自动初始化 todoStatus |
57
- | \\\`todoStatus\\\` | \\\`TodoStatus \\| null\\\` | RW | \\\`Finished\\\` / \\\`Unfinished\\\`。需先 isTodo=true |
58
- | \\\`isCode\\\` | \\\`boolean\\\` | RW | 是否代码块 |
59
- | \\\`isQuote\\\` | \\\`boolean\\\` | RW | 是否引用块 |
60
- | \\\`isListItem\\\` | \\\`boolean\\\` | RW | 是否列表项(有序列表样式) |
61
- | \\\`isCardItem\\\` | \\\`boolean\\\` | RW | 是否卡片项(多行答案行标记) |
56
+ | \\\`isTodo\\\` | \\\`boolean\\\` | RW | 是否待办。设为 true 时自动初始化 todoStatus。UI:文本前出现空心 checkbox(☐) |
57
+ | \\\`todoStatus\\\` | \\\`TodoStatus \\| null\\\` | RW | \\\`Finished\\\` / \\\`Unfinished\\\`。需先 isTodo=true。UI:Finished=蓝色已勾选(☑)+文本删除线 |
58
+ | \\\`isCode\\\` | \\\`boolean\\\` | RW | 是否代码块。UI:等宽字体、灰色背景、块级缩进 |
59
+ | \\\`isQuote\\\` | \\\`boolean\\\` | RW | 是否引用块。UI:左侧灰色竖线+背景浅灰(blockquote 样式) |
60
+ | \\\`isListItem\\\` | \\\`boolean\\\` | RW | 是否列表项。UI:bullet(•)变为数字编号"1."(有序列表) |
61
+ | \\\`isCardItem\\\` | \\\`boolean\\\` | RW | 是否卡片项(多行答案行标记)。UI:无明显变化,在 Card View 中生效 |
62
62
  | \\\`isTable\\\` | \\\`boolean\\\` | R | 是否表格(只读) |
63
- | \\\`isSlot\\\` | \\\`boolean\\\` | RW | 是否 Powerup 插槽。与 isProperty 底层相同 |
64
- | \\\`isProperty\\\` | \\\`boolean\\\` | RW | 是否 Tag 属性(表格列)。与 isSlot 底层相同 |
63
+ | \\\`isSlot\\\` | \\\`boolean\\\` | RW | 是否 Powerup 插槽。与 isProperty 底层相同。UI:bullet(•)变为方形图标(☐) |
64
+ | \\\`isProperty\\\` | \\\`boolean\\\` | RW | 是否 Tag 属性(表格列)。与 isSlot 底层相同。UI:bullet(•)变为方形图标(☐) |
65
65
 
66
66
  ## Powerup 系统标识
67
67
 
@@ -90,15 +90,15 @@ RemObject 是本项目对 RemNote Rem 的标准化表示,包含 51 个字段
90
90
 
91
91
  | 字段 | 类型 | 权限 | 说明 |
92
92
  |------|------|:----:|------|
93
- | \\\`enablePractice\\\` | \\\`boolean\\\` | RW | 是否启用间隔重复练习 |
94
- | \\\`practiceDirection\\\` | \\\`PracticeDirection\\\` | RW | 练习方向:\\\`forward\\\` / \\\`backward\\\` / \\\`both\\\` / \\\`none\\\` |
93
+ | \\\`enablePractice\\\` | \\\`boolean\\\` | RW | 是否启用间隔重复练习。UI:无明显变化 |
94
+ | \\\`practiceDirection\\\` | \\\`PracticeDirection\\\` | RW | 练习方向:\\\`forward\\\` / \\\`backward\\\` / \\\`both\\\` / \\\`none\\\`。UI:无明显变化 |
95
95
 
96
96
  ## 关联 — 直接关系
97
97
 
98
98
  | 字段 | 类型 | 权限 | 说明 |
99
99
  |------|------|:----:|------|
100
- | \\\`tags\\\` | \\\`string[]\\\` | RW | 标签 Rem ID 数组。写入时使用 diff 机制(add/remove |
101
- | \\\`sources\\\` | \\\`string[]\\\` | RW | 来源 Rem ID 数组。写入时使用 diff 机制 |
100
+ | \\\`tags\\\` | \\\`string[]\\\` | RW | 标签 Rem ID 数组。写入时使用 diff 机制(add/remove)。UI:行右侧出现标签徽章 |
101
+ | \\\`sources\\\` | \\\`string[]\\\` | RW | 来源 Rem ID 数组。写入时使用 diff 机制。UI:Rem 下方出现灰色来源引用框 |
102
102
  | \\\`aliases\\\` | \\\`string[]\\\` | R | 别名 Rem ID 数组 |
103
103
 
104
104
  ## 关联 — 引用关系
@@ -131,7 +131,7 @@ RemObject 是本项目对 RemNote Rem 的标准化表示,包含 51 个字段
131
131
 
132
132
  | 字段 | 类型 | 权限 | 说明 |
133
133
  |------|------|:----:|------|
134
- | \\\`positionAmongstSiblings\\\` | \\\`number \\| null\\\` | RW | 在兄弟间的位置(0 起始) |
134
+ | \\\`positionAmongstSiblings\\\` | \\\`number \\| null\\\` | RW | 在兄弟间的位置(0 起始)。UI:Rem 在父级子列表中的显示位置改变 |
135
135
  | \\\`timesSelectedInSearch\\\` | \\\`number\\\` | R-F | 搜索中被选次数 |
136
136
  | \\\`lastTimeMovedTo\\\` | \\\`number\\\` | R-F | 上次移动时间(毫秒时间戳) |
137
137
  | \\\`schemaVersion\\\` | \\\`number\\\` | R-F | Schema 版本号 |
@@ -303,29 +303,71 @@ RemObject 中的 \\\`text\\\` 和 \\\`backText\\\` 字段使用 RichText 格式
303
303
 
304
304
  ### 元素类型
305
305
 
306
- | \\\`i\\\` 值 | 含义 | 核心字段 |
307
- |--------|------|----------|
308
- | (纯 string) | 纯文本片段 | — |
309
- | \\\`"m"\\\` | 带格式文本 | \\\`text\\\` + 格式标记 |
310
- | \\\`"q"\\\` | Rem 引用 | \\\`_id\\\`(被引用 Rem ID) |
311
- | \\\`"i"\\\` | 图片 | \\\`url\\\`, \\\`width\\\`, \\\`height\\\` |
312
- | \\\`"x"\\\` | LaTeX | \\\`text\\\` |
313
- | \\\`"a"\\\` | 音频 | \\\`url\\\` |
306
+ | \\\`i\\\` 值 | 含义 | 必填字段 | 可选字段 |
307
+ |--------|------|----------|----------|
308
+ | (纯 string) | 纯文本片段 | — | — |
309
+ | \\\`"m"\\\` | 带格式文本 | \\\`text\\\` | 格式标记(见下表) |
310
+ | \\\`"q"\\\` | Rem 引用 | \\\`_id\\\` | \\\`content\\\`, \\\`showFullName\\\`, \\\`aliasId\\\` |
311
+ | \\\`"i"\\\` | 图片 | \\\`url\\\` | \\\`width\\\`, \\\`height\\\`, \\\`percent\\\`(25/50/100) |
312
+ | \\\`"x"\\\` | LaTeX | \\\`text\\\` | \\\`block\\\`(true=块级公式) |
313
+ | \\\`"a"\\\` | 音频/视频 | \\\`url\\\`, \\\`onlyAudio\\\`(**必填**) | \\\`width\\\`, \\\`height\\\` |
314
+ | \\\`"s"\\\` | 卡片分隔符 | — | \\\`delimiterCharacterForSerialization\\\` |
314
315
 
315
- ### 行内格式标记(\\\`i:"m"\\\` 元素内)
316
+ **注意**:\\\`i:"a"\\\` 的 \\\`onlyAudio\\\` 是**必填**字段(\\\`true\\\`=音频,\\\`false\\\`=视频),缺少会导致 SDK 拒绝写入。
317
+
318
+ ### 格式标记(主要用于 \\\`i:"m"\\\`,但 \\\`i:"q"\\\` 等元素也支持)
316
319
 
317
320
  | 字段 | 类型 | 含义 |
318
321
  |------|------|------|
319
322
  | \\\`b\\\` | \\\`true\\\` | 加粗 |
320
- | \\\`l\\\` | \\\`true\\\` | 斜体 |
323
+ | \\\`l\\\` | \\\`true\\\` | 斜体(小写字母 L,不是 I) |
321
324
  | \\\`u\\\` | \\\`true\\\` | 下划线 |
322
- | \\\`h\\\` | \\\`number\\\` | 高亮颜色(1=红, 2=橙, 3=黄, 4=绿, 5=蓝, 6=紫) |
323
- | \\\`tc\\\` | \\\`number\\\` | 文字颜色 |
324
- | \\\`code\\\` | \\\`true\\\` | 行内代码 |
325
+ | \\\`h\\\` | \\\`number\\\` | 高亮颜色(RemColor 枚举:1=Red, 2=Orange, 3=Yellow, 4=Green, 5=Purple, 6=Blue, 7=Gray, 8=Brown, 9=Pink) |
326
+ | \\\`tc\\\` | \\\`number\\\` | 文字颜色(同 RemColor 枚举) |
327
+ | \\\`q\\\` | \\\`true\\\` | 行内代码(红色等宽样式) |
328
+ | \\\`code\\\` | \\\`true\\\` | 代码块(带语言标签和复制按钮) |
329
+ | \\\`language\\\` | \\\`string\\\` | 代码块语言(如 \\\`"javascript"\\\`、\\\`"python"\\\`) |
325
330
  | \\\`cId\\\` | \\\`string\\\` | 完形填空 ID |
326
- | \\\`iUrl\\\` | \\\`string\\\` | 外部链接 URL |
331
+ | \\\`hiddenCloze\\\` | \\\`true\\\` | 完形填空隐藏状态 |
332
+ | \\\`revealedCloze\\\` | \\\`true\\\` | 完形填空已揭示状态 |
333
+ | \\\`iUrl\\\` | \\\`string\\\` | 外部超链接 URL(\\\`url\\\` 字段已废弃,必须用 \\\`iUrl\\\`) |
334
+ | \\\`qId\\\` | \\\`string\\\` | 行内引用链接的 Rem ID |
335
+
336
+ ### RemColor 颜色枚举(\\\`h\\\` 和 \\\`tc\\\` 共用)
337
+
338
+ | 值 | 颜色 | 值 | 颜色 | 值 | 颜色 |
339
+ |:---|:-----|:---|:-----|:---|:-----|
340
+ | 0 | 无颜色/默认 | 4 | Green | 7 | Gray |
341
+ | 1 | Red | 5 | Purple | 8 | Brown |
342
+ | 2 | Orange | 6 | Blue | 9 | Pink |
343
+ | 3 | Yellow | — | — | — | — |
344
+
345
+ ### 常用构造示例
346
+
347
+ 以下为 key 字母序排列的格式:
348
+
349
+ \\\`\\\`\\\`jsonc
350
+ { "b": true, "i": "m", "text": "粗体" } // 粗体(key 序:b < i < text)
351
+ { "i": "m", "q": true, "text": "code" } // 行内代码
352
+ { "i": "m", "iUrl": "https://...", "text": "链接" } // 超链接(iUrl 不是 url!)
353
+ { "b": true, "h": 1, "i": "m", "text": "重点" } // 粗体+红色高亮(h 是数字 0-9)
354
+ { "cId": "c1", "i": "m", "text": "答案" } // 完形填空
355
+ { "_id": "remId", "b": true, "i": "q" } // Rem 引用加粗(_id 排最前)
356
+ { "i": "x", "text": "E = mc^2" } // LaTeX
357
+ { "i": "a", "onlyAudio": false, "url": "..." } // 视频(onlyAudio 必填!)
358
+ { "i": "a", "onlyAudio": true, "url": "..." } // 音频
359
+ \\\`\\\`\\\`
360
+
361
+ > 在 RemObject 格式化 JSON 中,数组内对象会展开为多行(每个 key 一行)。构造 \\\`edit_rem\\\` 的 oldStr/newStr 时必须使用实际的多行格式。
362
+
363
+ ### highlightColor(Rem 级别)vs h(RichText 行内)
364
+
365
+ - \\\`highlightColor\\\`:RemObject 顶层字段,值为字符串(\\\`"Red"\\\`, \\\`"Blue"\\\` 等)或 \\\`null\\\`,整行背景色
366
+ - \\\`h\\\`:RichText 元素内格式标记,值为数字 0-9(RemColor 枚举),行内文字片段高亮
367
+
368
+ 两者完全独立,互不影响。
327
369
 
328
370
  ### 序列化确定性
329
371
 
330
- RichText 对象元素内部按 **key 字母序排列**(Plugin 端 \\\`sortRichTextKeys()\\\` 处理),确保同一内容的序列化 JSON 始终一致。这对 \\\`edit_rem\\\` 的 str_replace 和乐观并发检测至关重要。
372
+ RichText 对象元素内部按 **key 字母序排列**(Plugin 端 \\\`sortRichTextKeys()\\\` 处理),确保同一内容的序列化 JSON 始终一致。\\\`_id\\\` 中的 \\\`_\\\`(U+005F)排在所有小写字母(\\\`a\\\`=U+0061)之前,所以 \\\`_id\\\` 总是第一个 key。这对 \\\`edit_rem\\\` 的 str_replace 和乐观并发检测至关重要。
331
373
  `;
@@ -1,30 +1,32 @@
1
1
  export const SEPARATOR_FLASHCARD_CONTENT = `
2
2
  # Separator & Flashcard Reference
3
3
 
4
- 用户在 RemNote 中输入的分隔符决定了 Rem typebackText 和默认 practiceDirection。理解分隔符是理解用户创建闪卡意图的关键。
4
+ **重要**:分隔符(\\\`::\\\`、\\\`;;\\\`、\\\`>>\\\` 等)是 RemNote 编辑器的输入语法,**CLI 不使用分隔符**。通过 CLI 创建/修改闪卡,操作的是 \\\`type\\\`、\\\`backText\\\`、\\\`practiceDirection\\\` 字段和大纲箭头(\\\`→←↔↓↑↕\\\`)。本参考表用于理解用户意图——当用户提到分隔符时,映射到对应的 CLI 操作。
5
5
 
6
6
  ---
7
7
 
8
- ## 分隔符完整映射表
9
-
10
- | 分隔符 | 用户输入示例 | type | backText | 默认 practiceDirection | 用途 |
11
- |:-------|:-------------|:-----|:---------|:-----------------------|:-----|
12
- | (无) | \\\`普通文本\\\` | \\\`default\\\` | \\\`null\\\` | | 无闪卡行为 |
13
- | \\\`::\\\` | \\\`概念 :: 定义\\\` | \\\`concept\\\` | 后半部分 | \\\`both\\\` | 概念定义(CDF 框架) |
14
- | \\\`;;\\\` | \\\`属性 ;; 值\\\` | \\\`descriptor\\\` | 后半部分 | \\\`forward\\\` | 描述属性(CDF 框架) |
15
- | \\\`>>\\\` | \\\`问题 >> 答案\\\` | \\\`default\\\` | 后半部分 | \\\`forward\\\` | 正向问答 |
16
- | \\\`<<\\\` | \\\`问题 << 答案\\\` | \\\`default\\\` | 后半部分 | \\\`backward\\\` | 反向问答 |
17
- | \\\`<>\\\` | \\\`问题 <> 答案\\\` | \\\`default\\\` | 后半部分 | \\\`both\\\` | 双向问答 |
18
- | \\\`>>>\\\` | \\\`问题 >>>\\\` | \\\`default\\\` | \\\`null\\\` | \\\`forward\\\` | 多行答案(子 Rem 为答案) |
19
- | \\\`::>\\\` | \\\`概念 ::>\\\` | \\\`concept\\\` | \\\`null\\\` | \\\`both\\\` | 概念型多行答案 |
20
- | \\\`;;>\\\` | \\\`属性 ;;>\\\` | \\\`descriptor\\\` | \\\`null\\\` | \\\`forward\\\` | 描述型多行答案 |
21
- | \\\`{{}}\\\` | \\\`The {{capital}} of France is Paris\\\` | \\\`default\\\` | \\\`null\\\` | \\\`forward\\\` | 完形填空(Cloze) |
8
+ ## 1. 用户意图映射:编辑器分隔符 → CLI 操作
9
+
10
+ 当用户提到以下分隔符时,对应的 Rem 属性为:
11
+
12
+ | 编辑器分隔符 | 用户输入示例 | type | backText | 默认 practiceDirection |
13
+ |:-------------|:-------------|:-----|:---------|:-----------------------|
14
+ | (无) | \\\`普通文本\\\` | \\\`default\\\` | \\\`null\\\` | |
15
+ | \\\`::\\\` | \\\`概念 :: 定义\\\` | \\\`concept\\\` | 后半部分 | \\\`both\\\` |
16
+ | \\\`;;\\\` | \\\`属性 ;; 值\\\` | \\\`descriptor\\\` | 后半部分 | \\\`forward\\\` |
17
+ | \\\`>>\\\` | \\\`问题 >> 答案\\\` | \\\`default\\\` | 后半部分 | \\\`forward\\\` |
18
+ | \\\`<<\\\` | \\\`问题 << 答案\\\` | \\\`default\\\` | 后半部分 | \\\`backward\\\` |
19
+ | \\\`<>\\\` | \\\`问题 <> 答案\\\` | \\\`default\\\` | 后半部分 | \\\`both\\\` |
20
+ | \\\`>>>\\\` | \\\`问题 >>>\\\` | \\\`default\\\` | \\\`null\\\` | \\\`forward\\\`(多行) |
21
+ | \\\`::>\\\` | \\\`概念 ::>\\\` | \\\`concept\\\` | \\\`null\\\` | \\\`both\\\`(多行) |
22
+ | \\\`;;>\\\` | \\\`属性 ;;>\\\` | \\\`descriptor\\\` | \\\`null\\\` | \\\`forward\\\`(多行) |
23
+ | \\\`{{}}\\\` | \\\`The {{capital}} of France\\\` | \\\`default\\\` | \\\`null\\\` | \\\`forward\\\`(完形填空) |
22
24
 
23
25
  ---
24
26
 
25
- ## 大纲中的箭头分隔符
27
+ ## 2. CLI 操作方式:大纲箭头
26
28
 
27
- 在 Markdown 大纲(read_tree / edit_tree)中,分隔符被编码为 Unicode 箭头:
29
+ 在 Markdown 大纲(read_tree / edit_tree)中,practiceDirection 编码为 Unicode 箭头:
28
30
 
29
31
  ### 单行箭头(有 backText)
30
32
 
@@ -46,7 +48,7 @@ export const SEPARATOR_FLASHCARD_CONTENT = `
46
48
 
47
49
  ---
48
50
 
49
- ## practiceDirection 取值
51
+ ## 3. practiceDirection 取值
50
52
 
51
53
  | 值 | 含义 | 闪卡生成 |
52
54
  |:---|:-----|:---------|
@@ -57,28 +59,28 @@ export const SEPARATOR_FLASHCARD_CONTENT = `
57
59
 
58
60
  ---
59
61
 
60
- ## CDF(Concept-Descriptor Framework)
62
+ ## 4. CDF(Concept-Descriptor Framework)
61
63
 
62
64
  RemNote 推荐的知识结构化方法:
63
65
 
64
- - **Concept**(\\\`::\\\`):需要理解的核心概念——"X 是什么?"
65
- - **Descriptor**(\\\`;;\\\`):概念的属性/描述——"X 的 Y 是什么?"
66
+ - **Concept**(type:concept):需要理解的核心概念——"X 是什么?"
67
+ - **Descriptor**(type:descriptor):概念的属性/描述——"X 的 Y 是什么?"
66
68
 
67
- 典型结构:
69
+ 在 CLI 大纲中的表现(注意:用箭头和元数据标记,不用分隔符):
68
70
 
69
71
  \\\`\\\`\\\`
70
- 线性回归 :: 最基本的回归模型 Concept (type:concept, both)
71
- 假设 ;; 因变量与自变量呈线性关系 Descriptor (type:descriptor, forward)
72
- 损失函数 ;; 均方误差 (MSE) Descriptor (type:descriptor, forward)
72
+ 线性回归 最基本的回归模型 <!--id1 type:concept-->
73
+ 假设 因变量与自变量呈线性关系 <!--id2 type:descriptor-->
74
+ 损失函数 均方误差 (MSE) <!--id3 type:descriptor-->
73
75
  \\\`\\\`\\\`
74
76
 
75
77
  ---
76
78
 
77
- ## 通过 CLI 创建闪卡
79
+ ## 5. 通过 CLI 创建闪卡
78
80
 
79
81
  ### 使用 edit_tree 新增行
80
82
 
81
- 通过箭头分隔符创建:
83
+ 通过箭头创建(**禁止在文本中插入 \\\`::\\\`、\\\`;;\\\`、\\\`>>\\\` 等编辑器分隔符**):
82
84
 
83
85
  | 要创建的闪卡类型 | edit_tree 新增行格式 | 额外步骤 |
84
86
  |:-----------------|:--------------------|:---------|
@@ -90,7 +92,7 @@ RemNote 推荐的知识结构化方法:
90
92
  | 多行正向 | \\\`问题 ↓\\\`(子行为答案) | — |
91
93
  | 多行双向 | \\\`问题 ↕\\\`(子行为答案) | — |
92
94
 
93
- ### 使用 edit_rem 修改现有闪卡
95
+ ### 使用 edit_rem 修改现有 Rem 的闪卡行为
94
96
 
95
97
  修改闪卡行为的相关字段:
96
98
 
@@ -104,7 +106,7 @@ RemNote 推荐的知识结构化方法:
104
106
 
105
107
  ---
106
108
 
107
- ## 完形填空(Cloze)
109
+ ## 6. 完形填空(Cloze)
108
110
 
109
111
  完形填空通过 RichText 中的 \\\`cId\\\` 标记实现:
110
112
 
@@ -9,7 +9,7 @@ export function registerEditTools(server) {
9
9
  // -------------------------------------------------------------------------
10
10
  server.addTool({
11
11
  name: 'edit_rem',
12
- description: '通过 str_replace 语义修改单个 Rem 的属性字段。\n\n操作对象是 Rem 序列化后的 JSON 文本(JSON.stringify 2 空格缩进),\n在其中将 oldStr 精确替换为 newStr,自动推导变更字段并写入。\n\n适用场景:修改文本、类型、标题级别、practiceDirection、高亮色、Todo 状态等属性;\n修改分隔符以改变闪卡类型。不适合修改子树结构(用 edit_tree)。\n\n前置条件:必须先 read_rem 建立缓存,否则被防线拒绝。\n工作流:read_rem → 查看 JSON → edit_rem 替换。\n\n三道防线:缓存存在 → 并发检测 → 精确匹配(恰好 1 次)。\nstr_replace 要点:oldStr 建议包含字段名避免歧义(如 "text": "旧值")。替换后须是合法 JSON。\n关联工具:read_rem(前置)、edit_tree(结构编辑)',
12
+ description: '通过 str_replace 语义修改单个 Rem 的属性字段。\n\n操作对象是 Rem 序列化后的 JSON 文本(JSON.stringify 2 空格缩进),\n在其中将 oldStr 精确替换为 newStr,自动推导变更字段并写入。\n\n适用场景:修改文本、类型、标题级别、practiceDirection、高亮色、Todo 状态等属性;\n修改分隔符以改变闪卡类型。不适合修改子树结构(用 edit_tree)。\n\n前置条件:必须先 read_rem 建立缓存,否则被防线拒绝。\n工作流:read_rem → 查看 JSON → edit_rem 替换。\n\n三道防线:缓存存在 → 并发检测 → 精确匹配(恰好 1 次)。\nstr_replace 要点:oldStr 建议包含字段名避免歧义(如 "text": "旧值")。替换后须是合法 JSON。\n详细操作指南(含 RichText 编辑实战示例)见 resource://edit-rem-guide。\n关联工具:read_rem(前置)、edit_tree(结构编辑)',
13
13
  parameters: z.object({
14
14
  remId: z.string().describe('目标 Rem 的 ID'),
15
15
  oldStr: z.string().describe('要替换的原始文本(必须精确匹配缓存中的内容,且恰好匹配 1 次)'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remnote-bridge",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "RemNote 自动化桥接工具集:CLI + MCP Server + Plugin",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,8 +23,7 @@
23
23
  "remnote-plugin/tailwind.config.js",
24
24
  "remnote-plugin/webpack.config.js",
25
25
  "remnote-plugin/tsconfig.json",
26
- "skill/",
27
- "docs/instruction/"
26
+ "skills/"
28
27
  ],
29
28
  "scripts": {
30
29
  "build": "tsc",
@@ -7,21 +7,21 @@ description: "RemNote 知识库操作指南。通过 remnote-bridge 命令行工
7
7
 
8
8
  本 skill 指导 AI Agent 通过 remnote-bridge 操作 RemNote 知识库。
9
9
 
10
- **详细命令文档**位于 `docs/instruction/` 目录下,执行具体命令前务必先读取对应文件:
10
+ **详细命令文档**位于 `instructions/` 目录下,执行具体命令前务必先读取对应文件:
11
11
 
12
12
  | 命令 | 文档路径 |
13
13
  |:-----|:---------|
14
- | connect | `docs/instruction/connect.md` |
15
- | disconnect | `docs/instruction/disconnect.md` |
16
- | health | `docs/instruction/health.md` |
17
- | read-rem | `docs/instruction/read-rem.md` |
18
- | edit-rem | `docs/instruction/edit-rem.md` |
19
- | read-tree | `docs/instruction/read-tree.md` |
20
- | edit-tree | `docs/instruction/edit-tree.md` |
21
- | read-globe | `docs/instruction/read-globe.md` |
22
- | read-context | `docs/instruction/read-context.md` |
23
- | search | `docs/instruction/search.md` |
24
- | 全局概览 | `docs/instruction/overall.md` |
14
+ | connect | `instructions/connect.md` |
15
+ | disconnect | `instructions/disconnect.md` |
16
+ | health | `instructions/health.md` |
17
+ | read-rem | `instructions/read-rem.md` |
18
+ | edit-rem | `instructions/edit-rem.md` |
19
+ | read-tree | `instructions/read-tree.md` |
20
+ | edit-tree | `instructions/edit-tree.md` |
21
+ | read-globe | `instructions/read-globe.md` |
22
+ | read-context | `instructions/read-context.md` |
23
+ | search | `instructions/search.md` |
24
+ | 全局概览 | `instructions/overall.md` |
25
25
 
26
26
  ---
27
27
 
@@ -42,7 +42,7 @@ RemNote 中所有内容的基本单元都是 **Rem**。文档、文件夹、闪
42
42
 
43
43
  两个独立维度:
44
44
 
45
- - **type**(闪卡语义):`concept`(`::`,加粗)、`descriptor`(`;;`,斜体)、`default`(普通)、`portal`(只读)
45
+ - **type**(闪卡语义):`concept`(加粗)、`descriptor`(正常字重)、`default`(普通)、`portal`(只读)
46
46
  - **isDocument**(页面语义):与 type 完全独立
47
47
 
48
48
  ### CDF 框架(Concept-Descriptor Framework)
@@ -55,19 +55,30 @@ RemNote 推荐的知识结构化方法:
55
55
  损失函数 ;; 均方误差 (MSE) ← Descriptor
56
56
  ```
57
57
 
58
- ### 分隔符与闪卡方向
58
+ ### 闪卡的 CLI 操作方式
59
59
 
60
- | 分隔符 | type | 默认方向 | 用途 |
61
- |:-------|:-----|:---------|:-----|
62
- | `::` | concept | both | 概念定义 |
63
- | `;;` | descriptor | forward | 描述属性 |
64
- | `>>` | default | forward | 正向问答 |
65
- | `<<` | default | backward | 反向问答 |
66
- | `<>` | default | both | 双向问答 |
67
- | `>>>` | default | forward | 多行答案 |
68
- | `::>` | concept | both | 概念型多行答案 |
69
- | `;;>` | descriptor | forward | 描述型多行答案 |
70
- | `{{}}` | default | forward | 完形填空 |
60
+ 闪卡由 `type`、`backText`、`practiceDirection` 三个字段控制。通过 CLI 操作闪卡,修改的是这些**字段**。
61
+
62
+ **禁止**:在文本中插入分隔符(`::`、`;;`、`>>`、`<<` 等)来创建闪卡。分隔符是 RemNote 编辑器的输入语法,CLI 无法识别。
63
+
64
+ | 闪卡操作 | CLI 方法 |
65
+ |:---------|:---------|
66
+ | 创建概念定义 | `edit-tree` 新增行 `概念 定义`,再 `edit-rem` 设 `type: "concept"` |
67
+ | 创建正向问答 | `edit-tree` 新增行 `问题 答案` |
68
+ | 创建多行答案 | `edit-tree` 新增行 `问题 ↓`(子行自动成为答案) |
69
+ | 改变闪卡类型 | `edit-rem` 修改 `type`、`backText`、`practiceDirection` |
70
+
71
+ ### 理解用户意图:分隔符映射
72
+
73
+ 用户在 RemNote 编辑器中通过分隔符创建闪卡。当用户提到这些分隔符时,理解其意图并映射到上述 CLI 操作:
74
+
75
+ | 用户说 / 编辑器分隔符 | 对应 type | 对应 practiceDirection |
76
+ |:----------------------|:----------|:----------------------|
77
+ | `::` | concept | both |
78
+ | `;;` | descriptor | forward |
79
+ | `>>` / `<<` / `<>` | default | forward / backward / both |
80
+ | `>>>` / `::>` / `;;>` | default / concept / descriptor | 多行(子 Rem 为答案) |
81
+ | `{{}}` | default | forward(完形填空) |
71
82
 
72
83
  ### 三种链接机制
73
84
 
@@ -87,16 +98,90 @@ Portal 的编辑同步意味着修改一处会影响另一处。Portal 引用的
87
98
  ["纯文本", {"i":"m","b":true,"text":"粗体"}, {"i":"q","_id":"remId"}]
88
99
  ```
89
100
 
90
- | `i` 值 | 类型 | 核心字段 |
91
- |:-------|:-----|:---------|
92
- | string | 纯文本 | |
93
- | `"m"` | 带格式文本 | `text` + `b`/`l`/`u`/`h`/`tc`/`code`/`cId`/`iUrl` |
94
- | `"q"` | Rem 引用 | `_id` |
95
- | `"i"` | 图片 | `url`, `width`, `height` |
96
- | `"x"` | LaTeX | `text` |
97
- | `"a"` | 音频 | `url` |
101
+ #### 元素类型
102
+
103
+ | `i` | 类型 | 必填字段 | 可选字段 |
104
+ |:-------|:-----|:---------|:---------|
105
+ | string | 纯文本 | | |
106
+ | `"m"` | 带格式文本 | `text` | 格式标记(见下表) |
107
+ | `"q"` | Rem 引用 | `_id` | `content`, `showFullName`, `aliasId` |
108
+ | `"i"` | 图片 | `url` | `width`, `height`, `percent`(25/50/100) |
109
+ | `"x"` | LaTeX | `text` | `block`(true=块级公式) |
110
+ | `"a"` | 音频/视频 | `url`, `onlyAudio` | `width`, `height` |
111
+ | `"s"` | 卡片分隔符 | — | `delimiterCharacterForSerialization` |
98
112
 
99
- **序列化确定性**:RichText 对象内部按 **key 字母序排列**(如 `{"b":true,"i":"m","text":"粗体"}`),确保 JSON 始终一致。构造 edit-rem oldStr 时必须保持相同的 key 顺序,否则匹配失败。
113
+ **注意**:`i:"a"``onlyAudio` 是**必填**字段(`true`=音频,`false`=视频),缺少会导致 SDK 拒绝写入。
114
+
115
+ #### 格式标记(主要用于 `i:"m"`,但 `i:"q"` 等也支持)
116
+
117
+ | 字段 | 类型 | 含义 |
118
+ |:-----|:-----|:-----|
119
+ | `b` | `true` | 加粗 |
120
+ | `l` | `true` | 斜体(小写字母 L,不是 I) |
121
+ | `u` | `true` | 下划线 |
122
+ | `h` | `number` | 高亮颜色(见 RemColor 枚举) |
123
+ | `tc` | `number` | 文字颜色(见 RemColor 枚举) |
124
+ | `q` | `true` | 行内代码(红色等宽样式) |
125
+ | `code` | `true` | 代码块(带语言标签和复制按钮) |
126
+ | `language` | `string` | 代码块语言(如 `"javascript"`、`"python"`) |
127
+ | `cId` | `string` | 完形填空 ID |
128
+ | `hiddenCloze` | `true` | 完形填空隐藏状态 |
129
+ | `revealedCloze` | `true` | 完形填空已揭示状态 |
130
+ | `iUrl` | `string` | 外部超链接 URL |
131
+ | `qId` | `string` | 行内引用链接的 Rem ID |
132
+
133
+ **注意**:`url` 字段已废弃无效,超链接必须用 `iUrl`。
134
+
135
+ #### RemColor 颜色枚举(`h` 和 `tc` 共用)
136
+
137
+ | 值 | 颜色 | 值 | 颜色 | 值 | 颜色 |
138
+ |:---|:-----|:---|:-----|:---|:-----|
139
+ | 0 | 无颜色/默认 | 4 | Green | 7 | Gray |
140
+ | 1 | Red | 5 | Purple | 8 | Brown |
141
+ | 2 | Orange | 6 | Blue | 9 | Pink |
142
+ | 3 | Yellow | — | — | — | — |
143
+
144
+ #### 常用构造示例
145
+
146
+ 以下为 `JSON.stringify(null, 2)` 格式化后的实际样式(key 按字母序):
147
+
148
+ ```jsonc
149
+ // 粗体(注意 key 顺序:b < i < text)
150
+ { "b": true, "i": "m", "text": "粗体" }
151
+ // 行内代码
152
+ { "i": "m", "q": true, "text": "console.log()" }
153
+ // 超链接(iUrl 不是 url!)
154
+ { "i": "m", "iUrl": "https://example.com", "text": "点击访问" }
155
+ // 红色高亮 + 粗体(h 是数字,不是字符串)
156
+ { "b": true, "h": 1, "i": "m", "text": "重点" }
157
+ // 完形填空
158
+ { "cId": "cloze1", "i": "m", "text": "答案内容" }
159
+ // Rem 引用(_id 排在所有小写 key 之前)
160
+ { "_id": "remId", "i": "q" }
161
+ // Rem 引用加粗
162
+ { "_id": "remId", "b": true, "i": "q" }
163
+ // LaTeX 公式
164
+ { "i": "x", "text": "E = mc^2" }
165
+ // 图片
166
+ { "i": "i", "url": "https://...", "width": 200, "height": 100 }
167
+ // 视频(onlyAudio 必填!)
168
+ { "i": "a", "onlyAudio": false, "url": "https://youtube.com/watch?v=xxx" }
169
+ // 音频
170
+ { "i": "a", "onlyAudio": true, "url": "https://example.com/audio.mp3" }
171
+ ```
172
+
173
+ > **注意**:在 RemObject 的格式化 JSON 中,数组内的对象会展开为多行(每个 key 一行,缩进 4+2 空格)。以上为简写——构造 edit-rem 的 oldStr/newStr 时必须使用实际的多行格式。
174
+
175
+ #### highlightColor(Rem 级别)vs h(RichText 行内)
176
+
177
+ - `highlightColor`:RemObject 顶层字段,值为字符串(`"Red"`, `"Blue"` 等)或 `null`,作用于整行背景
178
+ - `h`:RichText 元素内格式标记,值为数字 0-9(RemColor 枚举),作用于行内文字片段
179
+
180
+ 两者完全独立,互不影响。
181
+
182
+ #### 序列化确定性
183
+
184
+ RichText 对象内部按 **key 字母序排列**(`sortRichTextKeys()`),确保 JSON 始终一致。`_id` 中的 `_`(U+005F)排在所有小写字母(`a`=U+0061)之前。构造 edit-rem 的 oldStr 时必须保持相同的 key 顺序,否则匹配失败。
100
185
 
101
186
  ### Powerup 机制与噪音过滤
102
187
 
@@ -141,16 +226,35 @@ Rem 的属性(文本、类型、格式、标签) → edit-rem (前置
141
226
 
142
227
  ## 3. 标准工作流
143
228
 
229
+ ### ⚠️ connect 后需要用户配合(重要)
230
+
231
+ `connect` 成功只意味着 daemon 和 webpack-dev-server 已启动,**Plugin 并未自动连接**。用户必须在 RemNote 中完成操作,Plugin 才能连接到 daemon:
232
+
233
+ **首次使用**(RemNote 从未加载过此插件):
234
+ 1. 打开 RemNote 桌面端或网页端
235
+ 2. 点击左侧边栏底部的插件图标(拼图形状)
236
+ 3. 点击「开发你的插件」(Develop Your Plugin)
237
+ 4. 在输入框中填入 `http://localhost:8080`(即 connect 输出的 webpack-dev-server 地址)
238
+ 5. 等待插件加载完成
239
+
240
+ **非首次使用**(之前已加载过此插件):
241
+ - 只需**刷新 RemNote 页面**即可(浏览器 F5 或 Cmd+R),插件会自动重新连接
242
+
243
+ **你必须**:执行 `connect` 后,**立即告知用户需要完成上述操作**,不要直接调用业务命令。引导用户完成后,用 `health` 确认三层就绪再继续。
244
+
245
+ ### 完整流程
246
+
144
247
  ```
145
248
  1. connect -- 启动会话(幂等,重复调用安全)
146
- 2. health -- 确认三层就绪:daemon Plugin → SDK(链式依赖)
147
- 3. read-globe -- 了解知识库结构(首次探索)
249
+ 2. ⚠️ 引导用户在 RemNote 中加载插件(首次填端口,非首次刷新页面)
250
+ 3. health -- 确认三层就绪:daemon → Plugin → SDK(链式依赖)
251
+ 4. read-globe -- 了解知识库结构(首次探索)
148
252
  或 read-context -- 了解用户当前上下文
149
- 4. search "关键词" -- 定位目标 Rem(结果不进缓存!)
150
- 5. read-tree <id> -- 展开子树 → 写入缓存(edit-tree 的前置)
151
- 6. read-rem <id> -- 读取属性 → 写入缓存(edit-rem 的前置)
152
- 7. edit-rem / edit-tree -- 执行修改
153
- 8. disconnect -- 结束会话(缓存全部清空,幂等)
253
+ 5. search "关键词" -- 定位目标 Rem(结果不进缓存!)
254
+ 6. read-tree <id> -- 展开子树 → 写入缓存(edit-tree 的前置)
255
+ 7. read-rem <id> -- 读取属性 → 写入缓存(edit-rem 的前置)
256
+ 8. edit-rem / edit-tree -- 执行修改
257
+ 9. disconnect -- 结束会话(缓存全部清空,幂等)
154
258
  ```
155
259
 
156
260
  **超时**:daemon 默认 30 分钟无 CLI 交互自动关闭,每次请求重置计时器。长时间操作间可用 health 保活。
@@ -299,12 +403,12 @@ read-tree / read-globe / read-context 输出 Markdown 大纲,edit-tree 基于
299
403
 
300
404
  ### 新增行格式
301
405
 
302
- 新增行可用 Markdown 前缀和箭头分隔符:
406
+ 新增行可用 Markdown 前缀和箭头:
303
407
 
304
408
  ```markdown
305
409
  # 新标题
306
410
  新闪卡 → 答案
307
- 概念 :: 定义
411
+ 问题 回答
308
412
  - [ ] 新待办
309
413
  `代码块`
310
414
  ```
@@ -18,6 +18,30 @@ daemon 启动后脱离父进程(detached),CLI 进程退出但 daemon 继
18
18
 
19
19
  ---
20
20
 
21
+ ## ⚠️ connect 后需要用户配合
22
+
23
+ `connect` 成功只意味着 daemon 和 webpack-dev-server 已启动,**Plugin 并未自动连接**。用户必须在 RemNote 中完成以下操作:
24
+
25
+ ### 首次使用(RemNote 从未加载过此插件)
26
+
27
+ 1. 打开 RemNote 桌面端或网页端
28
+ 2. 点击左侧边栏底部的插件图标(拼图形状)
29
+ 3. 点击「开发你的插件」(Develop Your Plugin)
30
+ 4. 在输入框中填入 `http://localhost:8080`(即 connect 输出的 webpack-dev-server 地址)
31
+ 5. 等待插件加载完成
32
+
33
+ ### 非首次使用(之前已加载过此插件)
34
+
35
+ 只需**刷新 RemNote 页面**即可(浏览器 F5 或 Cmd+R),插件会自动重新连接到已启动的 daemon。
36
+
37
+ ### AI Agent 注意事项
38
+
39
+ - 执行 `connect` 后,**必须立即告知用户完成上述操作**
40
+ - **禁止在 connect 后直接调用业务命令**——此时 Plugin 尚未连接,命令会报 "Plugin 未连接" 错误
41
+ - 引导用户完成操作后,用 `health` 确认三层就绪(daemon → Plugin → SDK),再执行业务命令
42
+
43
+ ---
44
+
21
45
  ## 用法
22
46
 
23
47
  ### 人类模式