remnote-bridge 0.1.14 → 0.1.15
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 +6 -0
- package/README.zh-CN.md +6 -0
- package/dist/cli/handlers/edit-handler.js +14 -0
- package/dist/mcp/instructions.js +62 -68
- package/dist/mcp/tools/infra-tools.js +1 -1
- package/package.json +1 -1
- package/remnote-plugin/dist/index-sandbox.js +1 -1
- package/remnote-plugin/dist/index.js +1 -1
- package/remnote-plugin/dist/manifest.json +1 -1
- package/remnote-plugin/package.json +1 -1
- package/remnote-plugin/public/manifest.json +1 -1
- package/remnote-plugin/src/services/read-rem.ts +16 -16
- package/remnote-plugin/src/settings.ts +1 -1
- package/skills/remnote-bridge/SKILL.md +13 -4
- package/skills/remnote-bridge/instructions/connect.md +19 -7
- package/skills/remnote-bridge/instructions/edit-tree.md +89 -92
- package/skills/remnote-bridge/instructions/overall.md +6 -0
package/README.md
CHANGED
|
@@ -292,6 +292,12 @@ remnote-bridge addon uninstall remnote-rag --purge
|
|
|
292
292
|
|
|
293
293
|
## Changelog
|
|
294
294
|
|
|
295
|
+
### 0.1.15 (2026-03-18)
|
|
296
|
+
|
|
297
|
+
- **Defense-2 false positive fix** — Removed `.sort()` from ID arrays in RemObject serialization that caused spurious concurrency conflicts in edit-rem
|
|
298
|
+
- **Documentation overhaul** — Strengthened headless mode warnings, added anti-hallucination guardrails for Plugin loading instructions, restructured edit-tree template/fallback dual-mode docs
|
|
299
|
+
- **Skill ↔ MCP doc sync** — Aligned connect, edit-tree, overall instructions across Skill and MCP layers
|
|
300
|
+
|
|
295
301
|
### 0.1.14 (2026-03-18)
|
|
296
302
|
|
|
297
303
|
- **read-rem-in-tree** — New command: fetch subtree outline + all Rem objects in one call (batch read for bulk edits)
|
package/README.zh-CN.md
CHANGED
|
@@ -292,6 +292,12 @@ remnote-bridge addon uninstall remnote-rag --purge
|
|
|
292
292
|
|
|
293
293
|
## Changelog
|
|
294
294
|
|
|
295
|
+
### 0.1.15 (2026-03-18)
|
|
296
|
+
|
|
297
|
+
- **防线 2 误判修复** — 移除 RemObject 序列化中 ID 数组的 `.sort()`,消除 edit-rem 并发检测的假阳性
|
|
298
|
+
- **文档全面优化** — 加强 headless 模式警告、新增 Plugin 加载防幻觉红线、重组 edit-tree 模板/回退双模式文档
|
|
299
|
+
- **Skill ↔ MCP 文档同步** — connect、edit-tree、overall 说明在 Skill 和 MCP 层全面对齐
|
|
300
|
+
|
|
295
301
|
### 0.1.14 (2026-03-18)
|
|
296
302
|
|
|
297
303
|
- **read-rem-in-tree** — 新命令:一次调用获取子树大纲 + 所有 Rem 对象(批量读取,用于批量编辑场景)
|
|
@@ -63,6 +63,9 @@ export class EditHandler {
|
|
|
63
63
|
const currentJson = JSON.stringify(currentRemObject, null, 2);
|
|
64
64
|
const cachedJson = JSON.stringify(cachedObj, null, 2);
|
|
65
65
|
if (currentJson !== cachedJson) {
|
|
66
|
+
// 诊断日志:打出具体哪些字段不同,帮助定位并发误判
|
|
67
|
+
const diff = diffFields(cachedObj, currentRemObject);
|
|
68
|
+
console.error(`[defense-2] Rem ${remId} conflict detected. Changed fields: ${diff.join(', ') || '(JSON differs but no top-level field diff — possible nested change)'}`);
|
|
66
69
|
// 不更新缓存 — 迫使 AI re-read
|
|
67
70
|
throw new Error(`Rem ${remId} has been modified since last read. Please read it again before editing.`);
|
|
68
71
|
}
|
|
@@ -126,3 +129,14 @@ export class EditHandler {
|
|
|
126
129
|
};
|
|
127
130
|
}
|
|
128
131
|
}
|
|
132
|
+
/** 比较两个 RemObject 的顶层字段,返回值不同的 key 列表 */
|
|
133
|
+
function diffFields(cached, current) {
|
|
134
|
+
const allKeys = new Set([...Object.keys(cached), ...Object.keys(current)]);
|
|
135
|
+
const changed = [];
|
|
136
|
+
for (const key of allKeys) {
|
|
137
|
+
if (JSON.stringify(cached[key]) !== JSON.stringify(current[key])) {
|
|
138
|
+
changed.push(key);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return changed;
|
|
142
|
+
}
|
package/dist/mcp/instructions.js
CHANGED
|
@@ -119,12 +119,20 @@ RemNote 格式设置(标题、高亮、代码等)底层通过 Powerup 机制
|
|
|
119
119
|
|
|
120
120
|
所有操作都依赖一个活跃的会话(= 守护进程的生命周期)。
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
> **⚠️ 关于 Plugin 加载方式(防幻觉红线)**:
|
|
123
|
+
> - 本插件是**开发者插件**,通过 RemNote「开发你的插件」功能加载本地 URL
|
|
124
|
+
> - **禁止**告诉用户"去插件市场/商店搜索安装"——本插件**不在 RemNote 插件市场中**
|
|
125
|
+
> - **禁止**告诉用户"Settings → Plugins"——这个路径不存在
|
|
126
|
+
> - **禁止**编造不存在的安装流程——严格按照下方步骤引导用户
|
|
127
|
+
|
|
128
|
+
### 标准模式(默认,推荐)
|
|
129
|
+
|
|
130
|
+
**标准模式是日常使用的推荐方式**。用户在自己的浏览器中打开 RemNote 并加载 Plugin,Agent 可以通过 \`read_context\` 感知用户正在浏览的页面和焦点位置,实现真正的协作。
|
|
123
131
|
|
|
124
132
|
\`\`\`
|
|
125
133
|
connect → 启动 daemon(幂等)
|
|
126
134
|
↓
|
|
127
|
-
⚠️
|
|
135
|
+
⚠️ 引导用户加载 Plugin(见下方 ★ 标记的步骤)
|
|
128
136
|
↓
|
|
129
137
|
health → 确认三层就绪(daemon / Plugin / SDK)
|
|
130
138
|
↓
|
|
@@ -133,11 +141,34 @@ health → 确认三层就绪(daemon / Plugin / SDK)
|
|
|
133
141
|
disconnect → 关闭 daemon,清空所有缓存
|
|
134
142
|
\`\`\`
|
|
135
143
|
|
|
136
|
-
###
|
|
144
|
+
### ★ 标准模式:connect 后引导用户加载 Plugin(核心步骤)
|
|
145
|
+
|
|
146
|
+
\`connect\`(不传 headless)成功只意味着 daemon 和 Plugin 服务已启动,**Plugin 并未自动连接**。你必须引导用户完成以下操作:
|
|
147
|
+
|
|
148
|
+
**首次使用**(RemNote 从未加载过此插件):
|
|
149
|
+
1. 打开 RemNote 桌面端或网页端
|
|
150
|
+
2. 点击左侧边栏底部的**插件图标**(拼图形状)
|
|
151
|
+
3. 点击「**开发你的插件**」(Develop Your Plugin)
|
|
152
|
+
4. 在输入框中填入 connect 输出的 **Plugin 服务地址**(如 \`http://localhost:29101\`)
|
|
153
|
+
5. 等待插件加载完成
|
|
154
|
+
|
|
155
|
+
**非首次使用**(之前已加载过此插件):
|
|
156
|
+
- 只需**刷新 RemNote 页面**即可(浏览器 F5 或 Cmd+R)
|
|
157
|
+
|
|
158
|
+
**你必须**:执行 \`connect\` 后,**立即**将上述步骤告知用户,**禁止**跳过此步直接调用业务命令。引导用户完成后,用 \`health\` 确认三层就绪再继续。
|
|
159
|
+
|
|
160
|
+
### Headless 模式(特殊场景,不推荐日常使用)
|
|
137
161
|
|
|
138
162
|
通过 setup(一次性)+ headless Chrome 实现自动连接,后续 connect 无需用户介入。
|
|
139
163
|
|
|
140
|
-
**⚠️
|
|
164
|
+
**⚠️ 不推荐日常使用**。Headless Chrome 是后台独立实例,**会丢失用户上下文**:\`read_context\` 返回的是 headless Chrome 的上下文,不是用户浏览器的。Agent 无法感知用户正在浏览和操作的页面,协作体验大打折扣。
|
|
165
|
+
|
|
166
|
+
**仅在以下场景使用 headless**:
|
|
167
|
+
- 用户明确要求在**服务器/无 GUI 环境**中运行
|
|
168
|
+
- 用户明确表示**不想参与操作**,希望全自动化(CI/CD、定时任务、批量处理等)
|
|
169
|
+
- 用户自己不在 RemNote 前面,不需要与 Agent 协作浏览
|
|
170
|
+
|
|
171
|
+
**默认始终使用标准模式**,除非用户主动要求 headless。
|
|
141
172
|
|
|
142
173
|
#### 首次使用(setup)
|
|
143
174
|
|
|
@@ -169,22 +200,6 @@ disconnect → 关闭 daemon + headless Chrome,清空所有缓存,清除 hea
|
|
|
169
200
|
- \`health(reload=true)\`:重载 headless Chrome 页面
|
|
170
201
|
- Plugin 始终不连接 → 可能登录 session 过期,需重新 setup
|
|
171
202
|
|
|
172
|
-
### ⚠️ 标准模式:connect 后需要用户配合(重要)
|
|
173
|
-
|
|
174
|
-
\`connect\`(不传 headless)成功只意味着 daemon 和 Plugin 服务已启动,**Plugin 并未自动连接**。
|
|
175
|
-
|
|
176
|
-
**首次使用**(RemNote 从未加载过此插件):
|
|
177
|
-
1. 打开 RemNote 桌面端或网页端
|
|
178
|
-
2. 点击左侧边栏底部的插件图标(拼图形状)
|
|
179
|
-
3. 点击「开发你的插件」(Develop Your Plugin)
|
|
180
|
-
4. 在输入框中填入 connect 输出的 Plugin 服务地址(如 \`http://localhost:29101\`)
|
|
181
|
-
5. 等待插件加载完成
|
|
182
|
-
|
|
183
|
-
**非首次使用**(之前已加载过此插件):
|
|
184
|
-
- 只需**刷新 RemNote 页面**即可(浏览器 F5 或 Cmd+R)
|
|
185
|
-
|
|
186
|
-
**你必须**:执行 \`connect\` 后,**立即告知用户需要完成上述操作**,不要直接调用业务命令。引导用户完成后,用 \`health\` 确认三层就绪再继续。
|
|
187
|
-
|
|
188
203
|
### Windows 注意事项
|
|
189
204
|
|
|
190
205
|
- 默认模式秒级启动(预构建 plugin)
|
|
@@ -326,59 +341,39 @@ changes: { "type": "concept", "highlightColor": "Yellow", "fontSize": "H1" }
|
|
|
326
341
|
- \`<!--portal refs:id1,id2-->\`(创建并引用指定 Rem)
|
|
327
342
|
- \`<!--portal-->\`(创建空 Portal)
|
|
328
343
|
|
|
329
|
-
####
|
|
344
|
+
#### 行引用模板 \`{{remId}}\`
|
|
330
345
|
|
|
331
|
-
|
|
332
|
-
\`\`\`
|
|
333
|
-
oldStr:
|
|
334
|
-
最后一个兄弟 <!--idZ-->
|
|
335
|
-
newStr:
|
|
336
|
-
最后一个兄弟 <!--idZ-->
|
|
337
|
-
新增行
|
|
338
|
-
\`\`\`
|
|
346
|
+
已有行支持两种写法:**模板模式**(推荐)和**完整匹配模式**(回退)。
|
|
339
347
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
oldStr:
|
|
343
|
-
子节点 A <!--idA-->
|
|
344
|
-
孙节点 A1 <!--idA1-->
|
|
345
|
-
子节点 B <!--idB-->
|
|
346
|
-
newStr:
|
|
347
|
-
子节点 B <!--idB-->
|
|
348
|
-
\`\`\`
|
|
348
|
+
**模板模式**(优先使用):用 \`{{remId}}\` 引用已有行,系统自动展开为完整内容(不含缩进)。节省 token、减少复制错误。
|
|
349
|
+
**完整匹配模式**(回退):直接从大纲复制完整行内容(含 \`<!--remId 元数据-->\`)。
|
|
349
350
|
|
|
350
|
-
|
|
351
|
-
\`\`\`
|
|
352
|
-
oldStr:
|
|
353
|
-
子节点 A <!--idA-->
|
|
354
|
-
newStr:
|
|
355
|
-
什么是线性回归? ↓
|
|
356
|
-
一种基本的回归分析方法
|
|
357
|
-
假设因变量与自变量呈线性关系
|
|
358
|
-
子节点 A <!--idA-->
|
|
359
|
-
\`\`\`
|
|
351
|
+
**策略:优先模板,连续失败则回退**。如果模板模式连续 2+ 次因 ID 错误导致 \`old_str not found\`,说明当前上下文不足以准确引用 ID——立即切换到完整匹配模式(从最新大纲复制完整行内容),不要反复重试模板。
|
|
360
352
|
|
|
361
|
-
|
|
353
|
+
**模板模式示例**:
|
|
354
|
+
\`\`\`
|
|
355
|
+
# 重排两个节点
|
|
356
|
+
oldStr: " {{id1_1}}\\n {{id1_2}}"
|
|
357
|
+
newStr: " {{id1_2}}\\n {{id1_1}}"
|
|
362
358
|
|
|
363
|
-
|
|
359
|
+
# 删除带子节点的行
|
|
360
|
+
oldStr: " {{idA}}\\n {{idA1}}\\n {{idB}}"
|
|
361
|
+
newStr: " {{idB}}"
|
|
364
362
|
|
|
365
|
-
|
|
363
|
+
# 末尾新增行(新增行手动写,已有行用模板)
|
|
364
|
+
oldStr: " {{idZ}}"
|
|
365
|
+
newStr: " 新增行\\n {{idZ}}"
|
|
366
|
+
\`\`\`
|
|
366
367
|
|
|
367
|
-
|
|
368
|
+
**完整匹配模式示例**(回退时使用):
|
|
368
369
|
\`\`\`
|
|
369
|
-
// 不用模板:
|
|
370
370
|
oldStr: " 动态数组 <!--id1_1 type:concept-->\\n 静态数组 <!--id1_2 type:concept-->"
|
|
371
371
|
newStr: " 静态数组 <!--id1_2 type:concept-->\\n 动态数组 <!--id1_1 type:concept-->"
|
|
372
|
-
|
|
373
|
-
// 用模板:
|
|
374
|
-
oldStr: " {{id1_1}}\\n {{id1_2}}"
|
|
375
|
-
newStr: " {{id1_2}}\\n {{id1_1}}"
|
|
376
372
|
\`\`\`
|
|
377
373
|
|
|
378
|
-
|
|
374
|
+
**模板规则**:
|
|
379
375
|
- \`{{remId}}\` 展开为**不含缩进**的完整行内容,缩进由你控制
|
|
380
|
-
- 只匹配纯字母数字(\`[a-zA-Z0-9]+\`),与 RemNote cloze 语法 \`{{text}}\`
|
|
381
|
-
- 匹配到但不在缓存大纲中的 \`{{xxx}}\` 原样保留(可能是 cloze),并输出 warning
|
|
376
|
+
- 只匹配纯字母数字(\`[a-zA-Z0-9]+\`),与 RemNote cloze 语法 \`{{text}}\` 不冲突
|
|
382
377
|
- 新增行不能用 \`{{}}\`(新增行没有 remId)
|
|
383
378
|
- 可以混用:部分行用 \`{{id}}\`,部分行手动写
|
|
384
379
|
|
|
@@ -387,15 +382,13 @@ newStr: " {{id1_2}}\\n {{id1_1}}"
|
|
|
387
382
|
在有子节点的 Rem 和其 children 之间插入新行,会导致新行"劫持"已有 children:
|
|
388
383
|
|
|
389
384
|
\`\`\`
|
|
390
|
-
❌
|
|
391
|
-
oldStr:
|
|
392
|
-
|
|
393
|
-
|
|
385
|
+
❌ 错误(模板):
|
|
386
|
+
oldStr: " {{idA}}" newStr: " {{idA}}\\n 新行" ← idA 有子节点,新行劫持 children!
|
|
387
|
+
❌ 错误(完整匹配):
|
|
388
|
+
oldStr: " 水分子 ↓ <!--idA-->" newStr: " 水分子 ↓ <!--idA-->\\n 新行" ← 同理
|
|
394
389
|
|
|
395
390
|
✅ 正确:插在末尾
|
|
396
|
-
oldStr:
|
|
397
|
-
newStr: 最后一个兄弟 <!--idZ-->
|
|
398
|
-
新行
|
|
391
|
+
oldStr: " {{idZ}}" newStr: " {{idZ}}\\n 新行"
|
|
399
392
|
\`\`\`
|
|
400
393
|
|
|
401
394
|
#### 创建新节点并移动已有 children
|
|
@@ -427,7 +420,7 @@ newStr: 最后一个兄弟 <!--idZ-->
|
|
|
427
420
|
|
|
428
421
|
\`health\` 默认查询所有活跃实例的三层状态(daemon / Plugin / SDK),返回 \`instances\` 数组。有 \`--instance\` 或 \`--headless\` 时只查询指定实例。每个实例的 \`plugin.isTwin\` 标记是否为孪生连接。
|
|
429
422
|
|
|
430
|
-
故障定位:无活跃实例 → \`connect\`;Plugin 未连接 → 引导用户操作 RemNote
|
|
423
|
+
故障定位:无活跃实例 → \`connect\`;Plugin 未连接 → 引导用户操作 RemNote(标准模式下刷新页面或首次加载插件);SDK 未就绪 → 等待重试。
|
|
431
424
|
|
|
432
425
|
### 场景 H:管理增强项目
|
|
433
426
|
|
|
@@ -643,6 +636,7 @@ tags, sources, positionAmongstSiblings, portalDirectlyIncludedRem
|
|
|
643
636
|
|
|
644
637
|
### 诊断决策树
|
|
645
638
|
|
|
639
|
+
|
|
646
640
|
\`\`\`
|
|
647
641
|
命令报错
|
|
648
642
|
├─ "守护进程未运行" → connect 未执行或 daemon 已超时 → 执行 connect
|
|
@@ -22,7 +22,7 @@ export function registerInfraTools(server) {
|
|
|
22
22
|
// -------------------------------------------------------------------------
|
|
23
23
|
server.addTool({
|
|
24
24
|
name: 'connect',
|
|
25
|
-
description: '启动守护进程(daemon),建立 CLI 与 RemNote Plugin 之间的通信通道。这是所有业务命令(read_rem、edit_rem、search 等)的前置步骤。\n\n适用场景:\n- 开始一次 RemNote 操作会话前,必须先调用此工具\n- 不确定 daemon 是否在运行时,也可安全调用(幂等)\n\n两种模式:\n-
|
|
25
|
+
description: '启动守护进程(daemon),建立 CLI 与 RemNote Plugin 之间的通信通道。这是所有业务命令(read_rem、edit_rem、search 等)的前置步骤。\n\n适用场景:\n- 开始一次 RemNote 操作会话前,必须先调用此工具\n- 不确定 daemon 是否在运行时,也可安全调用(幂等)\n\n两种模式:\n- 标准模式(默认,推荐):启动 daemon 后用户在自己的浏览器中加载 Plugin。优势:Agent 可通过 read_context 感知用户正在浏览的页面,实现协作\n- Headless 模式(headless=true,不推荐日常使用):自动启动 headless Chrome,无需用户操作。⚠️ 会丢失用户上下文(read_context 返回 headless 实例的上下文,不是用户浏览器的)。仅在以下场景使用:用户明确要求在服务器/无 GUI 环境运行、用户不想参与操作(全自动化)、用户不在 RemNote 前面。需先完成 setup\n\n⚠️ 标准模式 Plugin 加载(connect 后必须引导用户完成):\n本插件是开发者插件,不在 RemNote 插件市场中,禁止告诉用户去市场搜索安装。正确步骤:\n- 首次:RemNote → 左下角插件图标 → 「开发你的插件」→ 填入 Plugin 服务地址(如 http://localhost:29101)\n- 非首次:刷新 RemNote 页面即可\n引导用户完成后用 health 确认三层就绪再执行业务命令。\n\nHeadless 模式会话持久化:connect(headless=true) 后,MCP Server 自动记住 headless 状态,后续所有工具调用自动路由到 headless 实例。disconnect 或 clean 后自动清除。\n\n输出:返回 JSON,关键字段 ok、alreadyRunning、instance、slotIndex、pid、wsPort、headless。\n幂等:重复调用不会启动多个 daemon。daemon 默认 30 分钟无活动自动关闭。\n关联工具:setup(headless 前置)、disconnect(结束会话)、health(检查状态)',
|
|
26
26
|
parameters: z.object({
|
|
27
27
|
headless: z.boolean().optional().describe('启用 headless 模式:自动启动 Chrome 加载 Plugin(需先 setup)'),
|
|
28
28
|
}),
|
package/package.json
CHANGED
|
@@ -55,7 +55,7 @@ Add a <Suspense fallback=...> component higher in the tree to provide a loading
|
|
|
55
55
|
*
|
|
56
56
|
* This source code is licensed under the MIT license found in the
|
|
57
57
|
* LICENSE file in the root directory of this source tree.
|
|
58
|
-
*/var Xe,Ot,Ae,it;if(typeof performance=="object"&&typeof performance.now=="function"){var B=performance;K.unstable_now=function(){return B.now()}}else{var wn=Date,Et=wn.now();K.unstable_now=function(){return wn.now()-Et}}if(typeof window>"u"||typeof MessageChannel!="function"){var Tt=null,b=null,Y=function(){if(Tt!==null)try{var F=K.unstable_now();Tt(!0,F),Tt=null}catch(A){throw setTimeout(Y,0),A}};Xe=function(F){Tt!==null?setTimeout(Xe,0,F):(Tt=F,setTimeout(Y,0))},Ot=function(F,A){b=setTimeout(F,A)},Ae=function(){clearTimeout(b)},K.unstable_shouldYield=function(){return!1},it=K.unstable_forceFrameRate=function(){}}else{var c=window.setTimeout,Mt=window.clearTimeout;if(typeof console<"u"){var ht=window.cancelAnimationFrame;typeof window.requestAnimationFrame!="function"&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills"),typeof ht!="function"&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills")}var Ve=!1,at=null,Pt=-1,tt=5,ot=0;K.unstable_shouldYield=function(){return K.unstable_now()>=ot},it=function(){},K.unstable_forceFrameRate=function(F){0>F||125<F?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):tt=0<F?Math.floor(1e3/F):5};var te=new MessageChannel,ae=te.port2;te.port1.onmessage=function(){if(at!==null){var F=K.unstable_now();ot=F+tt;try{at(!0,F)?ae.postMessage(null):(Ve=!1,at=null)}catch(A){throw ae.postMessage(null),A}}else Ve=!1},Xe=function(F){at=F,Ve||(Ve=!0,ae.postMessage(null))},Ot=function(F,A){Pt=c(function(){F(K.unstable_now())},A)},Ae=function(){Mt(Pt),Pt=-1}}function _e(F,A){var $=F.length;F.push(A);e:for(;;){var H=$-1>>>1,ne=F[H];if(ne!==void 0&&0<st(ne,A))F[H]=A,F[$]=ne,$=H;else break e}}function Ne(F){return F=F[0],F===void 0?null:F}function He(F){var A=F[0];if(A!==void 0){var $=F.pop();if($!==A){F[0]=$;e:for(var H=0,ne=F.length;H<ne;){var U=2*(H+1)-1,ye=F[U],M=U+1,sn=F[M];if(ye!==void 0&&0>st(ye,$))sn!==void 0&&0>st(sn,ye)?(F[H]=sn,F[M]=$,H=M):(F[H]=ye,F[U]=$,H=U);else if(sn!==void 0&&0>st(sn,$))F[H]=sn,F[M]=$,H=M;else break e}}return A}return null}function st(F,A){var $=F.sortIndex-A.sortIndex;return $!==0?$:F.id-A.id}var Z=[],Ze=[],Ye=1,ue=null,fe=3,ee=!1,ie=!1,me=!1;function q(F){for(var A=Ne(Ze);A!==null;){if(A.callback===null)He(Ze);else if(A.startTime<=F)He(Ze),A.sortIndex=A.expirationTime,_e(Z,A);else break;A=Ne(Ze)}}function ge(F){if(me=!1,q(F),!ie)if(Ne(Z)!==null)ie=!0,Xe(C);else{var A=Ne(Ze);A!==null&&Ot(ge,A.startTime-F)}}function C(F,A){ie=!1,me&&(me=!1,Ae()),ee=!0;var $=fe;try{for(q(A),ue=Ne(Z);ue!==null&&(!(ue.expirationTime>A)||F&&!K.unstable_shouldYield());){var H=ue.callback;if(typeof H=="function"){ue.callback=null,fe=ue.priorityLevel;var ne=H(ue.expirationTime<=A);A=K.unstable_now(),typeof ne=="function"?ue.callback=ne:ue===Ne(Z)&&He(Z),q(A)}else He(Z);ue=Ne(Z)}if(ue!==null)var U=!0;else{var ye=Ne(Ze);ye!==null&&Ot(ge,ye.startTime-A),U=!1}return U}finally{ue=null,fe=$,ee=!1}}var V=it;K.unstable_IdlePriority=5,K.unstable_ImmediatePriority=1,K.unstable_LowPriority=4,K.unstable_NormalPriority=3,K.unstable_Profiling=null,K.unstable_UserBlockingPriority=2,K.unstable_cancelCallback=function(F){F.callback=null},K.unstable_continueExecution=function(){ie||ee||(ie=!0,Xe(C))},K.unstable_getCurrentPriorityLevel=function(){return fe},K.unstable_getFirstCallbackNode=function(){return Ne(Z)},K.unstable_next=function(F){switch(fe){case 1:case 2:case 3:var A=3;break;default:A=fe}var $=fe;fe=A;try{return F()}finally{fe=$}},K.unstable_pauseExecution=function(){},K.unstable_requestPaint=V,K.unstable_runWithPriority=function(F,A){switch(F){case 1:case 2:case 3:case 4:case 5:break;default:F=3}var $=fe;fe=F;try{return A()}finally{fe=$}},K.unstable_scheduleCallback=function(F,A,$){var H=K.unstable_now();switch(typeof $=="object"&&$!==null?($=$.delay,$=typeof $=="number"&&0<$?H+$:H):$=H,F){case 1:var ne=-1;break;case 2:ne=250;break;case 5:ne=1073741823;break;case 4:ne=1e4;break;default:ne=5e3}return ne=$+ne,F={id:Ye++,callback:A,priorityLevel:F,startTime:$,expirationTime:ne,sortIndex:-1},$>H?(F.sortIndex=$,_e(Ze,F),Ne(Z)===null&&F===Ne(Ze)&&(me?Ae():me=!0,Ot(ge,$-H))):(F.sortIndex=ne,_e(Z,F),ie||ee||(ie=!0,Xe(C))),F},K.unstable_wrapCallback=function(F){var A=fe;return function(){var $=fe;fe=A;try{return F.apply(this,arguments)}finally{fe=$}}}},825(en,K,Xe){"use strict";en.exports=Xe(742)}},vd={};function Ac(en){var K=vd[en];if(K!==void 0)return K.exports;var Xe=vd[en]={exports:{}};return Pf[en](Xe,Xe.exports,Ac),Xe.exports}Ac.g=(function(){if(typeof globalThis=="object")return globalThis;try{return this||new Function("return this")()}catch{if(typeof window=="object")return window}})();var wp={};(()=>{"use strict";var en=Ac(216);const K="0.2.0",Xe=[29100,29110,29120,29130],Ot=18e3;var Ae=Object.defineProperty,it=(w,y,E)=>y in w?Ae(w,y,{enumerable:!0,configurable:!0,writable:!0,value:E}):w[y]=E,B=(w,y,E)=>it(w,typeof y!="symbol"?y+"":y,E);const wn=4e3,Et=4003;class Tt{constructor(y){B(this,"ws",null),B(this,"reconnectAttempts",0),B(this,"reconnectTimeout",null),B(this,"messageHandler",null),B(this,"status","disconnected"),B(this,"isShuttingDown",!1),B(this,"isPreempted",!1),B(this,"_sdkReady"),B(this,"config"),this._sdkReady=y.sdkReady,this.config={url:y.url,pluginVersion:y.pluginVersion,sdkReady:y.sdkReady,twinSlotIndex:y.twinSlotIndex,isTwinConnection:y.isTwinConnection??!1,maxReconnectAttempts:y.maxReconnectAttempts??10,initialReconnectDelay:y.initialReconnectDelay??1e3,maxReconnectDelay:y.maxReconnectDelay??3e4,onStatusChange:y.onStatusChange,onLog:y.onLog,onPreempted:y.onPreempted,onTwinOccupied:y.onTwinOccupied,onOtherOccupied:y.onOtherOccupied}}log(y,E="info"){this.config.onLog?.(y,E)}setStatus(y){this.status!==y&&(this.status=y,this.config.onStatusChange?.(y))}sendHello(){const y={type:"hello",version:this.config.pluginVersion,sdkReady:this._sdkReady,twinSlotIndex:this.config.twinSlotIndex};try{this.ws?.send(JSON.stringify(y)),this.log(`\u53D1\u9001 hello\uFF08v${this.config.pluginVersion}, sdkReady=${this._sdkReady}, twinSlot=${this.config.twinSlotIndex}\uFF09`)}catch(E){this.log(`\u53D1\u9001 hello \u5931\u8D25: ${E}`,"warn")}}connect(){if(!(this.ws?.readyState===WebSocket.OPEN||this.ws?.readyState===WebSocket.CONNECTING)){this.isShuttingDown=!1,this.isPreempted=!1,this.setStatus("connecting");try{this.ws=new WebSocket(this.config.url),this.ws.onopen=()=>{this.log("\u5DF2\u8FDE\u63A5\u5230\u5B88\u62A4\u8FDB\u7A0B"),this.reconnectAttempts=0,this.setStatus("connected"),this.sendHello()},this.ws.onmessage=async y=>{await this.handleMessage(typeof y.data=="string"?y.data:String(y.data))},this.ws.onclose=y=>{y.code!==1006&&this.log(`\u8FDE\u63A5\u65AD\u5F00: ${y.code} ${y.reason}`,"warn"),this.setStatus("disconnected"),y.code===Et?this.config.onTwinOccupied?.():y.code===wn&&this.config.onOtherOccupied?.(),this.isShuttingDown||this.scheduleReconnect()},this.ws.onerror=()=>{}}catch(y){this.log(`\u8FDE\u63A5\u5931\u8D25: ${y}`,"error"),this.setStatus("disconnected"),this.scheduleReconnect()}}}async handleMessage(y){try{const E=JSON.parse(y);if(E.type==="preempted"){this.isPreempted=!0,this.log(`\u88AB\u5B6A\u751F Plugin \u62A2\u5360: ${E.reason}`,"warn"),this.config.onPreempted?.();return}if(E.type==="ping"){this.ws?.send(JSON.stringify({type:"pong"}));return}if(E.id&&E.action&&this.messageHandler){const R=E;this.log(`\u6536\u5230\u8BF7\u6C42: ${R.action}`);try{const N=await this.messageHandler(R),j={id:R.id,result:N};this.ws?.send(JSON.stringify(j)),this.log(`\u5B8C\u6210: ${R.action}`)}catch(N){const j=N instanceof Error?N.message:String(N),Q={id:R.id,error:j};this.ws?.send(JSON.stringify(Q)),this.log(`\u5931\u8D25: ${R.action} - ${j}`,"error")}}}catch(E){this.log(`\u5904\u7406\u6D88\u606F\u5931\u8D25: ${E}`,"error")}}scheduleReconnect(){if(this.isShuttingDown||this.isPreempted||!this.config.isTwinConnection)return;if(this.reconnectAttempts>=this.config.maxReconnectAttempts){this.log("\u5DF2\u8FBE\u6700\u5927\u91CD\u8FDE\u6B21\u6570","error");return}const y=Math.min(this.config.initialReconnectDelay*Math.pow(2,this.reconnectAttempts),this.config.maxReconnectDelay),E=Math.random()*.3*y,R=y+E;this.reconnectAttempts++,this.log(`${Math.round(R)}ms \u540E\u91CD\u8FDE\uFF08\u7B2C ${this.reconnectAttempts}/${this.config.maxReconnectAttempts} \u6B21\uFF09`),this.reconnectTimeout=setTimeout(()=>{this.connect()},R)}setMessageHandler(y){this.messageHandler=y}disconnect(){this.isShuttingDown=!0,this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.ws&&(this.ws.close(1e3,"Plugin disconnect"),this.ws=null),this.setStatus("disconnected")}getStatus(){return this.status}}var b=Object.defineProperty,Y=(w,y,E)=>y in w?b(w,y,{enumerable:!0,configurable:!0,writable:!0,value:E}):w[y]=E,c=(w,y,E)=>Y(w,typeof y!="symbol"?y+"":y,E);class Mt{constructor(y){c(this,"clients",[]),c(this,"slotStates",[]),c(this,"scanTimer",null),c(this,"config"),this.config=y;for(let E=0;E<Xe.length;E++){const R=E===y.twinSlotIndex;this.slotStates.push({slotIndex:E,wsPort:Xe[E],status:"disconnected",isTwin:R,disconnectReason:"not_started"}),this.clients.push(new Tt({url:`ws://127.0.0.1:${Xe[E]}`,pluginVersion:y.pluginVersion,sdkReady:y.sdkReady,twinSlotIndex:y.twinSlotIndex,isTwinConnection:R,maxReconnectAttempts:R?10:0,initialReconnectDelay:1e3,maxReconnectDelay:3e4,onStatusChange:N=>this.handleStatusChange(E,N),onLog:(N,j)=>y.onLog(E,N,j),onPreempted:()=>this.handleDisconnectReason(E,"preempted"),onTwinOccupied:()=>this.handleDisconnectReason(E,"twin_occupied"),onOtherOccupied:()=>this.handleDisconnectReason(E,"other_occupied")}))}}setMessageHandler(y){for(const E of this.clients)E.setMessageHandler(y)}start(){const y=this.config.twinSlotIndex;this.clients[y].connect(),setTimeout(()=>{for(let E=0;E<this.clients.length;E++)E!==y&&this.clients[E].connect()},2e3),this.scanTimer=setInterval(()=>this.scanAndReconnect(),Ot)}stop(){this.scanTimer&&(clearInterval(this.scanTimer),this.scanTimer=null);for(const y of this.clients)y.disconnect()}getSlots(){return this.slotStates.slice()}handleStatusChange(y,E){const R=this.slotStates[y];R.status=E,E==="connected"&&(R.disconnectReason=null),this.notifySlotsChange()}handleDisconnectReason(y,E){this.slotStates[y].disconnectReason=E,this.notifySlotsChange()}notifySlotsChange(){this.config.onSlotsChange(this.slotStates.slice())}scanAndReconnect(){for(let y=0;y<this.clients.length;y++){const E=this.slotStates[y];E.isTwin||E.status==="connected"||E.status==="connecting"||this.clients[y].connect()}}}async function ht(w){const[y,E,R,N]=await Promise.all([w.isPowerupProperty(),w.isPowerupSlot(),w.isPowerupPropertyListItem(),w.isPowerupEnum()]);return y||E||R||N}async function Ve(w){const y=await Promise.all(w.map(ht));return w.filter((E,R)=>!y[R])}async function at(w){const y=await Promise.all(w.map(E=>E.isPowerup()));return w.filter((E,R)=>!y[R])}async function Pt(w,y){try{return await w.richText.toMarkdown(y)}catch{return tt(y)}}function tt(w){return w.map(y=>{if(typeof y=="string")return y;if(typeof y!="object"||y===null)return"";const E=y;switch(E.i){case"m":return String(E.text??"");case"q":return`[[${String(E._id??"")}]]`;case"u":return E.title?`[${String(E.title)}](${String(E.url)})`:String(E.url??"");case"x":return`$${String(E.text??"")}$`;case"i":return`})`;case"a":return`[audio](${String(E.url??"")})`;case"p":return String(E.text??"");case"g":return String(E.text??"");case"n":return String(E.text??"");case"o":return String(E.text??"");case"s":case"fi":case"ai":return"";default:return String(E.text??E.url??"")}}).join("")}async function ot(w,y,E,R){const N=R?.includePowerup??!1,[j,Q,G,ve,Fe,De,he,mt,Me,$e,wt,Pe,Be]=await Promise.all([Pt(w,y.text??[]),y.backText?Pt(w,y.backText):Promise.resolve(null),y.getType(),y.isCardItem(),y.isDocument(),y.getTagRems().then(qe=>N?qe:at(qe).then(Re=>{const xt=qe.length-Re.length;return xt>0&&R?.onFilteredTags?.(xt),Re})),y.getPracticeDirection(),y.getFontSize(),y.isTodo(),y.getTodoStatus(),y.isCode(),y.hasPowerup("dv"),y.type===6?y.getPortalDirectlyIncludedRem():Promise.resolve([])]);let Ge=!1;E.length>0&&(Ge=(await Promise.all(E.map(Re=>Re.isCardItem()))).some(Boolean));const nt=Pe&&(y.text??[]).length===0,Qe=await Promise.all(De.map(async qe=>({id:qe._id,name:te(await Pt(w,qe.text??[]))})));return{id:y._id,markdownText:te(j),markdownBackText:Q!==null?te(Q):null,type:ae(G),hasMultilineChildren:Ge,practiceDirection:he??"none",isCardItem:ve,isDocument:Fe,isPortal:y.type===6,portalRefs:Be.map(qe=>qe._id),childrenCount:E.length,tags:Qe,fontSize:mt??null,isTodo:Me,todoStatus:$e??null,isCode:wt,isDivider:nt,isTopLevel:y.parent===null}}function te(w){return w.replace(/\n/g," ")}function ae(w){switch(w){case 1:return"concept";case 2:return"descriptor";case 6:return"portal";default:return"default"}}async function _e(w,y){const{includePowerup:E=!1}=y,R=await w.rem.findOne(y.remId);if(!R)throw new Error(`Rem not found: ${y.remId}`);return Ne(w,R,{includePowerup:E})}async function Ne(w,y,E){const{includePowerup:R=!1}=E,[N,j,Q,G,ve,Fe,De,he,mt,Me,$e,wt,Pe,Be,Ge,nt,Qe,qe,Re,xt,Je,an,Ke,Qt,un,pn,Zt,ut,ct,Nt,Bt,hn,Dt,Jn,ur,So,er,Tr,_n,Sn,Dn,cr,xr]=await Promise.all([y.isDocument(),y.getFontSize(),y.getHighlightColor(),y.isTodo(),y.getTodoStatus(),y.isCode(),y.isQuote(),y.isListItem(),y.isCardItem(),y.isTable(),y.isSlot(),y.isProperty(),y.getEnablePractice(),y.getPracticeDirection(),y.getTagRems(),y.getSources(),y.getAliases(),y.positionAmongstSiblings(),y.isPowerup(),y.isPowerupEnum(),y.isPowerupProperty(),y.isPowerupPropertyListItem(),y.isPowerupSlot(),y.getPortalType(),y.getPortalDirectlyIncludedRem(),y.getPropertyType(),y.remsBeingReferenced(),y.deepRemsBeingReferenced(),y.remsReferencingThis(),y.taggedRem(),y.ancestorTagRem(),y.descendantTagRem(),y.getDescendants(),y.siblingRem(),y.portalsAndDocumentsIn(),y.allRemInDocumentOrPortal(),y.allRemInFolderQueue(),y.getChildrenRem(),y.timesSelectedInSearch(),y.getLastTimeMovedTo(),y.getSchemaVersion(),y.embeddedQueueViewMode(),y.getLastPracticed()]);let dr=Ge,Cr=y.children??[],mi=0,Mr=0;if(!R){dr=await at(Ge),mi=Ge.length-dr.length;const ze=await Ve(Tr);Mr=Tr.length-ze.length,Cr=ze.map(An=>An._id)}const fr={id:y._id,text:He(y.text??[]),backText:y.backText?He(y.backText):null,type:ae(y.type),isDocument:N,parent:y.parent,children:Cr,fontSize:j??null,highlightColor:Q??null,isTodo:G,todoStatus:ve??null,isCode:Fe,isQuote:De,isListItem:he,isCardItem:mt,isTable:!!Me,isSlot:$e,isProperty:wt,isPowerup:Re,isPowerupEnum:xt,isPowerupProperty:Je,isPowerupPropertyListItem:an,isPowerupSlot:Ke,portalType:ae(y.type)==="portal"?st(Qt):null,portalDirectlyIncludedRem:un.map(ze=>ze._id),propertyType:pn??null,enablePractice:Pe,practiceDirection:Be,tags:dr.map(ze=>ze._id),sources:nt.map(ze=>ze._id),aliases:Qe.map(ze=>ze._id),remsBeingReferenced:Zt.map(ze=>ze._id),deepRemsBeingReferenced:ut.map(ze=>ze._id),remsReferencingThis:ct.map(ze=>ze._id),taggedRem:Nt.map(ze=>ze._id),ancestorTagRem:Bt.map(ze=>ze._id),descendantTagRem:hn.map(ze=>ze._id),descendants:Dt.map(ze=>ze._id),siblingRem:Jn.map(ze=>ze._id),portalsAndDocumentsIn:ur.map(ze=>ze._id),allRemInDocumentOrPortal:So.map(ze=>ze._id),allRemInFolderQueue:er.map(ze=>ze._id),positionAmongstSiblings:qe??null,timesSelectedInSearch:_n,lastTimeMovedTo:Sn,schemaVersion:Dn,embeddedQueueViewMode:cr,createdAt:y.createdAt,updatedAt:y.updatedAt,localUpdatedAt:y.localUpdatedAt,lastPracticed:xr};return!R&&(mi>0||Mr>0)?{...fr,powerupFiltered:{tags:mi,children:Mr}}:fr}function He(w){return w.map(y=>{if(typeof y=="string")return y;const E={};for(const R of Object.keys(y).sort())E[R]=y[R];return E})}function st(w){switch(w){case 2:return"embedded_queue";case 3:return"scaffold";case 4:return"search_portal";default:return"portal"}}function Z(w){return"type"in w&&w.type==="elided"}function Ze(w){if(w.isDivider)return"---";const{markdownText:y,markdownBackText:E,hasMultilineChildren:R,practiceDirection:N}=w;let j;return E!==null?j=y+(R?N==="backward"?" \u2191 ":N==="both"?" \u2195 ":" \u2193 ":N==="backward"?" \u2190 ":N==="both"?" \u2194 ":" \u2192 ")+E:R?j=y+(N==="backward"?" \u2191":N==="both"?" \u2195":" \u2193"):j=y,w.isCode&&(j="`"+j+"`"),w.isTodo&&(j=(w.todoStatus==="Finished"?"- [x] ":"- [ ] ")+j),w.fontSize&&(j=(w.fontSize==="H1"?"# ":w.fontSize==="H2"?"## ":"### ")+j),j}function Ye(w,y){const E=[];w.type==="concept"?E.push("type:concept"):w.type==="descriptor"?E.push("type:descriptor"):w.type==="portal"&&(E.push("type:portal"),w.portalRefs.length>0&&E.push("refs:"+w.portalRefs.join(","))),w.isDocument&&E.push("doc"),w.isCardItem&&E.push("role:card-item"),y&&w.childrenCount>0&&E.push("children:"+w.childrenCount);for(const R of w.tags)E.push("tag:"+R.name+"("+R.id+")");return w.isTopLevel&&E.push("top"),E}function ue(w){return{markdownBackText:null,type:"default",hasMultilineChildren:!1,practiceDirection:"none",isCardItem:!1,isDocument:!1,isPortal:!1,portalRefs:[],tags:[],fontSize:null,isTodo:!1,todoStatus:null,isCode:!1,isDivider:!1,...w}}function fe(w,y=!1){const E=Ze(w),R=Ye(w,y),N=R.length>0?" "+R.join(" "):"";return`${E} <!--${w.id}${N}-->`}function ee(w){const y=w.isExact?"siblings":"nodes";return`<!--...elided ${w.isExact?"":">="}${w.count} ${y} (parent:${w.parentId} range:${w.rangeFrom}-${w.rangeTo} total:${w.totalSiblings})-->`}function ie(w){const y=[];function E(R,N){const j=" ".repeat(N);if(Z(R)){y.push(j+ee(R));return}const Q=fe(R.rem,R.folded);if(y.push(j+Q),!R.folded)for(const G of R.children)E(G,N+1)}return E(w,0),y.join(`
|
|
58
|
+
*/var Xe,Ot,Ae,it;if(typeof performance=="object"&&typeof performance.now=="function"){var B=performance;K.unstable_now=function(){return B.now()}}else{var wn=Date,Et=wn.now();K.unstable_now=function(){return wn.now()-Et}}if(typeof window>"u"||typeof MessageChannel!="function"){var Tt=null,b=null,Y=function(){if(Tt!==null)try{var F=K.unstable_now();Tt(!0,F),Tt=null}catch(A){throw setTimeout(Y,0),A}};Xe=function(F){Tt!==null?setTimeout(Xe,0,F):(Tt=F,setTimeout(Y,0))},Ot=function(F,A){b=setTimeout(F,A)},Ae=function(){clearTimeout(b)},K.unstable_shouldYield=function(){return!1},it=K.unstable_forceFrameRate=function(){}}else{var c=window.setTimeout,Mt=window.clearTimeout;if(typeof console<"u"){var ht=window.cancelAnimationFrame;typeof window.requestAnimationFrame!="function"&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills"),typeof ht!="function"&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills")}var Ve=!1,at=null,Pt=-1,tt=5,ot=0;K.unstable_shouldYield=function(){return K.unstable_now()>=ot},it=function(){},K.unstable_forceFrameRate=function(F){0>F||125<F?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):tt=0<F?Math.floor(1e3/F):5};var te=new MessageChannel,ae=te.port2;te.port1.onmessage=function(){if(at!==null){var F=K.unstable_now();ot=F+tt;try{at(!0,F)?ae.postMessage(null):(Ve=!1,at=null)}catch(A){throw ae.postMessage(null),A}}else Ve=!1},Xe=function(F){at=F,Ve||(Ve=!0,ae.postMessage(null))},Ot=function(F,A){Pt=c(function(){F(K.unstable_now())},A)},Ae=function(){Mt(Pt),Pt=-1}}function _e(F,A){var $=F.length;F.push(A);e:for(;;){var H=$-1>>>1,ne=F[H];if(ne!==void 0&&0<st(ne,A))F[H]=A,F[$]=ne,$=H;else break e}}function Ne(F){return F=F[0],F===void 0?null:F}function He(F){var A=F[0];if(A!==void 0){var $=F.pop();if($!==A){F[0]=$;e:for(var H=0,ne=F.length;H<ne;){var U=2*(H+1)-1,ye=F[U],M=U+1,sn=F[M];if(ye!==void 0&&0>st(ye,$))sn!==void 0&&0>st(sn,ye)?(F[H]=sn,F[M]=$,H=M):(F[H]=ye,F[U]=$,H=U);else if(sn!==void 0&&0>st(sn,$))F[H]=sn,F[M]=$,H=M;else break e}}return A}return null}function st(F,A){var $=F.sortIndex-A.sortIndex;return $!==0?$:F.id-A.id}var Z=[],Ze=[],Ye=1,ue=null,fe=3,ee=!1,ie=!1,me=!1;function q(F){for(var A=Ne(Ze);A!==null;){if(A.callback===null)He(Ze);else if(A.startTime<=F)He(Ze),A.sortIndex=A.expirationTime,_e(Z,A);else break;A=Ne(Ze)}}function ge(F){if(me=!1,q(F),!ie)if(Ne(Z)!==null)ie=!0,Xe(C);else{var A=Ne(Ze);A!==null&&Ot(ge,A.startTime-F)}}function C(F,A){ie=!1,me&&(me=!1,Ae()),ee=!0;var $=fe;try{for(q(A),ue=Ne(Z);ue!==null&&(!(ue.expirationTime>A)||F&&!K.unstable_shouldYield());){var H=ue.callback;if(typeof H=="function"){ue.callback=null,fe=ue.priorityLevel;var ne=H(ue.expirationTime<=A);A=K.unstable_now(),typeof ne=="function"?ue.callback=ne:ue===Ne(Z)&&He(Z),q(A)}else He(Z);ue=Ne(Z)}if(ue!==null)var U=!0;else{var ye=Ne(Ze);ye!==null&&Ot(ge,ye.startTime-A),U=!1}return U}finally{ue=null,fe=$,ee=!1}}var V=it;K.unstable_IdlePriority=5,K.unstable_ImmediatePriority=1,K.unstable_LowPriority=4,K.unstable_NormalPriority=3,K.unstable_Profiling=null,K.unstable_UserBlockingPriority=2,K.unstable_cancelCallback=function(F){F.callback=null},K.unstable_continueExecution=function(){ie||ee||(ie=!0,Xe(C))},K.unstable_getCurrentPriorityLevel=function(){return fe},K.unstable_getFirstCallbackNode=function(){return Ne(Z)},K.unstable_next=function(F){switch(fe){case 1:case 2:case 3:var A=3;break;default:A=fe}var $=fe;fe=A;try{return F()}finally{fe=$}},K.unstable_pauseExecution=function(){},K.unstable_requestPaint=V,K.unstable_runWithPriority=function(F,A){switch(F){case 1:case 2:case 3:case 4:case 5:break;default:F=3}var $=fe;fe=F;try{return A()}finally{fe=$}},K.unstable_scheduleCallback=function(F,A,$){var H=K.unstable_now();switch(typeof $=="object"&&$!==null?($=$.delay,$=typeof $=="number"&&0<$?H+$:H):$=H,F){case 1:var ne=-1;break;case 2:ne=250;break;case 5:ne=1073741823;break;case 4:ne=1e4;break;default:ne=5e3}return ne=$+ne,F={id:Ye++,callback:A,priorityLevel:F,startTime:$,expirationTime:ne,sortIndex:-1},$>H?(F.sortIndex=$,_e(Ze,F),Ne(Z)===null&&F===Ne(Ze)&&(me?Ae():me=!0,Ot(ge,$-H))):(F.sortIndex=ne,_e(Z,F),ie||ee||(ie=!0,Xe(C))),F},K.unstable_wrapCallback=function(F){var A=fe;return function(){var $=fe;fe=A;try{return F.apply(this,arguments)}finally{fe=$}}}},825(en,K,Xe){"use strict";en.exports=Xe(742)}},vd={};function Ac(en){var K=vd[en];if(K!==void 0)return K.exports;var Xe=vd[en]={exports:{}};return Pf[en](Xe,Xe.exports,Ac),Xe.exports}Ac.g=(function(){if(typeof globalThis=="object")return globalThis;try{return this||new Function("return this")()}catch{if(typeof window=="object")return window}})();var wp={};(()=>{"use strict";var en=Ac(216);const K="0.2.1",Xe=[29100,29110,29120,29130],Ot=18e3;var Ae=Object.defineProperty,it=(w,y,E)=>y in w?Ae(w,y,{enumerable:!0,configurable:!0,writable:!0,value:E}):w[y]=E,B=(w,y,E)=>it(w,typeof y!="symbol"?y+"":y,E);const wn=4e3,Et=4003;class Tt{constructor(y){B(this,"ws",null),B(this,"reconnectAttempts",0),B(this,"reconnectTimeout",null),B(this,"messageHandler",null),B(this,"status","disconnected"),B(this,"isShuttingDown",!1),B(this,"isPreempted",!1),B(this,"_sdkReady"),B(this,"config"),this._sdkReady=y.sdkReady,this.config={url:y.url,pluginVersion:y.pluginVersion,sdkReady:y.sdkReady,twinSlotIndex:y.twinSlotIndex,isTwinConnection:y.isTwinConnection??!1,maxReconnectAttempts:y.maxReconnectAttempts??10,initialReconnectDelay:y.initialReconnectDelay??1e3,maxReconnectDelay:y.maxReconnectDelay??3e4,onStatusChange:y.onStatusChange,onLog:y.onLog,onPreempted:y.onPreempted,onTwinOccupied:y.onTwinOccupied,onOtherOccupied:y.onOtherOccupied}}log(y,E="info"){this.config.onLog?.(y,E)}setStatus(y){this.status!==y&&(this.status=y,this.config.onStatusChange?.(y))}sendHello(){const y={type:"hello",version:this.config.pluginVersion,sdkReady:this._sdkReady,twinSlotIndex:this.config.twinSlotIndex};try{this.ws?.send(JSON.stringify(y)),this.log(`\u53D1\u9001 hello\uFF08v${this.config.pluginVersion}, sdkReady=${this._sdkReady}, twinSlot=${this.config.twinSlotIndex}\uFF09`)}catch(E){this.log(`\u53D1\u9001 hello \u5931\u8D25: ${E}`,"warn")}}connect(){if(!(this.ws?.readyState===WebSocket.OPEN||this.ws?.readyState===WebSocket.CONNECTING)){this.isShuttingDown=!1,this.isPreempted=!1,this.setStatus("connecting");try{this.ws=new WebSocket(this.config.url),this.ws.onopen=()=>{this.log("\u5DF2\u8FDE\u63A5\u5230\u5B88\u62A4\u8FDB\u7A0B"),this.reconnectAttempts=0,this.setStatus("connected"),this.sendHello()},this.ws.onmessage=async y=>{await this.handleMessage(typeof y.data=="string"?y.data:String(y.data))},this.ws.onclose=y=>{y.code!==1006&&this.log(`\u8FDE\u63A5\u65AD\u5F00: ${y.code} ${y.reason}`,"warn"),this.setStatus("disconnected"),y.code===Et?this.config.onTwinOccupied?.():y.code===wn&&this.config.onOtherOccupied?.(),this.isShuttingDown||this.scheduleReconnect()},this.ws.onerror=()=>{}}catch(y){this.log(`\u8FDE\u63A5\u5931\u8D25: ${y}`,"error"),this.setStatus("disconnected"),this.scheduleReconnect()}}}async handleMessage(y){try{const E=JSON.parse(y);if(E.type==="preempted"){this.isPreempted=!0,this.log(`\u88AB\u5B6A\u751F Plugin \u62A2\u5360: ${E.reason}`,"warn"),this.config.onPreempted?.();return}if(E.type==="ping"){this.ws?.send(JSON.stringify({type:"pong"}));return}if(E.id&&E.action&&this.messageHandler){const R=E;this.log(`\u6536\u5230\u8BF7\u6C42: ${R.action}`);try{const N=await this.messageHandler(R),j={id:R.id,result:N};this.ws?.send(JSON.stringify(j)),this.log(`\u5B8C\u6210: ${R.action}`)}catch(N){const j=N instanceof Error?N.message:String(N),Q={id:R.id,error:j};this.ws?.send(JSON.stringify(Q)),this.log(`\u5931\u8D25: ${R.action} - ${j}`,"error")}}}catch(E){this.log(`\u5904\u7406\u6D88\u606F\u5931\u8D25: ${E}`,"error")}}scheduleReconnect(){if(this.isShuttingDown||this.isPreempted||!this.config.isTwinConnection)return;if(this.reconnectAttempts>=this.config.maxReconnectAttempts){this.log("\u5DF2\u8FBE\u6700\u5927\u91CD\u8FDE\u6B21\u6570","error");return}const y=Math.min(this.config.initialReconnectDelay*Math.pow(2,this.reconnectAttempts),this.config.maxReconnectDelay),E=Math.random()*.3*y,R=y+E;this.reconnectAttempts++,this.log(`${Math.round(R)}ms \u540E\u91CD\u8FDE\uFF08\u7B2C ${this.reconnectAttempts}/${this.config.maxReconnectAttempts} \u6B21\uFF09`),this.reconnectTimeout=setTimeout(()=>{this.connect()},R)}setMessageHandler(y){this.messageHandler=y}disconnect(){this.isShuttingDown=!0,this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.ws&&(this.ws.close(1e3,"Plugin disconnect"),this.ws=null),this.setStatus("disconnected")}getStatus(){return this.status}}var b=Object.defineProperty,Y=(w,y,E)=>y in w?b(w,y,{enumerable:!0,configurable:!0,writable:!0,value:E}):w[y]=E,c=(w,y,E)=>Y(w,typeof y!="symbol"?y+"":y,E);class Mt{constructor(y){c(this,"clients",[]),c(this,"slotStates",[]),c(this,"scanTimer",null),c(this,"config"),this.config=y;for(let E=0;E<Xe.length;E++){const R=E===y.twinSlotIndex;this.slotStates.push({slotIndex:E,wsPort:Xe[E],status:"disconnected",isTwin:R,disconnectReason:"not_started"}),this.clients.push(new Tt({url:`ws://127.0.0.1:${Xe[E]}`,pluginVersion:y.pluginVersion,sdkReady:y.sdkReady,twinSlotIndex:y.twinSlotIndex,isTwinConnection:R,maxReconnectAttempts:R?10:0,initialReconnectDelay:1e3,maxReconnectDelay:3e4,onStatusChange:N=>this.handleStatusChange(E,N),onLog:(N,j)=>y.onLog(E,N,j),onPreempted:()=>this.handleDisconnectReason(E,"preempted"),onTwinOccupied:()=>this.handleDisconnectReason(E,"twin_occupied"),onOtherOccupied:()=>this.handleDisconnectReason(E,"other_occupied")}))}}setMessageHandler(y){for(const E of this.clients)E.setMessageHandler(y)}start(){const y=this.config.twinSlotIndex;this.clients[y].connect(),setTimeout(()=>{for(let E=0;E<this.clients.length;E++)E!==y&&this.clients[E].connect()},2e3),this.scanTimer=setInterval(()=>this.scanAndReconnect(),Ot)}stop(){this.scanTimer&&(clearInterval(this.scanTimer),this.scanTimer=null);for(const y of this.clients)y.disconnect()}getSlots(){return this.slotStates.slice()}handleStatusChange(y,E){const R=this.slotStates[y];R.status=E,E==="connected"&&(R.disconnectReason=null),this.notifySlotsChange()}handleDisconnectReason(y,E){this.slotStates[y].disconnectReason=E,this.notifySlotsChange()}notifySlotsChange(){this.config.onSlotsChange(this.slotStates.slice())}scanAndReconnect(){for(let y=0;y<this.clients.length;y++){const E=this.slotStates[y];E.isTwin||E.status==="connected"||E.status==="connecting"||this.clients[y].connect()}}}async function ht(w){const[y,E,R,N]=await Promise.all([w.isPowerupProperty(),w.isPowerupSlot(),w.isPowerupPropertyListItem(),w.isPowerupEnum()]);return y||E||R||N}async function Ve(w){const y=await Promise.all(w.map(ht));return w.filter((E,R)=>!y[R])}async function at(w){const y=await Promise.all(w.map(E=>E.isPowerup()));return w.filter((E,R)=>!y[R])}async function Pt(w,y){try{return await w.richText.toMarkdown(y)}catch{return tt(y)}}function tt(w){return w.map(y=>{if(typeof y=="string")return y;if(typeof y!="object"||y===null)return"";const E=y;switch(E.i){case"m":return String(E.text??"");case"q":return`[[${String(E._id??"")}]]`;case"u":return E.title?`[${String(E.title)}](${String(E.url)})`:String(E.url??"");case"x":return`$${String(E.text??"")}$`;case"i":return`})`;case"a":return`[audio](${String(E.url??"")})`;case"p":return String(E.text??"");case"g":return String(E.text??"");case"n":return String(E.text??"");case"o":return String(E.text??"");case"s":case"fi":case"ai":return"";default:return String(E.text??E.url??"")}}).join("")}async function ot(w,y,E,R){const N=R?.includePowerup??!1,[j,Q,G,ve,Fe,De,he,mt,Me,$e,wt,Pe,Be]=await Promise.all([Pt(w,y.text??[]),y.backText?Pt(w,y.backText):Promise.resolve(null),y.getType(),y.isCardItem(),y.isDocument(),y.getTagRems().then(qe=>N?qe:at(qe).then(Re=>{const xt=qe.length-Re.length;return xt>0&&R?.onFilteredTags?.(xt),Re})),y.getPracticeDirection(),y.getFontSize(),y.isTodo(),y.getTodoStatus(),y.isCode(),y.hasPowerup("dv"),y.type===6?y.getPortalDirectlyIncludedRem():Promise.resolve([])]);let Ge=!1;E.length>0&&(Ge=(await Promise.all(E.map(Re=>Re.isCardItem()))).some(Boolean));const nt=Pe&&(y.text??[]).length===0,Qe=await Promise.all(De.map(async qe=>({id:qe._id,name:te(await Pt(w,qe.text??[]))})));return{id:y._id,markdownText:te(j),markdownBackText:Q!==null?te(Q):null,type:ae(G),hasMultilineChildren:Ge,practiceDirection:he??"none",isCardItem:ve,isDocument:Fe,isPortal:y.type===6,portalRefs:Be.map(qe=>qe._id),childrenCount:E.length,tags:Qe,fontSize:mt??null,isTodo:Me,todoStatus:$e??null,isCode:wt,isDivider:nt,isTopLevel:y.parent===null}}function te(w){return w.replace(/\n/g," ")}function ae(w){switch(w){case 1:return"concept";case 2:return"descriptor";case 6:return"portal";default:return"default"}}async function _e(w,y){const{includePowerup:E=!1}=y,R=await w.rem.findOne(y.remId);if(!R)throw new Error(`Rem not found: ${y.remId}`);return Ne(w,R,{includePowerup:E})}async function Ne(w,y,E){const{includePowerup:R=!1}=E,[N,j,Q,G,ve,Fe,De,he,mt,Me,$e,wt,Pe,Be,Ge,nt,Qe,qe,Re,xt,Je,an,Ke,Qt,un,pn,Zt,ut,ct,Nt,Bt,hn,Dt,Jn,ur,So,er,Tr,_n,Sn,Dn,cr,xr]=await Promise.all([y.isDocument(),y.getFontSize(),y.getHighlightColor(),y.isTodo(),y.getTodoStatus(),y.isCode(),y.isQuote(),y.isListItem(),y.isCardItem(),y.isTable(),y.isSlot(),y.isProperty(),y.getEnablePractice(),y.getPracticeDirection(),y.getTagRems(),y.getSources(),y.getAliases(),y.positionAmongstSiblings(),y.isPowerup(),y.isPowerupEnum(),y.isPowerupProperty(),y.isPowerupPropertyListItem(),y.isPowerupSlot(),y.getPortalType(),y.getPortalDirectlyIncludedRem(),y.getPropertyType(),y.remsBeingReferenced(),y.deepRemsBeingReferenced(),y.remsReferencingThis(),y.taggedRem(),y.ancestorTagRem(),y.descendantTagRem(),y.getDescendants(),y.siblingRem(),y.portalsAndDocumentsIn(),y.allRemInDocumentOrPortal(),y.allRemInFolderQueue(),y.getChildrenRem(),y.timesSelectedInSearch(),y.getLastTimeMovedTo(),y.getSchemaVersion(),y.embeddedQueueViewMode(),y.getLastPracticed()]);let dr=Ge,Cr=y.children??[],mi=0,Mr=0;if(!R){dr=await at(Ge),mi=Ge.length-dr.length;const ze=await Ve(Tr);Mr=Tr.length-ze.length,Cr=ze.map(An=>An._id)}const fr={id:y._id,text:He(y.text??[]),backText:y.backText?He(y.backText):null,type:ae(y.type),isDocument:N,parent:y.parent,children:Cr,fontSize:j??null,highlightColor:Q??null,isTodo:G,todoStatus:ve??null,isCode:Fe,isQuote:De,isListItem:he,isCardItem:mt,isTable:!!Me,isSlot:$e,isProperty:wt,isPowerup:Re,isPowerupEnum:xt,isPowerupProperty:Je,isPowerupPropertyListItem:an,isPowerupSlot:Ke,portalType:ae(y.type)==="portal"?st(Qt):null,portalDirectlyIncludedRem:un.map(ze=>ze._id).sort(),propertyType:pn??null,enablePractice:Pe,practiceDirection:Be,tags:dr.map(ze=>ze._id).sort(),sources:nt.map(ze=>ze._id).sort(),aliases:Qe.map(ze=>ze._id).sort(),remsBeingReferenced:Zt.map(ze=>ze._id).sort(),deepRemsBeingReferenced:ut.map(ze=>ze._id).sort(),remsReferencingThis:ct.map(ze=>ze._id).sort(),taggedRem:Nt.map(ze=>ze._id).sort(),ancestorTagRem:Bt.map(ze=>ze._id).sort(),descendantTagRem:hn.map(ze=>ze._id).sort(),descendants:Dt.map(ze=>ze._id).sort(),siblingRem:Jn.map(ze=>ze._id).sort(),portalsAndDocumentsIn:ur.map(ze=>ze._id).sort(),allRemInDocumentOrPortal:So.map(ze=>ze._id).sort(),allRemInFolderQueue:er.map(ze=>ze._id).sort(),positionAmongstSiblings:qe??null,timesSelectedInSearch:_n,lastTimeMovedTo:Sn,schemaVersion:Dn,embeddedQueueViewMode:cr,createdAt:y.createdAt,updatedAt:y.updatedAt,localUpdatedAt:y.localUpdatedAt,lastPracticed:xr};return!R&&(mi>0||Mr>0)?{...fr,powerupFiltered:{tags:mi,children:Mr}}:fr}function He(w){return w.map(y=>{if(typeof y=="string")return y;const E={};for(const R of Object.keys(y).sort())E[R]=y[R];return E})}function st(w){switch(w){case 2:return"embedded_queue";case 3:return"scaffold";case 4:return"search_portal";default:return"portal"}}function Z(w){return"type"in w&&w.type==="elided"}function Ze(w){if(w.isDivider)return"---";const{markdownText:y,markdownBackText:E,hasMultilineChildren:R,practiceDirection:N}=w;let j;return E!==null?j=y+(R?N==="backward"?" \u2191 ":N==="both"?" \u2195 ":" \u2193 ":N==="backward"?" \u2190 ":N==="both"?" \u2194 ":" \u2192 ")+E:R?j=y+(N==="backward"?" \u2191":N==="both"?" \u2195":" \u2193"):j=y,w.isCode&&(j="`"+j+"`"),w.isTodo&&(j=(w.todoStatus==="Finished"?"- [x] ":"- [ ] ")+j),w.fontSize&&(j=(w.fontSize==="H1"?"# ":w.fontSize==="H2"?"## ":"### ")+j),j}function Ye(w,y){const E=[];w.type==="concept"?E.push("type:concept"):w.type==="descriptor"?E.push("type:descriptor"):w.type==="portal"&&(E.push("type:portal"),w.portalRefs.length>0&&E.push("refs:"+w.portalRefs.join(","))),w.isDocument&&E.push("doc"),w.isCardItem&&E.push("role:card-item"),y&&w.childrenCount>0&&E.push("children:"+w.childrenCount);for(const R of w.tags)E.push("tag:"+R.name+"("+R.id+")");return w.isTopLevel&&E.push("top"),E}function ue(w){return{markdownBackText:null,type:"default",hasMultilineChildren:!1,practiceDirection:"none",isCardItem:!1,isDocument:!1,isPortal:!1,portalRefs:[],tags:[],fontSize:null,isTodo:!1,todoStatus:null,isCode:!1,isDivider:!1,...w}}function fe(w,y=!1){const E=Ze(w),R=Ye(w,y),N=R.length>0?" "+R.join(" "):"";return`${E} <!--${w.id}${N}-->`}function ee(w){const y=w.isExact?"siblings":"nodes";return`<!--...elided ${w.isExact?"":">="}${w.count} ${y} (parent:${w.parentId} range:${w.rangeFrom}-${w.rangeTo} total:${w.totalSiblings})-->`}function ie(w){const y=[];function E(R,N){const j=" ".repeat(N);if(Z(R)){y.push(j+ee(R));return}const Q=fe(R.rem,R.folded);if(y.push(j+Q),!R.folded)for(const G of R.children)E(G,N+1)}return E(w,0),y.join(`
|
|
59
59
|
`)}function me(w,y,E){if(w<=y)return{visibleIndices:null,elided:null};const R=Math.ceil(y*.7),N=Math.floor(y*.3);return{visibleIndices:{head:R,tail:N},elided:{count:w-R-N,parentId:E,rangeFrom:R,rangeTo:w-N-1,totalSiblings:w}}}async function q(w,y){const{remId:E,depth:R=3,maxNodes:N=200,maxSiblings:j=20,ancestorLevels:Q=0,includePowerup:G=!1}=y,ve=await w.rem.findOne(E);if(!ve)throw new Error(`Rem not found: ${E}`);let Fe=0,De=0,he=0;const mt={remaining:N},Me=[];async function $e(Qe,qe,Re){Fe++,mt.remaining--,Me.push(Qe._id);const xt=await Qe.getChildrenRem(),Je=G?xt:await Ve(xt);G||(he+=xt.length-Je.length);const Ke=Re!==-1&&qe>=Re&&Je.length>0,Qt=await ot(w,Qe,Je,{includePowerup:G,onFilteredTags:pn=>{De+=pn}});Ke&&(Qt.hasMultilineChildren=!1);const un=[];if(!Ke){const{visibleIndices:pn,elided:Zt}=me(Je.length,j,Qe._id);if(pn){const{head:ut,tail:ct}=pn;for(let Bt=0;Bt<ut&&mt.remaining>0;Bt++)un.push(await $e(Je[Bt],qe+1,Re));if(Zt){const Bt=Re!==-1&&qe+1>=Re;un.push({type:"elided",count:Zt.count,isExact:Bt,parentId:Zt.parentId,rangeFrom:Zt.rangeFrom,rangeTo:Zt.rangeTo,totalSiblings:Zt.totalSiblings})}const Nt=Je.length-ct;for(let Bt=Nt;Bt<Je.length&&mt.remaining>0;Bt++)un.push(await $e(Je[Bt],qe+1,Re))}else for(let ut=0;ut<Je.length;ut++){if(mt.remaining<=0){const ct=Je.length-ut;un.push({type:"elided",count:ct,isExact:!1,parentId:Qe._id,rangeFrom:ut,rangeTo:Je.length-1,totalSiblings:Je.length});break}un.push(await $e(Je[ut],qe+1,Re))}}return{rem:Qt,children:un,folded:Ke}}const wt=await $e(ve,0,R);await ve.getParentRem()||(wt.rem.isTopLevel=!0);const Be=ie(wt),Ge={rootId:E,depth:R,nodeCount:Fe,outline:Be,nodeRemIds:Me},nt=Math.min(Math.max(Q,0),10);if(nt>0){const Qe=[];let qe=ve;for(let Re=0;Re<nt;Re++){const xt=await qe.getParentRem();if(!xt)break;const[Je,an,Ke]=await Promise.all([Pt(w,xt.text??[]),xt.getChildrenRem(),xt.isDocument()]);Qe.push({id:xt._id,name:te(Je),childrenCount:an.length,isDocument:Ke}),qe=xt}if(Qe.length>0){const Re=Qe[Qe.length-1];await qe.getParentRem()||(Re.isTopLevel=!0),Ge.ancestors=Qe}}return!G&&(De>0||he>0)&&(Ge.powerupFiltered={tags:De,children:he}),Ge}async function ge(w,y){const E=await q(w,y),R=y.includePowerup??!1,N={};return await Promise.all(E.nodeRemIds.map(async j=>{const Q=await w.rem.findOne(j);Q&&(N[j]=await Ne(w,Q,{includePowerup:R}))})),{...E,remObjects:N}}async function C(w,y){const{depth:E=-1,maxNodes:R=200,maxSiblings:N=20}=y,Q=(await w.rem.getAll()).filter(Pe=>Pe.parent===null),G=await Promise.all(Q.map(Pe=>Pe.isDocument())),ve=Q.filter((Pe,Be)=>G[Be]);let Fe=0;const De={remaining:R};async function he(Pe,Be,Ge){Fe++,De.remaining--;const nt=await Pe.getChildrenRem(),Qe=await Ve(nt),qe=await Promise.all(Qe.map(ut=>ut.isDocument())),Re=Qe.filter((ut,ct)=>qe[ct]),xt=Qe.length-Re.length,an=Ge!==-1&&Be>=Ge&&Re.length>0,Ke=Pe.type===6,[Qt,un]=await Promise.all([Pt(w,Pe.text??[]),Ke?Pe.getPortalDirectlyIncludedRem():Promise.resolve([])]),pn=ue({id:Pe._id,markdownText:Qt.replace(/\n/g," "),childrenCount:Qe.length,isDocument:!0,isTopLevel:Pe.parent===null,isPortal:Ke,...Ke?{type:"portal",portalRefs:un.map(ut=>ut._id)}:{}}),Zt=[];if(!an&&Re.length>0){const{visibleIndices:ut,elided:ct}=me(Re.length,N,Pe._id);if(ut){const{head:Nt,tail:Bt}=ut;for(let Dt=0;Dt<Nt&&De.remaining>0;Dt++)Zt.push(await he(Re[Dt],Be+1,Ge));if(ct){const Dt=Ge!==-1&&Be+1>=Ge;Zt.push({type:"elided",count:ct.count,isExact:Dt,parentId:ct.parentId,rangeFrom:ct.rangeFrom,rangeTo:ct.rangeTo,totalSiblings:ct.totalSiblings})}const hn=Re.length-Bt;for(let Dt=hn;Dt<Re.length&&De.remaining>0;Dt++)Zt.push(await he(Re[Dt],Be+1,Ge))}else for(let Nt=0;Nt<Re.length;Nt++){if(De.remaining<=0){const Bt=Re.length-Nt;Zt.push({type:"elided",count:Bt,isExact:!1,parentId:Pe._id,rangeFrom:Nt,rangeTo:Re.length-1,totalSiblings:Re.length});break}Zt.push(await he(Re[Nt],Be+1,Ge))}}return{rem:pn,children:Zt,folded:an}}const mt=[],{visibleIndices:Me,elided:$e}=me(ve.length,N,"root");if(Me){const{head:Pe,tail:Be}=Me;for(let nt=0;nt<Pe&&De.remaining>0;nt++)mt.push(await he(ve[nt],0,E));$e&&mt.push({type:"elided",count:$e.count,isExact:!1,parentId:"root",rangeFrom:$e.rangeFrom,rangeTo:$e.rangeTo,totalSiblings:$e.totalSiblings});const Ge=ve.length-Be;for(let nt=Ge;nt<ve.length&&De.remaining>0;nt++)mt.push(await he(ve[nt],0,E))}else for(let Pe=0;Pe<ve.length;Pe++){if(De.remaining<=0){const Be=ve.length-Pe;mt.push({type:"elided",count:Be,isExact:!1,parentId:"root",rangeFrom:Pe,rangeTo:ve.length-1,totalSiblings:ve.length});break}mt.push(await he(ve[Pe],0,E))}const wt=["<!-- globe: \u77E5\u8BC6\u5E93\u6982\u89C8 -->"];for(const Pe of mt)Z(Pe)?wt.push(ee(Pe)):wt.push(ie(Pe));return{nodeCount:Fe,outline:wt.join(`
|
|
60
60
|
`)}}async function V(w,y){const E=[];let R=y;for(;R;){const N=await Pt(w,R.text??[]);E.unshift(N.replace(/\n/g," ").trim()||R._id),R=await R.getParentRem()}return E}async function F(w,y){const{mode:E="focus",ancestorLevels:R=2,maxNodes:N=200,maxSiblings:j=20,depth:Q=3,focusRemId:G}=y;if(E==="page"){if(G)throw new Error("focusRemId \u4EC5\u5728 focus \u6A21\u5F0F\u4E0B\u6709\u6548\uFF0Cpage \u6A21\u5F0F\u4E0B\u8BF7\u52FF\u6307\u5B9A");return A(w,{maxNodes:N,maxSiblings:j,depth:Q})}return $(w,{ancestorLevels:R,maxNodes:N,maxSiblings:j,focusRemId:G})}async function A(w,y){const E=await w.window.getFocusedPaneId();if(!E)throw new Error("\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u9762\u677F ID\uFF0C\u8BF7\u786E\u4FDD\u6709\u6253\u5F00\u7684\u9875\u9762");const R=await w.window.getOpenPaneRemId(E);if(!R)throw new Error("\u5F53\u524D\u9762\u677F\u6CA1\u6709\u6253\u5F00\u4EFB\u4F55 Rem");const N=await w.rem.findOne(R);if(!N)throw new Error(`Page Rem not found: ${R}`);const j=await V(w,N);let Q=0;const G={remaining:y.maxNodes},ve=await U(w,N,0,y.depth,y.maxSiblings,G);Q=y.maxNodes-G.remaining;const De=`<!-- page: ${j[j.length-1]||R} -->
|
|
61
61
|
<!-- path: ${j.join(" > ")} -->`+`
|
|
@@ -55,7 +55,7 @@ Add a <Suspense fallback=...> component higher in the tree to provide a loading
|
|
|
55
55
|
*
|
|
56
56
|
* This source code is licensed under the MIT license found in the
|
|
57
57
|
* LICENSE file in the root directory of this source tree.
|
|
58
|
-
*/var Xe,Ot,Ae,it;if(typeof performance=="object"&&typeof performance.now=="function"){var B=performance;K.unstable_now=function(){return B.now()}}else{var wn=Date,Et=wn.now();K.unstable_now=function(){return wn.now()-Et}}if(typeof window>"u"||typeof MessageChannel!="function"){var Tt=null,b=null,Y=function(){if(Tt!==null)try{var F=K.unstable_now();Tt(!0,F),Tt=null}catch(A){throw setTimeout(Y,0),A}};Xe=function(F){Tt!==null?setTimeout(Xe,0,F):(Tt=F,setTimeout(Y,0))},Ot=function(F,A){b=setTimeout(F,A)},Ae=function(){clearTimeout(b)},K.unstable_shouldYield=function(){return!1},it=K.unstable_forceFrameRate=function(){}}else{var c=window.setTimeout,Mt=window.clearTimeout;if(typeof console<"u"){var ht=window.cancelAnimationFrame;typeof window.requestAnimationFrame!="function"&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills"),typeof ht!="function"&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills")}var Ve=!1,at=null,Pt=-1,tt=5,ot=0;K.unstable_shouldYield=function(){return K.unstable_now()>=ot},it=function(){},K.unstable_forceFrameRate=function(F){0>F||125<F?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):tt=0<F?Math.floor(1e3/F):5};var te=new MessageChannel,ae=te.port2;te.port1.onmessage=function(){if(at!==null){var F=K.unstable_now();ot=F+tt;try{at(!0,F)?ae.postMessage(null):(Ve=!1,at=null)}catch(A){throw ae.postMessage(null),A}}else Ve=!1},Xe=function(F){at=F,Ve||(Ve=!0,ae.postMessage(null))},Ot=function(F,A){Pt=c(function(){F(K.unstable_now())},A)},Ae=function(){Mt(Pt),Pt=-1}}function _e(F,A){var $=F.length;F.push(A);e:for(;;){var H=$-1>>>1,ne=F[H];if(ne!==void 0&&0<st(ne,A))F[H]=A,F[$]=ne,$=H;else break e}}function Ne(F){return F=F[0],F===void 0?null:F}function He(F){var A=F[0];if(A!==void 0){var $=F.pop();if($!==A){F[0]=$;e:for(var H=0,ne=F.length;H<ne;){var U=2*(H+1)-1,ye=F[U],M=U+1,sn=F[M];if(ye!==void 0&&0>st(ye,$))sn!==void 0&&0>st(sn,ye)?(F[H]=sn,F[M]=$,H=M):(F[H]=ye,F[U]=$,H=U);else if(sn!==void 0&&0>st(sn,$))F[H]=sn,F[M]=$,H=M;else break e}}return A}return null}function st(F,A){var $=F.sortIndex-A.sortIndex;return $!==0?$:F.id-A.id}var Z=[],Ze=[],Ye=1,ue=null,fe=3,ee=!1,ie=!1,me=!1;function q(F){for(var A=Ne(Ze);A!==null;){if(A.callback===null)He(Ze);else if(A.startTime<=F)He(Ze),A.sortIndex=A.expirationTime,_e(Z,A);else break;A=Ne(Ze)}}function ge(F){if(me=!1,q(F),!ie)if(Ne(Z)!==null)ie=!0,Xe(C);else{var A=Ne(Ze);A!==null&&Ot(ge,A.startTime-F)}}function C(F,A){ie=!1,me&&(me=!1,Ae()),ee=!0;var $=fe;try{for(q(A),ue=Ne(Z);ue!==null&&(!(ue.expirationTime>A)||F&&!K.unstable_shouldYield());){var H=ue.callback;if(typeof H=="function"){ue.callback=null,fe=ue.priorityLevel;var ne=H(ue.expirationTime<=A);A=K.unstable_now(),typeof ne=="function"?ue.callback=ne:ue===Ne(Z)&&He(Z),q(A)}else He(Z);ue=Ne(Z)}if(ue!==null)var U=!0;else{var ye=Ne(Ze);ye!==null&&Ot(ge,ye.startTime-A),U=!1}return U}finally{ue=null,fe=$,ee=!1}}var V=it;K.unstable_IdlePriority=5,K.unstable_ImmediatePriority=1,K.unstable_LowPriority=4,K.unstable_NormalPriority=3,K.unstable_Profiling=null,K.unstable_UserBlockingPriority=2,K.unstable_cancelCallback=function(F){F.callback=null},K.unstable_continueExecution=function(){ie||ee||(ie=!0,Xe(C))},K.unstable_getCurrentPriorityLevel=function(){return fe},K.unstable_getFirstCallbackNode=function(){return Ne(Z)},K.unstable_next=function(F){switch(fe){case 1:case 2:case 3:var A=3;break;default:A=fe}var $=fe;fe=A;try{return F()}finally{fe=$}},K.unstable_pauseExecution=function(){},K.unstable_requestPaint=V,K.unstable_runWithPriority=function(F,A){switch(F){case 1:case 2:case 3:case 4:case 5:break;default:F=3}var $=fe;fe=F;try{return A()}finally{fe=$}},K.unstable_scheduleCallback=function(F,A,$){var H=K.unstable_now();switch(typeof $=="object"&&$!==null?($=$.delay,$=typeof $=="number"&&0<$?H+$:H):$=H,F){case 1:var ne=-1;break;case 2:ne=250;break;case 5:ne=1073741823;break;case 4:ne=1e4;break;default:ne=5e3}return ne=$+ne,F={id:Ye++,callback:A,priorityLevel:F,startTime:$,expirationTime:ne,sortIndex:-1},$>H?(F.sortIndex=$,_e(Ze,F),Ne(Z)===null&&F===Ne(Ze)&&(me?Ae():me=!0,Ot(ge,$-H))):(F.sortIndex=ne,_e(Z,F),ie||ee||(ie=!0,Xe(C))),F},K.unstable_wrapCallback=function(F){var A=fe;return function(){var $=fe;fe=A;try{return F.apply(this,arguments)}finally{fe=$}}}},825(en,K,Xe){"use strict";en.exports=Xe(742)}},vd={};function Ac(en){var K=vd[en];if(K!==void 0)return K.exports;var Xe=vd[en]={exports:{}};return Pf[en](Xe,Xe.exports,Ac),Xe.exports}Ac.g=(function(){if(typeof globalThis=="object")return globalThis;try{return this||new Function("return this")()}catch{if(typeof window=="object")return window}})();var wp={};(()=>{"use strict";var en=Ac(216);const K="0.2.0",Xe=[29100,29110,29120,29130],Ot=18e3;var Ae=Object.defineProperty,it=(w,y,E)=>y in w?Ae(w,y,{enumerable:!0,configurable:!0,writable:!0,value:E}):w[y]=E,B=(w,y,E)=>it(w,typeof y!="symbol"?y+"":y,E);const wn=4e3,Et=4003;class Tt{constructor(y){B(this,"ws",null),B(this,"reconnectAttempts",0),B(this,"reconnectTimeout",null),B(this,"messageHandler",null),B(this,"status","disconnected"),B(this,"isShuttingDown",!1),B(this,"isPreempted",!1),B(this,"_sdkReady"),B(this,"config"),this._sdkReady=y.sdkReady,this.config={url:y.url,pluginVersion:y.pluginVersion,sdkReady:y.sdkReady,twinSlotIndex:y.twinSlotIndex,isTwinConnection:y.isTwinConnection??!1,maxReconnectAttempts:y.maxReconnectAttempts??10,initialReconnectDelay:y.initialReconnectDelay??1e3,maxReconnectDelay:y.maxReconnectDelay??3e4,onStatusChange:y.onStatusChange,onLog:y.onLog,onPreempted:y.onPreempted,onTwinOccupied:y.onTwinOccupied,onOtherOccupied:y.onOtherOccupied}}log(y,E="info"){this.config.onLog?.(y,E)}setStatus(y){this.status!==y&&(this.status=y,this.config.onStatusChange?.(y))}sendHello(){const y={type:"hello",version:this.config.pluginVersion,sdkReady:this._sdkReady,twinSlotIndex:this.config.twinSlotIndex};try{this.ws?.send(JSON.stringify(y)),this.log(`\u53D1\u9001 hello\uFF08v${this.config.pluginVersion}, sdkReady=${this._sdkReady}, twinSlot=${this.config.twinSlotIndex}\uFF09`)}catch(E){this.log(`\u53D1\u9001 hello \u5931\u8D25: ${E}`,"warn")}}connect(){if(!(this.ws?.readyState===WebSocket.OPEN||this.ws?.readyState===WebSocket.CONNECTING)){this.isShuttingDown=!1,this.isPreempted=!1,this.setStatus("connecting");try{this.ws=new WebSocket(this.config.url),this.ws.onopen=()=>{this.log("\u5DF2\u8FDE\u63A5\u5230\u5B88\u62A4\u8FDB\u7A0B"),this.reconnectAttempts=0,this.setStatus("connected"),this.sendHello()},this.ws.onmessage=async y=>{await this.handleMessage(typeof y.data=="string"?y.data:String(y.data))},this.ws.onclose=y=>{y.code!==1006&&this.log(`\u8FDE\u63A5\u65AD\u5F00: ${y.code} ${y.reason}`,"warn"),this.setStatus("disconnected"),y.code===Et?this.config.onTwinOccupied?.():y.code===wn&&this.config.onOtherOccupied?.(),this.isShuttingDown||this.scheduleReconnect()},this.ws.onerror=()=>{}}catch(y){this.log(`\u8FDE\u63A5\u5931\u8D25: ${y}`,"error"),this.setStatus("disconnected"),this.scheduleReconnect()}}}async handleMessage(y){try{const E=JSON.parse(y);if(E.type==="preempted"){this.isPreempted=!0,this.log(`\u88AB\u5B6A\u751F Plugin \u62A2\u5360: ${E.reason}`,"warn"),this.config.onPreempted?.();return}if(E.type==="ping"){this.ws?.send(JSON.stringify({type:"pong"}));return}if(E.id&&E.action&&this.messageHandler){const R=E;this.log(`\u6536\u5230\u8BF7\u6C42: ${R.action}`);try{const N=await this.messageHandler(R),j={id:R.id,result:N};this.ws?.send(JSON.stringify(j)),this.log(`\u5B8C\u6210: ${R.action}`)}catch(N){const j=N instanceof Error?N.message:String(N),Q={id:R.id,error:j};this.ws?.send(JSON.stringify(Q)),this.log(`\u5931\u8D25: ${R.action} - ${j}`,"error")}}}catch(E){this.log(`\u5904\u7406\u6D88\u606F\u5931\u8D25: ${E}`,"error")}}scheduleReconnect(){if(this.isShuttingDown||this.isPreempted||!this.config.isTwinConnection)return;if(this.reconnectAttempts>=this.config.maxReconnectAttempts){this.log("\u5DF2\u8FBE\u6700\u5927\u91CD\u8FDE\u6B21\u6570","error");return}const y=Math.min(this.config.initialReconnectDelay*Math.pow(2,this.reconnectAttempts),this.config.maxReconnectDelay),E=Math.random()*.3*y,R=y+E;this.reconnectAttempts++,this.log(`${Math.round(R)}ms \u540E\u91CD\u8FDE\uFF08\u7B2C ${this.reconnectAttempts}/${this.config.maxReconnectAttempts} \u6B21\uFF09`),this.reconnectTimeout=setTimeout(()=>{this.connect()},R)}setMessageHandler(y){this.messageHandler=y}disconnect(){this.isShuttingDown=!0,this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.ws&&(this.ws.close(1e3,"Plugin disconnect"),this.ws=null),this.setStatus("disconnected")}getStatus(){return this.status}}var b=Object.defineProperty,Y=(w,y,E)=>y in w?b(w,y,{enumerable:!0,configurable:!0,writable:!0,value:E}):w[y]=E,c=(w,y,E)=>Y(w,typeof y!="symbol"?y+"":y,E);class Mt{constructor(y){c(this,"clients",[]),c(this,"slotStates",[]),c(this,"scanTimer",null),c(this,"config"),this.config=y;for(let E=0;E<Xe.length;E++){const R=E===y.twinSlotIndex;this.slotStates.push({slotIndex:E,wsPort:Xe[E],status:"disconnected",isTwin:R,disconnectReason:"not_started"}),this.clients.push(new Tt({url:`ws://127.0.0.1:${Xe[E]}`,pluginVersion:y.pluginVersion,sdkReady:y.sdkReady,twinSlotIndex:y.twinSlotIndex,isTwinConnection:R,maxReconnectAttempts:R?10:0,initialReconnectDelay:1e3,maxReconnectDelay:3e4,onStatusChange:N=>this.handleStatusChange(E,N),onLog:(N,j)=>y.onLog(E,N,j),onPreempted:()=>this.handleDisconnectReason(E,"preempted"),onTwinOccupied:()=>this.handleDisconnectReason(E,"twin_occupied"),onOtherOccupied:()=>this.handleDisconnectReason(E,"other_occupied")}))}}setMessageHandler(y){for(const E of this.clients)E.setMessageHandler(y)}start(){const y=this.config.twinSlotIndex;this.clients[y].connect(),setTimeout(()=>{for(let E=0;E<this.clients.length;E++)E!==y&&this.clients[E].connect()},2e3),this.scanTimer=setInterval(()=>this.scanAndReconnect(),Ot)}stop(){this.scanTimer&&(clearInterval(this.scanTimer),this.scanTimer=null);for(const y of this.clients)y.disconnect()}getSlots(){return this.slotStates.slice()}handleStatusChange(y,E){const R=this.slotStates[y];R.status=E,E==="connected"&&(R.disconnectReason=null),this.notifySlotsChange()}handleDisconnectReason(y,E){this.slotStates[y].disconnectReason=E,this.notifySlotsChange()}notifySlotsChange(){this.config.onSlotsChange(this.slotStates.slice())}scanAndReconnect(){for(let y=0;y<this.clients.length;y++){const E=this.slotStates[y];E.isTwin||E.status==="connected"||E.status==="connecting"||this.clients[y].connect()}}}async function ht(w){const[y,E,R,N]=await Promise.all([w.isPowerupProperty(),w.isPowerupSlot(),w.isPowerupPropertyListItem(),w.isPowerupEnum()]);return y||E||R||N}async function Ve(w){const y=await Promise.all(w.map(ht));return w.filter((E,R)=>!y[R])}async function at(w){const y=await Promise.all(w.map(E=>E.isPowerup()));return w.filter((E,R)=>!y[R])}async function Pt(w,y){try{return await w.richText.toMarkdown(y)}catch{return tt(y)}}function tt(w){return w.map(y=>{if(typeof y=="string")return y;if(typeof y!="object"||y===null)return"";const E=y;switch(E.i){case"m":return String(E.text??"");case"q":return`[[${String(E._id??"")}]]`;case"u":return E.title?`[${String(E.title)}](${String(E.url)})`:String(E.url??"");case"x":return`$${String(E.text??"")}$`;case"i":return`})`;case"a":return`[audio](${String(E.url??"")})`;case"p":return String(E.text??"");case"g":return String(E.text??"");case"n":return String(E.text??"");case"o":return String(E.text??"");case"s":case"fi":case"ai":return"";default:return String(E.text??E.url??"")}}).join("")}async function ot(w,y,E,R){const N=R?.includePowerup??!1,[j,Q,G,ve,Fe,De,he,mt,Me,$e,wt,Pe,Be]=await Promise.all([Pt(w,y.text??[]),y.backText?Pt(w,y.backText):Promise.resolve(null),y.getType(),y.isCardItem(),y.isDocument(),y.getTagRems().then(qe=>N?qe:at(qe).then(Re=>{const xt=qe.length-Re.length;return xt>0&&R?.onFilteredTags?.(xt),Re})),y.getPracticeDirection(),y.getFontSize(),y.isTodo(),y.getTodoStatus(),y.isCode(),y.hasPowerup("dv"),y.type===6?y.getPortalDirectlyIncludedRem():Promise.resolve([])]);let Ge=!1;E.length>0&&(Ge=(await Promise.all(E.map(Re=>Re.isCardItem()))).some(Boolean));const nt=Pe&&(y.text??[]).length===0,Qe=await Promise.all(De.map(async qe=>({id:qe._id,name:te(await Pt(w,qe.text??[]))})));return{id:y._id,markdownText:te(j),markdownBackText:Q!==null?te(Q):null,type:ae(G),hasMultilineChildren:Ge,practiceDirection:he??"none",isCardItem:ve,isDocument:Fe,isPortal:y.type===6,portalRefs:Be.map(qe=>qe._id),childrenCount:E.length,tags:Qe,fontSize:mt??null,isTodo:Me,todoStatus:$e??null,isCode:wt,isDivider:nt,isTopLevel:y.parent===null}}function te(w){return w.replace(/\n/g," ")}function ae(w){switch(w){case 1:return"concept";case 2:return"descriptor";case 6:return"portal";default:return"default"}}async function _e(w,y){const{includePowerup:E=!1}=y,R=await w.rem.findOne(y.remId);if(!R)throw new Error(`Rem not found: ${y.remId}`);return Ne(w,R,{includePowerup:E})}async function Ne(w,y,E){const{includePowerup:R=!1}=E,[N,j,Q,G,ve,Fe,De,he,mt,Me,$e,wt,Pe,Be,Ge,nt,Qe,qe,Re,xt,Je,an,Ke,Qt,un,pn,Zt,ut,ct,Nt,Bt,hn,Dt,Jn,ur,So,er,Tr,_n,Sn,Dn,cr,xr]=await Promise.all([y.isDocument(),y.getFontSize(),y.getHighlightColor(),y.isTodo(),y.getTodoStatus(),y.isCode(),y.isQuote(),y.isListItem(),y.isCardItem(),y.isTable(),y.isSlot(),y.isProperty(),y.getEnablePractice(),y.getPracticeDirection(),y.getTagRems(),y.getSources(),y.getAliases(),y.positionAmongstSiblings(),y.isPowerup(),y.isPowerupEnum(),y.isPowerupProperty(),y.isPowerupPropertyListItem(),y.isPowerupSlot(),y.getPortalType(),y.getPortalDirectlyIncludedRem(),y.getPropertyType(),y.remsBeingReferenced(),y.deepRemsBeingReferenced(),y.remsReferencingThis(),y.taggedRem(),y.ancestorTagRem(),y.descendantTagRem(),y.getDescendants(),y.siblingRem(),y.portalsAndDocumentsIn(),y.allRemInDocumentOrPortal(),y.allRemInFolderQueue(),y.getChildrenRem(),y.timesSelectedInSearch(),y.getLastTimeMovedTo(),y.getSchemaVersion(),y.embeddedQueueViewMode(),y.getLastPracticed()]);let dr=Ge,Cr=y.children??[],mi=0,Mr=0;if(!R){dr=await at(Ge),mi=Ge.length-dr.length;const ze=await Ve(Tr);Mr=Tr.length-ze.length,Cr=ze.map(An=>An._id)}const fr={id:y._id,text:He(y.text??[]),backText:y.backText?He(y.backText):null,type:ae(y.type),isDocument:N,parent:y.parent,children:Cr,fontSize:j??null,highlightColor:Q??null,isTodo:G,todoStatus:ve??null,isCode:Fe,isQuote:De,isListItem:he,isCardItem:mt,isTable:!!Me,isSlot:$e,isProperty:wt,isPowerup:Re,isPowerupEnum:xt,isPowerupProperty:Je,isPowerupPropertyListItem:an,isPowerupSlot:Ke,portalType:ae(y.type)==="portal"?st(Qt):null,portalDirectlyIncludedRem:un.map(ze=>ze._id),propertyType:pn??null,enablePractice:Pe,practiceDirection:Be,tags:dr.map(ze=>ze._id),sources:nt.map(ze=>ze._id),aliases:Qe.map(ze=>ze._id),remsBeingReferenced:Zt.map(ze=>ze._id),deepRemsBeingReferenced:ut.map(ze=>ze._id),remsReferencingThis:ct.map(ze=>ze._id),taggedRem:Nt.map(ze=>ze._id),ancestorTagRem:Bt.map(ze=>ze._id),descendantTagRem:hn.map(ze=>ze._id),descendants:Dt.map(ze=>ze._id),siblingRem:Jn.map(ze=>ze._id),portalsAndDocumentsIn:ur.map(ze=>ze._id),allRemInDocumentOrPortal:So.map(ze=>ze._id),allRemInFolderQueue:er.map(ze=>ze._id),positionAmongstSiblings:qe??null,timesSelectedInSearch:_n,lastTimeMovedTo:Sn,schemaVersion:Dn,embeddedQueueViewMode:cr,createdAt:y.createdAt,updatedAt:y.updatedAt,localUpdatedAt:y.localUpdatedAt,lastPracticed:xr};return!R&&(mi>0||Mr>0)?{...fr,powerupFiltered:{tags:mi,children:Mr}}:fr}function He(w){return w.map(y=>{if(typeof y=="string")return y;const E={};for(const R of Object.keys(y).sort())E[R]=y[R];return E})}function st(w){switch(w){case 2:return"embedded_queue";case 3:return"scaffold";case 4:return"search_portal";default:return"portal"}}function Z(w){return"type"in w&&w.type==="elided"}function Ze(w){if(w.isDivider)return"---";const{markdownText:y,markdownBackText:E,hasMultilineChildren:R,practiceDirection:N}=w;let j;return E!==null?j=y+(R?N==="backward"?" \u2191 ":N==="both"?" \u2195 ":" \u2193 ":N==="backward"?" \u2190 ":N==="both"?" \u2194 ":" \u2192 ")+E:R?j=y+(N==="backward"?" \u2191":N==="both"?" \u2195":" \u2193"):j=y,w.isCode&&(j="`"+j+"`"),w.isTodo&&(j=(w.todoStatus==="Finished"?"- [x] ":"- [ ] ")+j),w.fontSize&&(j=(w.fontSize==="H1"?"# ":w.fontSize==="H2"?"## ":"### ")+j),j}function Ye(w,y){const E=[];w.type==="concept"?E.push("type:concept"):w.type==="descriptor"?E.push("type:descriptor"):w.type==="portal"&&(E.push("type:portal"),w.portalRefs.length>0&&E.push("refs:"+w.portalRefs.join(","))),w.isDocument&&E.push("doc"),w.isCardItem&&E.push("role:card-item"),y&&w.childrenCount>0&&E.push("children:"+w.childrenCount);for(const R of w.tags)E.push("tag:"+R.name+"("+R.id+")");return w.isTopLevel&&E.push("top"),E}function ue(w){return{markdownBackText:null,type:"default",hasMultilineChildren:!1,practiceDirection:"none",isCardItem:!1,isDocument:!1,isPortal:!1,portalRefs:[],tags:[],fontSize:null,isTodo:!1,todoStatus:null,isCode:!1,isDivider:!1,...w}}function fe(w,y=!1){const E=Ze(w),R=Ye(w,y),N=R.length>0?" "+R.join(" "):"";return`${E} <!--${w.id}${N}-->`}function ee(w){const y=w.isExact?"siblings":"nodes";return`<!--...elided ${w.isExact?"":">="}${w.count} ${y} (parent:${w.parentId} range:${w.rangeFrom}-${w.rangeTo} total:${w.totalSiblings})-->`}function ie(w){const y=[];function E(R,N){const j=" ".repeat(N);if(Z(R)){y.push(j+ee(R));return}const Q=fe(R.rem,R.folded);if(y.push(j+Q),!R.folded)for(const G of R.children)E(G,N+1)}return E(w,0),y.join(`
|
|
58
|
+
*/var Xe,Ot,Ae,it;if(typeof performance=="object"&&typeof performance.now=="function"){var B=performance;K.unstable_now=function(){return B.now()}}else{var wn=Date,Et=wn.now();K.unstable_now=function(){return wn.now()-Et}}if(typeof window>"u"||typeof MessageChannel!="function"){var Tt=null,b=null,Y=function(){if(Tt!==null)try{var F=K.unstable_now();Tt(!0,F),Tt=null}catch(A){throw setTimeout(Y,0),A}};Xe=function(F){Tt!==null?setTimeout(Xe,0,F):(Tt=F,setTimeout(Y,0))},Ot=function(F,A){b=setTimeout(F,A)},Ae=function(){clearTimeout(b)},K.unstable_shouldYield=function(){return!1},it=K.unstable_forceFrameRate=function(){}}else{var c=window.setTimeout,Mt=window.clearTimeout;if(typeof console<"u"){var ht=window.cancelAnimationFrame;typeof window.requestAnimationFrame!="function"&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills"),typeof ht!="function"&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills")}var Ve=!1,at=null,Pt=-1,tt=5,ot=0;K.unstable_shouldYield=function(){return K.unstable_now()>=ot},it=function(){},K.unstable_forceFrameRate=function(F){0>F||125<F?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):tt=0<F?Math.floor(1e3/F):5};var te=new MessageChannel,ae=te.port2;te.port1.onmessage=function(){if(at!==null){var F=K.unstable_now();ot=F+tt;try{at(!0,F)?ae.postMessage(null):(Ve=!1,at=null)}catch(A){throw ae.postMessage(null),A}}else Ve=!1},Xe=function(F){at=F,Ve||(Ve=!0,ae.postMessage(null))},Ot=function(F,A){Pt=c(function(){F(K.unstable_now())},A)},Ae=function(){Mt(Pt),Pt=-1}}function _e(F,A){var $=F.length;F.push(A);e:for(;;){var H=$-1>>>1,ne=F[H];if(ne!==void 0&&0<st(ne,A))F[H]=A,F[$]=ne,$=H;else break e}}function Ne(F){return F=F[0],F===void 0?null:F}function He(F){var A=F[0];if(A!==void 0){var $=F.pop();if($!==A){F[0]=$;e:for(var H=0,ne=F.length;H<ne;){var U=2*(H+1)-1,ye=F[U],M=U+1,sn=F[M];if(ye!==void 0&&0>st(ye,$))sn!==void 0&&0>st(sn,ye)?(F[H]=sn,F[M]=$,H=M):(F[H]=ye,F[U]=$,H=U);else if(sn!==void 0&&0>st(sn,$))F[H]=sn,F[M]=$,H=M;else break e}}return A}return null}function st(F,A){var $=F.sortIndex-A.sortIndex;return $!==0?$:F.id-A.id}var Z=[],Ze=[],Ye=1,ue=null,fe=3,ee=!1,ie=!1,me=!1;function q(F){for(var A=Ne(Ze);A!==null;){if(A.callback===null)He(Ze);else if(A.startTime<=F)He(Ze),A.sortIndex=A.expirationTime,_e(Z,A);else break;A=Ne(Ze)}}function ge(F){if(me=!1,q(F),!ie)if(Ne(Z)!==null)ie=!0,Xe(C);else{var A=Ne(Ze);A!==null&&Ot(ge,A.startTime-F)}}function C(F,A){ie=!1,me&&(me=!1,Ae()),ee=!0;var $=fe;try{for(q(A),ue=Ne(Z);ue!==null&&(!(ue.expirationTime>A)||F&&!K.unstable_shouldYield());){var H=ue.callback;if(typeof H=="function"){ue.callback=null,fe=ue.priorityLevel;var ne=H(ue.expirationTime<=A);A=K.unstable_now(),typeof ne=="function"?ue.callback=ne:ue===Ne(Z)&&He(Z),q(A)}else He(Z);ue=Ne(Z)}if(ue!==null)var U=!0;else{var ye=Ne(Ze);ye!==null&&Ot(ge,ye.startTime-A),U=!1}return U}finally{ue=null,fe=$,ee=!1}}var V=it;K.unstable_IdlePriority=5,K.unstable_ImmediatePriority=1,K.unstable_LowPriority=4,K.unstable_NormalPriority=3,K.unstable_Profiling=null,K.unstable_UserBlockingPriority=2,K.unstable_cancelCallback=function(F){F.callback=null},K.unstable_continueExecution=function(){ie||ee||(ie=!0,Xe(C))},K.unstable_getCurrentPriorityLevel=function(){return fe},K.unstable_getFirstCallbackNode=function(){return Ne(Z)},K.unstable_next=function(F){switch(fe){case 1:case 2:case 3:var A=3;break;default:A=fe}var $=fe;fe=A;try{return F()}finally{fe=$}},K.unstable_pauseExecution=function(){},K.unstable_requestPaint=V,K.unstable_runWithPriority=function(F,A){switch(F){case 1:case 2:case 3:case 4:case 5:break;default:F=3}var $=fe;fe=F;try{return A()}finally{fe=$}},K.unstable_scheduleCallback=function(F,A,$){var H=K.unstable_now();switch(typeof $=="object"&&$!==null?($=$.delay,$=typeof $=="number"&&0<$?H+$:H):$=H,F){case 1:var ne=-1;break;case 2:ne=250;break;case 5:ne=1073741823;break;case 4:ne=1e4;break;default:ne=5e3}return ne=$+ne,F={id:Ye++,callback:A,priorityLevel:F,startTime:$,expirationTime:ne,sortIndex:-1},$>H?(F.sortIndex=$,_e(Ze,F),Ne(Z)===null&&F===Ne(Ze)&&(me?Ae():me=!0,Ot(ge,$-H))):(F.sortIndex=ne,_e(Z,F),ie||ee||(ie=!0,Xe(C))),F},K.unstable_wrapCallback=function(F){var A=fe;return function(){var $=fe;fe=A;try{return F.apply(this,arguments)}finally{fe=$}}}},825(en,K,Xe){"use strict";en.exports=Xe(742)}},vd={};function Ac(en){var K=vd[en];if(K!==void 0)return K.exports;var Xe=vd[en]={exports:{}};return Pf[en](Xe,Xe.exports,Ac),Xe.exports}Ac.g=(function(){if(typeof globalThis=="object")return globalThis;try{return this||new Function("return this")()}catch{if(typeof window=="object")return window}})();var wp={};(()=>{"use strict";var en=Ac(216);const K="0.2.1",Xe=[29100,29110,29120,29130],Ot=18e3;var Ae=Object.defineProperty,it=(w,y,E)=>y in w?Ae(w,y,{enumerable:!0,configurable:!0,writable:!0,value:E}):w[y]=E,B=(w,y,E)=>it(w,typeof y!="symbol"?y+"":y,E);const wn=4e3,Et=4003;class Tt{constructor(y){B(this,"ws",null),B(this,"reconnectAttempts",0),B(this,"reconnectTimeout",null),B(this,"messageHandler",null),B(this,"status","disconnected"),B(this,"isShuttingDown",!1),B(this,"isPreempted",!1),B(this,"_sdkReady"),B(this,"config"),this._sdkReady=y.sdkReady,this.config={url:y.url,pluginVersion:y.pluginVersion,sdkReady:y.sdkReady,twinSlotIndex:y.twinSlotIndex,isTwinConnection:y.isTwinConnection??!1,maxReconnectAttempts:y.maxReconnectAttempts??10,initialReconnectDelay:y.initialReconnectDelay??1e3,maxReconnectDelay:y.maxReconnectDelay??3e4,onStatusChange:y.onStatusChange,onLog:y.onLog,onPreempted:y.onPreempted,onTwinOccupied:y.onTwinOccupied,onOtherOccupied:y.onOtherOccupied}}log(y,E="info"){this.config.onLog?.(y,E)}setStatus(y){this.status!==y&&(this.status=y,this.config.onStatusChange?.(y))}sendHello(){const y={type:"hello",version:this.config.pluginVersion,sdkReady:this._sdkReady,twinSlotIndex:this.config.twinSlotIndex};try{this.ws?.send(JSON.stringify(y)),this.log(`\u53D1\u9001 hello\uFF08v${this.config.pluginVersion}, sdkReady=${this._sdkReady}, twinSlot=${this.config.twinSlotIndex}\uFF09`)}catch(E){this.log(`\u53D1\u9001 hello \u5931\u8D25: ${E}`,"warn")}}connect(){if(!(this.ws?.readyState===WebSocket.OPEN||this.ws?.readyState===WebSocket.CONNECTING)){this.isShuttingDown=!1,this.isPreempted=!1,this.setStatus("connecting");try{this.ws=new WebSocket(this.config.url),this.ws.onopen=()=>{this.log("\u5DF2\u8FDE\u63A5\u5230\u5B88\u62A4\u8FDB\u7A0B"),this.reconnectAttempts=0,this.setStatus("connected"),this.sendHello()},this.ws.onmessage=async y=>{await this.handleMessage(typeof y.data=="string"?y.data:String(y.data))},this.ws.onclose=y=>{y.code!==1006&&this.log(`\u8FDE\u63A5\u65AD\u5F00: ${y.code} ${y.reason}`,"warn"),this.setStatus("disconnected"),y.code===Et?this.config.onTwinOccupied?.():y.code===wn&&this.config.onOtherOccupied?.(),this.isShuttingDown||this.scheduleReconnect()},this.ws.onerror=()=>{}}catch(y){this.log(`\u8FDE\u63A5\u5931\u8D25: ${y}`,"error"),this.setStatus("disconnected"),this.scheduleReconnect()}}}async handleMessage(y){try{const E=JSON.parse(y);if(E.type==="preempted"){this.isPreempted=!0,this.log(`\u88AB\u5B6A\u751F Plugin \u62A2\u5360: ${E.reason}`,"warn"),this.config.onPreempted?.();return}if(E.type==="ping"){this.ws?.send(JSON.stringify({type:"pong"}));return}if(E.id&&E.action&&this.messageHandler){const R=E;this.log(`\u6536\u5230\u8BF7\u6C42: ${R.action}`);try{const N=await this.messageHandler(R),j={id:R.id,result:N};this.ws?.send(JSON.stringify(j)),this.log(`\u5B8C\u6210: ${R.action}`)}catch(N){const j=N instanceof Error?N.message:String(N),Q={id:R.id,error:j};this.ws?.send(JSON.stringify(Q)),this.log(`\u5931\u8D25: ${R.action} - ${j}`,"error")}}}catch(E){this.log(`\u5904\u7406\u6D88\u606F\u5931\u8D25: ${E}`,"error")}}scheduleReconnect(){if(this.isShuttingDown||this.isPreempted||!this.config.isTwinConnection)return;if(this.reconnectAttempts>=this.config.maxReconnectAttempts){this.log("\u5DF2\u8FBE\u6700\u5927\u91CD\u8FDE\u6B21\u6570","error");return}const y=Math.min(this.config.initialReconnectDelay*Math.pow(2,this.reconnectAttempts),this.config.maxReconnectDelay),E=Math.random()*.3*y,R=y+E;this.reconnectAttempts++,this.log(`${Math.round(R)}ms \u540E\u91CD\u8FDE\uFF08\u7B2C ${this.reconnectAttempts}/${this.config.maxReconnectAttempts} \u6B21\uFF09`),this.reconnectTimeout=setTimeout(()=>{this.connect()},R)}setMessageHandler(y){this.messageHandler=y}disconnect(){this.isShuttingDown=!0,this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.ws&&(this.ws.close(1e3,"Plugin disconnect"),this.ws=null),this.setStatus("disconnected")}getStatus(){return this.status}}var b=Object.defineProperty,Y=(w,y,E)=>y in w?b(w,y,{enumerable:!0,configurable:!0,writable:!0,value:E}):w[y]=E,c=(w,y,E)=>Y(w,typeof y!="symbol"?y+"":y,E);class Mt{constructor(y){c(this,"clients",[]),c(this,"slotStates",[]),c(this,"scanTimer",null),c(this,"config"),this.config=y;for(let E=0;E<Xe.length;E++){const R=E===y.twinSlotIndex;this.slotStates.push({slotIndex:E,wsPort:Xe[E],status:"disconnected",isTwin:R,disconnectReason:"not_started"}),this.clients.push(new Tt({url:`ws://127.0.0.1:${Xe[E]}`,pluginVersion:y.pluginVersion,sdkReady:y.sdkReady,twinSlotIndex:y.twinSlotIndex,isTwinConnection:R,maxReconnectAttempts:R?10:0,initialReconnectDelay:1e3,maxReconnectDelay:3e4,onStatusChange:N=>this.handleStatusChange(E,N),onLog:(N,j)=>y.onLog(E,N,j),onPreempted:()=>this.handleDisconnectReason(E,"preempted"),onTwinOccupied:()=>this.handleDisconnectReason(E,"twin_occupied"),onOtherOccupied:()=>this.handleDisconnectReason(E,"other_occupied")}))}}setMessageHandler(y){for(const E of this.clients)E.setMessageHandler(y)}start(){const y=this.config.twinSlotIndex;this.clients[y].connect(),setTimeout(()=>{for(let E=0;E<this.clients.length;E++)E!==y&&this.clients[E].connect()},2e3),this.scanTimer=setInterval(()=>this.scanAndReconnect(),Ot)}stop(){this.scanTimer&&(clearInterval(this.scanTimer),this.scanTimer=null);for(const y of this.clients)y.disconnect()}getSlots(){return this.slotStates.slice()}handleStatusChange(y,E){const R=this.slotStates[y];R.status=E,E==="connected"&&(R.disconnectReason=null),this.notifySlotsChange()}handleDisconnectReason(y,E){this.slotStates[y].disconnectReason=E,this.notifySlotsChange()}notifySlotsChange(){this.config.onSlotsChange(this.slotStates.slice())}scanAndReconnect(){for(let y=0;y<this.clients.length;y++){const E=this.slotStates[y];E.isTwin||E.status==="connected"||E.status==="connecting"||this.clients[y].connect()}}}async function ht(w){const[y,E,R,N]=await Promise.all([w.isPowerupProperty(),w.isPowerupSlot(),w.isPowerupPropertyListItem(),w.isPowerupEnum()]);return y||E||R||N}async function Ve(w){const y=await Promise.all(w.map(ht));return w.filter((E,R)=>!y[R])}async function at(w){const y=await Promise.all(w.map(E=>E.isPowerup()));return w.filter((E,R)=>!y[R])}async function Pt(w,y){try{return await w.richText.toMarkdown(y)}catch{return tt(y)}}function tt(w){return w.map(y=>{if(typeof y=="string")return y;if(typeof y!="object"||y===null)return"";const E=y;switch(E.i){case"m":return String(E.text??"");case"q":return`[[${String(E._id??"")}]]`;case"u":return E.title?`[${String(E.title)}](${String(E.url)})`:String(E.url??"");case"x":return`$${String(E.text??"")}$`;case"i":return`})`;case"a":return`[audio](${String(E.url??"")})`;case"p":return String(E.text??"");case"g":return String(E.text??"");case"n":return String(E.text??"");case"o":return String(E.text??"");case"s":case"fi":case"ai":return"";default:return String(E.text??E.url??"")}}).join("")}async function ot(w,y,E,R){const N=R?.includePowerup??!1,[j,Q,G,ve,Fe,De,he,mt,Me,$e,wt,Pe,Be]=await Promise.all([Pt(w,y.text??[]),y.backText?Pt(w,y.backText):Promise.resolve(null),y.getType(),y.isCardItem(),y.isDocument(),y.getTagRems().then(qe=>N?qe:at(qe).then(Re=>{const xt=qe.length-Re.length;return xt>0&&R?.onFilteredTags?.(xt),Re})),y.getPracticeDirection(),y.getFontSize(),y.isTodo(),y.getTodoStatus(),y.isCode(),y.hasPowerup("dv"),y.type===6?y.getPortalDirectlyIncludedRem():Promise.resolve([])]);let Ge=!1;E.length>0&&(Ge=(await Promise.all(E.map(Re=>Re.isCardItem()))).some(Boolean));const nt=Pe&&(y.text??[]).length===0,Qe=await Promise.all(De.map(async qe=>({id:qe._id,name:te(await Pt(w,qe.text??[]))})));return{id:y._id,markdownText:te(j),markdownBackText:Q!==null?te(Q):null,type:ae(G),hasMultilineChildren:Ge,practiceDirection:he??"none",isCardItem:ve,isDocument:Fe,isPortal:y.type===6,portalRefs:Be.map(qe=>qe._id),childrenCount:E.length,tags:Qe,fontSize:mt??null,isTodo:Me,todoStatus:$e??null,isCode:wt,isDivider:nt,isTopLevel:y.parent===null}}function te(w){return w.replace(/\n/g," ")}function ae(w){switch(w){case 1:return"concept";case 2:return"descriptor";case 6:return"portal";default:return"default"}}async function _e(w,y){const{includePowerup:E=!1}=y,R=await w.rem.findOne(y.remId);if(!R)throw new Error(`Rem not found: ${y.remId}`);return Ne(w,R,{includePowerup:E})}async function Ne(w,y,E){const{includePowerup:R=!1}=E,[N,j,Q,G,ve,Fe,De,he,mt,Me,$e,wt,Pe,Be,Ge,nt,Qe,qe,Re,xt,Je,an,Ke,Qt,un,pn,Zt,ut,ct,Nt,Bt,hn,Dt,Jn,ur,So,er,Tr,_n,Sn,Dn,cr,xr]=await Promise.all([y.isDocument(),y.getFontSize(),y.getHighlightColor(),y.isTodo(),y.getTodoStatus(),y.isCode(),y.isQuote(),y.isListItem(),y.isCardItem(),y.isTable(),y.isSlot(),y.isProperty(),y.getEnablePractice(),y.getPracticeDirection(),y.getTagRems(),y.getSources(),y.getAliases(),y.positionAmongstSiblings(),y.isPowerup(),y.isPowerupEnum(),y.isPowerupProperty(),y.isPowerupPropertyListItem(),y.isPowerupSlot(),y.getPortalType(),y.getPortalDirectlyIncludedRem(),y.getPropertyType(),y.remsBeingReferenced(),y.deepRemsBeingReferenced(),y.remsReferencingThis(),y.taggedRem(),y.ancestorTagRem(),y.descendantTagRem(),y.getDescendants(),y.siblingRem(),y.portalsAndDocumentsIn(),y.allRemInDocumentOrPortal(),y.allRemInFolderQueue(),y.getChildrenRem(),y.timesSelectedInSearch(),y.getLastTimeMovedTo(),y.getSchemaVersion(),y.embeddedQueueViewMode(),y.getLastPracticed()]);let dr=Ge,Cr=y.children??[],mi=0,Mr=0;if(!R){dr=await at(Ge),mi=Ge.length-dr.length;const ze=await Ve(Tr);Mr=Tr.length-ze.length,Cr=ze.map(An=>An._id)}const fr={id:y._id,text:He(y.text??[]),backText:y.backText?He(y.backText):null,type:ae(y.type),isDocument:N,parent:y.parent,children:Cr,fontSize:j??null,highlightColor:Q??null,isTodo:G,todoStatus:ve??null,isCode:Fe,isQuote:De,isListItem:he,isCardItem:mt,isTable:!!Me,isSlot:$e,isProperty:wt,isPowerup:Re,isPowerupEnum:xt,isPowerupProperty:Je,isPowerupPropertyListItem:an,isPowerupSlot:Ke,portalType:ae(y.type)==="portal"?st(Qt):null,portalDirectlyIncludedRem:un.map(ze=>ze._id).sort(),propertyType:pn??null,enablePractice:Pe,practiceDirection:Be,tags:dr.map(ze=>ze._id).sort(),sources:nt.map(ze=>ze._id).sort(),aliases:Qe.map(ze=>ze._id).sort(),remsBeingReferenced:Zt.map(ze=>ze._id).sort(),deepRemsBeingReferenced:ut.map(ze=>ze._id).sort(),remsReferencingThis:ct.map(ze=>ze._id).sort(),taggedRem:Nt.map(ze=>ze._id).sort(),ancestorTagRem:Bt.map(ze=>ze._id).sort(),descendantTagRem:hn.map(ze=>ze._id).sort(),descendants:Dt.map(ze=>ze._id).sort(),siblingRem:Jn.map(ze=>ze._id).sort(),portalsAndDocumentsIn:ur.map(ze=>ze._id).sort(),allRemInDocumentOrPortal:So.map(ze=>ze._id).sort(),allRemInFolderQueue:er.map(ze=>ze._id).sort(),positionAmongstSiblings:qe??null,timesSelectedInSearch:_n,lastTimeMovedTo:Sn,schemaVersion:Dn,embeddedQueueViewMode:cr,createdAt:y.createdAt,updatedAt:y.updatedAt,localUpdatedAt:y.localUpdatedAt,lastPracticed:xr};return!R&&(mi>0||Mr>0)?{...fr,powerupFiltered:{tags:mi,children:Mr}}:fr}function He(w){return w.map(y=>{if(typeof y=="string")return y;const E={};for(const R of Object.keys(y).sort())E[R]=y[R];return E})}function st(w){switch(w){case 2:return"embedded_queue";case 3:return"scaffold";case 4:return"search_portal";default:return"portal"}}function Z(w){return"type"in w&&w.type==="elided"}function Ze(w){if(w.isDivider)return"---";const{markdownText:y,markdownBackText:E,hasMultilineChildren:R,practiceDirection:N}=w;let j;return E!==null?j=y+(R?N==="backward"?" \u2191 ":N==="both"?" \u2195 ":" \u2193 ":N==="backward"?" \u2190 ":N==="both"?" \u2194 ":" \u2192 ")+E:R?j=y+(N==="backward"?" \u2191":N==="both"?" \u2195":" \u2193"):j=y,w.isCode&&(j="`"+j+"`"),w.isTodo&&(j=(w.todoStatus==="Finished"?"- [x] ":"- [ ] ")+j),w.fontSize&&(j=(w.fontSize==="H1"?"# ":w.fontSize==="H2"?"## ":"### ")+j),j}function Ye(w,y){const E=[];w.type==="concept"?E.push("type:concept"):w.type==="descriptor"?E.push("type:descriptor"):w.type==="portal"&&(E.push("type:portal"),w.portalRefs.length>0&&E.push("refs:"+w.portalRefs.join(","))),w.isDocument&&E.push("doc"),w.isCardItem&&E.push("role:card-item"),y&&w.childrenCount>0&&E.push("children:"+w.childrenCount);for(const R of w.tags)E.push("tag:"+R.name+"("+R.id+")");return w.isTopLevel&&E.push("top"),E}function ue(w){return{markdownBackText:null,type:"default",hasMultilineChildren:!1,practiceDirection:"none",isCardItem:!1,isDocument:!1,isPortal:!1,portalRefs:[],tags:[],fontSize:null,isTodo:!1,todoStatus:null,isCode:!1,isDivider:!1,...w}}function fe(w,y=!1){const E=Ze(w),R=Ye(w,y),N=R.length>0?" "+R.join(" "):"";return`${E} <!--${w.id}${N}-->`}function ee(w){const y=w.isExact?"siblings":"nodes";return`<!--...elided ${w.isExact?"":">="}${w.count} ${y} (parent:${w.parentId} range:${w.rangeFrom}-${w.rangeTo} total:${w.totalSiblings})-->`}function ie(w){const y=[];function E(R,N){const j=" ".repeat(N);if(Z(R)){y.push(j+ee(R));return}const Q=fe(R.rem,R.folded);if(y.push(j+Q),!R.folded)for(const G of R.children)E(G,N+1)}return E(w,0),y.join(`
|
|
59
59
|
`)}function me(w,y,E){if(w<=y)return{visibleIndices:null,elided:null};const R=Math.ceil(y*.7),N=Math.floor(y*.3);return{visibleIndices:{head:R,tail:N},elided:{count:w-R-N,parentId:E,rangeFrom:R,rangeTo:w-N-1,totalSiblings:w}}}async function q(w,y){const{remId:E,depth:R=3,maxNodes:N=200,maxSiblings:j=20,ancestorLevels:Q=0,includePowerup:G=!1}=y,ve=await w.rem.findOne(E);if(!ve)throw new Error(`Rem not found: ${E}`);let Fe=0,De=0,he=0;const mt={remaining:N},Me=[];async function $e(Qe,qe,Re){Fe++,mt.remaining--,Me.push(Qe._id);const xt=await Qe.getChildrenRem(),Je=G?xt:await Ve(xt);G||(he+=xt.length-Je.length);const Ke=Re!==-1&&qe>=Re&&Je.length>0,Qt=await ot(w,Qe,Je,{includePowerup:G,onFilteredTags:pn=>{De+=pn}});Ke&&(Qt.hasMultilineChildren=!1);const un=[];if(!Ke){const{visibleIndices:pn,elided:Zt}=me(Je.length,j,Qe._id);if(pn){const{head:ut,tail:ct}=pn;for(let Bt=0;Bt<ut&&mt.remaining>0;Bt++)un.push(await $e(Je[Bt],qe+1,Re));if(Zt){const Bt=Re!==-1&&qe+1>=Re;un.push({type:"elided",count:Zt.count,isExact:Bt,parentId:Zt.parentId,rangeFrom:Zt.rangeFrom,rangeTo:Zt.rangeTo,totalSiblings:Zt.totalSiblings})}const Nt=Je.length-ct;for(let Bt=Nt;Bt<Je.length&&mt.remaining>0;Bt++)un.push(await $e(Je[Bt],qe+1,Re))}else for(let ut=0;ut<Je.length;ut++){if(mt.remaining<=0){const ct=Je.length-ut;un.push({type:"elided",count:ct,isExact:!1,parentId:Qe._id,rangeFrom:ut,rangeTo:Je.length-1,totalSiblings:Je.length});break}un.push(await $e(Je[ut],qe+1,Re))}}return{rem:Qt,children:un,folded:Ke}}const wt=await $e(ve,0,R);await ve.getParentRem()||(wt.rem.isTopLevel=!0);const Be=ie(wt),Ge={rootId:E,depth:R,nodeCount:Fe,outline:Be,nodeRemIds:Me},nt=Math.min(Math.max(Q,0),10);if(nt>0){const Qe=[];let qe=ve;for(let Re=0;Re<nt;Re++){const xt=await qe.getParentRem();if(!xt)break;const[Je,an,Ke]=await Promise.all([Pt(w,xt.text??[]),xt.getChildrenRem(),xt.isDocument()]);Qe.push({id:xt._id,name:te(Je),childrenCount:an.length,isDocument:Ke}),qe=xt}if(Qe.length>0){const Re=Qe[Qe.length-1];await qe.getParentRem()||(Re.isTopLevel=!0),Ge.ancestors=Qe}}return!G&&(De>0||he>0)&&(Ge.powerupFiltered={tags:De,children:he}),Ge}async function ge(w,y){const E=await q(w,y),R=y.includePowerup??!1,N={};return await Promise.all(E.nodeRemIds.map(async j=>{const Q=await w.rem.findOne(j);Q&&(N[j]=await Ne(w,Q,{includePowerup:R}))})),{...E,remObjects:N}}async function C(w,y){const{depth:E=-1,maxNodes:R=200,maxSiblings:N=20}=y,Q=(await w.rem.getAll()).filter(Pe=>Pe.parent===null),G=await Promise.all(Q.map(Pe=>Pe.isDocument())),ve=Q.filter((Pe,Be)=>G[Be]);let Fe=0;const De={remaining:R};async function he(Pe,Be,Ge){Fe++,De.remaining--;const nt=await Pe.getChildrenRem(),Qe=await Ve(nt),qe=await Promise.all(Qe.map(ut=>ut.isDocument())),Re=Qe.filter((ut,ct)=>qe[ct]),xt=Qe.length-Re.length,an=Ge!==-1&&Be>=Ge&&Re.length>0,Ke=Pe.type===6,[Qt,un]=await Promise.all([Pt(w,Pe.text??[]),Ke?Pe.getPortalDirectlyIncludedRem():Promise.resolve([])]),pn=ue({id:Pe._id,markdownText:Qt.replace(/\n/g," "),childrenCount:Qe.length,isDocument:!0,isTopLevel:Pe.parent===null,isPortal:Ke,...Ke?{type:"portal",portalRefs:un.map(ut=>ut._id)}:{}}),Zt=[];if(!an&&Re.length>0){const{visibleIndices:ut,elided:ct}=me(Re.length,N,Pe._id);if(ut){const{head:Nt,tail:Bt}=ut;for(let Dt=0;Dt<Nt&&De.remaining>0;Dt++)Zt.push(await he(Re[Dt],Be+1,Ge));if(ct){const Dt=Ge!==-1&&Be+1>=Ge;Zt.push({type:"elided",count:ct.count,isExact:Dt,parentId:ct.parentId,rangeFrom:ct.rangeFrom,rangeTo:ct.rangeTo,totalSiblings:ct.totalSiblings})}const hn=Re.length-Bt;for(let Dt=hn;Dt<Re.length&&De.remaining>0;Dt++)Zt.push(await he(Re[Dt],Be+1,Ge))}else for(let Nt=0;Nt<Re.length;Nt++){if(De.remaining<=0){const Bt=Re.length-Nt;Zt.push({type:"elided",count:Bt,isExact:!1,parentId:Pe._id,rangeFrom:Nt,rangeTo:Re.length-1,totalSiblings:Re.length});break}Zt.push(await he(Re[Nt],Be+1,Ge))}}return{rem:pn,children:Zt,folded:an}}const mt=[],{visibleIndices:Me,elided:$e}=me(ve.length,N,"root");if(Me){const{head:Pe,tail:Be}=Me;for(let nt=0;nt<Pe&&De.remaining>0;nt++)mt.push(await he(ve[nt],0,E));$e&&mt.push({type:"elided",count:$e.count,isExact:!1,parentId:"root",rangeFrom:$e.rangeFrom,rangeTo:$e.rangeTo,totalSiblings:$e.totalSiblings});const Ge=ve.length-Be;for(let nt=Ge;nt<ve.length&&De.remaining>0;nt++)mt.push(await he(ve[nt],0,E))}else for(let Pe=0;Pe<ve.length;Pe++){if(De.remaining<=0){const Be=ve.length-Pe;mt.push({type:"elided",count:Be,isExact:!1,parentId:"root",rangeFrom:Pe,rangeTo:ve.length-1,totalSiblings:ve.length});break}mt.push(await he(ve[Pe],0,E))}const wt=["<!-- globe: \u77E5\u8BC6\u5E93\u6982\u89C8 -->"];for(const Pe of mt)Z(Pe)?wt.push(ee(Pe)):wt.push(ie(Pe));return{nodeCount:Fe,outline:wt.join(`
|
|
60
60
|
`)}}async function V(w,y){const E=[];let R=y;for(;R;){const N=await Pt(w,R.text??[]);E.unshift(N.replace(/\n/g," ").trim()||R._id),R=await R.getParentRem()}return E}async function F(w,y){const{mode:E="focus",ancestorLevels:R=2,maxNodes:N=200,maxSiblings:j=20,depth:Q=3,focusRemId:G}=y;if(E==="page"){if(G)throw new Error("focusRemId \u4EC5\u5728 focus \u6A21\u5F0F\u4E0B\u6709\u6548\uFF0Cpage \u6A21\u5F0F\u4E0B\u8BF7\u52FF\u6307\u5B9A");return A(w,{maxNodes:N,maxSiblings:j,depth:Q})}return $(w,{ancestorLevels:R,maxNodes:N,maxSiblings:j,focusRemId:G})}async function A(w,y){const E=await w.window.getFocusedPaneId();if(!E)throw new Error("\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u9762\u677F ID\uFF0C\u8BF7\u786E\u4FDD\u6709\u6253\u5F00\u7684\u9875\u9762");const R=await w.window.getOpenPaneRemId(E);if(!R)throw new Error("\u5F53\u524D\u9762\u677F\u6CA1\u6709\u6253\u5F00\u4EFB\u4F55 Rem");const N=await w.rem.findOne(R);if(!N)throw new Error(`Page Rem not found: ${R}`);const j=await V(w,N);let Q=0;const G={remaining:y.maxNodes},ve=await U(w,N,0,y.depth,y.maxSiblings,G);Q=y.maxNodes-G.remaining;const De=`<!-- page: ${j[j.length-1]||R} -->
|
|
61
61
|
<!-- path: ${j.join(" > ")} -->`+`
|
|
@@ -211,7 +211,7 @@ export async function buildRemObject(
|
|
|
211
211
|
portalType: remTypeToString(rem.type as number) === 'portal'
|
|
212
212
|
? portalTypeToString(portalType as number)
|
|
213
213
|
: null,
|
|
214
|
-
portalDirectlyIncludedRem: portalDirectlyIncludedRems.map(r => r._id),
|
|
214
|
+
portalDirectlyIncludedRem: portalDirectlyIncludedRems.map(r => r._id).sort(),
|
|
215
215
|
|
|
216
216
|
// 属性类型
|
|
217
217
|
propertyType: (propertyType as PropertyTypeValue | undefined) ?? null,
|
|
@@ -220,27 +220,27 @@ export async function buildRemObject(
|
|
|
220
220
|
enablePractice,
|
|
221
221
|
practiceDirection: practiceDirection as RemObject['practiceDirection'],
|
|
222
222
|
|
|
223
|
-
// 关联 —
|
|
224
|
-
tags: filteredTagRems.map(r => r._id),
|
|
225
|
-
sources: sourceRems.map(r => r._id),
|
|
226
|
-
aliases: aliasRems.map(r => r._id),
|
|
223
|
+
// 关联 — 直接关系(排序保证确定性序列化)
|
|
224
|
+
tags: filteredTagRems.map(r => r._id).sort(),
|
|
225
|
+
sources: sourceRems.map(r => r._id).sort(),
|
|
226
|
+
aliases: aliasRems.map(r => r._id).sort(),
|
|
227
227
|
|
|
228
228
|
// 关联 — 引用关系
|
|
229
|
-
remsBeingReferenced: refsBeingReferenced.map(r => r._id),
|
|
230
|
-
deepRemsBeingReferenced: deepRefsBeingReferenced.map(r => r._id),
|
|
231
|
-
remsReferencingThis: refsReferencingThis.map(r => r._id),
|
|
229
|
+
remsBeingReferenced: refsBeingReferenced.map(r => r._id).sort(),
|
|
230
|
+
deepRemsBeingReferenced: deepRefsBeingReferenced.map(r => r._id).sort(),
|
|
231
|
+
remsReferencingThis: refsReferencingThis.map(r => r._id).sort(),
|
|
232
232
|
|
|
233
233
|
// 关联 — 标签体系
|
|
234
|
-
taggedRem: taggedRems.map(r => r._id),
|
|
235
|
-
ancestorTagRem: ancestorTagRems.map(r => r._id),
|
|
236
|
-
descendantTagRem: descendantTagRems.map(r => r._id),
|
|
234
|
+
taggedRem: taggedRems.map(r => r._id).sort(),
|
|
235
|
+
ancestorTagRem: ancestorTagRems.map(r => r._id).sort(),
|
|
236
|
+
descendantTagRem: descendantTagRems.map(r => r._id).sort(),
|
|
237
237
|
|
|
238
238
|
// 关联 — 层级遍历
|
|
239
|
-
descendants: descendantRems.map(r => r._id),
|
|
240
|
-
siblingRem: siblingRems.map(r => r._id),
|
|
241
|
-
portalsAndDocumentsIn: portalsAndDocsIn.map(r => r._id),
|
|
242
|
-
allRemInDocumentOrPortal: allRemInDocOrPortal.map(r => r._id),
|
|
243
|
-
allRemInFolderQueue: allRemInFolderQ.map(r => r._id),
|
|
239
|
+
descendants: descendantRems.map(r => r._id).sort(),
|
|
240
|
+
siblingRem: siblingRems.map(r => r._id).sort(),
|
|
241
|
+
portalsAndDocumentsIn: portalsAndDocsIn.map(r => r._id).sort(),
|
|
242
|
+
allRemInDocumentOrPortal: allRemInDocOrPortal.map(r => r._id).sort(),
|
|
243
|
+
allRemInFolderQueue: allRemInFolderQ.map(r => r._id).sort(),
|
|
244
244
|
|
|
245
245
|
// 位置 / 统计
|
|
246
246
|
positionAmongstSiblings: position ?? null,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 多 daemon 连接:Plugin 同时连接 ALL_WS_PORTS 对应的 4 个槽位。
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export const DEFAULT_PLUGIN_VERSION = '0.2.
|
|
8
|
+
export const DEFAULT_PLUGIN_VERSION = '0.2.1';
|
|
9
9
|
|
|
10
10
|
/** 4 个固定 WS 端口,对应 4 个 daemon 槽位 */
|
|
11
11
|
export const ALL_WS_PORTS = [29100, 29110, 29120, 29130] as const;
|
|
@@ -283,7 +283,9 @@ edit-tree --json '{"remId":"kLr...","oldStr":"...","newStr":"..."}'
|
|
|
283
283
|
|
|
284
284
|
## 3. 标准工作流
|
|
285
285
|
|
|
286
|
-
### ⚠️
|
|
286
|
+
### ⚠️ 标准模式(推荐):connect 后需要用户配合
|
|
287
|
+
|
|
288
|
+
**标准模式是推荐的日常使用方式**。用户在自己的浏览器中操作 RemNote,Agent 可以通过 `read-context` 感知用户正在浏览的页面和焦点位置,实现真正的协作。
|
|
287
289
|
|
|
288
290
|
`connect` 成功只意味着 daemon 和 Plugin 服务已启动,**Plugin 并未自动连接**。用户必须在 RemNote 中完成操作,Plugin 才能连接到 daemon:
|
|
289
291
|
|
|
@@ -299,11 +301,18 @@ edit-tree --json '{"remId":"kLr...","oldStr":"...","newStr":"..."}'
|
|
|
299
301
|
|
|
300
302
|
**你必须**:执行 `connect` 后,**立即告知用户需要完成上述操作**,不要直接调用业务命令。引导用户完成后,用 `health` 确认三层就绪再继续。
|
|
301
303
|
|
|
302
|
-
### Headless
|
|
304
|
+
### Headless 模式(特殊场景,不推荐日常使用)
|
|
305
|
+
|
|
306
|
+
通过 setup(一次性)+ headless Chrome 实现自动连接,后续 connect 无需用户介入。
|
|
307
|
+
|
|
308
|
+
**⚠️ 不推荐日常使用**。Headless Chrome 是后台独立实例,**会丢失用户上下文**——`read-context` 返回的是 headless Chrome 的上下文,不是用户浏览器的。Agent 无法感知用户正在浏览和操作的页面,协作体验大打折扣。
|
|
303
309
|
|
|
304
|
-
|
|
310
|
+
**仅在以下场景使用 headless**:
|
|
311
|
+
- 用户明确要求在**服务器/无 GUI 环境**中运行
|
|
312
|
+
- 用户明确表示**不想参与操作**,希望全自动化(CI/CD、定时任务、批量处理等)
|
|
313
|
+
- 用户自己不在 RemNote 前面,不需要与 Agent 协作浏览
|
|
305
314
|
|
|
306
|
-
|
|
315
|
+
**默认始终使用标准模式**,除非用户主动要求 headless。
|
|
307
316
|
|
|
308
317
|
#### 首次使用(setup)
|
|
309
318
|
|
|
@@ -53,13 +53,20 @@ remnote-bridge --json connect --instance work
|
|
|
53
53
|
|
|
54
54
|
## 两种模式
|
|
55
55
|
|
|
56
|
-
###
|
|
56
|
+
### 标准模式(默认,推荐)
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
**标准模式是推荐的日常使用方式**。启动 daemon 后用户在自己的浏览器中加载 Plugin。优势:Agent 可以通过 `read-context` 感知用户正在浏览的页面和焦点位置,实现真正的协作。
|
|
59
59
|
|
|
60
|
-
### Headless 模式(`--headless
|
|
60
|
+
### Headless 模式(`--headless`,特殊场景)
|
|
61
61
|
|
|
62
|
-
自动启动 headless Chrome 加载 Plugin
|
|
62
|
+
自动启动 headless Chrome 加载 Plugin,无需用户操作。
|
|
63
|
+
|
|
64
|
+
**⚠️ 不推荐日常使用**。Headless Chrome 是后台独立实例,**会丢失用户上下文**——`read-context` 返回的是 headless Chrome 的上下文,不是用户浏览器的。
|
|
65
|
+
|
|
66
|
+
**仅在以下场景使用 headless**:
|
|
67
|
+
- 用户明确要求在**服务器/无 GUI 环境**中运行
|
|
68
|
+
- 用户明确表示**不想参与操作**,希望全自动化(CI/CD、定时任务、批量处理等)
|
|
69
|
+
- 用户自己不在 RemNote 前面,不需要与 Agent 协作浏览
|
|
63
70
|
|
|
64
71
|
**前置条件**:必须先执行 `setup` 完成 RemNote 登录。
|
|
65
72
|
|
|
@@ -93,12 +100,17 @@ remnote-bridge --headless disconnect # 结束
|
|
|
93
100
|
|
|
94
101
|
`connect`(不传 `--headless`)成功只意味着 daemon 和 Plugin 服务已启动,**Plugin 并未自动连接**。用户必须在 RemNote 中完成以下操作:
|
|
95
102
|
|
|
103
|
+
> **⚠️ 防幻觉红线**:本插件是**开发者插件**,通过「开发你的插件」功能加载本地 URL。
|
|
104
|
+
> - **禁止**告诉用户"去插件市场/商店搜索安装"——本插件**不在 RemNote 插件市场中**
|
|
105
|
+
> - **禁止**告诉用户"Settings → Plugins"——这个路径不存在
|
|
106
|
+
> - **禁止**编造不存在的安装流程——严格按照下方步骤引导用户
|
|
107
|
+
|
|
96
108
|
### 首次使用(RemNote 从未加载过此插件)
|
|
97
109
|
|
|
98
110
|
1. 打开 RemNote 桌面端或网页端
|
|
99
|
-
2.
|
|
100
|
-
3.
|
|
101
|
-
4. 在输入框中填入 connect 输出的 Plugin
|
|
111
|
+
2. 点击左侧边栏底部的**插件图标**(拼图形状)
|
|
112
|
+
3. 点击「**开发你的插件**」(Develop Your Plugin)
|
|
113
|
+
4. 在输入框中填入 connect 输出的 **Plugin 服务地址**(如 `http://localhost:29101`)
|
|
102
114
|
5. 等待插件加载完成
|
|
103
115
|
|
|
104
116
|
### 非首次使用(之前已加载过此插件)
|
|
@@ -174,64 +174,56 @@ oldStr 必须在缓存大纲中恰好匹配 1 次
|
|
|
174
174
|
|
|
175
175
|
---
|
|
176
176
|
|
|
177
|
-
##
|
|
177
|
+
## 两种写法:模板模式与完整匹配模式
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
已有行(带 `<!--remId-->` 注释的行)在 oldStr/newStr 中支持两种写法:
|
|
180
180
|
|
|
181
|
-
###
|
|
181
|
+
### 模板模式(优先使用)
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
### 展开规则
|
|
186
|
-
|
|
187
|
-
| 输入 | 展开为 |
|
|
188
|
-
|------|--------|
|
|
189
|
-
| `{{remId}}` | 该 remId 对应行的去缩进完整内容(含 `<!--remId 元数据-->`) |
|
|
190
|
-
| ` {{remId}}` | AI 写的缩进 + 展开后的完整内容 |
|
|
191
|
-
| 不含 `{{}}` 的文本 | 原样不变 |
|
|
192
|
-
|
|
193
|
-
### 示例
|
|
194
|
-
|
|
195
|
-
**重排(对比传统写法)**
|
|
183
|
+
用 `{{remId}}` 引用已有行,系统在 str_replace 前自动展开为完整行内容(不含缩进)。节省 token、减少复制错误。
|
|
196
184
|
|
|
197
185
|
```
|
|
198
|
-
#
|
|
199
|
-
oldStr: " 动态数组 <!--id1_1 type:concept-->\n 静态数组 <!--id1_2 type:concept-->"
|
|
200
|
-
newStr: " 静态数组 <!--id1_2 type:concept-->\n 动态数组 <!--id1_1 type:concept-->"
|
|
201
|
-
|
|
202
|
-
# 模板写法(~50 tokens)
|
|
186
|
+
# 重排
|
|
203
187
|
oldStr: " {{id1_1}}\n {{id1_2}}"
|
|
204
188
|
newStr: " {{id1_2}}\n {{id1_1}}"
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
**移动(改变缩进)**
|
|
208
189
|
|
|
209
|
-
|
|
190
|
+
# 移动(改变缩进 = 改变父节点)
|
|
210
191
|
oldStr: " {{idA}}\n {{idT}}\n {{idB}}"
|
|
211
192
|
newStr: " {{idA}}\n {{idB}}\n {{idT}}"
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**删除(模板用于上下文定位)**
|
|
215
193
|
|
|
216
|
-
|
|
194
|
+
# 删除(必须同时删子行)
|
|
217
195
|
oldStr: " {{idA}}\n {{idA1}}\n {{idB}}"
|
|
218
196
|
newStr: " {{idB}}"
|
|
219
|
-
```
|
|
220
197
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
```
|
|
198
|
+
# 新增(新增行手动写,已有行用模板)
|
|
224
199
|
oldStr: " {{idZ}}"
|
|
225
200
|
newStr: " 新增行\n {{idZ}}"
|
|
226
201
|
```
|
|
227
202
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
- 只匹配纯字母数字(`[a-zA-Z0-9]+`),与 RemNote cloze 语法 `{{text}}`
|
|
203
|
+
**模板规则**:
|
|
204
|
+
- `{{remId}}` 展开为**不含缩进**的完整行内容,缩进由你控制
|
|
205
|
+
- 只匹配纯字母数字(`[a-zA-Z0-9]+`),与 RemNote cloze 语法 `{{text}}` 不冲突
|
|
231
206
|
- 匹配到但不在缓存大纲中的 `{{xxx}}` 原样保留(可能是 cloze),并输出 templateWarnings
|
|
232
|
-
- `{{remId}}` 不含缩进,缩进由 AI 控制(move 操作会改变缩进)
|
|
233
207
|
- 新增行没有 remId,不能用模板表示
|
|
234
208
|
|
|
209
|
+
### 完整匹配模式(回退)
|
|
210
|
+
|
|
211
|
+
直接从大纲复制已有行的完整内容(含 `<!--remId 元数据-->`)。
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
# 重排
|
|
215
|
+
oldStr: " 动态数组 <!--id1_1 type:concept-->\n 静态数组 <!--id1_2 type:concept-->"
|
|
216
|
+
newStr: " 静态数组 <!--id1_2 type:concept-->\n 动态数组 <!--id1_1 type:concept-->"
|
|
217
|
+
|
|
218
|
+
# 移动
|
|
219
|
+
oldStr: " 子节点 A <!--idA-->\n 目标行 <!--idT-->\n 子节点 B <!--idB-->"
|
|
220
|
+
newStr: " 子节点 A <!--idA-->\n 子节点 B <!--idB-->\n 目标行 <!--idT-->"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### ⚠️ 回退策略
|
|
224
|
+
|
|
225
|
+
**优先使用模板模式**。但如果模板模式连续 2+ 次因 ID 错误导致 `old_str not found`,说明当前上下文不足以准确引用 ID——**立即切换到完整匹配模式**(重新 read_tree,从最新大纲复制完整行内容),不要反复重试模板。
|
|
226
|
+
|
|
235
227
|
---
|
|
236
228
|
|
|
237
229
|
## 支持的操作
|
|
@@ -241,12 +233,13 @@ newStr: " 新增行\n {{idZ}}"
|
|
|
241
233
|
在 newStr 中添加**无 remId 注释**的新行。新行可以使用 Markdown 前缀和箭头分隔符来设置属性。
|
|
242
234
|
|
|
243
235
|
```
|
|
244
|
-
|
|
245
|
-
|
|
236
|
+
# 模板模式
|
|
237
|
+
oldStr: " {{idA}}"
|
|
238
|
+
newStr: " 新增节点\n {{idA}}"
|
|
246
239
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
子节点 A <!--idA-->
|
|
240
|
+
# 完整匹配模式
|
|
241
|
+
oldStr: " 子节点 A <!--idA-->"
|
|
242
|
+
newStr: " 新增节点\n 子节点 A <!--idA-->"
|
|
250
243
|
```
|
|
251
244
|
|
|
252
245
|
#### 新增行的 Markdown 前缀
|
|
@@ -326,12 +319,13 @@ newStr:
|
|
|
326
319
|
示例:
|
|
327
320
|
|
|
328
321
|
```
|
|
329
|
-
|
|
330
|
-
|
|
322
|
+
# 模板模式
|
|
323
|
+
oldStr: " {{idA}}"
|
|
324
|
+
newStr: " <!--portal refs:refId1,refId2-->\n {{idA}}"
|
|
331
325
|
|
|
332
|
-
|
|
333
|
-
<!--
|
|
334
|
-
子节点 A <!--idA-->
|
|
326
|
+
# 完整匹配模式
|
|
327
|
+
oldStr: " 子节点 A <!--idA-->"
|
|
328
|
+
newStr: " <!--portal refs:refId1,refId2-->\n 子节点 A <!--idA-->"
|
|
335
329
|
```
|
|
336
330
|
|
|
337
331
|
#### 嵌套新增
|
|
@@ -339,11 +333,13 @@ newStr:
|
|
|
339
333
|
新增行下面可以再嵌套新增行,通过缩进表示父子关系:
|
|
340
334
|
|
|
341
335
|
```
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
答案行 1
|
|
345
|
-
|
|
346
|
-
|
|
336
|
+
# 模板模式
|
|
337
|
+
oldStr: " {{idA}}"
|
|
338
|
+
newStr: " 父节点 ↓\n 答案行 1\n 答案行 2\n {{idA}}"
|
|
339
|
+
|
|
340
|
+
# 完整匹配模式
|
|
341
|
+
oldStr: " 子节点 A <!--idA-->"
|
|
342
|
+
newStr: " 父节点 ↓\n 答案行 1\n 答案行 2\n 子节点 A <!--idA-->"
|
|
347
343
|
```
|
|
348
344
|
|
|
349
345
|
嵌套新增行的父 ID 通过内部占位标记 `__new_N__` 管理,创建顺序保证从浅到深。
|
|
@@ -353,14 +349,13 @@ newStr:
|
|
|
353
349
|
新行**不能**插在一个有子节点的 Rem 和它的 children 之间,否则 children 会被新行"劫持",触发 `children_captured` 错误。
|
|
354
350
|
|
|
355
351
|
```
|
|
356
|
-
❌
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
352
|
+
❌ 错误(模板):
|
|
353
|
+
oldStr: " {{idA}}" newStr: " {{idA}}\n 新行" ← idA 有子节点,新行劫持 children!
|
|
354
|
+
❌ 错误(完整匹配):
|
|
355
|
+
oldStr: " 水分子 ↓ <!--idA-->" newStr: " 水分子 ↓ <!--idA-->\n 新行" ← 同理
|
|
360
356
|
|
|
361
|
-
✅
|
|
362
|
-
|
|
363
|
-
新行 ← 不影响任何已有节点
|
|
357
|
+
✅ 正确:插在末尾
|
|
358
|
+
oldStr: " {{idZ}}" newStr: " {{idZ}}\n 新行"
|
|
364
359
|
```
|
|
365
360
|
|
|
366
361
|
#### 两步操作:创建新节点并移入已有 children
|
|
@@ -377,13 +372,13 @@ newStr:
|
|
|
377
372
|
从 newStr 中移除带 remId 的行。**必须同时删除该行的所有可见子行**,否则报 orphan_detected 错误。
|
|
378
373
|
|
|
379
374
|
```
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
子节点 B <!--idB-->
|
|
375
|
+
# 模板模式
|
|
376
|
+
oldStr: " {{idA}}\n {{idA1}}\n {{idB}}"
|
|
377
|
+
newStr: " {{idB}}"
|
|
384
378
|
|
|
385
|
-
|
|
386
|
-
子节点 B <!--idB-->
|
|
379
|
+
# 完整匹配模式
|
|
380
|
+
oldStr: " 子节点 A <!--idA-->\n 孙节点 A1 <!--idA1-->\n 子节点 B <!--idB-->"
|
|
381
|
+
newStr: " 子节点 B <!--idB-->"
|
|
387
382
|
```
|
|
388
383
|
|
|
389
384
|
删除操作按深度**从深到浅**执行(先删子后删父),确保 RemNote SDK 不会拒绝操作。
|
|
@@ -393,15 +388,13 @@ newStr:
|
|
|
393
388
|
改变行的缩进级别或位置,使其移动到新的父节点下:
|
|
394
389
|
|
|
395
390
|
```
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
子节点 B <!--idB-->
|
|
391
|
+
# 模板模式
|
|
392
|
+
oldStr: " {{idA}}\n {{idT}}\n {{idB}}"
|
|
393
|
+
newStr: " {{idA}}\n {{idB}}\n {{idT}}"
|
|
400
394
|
|
|
401
|
-
|
|
402
|
-
子节点 A <!--idA-->
|
|
403
|
-
子节点 B <!--idB-->
|
|
404
|
-
目标行 <!--idT-->
|
|
395
|
+
# 完整匹配模式
|
|
396
|
+
oldStr: " 子节点 A <!--idA-->\n 目标行 <!--idT-->\n 子节点 B <!--idB-->"
|
|
397
|
+
newStr: " 子节点 A <!--idA-->\n 子节点 B <!--idB-->\n 目标行 <!--idT-->"
|
|
405
398
|
```
|
|
406
399
|
|
|
407
400
|
### 重排行
|
|
@@ -409,15 +402,13 @@ newStr:
|
|
|
409
402
|
调换同级行的顺序:
|
|
410
403
|
|
|
411
404
|
```
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
子节点 C <!--idC-->
|
|
405
|
+
# 模板模式
|
|
406
|
+
oldStr: " {{idA}}\n {{idB}}\n {{idC}}"
|
|
407
|
+
newStr: " {{idC}}\n {{idA}}\n {{idB}}"
|
|
416
408
|
|
|
417
|
-
|
|
418
|
-
子节点 C <!--idC-->
|
|
419
|
-
子节点 A <!--idA-->
|
|
420
|
-
子节点 B <!--idB-->
|
|
409
|
+
# 完整匹配模式
|
|
410
|
+
oldStr: " 子节点 A <!--idA-->\n 子节点 B <!--idB-->\n 子节点 C <!--idC-->"
|
|
411
|
+
newStr: " 子节点 C <!--idC-->\n 子节点 A <!--idA-->\n 子节点 B <!--idB-->"
|
|
421
412
|
```
|
|
422
413
|
|
|
423
414
|
---
|
|
@@ -543,36 +534,42 @@ RemNote SDK 存在已知 bug:
|
|
|
543
534
|
|
|
544
535
|
---
|
|
545
536
|
|
|
546
|
-
##
|
|
537
|
+
## 常见使用模式(JSON 模式)
|
|
538
|
+
|
|
539
|
+
> 优先使用模板模式;连续 2+ 次 `old_str not found` 则回退到完整匹配模式。
|
|
547
540
|
|
|
548
541
|
### 在指定位置插入新行
|
|
549
542
|
|
|
550
543
|
```bash
|
|
551
|
-
|
|
544
|
+
# 模板模式
|
|
545
|
+
remnote-bridge edit-tree --json '{"remId":"kLr","oldStr":" {{idA}}","newStr":" 新增行\n {{idA}}"}'
|
|
546
|
+
|
|
547
|
+
# 完整匹配模式
|
|
548
|
+
remnote-bridge edit-tree --json '{"remId":"kLr","oldStr":" 子节点 A <!--idA-->","newStr":" 新增行\n 子节点 A <!--idA-->"}'
|
|
552
549
|
```
|
|
553
550
|
|
|
554
551
|
### 删除一个叶子节点
|
|
555
552
|
|
|
556
553
|
```bash
|
|
557
|
-
remnote-bridge edit-tree
|
|
554
|
+
remnote-bridge edit-tree --json '{"remId":"kLr","oldStr":" {{leaf}}\n","newStr":""}'
|
|
558
555
|
```
|
|
559
556
|
|
|
560
557
|
### 调换两个兄弟的顺序
|
|
561
558
|
|
|
562
559
|
```bash
|
|
563
|
-
#
|
|
564
|
-
remnote-bridge edit-tree kLr --old-str ' 节点 A <!--idA-->\n 节点 B <!--idB-->' --new-str ' 节点 B <!--idB-->\n 节点 A <!--idA-->'
|
|
565
|
-
|
|
566
|
-
# 模板写法(JSON 模式)
|
|
560
|
+
# 模板模式
|
|
567
561
|
remnote-bridge edit-tree --json '{"remId":"kLr","oldStr":" {{idA}}\n {{idB}}","newStr":" {{idB}}\n {{idA}}"}'
|
|
562
|
+
|
|
563
|
+
# 完整匹配模式
|
|
564
|
+
remnote-bridge edit-tree --json '{"remId":"kLr","oldStr":" 节点 A <!--idA-->\n 节点 B <!--idB-->","newStr":" 节点 B <!--idB-->\n 节点 A <!--idA-->"}'
|
|
568
565
|
```
|
|
569
566
|
|
|
570
567
|
### 将节点移到另一个父节点下
|
|
571
568
|
|
|
572
569
|
```bash
|
|
573
|
-
#
|
|
574
|
-
remnote-bridge edit-tree kLr --old-str ' 旧父 <!--oldP-->\n 目标 <!--target-->\n 新父 <!--newP-->' --new-str ' 旧父 <!--oldP-->\n 新父 <!--newP-->\n 目标 <!--target-->'
|
|
575
|
-
|
|
576
|
-
# 模板写法(JSON 模式)
|
|
570
|
+
# 模板模式
|
|
577
571
|
remnote-bridge edit-tree --json '{"remId":"kLr","oldStr":" {{oldP}}\n {{target}}\n {{newP}}","newStr":" {{oldP}}\n {{newP}}\n {{target}}"}'
|
|
572
|
+
|
|
573
|
+
# 完整匹配模式
|
|
574
|
+
remnote-bridge edit-tree --json '{"remId":"kLr","oldStr":" 旧父 <!--oldP-->\n 目标 <!--target-->\n 新父 <!--newP-->","newStr":" 旧父 <!--oldP-->\n 新父 <!--newP-->\n 目标 <!--target-->"}'
|
|
578
575
|
```
|
|
@@ -190,6 +190,8 @@ RemNote SDK → 知识库
|
|
|
190
190
|
|
|
191
191
|
一次**会话(Session)= 守护进程的生命周期**。
|
|
192
192
|
|
|
193
|
+
**标准模式(推荐)**——用户在自己的浏览器中操作 RemNote,Agent 可感知用户上下文:
|
|
194
|
+
|
|
193
195
|
```
|
|
194
196
|
connect → daemon 启动
|
|
195
197
|
↓
|
|
@@ -201,6 +203,10 @@ disconnect → daemon 关闭 → 会话结束,缓存清空
|
|
|
201
203
|
```
|
|
202
204
|
|
|
203
205
|
> **重要**:`connect` 成功只意味着 daemon 已启动,Plugin 并未自动连接。首次使用需用户在 RemNote「开发你的插件」中填入对应的 Plugin 服务地址;非首次只需刷新 RemNote 页面。必须引导用户完成此步后再用 `health` 确认就绪。
|
|
206
|
+
>
|
|
207
|
+
> **⚠️ 防幻觉红线**:本插件是**开发者插件**,通过「开发你的插件」加载本地 URL。**禁止**告诉用户去插件市场/商店搜索安装(插件不在市场中);**禁止**编造"Settings → Plugins"等不存在的路径。
|
|
208
|
+
|
|
209
|
+
**Headless 模式(不推荐日常使用)**——通过后台 Chrome 自动连接,但**会丢失用户上下文**(`read-context` 返回 headless 实例的上下文,不是用户浏览器的)。仅在以下场景使用:用户明确要求在服务器/无 GUI 环境运行、用户明确不想参与操作(全自动化)、用户不在 RemNote 前面。详见 `connect.md`。
|
|
204
210
|
|
|
205
211
|
`connect` 启动三个服务,端口由槽位自动分配:
|
|
206
212
|
|