Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ jobs:
exit 1
fi
npx --yes wrangler@4.135.0 pages deploy dist --project-name codewiki --branch main --commit-hash "$GITHUB_SHA" --force
- if: failure()
- name: Upload validation reports
if: always()
uses: actions/upload-artifact@v4
with:
name: test-artifacts
Expand Down
4 changes: 4 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import preact from '@astrojs/preact';
import tailwindcss from '@tailwindcss/vite';

import pwa from './scripts/build-sw.mjs';
import { contentModificationDates, sitemapMetadata } from './scripts/sitemap-metadata.ts';

import { LEGACY_REDIRECTS } from './src/data/redirects.ts';
import { rehypeCodebox } from './src/markdown/rehype-codebox.ts';
Expand All @@ -16,6 +17,8 @@ import { remarkDepth } from './src/markdown/remark-depth.ts';
import { rehypeSectionActions } from './src/markdown/rehype-section-actions.ts';
import { shikiMetaTransformer } from './src/markdown/shiki-meta.ts';

let sitemapDates;

export default defineConfig({
site: 'https://codewiki.com',
output: 'static',
Expand Down Expand Up @@ -61,6 +64,7 @@ export default defineConfig({
preact(),
sitemap({
i18n: { defaultLocale: 'en', locales: { en: 'en', zh: 'zh-Hans' } },
serialize: (item) => sitemapMetadata(item, (sitemapDates ??= contentModificationDates())),
// Settings is `noindex`, search is a query interface and the offline notice only ever
// renders without a network: none belongs in the sitemap, and listing an unindexable URL is
// a Search Console warning. A 301 is not a document either.
Expand Down
18 changes: 14 additions & 4 deletions docs/dev/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,20 @@ other static host, serves for a directory request.

## Performance budgets

Page metadata is built in `src/lib/seo.ts`: canonical URLs, language alternates, Open Graph and
Twitter cards, and page-specific JSON-LD. Topics expose their reviewed date, section and tags to
Open Graph and their generated image to JSON-LD. Publication dates and author profiles are omitted
when the content has no reliable source for them. Reused sharing images describe the source card.
`scripts/sitemap-metadata.ts` uses content review/verification dates for topic and cheatsheet
`lastmod` values; it never substitutes the build date. Undated index pages remain undated.
The link checker also validates destinations in structured data.

`lighthouserc.json` drives `pnpm exec lhci autorun`, which builds nothing itself: it serves the
existing `dist` and audits ten URLs. The four P1 surfaces are the English home page, a track hub,
existing `dist` and audits eleven URLs. The four P1 surfaces are the English home page, a track hub,
and the topic page in both languages. The P2 surfaces are Practice, Python from zero, Flashcards,
the Python cheatsheet, Prompt Builder and Playground. Lighthouse uses its default mobile
emulation. All four category scores are gated at 0.95. The transferred-script ceilings are 60 KiB
the Python cheatsheet, Prompt Builder and Playground, plus the JavaScript practice catalogue. Lighthouse uses its default mobile
emulation, with three runs per URL and median assertions. Performance is gated at 0.95; accessibility,
best practices and SEO must score 1.00. The transferred-script ceilings are 66 KiB
for P1, 100 KiB for Practice, paths, Flashcards, cheatsheets and Prompt Builder, and 300 KiB for
Playground.

Expand All @@ -136,7 +145,8 @@ Three notes on that file, since JSON cannot carry comments:
and 0.99 respectively, so the 0.95 performance gate remains unchanged.
- **Accessibility is release-gated.** `color-contrast` and `label-content-name-mismatch` are
Lighthouse errors, and the Playwright accessibility coverage checks the contrast-sensitive UI
directly. The earlier set of ten open contrast pairs has been corrected.
directly. Playwright checks all reported WCAG 2.2 AA and best-practice violations across both
languages and themes, plus keyboard focus, mobile menus and the open search dialog.
- **`network-dependency-tree-insight` is `off`.** It scores zero for any critical request chain
deeper than one hop, which the HTML → stylesheet → webfont chain of a self-hosted static site
always has.
Expand Down
18 changes: 18 additions & 0 deletions docs/dev/seo-a11y-validation-2026-09-22.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# CodeWiki local validation report — 2026-09-22

This report records local validation before deployment. At the user's request, the remaining local Lighthouse measurements were stopped and the Chromium processes started for this audit were closed. The local Lighthouse gate covering 11 pages did not finish. The complete gate runs in [PR #1's GitHub CI checks](https://github.com/codewiki-com/codewiki/pull/1/checks), with reports retained in the `test-artifacts` artifact for seven days. The local measurements below are historical evidence, not the final CI result.

- Lint, formatting and type checks passed; the build succeeded.
- All 2,427 unit tests and 364 Chromium browser tests passed.
- Accessibility checks cover 23 page types in English and Chinese, light and dark themes, search dialogs, failure states, both sides of review cards, and keyboard interactions.
- All 155,274 internal links and structured-data destinations resolve.
- SEO metadata checks passed for 5,909 pages; all 2,349 OG images exist and measure 1200×630.
- The sitemap contains 5,902 URLs, all with x-default alternates; 598 URLs with reliable content dates include lastmod.
- robots.txt, llms.txt and llms-full.txt exist; missing applicable OG/Twitter tags, article metadata and JSON-LD fields have been added.

The table includes only completed Lighthouse measurements from this audit. Scores are medians of the completed runs; pages with incomplete measurements are not claimed to have passed.

| Page | Completed runs | Performance | Accessibility | Best practices | SEO |
| --- | ---: | ---: | ---: | ---: | ---: |
| /index.html | 3 | 99 | 100 | 100 | 100 |
| /python/index.html | 1 | 88 | 100 | 100 | 100 |
6 changes: 3 additions & 3 deletions docs/superpowers/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Legend: **[user]** = needs a decision or action from the owner · **[design]** =

Small changes with the largest effect on first impressions and credibility.

- [x] **A1. Home page leads with a review kata.** [design → Opus] Move the daily kata (每日一练) card to the first screen as the hero ("Review this generated code before you would ship it"), with the search palette mock and the article entry points below it. Navigation order: Practice before Tracks. Keep the palette mock capped at four rows.
- [x] **A1. Home page leads with a review kata.** [design → Opus] Move the daily kata card to the first screen as the hero ("Review this generated code before you would ship it"), with the search palette mock and the article entry points below it. Navigation order: Practice before Tracks. Keep the palette mock capped at four rows.
- [x] **A2. Honest framing.** [Codex] Home tagline copy, About page and `home.sub`: replace "most first drafts are written by AI / the skill has become…" with "AI increasingly writes code; developers still have to understand implementations, state constraints and verify results." About page gains a section "How this content is made": AI-assisted drafts, every example executed, editorial standard, human spot checks, open corrections.
- [x] **A3. Visible verification on every topic.** [design → Opus] A small verification panel near the title: target environment with the exact patch version used (e.g. Python 3.14.2), the date the examples last ran, "N of N outputs matched", and a link to the checker log. Data comes from `content:check` writing a per-topic sidecar (`reports/verify/{track}/{slug}.json`) that the build reads. Distinguish **target environment** (what the article pins) from **the in-browser runner** (Pyodide's Python version, esbuild for TS, sql.js): label runnable blocks with the runner and its version; mark blocks whose output was recorded only on the target environment.
- [x] **A4. Report an error.** [Codex] A "Report an error" link on every topic, kata and interview page that opens a prefilled GitHub issue (title, URL, section). Requires the repository URL to be real (see A7).
Expand All @@ -36,7 +36,7 @@ Deepens the training side. Each item is a small design + implementation task; B1

## Phase C — later

- [ ] **C1. Distribution.** [user] Chinese channels (掘金, 知乎, V2EX) for the Chinese edition; measure mainland access speed and decide on a mirror if needed.
- [ ] **C1. Distribution.** [user] Chinese channels (Juejin, Zhihu, V2EX) for the Chinese edition; measure mainland access speed and decide on a mirror if needed.
- [ ] **C2. Tier 2 polish (423 topics), flagship tracks first.** Deferred by the user on 2026-09-04. Estimate at 10-way parallelism: ~6 h wall, ~79 M Codex tokens. Command: `pnpm content:polish --tier 2 --n 10 --max 100` per batch from `.worktrees/p0-content-pipeline`. Before running it, solve the Cloudflare Pages 20,000-file ceiling (Pagefind fragment size, OG images) or change hosts.
- [ ] **C3. Tier 3 polish (238 topics).** After C2.
- [ ] **C4. Visualizer tools and compare pages.** Deferred until after launch (2026-09-05); decide from which topics readers actually use. The cheatsheet footer still shows "compared with other languages · soon".
Expand All @@ -51,4 +51,4 @@ Deepens the training side. Each item is a small design + implementation task; B1

## Done (for orientation)

P0 content pipeline · P1 site foundation · P2 learning layer · tier-1 content wave (287 topics, 6 paths, 12 cheatsheets, 1,146 interview items, 1,733 practice items) · real-content UI fixes · content taxonomy pass · whole-site review and its fix round · daily kata (每日一练) · PWA offline · About page · full gate green on `main` (2026-09-05). Codex usage to date ≈ 60 M tokens.
P0 content pipeline · P1 site foundation · P2 learning layer · tier-1 content wave (287 topics, 6 paths, 12 cheatsheets, 1,146 interview items, 1,733 practice items) · real-content UI fixes · content taxonomy pass · whole-site review and its fix round · daily kata · PWA offline · About page · full gate green on `main` (2026-09-05). Codex usage to date ≈ 60 M tokens.
2 changes: 1 addition & 1 deletion docs/superpowers/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Read this first in any new session. Update it at every milestone and before star
- 2026-09-03 AI-era integration added to spec §6.1 (content: "In the AI era" block per topic, ai-era track, rules packs, review katas; form: prompt-ready pages, JSON API, MCP server later).
- 2026-09-03 Execution mode: subagent-driven development, one Opus subagent per plan task, Fable reviews between tasks. P1 first (Tasks 0–8), then P0 in parallel once P1 Task 6 (schemas) exists.
- 2026-09-04 P2 learning-layer mockups drafted by Fable (practice hub, review-the-AI's-code kata, flashcards, interview bank, cheatsheet, bilingual topic, prompt builder; light + dark). Sources `docs/design/mockups/p2/`, canvas: https://claude.ai/code/artifact/23900b82-1533-43f2-b3fc-eeb05070c137 — awaiting user review before the P2 plan.
- 2026-09-04 P2 sub-spec `docs/superpowers/specs/2026-09-04-p2-learning-layer-design.md` approved (user: “你自己决策,选最佳的”); decisions: 4 flashcard ratings, 2-column print, paired bilingual default, kata with line comments, CodeMirror 6 lazy. Next: P2 plan.
- 2026-09-04 P2 sub-spec `docs/superpowers/specs/2026-09-04-p2-learning-layer-design.md` approved (user instruction, translated: “Use your judgment and choose the best option”); decisions: 4 flashcard ratings, 2-column print, paired bilingual default, kata with line comments, CodeMirror 6 lazy. Next: P2 plan.
- 2026-09-03 Spec approved by user: `docs/superpowers/specs/2026-09-03-codewiki-design.md` (including §6.1 AI-era integration).

## Earlier milestones (2026-09-05 09:30 local; current state below)
Expand Down
2 changes: 1 addition & 1 deletion docs/superpowers/specs/2026-09-03-codewiki-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ Shell: `Nav`, `Footer`, `Search` (island: palette), `ThemeToggle` (island), `Lan
- Structure: **TL;DR** (3 cells) → **What it is / why it exists** → **How it works** (mechanics, one diagram if it helps) → **Examples** (runnable, realistic, with outputs produced by running them) → **Pitfalls** (merged with "best practices"; each pitfall states the fix) → **In the AI era** (what generated code gets wrong here, what to ask your AI to check, a 3-item review checklist, prompt vocabulary) → **Deep dive** (marked `deep`; internals, edge cases, performance with numbers or nothing) → **Checkpoint** (3–8 items, YAML sidecar, at least one `predict` and, where the topic allows, one `review` item) → **Further reading** (only links verified by fetching; official docs first). Interview questions go to the interview sidecar, not the article.
- Length 400–900 lines including code. No "In this article", "comprehensive guide", "By mastering…". No rule-of-three padding. Prose written for a smart colleague.
- Currency: verified against a named version as of 2026 (e.g. Python 3.14, Node 24, TypeScript 6, Go 1.27, Rust 1.98, React 19, Java 25 LTS, LangChain 1.x, current Claude/OpenAI SDKs); flag deprecated APIs explicitly.
- English: native, direct, present tense. Chinese: 地道的技术写作,术语一致(以术语表为准),中西文之间加空格,全角标点,不使用翻译腔句式。
- English: native, direct, present tense. Chinese: natural technical writing, consistent terminology from the glossary, a space between Chinese characters and adjacent Latin text, Chinese punctuation, and no translationese.
- Bilingual: the canonical language is chosen per topic (the better draft); the other language is a faithful paragraph-aligned translation; code identical; headings 1:1.
- Diagrams: Mermaid where a structure/flow helps (rendered to SVG at build); no decorative images.
- Every claim about behaviour must be backed by a runnable example or a cited primary source.
Expand Down
8 changes: 4 additions & 4 deletions lighthouserc.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,25 @@
"categories:accessibility": [
"error",
{
"minScore": 0.95
"minScore": 1
}
],
"categories:best-practices": [
"error",
{
"minScore": 0.95
"minScore": 1
}
],
"categories:seo": [
"error",
{
"minScore": 0.95
"minScore": 1
}
],
"color-contrast": "error",
"forced-reflow-insight": ["warn", {}],
"unused-css-rules": ["warn", {}],
"label-content-name-mismatch": "warn",
"label-content-name-mismatch": "error",
"network-dependency-tree-insight": "off"
}
},
Expand Down
2 changes: 1 addition & 1 deletion prompts/editorial-standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Sections 3–6 are `standard` depth by default. Mark the first example and the T
## 4. Voice

- English: native, direct, present tense, second person where natural. Contractions are fine. Prefer short sentences. Technical terms are used precisely and consistently with the glossary.
- Chinese: 地道的中文技术写作,不是翻译腔。段落同样以两到五句为一段,围绕一个意思展开;不要一句一段。用短句,主谓宾清楚。术语以术语表为准;首次出现的术语给出英文原文,例如「闭包(closure)」。中西文之间加一个空格(「Python 3.14 的 `asyncio`」),全角标点(,。;:?!「」),代码、数字、英文单词之间不用全角标点。不用「本文将」「让我们」「值得注意的是」。
- Chinese: write natural technical Chinese, without translationese. Use two to five sentences per paragraph to develop one idea; do not put every sentence in its own paragraph. Prefer short sentences with a clear subject, verb and object. Follow the glossary and include the English term on first mention, for example 「闭包(closure)」. Put one space between Chinese characters and adjacent Latin text, as in 「Python 3.14 的 `asyncio`」. Use Chinese punctuation (,。;:?!「」) in Chinese prose, but not between code tokens, numbers or English words. Avoid 「本文将」「让我们」「值得注意的是」.
- Both: no emoji, no exclamation marks, no marketing adjectives.
- MDX: raw `<` or `>` in prose must be escaped as an HTML entity or placed in inline code.

Expand Down
2 changes: 1 addition & 1 deletion prompts/translate-topic.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Use this brief when a topic already has one language at `status: reviewed` and t

1. Read the source fully. Note every heading, paragraph, list, code block, callout, component and their order. The target must have the same sequence of blocks.
2. Write the target file. Translate prose faithfully and natively. Keep identifiers, commands, file names, URLs, error messages and code verbatim; translate code comments only. Keep `<TLDR>`, `<Depth>`, `<Term id>`, `<Checkpoint>` and fence meta exactly as in the source. Translate only `title` and `description` in the frontmatter; copy every other frontmatter field unchanged.
3. Chinese targets: 中西文之间加空格,全角标点,术语首次出现附英文原文,不用翻译腔句式。English targets: native, direct, present tense.
3. Chinese targets: put a space between Chinese characters and adjacent Latin text, use Chinese punctuation, include the English term on first mention, and avoid translationese. English targets: native, direct, present tense.
4. Run `pnpm content:align {{TOPIC_ID}}` and `pnpm content:check {{TOPIC_ID}}`; fix until both print `OK`.
5. Print `TRANSLATE DONE {{TOPIC_ID}}` as the last line, or `TRANSLATE FAILED {{TOPIC_ID}}: {reason}`.

Expand Down
22 changes: 18 additions & 4 deletions scripts/check-dist-links.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* Dead-link check over the built site.
*
* Walks `dist/**\/*.html`, collects every same-origin `href` and `src` plus the URL-valued head
* metadata (`og:image`, `twitter:image`, canonical and hreflang alternates), and resolves each one
* metadata (`og:image`, `twitter:image`, canonical and hreflang alternates), JSON-LD destinations,
* and resolves each one
* against `dist/` the way the deployed site does. `trailingSlash: 'always'` in astro.config.mjs
* means a directory URL `/x/` is the file `dist/x/index.html`; a path that carries an extension
* is the file itself.
Expand All @@ -20,8 +21,6 @@ import { fileURLToPath } from 'node:url';
const ROOT = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
const DIST = path.join(ROOT, 'dist');

const KNOWN_LATER = ['/java/type-erasure/', '/java/wildcards-pecs/'];

/** The canonical origin, read from src/data/site.ts so the two never drift. */
async function siteOrigin() {
const source = await readFile(path.join(ROOT, 'src/data/site.ts'), 'utf8');
Expand Down Expand Up @@ -81,6 +80,21 @@ function metadataUrls(html) {
if (href !== undefined) values.push(href);
}
}
// Structured breadcrumbs and article images must resolve just like visible links. Schema
// identifiers and search URL templates are not page destinations and are left alone.
const urlKeys = new Set(['url', 'item', 'image', 'mainEntityOfPage']);
function visit(value, key = '') {
if (typeof value === 'string' && urlKeys.has(key)) values.push(value);
else if (Array.isArray(value)) value.forEach((item) => visit(item, key));
else if (value && typeof value === 'object') {
for (const [key, item] of Object.entries(value)) visit(item, key);
}
}
for (const match of html.matchAll(
/<script\b[^>]*type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi,
)) {
visit(JSON.parse(match[1]));
}
return values;
}

Expand Down Expand Up @@ -189,7 +203,7 @@ for (const page of pages) {
if (!pathname || seen.has(pathname)) continue;
seen.add(pathname);
checked += 1;
if (!resolveTarget(pathname) && !KNOWN_LATER.includes(pathname)) {
if (!resolveTarget(pathname)) {
failures.push({ page: `/${page}`, target: pathname });
}
}
Expand Down
Loading
Loading