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.
Files changed (2) hide show
  1. package/lib/index.js +54 -66
  2. 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/14 标准)
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(/<p>\s*&nbsp;\s*<\/p>/gi, "").replace(/<p>\s*<br\s*\/?>\s*<\/p>/gi, "").replace(/<p>\s*<\/p>/gi, "").replace(/(<br\s*\/?>){2,}/gi, "<br>").replace(/margin-bottom:\s*\d+px/gi, "");
86
+ return html.replace(/style="[^"]*"/gi, "").replace(/<p[^>]*>\s*(&nbsp;|<br\s*\/?>|\s)*\s*<\/p>/gi, "").replace(/(<br\s*\/?>\s*){2,}/gi, "<br>").trim();
87
87
  }, "cleanContent");
88
- const fontStack = '"Noto Sans SC", "Microsoft YaHei", "PingFang SC", sans-serif';
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: 340px; background: #fff; border-radius: 16px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); display: flex; overflow: hidden; }
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
- /* 标签栏 flex-shrink 设为 0,保证标签不被压缩 */
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; line-height: 1.6;
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 || "暂无数据"}</div>
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
- height: ${config.deviceHeight}px;
392
- column-width: ${config.deviceWidth}px;
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
- /* 固定位置的页眉页脚 (overlay) */
400
+ /* 固定页眉页脚 (Overlay) */
414
401
  .fixed-header {
415
- position: fixed; top: 0; left: 0; width: 100%; height: 40px;
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 20px; box-sizing: border-box;
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: fixed; bottom: 0; left: 0; width: 100%; height: 30px;
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.2em; margin: 0.8em 0; color: #5d4037; text-indent: 0; font-weight: bold; break-after: avoid; }
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
- <div class="fixed-header">
439
- <span>${info.Title.substring(0, 12) + (info.Title.length > 12 ? "..." : "")}</span>
440
- <span>${info.UserName}</span>
441
- </div>
442
-
443
- <div id="content-wrapper">
444
- <div class="inner-content">
445
- ${content}
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
- <div class="fixed-footer" id="page-indicator">- 1 -</div>
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").scrollLeft = x;
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: 100 });
454
+ const img = await page.screenshot({ type: "jpeg", quality: 80 });
467
455
  imgs.push(img);
468
456
  }
469
457
  return imgs;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-fimtale-api",
3
3
  "description": "Koishi插件,从fimtale搜索/订阅/随机获取小说/解析链接等",
4
- "version": "0.0.9",
4
+ "version": "0.0.95",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [