Repository Analysis

readest/readest

Readest is a modern, feature-rich ebook reader designed for avid readers offering seamless cross-platform access, powerful tools, and an intuitive interface to elevate your reading experience.

2.0 Likely human-written View on GitHub
2.0
Adjusted Score
2.0
Raw Score
100%
Time Factor
2026-05-30
Last Push
20,977
Stars
TypeScript
Language
333,489
Lines of Code
1559
Files
296
Pattern Hits
2026-05-31
Scan Date

Score History

Severity Breakdown

CRITICAL 1HIGH 0MEDIUM 169LOW 126

Pattern Findings

296 matches across 9 categories. Click a row to expand file-level details.

Decorative Section Separators168 hits · 513 pts
SeverityFileLineSnippet
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs24// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs26// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs113// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs115// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs243// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs245// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs274// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs276// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs337// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs339// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs362// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs364// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs379// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs381// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs466// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs468// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs528// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…est-app/extensions/windows-thumbnail/src/extraction.rs530// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs38// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs40// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs74// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs76// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs113// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs115// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs268// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs270// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs325// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs327// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs383// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…t-app/extensions/windows-thumbnail/src/com_provider.rs385// ─────────────────────────────────────────────────────────────────────────────
MEDIUM…gin-native-bridge/ios/Sources/NativeBridgePlugin.swift1159 // ── Sync passphrase keychain ──────────────────────────────────────────
MEDIUM…ive-bridge/android/src/main/java/NativeBridgePlugin.kt800 // ── Sync passphrase keychain ──────────────────────────────────────
MEDIUM…tauri/plugins/tauri-plugin-native-bridge/src/models.rs271// ── Sync passphrase keychain ────────────────────────────────────────────
MEDIUM…auri/plugins/tauri-plugin-native-bridge/src/desktop.rs196 // ── Sync passphrase keychain ────────────────────────────────────────
MEDIUMapps/readest-app/src-tauri/gen/apple/project.yml32 # ----------------------------------
MEDIUMapps/readest-app/src/utils/bridge.ts222// ── Sync passphrase keychain ────────────────────────────────────────────
MEDIUMapps/readest-app/src/utils/warichu.ts120// ── Core measurement ──────────────────────────────────────────────────
MEDIUMapps/readest-app/src/utils/warichu.ts173// ── Build nodes ───────────────────────────────────────────────────────
MEDIUMapps/readest-app/src/utils/warichu.ts356// ── Helpers ───────────────────────────────────────────────────────────
MEDIUMapps/readest-app/src/utils/warichu.ts472// ── HTML slicing helpers ──────────────────────────────────────────────
MEDIUMapps/readest-app/src/utils/event.ts65// ─── Settled events (one-shot, replay-on-subscribe) ────────────────────
MEDIUMapps/readest-app/src/__tests__/utils/image.test.ts3// ── Mock globals for jsdom canvas / Image / fetch ─────────────────────
MEDIUMapps/readest-app/src/__tests__/utils/nav.test.ts3// ── Module mocks ─────────────────────────────────────────────────────
MEDIUMapps/readest-app/src/__tests__/utils/nav.test.ts52// ── Helpers ──────────────────────────────────────────────────────────
MEDIUMapps/readest-app/src/__tests__/utils/nav.test.ts92// ── Tests ────────────────────────────────────────────────────────────
MEDIUM…__/components/annotation-popup-layout.browser.test.tsx18// ── Tailwind / DaisyUI styles ───────────────────────────────────────────
MEDIUM…__/components/annotation-popup-layout.browser.test.tsx21// ── Per-test state read by mocks ────────────────────────────────────────
MEDIUM…__/components/annotation-popup-layout.browser.test.tsx24// ── Mocks (must be before component imports) ────────────────────────────
MEDIUM…__/components/annotation-popup-layout.browser.test.tsx79// ── Real component imports ──────────────────────────────────────────────
MEDIUM…__/components/annotation-popup-layout.browser.test.tsx84// ── Constants ───────────────────────────────────────────────────────────
MEDIUM…__/components/annotation-popup-layout.browser.test.tsx153// ── Lifecycle ───────────────────────────────────────────────────────────
MEDIUM…__/components/annotation-popup-layout.browser.test.tsx167// ── Tests ───────────────────────────────────────────────────────────────
MEDIUMapps/readest-app/src/__tests__/helpers/updater.test.ts4// ── Mocks for Tauri and internal modules ─────────────────────────
MEDIUMapps/readest-app/src/__tests__/helpers/updater.test.ts78// ── Helper to create a dummy TranslationFunc ─────────────────────
MEDIUMapps/readest-app/src/__tests__/helpers/updater.test.ts100 // ── checkForAppUpdates ─────────────────────────────────────────
MEDIUMapps/readest-app/src/__tests__/helpers/updater.test.ts267 // ── checkAppReleaseNotes ───────────────────────────────────────
MEDIUMapps/readest-app/src/__tests__/helpers/updater.test.ts351 // ── semver usage validation ────────────────────────────────────
MEDIUM…ps/readest-app/src/__tests__/helpers/open-with.test.ts3// ── Mocks ────────────────────────────────────────────────────────
MEDIUM…ps/readest-app/src/__tests__/helpers/open-with.test.ts50 // ── Web platform ───────────────────────────────────────────────
MEDIUM…ps/readest-app/src/__tests__/helpers/open-with.test.ts61 // ── Window URL params ──────────────────────────────────────────
108 more matches not shown…
Over-Commented Block74 hits · 74 pts
SeverityFileLineSnippet
LOW.github/workflows/codeql.yml1# For most projects, this workflow file will not need changing; you simply need
LOW.github/workflows/scorecard.yml41 - name: "Run analysis"
LOWapps/readest-app/vitest.tauri.setup.ts1// Vitest runs tests inside an iframe, but Tauri injects its plugin internals
LOWapps/readest-app/scripts/sync-release-notes.sh1#!/bin/bash
LOWapps/readest-app/scripts/worktree-new.ts241}
LOW…gin-native-bridge/ios/Sources/NativeBridgePlugin.swift561 ///
LOW…gin-native-bridge/ios/Sources/NativeBridgePlugin.swift1241 // views. Two notes:
LOW…gin-native-bridge/ios/Sources/NativeBridgePlugin.swift1361 /// it survives until the user dismisses the picker. `present`
LOW…gin-native-bridge/ios/Sources/NativeBridgePlugin.swift1421
LOW…gin-native-bridge/ios/Sources/NativeBridgePlugin.swift1441/// migrated) `URL(resolvingBookmarkData:..., bookmarkDataIsStale:)`
LOW…gin-native-bridge/ios/Sources/NativeBridgePlugin.swift1541 url.stopAccessingSecurityScopedResource()
LOW…gin-native-bridge/ios/Sources/NativeBridgePlugin.swift1581 // returned by the picker that lives outside our sandbox (which is
LOW…ugin-native-bridge/ios/Sources/ClipUrlController.swift41 case .loadFailed(let detail): return "Could not fetch this page: \(detail)"
LOW…-native-bridge/ios/Sources/ContextMenuSuppressor.swift1import ObjectiveC
LOW…-native-bridge/ios/Sources/ContextMenuSuppressor.swift21/// a backstop in case WebKit presents without re-querying the delegate.
LOW…tauri/plugins/tauri-plugin-native-bridge/src/models.rs221 pub success: bool,
LOW…tauri/plugins/tauri-plugin-native-bridge/src/models.rs301/// struct in `clip_url.rs` so the JS caller can pass the same payload
LOWapps/readest-app/src-tauri/gen/apple/project.yml41 # add IAP via Signing & Capabilities is informational only; it
LOWapps/readest-app/src-tauri/gen/apple/project.yml141 # Entitlements referenced via CODE_SIGN_ENTITLEMENTS build setting
LOW…rc-tauri/gen/apple/ShareExtension/AppGroupBridge.swift1// Shared App Group container schema between the Readest Share Extension and
LOW…uri/gen/apple/ShareExtension/ShareViewController.swift1// Share Extension for Readest: catches an article URL from any iOS share
LOW…uri/gen/apple/ShareExtension/ShareViewController.swift21//
LOWapps/readest-app/src-tauri/src/lib.rs81/// protocol (`RemoteFile`) to read user-selected files. Without this,
LOWapps/readest-app/src-tauri/src/lib.rs101/// access to the entire user home directory via the asset
LOWapps/readest-app/src-tauri/src/clip_url.rs1// Spawn a hidden Tauri webview that loads the target URL with the real
LOWapps/readest-app/src-tauri/src/clip_url.rs21// invoke('clip_url', url) ─┬─▶ bind 127.0.0.1:RANDOM_PORT
LOWapps/readest-app/src-tauri/src/clip_url.rs541 window.__readest_setStatus__(CAPTURING_STATUS);
LOWapps/readest-app/src-tauri/src/clip_url.rs581 // Hard fallback in case `load` never fires (SPA, error state,
LOWapps/readest-app/src-tauri/src/clip_url.rs661}
LOWapps/readest-app/src-tauri/src/macos/traffic_light.rs61 Err(_) => return,
LOWapps/readest-app/src-tauri/src/macos/traffic_light.rs81 ((header_height - button_height) / 2.0 + button_origin_y).max(0.0)
LOWapps/readest-app/src-tauri/src/macos/traffic_light.rs181
LOW…s/readest-app/src-tauri/src/macos/system_dictionary.rs1/// macOS native dictionary "Look Up" HUD bridge.
LOW…s/readest-app/src-tauri/src/macos/system_dictionary.rs41/// points, which on standard Tauri/macOS already match CSS pixels
LOW…s/readest-app/src-tauri/src/macos/system_dictionary.rs201 if let Some(size) = font_size {
LOWapps/readest-app/workers/send-email/src/index.ts61 const userId = addressRow.user_id as string;
LOWapps/readest-app/src/types/settings.ts101 password: string;
LOWapps/readest-app/src/app/layout.tsx81// - Android emul.: page at `http://tauri.localhost` → `ws://tauri.localhost/_next/...`
LOWapps/readest-app/src/app/reader/utils/wheelGesture.ts1// Wheel-to-page-flip gesture detection.
LOW…dest-app/src/app/reader/components/sidebar/TOCView.tsx61 const visibleCenterRef = useRef(0);
LOWapps/readest-app/src/app/reader/hooks/useWebDAVSync.ts221 // targets where streaming PUTs aren't available.
LOWapps/readest-app/src/app/opds/page.tsx101 const [newCatalogName, setNewCatalogName] = useState('');
LOWapps/readest-app/src/app/opds/page.tsx761 didRestorePubRef.current = true;
LOWapps/readest-app/src/app/library/page.tsx301 // URL cosmetically via window.history.replaceState — Next.js' patched
LOWapps/readest-app/src/app/library/page.tsx681 // named "Books" (e.g. Baidu Netdisk's default `Books/` directory
LOWapps/readest-app/src/app/library/page.tsx1081 * for "virtual" Files-app entries (the "On My iPhone" root, "Recents",
LOWapps/readest-app/src/app/library/page.tsx1201 // come from the persisted "last import folder" in localStorage —
LOWapps/readest-app/src/app/library/page.tsx1221 // didn't authorise — typically the persisted "last import folder"
LOWapps/readest-app/src/app/api/opds/proxy/route.ts121 // Headers that must NOT be forwarded as-is when we proxy the body:
LOW…/readest-app/src/app/api/share/[token]/og.png/route.ts1import { renderShareOgImage } from './render';
LOWapps/readest-app/src/utils/sel.ts161 const sy = parts?.[3];
LOWapps/readest-app/src/utils/serializer.ts21 config = JSON.parse(JSON.stringify(config));
LOWapps/readest-app/src/utils/event.ts61}
LOWapps/readest-app/src/components/SegmentedControl.tsx21 size?: 'sm' | 'md';
LOWapps/readest-app/src/components/Providers.tsx161 applyBackgroundTexture(envConfig, globalViewSettings);
LOW…/components/settings/integrations/WebDAVBrowsePane.tsx61 const { settings: globalSettings } = useSettingsStore();
LOW…pp/src/components/settings/integrations/WebDAVForm.tsx181 enabled: false,
LOW…pp/src/components/settings/integrations/WebDAVForm.tsx301 loadBookFile: async (book) => {
LOW…dest-app/src/__tests__/database/suites/vector-tests.ts241 // @readest/turso-database-wasm) has vector storage + distance functions
LOW…ests__/document/fixed-layout-overlayer-viewbox.test.ts1// Regression test for: TTS highlight overlay is off the text boxes in
14 more matches not shown…
Hyper-Verbose Identifiers27 hits · 30 pts
SeverityFileLineSnippet
LOW…extensions/send-to-readest/src/background/offscreen.ts119export async function clipAndUploadViaOffscreen(opts: {
LOW…/readest-app/src/app/reader/utils/globalAnnotations.ts265export function expandAllRenderedSections(view: FoliateView | null, note: BookNote): void {
LOW…/readest-app/src/app/reader/utils/globalAnnotations.ts304export function removeGlobalAnnotationOverlays(
LOW…app/src/app/reader/components/notebook/AIAssistant.tsx40function convertToExportedMessages(
LOWapps/readest-app/src/app/api/stripe/webhook/route.ts83async function handleSuccessfulSubscription(session: Stripe.Checkout.Session, userId: string) {
LOWapps/readest-app/src/app/api/stripe/webhook/route.ts161async function handleSubscriptionUpdated(subscription: Stripe.Subscription) {
LOWapps/readest-app/src/app/api/stripe/webhook/route.ts179async function handleSubscriptionCancelled(subscription: Stripe.Subscription) {
LOWapps/readest-app/src/libs/payment/iap/apple/server.ts28export async function createOrUpdateSubscription(userId: string, purchase: VerifiedPurchase) {
LOWapps/readest-app/src/libs/payment/iap/google/server.ts39export async function createOrUpdateSubscription(userId: string, purchase: VerifiedPurchase) {
LOW…-app/src/__tests__/hooks/useSpatialNavigation.test.tsx56function simulateKeyboardActivation() {
LOWapps/readest-app/src/hooks/usePullToRefresh.ts104 function updateLoadingSpinnerPosition(el: HTMLDivElement, transform: number, dy: number) {
LOWapps/readest-app/src/services/cloudService.ts122export async function downloadReplicaFileFromCloud(
LOWapps/readest-app/src/services/cloudService.ts141export async function deleteReplicaBundleFromCloud(
LOWapps/readest-app/src/services/backupService.ts96export function sanitizeSettingsForBackup(
LOWapps/readest-app/src/services/bookContent.ts26async function resolveLegacyManagedSource(
LOWapps/readest-app/src/services/settingsService.ts62export function migrateHighlightColorPrefs(read: ReadSettings): void {
LOW…adest-app/src/services/translators/providers/yandex.ts10async function translateSingleTextForService(
LOW…p/src/services/reedy/tools/builtins/createHighlight.ts26export function createCreateHighlightTool(
LOW…src/services/reedy/tools/builtins/getReadingContext.ts14export function createGetReadingContextTool(
LOW…t-app/src/services/reedy/tools/builtins/memoryTools.ts135export function createSearchUserMemoryTool(deps: SearchMemoryToolDeps) {
LOW…t-app/src/services/reedy/tools/builtins/memoryTools.ts144export function createWriteUserMemoryTool(deps: WriteMemoryToolDeps) {
LOW…t-app/src/services/reedy/tools/builtins/memoryTools.ts153export function createSearchBookMemoryTool(deps: SearchMemoryToolDeps) {
LOW…t-app/src/services/reedy/tools/builtins/memoryTools.ts162export function createWriteBookMemoryTool(deps: WriteMemoryToolDeps) {
LOW…t-app/src/services/reedy/tools/builtins/memoryTools.ts171export function createSearchSessionMemoryTool(deps: SearchMemoryToolDeps) {
LOW…p/src/services/ai/providers/ProxiedGatewayEmbedding.ts8export function createProxiedEmbeddingModel(options: ProxiedEmbeddingOptions): EmbeddingModel {
LOW…s/readest-app/src/services/ai/adapters/ReedyBackend.ts162function mapToolEventToMetricEvent(
LOWapps/readest-app/src/store/proofreadStore.ts306export function validateReplacementRulePattern(
Fake / Example Data20 hits · 24 pts
SeverityFileLineSnippet
LOWapps/readest-app/src/__tests__/reedy/CfiChunker.test.ts112 const paragraph = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '.repeat(40);
LOWapps/readest-app/src/__tests__/reedy/CfiChunker.test.ts112 const paragraph = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '.repeat(40);
LOW…/readest-app/src/__tests__/utils/library-utils.test.ts43 expect(parseAuthors('John Smith, Jane Doe')).toEqual(['John Smith', 'Jane Doe']);
LOW…/readest-app/src/__tests__/utils/library-utils.test.ts47 expect(parseAuthors('John Smith & Jane Doe')).toEqual(['John Smith', 'Jane Doe']);
LOW…/readest-app/src/__tests__/utils/library-utils.test.ts51 expect(parseAuthors('John Smith and Jane Doe')).toEqual(['John Smith', 'Jane Doe']);
LOW…/readest-app/src/__tests__/utils/library-utils.test.ts57 'Jane Doe',
LOW…/readest-app/src/__tests__/utils/library-utils.test.ts65 'Jane Doe',
LOW…/readest-app/src/__tests__/utils/library-utils.test.ts71 expect(parseAuthors(' John Smith , Jane Doe ')).toEqual(['John Smith', 'Jane Doe']);
LOW…/readest-app/src/__tests__/utils/library-utils.test.ts215 const jane = groups.find((g) => g.name === 'Jane Doe');
LOWapps/readest-app/src/__tests__/utils/txt-worker.test.ts55 const options = { file, author: 'Jane Doe', language: 'zh' };
LOWapps/readest-app/src/__tests__/ai/chunker.test.ts67 const longText = 'Lorem ipsum dolor sit amet. '.repeat(50);
LOWapps/readest-app/src/__tests__/ai/chunker.test.ts67 const longText = 'Lorem ipsum dolor sit amet. '.repeat(50);
LOW…c/__tests__/services/send-convert-page-unified.test.ts41 <p>${'lorem ipsum dolor sit amet '.repeat(40)}</p>
LOW…c/__tests__/services/send-convert-page-unified.test.ts41 <p>${'lorem ipsum dolor sit amet '.repeat(40)}</p>
LOW…eadest-app/src/__tests__/services/send-address.test.ts20 expect(slugFromIdentity('Jane Doe')).toBe('janedoe');
LOW…pp/src/__tests__/services/send-cover-generator.test.ts43 author: 'Jane Doe',
LOW…pp/src/__tests__/services/send-cover-generator.test.ts46 expect(svg).toContain('Jane Doe');
LOW…pp/src/__tests__/services/send-cover-generator.test.ts126 expect(pickTheme('Jane Doe')).toEqual(pickTheme('Jane Doe'));
LOW…pp/src/__tests__/services/send-cover-generator.test.ts131 const theme = pickTheme('Jane Doe');
LOW…pp/src/__tests__/services/send-cover-generator.test.ts137 expect(pickTheme(' Jane Doe ')).toEqual(pickTheme('Jane Doe'));
Hallucination Indicators1 hit · 10 pts
SeverityFileLineSnippet
CRITICAL…app/extensions/send-to-readest/src/popup/popup.test.ts188 const clipCall = chromeMock.runtime.sendMessage.mock.calls.find((c) => {
Slop Phrases1 hit · 3 pts
SeverityFileLineSnippet
MEDIUM.github/workflows/codeql.yml60 # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
Redundant / Tautological Comments2 hits · 3 pts
SeverityFileLineSnippet
LOWapps/readest-app/scripts/sync-release-notes.sh36# Check if required commands are available
LOWapps/readest-app/scripts/sync-release-notes.sh64# Check if releases key exists
Overly Generic Function Names2 hits · 2 pts
SeverityFileLineSnippet
LOWapps/readest-app/src/app/api/opds/proxy/route.ts5async function handleRequest(request: NextRequest, method: 'GET' | 'HEAD') {
LOWapps/readest-app/src/utils/warichu.ts367function getData(el: HTMLElement) {
Verbosity Indicators1 hit · 2 pts
SeverityFileLineSnippet
LOWapps/readest-app/src/services/cloudService.ts315 // some books may not have cover image, so we need to check if the book is downloaded