Conversation
Add DiffOutlined menu entry and active-key detection for /admin/similarity. Create similarity page, SimilarityDashboardClient tabs shell, and five placeholder tab components (PairsTable, SuspectsTable, IntegrityReviewTable, IntegrityWhitelistTable, PairWhitelistTable).
Typed service class wrapping all admin + public similarity endpoints: pairs list/detail, suspects list, pair whitelist, integrity reviews, integrity whitelist, and evidence pair detail.
Merge admin.navigation.similarity, admin.similarity (tabs, columns, status labels, action labels, drawer/modal strings), similarity.evidence (disclaimer), and errors.integrity_rejected into the zh-CN locale file.
…orts - integrity_score === 0 now renders as 0.00 instead of - - merge split antd imports in SuspectsTable
Implements Task 17 — pair detail page under admin/similarity/pairs/[pairId] with metadata descriptions, whitelist action button, and a CodeDiffViewer component that uses @monaco-editor/react DiffEditor with per-segment line decorations via createDecorationsCollection.
Implements Task 18 — public-facing evidence page at similarity/pair/[id] that wraps PairDetailClient with a warning Alert disclaimer banner, sourced from getEvidencePair API endpoint.
…yte→line conversion - replace Tailwind class names (which Tailwind purges from non-JSX strings) with similarity-match-highlight defined in globals.css - convert UTF-8 byte offsets via TextEncoder to handle non-ASCII source code correctly, instead of iterating JS UTF-16 code units
- en-US: proper English translations - zh-TW: Traditional Chinese translations - ja-JP / de-DE / ru-RU: English stopgap (pending Crowdin)
Adds the admin UI for §8.5 bootstrap operations:
- BackfillControl component in a new "回填与重扫" dashboard tab,
polling /admin/similarity/backfill/status every 5s while running.
Shows total/cursor/progress bar/started_at/finished_at in a
Descriptions panel with start + restart-from-zero (§8.5 step 9) +
refresh buttons gated by running flag.
- Manual per-script rescan card with script ID input.
- Stop-fingerprint refresh card with warning copy ("通常不需要手动
触发"), invoking POST /admin/similarity/stop-fp/refresh — used at
§8.5 step 8 after the first full backfill completes.
- similarityService adds triggerBackfill / getBackfillStatus /
manualScan / refreshStopFp methods and BackfillStatus type.
- New admin.similarity.tab_backfill + admin.similarity.backfill.*
translation keys in zh-CN.
Backend now marks each ScriptBrief with is_deleted and accepts an exclude_deleted query param. Render deleted scripts with a strikethrough link and red tag, and add a Switch above the table that lets admins hide any pair whose either side has been soft-deleted.
There was a problem hiding this comment.
Pull request overview
This PR adds the frontend surface for the “similarity detection / integrity review” system, including an admin dashboard (pairs/suspects/reviews/whitelists/backfill) and a public “evidence” view for a similarity pair.
Changes:
- Introduces
similarityServiceAPI client + related response types for similarity/pair/integrity/backfill endpoints. - Adds new admin pages/components for viewing pairs/suspects, reviewing integrity alerts, managing whitelists, and triggering backfill/manual scans.
- Adds a public evidence page, integrity-rejection UX in the script editor, and supporting i18n + styling.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
src/lib/api/services/similarity.ts |
Adds similarity/integrity/backfill API service and typed models. |
src/lib/api/errorCodes.ts |
Adds an error-code constant used for integrity rejection handling. |
src/components/ScriptEditor/index.tsx |
Shows a dedicated integrity-rejection alert when submit fails with a specific error code. |
src/components/IntegrityErrorAlert/IntegrityErrorAlert.tsx |
New component to display integrity rejection details + help text. |
src/app/globals.css |
Adds global highlight style for similarity match segments (Monaco decorations). |
src/app/[locale]/(main)/similarity/pair/[id]/page.tsx |
Adds public evidence route for a similarity pair. |
src/app/[locale]/(main)/similarity/pair/[id]/components/EvidencePageClient.tsx |
Renders disclaimer + reuses pair detail UI for evidence mode. |
src/app/[locale]/(main)/admin/similarity/page.tsx |
Adds admin similarity dashboard route. |
src/app/[locale]/(main)/admin/similarity/components/SimilarityDashboardClient.tsx |
Tabs container for all similarity admin tools. |
src/app/[locale]/(main)/admin/similarity/components/PairsTable.tsx |
Admin table for similarity pairs + filter for deleted scripts. |
src/app/[locale]/(main)/admin/similarity/components/SuspectsTable.tsx |
Admin table for suspect scripts + expandable “top sources”. |
src/app/[locale]/(main)/admin/similarity/components/IntegrityReviewTable.tsx |
Admin table + detail drawer + resolve flow for integrity reviews. |
src/app/[locale]/(main)/admin/similarity/components/ResolveReviewModal.tsx |
Modal to resolve an integrity review. |
src/app/[locale]/(main)/admin/similarity/components/IntegrityWhitelistTable.tsx |
Admin CRUD UI for integrity exemptions/whitelist. |
src/app/[locale]/(main)/admin/similarity/components/PairWhitelistTable.tsx |
Admin table to remove pair whitelist entries. |
src/app/[locale]/(main)/admin/similarity/components/BackfillControl.tsx |
Admin controls for backfill, manual scan, and stop-fp refresh. |
src/app/[locale]/(main)/admin/similarity/pairs/[pairId]/page.tsx |
Adds admin route for pair detail page. |
src/app/[locale]/(main)/admin/similarity/pairs/[pairId]/components/PairDetailClient.tsx |
Fetches pair detail and renders metadata + diff + whitelist action. |
src/app/[locale]/(main)/admin/similarity/pairs/[pairId]/components/CodeDiffViewer.tsx |
Monaco diff + match segment highlighting. |
src/app/[locale]/(main)/admin/components/AdminLayout.tsx |
Adds “Similarity” entry to the admin navigation menu. |
public/locales/zh-CN/translations.json |
Adds zh-CN strings for similarity admin UI + evidence + integrity error. |
public/locales/zh-TW/translations.json |
Adds zh-TW strings for similarity admin UI + evidence + integrity error (partial). |
public/locales/en-US/translations.json |
Adds en-US strings for similarity admin UI + evidence + integrity error (partial). |
public/locales/ru-RU/translations.json |
Adds ru-RU strings for similarity admin UI + evidence + integrity error (partial). |
public/locales/ja-JP/translations.json |
Adds ja-JP strings for similarity admin UI + evidence + integrity error (partial). |
public/locales/de-DE/translations.json |
Adds de-DE strings for similarity admin UI + evidence + integrity error (partial). |
.gitignore |
Ignores .omc. |
| title: t('confirm_remove_whitelist'), | ||
| onOk: async () => { | ||
| try { | ||
| await similarityService.removePairWhitelistByID(row.id); |
There was a problem hiding this comment.
Call site uses removePairWhitelistByID(); if the service method is renamed to consistent casing (e.g., removePairWhitelistById), update this usage accordingly to avoid churn and keep the API surface consistent.
| await similarityService.removePairWhitelistByID(row.id); | |
| await similarityService.removePairWhitelistById(row.id); |
| "similarity": { | ||
| "tab_pairs": "Pairs", | ||
| "tab_suspects": "Suspects", | ||
| "tab_integrity_reviews": "Integrity Reviews", | ||
| "tab_pair_whitelist": "Pair Whitelist", |
There was a problem hiding this comment.
New similarity UI references additional translation keys (e.g. tab_backfill, backfill.*, script_deleted, filter_exclude_deleted) that are not present in this locale file. Add these keys here to avoid missing-message fallbacks at runtime.
| "similarity": { | ||
| "tab_pairs": "Pairs", | ||
| "tab_suspects": "Suspects", | ||
| "tab_integrity_reviews": "Integrity Reviews", | ||
| "tab_pair_whitelist": "Pair Whitelist", |
There was a problem hiding this comment.
New similarity UI references additional translation keys (e.g. tab_backfill, backfill.*, script_deleted, filter_exclude_deleted) that are not present in this locale file. Add these keys here to avoid missing-message fallbacks at runtime.
| "similarity": { | ||
| "tab_pairs": "Pairs", | ||
| "tab_suspects": "Suspects", | ||
| "tab_integrity_reviews": "Integrity Reviews", | ||
| "tab_pair_whitelist": "Pair Whitelist", |
There was a problem hiding this comment.
New similarity UI references additional translation keys (e.g. tab_backfill, backfill.*, script_deleted, filter_exclude_deleted) that are not present in this locale file. Add these keys here to avoid missing-message fallbacks at runtime.
| const SIGNAL_DESCRIPTIONS: Record<string, string> = { | ||
| avg_line_length: '平均行长度过长(代码可能被压缩为少量长行)', | ||
| max_line_length: '最大行长度过长(存在超长代码行)', | ||
| whitespace_ratio: '空白字符比例过低(代码缺少正常的空格和缩进)', | ||
| comment_ratio: '注释比例过低(代码几乎没有注释)', |
There was a problem hiding this comment.
SIGNAL_DESCRIPTIONS is hard-coded in zh-CN inside the component, but the rest of this admin UI is localized via next-intl. Consider moving these descriptions into the locale JSON (or providing per-locale strings) so non-Chinese locales don’t see mixed-language UI.
| try { | ||
| await similarityService.addPairWhitelist(pairID, 'admin whitelist'); |
There was a problem hiding this comment.
Whitelisting currently always posts the hard-coded reason "admin whitelist". This makes the whitelist audit trail low-signal and non-localized. Consider prompting the admin for a reason (or at least using a translated default) and pass that value to the API.
| try { | |
| await similarityService.addPairWhitelist(pairID, 'admin whitelist'); | |
| const input = window.prompt('请输入加入白名单的原因'); | |
| if (input === null) return; | |
| const reason = input.trim(); | |
| if (!reason) { | |
| message.error('请输入加入白名单的原因'); | |
| return; | |
| } | |
| try { | |
| await similarityService.addPairWhitelist(pairID, reason); |
| "similarity": { | ||
| "tab_pairs": "Pairs", | ||
| "tab_suspects": "Suspects", | ||
| "tab_integrity_reviews": "Integrity Reviews", | ||
| "tab_pair_whitelist": "Pair Whitelist", |
There was a problem hiding this comment.
New similarity UI references additional translation keys (e.g. tab_backfill, backfill.*, script_deleted, filter_exclude_deleted) that are not present in this locale file. Add these keys here to avoid missing-message fallbacks at runtime.
| "similarity": { | ||
| "tab_pairs": "相似對", | ||
| "tab_suspects": "嫌疑腳本", | ||
| "tab_integrity_reviews": "完整性警告", | ||
| "tab_pair_whitelist": "相似對白名單", |
There was a problem hiding this comment.
New similarity UI references additional translation keys (e.g. tab_backfill, backfill.*, script_deleted, filter_exclude_deleted) that are not present in this locale file. Add these keys here to avoid missing-message fallbacks at runtime.
| removePairWhitelistByID(whitelistID: number) { | ||
| return apiClient.delete<void>(`${this.adminBase}/whitelist/${whitelistID}`); |
There was a problem hiding this comment.
Method name uses "ByID"/"whitelistID" which is inconsistent with the rest of the codebase's Id/ID casing and makes call sites noisier. Consider renaming to removePairWhitelistById(whitelistId) (and updating callers) for consistency.
| removePairWhitelistByID(whitelistID: number) { | |
| return apiClient.delete<void>(`${this.adminBase}/whitelist/${whitelistID}`); | |
| removePairWhitelistById(whitelistId: number) { | |
| return apiClient.delete<void>(`${this.adminBase}/whitelist/${whitelistId}`); |
Code reviewReviewed the diff and also evaluated Copilot's 9 review comments. Found 2 issues that align with Copilot's observations:
The component already uses
scriptlist-frontend/public/locales/zh-CN/translations.json Lines 293 to 294 in d0a6b7c scriptlist-frontend/public/locales/zh-CN/translations.json Lines 328 to 360 in d0a6b7c These keys are used by Copilot comment assessment:
🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
…ngs to i18n Replace the hardcoded SIGNAL_DESCRIPTIONS constant in IntegrityReviewTable with next-intl t() calls under admin.similarity.signal_desc.*, adding translations for all 6 locales (zh-CN, en-US, de-DE, ja-JP, ru-RU, zh-TW).
Backfills script_deleted, filter_exclude_deleted, tab_backfill, and the full backfill sub-object (28 keys) into en-US, de-DE, ja-JP, ru-RU, and zh-TW — inserted before signal_desc to maintain consistent key ordering.
…nents Move these components from admin route directory to src/components/similarity/ so both the admin detail page and public evidence page import from the same shared location, eliminating the cross-layer dependency.
No description provided.