oceanpress 1.0.3 → 1.0.9
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/CLAUDE.md +3 -0
- package/README.md +81 -1
- package/dist/assets/index-SyBTrsYP.js +283 -0
- package/dist/assets/steps-Cpyl02WI.js +2030 -0
- package/dist/index.html +1 -1
- package/dist-app/app.es.js +29 -30
- package/dist-app/app.umd.js +5 -6
- package/dist-cli/cli.js +2395 -1219
- package/dist-cli/cli.js.map +1 -1
- package/package.json +37 -36
- package/dist/assets/index-CgSYWV1W.js +0 -271
- package/dist/assets/steps-MZA5h6R-.js +0 -1735
package/dist-cli/cli.js
CHANGED
|
@@ -4,101 +4,175 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
4
4
|
|
|
5
5
|
// src/cli/deploy.ts
|
|
6
6
|
import { readFile } from "fs/promises";
|
|
7
|
-
import { createRPC as createRPC2 } from "oceanpress-rpc";
|
|
8
|
-
import { stringify as stringify2 } from "superjson";
|
|
9
|
-
|
|
10
|
-
// src/core/dependency.ts
|
|
11
|
-
var nullDep = () => {
|
|
12
|
-
throw new Error("\u4E0D\u53EF\u8C03\u7528\u672A\u586B\u5145\u7684\u4F9D\u8D56");
|
|
13
|
-
};
|
|
14
|
-
var storeDep = {
|
|
15
|
-
// 读写配置文件所依赖的副作用
|
|
16
|
-
setItem: nullDep,
|
|
17
|
-
getItem: nullDep,
|
|
18
|
-
// render功能依赖的副作用
|
|
19
|
-
getNodeByID: nullDep,
|
|
20
|
-
getDocPathBySY: nullDep,
|
|
21
|
-
getDocByChildID: nullDep,
|
|
22
|
-
getHPathByID_Node: nullDep
|
|
23
|
-
};
|
|
24
7
|
|
|
25
|
-
//
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
deepAssign(target[key], source[key], config);
|
|
8
|
+
// ../rpc/dist/index.js
|
|
9
|
+
async function createRPC(...[type, options]) {
|
|
10
|
+
const apiModule = type === "apiProvider" ? await options.genApiModule() : void 0;
|
|
11
|
+
const remoteCall = type === "apiConsumer" ? options.remoteCall : void 0;
|
|
12
|
+
async function RC(method, data) {
|
|
13
|
+
async function executeMiddleware(index) {
|
|
14
|
+
if (options.middleware && index < options.middleware.length) {
|
|
15
|
+
return options.middleware[index](method, data, () => executeMiddleware(index + 1));
|
|
34
16
|
} else {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
17
|
+
return executeCall();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function executeCall() {
|
|
21
|
+
try {
|
|
22
|
+
if (type === "apiProvider") {
|
|
23
|
+
const methodParts = method.split(".");
|
|
24
|
+
let currentObj = apiModule;
|
|
25
|
+
for (const part of methodParts) {
|
|
26
|
+
if (currentObj && typeof currentObj === "object" && part in currentObj) {
|
|
27
|
+
currentObj = currentObj[part];
|
|
28
|
+
} else {
|
|
29
|
+
throw new Error(`Method ${method} not found`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (typeof currentObj === "function") {
|
|
33
|
+
return await currentObj(...data);
|
|
34
|
+
} else {
|
|
35
|
+
throw new Error(`${method} is not a function`);
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
return await remoteCall(method, data);
|
|
39
39
|
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error("API call failed:", error);
|
|
42
|
+
throw error;
|
|
40
43
|
}
|
|
41
44
|
}
|
|
45
|
+
return await executeMiddleware(0);
|
|
42
46
|
}
|
|
43
|
-
|
|
47
|
+
function createNestedProxy(path = []) {
|
|
48
|
+
return {
|
|
49
|
+
get(target, prop) {
|
|
50
|
+
if (prop === "then") {
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
const newPath = [...path, prop];
|
|
54
|
+
return new Proxy(function(...args) {
|
|
55
|
+
const method = newPath.join(".");
|
|
56
|
+
return RC(method, args);
|
|
57
|
+
}, createNestedProxy(newPath));
|
|
58
|
+
},
|
|
59
|
+
apply(target, thisArg, args) {
|
|
60
|
+
const method = path.join(".");
|
|
61
|
+
return RC(method, args);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const API2 = new Proxy(function() {
|
|
66
|
+
}, createNestedProxy());
|
|
67
|
+
return { API: API2, RC };
|
|
44
68
|
}
|
|
45
69
|
|
|
70
|
+
// src/cli/deploy.ts
|
|
71
|
+
import { stringify as stringify2 } from "superjson";
|
|
72
|
+
|
|
46
73
|
// src/core/config.ts
|
|
74
|
+
import { Effect } from "effect";
|
|
47
75
|
import { computed, reactive, watch } from "vue";
|
|
48
76
|
|
|
49
77
|
// package.json
|
|
50
78
|
var package_default = {
|
|
51
79
|
name: "oceanpress",
|
|
52
|
-
|
|
53
|
-
version: "1.0.2",
|
|
80
|
+
version: "1.0.9",
|
|
54
81
|
type: "module",
|
|
55
82
|
scripts: {
|
|
56
83
|
dev: "vite",
|
|
57
84
|
cli: "tsx ./src/cli.ts",
|
|
58
|
-
|
|
85
|
+
"cli-w": "tsx watch ./src/cli.ts",
|
|
59
86
|
build: "vite build && npm run build_lib",
|
|
60
87
|
build_lib: "vite build --config vite.sw.config.ts",
|
|
61
|
-
|
|
88
|
+
build_app: "vite build --mode library",
|
|
89
|
+
build_cli: "tsup",
|
|
62
90
|
build_plugin_ui: "vite build --config vite.plugin.config.ts",
|
|
63
91
|
dev_plugin_ui: "vite build --watch --config vite.plugin.config.ts",
|
|
64
|
-
generate_dependency_graph: "depcruise src --include-only '^src' --output-type dot > ./assets/dep.dot",
|
|
65
92
|
preview: "vite preview"
|
|
66
93
|
},
|
|
67
|
-
bin:
|
|
94
|
+
bin: {
|
|
95
|
+
oceanpress: "./dist-cli/cli.js"
|
|
96
|
+
},
|
|
97
|
+
files: [
|
|
98
|
+
"dist/assets",
|
|
99
|
+
"dist/dev",
|
|
100
|
+
"dist/index.html",
|
|
101
|
+
"dist/ocean_press-log.png",
|
|
102
|
+
"dist-app/assets",
|
|
103
|
+
"dist-app/dev",
|
|
104
|
+
"dist-app/app.*",
|
|
105
|
+
"dist-cli",
|
|
106
|
+
"*.md"
|
|
107
|
+
],
|
|
68
108
|
dependencies: {
|
|
69
|
-
"@aws-sdk/client-s3": "^3.
|
|
70
|
-
"@hono/node-server": "^1.
|
|
71
|
-
cheerio: "1.
|
|
72
|
-
commander: "^
|
|
109
|
+
"@aws-sdk/client-s3": "^3.864.0",
|
|
110
|
+
"@hono/node-server": "^1.18.1",
|
|
111
|
+
cheerio: "1.1.2",
|
|
112
|
+
commander: "^14.0.0",
|
|
113
|
+
effect: "^3.17.7",
|
|
73
114
|
fzstd: "^0.1.1",
|
|
74
|
-
hono: "^4.
|
|
115
|
+
hono: "^4.9.0",
|
|
75
116
|
jszip: "^3.10.1",
|
|
76
|
-
meilisearch: "^0.
|
|
77
|
-
"naive-ui": "^2.
|
|
78
|
-
|
|
79
|
-
"oceanpress-server": "workspace:*",
|
|
80
|
-
octokit: "^4.1.2",
|
|
117
|
+
meilisearch: "^0.51.0",
|
|
118
|
+
"naive-ui": "^2.42.0",
|
|
119
|
+
octokit: "^5.0.3",
|
|
81
120
|
superjson: "^2.2.2",
|
|
82
|
-
tsx: "^4.
|
|
83
|
-
vditor: "^3.
|
|
84
|
-
vue: "^3.5.
|
|
85
|
-
"vue-router": "^4.5.
|
|
121
|
+
tsx: "^4.20.3",
|
|
122
|
+
vditor: "^3.11.1",
|
|
123
|
+
vue: "^3.5.18",
|
|
124
|
+
"vue-router": "^4.5.1",
|
|
86
125
|
"zstd-codec": "^0.1.5"
|
|
87
126
|
},
|
|
88
127
|
devDependencies: {
|
|
89
|
-
"@vitejs/plugin-vue": "^
|
|
90
|
-
"@vitejs/plugin-vue-jsx": "^
|
|
91
|
-
"dependency-cruiser": "^
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
128
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
129
|
+
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
|
130
|
+
"dependency-cruiser": "^17.0.1",
|
|
131
|
+
"oceanpress-rpc": "workspace:*",
|
|
132
|
+
"oceanpress-server": "workspace:*",
|
|
133
|
+
tsup: "^8.5.0",
|
|
134
|
+
typescript: "^5.9.2",
|
|
135
|
+
vite: "^7.1.2",
|
|
136
|
+
"vite-plugin-vue-devtools": "^8.0.0",
|
|
137
|
+
"vue-tsc": "^3.0.5"
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/util/deep_assign.ts
|
|
142
|
+
function deepAssign(target, source, config = { add: true, update: true }) {
|
|
143
|
+
for (let key in source) {
|
|
144
|
+
if (source.hasOwnProperty(key)) {
|
|
145
|
+
if (source[key] instanceof Object && !Array.isArray(source[key])) {
|
|
146
|
+
if (!target.hasOwnProperty(key)) {
|
|
147
|
+
target[key] = {};
|
|
148
|
+
}
|
|
149
|
+
deepAssign(target[key], source[key], config);
|
|
150
|
+
} else {
|
|
151
|
+
if (!target.hasOwnProperty(key) && config.add) {
|
|
152
|
+
target[key] = source[key];
|
|
153
|
+
} else if (config.update) {
|
|
154
|
+
target[key] = source[key];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
97
158
|
}
|
|
159
|
+
return target;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/core/EffectDep.ts
|
|
163
|
+
import { Context } from "effect";
|
|
164
|
+
var EffectRender = class extends Context.Tag("EffectDep")() {
|
|
165
|
+
};
|
|
166
|
+
var EffectLocalStorageDep = class extends Context.Tag("EffectLocalStorageDep")() {
|
|
167
|
+
};
|
|
168
|
+
var EffectLogDep = class extends Context.Tag("EffectLogDep")() {
|
|
169
|
+
};
|
|
170
|
+
var EffectConfigDep = class extends Context.Tag("EffectConfigDep")() {
|
|
98
171
|
};
|
|
99
172
|
|
|
100
173
|
// src/core/config.ts
|
|
101
174
|
var version = package_default.version;
|
|
175
|
+
console.log("[version]", version);
|
|
102
176
|
var defaultConfig = {
|
|
103
177
|
name: "default",
|
|
104
178
|
/** 需要编译的笔记本 */
|
|
@@ -150,7 +224,7 @@ var defaultConfig = {
|
|
|
150
224
|
// publicZip:
|
|
151
225
|
// 'https://fastly.jsdelivr.net/gh/siyuan-note/oceanpress@v0.0.7/apps/frontend/public/public.zip',
|
|
152
226
|
// },
|
|
153
|
-
/** s3 上传配置
|
|
227
|
+
/** 部署到 s3 上传配置
|
|
154
228
|
* https://help.aliyun.com/zh/oss/developer-reference/use-amazon-s3-sdks-to-access-oss#section-2ri-suq-pb3
|
|
155
229
|
*/
|
|
156
230
|
s3: {
|
|
@@ -187,6 +261,15 @@ var defaultConfig = {
|
|
|
187
261
|
</p>
|
|
188
262
|
</footer>`
|
|
189
263
|
},
|
|
264
|
+
/** 侧边栏配置 */
|
|
265
|
+
sidebarCode: {
|
|
266
|
+
/** 启用文档树,则 leftCode 将插入在文档树上方 */
|
|
267
|
+
enableDocTree: true,
|
|
268
|
+
/** 将插入主文档的左侧div中的html代码 */
|
|
269
|
+
leftCode: "",
|
|
270
|
+
/** 将插入主文档的右侧div中的html代码 */
|
|
271
|
+
rightCode: ""
|
|
272
|
+
},
|
|
190
273
|
OceanPress: {
|
|
191
274
|
/** 此配置文件编译时的版本 */
|
|
192
275
|
version
|
|
@@ -200,47 +283,49 @@ var configs = reactive({
|
|
|
200
283
|
default: deepAssign({}, defaultConfig)
|
|
201
284
|
});
|
|
202
285
|
var loadConfigFile = (c) => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
286
|
+
return Effect.gen(function* () {
|
|
287
|
+
const effectDep = yield* EffectLocalStorageDep;
|
|
288
|
+
if (c) {
|
|
289
|
+
deepAssign(configs, c);
|
|
290
|
+
} else {
|
|
291
|
+
const localConfig = effectDep.getItem("configs");
|
|
292
|
+
if (localConfig) {
|
|
293
|
+
deepAssign(configs, JSON.parse(localConfig));
|
|
294
|
+
}
|
|
209
295
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
296
|
+
Object.entries(configs).filter(([key]) => key.startsWith("__") === false).forEach(([_key, obj]) => {
|
|
297
|
+
deepAssign(obj, defaultConfig, { update: false, add: true });
|
|
298
|
+
});
|
|
299
|
+
const saveConfig = () => {
|
|
300
|
+
if (configs.__init__ === false)
|
|
301
|
+
effectDep.setItem("configs", JSON.stringify(configs, null, 2));
|
|
302
|
+
};
|
|
303
|
+
let timer = null;
|
|
304
|
+
const debounceSaveConfig = () => {
|
|
305
|
+
if (timer) {
|
|
306
|
+
clearTimeout(timer);
|
|
307
|
+
}
|
|
308
|
+
timer = setTimeout(() => {
|
|
309
|
+
saveConfig();
|
|
310
|
+
timer = null;
|
|
311
|
+
}, 700);
|
|
312
|
+
};
|
|
313
|
+
watch(configs, debounceSaveConfig, { deep: true });
|
|
314
|
+
return configs;
|
|
213
315
|
});
|
|
214
316
|
};
|
|
215
317
|
var currentConfig = computed(() => configs[configs.__current__]);
|
|
216
318
|
var tempConfig = {
|
|
217
319
|
cdn: {
|
|
218
320
|
/** 思源 js、css等文件的前缀 */
|
|
219
|
-
siyuanPrefix:
|
|
321
|
+
siyuanPrefix: `https://fastly.jsdelivr.net/gh/siyuan-note/oceanpress@${version}/apps/frontend/public/notebook/`,
|
|
322
|
+
// 'https://fastly.jsdelivr.net/gh/siyuan-note/oceanpress@latest/apps/frontend/public/notebook/',
|
|
220
323
|
/** 思源 js、css等文件zip包地址 */
|
|
221
324
|
publicZip: "https://fastly.jsdelivr.net/gh/siyuan-note/oceanpress@v0.0.7/apps/frontend/public/public.zip"
|
|
222
325
|
},
|
|
223
326
|
withoutPublicZip: true
|
|
224
327
|
};
|
|
225
|
-
var saveConfig = () => {
|
|
226
|
-
if (configs.__init__ === false)
|
|
227
|
-
storeDep.setItem("configs", JSON.stringify(configs, null, 2));
|
|
228
|
-
};
|
|
229
|
-
var timer = null;
|
|
230
|
-
var debounceSaveConfig = () => {
|
|
231
|
-
if (timer) {
|
|
232
|
-
clearTimeout(timer);
|
|
233
|
-
}
|
|
234
|
-
timer = setTimeout(() => {
|
|
235
|
-
saveConfig();
|
|
236
|
-
timer = null;
|
|
237
|
-
}, 700);
|
|
238
|
-
};
|
|
239
|
-
watch(configs, debounceSaveConfig, { deep: true });
|
|
240
328
|
configs.__init__ = false;
|
|
241
|
-
if (globalThis.document) {
|
|
242
|
-
loadConfigFile();
|
|
243
|
-
}
|
|
244
329
|
|
|
245
330
|
// src/core/genZip.ts
|
|
246
331
|
import JSZip from "jszip";
|
|
@@ -306,7 +391,7 @@ var MeilisearchPlugin = class {
|
|
|
306
391
|
if (path.endsWith("index.html")) break;
|
|
307
392
|
}
|
|
308
393
|
this.updateDocument();
|
|
309
|
-
return next(
|
|
394
|
+
return next(...c);
|
|
310
395
|
});
|
|
311
396
|
this.host = option.host;
|
|
312
397
|
this.apiKey = option.apiKey;
|
|
@@ -339,91 +424,72 @@ var MeilisearchPlugin = class {
|
|
|
339
424
|
}
|
|
340
425
|
};
|
|
341
426
|
|
|
342
|
-
// src/
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const m = new middlewareRunner(fn);
|
|
378
|
-
for (const plugin of this.plugins) {
|
|
379
|
-
const middleware = plugin[name];
|
|
380
|
-
if (middleware) {
|
|
381
|
-
m.use(middleware);
|
|
427
|
+
// src/plugins/publish/OceanPressServer.ts
|
|
428
|
+
import { stringify } from "superjson";
|
|
429
|
+
function deployOceanPressServer_plugin(config) {
|
|
430
|
+
const plugin = {
|
|
431
|
+
async build_onFileTree([tree, effectApi], next) {
|
|
432
|
+
next(tree, effectApi);
|
|
433
|
+
const client = await createRPC("apiConsumer", {
|
|
434
|
+
async remoteCall(method, data) {
|
|
435
|
+
let body;
|
|
436
|
+
let content_type;
|
|
437
|
+
if (data[0] instanceof ReadableStream) {
|
|
438
|
+
body = await new Response(data[0]).blob();
|
|
439
|
+
content_type = "application/octet-stream";
|
|
440
|
+
} else {
|
|
441
|
+
body = stringify(data);
|
|
442
|
+
console.log("[body]", body);
|
|
443
|
+
content_type = "application/json";
|
|
444
|
+
}
|
|
445
|
+
return fetch(`${config.oceanPressServer.apiBase}/api/${method}`, {
|
|
446
|
+
method: "POST",
|
|
447
|
+
body,
|
|
448
|
+
headers: {
|
|
449
|
+
"x-api-key": config.oceanPressServer.apiKey,
|
|
450
|
+
"Content-Type": content_type
|
|
451
|
+
},
|
|
452
|
+
// @ts-expect-error 在 node 运行的时候需要声明双工模式才能正确发送 ReadableStream,TODO 需要验证浏览器端可以这样运行吗
|
|
453
|
+
duplex: "half"
|
|
454
|
+
// 关键:显式声明半双工模式
|
|
455
|
+
}).then((res2) => res2.json()).then((r) => {
|
|
456
|
+
if (r.error) {
|
|
457
|
+
console.log("[r]", r);
|
|
458
|
+
throw new Error();
|
|
459
|
+
}
|
|
460
|
+
return r.result;
|
|
461
|
+
});
|
|
382
462
|
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
runMiddlewareHandel(...ctx) {
|
|
397
|
-
let index = 0;
|
|
398
|
-
const next = (...ctx2) => {
|
|
399
|
-
const middleware = this.middlewares[index];
|
|
400
|
-
index++;
|
|
401
|
-
if (middleware === void 0) {
|
|
402
|
-
return this.handel.call(this, ...ctx2);
|
|
403
|
-
}
|
|
404
|
-
return middleware(ctx2, next);
|
|
405
|
-
};
|
|
406
|
-
return next.call(this, ...ctx);
|
|
407
|
-
}
|
|
408
|
-
};
|
|
463
|
+
});
|
|
464
|
+
const zip = await genZIP(tree, { withoutZip: true });
|
|
465
|
+
const sizeInMB = zip.size / (1024 * 1024);
|
|
466
|
+
console.log("[zip.size in MB]", sizeInMB.toFixed(2));
|
|
467
|
+
const readableStream = zip.stream();
|
|
468
|
+
const { chunkCount, fileId } = await client.API.upload(readableStream);
|
|
469
|
+
console.log("[res]", { chunkCount, fileId });
|
|
470
|
+
const res = await client.API.deploy({ zipFileId: fileId });
|
|
471
|
+
console.log("[deploy res]", res);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
return plugin;
|
|
475
|
+
}
|
|
409
476
|
|
|
410
477
|
// src/plugins/publish/s3.ts
|
|
411
478
|
import { S3 } from "@aws-sdk/client-s3";
|
|
412
479
|
var s3Upload_plugin = {
|
|
413
|
-
build:
|
|
414
|
-
|
|
415
|
-
...
|
|
416
|
-
onFileTree: async (tree) => {
|
|
417
|
-
if (
|
|
418
|
-
await
|
|
480
|
+
build: function([config, otherConfig], next) {
|
|
481
|
+
return next(config, {
|
|
482
|
+
...otherConfig,
|
|
483
|
+
onFileTree: async (tree, effectApi) => {
|
|
484
|
+
if (otherConfig?.onFileTree) {
|
|
485
|
+
await otherConfig.onFileTree(tree, effectApi);
|
|
419
486
|
}
|
|
420
487
|
for await (const [fileName, ETag] of s3_uploads(tree, config)) {
|
|
421
|
-
|
|
488
|
+
effectApi.log(`\u4E0A\u4F20\uFF1A ${fileName} ${ETag}`);
|
|
422
489
|
}
|
|
490
|
+
effectApi.log(`s3 \u4E0A\u4F20\u5B8C\u6BD5`);
|
|
423
491
|
}
|
|
424
492
|
});
|
|
425
|
-
effect.log(`s3 \u4E0A\u4F20\u5B8C\u6BD5`);
|
|
426
|
-
return res;
|
|
427
493
|
}
|
|
428
494
|
};
|
|
429
495
|
var s3_uploads = async function* (tree, config) {
|
|
@@ -452,115 +518,22 @@ var s3_uploads = async function* (tree, config) {
|
|
|
452
518
|
}
|
|
453
519
|
};
|
|
454
520
|
|
|
455
|
-
// src/core/
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
521
|
+
// src/core/build.ts
|
|
522
|
+
import { Effect as Effect4 } from "effect";
|
|
523
|
+
|
|
524
|
+
// src/components/data_promise/index.ts
|
|
525
|
+
import { customRef, nextTick, watch as watch2, watchEffect } from "vue";
|
|
526
|
+
var PromiseObj = class {
|
|
527
|
+
constructor() {
|
|
528
|
+
__publicField(this, "pending", false);
|
|
529
|
+
__publicField(this, "fulfilled", false);
|
|
530
|
+
__publicField(this, "rejected", false);
|
|
531
|
+
__publicField(this, "data", {});
|
|
532
|
+
__publicField(this, "error", {});
|
|
533
|
+
__publicField(this, "_p", Promise.resolve());
|
|
464
534
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
<html lang="zh_CN" data-theme-mode="light" data-light-theme="daylight" data-dark-theme="midnight">
|
|
468
|
-
<head>
|
|
469
|
-
${config?.embedCode?.head ?? ""}
|
|
470
|
-
<meta charset="utf-8" />
|
|
471
|
-
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
472
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
|
|
473
|
-
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
474
|
-
<meta name="mobile-web-app-capable" content="yes" />
|
|
475
|
-
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
|
476
|
-
<link rel="stylesheet" type="text/css" id="baseStyle" href="${prePath}stage/build/export/base.css?${version2}"/>
|
|
477
|
-
<script>
|
|
478
|
-
function isNightTime() {
|
|
479
|
-
const currentHour = new Date().getHours();
|
|
480
|
-
return currentHour >= 18 || currentHour < 6;
|
|
481
|
-
}
|
|
482
|
-
document.write('<link rel="stylesheet" type="text/css" id="themeDefaultStyle" href="${prePath}appearance/themes/'+(isNightTime()?'midnight':'daylight')+'/theme.css?${version2}"/>');
|
|
483
|
-
</script>
|
|
484
|
-
<link rel="stylesheet" type="text/css" href="${prePath}appearance/oceanpress.css"/>
|
|
485
|
-
<title>${p.title}</title>
|
|
486
|
-
</head>
|
|
487
|
-
<body>
|
|
488
|
-
${config?.embedCode?.beforeBody ?? ""}
|
|
489
|
-
<div class="protyle-wysiwyg protyle-wysiwyg--attr" id="preview">
|
|
490
|
-
${p.htmlContent}
|
|
491
|
-
</div>
|
|
492
|
-
<script src="${prePath}appearance/icons/material/icon.js?${version2}"></script>
|
|
493
|
-
<script src="${prePath}stage/build/export/protyle-method.js?${version2}"></script>
|
|
494
|
-
<script src="${prePath}stage/protyle/js/lute/lute.min.js?${version2}"></script>
|
|
495
|
-
<script>
|
|
496
|
-
window.siyuan = {
|
|
497
|
-
config: {
|
|
498
|
-
appearance: {
|
|
499
|
-
mode: isNightTime()?1:0,//\u4E3B\u9898 \u660E\u4EAE=0 \u6697\u9ED1=1
|
|
500
|
-
codeBlockThemeDark: "base16/dracula",
|
|
501
|
-
codeBlockThemeLight: "github",
|
|
502
|
-
},
|
|
503
|
-
editor: {
|
|
504
|
-
codeLineWrap: true,
|
|
505
|
-
codeLigatures: false,
|
|
506
|
-
plantUMLServePath: "https://www.plantuml.com/plantuml/svg/~1",
|
|
507
|
-
codeSyntaxHighlightLineNum: true,
|
|
508
|
-
katexMacros: JSON.stringify({}),
|
|
509
|
-
},
|
|
510
|
-
},
|
|
511
|
-
languages: { copy: "\u590D\u5236" },
|
|
512
|
-
};
|
|
513
|
-
const cdn = "${prePath}stage/protyle";
|
|
514
|
-
const previewElement = document.getElementById("preview");
|
|
515
|
-
|
|
516
|
-
Protyle.highlightRender(previewElement, cdn);
|
|
517
|
-
Protyle.mathRender(previewElement, cdn, false);
|
|
518
|
-
Protyle.mermaidRender(previewElement, cdn);
|
|
519
|
-
Protyle.flowchartRender(previewElement, cdn);
|
|
520
|
-
Protyle.graphvizRender(previewElement, cdn);
|
|
521
|
-
Protyle.chartRender(previewElement, cdn);
|
|
522
|
-
Protyle.mindmapRender(previewElement, cdn);
|
|
523
|
-
Protyle.abcRender(previewElement, cdn);
|
|
524
|
-
Protyle.htmlRender(previewElement);
|
|
525
|
-
Protyle.plantumlRender(previewElement, cdn);
|
|
526
|
-
document.querySelectorAll(".protyle-action__copy").forEach((item) => {
|
|
527
|
-
item.addEventListener("click", (event) => {
|
|
528
|
-
navigator.clipboard.writeText(
|
|
529
|
-
item.parentElement.nextElementSibling.textContent.trimEnd(),
|
|
530
|
-
);
|
|
531
|
-
event.preventDefault();
|
|
532
|
-
event.stopPropagation();
|
|
533
|
-
});
|
|
534
|
-
});
|
|
535
|
-
</script>
|
|
536
|
-
${config?.embedCode?.afterBody ?? ""}
|
|
537
|
-
</body>
|
|
538
|
-
</html>`;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// src/util/escaping.ts
|
|
542
|
-
function escaping(s) {
|
|
543
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "&apos");
|
|
544
|
-
}
|
|
545
|
-
function unescaping(s) {
|
|
546
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&#(\d+);/g, (_sub, code) => {
|
|
547
|
-
return String.fromCharCode(Number(code));
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
// src/components/data_promise/index.ts
|
|
552
|
-
import { customRef, nextTick, watch as watch2, watchEffect } from "vue";
|
|
553
|
-
var PromiseObj = class {
|
|
554
|
-
constructor() {
|
|
555
|
-
__publicField(this, "pending", false);
|
|
556
|
-
__publicField(this, "fulfilled", false);
|
|
557
|
-
__publicField(this, "rejected", false);
|
|
558
|
-
__publicField(this, "data", {});
|
|
559
|
-
__publicField(this, "error", {});
|
|
560
|
-
__publicField(this, "_p", Promise.resolve());
|
|
561
|
-
}
|
|
562
|
-
setP(p) {
|
|
563
|
-
this._p = p;
|
|
535
|
+
setP(p) {
|
|
536
|
+
this._p = p;
|
|
564
537
|
}
|
|
565
538
|
equalP(p) {
|
|
566
539
|
return this._p === p;
|
|
@@ -727,606 +700,100 @@ var vApi = new Proxy(
|
|
|
727
700
|
}
|
|
728
701
|
);
|
|
729
702
|
|
|
730
|
-
// src/core/
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
);
|
|
703
|
+
// src/core/siyuan_type.ts
|
|
704
|
+
function DB_block_path(p) {
|
|
705
|
+
return `data/${p.box}${p.path}`;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/core/cache.ts
|
|
709
|
+
var cache = true;
|
|
710
|
+
function setCache(b) {
|
|
711
|
+
cache = b;
|
|
712
|
+
}
|
|
713
|
+
var sqlCacheMap = /* @__PURE__ */ new Map();
|
|
714
|
+
var hpathCacheMap = /* @__PURE__ */ new Map();
|
|
715
|
+
var idCacheMap = /* @__PURE__ */ new Map();
|
|
716
|
+
var blockCacheMap = /* @__PURE__ */ new Map();
|
|
717
|
+
async function query_sql(stmt) {
|
|
718
|
+
if (cache && sqlCacheMap.has(stmt)) {
|
|
719
|
+
return sqlCacheMap.get(stmt);
|
|
747
720
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
renderObj.nodeStack.push(sy);
|
|
754
|
-
if (sy.ID && renderInstance.nodeStack[0]?.ID) {
|
|
755
|
-
const targetDoc = await storeDep.getDocByChildID(sy.ID);
|
|
756
|
-
const currentDoc = renderInstance.nodeStack[0];
|
|
757
|
-
if (targetDoc?.ID !== void 0 && targetDoc.ID !== currentDoc.ID && currentDoc.ID) {
|
|
758
|
-
renderObj.refs.add(targetDoc.ID);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
const r = await renderObj[sy.Type](sy);
|
|
762
|
-
renderObj.nodeStack.pop();
|
|
763
|
-
return r;
|
|
721
|
+
const res = await API.query_sql({
|
|
722
|
+
stmt
|
|
723
|
+
});
|
|
724
|
+
if (cache) {
|
|
725
|
+
sqlCacheMap.set(stmt, res);
|
|
764
726
|
}
|
|
727
|
+
return res;
|
|
765
728
|
}
|
|
766
|
-
function
|
|
767
|
-
|
|
768
|
-
|
|
729
|
+
async function get_doc_by_hpath(hpath) {
|
|
730
|
+
if (cache) {
|
|
731
|
+
const c = hpathCacheMap.get(hpath);
|
|
732
|
+
if (c) return c;
|
|
733
|
+
}
|
|
734
|
+
const docBlock = (await query_sql(
|
|
735
|
+
`SELECT * FROM blocks WHERE hpath = '${hpath}'`
|
|
736
|
+
))[0];
|
|
737
|
+
if (docBlock === void 0) throw new Error(`not doc by:${hpath}`);
|
|
738
|
+
const res = await get_doc_by_SyPath(DB_block_path(docBlock));
|
|
739
|
+
if (cache) {
|
|
740
|
+
hpathCacheMap.set(hpath, res);
|
|
741
|
+
}
|
|
742
|
+
return res;
|
|
769
743
|
}
|
|
770
|
-
function
|
|
771
|
-
const
|
|
772
|
-
|
|
744
|
+
async function get_doc_by_SyPath(path) {
|
|
745
|
+
const res = parentRef(
|
|
746
|
+
await API.file_getFile({
|
|
747
|
+
path
|
|
748
|
+
})
|
|
773
749
|
);
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
"mindmap",
|
|
777
|
-
"mermaid",
|
|
778
|
-
"echarts",
|
|
779
|
-
"abc",
|
|
780
|
-
"graphviz",
|
|
781
|
-
"flowchart",
|
|
782
|
-
"plantuml"
|
|
783
|
-
].includes(mark),
|
|
784
|
-
mark
|
|
785
|
-
];
|
|
786
|
-
}
|
|
787
|
-
var html = String.raw;
|
|
788
|
-
async function childRender(sy, renderInstance) {
|
|
789
|
-
let h = "";
|
|
790
|
-
for await (const el of sy?.Children ?? []) {
|
|
791
|
-
h += await renderHTML(el, renderInstance);
|
|
750
|
+
if (cache) {
|
|
751
|
+
idCache(res);
|
|
792
752
|
}
|
|
793
|
-
return
|
|
753
|
+
return res;
|
|
794
754
|
}
|
|
795
|
-
function
|
|
796
|
-
if (
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
/** 有序列表 */
|
|
800
|
-
"o"
|
|
801
|
-
) : sy.ListData?.Typ === 3 ? (
|
|
802
|
-
/** 任务列表 */
|
|
803
|
-
"t"
|
|
804
|
-
) : (
|
|
805
|
-
/** 无序列表 */
|
|
806
|
-
"u"
|
|
807
|
-
);
|
|
808
|
-
if (sy.Type === "NodeDocument") return "h1";
|
|
809
|
-
else if (sy.Type === "NodeHeading") return `h${sy.HeadingLevel}`;
|
|
810
|
-
else if (sy.Type === "NodeList") return [typ_subtype, "list"];
|
|
811
|
-
else if (sy.Type === "NodeListItem") return [typ_subtype, "li"];
|
|
812
|
-
else if (sy.Type === "NodeParagraph") return ["", "p"];
|
|
813
|
-
else if (sy.Type === "NodeImage") return ["", "img"];
|
|
814
|
-
else if (sy.Type === "NodeBlockquote") return ["", "bq"];
|
|
815
|
-
else if (sy.Type === "NodeSuperBlock") return ["", "sb"];
|
|
816
|
-
else if (sy.Type === "NodeCodeBlock") {
|
|
817
|
-
const [yes, mark] = isRenderCode(sy);
|
|
818
|
-
if (yes) {
|
|
819
|
-
return [mark, "render-node"];
|
|
820
|
-
} else {
|
|
821
|
-
return ["", "code-block"];
|
|
822
|
-
}
|
|
823
|
-
} else if (sy.Type === "NodeTable") return ["", "table"];
|
|
824
|
-
else if (sy.Type === "NodeThematicBreak") return ["", "hr"];
|
|
825
|
-
else if (sy.Type === "NodeMathBlock") return ["math", "render-node"];
|
|
826
|
-
else if (sy.Type === "NodeIFrame") return ["", "iframe"];
|
|
827
|
-
else if (sy.Type === "NodeVideo") return ["", "iframe"];
|
|
828
|
-
else return "";
|
|
829
|
-
})();
|
|
830
|
-
}
|
|
831
|
-
const attrObj = {};
|
|
832
|
-
function addAttr(key, value) {
|
|
833
|
-
attrObj[key] = value;
|
|
755
|
+
async function get_block_by_id(id) {
|
|
756
|
+
if (cache) {
|
|
757
|
+
const block = blockCacheMap.get(id);
|
|
758
|
+
if (block) return block;
|
|
834
759
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
760
|
+
const blocks = await query_sql(`
|
|
761
|
+
SELECT * from blocks
|
|
762
|
+
WHERE id = '${id}'
|
|
763
|
+
`);
|
|
764
|
+
if (blocks.length === 0) {
|
|
765
|
+
return;
|
|
838
766
|
}
|
|
839
|
-
if (
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
767
|
+
if (cache) blockCacheMap.set(id, blocks[0]);
|
|
768
|
+
return blocks[0];
|
|
769
|
+
}
|
|
770
|
+
async function get_node_by_id(id) {
|
|
771
|
+
if (id === void 0) return;
|
|
772
|
+
if (cache) {
|
|
773
|
+
const node = idCacheMap.get(id);
|
|
774
|
+
if (node) return node;
|
|
843
775
|
}
|
|
844
|
-
|
|
845
|
-
if (
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
addAttr("data-subtype", config.subtype_class[0]);
|
|
852
|
-
if (config.subtype_class[1] !== "")
|
|
853
|
-
addAttr("class", config.subtype_class[1]);
|
|
776
|
+
const doc = await get_doc_by_child_id(id);
|
|
777
|
+
if (doc === void 0) return;
|
|
778
|
+
return findNode(doc);
|
|
779
|
+
function findNode(node) {
|
|
780
|
+
if (node.ID === id) return node;
|
|
781
|
+
if (node.Children) {
|
|
782
|
+
return node.Children.find((child) => findNode(child));
|
|
854
783
|
}
|
|
855
784
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
)
|
|
866
|
-
) {
|
|
867
|
-
attrObj["class"] = (attrObj["class"] ?? "") + " protyle-task--done ";
|
|
785
|
+
}
|
|
786
|
+
async function allDocBlock_by_bookId(id) {
|
|
787
|
+
const res = await query_sql(`
|
|
788
|
+
SELECT * from blocks
|
|
789
|
+
WHERE box = '${id}'
|
|
790
|
+
AND type = 'd'
|
|
791
|
+
limit 150000 OFFSET 0
|
|
792
|
+
`);
|
|
793
|
+
if (cache) {
|
|
794
|
+
res.forEach((block) => blockCacheMap.set(block.id, block));
|
|
868
795
|
}
|
|
869
|
-
|
|
870
|
-
if (sy.Type === "NodeDocument") delete attrObj["title"];
|
|
871
|
-
return Object.entries(attrObj).map(([k, v]) => `${k}="${v}"`).join(" ");
|
|
872
|
-
}
|
|
873
|
-
var _emptyString = async (_sy) => "";
|
|
874
|
-
var _dataString = async (sy) => sy.Data ?? "";
|
|
875
|
-
var getRender = () => {
|
|
876
|
-
return {
|
|
877
|
-
...render,
|
|
878
|
-
nodeStack: [],
|
|
879
|
-
refs: /* @__PURE__ */ new Set()
|
|
880
|
-
};
|
|
881
|
-
};
|
|
882
|
-
var render = {
|
|
883
|
-
nodeStack: [],
|
|
884
|
-
refs: /* @__PURE__ */ new Set(),
|
|
885
|
-
async getTopPathPrefix() {
|
|
886
|
-
const sy = this.nodeStack[0];
|
|
887
|
-
let prefix = ".";
|
|
888
|
-
if (sy.Type === "NodeDocument" && sy.ID) {
|
|
889
|
-
const path = await storeDep.getDocPathBySY(sy);
|
|
890
|
-
if (path) {
|
|
891
|
-
const level = path.split("/").length - 3;
|
|
892
|
-
for (let i = 0; i < level; i++) {
|
|
893
|
-
prefix += "/..";
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
return prefix;
|
|
897
|
-
} else {
|
|
898
|
-
console.log("\u672A\u5B9A\u4E49\u9876\u5C42\u5143\u7D20\u975E NodeDocument \u65F6\u7684\u5904\u7406\u65B9\u5F0F", sy);
|
|
899
|
-
return "";
|
|
900
|
-
}
|
|
901
|
-
},
|
|
902
|
-
async NodeDocument(sy) {
|
|
903
|
-
let html2 = "";
|
|
904
|
-
if (
|
|
905
|
-
/** 只有顶层的文档块才渲染题图 */
|
|
906
|
-
this.nodeStack.length === 1
|
|
907
|
-
) {
|
|
908
|
-
html2 += `<div style="min-height: 150px;" ${strAttr(sy)}>`;
|
|
909
|
-
if (sy.Properties?.["title-img"]) {
|
|
910
|
-
html2 += `<div class="protyle-background__img" style="margin-bottom: 30px;position: relative;height: 16vh;${sy.Properties?.["title-img"].replace(
|
|
911
|
-
/assets/,
|
|
912
|
-
// 修改为相对路径
|
|
913
|
-
await this.getTopPathPrefix() + "/assets"
|
|
914
|
-
)}"/>${sy.Properties?.["icon"] ? `<div style="position: absolute;bottom:-10px;left:15px;height: 80px;width: 80px;transition: var(--b3-transition);cursor: pointer;font-size: 68px;line-height: 80px;text-align: center;font-family: var(--b3-font-family-emoji);margin-right: 16px;"> &#x${sy.Properties?.["icon"]} </div>` : ""}</div>`;
|
|
915
|
-
}
|
|
916
|
-
html2 += "</div>";
|
|
917
|
-
html2 += `<h1 ${strAttr(sy)} data-type="NodeHeading" class="h1">${sy.Properties?.title}</h1>`;
|
|
918
|
-
}
|
|
919
|
-
html2 += await childRender(sy, this);
|
|
920
|
-
return html2;
|
|
921
|
-
},
|
|
922
|
-
async NodeHeading(sy) {
|
|
923
|
-
const tagName = `h${sy.HeadingLevel}`;
|
|
924
|
-
let html2 = `<${tagName} ${strAttr(sy)}>${await childRender(
|
|
925
|
-
sy,
|
|
926
|
-
this
|
|
927
|
-
)}</${tagName}>`;
|
|
928
|
-
const parentNode = this.nodeStack[
|
|
929
|
-
this.nodeStack.length - 2
|
|
930
|
-
/** 最后一个元素是 sy本身(NodeHeading)还得要往前一个,所以是2 */
|
|
931
|
-
];
|
|
932
|
-
if (parentNode?.Type === "NodeBlockQueryEmbedScript") {
|
|
933
|
-
let afterFlag = false;
|
|
934
|
-
for (const node of sy.Parent.Children ?? []) {
|
|
935
|
-
if (node === sy) {
|
|
936
|
-
afterFlag = true;
|
|
937
|
-
} else if (node !== sy && node.Type === "NodeHeading") {
|
|
938
|
-
afterFlag = false;
|
|
939
|
-
} else if (afterFlag) {
|
|
940
|
-
html2 += "\n" + await renderHTML(node, this);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
return html2;
|
|
945
|
-
},
|
|
946
|
-
NodeText: _dataString,
|
|
947
|
-
async NodeList(sy) {
|
|
948
|
-
return html`<div ${strAttr(sy)}>${await childRender(sy, this)}</div>`;
|
|
949
|
-
},
|
|
950
|
-
async NodeListItem(sy) {
|
|
951
|
-
return html`<div ${await strAttr(sy)}>
|
|
952
|
-
<div class="protyle-action">
|
|
953
|
-
${sy.ListData?.Typ === 1 ? (
|
|
954
|
-
/** 有序列表 */
|
|
955
|
-
atob(sy.ListData?.Marker ?? "")
|
|
956
|
-
) : sy.ListData?.Typ === 3 ? (
|
|
957
|
-
/** 任务列表 */
|
|
958
|
-
`<svg><use xlink:href="#${sy.Children?.find((el) => el.Type === "NodeTaskListItemMarker")?.TaskListItemChecked ? "iconCheck" : "iconUncheck"}"></use></svg>`
|
|
959
|
-
) : (
|
|
960
|
-
/** 无序列表 */
|
|
961
|
-
`<svg><use xlink:href="#iconDot"></use></svg>`
|
|
962
|
-
)}
|
|
963
|
-
</div>
|
|
964
|
-
${await childRender(sy, this)}
|
|
965
|
-
</div>`;
|
|
966
|
-
},
|
|
967
|
-
NodeTaskListItemMarker: _emptyString,
|
|
968
|
-
async NodeParagraph(sy) {
|
|
969
|
-
return `<div ${strAttr(sy)}><div spellcheck="false">${await childRender(
|
|
970
|
-
sy,
|
|
971
|
-
this
|
|
972
|
-
)}</div></div>`;
|
|
973
|
-
},
|
|
974
|
-
async NodeTextMark(sy) {
|
|
975
|
-
const that = this;
|
|
976
|
-
let r = "";
|
|
977
|
-
for (const type of (sy.TextMarkType?.split(" ") ?? []).reverse()) {
|
|
978
|
-
if (r === "") {
|
|
979
|
-
r = await TextMarkRender(sy, type, sy.TextMarkTextContent ?? "");
|
|
980
|
-
} else {
|
|
981
|
-
r = await TextMarkRender(sy, type, r);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
return r;
|
|
985
|
-
async function TextMarkRender(sy2, type, content) {
|
|
986
|
-
if (type === "inline-math") {
|
|
987
|
-
return `<span data-type="inline-math" data-subtype="math" data-content="${sy2.TextMarkInlineMathContent}" class="render-node"></span>`;
|
|
988
|
-
} else if (type === "inline-memo") {
|
|
989
|
-
return `${content}<sup>\uFF08${sy2.TextMarkInlineMemoContent}\uFF09</sup>`;
|
|
990
|
-
} else if (type === "block-ref") {
|
|
991
|
-
let href = "";
|
|
992
|
-
if (sy2.TextMarkBlockRefID) {
|
|
993
|
-
const doc = await storeDep.getDocByChildID(sy2.TextMarkBlockRefID);
|
|
994
|
-
if (doc?.ID) {
|
|
995
|
-
href = `${await that.getTopPathPrefix()}${await storeDep.getHPathByID_Node(
|
|
996
|
-
doc
|
|
997
|
-
)}.html#${sy2.TextMarkBlockRefID}`;
|
|
998
|
-
that.refs.add(doc.ID);
|
|
999
|
-
} else {
|
|
1000
|
-
warn(`\u672A\u67E5\u627E\u5230${sy2.ID}\u6240\u6307\u5411\u7684\u6587\u6863\u8282\u70B9 ${sy2.TextMarkBlockRefID}`);
|
|
1001
|
-
}
|
|
1002
|
-
} else {
|
|
1003
|
-
warn(`${sy2.ID} \u5757\u5F15\u7528\u6CA1\u6709\u8BBE\u5B9A ref id`);
|
|
1004
|
-
}
|
|
1005
|
-
return `<span data-type="${sy2.TextMarkType}" data-subtype="${/** "s" */
|
|
1006
|
-
sy2.TextMarkBlockRefSubtype}" data-id="${/** 被引用块的id */
|
|
1007
|
-
sy2.TextMarkBlockRefID}"><a href="${href}">${content}</a></span>`;
|
|
1008
|
-
} else if (type === "a") {
|
|
1009
|
-
let href = sy2.TextMarkAHref;
|
|
1010
|
-
if (href?.startsWith("assets/")) {
|
|
1011
|
-
href = `${await that.getTopPathPrefix()}/${href}`;
|
|
1012
|
-
}
|
|
1013
|
-
return `<a href="${href}">${content}</a>`;
|
|
1014
|
-
} else if (`strong em u s mark sup sub kbd tag code strong code text`.includes(
|
|
1015
|
-
type ?? ""
|
|
1016
|
-
)) {
|
|
1017
|
-
return `<span ${strAttr(sy2, { data_type: type })}>${content}</span>`;
|
|
1018
|
-
} else {
|
|
1019
|
-
return warnDiv(
|
|
1020
|
-
`\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u7684\u6E32\u67D3\u5668 ${sy2.TextMarkType} ${that.nodeStack[0].Properties?.title}`
|
|
1021
|
-
);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
},
|
|
1025
|
-
async NodeImage(sy) {
|
|
1026
|
-
let link = "";
|
|
1027
|
-
const LinkDest = sy.Children?.filter((c) => c.Type === "NodeLinkDest");
|
|
1028
|
-
if (LinkDest?.length === 1) {
|
|
1029
|
-
link = await renderHTML(LinkDest[0], this);
|
|
1030
|
-
} else if (LinkDest?.length && LinkDest.length > 1) {
|
|
1031
|
-
warn("NodeImage \u5B58\u5728\u591A\u4E2A LinkDest", sy);
|
|
1032
|
-
}
|
|
1033
|
-
let title = "";
|
|
1034
|
-
const LinkTitle = sy.Children?.filter((c) => c.Type === "NodeLinkTitle");
|
|
1035
|
-
if (LinkTitle?.length === 1) {
|
|
1036
|
-
title = await renderHTML(LinkTitle[0], this);
|
|
1037
|
-
} else if (LinkTitle?.length && LinkTitle.length > 1) {
|
|
1038
|
-
warn("NodeImage \u5B58\u5728\u591A\u4E2A LinkTitle", sy);
|
|
1039
|
-
}
|
|
1040
|
-
return `<span ${await strAttr(sy)} style="${sy.Properties?.["parent-style"] ?? ""}">
|
|
1041
|
-
<img
|
|
1042
|
-
src="${link}"
|
|
1043
|
-
data-src="${link}"
|
|
1044
|
-
title="${title}"
|
|
1045
|
-
style="${sy.Properties?.style ?? ""}"
|
|
1046
|
-
loading="lazy"
|
|
1047
|
-
/>
|
|
1048
|
-
<span class="protyle-action__title">${title}</span></span>`;
|
|
1049
|
-
},
|
|
1050
|
-
async NodeLinkDest(sy) {
|
|
1051
|
-
if (/^(?:[a-z]+:)?\/\/|^(?:\/)/.test(sy.Data ?? "")) {
|
|
1052
|
-
return sy.Data ?? "";
|
|
1053
|
-
}
|
|
1054
|
-
return `${await this.getTopPathPrefix()}/${sy.Data}`;
|
|
1055
|
-
},
|
|
1056
|
-
NodeLinkTitle: _dataString,
|
|
1057
|
-
NodeKramdownSpanIAL: _emptyString,
|
|
1058
|
-
async NodeSuperBlock(sy) {
|
|
1059
|
-
return `<div ${strAttr(sy)} data-sb-layout="${childDateByType(
|
|
1060
|
-
sy,
|
|
1061
|
-
"NodeSuperBlockLayoutMarker"
|
|
1062
|
-
)}">${await childRender(sy, this)}</div>`;
|
|
1063
|
-
},
|
|
1064
|
-
NodeSuperBlockOpenMarker: _emptyString,
|
|
1065
|
-
NodeSuperBlockCloseMarker: _emptyString,
|
|
1066
|
-
NodeSuperBlockLayoutMarker: _emptyString,
|
|
1067
|
-
async NodeBlockQueryEmbed(sy) {
|
|
1068
|
-
return `<div ${strAttr(sy)} data-type="NodeBlockquote" class="bq">${await childRender(sy, this)}</div>`;
|
|
1069
|
-
},
|
|
1070
|
-
NodeOpenBrace: _emptyString,
|
|
1071
|
-
NodeCloseBrace: _emptyString,
|
|
1072
|
-
async NodeBlockQueryEmbedScript(sy) {
|
|
1073
|
-
const sql = sy.Data;
|
|
1074
|
-
if (!sql) {
|
|
1075
|
-
console.log("no sql", sy);
|
|
1076
|
-
return html`<pre>${sql}</pre>`;
|
|
1077
|
-
}
|
|
1078
|
-
let htmlStr = "";
|
|
1079
|
-
const blocks = await API.query_sql({
|
|
1080
|
-
stmt: (
|
|
1081
|
-
/** sql 被思源转义了,类似 :SELECT * FROM blocks WHERE id = '20201227174241-nxny1tq'
|
|
1082
|
-
所以这里将它转义回来
|
|
1083
|
-
TODO 当用户确实使用了包含转义的字符串时,这个实现是错误的 */
|
|
1084
|
-
unescaping(
|
|
1085
|
-
sql
|
|
1086
|
-
).replace(
|
|
1087
|
-
/** 我不理解lute为什么这样实现 https://github.com/88250/lute/blob/HEAD/editor/const.go#L38
|
|
1088
|
-
* https://ld246.com/article/1696750832289
|
|
1089
|
-
*/
|
|
1090
|
-
/_esc_newline_/g,
|
|
1091
|
-
"\n"
|
|
1092
|
-
)
|
|
1093
|
-
)
|
|
1094
|
-
}).catch((err) => {
|
|
1095
|
-
throw new Error(
|
|
1096
|
-
`sql error: ${err.message}
|
|
1097
|
-
rawSql:${sql}
|
|
1098
|
-
unescapingSql:${unescaping(
|
|
1099
|
-
sql
|
|
1100
|
-
)}`
|
|
1101
|
-
);
|
|
1102
|
-
});
|
|
1103
|
-
for (const block of blocks) {
|
|
1104
|
-
const node = await storeDep.getNodeByID(block.id);
|
|
1105
|
-
if (node === void 0) {
|
|
1106
|
-
return warnDiv("\u672A\u627E\u5230\u6B64\u5757\uFF0C\u53EF\u80FD\u4E3A\u8DE8\u7B14\u8BB0\u5F15\u7528", block.id, sql);
|
|
1107
|
-
}
|
|
1108
|
-
htmlStr += await renderHTML(node, this);
|
|
1109
|
-
}
|
|
1110
|
-
return htmlStr;
|
|
1111
|
-
},
|
|
1112
|
-
async NodeBlockquote(sy) {
|
|
1113
|
-
return html`<div ${strAttr(sy)}>${await childRender(sy, this)}</div>`;
|
|
1114
|
-
},
|
|
1115
|
-
NodeBlockquoteMarker: _emptyString,
|
|
1116
|
-
NodeCodeBlock: async (sy) => {
|
|
1117
|
-
const [yes, _] = isRenderCode(sy);
|
|
1118
|
-
if (yes) {
|
|
1119
|
-
return `<div ${strAttr(sy)} data-content="${escaping(
|
|
1120
|
-
sy.Children?.find((el) => el.Type === "NodeCodeBlockCode")?.Data ?? ""
|
|
1121
|
-
)}">
|
|
1122
|
-
<div spin="1"></div>
|
|
1123
|
-
<div class="protyle-attr" contenteditable="false"></div>
|
|
1124
|
-
</div>`;
|
|
1125
|
-
}
|
|
1126
|
-
return `<div ${strAttr(sy)}>
|
|
1127
|
-
<div class="protyle-action">
|
|
1128
|
-
<span class="protyle-action--first protyle-action__language">${await renderHTML(
|
|
1129
|
-
sy.Children?.find(
|
|
1130
|
-
(el) => el.Type === "NodeCodeBlockFenceInfoMarker"
|
|
1131
|
-
),
|
|
1132
|
-
void 0
|
|
1133
|
-
)}</span>
|
|
1134
|
-
<span class="fn__flex-1"></span><span class="protyle-icon protyle-icon--only protyle-action__copy"><svg><use xlink:href="#iconCopy"></use></svg></span>
|
|
1135
|
-
</div>
|
|
1136
|
-
${await renderHTML(
|
|
1137
|
-
sy.Children?.find((el) => el.Type === "NodeCodeBlockCode"),
|
|
1138
|
-
void 0
|
|
1139
|
-
)}
|
|
1140
|
-
</div>`;
|
|
1141
|
-
},
|
|
1142
|
-
NodeCodeBlockFenceInfoMarker: async (sy) => atob(sy.CodeBlockInfo ?? ""),
|
|
1143
|
-
NodeCodeBlockCode: async (sy) => `<div class="hljs" spellcheck="false">${sy.Data}</div>`,
|
|
1144
|
-
NodeCodeBlockFenceOpenMarker: _emptyString,
|
|
1145
|
-
NodeCodeBlockFenceCloseMarker: _emptyString,
|
|
1146
|
-
async NodeTable(sy) {
|
|
1147
|
-
return `<div ${strAttr(sy)}>
|
|
1148
|
-
<div>
|
|
1149
|
-
<table spellcheck="false">
|
|
1150
|
-
<colgroup>
|
|
1151
|
-
${sy.TableAligns?.map(() => "<col />").join("")}
|
|
1152
|
-
</colgroup>
|
|
1153
|
-
${await renderHTML(
|
|
1154
|
-
sy.Children?.find((el) => el.Type === "NodeTableHead"),
|
|
1155
|
-
this
|
|
1156
|
-
)}
|
|
1157
|
-
<tbody>
|
|
1158
|
-
${(await Promise.all(
|
|
1159
|
-
sy.Children?.filter((el) => el.Type === "NodeTableRow").map(
|
|
1160
|
-
(el) => renderHTML(el, this)
|
|
1161
|
-
) ?? []
|
|
1162
|
-
)).join("\n")}
|
|
1163
|
-
</tbody>
|
|
1164
|
-
</table>
|
|
1165
|
-
</div>
|
|
1166
|
-
</div>`;
|
|
1167
|
-
},
|
|
1168
|
-
async NodeTableHead(sy) {
|
|
1169
|
-
return `<${sy.Data}>${await childRender(sy, this)}</${sy.Data}>`;
|
|
1170
|
-
},
|
|
1171
|
-
async NodeTableRow(sy) {
|
|
1172
|
-
return `<tr>${await childRender(sy, this)}</tr>`;
|
|
1173
|
-
},
|
|
1174
|
-
async NodeTableCell(sy) {
|
|
1175
|
-
return `<td>${await childRender(sy, this)}</td>`;
|
|
1176
|
-
},
|
|
1177
|
-
NodeHTMLBlock: async (sy) => `<div ${strAttr(sy)}>${sy.Data}</div>`,
|
|
1178
|
-
NodeThematicBreak: async (sy) => `<div ${strAttr(sy)}><div></div></div>`,
|
|
1179
|
-
NodeMathBlock: async (sy) => `<div ${strAttr(
|
|
1180
|
-
sy
|
|
1181
|
-
)} data-content="${childDateByType(sy, "NodeMathBlockContent")}">
|
|
1182
|
-
<div spin="1"></div>
|
|
1183
|
-
</div>`,
|
|
1184
|
-
NodeMathBlockOpenMarker: _emptyString,
|
|
1185
|
-
NodeMathBlockCloseMarker: _emptyString,
|
|
1186
|
-
async NodeIFrame(sy) {
|
|
1187
|
-
return ` <div ${strAttr(sy)}>
|
|
1188
|
-
<div class="iframe-content">
|
|
1189
|
-
${/** 资源总是被复制到顶层目录,所以直接跳到顶层即可 */
|
|
1190
|
-
/** TODO 应该有一个统一处理资源的方案 */
|
|
1191
|
-
sy.Data?.replace(
|
|
1192
|
-
/src="assets\//,
|
|
1193
|
-
`src="${await this.getTopPathPrefix()}/assets/`
|
|
1194
|
-
)}
|
|
1195
|
-
</div>
|
|
1196
|
-
</div>`;
|
|
1197
|
-
},
|
|
1198
|
-
async NodeVideo(sy) {
|
|
1199
|
-
return await this.NodeIFrame(sy);
|
|
1200
|
-
},
|
|
1201
|
-
async NodeAudio(sy) {
|
|
1202
|
-
return await this.NodeIFrame(sy);
|
|
1203
|
-
},
|
|
1204
|
-
/** 虚拟链接 */
|
|
1205
|
-
NodeHeadingC8hMarker: _emptyString,
|
|
1206
|
-
async NodeSoftBreak(_sy) {
|
|
1207
|
-
return "\u200B";
|
|
1208
|
-
},
|
|
1209
|
-
async NodeBr(sy) {
|
|
1210
|
-
return `<${sy.Data}>`;
|
|
1211
|
-
},
|
|
1212
|
-
async NodeWidget(sy) {
|
|
1213
|
-
return `<div ${strAttr(
|
|
1214
|
-
sy
|
|
1215
|
-
)}><img src="${await this.getTopPathPrefix()}/assets/widget/${sy.ID}.jpg"/></div>`;
|
|
1216
|
-
},
|
|
1217
|
-
async NodeBackslash(sy) {
|
|
1218
|
-
if (sy.Data === void 0 || sy.Data === "span") {
|
|
1219
|
-
return `${await childRender(sy, this)}`;
|
|
1220
|
-
} else {
|
|
1221
|
-
return warnDiv(
|
|
1222
|
-
`\u672A\u5B9A\u4E49\u7684 NodeBackslash \u5904\u7406 ${sy.Data}`,
|
|
1223
|
-
this.nodeStack[0].Properties?.title
|
|
1224
|
-
);
|
|
1225
|
-
}
|
|
1226
|
-
},
|
|
1227
|
-
NodeBackslashContent: _dataString
|
|
1228
|
-
};
|
|
1229
|
-
function childDateByType(sy, type) {
|
|
1230
|
-
return sy.Children?.find((el) => el.Type === type)?.Data;
|
|
1231
|
-
}
|
|
1232
|
-
function warn(...arg) {
|
|
1233
|
-
console.warn("\n", ...arg);
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
// src/core/siyuan_type.ts
|
|
1237
|
-
function DB_block_path(p) {
|
|
1238
|
-
return `data/${p.box}${p.path}`;
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
// src/core/cache.ts
|
|
1242
|
-
var cache = true;
|
|
1243
|
-
function setCache(b) {
|
|
1244
|
-
cache = b;
|
|
1245
|
-
}
|
|
1246
|
-
var sqlCacheMap = /* @__PURE__ */ new Map();
|
|
1247
|
-
var hpathCacheMap = /* @__PURE__ */ new Map();
|
|
1248
|
-
var idCacheMap = /* @__PURE__ */ new Map();
|
|
1249
|
-
var blockCacheMap = /* @__PURE__ */ new Map();
|
|
1250
|
-
async function query_sql(stmt) {
|
|
1251
|
-
if (cache && sqlCacheMap.has(stmt)) {
|
|
1252
|
-
return sqlCacheMap.get(stmt);
|
|
1253
|
-
}
|
|
1254
|
-
const res = await API.query_sql({
|
|
1255
|
-
stmt
|
|
1256
|
-
});
|
|
1257
|
-
if (cache) {
|
|
1258
|
-
sqlCacheMap.set(stmt, res);
|
|
1259
|
-
}
|
|
1260
|
-
return res;
|
|
1261
|
-
}
|
|
1262
|
-
async function get_doc_by_hpath(hpath) {
|
|
1263
|
-
if (cache) {
|
|
1264
|
-
const c = hpathCacheMap.get(hpath);
|
|
1265
|
-
if (c) return c;
|
|
1266
|
-
}
|
|
1267
|
-
const docBlock = (await query_sql(
|
|
1268
|
-
`SELECT * FROM blocks WHERE hpath = '${hpath}'`
|
|
1269
|
-
))[0];
|
|
1270
|
-
if (docBlock === void 0) throw new Error(`not doc by:${hpath}`);
|
|
1271
|
-
const res = await get_doc_by_SyPath(DB_block_path(docBlock));
|
|
1272
|
-
if (cache) {
|
|
1273
|
-
hpathCacheMap.set(hpath, res);
|
|
1274
|
-
}
|
|
1275
|
-
return res;
|
|
1276
|
-
}
|
|
1277
|
-
async function get_doc_by_SyPath(path) {
|
|
1278
|
-
const res = parentRef(
|
|
1279
|
-
await API.file_getFile({
|
|
1280
|
-
path
|
|
1281
|
-
})
|
|
1282
|
-
);
|
|
1283
|
-
if (cache) {
|
|
1284
|
-
idCache(res);
|
|
1285
|
-
}
|
|
1286
|
-
return res;
|
|
1287
|
-
}
|
|
1288
|
-
async function get_block_by_id(id) {
|
|
1289
|
-
if (cache) {
|
|
1290
|
-
const block = blockCacheMap.get(id);
|
|
1291
|
-
if (block) return block;
|
|
1292
|
-
}
|
|
1293
|
-
const blocks = await query_sql(`
|
|
1294
|
-
SELECT * from blocks
|
|
1295
|
-
WHERE id = '${id}'
|
|
1296
|
-
`);
|
|
1297
|
-
if (blocks.length === 0) {
|
|
1298
|
-
return;
|
|
1299
|
-
}
|
|
1300
|
-
if (cache) blockCacheMap.set(id, blocks[0]);
|
|
1301
|
-
return blocks[0];
|
|
1302
|
-
}
|
|
1303
|
-
async function get_node_by_id(id) {
|
|
1304
|
-
if (id === void 0) return;
|
|
1305
|
-
if (cache) {
|
|
1306
|
-
const node = idCacheMap.get(id);
|
|
1307
|
-
if (node) return node;
|
|
1308
|
-
}
|
|
1309
|
-
const doc = await get_doc_by_child_id(id);
|
|
1310
|
-
if (doc === void 0) return;
|
|
1311
|
-
return findNode(doc);
|
|
1312
|
-
function findNode(node) {
|
|
1313
|
-
if (node.ID === id) return node;
|
|
1314
|
-
if (node.Children) {
|
|
1315
|
-
return node.Children.find((child) => findNode(child));
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
async function allDocBlock_by_bookId(id) {
|
|
1320
|
-
const res = await query_sql(`
|
|
1321
|
-
SELECT * from blocks
|
|
1322
|
-
WHERE box = '${id}'
|
|
1323
|
-
AND type = 'd'
|
|
1324
|
-
limit 150000 OFFSET 0
|
|
1325
|
-
`);
|
|
1326
|
-
if (cache) {
|
|
1327
|
-
res.forEach((block) => blockCacheMap.set(block.id, block));
|
|
1328
|
-
}
|
|
1329
|
-
return res;
|
|
796
|
+
return res;
|
|
1330
797
|
}
|
|
1331
798
|
async function get_doc_by_child_id(id) {
|
|
1332
799
|
if (cache) {
|
|
@@ -1364,7 +831,7 @@ function parentRef(sy) {
|
|
|
1364
831
|
}
|
|
1365
832
|
|
|
1366
833
|
// src/core/genRssXml.ts
|
|
1367
|
-
async function generateRSSXML(path, renderInstance, config) {
|
|
834
|
+
async function generateRSSXML(path, renderInstance, config, getHPathByID_Node) {
|
|
1368
835
|
const refNode = (await Promise.all([...renderInstance.refs.values()].map(get_node_by_id))).filter((el) => el);
|
|
1369
836
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1370
837
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
@@ -1378,7 +845,7 @@ async function generateRSSXML(path, renderInstance, config) {
|
|
|
1378
845
|
refNode.map(
|
|
1379
846
|
async (node) => `<item>
|
|
1380
847
|
<title>${node?.Properties?.title}</title>
|
|
1381
|
-
<link>${config.sitemap.sitePrefix}${node?.ID ? await
|
|
848
|
+
<link>${config.sitemap.sitePrefix}${node?.ID ? await getHPathByID_Node(node?.ID) + ".html" : ""}</link>
|
|
1382
849
|
<description>${""}</description>
|
|
1383
850
|
<pubDate>${node?.Properties?.updated ? new Date(
|
|
1384
851
|
node.Properties.updated.replace(
|
|
@@ -1414,181 +881,1790 @@ function sitemap_xml(docArr, config) {
|
|
|
1414
881
|
</urlset>`;
|
|
1415
882
|
}
|
|
1416
883
|
|
|
1417
|
-
// src/core/
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
}
|
|
884
|
+
// src/core/seo.ts
|
|
885
|
+
function extractDateFromId(id) {
|
|
886
|
+
if (!id || id.length < 14) return (/* @__PURE__ */ new Date()).toISOString();
|
|
887
|
+
try {
|
|
888
|
+
const datePart = id.slice(0, 14);
|
|
889
|
+
if (datePart.length === 14) {
|
|
890
|
+
const year = datePart.slice(0, 4);
|
|
891
|
+
const month = datePart.slice(4, 6);
|
|
892
|
+
const day = datePart.slice(6, 8);
|
|
893
|
+
const hour = datePart.slice(8, 10);
|
|
894
|
+
const minute = datePart.slice(10, 12);
|
|
895
|
+
const second = datePart.slice(12, 14);
|
|
896
|
+
return `${year}-${month}-${day}T${hour}:${minute}:${second}Z`;
|
|
897
|
+
}
|
|
898
|
+
} catch {
|
|
899
|
+
}
|
|
900
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
901
|
+
}
|
|
902
|
+
function formatDate(dateString) {
|
|
903
|
+
if (!dateString) return (/* @__PURE__ */ new Date()).toISOString();
|
|
904
|
+
try {
|
|
905
|
+
if (dateString.length === 14) {
|
|
906
|
+
const year = dateString.slice(0, 4);
|
|
907
|
+
const month = dateString.slice(4, 6);
|
|
908
|
+
const day = dateString.slice(6, 8);
|
|
909
|
+
const hour = dateString.slice(8, 10);
|
|
910
|
+
const minute = dateString.slice(10, 12);
|
|
911
|
+
const second = dateString.slice(12, 14);
|
|
912
|
+
return `${year}-${month}-${day}T${hour}:${minute}:${second}Z`;
|
|
913
|
+
}
|
|
914
|
+
const timestamp = parseInt(dateString);
|
|
915
|
+
if (!isNaN(timestamp)) {
|
|
916
|
+
return new Date(timestamp).toISOString();
|
|
917
|
+
}
|
|
918
|
+
return new Date(dateString).toISOString();
|
|
919
|
+
} catch {
|
|
920
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
var TFIDFKeywordExtractor = class {
|
|
924
|
+
constructor() {
|
|
925
|
+
__publicField(this, "stopWords", /* @__PURE__ */ new Set([
|
|
926
|
+
// 中文停用词
|
|
927
|
+
"\u7684",
|
|
928
|
+
"\u4E86",
|
|
929
|
+
"\u5728",
|
|
930
|
+
"\u662F",
|
|
931
|
+
"\u6211",
|
|
932
|
+
"\u6709",
|
|
933
|
+
"\u548C",
|
|
934
|
+
"\u5C31",
|
|
935
|
+
"\u4E0D",
|
|
936
|
+
"\u4EBA",
|
|
937
|
+
"\u90FD",
|
|
938
|
+
"\u4E00",
|
|
939
|
+
"\u4E2A",
|
|
940
|
+
"\u4E0A",
|
|
941
|
+
"\u4E5F",
|
|
942
|
+
"\u5F88",
|
|
943
|
+
"\u5230",
|
|
944
|
+
"\u8BF4",
|
|
945
|
+
"\u8981",
|
|
946
|
+
"\u53BB",
|
|
947
|
+
"\u4F60",
|
|
948
|
+
"\u4F1A",
|
|
949
|
+
"\u7740",
|
|
950
|
+
"\u6CA1\u6709",
|
|
951
|
+
"\u770B",
|
|
952
|
+
"\u597D",
|
|
953
|
+
"\u81EA\u5DF1",
|
|
954
|
+
"\u8FD9",
|
|
955
|
+
"\u90A3",
|
|
956
|
+
"\u73B0\u5728",
|
|
957
|
+
"\u53EF\u4EE5",
|
|
958
|
+
"\u4F46\u662F",
|
|
959
|
+
"\u8FD8\u662F",
|
|
960
|
+
"\u56E0\u4E3A",
|
|
961
|
+
"\u4EC0\u4E48",
|
|
962
|
+
"\u5982\u679C",
|
|
963
|
+
"\u6240\u4EE5",
|
|
964
|
+
"\u5BF9\u4E8E",
|
|
965
|
+
"\u5173\u4E8E",
|
|
966
|
+
"\u901A\u8FC7",
|
|
967
|
+
"\u8FDB\u884C",
|
|
968
|
+
"\u57FA\u4E8E",
|
|
969
|
+
"\u4EE5\u53CA",
|
|
970
|
+
"\u6216\u8005",
|
|
971
|
+
"\u800C\u4E14",
|
|
972
|
+
"\u7136\u540E",
|
|
973
|
+
"\u53EA\u662F",
|
|
974
|
+
"\u5DF2\u7ECF",
|
|
975
|
+
"\u6B63\u5728",
|
|
976
|
+
"\u5E94\u8BE5",
|
|
977
|
+
"\u80FD\u591F",
|
|
978
|
+
"\u9700\u8981",
|
|
979
|
+
"\u53EF\u80FD",
|
|
980
|
+
"\u4E00\u5B9A",
|
|
981
|
+
"\u8FD9\u6837",
|
|
982
|
+
"\u90A3\u6837",
|
|
983
|
+
"\u600E\u4E48",
|
|
984
|
+
"\u4E3A\u4EC0\u4E48",
|
|
985
|
+
"\u54EA\u91CC",
|
|
986
|
+
"\u54EA\u4E2A",
|
|
987
|
+
"\u591A\u5C11",
|
|
988
|
+
"\u51E0\u4E2A",
|
|
989
|
+
"\u4EC0\u4E48",
|
|
990
|
+
"\u600E\u4E48",
|
|
991
|
+
"\u5982\u4F55",
|
|
992
|
+
"\u4E3A\u4EC0\u4E48",
|
|
993
|
+
// 英文停用词
|
|
994
|
+
"the",
|
|
995
|
+
"a",
|
|
996
|
+
"an",
|
|
997
|
+
"and",
|
|
998
|
+
"or",
|
|
999
|
+
"but",
|
|
1000
|
+
"in",
|
|
1001
|
+
"on",
|
|
1002
|
+
"at",
|
|
1003
|
+
"to",
|
|
1004
|
+
"for",
|
|
1005
|
+
"of",
|
|
1006
|
+
"with",
|
|
1007
|
+
"by",
|
|
1008
|
+
"is",
|
|
1009
|
+
"are",
|
|
1010
|
+
"was",
|
|
1011
|
+
"were",
|
|
1012
|
+
"be",
|
|
1013
|
+
"been",
|
|
1014
|
+
"being",
|
|
1015
|
+
"have",
|
|
1016
|
+
"has",
|
|
1017
|
+
"had",
|
|
1018
|
+
"do",
|
|
1019
|
+
"does",
|
|
1020
|
+
"did",
|
|
1021
|
+
"will",
|
|
1022
|
+
"would",
|
|
1023
|
+
"could",
|
|
1024
|
+
"should",
|
|
1025
|
+
"may",
|
|
1026
|
+
"might",
|
|
1027
|
+
"must",
|
|
1028
|
+
"can",
|
|
1029
|
+
"this",
|
|
1030
|
+
"that",
|
|
1031
|
+
"these",
|
|
1032
|
+
"those",
|
|
1033
|
+
"i",
|
|
1034
|
+
"you",
|
|
1035
|
+
"he",
|
|
1036
|
+
"she",
|
|
1037
|
+
"it",
|
|
1038
|
+
"we",
|
|
1039
|
+
"they",
|
|
1040
|
+
"me",
|
|
1041
|
+
"him",
|
|
1042
|
+
"her",
|
|
1043
|
+
"us",
|
|
1044
|
+
"them",
|
|
1045
|
+
"my",
|
|
1046
|
+
"your",
|
|
1047
|
+
"his",
|
|
1048
|
+
"its",
|
|
1049
|
+
"our",
|
|
1050
|
+
"their",
|
|
1051
|
+
"not",
|
|
1052
|
+
"no",
|
|
1053
|
+
"yes",
|
|
1054
|
+
"so",
|
|
1055
|
+
"if",
|
|
1056
|
+
"when",
|
|
1057
|
+
"where",
|
|
1058
|
+
"how",
|
|
1059
|
+
"why",
|
|
1060
|
+
"what",
|
|
1061
|
+
"which",
|
|
1062
|
+
"who",
|
|
1063
|
+
"whom",
|
|
1064
|
+
"there",
|
|
1065
|
+
"here"
|
|
1066
|
+
]));
|
|
1431
1067
|
}
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
)
|
|
1441
|
-
|
|
1442
|
-
|
|
1068
|
+
/**
|
|
1069
|
+
* 计算词频 (TF)
|
|
1070
|
+
*/
|
|
1071
|
+
calculateTermFrequency(text) {
|
|
1072
|
+
const words = this.tokenize(text);
|
|
1073
|
+
const tf = /* @__PURE__ */ new Map();
|
|
1074
|
+
const totalWords = words.length;
|
|
1075
|
+
for (const word of words) {
|
|
1076
|
+
tf.set(word, (tf.get(word) || 0) + 1);
|
|
1077
|
+
}
|
|
1078
|
+
for (const [word, count] of tf) {
|
|
1079
|
+
tf.set(word, count / totalWords);
|
|
1080
|
+
}
|
|
1081
|
+
return tf;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* 分词处理(针对中文和英文优化)
|
|
1085
|
+
*/
|
|
1086
|
+
tokenize(text) {
|
|
1087
|
+
const tokens = [];
|
|
1088
|
+
const plainText = text.replace(/<[^>]*>/g, " ").replace(/&[^;]+;/g, " ").replace(/\s+/g, " ").trim();
|
|
1089
|
+
const englishWords = plainText.match(/[a-zA-Z]{3,}/g) || [];
|
|
1090
|
+
tokens.push(...englishWords.map((word) => word.toLowerCase()).filter((word) => !this.stopWords.has(word)));
|
|
1091
|
+
const camelCaseWords = plainText.match(/[a-z]+[A-Z][a-zA-Z0-9]*|[A-Z][a-z0-9]+[A-Z][a-zA-Z0-9]*/g) || [];
|
|
1092
|
+
tokens.push(...camelCaseWords.map((word) => word.toLowerCase()));
|
|
1093
|
+
const chineseTokens = this.extractChineseTokens(plainText);
|
|
1094
|
+
tokens.push(...chineseTokens);
|
|
1095
|
+
const filteredTokens = tokens.filter((token) => {
|
|
1096
|
+
if (token.length < 2) return false;
|
|
1097
|
+
const htmlRelatedWords = ["div", "span", "class", "id", "type", "data", "href", "src", "alt", "title", "style", "width", "height", "rootid", "endid", "nodedocument", "ezcqbj", "cktc", "quot"];
|
|
1098
|
+
if (htmlRelatedWords.includes(token.toLowerCase())) {
|
|
1443
1099
|
return false;
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1100
|
+
}
|
|
1101
|
+
if (token.match(/^[a-f0-9]{6,}$/)) return false;
|
|
1102
|
+
if (/[\u4e00-\u9fa5]/.test(token) && token.length < 2) {
|
|
1447
1103
|
return false;
|
|
1448
1104
|
}
|
|
1105
|
+
return true;
|
|
1106
|
+
});
|
|
1107
|
+
return filteredTokens;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* 智能中文分词 - 基于文本特征分析
|
|
1111
|
+
*/
|
|
1112
|
+
extractChineseTokens(text) {
|
|
1113
|
+
const tokens = [];
|
|
1114
|
+
const chineseText = text.replace(/[^\u4e00-\u9fa5\s]/g, " ");
|
|
1115
|
+
const wordPatterns = [
|
|
1116
|
+
/[\u4e00-\u9fa5]{4}/g,
|
|
1117
|
+
// 4字词汇
|
|
1118
|
+
/[\u4e00-\u9fa5]{3}/g,
|
|
1119
|
+
// 3字词汇
|
|
1120
|
+
/[\u4e00-\u9fa5]{2}/g
|
|
1121
|
+
// 2字词汇
|
|
1122
|
+
];
|
|
1123
|
+
for (const pattern of wordPatterns) {
|
|
1124
|
+
const matches = chineseText.match(pattern) || [];
|
|
1125
|
+
for (const word of matches) {
|
|
1126
|
+
if (!this.stopWords.has(word) && this.isMeaningfulChineseWord(word)) {
|
|
1127
|
+
tokens.push(word.toLowerCase());
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1449
1130
|
}
|
|
1450
|
-
return
|
|
1131
|
+
return tokens;
|
|
1451
1132
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1133
|
+
/**
|
|
1134
|
+
* 判断是否为有意义的中文词汇
|
|
1135
|
+
*/
|
|
1136
|
+
isMeaningfulChineseWord(word) {
|
|
1137
|
+
const meaninglessPatterns = [
|
|
1138
|
+
/^的.*$/,
|
|
1139
|
+
/^.*的$/,
|
|
1140
|
+
/^了.*$/,
|
|
1141
|
+
/^.*了$/,
|
|
1142
|
+
/^在.*$/,
|
|
1143
|
+
/^.*在$/,
|
|
1144
|
+
/^是.*$/,
|
|
1145
|
+
/^.*是$/,
|
|
1146
|
+
/^我.*$/,
|
|
1147
|
+
/^.*我$/,
|
|
1148
|
+
/^有.*$/,
|
|
1149
|
+
/^.*有$/,
|
|
1150
|
+
/^和.*$/,
|
|
1151
|
+
/^.*和$/,
|
|
1152
|
+
/^就.*$/,
|
|
1153
|
+
/^.*就$/,
|
|
1154
|
+
/^不.*$/,
|
|
1155
|
+
/^.*不$/,
|
|
1156
|
+
/^人.*$/,
|
|
1157
|
+
/^.*人$/
|
|
1158
|
+
];
|
|
1159
|
+
for (const pattern of meaninglessPatterns) {
|
|
1160
|
+
if (pattern.test(word)) {
|
|
1161
|
+
return false;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
if (/(.)\1{2,}/.test(word)) {
|
|
1469
1165
|
return false;
|
|
1470
1166
|
}
|
|
1471
|
-
return
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1167
|
+
return true;
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* 计算逆文档频率 (IDF) - 简化版本
|
|
1171
|
+
*/
|
|
1172
|
+
calculateInverseDocumentFrequency(term) {
|
|
1173
|
+
const commonTerms = /* @__PURE__ */ new Set([
|
|
1174
|
+
"\u6280\u672F",
|
|
1175
|
+
"\u5F00\u53D1",
|
|
1176
|
+
"\u4EE3\u7801",
|
|
1177
|
+
"\u7CFB\u7EDF",
|
|
1178
|
+
"\u6570\u636E",
|
|
1179
|
+
"\u529F\u80FD",
|
|
1180
|
+
"\u5E94\u7528",
|
|
1181
|
+
"\u5B9E\u73B0",
|
|
1182
|
+
"\u65B9\u6CD5",
|
|
1183
|
+
"\u95EE\u9898",
|
|
1184
|
+
"time",
|
|
1185
|
+
"data",
|
|
1186
|
+
"system",
|
|
1187
|
+
"code",
|
|
1188
|
+
"development",
|
|
1189
|
+
"application",
|
|
1190
|
+
"function",
|
|
1191
|
+
"method",
|
|
1192
|
+
"problem",
|
|
1193
|
+
"solution"
|
|
1194
|
+
]);
|
|
1195
|
+
if (commonTerms.has(term.toLowerCase())) {
|
|
1196
|
+
return Math.log(1e3 / 500);
|
|
1197
|
+
}
|
|
1198
|
+
return Math.log(1e3 / 10);
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* 提取关键词
|
|
1202
|
+
*/
|
|
1203
|
+
extractKeywords(content, maxKeywords = 10) {
|
|
1204
|
+
const tf = this.calculateTermFrequency(content);
|
|
1205
|
+
const keywordScores = /* @__PURE__ */ new Map();
|
|
1206
|
+
for (const [term, frequency] of tf) {
|
|
1207
|
+
const idf = this.calculateInverseDocumentFrequency(term);
|
|
1208
|
+
const tfidf = frequency * idf;
|
|
1209
|
+
let bonus = 1;
|
|
1210
|
+
if (content.toLowerCase().includes(term.toLowerCase()) && (content.match(new RegExp(`^${term}`, "mi")) || content.match(new RegExp(`${term}$`, "mi")))) {
|
|
1211
|
+
bonus *= 1.5;
|
|
1212
|
+
}
|
|
1213
|
+
if (term.length >= 2 && term.length <= 6) {
|
|
1214
|
+
bonus *= 1.2;
|
|
1215
|
+
}
|
|
1216
|
+
keywordScores.set(term, tfidf * bonus);
|
|
1217
|
+
}
|
|
1218
|
+
return Array.from(keywordScores.entries()).sort((a, b) => b[1] - a[1]).slice(0, maxKeywords).map(([term]) => term);
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
function extractDescription(content, maxLength = 160) {
|
|
1222
|
+
const plainText = content.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
1223
|
+
if (plainText.length <= maxLength) return plainText;
|
|
1224
|
+
const truncated = plainText.substring(0, maxLength);
|
|
1225
|
+
const lastSentenceEnd = Math.max(
|
|
1226
|
+
truncated.lastIndexOf("\u3002"),
|
|
1227
|
+
truncated.lastIndexOf("\uFF01"),
|
|
1228
|
+
truncated.lastIndexOf("\uFF1F"),
|
|
1229
|
+
truncated.lastIndexOf("."),
|
|
1230
|
+
truncated.lastIndexOf("!"),
|
|
1231
|
+
truncated.lastIndexOf("?")
|
|
1232
|
+
);
|
|
1233
|
+
if (lastSentenceEnd > maxLength * 0.7) {
|
|
1234
|
+
return truncated.substring(0, lastSentenceEnd + 1);
|
|
1235
|
+
}
|
|
1236
|
+
return truncated + "...";
|
|
1237
|
+
}
|
|
1238
|
+
function extractKeywords(content) {
|
|
1239
|
+
const tfidfExtractor = new TFIDFKeywordExtractor();
|
|
1240
|
+
return tfidfExtractor.extractKeywords(content, 8);
|
|
1241
|
+
}
|
|
1242
|
+
function generateArticleJsonLd(doc, config, pageUrl, content) {
|
|
1243
|
+
const title = doc.Properties?.title || "\u672A\u547D\u540D\u6587\u6863";
|
|
1244
|
+
const description = extractDescription(content);
|
|
1245
|
+
const keywords = extractKeywords(content);
|
|
1246
|
+
const datePublished = extractDateFromId(doc.ID);
|
|
1247
|
+
const dateModified = formatDate(doc.Properties?.updated);
|
|
1248
|
+
const jsonLd = {
|
|
1249
|
+
"@context": "https://schema.org",
|
|
1250
|
+
"@type": "Article",
|
|
1251
|
+
headline: title,
|
|
1252
|
+
description,
|
|
1253
|
+
keywords: keywords.join(", "),
|
|
1254
|
+
datePublished,
|
|
1255
|
+
dateModified,
|
|
1256
|
+
author: {
|
|
1257
|
+
"@type": "Person",
|
|
1258
|
+
name: config.sitemap?.title || "\u5D2E\u751F",
|
|
1259
|
+
url: config.sitemap?.siteLink || ""
|
|
1260
|
+
},
|
|
1261
|
+
publisher: {
|
|
1262
|
+
"@type": "Organization",
|
|
1263
|
+
name: config.sitemap?.title || "OceanPress",
|
|
1264
|
+
logo: {
|
|
1265
|
+
"@type": "ImageObject",
|
|
1266
|
+
url: `${config.sitemap?.siteLink || ""}/assets/logo.png`
|
|
1267
|
+
}
|
|
1268
|
+
},
|
|
1269
|
+
mainEntityOfPage: {
|
|
1270
|
+
"@type": "WebPage",
|
|
1271
|
+
"@id": pageUrl
|
|
1272
|
+
},
|
|
1273
|
+
image: doc.Properties?.["title-img"] ? {
|
|
1274
|
+
"@type": "ImageObject",
|
|
1275
|
+
url: `${config.sitemap?.siteLink || ""}${doc.Properties["title-img"].replace("assets", "/assets")}`,
|
|
1276
|
+
width: 1200,
|
|
1277
|
+
height: 630
|
|
1278
|
+
} : void 0,
|
|
1279
|
+
wordCount: content.replace(/<[^>]*>/g, "").length,
|
|
1280
|
+
articleSection: "\u6280\u672F\u6587\u6863",
|
|
1281
|
+
inLanguage: "zh-CN"
|
|
1282
|
+
};
|
|
1283
|
+
return `<script type="application/ld+json">
|
|
1284
|
+
${JSON.stringify(jsonLd, null, 2)}
|
|
1285
|
+
</script>`;
|
|
1286
|
+
}
|
|
1287
|
+
function generateBreadcrumbJsonLd(breadcrumbs) {
|
|
1288
|
+
const itemListElement = breadcrumbs.map((crumb, index) => ({
|
|
1289
|
+
"@type": "ListItem",
|
|
1290
|
+
position: index + 1,
|
|
1291
|
+
name: crumb.name,
|
|
1292
|
+
item: crumb.url
|
|
1293
|
+
}));
|
|
1294
|
+
const jsonLd = {
|
|
1295
|
+
"@context": "https://schema.org",
|
|
1296
|
+
"@type": "BreadcrumbList",
|
|
1297
|
+
itemListElement
|
|
1298
|
+
};
|
|
1299
|
+
return `<script type="application/ld+json">
|
|
1300
|
+
${JSON.stringify(jsonLd, null, 2)}
|
|
1301
|
+
</script>`;
|
|
1302
|
+
}
|
|
1303
|
+
function generateSeoMetaTags(doc, config, pageUrl, content) {
|
|
1304
|
+
const title = doc.Properties?.title || "\u672A\u547D\u540D\u6587\u6863";
|
|
1305
|
+
const description = extractDescription(content);
|
|
1306
|
+
const keywords = extractKeywords(content);
|
|
1307
|
+
let metaTags = "";
|
|
1308
|
+
metaTags += ` <meta name="description" content="${description.replace(/"/g, """)}" />
|
|
1309
|
+
`;
|
|
1310
|
+
metaTags += ` <meta name="keywords" content="${keywords.join(", ")}" />
|
|
1311
|
+
`;
|
|
1312
|
+
metaTags += ` <meta name="author" content="${config.sitemap?.title || "\u5D2E\u751F"}" />
|
|
1313
|
+
`;
|
|
1314
|
+
metaTags += ` <meta property="og:title" content="${title.replace(/"/g, """)}" />
|
|
1315
|
+
`;
|
|
1316
|
+
metaTags += ` <meta property="og:description" content="${description.replace(/"/g, """)}" />
|
|
1317
|
+
`;
|
|
1318
|
+
metaTags += ` <meta property="og:type" content="article" />
|
|
1319
|
+
`;
|
|
1320
|
+
metaTags += ` <meta property="og:url" content="${pageUrl}" />
|
|
1321
|
+
`;
|
|
1322
|
+
metaTags += ` <meta property="og:site_name" content="${config.sitemap?.title || "OceanPress"}" />
|
|
1323
|
+
`;
|
|
1324
|
+
if (doc.Properties?.["title-img"]) {
|
|
1325
|
+
const imageUrl = `${config.sitemap?.siteLink || ""}${doc.Properties["title-img"].replace("assets", "/assets")}`;
|
|
1326
|
+
metaTags += ` <meta property="og:image" content="${imageUrl}" />
|
|
1327
|
+
`;
|
|
1328
|
+
metaTags += ` <meta property="og:image:width" content="1200" />
|
|
1329
|
+
`;
|
|
1330
|
+
metaTags += ` <meta property="og:image:height" content="630" />
|
|
1331
|
+
`;
|
|
1332
|
+
}
|
|
1333
|
+
const datePublished = extractDateFromId(doc.ID);
|
|
1334
|
+
const dateModified = formatDate(doc.Properties?.updated);
|
|
1335
|
+
metaTags += ` <meta property="article:published_time" content="${datePublished}" />
|
|
1336
|
+
`;
|
|
1337
|
+
metaTags += ` <meta property="article:modified_time" content="${dateModified}" />
|
|
1338
|
+
`;
|
|
1339
|
+
metaTags += ` <meta name="twitter:card" content="summary_large_image" />
|
|
1340
|
+
`;
|
|
1341
|
+
metaTags += ` <meta name="twitter:title" content="${title.replace(/"/g, """)}" />
|
|
1342
|
+
`;
|
|
1343
|
+
metaTags += ` <meta name="twitter:description" content="${description.replace(/"/g, """)}" />
|
|
1344
|
+
`;
|
|
1345
|
+
if (doc.Properties?.["title-img"]) {
|
|
1346
|
+
const imageUrl = `${config.sitemap?.siteLink || ""}${doc.Properties["title-img"].replace("assets", "/assets")}`;
|
|
1347
|
+
metaTags += ` <meta name="twitter:image" content="${imageUrl}" />
|
|
1348
|
+
`;
|
|
1349
|
+
}
|
|
1350
|
+
metaTags += ` <meta name="robots" content="index, follow" />
|
|
1351
|
+
`;
|
|
1352
|
+
metaTags += ` <link rel="canonical" href="${pageUrl}" />
|
|
1353
|
+
`;
|
|
1354
|
+
return metaTags;
|
|
1355
|
+
}
|
|
1356
|
+
function generateSeoContent(data) {
|
|
1357
|
+
const metaTags = generateSeoMetaTags(data.doc, data.config, data.pageUrl, data.content);
|
|
1358
|
+
let jsonLd = generateArticleJsonLd(data.doc, data.config, data.pageUrl, data.content);
|
|
1359
|
+
if (data.breadcrumbs && data.breadcrumbs.length > 0) {
|
|
1360
|
+
jsonLd += "\n" + generateBreadcrumbJsonLd(data.breadcrumbs);
|
|
1361
|
+
}
|
|
1362
|
+
return {
|
|
1363
|
+
metaTags,
|
|
1364
|
+
jsonLd
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
// src/core/htmlTemplate.ts
|
|
1369
|
+
async function htmlTemplate(p, config) {
|
|
1370
|
+
let prePath = "";
|
|
1371
|
+
if (config?.siyuanPrefix) {
|
|
1372
|
+
prePath = config.siyuanPrefix;
|
|
1373
|
+
} else {
|
|
1374
|
+
for (let i = 0; i < p.level; i++) {
|
|
1375
|
+
prePath += "../";
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
const version2 = "2.10.5";
|
|
1379
|
+
return `<!DOCTYPE html>
|
|
1380
|
+
<html lang="zh_CN" data-theme-mode="light" data-light-theme="daylight" data-dark-theme="midnight">
|
|
1381
|
+
<head>
|
|
1382
|
+
${config?.embedCode?.head ?? ""}
|
|
1383
|
+
<meta charset="utf-8" />
|
|
1384
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
1385
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
|
|
1386
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
1387
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
1388
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
|
1389
|
+
${p.seoData ? generateSeoContent({
|
|
1390
|
+
doc: p.seoData.doc,
|
|
1391
|
+
config: p.seoData.config,
|
|
1392
|
+
pageUrl: p.seoData.pageUrl,
|
|
1393
|
+
content: p.htmlContent,
|
|
1394
|
+
breadcrumbs: p.seoData.breadcrumbs
|
|
1395
|
+
}).metaTags : ""}
|
|
1396
|
+
<link rel="stylesheet" type="text/css" id="baseStyle" href="${prePath}stage/build/export/base.css?${version2}"/>
|
|
1397
|
+
<script>
|
|
1398
|
+
// \u66F4\u597D\u7684\u4E3B\u9898\u5207\u6362\u65B9\u6848
|
|
1399
|
+
(function() {
|
|
1400
|
+
// \u4E3B\u9898\u914D\u7F6E
|
|
1401
|
+
const themes = {
|
|
1402
|
+
light: {
|
|
1403
|
+
name: 'daylight',
|
|
1404
|
+
mode: 'light',
|
|
1405
|
+
icon: '\u2600\uFE0F'
|
|
1406
|
+
},
|
|
1407
|
+
dark: {
|
|
1408
|
+
name: 'midnight',
|
|
1409
|
+
mode: 'dark',
|
|
1410
|
+
icon: '\u{1F319}'
|
|
1411
|
+
},
|
|
1412
|
+
auto: {
|
|
1413
|
+
name: 'auto',
|
|
1414
|
+
mode: 'auto',
|
|
1415
|
+
icon: '\u{1F317}',
|
|
1416
|
+
getTheme: function() {
|
|
1417
|
+
const currentHour = new Date().getHours();
|
|
1418
|
+
return currentHour >= 18 || currentHour < 6 ? 'dark' : 'light';
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
|
|
1423
|
+
// \u83B7\u53D6\u5F53\u524D\u4E3B\u9898\u8BBE\u7F6E
|
|
1424
|
+
function getCurrentTheme() {
|
|
1425
|
+
// \u4F18\u5148\u7EA7\uFF1AlocalStorage > \u7CFB\u7EDF\u504F\u597D > auto
|
|
1426
|
+
const savedTheme = localStorage.getItem('oceanpress-theme');
|
|
1427
|
+
if (savedTheme && themes[savedTheme]) {
|
|
1428
|
+
return savedTheme;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// \u68C0\u6D4B\u7CFB\u7EDF\u504F\u597D
|
|
1432
|
+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
1433
|
+
return 'dark';
|
|
1434
|
+
}
|
|
1435
|
+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
|
|
1436
|
+
return 'light';
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
return 'auto';
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// \u5E94\u7528\u4E3B\u9898
|
|
1443
|
+
function applyTheme(themeName) {
|
|
1444
|
+
const theme = themes[themeName] || themes.auto;
|
|
1445
|
+
const actualTheme = themeName === 'auto' ? theme.getTheme() : themeName;
|
|
1446
|
+
const themeConfig = themes[actualTheme];
|
|
1447
|
+
|
|
1448
|
+
// \u8BBE\u7F6E CSS \u53D8\u91CF
|
|
1449
|
+
document.documentElement.setAttribute('data-theme-mode', themeConfig.mode);
|
|
1450
|
+
document.documentElement.setAttribute('data-light-theme', 'daylight');
|
|
1451
|
+
document.documentElement.setAttribute('data-dark-theme', 'midnight');
|
|
1452
|
+
|
|
1453
|
+
// \u52A0\u8F7D\u5BF9\u5E94\u7684 CSS
|
|
1454
|
+
const themeStyle = document.getElementById('themeDefaultStyle');
|
|
1455
|
+
if (themeStyle) {
|
|
1456
|
+
themeStyle.href = '${prePath}appearance/themes/' + themeConfig.name + '/theme.css?${version2}';
|
|
1457
|
+
} else {
|
|
1458
|
+
// \u5982\u679C\u5143\u7D20\u4E0D\u5B58\u5728\uFF0C\u521B\u5EFA\u5B83
|
|
1459
|
+
const link = document.createElement('link');
|
|
1460
|
+
link.id = 'themeDefaultStyle';
|
|
1461
|
+
link.rel = 'stylesheet';
|
|
1462
|
+
link.type = 'text/css';
|
|
1463
|
+
link.href = '${prePath}appearance/themes/' + themeConfig.name + '/theme.css?${version2}';
|
|
1464
|
+
document.head.appendChild(link);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// \u66F4\u65B0\u4E3B\u9898\u5207\u6362\u6309\u94AE
|
|
1468
|
+
updateThemeToggle(themeName);
|
|
1469
|
+
|
|
1470
|
+
// \u4FDD\u5B58\u5230 localStorage
|
|
1471
|
+
localStorage.setItem('oceanpress-theme', themeName);
|
|
1472
|
+
|
|
1473
|
+
// \u89E6\u53D1\u81EA\u5B9A\u4E49\u4E8B\u4EF6
|
|
1474
|
+
window.dispatchEvent(new CustomEvent('oceanpress-theme-changed', {
|
|
1475
|
+
detail: { theme: themeName, actualTheme: actualTheme }
|
|
1476
|
+
}));
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// \u521B\u5EFA\u4E3B\u9898\u5207\u6362\u6309\u94AE
|
|
1480
|
+
function createThemeToggle() {
|
|
1481
|
+
const toggle = document.createElement('div');
|
|
1482
|
+
toggle.id = 'oceanpress-theme-toggle';
|
|
1483
|
+
toggle.innerHTML = '<span class="theme-icon">\u{1F317}</span><span class="theme-text">\u81EA\u52A8</span>';
|
|
1484
|
+
toggle.addEventListener('click', toggleTheme);
|
|
1485
|
+
(document.querySelector('[data-type="NodeDocument"]')||document.body).appendChild(toggle);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// \u66F4\u65B0\u4E3B\u9898\u5207\u6362\u6309\u94AE
|
|
1489
|
+
function updateThemeToggle(themeName) {
|
|
1490
|
+
const toggle = document.getElementById('oceanpress-theme-toggle');
|
|
1491
|
+
if (!toggle) return;
|
|
1492
|
+
|
|
1493
|
+
const theme = themes[themeName] || themes.auto;
|
|
1494
|
+
toggle.innerHTML = '<span class="theme-icon">' + theme.icon + '</span><span class="theme-text">' +
|
|
1495
|
+
(themeName === 'auto' ? '\u81EA\u52A8' : (themeName === 'dark' ? '\u6DF1\u8272' : '\u6D45\u8272')) + '</span>';
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// \u5207\u6362\u4E3B\u9898
|
|
1499
|
+
function toggleTheme() {
|
|
1500
|
+
const currentTheme = getCurrentTheme();
|
|
1501
|
+
const themeOrder = ['auto', 'light', 'dark'];
|
|
1502
|
+
const currentIndex = themeOrder.indexOf(currentTheme);
|
|
1503
|
+
const nextIndex = (currentIndex + 1) % themeOrder.length;
|
|
1504
|
+
const nextTheme = themeOrder[nextIndex];
|
|
1505
|
+
|
|
1506
|
+
applyTheme(nextTheme);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// \u76D1\u542C\u7CFB\u7EDF\u4E3B\u9898\u53D8\u5316
|
|
1510
|
+
if (window.matchMedia) {
|
|
1511
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
|
1512
|
+
const currentTheme = getCurrentTheme();
|
|
1513
|
+
if (currentTheme === 'auto') {
|
|
1514
|
+
applyTheme('auto');
|
|
1515
|
+
}
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// \u76D1\u542C\u81EA\u5B9A\u4E49\u4E3B\u9898\u53D8\u5316\u4E8B\u4EF6\uFF08\u7528\u4E8E\u4FA7\u8FB9\u680F\u7B49\u7EC4\u4EF6\uFF09
|
|
1520
|
+
window.addEventListener('oceanpress-theme-changed', function(e) {
|
|
1521
|
+
// \u8FD9\u91CC\u53EF\u4EE5\u6DFB\u52A0\u5176\u4ED6\u7EC4\u4EF6\u9700\u8981\u54CD\u5E94\u4E3B\u9898\u53D8\u5316\u7684\u903B\u8F91
|
|
1522
|
+
console.log('Theme changed to:', e.detail);
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
// \u521D\u59CB\u5316\u4E3B\u9898
|
|
1526
|
+
function initTheme() {
|
|
1527
|
+
const themeName = getCurrentTheme();
|
|
1528
|
+
const theme = themes[themeName] || themes.auto;
|
|
1529
|
+
const actualTheme = themeName === 'auto' ? theme.getTheme() : themeName;
|
|
1530
|
+
const themeConfig = themes[actualTheme];
|
|
1531
|
+
|
|
1532
|
+
// \u7ACB\u5373\u8BBE\u7F6E\u57FA\u7840\u4E3B\u9898\uFF0C\u907F\u514D\u95EA\u70C1
|
|
1533
|
+
document.documentElement.setAttribute('data-theme-mode', themeConfig.mode);
|
|
1534
|
+
document.documentElement.setAttribute('data-light-theme', 'daylight');
|
|
1535
|
+
document.documentElement.setAttribute('data-dark-theme', 'midnight');
|
|
1536
|
+
|
|
1537
|
+
// \u521B\u5EFA\u4E3B\u9898\u5207\u6362\u6309\u94AE
|
|
1538
|
+
createThemeToggle();
|
|
1539
|
+
|
|
1540
|
+
// \u5E94\u7528\u5B8C\u6574\u4E3B\u9898
|
|
1541
|
+
applyTheme(themeName);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
// \u9875\u9762\u52A0\u8F7D\u5B8C\u6210\u540E\u521D\u59CB\u5316
|
|
1545
|
+
if (document.readyState === 'loading') {
|
|
1546
|
+
document.addEventListener('DOMContentLoaded', initTheme);
|
|
1547
|
+
} else {
|
|
1548
|
+
initTheme();
|
|
1549
|
+
}
|
|
1550
|
+
})();
|
|
1551
|
+
</script>
|
|
1552
|
+
<link rel="stylesheet" type="text/css" href="${prePath}appearance/oceanpress.css"/>
|
|
1553
|
+
<title>${p.title}</title>
|
|
1554
|
+
${p.seoData ? generateSeoContent({
|
|
1555
|
+
doc: p.seoData.doc,
|
|
1556
|
+
config: p.seoData.config,
|
|
1557
|
+
pageUrl: p.seoData.pageUrl,
|
|
1558
|
+
content: p.htmlContent,
|
|
1559
|
+
breadcrumbs: p.seoData.breadcrumbs
|
|
1560
|
+
}).jsonLd : ""}
|
|
1561
|
+
</head>
|
|
1562
|
+
<body>
|
|
1563
|
+
${config?.embedCode?.beforeBody ?? ""}
|
|
1564
|
+
${p.htmlContent}
|
|
1565
|
+
<script src="${prePath}appearance/icons/material/icon.js?${version2}"></script>
|
|
1566
|
+
<script src="${prePath}stage/build/export/protyle-method.js?${version2}"></script>
|
|
1567
|
+
<script src="${prePath}stage/protyle/js/lute/lute.min.js?${version2}"></script>
|
|
1568
|
+
<script>
|
|
1569
|
+
window.siyuan = {
|
|
1570
|
+
config: {
|
|
1571
|
+
appearance: {
|
|
1572
|
+
mode: document.documentElement.getAttribute('data-theme-mode') === 'dark' ? 1 : 0,//\u4E3B\u9898 \u660E\u4EAE=0 \u6697\u9ED1=1
|
|
1573
|
+
codeBlockThemeDark: "base16/dracula",
|
|
1574
|
+
codeBlockThemeLight: "github",
|
|
1575
|
+
},
|
|
1576
|
+
editor: {
|
|
1577
|
+
codeLineWrap: true,
|
|
1578
|
+
codeLigatures: false,
|
|
1579
|
+
plantUMLServePath: "https://www.plantuml.com/plantuml/svg/~1",
|
|
1580
|
+
codeSyntaxHighlightLineNum: true,
|
|
1581
|
+
katexMacros: JSON.stringify({}),
|
|
1582
|
+
},
|
|
1583
|
+
},
|
|
1584
|
+
languages: { copy: "\u590D\u5236" },
|
|
1585
|
+
};
|
|
1586
|
+
const cdn = "${prePath}stage/protyle";
|
|
1587
|
+
const previewElement = document.getElementById("preview");
|
|
1588
|
+
|
|
1589
|
+
Protyle.highlightRender(previewElement, cdn);
|
|
1590
|
+
Protyle.mathRender(previewElement, cdn, false);
|
|
1591
|
+
Protyle.mermaidRender(previewElement, cdn);
|
|
1592
|
+
Protyle.flowchartRender(previewElement, cdn);
|
|
1593
|
+
Protyle.graphvizRender(previewElement, cdn);
|
|
1594
|
+
Protyle.chartRender(previewElement, cdn);
|
|
1595
|
+
Protyle.mindmapRender(previewElement, cdn);
|
|
1596
|
+
Protyle.abcRender(previewElement, cdn);
|
|
1597
|
+
Protyle.htmlRender(previewElement);
|
|
1598
|
+
Protyle.plantumlRender(previewElement, cdn);
|
|
1599
|
+
document.querySelectorAll(".protyle-action__copy").forEach((item) => {
|
|
1600
|
+
item.addEventListener("click", (event) => {
|
|
1601
|
+
navigator.clipboard.writeText(
|
|
1602
|
+
item.parentElement.nextElementSibling.textContent.trimEnd(),
|
|
1603
|
+
);
|
|
1604
|
+
event.preventDefault();
|
|
1605
|
+
event.stopPropagation();
|
|
1606
|
+
});
|
|
1607
|
+
});
|
|
1608
|
+
</script>
|
|
1609
|
+
${config?.embedCode?.afterBody ?? ""}
|
|
1610
|
+
</body>
|
|
1611
|
+
</html>`;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// src/core/render.ts
|
|
1615
|
+
import { Context as Context2, Effect as Effect3 } from "effect";
|
|
1616
|
+
|
|
1617
|
+
// src/util/escaping.ts
|
|
1618
|
+
function escaping(s) {
|
|
1619
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "&apos");
|
|
1620
|
+
}
|
|
1621
|
+
function unescaping(s) {
|
|
1622
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&#(\d+);/g, (_sub, code) => {
|
|
1623
|
+
return String.fromCharCode(Number(code));
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
// src/core/renderDocTree.ts
|
|
1628
|
+
import { Effect as Effect2 } from "effect";
|
|
1629
|
+
var renderDocTreeJsPath = `/__oceanpress/docTree.js`;
|
|
1630
|
+
function renderDocTree() {
|
|
1631
|
+
return Effect2.gen(function* () {
|
|
1632
|
+
const config = yield* EffectConfigDep;
|
|
1633
|
+
const Doc_blocks = yield* Effect2.tryPromise(
|
|
1634
|
+
() => allDocBlock_by_bookId(config.notebook.id)
|
|
1635
|
+
);
|
|
1636
|
+
const sortJSON = yield* Effect2.tryPromise(
|
|
1637
|
+
() => API.file_getFile({
|
|
1638
|
+
path: `/data/${config.notebook.id}/.siyuan/sort.json`
|
|
1639
|
+
}).then((r) => {
|
|
1640
|
+
const decoder = new TextDecoder("utf-8");
|
|
1641
|
+
const jsonString = decoder.decode(r);
|
|
1642
|
+
return JSON.parse(jsonString);
|
|
1643
|
+
})
|
|
1644
|
+
);
|
|
1645
|
+
const docs = Doc_blocks.map((el) => ({
|
|
1646
|
+
id: el.id,
|
|
1647
|
+
/** 类似 '/record/cssFlex' */
|
|
1648
|
+
hpath: el.hpath,
|
|
1649
|
+
title: el.content,
|
|
1650
|
+
sort: sortJSON[el.id]
|
|
1651
|
+
}));
|
|
1652
|
+
const tree = buildTree(docs);
|
|
1653
|
+
const jsCode = generateJSTree(tree);
|
|
1654
|
+
return `
|
|
1655
|
+
// OceanPress DocTree - \u52A8\u6001\u52A0\u8F7D\u7684\u6587\u6863\u6811
|
|
1656
|
+
(function() {
|
|
1657
|
+
'use strict';
|
|
1658
|
+
|
|
1659
|
+
// \u6587\u6863\u6811\u6570\u636E
|
|
1660
|
+
const docTreeData = ${jsCode};
|
|
1661
|
+
|
|
1662
|
+
// \u6E32\u67D3\u51FD\u6570
|
|
1663
|
+
function renderDocTree(containerId, options = {}) {
|
|
1664
|
+
const container = document.getElementById(containerId);
|
|
1665
|
+
if (!container) {
|
|
1666
|
+
console.error('Container not found:', containerId);
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
const currentPath = options.currentPath || window.location.pathname.replace(/\\.html$/, '');
|
|
1671
|
+
|
|
1672
|
+
// \u751F\u6210 HTML
|
|
1673
|
+
const html = generateHTMLTree(docTreeData, currentPath);
|
|
1674
|
+
container.innerHTML = html;
|
|
1675
|
+
|
|
1676
|
+
// \u52A0\u8F7D\u6837\u5F0F
|
|
1677
|
+
loadStyles();
|
|
1678
|
+
|
|
1679
|
+
// \u521D\u59CB\u5316\u4EA4\u4E92
|
|
1680
|
+
initInteractions(container, currentPath);
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
// \u68C0\u67E5\u662F\u5426\u4E3A\u5355\u4E00\u8DEF\u5F84\uFF08\u53EA\u6709\u5355\u4E2A\u5B50\u8282\u70B9\u7684\u6587\u4EF6\u5939\uFF09
|
|
1684
|
+
function isSinglePath(node) {
|
|
1685
|
+
if (!node.children || node.children.length !== 1) {
|
|
1686
|
+
return false;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
let current = node;
|
|
1690
|
+
while (current.children && current.children.length === 1) {
|
|
1691
|
+
current = current.children[0];
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// \u5982\u679C\u6700\u7EC8\u8282\u70B9\u6CA1\u6709\u5B50\u8282\u70B9\uFF0C\u8BF4\u660E\u662F\u5355\u4E00\u8DEF\u5F84
|
|
1695
|
+
return !current.children || current.children.length === 0;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
// \u83B7\u53D6\u5355\u4E00\u8DEF\u5F84\u7684\u6240\u6709\u8282\u70B9
|
|
1699
|
+
function getSinglePathNodes(node) {
|
|
1700
|
+
const pathNodes = [node];
|
|
1701
|
+
let current = node;
|
|
1702
|
+
|
|
1703
|
+
while (current.children && current.children.length === 1) {
|
|
1704
|
+
current = current.children[0];
|
|
1705
|
+
pathNodes.push(current);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
return pathNodes;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// \u751F\u6210\u9762\u5305\u5C51\u8DEF\u5F84
|
|
1712
|
+
function generateBreadcrumb(pathNodes, currentPath) {
|
|
1713
|
+
const lastNode = pathNodes[pathNodes.length - 1];
|
|
1714
|
+
const isCurrent = lastNode.hpath === currentPath;
|
|
1715
|
+
|
|
1716
|
+
let breadcrumbHtml = '<div class="breadcrumb-path">';
|
|
1717
|
+
|
|
1718
|
+
pathNodes.forEach((node, index) => {
|
|
1719
|
+
if (index > 0) {
|
|
1720
|
+
breadcrumbHtml += '<span class="breadcrumb-separator">/</span>';
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
const isLast = index === pathNodes.length - 1;
|
|
1724
|
+
const nodeCurrent = node.hpath === currentPath;
|
|
1725
|
+
|
|
1726
|
+
breadcrumbHtml += \`
|
|
1727
|
+
<a href="\${node.hpath}.html" class="breadcrumb-part \${(isLast && isCurrent) ? 'current' : ''}" target="_top">\${node.title}</a>
|
|
1728
|
+
\`;
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
breadcrumbHtml += '</div>';
|
|
1732
|
+
return breadcrumbHtml;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// \u751F\u6210 HTML \u6811
|
|
1736
|
+
function generateHTMLTree(nodes, currentPath, level = 0) {
|
|
1737
|
+
let html = '';
|
|
1738
|
+
for (const node of nodes) {
|
|
1739
|
+
const isCurrent = node.hpath === currentPath;
|
|
1740
|
+
const isActive = isCurrent || (currentPath && node.hpath && currentPath.startsWith(node.hpath));
|
|
1741
|
+
|
|
1742
|
+
// \u68C0\u67E5\u662F\u5426\u4E3A\u5355\u4E00\u8DEF\u5F84\uFF0C\u5982\u679C\u662F\u5219\u663E\u793A\u4E3A\u9762\u5305\u5C51
|
|
1743
|
+
if (isSinglePath(node)) {
|
|
1744
|
+
const pathNodes = getSinglePathNodes(node);
|
|
1745
|
+
html += generateBreadcrumb(pathNodes, currentPath);
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
if (node.children && node.children.length > 0) {
|
|
1750
|
+
// \u6709\u5B50\u8282\u70B9\u65F6\u4F7F\u7528 details/summary
|
|
1751
|
+
const isExpanded = isActive ? 'open' : '';
|
|
1752
|
+
html += \`
|
|
1753
|
+
<details class="folder" \${isExpanded}>
|
|
1754
|
+
<summary class="folder-summary">
|
|
1755
|
+
<a href="\${node.hpath}.html" class="folder-link \${isCurrent ? 'current' : ''}" target="_top">\${node.title}</a>
|
|
1756
|
+
</summary>
|
|
1757
|
+
<div class="folder-children" style="padding:0 0 0 10px;">
|
|
1758
|
+
\${generateHTMLTree(node.children, currentPath, level + 1)}
|
|
1759
|
+
</div>
|
|
1760
|
+
</details>
|
|
1761
|
+
\`;
|
|
1762
|
+
} else {
|
|
1763
|
+
// \u6CA1\u6709\u5B50\u8282\u70B9\u7684\u666E\u901A\u9879\u76EE
|
|
1764
|
+
html += \`
|
|
1765
|
+
<div class="file \${isCurrent ? 'current' : ''}">
|
|
1766
|
+
<a href="\${node.hpath}.html" class="file-link" target="_top">\${node.title}</a>
|
|
1767
|
+
</div>
|
|
1768
|
+
\`;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
return html;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
// \u52A0\u8F7D\u6837\u5F0F
|
|
1775
|
+
function loadStyles() {
|
|
1776
|
+
if (document.getElementById('oceanpress-doctree-styles')) return;
|
|
1777
|
+
|
|
1778
|
+
const link = document.createElement('link');
|
|
1779
|
+
link.id = 'oceanpress-doctree-styles';
|
|
1780
|
+
link.rel = 'stylesheet';
|
|
1781
|
+
link.type = 'text/css';
|
|
1782
|
+
link.href = '${tempConfig.cdn.siyuanPrefix}appearance/docTree.css';
|
|
1783
|
+
document.head.appendChild(link);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
// \u521D\u59CB\u5316\u4EA4\u4E92
|
|
1787
|
+
function initInteractions(container, currentPath) {
|
|
1788
|
+
// \u4E3A\u5F53\u524D\u9875\u9762\u9879\u6DFB\u52A0\u9AD8\u4EAE\u6837\u5F0F
|
|
1789
|
+
const currentItems = container.querySelectorAll('.current');
|
|
1790
|
+
currentItems.forEach(item => {
|
|
1791
|
+
// \u5982\u679C\u662F\u9762\u5305\u5C51\u8DEF\u5F84\u4E2D\u7684\u5F53\u524D\u9879\uFF0C\u9700\u8981\u7279\u6B8A\u5904\u7406
|
|
1792
|
+
if (item.classList.contains('breadcrumb-part')) {
|
|
1793
|
+
item.style.fontWeight = 'bold';
|
|
1794
|
+
item.style.color = 'var(--oceanpress-sidebar-current-border)';
|
|
1795
|
+
} else {
|
|
1796
|
+
// \u4F7F\u7528 CSS \u53D8\u91CF\u800C\u4E0D\u662F\u786C\u7F16\u7801\u989C\u8272
|
|
1797
|
+
item.style.backgroundColor = 'var(--oceanpress-sidebar-current-bg)';
|
|
1798
|
+
item.style.borderLeft = '3px solid var(--oceanpress-sidebar-current-border)';
|
|
1799
|
+
item.style.paddingLeft = '7px';
|
|
1800
|
+
}
|
|
1801
|
+
});
|
|
1802
|
+
|
|
1803
|
+
// \u786E\u4FDD\u5F53\u524D\u9875\u9762\u7684\u6240\u6709\u7236\u6587\u4EF6\u5939\u90FD\u5C55\u5F00
|
|
1804
|
+
const currentElements = container.querySelectorAll('.current');
|
|
1805
|
+
currentElements.forEach(currentElement => {
|
|
1806
|
+
// \u5411\u4E0A\u904D\u5386\u6240\u6709\u7236\u7EA7 details \u5143\u7D20\u5E76\u5C55\u5F00
|
|
1807
|
+
let parent = currentElement.parentElement;
|
|
1808
|
+
while (parent) {
|
|
1809
|
+
if (parent.tagName === 'DETAILS') {
|
|
1810
|
+
parent.setAttribute('open', 'open');
|
|
1811
|
+
}
|
|
1812
|
+
parent = parent.parentElement;
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
|
|
1816
|
+
// \u81EA\u52A8\u6EDA\u52A8\u5230\u5F53\u524D\u9875\u9762
|
|
1817
|
+
const firstCurrent = container.querySelector('.current');
|
|
1818
|
+
if (firstCurrent) {
|
|
1819
|
+
setTimeout(() => {
|
|
1820
|
+
firstCurrent.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
1821
|
+
}, 100);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// \u76D1\u542C\u4E3B\u9898\u53D8\u5316\u4E8B\u4EF6\uFF0C\u66F4\u65B0\u9AD8\u4EAE\u6837\u5F0F
|
|
1825
|
+
window.addEventListener('oceanpress-theme-changed', function(e) {
|
|
1826
|
+
currentItems.forEach(item => {
|
|
1827
|
+
if (item.classList.contains('breadcrumb-part')) {
|
|
1828
|
+
item.style.fontWeight = 'bold';
|
|
1829
|
+
item.style.color = 'var(--oceanpress-sidebar-current-border)';
|
|
1830
|
+
} else {
|
|
1831
|
+
item.style.backgroundColor = 'var(--oceanpress-sidebar-current-bg)';
|
|
1832
|
+
item.style.borderLeft = '3px solid var(--oceanpress-sidebar-current-border)';
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
// \u66B4\u9732\u5230\u5168\u5C40
|
|
1839
|
+
window.OceanPressDocTree = {
|
|
1840
|
+
render: renderDocTree,
|
|
1841
|
+
data: docTreeData
|
|
1842
|
+
};
|
|
1843
|
+
|
|
1844
|
+
// \u81EA\u52A8\u6E32\u67D3\uFF08\u5982\u679C\u5BB9\u5668\u5B58\u5728\uFF09
|
|
1845
|
+
if (document.readyState === 'loading') {
|
|
1846
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
1847
|
+
if (document.getElementById('oceanpress-doctree')) {
|
|
1848
|
+
renderDocTree('oceanpress-doctree');
|
|
1849
|
+
}
|
|
1850
|
+
});
|
|
1851
|
+
} else {
|
|
1852
|
+
if (document.getElementById('oceanpress-doctree')) {
|
|
1853
|
+
renderDocTree('oceanpress-doctree');
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
})();
|
|
1857
|
+
`;
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
function generateJSTree(nodes) {
|
|
1861
|
+
return JSON.stringify(nodes, null, 2);
|
|
1862
|
+
}
|
|
1863
|
+
function buildTree(docs) {
|
|
1864
|
+
const root = [];
|
|
1865
|
+
const pathMap = {};
|
|
1866
|
+
docs.sort((a, b) => a.hpath.localeCompare(b.hpath));
|
|
1867
|
+
for (const doc of docs) {
|
|
1868
|
+
const pathParts = doc.hpath.split("/").filter((part) => part !== "");
|
|
1869
|
+
let currentPath = "";
|
|
1870
|
+
let parentNode = void 0;
|
|
1871
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
1872
|
+
currentPath += "/" + pathParts[i];
|
|
1873
|
+
if (!pathMap[currentPath]) {
|
|
1874
|
+
pathMap[currentPath] = {
|
|
1875
|
+
id: "virtual_" + currentPath,
|
|
1876
|
+
hpath: currentPath,
|
|
1877
|
+
title: pathParts[i],
|
|
1878
|
+
sort: void 0,
|
|
1879
|
+
children: []
|
|
1880
|
+
};
|
|
1881
|
+
if (parentNode) {
|
|
1882
|
+
parentNode.children = parentNode.children || [];
|
|
1883
|
+
parentNode.children.push(pathMap[currentPath]);
|
|
1884
|
+
} else {
|
|
1885
|
+
root.push(pathMap[currentPath]);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
parentNode = pathMap[currentPath];
|
|
1889
|
+
}
|
|
1890
|
+
if (parentNode) {
|
|
1891
|
+
parentNode.children = parentNode.children || [];
|
|
1892
|
+
parentNode.children.push(doc);
|
|
1893
|
+
} else {
|
|
1894
|
+
root.push(doc);
|
|
1895
|
+
}
|
|
1896
|
+
pathMap[doc.hpath] = doc;
|
|
1897
|
+
}
|
|
1898
|
+
function sortNodes(nodes) {
|
|
1899
|
+
return nodes.map((node) => {
|
|
1900
|
+
if (node.children) {
|
|
1901
|
+
node.children = sortNodes(node.children);
|
|
1902
|
+
}
|
|
1903
|
+
return node;
|
|
1904
|
+
}).sort((a, b) => {
|
|
1905
|
+
if (a.sort !== void 0 && b.sort !== void 0) {
|
|
1906
|
+
return a.sort - b.sort;
|
|
1907
|
+
} else if (a.sort !== void 0) {
|
|
1908
|
+
return -1;
|
|
1909
|
+
} else if (b.sort !== void 0) {
|
|
1910
|
+
return 1;
|
|
1911
|
+
} else {
|
|
1912
|
+
return (a.title || "").localeCompare(b.title || "");
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
return sortNodes(root);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
// src/core/render.ts
|
|
1920
|
+
var renderHTML = (sy, render) => Effect3.gen(function* () {
|
|
1921
|
+
if (sy === void 0) return "";
|
|
1922
|
+
const defaultRender = yield* getRender;
|
|
1923
|
+
const renderInstance = render ?? defaultRender;
|
|
1924
|
+
const renderObj = {
|
|
1925
|
+
...renderInstance,
|
|
1926
|
+
nodeStack: [
|
|
1927
|
+
/** 避免让所有的 renderInstance.nodeStack 是同一个对象 ,所以这里复制一个新的 */
|
|
1928
|
+
...renderInstance.nodeStack
|
|
1929
|
+
]
|
|
1930
|
+
};
|
|
1931
|
+
if (renderInstance.nodeStack.find(
|
|
1932
|
+
(node) => node.ID && sy.ID && node.ID === sy.ID
|
|
1933
|
+
)) {
|
|
1934
|
+
return warnDiv(
|
|
1935
|
+
"\u5FAA\u73AF\u5F15\u7528",
|
|
1936
|
+
[...renderInstance.nodeStack, sy].map((el) => el.ID)
|
|
1937
|
+
);
|
|
1938
|
+
}
|
|
1939
|
+
if (renderObj[sy.Type] === void 0) {
|
|
1940
|
+
return warnDiv(
|
|
1941
|
+
`\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u7684\u6E32\u67D3\u65B9\u6CD5 ${sy.Type} ${renderObj.nodeStack[0].Properties?.title}`
|
|
1942
|
+
);
|
|
1943
|
+
} else {
|
|
1944
|
+
renderObj.nodeStack.push(sy);
|
|
1945
|
+
if (sy.ID && renderInstance.nodeStack[0]?.ID) {
|
|
1946
|
+
const storeDep = yield* EffectRender;
|
|
1947
|
+
const id = sy.ID;
|
|
1948
|
+
const targetDoc = yield* Effect3.tryPromise(
|
|
1949
|
+
() => storeDep.getDocByChildID(id)
|
|
1950
|
+
);
|
|
1951
|
+
const currentDoc = renderInstance.nodeStack[0];
|
|
1952
|
+
if (targetDoc?.ID !== void 0 && targetDoc.ID !== currentDoc.ID && currentDoc.ID) {
|
|
1953
|
+
renderObj.refs.add(targetDoc.ID);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
const r = yield* Effect3.tryPromise(() => renderObj[sy.Type](sy));
|
|
1957
|
+
renderObj.nodeStack.pop();
|
|
1958
|
+
return r;
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
function warnDiv(msg, ...args) {
|
|
1962
|
+
warn(msg, ...args);
|
|
1963
|
+
return `<div class="ft__smaller ft__secondary b3-form__space--small">${msg}</div>`;
|
|
1964
|
+
}
|
|
1965
|
+
function isRenderCode(sy) {
|
|
1966
|
+
const mark = atob(
|
|
1967
|
+
sy.CodeBlockInfo ?? sy.Children?.find((el) => el.Type === "NodeCodeBlockFenceInfoMarker")?.CodeBlockInfo ?? ""
|
|
1968
|
+
);
|
|
1969
|
+
return [
|
|
1970
|
+
[
|
|
1971
|
+
"mindmap",
|
|
1972
|
+
"mermaid",
|
|
1973
|
+
"echarts",
|
|
1974
|
+
"abc",
|
|
1975
|
+
"graphviz",
|
|
1976
|
+
"flowchart",
|
|
1977
|
+
"plantuml"
|
|
1978
|
+
].includes(mark),
|
|
1979
|
+
mark
|
|
1980
|
+
];
|
|
1981
|
+
}
|
|
1982
|
+
var html = String.raw;
|
|
1983
|
+
function strAttr(sy, config = {}) {
|
|
1984
|
+
if (config?.subtype_class === void 0) {
|
|
1985
|
+
config.subtype_class = (() => {
|
|
1986
|
+
const typ_subtype = sy.ListData?.Typ === 1 ? (
|
|
1987
|
+
/** 有序列表 */
|
|
1988
|
+
"o"
|
|
1989
|
+
) : sy.ListData?.Typ === 3 ? (
|
|
1990
|
+
/** 任务列表 */
|
|
1991
|
+
"t"
|
|
1992
|
+
) : (
|
|
1993
|
+
/** 无序列表 */
|
|
1994
|
+
"u"
|
|
1995
|
+
);
|
|
1996
|
+
if (sy.Type === "NodeDocument") return "";
|
|
1997
|
+
else if (sy.Type === "NodeHeading") return `h${sy.HeadingLevel}`;
|
|
1998
|
+
else if (sy.Type === "NodeList") return [typ_subtype, "list"];
|
|
1999
|
+
else if (sy.Type === "NodeListItem") return [typ_subtype, "li"];
|
|
2000
|
+
else if (sy.Type === "NodeParagraph") return ["", "p"];
|
|
2001
|
+
else if (sy.Type === "NodeImage") return ["", "img"];
|
|
2002
|
+
else if (sy.Type === "NodeBlockquote") return ["", "bq"];
|
|
2003
|
+
else if (sy.Type === "NodeSuperBlock") return ["", "sb"];
|
|
2004
|
+
else if (sy.Type === "NodeCodeBlock") {
|
|
2005
|
+
const [yes, mark] = isRenderCode(sy);
|
|
2006
|
+
if (yes) {
|
|
2007
|
+
return [mark, "render-node"];
|
|
2008
|
+
} else {
|
|
2009
|
+
return ["", "code-block"];
|
|
2010
|
+
}
|
|
2011
|
+
} else if (sy.Type === "NodeTable") return ["", "table"];
|
|
2012
|
+
else if (sy.Type === "NodeThematicBreak") return ["", "hr"];
|
|
2013
|
+
else if (sy.Type === "NodeMathBlock") return ["math", "render-node"];
|
|
2014
|
+
else if (sy.Type === "NodeIFrame") return ["", "iframe"];
|
|
2015
|
+
else if (sy.Type === "NodeVideo") return ["", "iframe"];
|
|
2016
|
+
else return "";
|
|
2017
|
+
})();
|
|
2018
|
+
}
|
|
2019
|
+
const attrObj = {};
|
|
2020
|
+
function addAttr(key, value) {
|
|
2021
|
+
attrObj[key] = value;
|
|
2022
|
+
}
|
|
2023
|
+
if (sy.ID) {
|
|
2024
|
+
addAttr("id", sy.ID);
|
|
2025
|
+
addAttr("data-node-id", sy.ID);
|
|
2026
|
+
}
|
|
2027
|
+
if (sy?.TextMarkType === "tag") {
|
|
2028
|
+
addAttr(`data-type`, sy.TextMarkType ?? "");
|
|
2029
|
+
} else {
|
|
2030
|
+
addAttr(`data-type`, config?.data_type ?? sy.Type);
|
|
2031
|
+
}
|
|
2032
|
+
if (sy.Properties?.updated) addAttr("updated", sy.Properties.updated);
|
|
2033
|
+
if (config?.subtype_class) {
|
|
2034
|
+
if (typeof config.subtype_class === "string") {
|
|
2035
|
+
addAttr("data-subtype", config.subtype_class);
|
|
2036
|
+
addAttr("class", config.subtype_class);
|
|
2037
|
+
} else {
|
|
2038
|
+
if (config.subtype_class[0] !== "")
|
|
2039
|
+
addAttr("data-subtype", config.subtype_class[0]);
|
|
2040
|
+
if (config.subtype_class[1] !== "")
|
|
2041
|
+
addAttr("class", config.subtype_class[1]);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
if (sy.Properties) {
|
|
2045
|
+
Object.entries(sy.Properties).forEach(([k, v]) => addAttr(k, v));
|
|
2046
|
+
}
|
|
2047
|
+
if (sy.ListData?.Marker) addAttr("data-marker", atob(sy.ListData.Marker));
|
|
2048
|
+
if (
|
|
2049
|
+
/** 任务列表 */
|
|
2050
|
+
sy.ListData?.Typ === 3 && /** 该项被选中 */
|
|
2051
|
+
sy.Children?.find(
|
|
2052
|
+
(el) => el.Type === "NodeTaskListItemMarker"
|
|
2053
|
+
)?.TaskListItemChecked
|
|
2054
|
+
) {
|
|
2055
|
+
attrObj["class"] = (attrObj["class"] ?? "") + " protyle-task--done ";
|
|
2056
|
+
}
|
|
2057
|
+
delete attrObj["fold"];
|
|
2058
|
+
if (sy.Type === "NodeDocument") delete attrObj["title"];
|
|
2059
|
+
return Object.entries(attrObj).map(([k, v]) => `${k}="${v}"`).join(" ");
|
|
2060
|
+
}
|
|
2061
|
+
var _emptyString = async (_sy) => "";
|
|
2062
|
+
var _dataString = async (sy) => sy.Data ?? "";
|
|
2063
|
+
var getRender = Effect3.gen(function* () {
|
|
2064
|
+
const render = yield* renderProgram;
|
|
2065
|
+
return {
|
|
2066
|
+
...render,
|
|
2067
|
+
nodeStack: [],
|
|
2068
|
+
refs: /* @__PURE__ */ new Set()
|
|
2069
|
+
};
|
|
2070
|
+
});
|
|
2071
|
+
var renderProgram = Effect3.gen(function* () {
|
|
2072
|
+
const storeDep = yield* EffectRender;
|
|
2073
|
+
const config = yield* EffectConfigDep;
|
|
2074
|
+
const context = Context2.empty().pipe(
|
|
2075
|
+
Context2.add(EffectRender, storeDep),
|
|
2076
|
+
Context2.add(EffectConfigDep, config)
|
|
2077
|
+
);
|
|
2078
|
+
async function callChildRender(sy, renderInstance) {
|
|
2079
|
+
const children = sy?.Children ?? [];
|
|
2080
|
+
const promises = children.map(
|
|
2081
|
+
(el) => Effect3.runPromise(
|
|
2082
|
+
Effect3.provide(renderHTML(el, renderInstance), context)
|
|
2083
|
+
)
|
|
2084
|
+
);
|
|
2085
|
+
const results = await Promise.all(promises);
|
|
2086
|
+
return results.join("");
|
|
2087
|
+
}
|
|
2088
|
+
async function callRenderHTML(sy, render2) {
|
|
2089
|
+
return Effect3.runPromise(Effect3.provide(renderHTML(sy, render2), context));
|
|
2090
|
+
}
|
|
2091
|
+
const render = {
|
|
2092
|
+
nodeStack: [],
|
|
2093
|
+
refs: /* @__PURE__ */ new Set(),
|
|
2094
|
+
async getTopPathPrefix() {
|
|
2095
|
+
const sy = this.nodeStack[0];
|
|
2096
|
+
let prefix = ".";
|
|
2097
|
+
if (sy.Type === "NodeDocument" && sy.ID) {
|
|
2098
|
+
const path = await storeDep.getDocPathBySY(sy);
|
|
2099
|
+
if (path) {
|
|
2100
|
+
const level = path.split("/").length - 3;
|
|
2101
|
+
for (let i = 0; i < level; i++) {
|
|
2102
|
+
prefix += "/..";
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
return prefix;
|
|
2106
|
+
} else {
|
|
2107
|
+
console.log("\u672A\u5B9A\u4E49\u9876\u5C42\u5143\u7D20\u975E NodeDocument \u65F6\u7684\u5904\u7406\u65B9\u5F0F", sy);
|
|
2108
|
+
return "";
|
|
2109
|
+
}
|
|
2110
|
+
},
|
|
2111
|
+
async NodeDocument(sy) {
|
|
2112
|
+
const isTopDoc = this.nodeStack.length === 1;
|
|
2113
|
+
let html2 = `<div style="min-height: 150px;" ${strAttr(sy)}>
|
|
2114
|
+
${/** 题头图 */
|
|
2115
|
+
isTopDoc && sy.Properties?.["title-img"] ? `<div class="protyle-background__img" style="margin-bottom: 30px;position: relative;height: 16vh;${sy.Properties?.["title-img"].replace(
|
|
2116
|
+
/assets/,
|
|
2117
|
+
// 修改为相对路径
|
|
2118
|
+
await this.getTopPathPrefix() + "/assets"
|
|
2119
|
+
)}"/>${sy.Properties?.["icon"] ? `<div style="position: absolute;bottom:-10px;left:15px;height: 80px;width: 80px;transition: var(--b3-transition);cursor: pointer;font-size: 68px;line-height: 80px;text-align: center;font-family: var(--b3-font-family-emoji);margin-right: 16px;"> &#x${sy.Properties?.["icon"]} </div>` : ""}</div>` : ""}
|
|
2120
|
+
${/** h1 文档标题 */
|
|
2121
|
+
isTopDoc ? `<h1 ${strAttr(sy)} data-type="NodeHeading" class="h1">${sy.Properties?.title}</h1>` : ""}
|
|
2122
|
+
${await callChildRender(sy, this)}</div>`;
|
|
2123
|
+
if (isTopDoc) {
|
|
2124
|
+
html2 = `<div class="protyle-wysiwyg protyle-wysiwyg--attr" id="preview">
|
|
2125
|
+
<div id="oceanpress-left-sidebar">
|
|
2126
|
+
${config.sidebarCode.leftCode}
|
|
2127
|
+
${config.sidebarCode.enableDocTree ? `<div id="oceanpress-doctree"></div>
|
|
2128
|
+
<script src="${await this.getTopPathPrefix()}${renderDocTreeJsPath}" async></script>` : ""}
|
|
2129
|
+
</div>
|
|
2130
|
+
${html2}
|
|
2131
|
+
<div id="oceanpress-right-sidebar">${config.sidebarCode.rightCode}</div>
|
|
2132
|
+
</div>`;
|
|
2133
|
+
}
|
|
2134
|
+
return html2;
|
|
2135
|
+
},
|
|
2136
|
+
async NodeHeading(sy) {
|
|
2137
|
+
const tagName = `h${sy.HeadingLevel}`;
|
|
2138
|
+
let html2 = `<${tagName} ${strAttr(sy)}>${await callChildRender(
|
|
2139
|
+
sy,
|
|
2140
|
+
this
|
|
2141
|
+
)}</${tagName}>`;
|
|
2142
|
+
const parentNode = this.nodeStack[
|
|
2143
|
+
this.nodeStack.length - 2
|
|
2144
|
+
/** 最后一个元素是 sy本身(NodeHeading)还得要往前一个,所以是2 */
|
|
2145
|
+
];
|
|
2146
|
+
if (parentNode?.Type === "NodeBlockQueryEmbedScript") {
|
|
2147
|
+
let afterFlag = false;
|
|
2148
|
+
for (const node of sy.Parent.Children ?? []) {
|
|
2149
|
+
if (node === sy) {
|
|
2150
|
+
afterFlag = true;
|
|
2151
|
+
} else if (node !== sy && node.Type === "NodeHeading") {
|
|
2152
|
+
afterFlag = false;
|
|
2153
|
+
} else if (afterFlag) {
|
|
2154
|
+
html2 += "\n" + await callRenderHTML(node, this);
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
return html2;
|
|
2159
|
+
},
|
|
2160
|
+
NodeText: _dataString,
|
|
2161
|
+
async NodeList(sy) {
|
|
2162
|
+
return html`<div ${strAttr(sy)}>${await callChildRender(sy, this)}</div>`;
|
|
2163
|
+
},
|
|
2164
|
+
async NodeListItem(sy) {
|
|
2165
|
+
return html`<div ${await strAttr(sy)}>
|
|
2166
|
+
<div class="protyle-action">
|
|
2167
|
+
${sy.ListData?.Typ === 1 ? (
|
|
2168
|
+
/** 有序列表 */
|
|
2169
|
+
atob(sy.ListData?.Marker ?? "")
|
|
2170
|
+
) : sy.ListData?.Typ === 3 ? (
|
|
2171
|
+
/** 任务列表 */
|
|
2172
|
+
`<svg><use xlink:href="#${sy.Children?.find(
|
|
2173
|
+
(el) => el.Type === "NodeTaskListItemMarker"
|
|
2174
|
+
)?.TaskListItemChecked ? "iconCheck" : "iconUncheck"}"></use></svg>`
|
|
2175
|
+
) : (
|
|
2176
|
+
/** 无序列表 */
|
|
2177
|
+
`<svg><use xlink:href="#iconDot"></use></svg>`
|
|
2178
|
+
)}
|
|
2179
|
+
</div>
|
|
2180
|
+
${await callChildRender(sy, this)}
|
|
2181
|
+
</div>`;
|
|
2182
|
+
},
|
|
2183
|
+
NodeTaskListItemMarker: _emptyString,
|
|
2184
|
+
async NodeParagraph(sy) {
|
|
2185
|
+
return `<div ${strAttr(
|
|
2186
|
+
sy
|
|
2187
|
+
)}><div spellcheck="false">${await callChildRender(sy, this)}</div></div>`;
|
|
2188
|
+
},
|
|
2189
|
+
async NodeTextMark(sy) {
|
|
2190
|
+
const that = this;
|
|
2191
|
+
let r = "";
|
|
2192
|
+
for (const type of (sy.TextMarkType?.split(" ") ?? []).reverse()) {
|
|
2193
|
+
if (r === "") {
|
|
2194
|
+
r = await TextMarkRender(sy, type, sy.TextMarkTextContent ?? "");
|
|
2195
|
+
} else {
|
|
2196
|
+
r = await TextMarkRender(sy, type, r);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
return r;
|
|
2200
|
+
async function TextMarkRender(sy2, type, content) {
|
|
2201
|
+
if (type === "inline-math") {
|
|
2202
|
+
return `<span data-type="inline-math" data-subtype="math" data-content="${sy2.TextMarkInlineMathContent}" class="render-node"></span>`;
|
|
2203
|
+
} else if (type === "inline-memo") {
|
|
2204
|
+
return `${content}<sup>\uFF08${sy2.TextMarkInlineMemoContent}\uFF09</sup>`;
|
|
2205
|
+
} else if (type === "block-ref") {
|
|
2206
|
+
let href = "";
|
|
2207
|
+
if (sy2.TextMarkBlockRefID) {
|
|
2208
|
+
const doc = await storeDep.getDocByChildID(sy2.TextMarkBlockRefID);
|
|
2209
|
+
if (doc?.ID) {
|
|
2210
|
+
href = `${await that.getTopPathPrefix()}${await storeDep.getHPathByID_Node(
|
|
2211
|
+
doc
|
|
2212
|
+
)}.html#${sy2.TextMarkBlockRefID}`;
|
|
2213
|
+
that.refs.add(doc.ID);
|
|
2214
|
+
} else {
|
|
2215
|
+
warn(`\u672A\u67E5\u627E\u5230${sy2.ID}\u6240\u6307\u5411\u7684\u6587\u6863\u8282\u70B9 ${sy2.TextMarkBlockRefID}`);
|
|
2216
|
+
}
|
|
2217
|
+
} else {
|
|
2218
|
+
warn(`${sy2.ID} \u5757\u5F15\u7528\u6CA1\u6709\u8BBE\u5B9A ref id`);
|
|
2219
|
+
}
|
|
2220
|
+
return `<span data-type="${sy2.TextMarkType}" data-subtype="${/** "s" */
|
|
2221
|
+
sy2.TextMarkBlockRefSubtype}" data-id="${/** 被引用块的id */
|
|
2222
|
+
sy2.TextMarkBlockRefID}"><a href="${href}">${content}</a></span>`;
|
|
2223
|
+
} else if (type === "a") {
|
|
2224
|
+
let href = sy2.TextMarkAHref;
|
|
2225
|
+
if (href?.startsWith("assets/")) {
|
|
2226
|
+
href = `${await that.getTopPathPrefix()}/${href}`;
|
|
2227
|
+
}
|
|
2228
|
+
return `<a href="${href}">${content}</a>`;
|
|
2229
|
+
} else if (`strong em u s mark sup sub kbd tag code strong code text`.includes(
|
|
2230
|
+
type ?? ""
|
|
2231
|
+
)) {
|
|
2232
|
+
return `<span ${strAttr(sy2, { data_type: type })}>${content}</span>`;
|
|
2233
|
+
} else {
|
|
2234
|
+
return warnDiv(
|
|
2235
|
+
`\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u7684\u6E32\u67D3\u5668 ${sy2.TextMarkType} ${that.nodeStack[0].Properties?.title}`
|
|
2236
|
+
);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
},
|
|
2240
|
+
async NodeImage(sy) {
|
|
2241
|
+
let link = "";
|
|
2242
|
+
const LinkDest = sy.Children?.filter((c) => c.Type === "NodeLinkDest");
|
|
2243
|
+
if (LinkDest?.length === 1) {
|
|
2244
|
+
link = await callRenderHTML(LinkDest[0], this);
|
|
2245
|
+
} else if (LinkDest?.length && LinkDest.length > 1) {
|
|
2246
|
+
warn("NodeImage \u5B58\u5728\u591A\u4E2A LinkDest", sy);
|
|
2247
|
+
}
|
|
2248
|
+
let title = "";
|
|
2249
|
+
const LinkTitle = sy.Children?.filter((c) => c.Type === "NodeLinkTitle");
|
|
2250
|
+
if (LinkTitle?.length === 1) {
|
|
2251
|
+
title = await callRenderHTML(LinkTitle[0], this);
|
|
2252
|
+
} else if (LinkTitle?.length && LinkTitle.length > 1) {
|
|
2253
|
+
warn("NodeImage \u5B58\u5728\u591A\u4E2A LinkTitle", sy);
|
|
2254
|
+
}
|
|
2255
|
+
return `<span ${await strAttr(sy)} style="${sy.Properties?.["parent-style"] ?? ""}">
|
|
2256
|
+
<img
|
|
2257
|
+
src="${link}"
|
|
2258
|
+
data-src="${link}"
|
|
2259
|
+
title="${title}"
|
|
2260
|
+
style="${sy.Properties?.style ?? ""}"
|
|
2261
|
+
loading="lazy"
|
|
2262
|
+
/>
|
|
2263
|
+
<span class="protyle-action__title">${title}</span></span>`;
|
|
2264
|
+
},
|
|
2265
|
+
async NodeLinkDest(sy) {
|
|
2266
|
+
if (/^(?:[a-z]+:)?\/\/|^(?:\/)/.test(sy.Data ?? "")) {
|
|
2267
|
+
return sy.Data ?? "";
|
|
2268
|
+
}
|
|
2269
|
+
return `${await this.getTopPathPrefix()}/${sy.Data}`;
|
|
2270
|
+
},
|
|
2271
|
+
NodeLinkTitle: _dataString,
|
|
2272
|
+
NodeKramdownSpanIAL: _emptyString,
|
|
2273
|
+
async NodeSuperBlock(sy) {
|
|
2274
|
+
return `<div ${strAttr(sy)} data-sb-layout="${childDateByType(
|
|
2275
|
+
sy,
|
|
2276
|
+
"NodeSuperBlockLayoutMarker"
|
|
2277
|
+
)}">${await callChildRender(sy, this)}</div>`;
|
|
2278
|
+
},
|
|
2279
|
+
NodeSuperBlockOpenMarker: _emptyString,
|
|
2280
|
+
NodeSuperBlockCloseMarker: _emptyString,
|
|
2281
|
+
NodeSuperBlockLayoutMarker: _emptyString,
|
|
2282
|
+
async NodeBlockQueryEmbed(sy) {
|
|
2283
|
+
return `<div ${strAttr(sy)} data-type="NodeBlockquote" class="bq"> ${await callChildRender(sy, this)} </div>`;
|
|
2284
|
+
},
|
|
2285
|
+
NodeOpenBrace: _emptyString,
|
|
2286
|
+
NodeCloseBrace: _emptyString,
|
|
2287
|
+
async NodeBlockQueryEmbedScript(sy) {
|
|
2288
|
+
const sql = sy.Data;
|
|
2289
|
+
if (!sql) {
|
|
2290
|
+
console.log("no sql", sy);
|
|
2291
|
+
return html`<pre>${sql}</pre>`;
|
|
2292
|
+
}
|
|
2293
|
+
let htmlStr = "";
|
|
2294
|
+
const blocks = await API.query_sql({
|
|
2295
|
+
stmt: (
|
|
2296
|
+
/** sql 被思源转义了,类似 :SELECT * FROM blocks WHERE id = '20201227174241-nxny1tq'
|
|
2297
|
+
所以这里将它转义回来
|
|
2298
|
+
TODO 当用户确实使用了包含转义的字符串时,这个实现是错误的 */
|
|
2299
|
+
unescaping(
|
|
2300
|
+
sql
|
|
2301
|
+
).replace(
|
|
2302
|
+
/** 我不理解lute为什么这样实现 https://github.com/88250/lute/blob/HEAD/editor/const.go#L38
|
|
2303
|
+
* https://ld246.com/article/1696750832289
|
|
2304
|
+
*/
|
|
2305
|
+
/_esc_newline_/g,
|
|
2306
|
+
"\n"
|
|
2307
|
+
)
|
|
2308
|
+
)
|
|
2309
|
+
}).catch((err) => {
|
|
2310
|
+
throw new Error(
|
|
2311
|
+
`sql error: ${err.message}
|
|
2312
|
+
rawSql:${sql}
|
|
2313
|
+
unescapingSql:${unescaping(
|
|
2314
|
+
sql
|
|
2315
|
+
)}`
|
|
2316
|
+
);
|
|
2317
|
+
});
|
|
2318
|
+
for (const block of blocks) {
|
|
2319
|
+
const node = await storeDep.getNodeByID(block.id);
|
|
2320
|
+
if (node === void 0) {
|
|
2321
|
+
return warnDiv("\u672A\u627E\u5230\u6B64\u5757\uFF0C\u53EF\u80FD\u4E3A\u8DE8\u7B14\u8BB0\u5F15\u7528", block.id, sql);
|
|
2322
|
+
}
|
|
2323
|
+
htmlStr += await callRenderHTML(node, this);
|
|
2324
|
+
}
|
|
2325
|
+
return htmlStr;
|
|
2326
|
+
},
|
|
2327
|
+
async NodeBlockquote(sy) {
|
|
2328
|
+
return html`<div ${strAttr(sy)}>${await callChildRender(sy, this)}</div>`;
|
|
2329
|
+
},
|
|
2330
|
+
NodeBlockquoteMarker: _emptyString,
|
|
2331
|
+
NodeCodeBlock: async function(sy) {
|
|
2332
|
+
const [yes, _] = isRenderCode(sy);
|
|
2333
|
+
if (yes) {
|
|
2334
|
+
return `<div ${strAttr(sy)} data-content="${escaping(
|
|
2335
|
+
sy.Children?.find((el) => el.Type === "NodeCodeBlockCode")?.Data ?? ""
|
|
2336
|
+
)}">
|
|
2337
|
+
<div spin="1"></div>
|
|
2338
|
+
<div class="protyle-attr" contenteditable="false"></div>
|
|
2339
|
+
</div>`;
|
|
2340
|
+
}
|
|
2341
|
+
return `<div ${strAttr(sy)}>
|
|
2342
|
+
<div class="protyle-action">
|
|
2343
|
+
<span class="protyle-action--first protyle-action__language">${await callRenderHTML(
|
|
2344
|
+
sy.Children?.find(
|
|
2345
|
+
(el) => el.Type === "NodeCodeBlockFenceInfoMarker"
|
|
2346
|
+
),
|
|
2347
|
+
this
|
|
2348
|
+
)}</span>
|
|
2349
|
+
<span class="fn__flex-1"></span><span class="protyle-icon protyle-icon--only protyle-action__copy"><svg><use xlink:href="#iconCopy"></use></svg></span>
|
|
2350
|
+
</div>
|
|
2351
|
+
${await callRenderHTML(
|
|
2352
|
+
sy.Children?.find((el) => el.Type === "NodeCodeBlockCode"),
|
|
2353
|
+
this
|
|
2354
|
+
)}
|
|
2355
|
+
</div>`;
|
|
2356
|
+
},
|
|
2357
|
+
NodeCodeBlockFenceInfoMarker: async (sy) => atob(sy.CodeBlockInfo ?? ""),
|
|
2358
|
+
NodeCodeBlockCode: async (sy) => `<div class="hljs" spellcheck="false">${sy.Data}</div>`,
|
|
2359
|
+
NodeCodeBlockFenceOpenMarker: _emptyString,
|
|
2360
|
+
NodeCodeBlockFenceCloseMarker: _emptyString,
|
|
2361
|
+
async NodeTable(sy) {
|
|
2362
|
+
return `<div ${strAttr(sy)}>
|
|
2363
|
+
<div>
|
|
2364
|
+
<table spellcheck="false">
|
|
2365
|
+
<colgroup>
|
|
2366
|
+
${sy.TableAligns?.map(() => "<col />").join("")}
|
|
2367
|
+
</colgroup>
|
|
2368
|
+
${await callRenderHTML(
|
|
2369
|
+
sy.Children?.find((el) => el.Type === "NodeTableHead"),
|
|
2370
|
+
this
|
|
2371
|
+
)}
|
|
2372
|
+
<tbody>
|
|
2373
|
+
${(await Promise.all(
|
|
2374
|
+
sy.Children?.filter((el) => el.Type === "NodeTableRow").map(
|
|
2375
|
+
(el) => callRenderHTML(el, this)
|
|
2376
|
+
) ?? []
|
|
2377
|
+
)).join("\n")}
|
|
2378
|
+
</tbody>
|
|
2379
|
+
</table>
|
|
2380
|
+
</div>
|
|
2381
|
+
</div>`;
|
|
2382
|
+
},
|
|
2383
|
+
async NodeTableHead(sy) {
|
|
2384
|
+
return `<${sy.Data}>${await callChildRender(sy, this)}</${sy.Data}>`;
|
|
2385
|
+
},
|
|
2386
|
+
async NodeTableRow(sy) {
|
|
2387
|
+
return `<tr>${await callChildRender(sy, this)}</tr>`;
|
|
2388
|
+
},
|
|
2389
|
+
async NodeTableCell(sy) {
|
|
2390
|
+
return `<td>${await callChildRender(sy, this)}</td>`;
|
|
2391
|
+
},
|
|
2392
|
+
NodeHTMLBlock: async (sy) => `<div ${strAttr(sy)}>${sy.Data}</div>`,
|
|
2393
|
+
NodeThematicBreak: async (sy) => `<div ${strAttr(sy)}><div></div></div>`,
|
|
2394
|
+
NodeMathBlock: async (sy) => `<div ${strAttr(
|
|
2395
|
+
sy
|
|
2396
|
+
)} data-content="${childDateByType(sy, "NodeMathBlockContent")}">
|
|
2397
|
+
<div spin="1"></div>
|
|
2398
|
+
</div>`,
|
|
2399
|
+
NodeMathBlockOpenMarker: _emptyString,
|
|
2400
|
+
NodeMathBlockCloseMarker: _emptyString,
|
|
2401
|
+
async NodeIFrame(sy) {
|
|
2402
|
+
return ` <div ${strAttr(sy)}>
|
|
2403
|
+
<div class="iframe-content">
|
|
2404
|
+
${/** 资源总是被复制到顶层目录,所以直接跳到顶层即可 */
|
|
2405
|
+
/** TODO 应该有一个统一处理资源的方案 */
|
|
2406
|
+
sy.Data?.replace(
|
|
2407
|
+
/src="assets\//,
|
|
2408
|
+
`src="${await this.getTopPathPrefix()}/assets/`
|
|
2409
|
+
)}
|
|
2410
|
+
</div>
|
|
2411
|
+
</div>`;
|
|
2412
|
+
},
|
|
2413
|
+
async NodeVideo(sy) {
|
|
2414
|
+
return await this.NodeIFrame(sy);
|
|
2415
|
+
},
|
|
2416
|
+
async NodeAudio(sy) {
|
|
2417
|
+
return await this.NodeIFrame(sy);
|
|
2418
|
+
},
|
|
2419
|
+
/** 虚拟链接 */
|
|
2420
|
+
NodeHeadingC8hMarker: _emptyString,
|
|
2421
|
+
async NodeSoftBreak(_sy) {
|
|
2422
|
+
return "\u200B";
|
|
2423
|
+
},
|
|
2424
|
+
async NodeBr(sy) {
|
|
2425
|
+
return `<${sy.Data}>`;
|
|
2426
|
+
},
|
|
2427
|
+
async NodeWidget(sy) {
|
|
2428
|
+
return `<div ${strAttr(
|
|
2429
|
+
sy
|
|
2430
|
+
)}><img src="${await this.getTopPathPrefix()}/assets/widget/${sy.ID}.jpg"/></div>`;
|
|
2431
|
+
},
|
|
2432
|
+
async NodeBackslash(sy) {
|
|
2433
|
+
if (sy.Data === void 0 || sy.Data === "span") {
|
|
2434
|
+
return `${await callChildRender(sy, this)}`;
|
|
2435
|
+
} else {
|
|
2436
|
+
return warnDiv(
|
|
2437
|
+
`\u672A\u5B9A\u4E49\u7684 NodeBackslash \u5904\u7406 ${sy.Data}`,
|
|
2438
|
+
this.nodeStack[0].Properties?.title
|
|
2439
|
+
);
|
|
2440
|
+
}
|
|
2441
|
+
},
|
|
2442
|
+
NodeBackslashContent: _dataString
|
|
2443
|
+
};
|
|
2444
|
+
return render;
|
|
2445
|
+
});
|
|
2446
|
+
function childDateByType(sy, type) {
|
|
2447
|
+
return sy.Children?.find((el) => el.Type === type)?.Data;
|
|
2448
|
+
}
|
|
2449
|
+
function warn(...arg) {
|
|
2450
|
+
console.warn("\n", ...arg);
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
// src/core/build.ts
|
|
2454
|
+
function generateBreadcrumbs(path, config) {
|
|
2455
|
+
const breadcrumbs = [];
|
|
2456
|
+
if (config.sitemap?.siteLink) {
|
|
2457
|
+
breadcrumbs.push({
|
|
2458
|
+
name: config.sitemap?.title || "\u9996\u9875",
|
|
2459
|
+
url: config.sitemap.siteLink
|
|
2460
|
+
});
|
|
2461
|
+
}
|
|
2462
|
+
const pathSegments = path.split("/").filter((segment) => segment.length > 0);
|
|
2463
|
+
let currentPath = "";
|
|
2464
|
+
for (const segment of pathSegments) {
|
|
2465
|
+
currentPath += "/" + segment;
|
|
2466
|
+
breadcrumbs.push({
|
|
2467
|
+
name: segment,
|
|
2468
|
+
url: `${config.sitemap?.siteLink || ""}${currentPath}.html`
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
return breadcrumbs;
|
|
2472
|
+
}
|
|
2473
|
+
function build(config, otherConfig) {
|
|
2474
|
+
return Effect4.gen(function* () {
|
|
2475
|
+
const effectApi = yield* EffectRender;
|
|
2476
|
+
const effectLog = yield* EffectLogDep;
|
|
2477
|
+
const _renderHTML = otherConfig?.renderHtmlFn ?? renderHTML;
|
|
2478
|
+
const book = config.notebook;
|
|
2479
|
+
const docTree = {};
|
|
2480
|
+
const skipBuilds = useSkipBuilds();
|
|
2481
|
+
let oldPercentage = 0;
|
|
2482
|
+
let total = 0;
|
|
2483
|
+
function processPercentage(percentage) {
|
|
2484
|
+
total += oldPercentage;
|
|
2485
|
+
return (process3) => {
|
|
2486
|
+
oldPercentage = process3 * percentage;
|
|
2487
|
+
effectLog.percentage((total + oldPercentage) * 100);
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
effectLog.log(`=== \u5F00\u59CB\u7F16\u8BD1 ${book.name} ===`);
|
|
2491
|
+
let process2 = processPercentage(0.4);
|
|
2492
|
+
const Doc_blocks = yield* Effect4.tryPromise(() => allDocBlock_by_bookId(book.id));
|
|
2493
|
+
function refsNotUpdated(docBlock) {
|
|
2494
|
+
const refs = config.__skipBuilds__[docBlock.id]?.refs ?? [];
|
|
2495
|
+
for (const ref_id of refs) {
|
|
2496
|
+
const new_doc_hash = Doc_blocks.find(
|
|
2497
|
+
(docBlock2) => docBlock2.id === ref_id
|
|
2498
|
+
)?.hash;
|
|
2499
|
+
const old_doc_hash = config.__skipBuilds__[ref_id]?.hash;
|
|
2500
|
+
if (new_doc_hash === void 0 || old_doc_hash === void 0) {
|
|
2501
|
+
return false;
|
|
2502
|
+
} else if (new_doc_hash === old_doc_hash) {
|
|
2503
|
+
continue;
|
|
2504
|
+
} else {
|
|
2505
|
+
return false;
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
return true;
|
|
2509
|
+
}
|
|
2510
|
+
effectLog.log(`=== \u67E5\u8BE2\u6587\u6863\u7EA7block\u5B8C\u6210 ===`);
|
|
2511
|
+
let i = 0;
|
|
2512
|
+
yield* Effect4.tryPromise(() => Promise.all(
|
|
2513
|
+
Doc_blocks.map(async (docBlock) => {
|
|
2514
|
+
const sy = await get_doc_by_SyPath(DB_block_path(docBlock));
|
|
2515
|
+
docTree[docBlock.hpath] = { sy, docBlock };
|
|
2516
|
+
i++;
|
|
2517
|
+
process2(i / Doc_blocks.length);
|
|
2518
|
+
})
|
|
2519
|
+
));
|
|
2520
|
+
const fileTree = {};
|
|
2521
|
+
process2 = processPercentage(0.4);
|
|
2522
|
+
const enableIncrementalCompilation_doc = (() => {
|
|
2523
|
+
if (package_default.version !== config.OceanPress.version) {
|
|
2524
|
+
effectLog.log(
|
|
2525
|
+
`\u914D\u7F6E\u6587\u4EF6\u7248\u672C\u53F7[${config.OceanPress.version}]\u4E0EOceanPress\u7248\u672C[${package_default.version}]\u4E0D\u4E00\u81F4\uFF0C\u5C06\u8FDB\u884C\u6587\u6863\u5168\u91CF\u7F16\u8BD1`
|
|
2526
|
+
);
|
|
2527
|
+
return false;
|
|
2528
|
+
}
|
|
2529
|
+
return config.enableIncrementalCompilation_doc;
|
|
2530
|
+
})();
|
|
2531
|
+
effectLog.log(`=== \u5F00\u59CB\u6E32\u67D3\u6587\u6863 ===`);
|
|
2532
|
+
yield* Effect4.forEach(Object.entries(docTree), ([path, { sy, docBlock }]) => {
|
|
2533
|
+
return Effect4.gen(function* () {
|
|
2534
|
+
if (config.enableIncrementalCompilation && enableIncrementalCompilation_doc && /** 文档本身没有发生变化 */
|
|
2535
|
+
config.__skipBuilds__[docBlock.id]?.hash === docBlock.hash && /** docBlock所引用的文档也没有更新 */
|
|
2536
|
+
refsNotUpdated(docBlock)) {
|
|
2537
|
+
return "/** skip */";
|
|
1498
2538
|
}
|
|
1499
|
-
|
|
2539
|
+
const renderInstance = yield* getRender;
|
|
2540
|
+
try {
|
|
2541
|
+
const rootLevel = path.split("/").length - 2;
|
|
2542
|
+
const htmlContent = yield* _renderHTML(sy, renderInstance);
|
|
2543
|
+
const pageUrl = config.sitemap.siteLink ? `${config.sitemap.siteLink}${path}.html` : `${path}.html`;
|
|
2544
|
+
fileTree[path + ".html"] = yield* Effect4.tryPromise(() => htmlTemplate(
|
|
2545
|
+
{
|
|
2546
|
+
title: sy.Properties?.title || "",
|
|
2547
|
+
htmlContent,
|
|
2548
|
+
level: rootLevel,
|
|
2549
|
+
seoData: {
|
|
2550
|
+
doc: sy,
|
|
2551
|
+
config,
|
|
2552
|
+
pageUrl,
|
|
2553
|
+
breadcrumbs: generateBreadcrumbs(path, config)
|
|
2554
|
+
}
|
|
2555
|
+
},
|
|
2556
|
+
{
|
|
2557
|
+
...tempConfig.cdn,
|
|
2558
|
+
embedCode: config.embedCode
|
|
2559
|
+
}
|
|
2560
|
+
));
|
|
2561
|
+
if (config.sitemap.rss && path.endsWith(".rss.xml")) {
|
|
2562
|
+
const rssPath = path;
|
|
2563
|
+
fileTree[rssPath] = yield* Effect4.tryPromise(() => generateRSSXML(rssPath, renderInstance, config, effectApi.getHPathByID_Node));
|
|
2564
|
+
effectLog.log(`\u6E32\u67D3 rss.xml:${rssPath} \u5B8C\u6BD5`);
|
|
2565
|
+
}
|
|
2566
|
+
if (config.enableIncrementalCompilation && config.enableIncrementalCompilation_doc) {
|
|
2567
|
+
skipBuilds.add(docBlock.id, {
|
|
2568
|
+
hash: docBlock.hash
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
1500
2571
|
skipBuilds.add(docBlock.id, {
|
|
1501
|
-
|
|
2572
|
+
refs: (
|
|
2573
|
+
/** 保存引用 */
|
|
2574
|
+
[...renderInstance.refs.values()]
|
|
2575
|
+
)
|
|
1502
2576
|
});
|
|
2577
|
+
effectLog.log(`\u6E32\u67D3\u5B8C\u6BD5:${path}`);
|
|
2578
|
+
} catch (error) {
|
|
2579
|
+
effectLog.log(`${path} \u6E32\u67D3\u5931\u8D25:${error}`);
|
|
2580
|
+
console.log(error);
|
|
1503
2581
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
[...renderInstance.refs.values()]
|
|
1508
|
-
)
|
|
1509
|
-
});
|
|
1510
|
-
} catch (error) {
|
|
1511
|
-
effect.log(`${path} \u6E32\u67D3\u5931\u8D25:${error}`);
|
|
1512
|
-
console.log(error);
|
|
1513
|
-
}
|
|
1514
|
-
process2(i / Doc_blocks.length);
|
|
1515
|
-
effect.log(`\u6E32\u67D3\u5B8C\u6BD5:${path}`);
|
|
1516
|
-
})
|
|
1517
|
-
);
|
|
1518
|
-
effect.log(`=== \u6E32\u67D3\u6587\u6863\u5B8C\u6210 ===`);
|
|
1519
|
-
effect.log(`=== \u5F00\u59CB\u751F\u6210 sitemap.xml ===`);
|
|
1520
|
-
if (config.sitemap.enable) {
|
|
1521
|
-
fileTree["sitemap.xml"] = sitemap_xml(Doc_blocks, config.sitemap);
|
|
1522
|
-
}
|
|
1523
|
-
if (config.excludeAssetsCopy === false) {
|
|
1524
|
-
effect.log(`=== \u5F00\u59CB\u590D\u5236\u8D44\u6E90\u6587\u4EF6 ===`);
|
|
1525
|
-
const assets = await API.query_sql({
|
|
1526
|
-
stmt: `SELECT * from assets
|
|
1527
|
-
WHERE box = '${book.id}'
|
|
1528
|
-
limit 150000 OFFSET 0`
|
|
2582
|
+
process2(i / Doc_blocks.length);
|
|
2583
|
+
return `\u6E32\u67D3\u5B8C\u6BD5:${path}`;
|
|
2584
|
+
});
|
|
1529
2585
|
});
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
2586
|
+
effectLog.log(`=== \u6E32\u67D3\u6587\u6863\u5B8C\u6210 ===`);
|
|
2587
|
+
effectLog.log(`=== \u5F00\u59CB\u751F\u6210 sitemap.xml ===`);
|
|
2588
|
+
if (config.sitemap.enable) {
|
|
2589
|
+
fileTree["sitemap.xml"] = sitemap_xml(Doc_blocks, config.sitemap);
|
|
2590
|
+
}
|
|
2591
|
+
if (config.excludeAssetsCopy === false) {
|
|
2592
|
+
effectLog.log(`=== \u5F00\u59CB\u590D\u5236\u8D44\u6E90\u6587\u4EF6 ===`);
|
|
2593
|
+
const assets = yield* Effect4.tryPromise(() => API.query_sql({
|
|
2594
|
+
stmt: `SELECT * from assets
|
|
2595
|
+
WHERE box = '${book.id}'
|
|
2596
|
+
limit 150000 OFFSET 0`
|
|
2597
|
+
}));
|
|
2598
|
+
yield* Effect4.tryPromise(() => Promise.all(
|
|
2599
|
+
assets.map(async (item) => {
|
|
2600
|
+
if (config.enableIncrementalCompilation && /** 资源没有变化,直接跳过 */
|
|
2601
|
+
config.__skipBuilds__[item.id]?.hash === item.hash) {
|
|
2602
|
+
return;
|
|
2603
|
+
} else {
|
|
2604
|
+
fileTree[item.path] = await API.get_assets({
|
|
2605
|
+
path: item.path
|
|
2606
|
+
});
|
|
2607
|
+
if (config.enableIncrementalCompilation) {
|
|
2608
|
+
skipBuilds.add(item.id, { hash: item.hash });
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
})
|
|
2612
|
+
));
|
|
2613
|
+
effectLog.log(`=== \u5F00\u59CB\u590D\u5236\u6302\u4EF6\u8D44\u6E90\u6587\u4EF6 ===`);
|
|
2614
|
+
const widgetList = yield* Effect4.tryPromise(() => API.query_sql({
|
|
2615
|
+
stmt: `
|
|
2616
|
+
SELECT *
|
|
2617
|
+
from blocks
|
|
2618
|
+
WHERE box = '${book.id}'
|
|
2619
|
+
AND type = 'widget'
|
|
2620
|
+
limit 150000 OFFSET 0
|
|
2621
|
+
`
|
|
2622
|
+
}));
|
|
2623
|
+
const widgetNode = (yield* Effect4.tryPromise(() => Promise.all(
|
|
2624
|
+
widgetList.map(async (el) => await get_node_by_id(el.id))
|
|
2625
|
+
))).filter(
|
|
2626
|
+
(widget) => widget?.Properties?.["custom-oceanpress-widget-update"]
|
|
2627
|
+
).map(async (widget) => {
|
|
2628
|
+
if (!widget || !widget?.ID) return;
|
|
2629
|
+
const update = widget?.Properties?.["custom-oceanpress-widget-update"];
|
|
2630
|
+
if (config.enableIncrementalCompilation && config.__skipBuilds__[widget.ID]?.updated === update) {
|
|
1534
2631
|
return;
|
|
1535
2632
|
} else {
|
|
1536
|
-
|
|
1537
|
-
|
|
2633
|
+
const id = widget.ID;
|
|
2634
|
+
fileTree[`assets/widget/${id}.jpg`] = await API.file_getFile({
|
|
2635
|
+
path: `data/storage/oceanpress/widget_img/${id}.jpg`
|
|
1538
2636
|
});
|
|
1539
2637
|
if (config.enableIncrementalCompilation) {
|
|
1540
|
-
skipBuilds.add(
|
|
2638
|
+
skipBuilds.add(id, { updated: update });
|
|
1541
2639
|
}
|
|
1542
2640
|
}
|
|
1543
|
-
})
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
skipBuilds.add(id, { updated: update });
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
});
|
|
1574
|
-
await Promise.all(widgetNode);
|
|
1575
|
-
}
|
|
1576
|
-
if (otherConfig?.onFileTree) {
|
|
1577
|
-
otherConfig.onFileTree(fileTree);
|
|
1578
|
-
}
|
|
1579
|
-
if (config.compressedZip) {
|
|
1580
|
-
effect.log(`=== \u5F00\u59CB\u751F\u6210\u538B\u7F29\u5305 ===`);
|
|
1581
|
-
await downloadZIP(fileTree, {
|
|
1582
|
-
// TODO 这里应该移出来成为全局的写选项
|
|
1583
|
-
withoutZip: tempConfig.withoutPublicZip,
|
|
1584
|
-
publicZip: tempConfig.cdn.publicZip
|
|
1585
|
-
});
|
|
1586
|
-
}
|
|
1587
|
-
config.OceanPress.version = package_default.version;
|
|
1588
|
-
skipBuilds.write();
|
|
1589
|
-
effect.percentage(100);
|
|
1590
|
-
effect.log("\u7F16\u8BD1\u5B8C\u6BD5");
|
|
1591
|
-
return { fileTree };
|
|
2641
|
+
});
|
|
2642
|
+
yield* Effect4.tryPromise(() => Promise.all(widgetNode));
|
|
2643
|
+
}
|
|
2644
|
+
if (config.sidebarCode.enableDocTree) {
|
|
2645
|
+
effectLog.log(`=== \u5F00\u59CB\u751F\u6210\u6587\u6863\u6811 ===`);
|
|
2646
|
+
fileTree[renderDocTreeJsPath] = yield* renderDocTree();
|
|
2647
|
+
effectLog.log(`=== \u6587\u6863\u6811\u751F\u6210\u5B8C\u6210 ===`);
|
|
2648
|
+
}
|
|
2649
|
+
if (otherConfig?.onFileTree) {
|
|
2650
|
+
otherConfig.onFileTree(fileTree, effectLog);
|
|
2651
|
+
}
|
|
2652
|
+
if (config.compressedZip) {
|
|
2653
|
+
effectLog.log(`=== \u5F00\u59CB\u751F\u6210\u538B\u7F29\u5305 ===`);
|
|
2654
|
+
yield* Effect4.tryPromise(
|
|
2655
|
+
() => downloadZIP(fileTree, {
|
|
2656
|
+
// TODO 这里应该移出来成为全局的写选项
|
|
2657
|
+
withoutZip: tempConfig.withoutPublicZip,
|
|
2658
|
+
publicZip: tempConfig.cdn.publicZip
|
|
2659
|
+
})
|
|
2660
|
+
);
|
|
2661
|
+
}
|
|
2662
|
+
config.OceanPress.version = package_default.version;
|
|
2663
|
+
skipBuilds.write();
|
|
2664
|
+
effectLog.percentage(100);
|
|
2665
|
+
effectLog.log("\u7F16\u8BD1\u5B8C\u6BD5");
|
|
2666
|
+
return { fileTree };
|
|
2667
|
+
});
|
|
1592
2668
|
}
|
|
1593
2669
|
function useSkipBuilds() {
|
|
1594
2670
|
const obj = {};
|
|
@@ -1606,56 +2682,73 @@ function useSkipBuilds() {
|
|
|
1606
2682
|
};
|
|
1607
2683
|
}
|
|
1608
2684
|
|
|
1609
|
-
// src/
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
body,
|
|
1631
|
-
headers: {
|
|
1632
|
-
"x-api-key": config.oceanPressServer.apiKey,
|
|
1633
|
-
"Content-Type": content_type
|
|
1634
|
-
},
|
|
1635
|
-
// @ts-expect-error 在 node 运行的时候需要声明双工模式才能正确发送 ReadableStream,TODO 需要验证浏览器端可以这样运行吗
|
|
1636
|
-
duplex: "half"
|
|
1637
|
-
// 关键:显式声明半双工模式
|
|
1638
|
-
}).then((res2) => res2.json()).then((r) => {
|
|
1639
|
-
if (r.error) {
|
|
1640
|
-
console.log("[r]", r);
|
|
1641
|
-
throw new Error();
|
|
1642
|
-
}
|
|
1643
|
-
return r.result;
|
|
1644
|
-
});
|
|
2685
|
+
// src/core/plugin.ts
|
|
2686
|
+
var PluginCenter = class {
|
|
2687
|
+
constructor(_funMap) {
|
|
2688
|
+
this._funMap = _funMap;
|
|
2689
|
+
__publicField(this, "plugins", []);
|
|
2690
|
+
/** 辅助类型,不可调用! */
|
|
2691
|
+
__publicField(this, "pluginType", 0);
|
|
2692
|
+
/** 对需要调用的函数进行代理,完成插件hook介入。 */
|
|
2693
|
+
__publicField(this, "fun");
|
|
2694
|
+
const that = this;
|
|
2695
|
+
this.fun = new Proxy({}, {
|
|
2696
|
+
get(_target, propertyKey, receiver) {
|
|
2697
|
+
const method = Reflect.get(that._funMap, propertyKey, receiver);
|
|
2698
|
+
if (typeof method === "function") {
|
|
2699
|
+
return (...args) => {
|
|
2700
|
+
return that.callFn(
|
|
2701
|
+
propertyKey,
|
|
2702
|
+
//@ts-ignore 懒得推类型了。属于内部实现,就直接忽略掉吧
|
|
2703
|
+
method
|
|
2704
|
+
)(...args);
|
|
2705
|
+
};
|
|
1645
2706
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
2707
|
+
return method;
|
|
2708
|
+
}
|
|
2709
|
+
});
|
|
2710
|
+
}
|
|
2711
|
+
registerPlugin(plugin) {
|
|
2712
|
+
this.plugins.push(plugin);
|
|
2713
|
+
}
|
|
2714
|
+
removePlugin(plugin) {
|
|
2715
|
+
this.plugins = this.plugins.filter((p) => p !== plugin);
|
|
2716
|
+
}
|
|
2717
|
+
/** 洋葱hook调用机制的实现 */
|
|
2718
|
+
callFn(name, fn) {
|
|
2719
|
+
return (...arg) => {
|
|
2720
|
+
const m = new middlewareRunner(fn);
|
|
2721
|
+
for (const plugin of this.plugins) {
|
|
2722
|
+
const middleware = plugin[name];
|
|
2723
|
+
if (middleware) {
|
|
2724
|
+
m.use(middleware);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
return m.runMiddlewareHandel(...arg);
|
|
2728
|
+
};
|
|
2729
|
+
}
|
|
2730
|
+
};
|
|
2731
|
+
var middlewareRunner = class {
|
|
2732
|
+
constructor(handel) {
|
|
2733
|
+
this.handel = handel;
|
|
2734
|
+
__publicField(this, "middlewares", []);
|
|
2735
|
+
}
|
|
2736
|
+
use(middleware) {
|
|
2737
|
+
this.middlewares.push(middleware);
|
|
2738
|
+
}
|
|
2739
|
+
runMiddlewareHandel(...ctx) {
|
|
2740
|
+
let index = 0;
|
|
2741
|
+
const next = (...ctx2) => {
|
|
2742
|
+
const middleware = this.middlewares[index];
|
|
2743
|
+
index++;
|
|
2744
|
+
if (middleware === void 0) {
|
|
2745
|
+
return this.handel.call(this, ...ctx2);
|
|
2746
|
+
}
|
|
2747
|
+
return middleware(ctx2, next);
|
|
2748
|
+
};
|
|
2749
|
+
return next.call(this, ...ctx);
|
|
2750
|
+
}
|
|
2751
|
+
};
|
|
1659
2752
|
|
|
1660
2753
|
// src/core/ocean_press.ts
|
|
1661
2754
|
var OceanPress = class {
|
|
@@ -1667,7 +2760,7 @@ var OceanPress = class {
|
|
|
1667
2760
|
/** 用于渲染文档的函数 */
|
|
1668
2761
|
build_renderHTML: renderHTML,
|
|
1669
2762
|
/** 编译完成后文件树的处理回调函数 */
|
|
1670
|
-
build_onFileTree: (_tree) => {
|
|
2763
|
+
build_onFileTree: (_tree, _effectApi) => {
|
|
1671
2764
|
}
|
|
1672
2765
|
});
|
|
1673
2766
|
__publicField(this, "pluginCenter", new PluginCenter(
|
|
@@ -1687,8 +2780,8 @@ var OceanPress = class {
|
|
|
1687
2780
|
);
|
|
1688
2781
|
}
|
|
1689
2782
|
}
|
|
1690
|
-
|
|
1691
|
-
const build_res = this.pluginCenter.fun.build(this.config,
|
|
2783
|
+
build() {
|
|
2784
|
+
const build_res = this.pluginCenter.fun.build(this.config, {
|
|
1692
2785
|
renderHtmlFn: this.pluginCenter.fun.build_renderHTML,
|
|
1693
2786
|
onFileTree: this.pluginCenter.fun.build_onFileTree
|
|
1694
2787
|
});
|
|
@@ -1697,45 +2790,52 @@ var OceanPress = class {
|
|
|
1697
2790
|
};
|
|
1698
2791
|
|
|
1699
2792
|
// src/core/render.api.dep.ts
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
2793
|
+
var renderApiDep = {
|
|
2794
|
+
getDocByChildID: async (id) => {
|
|
2795
|
+
return await get_doc_by_child_id(id);
|
|
2796
|
+
},
|
|
2797
|
+
getDocPathBySY: async (sy) => {
|
|
2798
|
+
if (sy?.ID) {
|
|
2799
|
+
const block = await get_block_by_id(sy.ID);
|
|
2800
|
+
if (block) {
|
|
2801
|
+
return DB_block_path(block);
|
|
2802
|
+
}
|
|
1708
2803
|
}
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
2804
|
+
},
|
|
2805
|
+
/** TODO 应该可以从cache中获取,而不需要查询 */
|
|
2806
|
+
getHPathByID_Node: async (id_node) => {
|
|
2807
|
+
const id = typeof id_node === "string" ? id_node : id_node.ID;
|
|
2808
|
+
if (id === void 0) throw new Error("id is undefined");
|
|
2809
|
+
const docNode = await get_doc_by_child_id(id);
|
|
2810
|
+
if (docNode === void 0) throw new Error("docNode is undefined");
|
|
2811
|
+
const docBlock = await get_block_by_id(id);
|
|
2812
|
+
if (docBlock === void 0) throw new Error("docBlock is undefined");
|
|
2813
|
+
return docBlock.hpath;
|
|
2814
|
+
},
|
|
2815
|
+
getNodeByID: async (id) => {
|
|
2816
|
+
if (id === void 0) return;
|
|
2817
|
+
const doc = await renderApiDep.getDocByChildID(id);
|
|
2818
|
+
if (doc === void 0) return;
|
|
2819
|
+
return getNode(doc);
|
|
2820
|
+
function getNode(node) {
|
|
2821
|
+
if (node.ID === id) return node;
|
|
2822
|
+
if (node.Children === void 0) return;
|
|
2823
|
+
for (const child of node.Children) {
|
|
2824
|
+
const n = getNode(child);
|
|
2825
|
+
if (n) return n;
|
|
2826
|
+
}
|
|
1731
2827
|
}
|
|
2828
|
+
},
|
|
2829
|
+
log(msg) {
|
|
2830
|
+
console.log(msg);
|
|
2831
|
+
},
|
|
2832
|
+
percentage: (n) => {
|
|
2833
|
+
console.log(n);
|
|
1732
2834
|
}
|
|
1733
2835
|
};
|
|
1734
2836
|
|
|
1735
2837
|
// src/util/store.node.dep.ts
|
|
1736
|
-
import { writeFileSync, readFileSync, existsSync, mkdirSync } from "
|
|
1737
|
-
storeDep.getItem = getItem;
|
|
1738
|
-
storeDep.setItem = setItem;
|
|
2838
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync } from "fs";
|
|
1739
2839
|
function setItem(key, value) {
|
|
1740
2840
|
if (!existsSync("./store/")) {
|
|
1741
2841
|
mkdirSync("./store/", { recursive: true });
|
|
@@ -1751,6 +2851,10 @@ function getItem(key) {
|
|
|
1751
2851
|
return void 0;
|
|
1752
2852
|
}
|
|
1753
2853
|
}
|
|
2854
|
+
var nodeApiDep = {
|
|
2855
|
+
setItem,
|
|
2856
|
+
getItem
|
|
2857
|
+
};
|
|
1754
2858
|
|
|
1755
2859
|
// src/cli/common.ts
|
|
1756
2860
|
import { Command } from "commander";
|
|
@@ -1758,13 +2862,13 @@ var program = new Command();
|
|
|
1758
2862
|
program.name("OceanPress").description("\u8FD9\u662F\u4E00\u6B3E\u4ECE\u601D\u6E90\u7B14\u8BB0\u672C\u751F\u6210\u4E00\u4E2A\u9759\u6001\u7AD9\u70B9\u7684\u5DE5\u5177");
|
|
1759
2863
|
|
|
1760
2864
|
// src/cli/deploy.ts
|
|
2865
|
+
import { Context as Context3, Effect as Effect5 } from "effect";
|
|
1761
2866
|
program.command("deploy").description("\u90E8\u7F72\u7AD9\u70B9").option("-c, --config <string>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u7684\u4F4D\u7F6E").option("-h, --apiBase <string>", "OceanPress server \u5730\u5740").option("-k, --apiKey <string>", "OceanPress server Api \u5BC6\u94A5").action(async (opt) => {
|
|
1762
2867
|
if (!opt.apiBase || !opt.apiKey) {
|
|
1763
2868
|
return console.error(`\u8BF7\u914D\u7F6E apiBase \u548C apiKey`);
|
|
1764
2869
|
}
|
|
1765
2870
|
const config = await readFile(opt.config, "utf-8");
|
|
1766
|
-
|
|
1767
|
-
const client = await createRPC2("apiConsumer", {
|
|
2871
|
+
const client = await createRPC("apiConsumer", {
|
|
1768
2872
|
remoteCall(method, data) {
|
|
1769
2873
|
let body;
|
|
1770
2874
|
let content_type;
|
|
@@ -1795,79 +2899,115 @@ program.command("deploy").description("\u90E8\u7F72\u7AD9\u70B9").option("-c, --
|
|
|
1795
2899
|
});
|
|
1796
2900
|
}
|
|
1797
2901
|
});
|
|
1798
|
-
const
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
console.log("[deploy res]", res);
|
|
1809
|
-
}
|
|
1810
|
-
});
|
|
1811
|
-
await ocean_press.build({
|
|
1812
|
-
log: (msg) => {
|
|
1813
|
-
if (msg.startsWith("\u6E32\u67D3\uFF1A")) {
|
|
1814
|
-
process.stdout.write(`\r\x1B[K${msg}`);
|
|
1815
|
-
} else {
|
|
1816
|
-
process.stdout.write(`
|
|
2902
|
+
const context = Context3.empty().pipe(
|
|
2903
|
+
Context3.add(EffectRender, renderApiDep),
|
|
2904
|
+
Context3.add(EffectLocalStorageDep, nodeApiDep),
|
|
2905
|
+
Context3.add(EffectConfigDep, currentConfig.value),
|
|
2906
|
+
Context3.add(EffectLogDep, {
|
|
2907
|
+
log: (msg) => {
|
|
2908
|
+
if (msg.startsWith("\u6E32\u67D3\uFF1A")) {
|
|
2909
|
+
process.stdout.write(`\r\x1B[K${msg}`);
|
|
2910
|
+
} else {
|
|
2911
|
+
process.stdout.write(`
|
|
1817
2912
|
${msg}`);
|
|
2913
|
+
}
|
|
2914
|
+
},
|
|
2915
|
+
percentage: (n) => {
|
|
2916
|
+
process.stdout.write(`\r\x1B[K\u8FDB\u5EA6\uFF1A${n}%`);
|
|
1818
2917
|
}
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
2918
|
+
})
|
|
2919
|
+
);
|
|
2920
|
+
const p = Effect5.provide(
|
|
2921
|
+
Effect5.gen(function* () {
|
|
2922
|
+
yield* loadConfigFile(JSON.parse(config));
|
|
2923
|
+
const ocean_press = new OceanPress(currentConfig.value);
|
|
2924
|
+
ocean_press.pluginCenter.registerPlugin({
|
|
2925
|
+
async build_onFileTree([tree], next) {
|
|
2926
|
+
const zip = await genZIP(tree, { withoutZip: true });
|
|
2927
|
+
const sizeInMB = zip.size / (1024 * 1024);
|
|
2928
|
+
console.log("[zip.size in MB]", sizeInMB.toFixed(2));
|
|
2929
|
+
const readableStream = zip.stream();
|
|
2930
|
+
const { chunkCount, fileId } = await client.API.upload(
|
|
2931
|
+
readableStream
|
|
2932
|
+
);
|
|
2933
|
+
console.log("[res]", { chunkCount, fileId });
|
|
2934
|
+
const res = await client.API.deploy({ zipFileId: fileId });
|
|
2935
|
+
console.log("[deploy res]", res);
|
|
2936
|
+
}
|
|
2937
|
+
});
|
|
2938
|
+
return yield* ocean_press.build();
|
|
2939
|
+
}),
|
|
2940
|
+
context
|
|
2941
|
+
);
|
|
2942
|
+
await Effect5.runPromise(p);
|
|
1824
2943
|
});
|
|
1825
2944
|
|
|
1826
2945
|
// src/cli/build.ts
|
|
1827
2946
|
import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
1828
2947
|
import { resolve } from "path";
|
|
1829
2948
|
import { join } from "path/posix";
|
|
2949
|
+
import { Context as Context4, Effect as Effect6 } from "effect";
|
|
1830
2950
|
program.command("build").description("\u8F93\u51FA\u9759\u6001\u7AD9\u70B9\u6E90\u7801").option("-c, --config <string>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u7684\u4F4D\u7F6E").option("-o, --output <string>", "\u6307\u5B9A\u8F93\u51FA\u76EE\u5F55\u4F4D\u7F6E").action(async (opt) => {
|
|
1831
2951
|
if (!opt.config || !opt.output) {
|
|
1832
2952
|
console.log(`\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E\u548C\u8F93\u51FA\u76EE\u5F55\u4F4D\u7F6E`);
|
|
1833
2953
|
throw new Error("\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E\u548C\u8F93\u51FA\u76EE\u5F55\u4F4D\u7F6E");
|
|
1834
2954
|
}
|
|
1835
2955
|
const config = await readFile2(opt.config, "utf-8");
|
|
1836
|
-
loadConfigFile(JSON.parse(config));
|
|
1837
2956
|
const filePath = resolve(opt.output);
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
});
|
|
1858
|
-
await ocean_press.build({
|
|
1859
|
-
log: (msg) => {
|
|
1860
|
-
if (msg.startsWith("\u6E32\u67D3\uFF1A")) {
|
|
1861
|
-
process.stdout.write(`\r\x1B[K${msg}`);
|
|
1862
|
-
} else {
|
|
1863
|
-
process.stdout.write(`
|
|
2957
|
+
await Effect6.runPromise(
|
|
2958
|
+
Effect6.provideService(
|
|
2959
|
+
loadConfigFile(JSON.parse(config)),
|
|
2960
|
+
EffectLocalStorageDep,
|
|
2961
|
+
nodeApiDep
|
|
2962
|
+
)
|
|
2963
|
+
);
|
|
2964
|
+
const context = Context4.empty().pipe(
|
|
2965
|
+
Context4.add(EffectRender, renderApiDep),
|
|
2966
|
+
Context4.add(EffectLocalStorageDep, nodeApiDep),
|
|
2967
|
+
Context4.add(EffectConfigDep, currentConfig.value),
|
|
2968
|
+
Context4.add(EffectLogDep, {
|
|
2969
|
+
log: (msg) => {
|
|
2970
|
+
if (msg.startsWith("\u6E32\u67D3\uFF1A")) {
|
|
2971
|
+
process.stdout.write(`\r\x1B[K${msg}`);
|
|
2972
|
+
} else {
|
|
2973
|
+
process.stdout.write(`
|
|
1864
2974
|
${msg}`);
|
|
2975
|
+
}
|
|
2976
|
+
},
|
|
2977
|
+
percentage: (n) => {
|
|
2978
|
+
process.stdout.write(`\r\x1B[K\u8FDB\u5EA6\uFF1A${n}%`);
|
|
1865
2979
|
}
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
2980
|
+
})
|
|
2981
|
+
);
|
|
2982
|
+
const p = Effect6.provide(
|
|
2983
|
+
Effect6.gen(function* () {
|
|
2984
|
+
const ocean_press = new OceanPress(currentConfig.value);
|
|
2985
|
+
ocean_press.pluginCenter.registerPlugin({
|
|
2986
|
+
async build_onFileTree([tree]) {
|
|
2987
|
+
for (const [path, data] of Object.entries(tree)) {
|
|
2988
|
+
const fullPath = join(filePath, "./", path);
|
|
2989
|
+
const pathArray = fullPath.split("/").slice(0, -1);
|
|
2990
|
+
const dirPath = pathArray.join("/");
|
|
2991
|
+
mkdir(dirPath, { recursive: true });
|
|
2992
|
+
try {
|
|
2993
|
+
if (typeof data === "string") {
|
|
2994
|
+
await writeFile(fullPath, data, "utf-8");
|
|
2995
|
+
} else {
|
|
2996
|
+
await writeFile(fullPath, new DataView(data));
|
|
2997
|
+
}
|
|
2998
|
+
} catch (error) {
|
|
2999
|
+
console.log(`${fullPath} \u65E0\u6CD5\u5199\u5165`);
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
});
|
|
3004
|
+
console.log("[config.__current__]", JSON.parse(config).__current__);
|
|
3005
|
+
console.log("[currentConfig.value.name]", currentConfig.value.name);
|
|
3006
|
+
return yield* ocean_press.build();
|
|
3007
|
+
}),
|
|
3008
|
+
context
|
|
3009
|
+
);
|
|
3010
|
+
await Effect6.runPromise(p);
|
|
1871
3011
|
});
|
|
1872
3012
|
|
|
1873
3013
|
// src/cli/server.ts
|
|
@@ -1875,28 +3015,35 @@ import { readFile as readFile3 } from "fs/promises";
|
|
|
1875
3015
|
|
|
1876
3016
|
// src/server.ts
|
|
1877
3017
|
import { serve } from "@hono/node-server";
|
|
3018
|
+
import { serveStatic } from "@hono/node-server/serve-static";
|
|
3019
|
+
import { Effect as Effect8 } from "effect";
|
|
3020
|
+
import { Hono as Hono2 } from "hono";
|
|
1878
3021
|
|
|
1879
3022
|
// src/core/hono_server.ts
|
|
1880
3023
|
import { Hono } from "hono";
|
|
1881
3024
|
import { stream } from "hono/streaming";
|
|
1882
|
-
|
|
3025
|
+
import { Effect as Effect7, Context as Context5 } from "effect";
|
|
3026
|
+
function createHonoApp(app = new Hono(), renderapi) {
|
|
3027
|
+
const context = Context5.empty().pipe(
|
|
3028
|
+
Context5.add(EffectRender, renderapi),
|
|
3029
|
+
Context5.add(EffectConfigDep, currentConfig.value)
|
|
3030
|
+
);
|
|
3031
|
+
app.get(renderDocTreeJsPath, async (c) => {
|
|
3032
|
+
const p = Effect7.provide(renderDocTree(), context);
|
|
3033
|
+
const r = await Effect7.runPromise(p);
|
|
3034
|
+
return c.text(r, 200, {
|
|
3035
|
+
"Content-Type": "application/javascript",
|
|
3036
|
+
"Cache-Control": "public, max-age=3600"
|
|
3037
|
+
});
|
|
3038
|
+
});
|
|
1883
3039
|
app.get("/", (c) => c.redirect("/index.html"));
|
|
1884
3040
|
app.get("/assets/*", assetsHandle);
|
|
3041
|
+
app.get("/favicon.ico", assetsHandle);
|
|
1885
3042
|
app.get("*", async (c) => {
|
|
1886
3043
|
const path = decodeURIComponent(c.req.path);
|
|
1887
|
-
const
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
}
|
|
1891
|
-
throw err;
|
|
1892
|
-
});
|
|
1893
|
-
if (r instanceof Error) {
|
|
1894
|
-
throw r;
|
|
1895
|
-
} else if (typeof r === "string") {
|
|
1896
|
-
return c.html(r);
|
|
1897
|
-
} else {
|
|
1898
|
-
return r;
|
|
1899
|
-
}
|
|
3044
|
+
const p = Effect7.provide(renderHtmlByUriPath(path), context);
|
|
3045
|
+
const r = await Effect7.runPromise(p);
|
|
3046
|
+
return c.html(r);
|
|
1900
3047
|
});
|
|
1901
3048
|
return app;
|
|
1902
3049
|
}
|
|
@@ -1934,55 +3081,60 @@ async function assetsHandle(c) {
|
|
|
1934
3081
|
}
|
|
1935
3082
|
});
|
|
1936
3083
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
3084
|
+
function renderHtmlByUriPath(path) {
|
|
3085
|
+
return Effect7.gen(function* () {
|
|
3086
|
+
const hpath = decodeURIComponent(path).replace(/\#(.*)?$/, "").replace(/\.html$/, "");
|
|
3087
|
+
const doc = yield* Effect7.tryPromise(() => get_doc_by_hpath(hpath));
|
|
3088
|
+
const htmlContent = yield* renderHTML(doc);
|
|
3089
|
+
return yield* Effect7.tryPromise(
|
|
3090
|
+
() => htmlTemplate(
|
|
3091
|
+
{
|
|
3092
|
+
title: doc.Properties?.title || "",
|
|
3093
|
+
htmlContent,
|
|
3094
|
+
level: path.split("/").length - 1
|
|
3095
|
+
},
|
|
3096
|
+
{
|
|
3097
|
+
...tempConfig.cdn,
|
|
3098
|
+
embedCode: currentConfig.value.embedCode
|
|
3099
|
+
}
|
|
3100
|
+
)
|
|
3101
|
+
);
|
|
3102
|
+
});
|
|
1951
3103
|
}
|
|
1952
3104
|
|
|
1953
3105
|
// src/server.ts
|
|
1954
|
-
import { serveStatic } from "@hono/node-server/serve-static";
|
|
1955
|
-
import { Hono as Hono2 } from "hono";
|
|
1956
|
-
import { join as join2 } from "path/posix";
|
|
1957
|
-
console.log(join2(import.meta.url.slice(5), "../../public/"));
|
|
1958
3106
|
function server(config = { port: 80, hostname: "0.0.0.0" }) {
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
createHonoApp(app);
|
|
1970
|
-
return new Promise((resolve3, _reject) => {
|
|
1971
|
-
serve(
|
|
1972
|
-
{
|
|
1973
|
-
fetch: app.fetch,
|
|
1974
|
-
port: config.port,
|
|
1975
|
-
hostname: config.hostname
|
|
1976
|
-
},
|
|
1977
|
-
(info) => {
|
|
1978
|
-
resolve3({ info, app });
|
|
1979
|
-
console.log(`Listening on http://${info.address}:${info.port}`);
|
|
1980
|
-
}
|
|
3107
|
+
return Effect8.gen(function* () {
|
|
3108
|
+
const app = new Hono2();
|
|
3109
|
+
app.use(
|
|
3110
|
+
"/notebook/*",
|
|
3111
|
+
serveStatic({
|
|
3112
|
+
root: "./public/",
|
|
3113
|
+
onNotFound(path, c) {
|
|
3114
|
+
console.log("[onNotFound notebook path]", path);
|
|
3115
|
+
}
|
|
3116
|
+
})
|
|
1981
3117
|
);
|
|
3118
|
+
const effectDep = yield* EffectRender;
|
|
3119
|
+
createHonoApp(app, effectDep);
|
|
3120
|
+
return new Promise((resolve2, _reject) => {
|
|
3121
|
+
serve(
|
|
3122
|
+
{
|
|
3123
|
+
fetch: app.fetch,
|
|
3124
|
+
port: config.port,
|
|
3125
|
+
hostname: config.hostname
|
|
3126
|
+
},
|
|
3127
|
+
(info) => {
|
|
3128
|
+
resolve2({ info, app });
|
|
3129
|
+
console.log(`Listening on http://${info.address}:${info.port}`);
|
|
3130
|
+
}
|
|
3131
|
+
);
|
|
3132
|
+
});
|
|
1982
3133
|
});
|
|
1983
3134
|
}
|
|
1984
3135
|
|
|
1985
3136
|
// src/cli/server.ts
|
|
3137
|
+
import { Context as Context6, Effect as Effect9 } from "effect";
|
|
1986
3138
|
program.command("server").description("\u542F\u52A8\u52A8\u6001\u4EE3\u7406").option("-c, --config <string>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u7684\u4F4D\u7F6E").option("-h, --host <string>", "web\u670D\u52A1\u7ED1\u5B9A\u5230\u7684\u5730\u5740", "127.0.0.1").option("-p, --port <number>", "web\u670D\u52A1\u7ED1\u5B9A\u5230\u7684\u7AEF\u53E3", "80").option(
|
|
1987
3139
|
"--cache <boolean>",
|
|
1988
3140
|
"\u914D\u7F6E\u4E3A true \u65F6\u5F00\u542F\u7F13\u5B58,\u9ED8\u8BA4\u4E3A false \u4E0D\u5F00\u542F\u7F13\u5B58",
|
|
@@ -1993,12 +3145,36 @@ program.command("server").description("\u542F\u52A8\u52A8\u6001\u4EE3\u7406").op
|
|
|
1993
3145
|
console.log(`\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E`);
|
|
1994
3146
|
}
|
|
1995
3147
|
const config = await readFile3(opt.config, "utf-8");
|
|
1996
|
-
loadConfigFile(JSON.parse(config));
|
|
1997
3148
|
setCache(opt.cache !== "false");
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
3149
|
+
const context = Context6.empty().pipe(
|
|
3150
|
+
Context6.add(EffectRender, renderApiDep),
|
|
3151
|
+
Context6.add(EffectLocalStorageDep, nodeApiDep),
|
|
3152
|
+
Context6.add(EffectLogDep, {
|
|
3153
|
+
log: (msg) => {
|
|
3154
|
+
if (msg.startsWith("\u6E32\u67D3\uFF1A")) {
|
|
3155
|
+
process.stdout.write(`\r\x1B[K${msg}`);
|
|
3156
|
+
} else {
|
|
3157
|
+
process.stdout.write(`
|
|
3158
|
+
${msg}`);
|
|
3159
|
+
}
|
|
3160
|
+
},
|
|
3161
|
+
percentage: (n) => {
|
|
3162
|
+
process.stdout.write(`\r\x1B[K\u8FDB\u5EA6\uFF1A${n}%`);
|
|
3163
|
+
}
|
|
3164
|
+
})
|
|
3165
|
+
);
|
|
3166
|
+
const p = Effect9.provide(
|
|
3167
|
+
Effect9.gen(function* () {
|
|
3168
|
+
yield* loadConfigFile(JSON.parse(config));
|
|
3169
|
+
tempConfig.cdn.siyuanPrefix = "/notebook/";
|
|
3170
|
+
return yield* server({
|
|
3171
|
+
hostname: opt.host,
|
|
3172
|
+
port: Number(opt.port)
|
|
3173
|
+
});
|
|
3174
|
+
}),
|
|
3175
|
+
context
|
|
3176
|
+
);
|
|
3177
|
+
await Effect9.runPromise(p);
|
|
2002
3178
|
}
|
|
2003
3179
|
);
|
|
2004
3180
|
|