Skip to content

Commit 49dfadb

Browse files
committed
feat(design): scholarly-ink pass — RuleViewer, responsive article pages, partner marks
Completes the design pass: Inter Tight throughout (Fraunces fully removed incl. WONK/SOFT axis settings and the Tailwind serif token), redesigned RuleViewer and article pages, prettier+eslint tooling, partner authority marks on About.
1 parent f962a6c commit 49dfadb

78 files changed

Lines changed: 2601 additions & 1261 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.prettierignore

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Build output and caches
2+
dist/
3+
.astro/
4+
coverage/
5+
6+
# Tooling artifacts
7+
playwright-report/
8+
test-results/
9+
10+
# Generated files (regenerated by scripts — don't fight their formatting)
11+
src/data/maps-catalogue.json
12+
package-lock.json
13+
public/maps/
14+
15+
# Legacy/reference content predating the Astro migration
16+
_legacy-content/
17+
docs/
18+
posts/
19+
map/
20+
TODO.complete/
21+
22+
# prettier-plugin-astro breaks text↔tag line boundaries inside prose,
23+
# and Astro trims those breaks (rendered spaces vanish — see the
24+
# whitespace-collapse guard in test/site.test.ts). paragraphs with
25+
# text↔tag boundaries (e.g. compare.astro's hero, the map page's
26+
# preview deck) stay hand-wrapped — prettier-ignore corrupts files.
27+
src/pages/compare.astro
28+
src/pages/maps/\[systemCode\].astro

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": false,
3+
"printWidth": 100,
4+
"plugins": ["prettier-plugin-astro"]
5+
}

CLAUDE.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## What this is
6+
7+
interscript.org v2 — the Interscript website, rebuilt on Astro 7 (branch `astro-migration`). It replaces the legacy react-static site that still lives on the `main` branch — consult `main` (and `TODO.complete/`) as the reference for feature parity. Content claims must be factual: do not fabricate partner/authority/endorsement statements.
8+
9+
## Commands
10+
11+
```bash
12+
npm run dev # dev server (localhost:4321; bumps port if taken — e2e uses 4322)
13+
npm run build # static build to dist/
14+
npm run preview # serve the build
15+
16+
npm run check # astro check (TypeScript across .astro/.ts/.vue)
17+
npm test # vitest run (unit tests in test/)
18+
npx vitest run test/site.test.ts # single test file
19+
npm run test:e2e # Playwright (e2e/); local runs need `npm run dev` on 4322
20+
npm run lint # eslint
21+
npm run format # prettier --write .
22+
npm run generate:catalogue # regenerate src/data/maps-catalogue.json from .isc sources
23+
```
24+
25+
Dependency note: `interscript-ts` is a `file:` dependency on the sibling checkout `../interscript-ts`, and `npm run generate:catalogue` reads `../maps/maps/*.isc` — both sibling repos must exist locally.
26+
27+
## Architecture
28+
29+
- **Rendering**: Astro `output: "static"` + `@astrojs/node` adapter. Pages under `src/pages/` are prerendered by default; the real-time routes (`src/pages/api/{detect,systems,transliterate,transliterate/batch}.ts`) opt out with `export const prerender = false` and run `interscript-ts` server-side.
30+
- **Interactive islands**: Vue 3 (`@astrojs/vue`) for tools — `MapExplorer`, `CompareMode`, `BatchProcessor`, `DiffViewer`, `DetectPanel`, `MarcTool`, `SubtitlesProcessor`, etc. Static pages are plain `.astro` importing these islands.
31+
- **Map data**: ~289 compact `.isc` maps in `public/maps/`. The browsable catalogue `src/data/maps-catalogue.json` is generated by `scripts/generate-catalogue.mjs` (commit the regenerated file when maps change).
32+
- **Map loading strategies** (why two modules): browser runtimes use `src/scripts/map-strategies.ts` — ISC files first, compiled-JSON HTTP fallback only for `.iml` libraries (posix, unicode, var-Cyrl, var-kor) which have no ISC form. The SSR API routes use `src/lib/server-map-strategies.ts` — same order but ISC loads from the filesystem, no HTTP roundtrip. Keep both in sync.
33+
- **Transliteration worker**: `src/scripts/transliteration-worker.ts` runs interscript-ts off the main thread; `worker-client.ts` is the typed RPC client. Vite bundles the worker via `new Worker(new URL(...), { type: "module" })`.
34+
- **Design system**: single source of truth in `src/styles/global.css` — Tailwind 4 CSS-first `@theme` tokens (colors, fonts, type scale, spacing) plus component classes (`.btn`, `.card`, `.prose`, `.eyebrow`). All pages consume these tokens; don't hardcode hex values. `src/layouts/Base.astro` owns the header nav / drawer / footer and carries its scoped styles inline.
35+
- **Content**: AsciiDoc (`src/content/docs/`, `src/content/blog/`) loaded by `src/content/loaders/asciidoc.ts` (manual frontmatter parse + @asciidoctor/core), rendered into `.prose` wrappers.
36+
- **Theme**: light/dark via `data-theme` on `<html>`; tokens flip in `src/styles/global.css`. A service worker (`public/sw.js`) provides offline access.
37+
38+
## Testing layout
39+
40+
- `test/*.test.ts` — vitest + happy-dom; covers pages, components, catalogue integrity, API endpoints.
41+
- `e2e/*.spec.ts` — Playwright, Chromium only. In CI the config starts `npm run dev` itself; locally run the dev server on port 4322 first (config `baseURL`).
42+
43+
## Conventions
44+
45+
- Conventional Commits (`feat:`, `fix:`, `docs:`, …). All changes go through PRs to `main` (branch `astro-migration` is the active migration branch).
46+
- Lint: `eslint.config.mjs` (ESLint 10 flat config; TS + Astro + Vue). Prettier: `.prettierrc` (no semicolons, 100 cols, astro plugin), `.prettierignore` for generated/legacy paths.
47+
- **Astro whitespace trap:** Astro _trims_ the space at a text↔inline-tag line break — `Backed by\n<code>` renders as `byinterscript-ts`. `prettier-plugin-astro` reflows prose and will create such breaks; `test/site.test.ts` has a guard that fails on them. `prettier-ignore` comments corrupt `.astro` files — instead, hand-wrap the paragraph at text-only boundaries and add the file to `.prettierignore` (see `src/pages/compare.astro`).

e2e/compare.spec.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ test.describe("compare systems", () => {
2020
test("has system selectors", async ({ page }) => {
2121
await page.goto("/compare")
2222
const selects = page.locator("select")
23-
if (await selects.first().isVisible({ timeout: 3000 }).catch(() => false)) {
23+
if (
24+
await selects
25+
.first()
26+
.isVisible({ timeout: 3000 })
27+
.catch(() => false)
28+
) {
2429
const count = await selects.count()
2530
expect(count).toBeGreaterThanOrEqual(1)
2631
}

e2e/demo-edge-cases.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import { test, expect, type Page } from "@playwright/test"
1212

1313
async function ready(page: Page) {
1414
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
15+
// The page default is the first catalogue entry (Chinese); these tests
16+
// assert Cyrillic → Latin, so pin a Cyrillic system.
17+
await page.locator("select.field-input").selectOption("odni-rus-Cyrl-Latn-2015")
1518
}
1619

1720
test.describe("demo edge cases", () => {

e2e/demo.spec.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ test.describe("demo page — transliteration explorer", () => {
2121
const select = page.locator("select.field-input")
2222
await expect(select).toBeVisible()
2323
const options = select.locator("option")
24-
await expect(options).toHaveCount(5)
24+
await expect(options).toHaveCount(289)
25+
await expect(select.locator("optgroup").first()).toHaveAttribute("label", "ACADSIN")
2526
})
2627

27-
test("default system is BGN/PCGN Ukrainian", async ({ page }) => {
28+
test("default system is the first catalogue entry", async ({ page }) => {
2829
await page.goto("/demo")
2930
const select = page.locator("select.field-input")
30-
await expect(select).toHaveValue("bgnpcgn-ukr-Cyrl-Latn-2019")
31+
await expect(select).toHaveValue("acadsin-zho-Hani-Latn-2002")
3132
})
3233

3334
test("transliterates Ukrainian Cyrillic to Latin", async ({ page }) => {
@@ -37,6 +38,8 @@ test.describe("demo page — transliteration explorer", () => {
3738
const status = page.locator(".rail-status")
3839
await expect(status).toContainText("ready", { timeout: 20000 })
3940

41+
await page.locator("select.field-input").selectOption("bgnpcgn-ukr-Cyrl-Latn-2019")
42+
4043
// Type Cyrillic input
4144
const textarea = page.locator("textarea.pane-body")
4245
await textarea.fill("Антон")
@@ -158,6 +161,7 @@ test.describe("demo modes — API vs in-browser", () => {
158161
const status = page.locator(".rail-status")
159162
await expect(status).toContainText("ready", { timeout: 30000 })
160163

164+
await page.locator("select.field-input").selectOption("odni-rus-Cyrl-Latn-2015")
161165
await page.locator("textarea.pane-body").fill("Антон")
162166
await expect(page.locator(".pane-result")).toContainText("Anton", { timeout: 10000 })
163167
})
@@ -182,6 +186,7 @@ test.describe("demo modes — API vs in-browser", () => {
182186
// API mode first
183187
const status = page.locator(".rail-status")
184188
await expect(status).toContainText("ready", { timeout: 30000 })
189+
await page.locator("select.field-input").selectOption("bgnpcgn-ukr-Cyrl-Latn-2019")
185190
await page.locator("textarea.pane-body").fill("Київ")
186191
await expect(page.locator(".pane-result")).toContainText("Kyiv", { timeout: 10000 })
187192
const apiOutput = await page.locator(".pane-result").textContent()

e2e/maps.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ test.describe("maps browser", () => {
2222
test("has search or filter controls", async ({ page }) => {
2323
await page.goto("/maps")
2424
// Look for search input, select, or filter buttons
25-
const controls = page.locator("input[type='search'], input[placeholder*='search' i], select, button")
25+
const controls = page.locator(
26+
"input[type='search'], input[placeholder*='search' i], select, button",
27+
)
2628
await expect(controls.first()).toBeVisible({ timeout: 5000 })
2729
})
2830

eslint.config.mjs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// @ts-check
2+
import js from "@eslint/js"
3+
import tseslint from "typescript-eslint"
4+
import astro from "eslint-plugin-astro"
5+
import vue from "eslint-plugin-vue"
6+
import prettierConfig from "eslint-config-prettier"
7+
import globals from "globals"
8+
9+
export default tseslint.config(
10+
{
11+
ignores: [
12+
"dist/**",
13+
".astro/**",
14+
"coverage/**",
15+
"playwright-report/**",
16+
"test-results/**",
17+
"public/**",
18+
"_legacy-content/**",
19+
"docs/**",
20+
"posts/**",
21+
"map/**",
22+
"TODO.complete/**",
23+
],
24+
},
25+
js.configs.recommended,
26+
...tseslint.configs.recommended,
27+
...astro.configs.recommended,
28+
vue.configs["flat/essential"],
29+
prettierConfig,
30+
{
31+
languageOptions: {
32+
globals: { ...globals.browser, ...globals.node },
33+
},
34+
rules: {
35+
"@typescript-eslint/no-unused-vars": [
36+
"error",
37+
{
38+
argsIgnorePattern: "^_",
39+
varsIgnorePattern: "^_",
40+
caughtErrorsIgnorePattern: "^_",
41+
},
42+
],
43+
// The ISC JSON IR and catalogue entries are untyped by design.
44+
"@typescript-eslint/no-explicit-any": "off",
45+
},
46+
},
47+
{
48+
files: ["**/*.vue"],
49+
languageOptions: {
50+
parserOptions: { parser: tseslint.parser },
51+
},
52+
},
53+
{
54+
files: ["**/*.astro", "**/*.ts"],
55+
rules: {
56+
// tsc (astro check) owns undefined-variable detection; the
57+
// Astro frontmatter processor trips no-undef on TS globals.
58+
"no-undef": "off",
59+
},
60+
},
61+
)

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,15 @@
4747
"eslint": "^10.0.0",
4848
"eslint-config-prettier": "^10.0.0",
4949
"eslint-plugin-astro": "^1.0.0",
50+
"eslint-plugin-vue": "^10.10.0",
51+
"globals": "^17.11.0",
5052
"happy-dom": "^20.11.1",
5153
"playwright": "^1.62.1",
5254
"prettier": "^3.9.0",
5355
"prettier-plugin-astro": "^0.14.0",
5456
"tsx": "^4.23.7",
5557
"typescript": "^5.6.0",
58+
"typescript-eslint": "^8.67.0",
5659
"vitest": "^4.1.10"
5760
}
5861
}

public/img/partners/bas.png

33.8 KB
Loading

0 commit comments

Comments
 (0)