koishi-plugin-fimtale-api 1.0.4 → 1.0.6

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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Context, Schema } from 'koishi';
2
- export declare const name = "fimtale-watcher";
2
+ export declare const name = "fimtale-api";
3
3
  export declare const inject: string[];
4
4
  declare module 'koishi' {
5
5
  interface Tables {
package/lib/index.js CHANGED
@@ -38,7 +38,7 @@ __export(src_exports, {
38
38
  module.exports = __toCommonJS(src_exports);
39
39
  var import_koishi = require("koishi");
40
40
  var import_crypto = __toESM(require("crypto"));
41
- var name = "fimtale-watcher";
41
+ var name = "fimtale-api";
42
42
  var inject = ["puppeteer", "database", "http"];
43
43
  var Config = import_koishi.Schema.object({
44
44
  apiUrl: import_koishi.Schema.string().default("https://fimtale.com/api/v1").description("Fimtale API 基础路径"),
@@ -47,7 +47,6 @@ 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
- // 渲染配置 (标准手机比例)
51
50
  deviceWidth: import_koishi.Schema.number().default(390).description("阅读器渲染宽度(px)"),
52
51
  deviceHeight: import_koishi.Schema.number().default(844).description("阅读器渲染高度(px)"),
53
52
  fontSize: import_koishi.Schema.number().default(20).description("正文字号(px)")
@@ -85,7 +84,7 @@ function apply(ctx, config) {
85
84
  processed = processed.replace(/<div class="card-panel[\s\S]*?<\/div>/i, "");
86
85
  processed = processed.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gmi, "");
87
86
  processed = processed.replace(/<style\b[^>]*>([\s\S]*?)<\/style>/gmi, "");
88
- processed = processed.replace(/<iframe[^>]*>.*?<\/iframe>/gmi, '<p class="align-center" style="color:#999;font-size:0.8em;">[多媒体内容]</p>');
87
+ processed = processed.replace(/<iframe[^>]*>.*?<\/iframe>/gmi, '<p class="align-center" style="color:#78909C;font-size:0.8em;">[多媒体内容]</p>');
89
88
  processed = processed.replace(
90
89
  /<div class="material-placeholder">([\s\S]*?)<\/div>/gi,
91
90
  (match, content) => {
@@ -212,34 +211,74 @@ function apply(ctx, config) {
212
211
  const likes = isChapter && parent ? parent.Upvotes || 0 : info.Upvotes || 0;
213
212
  const html = `<!DOCTYPE html><html><head><style>
214
213
  body { margin: 0; padding: 0; font-family: ${fontStack}; background: transparent; }
215
- .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; }
216
- .cover { width: 220px; height: 100%; ${bgStyle} background-size: cover; background-position: center; position: relative; flex-shrink: 0; }
217
- .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; }
218
- .info { flex: 1; padding: 24px; display: flex; flex-direction: column; overflow: hidden; position: relative; }
214
+ .card { width: 620px; min-height: 420px; background: #fff; border-radius: 16px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); display: flex; overflow: hidden; }
215
+ .cover { width: 220px; min-height: 100%; ${bgStyle} background-size: cover; background-position: center; position: relative; flex-shrink: 0; }
219
216
 
220
- /* 头部区域优化:增加下划线和间距 */
221
- .header-group { flex-shrink: 0; margin-bottom: 16px; border-bottom: 1px dashed #f0f0f0; padding-bottom: 12px; }
222
- .title { font-size: 22px; font-weight: 700; color: #333; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 4px; }
223
- .subtitle { font-size: 15px; color: #555; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-left: 10px; border-left: 3px solid #e91e63; margin-top: 4px; }
217
+ /* ID Badge 分体式:使用 flex 强制垂直居中,消除字体基线差异 */
218
+ .id-badge-container {
219
+ position: absolute; top: 15px; left: 15px;
220
+ display: flex;
221
+ box-shadow: 0 4px 12px rgba(238,110,115, 0.3);
222
+ border-radius: 6px;
223
+ overflow: hidden;
224
+ border: 1px solid rgba(255,255,255,0.3);
225
+ height: 28px; /* 强制高度 */
226
+ }
227
+ .id-label {
228
+ background: #EE6E73;
229
+ color: #fff;
230
+ padding: 0 10px;
231
+ font-size: 12px;
232
+ font-weight: bold;
233
+ font-family: sans-serif;
234
+ text-transform: uppercase;
235
+ display: flex; align-items: center; justify-content: center;
236
+ height: 100%;
237
+ line-height: 1; margin: 0; /* 修复对齐 */
238
+ }
239
+ .id-val {
240
+ background: #fff;
241
+ color: #EE6E73;
242
+ padding: 0 12px;
243
+ font-family: "Consolas", "Monaco", monospace;
244
+ font-size: 15px;
245
+ font-weight: 900;
246
+ display: flex; align-items: center; justify-content: center;
247
+ height: 100%;
248
+ line-height: 1; margin: 0; /* 修复对齐 */
249
+ }
250
+
251
+ .info { flex: 1; padding: 26px; display: flex; flex-direction: column; overflow: hidden; position: relative; }
224
252
 
225
- /* 作者栏优化:增加顶部间距,避免紧贴标签 */
226
- .author { font-size: 14px; color: #777; margin-top: 10px; font-weight: 400; display:flex; align-items:center; }
253
+ .header-group { flex-shrink: 0; margin-bottom: 16px; border-bottom: 2px solid #f5f5f5; padding-bottom: 12px; }
254
+ .title { font-size: 24px; font-weight: 700; color: #333; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 6px; }
255
+ .subtitle { font-size: 16px; color: #78909C; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-left: 12px; border-left: 4px solid #EE6E73; margin-top: 6px; }
227
256
 
228
- .tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; flex-shrink: 0; }
257
+ .author { font-size: 14px; color: #78909C; margin-top: 12px; font-weight: 400; display:flex; align-items:center; }
258
+
259
+ .tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; flex-shrink: 0; }
229
260
  .tag { background: #eff2f5; color: #5c6b7f; padding: 3px 9px; border-radius: 4px; font-size: 11px; font-weight: 500; }
230
- .summary-box { flex: 1; position: relative; overflow: hidden; min-height: 0; }
231
- .summary { font-size: 13px; color: #666; line-height: 1.6; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; padding-bottom: 3px; }
232
- .footer { border-top: 1px solid #eee; padding-top: 14px; display: flex; justify-content: space-between; font-size: 12px; color: #888; margin-top: auto; flex-shrink: 0; }
233
- .stat b { color: #555; font-weight: bold; margin-right: 2px;}
261
+
262
+ .summary-box { flex: 1; position: relative; overflow: hidden; min-height: 0; margin-bottom: 16px; }
263
+ .summary { font-size: 14px; color: #546e7a; line-height: 1.7; display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical; overflow: hidden; text-align: justify; }
264
+
265
+ .footer { border-top: 1px solid #eee; padding-top: 14px; display: flex; justify-content: space-between; font-size: 13px; color: #78909C; margin-top: auto; flex-shrink: 0; }
266
+ .stat b { color: #455a64; font-weight: bold; margin-right: 3px;}
234
267
  </style></head><body>
235
- <div class="card"><div class="cover"><div class="id-tag">ID: ${info.ID}</div></div>
268
+ <div class="card">
269
+ <div class="cover">
270
+ <div class="id-badge-container">
271
+ <div class="id-label">ID</div>
272
+ <div class="id-val">${info.ID}</div>
273
+ </div>
274
+ </div>
236
275
  <div class="info">
237
276
  <div class="header-group"><div class="title">${displayTitle}</div>${subTitle ? `<div class="subtitle">${subTitle}</div>` : ""}<div class="author">@${info.UserName}</div></div>
238
277
  <div class="tags">${tagsArr.slice(0, 10).map((t) => `<span class="tag">${t}</span>`).join("")}</div>
239
278
  <div class="summary-box"><div class="summary">${summary}</div></div>
240
279
  <div class="footer">
241
- <span class="stat"><b style="color:#009688">热度</b>${info.Views || 0}</span><span class="stat"><b style="color:#673ab7">评论</b>${info.Comments || 0}</span>
242
- <span class="stat"><b style="color:#4caf50">赞</b>${likes}</span><span class="stat"><b style="color:#795548">字数</b>${info.WordCount || 0}</span>
280
+ <span class="stat"><b style="color:#009688">热度</b>${info.Views || 0}</span><span class="stat"><b style="color:#7e57c2">评论</b>${info.Comments || 0}</span>
281
+ <span class="stat"><b style="color:#4caf50">赞</b>${likes}</span><span class="stat"><b style="color:#8d6e63">字数</b>${info.WordCount || 0}</span>
243
282
  </div></div></div></body></html>`;
244
283
  const page = await ctx.puppeteer.page();
245
284
  try {
@@ -265,8 +304,40 @@ function apply(ctx, config) {
265
304
  .content { flex: 1; display: flex; flex-direction: column; justify-content: space-between; height: 100%; min-width: 0; }
266
305
  .top-row { display: flex; justify-content: space-between; align-items: flex-start; }
267
306
  .title { font-size: 16px; font-weight: bold; color: #222; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; flex:1; margin-right: 8px;}
268
- .id-badge { background: #455a64; color: #fff; padding: 2px 6px; border-radius: 4px; font-family: monospace; font-size: 11px; font-weight: bold; flex-shrink: 0; }
269
- .author { font-size: 12px; color: #666; }
307
+
308
+ /* ID Badge Search: 迷你分体式,保持与大图风格一致但更紧凑 */
309
+ .id-badge {
310
+ display: flex;
311
+ border-radius: 4px;
312
+ overflow: hidden;
313
+ border: 1px solid #EE6E73;
314
+ flex-shrink: 0;
315
+ height: 18px;
316
+ }
317
+ .id-label {
318
+ background: #EE6E73;
319
+ color: #fff;
320
+ padding: 0 4px;
321
+ font-family: sans-serif;
322
+ font-size: 10px;
323
+ font-weight: bold;
324
+ display: flex; align-items: center; justify-content: center;
325
+ height: 100%;
326
+ line-height: 1; margin: 0; /* 修复对齐 */
327
+ }
328
+ .id-val {
329
+ background: #fff;
330
+ color: #EE6E73;
331
+ padding: 0 6px;
332
+ font-family: "Consolas", monospace;
333
+ font-size: 11px;
334
+ font-weight: bold;
335
+ display: flex; align-items: center; justify-content: center;
336
+ height: 100%;
337
+ line-height: 1; margin: 0; /* 修复对齐 */
338
+ }
339
+
340
+ .author { font-size: 12px; color: #78909C; }
270
341
  .tags { display: flex; gap: 4px; flex-wrap: wrap; height: 18px; overflow: hidden; margin-top: 4px; }
271
342
  .tag { background: #f3f3f3; color: #666; padding: 0 5px; border-radius: 3px; font-size: 10px; white-space: nowrap; line-height: 1.6;}
272
343
  .meta-row { display: flex; gap: 10px; font-size: 11px; color: #999; margin-top: auto; border-top: 1px dashed #eee; padding-top: 5px; }
@@ -275,10 +346,15 @@ function apply(ctx, config) {
275
346
  ${results.map((r) => {
276
347
  const bg = r.cover ? `<img class="cover-img" src="${r.cover}"/>` : `<div style="width:100%;height:100%;background:${generateGradient(r.title)}"></div>`;
277
348
  return `<div class="item"><div class="cover-box">${bg}</div><div class="content">
278
- <div class="top-row"><div class="title">${r.title}</div><div class="id-badge">ID: ${r.id}</div></div>
349
+ <div class="top-row"><div class="title">${r.title}</div>
350
+ <div class="id-badge">
351
+ <div class="id-label">ID</div>
352
+ <div class="id-val">${r.id}</div>
353
+ </div>
354
+ </div>
279
355
  <div class="author">By ${r.author} ${r.status ? ` · ${r.status}` : ""}</div>
280
356
  <div class="tags">${r.tags.map((t) => `<span class="tag">${t}</span>`).join("")}</div>
281
- <div class="meta-row"><span style="color:#009688"><b>热</b>${r.stats.views}</span><span style="color:#673ab7"><b>评</b>${r.stats.comments}</span><span style="color:#4caf50"><b>赞</b>${r.stats.likes}</span><span style="margin-left:auto;color:#757575">${r.updateTime}</span></div>
357
+ <div class="meta-row"><span style="color:#009688"><b>热</b>${r.stats.views}</span><span style="color:#7e57c2"><b>评</b>${r.stats.comments}</span><span style="color:#4caf50"><b>赞</b>${r.stats.likes}</span><span style="margin-left:auto;color:#757575">${r.updateTime}</span></div>
282
358
  </div></div>`;
283
359
  }).join("")}
284
360
  </div></div></body></html>`;
@@ -308,59 +384,51 @@ function apply(ctx, config) {
308
384
  const marginTop = Math.floor((maxContentHeight - optimalContentHeight) / 2) + headerHeight;
309
385
  const html = `<!DOCTYPE html><html><head><style>
310
386
  body { margin: 0; padding: 0; width: ${config.deviceWidth}px; height: ${config.deviceHeight}px; background-color: #f6f4ec; color: #2c2c2c; font-family: ${fontSerif}; overflow: hidden; position: relative;}
311
- .fixed-header { position: absolute; top: 0; left: 0; width: 100%; height: ${headerHeight}px; border-bottom: 1px solid #d7ccc8; box-sizing: border-box; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; font-size: 12px; color: #8d6e63; background: #f6f4ec; z-index: 5; font-weight: bold; }
312
- .fixed-footer { position: absolute; bottom: 0; left: 0; width: 100%; height: ${footerHeight}px; display: flex; align-items: center; justify-content: center; font-size: 12px; color: #aaa; background: #f6f4ec; z-index: 5; }
387
+ /* Header Padding reduced to 12px to align closer to left edge */
388
+ .fixed-header { position: absolute; top: 0; left: 0; width: 100%; height: ${headerHeight}px; border-bottom: 2px solid #EE6E73; box-sizing: border-box; padding: 0 12px; display: flex; align-items: center; justify-content: space-between; font-size: 12px; color: #EE6E73; background: #f6f4ec; z-index: 5; font-weight: bold; }
389
+ .fixed-footer { position: absolute; bottom: 0; left: 0; width: 100%; height: ${footerHeight}px; display: flex; align-items: center; justify-content: center; font-size: 12px; color: #78909C; background: #f6f4ec; z-index: 5; }
390
+ /* 修复1: 强制重置 header 和 footer 的缩进,防止 p, div 全局规则影响 */
391
+ .fixed-header, .fixed-footer, .header-title, .header-author { text-indent: 0 !important; }
392
+
313
393
  #viewport { position: absolute; top: ${marginTop}px; left: ${paddingX}px; width: ${contentWidth}px; height: ${optimalContentHeight}px; overflow: hidden; }
314
-
315
- #content-scroller {
316
- height: 100%; width: 100%;
317
- column-width: ${contentWidth}px; column-gap: ${columnGap}px; column-fill: auto;
318
- padding: ${paddingY}px 0; box-sizing: border-box;
319
- font-size: ${config.fontSize}px; line-height: ${lineHeightRatio};
320
- text-align: left; /* 关键:左对齐,解决长空格 */
321
- transform: translateX(0); transition: none;
322
- }
394
+ #content-scroller { height: 100%; width: 100%; column-width: ${contentWidth}px; column-gap: ${columnGap}px; column-fill: auto; padding: ${paddingY}px 0; box-sizing: border-box; font-size: ${config.fontSize}px; line-height: ${lineHeightRatio}; text-align: left; transform: translateX(0); transition: none; }
323
395
 
324
396
  p, div { margin: 0 0 0.2em 0; text-indent: 2em; word-wrap: break-word; overflow-wrap: break-word; }
325
-
326
397
  .align-center { text-align: center !important; text-align-last: center !important; text-indent: 0 !important; margin: 0.8em 0; font-weight: bold; color: #5d4037; }
327
398
  .align-right { text-align: right !important; text-indent: 0 !important; margin-top: 0.5em; color: #666; font-style: italic; }
328
399
  .no-indent { text-indent: 0 !important; }
329
400
 
330
- blockquote { margin: 1em 0.5em; padding-left: 1em; border-left: 4px solid #d7ccc8; color: #666; }
401
+ /* Header Title absolute left */
402
+ .header-title { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: left; min-width: 0; margin-right: 10px; }
403
+ .header-author { flex-shrink: 0; color: #78909C; max-width: 35%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; }
404
+
405
+ blockquote { margin: 1em 0.5em; padding-left: 1em; border-left: 4px solid #EE6E73; color: #666; }
331
406
  blockquote p { text-indent: 0; margin: 0.3em 0; }
332
-
333
407
  ul, ol { margin: 0.5em 0; padding-left: 1.5em; }
334
408
  li { margin-bottom: 0.2em; }
335
-
336
409
  hr { border: 0; height: 1px; background: #d7ccc8; margin: 1.5em 0; }
337
-
338
410
  table { width: 100%; border-collapse: collapse; margin: 1em 0; font-size: 0.9em; }
339
411
  th, td { border: 1px solid #ccc; padding: 4px; text-align: left; }
340
412
  th { background: #eee; font-weight: bold; }
341
-
342
413
  pre { background: #eee; padding: 0.5em; overflow-x: auto; border-radius: 4px; margin: 0.5em 0; }
343
414
  code { font-family: monospace; background: #f0f0f0; padding: 2px 4px; border-radius: 3px; }
344
-
345
415
  s, strike, del { text-decoration: line-through; color: #888; }
346
416
  u { text-decoration: underline; }
347
417
  sup, sub { font-size: 0.75em; line-height: 0; position: relative; vertical-align: baseline; }
348
418
  sup { top: -0.5em; }
349
419
  sub { bottom: -0.25em; }
350
-
351
- a { color: #0277bd; text-decoration: none; }
352
-
420
+ a { color: #EE6E73; text-decoration: none; }
353
421
  figure.img-box { display: flex; justify-content: center; align-items: center; margin: 0.5em 0; width: 100%; }
354
422
  img { max-width: 100%; height: auto; display: block; border-radius: 6px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); }
355
-
356
423
  h1, h2, h3 { font-size: 1.1em; margin: 0.8em 0; color: #5d4037; text-indent: 0; font-weight: bold; text-align: center; text-align-last: center; break-after: avoid; }
357
-
358
424
  strong, b { font-weight: 900; color: #3e2723; }
359
425
  em, i { font-style: italic; }
360
-
361
426
  p:last-child { margin-bottom: 0; }
362
427
  </style></head><body>
363
- <div class="fixed-header"><span>${info.Title.substring(0, 12) + (info.Title.length > 12 ? "..." : "")}</span><span>${info.UserName}</span></div>
428
+ <div class="fixed-header">
429
+ <div class="header-title">${info.Title}</div>
430
+ <div class="header-author">${info.UserName}</div>
431
+ </div>
364
432
  <div id="viewport"><div id="content-scroller">${content}</div></div>
365
433
  <div class="fixed-footer" id="page-indicator">- 1 -</div></body></html>`;
366
434
  const page = await ctx.puppeteer.page();
@@ -424,7 +492,7 @@ function apply(ctx, config) {
424
492
  const nodes = pages.map((buf) => (0, import_koishi.h)("message", import_koishi.h.image(buf, "image/jpeg")));
425
493
  const navs = [];
426
494
  const mainId = res.parent ? res.parent.ID : res.data.ID;
427
- navs.push(`[目录] /ft.info ${mainId}`);
495
+ navs.push(`[首页] /ft.read ${mainId}`);
428
496
  if (res.menu?.length) {
429
497
  const idx = res.menu.findIndex((m) => m.ID.toString() === threadId);
430
498
  if (idx > 0) navs.push(`[上一章] /ft.read ${res.menu[idx - 1].ID}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-fimtale-api",
3
3
  "description": "Koishi插件,从fimtale搜索/订阅/随机获取小说/解析链接等",
4
- "version": "1.0.4",
4
+ "version": "1.0.6",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [