readshell 0.1.0 → 0.2.0
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/README.md +73 -64
- package/dist/index.js +299 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -351,18 +351,18 @@ ${plainText}
|
|
|
351
351
|
};
|
|
352
352
|
}
|
|
353
353
|
function openEpub(filePath) {
|
|
354
|
-
return new Promise((
|
|
354
|
+
return new Promise((resolve3, reject) => {
|
|
355
355
|
const epub = new EPub(filePath);
|
|
356
356
|
epub.on("error", (err) => reject(err));
|
|
357
|
-
epub.on("end", () =>
|
|
357
|
+
epub.on("end", () => resolve3(epub));
|
|
358
358
|
epub.parse();
|
|
359
359
|
});
|
|
360
360
|
}
|
|
361
361
|
function getChapterText(epub, chapterId) {
|
|
362
|
-
return new Promise((
|
|
362
|
+
return new Promise((resolve3, reject) => {
|
|
363
363
|
epub.getChapter(chapterId, (err, text) => {
|
|
364
364
|
if (err) return reject(err);
|
|
365
|
-
|
|
365
|
+
resolve3(text);
|
|
366
366
|
});
|
|
367
367
|
});
|
|
368
368
|
}
|
|
@@ -480,24 +480,251 @@ var BookService = class {
|
|
|
480
480
|
}
|
|
481
481
|
};
|
|
482
482
|
|
|
483
|
+
// src/locales/zh.ts
|
|
484
|
+
var zh_default = {
|
|
485
|
+
// Common
|
|
486
|
+
"common.yes": "\u662F",
|
|
487
|
+
"common.no": "\u5426",
|
|
488
|
+
"common.confirm": "\u786E\u8BA4",
|
|
489
|
+
"common.cancel": "\u53D6\u6D88",
|
|
490
|
+
"common.quit": "\u6309 q \u9000\u51FA",
|
|
491
|
+
// CLI
|
|
492
|
+
"cli.import.desc": "\u5BFC\u5165\u672C\u5730\u6587\u4EF6\u6216\u76EE\u5F55\u5230\u4E66\u67B6",
|
|
493
|
+
"cli.import.help": "\u6587\u4EF6\u6216\u76EE\u5F55\u8DEF\u5F84\uFF08\u652F\u6301 .txt / .epub\uFF09",
|
|
494
|
+
"cli.import.success": "\u2713 \u5DF2\u5BFC\u5165:",
|
|
495
|
+
"cli.import.fail": "\u5BFC\u5165\u5931\u8D25:",
|
|
496
|
+
"cli.import.not_found": "\u8DEF\u5F84\u4E0D\u5B58\u5728:",
|
|
497
|
+
"cli.import.unsupported": "\u4E0D\u652F\u6301\u7684\u6587\u4EF6\u683C\u5F0F\u3002\u76EE\u524D\u652F\u6301: .txt, .epub",
|
|
498
|
+
"cli.import.scan_dir": "\u626B\u63CF\u76EE\u5F55",
|
|
499
|
+
"cli.import.found_files": "\u627E\u5230\u4EE5\u4E0B\u4E66\u7C4D\uFF1A",
|
|
500
|
+
"cli.import.confirm_batch": "\u662F\u5426\u786E\u8BA4\u5BFC\u5165\u4E0A\u8FF0 {0} \u672C\u4E66\uFF1F(y/N)",
|
|
501
|
+
"cli.import.canceled": "\u2713 \u5BFC\u5165\u5DF2\u53D6\u6D88",
|
|
502
|
+
"cli.resume.desc": "\u6062\u590D\u4E0A\u6B21\u9605\u8BFB",
|
|
503
|
+
"cli.resume.none": "\u2717 \u6CA1\u6709\u627E\u5230\u6700\u8FD1\u9605\u8BFB\u8BB0\u5F55\uFF0C\u8BF7\u5148\u4F7F\u7528 novel import <file> \u6216 novel open <book-id> \u6253\u5F00\u4E00\u672C\u4E66\u3002",
|
|
504
|
+
"cli.open.desc": "\u6253\u5F00\u6307\u5B9A\u4E66\u7C4D",
|
|
505
|
+
"cli.open.help": "\u4E66\u7C4D ID \u6216\u4E66\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF09",
|
|
506
|
+
"cli.open.not_found": "\u2717 \u672A\u627E\u5230\u5339\u914D\u4E66\u7C4D:",
|
|
507
|
+
"cli.library.desc": "\u67E5\u770B\u4E66\u67B6\u5217\u8868",
|
|
508
|
+
"cli.library.help": "\u53EF\u9009\u5173\u952E\u5B57\u641C\u7D22",
|
|
509
|
+
"cli.library.none": "\u2717 \u4E66\u67B6\u4E3A\u7A7A\u3002\u4F7F\u7528 novel import <file> \u5BFC\u5165\u4F60\u7684\u7B2C\u4E00\u672C\u4E66\u3002",
|
|
510
|
+
"cli.library.search_none": "\u{1F4DA} \u672A\u627E\u5230\u5339\u914D\u300C{0}\u300D\u7684\u4E66\u7C4D\u3002",
|
|
511
|
+
"cli.library.search_result": "\u{1F4DA} \u641C\u7D22\u7ED3\u679C ({0} \u672C):\n",
|
|
512
|
+
"cli.remove.desc": "\u4ECE\u4E66\u67B6\u79FB\u9664\u4E66\u7C4D\uFF08\u4EC5\u5220\u9664\u8BB0\u5F55\uFF0C\u4E0D\u5220\u6E90\u6587\u4EF6\uFF09",
|
|
513
|
+
"cli.remove.help": "\u4E66\u7C4D ID \u6216\u4E66\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF09",
|
|
514
|
+
"cli.remove.not_found": "\u2717 \u672A\u627E\u5230\u5339\u914D\u4E66\u7C4D:",
|
|
515
|
+
"cli.remove.success": "\u2713 \u5DF2\u79FB\u9664\u4E66\u7C4D:",
|
|
516
|
+
"cli.remove.fail": "\u79FB\u9664\u4E66\u7C4D\u5931\u8D25:",
|
|
517
|
+
"cli.lang.desc": "\u5207\u6362\u8BED\u8A00",
|
|
518
|
+
"cli.lang.help": "\u8BED\u8A00\u4EE3\u7801: zh | en",
|
|
519
|
+
"cli.lang.success": "\u2713 \u8BED\u8A00\u5DF2\u5207\u6362\u4E3A: {0}",
|
|
520
|
+
"cli.lang.unsupported": "\u2717 \u4E0D\u652F\u6301\u7684\u8BED\u8A00: {0}",
|
|
521
|
+
// TUI - Library
|
|
522
|
+
"tui.lib.loading": "\u{1F4DA} \u52A0\u8F7D\u4E66\u67B6...",
|
|
523
|
+
"tui.lib.empty.title": "\u{1F4DA} \u4E66\u67B6",
|
|
524
|
+
"tui.lib.empty.desc": "\u4E66\u67B6\u4E3A\u7A7A\u3002\u4F7F\u7528 novel import <file> \u5BFC\u5165\u4F60\u7684\u7B2C\u4E00\u672C\u4E66\u3002",
|
|
525
|
+
"tui.lib.title": "\u{1F4DA} \u4E66\u67B6 ({0} \u672C)",
|
|
526
|
+
"tui.lib.tips": " \u2191\u2193/jk \u9009\u62E9 \xB7 Enter \u6253\u5F00 \xB7 d/x \u5220\u9664 \xB7 q \u9000\u51FA",
|
|
527
|
+
// TUI - Reader
|
|
528
|
+
"tui.reader.loading": "\u8BFB\u53D6\u4E2D...",
|
|
529
|
+
"tui.reader.bookmark_add": "\u2713 \u589E\u52A0\u4E66\u7B7E: {0}",
|
|
530
|
+
"tui.reader.status.remaining": "\u9884\u8BA1\u5269\u4F59 {0}",
|
|
531
|
+
// TUI - ChapterNav
|
|
532
|
+
"tui.nav.tab.chapters": "[\u5168\u90E8\u7AE0\u8282]",
|
|
533
|
+
"tui.nav.tab.bookmarks": "[\u6211\u7684\u4E66\u7B7E]",
|
|
534
|
+
"tui.nav.tips": "Enter \u8DF3\u8F6C \xB7 Tab \u5207\u6362 \xB7 Esc/q \u5173\u95ED",
|
|
535
|
+
"tui.nav.empty": "\u6CA1\u6709\u8BB0\u5F55",
|
|
536
|
+
"tui.nav.page": "\u7B2C {0} / {1} \u9875"
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// src/locales/en.ts
|
|
540
|
+
var en = {
|
|
541
|
+
// Common
|
|
542
|
+
"common.yes": "Yes",
|
|
543
|
+
"common.no": "No",
|
|
544
|
+
"common.confirm": "Confirm",
|
|
545
|
+
"common.cancel": "Cancel",
|
|
546
|
+
"common.quit": "Press q to quit",
|
|
547
|
+
// CLI
|
|
548
|
+
"cli.import.desc": "Import local file or directory to library",
|
|
549
|
+
"cli.import.help": "File or directory path (supports .txt / .epub)",
|
|
550
|
+
"cli.import.success": "\u2713 Imported:",
|
|
551
|
+
"cli.import.fail": "Import failed:",
|
|
552
|
+
"cli.import.not_found": "Path not found:",
|
|
553
|
+
"cli.import.unsupported": "Unsupported file format. Currently supports: .txt, .epub",
|
|
554
|
+
"cli.import.scan_dir": "Scanning directory",
|
|
555
|
+
"cli.import.found_files": "Found following books:",
|
|
556
|
+
"cli.import.confirm_batch": "Confirm importing these {0} books? (y/N)",
|
|
557
|
+
"cli.import.canceled": "\u2713 Import canceled",
|
|
558
|
+
"cli.resume.desc": "Resume last reading",
|
|
559
|
+
"cli.resume.none": "\u2717 No recent reading record found. Please use `novel import <file>` or `novel open <book-id>` first.",
|
|
560
|
+
"cli.open.desc": "Open specific book",
|
|
561
|
+
"cli.open.help": "Book ID or title (fuzzy match)",
|
|
562
|
+
"cli.open.not_found": "\u2717 Book not found:",
|
|
563
|
+
"cli.library.desc": "View library",
|
|
564
|
+
"cli.library.help": "Optional keyword search",
|
|
565
|
+
"cli.library.none": "\u2717 Library is empty. Use `novel import <file>` to import your first book.",
|
|
566
|
+
"cli.library.search_none": '\u{1F4DA} No books found matching "{0}".',
|
|
567
|
+
"cli.library.search_result": "\u{1F4DA} Search results ({0} books):\n",
|
|
568
|
+
"cli.remove.desc": "Remove book from library (only deletes records, not source file)",
|
|
569
|
+
"cli.remove.help": "Book ID or title (fuzzy match)",
|
|
570
|
+
"cli.remove.not_found": "\u2717 Book not found:",
|
|
571
|
+
"cli.remove.success": "\u2713 Book removed:",
|
|
572
|
+
"cli.remove.fail": "Failed to remove book:",
|
|
573
|
+
"cli.lang.desc": "Switch language",
|
|
574
|
+
"cli.lang.help": "Language code: zh | en",
|
|
575
|
+
"cli.lang.success": "\u2713 Language switched to: {0}",
|
|
576
|
+
"cli.lang.unsupported": "\u2717 Unsupported language: {0}",
|
|
577
|
+
// TUI - Library
|
|
578
|
+
"tui.lib.loading": "\u{1F4DA} Loading library...",
|
|
579
|
+
"tui.lib.empty.title": "\u{1F4DA} Library",
|
|
580
|
+
"tui.lib.empty.desc": "Library is empty. Use `novel import <file>` to import your first book.",
|
|
581
|
+
"tui.lib.title": "\u{1F4DA} Library ({0} books)",
|
|
582
|
+
"tui.lib.tips": " \u2191\u2193/jk select \xB7 Enter open \xB7 d/x delete \xB7 q quit",
|
|
583
|
+
// TUI - Reader
|
|
584
|
+
"tui.reader.loading": "Loading...",
|
|
585
|
+
"tui.reader.bookmark_add": "\u2713 Bookmark added: {0}",
|
|
586
|
+
"tui.reader.status.remaining": "Est. remaining {0}",
|
|
587
|
+
// TUI - ChapterNav
|
|
588
|
+
"tui.nav.tab.chapters": "[All Chapters]",
|
|
589
|
+
"tui.nav.tab.bookmarks": "[My Bookmarks]",
|
|
590
|
+
"tui.nav.tips": "Enter jump \xB7 Tab switch \xB7 Esc/q close",
|
|
591
|
+
"tui.nav.empty": "No records",
|
|
592
|
+
"tui.nav.page": "Page {0} / {1}"
|
|
593
|
+
};
|
|
594
|
+
var en_default = en;
|
|
595
|
+
|
|
596
|
+
// src/config/AppConfig.ts
|
|
597
|
+
import Conf from "conf";
|
|
598
|
+
var defaults = {
|
|
599
|
+
linesPerPage: 0,
|
|
600
|
+
showStatusBar: true,
|
|
601
|
+
readingMode: "page",
|
|
602
|
+
language: "zh"
|
|
603
|
+
};
|
|
604
|
+
var config = new Conf({
|
|
605
|
+
projectName: "readshell",
|
|
606
|
+
defaults
|
|
607
|
+
});
|
|
608
|
+
function getConfig() {
|
|
609
|
+
return {
|
|
610
|
+
linesPerPage: config.get("linesPerPage"),
|
|
611
|
+
showStatusBar: config.get("showStatusBar"),
|
|
612
|
+
readingMode: config.get("readingMode"),
|
|
613
|
+
language: config.get("language")
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function setConfig(key, value) {
|
|
617
|
+
config.set(key, value);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/locales/index.ts
|
|
621
|
+
var dictionaries = {
|
|
622
|
+
zh: zh_default,
|
|
623
|
+
en: en_default
|
|
624
|
+
};
|
|
625
|
+
var currentLang = "zh";
|
|
626
|
+
var currentDict = dictionaries.zh;
|
|
627
|
+
function initI18n() {
|
|
628
|
+
const config2 = getConfig();
|
|
629
|
+
currentLang = config2.language || "zh";
|
|
630
|
+
currentDict = dictionaries[currentLang] || dictionaries.zh;
|
|
631
|
+
}
|
|
632
|
+
function setLanguage(lang) {
|
|
633
|
+
currentLang = lang;
|
|
634
|
+
currentDict = dictionaries[lang] || dictionaries.zh;
|
|
635
|
+
}
|
|
636
|
+
function t(key, ...args) {
|
|
637
|
+
let template = currentDict[key];
|
|
638
|
+
if (!template) {
|
|
639
|
+
return key;
|
|
640
|
+
}
|
|
641
|
+
if (args.length > 0) {
|
|
642
|
+
args.forEach((arg, index) => {
|
|
643
|
+
template = template.replace(`{${index}}`, String(arg));
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
return template;
|
|
647
|
+
}
|
|
648
|
+
|
|
483
649
|
// src/cli/commands/import.ts
|
|
650
|
+
import { statSync as statSync2, readdirSync } from "fs";
|
|
651
|
+
import { resolve as resolve2, join as join2, extname } from "path";
|
|
652
|
+
import * as readline from "readline/promises";
|
|
653
|
+
function scanDirectory(dir) {
|
|
654
|
+
let results = [];
|
|
655
|
+
try {
|
|
656
|
+
const list = readdirSync(dir);
|
|
657
|
+
for (const file of list) {
|
|
658
|
+
const fullPath = join2(dir, file);
|
|
659
|
+
const stat = statSync2(fullPath);
|
|
660
|
+
if (stat.isDirectory()) {
|
|
661
|
+
results = results.concat(scanDirectory(fullPath));
|
|
662
|
+
} else {
|
|
663
|
+
const ext = extname(fullPath).toLowerCase();
|
|
664
|
+
if (ext === ".txt" || ext === ".epub") {
|
|
665
|
+
results.push(fullPath);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
} catch (err) {
|
|
670
|
+
logger.error("Scan error:", err);
|
|
671
|
+
}
|
|
672
|
+
return results;
|
|
673
|
+
}
|
|
484
674
|
var importCommand = {
|
|
485
675
|
command: "import <file>",
|
|
486
|
-
describe: "
|
|
676
|
+
describe: t("cli.import.desc"),
|
|
487
677
|
builder: (yargs2) => {
|
|
488
678
|
return yargs2.positional("file", {
|
|
489
|
-
describe: "
|
|
679
|
+
describe: t("cli.import.help"),
|
|
490
680
|
type: "string",
|
|
491
681
|
demandOption: true
|
|
492
682
|
});
|
|
493
683
|
},
|
|
494
684
|
handler: async (argv) => {
|
|
495
685
|
try {
|
|
686
|
+
const targetPath = resolve2(argv.file);
|
|
687
|
+
let stat;
|
|
688
|
+
try {
|
|
689
|
+
stat = statSync2(targetPath);
|
|
690
|
+
} catch (e) {
|
|
691
|
+
console.log(`${t("cli.import.not_found")} ${targetPath}`);
|
|
692
|
+
process.exit(1);
|
|
693
|
+
}
|
|
496
694
|
const bookService = new BookService();
|
|
497
|
-
|
|
498
|
-
|
|
695
|
+
if (stat.isDirectory()) {
|
|
696
|
+
console.log(`${t("cli.import.scan_dir")} ${targetPath}...`);
|
|
697
|
+
const files = scanDirectory(targetPath);
|
|
698
|
+
if (files.length === 0) {
|
|
699
|
+
console.log(`\u2717 ` + t("cli.import.unsupported"));
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
console.log(t("cli.import.found_files"));
|
|
703
|
+
files.forEach((f, i) => console.log(` ${i + 1}. ${f}`));
|
|
704
|
+
const rl = readline.createInterface({
|
|
705
|
+
input: process.stdin,
|
|
706
|
+
output: process.stdout
|
|
707
|
+
});
|
|
708
|
+
const answer = await rl.question(t("cli.import.confirm_batch", files.length) + " ");
|
|
709
|
+
rl.close();
|
|
710
|
+
if (answer.toLowerCase() === "y") {
|
|
711
|
+
for (const file of files) {
|
|
712
|
+
try {
|
|
713
|
+
const book = await bookService.importBook(file);
|
|
714
|
+
console.log(`${t("cli.import.success")} ${book.title} (${book.id})`);
|
|
715
|
+
} catch (err) {
|
|
716
|
+
console.log(`${t("cli.import.fail")} ${file} - ${err}`);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
} else {
|
|
720
|
+
console.log(t("cli.import.canceled"));
|
|
721
|
+
}
|
|
722
|
+
} else {
|
|
723
|
+
const book = await bookService.importBook(argv.file);
|
|
724
|
+
console.log(`${t("cli.import.success")} ${book.title} (${book.id})`);
|
|
725
|
+
}
|
|
499
726
|
} catch (error) {
|
|
500
|
-
|
|
727
|
+
console.log(`${t("cli.import.fail")} ${error}`);
|
|
501
728
|
process.exit(1);
|
|
502
729
|
}
|
|
503
730
|
}
|
|
@@ -659,24 +886,20 @@ function LibraryPage({ onNavigate }) {
|
|
|
659
886
|
}
|
|
660
887
|
}, { isActive: isRawModeSupported });
|
|
661
888
|
if (loading) {
|
|
662
|
-
return /* @__PURE__ */ jsx2(Box2, { padding: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "
|
|
889
|
+
return /* @__PURE__ */ jsx2(Box2, { padding: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: t("tui.lib.loading") }) });
|
|
663
890
|
}
|
|
664
891
|
if (books.length === 0) {
|
|
665
892
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", padding: 1, children: [
|
|
666
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "
|
|
893
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: t("tui.lib.empty.title") }),
|
|
667
894
|
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
|
|
668
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "
|
|
669
|
-
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "
|
|
895
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: t("tui.lib.empty.desc") }),
|
|
896
|
+
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: t("common.quit") }) })
|
|
670
897
|
] })
|
|
671
898
|
] });
|
|
672
899
|
}
|
|
673
900
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", padding: 1, children: [
|
|
674
|
-
/* @__PURE__ */
|
|
675
|
-
|
|
676
|
-
books.length,
|
|
677
|
-
" \u672C)"
|
|
678
|
-
] }),
|
|
679
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " \u2191\u2193/jk \u9009\u62E9 \xB7 Enter \u6253\u5F00 \xB7 d/x \u5220\u9664 \xB7 q \u9000\u51FA" }),
|
|
901
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: t("tui.lib.title", books.length) }),
|
|
902
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: t("tui.lib.tips") }),
|
|
680
903
|
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: books.map((book, index) => {
|
|
681
904
|
const isSelected = index === selectedIndex;
|
|
682
905
|
return /* @__PURE__ */ jsx2(Box2, { paddingX: 1, justifyContent: "space-between", children: /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -753,8 +976,7 @@ function StatusBar({
|
|
|
753
976
|
] }) }),
|
|
754
977
|
/* @__PURE__ */ jsxs3(Box4, { children: [
|
|
755
978
|
remainingTime && /* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
|
|
756
|
-
"
|
|
757
|
-
remainingTime,
|
|
979
|
+
t("tui.reader.status.remaining", remainingTime),
|
|
758
980
|
" "
|
|
759
981
|
] }),
|
|
760
982
|
/* @__PURE__ */ jsxs3(Text4, { color: "gray", children: [
|
|
@@ -766,7 +988,7 @@ function StatusBar({
|
|
|
766
988
|
displayPercent,
|
|
767
989
|
"%"
|
|
768
990
|
] }),
|
|
769
|
-
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "
|
|
991
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: t("common.quit") })
|
|
770
992
|
] })
|
|
771
993
|
] });
|
|
772
994
|
}
|
|
@@ -837,12 +1059,15 @@ function ChapterNav({
|
|
|
837
1059
|
children: [
|
|
838
1060
|
/* @__PURE__ */ jsxs4(Box5, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
839
1061
|
/* @__PURE__ */ jsxs4(Box5, { children: [
|
|
840
|
-
/* @__PURE__ */
|
|
841
|
-
|
|
1062
|
+
/* @__PURE__ */ jsxs4(Text5, { bold: true, color: activeTab === "chapters" ? "green" : "gray", children: [
|
|
1063
|
+
t("tui.nav.tab.chapters"),
|
|
1064
|
+
" "
|
|
1065
|
+
] }),
|
|
1066
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, color: activeTab === "bookmarks" ? "green" : "gray", children: t("tui.nav.tab.bookmarks") })
|
|
842
1067
|
] }),
|
|
843
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "
|
|
1068
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: t("tui.nav.tips") })
|
|
844
1069
|
] }),
|
|
845
|
-
visibleItems.length === 0 ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "
|
|
1070
|
+
visibleItems.length === 0 ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: t("tui.nav.empty") }) : visibleItems.map((item, idx) => {
|
|
846
1071
|
const actualIndex = windowStart + idx;
|
|
847
1072
|
const isSelected = actualIndex === selectedIndex;
|
|
848
1073
|
return /* @__PURE__ */ jsxs4(
|
|
@@ -858,13 +1083,7 @@ function ChapterNav({
|
|
|
858
1083
|
item.id
|
|
859
1084
|
);
|
|
860
1085
|
}),
|
|
861
|
-
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, justifyContent: "flex-end", children: /* @__PURE__ */
|
|
862
|
-
"\u7B2C ",
|
|
863
|
-
Math.floor(selectedIndex / pageSize) + 1,
|
|
864
|
-
" / ",
|
|
865
|
-
Math.ceil(currentList.length / pageSize) || 1,
|
|
866
|
-
" \u9875"
|
|
867
|
-
] }) })
|
|
1086
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, justifyContent: "flex-end", children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: t("tui.nav.page", Math.floor(selectedIndex / pageSize) + 1, Math.ceil(currentList.length / pageSize) || 1) }) })
|
|
868
1087
|
]
|
|
869
1088
|
}
|
|
870
1089
|
);
|
|
@@ -1260,7 +1479,7 @@ function ReaderContent({
|
|
|
1260
1479
|
}
|
|
1261
1480
|
const currentOffset = reader.getCurrentOffset();
|
|
1262
1481
|
bookmarkServiceRef.current.addBookmark(bookId, markTitle, currentOffset);
|
|
1263
|
-
setToastMessage(
|
|
1482
|
+
setToastMessage(t("tui.reader.bookmark_add", markTitle));
|
|
1264
1483
|
setTimeout(() => setToastMessage(null), 2e3);
|
|
1265
1484
|
};
|
|
1266
1485
|
useEffect4(() => {
|
|
@@ -1358,11 +1577,14 @@ function ReaderPage({ bookId, initialByteOffset, onNavigate: _onNavigate }) {
|
|
|
1358
1577
|
"\u2717 ",
|
|
1359
1578
|
error
|
|
1360
1579
|
] }),
|
|
1361
|
-
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "
|
|
1580
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: t("common.quit") })
|
|
1362
1581
|
] });
|
|
1363
1582
|
}
|
|
1364
1583
|
if (!book || !pages) {
|
|
1365
|
-
return /* @__PURE__ */ jsx6(Box6, { padding: 1, children: /* @__PURE__ */
|
|
1584
|
+
return /* @__PURE__ */ jsx6(Box6, { padding: 1, children: /* @__PURE__ */ jsxs5(Text6, { color: "cyan", children: [
|
|
1585
|
+
"\u{1F4D6} ",
|
|
1586
|
+
t("tui.reader.loading")
|
|
1587
|
+
] }) });
|
|
1366
1588
|
}
|
|
1367
1589
|
return /* @__PURE__ */ jsx6(
|
|
1368
1590
|
ReaderContent,
|
|
@@ -1421,19 +1643,19 @@ function renderApp(options = {}) {
|
|
|
1421
1643
|
// src/cli/commands/resume.ts
|
|
1422
1644
|
var resumeCommand = {
|
|
1423
1645
|
command: "resume",
|
|
1424
|
-
describe: "
|
|
1646
|
+
describe: t("cli.resume.desc"),
|
|
1425
1647
|
handler: async () => {
|
|
1426
1648
|
try {
|
|
1427
1649
|
const progressService = new ProgressService();
|
|
1428
1650
|
const lastProgress = progressService.getLastOpenedBook();
|
|
1429
1651
|
if (!lastProgress) {
|
|
1430
|
-
console.log("
|
|
1652
|
+
console.log(t("cli.resume.none"));
|
|
1431
1653
|
return;
|
|
1432
1654
|
}
|
|
1433
1655
|
const bookService = new BookService();
|
|
1434
1656
|
const book = bookService.findBook(lastProgress.book_id);
|
|
1435
1657
|
if (!book) {
|
|
1436
|
-
console.log("
|
|
1658
|
+
console.log(t("cli.resume.none"));
|
|
1437
1659
|
return;
|
|
1438
1660
|
}
|
|
1439
1661
|
logger.debug(`\u6062\u590D\u9605\u8BFB: ${book.title}, offset=${lastProgress.byte_offset}`);
|
|
@@ -1452,10 +1674,10 @@ var resumeCommand = {
|
|
|
1452
1674
|
// src/cli/commands/open.ts
|
|
1453
1675
|
var openCommand = {
|
|
1454
1676
|
command: "open <target>",
|
|
1455
|
-
describe: "
|
|
1677
|
+
describe: t("cli.open.desc"),
|
|
1456
1678
|
builder: (yargs2) => {
|
|
1457
1679
|
return yargs2.positional("target", {
|
|
1458
|
-
describe: "
|
|
1680
|
+
describe: t("cli.open.help"),
|
|
1459
1681
|
type: "string",
|
|
1460
1682
|
demandOption: true
|
|
1461
1683
|
});
|
|
@@ -1465,8 +1687,7 @@ var openCommand = {
|
|
|
1465
1687
|
const bookService = new BookService();
|
|
1466
1688
|
const book = bookService.findBook(argv.target);
|
|
1467
1689
|
if (!book) {
|
|
1468
|
-
console.log(
|
|
1469
|
-
console.log(" \u4F7F\u7528 novel library \u67E5\u770B\u4E66\u67B6\u4E2D\u7684\u6240\u6709\u4E66\u7C4D\u3002");
|
|
1690
|
+
console.log(`${t("cli.open.not_found")} ${argv.target}`);
|
|
1470
1691
|
process.exit(1);
|
|
1471
1692
|
}
|
|
1472
1693
|
const progressService = new ProgressService();
|
|
@@ -1488,11 +1709,11 @@ var openCommand = {
|
|
|
1488
1709
|
// src/cli/commands/library.ts
|
|
1489
1710
|
var libraryCommand = {
|
|
1490
1711
|
command: "library",
|
|
1491
|
-
describe: "
|
|
1712
|
+
describe: t("cli.library.desc"),
|
|
1492
1713
|
builder: (yargs2) => {
|
|
1493
1714
|
return yargs2.option("search", {
|
|
1494
1715
|
alias: "s",
|
|
1495
|
-
describe: "
|
|
1716
|
+
describe: t("cli.library.help"),
|
|
1496
1717
|
type: "string"
|
|
1497
1718
|
});
|
|
1498
1719
|
},
|
|
@@ -1502,11 +1723,10 @@ var libraryCommand = {
|
|
|
1502
1723
|
const bookService = new BookService();
|
|
1503
1724
|
const books = bookService.searchBooks(argv.search);
|
|
1504
1725
|
if (books.length === 0) {
|
|
1505
|
-
console.log(
|
|
1726
|
+
console.log(t("cli.library.search_none", argv.search));
|
|
1506
1727
|
return;
|
|
1507
1728
|
}
|
|
1508
|
-
console.log(
|
|
1509
|
-
`);
|
|
1729
|
+
console.log(t("cli.library.search_result", books.length));
|
|
1510
1730
|
books.forEach((book, index) => {
|
|
1511
1731
|
console.log(` ${index + 1}. ${book.title} [${book.id}] (${book.format})`);
|
|
1512
1732
|
});
|
|
@@ -1523,10 +1743,10 @@ var libraryCommand = {
|
|
|
1523
1743
|
// src/cli/commands/remove.ts
|
|
1524
1744
|
var removeCommand = {
|
|
1525
1745
|
command: "remove <target>",
|
|
1526
|
-
describe: "
|
|
1746
|
+
describe: t("cli.remove.desc"),
|
|
1527
1747
|
builder: (yargs2) => {
|
|
1528
1748
|
return yargs2.positional("target", {
|
|
1529
|
-
describe: "
|
|
1749
|
+
describe: t("cli.remove.help"),
|
|
1530
1750
|
type: "string",
|
|
1531
1751
|
demandOption: true
|
|
1532
1752
|
});
|
|
@@ -1536,11 +1756,11 @@ var removeCommand = {
|
|
|
1536
1756
|
const bookService = new BookService();
|
|
1537
1757
|
const book = bookService.findBook(argv.target);
|
|
1538
1758
|
if (!book) {
|
|
1539
|
-
console.log(
|
|
1759
|
+
console.log(`${t("cli.remove.not_found")} ${argv.target}`);
|
|
1540
1760
|
process.exit(1);
|
|
1541
1761
|
}
|
|
1542
1762
|
bookService.deleteBook(book.id);
|
|
1543
|
-
console.log(
|
|
1763
|
+
console.log(`${t("cli.remove.success")} ${book.title}`);
|
|
1544
1764
|
} catch (error) {
|
|
1545
1765
|
logger.error("\u79FB\u9664\u4E66\u7C4D\u5931\u8D25:", error);
|
|
1546
1766
|
process.exit(1);
|
|
@@ -1548,9 +1768,34 @@ var removeCommand = {
|
|
|
1548
1768
|
}
|
|
1549
1769
|
};
|
|
1550
1770
|
|
|
1771
|
+
// src/cli/commands/lang.ts
|
|
1772
|
+
var langCommand = {
|
|
1773
|
+
command: "lang <target>",
|
|
1774
|
+
describe: t("cli.lang.desc"),
|
|
1775
|
+
builder: (yargs2) => {
|
|
1776
|
+
return yargs2.positional("target", {
|
|
1777
|
+
describe: t("cli.lang.help"),
|
|
1778
|
+
type: "string",
|
|
1779
|
+
choices: ["zh", "en"],
|
|
1780
|
+
demandOption: true
|
|
1781
|
+
});
|
|
1782
|
+
},
|
|
1783
|
+
handler: (argv) => {
|
|
1784
|
+
const lang = argv.target;
|
|
1785
|
+
if (lang === "zh" || lang === "en") {
|
|
1786
|
+
setConfig("language", lang);
|
|
1787
|
+
setLanguage(lang);
|
|
1788
|
+
console.log(t("cli.lang.success", lang));
|
|
1789
|
+
} else {
|
|
1790
|
+
console.log(t("cli.lang.unsupported", lang));
|
|
1791
|
+
process.exit(1);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
|
|
1551
1796
|
// src/cli/parser.ts
|
|
1552
1797
|
function createParser() {
|
|
1553
|
-
return yargs(hideBin(process.argv)).scriptName("novel").usage("$0 <command> [options]").command(importCommand).command(resumeCommand).command(openCommand).command(libraryCommand).command(removeCommand).demandCommand(1, "\u8BF7\u6307\u5B9A\u4E00\u4E2A\u547D\u4EE4\u3002\u4F7F\u7528 --help \u67E5\u770B\u53EF\u7528\u547D\u4EE4\u3002").strict().alias("h", "help").alias("v", "version").version("0.1.0").epilogue("ReadShell \u2014 \u7EC8\u7AEF\u5185\u4F4E\u6253\u65AD\u8F7B\u9605\u8BFB\u5DE5\u5177");
|
|
1798
|
+
return yargs(hideBin(process.argv)).scriptName("novel").usage("$0 <command> [options]").command(importCommand).command(resumeCommand).command(openCommand).command(libraryCommand).command(removeCommand).command(langCommand).demandCommand(1, "\u8BF7\u6307\u5B9A\u4E00\u4E2A\u547D\u4EE4\u3002\u4F7F\u7528 --help \u67E5\u770B\u53EF\u7528\u547D\u4EE4\u3002").strict().alias("h", "help").alias("v", "version").version("0.1.0").epilogue("ReadShell \u2014 \u7EC8\u7AEF\u5185\u4F4E\u6253\u65AD\u8F7B\u9605\u8BFB\u5DE5\u5177");
|
|
1554
1799
|
}
|
|
1555
1800
|
|
|
1556
1801
|
// src/db/migrate.ts
|
|
@@ -1645,6 +1890,7 @@ function migrate(db2, fromVersion) {
|
|
|
1645
1890
|
async function main() {
|
|
1646
1891
|
try {
|
|
1647
1892
|
initDatabase();
|
|
1893
|
+
initI18n();
|
|
1648
1894
|
const parser = createParser();
|
|
1649
1895
|
await parser.parse();
|
|
1650
1896
|
} catch (error) {
|