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
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"js/ts.tsdk.path": "web/app/node_modules/typescript/lib",
"js/ts.validate.enabled": true,
"editor.formatOnSave": true,
"prettier.requireConfig": true,
"prettier.configPath": "web/app/.prettierrc.json",
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ make release
- Prefer existing patterns and the standard library before adding dependencies.
- Format with `make fmt`.
- Add or update tests when changing CLI, config, API, or runtime behavior.
- When changing the Vite web app, follow `docs/web/development.md` for frontend structure, source layout, components, styling, state, accessibility, and verification.
- Before changing the Vite web app, read and follow `docs/web/development.md`; it owns frontend structure, source layout, components, styling, state/data flow, accessibility, assets, and verification. This includes changes under `web/app/src`, frontend tests, package/config files, CSS/Tailwind/tokens, public asset paths, routes, i18n text, and generated `web/static-dist` handling.
- Do not change BoxLite sandbox integration or packaging paths unless the task is about sandbox/runtime integration.
- When changing config fields or defaults, update loader, saver, onboard flow, tests, and docs together.
- Never hardcode or print real secrets; startup and logs must keep tokens redacted.
Expand Down
16 changes: 15 additions & 1 deletion docs/web/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ These rules apply to the Vite web app in `web/app`.

Chinese companion: `development.zh.md`. This English document is the agent-facing source of truth.

## When To Use This Guide

- Read this guide before changing the Vite app's source, tests, package/config files, public assets, or generated web asset pipeline.
- Common triggers include React pages, components, hooks, shared models, route helpers, API clients, CSS/Tailwind/tokens, i18n text, Vite/Vitest/TypeScript config, dependency changes, public asset URLs, and `web/static-dist` handling.
- When changing UI primitives, Radix wrappers, form controls, overlays, or component-library exports, also follow `docs/web/ui-components.md`.

## Tooling

- Use Node.js 22.13.x or newer, up to but not including Node.js 25, for frontend development.
Expand Down Expand Up @@ -33,6 +39,14 @@ pnpm --dir web/app install
- Treat `web/static-dist` as generated output for Go embedding.
- Prefer local conventions already present in `web/app` before adding new patterns or dependencies.

## TypeScript Type Checking

- `pnpm --dir web/app typecheck` is the strict TypeScript gate and must stay passing for normal frontend changes.
- Treat missing type declarations, unresolved modules, implicit `any`, and other TypeScript errors as part of the change. Add or refine local types, package types, runtime guards, or typed adapters until type checking passes.
- When strict type errors appear, prioritize the touched code and shared foundations first: `src/models`, `src/shared`, controller hooks, API boundary helpers, and reusable UI primitives.
- Prefer precise input/output types, runtime guards at external boundaries, and small null handling fixes over broad assertions or `any`.
- Do not change user-visible behavior just to satisfy TypeScript. If a type fix needs behavior changes, cover the affected path with a focused test.

## Static Assets And Deployment Paths

- Reference `web/app/public` assets with deployment-relative paths, for example `brand/logo.svg` or `icons/users.svg`.
Expand Down Expand Up @@ -213,7 +227,7 @@ src/pages/WorkspacePage/components/

## Tests And Verification

- Run `pnpm --dir web/app typecheck` after TypeScript or import-path changes.
- Run `pnpm --dir web/app typecheck` after frontend TypeScript, import-path, package, or config changes, and fix missing type declarations or type errors before handing off the change.
- Run `pnpm --dir web/app lint` after frontend source changes.
- Run `pnpm --dir web/app format:check` before sharing changes, or `pnpm --dir web/app format` to apply Prettier formatting.
- Run `pnpm --dir web/app test` for the frontend Vitest suite.
Expand Down
16 changes: 15 additions & 1 deletion docs/web/development.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

英文版为 `development.md`,Agent 默认以英文版为准。

## 何时使用本规范

- 修改 Vite 应用源码、测试、包或配置文件、public 资源,或生成式前端资产链路前,先阅读并遵循本规范。
- 常见触发范围包括 React 页面、组件、hooks、共享 models、路由 helper、API client、CSS/Tailwind/tokens、i18n 文案、Vite/Vitest/TypeScript 配置、依赖变更、public 资源 URL 和 `web/static-dist` 处理。
- 修改 UI primitives、Radix wrappers、表单控件、浮层或组件库 export 时,还要遵循 `docs/web/ui-components.md` 或 `docs/web/ui-components.zh.md`。

## 工具链

- 前端开发使用 Node.js 22.13.x 或更新版本,但不包括 Node.js 25 及以上。
Expand Down Expand Up @@ -33,6 +39,14 @@ pnpm --dir web/app install
- `web/static-dist` 是给 Go embed 使用的构建产物。
- 新增模式或依赖前,优先沿用 `web/app` 里已有的约定。

## TypeScript 类型检查

- `pnpm --dir web/app typecheck` 是严格 TypeScript 门槛,普通前端改动必须保持通过。
- 缺失类型声明、模块无法解析、隐式 `any` 和其它 TypeScript 报错,都属于本次改动要处理的内容。通过补充或收敛本地类型、包类型、运行时 guard 或 typed adapter,让类型检查通过。
- 处理 strict 类型报错时,优先处理本次触碰的代码和共享底座:`src/models`、`src/shared`、controller hooks、API 边界 helper 和可复用 UI primitives。
- 优先使用准确的输入/输出类型、外部边界运行时 guard、小范围 null 处理;不要用大范围断言或 `any` 掩盖问题。
- 不要为了满足 TypeScript 改变用户可见行为。类型修复如果必须伴随行为变化,需要给受影响路径补聚焦测试。

## 静态资源与部署路径

- 引用 `web/app/public` 资源时用部署相对路径,例如 `brand/logo.svg` 或 `icons/users.svg`。
Expand Down Expand Up @@ -213,7 +227,7 @@ src/pages/WorkspacePage/components/

## 测试与验证

- TypeScript 或 import 路径变化后,运行 `pnpm --dir web/app typecheck`。
- 前端 TypeScript、import 路径、包依赖或配置变化后,运行 `pnpm --dir web/app typecheck`,并在交付前修复缺失类型声明和类型报错
- 前端源码变化后,运行 `pnpm --dir web/app lint`。
- 提交或共享改动前,运行 `pnpm --dir web/app format:check`;需要应用 Prettier 格式化时运行 `pnpm --dir web/app format`。
- 运行 `pnpm --dir web/app test` 执行前端 Vitest 测试。
Expand Down
11 changes: 8 additions & 3 deletions docs/web/ui-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,20 @@ When adding a Radix component with a portal, expose a container escape hatch whe
- Keep the Radix interaction contract intact, and compose CSGClaw styling around it.
- Prefer data-driven APIs for common form controls, with compound exports available only for custom layouts.
- Pages and business components should not depend on Radix primitives directly unless it is an explicit page-private exploration. Extract the wrapper to `src/components/ui` once the interaction needs reuse or becomes a stable convention.
- When adding Radix dependencies, follow the project's existing dependency style and keep Radix-related package versions aligned when practical to avoid duplicate shared dependencies.
- Import Radix namespaces from the unified `radix-ui` package, for example `import { Select as RadixSelect } from "radix-ui"`. Do not add `@radix-ui/react-*` package dependencies for primitives that are available through `radix-ui`.
- Add a separate Radix package only when the unified package does not expose the needed primitive, and document why in the change.
- If jsdom lacks browser APIs needed by a Radix primitive, add the smallest stable polyfill in `web/app/tests/setup.ts` and keep component tests focused on user-visible behavior.

## Select

`Select` is built on `@radix-ui/react-select`. Prefer the data-driven `options` prop for normal forms. Use the compound exports (`SelectRoot`, `SelectTrigger`, `SelectContent`, `SelectItem`) only when a custom layout is needed.
`Select` is built on the `Select` namespace from `radix-ui`. Prefer the data-driven `options` prop for normal forms. Use the compound exports (`SelectRoot`, `SelectTrigger`, `SelectContent`, `SelectItem`) only when a custom layout is needed.

`Select` maps an empty business value (`""`) to an internal Radix item value, because Radix reserves empty string for clearing selection. Callers should continue to read and write `""` normally.

## Dropdown Menu

`DropdownMenu` is built on `@radix-ui/react-dropdown-menu`. Compose menus with the shared `DropdownMenuRoot`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, and `DropdownMenuSeparator` exports so pages inherit consistent hover, keyboard, danger-state, portal, and focus behavior.
`DropdownMenu` is built on the `DropdownMenu` namespace from `radix-ui`. Compose menus with the shared `DropdownMenuRoot`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, and `DropdownMenuSeparator` exports so pages inherit consistent hover, keyboard, danger-state, portal, and focus behavior.

## Dialog

`Dialog` is built on the `Dialog` namespace from `radix-ui`. Use the shared dialog exports for modal workflows so pages inherit consistent backdrop, portal, focus trap, close button, title, description, and layout behavior. Expose `portalContainer` when a dialog or nested floating child needs to stay inside a specific layer.
13 changes: 11 additions & 2 deletions docs/web/ui-components.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,20 @@
- 保留 Radix 的交互契约,在其上组合 CSGClaw 的样式。
- 常规表单控件优先提供 data-driven API;只有需要自定义布局时才使用 compound exports。
- 页面或业务组件不要直接依赖 Radix primitive,除非这是一次明确的页面私有探索;一旦需要复用或形成稳定交互,应抽到 `src/components/ui`。
- 新增 Radix 依赖时,优先遵循项目现有依赖方式,并尽量让 Radix 相关包保持一致版本,避免重复 shared dependency。
- 从统一的 `radix-ui` 包导入 Radix namespace,例如 `import { Select as RadixSelect } from "radix-ui"`。不要为已经由 `radix-ui` 暴露的 primitive 新增 `@radix-ui/react-*` 分包依赖。
- 只有统一包没有暴露所需 primitive 时,才单独新增 Radix 分包,并在改动里说明原因。
- 如果 jsdom 缺少某个 Radix primitive 依赖的浏览器 API,在 `web/app/tests/setup.ts` 里补最小、稳定的 polyfill,并让组件测试聚焦用户可见行为。

## Select

`Select` 基于 `@radix-ui/react-select` 实现。常规表单优先使用 data-driven 的 `options` prop。只有需要自定义布局时才使用 compound exports(`SelectRoot`、`SelectTrigger`、`SelectContent`、`SelectItem`)。
`Select` 基于 `radix-ui` 包里的 `Select` namespace 实现。常规表单优先使用 data-driven 的 `options` prop。只有需要自定义布局时才使用 compound exports(`SelectRoot`、`SelectTrigger`、`SelectContent`、`SelectItem`)。

`Select` 会把业务侧空值(`""`)映射到内部 Radix item value,因为 Radix 把空字符串保留给清空选择。调用方仍然按 `""` 正常读写即可。

## Dropdown Menu

`DropdownMenu` 基于 `radix-ui` 包里的 `DropdownMenu` namespace 实现。菜单应组合共享的 `DropdownMenuRoot`、`DropdownMenuTrigger`、`DropdownMenuContent`、`DropdownMenuItem` 和 `DropdownMenuSeparator`,让页面继承一致的 hover、键盘、danger 状态、portal 和焦点行为。

## Dialog

`Dialog` 基于 `radix-ui` 包里的 `Dialog` namespace 实现。modal 流程应使用共享 dialog exports,让页面继承一致的 backdrop、portal、focus trap、关闭按钮、标题、描述和布局行为。dialog 或嵌套 floating child 需要留在特定层级内时,暴露并使用 `portalContainer`。
7 changes: 3 additions & 4 deletions web/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"build": "pnpm typecheck && vite build",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier . --write",
Expand All @@ -19,14 +19,12 @@
"pnpm": ">=9 <12"
},
"dependencies": {
"@radix-ui/react-dialog": "~1.1.15",
"@radix-ui/react-dropdown-menu": "~2.1.16",
"@radix-ui/react-select": "~2.2.6",
"@tanstack/react-query": "~5.100.10",
"dompurify": "~3.4.3",
"lucide-react": "~1.16.0",
"marked": "~18.0.3",
"mermaid": "~11.15.0",
"radix-ui": "~1.5.0",
"react": "~19.2.6",
"react-dom": "~19.2.6",
"react-hook-form": "~7.76.0",
Expand Down Expand Up @@ -54,6 +52,7 @@
"typescript": "~6.0.3",
"typescript-eslint": "~8.59.3",
"vite": "~8.0.13",
"vite-plugin-checker": "~0.14.1",
"vitest": "~4.1.6"
},
"packageManager": "pnpm@11.1.3"
Expand Down
Loading
Loading