feat: migrate workbench to React

This commit is contained in:
2026-08-26 15:01:07 +08:00
parent f15e69c868
commit 3ddb33e321
48 changed files with 16748 additions and 1509 deletions

View File

@@ -0,0 +1,37 @@
# Quality Guidelines
> Code quality standards for frontend development.
---
## Overview
The workbench frontend uses Prettier as the source formatting authority. Code is formatted before review; formatting is not left to individual editor settings.
---
## Forbidden Patterns
- Do not commit dense, manually minified, or single-line JSX/TypeScript.
- Do not hand-format around Prettier output.
---
## Required Patterns
- Run `npm run format` after editing frontend files.
- Run `npm run format:check` in the validation gate.
- Keep React components, hooks, types, and API clients in separately formatted modules once a component becomes non-trivial.
---
## Testing Requirements
- Run `npm run format:check`, `npm run test:client`, and `npm run build` for frontend changes.
---
## Code Review Checklist
- Formatting check passes.
- UI behavior changes do not rebuild the OpenLayers map unless the map lifecycle explicitly requires it.

View File

@@ -0,0 +1,4 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
{"file":".trellis/spec/frontend/index.md","reason":"Review frontend quality, component, hook, and state conventions."}
{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"Verify API-to-React-to-OpenLayers data flow and cleanup boundaries."}
{"file":".trellis/spec/guides/code-reuse-thinking-guide.md","reason":"Check that migration does not leave duplicate DOM and React implementations or duplicate map logic."}

View File

@@ -0,0 +1,32 @@
# Technical Design
## Frontend Boundary
Create a Vite app under `workbench/client/` (or a clearly scoped `workbench/frontend/` directory) with React 19 and TypeScript. Keep Node/server code CommonJS and keep `/api/*` payloads unchanged. The production build should emit a deterministic directory consumed by `workbench/server.js`; development mode may use a Vite proxy to the workbench API.
## OpenLayers Migration Strategy
- Import OpenLayers modules directly from the installed `ol` package in TypeScript; remove the runtime `/vendor/ol/*.js` import map from the React build.
- Keep one `Map` instance per mounted map component. A `useOpenLayersMap` hook creates it once after the container ref is available, registers interactions/listeners, and calls `map.setTarget(undefined)` plus listener cleanup on unmount.
- Keep the layer registry in an adapter module/hook (`map/layers.ts`), created once with stable `VectorSource` and `VectorLayer` instances. React state changes update sources, styles, visibility, and selection overlays through explicit adapter methods rather than recreating the map.
- Use typed GeoJSON/read helpers at the API boundary; preserve EPSG:4326 to EPSG:3857 conversion and current fit/selection behavior.
- OpenLayers event callbacks publish typed selection/pointer events to React state. React panels render from that state and issue API mutations; they do not query or mutate DOM nodes owned by OpenLayers.
- Import `ol/ol.css` from the Vite entry and keep map container dimensions in application CSS.
## UI and State
- Use shadcn/ui primitives for buttons, inputs, checkboxes, select, tabs/segmented filters, dialogs, sheets, alerts, and toast feedback. Keep the existing dense three-column workbench layout.
- Define API/state types in `src/types/` based on the actual `/api/state` and mutation payloads. Use a small typed client in `src/lib/api.ts`.
- Use React hooks/context for session and editor state. Keep transient map selection and staged overrides separate from server state; avoid introducing a large state library unless the migration proves one necessary.
- Split components by existing workflow boundaries: `ImportScreen`, `WorkbenchShell`, `LayerPanel`, `DiagnosticsPanel`, `MapCanvas`, `RoadInspector`, `SignalEditor`, and shared `ui/` primitives.
## Static Hosting
- Update `workbench/server.js` to serve Vite `index.html` and hashed assets in production, with a safe fallback for the SPA route while retaining `/vendor/*` only for compatibility during transition.
- Keep `npm run road:workbench` self-contained for production assets. Add Vite dev/proxy scripts without requiring the Node workbench server to become an ESM application.
## Risks and Compatibility
- OpenLayers owns imperative objects and must not be recreated on every React render; the hook/adapter boundary is mandatory.
- Existing style functions close over selected road state. Convert them to adapter-managed mutable selection refs or call `layer.changed()` after selection updates.
- The first migration should preserve behavior and layout; visual redesign is explicitly deferred.

View File

@@ -0,0 +1,4 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
{"file":".trellis/spec/frontend/index.md","reason":"Load the project frontend conventions before introducing Vite, React, TypeScript, and shadcn/ui."}
{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"The migration changes the browser build, static hosting, API client, and imperative map lifecycle together."}
{"file":".trellis/spec/guides/code-reuse-thinking-guide.md","reason":"Reuse current OpenLayers styles, API payloads, and editor behavior instead of duplicating domain logic."}

View File

@@ -0,0 +1,20 @@
# Implementation Plan
1. Add Vite, React 19, TypeScript, Tailwind/shadcn/ui dependencies and scripts; establish `tsconfig`, Vite config, aliases, CSS variables, and production output location.
2. Define typed API/state contracts and a fetch client from the existing `/api/*` payloads; add focused serialization tests.
3. Build the OpenLayers adapter and `useOpenLayersMap` hook. Port the current layer registry, styles, source updates, selection interaction, fit behavior, and cleanup tests before moving inspector UI.
4. Port the import-first flow and shell/layout to React, then migrate layer controls, summaries, diagnostics, road inspector, connection editor, centerline editor, signal editor, and save/compile actions.
5. Replace the server's legacy `/app.js`/`/app.css` production serving with Vite build output while retaining a compatibility path only where needed for rollback.
6. Remove the old DOM-driven entry after parity is demonstrated; keep OpenLayers vendor serving only if legacy mode still needs it.
7. Add browser/component smoke coverage for no-session import, active-session map render, a road selection/edit, and compile/export request wiring.
8. Run `npm run build`, frontend tests, existing `npm test`, and a manual workbench smoke check at desktop/mobile widths; fix parity regressions.
Validation commands:
- `npm run build`
- `npm run format:check`
- frontend test command added by this task
- `npm test`
- `node --check workbench/server.js`
Rollback point: retain the legacy client in a separate compatibility path until the React build serves the same API workflow and OpenLayers interaction checks pass.

View File

@@ -0,0 +1,43 @@
# 迁移工作台到 React 前端
## Goal
将现有原生 HTML/CSS/JS 工作台迁移到 Vite、React 19、TypeScript 和 shadcn/ui获得可维护、可扩展且具备类型安全的成熟前端工程结构同时保留当前 OSM 导入、地图查看、道路参数编辑、诊断、信号灯编辑、重新编译和道路包导出能力。
## Confirmed Facts
- 当前前端位于 `workbench/client/index.html``workbench/client/app.js``workbench/client/app.css`
- `workbench/client/app.js` 使用 OpenLayers 浏览器模块,并动态创建部分控制面板和图层开关。
- `workbench/server.js` 提供静态资源和 `/api/state``/api/import``/api/overrides``/api/traffic-signals``/api/compile``/api/export.zip` 等接口。
- 当前项目是 CommonJS Node 包尚无前端构建脚本、TypeScript 配置、Vite 配置或 shadcn/ui 依赖。
- 地图核心继续使用已有的 OpenLayers 10本任务是前端工程迁移不替换地图引擎。
## Requirements
1. 建立独立、可构建的 Vite React 19 TypeScript 前端入口并迁移工作台代码。
2. 配置 shadcn/ui 所需的 CSS 变量、组件基础设施和图标/交互约定,优先使用其组件承载按钮、表单、面板、弹层和提示。
3. 将页面状态、API 调用、地图图层/选择逻辑和编辑表单拆分为 React 组件、hooks、类型和服务模块避免主要业务 UI 继续依赖全局 `querySelector` 和动态 `innerHTML`
4. 保留现有服务端 API 契约和 OSM 导入工作流;必要时只调整静态资源托管和开发/生产构建入口。
5. 保留 OpenLayers 地图的现有图层、样式、选择和交互行为,并确保状态变化正确驱动地图与检查器更新。
6. 提供开发模式和生产构建命令Node 工作台服务能提供构建后的前端资源。
7. 为核心 API 客户端、状态转换和至少一个关键交互增加 TypeScript/自动化验证。
## Acceptance Criteria
- `npm run build` 能在干净依赖安装后生成前端生产构建产物。
- `npm run dev`(或等价命令)能启动 Vite 开发服务器并加载工作台页面。
- `npm run road:workbench` 使用构建产物时仍能打开工作台;无活动工作区时显示 OSM 导入界面,有活动工作区时显示地图和编辑器。
- OSM 导入、图层开关、道路选择/参数暂存、诊断筛选、交通信号编辑、保存、重新编译和 ZIP 导出均保持可用。
- 页面不再通过原生 `querySelector`/`innerHTML` 组织主要业务 UI业务状态和 API 数据具有明确 TypeScript 类型。
- 现有 Node 编译器测试继续通过;新增前端构建和关键工作流验证通过。
## Out Of Scope
- 不修改道路编译算法、OSM 解析规则、GeoJSON 数据结构或现有 HTTP API 语义。
- 不更换 OpenLayers 为其他地图引擎。
- 不引入认证、多用户协作、服务端渲染或远程部署平台。
- 不在本任务内重新定义道路参数模型或增加新的业务功能。
## Open Product Decision
迁移是否同时允许明显的视觉和信息架构重设计?推荐第一阶段以“功能和工作流等价 + shadcn/ui 统一视觉”为目标,保留现有三栏地图工作台布局;这样可以把风险集中在工程迁移,后续再单独做 UX 重构。若本任务同时重做布局,交付周期和回归范围会显著增加。

View File

@@ -0,0 +1,26 @@
{
"id": "react-workbench-migration",
"name": "react-workbench-migration",
"title": "迁移工作台到 React 前端",
"description": "将现有原生 HTML/CSS/JS 工作台迁移到 Vite、React 19、TypeScript 和 shadcn/ui保持现有工作流能力",
"status": "in_progress",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "dingkang",
"assignee": "dingkang",
"createdAt": "2026-08-26",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}