koishi-plugin-fimtale-api 0.0.6 → 0.0.8
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 +49 -20
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -47,6 +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 Pro 比例)
|
|
50
51
|
deviceWidth: import_koishi.Schema.number().default(390).description("阅读器渲染宽度(px)"),
|
|
51
52
|
deviceHeight: import_koishi.Schema.number().default(844).description("阅读器渲染高度(px)"),
|
|
52
53
|
fontSize: import_koishi.Schema.number().default(20).description("正文字号(px)")
|
|
@@ -203,11 +204,11 @@ function apply(ctx, config) {
|
|
|
203
204
|
const isChapter = info.IsChapter || !!parent && parent.ID !== info.ID;
|
|
204
205
|
const displayTitle = isChapter && parent ? parent.Title : info.Title;
|
|
205
206
|
let displayCover = null;
|
|
206
|
-
if (isChapter) {
|
|
207
|
-
displayCover = extractImage(
|
|
207
|
+
if (isChapter && parent) {
|
|
208
|
+
displayCover = parent.Background || extractImage(parent.Content);
|
|
208
209
|
}
|
|
209
210
|
if (!displayCover) {
|
|
210
|
-
displayCover =
|
|
211
|
+
displayCover = info.Background || extractImage(info.Content);
|
|
211
212
|
}
|
|
212
213
|
const displayTagsObj = isChapter && parent ? parent.Tags : info.Tags;
|
|
213
214
|
const subTitle = isChapter ? info.Title : null;
|
|
@@ -233,11 +234,12 @@ function apply(ctx, config) {
|
|
|
233
234
|
<head>
|
|
234
235
|
<style>
|
|
235
236
|
body { margin: 0; padding: 0; font-family: ${fontStack}; background: transparent; }
|
|
236
|
-
.card { width: 620px; height:
|
|
237
|
+
.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; }
|
|
237
238
|
.cover { width: 220px; height: 100%; ${bgStyle} background-size: cover; background-position: center; position: relative; flex-shrink: 0; }
|
|
238
239
|
.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; }
|
|
239
|
-
.info { flex: 1; padding:
|
|
240
|
+
.info { flex: 1; padding: 24px; display: flex; flex-direction: column; justify-content: space-between; overflow: hidden; }
|
|
240
241
|
|
|
242
|
+
.header-group { display: flex; flex-direction: column; }
|
|
241
243
|
.title {
|
|
242
244
|
font-size: 22px; font-weight: 700; color: #333; line-height: 1.35;
|
|
243
245
|
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
|
@@ -247,19 +249,22 @@ function apply(ctx, config) {
|
|
|
247
249
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
248
250
|
padding-left: 10px; border-left: 3px solid #e91e63;
|
|
249
251
|
}
|
|
252
|
+
.author { font-size: 13px; color: #888; margin-top: 8px; font-weight: 400; }
|
|
250
253
|
|
|
251
|
-
.
|
|
252
|
-
|
|
253
|
-
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0; max-height: 56px; overflow: hidden; align-content: flex-start; }
|
|
254
|
+
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; max-height: 56px; overflow: hidden; flex-shrink: 0;}
|
|
254
255
|
.tag { background: #eff2f5; color: #5c6b7f; padding: 3px 9px; border-radius: 4px; font-size: 11px; font-weight: 500; }
|
|
255
256
|
.tag-imp { background: #e3f2fd; color: #1565c0; }
|
|
256
257
|
|
|
258
|
+
.summary-box {
|
|
259
|
+
flex: 1; overflow: hidden; position: relative; margin-top: 8px;
|
|
260
|
+
}
|
|
257
261
|
.summary {
|
|
258
262
|
font-size: 13px; color: #666; line-height: 1.6;
|
|
263
|
+
max-height: 6.4em;
|
|
259
264
|
display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
|
|
260
|
-
margin-top: auto;
|
|
261
265
|
}
|
|
262
|
-
|
|
266
|
+
|
|
267
|
+
.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;}
|
|
263
268
|
.stat b { color: #555; font-weight: bold; margin-right: 2px;}
|
|
264
269
|
</style>
|
|
265
270
|
</head>
|
|
@@ -267,13 +272,13 @@ function apply(ctx, config) {
|
|
|
267
272
|
<div class="card">
|
|
268
273
|
<div class="cover"><div class="id-tag">ID: ${info.ID}</div></div>
|
|
269
274
|
<div class="info">
|
|
270
|
-
<div>
|
|
275
|
+
<div class="header-group">
|
|
271
276
|
<div class="title">${displayTitle}</div>
|
|
272
277
|
${subTitle ? `<div class="subtitle">${subTitle}</div>` : ""}
|
|
273
278
|
<div class="author">@${info.UserName}</div>
|
|
274
|
-
<div class="tags">${displayTags.map((t) => `<span class="tag ${["文", "译", "R"].includes(t) ? "tag-imp" : ""}">${t}</span>`).join("")}</div>
|
|
275
279
|
</div>
|
|
276
|
-
<div class="
|
|
280
|
+
<div class="tags">${displayTags.map((t) => `<span class="tag ${["文", "译", "R"].includes(t) ? "tag-imp" : ""}">${t}</span>`).join("")}</div>
|
|
281
|
+
<div class="summary-box"><div class="summary">${summary}</div></div>
|
|
277
282
|
<div class="footer">
|
|
278
283
|
<span class="stat"><b style="color:#009688">热度</b>${views}</span>
|
|
279
284
|
<span class="stat"><b style="color:#673ab7">评论</b>${comments}</span>
|
|
@@ -286,7 +291,7 @@ function apply(ctx, config) {
|
|
|
286
291
|
const page = await ctx.puppeteer.page();
|
|
287
292
|
await injectCookies(page);
|
|
288
293
|
await page.setContent(html);
|
|
289
|
-
await page.setViewport({ width: 660, height:
|
|
294
|
+
await page.setViewport({ width: 660, height: 480, deviceScaleFactor: 2 });
|
|
290
295
|
const el = await page.$(".card");
|
|
291
296
|
const img = await el.screenshot({ type: "png" });
|
|
292
297
|
await page.close();
|
|
@@ -333,7 +338,7 @@ function apply(ctx, config) {
|
|
|
333
338
|
<div class="top-row"><div class="title">${r.title}</div><div class="id-badge">ID: ${r.id}</div></div>
|
|
334
339
|
<div class="author">By ${r.author} ${r.status ? ` · ${r.status}` : ""}</div>
|
|
335
340
|
<div class="tags">${r.tags.map((t) => `<span class="tag">${t}</span>`).join("")}</div>
|
|
336
|
-
<div class="meta-row">${stats || "
|
|
341
|
+
<div class="meta-row">${stats || "No Data"}</div>
|
|
337
342
|
</div></div>`;
|
|
338
343
|
}).join("")}
|
|
339
344
|
</div>
|
|
@@ -357,22 +362,35 @@ function apply(ctx, config) {
|
|
|
357
362
|
<style>
|
|
358
363
|
body { margin: 0; padding: 0; width: ${config.deviceWidth}px; background-color: #f6f4ec; color: #2c2c2c; font-family: ${fontStack}; }
|
|
359
364
|
#source-container { display: none; }
|
|
365
|
+
|
|
366
|
+
/* 使用 Flex 布局,让页脚自然占据底部空间,不覆盖正文 */
|
|
360
367
|
.page {
|
|
361
368
|
width: ${config.deviceWidth}px; height: ${config.deviceHeight}px;
|
|
362
369
|
padding: 35px 28px; box-sizing: border-box;
|
|
363
370
|
position: relative; background: #f6f4ec; overflow: hidden;
|
|
364
371
|
display: flex; flex-direction: column;
|
|
365
372
|
}
|
|
373
|
+
|
|
366
374
|
.page-header {
|
|
367
375
|
font-size: 12px; color: #8d6e63; border-bottom: 2px solid #d7ccc8;
|
|
368
376
|
padding-bottom: 12px; margin-bottom: 15px; flex-shrink: 0;
|
|
369
377
|
display: flex; justify-content: space-between; font-weight: bold;
|
|
370
378
|
}
|
|
379
|
+
|
|
380
|
+
/* 页脚改为相对定位,并给予上边距 */
|
|
371
381
|
.page-footer {
|
|
372
|
-
|
|
373
|
-
|
|
382
|
+
text-align: center; font-size: 12px; color: #aaa; font-family: sans-serif;
|
|
383
|
+
flex-shrink: 0; padding-top: 10px; margin-top: auto;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.page-content {
|
|
387
|
+
flex: 1; /* 占据剩余所有空间 */
|
|
388
|
+
overflow: hidden; /* 隐藏溢出部分 */
|
|
389
|
+
font-size: ${config.fontSize}px;
|
|
390
|
+
line-height: 1.7;
|
|
391
|
+
text-align: justify;
|
|
374
392
|
}
|
|
375
|
-
|
|
393
|
+
|
|
376
394
|
p { margin: 0 0 0.6em 0; text-indent: 2em; }
|
|
377
395
|
img { max-width: 100%; height: auto; display: block; margin: 10px auto; border-radius: 6px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); }
|
|
378
396
|
h1, h2, h3 { font-size: 1.1em; margin: 0.5em 0; color: #5d4037; text-indent: 0; font-weight: bold; }
|
|
@@ -389,32 +407,42 @@ function apply(ctx, config) {
|
|
|
389
407
|
const author = source.querySelector('.meta-info').dataset.author;
|
|
390
408
|
let pageIndex = 1;
|
|
391
409
|
let currentPageContent = null;
|
|
410
|
+
|
|
392
411
|
function createNewPage() {
|
|
393
412
|
const page = document.createElement('div'); page.className = 'page';
|
|
394
413
|
const header = document.createElement('div'); header.className = 'page-header';
|
|
395
414
|
header.innerHTML = \`<span>\${title.substring(0, 12) + (title.length>12?'...':'')}</span><span>\${author}</span>\`;
|
|
396
415
|
page.appendChild(header);
|
|
416
|
+
|
|
397
417
|
const content = document.createElement('div'); content.className = 'page-content';
|
|
398
418
|
page.appendChild(content);
|
|
419
|
+
|
|
399
420
|
const footer = document.createElement('div'); footer.className = 'page-footer';
|
|
400
421
|
footer.id = 'footer-' + pageIndex;
|
|
401
422
|
page.appendChild(footer);
|
|
423
|
+
|
|
402
424
|
output.appendChild(page);
|
|
403
425
|
currentPageContent = content;
|
|
404
426
|
return page;
|
|
405
427
|
}
|
|
428
|
+
|
|
406
429
|
createNewPage();
|
|
407
430
|
const children = Array.from(source.children);
|
|
431
|
+
|
|
408
432
|
for (const child of children) {
|
|
409
433
|
if (child.className === 'meta-info') continue;
|
|
434
|
+
|
|
410
435
|
currentPageContent.appendChild(child.cloneNode(true));
|
|
411
|
-
|
|
436
|
+
|
|
437
|
+
// 核心修复:增加 10px 的安全缓冲,防止 descenders 被切
|
|
438
|
+
if (currentPageContent.scrollHeight > currentPageContent.clientHeight - 10) {
|
|
412
439
|
currentPageContent.removeChild(currentPageContent.lastChild);
|
|
413
440
|
pageIndex++;
|
|
414
441
|
createNewPage();
|
|
415
442
|
currentPageContent.appendChild(child.cloneNode(true));
|
|
416
443
|
}
|
|
417
444
|
}
|
|
445
|
+
|
|
418
446
|
for(let i=1; i<=pageIndex; i++) document.getElementById('footer-'+i).innerText = \`- \${i} / \${pageIndex} -\`;
|
|
419
447
|
return pageIndex;
|
|
420
448
|
}
|
|
@@ -482,7 +510,8 @@ function apply(ctx, config) {
|
|
|
482
510
|
if (!results.length) return "未找到结果。";
|
|
483
511
|
const img = await renderSearchResults(keyword, results);
|
|
484
512
|
await session.send(import_koishi.h.image(img, "image/png"));
|
|
485
|
-
|
|
513
|
+
const exampleId = results[0]?.id || "12345";
|
|
514
|
+
return `Tip: 发送 /ft.read [ID] 阅读 (例: /ft.read ${exampleId})`;
|
|
486
515
|
});
|
|
487
516
|
ctx.command("ft.sub <threadId:string>", "订阅").action(async ({ session }, threadId) => {
|
|
488
517
|
if (!/^\d+$/.test(threadId)) return "ID错误";
|