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.
- package/README.md +67 -3
- package/dist/cli/commands/install-skill.js +51 -11
- package/dist/cli/main.js +12 -4
- package/dist/mcp/index.js +10 -1
- package/dist/mcp/instructions.js +87 -22
- package/dist/mcp/resources/edit-rem-guide.js +192 -0
- package/dist/mcp/resources/rem-object-fields.js +75 -33
- package/dist/mcp/resources/separator-flashcard.js +31 -29
- package/dist/mcp/tools/edit-tools.js +1 -1
- package/package.json +2 -3
- package/{skill → skills/remnote-bridge}/SKILL.md +147 -43
- package/{docs/instruction → skills/remnote-bridge/instructions}/connect.md +24 -0
- package/{docs/instruction → skills/remnote-bridge/instructions}/edit-rem.md +151 -5
- package/{docs/instruction → skills/remnote-bridge/instructions}/overall.md +103 -44
- package/{docs/instruction → skills/remnote-bridge/instructions}/read-rem.md +169 -32
- /package/{docs/instruction → skills/remnote-bridge/instructions}/disconnect.md +0 -0
- /package/{docs/instruction → skills/remnote-bridge/instructions}/edit-tree.md +0 -0
- /package/{docs/instruction → skills/remnote-bridge/instructions}/health.md +0 -0
- /package/{docs/instruction → skills/remnote-bridge/instructions}/read-context.md +0 -0
- /package/{docs/instruction → skills/remnote-bridge/instructions}/read-globe.md +0 -0
- /package/{docs/instruction → skills/remnote-bridge/instructions}/read-tree.md +0 -0
- /package/{docs/instruction → skills/remnote-bridge/instructions}/search.md +0 -0
|
@@ -461,12 +461,158 @@ str_replace 操作的对象是 `JSON.stringify(remObject, null, 2)` 的文本—
|
|
|
461
461
|
|
|
462
462
|
2. **替换后必须是合法 JSON**:检查引号、逗号、括号的完整性
|
|
463
463
|
|
|
464
|
-
3. **修改 RichText 字段**:直接操作 JSON
|
|
464
|
+
3. **修改 RichText 字段**:直接操作 JSON 数组结构(见下方完整示例)
|
|
465
465
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## RichText 编辑实战指南
|
|
469
|
+
|
|
470
|
+
### 理解格式化 JSON 中的 RichText
|
|
471
|
+
|
|
472
|
+
`edit-rem` 的 str_replace 操作对象是 `JSON.stringify(remObject, null, 2)` 的格式化文本。RichText 数组在格式化后是多行缩进结构,**不是**紧凑的单行 JSON。
|
|
473
|
+
|
|
474
|
+
以下是一个包含 RichText 的 RemObject **实际输出片段**:
|
|
475
|
+
|
|
476
|
+
```json
|
|
477
|
+
{
|
|
478
|
+
"id": "kLrIOHJLyMd8Y2lyA",
|
|
479
|
+
"text": [
|
|
480
|
+
"这是",
|
|
481
|
+
{
|
|
482
|
+
"b": true,
|
|
483
|
+
"i": "m",
|
|
484
|
+
"text": "粗体"
|
|
485
|
+
},
|
|
486
|
+
"普通文本"
|
|
487
|
+
],
|
|
488
|
+
"backText": null,
|
|
489
|
+
"type": "concept",
|
|
490
|
+
"highlightColor": null,
|
|
491
|
+
"isTodo": false
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**关键要点**:
|
|
496
|
+
- RichText 对象内部的 key 按**字母序**排列(`b` < `i` < `text`),由 Plugin 端 `sortRichTextKeys()` 保证
|
|
497
|
+
- `_id` 中的 `_` 在 Unicode 中排在小写字母之前,所以 `_id` 排在所有小写 key 的最前面
|
|
498
|
+
- 每个 key-value 对占一行,缩进 4 空格(对象在数组中时嵌套 2+2)
|
|
499
|
+
- 纯字符串元素直接是 `"字符串"`,对象元素展开为多行
|
|
500
|
+
|
|
501
|
+
### 示例 1:将纯文本改为粗体
|
|
502
|
+
|
|
503
|
+
**read-rem 返回**(部分):
|
|
504
|
+
|
|
505
|
+
```json
|
|
506
|
+
"text": [
|
|
507
|
+
"普通标题"
|
|
508
|
+
],
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**edit-rem 调用**:
|
|
512
|
+
|
|
513
|
+
```
|
|
514
|
+
oldStr: "\"text\": [\n \"普通标题\"\n ]"
|
|
515
|
+
|
|
516
|
+
newStr: "\"text\": [\n {\n \"b\": true,\n \"i\": \"m\",\n \"text\": \"粗体标题\"\n }\n ]"
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
替换后 JSON 变为:
|
|
520
|
+
|
|
521
|
+
```json
|
|
522
|
+
"text": [
|
|
523
|
+
{
|
|
524
|
+
"b": true,
|
|
525
|
+
"i": "m",
|
|
526
|
+
"text": "粗体标题"
|
|
527
|
+
}
|
|
528
|
+
],
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### 示例 2:修改 Rem 引用旁的文本
|
|
532
|
+
|
|
533
|
+
**read-rem 返回**(部分):
|
|
534
|
+
|
|
535
|
+
```json
|
|
536
|
+
"text": [
|
|
537
|
+
"参考 ",
|
|
538
|
+
{
|
|
539
|
+
"_id": "abc123",
|
|
540
|
+
"i": "q"
|
|
541
|
+
},
|
|
542
|
+
" 的内容"
|
|
543
|
+
],
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
将 " 的内容" 替换为 " 的详细说明":
|
|
547
|
+
|
|
548
|
+
```
|
|
549
|
+
oldStr: " 的内容"
|
|
550
|
+
newStr: " 的详细说明"
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
> 注意:纯字符串可以直接匹配,不需要包含数组结构。但如果 " 的内容" 在 JSON 中出现多次,需要加上下文:
|
|
554
|
+
> `oldStr: " \" 的内容\"\n ]"`
|
|
555
|
+
|
|
556
|
+
### 示例 3:给文本添加超链接
|
|
557
|
+
|
|
558
|
+
**read-rem 返回**(部分):
|
|
559
|
+
|
|
560
|
+
```json
|
|
561
|
+
"text": [
|
|
562
|
+
"点击访问官网"
|
|
563
|
+
],
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**edit-rem 调用**:
|
|
567
|
+
|
|
568
|
+
```
|
|
569
|
+
oldStr: "\"text\": [\n \"点击访问官网\"\n ]"
|
|
570
|
+
|
|
571
|
+
newStr: "\"text\": [\n \"点击\",\n {\n \"i\": \"m\",\n \"iUrl\": \"https://remnote.com\",\n \"text\": \"访问官网\"\n }\n ]"
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### 示例 4:修改高亮颜色(Rem 级别 vs RichText 级别)
|
|
575
|
+
|
|
576
|
+
**Rem 级别的 `highlightColor`**(整行背景色,值为英文字符串):
|
|
577
|
+
|
|
578
|
+
```
|
|
579
|
+
oldStr: "\"highlightColor\": null"
|
|
580
|
+
newStr: "\"highlightColor\": \"Red\""
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
**RichText 级别的 `h`**(行内文字高亮,值为数字 0-9):
|
|
584
|
+
|
|
585
|
+
```
|
|
586
|
+
oldStr: "\"text\": [\n \"普通文本\"\n ]"
|
|
587
|
+
newStr: "\"text\": [\n {\n \"h\": 1,\n \"i\": \"m\",\n \"text\": \"红色高亮文本\"\n }\n ]"
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
> **区分**:`highlightColor` 是 RemObject 顶层字段,值为字符串(`"Red"`, `"Blue"` 等);RichText 的 `h` 是行内格式标记,值为数字(1=Red, 2=Orange 等,见 RemColor 枚举)。
|
|
591
|
+
|
|
592
|
+
### 示例 5:添加完形填空
|
|
593
|
+
|
|
594
|
+
**read-rem 返回**(部分):
|
|
595
|
+
|
|
596
|
+
```json
|
|
597
|
+
"text": [
|
|
598
|
+
"光合作用需要阳光"
|
|
599
|
+
],
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
将 "阳光" 变成完形填空:
|
|
603
|
+
|
|
604
|
+
```
|
|
605
|
+
oldStr: "\"text\": [\n \"光合作用需要阳光\"\n ]"
|
|
606
|
+
|
|
607
|
+
newStr: "\"text\": [\n \"光合作用需要\",\n {\n \"cId\": \"cloze1\",\n \"i\": \"m\",\n \"text\": \"阳光\"\n }\n ]"
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### 常见错误
|
|
611
|
+
|
|
612
|
+
1. **忘记 key 字母序**:写 `{"text":"xx","i":"m","b":true}` 不会被匹配——实际存储为 `{"b":true,"i":"m","text":"xx"}`
|
|
613
|
+
2. **缩进不匹配**:格式化 JSON 使用 2 空格缩进,数组内对象的 key 缩进 6 空格(顶层 2 + 数组 2 + 对象 2),但在 `JSON.stringify(obj, null, 2)` 中数组元素的对象缩进 4 空格(数组 2 + 对象内 2)
|
|
614
|
+
3. **混淆 highlightColor 和 h**:前者是字符串 `"Red"`,后者是数字 `1`
|
|
615
|
+
4. **忘记 `i:"a"` 的 `onlyAudio` 必填**:缺少此字段 SDK 拒绝写入
|
|
470
616
|
|
|
471
617
|
---
|
|
472
618
|
|
|
@@ -51,7 +51,7 @@ Agent 的核心任务是将用户的自然语言请求翻译为 CLI 命令。以
|
|
|
51
51
|
| 用户表述 | 操作类型 | CLI 命令 |
|
|
52
52
|
|:---------|:---------|:---------|
|
|
53
53
|
| "改文本"、"改标题"、"改颜色"、"改类型" | 修改 Rem 属性 | `edit-rem` |
|
|
54
|
-
| "创建一个概念定义
|
|
54
|
+
| "创建一个概念定义"、"做个 :: 卡" | 新增行(edit-tree 箭头)+ 改 type(edit-rem) | `edit-tree` + `edit-rem` |
|
|
55
55
|
| "新增一个子节点"、"添加笔记" | 结构操作:新增 | `edit-tree` |
|
|
56
56
|
| "删除这个"、"移除" | 结构操作:删除 | `edit-tree` |
|
|
57
57
|
| "移动到…下面" | 结构操作:移动 | `edit-tree` |
|
|
@@ -66,42 +66,52 @@ Rem 有两个**独立维度**的类型:
|
|
|
66
66
|
|
|
67
67
|
#### type 字段(闪卡语义)
|
|
68
68
|
|
|
69
|
-
| type 值 |
|
|
70
|
-
|
|
71
|
-
| `concept` |
|
|
72
|
-
| `descriptor` |
|
|
73
|
-
| `default` |
|
|
74
|
-
| `portal` |
|
|
69
|
+
| type 值 | 含义 | UI 表现 | CLI 设置方式 |
|
|
70
|
+
|:--------|:-----|:--------|:-------------|
|
|
71
|
+
| `concept` | 概念定义 | 文字**加粗** | `edit-rem` 设 `type: "concept"` |
|
|
72
|
+
| `descriptor` | 描述/属性 | 文字*斜体* | `edit-rem` 设 `type: "descriptor"` |
|
|
73
|
+
| `default` | 普通 Rem | 正常字重 | `edit-rem` 设 `type: "default"` |
|
|
74
|
+
| `portal` | 嵌入引用容器 | 紫色左边框 | **只读**,不可通过 CLI 设置 |
|
|
75
75
|
|
|
76
76
|
#### isDocument 字段(页面语义)
|
|
77
77
|
|
|
78
78
|
`isDocument` 与 `type` 完全独立——一个 `concept` 类型的 Rem 可以同时是 Document。
|
|
79
79
|
|
|
80
|
-
### 2.4
|
|
80
|
+
### 2.4 闪卡的 CLI 操作方式
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
闪卡由 `type`、`backText`、`practiceDirection` 三个字段控制。通过 CLI 操作闪卡,修改的是这些**字段**和大纲**箭头**。
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|:-------|:-----|:---------|:---------|:-----|
|
|
86
|
-
| (无) | `default` | `null` | — | 无闪卡行为 |
|
|
87
|
-
| `::` | `concept` | 后半部分 | `both` | 概念定义(CDF 框架) |
|
|
88
|
-
| `;;` | `descriptor` | 后半部分 | `forward` | 描述属性(CDF 框架) |
|
|
89
|
-
| `>>` | `default` | 后半部分 | `forward` | 正向问答 |
|
|
90
|
-
| `<<` | `default` | 后半部分 | `backward` | 反向问答 |
|
|
91
|
-
| `<>` | `default` | 后半部分 | `both` | 双向问答 |
|
|
92
|
-
| `>>>` | `default` | `null` | `forward` | 多行答案(子 Rem 为答案) |
|
|
93
|
-
| `::>` | `concept` | `null` | `both` | 概念型多行答案 |
|
|
94
|
-
| `;;>` | `descriptor` | `null` | `forward` | 描述型多行答案 |
|
|
95
|
-
| `{{}}` | `default` | `null` | `forward` | 完形填空(Cloze) |
|
|
84
|
+
**禁止**:在文本中插入分隔符(`::`、`;;`、`>>`、`<<` 等)来创建闪卡。分隔符是 RemNote 编辑器的输入语法,CLI 无法识别。
|
|
96
85
|
|
|
97
|
-
|
|
86
|
+
| 闪卡操作 | CLI 方法 |
|
|
87
|
+
|:---------|:---------|
|
|
88
|
+
| 创建概念定义 | `edit-tree` 新增行 `概念 ↔ 定义`,再 `edit-rem` 设 `type: "concept"` |
|
|
89
|
+
| 创建正向问答 | `edit-tree` 新增行 `问题 → 答案` |
|
|
90
|
+
| 创建反向问答 | `edit-tree` 新增行 `问题 ← 答案` |
|
|
91
|
+
| 创建双向问答 | `edit-tree` 新增行 `问题 ↔ 答案` |
|
|
92
|
+
| 创建多行答案 | `edit-tree` 新增行 `问题 ↓`(子行自动成为答案) |
|
|
93
|
+
| 改变闪卡类型/方向 | `edit-rem` 修改 `type`、`backText`、`practiceDirection` |
|
|
98
94
|
|
|
99
|
-
|
|
95
|
+
`practiceDirection` 的取值:`forward`(正向)、`backward`(反向)、`both`(双向)、`none`(不练习)。
|
|
96
|
+
|
|
97
|
+
### 2.5 理解用户意图:编辑器分隔符映射
|
|
98
|
+
|
|
99
|
+
用户在 RemNote 编辑器中通过分隔符创建闪卡。当用户提到这些分隔符时,理解其意图并映射到上述 CLI 操作:
|
|
100
|
+
|
|
101
|
+
| 用户说 / 编辑器分隔符 | 对应 type | 对应 practiceDirection |
|
|
102
|
+
|:----------------------|:----------|:----------------------|
|
|
103
|
+
| `::` | concept | both |
|
|
104
|
+
| `;;` | descriptor | forward |
|
|
105
|
+
| `>>` / `<<` / `<>` | default | forward / backward / both |
|
|
106
|
+
| `>>>` / `::>` / `;;>` | default / concept / descriptor | 多行(子 Rem 为答案) |
|
|
107
|
+
| `{{}}` | default | forward(完形填空) |
|
|
108
|
+
|
|
109
|
+
**CDF(Concept-Descriptor Framework)**:RemNote 推荐的知识结构化方法——`Concept`(type:concept)是需要理解的概念,`Descriptor`(type:descriptor)是概念的属性/描述。在 CLI 大纲中的表现:
|
|
100
110
|
|
|
101
111
|
```
|
|
102
|
-
线性回归
|
|
103
|
-
假设
|
|
104
|
-
损失函数
|
|
112
|
+
线性回归 ↔ 最基本的回归模型 <!--id1 type:concept-->
|
|
113
|
+
假设 → 因变量与自变量呈线性关系 <!--id2 type:descriptor-->
|
|
114
|
+
损失函数 → 均方误差 (MSE) <!--id3 type:descriptor-->
|
|
105
115
|
```
|
|
106
116
|
|
|
107
117
|
### 2.5 三种链接机制
|
|
@@ -170,11 +180,17 @@ RemNote SDK → 知识库
|
|
|
170
180
|
一次**会话(Session)= 守护进程的生命周期**。
|
|
171
181
|
|
|
172
182
|
```
|
|
173
|
-
connect → daemon 启动
|
|
183
|
+
connect → daemon 启动
|
|
184
|
+
↓
|
|
185
|
+
⚠️ 用户在 RemNote 中加载插件(首次填端口,非首次刷新页面)
|
|
186
|
+
↓
|
|
187
|
+
health → 确认三层就绪 → 会话可用
|
|
174
188
|
↕ (业务命令:read-rem, edit-tree, search, ...)
|
|
175
189
|
disconnect → daemon 关闭 → 会话结束,缓存清空
|
|
176
190
|
```
|
|
177
191
|
|
|
192
|
+
> **重要**:`connect` 成功只意味着 daemon 已启动,Plugin 并未自动连接。首次使用需用户在 RemNote「开发你的插件」中填入 `http://localhost:8080`;非首次只需刷新 RemNote 页面。必须引导用户完成此步后再用 `health` 确认就绪。
|
|
193
|
+
|
|
178
194
|
`connect` 启动三个服务:
|
|
179
195
|
|
|
180
196
|
| 服务 | 默认端口 | 用途 |
|
|
@@ -322,18 +338,19 @@ Agent 需要根据用户意图选择正确的读取命令:
|
|
|
322
338
|
|
|
323
339
|
```
|
|
324
340
|
1. connect ← 启动会话
|
|
325
|
-
2.
|
|
326
|
-
3.
|
|
341
|
+
2. ⚠️ 引导用户在 RemNote 中加载插件(首次填端口,非首次刷新页面)
|
|
342
|
+
3. health ← 确认系统就绪(daemon → Plugin → SDK 三层全通过)
|
|
343
|
+
4. read-globe ← 了解知识库结构(首次探索)
|
|
327
344
|
或 read-context ← 了解用户当前上下文
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
345
|
+
5. search "关键词" ← 定位目标 Rem(中文搜索可能需单字策略,详见 search.md)
|
|
346
|
+
6. read-tree <id> ← 展开目标区域的子树
|
|
347
|
+
7. read-rem <id> ← 读取详细属性(编辑前必需)
|
|
348
|
+
8. edit-rem <id> ... ← 修改 Rem 属性
|
|
332
349
|
或 edit-tree <id> ...← 修改树结构
|
|
333
|
-
|
|
350
|
+
9. disconnect ← 结束会话
|
|
334
351
|
```
|
|
335
352
|
|
|
336
|
-
**注意**:步骤
|
|
353
|
+
**注意**:步骤 7 是 `edit-rem` 的强制前置条件,步骤 6 是 `edit-tree` 的强制前置条件。跳过会触发防线 1 错误。步骤 2 是必须的——connect 后不引导用户加载插件就直接调用业务命令,会报"Plugin 未连接"错误。
|
|
337
354
|
|
|
338
355
|
---
|
|
339
356
|
|
|
@@ -457,16 +474,58 @@ Portal:portalType [R], portalDirectlyIncludedRem [R]
|
|
|
457
474
|
]
|
|
458
475
|
```
|
|
459
476
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
|
463
|
-
|
|
464
|
-
|
|
|
465
|
-
| `"
|
|
466
|
-
| `"
|
|
467
|
-
| `"
|
|
477
|
+
#### 元素类型
|
|
478
|
+
|
|
479
|
+
| `i` 值 | 类型 | 必填字段 | 可选字段 |
|
|
480
|
+
|:-------|:-----|:---------|:---------|
|
|
481
|
+
| (纯 string) | 纯文本 | — | — |
|
|
482
|
+
| `"m"` | 带格式文本 | `text` | 格式标记(见下表) |
|
|
483
|
+
| `"q"` | Rem 引用 | `_id` | `content`, `showFullName`, `aliasId` |
|
|
484
|
+
| `"i"` | 图片 | `url` | `width`, `height`, `percent`(25/50/100) |
|
|
485
|
+
| `"x"` | LaTeX | `text` | `block`(true=块级公式) |
|
|
486
|
+
| `"a"` | 音频/视频 | `url`, `onlyAudio`(**必填**) | `width`, `height` |
|
|
487
|
+
| `"s"` | 卡片分隔符 | — | `delimiterCharacterForSerialization` |
|
|
488
|
+
|
|
489
|
+
**注意**:`i:"a"` 的 `onlyAudio` 是必填字段(`true`=音频,`false`=视频),缺少会导致 SDK 拒绝写入。
|
|
490
|
+
|
|
491
|
+
#### 格式标记(主要用于 `i:"m"`,但 `i:"q"` 等也支持)
|
|
492
|
+
|
|
493
|
+
| 字段 | 类型 | 含义 |
|
|
494
|
+
|:-----|:-----|:-----|
|
|
495
|
+
| `b` | `true` | 加粗 |
|
|
496
|
+
| `l` | `true` | 斜体(小写字母 L) |
|
|
497
|
+
| `u` | `true` | 下划线 |
|
|
498
|
+
| `h` | `number` | 高亮颜色(RemColor 枚举:1=Red, 2=Orange, 3=Yellow, 4=Green, 5=Purple, 6=Blue, 7=Gray, 8=Brown, 9=Pink) |
|
|
499
|
+
| `tc` | `number` | 文字颜色(同 RemColor 枚举) |
|
|
500
|
+
| `q` | `true` | 行内代码(红色等宽样式) |
|
|
501
|
+
| `code` | `true` | 代码块(带语言标签和复制按钮) |
|
|
502
|
+
| `language` | `string` | 代码块语言(如 `"javascript"`) |
|
|
503
|
+
| `cId` | `string` | 完形填空 ID |
|
|
504
|
+
| `hiddenCloze` | `true` | 完形填空隐藏状态 |
|
|
505
|
+
| `iUrl` | `string` | 外部超链接 URL(`url` 字段已废弃,必须用 `iUrl`) |
|
|
506
|
+
| `qId` | `string` | 行内引用链接的 Rem ID |
|
|
507
|
+
|
|
508
|
+
#### 常用构造示例
|
|
509
|
+
|
|
510
|
+
以下为 `JSON.stringify(null, 2)` 格式化后的 key 字母序排列:
|
|
511
|
+
|
|
512
|
+
```jsonc
|
|
513
|
+
{ "b": true, "i": "m", "text": "粗体" } // 粗体
|
|
514
|
+
{ "i": "m", "q": true, "text": "code" } // 行内代码
|
|
515
|
+
{ "i": "m", "iUrl": "https://...", "text": "链接" } // 超链接
|
|
516
|
+
{ "b": true, "h": 1, "i": "m", "text": "重点" } // 粗体+红色高亮(h 是数字)
|
|
517
|
+
{ "cId": "c1", "i": "m", "text": "答案" } // 完形填空
|
|
518
|
+
{ "_id": "remId", "b": true, "i": "q" } // Rem 引用加粗(_id 排最前)
|
|
519
|
+
{ "i": "x", "text": "E = mc^2" } // LaTeX
|
|
520
|
+
{ "i": "a", "onlyAudio": false, "url": "..." } // 视频(onlyAudio 必填!)
|
|
521
|
+
{ "i": "a", "onlyAudio": true, "url": "..." } // 音频
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
> 在 RemObject 格式化 JSON 中,数组内对象展开为多行。构造 edit-rem 的 oldStr/newStr 必须用多行格式。
|
|
525
|
+
|
|
526
|
+
**highlightColor vs h**:`highlightColor` 是 RemObject 顶层字段(字符串如 `"Red"`),`h` 是 RichText 行内格式标记(数字 0-9)。两者独立。
|
|
468
527
|
|
|
469
|
-
**序列化确定性**:RichText 对象内部按 key 字母序排列(`sortRichTextKeys()
|
|
528
|
+
**序列化确定性**:RichText 对象内部按 key 字母序排列(`sortRichTextKeys()`)。`_id` 的 `_`(U+005F)排在所有小写字母之前。这对 edit-rem 的 str_replace 和并发检测至关重要。
|
|
470
529
|
|
|
471
530
|
---
|
|
472
531
|
|
|
@@ -184,21 +184,21 @@ RemObject 共 51 个字段,按读写权限分为三类:
|
|
|
184
184
|
|
|
185
185
|
| 字段 | 类型 | 权限 | 说明 |
|
|
186
186
|
|------|------|:----:|------|
|
|
187
|
-
| `text` | `RichText` | RW | 正面文本(RichText
|
|
188
|
-
| `backText` | `RichText \| null` | RW | 背面文本。null
|
|
187
|
+
| `text` | `RichText` | RW | 正面文本(RichText 数组)。UI:文本内容立即更新显示 |
|
|
188
|
+
| `backText` | `RichText \| null` | RW | 背面文本。null=无背面;设值即产生闪卡正反面结构。UI:显示为"正面 → 背面"箭头分隔格式 |
|
|
189
189
|
|
|
190
190
|
### 类型系统
|
|
191
191
|
|
|
192
192
|
| 字段 | 类型 | 权限 | 说明 |
|
|
193
193
|
|------|------|:----:|------|
|
|
194
194
|
| `type` | `RemTypeValue` | RW | `concept` / `descriptor` / `default` / `portal` |
|
|
195
|
-
| `isDocument` | `boolean` | RW | 是否作为独立文档页面。独立于 type |
|
|
195
|
+
| `isDocument` | `boolean` | RW | 是否作为独立文档页面。独立于 type。UI:bullet(•)变为文档图标,可独立打开 |
|
|
196
196
|
|
|
197
197
|
### 结构
|
|
198
198
|
|
|
199
199
|
| 字段 | 类型 | 权限 | 说明 |
|
|
200
200
|
|------|------|:----:|------|
|
|
201
|
-
| `parent` | `string \| null` | RW | 父 Rem ID。null
|
|
201
|
+
| `parent` | `string \| null` | RW | 父 Rem ID。null=顶级。UI:Rem 从原位置消失,出现在新父级下 |
|
|
202
202
|
| `children` | `string[]` | R | 子 Rem ID 有序数组 |
|
|
203
203
|
|
|
204
204
|
### 格式 / 显示
|
|
@@ -206,21 +206,21 @@ RemObject 共 51 个字段,按读写权限分为三类:
|
|
|
206
206
|
| 字段 | 类型 | 权限 | 说明 |
|
|
207
207
|
|------|------|:----:|------|
|
|
208
208
|
| `fontSize` | `FontSize \| null` | RW | 标题大小:`H1` / `H2` / `H3`。null=普通 |
|
|
209
|
-
| `highlightColor` | `HighlightColor \| null` | RW | 高亮颜色(9 种)。null
|
|
209
|
+
| `highlightColor` | `HighlightColor \| null` | RW | 高亮颜色(9 种)。null=无高亮。UI:整行背景变为对应颜色,bullet 也着色 |
|
|
210
210
|
|
|
211
211
|
### 状态标记
|
|
212
212
|
|
|
213
213
|
| 字段 | 类型 | 权限 | 说明 |
|
|
214
214
|
|------|------|:----:|------|
|
|
215
|
-
| `isTodo` | `boolean` | RW | 是否待办。设为 true 时自动初始化 todoStatus |
|
|
216
|
-
| `todoStatus` | `TodoStatus \| null` | RW | `Finished` / `Unfinished`。需先 isTodo=true |
|
|
217
|
-
| `isCode` | `boolean` | RW |
|
|
218
|
-
| `isQuote` | `boolean` | RW |
|
|
219
|
-
| `isListItem` | `boolean` | RW |
|
|
220
|
-
| `isCardItem` | `boolean` | RW |
|
|
215
|
+
| `isTodo` | `boolean` | RW | 是否待办。设为 true 时自动初始化 todoStatus。UI:文本前出现空心 checkbox(☐) |
|
|
216
|
+
| `todoStatus` | `TodoStatus \| null` | RW | `Finished` / `Unfinished`。需先 isTodo=true。UI:Finished=蓝色已勾选(☑)+文本删除线 |
|
|
217
|
+
| `isCode` | `boolean` | RW | 是否代码块。UI:等宽字体、灰色背景、块级缩进 |
|
|
218
|
+
| `isQuote` | `boolean` | RW | 是否引用块。UI:左侧灰色竖线+背景浅灰(blockquote 样式) |
|
|
219
|
+
| `isListItem` | `boolean` | RW | 是否列表项。UI:bullet(•)变为数字编号"1."(有序列表) |
|
|
220
|
+
| `isCardItem` | `boolean` | RW | 是否卡片项(多行答案行标记)。UI:无明显变化,在 Card View 中生效 |
|
|
221
221
|
| `isTable` | `boolean` | R | 是否表格(只读) |
|
|
222
|
-
| `isSlot` | `boolean` | RW | 是否 Powerup 插槽。与 isProperty
|
|
223
|
-
| `isProperty` | `boolean` | RW | 是否 Tag 属性(表格列)。与 isSlot
|
|
222
|
+
| `isSlot` | `boolean` | RW | 是否 Powerup 插槽。与 isProperty 底层相同。UI:bullet(•)变为方形图标(☐) |
|
|
223
|
+
| `isProperty` | `boolean` | RW | 是否 Tag 属性(表格列)。与 isSlot 底层相同。UI:bullet(•)变为方形图标(☐) |
|
|
224
224
|
|
|
225
225
|
### Powerup 系统标识
|
|
226
226
|
|
|
@@ -249,15 +249,15 @@ RemObject 共 51 个字段,按读写权限分为三类:
|
|
|
249
249
|
|
|
250
250
|
| 字段 | 类型 | 权限 | 说明 |
|
|
251
251
|
|------|------|:----:|------|
|
|
252
|
-
| `enablePractice` | `boolean` | RW |
|
|
253
|
-
| `practiceDirection` | `PracticeDirection` | RW | 练习方向:`forward` / `backward` / `both` / `none
|
|
252
|
+
| `enablePractice` | `boolean` | RW | 是否启用间隔重复练习。UI:无明显变化 |
|
|
253
|
+
| `practiceDirection` | `PracticeDirection` | RW | 练习方向:`forward` / `backward` / `both` / `none`。UI:无明显变化 |
|
|
254
254
|
|
|
255
255
|
### 关联 — 直接关系
|
|
256
256
|
|
|
257
257
|
| 字段 | 类型 | 权限 | 说明 |
|
|
258
258
|
|------|------|:----:|------|
|
|
259
|
-
| `tags` | `string[]` | RW | 标签 Rem ID 数组。写入时使用 diff 机制(add/remove
|
|
260
|
-
| `sources` | `string[]` | RW | 来源 Rem ID 数组。写入时使用 diff
|
|
259
|
+
| `tags` | `string[]` | RW | 标签 Rem ID 数组。写入时使用 diff 机制(add/remove)。UI:行右侧出现标签徽章 |
|
|
260
|
+
| `sources` | `string[]` | RW | 来源 Rem ID 数组。写入时使用 diff 机制。UI:Rem 下方出现灰色来源引用框 |
|
|
261
261
|
| `aliases` | `string[]` | R | 别名 Rem ID 数组 |
|
|
262
262
|
|
|
263
263
|
### 关联 — 引用关系
|
|
@@ -290,7 +290,7 @@ RemObject 共 51 个字段,按读写权限分为三类:
|
|
|
290
290
|
|
|
291
291
|
| 字段 | 类型 | 权限 | 说明 |
|
|
292
292
|
|------|------|:----:|------|
|
|
293
|
-
| `positionAmongstSiblings` | `number \| null` | RW | 在兄弟间的位置(0
|
|
293
|
+
| `positionAmongstSiblings` | `number \| null` | RW | 在兄弟间的位置(0 起始)。UI:Rem 在父级子列表中的显示位置改变 |
|
|
294
294
|
| `timesSelectedInSearch` | `number` | R-F | 搜索中被选次数 |
|
|
295
295
|
| `lastTimeMovedTo` | `number` | R-F | 上次移动时间(毫秒时间戳) |
|
|
296
296
|
| `schemaVersion` | `number` | R-F | Schema 版本号 |
|
|
@@ -394,38 +394,175 @@ localUpdatedAt, lastPracticed
|
|
|
394
394
|
|
|
395
395
|
---
|
|
396
396
|
|
|
397
|
+
## 可编辑字段约束表
|
|
398
|
+
|
|
399
|
+
以下为 20 个可编辑字段(RW)的写入约束:
|
|
400
|
+
|
|
401
|
+
| 字段 | SDK setter | 约束 / 特殊处理 |
|
|
402
|
+
|------|-----------|-----------------|
|
|
403
|
+
| `text` | `rem.setText()` | RichText 数组 |
|
|
404
|
+
| `backText` | `rem.setBackText()` | `null` → `setBackText([])`(清除背面);裸字符串自动包装为 `[string]` |
|
|
405
|
+
| `type` | `rem.setType()` | `portal` 不可设置(只能通过 `createPortal()` 创建) |
|
|
406
|
+
| `isDocument` | `rem.setIsDocument()` | — |
|
|
407
|
+
| `parent` | `rem.setParent(parentId, position?)` | 与 `positionAmongstSiblings` 联动(见下方说明) |
|
|
408
|
+
| `fontSize` | `rem.setFontSize()` | `null` → `setFontSize(undefined)`(恢复普通大小) |
|
|
409
|
+
| `highlightColor` | `rem.setHighlightColor()` / `rem.removePowerup('h')` | `null` → `removePowerup('h')`(SDK 不接受 null) |
|
|
410
|
+
| `isTodo` | `rem.setIsTodo()` | 设为 true 时自动初始化 todoStatus |
|
|
411
|
+
| `todoStatus` | `rem.setTodoStatus()` | `null` → 跳过(清除 todo 应通过 `isTodo=false`) |
|
|
412
|
+
| `isCode` | `rem.setIsCode()` | — |
|
|
413
|
+
| `isQuote` | `rem.setIsQuote()` | — |
|
|
414
|
+
| `isListItem` | `rem.setIsListItem()` | — |
|
|
415
|
+
| `isCardItem` | `rem.setIsCardItem()` | — |
|
|
416
|
+
| `isSlot` | `rem.setIsSlot()` | 与 `isProperty` 底层相同 |
|
|
417
|
+
| `isProperty` | `rem.setIsProperty()` | 与 `isSlot` 底层相同 |
|
|
418
|
+
| `enablePractice` | `rem.setEnablePractice()` | — |
|
|
419
|
+
| `practiceDirection` | `rem.setPracticeDirection()` | `forward` / `backward` / `both` / `none` |
|
|
420
|
+
| `tags` | `rem.addTag()` / `rem.removeTag()` | **Diff 机制**:对比当前 vs 目标,增删差异项。必须列出完整目标数组,缺少的会被删除 |
|
|
421
|
+
| `sources` | `rem.addSource()` / `rem.removeSource()` | **Diff 机制**:同 tags |
|
|
422
|
+
| `positionAmongstSiblings` | `rem.setParent(parent, position)` | 与 `parent` 联动(见下方说明) |
|
|
423
|
+
|
|
424
|
+
### parent + positionAmongstSiblings 联动
|
|
425
|
+
|
|
426
|
+
这两个字段通过同一个 SDK 调用 `rem.setParent(parentId, position)` 写入:
|
|
427
|
+
|
|
428
|
+
| 场景 | 行为 |
|
|
429
|
+
|------|------|
|
|
430
|
+
| 两个字段都变更 | 合并为一次 `setParent(newParent, newPosition)` 调用 |
|
|
431
|
+
| 只有 `parent` 变更 | `setParent(newParent)` 不带 position(保持末尾) |
|
|
432
|
+
| 只有 `positionAmongstSiblings` 变更 | 获取当前 parent → `setParent(currentParent, newPosition)` |
|
|
433
|
+
|
|
434
|
+
**应在同一次 str_replace 中同时修改这两个字段。**
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
397
438
|
## RichText 格式
|
|
398
439
|
|
|
399
440
|
RemObject 中的 `text` 和 `backText` 字段使用 RichText 格式——一个 JSON 数组,每个元素为纯字符串或带 `i` 字段的格式化对象。
|
|
400
441
|
|
|
401
442
|
### 元素类型
|
|
402
443
|
|
|
403
|
-
| `i` 值 | 含义 |
|
|
404
|
-
|
|
405
|
-
| (纯 string) | 纯文本片段 | — |
|
|
406
|
-
| `"m"` | 带格式文本 | `text`
|
|
407
|
-
| `"q"` | Rem 引用 | `_id`(被引用 Rem ID) |
|
|
408
|
-
| `"i"` | 图片 | `url
|
|
409
|
-
| `"x"` | LaTeX | `text` |
|
|
410
|
-
| `"a"` |
|
|
444
|
+
| `i` 值 | 含义 | 必填字段 | 可选字段 |
|
|
445
|
+
|--------|------|----------|----------|
|
|
446
|
+
| (纯 string) | 纯文本片段 | — | — |
|
|
447
|
+
| `"m"` | 带格式文本 | `text` | 格式标记(见下表) |
|
|
448
|
+
| `"q"` | Rem 引用 | `_id`(被引用 Rem ID) | `content`, `showFullName`, `aliasId` |
|
|
449
|
+
| `"i"` | 图片 | `url` | `width`, `height`, `percent`(25/50/100) |
|
|
450
|
+
| `"x"` | LaTeX | `text` | `block`(true=块级公式) |
|
|
451
|
+
| `"a"` | 音频/视频 | `url`, `onlyAudio`(**必填**) | `width`, `height` |
|
|
452
|
+
| `"s"` | 卡片分隔符 | — | `delimiterCharacterForSerialization` |
|
|
411
453
|
|
|
412
|
-
|
|
454
|
+
**注意**:`i:"a"` 的 `onlyAudio` 是**必填**字段(`true`=音频,`false`=视频),缺少会导致 SDK 拒绝写入(`Invalid input`)。
|
|
455
|
+
|
|
456
|
+
### 格式标记(主要用于 `i:"m"`,但 `i:"q"` 等元素也支持)
|
|
413
457
|
|
|
414
458
|
| 字段 | 类型 | 含义 |
|
|
415
459
|
|------|------|------|
|
|
416
460
|
| `b` | `true` | 加粗 |
|
|
417
|
-
| `l` | `true` |
|
|
461
|
+
| `l` | `true` | 斜体(小写字母 L,不是 I) |
|
|
418
462
|
| `u` | `true` | 下划线 |
|
|
419
|
-
| `h` | `number` |
|
|
420
|
-
| `tc` | `number` |
|
|
421
|
-
| `
|
|
463
|
+
| `h` | `number` | 高亮颜色(见 RemColor 枚举) |
|
|
464
|
+
| `tc` | `number` | 文字颜色(见 RemColor 枚举) |
|
|
465
|
+
| `q` | `true` | 行内代码(红色等宽样式) |
|
|
466
|
+
| `code` | `true` | 代码块(带语言标签和复制按钮) |
|
|
467
|
+
| `language` | `string` | 代码块语言(如 `"javascript"`、`"python"`) |
|
|
422
468
|
| `cId` | `string` | 完形填空 ID |
|
|
423
|
-
| `
|
|
469
|
+
| `hiddenCloze` | `true` | 完形填空隐藏状态 |
|
|
470
|
+
| `revealedCloze` | `true` | 完形填空已揭示状态 |
|
|
471
|
+
| `iUrl` | `string` | 外部超链接 URL(**注意**:`url` 字段已废弃无效,必须用 `iUrl`) |
|
|
472
|
+
| `qId` | `string` | 行内引用链接的 Rem ID |
|
|
473
|
+
|
|
474
|
+
### RemColor 颜色枚举(`h` 和 `tc` 共用)
|
|
475
|
+
|
|
476
|
+
| 值 | 颜色 | 值 | 颜色 | 值 | 颜色 |
|
|
477
|
+
|:---|:-----|:---|:-----|:---|:-----|
|
|
478
|
+
| 0 | 无颜色/默认 | 4 | Green | 7 | Gray |
|
|
479
|
+
| 1 | Red | 5 | Purple | 8 | Brown |
|
|
480
|
+
| 2 | Orange | 6 | Blue | 9 | Pink |
|
|
481
|
+
| 3 | Yellow | — | — | — | — |
|
|
482
|
+
|
|
483
|
+
### 常用构造示例
|
|
484
|
+
|
|
485
|
+
以下示例展示实际 `JSON.stringify(null, 2)` 格式化后的样子(key 按字母序排列):
|
|
486
|
+
|
|
487
|
+
```jsonc
|
|
488
|
+
// 粗体
|
|
489
|
+
{
|
|
490
|
+
"b": true,
|
|
491
|
+
"i": "m",
|
|
492
|
+
"text": "粗体"
|
|
493
|
+
}
|
|
494
|
+
// 行内代码
|
|
495
|
+
{
|
|
496
|
+
"i": "m",
|
|
497
|
+
"q": true,
|
|
498
|
+
"text": "console.log()"
|
|
499
|
+
}
|
|
500
|
+
// 超链接
|
|
501
|
+
{
|
|
502
|
+
"i": "m",
|
|
503
|
+
"iUrl": "https://example.com",
|
|
504
|
+
"text": "点击访问"
|
|
505
|
+
}
|
|
506
|
+
// 红色高亮 + 粗体(h 是 RichText 行内格式标记,值为数字)
|
|
507
|
+
{
|
|
508
|
+
"b": true,
|
|
509
|
+
"h": 1,
|
|
510
|
+
"i": "m",
|
|
511
|
+
"text": "重点"
|
|
512
|
+
}
|
|
513
|
+
// 完形填空
|
|
514
|
+
{
|
|
515
|
+
"cId": "cloze1",
|
|
516
|
+
"i": "m",
|
|
517
|
+
"text": "答案内容"
|
|
518
|
+
}
|
|
519
|
+
// Rem 引用(_id 排在小写字母之前)
|
|
520
|
+
{
|
|
521
|
+
"_id": "remId",
|
|
522
|
+
"i": "q"
|
|
523
|
+
}
|
|
524
|
+
// Rem 引用 + 加粗
|
|
525
|
+
{
|
|
526
|
+
"_id": "remId",
|
|
527
|
+
"b": true,
|
|
528
|
+
"i": "q"
|
|
529
|
+
}
|
|
530
|
+
// LaTeX 块级公式
|
|
531
|
+
{
|
|
532
|
+
"block": true,
|
|
533
|
+
"i": "x",
|
|
534
|
+
"text": "\\frac{a}{b}"
|
|
535
|
+
}
|
|
536
|
+
// 视频(onlyAudio 必填!)
|
|
537
|
+
{
|
|
538
|
+
"i": "a",
|
|
539
|
+
"onlyAudio": false,
|
|
540
|
+
"url": "https://youtube.com/watch?v=xxx"
|
|
541
|
+
}
|
|
542
|
+
// 音频
|
|
543
|
+
{
|
|
544
|
+
"i": "a",
|
|
545
|
+
"onlyAudio": true,
|
|
546
|
+
"url": "https://example.com/audio.mp3"
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### highlightColor(Rem 级别)vs h(RichText 行内格式)
|
|
551
|
+
|
|
552
|
+
| 属性 | 位置 | 值类型 | 作用范围 | 示例 |
|
|
553
|
+
|------|------|--------|----------|------|
|
|
554
|
+
| `highlightColor` | RemObject 顶层字段 | 字符串(`"Red"`, `"Blue"` 等)或 `null` | 整个 Rem(整行背景色) | `"highlightColor": "Red"` |
|
|
555
|
+
| `h` | RichText 元素内的格式标记 | 数字(0-9,见 RemColor 枚举) | 行内文字片段 | `{"h": 1, "i": "m", "text": "高亮"}` |
|
|
556
|
+
|
|
557
|
+
两者完全独立。`highlightColor` 通过 `rem.setHighlightColor()` / `rem.removePowerup('h')` 写入;`h` 是 RichText JSON 内的字段,通过 `rem.setText()` 写入。
|
|
424
558
|
|
|
425
559
|
### 序列化确定性
|
|
426
560
|
|
|
427
561
|
RichText 对象元素内部按 **key 字母序排列**(Plugin 端 `sortRichTextKeys()` 处理),确保同一内容的序列化 JSON 始终一致。这对 `edit-rem` 的 str_replace 和乐观并发检测至关重要。
|
|
428
562
|
|
|
563
|
+
- `_`(下划线)在 Unicode 中排在所有小写字母之前(`_` = U+005F,`a` = U+0061),所以 `_id` 总是排在第一位
|
|
564
|
+
- 排序由 `Object.keys().sort()` 决定,即 JavaScript 默认的 Unicode 字典序
|
|
565
|
+
|
|
429
566
|
---
|
|
430
567
|
|
|
431
568
|
## Powerup 过滤
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|