🐸 Read Frog - Open-source AI language learning & flashcards, an Immersive Translate alternative | 🐸 陪读蛙 - 开源 AI 语言学习与闪词卡,沉浸式翻译替代
This report summarizes scanner evidence for mengxi-ream/read-frog, a TypeScript project with 10,240 GitHub stars. SynthScan examined 387,055 lines of code across 1791 source files, recording 789 pattern matches distributed across 10 syntactic categories. The legacy adjusted pattern signal is 2.2 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 765 finding(s).
Are imports declared and package/API names verified? 8 finding(s).
Are wrappers, guards, and repeated structures necessary? 740 finding(s).
Which changes need focused human review? 789 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 789 distinct pattern matches across 10 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/scrape-ai-sdk-provider-models.ts | 427 | function discoverProvidersFromHtml(html: string, baseUrl: string): ProviderLink[] { | CODE |
| LOW | .github/scripts/contributor-trust/run.js | 61 | function buildScoreBreakdownSummary(score) { | CODE |
| LOW | .github/scripts/contributor-trust/github-api.js | 179 | export async function getCollaboratorPermission(token, owner, repo, username) { | CODE |
| LOW | .github/scripts/contributor-trust/github-api.js | 192 | function getPageNumberFromLinkHeader(linkHeader, relation) { | CODE |
| LOW | .github/scripts/contributor-trust/github-api.js | 264 | function countReviewsOnOthersPullRequests(nodes, authorLogin) { | CODE |
| LOW | .github/scripts/contributor-trust/github-api.js | 285 | function normalizeReviewSearchNodes(searchResult) { | CODE |
| LOW | .github/scripts/contributor-trust/github-api.js | 289 | export async function countReviewsOnOthersPullRequestsInRepo( | CODE |
| LOW | .github/scripts/contributor-trust/github-api.js | 359 | export function createPullRequestStateList({ closedPrs, mergedPrs, openPrs }) { | CODE |
| LOW | .github/scripts/contributor-trust/github-api.js | 397 | export function selectOwnedNonForkRepositories(nodes, ownerLogin) { | CODE |
| LOW | .github/scripts/contributor-trust/plan-actions.js | 22 | export function isExcludedChangedLineFile(filePath) { | CODE |
| LOW | .github/scripts/contributor-trust/plan-actions.js | 27 | function getFallbackChangedLineDetails(pullRequest) { | CODE |
| LOW | .github/scripts/contributor-trust/plan-actions.js | 43 | function getPullRequestChangedLineDetails(pullRequest, pullRequestFiles) { | CODE |
| LOW | .github/scripts/contributor-trust/plan-actions.js | 79 | function formatChangedLineThresholdReason(score, changedLineDetails) { | CODE |
| LOW | src/types/config/tts.ts | 3158 | function groupTTSVoiceItemsByLanguage(items: TTSVoiceItem[]): TTSVoiceGroup[] { | CODE |
| LOW | src/types/config/tts.ts | 3175 | function isTemporarilyDisabledEdgeTTSVoiceItem(item: TTSVoiceItem): boolean { | CODE |
| LOW | src/types/config/tts.ts | 3210 | export function getDefaultTTSVoiceForLanguage( | CODE |
| LOW | src/types/config/tts.ts | 3217 | export function createDefaultTTSLanguageVoices( | CODE |
| LOW | src/types/config/translate.ts | 73 | function normalizeLegacyReservedPromptIds(value: unknown, builtInPromptIds: readonly string[]) { | CODE |
| LOW | src/types/config/translate.ts | 128 | export function createCustomPromptsConfigSchema(builtInPromptIds: readonly string[]) { | CODE |
| LOW | src/types/config/provider/provider-specific-settings.ts | 100 | export function getProviderSpecificSettingFields( | CODE |
| LOW | src/types/config/provider/constants.ts | 117 | export function isOpenAICompatibleLLMProvider( | CODE |
| LOW | src/types/config/provider/constants.ts | 127 | export function isOpenResponsesLLMProvider( | CODE |
| LOW | src/types/config/provider/constants.ts | 140 | export function isProtocolCompatibleLLMProvider( | CODE |
| LOW | src/types/config/provider/constants.ts | 151 | export function isCustomModelOnlyProvider( | CODE |
| LOW | src/types/config/provider/constants.ts | 295 | export function supportsTopLevelReasoning( | CODE |
| LOW⚡ | src/types/config/provider/index.ts | 32 | export function isTranslateProviderConfig( | CODE |
| LOW⚡ | src/types/config/provider/index.ts | 42 | export function isTopLevelReasoningProviderConfig( | CODE |
| LOW⚡ | src/types/config/provider/index.ts | 48 | export function isOpenAICompatibleLLMProviderConfig( | CODE |
| LOW⚡ | src/types/config/provider/index.ts | 54 | export function isOpenResponsesLLMProviderConfig( | CODE |
| LOW⚡ | src/types/config/provider/index.ts | 60 | export function isProtocolCompatibleLLMProviderConfig( | CODE |
| LOW⚡ | src/types/config/provider/index.ts | 66 | export function isDedicatedLLMProviderConfig( | CODE |
| LOW | src/types/config/provider/index.ts | 84 | export function isPureTranslateProviderConfig(config: ProviderConfig): boolean { | CODE |
| LOW | …config/__tests__/config-note-suggestion-action.test.ts | 5 | function createConfigWithCustomSaveAction(enabled: boolean) { | CODE |
| LOW | src/utils/store-review.ts | 15 | export async function isStoreReviewPromptDismissed(): Promise<boolean> { | CODE |
| LOW | src/utils/store-review.ts | 24 | export function shouldShowStoreReviewPrompt(activeDayCount: number, dismissed: boolean): boolean { | CODE |
| LOW | src/utils/react-root.ts | 19 | export function renderPersistentReactRoot(container: HTMLElement, app: ReactNode): Root { | CODE |
| LOW | src/utils/react-root.ts | 34 | export function unmountPersistentReactRoot(container: HTMLElement): void { | CODE |
| LOW | src/utils/iframe-selection-trigger.ts | 19 | export function registerIframeSelectionTrigger({ | CODE |
| LOW | src/utils/crypto-polyfill.ts | 6 | function getCryptoWithRandomValues(): Crypto { | CODE |
| LOW | src/utils/selection-toolbar-items.ts | 63 | export function reorderSelectionToolbarItems( | CODE |
| LOW | src/utils/selection-toolbar-items.ts | 84 | export function setSelectionToolbarCustomActions( | CODE |
| LOW | src/utils/selection-toolbar-items.ts | 98 | export function setSelectionToolbarItemPinned( | CODE |
| LOW | src/utils/blog.ts | 103 | export function getBlogLocaleFromUILanguage(uiLanguage: UiLanguage): BlogLocale { | CODE |
| LOW | src/utils/navigation.ts | 34 | function buildProviderConfigRouteFrom( | CODE |
| LOW | src/utils/navigation.ts | 60 | export function buildProviderTypeConfigRoute( | CODE |
| LOW⚡ | src/utils/navigation.ts | 120 | export function buildCustomActionOptionsRoute( | CODE |
| LOW⚡ | src/utils/navigation.ts | 124 | export function buildCustomActionOptionsRoute( | CODE |
| LOW⚡ | src/utils/navigation.ts | 128 | export function buildCustomActionOptionsRoute( | CODE |
| LOW | src/utils/navigation.ts | 142 | export function buildAddCustomActionOptionsRoute(): `/${string}` { | CODE |
| LOW | src/utils/navigation.ts | 146 | function parseCustomActionEditorTab(value: string | null): CustomActionEditorTab | null { | CODE |
| LOW | src/utils/navigation.ts | 163 | export function consumeCustomActionDeepLink(search: string): CustomActionDeepLink | null { | CODE |
| LOW | src/utils/analytics.ts | 38 | export function buildFeatureUsedEventProperties( | CODE |
| LOW⚡ | src/utils/site-control.ts | 30 | export function resolveEffectiveSiteControlUrl( | CODE |
| LOW⚡ | src/utils/site-control.ts | 37 | export function getEffectiveSiteControlUrl(url: string): string { | CODE |
| LOW⚡ | src/utils/site-control.ts | 45 | export function clearEffectiveSiteControlUrl(): void { | CODE |
| LOW | src/utils/custom-actions.ts | 53 | export function getBuiltInDictionaryAction( | CODE |
| LOW | src/utils/custom-actions.ts | 65 | export function getSelectionToolbarActions( | CODE |
| LOW | src/utils/custom-actions.ts | 71 | export function findSelectionToolbarAction( | CODE |
| LOW | src/utils/custom-actions.ts | 95 | export function resolveNoteSuggestionAction( | CODE |
| LOW | src/utils/custom-actions.ts | 132 | export function replaceSelectionToolbarAction( | CODE |
| 664 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 57 | // ── Group context ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 79 | // ── Per-card context ────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 102 | // ── Link ────────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 130 | // ── FluidCardGroup ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 285 | // ── FluidCard ───────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 522 | // ── FluidCardHeader ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 546 | // ── FluidCardTitle ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 583 | // ── FluidCardDescription ────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 595 | // ── FluidCardAction ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 614 | // ── FluidCardContent ────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 628 | // ── FluidCardFooter ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 657 | // ── FluidCardMedia ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 726 | // ── FluidCardImage ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 764 | // ── FluidCardEyebrow ────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 780 | // ── FluidCardFeature ────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/components/ui/base-ui/fluid-card.tsx | 805 | // ── FluidCardButton ─────────────────────────────────────────────────────────── | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | wxt.config.ts | 181 | // Lets the runtime i18next facade (src/utils/i18n) `import` the `src/locales/*.yml` | COMMENT |
| LOW | vitest.setup.ts | 161 | // than 2048 characters (`Selector exceeds maximum allowed length of 2048`); the 8.x | COMMENT |
| LOW | .github/workflows/release.yml | 141 | # - name: Submit to Chrome & Edge Stores | COMMENT |
| LOW | src/types/config/translate.ts | 181 | // The combination `hotkey: "custom"` listens for. `.default()` for the same | COMMENT |
| LOW | src/utils/message.ts | 121 | // Glossary terms the SENDER found in `text`. Resolved where the page URL | COMMENT |
| LOW | src/utils/message.ts | 181 | providerRef: PromptableProviderRef | COMMENT |
| LOW | src/utils/layout-host/sample.ts | 21 | // Sample data for the layout preview on the options page: the selection, the | COMMENT |
| LOW | src/utils/host/translate/translate-text.ts | 381 | // Add extra hash tags for cache differentiation | COMMENT |
| LOW | src/utils/host/translate/core/translation-state.ts | 41 | walkId: string | COMMENT |
| LOW | …tils/host/translate/__tests__/google-roundtrip.test.ts | 1 | import { decodeHTML, escapeText } from "entities" | COMMENT |
| LOW | src/utils/host/translate/api/google.ts | 41 | COMMENT | |
| LOW | src/utils/host/translate/api/google.ts | 81 | "g", | COMMENT |
| LOW | src/utils/host/translate/api/google.ts | 141 | export function reassemblePreservedLine(line: PreservedLine, translation: string): string { | COMMENT |
| LOW | src/utils/host/translate/dom/translation-cleanup.ts | 241 | function finalizeTranslationOnlyAnchorIfEmpty(state: TranslationOnlyAnchorState): void { | COMMENT |
| LOW | src/utils/constants/translation-hub.ts | 1 | // The Translation Hub lives at its own extension page. Every entry point | COMMENT |
| LOW | src/utils/site-rules/__tests__/built-in.test.ts | 381 | // block translated as one unit at the parent's 16px instead of the headline's | COMMENT |
| LOW | src/components/test-orpc.tsx | 1 | // import { useState } from 'react' | COMMENT |
| LOW | src/components/test-orpc.tsx | 21 | COMMENT | |
| LOW | src/components/test-orpc.tsx | 41 | // </form> | COMMENT |
| LOW | src/components/badges/plan-badge.tsx | 81 | return ( | COMMENT |
| LOW | …ptions/pages/advanced/glossary/editor/details-item.tsx | 41 | orientation="vertical" | COMMENT |
| LOW | …t-runtime/host/translation-control/page-translation.ts | 501 | } | COMMENT |
| LOW | …t-runtime/host/translation-control/page-translation.ts | 961 | // guard exists to prevent (gating dies, and the translated-region | COMMENT |
| LOW | …t-runtime/host/translation-control/page-translation.ts | 1481 | COMMENT | |
| LOW | src/entrypoints/content-runtime/selection/main.tsx | 121 | // one; a failure here costs nothing but the warm-up. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | .claude/commands/update-ai-sdk-models.md | 7 | ## Step 1: Scrape | COMMENT |
| LOW⚡ | .claude/commands/update-ai-sdk-models.md | 17 | ## Step 2: Read current state | COMMENT |
| LOW⚡ | .claude/commands/update-ai-sdk-models.md | 21 | ## Step 3: Match providers | COMMENT |
| LOW | .claude/commands/update-ai-sdk-models.md | 41 | ## Step 4: Compare & update each matched provider | COMMENT |
| LOW | .claude/commands/update-ai-sdk-models.md | 57 | ## Step 5: Check `LLM_MODEL_OPTIONS` | COMMENT |
| LOW | .claude/commands/update-ai-sdk-models.md | 71 | ## Step 6: Check `DEFAULT_LLM_PROVIDER_MODELS` | COMMENT |
| LOW | .claude/commands/update-ai-sdk-models.md | 83 | ## Step 7: Migration script (only if models were removed) | COMMENT |
| LOW | .claude/commands/update-ai-sdk-models.md | 145 | ## Step 8: Verify | COMMENT |
| LOW | .claude/commands/update-ai-sdk-models.md | 155 | ## Step 9: Summary | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | .github/workflows/changeset-major-warning.yml | 49 | # Extract front matter and check for major (more robust parsing) | COMMENT |
| MEDIUM | …tils/host/translate/dom/__tests__/inline-atoms.test.ts | 24 | '<p id="S4.SS2.SSS0.Px3.p1.1" class="ltx_p">Our formulation not only enables conditioning on a single variable <math i | CODE |
| MEDIUM | src/utils/config/migration-scripts/v050-to-v051.ts | 12 | // Copy values from translate config for seamless migration | COMMENT |
| MEDIUM | src/entrypoints/background/background-stream.ts | 341 | // robust across isolated extension/test realms where Symbol identities may | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …tils/host/translate/dom/translation-html-attributes.ts | 18 | "placeholder", | CODE |
| LOW | …onfig/__tests__/migration-scripts/v070-to-v071.test.ts | 186 | deeplxProvider({ id: "placeholder", baseURL: "https://{{apiKey}}.host.example" }), | CODE |
| LOW | …ider-config-form/__tests__/provider-url-field.test.tsx | 28 | "placeholder", | CODE |
| LOW | …-config-form/__tests__/provider-options-field.test.tsx | 163 | "placeholder", | CODE |
| LOW | …-config-form/__tests__/provider-options-field.test.tsx | 184 | "placeholder", | CODE |
| LOW | …-config-form/__tests__/provider-options-field.test.tsx | 205 | "placeholder", | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | .github/PULL_REQUEST_TEMPLATE.md | 50 | - [ ] If my code was generated by AI, I have proofread and improved it as necessary. | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/utils/prompts/summary.ts | 55 | * "as an AI language model…"`, | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .agents/skills/record-extension-demo/scripts/qa_demo.py | 2 | CODE | |
| LOW | …nts/skills/record-extension-demo/scripts/build_demo.py | 2 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/entrypoints/netflix-interceptor.content/player.ts | 90 | async function handleRequest( | CODE |