forked from akai/readest
5e366018df
* fix(cbz,i18n): ComicInfo metadata + CBZ page count + WebDAV i18n Closes #4253 (ComicInfo.xml not read) and #4255 (CBZ shows "1 page left"). CBZ / ComicInfo (foliate-js submodule + Readest derivation): - comic-book.js: find ComicInfo.xml in subdirectories too, parse description / subject / identifier / published / series fields beyond the prior name+position pair. Series Count populates the canonical `belongsTo.series.total`; no top-level duplication. - bookService.ts / readerStore.ts: derive `metadata.seriesTotal` from `belongsTo.series.total` in parallel to the existing series / seriesIndex derivation. - ProgressBar / FooterBar / DesktopFooterBar: drop the hard-coded `pagesLeft = 1` for fixed-layout books and compute it from `section.total - section.current`. FooterBar uses `FIXED_LAYOUT_FORMATS.has(bookFormat)` so CBZ picks `section` (correct image count) instead of `pageinfo` (locations). - ProgressBar: switch the remaining-pages text to "in book" for fixed-layout titles (no chapter structure) and keep "in chapter" for reflowable books. WebDAV refactor for translation coverage: - WebDAVBrowsePane / SyncHistoryPanel called `t(...)` (passed as a prop) instead of `_(...)`. The i18next-scanner only looks for `_`, so ~53 strings were unreachable and shipped in English to every locale. Switched both components to call `useTranslation()` themselves; helpers that aren't React FCs take `_: TranslationFunc` so the scanner sees the literal calls. - WebDAVClient.checkConnection now returns a `code` discriminator (`SERVER_URL_REQUIRED` / `AUTH_FAILED` / `ROOT_NOT_FOUND` / `UNEXPECTED_STATUS` / `NETWORK`); raw English `message` is reserved for the dev console. New `formatConnectError` and `formatSyncError` helpers in WebDAVForm translate via a switch where each branch is a literal `_('...')`. Same treatment for the sync-failure path that previously surfaced raw e.message. - "Syncing 0 / {{total}}" is now parameterized as "Syncing {{n}} / {{total}}" with n=0 at startup so the digit formats naturally and the template can be reused mid-sync. - "Cleanup · {{count}} book(s)" hard-coded options used unsupported ternary; rewrote as plural-aware key. i18n scanner fix (i18next-scanner.config.cjs): - vinyl-fs walked into directories whose names end in source-file extensions (Next.js route folder `runtime-config.js/`, Playwright screenshot folder `*.test.tsx/`) and crashed with EISDIR. Resolved by expanding globs via `fs.globSync` and filtering to files only before handing to the scanner. TypeScript-syntax sites that broke esprima during extraction: - WebDAVBrowsePane / WebDAVForm: `(e as Error).message` and `failed[0]!.title` inside `_(..., options)` arguments. Replaced with `e instanceof Error ? e.message : String(e)` and `failed[0]?.title ?? ''` — also runtime-safer. User-facing em-dash cleanup: - Removed em-dashes from translation keys across SyncHistoryPanel / WebDAVForm / WebDAVBrowsePane / SyncPassphraseSection / send/page / replicaCryptoMiddleware / AIPanel. Tagline in `layout.tsx` kept. Locale translations: - ~2400 translations applied across all 33 locales for the keys that were either newly extractable, freshly worded, or pre-existing but untranslated. Zero `__STRING_NOT_TRANSLATED__` remain after the run. Misc: - next.config.mjs: drop `eslint.ignoreDuringBuilds: true` so build runs the same lint as CI. - Collection type: add `total?: string` for ComicInfo series count. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(test): fix vitest invocation, run with 4 workers `pnpm test:pr:web` was chaining `pnpm test -- --watch=false`, which pnpm expanded into: dotenv -e .env -e .env.test.local -- vitest -- --watch=false The second `--` made vitest treat `--watch=false` as a positional file pattern, not a flag. Vitest then fell back to defaults (in CI's non-TTY env that still meant a one-shot run, so the suite passed), but the worker pool was effectively serialized for big chunks of the 243-file run — wall ~90 s on a 4-vCPU runner where the parallel-sum of phases was ~236 s (≈2.6× effective parallelism). Replace the chained pnpm invocation with a direct call to `vitest run --maxWorkers=4`, matching the 4 vCPUs the GH Actions ubuntu-latest runner provides. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
265 lines
14 KiB
JSON
265 lines
14 KiB
JSON
{
|
|
"name": "@readest/readest-app",
|
|
"version": "0.11.1",
|
|
"private": true,
|
|
"type": "module",
|
|
"scripts": {
|
|
"dev": "dotenv -e .env.tauri -- next dev",
|
|
"build": "dotenv -e .env.tauri -- next build",
|
|
"start": "dotenv -e .env.tauri -- next start",
|
|
"dev-web": "dotenv -e .env.web -- next dev",
|
|
"build-web": "dotenv -e .env.web -- next build",
|
|
"start-web": "dotenv -e .env.web -- next start",
|
|
"dev-web:vinext": "dotenv -e .env.web -- vinext dev",
|
|
"build-web:vinext": "dotenv -e .env.web -- vinext build",
|
|
"start-web:vinext": "dotenv -e .env.web -- vinext start",
|
|
"build-tauri": "dotenv -e .env.tauri -- next build",
|
|
"build-browser-ext": "pnpm --filter @readest/send-to-readest-extension build",
|
|
"dev-android": "tauri android build -t aarch64 -- --features devtools && adb install -r src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk",
|
|
"dev-ios": "tauri ios build -- --features devtools && ideviceinstaller install src-tauri/gen/apple/build/arm64/Readest.ipa",
|
|
"i18n:extract": "i18next-scanner --config i18next-scanner.config.cjs",
|
|
"i18n:extract:ext": "pnpm --filter @readest/send-to-readest-extension i18n:extract",
|
|
"lint": "tsgo --noEmit && biome lint . && pnpm lint:lua",
|
|
"lint:lua": "node ../readest.koplugin/scripts/lint-koplugin.mjs",
|
|
"test:lua": "node ../readest.koplugin/scripts/test-koplugin.mjs",
|
|
"test": "dotenv -e .env -e .env.test.local -- vitest",
|
|
"test:extension": "dotenv -e .env -e .env.test.local -- vitest extensions/send-to-readest/ --run",
|
|
"test:coverage": "dotenv -e .env -e .env.test.local -- vitest run --coverage",
|
|
"test:browser": "dotenv -e .env -e .env.test.local -- vitest run --config vitest.browser.config.mts",
|
|
"test:tauri": "bash scripts/test-tauri.sh",
|
|
"test:pr:web": "dotenv -e .env -e .env.test.local -- vitest run --maxWorkers=4 && pnpm test:browser && pnpm test:extension && pnpm build-browser-ext",
|
|
"test:pr:tauri": "bash scripts/test-tauri.sh",
|
|
"test:all": "pnpm test -- --watch=false && pnpm test:browser && bash scripts/test-tauri.sh",
|
|
"test:e2e": "wdio run wdio.conf.ts",
|
|
"test:e2e:web": "playwright test",
|
|
"test:e2e:web:headed": "playwright test --headed --workers=1 --trace on",
|
|
"test:e2e:web:ui": "playwright test --ui",
|
|
"test:e2e:web:report": "playwright show-report",
|
|
"tauri:dev:test": "dotenv -e .env.tauri -- tauri dev --features webdriver",
|
|
"tauri:build:test": "dotenv -e .env.tauri -- tauri build --debug --features webdriver",
|
|
"tauri": "tauri",
|
|
"fmt:check": "cargo fmt -p Readest --check",
|
|
"clippy:check": "cargo clippy -p Readest --no-deps -- -D warnings",
|
|
"format": "pnpm -w format",
|
|
"format:check": "pnpm -w format:check",
|
|
"prepare-public-vendor": "mkdirp ./public/vendor/pdfjs ./public/vendor/simplecc ./public/vendor/jieba",
|
|
"copy-pdfjs-js": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/legacy/build/{pdf.worker.min.mjs,pdf.min.mjs,pdf.d.mts}\" ./public/vendor/pdfjs",
|
|
"copy-pdfjs-wasm": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/wasm/{openjpeg.wasm,qcms_bg.wasm}\" ./public/vendor/pdfjs",
|
|
"copy-pdfjs-fonts": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/{cmaps,standard_fonts}/*\" ./public/vendor/pdfjs",
|
|
"copy-flatten-pdfjs-annotation-layer-css": "npx postcss \"../../packages/foliate-js/vendor/pdfjs/annotation_layer_builder.css\" --no-map -u postcss-nested > ./public/vendor/pdfjs/annotation_layer_builder.css",
|
|
"copy-flatten-pdfjs-text-layer-css": "npx postcss \"../../packages/foliate-js/vendor/pdfjs/text_layer_builder.css\" --no-map -u postcss-nested > ./public/vendor/pdfjs/text_layer_builder.css",
|
|
"copy-flatten-pdfjs-css": "pnpm copy-flatten-pdfjs-annotation-layer-css && pnpm copy-flatten-pdfjs-text-layer-css",
|
|
"copy-pdfjs": "pnpm copy-pdfjs-js && pnpm copy-pdfjs-wasm && pnpm copy-pdfjs-fonts && pnpm copy-flatten-pdfjs-css",
|
|
"copy-simplecc": "cpx \"../../packages/simplecc-wasm/dist/web/*\" ./public/vendor/simplecc",
|
|
"copy-jieba": "cpx \"./node_modules/jieba-wasm/pkg/web/*\" ./public/vendor/jieba",
|
|
"setup-pdfjs": "pnpm prepare-public-vendor && pnpm copy-pdfjs",
|
|
"setup-simplecc": "pnpm prepare-public-vendor && pnpm copy-simplecc",
|
|
"setup-jieba": "pnpm prepare-public-vendor && pnpm copy-jieba",
|
|
"setup-vendors": "pnpm setup-pdfjs && pnpm setup-simplecc && pnpm setup-jieba",
|
|
"build-win-x64": "dotenv -e .env.tauri.local -- tauri build --target i686-pc-windows-msvc --bundles nsis",
|
|
"build-win-arm64": "dotenv -e .env.tauri.local -- tauri build --target aarch64-pc-windows-msvc --bundles nsis",
|
|
"build-linux-x64": "dotenv -e .env.tauri.local -- tauri build --target x86_64-unknown-linux-gnu --bundles appimage",
|
|
"build-macos-universial": "dotenv -e .env.tauri.local -e .env.apple-nonstore.local -- tauri build -t universal-apple-darwin --bundles dmg",
|
|
"build-macos-universial-appstore": "dotenv -e .env.tauri.local -e .env.apple-appstore.local -- tauri build -t universal-apple-darwin --bundles app --config src-tauri/tauri.appstore.conf.json",
|
|
"build-macos-universial-appstore-dev": "dotenv -e .env.tauri.local -e .env.apple-appstore-dev.local -- tauri build -t universal-apple-darwin --bundles app --config src-tauri/tauri.appstore-dev.conf.json",
|
|
"build-ios": "dotenv -e .env.ios-appstore-dev.local -- tauri ios build",
|
|
"build-ios-appstore": "dotenv -e .env.ios-appstore.local -- tauri ios build --export-method app-store-connect",
|
|
"release-macos-universial-appstore": "dotenv -e .env.tauri.local -e .env.apple-appstore.local -- bash scripts/release-mac-appstore.sh",
|
|
"release-ios-appstore": "dotenv -e .env.ios-appstore.local -- bash scripts/release-ios-appstore.sh",
|
|
"release-google-play": "dotenv -e .env.google-play.local -- bash scripts/release-google-play.sh",
|
|
"config-wrangler": "sed -i \"s/\\${TRANSLATIONS_KV_ID}/$TRANSLATIONS_KV_ID/g\" wrangler.toml",
|
|
"preview": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare preview --ip 0.0.0.0 --port 3001",
|
|
"deploy": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare deploy",
|
|
"upload": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare upload",
|
|
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts",
|
|
"patch-build-webpack": "if [ \"$(uname)\" = \"Darwin\" ]; then sed -i '' 's/next build\"/next build --webpack\"/' package.json; else sed -i 's/next build\"/next build --webpack\"/' package.json; fi",
|
|
"restore-build-original": "if [ \"$(uname)\" = \"Darwin\" ]; then sed -i '' 's/next build --webpack\"/next build\"/' package.json; else sed -i 's/next build --webpack\"/next build\"/' package.json; fi",
|
|
"update-metadata": "bash ./scripts/sync-release-notes.sh release-notes.json ../../data/metainfo/appdata.xml",
|
|
"check:optional-chaining": "count=$(grep -rno '\\?\\.[a-zA-Z_$]' .next/static/chunks/* out/_next/static/chunks/* | wc -l); if [ \"$count\" -gt 0 ]; then echo '❌ Optional chaining found in output!'; exit 1; else echo '✅ No optional chaining found.'; fi",
|
|
"check:translations": "count=$(grep -rno '__STRING_NOT_TRANSLATED__' public/locales/* | wc -l); if [ \"$count\" -gt 0 ]; then echo '❌ Untranslated strings found!'; exit 1; else echo '✅ All strings translated.'; fi",
|
|
"check:lookbehind-regex": "count=$(grep -rnoE '\\(\\?<[!=]' .next/static/chunks/* out/_next/static/chunks/* | wc -l); if [ \"$count\" -gt 0 ]; then echo '❌ Lookbehind regex found in output!'; exit 1; else echo '✅ No lookbehind regex found.'; fi",
|
|
"check:all": "pnpm check:translations && pnpm check:lookbehind-regex",
|
|
"build-check": "pnpm build && pnpm build-web && pnpm check:all",
|
|
"worktree:new": "pnpm exec tsx scripts/worktree-new.ts",
|
|
"worktree:rm": "pnpm exec tsx scripts/worktree-rm.ts"
|
|
},
|
|
"dependencies": {
|
|
"@ai-sdk/react": "^3.0.49",
|
|
"@assistant-ui/react": "0.11.56",
|
|
"@assistant-ui/react-ai-sdk": "1.1.21",
|
|
"@assistant-ui/react-markdown": "0.11.9",
|
|
"@aws-sdk/client-s3": "^3.1000.0",
|
|
"@aws-sdk/s3-request-presigner": "^3.1000.0",
|
|
"@choochmeque/tauri-plugin-sharekit-api": "^0.3.0",
|
|
"@dnd-kit/core": "^6.3.1",
|
|
"@dnd-kit/sortable": "^10.0.0",
|
|
"@dnd-kit/utilities": "^3.2.2",
|
|
"@emnapi/core": "^1.8.1",
|
|
"@emnapi/runtime": "^1.8.1",
|
|
"@fabianlars/tauri-plugin-oauth": "2",
|
|
"@mozilla/readability": "^0.6.0",
|
|
"@napi-rs/wasm-runtime": "^1.1.1",
|
|
"@opennextjs/cloudflare": "^1.19.4",
|
|
"@radix-ui/react-collapsible": "^1.1.12",
|
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
"@radix-ui/react-hover-card": "^1.1.15",
|
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
"@radix-ui/react-select": "^2.2.6",
|
|
"@radix-ui/react-separator": "^1.1.8",
|
|
"@radix-ui/react-slot": "^1.2.4",
|
|
"@radix-ui/react-tabs": "^1.1.13",
|
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
"@readest/turso-database-common": "0.6.0-pre.28-readest.0",
|
|
"@readest/turso-database-wasm": "0.6.0-pre.28-readest.0",
|
|
"@readest/turso-database-wasm-common": "0.6.0-pre.28-readest.0",
|
|
"@serwist/next": "^9.5.6",
|
|
"@serwist/webpack-plugin": "^9.5.6",
|
|
"@stripe/react-stripe-js": "^6.2.0",
|
|
"@stripe/stripe-js": "^9.3.1",
|
|
"@supabase/auth-ui-react": "^0.4.7",
|
|
"@supabase/auth-ui-shared": "^0.1.8",
|
|
"@supabase/supabase-js": "^2.76.1",
|
|
"@tauri-apps/api": "2.10.1",
|
|
"@tauri-apps/plugin-cli": "^2.4.1",
|
|
"@tauri-apps/plugin-deep-link": "^2.4.7",
|
|
"@tauri-apps/plugin-dialog": "^2.6.0",
|
|
"@tauri-apps/plugin-fs": "^2.4.5",
|
|
"@tauri-apps/plugin-haptics": "^2.3.2",
|
|
"@tauri-apps/plugin-http": "^2.5.7",
|
|
"@tauri-apps/plugin-log": "^2.8.0",
|
|
"@tauri-apps/plugin-opener": "^2.5.3",
|
|
"@tauri-apps/plugin-os": "^2.3.2",
|
|
"@tauri-apps/plugin-process": "^2.3.1",
|
|
"@tauri-apps/plugin-shell": "~2.3.5",
|
|
"@tauri-apps/plugin-updater": "^2.10.0",
|
|
"@tauri-apps/plugin-websocket": "~2.4.2",
|
|
"@tybys/wasm-util": "^0.10.1",
|
|
"@zip.js/zip.js": "^2.8.16",
|
|
"abortcontroller-polyfill": "^1.7.8",
|
|
"ai": "^6.0.47",
|
|
"ai-sdk-ollama": "^3.2.0",
|
|
"app-store-server-api": "^0.17.1",
|
|
"aws4fetch": "^1.0.20",
|
|
"buffer": "^6.0.3",
|
|
"class-variance-authority": "^0.7.1",
|
|
"clsx": "^2.1.1",
|
|
"cmdk": "^1.1.1",
|
|
"cors": "^2.8.5",
|
|
"dayjs": "^1.11.13",
|
|
"dompurify": "^3.4.0",
|
|
"fflate": "^0.8.2",
|
|
"foliate-js": "workspace:*",
|
|
"franc-min": "^6.2.0",
|
|
"fzf": "^0.5.2",
|
|
"google-auth-library": "^10.5.0",
|
|
"googleapis": "^164.1.0",
|
|
"highlight.js": "^11.11.1",
|
|
"i18next": "^24.2.0",
|
|
"i18next-browser-languagedetector": "^8.0.2",
|
|
"i18next-http-backend": "^3.0.5",
|
|
"iso-639-2": "^3.0.2",
|
|
"iso-639-3": "^3.0.1",
|
|
"isomorphic-ws": "^5.0.0",
|
|
"jieba-wasm": "^2.4.0",
|
|
"js-md5": "^0.8.3",
|
|
"jwt-decode": "^4.0.0",
|
|
"lucide-react": "^0.562.0",
|
|
"lunr": "^2.3.9",
|
|
"mammoth": "^1.12.0",
|
|
"marked": "^15.0.12",
|
|
"nanoid": "^5.1.6",
|
|
"next": "16.2.6",
|
|
"next-view-transitions": "^0.3.5",
|
|
"nunjucks": "^3.2.4",
|
|
"overlayscrollbars": "^2.11.4",
|
|
"overlayscrollbars-react": "^0.5.6",
|
|
"posthog-js": "^1.246.0",
|
|
"react": "19.2.5",
|
|
"react-color": "^2.19.3",
|
|
"react-dom": "19.2.5",
|
|
"react-i18next": "^15.2.0",
|
|
"react-icons": "^5.4.0",
|
|
"react-responsive": "^10.0.0",
|
|
"react-virtuoso": "^4.17.0",
|
|
"react-window": "^1.8.11",
|
|
"remark-gfm": "^4.0.1",
|
|
"semver": "^7.7.1",
|
|
"streamdown": "^1.6.10",
|
|
"stripe": "^22.0.2",
|
|
"styled-jsx": "^5.1.7",
|
|
"tailwind-merge": "^3.4.0",
|
|
"tauri-plugin-device-info-api": "^1.0.1",
|
|
"tinycolor2": "^1.6.0",
|
|
"uuid": "^14.0.0",
|
|
"ws": "^8.18.3",
|
|
"zod": "^4.0.8",
|
|
"zustand": "5.0.10"
|
|
},
|
|
"devDependencies": {
|
|
"@next/bundle-analyzer": "^15.4.2",
|
|
"@playwright/test": "^1.60.0",
|
|
"@tailwindcss/typography": "^0.5.16",
|
|
"@tauri-apps/cli": "2.10.1",
|
|
"@testing-library/dom": "^10.4.0",
|
|
"@testing-library/react": "^16.3.0",
|
|
"@tursodatabase/database": "0.6.0-pre.28",
|
|
"@types/cors": "^2.8.17",
|
|
"@types/cssbeautify": "^0.3.5",
|
|
"@types/lunr": "^2.3.7",
|
|
"@types/mocha": "^10.0.10",
|
|
"@types/node": "^22.15.31",
|
|
"@types/nunjucks": "^3.2.6",
|
|
"@types/react": "^19.0.0",
|
|
"@types/react-color": "^3.0.13",
|
|
"@types/react-dom": "^19.0.0",
|
|
"@types/react-window": "^1.8.8",
|
|
"@types/semver": "^7.7.0",
|
|
"@types/tinycolor2": "^1.4.6",
|
|
"@types/uuid": "^10.0.0",
|
|
"@types/ws": "^8.18.1",
|
|
"@typescript/native-preview": "7.0.0-dev.20260312.1",
|
|
"@vitejs/plugin-react": "^5.1.1",
|
|
"@vitejs/plugin-rsc": "^0.5.23",
|
|
"@vitest/browser-playwright": "^4.0.18",
|
|
"@vitest/browser-webdriverio": "^4.0.18",
|
|
"@vitest/coverage-v8": "^4.0.18",
|
|
"@wdio/cli": "^9.25.0",
|
|
"@wdio/globals": "^9.23.0",
|
|
"@wdio/local-runner": "^9.24.0",
|
|
"@wdio/mocha-framework": "^9.24.0",
|
|
"@wdio/spec-reporter": "^9.24.0",
|
|
"@wdio/types": "^9.24.0",
|
|
"autoprefixer": "^10.4.20",
|
|
"caniuse-lite": "^1.0.30001746",
|
|
"cpx2": "^8.0.0",
|
|
"daisyui": "^4.12.24",
|
|
"dotenv-cli": "^7.4.4",
|
|
"fast-check": "^4.8.0",
|
|
"i18next-scanner": "^4.6.0",
|
|
"jsdom": "^28.1.0",
|
|
"mkdirp": "^3.0.1",
|
|
"node-env-run": "^4.0.2",
|
|
"playwright": "^1.58.2",
|
|
"postcss": "^8.4.49",
|
|
"postcss-cli": "^11.0.0",
|
|
"postcss-nested": "^7.0.2",
|
|
"raw-loader": "^4.0.2",
|
|
"react-server-dom-webpack": "^19.2.5",
|
|
"serwist": "^9.3.0",
|
|
"tailwindcss": "^3.4.18",
|
|
"typescript": "^5.7.2",
|
|
"vinext": "^0.0.21",
|
|
"vite": "^7.3.3",
|
|
"vite-tsconfig-paths": "^5.1.4",
|
|
"vitest": "^4.0.18",
|
|
"wrangler": "^4.85.0"
|
|
},
|
|
"browserslist": [
|
|
"chrome 92",
|
|
"edge 92",
|
|
"firefox 92",
|
|
"safari 15.2"
|
|
]
|
|
}
|