chore(task): plan crosswalk element editor experiment

This commit is contained in:
2026-08-28 17:57:37 +08:00
parent b2da3b6866
commit a0af1c0e84
6 changed files with 204 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
{"file":".trellis/spec/backend/direct-edit-api.md","reason":"检查 preview 零写入、非空约束改变正式编译输出、保存与 rebase 合同。"}
{"file":".trellis/spec/frontend/direct-edit-client.md","reason":"检查 baseline 不被元素编辑代码写入、控件由 manifest 驱动且预览时序正确。"}
{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"核对 feature -> element -> manifest -> constraint 的完整读写链路。"}

View File

@@ -0,0 +1,59 @@
# 斑马线元素级编辑器设计
## 架构边界
本实验扩展现有直接编辑链路,而非建立画布对象存储:
```text
crosswalk feature 点击
-> elementId
-> element manifest (anchor + parameters + legal controls)
-> native-road-edits/v2 constraint
-> resolveDirectEditConstraints
-> compileControlMarkings
-> preview / save / recompile
```
`elementId` 是选择和分组标识;持久化仍使用稳定语义 anchor 上的 constraint。它不能是 GeoJSON 数组下标、屏幕坐标或临时 UUID。
## 选择合同
斑马线的每一条 stripe feature 必须包含 `native_id``element_id` 与可回链的 crossing / approach 身份。选择适配层只读取 `element_id`,再向服务器取得选中元素的权威 manifest。客户端不会据条带数量、空间距离或渲染顺序自行聚类。
元素 manifest 至少包含:
```ts
interface EditableElement {
elementId: string
kind: 'crosswalk'
anchor: { type: 'junction-approach'; nodeId: string; segmentId: string }
featureIds: string[]
controls: Array<{
kind: 'position' | 'length' | 'width'
value: { current: number; min: number; max: number; unit: 'meter' }
axisAzimuth?: number
}>
fields: Array<{ key: string; value: number; min: number; max: number; unit: 'meter' | 'count' }>
}
```
第一阶段只发布 `position` 及其现有 `junction-crosswalk-inset` 回写规则。第二阶段再加入尺寸和条带参数,避免在实验开始前规定尚未验证的通用元素协议。
## 编辑与预览
- 选择由 `MapCanvas` 的 feature 事件产生,但选择状态由 App/编辑会话持有,不能写入 baseline source。
- 元素控件写入的是 manifest 指定的 constraint kind/value表单复用同一写入器。
- 选中框和控件只是 `editHandles` / `editGhost` 层的临时 UI权威预览仍由服务端返回baseline source 不被客户端改写。
- 空白点击会清除元素选择;道路选择可继续决定道路级 handle manifest。斑马线元素选中与道路选择可并存但两组控件不得重叠或竞争同一 pointer 手势。
## 数据演进
第一阶段不新增 persisted schema位置继续使用已有 `junction-crosswalk-inset`。第二阶段如果确实需要尺寸参数,新增一个专属 kind 或扩展同一 kind 的严格 schema 校验;具体选择以第一阶段运行结果为准,不预先固化。
缺省参数必须字节级保持现有斑马线输出。参数越界、条带间互相重叠或越过可用进口范围均返回 blocking diagnostic不在客户端静默钳制真实约束。
## 兼容与回滚
- 无新 constraint 时,编译输出与当前版本一致。
- `directEdit` 关闭时不请求 element manifest、不安装元素选择交互、不产生额外图层。
- 第一阶段每个步骤独立提交;回滚元素 UI 或 manifest 不影响既有道路级控制。

View File

@@ -0,0 +1,4 @@
{"file":".trellis/spec/backend/direct-edit-api.md","reason":"元素 manifest、preview 和持久化必须继续满足直接编辑的服务端合同。"}
{"file":".trellis/spec/frontend/direct-edit-client.md","reason":"元素选择、临时控件与表单必须保持 handle/ghost/preview 图层所有权。"}
{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"feature 属性、element manifest、constraint schema 与客户端选择状态跨越编译器、API 和 UI。"}
{"file":".trellis/tasks/08-26-direct-manipulation-road-editor/design.md","reason":"继承 v2 语义约束、坐标分层、预览和 revision 的权威合同。"}

View File

@@ -0,0 +1,49 @@
# 实施计划
## 0. 建立事实与测试夹具
- 追踪 `compileControlMarkings()` 的每条 stripe 属性、`native_id` 构成和从地图点击到 `onFeature` 的路径。
- 添加失败优先的测试:同组 stripe 需要稳定共享 `elementId`,不同 crossing 不得共享;空编辑输出与基线一致。
- 门禁:能证明一个具体 feature 无歧义回链到一个 junction approach 和 crossing。
## 1. 第一阶段:元素 manifest 与选择
- 在编译器为 crosswalk stripe 写稳定的元素标识和回链属性。
- 在 direct-edit 服务响应中增量发布按 selection/element 获取的元素 manifest不扩大现有道路 handle 的职责。
- 客户端建立 `ElementSelection` 纯状态与 feature 点击适配;点击 stripe 选择整组、空白清除。
- 验证编译器单元测试、API 测试、客户端 selection 单元测试。
## 2. 第一阶段:位置编辑闭环
- 用 element manifest 的 position control 渲染元素级控件和选中外框;安装时不得影响道路 handle hit-test。
- 从位置手势和表单走同一个 draft builder写入 `junction-crosswalk-inset`
- 验证:拖拽与表单值生成同形约束;预览/保存/重载一致;`exact` 状态和撤销/重做覆盖。
- 门禁:一次非空编辑可在正式重编译结果中观察到斑马线位置变化。失败即停止,不能只交付 UI。
## 3. 复盘门禁
- 手测选中、取消、道路级手柄共存、拖拽钳位、表单验证、保存重载。
- 只有在第一阶段验收全部通过后,才能为尺寸/外观参数做持久化 schema 决策。
## 4. 第二阶段:尺寸与外观
- 依据第一阶段证据设计并验证长度、宽度、条带数、间距的约束 schema。
- 编译器读取参数、返回 blocking diagnostics元素 manifest 表达对应控件/字段。
- 实现与表单字段完全一致的尺寸控件,补齐组合参数、持久化和 rebase 测试。
## 验证命令
```sh
npm run format:check
npm run test
npm run test:client
npm run test:client:unit
npm run build
```
## 风险文件
- `src/compile/native-road.js`:斑马线生成与默认输出兼容性。
- `src/compile/direct-edit-solver.js` / `native-road-edits.js`约束求解、schema 和 manifest 必须同步。
- `workbench/server.js`element manifest 查询与 preview 不得写入磁盘。
- `workbench/client/src/components/MapCanvas.tsx``App.tsx``edit/`:选择、临时控件和表单不得污染 baseline layer。

View File

@@ -0,0 +1,63 @@
# 斑马线元素级编辑器实验
## 目标
验证“元素级编辑器”能取代只显示固定道路参数手柄的交互:用户点击一组斑马线的任一条带后,整组进入选中状态,地图显示该元素的编辑控件,参数表单与地图操作都能预览、保存和撤销同一份结构化编辑。
斑马线是实验对象,实验通过后才抽象和推广到停止线、箭头、信号灯等元素。
## 已确认的事实
- 当前 `junction-crosswalk-inset` 只改变斑马线相对路口的位置;控制手柄属于被选道路的进口,而非具体斑马线。
- `compileControlMarkings()` 为一组斑马线生成多条 `crosswalk` GeoJSON feature每条 feature 的属性目前不足以作为稳定的组级编辑目标。
- 浏览器已有 `EditSession`、服务端权威 preview、保存、重编译与撤销链路。编辑数据必须保持语义约束不得保存派生 GeoJSON 或屏幕坐标。
- 当前普通道路级手柄仍服务于横断面参数,不应与元素级选择共用或互相覆盖。
## 范围
### 第一阶段:元素选择与位置闭环
1. 点击任意一条斑马线条带,选中整组斑马线;点击空白处或另一道路元素清除或切换选择。
2. 后端为每组斑马线发布稳定 `elementId`、元素类型、语义锚点、可编辑参数与受影响 feature ID客户端不得由 feature 数组位置猜测分组。
3. 选中后显示轻量的元素级编辑状态,至少有位置控制;移动只允许沿该进口定义的合法轴和范围,钳位来自服务端 manifest。
4. 位置控制和参数表单共同更新既有 `junction-crosswalk-inset` 约束,并复用 preview、保存、撤销与重编译链路。
5. 表单显示当前值、单位、合法范围和阻塞性诊断;修改后更新地图预览,保存后重新编译仍为 `exact`
### 第二阶段:尺寸与外观闭环(本任务内设计、第一阶段通过后实施)
1. 选中框提供与斑马线语义相符的长度/宽度调整,不写自由像素缩放。
2. 表单可修改斑马线长度、宽度、条带数量和条带间距;地图控件与表单写同一结构化元素参数。
3. 编译器消费这些参数生成整组条带,并对不合法组合返回阻塞诊断。
## 明确不做
- 不实现通用的任意旋转、自由二维移动、任意缩放或把 transform 矩阵持久化。
- 不编辑多组斑马线,不跨进口/跨路口拖放,不改变 OSM 中心线或路口拓扑。
- 不在本任务中推广停止线、箭头或信号灯;不提前建立“所有元素”的大而全抽象。
- 不移除或改变道路横断面、路口 shape 的现有手柄和所有权。
## 验收标准
- [ ] 点击一条条带后,整组条带显示为同一选中元素;切换到另一组或空白处时状态正确更新。
- [ ] `elementId` 在同一输入、重编译、保存和重新加载后稳定;同组条带共享一个 ID不同斑马线不共享。
- [ ] 移动控件与表单修改位置时,预览只更新该元素及其声明的受影响层;无效输入保留最后一个有效预览并显示诊断。
- [ ] 保存、刷新、重新编译后位置不回退,相关约束状态为 `exact`;未保存编辑可以撤销/重做。
- [ ] 第二阶段完成后,长度/宽度/条带数量/间距能从表单和地图控件一致地更新,非法尺寸或间距被拒绝而非静默修复。
- [ ] 现有道路级手柄仍可独立工作,且未选中斑马线时不显示元素编辑 UI。
- [ ] `npm run format:check``npm run test``npm run test:client``npm run test:client:unit``npm run build` 通过。
## 风险与门槛
- 第一阶段必须先证明 feature -> element -> semantic anchor 的稳定回链,以及非空约束确实改变正式编译输出;两者任一失败时停止,不进入尺寸和外观参数。
- 选中框是交互反馈,不能成为第二个几何模型;所有位置、范围和尺寸的权威值由后端 element manifest 给出。
- 当前地图 feature 的属性可能缺少足以识别 crossing 的稳定字段。需要先通过编译器补充,不可在客户端按经纬度近似聚类。
## Acceptance Criteria
- [ ] TBD
## Notes
- Keep `prd.md` focused on requirements, constraints, and acceptance criteria.
- Lightweight tasks can remain PRD-only.
- For complex tasks, add `design.md` for technical design and `implement.md` for execution planning before `task.py start`.

View File

@@ -0,0 +1,26 @@
{
"id": "crosswalk-element-editor",
"name": "crosswalk-element-editor",
"title": "斑马线元素级编辑器实验",
"description": "",
"status": "planning",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "dingkang",
"assignee": "dingkang",
"createdAt": "2026-08-28",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "08-26-direct-manipulation-road-editor",
"relatedFiles": [],
"notes": "",
"meta": {}
}