diff --git a/AGENTS.md b/AGENTS.md index f7c2b33..9c1daa0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,14 +14,14 @@ ## Development Commands ```bash -make dev # 增量构建 4 个 libs (pnpm -r run build) + tailwindcss watch + dx serve (needs PostgreSQL, SSR_CACHE_SECS=0) +make dev # 增量构建全部 libs (pnpm -r run build) + tailwindcss watch + dx serve (needs PostgreSQL, SSR_CACHE_SECS=0) make build # pnpm install → build-libs → highlight-css → tailwindcss → doc → dx build --release → restore-webp make build-linux # 客户端 + 服务端分离构建,target x86_64-unknown-linux-musl make build-freebsd # cross-compile FreeBSD x86_64 server binary (clang + lld + sysroot, via cargo, not dx) make freebsd-sysroot # download/extract FreeBSD base.txz → .freebsd-sysroot/ (idempotent) make css # one-shot Tailwind build make css-watch # Tailwind watch mode -make test # cargo test + pnpm -r run test (all 4 libs: tiptap-editor / lightbox / yggdrasil-core / codemirror-editor) +make test # cargo test + pnpm -r run test (全部 libs: tiptap-editor / lightbox / yggdrasil-core / codemirror-editor / xterm-terminal) make doc # cargo doc (ayu 主题) → 拷贝到 public/doc/,随 build 发布 make doc-open # 同 doc,生成后自动用浏览器打开(本地预览,不拷贝) make lint # Biome check (libs) + cargo clippy (严格模式,warning 即失败) @@ -29,11 +29,11 @@ make fix # biome format --write (libs) + cargo fix --allow-dirty make clean # cargo clean + rm public/{style.css,highlight.css,doc} + rm -rf uploads/.cache + rm -rf libs/node_modules libs/*/node_modules ``` -**Build order matters**: `make build` runs `pnpm install --frozen-lockfile` (in `libs/`) → `build-libs` (`pnpm -r run build`, all 4 libs in parallel) → `highlight-css` (`cargo run --bin generate_highlight_css`) → `tailwindcss --minify` → `doc` → `dx build --release` → `restore-webp`. Do not run `dx build --release` alone. +**Build order matters**: `make build` runs `pnpm install --frozen-lockfile` (in `libs/`) → `build-libs` (`pnpm -r run build`, all libs in parallel) → `highlight-css` (`cargo run --bin generate_highlight_css`) → `tailwindcss --minify` → `doc` → `dx build --release` → `restore-webp`. Do not run `dx build --release` alone. **`restore-webp` workaround**: dx build 0.7.9 re-encodes `public/*.webp` into VP8L lossless stills (drops animation frames, 7-8× larger), contradicting the "verbatim copy" promise. `restore-webp` overwrites `.webp` in `target/dx/**/web/public/` from source `public/`. SVG/ICO are unaffected. Remove once upstream fixes it. -**JS workspace lives under `libs/`**: `libs/` is a pnpm workspace — root `libs/package.json` hoists the 4 shared devDeps (`happy-dom`/`typescript`/`vite`/`vitest`) + Biome; `libs/pnpm-workspace.yaml` lists `libs/*` packages; `libs/pnpm-lock.yaml` is the single lockfile. First-time setup: `cd libs && pnpm install`. All Makefile recipes `cd libs && pnpm ...`. Build a single lib: `make build-editor` (≈ `cd libs && pnpm --filter @yggdrasil/tiptap-editor run build`). +**JS workspace lives under `libs/`**: `libs/` is a pnpm workspace — root `libs/package.json` hoists the 4 shared devDeps (`happy-dom`/`typescript`/`vite`/`vitest`) + Biome; `libs/pnpm-workspace.yaml` lists `libs/*` packages; `libs/pnpm-lock.yaml` is the single lockfile. First-time setup: `cd libs && pnpm install`. All Makefile recipes `cd libs && pnpm ...`. Build a single lib: `make build-editor` (≈ `cd libs && pnpm --filter @yggdrasil/tiptap-editor run build`). There is also `@yggdrasil/shared` (`libs/shared/`) — a non-IIFE **internal source-shared package** (exports `ThemeName`/`THEME_CHANGE_EVENT`/`prefersReducedMotion`, `main` points at `src/index.ts` with no Vite build) consumed via `workspace:*` by codemirror-editor/lightbox/xterm-terminal/yggdrasil-core and inlined into each lib's IIFE bundle at build time; it has no `public/` artifact. ## Prerequisites @@ -180,7 +180,7 @@ Readers can execute fenced code blocks in isolated Docker containers; authors ge - **Execution layer** (`src/infra/docker.rs`, server-only): `bollard` client over Unix socket (`DOCKER_CLIENT` LazyLock). `run_in_container` creates a read-only-rootfs container (tmpfs `/code`,`/tmp`,`/run`; cpu/memory/pids/ulimits cap; `cap_drop=ALL`; `no-new-privileges`; non-root `1000:1000`; `network_mode` none/bridge), injects source via stdin, waits with timeout, captures stdout/stderr (truncated to `output_bytes`), inspects OOM, force-removes via `ContainerGuard` Drop. `src/infra/runner_config.rs` (`RUNNER_CONFIG`) reads all `CODE_RUNNER_*` env vars; `clamp_limits` AND-merges request overrides × language `allow_network` × global switch. - **API layer** (`src/api/code_runner/`): shared `ExecRequest`/`ExecResult`/`ExecStatus`/`ExecTask` structs (compile on both targets); `progress.rs` = DashMap task registry + `gc_old_tasks`; `languages.rs` = `LANGUAGES` registry + `parse_fence_info`; `execute.rs` = `StartExec`/`GetExecResult` server functions (double rate-limit → whitelist → size check → enqueue → `tokio::spawn` + `RUNNER_SEMAPHORE` concurrency cap → clamp → run_in_container). System errors are sanitized to 「系统暂时不可用」 for anonymous callers; full errors in server logs only. -- **Markdown/render layer**: a fenced block ` ```python runnable {...overrides} ` renders to `
` (sanitizer whitelists these 4 attrs on ``). `PostContent` (`src/components/post/post_content.rs`) splits `content_html` into `Html`/`Runnable` fragments so each runnable block renders as a real `` vdom element (no manual DOM mutation → no hydration conflict). `CodeRunner` component polls `GetExecResult` via WASM-friendly `sleep_ms`. +- **Markdown/render layer**: a fenced block ` ```python runnable {...overrides} ` renders to ` ` (sanitizer whitelists these 4 attrs on ``). `PostContent` (`src/components/post/post_content.rs`) splits `content_html` into `Html`/`Runnable` fragments so each runnable block renders as a real `` vdom element (no manual DOM mutation → no hydration conflict). `CodeRunner` component polls `GetExecResult` via WASM-friendly `sleep_ms`. Output is rendered into an xterm.js terminal instance (`src/xterm_bridge.rs` + `libs/xterm-terminal`, mirroring the codemirror bridge pattern) — stdout/stderr streamed via SSE, with full-buffer `writeAll` as a polling fallback. **Critical WASM-visibility rule**: `code_runner/execute.rs` is **not** cfg-gated (server functions must be visible to the client), but every server-only `use`/static inside it is individually `#[cfg(feature = "server")]`. The shared `use` of `ExecRequest`/`ExecTask` (used in signatures) stays ungated. `code_runner/languages.rs` and `code_runner/progress.rs` (pure server helpers) are wholly gated. Mirrors the `posts/` module convention. @@ -190,7 +190,7 @@ Readers can execute fenced code blocks in isolated Docker containers; authors ge ## Frontend Lib Subprojects -Four Vite-built IIFE libraries under `libs/`, managed as a **pnpm workspace** (single `libs/pnpm-lock.yaml`, shared `libs/tsconfig.base.json` + `libs/biome.json`, hoisted devDeps). Built artifacts go to `public/ /` — **do not edit `public/ /` files; they are build artifacts**. Each `build` script is `tsc --noEmit && vite build` (type-check before bundle). Output is IIFE because Dioxus `[web.resource] script` injects bare `