koishi-plugin-fimtale-api 0.0.9 → 0.0.95
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/lib/index.js +54 -66
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -47,7 +47,7 @@ var Config = import_koishi.Schema.object({
|
|
|
47
47
|
cookies: import_koishi.Schema.string().role("secret").description("浏览器 Cookie (用于解除安全模式,必填)"),
|
|
48
48
|
pollInterval: import_koishi.Schema.number().default(60 * 1e3).description("追更轮询间隔(ms)"),
|
|
49
49
|
autoParseLink: import_koishi.Schema.boolean().default(true).description("自动解析链接为预览卡片"),
|
|
50
|
-
// 渲染配置 (iPhone 12/13
|
|
50
|
+
// 渲染配置 (iPhone 12/13 标准)
|
|
51
51
|
deviceWidth: import_koishi.Schema.number().default(390).description("阅读器渲染宽度(px)"),
|
|
52
52
|
deviceHeight: import_koishi.Schema.number().default(844).description("阅读器渲染高度(px)"),
|
|
53
53
|
fontSize: import_koishi.Schema.number().default(20).description("正文字号(px)")
|
|
@@ -62,7 +62,7 @@ function apply(ctx, config) {
|
|
|
62
62
|
}, { primary: "id", autoInc: true });
|
|
63
63
|
const sleep = /* @__PURE__ */ __name((ms) => new Promise((resolve) => setTimeout(resolve, ms)), "sleep");
|
|
64
64
|
const formatDate = /* @__PURE__ */ __name((timestamp) => {
|
|
65
|
-
if (!timestamp) return "
|
|
65
|
+
if (!timestamp) return "Unknown";
|
|
66
66
|
const date = new Date(timestamp * 1e3);
|
|
67
67
|
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
|
|
68
68
|
}, "formatDate");
|
|
@@ -83,9 +83,9 @@ function apply(ctx, config) {
|
|
|
83
83
|
}, "generateGradient");
|
|
84
84
|
const cleanContent = /* @__PURE__ */ __name((html) => {
|
|
85
85
|
if (!html) return "";
|
|
86
|
-
return html.replace(
|
|
86
|
+
return html.replace(/style="[^"]*"/gi, "").replace(/<p[^>]*>\s*( |<br\s*\/?>|\s)*\s*<\/p>/gi, "").replace(/(<br\s*\/?>\s*){2,}/gi, "<br>").trim();
|
|
87
87
|
}, "cleanContent");
|
|
88
|
-
const fontStack = '"
|
|
88
|
+
const fontStack = '"Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", "WenQuanYi Micro Hei", Arial, sans-serif';
|
|
89
89
|
const fontSerif = '"Noto Serif SC", "Source Han Serif SC", "SimSun", serif';
|
|
90
90
|
const injectCookies = /* @__PURE__ */ __name(async (page) => {
|
|
91
91
|
if (!config.cookies) return;
|
|
@@ -235,19 +235,12 @@ function apply(ctx, config) {
|
|
|
235
235
|
<head>
|
|
236
236
|
<style>
|
|
237
237
|
body { margin: 0; padding: 0; font-family: ${fontStack}; background: transparent; }
|
|
238
|
-
.card { width: 620px; height:
|
|
238
|
+
.card { width: 620px; height: 360px; background: #fff; border-radius: 16px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); display: flex; overflow: hidden; }
|
|
239
239
|
.cover { width: 220px; height: 100%; ${bgStyle} background-size: cover; background-position: center; position: relative; flex-shrink: 0; }
|
|
240
240
|
.id-tag { position: absolute; top: 12px; left: 12px; background: rgba(0,0,0,0.6); color: #fff; padding: 4px 10px; border-radius: 6px; font-size: 13px; font-weight: bold; backdrop-filter: blur(4px); font-family: monospace; }
|
|
241
|
-
|
|
242
|
-
.info {
|
|
243
|
-
flex: 1; padding: 24px;
|
|
244
|
-
display: flex; flex-direction: column;
|
|
245
|
-
/* 关键:内容溢出隐藏,防止撑开 */
|
|
246
|
-
overflow: hidden;
|
|
247
|
-
}
|
|
241
|
+
.info { flex: 1; padding: 24px; display: flex; flex-direction: column; justify-content: space-between; overflow: hidden; }
|
|
248
242
|
|
|
249
243
|
.header-group { display: flex; flex-direction: column; flex-shrink: 0; }
|
|
250
|
-
|
|
251
244
|
.title {
|
|
252
245
|
font-size: 22px; font-weight: 700; color: #333; line-height: 1.35;
|
|
253
246
|
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
|
@@ -259,29 +252,23 @@ function apply(ctx, config) {
|
|
|
259
252
|
}
|
|
260
253
|
.author { font-size: 13px; color: #888; margin-top: 8px; font-weight: 400; }
|
|
261
254
|
|
|
262
|
-
|
|
263
|
-
.tags {
|
|
264
|
-
display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0;
|
|
265
|
-
max-height: 56px; overflow: hidden; flex-shrink: 0;
|
|
266
|
-
}
|
|
255
|
+
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; max-height: 56px; overflow: hidden; flex-shrink: 0;}
|
|
267
256
|
.tag { background: #eff2f5; color: #5c6b7f; padding: 3px 9px; border-radius: 4px; font-size: 11px; font-weight: 500; }
|
|
268
257
|
.tag-imp { background: #e3f2fd; color: #1565c0; }
|
|
269
258
|
|
|
270
|
-
/*
|
|
259
|
+
/* 简介区域防截断优化 */
|
|
271
260
|
.summary-box {
|
|
272
261
|
flex: 1; position: relative; margin-top: 4px; overflow: hidden;
|
|
273
262
|
}
|
|
274
263
|
.summary {
|
|
275
|
-
font-size: 13px; color: #666;
|
|
264
|
+
font-size: 13px; color: #666;
|
|
265
|
+
line-height: 1.5; /* 设定标准行高 */
|
|
266
|
+
max-height: 6em; /* 4行 = 1.5 * 4 */
|
|
276
267
|
display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
|
|
268
|
+
padding-bottom: 2px; /* 底部缓冲 */
|
|
277
269
|
}
|
|
278
270
|
|
|
279
|
-
|
|
280
|
-
.footer {
|
|
281
|
-
border-top: 1px solid #eee; padding-top: 14px;
|
|
282
|
-
display: flex; justify-content: space-between;
|
|
283
|
-
font-size: 12px; color: #888; margin-top: 15px; flex-shrink: 0;
|
|
284
|
-
}
|
|
271
|
+
.footer { border-top: 1px solid #eee; padding-top: 14px; display: flex; justify-content: space-between; font-size: 12px; color: #888; margin-top: 15px; flex-shrink: 0;}
|
|
285
272
|
.stat b { color: #555; font-weight: bold; margin-right: 2px;}
|
|
286
273
|
</style>
|
|
287
274
|
</head>
|
|
@@ -360,7 +347,7 @@ function apply(ctx, config) {
|
|
|
360
347
|
<div class="top-row"><div class="title">${r.title}</div><div class="id-badge">ID: ${r.id}</div></div>
|
|
361
348
|
<div class="author">By ${r.author} ${r.status ? ` · ${r.status}` : ""}</div>
|
|
362
349
|
<div class="tags">${r.tags.map((t) => `<span class="tag">${t}</span>`).join("")}</div>
|
|
363
|
-
<div class="meta-row">${stats || "
|
|
350
|
+
<div class="meta-row">${stats || "No Data"}</div>
|
|
364
351
|
</div></div>`;
|
|
365
352
|
}).join("")}
|
|
366
353
|
</div>
|
|
@@ -383,70 +370,71 @@ function apply(ctx, config) {
|
|
|
383
370
|
<head>
|
|
384
371
|
<style>
|
|
385
372
|
body { margin: 0; padding: 0; width: ${config.deviceWidth}px; background-color: #f6f4ec; color: #2c2c2c; font-family: ${fontSerif}; }
|
|
373
|
+
#source-container { display: none; }
|
|
374
|
+
.page {
|
|
375
|
+
width: ${config.deviceWidth}px; height: ${config.deviceHeight}px;
|
|
376
|
+
/* 增加 padding,减少每页内容量,看起来更舒适 */
|
|
377
|
+
padding: 40px 30px; box-sizing: border-box;
|
|
378
|
+
position: relative; background: #f6f4ec; overflow: hidden;
|
|
379
|
+
/* 关键:CSS多列布局,无需手动分页 */
|
|
380
|
+
display: flex; flex-direction: column;
|
|
381
|
+
}
|
|
386
382
|
|
|
387
|
-
/*
|
|
388
|
-
/* 固定高度,让 CSS Column 生效 */
|
|
383
|
+
/* 自定义 CSS Column 容器 */
|
|
389
384
|
#content-wrapper {
|
|
390
|
-
width: ${config.deviceWidth}px;
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
column-gap: 0;
|
|
394
|
-
|
|
395
|
-
/* 强制水平滚动 */
|
|
396
|
-
overflow-y: hidden;
|
|
397
|
-
overflow-x: scroll;
|
|
385
|
+
width: ${config.deviceWidth}px; height: ${config.deviceHeight}px;
|
|
386
|
+
column-width: ${config.deviceWidth}px; column-gap: 0; column-fill: auto;
|
|
387
|
+
overflow-x: hidden; overflow-y: hidden; /* 隐藏滚动条 */
|
|
398
388
|
}
|
|
399
389
|
|
|
400
|
-
/*
|
|
401
|
-
/* 我们需要用 padding 来模拟页眉页脚的空间 */
|
|
402
|
-
/* 上留 50px,下留 40px,左右 25px */
|
|
390
|
+
/* 实际内容区 */
|
|
403
391
|
.inner-content {
|
|
404
|
-
padding: 50px 25px 40px 25px;
|
|
392
|
+
padding: 50px 25px 40px 25px; /* 避让页眉页脚 */
|
|
405
393
|
box-sizing: border-box;
|
|
406
|
-
height: 100%;
|
|
407
|
-
|
|
394
|
+
height: 100%;
|
|
408
395
|
font-size: ${config.fontSize}px;
|
|
409
396
|
line-height: 1.8;
|
|
410
397
|
text-align: justify;
|
|
411
398
|
}
|
|
412
399
|
|
|
413
|
-
/*
|
|
400
|
+
/* 固定页眉页脚 (Overlay) */
|
|
414
401
|
.fixed-header {
|
|
415
|
-
position:
|
|
416
|
-
background: #f6f4ec;
|
|
417
|
-
z-index: 10;
|
|
402
|
+
position: absolute; top: 0; left: 0; width: 100%; height: 40px;
|
|
403
|
+
background: #f6f4ec; z-index: 10;
|
|
418
404
|
border-bottom: 1px solid #d7ccc8;
|
|
419
405
|
display: flex; align-items: center; justify-content: space-between;
|
|
420
|
-
padding: 0
|
|
406
|
+
padding: 0 25px; box-sizing: border-box;
|
|
421
407
|
font-size: 12px; color: #8d6e63; font-weight: bold; font-family: ${fontStack};
|
|
422
408
|
}
|
|
423
409
|
.fixed-footer {
|
|
424
|
-
position:
|
|
425
|
-
background: #f6f4ec;
|
|
426
|
-
z-index: 10;
|
|
410
|
+
position: absolute; bottom: 0; left: 0; width: 100%; height: 30px;
|
|
411
|
+
background: #f6f4ec; z-index: 10;
|
|
427
412
|
display: flex; align-items: center; justify-content: center;
|
|
428
413
|
font-size: 12px; color: #aaa; font-family: ${fontStack};
|
|
429
414
|
}
|
|
430
415
|
|
|
416
|
+
/* 排版细节 */
|
|
431
417
|
p { margin: 0 0 0.8em 0; text-indent: 2em; }
|
|
432
|
-
/* 防止图片被切分 */
|
|
433
418
|
img { max-width: 100%; height: auto; display: block; margin: 15px auto; border-radius: 6px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); break-inside: avoid; }
|
|
434
|
-
h1, h2, h3 { font-size: 1.
|
|
419
|
+
h1, h2, h3 { font-size: 1.1em; margin: 0.8em 0; color: #5d4037; text-indent: 0; font-weight: bold; break-after: avoid; }
|
|
435
420
|
</style>
|
|
436
421
|
</head>
|
|
437
422
|
<body>
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
</div>
|
|
447
|
-
</div>
|
|
423
|
+
<div id="source-container"></div>
|
|
424
|
+
|
|
425
|
+
<!-- 使用 CSS Columns 进行分页布局 -->
|
|
426
|
+
<div id="content-wrapper">
|
|
427
|
+
<div class="inner-content">
|
|
428
|
+
${content}
|
|
429
|
+
</div>
|
|
430
|
+
</div>
|
|
448
431
|
|
|
449
|
-
|
|
432
|
+
<!-- 覆盖层:页眉页脚,截图时通过 JS 更新 -->
|
|
433
|
+
<div class="fixed-header">
|
|
434
|
+
<span>${info.Title.substring(0, 12) + (info.Title.length > 12 ? "..." : "")}</span>
|
|
435
|
+
<span>${info.UserName}</span>
|
|
436
|
+
</div>
|
|
437
|
+
<div class="fixed-footer" id="page-indicator">- 1 -</div>
|
|
450
438
|
</body></html>`;
|
|
451
439
|
const page = await ctx.puppeteer.page();
|
|
452
440
|
try {
|
|
@@ -458,12 +446,12 @@ function apply(ctx, config) {
|
|
|
458
446
|
const imgs = [];
|
|
459
447
|
for (let i = 0; i < totalPages; i++) {
|
|
460
448
|
await page.evaluate((x) => {
|
|
461
|
-
document.getElementById("content-wrapper").
|
|
449
|
+
document.getElementById("content-wrapper").scrollTo(x, 0);
|
|
462
450
|
}, i * config.deviceWidth);
|
|
463
451
|
await page.evaluate((curr, total) => {
|
|
464
452
|
document.getElementById("page-indicator").innerText = `- ${curr} / ${total} -`;
|
|
465
453
|
}, i + 1, totalPages);
|
|
466
|
-
const img = await page.screenshot({ type: "jpeg", quality:
|
|
454
|
+
const img = await page.screenshot({ type: "jpeg", quality: 80 });
|
|
467
455
|
imgs.push(img);
|
|
468
456
|
}
|
|
469
457
|
return imgs;
|