remnote-bridge 0.1.9 → 0.1.10
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/dist/cli/commands/connect.js +3 -1
- package/dist/cli/commands/disconnect.js +5 -0
- package/dist/cli/daemon/headless-browser.js +88 -0
- package/dist/cli/daemon/static-server.js +4 -1
- package/package.json +1 -1
- package/remnote-plugin/dist/index-sandbox.js +26 -26
- package/remnote-plugin/dist/index.js +26 -26
- package/remnote-plugin/src/services/breadcrumb.ts +2 -1
- package/remnote-plugin/src/services/read-context.ts +2 -2
- package/remnote-plugin/src/services/read-globe.ts +2 -1
- package/remnote-plugin/src/services/read-tree.ts +2 -2
- package/remnote-plugin/src/services/rem-builder.ts +53 -3
- package/remnote-plugin/src/services/search.ts +2 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ReactRNPlugin, PluginRem as Rem } from '@remnote/plugin-sdk';
|
|
8
|
+
import { safeToMarkdown } from './rem-builder';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* 从 rem 向上追溯到根,返回路径名称数组(从根到当前)。
|
|
@@ -17,7 +18,7 @@ export async function buildBreadcrumb(
|
|
|
17
18
|
let current: Rem | undefined = rem;
|
|
18
19
|
|
|
19
20
|
while (current) {
|
|
20
|
-
const text = await plugin
|
|
21
|
+
const text = await safeToMarkdown(plugin, current.text ?? []);
|
|
21
22
|
path.unshift(text.replace(/\n/g, ' ').trim() || current._id);
|
|
22
23
|
current = await current.getParentRem();
|
|
23
24
|
}
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import { filterNoisyChildren } from './powerup-filter';
|
|
20
20
|
import { sliceSiblings } from '../utils/elision';
|
|
21
21
|
import { buildBreadcrumb } from './breadcrumb';
|
|
22
|
-
import { buildFullSerializableRem as buildFullRem } from './rem-builder';
|
|
22
|
+
import { buildFullSerializableRem as buildFullRem, safeToMarkdown } from './rem-builder';
|
|
23
23
|
|
|
24
24
|
export interface ReadContextPayload {
|
|
25
25
|
mode?: 'focus' | 'page';
|
|
@@ -350,7 +350,7 @@ async function buildMinimalSerializableRem(
|
|
|
350
350
|
) {
|
|
351
351
|
const isPortal = rem.type === 6;
|
|
352
352
|
const [markdownText, isDocument, portalIncludedRems] = await Promise.all([
|
|
353
|
-
plugin
|
|
353
|
+
safeToMarkdown(plugin, rem.text ?? []),
|
|
354
354
|
rem.isDocument(),
|
|
355
355
|
isPortal ? rem.getPortalDirectlyIncludedRem() : Promise.resolve([]),
|
|
356
356
|
]);
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from '../utils/tree-serializer';
|
|
22
22
|
import { sliceSiblings } from '../utils/elision';
|
|
23
23
|
import { filterNoisyChildren } from './powerup-filter';
|
|
24
|
+
import { safeToMarkdown } from './rem-builder';
|
|
24
25
|
|
|
25
26
|
export interface ReadGlobePayload {
|
|
26
27
|
depth?: number;
|
|
@@ -75,7 +76,7 @@ export async function readGlobe(
|
|
|
75
76
|
|
|
76
77
|
const isPortal = rem.type === 6;
|
|
77
78
|
const [markdownText, portalIncludedRems] = await Promise.all([
|
|
78
|
-
plugin
|
|
79
|
+
safeToMarkdown(plugin, rem.text ?? []),
|
|
79
80
|
isPortal ? rem.getPortalDirectlyIncludedRem() : Promise.resolve([]),
|
|
80
81
|
]);
|
|
81
82
|
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from '../utils/tree-serializer';
|
|
19
19
|
import { filterNoisyChildren } from './powerup-filter';
|
|
20
20
|
import { sliceSiblings } from '../utils/elision';
|
|
21
|
-
import { buildFullSerializableRem, sanitizeNewlines } from './rem-builder';
|
|
21
|
+
import { buildFullSerializableRem, sanitizeNewlines, safeToMarkdown } from './rem-builder';
|
|
22
22
|
|
|
23
23
|
export interface ReadTreePayload {
|
|
24
24
|
remId: string;
|
|
@@ -190,7 +190,7 @@ export async function readTree(
|
|
|
190
190
|
const parent = await current.getParentRem();
|
|
191
191
|
if (!parent) break;
|
|
192
192
|
const [name, children, isDoc] = await Promise.all([
|
|
193
|
-
plugin
|
|
193
|
+
safeToMarkdown(plugin, parent.text ?? []),
|
|
194
194
|
parent.getChildrenRem(),
|
|
195
195
|
parent.isDocument(),
|
|
196
196
|
]);
|
|
@@ -11,6 +11,56 @@ import type { ReactRNPlugin, PluginRem as Rem } from '@remnote/plugin-sdk';
|
|
|
11
11
|
import type { SerializableRem } from '../utils/tree-serializer';
|
|
12
12
|
import { filterNoisyTags } from './powerup-filter';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* SDK richText.toMarkdown 的安全包装。
|
|
16
|
+
* SDK 不认识某些 RichText 类型(如 "i":"u" URL 链接),会抛 Invalid input。
|
|
17
|
+
* 失败时回退到本地解析。
|
|
18
|
+
*/
|
|
19
|
+
export async function safeToMarkdown(
|
|
20
|
+
plugin: ReactRNPlugin,
|
|
21
|
+
richText: unknown[],
|
|
22
|
+
): Promise<string> {
|
|
23
|
+
try {
|
|
24
|
+
return await plugin.richText.toMarkdown(richText);
|
|
25
|
+
} catch {
|
|
26
|
+
return richTextFallback(richText);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 本地 RichText → 纯文本回退,处理 SDK 不支持的类型。
|
|
32
|
+
*
|
|
33
|
+
* 覆盖 RICH_TEXT_ELEMENT_TYPE 枚举全部 12 种类型 + 遗留 "u" 类型:
|
|
34
|
+
* m=TEXT, q=REM, i=IMAGE, a=AUDIO, x=LATEX, p=PLUGIN,
|
|
35
|
+
* g=GLOBAL_NAME, s=CARD_DELIMITER, n=ANNOTATION,
|
|
36
|
+
* fi=FLASHCARD_ICON, ai=ADD_ICON, o=DEPRECATED_CODE, u=URL(遗留)
|
|
37
|
+
*/
|
|
38
|
+
function richTextFallback(richText: unknown[]): string {
|
|
39
|
+
return richText.map(item => {
|
|
40
|
+
if (typeof item === 'string') return item;
|
|
41
|
+
if (typeof item !== 'object' || item === null) return '';
|
|
42
|
+
const obj = item as Record<string, unknown>;
|
|
43
|
+
switch (obj.i) {
|
|
44
|
+
case 'm': return String(obj.text ?? '');
|
|
45
|
+
case 'q': return `[[${String(obj._id ?? '')}]]`;
|
|
46
|
+
case 'u': return obj.title
|
|
47
|
+
? `[${String(obj.title)}](${String(obj.url)})`
|
|
48
|
+
: String(obj.url ?? '');
|
|
49
|
+
case 'x': return `$${String(obj.text ?? '')}$`;
|
|
50
|
+
case 'i': return `})`;
|
|
51
|
+
case 'a': return `[audio](${String(obj.url ?? '')})`;
|
|
52
|
+
case 'p': return String(obj.text ?? ''); // PLUGIN
|
|
53
|
+
case 'g': return String(obj.text ?? ''); // GLOBAL_NAME
|
|
54
|
+
case 'n': return String(obj.text ?? ''); // ANNOTATION
|
|
55
|
+
case 'o': return String(obj.text ?? ''); // DEPRECATED_CODE
|
|
56
|
+
case 's': // CARD_DELIMITER
|
|
57
|
+
case 'fi': // FLASHCARD_ICON
|
|
58
|
+
case 'ai': return ''; // ADD_ICON(纯视觉标记)
|
|
59
|
+
default: return String(obj.text ?? obj.url ?? '');
|
|
60
|
+
}
|
|
61
|
+
}).join('');
|
|
62
|
+
}
|
|
63
|
+
|
|
14
64
|
export interface BuildFullRemOptions {
|
|
15
65
|
/** 是否保留 Powerup 系统 Tag(默认 false = 过滤掉) */
|
|
16
66
|
includePowerup?: boolean;
|
|
@@ -46,8 +96,8 @@ export async function buildFullSerializableRem(
|
|
|
46
96
|
hasDvPowerup,
|
|
47
97
|
portalIncludedRems,
|
|
48
98
|
] = await Promise.all([
|
|
49
|
-
plugin
|
|
50
|
-
rem.backText ? plugin
|
|
99
|
+
safeToMarkdown(plugin, rem.text ?? []),
|
|
100
|
+
rem.backText ? safeToMarkdown(plugin, rem.backText) : Promise.resolve(null),
|
|
51
101
|
rem.getType(),
|
|
52
102
|
rem.isCardItem(),
|
|
53
103
|
rem.isDocument(),
|
|
@@ -80,7 +130,7 @@ export async function buildFullSerializableRem(
|
|
|
80
130
|
const tags = await Promise.all(
|
|
81
131
|
tagRems.map(async (t) => ({
|
|
82
132
|
id: t._id,
|
|
83
|
-
name: sanitizeNewlines(await plugin
|
|
133
|
+
name: sanitizeNewlines(await safeToMarkdown(plugin, t.text ?? [])),
|
|
84
134
|
})),
|
|
85
135
|
);
|
|
86
136
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { ReactRNPlugin } from '@remnote/plugin-sdk';
|
|
10
|
+
import { safeToMarkdown } from './rem-builder';
|
|
10
11
|
|
|
11
12
|
export interface SearchPayload {
|
|
12
13
|
query: string;
|
|
@@ -39,7 +40,7 @@ export async function search(
|
|
|
39
40
|
|
|
40
41
|
const results: SearchResultItem[] = [];
|
|
41
42
|
for (const rem of rems) {
|
|
42
|
-
const markdownText = await plugin
|
|
43
|
+
const markdownText = await safeToMarkdown(plugin, rem.text ?? []);
|
|
43
44
|
const isDocument = await rem.isDocument();
|
|
44
45
|
results.push({
|
|
45
46
|
remId: rem._id,
|