oceanpress 1.0.4 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +2234 -1133
- package/dist-cli/cli.js.map +1 -1
- package/package.json +37 -37
- package/dist/assets/index-CgSYWV1W.js +0 -271
- package/dist/assets/steps-MZA5h6R-.js +0 -1735
package/dist-cli/cli.js
CHANGED
|
@@ -70,61 +70,25 @@ async function createRPC(...[type, options]) {
|
|
|
70
70
|
// src/cli/deploy.ts
|
|
71
71
|
import { stringify as stringify2 } from "superjson";
|
|
72
72
|
|
|
73
|
-
// src/core/dependency.ts
|
|
74
|
-
var nullDep = () => {
|
|
75
|
-
throw new Error("\u4E0D\u53EF\u8C03\u7528\u672A\u586B\u5145\u7684\u4F9D\u8D56");
|
|
76
|
-
};
|
|
77
|
-
var storeDep = {
|
|
78
|
-
// 读写配置文件所依赖的副作用
|
|
79
|
-
setItem: nullDep,
|
|
80
|
-
getItem: nullDep,
|
|
81
|
-
// render功能依赖的副作用
|
|
82
|
-
getNodeByID: nullDep,
|
|
83
|
-
getDocPathBySY: nullDep,
|
|
84
|
-
getDocByChildID: nullDep,
|
|
85
|
-
getHPathByID_Node: nullDep
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// src/util/deep_assign.ts
|
|
89
|
-
function deepAssign(target, source, config = { add: true, update: true }) {
|
|
90
|
-
for (let key in source) {
|
|
91
|
-
if (source.hasOwnProperty(key)) {
|
|
92
|
-
if (source[key] instanceof Object && !Array.isArray(source[key])) {
|
|
93
|
-
if (!target.hasOwnProperty(key)) {
|
|
94
|
-
target[key] = {};
|
|
95
|
-
}
|
|
96
|
-
deepAssign(target[key], source[key], config);
|
|
97
|
-
} else {
|
|
98
|
-
if (!target.hasOwnProperty(key) && config.add) {
|
|
99
|
-
target[key] = source[key];
|
|
100
|
-
} else if (config.update) {
|
|
101
|
-
target[key] = source[key];
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return target;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
73
|
// src/core/config.ts
|
|
74
|
+
import { Effect } from "effect";
|
|
110
75
|
import { computed, reactive, watch } from "vue";
|
|
111
76
|
|
|
112
77
|
// package.json
|
|
113
78
|
var package_default = {
|
|
114
79
|
name: "oceanpress",
|
|
115
|
-
version: "1.0.
|
|
80
|
+
version: "1.0.9",
|
|
116
81
|
type: "module",
|
|
117
82
|
scripts: {
|
|
118
83
|
dev: "vite",
|
|
119
84
|
cli: "tsx ./src/cli.ts",
|
|
120
|
-
|
|
85
|
+
"cli-w": "tsx watch ./src/cli.ts",
|
|
121
86
|
build: "vite build && npm run build_lib",
|
|
122
87
|
build_lib: "vite build --config vite.sw.config.ts",
|
|
123
88
|
build_app: "vite build --mode library",
|
|
124
89
|
build_cli: "tsup",
|
|
125
90
|
build_plugin_ui: "vite build --config vite.plugin.config.ts",
|
|
126
91
|
dev_plugin_ui: "vite build --watch --config vite.plugin.config.ts",
|
|
127
|
-
generate_dependency_graph: "depcruise src --include-only '^src' --output-type dot > ./assets/dep.dot",
|
|
128
92
|
preview: "vite preview"
|
|
129
93
|
},
|
|
130
94
|
bin: {
|
|
@@ -142,39 +106,73 @@ var package_default = {
|
|
|
142
106
|
"*.md"
|
|
143
107
|
],
|
|
144
108
|
dependencies: {
|
|
145
|
-
"@aws-sdk/client-s3": "^3.
|
|
146
|
-
"@hono/node-server": "^1.
|
|
147
|
-
cheerio: "1.
|
|
148
|
-
commander: "^
|
|
109
|
+
"@aws-sdk/client-s3": "^3.873.0",
|
|
110
|
+
"@hono/node-server": "^1.19.0",
|
|
111
|
+
cheerio: "1.1.2",
|
|
112
|
+
commander: "^14.0.0",
|
|
113
|
+
effect: "^3.17.8",
|
|
149
114
|
fzstd: "^0.1.1",
|
|
150
|
-
hono: "^4.
|
|
115
|
+
hono: "^4.9.4",
|
|
151
116
|
jszip: "^3.10.1",
|
|
152
|
-
meilisearch: "^0.
|
|
153
|
-
"naive-ui": "^2.
|
|
154
|
-
octokit: "^
|
|
117
|
+
meilisearch: "^0.52.0",
|
|
118
|
+
"naive-ui": "^2.42.0",
|
|
119
|
+
octokit: "^5.0.3",
|
|
155
120
|
superjson: "^2.2.2",
|
|
156
|
-
tsx: "^4.
|
|
157
|
-
vditor: "^3.
|
|
158
|
-
vue: "^3.5.
|
|
159
|
-
"vue-router": "^4.5.
|
|
121
|
+
tsx: "^4.20.4",
|
|
122
|
+
vditor: "^3.11.1",
|
|
123
|
+
vue: "^3.5.19",
|
|
124
|
+
"vue-router": "^4.5.1",
|
|
160
125
|
"zstd-codec": "^0.1.5"
|
|
161
126
|
},
|
|
162
127
|
devDependencies: {
|
|
128
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
129
|
+
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
|
130
|
+
"dependency-cruiser": "^17.0.1",
|
|
163
131
|
"oceanpress-rpc": "workspace:*",
|
|
164
132
|
"oceanpress-server": "workspace:*",
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
133
|
+
tsup: "^8.5.0",
|
|
134
|
+
typescript: "^5.9.2",
|
|
135
|
+
vite: "^7.1.3",
|
|
136
|
+
"vite-plugin-vue-devtools": "^8.0.0",
|
|
137
|
+
"vue-tsc": "^3.0.6"
|
|
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
|
+
}
|
|
173
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")() {
|
|
174
171
|
};
|
|
175
172
|
|
|
176
173
|
// src/core/config.ts
|
|
177
174
|
var version = package_default.version;
|
|
175
|
+
console.log("[version]", version);
|
|
178
176
|
var defaultConfig = {
|
|
179
177
|
name: "default",
|
|
180
178
|
/** 需要编译的笔记本 */
|
|
@@ -226,7 +224,7 @@ var defaultConfig = {
|
|
|
226
224
|
// publicZip:
|
|
227
225
|
// 'https://fastly.jsdelivr.net/gh/siyuan-note/oceanpress@v0.0.7/apps/frontend/public/public.zip',
|
|
228
226
|
// },
|
|
229
|
-
/** s3 上传配置
|
|
227
|
+
/** 部署到 s3 上传配置
|
|
230
228
|
* https://help.aliyun.com/zh/oss/developer-reference/use-amazon-s3-sdks-to-access-oss#section-2ri-suq-pb3
|
|
231
229
|
*/
|
|
232
230
|
s3: {
|
|
@@ -263,6 +261,15 @@ var defaultConfig = {
|
|
|
263
261
|
</p>
|
|
264
262
|
</footer>`
|
|
265
263
|
},
|
|
264
|
+
/** 侧边栏配置 */
|
|
265
|
+
sidebarCode: {
|
|
266
|
+
/** 启用文档树,则 leftCode 将插入在文档树上方 */
|
|
267
|
+
enableDocTree: true,
|
|
268
|
+
/** 将插入主文档的左侧div中的html代码 */
|
|
269
|
+
leftCode: "",
|
|
270
|
+
/** 将插入主文档的右侧div中的html代码 */
|
|
271
|
+
rightCode: ""
|
|
272
|
+
},
|
|
266
273
|
OceanPress: {
|
|
267
274
|
/** 此配置文件编译时的版本 */
|
|
268
275
|
version
|
|
@@ -276,47 +283,49 @@ var configs = reactive({
|
|
|
276
283
|
default: deepAssign({}, defaultConfig)
|
|
277
284
|
});
|
|
278
285
|
var loadConfigFile = (c) => {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
+
}
|
|
285
295
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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;
|
|
289
315
|
});
|
|
290
316
|
};
|
|
291
317
|
var currentConfig = computed(() => configs[configs.__current__]);
|
|
292
318
|
var tempConfig = {
|
|
293
319
|
cdn: {
|
|
294
320
|
/** 思源 js、css等文件的前缀 */
|
|
295
|
-
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/',
|
|
296
323
|
/** 思源 js、css等文件zip包地址 */
|
|
297
324
|
publicZip: "https://fastly.jsdelivr.net/gh/siyuan-note/oceanpress@v0.0.7/apps/frontend/public/public.zip"
|
|
298
325
|
},
|
|
299
326
|
withoutPublicZip: true
|
|
300
327
|
};
|
|
301
|
-
var saveConfig = () => {
|
|
302
|
-
if (configs.__init__ === false)
|
|
303
|
-
storeDep.setItem("configs", JSON.stringify(configs, null, 2));
|
|
304
|
-
};
|
|
305
|
-
var timer = null;
|
|
306
|
-
var debounceSaveConfig = () => {
|
|
307
|
-
if (timer) {
|
|
308
|
-
clearTimeout(timer);
|
|
309
|
-
}
|
|
310
|
-
timer = setTimeout(() => {
|
|
311
|
-
saveConfig();
|
|
312
|
-
timer = null;
|
|
313
|
-
}, 700);
|
|
314
|
-
};
|
|
315
|
-
watch(configs, debounceSaveConfig, { deep: true });
|
|
316
328
|
configs.__init__ = false;
|
|
317
|
-
if (globalThis.document) {
|
|
318
|
-
loadConfigFile();
|
|
319
|
-
}
|
|
320
329
|
|
|
321
330
|
// src/core/genZip.ts
|
|
322
331
|
import JSZip from "jszip";
|
|
@@ -382,7 +391,7 @@ var MeilisearchPlugin = class {
|
|
|
382
391
|
if (path.endsWith("index.html")) break;
|
|
383
392
|
}
|
|
384
393
|
this.updateDocument();
|
|
385
|
-
return next(
|
|
394
|
+
return next(...c);
|
|
386
395
|
});
|
|
387
396
|
this.host = option.host;
|
|
388
397
|
this.apiKey = option.apiKey;
|
|
@@ -415,91 +424,72 @@ var MeilisearchPlugin = class {
|
|
|
415
424
|
}
|
|
416
425
|
};
|
|
417
426
|
|
|
418
|
-
// src/
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const m = new middlewareRunner(fn);
|
|
454
|
-
for (const plugin of this.plugins) {
|
|
455
|
-
const middleware = plugin[name];
|
|
456
|
-
if (middleware) {
|
|
457
|
-
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
|
+
});
|
|
458
462
|
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
runMiddlewareHandel(...ctx) {
|
|
473
|
-
let index = 0;
|
|
474
|
-
const next = (...ctx2) => {
|
|
475
|
-
const middleware = this.middlewares[index];
|
|
476
|
-
index++;
|
|
477
|
-
if (middleware === void 0) {
|
|
478
|
-
return this.handel.call(this, ...ctx2);
|
|
479
|
-
}
|
|
480
|
-
return middleware(ctx2, next);
|
|
481
|
-
};
|
|
482
|
-
return next.call(this, ...ctx);
|
|
483
|
-
}
|
|
484
|
-
};
|
|
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
|
+
}
|
|
485
476
|
|
|
486
477
|
// src/plugins/publish/s3.ts
|
|
487
478
|
import { S3 } from "@aws-sdk/client-s3";
|
|
488
479
|
var s3Upload_plugin = {
|
|
489
|
-
build:
|
|
490
|
-
|
|
491
|
-
...
|
|
492
|
-
onFileTree: async (tree) => {
|
|
493
|
-
if (
|
|
494
|
-
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);
|
|
495
486
|
}
|
|
496
487
|
for await (const [fileName, ETag] of s3_uploads(tree, config)) {
|
|
497
|
-
|
|
488
|
+
effectApi.log(`\u4E0A\u4F20\uFF1A ${fileName} ${ETag}`);
|
|
498
489
|
}
|
|
490
|
+
effectApi.log(`s3 \u4E0A\u4F20\u5B8C\u6BD5`);
|
|
499
491
|
}
|
|
500
492
|
});
|
|
501
|
-
effect.log(`s3 \u4E0A\u4F20\u5B8C\u6BD5`);
|
|
502
|
-
return res;
|
|
503
493
|
}
|
|
504
494
|
};
|
|
505
495
|
var s3_uploads = async function* (tree, config) {
|
|
@@ -528,115 +518,22 @@ var s3_uploads = async function* (tree, config) {
|
|
|
528
518
|
}
|
|
529
519
|
};
|
|
530
520
|
|
|
531
|
-
// src/core/
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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());
|
|
540
534
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
<html lang="zh_CN" data-theme-mode="light" data-light-theme="daylight" data-dark-theme="midnight">
|
|
544
|
-
<head>
|
|
545
|
-
${config?.embedCode?.head ?? ""}
|
|
546
|
-
<meta charset="utf-8" />
|
|
547
|
-
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
548
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
|
|
549
|
-
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
550
|
-
<meta name="mobile-web-app-capable" content="yes" />
|
|
551
|
-
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
|
552
|
-
<link rel="stylesheet" type="text/css" id="baseStyle" href="${prePath}stage/build/export/base.css?${version2}"/>
|
|
553
|
-
<script>
|
|
554
|
-
function isNightTime() {
|
|
555
|
-
const currentHour = new Date().getHours();
|
|
556
|
-
return currentHour >= 18 || currentHour < 6;
|
|
557
|
-
}
|
|
558
|
-
document.write('<link rel="stylesheet" type="text/css" id="themeDefaultStyle" href="${prePath}appearance/themes/'+(isNightTime()?'midnight':'daylight')+'/theme.css?${version2}"/>');
|
|
559
|
-
</script>
|
|
560
|
-
<link rel="stylesheet" type="text/css" href="${prePath}appearance/oceanpress.css"/>
|
|
561
|
-
<title>${p.title}</title>
|
|
562
|
-
</head>
|
|
563
|
-
<body>
|
|
564
|
-
${config?.embedCode?.beforeBody ?? ""}
|
|
565
|
-
<div class="protyle-wysiwyg protyle-wysiwyg--attr" id="preview">
|
|
566
|
-
${p.htmlContent}
|
|
567
|
-
</div>
|
|
568
|
-
<script src="${prePath}appearance/icons/material/icon.js?${version2}"></script>
|
|
569
|
-
<script src="${prePath}stage/build/export/protyle-method.js?${version2}"></script>
|
|
570
|
-
<script src="${prePath}stage/protyle/js/lute/lute.min.js?${version2}"></script>
|
|
571
|
-
<script>
|
|
572
|
-
window.siyuan = {
|
|
573
|
-
config: {
|
|
574
|
-
appearance: {
|
|
575
|
-
mode: isNightTime()?1:0,//\u4E3B\u9898 \u660E\u4EAE=0 \u6697\u9ED1=1
|
|
576
|
-
codeBlockThemeDark: "base16/dracula",
|
|
577
|
-
codeBlockThemeLight: "github",
|
|
578
|
-
},
|
|
579
|
-
editor: {
|
|
580
|
-
codeLineWrap: true,
|
|
581
|
-
codeLigatures: false,
|
|
582
|
-
plantUMLServePath: "https://www.plantuml.com/plantuml/svg/~1",
|
|
583
|
-
codeSyntaxHighlightLineNum: true,
|
|
584
|
-
katexMacros: JSON.stringify({}),
|
|
585
|
-
},
|
|
586
|
-
},
|
|
587
|
-
languages: { copy: "\u590D\u5236" },
|
|
588
|
-
};
|
|
589
|
-
const cdn = "${prePath}stage/protyle";
|
|
590
|
-
const previewElement = document.getElementById("preview");
|
|
591
|
-
|
|
592
|
-
Protyle.highlightRender(previewElement, cdn);
|
|
593
|
-
Protyle.mathRender(previewElement, cdn, false);
|
|
594
|
-
Protyle.mermaidRender(previewElement, cdn);
|
|
595
|
-
Protyle.flowchartRender(previewElement, cdn);
|
|
596
|
-
Protyle.graphvizRender(previewElement, cdn);
|
|
597
|
-
Protyle.chartRender(previewElement, cdn);
|
|
598
|
-
Protyle.mindmapRender(previewElement, cdn);
|
|
599
|
-
Protyle.abcRender(previewElement, cdn);
|
|
600
|
-
Protyle.htmlRender(previewElement);
|
|
601
|
-
Protyle.plantumlRender(previewElement, cdn);
|
|
602
|
-
document.querySelectorAll(".protyle-action__copy").forEach((item) => {
|
|
603
|
-
item.addEventListener("click", (event) => {
|
|
604
|
-
navigator.clipboard.writeText(
|
|
605
|
-
item.parentElement.nextElementSibling.textContent.trimEnd(),
|
|
606
|
-
);
|
|
607
|
-
event.preventDefault();
|
|
608
|
-
event.stopPropagation();
|
|
609
|
-
});
|
|
610
|
-
});
|
|
611
|
-
</script>
|
|
612
|
-
${config?.embedCode?.afterBody ?? ""}
|
|
613
|
-
</body>
|
|
614
|
-
</html>`;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// src/util/escaping.ts
|
|
618
|
-
function escaping(s) {
|
|
619
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "&apos");
|
|
620
|
-
}
|
|
621
|
-
function unescaping(s) {
|
|
622
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&#(\d+);/g, (_sub, code) => {
|
|
623
|
-
return String.fromCharCode(Number(code));
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// src/components/data_promise/index.ts
|
|
628
|
-
import { customRef, nextTick, watch as watch2, watchEffect } from "vue";
|
|
629
|
-
var PromiseObj = class {
|
|
630
|
-
constructor() {
|
|
631
|
-
__publicField(this, "pending", false);
|
|
632
|
-
__publicField(this, "fulfilled", false);
|
|
633
|
-
__publicField(this, "rejected", false);
|
|
634
|
-
__publicField(this, "data", {});
|
|
635
|
-
__publicField(this, "error", {});
|
|
636
|
-
__publicField(this, "_p", Promise.resolve());
|
|
637
|
-
}
|
|
638
|
-
setP(p) {
|
|
639
|
-
this._p = p;
|
|
535
|
+
setP(p) {
|
|
536
|
+
this._p = p;
|
|
640
537
|
}
|
|
641
538
|
equalP(p) {
|
|
642
539
|
return this._p === p;
|
|
@@ -803,512 +700,6 @@ var vApi = new Proxy(
|
|
|
803
700
|
}
|
|
804
701
|
);
|
|
805
702
|
|
|
806
|
-
// src/core/render.ts
|
|
807
|
-
async function renderHTML(sy, renderInstance = getRender()) {
|
|
808
|
-
if (sy === void 0) return "";
|
|
809
|
-
const renderObj = {
|
|
810
|
-
...renderInstance,
|
|
811
|
-
nodeStack: [
|
|
812
|
-
/** 避免让所有的 renderInstance.nodeStack 是同一个对象 ,所以这里复制一个新的 */
|
|
813
|
-
...renderInstance.nodeStack
|
|
814
|
-
]
|
|
815
|
-
};
|
|
816
|
-
if (renderInstance.nodeStack.find(
|
|
817
|
-
(node) => node.ID && sy.ID && node.ID === sy.ID
|
|
818
|
-
)) {
|
|
819
|
-
return warnDiv(
|
|
820
|
-
"\u5FAA\u73AF\u5F15\u7528",
|
|
821
|
-
[...renderInstance.nodeStack, sy].map((el) => el.ID)
|
|
822
|
-
);
|
|
823
|
-
}
|
|
824
|
-
if (renderObj[sy.Type] === void 0) {
|
|
825
|
-
return warnDiv(
|
|
826
|
-
`\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u7684\u6E32\u67D3\u65B9\u6CD5 ${sy.Type} ${renderObj.nodeStack[0].Properties?.title}`
|
|
827
|
-
);
|
|
828
|
-
} else {
|
|
829
|
-
renderObj.nodeStack.push(sy);
|
|
830
|
-
if (sy.ID && renderInstance.nodeStack[0]?.ID) {
|
|
831
|
-
const targetDoc = await storeDep.getDocByChildID(sy.ID);
|
|
832
|
-
const currentDoc = renderInstance.nodeStack[0];
|
|
833
|
-
if (targetDoc?.ID !== void 0 && targetDoc.ID !== currentDoc.ID && currentDoc.ID) {
|
|
834
|
-
renderObj.refs.add(targetDoc.ID);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
const r = await renderObj[sy.Type](sy);
|
|
838
|
-
renderObj.nodeStack.pop();
|
|
839
|
-
return r;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
function warnDiv(msg, ...args) {
|
|
843
|
-
warn(msg, ...args);
|
|
844
|
-
return `<div class="ft__smaller ft__secondary b3-form__space--small">${msg}</div>`;
|
|
845
|
-
}
|
|
846
|
-
function isRenderCode(sy) {
|
|
847
|
-
const mark = atob(
|
|
848
|
-
sy.CodeBlockInfo ?? sy.Children?.find((el) => el.Type === "NodeCodeBlockFenceInfoMarker")?.CodeBlockInfo ?? ""
|
|
849
|
-
);
|
|
850
|
-
return [
|
|
851
|
-
[
|
|
852
|
-
"mindmap",
|
|
853
|
-
"mermaid",
|
|
854
|
-
"echarts",
|
|
855
|
-
"abc",
|
|
856
|
-
"graphviz",
|
|
857
|
-
"flowchart",
|
|
858
|
-
"plantuml"
|
|
859
|
-
].includes(mark),
|
|
860
|
-
mark
|
|
861
|
-
];
|
|
862
|
-
}
|
|
863
|
-
var html = String.raw;
|
|
864
|
-
async function childRender(sy, renderInstance) {
|
|
865
|
-
let h = "";
|
|
866
|
-
for await (const el of sy?.Children ?? []) {
|
|
867
|
-
h += await renderHTML(el, renderInstance);
|
|
868
|
-
}
|
|
869
|
-
return h;
|
|
870
|
-
}
|
|
871
|
-
function strAttr(sy, config = {}) {
|
|
872
|
-
if (config?.subtype_class === void 0) {
|
|
873
|
-
config.subtype_class = (() => {
|
|
874
|
-
const typ_subtype = sy.ListData?.Typ === 1 ? (
|
|
875
|
-
/** 有序列表 */
|
|
876
|
-
"o"
|
|
877
|
-
) : sy.ListData?.Typ === 3 ? (
|
|
878
|
-
/** 任务列表 */
|
|
879
|
-
"t"
|
|
880
|
-
) : (
|
|
881
|
-
/** 无序列表 */
|
|
882
|
-
"u"
|
|
883
|
-
);
|
|
884
|
-
if (sy.Type === "NodeDocument") return "h1";
|
|
885
|
-
else if (sy.Type === "NodeHeading") return `h${sy.HeadingLevel}`;
|
|
886
|
-
else if (sy.Type === "NodeList") return [typ_subtype, "list"];
|
|
887
|
-
else if (sy.Type === "NodeListItem") return [typ_subtype, "li"];
|
|
888
|
-
else if (sy.Type === "NodeParagraph") return ["", "p"];
|
|
889
|
-
else if (sy.Type === "NodeImage") return ["", "img"];
|
|
890
|
-
else if (sy.Type === "NodeBlockquote") return ["", "bq"];
|
|
891
|
-
else if (sy.Type === "NodeSuperBlock") return ["", "sb"];
|
|
892
|
-
else if (sy.Type === "NodeCodeBlock") {
|
|
893
|
-
const [yes, mark] = isRenderCode(sy);
|
|
894
|
-
if (yes) {
|
|
895
|
-
return [mark, "render-node"];
|
|
896
|
-
} else {
|
|
897
|
-
return ["", "code-block"];
|
|
898
|
-
}
|
|
899
|
-
} else if (sy.Type === "NodeTable") return ["", "table"];
|
|
900
|
-
else if (sy.Type === "NodeThematicBreak") return ["", "hr"];
|
|
901
|
-
else if (sy.Type === "NodeMathBlock") return ["math", "render-node"];
|
|
902
|
-
else if (sy.Type === "NodeIFrame") return ["", "iframe"];
|
|
903
|
-
else if (sy.Type === "NodeVideo") return ["", "iframe"];
|
|
904
|
-
else return "";
|
|
905
|
-
})();
|
|
906
|
-
}
|
|
907
|
-
const attrObj = {};
|
|
908
|
-
function addAttr(key, value) {
|
|
909
|
-
attrObj[key] = value;
|
|
910
|
-
}
|
|
911
|
-
if (sy.ID) {
|
|
912
|
-
addAttr("id", sy.ID);
|
|
913
|
-
addAttr("data-node-id", sy.ID);
|
|
914
|
-
}
|
|
915
|
-
if (sy?.TextMarkType === "tag") {
|
|
916
|
-
addAttr(`data-type`, sy.TextMarkType ?? "");
|
|
917
|
-
} else {
|
|
918
|
-
addAttr(`data-type`, config?.data_type ?? sy.Type);
|
|
919
|
-
}
|
|
920
|
-
if (sy.Properties?.updated) addAttr("updated", sy.Properties.updated);
|
|
921
|
-
if (config?.subtype_class) {
|
|
922
|
-
if (typeof config.subtype_class === "string") {
|
|
923
|
-
addAttr("data-subtype", config.subtype_class);
|
|
924
|
-
addAttr("class", config.subtype_class);
|
|
925
|
-
} else {
|
|
926
|
-
if (config.subtype_class[0] !== "")
|
|
927
|
-
addAttr("data-subtype", config.subtype_class[0]);
|
|
928
|
-
if (config.subtype_class[1] !== "")
|
|
929
|
-
addAttr("class", config.subtype_class[1]);
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
if (sy.Properties) {
|
|
933
|
-
Object.entries(sy.Properties).forEach(([k, v]) => addAttr(k, v));
|
|
934
|
-
}
|
|
935
|
-
if (sy.ListData?.Marker) addAttr("data-marker", atob(sy.ListData.Marker));
|
|
936
|
-
if (
|
|
937
|
-
/** 任务列表 */
|
|
938
|
-
sy.ListData?.Typ === 3 && /** 该项被选中 */
|
|
939
|
-
sy.Children?.find(
|
|
940
|
-
(el) => el.Type === "NodeTaskListItemMarker"
|
|
941
|
-
)?.TaskListItemChecked
|
|
942
|
-
) {
|
|
943
|
-
attrObj["class"] = (attrObj["class"] ?? "") + " protyle-task--done ";
|
|
944
|
-
}
|
|
945
|
-
delete attrObj["fold"];
|
|
946
|
-
if (sy.Type === "NodeDocument") delete attrObj["title"];
|
|
947
|
-
return Object.entries(attrObj).map(([k, v]) => `${k}="${v}"`).join(" ");
|
|
948
|
-
}
|
|
949
|
-
var _emptyString = async (_sy) => "";
|
|
950
|
-
var _dataString = async (sy) => sy.Data ?? "";
|
|
951
|
-
var getRender = () => {
|
|
952
|
-
return {
|
|
953
|
-
...render,
|
|
954
|
-
nodeStack: [],
|
|
955
|
-
refs: /* @__PURE__ */ new Set()
|
|
956
|
-
};
|
|
957
|
-
};
|
|
958
|
-
var render = {
|
|
959
|
-
nodeStack: [],
|
|
960
|
-
refs: /* @__PURE__ */ new Set(),
|
|
961
|
-
async getTopPathPrefix() {
|
|
962
|
-
const sy = this.nodeStack[0];
|
|
963
|
-
let prefix = ".";
|
|
964
|
-
if (sy.Type === "NodeDocument" && sy.ID) {
|
|
965
|
-
const path = await storeDep.getDocPathBySY(sy);
|
|
966
|
-
if (path) {
|
|
967
|
-
const level = path.split("/").length - 3;
|
|
968
|
-
for (let i = 0; i < level; i++) {
|
|
969
|
-
prefix += "/..";
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
return prefix;
|
|
973
|
-
} else {
|
|
974
|
-
console.log("\u672A\u5B9A\u4E49\u9876\u5C42\u5143\u7D20\u975E NodeDocument \u65F6\u7684\u5904\u7406\u65B9\u5F0F", sy);
|
|
975
|
-
return "";
|
|
976
|
-
}
|
|
977
|
-
},
|
|
978
|
-
async NodeDocument(sy) {
|
|
979
|
-
let html2 = "";
|
|
980
|
-
if (
|
|
981
|
-
/** 只有顶层的文档块才渲染题图 */
|
|
982
|
-
this.nodeStack.length === 1
|
|
983
|
-
) {
|
|
984
|
-
html2 += `<div style="min-height: 150px;" ${strAttr(sy)}>`;
|
|
985
|
-
if (sy.Properties?.["title-img"]) {
|
|
986
|
-
html2 += `<div class="protyle-background__img" style="margin-bottom: 30px;position: relative;height: 16vh;${sy.Properties?.["title-img"].replace(
|
|
987
|
-
/assets/,
|
|
988
|
-
// 修改为相对路径
|
|
989
|
-
await this.getTopPathPrefix() + "/assets"
|
|
990
|
-
)}"/>${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>`;
|
|
991
|
-
}
|
|
992
|
-
html2 += "</div>";
|
|
993
|
-
html2 += `<h1 ${strAttr(sy)} data-type="NodeHeading" class="h1">${sy.Properties?.title}</h1>`;
|
|
994
|
-
}
|
|
995
|
-
html2 += await childRender(sy, this);
|
|
996
|
-
return html2;
|
|
997
|
-
},
|
|
998
|
-
async NodeHeading(sy) {
|
|
999
|
-
const tagName = `h${sy.HeadingLevel}`;
|
|
1000
|
-
let html2 = `<${tagName} ${strAttr(sy)}>${await childRender(
|
|
1001
|
-
sy,
|
|
1002
|
-
this
|
|
1003
|
-
)}</${tagName}>`;
|
|
1004
|
-
const parentNode = this.nodeStack[
|
|
1005
|
-
this.nodeStack.length - 2
|
|
1006
|
-
/** 最后一个元素是 sy本身(NodeHeading)还得要往前一个,所以是2 */
|
|
1007
|
-
];
|
|
1008
|
-
if (parentNode?.Type === "NodeBlockQueryEmbedScript") {
|
|
1009
|
-
let afterFlag = false;
|
|
1010
|
-
for (const node of sy.Parent.Children ?? []) {
|
|
1011
|
-
if (node === sy) {
|
|
1012
|
-
afterFlag = true;
|
|
1013
|
-
} else if (node !== sy && node.Type === "NodeHeading") {
|
|
1014
|
-
afterFlag = false;
|
|
1015
|
-
} else if (afterFlag) {
|
|
1016
|
-
html2 += "\n" + await renderHTML(node, this);
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
return html2;
|
|
1021
|
-
},
|
|
1022
|
-
NodeText: _dataString,
|
|
1023
|
-
async NodeList(sy) {
|
|
1024
|
-
return html`<div ${strAttr(sy)}>${await childRender(sy, this)}</div>`;
|
|
1025
|
-
},
|
|
1026
|
-
async NodeListItem(sy) {
|
|
1027
|
-
return html`<div ${await strAttr(sy)}>
|
|
1028
|
-
<div class="protyle-action">
|
|
1029
|
-
${sy.ListData?.Typ === 1 ? (
|
|
1030
|
-
/** 有序列表 */
|
|
1031
|
-
atob(sy.ListData?.Marker ?? "")
|
|
1032
|
-
) : sy.ListData?.Typ === 3 ? (
|
|
1033
|
-
/** 任务列表 */
|
|
1034
|
-
`<svg><use xlink:href="#${sy.Children?.find((el) => el.Type === "NodeTaskListItemMarker")?.TaskListItemChecked ? "iconCheck" : "iconUncheck"}"></use></svg>`
|
|
1035
|
-
) : (
|
|
1036
|
-
/** 无序列表 */
|
|
1037
|
-
`<svg><use xlink:href="#iconDot"></use></svg>`
|
|
1038
|
-
)}
|
|
1039
|
-
</div>
|
|
1040
|
-
${await childRender(sy, this)}
|
|
1041
|
-
</div>`;
|
|
1042
|
-
},
|
|
1043
|
-
NodeTaskListItemMarker: _emptyString,
|
|
1044
|
-
async NodeParagraph(sy) {
|
|
1045
|
-
return `<div ${strAttr(sy)}><div spellcheck="false">${await childRender(
|
|
1046
|
-
sy,
|
|
1047
|
-
this
|
|
1048
|
-
)}</div></div>`;
|
|
1049
|
-
},
|
|
1050
|
-
async NodeTextMark(sy) {
|
|
1051
|
-
const that = this;
|
|
1052
|
-
let r = "";
|
|
1053
|
-
for (const type of (sy.TextMarkType?.split(" ") ?? []).reverse()) {
|
|
1054
|
-
if (r === "") {
|
|
1055
|
-
r = await TextMarkRender(sy, type, sy.TextMarkTextContent ?? "");
|
|
1056
|
-
} else {
|
|
1057
|
-
r = await TextMarkRender(sy, type, r);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
return r;
|
|
1061
|
-
async function TextMarkRender(sy2, type, content) {
|
|
1062
|
-
if (type === "inline-math") {
|
|
1063
|
-
return `<span data-type="inline-math" data-subtype="math" data-content="${sy2.TextMarkInlineMathContent}" class="render-node"></span>`;
|
|
1064
|
-
} else if (type === "inline-memo") {
|
|
1065
|
-
return `${content}<sup>\uFF08${sy2.TextMarkInlineMemoContent}\uFF09</sup>`;
|
|
1066
|
-
} else if (type === "block-ref") {
|
|
1067
|
-
let href = "";
|
|
1068
|
-
if (sy2.TextMarkBlockRefID) {
|
|
1069
|
-
const doc = await storeDep.getDocByChildID(sy2.TextMarkBlockRefID);
|
|
1070
|
-
if (doc?.ID) {
|
|
1071
|
-
href = `${await that.getTopPathPrefix()}${await storeDep.getHPathByID_Node(
|
|
1072
|
-
doc
|
|
1073
|
-
)}.html#${sy2.TextMarkBlockRefID}`;
|
|
1074
|
-
that.refs.add(doc.ID);
|
|
1075
|
-
} else {
|
|
1076
|
-
warn(`\u672A\u67E5\u627E\u5230${sy2.ID}\u6240\u6307\u5411\u7684\u6587\u6863\u8282\u70B9 ${sy2.TextMarkBlockRefID}`);
|
|
1077
|
-
}
|
|
1078
|
-
} else {
|
|
1079
|
-
warn(`${sy2.ID} \u5757\u5F15\u7528\u6CA1\u6709\u8BBE\u5B9A ref id`);
|
|
1080
|
-
}
|
|
1081
|
-
return `<span data-type="${sy2.TextMarkType}" data-subtype="${/** "s" */
|
|
1082
|
-
sy2.TextMarkBlockRefSubtype}" data-id="${/** 被引用块的id */
|
|
1083
|
-
sy2.TextMarkBlockRefID}"><a href="${href}">${content}</a></span>`;
|
|
1084
|
-
} else if (type === "a") {
|
|
1085
|
-
let href = sy2.TextMarkAHref;
|
|
1086
|
-
if (href?.startsWith("assets/")) {
|
|
1087
|
-
href = `${await that.getTopPathPrefix()}/${href}`;
|
|
1088
|
-
}
|
|
1089
|
-
return `<a href="${href}">${content}</a>`;
|
|
1090
|
-
} else if (`strong em u s mark sup sub kbd tag code strong code text`.includes(
|
|
1091
|
-
type ?? ""
|
|
1092
|
-
)) {
|
|
1093
|
-
return `<span ${strAttr(sy2, { data_type: type })}>${content}</span>`;
|
|
1094
|
-
} else {
|
|
1095
|
-
return warnDiv(
|
|
1096
|
-
`\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u7684\u6E32\u67D3\u5668 ${sy2.TextMarkType} ${that.nodeStack[0].Properties?.title}`
|
|
1097
|
-
);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
},
|
|
1101
|
-
async NodeImage(sy) {
|
|
1102
|
-
let link = "";
|
|
1103
|
-
const LinkDest = sy.Children?.filter((c) => c.Type === "NodeLinkDest");
|
|
1104
|
-
if (LinkDest?.length === 1) {
|
|
1105
|
-
link = await renderHTML(LinkDest[0], this);
|
|
1106
|
-
} else if (LinkDest?.length && LinkDest.length > 1) {
|
|
1107
|
-
warn("NodeImage \u5B58\u5728\u591A\u4E2A LinkDest", sy);
|
|
1108
|
-
}
|
|
1109
|
-
let title = "";
|
|
1110
|
-
const LinkTitle = sy.Children?.filter((c) => c.Type === "NodeLinkTitle");
|
|
1111
|
-
if (LinkTitle?.length === 1) {
|
|
1112
|
-
title = await renderHTML(LinkTitle[0], this);
|
|
1113
|
-
} else if (LinkTitle?.length && LinkTitle.length > 1) {
|
|
1114
|
-
warn("NodeImage \u5B58\u5728\u591A\u4E2A LinkTitle", sy);
|
|
1115
|
-
}
|
|
1116
|
-
return `<span ${await strAttr(sy)} style="${sy.Properties?.["parent-style"] ?? ""}">
|
|
1117
|
-
<img
|
|
1118
|
-
src="${link}"
|
|
1119
|
-
data-src="${link}"
|
|
1120
|
-
title="${title}"
|
|
1121
|
-
style="${sy.Properties?.style ?? ""}"
|
|
1122
|
-
loading="lazy"
|
|
1123
|
-
/>
|
|
1124
|
-
<span class="protyle-action__title">${title}</span></span>`;
|
|
1125
|
-
},
|
|
1126
|
-
async NodeLinkDest(sy) {
|
|
1127
|
-
if (/^(?:[a-z]+:)?\/\/|^(?:\/)/.test(sy.Data ?? "")) {
|
|
1128
|
-
return sy.Data ?? "";
|
|
1129
|
-
}
|
|
1130
|
-
return `${await this.getTopPathPrefix()}/${sy.Data}`;
|
|
1131
|
-
},
|
|
1132
|
-
NodeLinkTitle: _dataString,
|
|
1133
|
-
NodeKramdownSpanIAL: _emptyString,
|
|
1134
|
-
async NodeSuperBlock(sy) {
|
|
1135
|
-
return `<div ${strAttr(sy)} data-sb-layout="${childDateByType(
|
|
1136
|
-
sy,
|
|
1137
|
-
"NodeSuperBlockLayoutMarker"
|
|
1138
|
-
)}">${await childRender(sy, this)}</div>`;
|
|
1139
|
-
},
|
|
1140
|
-
NodeSuperBlockOpenMarker: _emptyString,
|
|
1141
|
-
NodeSuperBlockCloseMarker: _emptyString,
|
|
1142
|
-
NodeSuperBlockLayoutMarker: _emptyString,
|
|
1143
|
-
async NodeBlockQueryEmbed(sy) {
|
|
1144
|
-
return `<div ${strAttr(sy)} data-type="NodeBlockquote" class="bq">${await childRender(sy, this)}</div>`;
|
|
1145
|
-
},
|
|
1146
|
-
NodeOpenBrace: _emptyString,
|
|
1147
|
-
NodeCloseBrace: _emptyString,
|
|
1148
|
-
async NodeBlockQueryEmbedScript(sy) {
|
|
1149
|
-
const sql = sy.Data;
|
|
1150
|
-
if (!sql) {
|
|
1151
|
-
console.log("no sql", sy);
|
|
1152
|
-
return html`<pre>${sql}</pre>`;
|
|
1153
|
-
}
|
|
1154
|
-
let htmlStr = "";
|
|
1155
|
-
const blocks = await API.query_sql({
|
|
1156
|
-
stmt: (
|
|
1157
|
-
/** sql 被思源转义了,类似 :SELECT * FROM blocks WHERE id = '20201227174241-nxny1tq'
|
|
1158
|
-
所以这里将它转义回来
|
|
1159
|
-
TODO 当用户确实使用了包含转义的字符串时,这个实现是错误的 */
|
|
1160
|
-
unescaping(
|
|
1161
|
-
sql
|
|
1162
|
-
).replace(
|
|
1163
|
-
/** 我不理解lute为什么这样实现 https://github.com/88250/lute/blob/HEAD/editor/const.go#L38
|
|
1164
|
-
* https://ld246.com/article/1696750832289
|
|
1165
|
-
*/
|
|
1166
|
-
/_esc_newline_/g,
|
|
1167
|
-
"\n"
|
|
1168
|
-
)
|
|
1169
|
-
)
|
|
1170
|
-
}).catch((err) => {
|
|
1171
|
-
throw new Error(
|
|
1172
|
-
`sql error: ${err.message}
|
|
1173
|
-
rawSql:${sql}
|
|
1174
|
-
unescapingSql:${unescaping(
|
|
1175
|
-
sql
|
|
1176
|
-
)}`
|
|
1177
|
-
);
|
|
1178
|
-
});
|
|
1179
|
-
for (const block of blocks) {
|
|
1180
|
-
const node = await storeDep.getNodeByID(block.id);
|
|
1181
|
-
if (node === void 0) {
|
|
1182
|
-
return warnDiv("\u672A\u627E\u5230\u6B64\u5757\uFF0C\u53EF\u80FD\u4E3A\u8DE8\u7B14\u8BB0\u5F15\u7528", block.id, sql);
|
|
1183
|
-
}
|
|
1184
|
-
htmlStr += await renderHTML(node, this);
|
|
1185
|
-
}
|
|
1186
|
-
return htmlStr;
|
|
1187
|
-
},
|
|
1188
|
-
async NodeBlockquote(sy) {
|
|
1189
|
-
return html`<div ${strAttr(sy)}>${await childRender(sy, this)}</div>`;
|
|
1190
|
-
},
|
|
1191
|
-
NodeBlockquoteMarker: _emptyString,
|
|
1192
|
-
NodeCodeBlock: async (sy) => {
|
|
1193
|
-
const [yes, _] = isRenderCode(sy);
|
|
1194
|
-
if (yes) {
|
|
1195
|
-
return `<div ${strAttr(sy)} data-content="${escaping(
|
|
1196
|
-
sy.Children?.find((el) => el.Type === "NodeCodeBlockCode")?.Data ?? ""
|
|
1197
|
-
)}">
|
|
1198
|
-
<div spin="1"></div>
|
|
1199
|
-
<div class="protyle-attr" contenteditable="false"></div>
|
|
1200
|
-
</div>`;
|
|
1201
|
-
}
|
|
1202
|
-
return `<div ${strAttr(sy)}>
|
|
1203
|
-
<div class="protyle-action">
|
|
1204
|
-
<span class="protyle-action--first protyle-action__language">${await renderHTML(
|
|
1205
|
-
sy.Children?.find(
|
|
1206
|
-
(el) => el.Type === "NodeCodeBlockFenceInfoMarker"
|
|
1207
|
-
),
|
|
1208
|
-
void 0
|
|
1209
|
-
)}</span>
|
|
1210
|
-
<span class="fn__flex-1"></span><span class="protyle-icon protyle-icon--only protyle-action__copy"><svg><use xlink:href="#iconCopy"></use></svg></span>
|
|
1211
|
-
</div>
|
|
1212
|
-
${await renderHTML(
|
|
1213
|
-
sy.Children?.find((el) => el.Type === "NodeCodeBlockCode"),
|
|
1214
|
-
void 0
|
|
1215
|
-
)}
|
|
1216
|
-
</div>`;
|
|
1217
|
-
},
|
|
1218
|
-
NodeCodeBlockFenceInfoMarker: async (sy) => atob(sy.CodeBlockInfo ?? ""),
|
|
1219
|
-
NodeCodeBlockCode: async (sy) => `<div class="hljs" spellcheck="false">${sy.Data}</div>`,
|
|
1220
|
-
NodeCodeBlockFenceOpenMarker: _emptyString,
|
|
1221
|
-
NodeCodeBlockFenceCloseMarker: _emptyString,
|
|
1222
|
-
async NodeTable(sy) {
|
|
1223
|
-
return `<div ${strAttr(sy)}>
|
|
1224
|
-
<div>
|
|
1225
|
-
<table spellcheck="false">
|
|
1226
|
-
<colgroup>
|
|
1227
|
-
${sy.TableAligns?.map(() => "<col />").join("")}
|
|
1228
|
-
</colgroup>
|
|
1229
|
-
${await renderHTML(
|
|
1230
|
-
sy.Children?.find((el) => el.Type === "NodeTableHead"),
|
|
1231
|
-
this
|
|
1232
|
-
)}
|
|
1233
|
-
<tbody>
|
|
1234
|
-
${(await Promise.all(
|
|
1235
|
-
sy.Children?.filter((el) => el.Type === "NodeTableRow").map(
|
|
1236
|
-
(el) => renderHTML(el, this)
|
|
1237
|
-
) ?? []
|
|
1238
|
-
)).join("\n")}
|
|
1239
|
-
</tbody>
|
|
1240
|
-
</table>
|
|
1241
|
-
</div>
|
|
1242
|
-
</div>`;
|
|
1243
|
-
},
|
|
1244
|
-
async NodeTableHead(sy) {
|
|
1245
|
-
return `<${sy.Data}>${await childRender(sy, this)}</${sy.Data}>`;
|
|
1246
|
-
},
|
|
1247
|
-
async NodeTableRow(sy) {
|
|
1248
|
-
return `<tr>${await childRender(sy, this)}</tr>`;
|
|
1249
|
-
},
|
|
1250
|
-
async NodeTableCell(sy) {
|
|
1251
|
-
return `<td>${await childRender(sy, this)}</td>`;
|
|
1252
|
-
},
|
|
1253
|
-
NodeHTMLBlock: async (sy) => `<div ${strAttr(sy)}>${sy.Data}</div>`,
|
|
1254
|
-
NodeThematicBreak: async (sy) => `<div ${strAttr(sy)}><div></div></div>`,
|
|
1255
|
-
NodeMathBlock: async (sy) => `<div ${strAttr(
|
|
1256
|
-
sy
|
|
1257
|
-
)} data-content="${childDateByType(sy, "NodeMathBlockContent")}">
|
|
1258
|
-
<div spin="1"></div>
|
|
1259
|
-
</div>`,
|
|
1260
|
-
NodeMathBlockOpenMarker: _emptyString,
|
|
1261
|
-
NodeMathBlockCloseMarker: _emptyString,
|
|
1262
|
-
async NodeIFrame(sy) {
|
|
1263
|
-
return ` <div ${strAttr(sy)}>
|
|
1264
|
-
<div class="iframe-content">
|
|
1265
|
-
${/** 资源总是被复制到顶层目录,所以直接跳到顶层即可 */
|
|
1266
|
-
/** TODO 应该有一个统一处理资源的方案 */
|
|
1267
|
-
sy.Data?.replace(
|
|
1268
|
-
/src="assets\//,
|
|
1269
|
-
`src="${await this.getTopPathPrefix()}/assets/`
|
|
1270
|
-
)}
|
|
1271
|
-
</div>
|
|
1272
|
-
</div>`;
|
|
1273
|
-
},
|
|
1274
|
-
async NodeVideo(sy) {
|
|
1275
|
-
return await this.NodeIFrame(sy);
|
|
1276
|
-
},
|
|
1277
|
-
async NodeAudio(sy) {
|
|
1278
|
-
return await this.NodeIFrame(sy);
|
|
1279
|
-
},
|
|
1280
|
-
/** 虚拟链接 */
|
|
1281
|
-
NodeHeadingC8hMarker: _emptyString,
|
|
1282
|
-
async NodeSoftBreak(_sy) {
|
|
1283
|
-
return "\u200B";
|
|
1284
|
-
},
|
|
1285
|
-
async NodeBr(sy) {
|
|
1286
|
-
return `<${sy.Data}>`;
|
|
1287
|
-
},
|
|
1288
|
-
async NodeWidget(sy) {
|
|
1289
|
-
return `<div ${strAttr(
|
|
1290
|
-
sy
|
|
1291
|
-
)}><img src="${await this.getTopPathPrefix()}/assets/widget/${sy.ID}.jpg"/></div>`;
|
|
1292
|
-
},
|
|
1293
|
-
async NodeBackslash(sy) {
|
|
1294
|
-
if (sy.Data === void 0 || sy.Data === "span") {
|
|
1295
|
-
return `${await childRender(sy, this)}`;
|
|
1296
|
-
} else {
|
|
1297
|
-
return warnDiv(
|
|
1298
|
-
`\u672A\u5B9A\u4E49\u7684 NodeBackslash \u5904\u7406 ${sy.Data}`,
|
|
1299
|
-
this.nodeStack[0].Properties?.title
|
|
1300
|
-
);
|
|
1301
|
-
}
|
|
1302
|
-
},
|
|
1303
|
-
NodeBackslashContent: _dataString
|
|
1304
|
-
};
|
|
1305
|
-
function childDateByType(sy, type) {
|
|
1306
|
-
return sy.Children?.find((el) => el.Type === type)?.Data;
|
|
1307
|
-
}
|
|
1308
|
-
function warn(...arg) {
|
|
1309
|
-
console.warn("\n", ...arg);
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
703
|
// src/core/siyuan_type.ts
|
|
1313
704
|
function DB_block_path(p) {
|
|
1314
705
|
return `data/${p.box}${p.path}`;
|
|
@@ -1440,7 +831,7 @@ function parentRef(sy) {
|
|
|
1440
831
|
}
|
|
1441
832
|
|
|
1442
833
|
// src/core/genRssXml.ts
|
|
1443
|
-
async function generateRSSXML(path, renderInstance, config) {
|
|
834
|
+
async function generateRSSXML(path, renderInstance, config, getHPathByID_Node) {
|
|
1444
835
|
const refNode = (await Promise.all([...renderInstance.refs.values()].map(get_node_by_id))).filter((el) => el);
|
|
1445
836
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1446
837
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
@@ -1454,7 +845,7 @@ async function generateRSSXML(path, renderInstance, config) {
|
|
|
1454
845
|
refNode.map(
|
|
1455
846
|
async (node) => `<item>
|
|
1456
847
|
<title>${node?.Properties?.title}</title>
|
|
1457
|
-
<link>${config.sitemap.sitePrefix}${node?.ID ? await
|
|
848
|
+
<link>${config.sitemap.sitePrefix}${node?.ID ? await getHPathByID_Node(node?.ID) + ".html" : ""}</link>
|
|
1458
849
|
<description>${""}</description>
|
|
1459
850
|
<pubDate>${node?.Properties?.updated ? new Date(
|
|
1460
851
|
node.Properties.updated.replace(
|
|
@@ -1490,181 +881,1790 @@ function sitemap_xml(docArr, config) {
|
|
|
1490
881
|
</urlset>`;
|
|
1491
882
|
}
|
|
1492
883
|
|
|
1493
|
-
// src/core/
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
}
|
|
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
|
+
]));
|
|
1067
|
+
}
|
|
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;
|
|
1507
1082
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
const
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
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())) {
|
|
1519
1099
|
return false;
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1100
|
+
}
|
|
1101
|
+
if (token.match(/^[a-f0-9]{6,}$/)) return false;
|
|
1102
|
+
if (/[\u4e00-\u9fa5]/.test(token) && token.length < 2) {
|
|
1103
|
+
return false;
|
|
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
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return tokens;
|
|
1132
|
+
}
|
|
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)) {
|
|
1523
1161
|
return false;
|
|
1524
1162
|
}
|
|
1525
1163
|
}
|
|
1164
|
+
if (/(.)\1{2,}/.test(word)) {
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1526
1167
|
return true;
|
|
1527
1168
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
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("?")
|
|
1537
1232
|
);
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
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();
|
|
1546
1549
|
}
|
|
1547
|
-
return config.enableIncrementalCompilation_doc;
|
|
1548
1550
|
})();
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
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`
|
|
1569
2526
|
);
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
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 */";
|
|
1574
2538
|
}
|
|
1575
|
-
|
|
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
|
+
}
|
|
1576
2571
|
skipBuilds.add(docBlock.id, {
|
|
1577
|
-
|
|
2572
|
+
refs: (
|
|
2573
|
+
/** 保存引用 */
|
|
2574
|
+
[...renderInstance.refs.values()]
|
|
2575
|
+
)
|
|
1578
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);
|
|
1579
2581
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
[...renderInstance.refs.values()]
|
|
1584
|
-
)
|
|
1585
|
-
});
|
|
1586
|
-
} catch (error) {
|
|
1587
|
-
effect.log(`${path} \u6E32\u67D3\u5931\u8D25:${error}`);
|
|
1588
|
-
console.log(error);
|
|
1589
|
-
}
|
|
1590
|
-
process2(i / Doc_blocks.length);
|
|
1591
|
-
effect.log(`\u6E32\u67D3\u5B8C\u6BD5:${path}`);
|
|
1592
|
-
})
|
|
1593
|
-
);
|
|
1594
|
-
effect.log(`=== \u6E32\u67D3\u6587\u6863\u5B8C\u6210 ===`);
|
|
1595
|
-
effect.log(`=== \u5F00\u59CB\u751F\u6210 sitemap.xml ===`);
|
|
1596
|
-
if (config.sitemap.enable) {
|
|
1597
|
-
fileTree["sitemap.xml"] = sitemap_xml(Doc_blocks, config.sitemap);
|
|
1598
|
-
}
|
|
1599
|
-
if (config.excludeAssetsCopy === false) {
|
|
1600
|
-
effect.log(`=== \u5F00\u59CB\u590D\u5236\u8D44\u6E90\u6587\u4EF6 ===`);
|
|
1601
|
-
const assets = await API.query_sql({
|
|
1602
|
-
stmt: `SELECT * from assets
|
|
1603
|
-
WHERE box = '${book.id}'
|
|
1604
|
-
limit 150000 OFFSET 0`
|
|
2582
|
+
process2(i / Doc_blocks.length);
|
|
2583
|
+
return `\u6E32\u67D3\u5B8C\u6BD5:${path}`;
|
|
2584
|
+
});
|
|
1605
2585
|
});
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
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) {
|
|
1610
2631
|
return;
|
|
1611
2632
|
} else {
|
|
1612
|
-
|
|
1613
|
-
|
|
2633
|
+
const id = widget.ID;
|
|
2634
|
+
fileTree[`assets/widget/${id}.jpg`] = await API.file_getFile({
|
|
2635
|
+
path: `data/storage/oceanpress/widget_img/${id}.jpg`
|
|
1614
2636
|
});
|
|
1615
2637
|
if (config.enableIncrementalCompilation) {
|
|
1616
|
-
skipBuilds.add(
|
|
2638
|
+
skipBuilds.add(id, { updated: update });
|
|
1617
2639
|
}
|
|
1618
2640
|
}
|
|
1619
|
-
})
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
skipBuilds.add(id, { updated: update });
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
});
|
|
1650
|
-
await Promise.all(widgetNode);
|
|
1651
|
-
}
|
|
1652
|
-
if (otherConfig?.onFileTree) {
|
|
1653
|
-
otherConfig.onFileTree(fileTree);
|
|
1654
|
-
}
|
|
1655
|
-
if (config.compressedZip) {
|
|
1656
|
-
effect.log(`=== \u5F00\u59CB\u751F\u6210\u538B\u7F29\u5305 ===`);
|
|
1657
|
-
await downloadZIP(fileTree, {
|
|
1658
|
-
// TODO 这里应该移出来成为全局的写选项
|
|
1659
|
-
withoutZip: tempConfig.withoutPublicZip,
|
|
1660
|
-
publicZip: tempConfig.cdn.publicZip
|
|
1661
|
-
});
|
|
1662
|
-
}
|
|
1663
|
-
config.OceanPress.version = package_default.version;
|
|
1664
|
-
skipBuilds.write();
|
|
1665
|
-
effect.percentage(100);
|
|
1666
|
-
effect.log("\u7F16\u8BD1\u5B8C\u6BD5");
|
|
1667
|
-
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
|
+
});
|
|
1668
2668
|
}
|
|
1669
2669
|
function useSkipBuilds() {
|
|
1670
2670
|
const obj = {};
|
|
@@ -1682,55 +2682,73 @@ function useSkipBuilds() {
|
|
|
1682
2682
|
};
|
|
1683
2683
|
}
|
|
1684
2684
|
|
|
1685
|
-
// src/
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
headers: {
|
|
1707
|
-
"x-api-key": config.oceanPressServer.apiKey,
|
|
1708
|
-
"Content-Type": content_type
|
|
1709
|
-
},
|
|
1710
|
-
// @ts-expect-error 在 node 运行的时候需要声明双工模式才能正确发送 ReadableStream,TODO 需要验证浏览器端可以这样运行吗
|
|
1711
|
-
duplex: "half"
|
|
1712
|
-
// 关键:显式声明半双工模式
|
|
1713
|
-
}).then((res2) => res2.json()).then((r) => {
|
|
1714
|
-
if (r.error) {
|
|
1715
|
-
console.log("[r]", r);
|
|
1716
|
-
throw new Error();
|
|
1717
|
-
}
|
|
1718
|
-
return r.result;
|
|
1719
|
-
});
|
|
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
|
+
};
|
|
1720
2706
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
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
|
+
};
|
|
1734
2752
|
|
|
1735
2753
|
// src/core/ocean_press.ts
|
|
1736
2754
|
var OceanPress = class {
|
|
@@ -1742,7 +2760,7 @@ var OceanPress = class {
|
|
|
1742
2760
|
/** 用于渲染文档的函数 */
|
|
1743
2761
|
build_renderHTML: renderHTML,
|
|
1744
2762
|
/** 编译完成后文件树的处理回调函数 */
|
|
1745
|
-
build_onFileTree: (_tree) => {
|
|
2763
|
+
build_onFileTree: (_tree, _effectApi) => {
|
|
1746
2764
|
}
|
|
1747
2765
|
});
|
|
1748
2766
|
__publicField(this, "pluginCenter", new PluginCenter(
|
|
@@ -1762,8 +2780,8 @@ var OceanPress = class {
|
|
|
1762
2780
|
);
|
|
1763
2781
|
}
|
|
1764
2782
|
}
|
|
1765
|
-
|
|
1766
|
-
const build_res = this.pluginCenter.fun.build(this.config,
|
|
2783
|
+
build() {
|
|
2784
|
+
const build_res = this.pluginCenter.fun.build(this.config, {
|
|
1767
2785
|
renderHtmlFn: this.pluginCenter.fun.build_renderHTML,
|
|
1768
2786
|
onFileTree: this.pluginCenter.fun.build_onFileTree
|
|
1769
2787
|
});
|
|
@@ -1772,45 +2790,52 @@ var OceanPress = class {
|
|
|
1772
2790
|
};
|
|
1773
2791
|
|
|
1774
2792
|
// src/core/render.api.dep.ts
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
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
|
+
}
|
|
1783
2803
|
}
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
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
|
+
}
|
|
1806
2827
|
}
|
|
2828
|
+
},
|
|
2829
|
+
log(msg) {
|
|
2830
|
+
console.log(msg);
|
|
2831
|
+
},
|
|
2832
|
+
percentage: (n) => {
|
|
2833
|
+
console.log(n);
|
|
1807
2834
|
}
|
|
1808
2835
|
};
|
|
1809
2836
|
|
|
1810
2837
|
// src/util/store.node.dep.ts
|
|
1811
|
-
import { writeFileSync, readFileSync, existsSync, mkdirSync } from "
|
|
1812
|
-
storeDep.getItem = getItem;
|
|
1813
|
-
storeDep.setItem = setItem;
|
|
2838
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync } from "fs";
|
|
1814
2839
|
function setItem(key, value) {
|
|
1815
2840
|
if (!existsSync("./store/")) {
|
|
1816
2841
|
mkdirSync("./store/", { recursive: true });
|
|
@@ -1826,6 +2851,10 @@ function getItem(key) {
|
|
|
1826
2851
|
return void 0;
|
|
1827
2852
|
}
|
|
1828
2853
|
}
|
|
2854
|
+
var nodeApiDep = {
|
|
2855
|
+
setItem,
|
|
2856
|
+
getItem
|
|
2857
|
+
};
|
|
1829
2858
|
|
|
1830
2859
|
// src/cli/common.ts
|
|
1831
2860
|
import { Command } from "commander";
|
|
@@ -1833,12 +2862,12 @@ var program = new Command();
|
|
|
1833
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");
|
|
1834
2863
|
|
|
1835
2864
|
// src/cli/deploy.ts
|
|
2865
|
+
import { Context as Context3, Effect as Effect5 } from "effect";
|
|
1836
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) => {
|
|
1837
2867
|
if (!opt.apiBase || !opt.apiKey) {
|
|
1838
2868
|
return console.error(`\u8BF7\u914D\u7F6E apiBase \u548C apiKey`);
|
|
1839
2869
|
}
|
|
1840
2870
|
const config = await readFile(opt.config, "utf-8");
|
|
1841
|
-
loadConfigFile(JSON.parse(config));
|
|
1842
2871
|
const client = await createRPC("apiConsumer", {
|
|
1843
2872
|
remoteCall(method, data) {
|
|
1844
2873
|
let body;
|
|
@@ -1870,79 +2899,115 @@ program.command("deploy").description("\u90E8\u7F72\u7AD9\u70B9").option("-c, --
|
|
|
1870
2899
|
});
|
|
1871
2900
|
}
|
|
1872
2901
|
});
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
console.log("[deploy res]", res);
|
|
1884
|
-
}
|
|
1885
|
-
});
|
|
1886
|
-
await ocean_press.build({
|
|
1887
|
-
log: (msg) => {
|
|
1888
|
-
if (msg.startsWith("\u6E32\u67D3\uFF1A")) {
|
|
1889
|
-
process.stdout.write(`\r\x1B[K${msg}`);
|
|
1890
|
-
} else {
|
|
1891
|
-
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(`
|
|
1892
2912
|
${msg}`);
|
|
2913
|
+
}
|
|
2914
|
+
},
|
|
2915
|
+
percentage: (n) => {
|
|
2916
|
+
process.stdout.write(`\r\x1B[K\u8FDB\u5EA6\uFF1A${n}%`);
|
|
1893
2917
|
}
|
|
1894
|
-
}
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
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);
|
|
1899
2943
|
});
|
|
1900
2944
|
|
|
1901
2945
|
// src/cli/build.ts
|
|
1902
2946
|
import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
1903
2947
|
import { resolve } from "path";
|
|
1904
2948
|
import { join } from "path/posix";
|
|
2949
|
+
import { Context as Context4, Effect as Effect6 } from "effect";
|
|
1905
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) => {
|
|
1906
2951
|
if (!opt.config || !opt.output) {
|
|
1907
2952
|
console.log(`\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E\u548C\u8F93\u51FA\u76EE\u5F55\u4F4D\u7F6E`);
|
|
1908
2953
|
throw new Error("\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E\u548C\u8F93\u51FA\u76EE\u5F55\u4F4D\u7F6E");
|
|
1909
2954
|
}
|
|
1910
2955
|
const config = await readFile2(opt.config, "utf-8");
|
|
1911
|
-
loadConfigFile(JSON.parse(config));
|
|
1912
2956
|
const filePath = resolve(opt.output);
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
|
-
});
|
|
1933
|
-
await ocean_press.build({
|
|
1934
|
-
log: (msg) => {
|
|
1935
|
-
if (msg.startsWith("\u6E32\u67D3\uFF1A")) {
|
|
1936
|
-
process.stdout.write(`\r\x1B[K${msg}`);
|
|
1937
|
-
} else {
|
|
1938
|
-
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(`
|
|
1939
2974
|
${msg}`);
|
|
2975
|
+
}
|
|
2976
|
+
},
|
|
2977
|
+
percentage: (n) => {
|
|
2978
|
+
process.stdout.write(`\r\x1B[K\u8FDB\u5EA6\uFF1A${n}%`);
|
|
1940
2979
|
}
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
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);
|
|
1946
3011
|
});
|
|
1947
3012
|
|
|
1948
3013
|
// src/cli/server.ts
|
|
@@ -1950,28 +3015,35 @@ import { readFile as readFile3 } from "fs/promises";
|
|
|
1950
3015
|
|
|
1951
3016
|
// src/server.ts
|
|
1952
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";
|
|
1953
3021
|
|
|
1954
3022
|
// src/core/hono_server.ts
|
|
1955
3023
|
import { Hono } from "hono";
|
|
1956
3024
|
import { stream } from "hono/streaming";
|
|
1957
|
-
|
|
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
|
+
});
|
|
1958
3039
|
app.get("/", (c) => c.redirect("/index.html"));
|
|
1959
3040
|
app.get("/assets/*", assetsHandle);
|
|
3041
|
+
app.get("/favicon.ico", assetsHandle);
|
|
1960
3042
|
app.get("*", async (c) => {
|
|
1961
3043
|
const path = decodeURIComponent(c.req.path);
|
|
1962
|
-
const
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
}
|
|
1966
|
-
throw err;
|
|
1967
|
-
});
|
|
1968
|
-
if (r instanceof Error) {
|
|
1969
|
-
throw r;
|
|
1970
|
-
} else if (typeof r === "string") {
|
|
1971
|
-
return c.html(r);
|
|
1972
|
-
} else {
|
|
1973
|
-
return r;
|
|
1974
|
-
}
|
|
3044
|
+
const p = Effect7.provide(renderHtmlByUriPath(path), context);
|
|
3045
|
+
const r = await Effect7.runPromise(p);
|
|
3046
|
+
return c.html(r);
|
|
1975
3047
|
});
|
|
1976
3048
|
return app;
|
|
1977
3049
|
}
|
|
@@ -2009,55 +3081,60 @@ async function assetsHandle(c) {
|
|
|
2009
3081
|
}
|
|
2010
3082
|
});
|
|
2011
3083
|
}
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
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
|
+
});
|
|
2026
3103
|
}
|
|
2027
3104
|
|
|
2028
3105
|
// src/server.ts
|
|
2029
|
-
import { serveStatic } from "@hono/node-server/serve-static";
|
|
2030
|
-
import { Hono as Hono2 } from "hono";
|
|
2031
|
-
import { join as join2 } from "path/posix";
|
|
2032
|
-
console.log(join2(import.meta.url.slice(5), "../../public/"));
|
|
2033
3106
|
function server(config = { port: 80, hostname: "0.0.0.0" }) {
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
createHonoApp(app);
|
|
2045
|
-
return new Promise((resolve3, _reject) => {
|
|
2046
|
-
serve(
|
|
2047
|
-
{
|
|
2048
|
-
fetch: app.fetch,
|
|
2049
|
-
port: config.port,
|
|
2050
|
-
hostname: config.hostname
|
|
2051
|
-
},
|
|
2052
|
-
(info) => {
|
|
2053
|
-
resolve3({ info, app });
|
|
2054
|
-
console.log(`Listening on http://${info.address}:${info.port}`);
|
|
2055
|
-
}
|
|
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
|
+
})
|
|
2056
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
|
+
});
|
|
2057
3133
|
});
|
|
2058
3134
|
}
|
|
2059
3135
|
|
|
2060
3136
|
// src/cli/server.ts
|
|
3137
|
+
import { Context as Context6, Effect as Effect9 } from "effect";
|
|
2061
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(
|
|
2062
3139
|
"--cache <boolean>",
|
|
2063
3140
|
"\u914D\u7F6E\u4E3A true \u65F6\u5F00\u542F\u7F13\u5B58,\u9ED8\u8BA4\u4E3A false \u4E0D\u5F00\u542F\u7F13\u5B58",
|
|
@@ -2068,12 +3145,36 @@ program.command("server").description("\u542F\u52A8\u52A8\u6001\u4EE3\u7406").op
|
|
|
2068
3145
|
console.log(`\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E`);
|
|
2069
3146
|
}
|
|
2070
3147
|
const config = await readFile3(opt.config, "utf-8");
|
|
2071
|
-
loadConfigFile(JSON.parse(config));
|
|
2072
3148
|
setCache(opt.cache !== "false");
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
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);
|
|
2077
3178
|
}
|
|
2078
3179
|
);
|
|
2079
3180
|
|