Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
1bdff54
📄 修复 develop-testing.md 中的语法错误
cyfung1031 Jul 17, 2026
16a3fa5
📄 突出 verification.md 中"先建记录再跑浏览器"的顺序要求
cyfung1031 Jul 17, 2026
65423a3
📄 消除 verification.md 中 "Scratch copying..." 的主语歧义
cyfung1031 Jul 17, 2026
321092e
📄 补全 architecture-gm-api.md 新增 GM API 步骤 3/4 的具体文件路径
cyfung1031 Jul 17, 2026
7780292
📄 补充 verification.md 何时可跳过本指南的边界说明
cyfung1031 Jul 17, 2026
9e5b424
📄 补上 architecture-gm-api.md 步骤 1 中 sendMessage/connect 的选择依据
cyfung1031 Jul 17, 2026
57e7979
📄 补上 verification.md 中驱动双主题验证的具体写法
cyfung1031 Jul 17, 2026
72c93ee
📄 translation.md 补充 i18n-usage.test.ts 的存在与作用范围
cyfung1031 Jul 17, 2026
fe08936
📄 verification-debugging.md 补充 Firefox 无独立 Offscreen 上下文的提示
cyfung1031 Jul 17, 2026
7e84e92
📄 pull-request.md 补充 PR 标题规则的交叉链接
cyfung1031 Jul 17, 2026
3a6b591
📄 移除 6 个 references 文档中重复的标题(H1 紧跟同义 H2)
cyfung1031 Jul 17, 2026
c4c19ae
📄 消除 verification-report-template.md 中两处 "Evidence Index" 标题重名
cyfung1031 Jul 17, 2026
fa34afe
📄 DOC-MAINTENANCE.md 新增锚点完整性校验脚本
cyfung1031 Jul 17, 2026
6eea05e
📄 把指名具体章节的散文补成 file.md#anchor 精确链接
cyfung1031 Jul 17, 2026
cf5d6d6
📄 补上三处 "UI section" 链接遗漏的 #ui 锚点
cyfung1031 Jul 17, 2026
8659100
📄 修复 .github/copilot-instructions.md 中两处架构性错误
cyfung1031 Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/copilot-instructions.md

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好像都没什么人用coplit了

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我用呀。 github merge 有冲突时都用 免费的copilot 处理

Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ pnpm run coverage # Generate coverage reports
### Browser Extension Specifics
- **Manifest V3** with service worker background
- **User Scripts API** for script injection (Chrome/Edge)
- **Offscreen API** for DOM access in background contexts
- **Offscreen API** for DOM access in background contexts — **Firefox MV3 has no Offscreen API**;
`EventPageOffscreenManager` runs the same DOM-dependent logic inside the background event page instead
- **Declarative Net Request** for script installation interception

## Critical Integration Points
Expand All @@ -136,7 +137,8 @@ pnpm run coverage # Generate coverage reports
- Use `pnpm run dev:noMap` for incognito window development
- Background script changes require extension reload
- Message passing debugging available in service worker console
- Sandbox script execution isolated from page context - use `unsafeWindow` for page access
- Sandbox (background/scheduled scripts) has no page at all — not "isolated from" a page, it never has one.
`unsafeWindow` belongs to the **Inject** context (page scripts), not Sandbox

## File Structure Patterns
- Tests co-located with source files (`.test.ts` suffix)
Expand Down
23 changes: 23 additions & 0 deletions docs/DOC-MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,29 @@ for doc in AGENTS.md docs/README.md docs/DOC-MAINTENANCE.md docs/develop.md docs
done
```

Anchor integrity — the check above only confirms the **target file** exists; a `file.md#anchor` link's fragment
can still rot silently if the target heading is renamed. This second pass re-derives each anchor from the
target's actual headings using a best-effort GitHub-slug approximation (lowercase; drop everything but letters,
digits, spaces, hyphens, underscores; each space becomes its own hyphen — consecutive spaces do **not** collapse,
which is why `## Commit & Pull Request Guidelines` slugs to `commit--pull-request-guidelines` with a double
hyphen). **ASCII-only:** it cannot slugify CJK/non-ASCII headings correctly, so anchors into those headings
(e.g. `translation.md#各语言术语规范--per-locale-terminology`) need manual verification instead.

```bash
slugify() { printf '%s\n' "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[`]//g; s/[^a-z0-9 _-]//g; s/[[:space:]]/-/g'; }
for doc in AGENTS.md docs/README.md docs/DOC-MAINTENANCE.md docs/develop.md docs/references/develop-testing.md docs/design.md docs/references/design-tokens.md docs/references/design-components.md docs/references/design-patterns.md docs/architecture.md docs/references/architecture-services.md docs/references/architecture-data.md docs/references/architecture-gm-api.md docs/references/architecture-execution.md docs/references/architecture-build.md docs/verification.md docs/references/verification-debugging.md docs/references/verification-report-template.md docs/cloud-sync.md docs/translation.md; do
sed '/^```/,/^```/d' "$doc" | sed -E 's/`[^`]*`//g' | grep -oE '\]\([^)]+#[^)]+\)' | sed -E 's/^\]\(|\)$//g' | while read -r link; do
tf="${link%%#*}"; anchor="${link#*#}"
target="$doc"; [ -n "$tf" ] && target="$(dirname "$doc")/$tf"
[ -e "$target" ] || { echo "BROKEN-FILE $doc → $link"; continue; }
found=0
while IFS= read -r h; do [ "$(slugify "$h")" = "$anchor" ] && found=1 && break; done \
< <(grep -E "^#{1,6} " "$target" | sed -E 's/^#{1,6} +//')
[ "$found" -eq 1 ] && echo "ok $doc → $link" || echo "BROKEN-ANCHOR $doc → $link (target: $target)"
done
done
```

## When you find a discrepancy

Fix the **doc** to match the code — the code on this branch is the source of truth. The exception: if the code
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ Map a change onto the existing extension points instead of inventing new structu
- **A new GM API.** Decorate the method with `@GMContext.API` on the content side, add a privileged/offscreen
handler if needed, register the `@grant` (see [GM API system](./references/architecture-gm-api.md#adding-a-gm-api-sketch)).

Follow the engineering principles in [`AGENTS.md`](../AGENTS.md): fix root causes (no `as any` / swallowed
Follow the engineering principles in [`AGENTS.md`](../AGENTS.md#engineering-principles): fix root causes (no `as any` / swallowed
errors), prefer direct replacement over adapter sandwiches, and keep scope tight — three similar lines beat a
premature abstraction.

Expand Down
2 changes: 1 addition & 1 deletion docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

| Owned here | Owned elsewhere |
| --- | --- |
| Color-token values, semantics, usage → [`tokens.md`](./references/design-tokens.md) | The hard rules that mandate them (no hard-coded colors, hover via pseudo-classes, `cn()` / CVA / `lucide`) → [`DEVELOP.md` UI section](./develop.md) |
| Color-token values, semantics, usage → [`tokens.md`](./references/design-tokens.md) | The hard rules that mandate them (no hard-coded colors, hover via pseudo-classes, `cn()` / CVA / `lucide`) → [`DEVELOP.md` UI section](./develop.md#ui) |
| Theming mechanism, `dark:` usage | Commands, structure, coding style, testing, i18n, commit/PR → [`DEVELOP.md`](./develop.md) |
| Component palette, variants, selection guidance → [`components.md`](./references/design-components.md) | Process model, message passing, service layers, internals → [`ARCHITECTURE.md`](./architecture.md) |
| Layout shell, responsive patterns, **layering (z-index)**, motion, state patterns, **accessibility** → [`patterns.md`](./references/design-patterns.md); **elevation (shadows)** → [`tokens.md`](./references/design-tokens.md#elevation-shadows); page recipe (this doc) | — |
Expand Down
2 changes: 1 addition & 1 deletion docs/pull-request.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Pull Request Description Guide

This guide defines the detailed PR description format for agents and contributors. The human-facing template at [`../.github/pull_request_template.md`](../.github/pull_request_template.md) intentionally remains lightweight; use it as the starting point and expand its `Description / 描述` section when the change needs more context.
This guide defines the detailed PR description format for agents and contributors. The human-facing template at [`../.github/pull_request_template.md`](../.github/pull_request_template.md) intentionally remains lightweight; use it as the starting point and expand its `Description / 描述` section when the change needs more context. This guide covers the description body only — for the PR **title** (gitmoji + Chinese preferred), see [`develop.md`](./develop.md#commit--pull-request-guidelines).

## Recommended structure

Expand Down
2 changes: 0 additions & 2 deletions docs/references/architecture-build.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Build pipeline & manifest

## Build Pipeline & Manifest

### Rspack

[`rspack.config.ts`](../../rspack.config.ts) emits one bundle per context/page. Entry points:
Expand Down
2 changes: 0 additions & 2 deletions docs/references/architecture-data.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Data layer (Repo<T> + DAOs)

## The Data Layer (`Repo<T>` + DAOs)

Persistence is a thin generic over `chrome.storage.local` with an optional in-memory cache, in
[`src/app/repo/repo.ts`](../../src/app/repo/repo.ts).

Expand Down
2 changes: 0 additions & 2 deletions docs/references/architecture-execution.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Script execution

## Script Execution

There are three execution paths; all share one **compilation** step.

### Compilation — the `with(){}` sandbox wrapper
Expand Down
12 changes: 6 additions & 6 deletions docs/references/architecture-gm-api.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# GM API system

## The GM API System

The `GM_*` / `GM.*` functions a userscript calls are not one function — each is a small client that forwards
across contexts to a privileged handler, then streams the result back. The implementation is split:

Expand Down Expand Up @@ -57,9 +55,11 @@ work.
### Adding a GM API (sketch)

1. Add the method to the content `GMApi` with `@GMContext.API({ alias: "GM.foo" })`; for sync APIs return
directly, for async ones forward via `sendMessage`/`connect`.
directly, for async ones forward via `sendMessage` (single request/reply) — or `connect` only when the reply
is streamed in chunks (progress events, large payloads; see the `GM_xmlhttpRequest` walkthrough above).
2. If it needs privilege (network, cookies, tabs), add the handler on the SW `GMApi` with
`@PermissionVerify.API(...)`.
3. If it needs DOM, route through the offscreen GM API instead.
4. Register the `@grant` so the linter and the context builder recognize it (see
[`packages/eslint`](../../packages/eslint)).
3. If it needs DOM, route through the offscreen GM API instead
([`src/app/service/offscreen/gm_api.ts`](../../src/app/service/offscreen/gm_api.ts)).
4. Register the `@grant` so the linter and the context builder recognize it
([`packages/eslint/linter-config.ts`](../../packages/eslint/linter-config.ts)).
2 changes: 0 additions & 2 deletions docs/references/architecture-services.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Service layer

## The Service Layer

Services live under `src/app/service/<context>/` — **split by the context they run in** — and hold the domain
logic. They are deliberately "dumb plumbing on the outside, smart logic on the inside": construction is pure DI,
wiring happens once in a manager, and message handlers are registered in `init()`.
Expand Down
4 changes: 1 addition & 3 deletions docs/references/design-components.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Component palette

## Component palette & usage

The shadcn primitives live in [`src/pages/components/ui/`](../../src/pages/components/ui/) — `new-york` style, CSS variables enabled, no class prefix (`components.json`). Icons are always `lucide-react`; class merging is always `cn()` ([`src/pkg/utils/cn.ts`](../../src/pkg/utils/cn.ts)); variants are always CVA — these are the [`DEVELOP.md` UI section](../develop.md) hard rules, not repeated here. This section is "what exists and how to choose."
The shadcn primitives live in [`src/pages/components/ui/`](../../src/pages/components/ui/) — `new-york` style, CSS variables enabled, no class prefix (`components.json`). Icons are always `lucide-react`; class merging is always `cn()` ([`src/pkg/utils/cn.ts`](../../src/pkg/utils/cn.ts)); variants are always CVA — these are the [`DEVELOP.md` UI section](../develop.md#ui) hard rules, not repeated here. This section is "what exists and how to choose."

### Primitives & shared composites

Expand Down
2 changes: 1 addition & 1 deletion docs/references/design-tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
**Usage:**
- Background `bg-<token>`, text `text-<token>`, border `border-<token>`, focus ring `ring-ring`.
- Opacity modifiers compose directly: `bg-primary-background/90` (solid primary hover), `ring-destructive/20`, `bg-input/30`.
- **Never hard-code a color value** — see Constraint 1 and [`DEVELOP.md` UI section](../develop.md). For dark-only tweaks use the `dark:` variant.
- **Never hard-code a color value** — see Constraint 1 and [`DEVELOP.md` UI section](../develop.md#ui). For dark-only tweaks use the `dark:` variant.

### Base surfaces & text

Expand Down
8 changes: 4 additions & 4 deletions docs/references/develop-testing.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Testing

> The **TDD/BDD-first principle** (write failing tests before implementation; fix code not tests) lives in
> [`AGENTS.md`](../../AGENTS.md) → *Engineering Principles*. This section is the mechanics.
> [`AGENTS.md`](../../AGENTS.md#engineering-principles) → *Engineering Principles*. This section is the mechanics.

Vitest + happy-dom. Per-test budgets live in `vitest.config.ts` per project: non-UI projects (`fast`,
`isolated`) use 340ms; the `ui` project (`src/pages/**/*.test.{ts,tsx}` — React renders, including
`renderHook` tests in `.ts` files) uses 850ms because a render + interaction case genuinely costs 100–200ms
solo under coverage and worker parallelism multiplies that (fake-timer countdown cases have been observed at
~630ms under full local load). Don't pass `--test-timeout` on the CLI — it would override every project's
budget at once. Chrome APIs mocked via
`@Packages/chrome-extension-mock` (`tests/vitest.setup.ts`). `MockMessage` available for message-system tests.
budget at once. Chrome APIs are mocked via
`@Packages/chrome-extension-mock` (`tests/vitest.setup.ts`). `MockMessage` is available for message-system tests.
`happy-dom` is patched via `patches/` (see `pnpm-workspace.yaml` `patchedDependencies`) to build its
invalid-selector `DOMException` lazily — the upstream eager construction captures a deep stack on every
`matches()`/`querySelector()` call and cost ~15% of TSX suite time.
`matches()`/`querySelector()` call and costs ~15% of TSX suite time.

- Write failing tests **before** implementation; co-locate `*.test.ts`/`*.test.tsx` next to source (or place in `tests`).
- BDD-style Chinese `describe`/`it` titles. Use `describe.concurrent()` / `it.concurrent()` where independent.
Expand Down
7 changes: 6 additions & 1 deletion docs/references/verification-debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Five-context debug map

A feature can break in any of ScriptCat's five isolated contexts. Match the symptom to where its logs live (deep
model in [`ARCHITECTURE.md`](../architecture.md)):
model in [`ARCHITECTURE.md`](../architecture.md#the-five-contexts-process-model)):

| Symptom | Where to look |
| --- | --- |
Expand All @@ -12,6 +12,11 @@ model in [`ARCHITECTURE.md`](../architecture.md)):
| Background / scheduled script, DOM-needing GM APIs | **Offscreen** — `dist/ext/src/offscreen.html` context |
| Cron scheduling, `with`-sandboxed execution | **Sandbox** — `dist/ext/src/sandbox.html` context |

> **Firefox has no separate Offscreen context.** `chrome.offscreen.createDocument` doesn't exist there, so
> `offscreen.html` is never loaded — `EventPageOffscreenManager` runs the same logic inside the background event
> page instead (see [`ARCHITECTURE.md`](../architecture.md#chrome-vs-firefox-the-offscreen-split)). On Firefox,
> inspect the background/event page, not `offscreen.html`.

In a scratch script, capture the page console (`page.on("console", …)`), take screenshots
(`await page.screenshot({ path: "test-results/verify/<scenario>/screenshots/…png" })`), and write any manual
notes to `test-results/verify/<scenario>/`. Playwright's automatic failure artifacts also go under
Expand Down
8 changes: 6 additions & 2 deletions docs/references/verification-report-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ Before running the browser, create a short verification record in the scenario d
`test-results/verify/<scenario>/report.md`. Keep the reusable template headings in English, but write the actual
record content in the user's language. Update it as the run proceeds instead of filling it in only at the end.

This is the `Evidence Index` section alone, filled in, to show its shape — it is not the full report; the full
report shape (which reuses this same section) is below.

```md
## Evidence Index
## Evidence Index (filled-in example)

### Screenshots

Expand Down Expand Up @@ -81,7 +84,8 @@ Use this shape:

## Evidence Index

Embed screenshots inline, link videos / logs / resources, and annotate every item — see the shape above.
Embed screenshots inline, link videos / logs / resources, and annotate every item — see `Evidence Index
(filled-in example)` above.
```

Fill `Result` at the end — the honest verdict, per *Step 4 — Report honestly* in [`verification.md`](../verification.md).
Expand Down
1 change: 1 addition & 0 deletions docs/translation.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
- **关键字冲突**:同一页面中关键字相同但翻译不同时,使用 `page.key` 的方式区分。
- 为满足部分扩展市场要求,`chrome.i18n` 语言文件位于 `src/assets/_locales`。
- i18n 方案的实现细节见 [`src/locales/README.md`](../src/locales/README.md)。
- 自动检查:[`src/locales/i18n-usage.test.ts`](../src/locales/i18n-usage.test.ts)(`pnpm test`)静态扫描代码中的 `t()`/`i18n.t()` 调用,但只按 **zh-CN** 资源解析 key 是否存在;修改其它 locale 时该测试不会给出任何信号,仍需人工核对目标 locale 是否缺 key。

## 提取翻译提示词 / Extract-translation prompt

Expand Down
33 changes: 24 additions & 9 deletions docs/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@
> scripts and local-only evidence — kept out of Git and never deleted as part of a run; cleanup is the user's call.
>
> **What this is NOT.** It is *not* the test-suite reference. The mechanics of Vitest unit tests and the
> permanent Playwright E2E suite live in [`DEVELOP.md`](./develop.md) → *Testing*; the TDD-first principle and
> engineering rules live in [`../AGENTS.md`](../AGENTS.md). Read those for writing committed tests.
> permanent Playwright E2E suite live in [`DEVELOP.md`](./develop.md#testing) → *Testing*; the TDD-first principle
> and engineering rules live in [`../AGENTS.md`](../AGENTS.md#engineering-principles). Read those for writing
> committed tests.
>
> **When to skip this guide.** Docs-only, comment-only, and type-only changes need no browser run. When a change
> or bug is fully reproducible in pure service/unit logic — no cross-context wiring, no real Chrome API, no DOM —
> reproduce and verify it with a throwaway Vitest case instead of a scratch script; reach for this guide only when
> the behavior only manifests through the built extension.

## The one rule: verification ≠ growing the E2E suite

Expand All @@ -20,12 +26,13 @@ you only want to *check that a feature works*, do not pay that cost and do not l
- ✅ Write a **throwaway scratch script** under `e2e/scratch/` (git-ignored), run it, and keep any evidence local.

Promoting a scenario into the permanent suite is a *separate, deliberate* decision — only when it deserves
permanent regression coverage. That path is owned by [`DEVELOP.md`](./develop.md), not this guide.
permanent regression coverage. That path is owned by [`DEVELOP.md`](./develop.md#testing), not this guide.

**Reproducing a bug you intend to fix is *not* "casual verification."** A scratch reproduction is the *确定 bug
存在* step from [`../AGENTS.md`](../AGENTS.md) (确定 bug 存在 → 写测试 → 修复): it confirms the bug is real but does
**not** replace the test. Next, promote it into a *failing* committed test, then fix, then confirm it goes green —
that permanent test is owned by [`DEVELOP.md`](./develop.md) / [`../AGENTS.md`](../AGENTS.md).
存在* step from [`../AGENTS.md`](../AGENTS.md#engineering-principles) (确定 bug 存在 → 写测试 → 修复): it confirms the
bug is real but does **not** replace the test. Next, promote it into a *failing* committed test, then fix, then
confirm it goes green — that permanent test is owned by [`DEVELOP.md`](./develop.md#testing) /
[`../AGENTS.md`](../AGENTS.md#engineering-principles).

## Prerequisite gate (cheap signals first)

Expand Down Expand Up @@ -89,8 +96,9 @@ sanitize them before saving evidence.

Embed screenshots inline with `![alt](screenshots/…png)` plus a caption line, so `report.md` renders as a visual
record you can skim without opening files. Link videos, logs, and resources instead — markdown can't inline them.
Never list bare links: every item carries a short note explaining what it proves and how to read it. Before running
the browser, create the record following the Evidence Index shape in the
Never list bare links: every item carries a short note explaining what it proves and how to read it.

**Before running the browser**, create the record following the Evidence Index shape in the
[verification record template](./references/verification-report-template.md).

### Minimal template (drive a UI page)
Expand All @@ -114,6 +122,13 @@ test("verify: options page opens and renders the script list area", async ({ con
});
```

To verify a UI change in **both themes** ([design.md](./design.md#theming) requires it), set the theme before
navigating and capture one screenshot per theme:

```ts
await context.addInitScript(() => localStorage.setItem("lightMode", "dark")); // or "light"
```

If you need video evidence, enable it explicitly for the run. The shared fixture writes videos only when
`E2E_RECORD_VIDEO_DIR` is set:

Expand All @@ -126,7 +141,7 @@ Playwright finalizes `.webm` files when pages/contexts close at the end of the t
produce multiple videos because the harness may open setup pages as well as the page under verification; keep all
of them beside the screenshots for the same scenario.

> Scratch copying the inline fixture won't read `E2E_RECORD_VIDEO_DIR` — see [gotchas](./references/verification-debugging.md#common-gotchas).
> A scratch script that copies the inline fixture won't read `E2E_RECORD_VIDEO_DIR` — see [gotchas](./references/verification-debugging.md#common-gotchas).

### Run only your scratch script

Expand Down
Loading