# 前端逐元素审计

状态说明：

- 真实可用：接真实状态或后端接口，可以完成主要动作。
- 部分可用：能展示或完成一部分，但存在关键缺口。
- Mock / UI-only：只有展示或本地状态变化，没有真实业务效果。
- 接口不通：前端调用与后端接口不一致，按当前代码大概率失败。

## App 与导航

| 元素 | 位置 | 状态 | 说明 |
|------|------|------|------|
| 登录拦截 | `App.tsx` | 真实可用 | 未登录显示 `Login`，登录后显示主界面 |
| 模块切换 | `Sidebar.tsx` + `App.tsx` | 真实可用 | 切换 `dashboard/projects/workspace/ai/templates`；“AI智能分割”入口使用 Bot + Sparkles 组合图标，强化 AI 语义 |
| Logo | `Sidebar.tsx` | 真实可用 | 侧边栏通过 Vite 资源导入使用根目录 `logo_square.png`；favicon 使用 `/logo_square.png`，前端服务会从项目根目录提供该文件 |
| GPU 状态圆标 | `Sidebar.tsx` | 真实可用 | 通过 `GET /api/ai/models/status` 显示 GPU/CPU 和当前模型可用性 |

## 登录页

| 元素 | 状态 | 说明 |
|------|------|------|
| 用户名/密码输入 | 真实可用 | 默认填入 `admin / 123456`，用户名使用 `autocomplete=username`，密码使用 `autocomplete=current-password` |
| 安全登录按钮 | 真实可用 | 调用 `POST /api/auth/login`，后端校验 `users` 表密码哈希并返回签名 JWT |
| 错误提示 | 真实可用 | 捕获后端错误并显示 |
| 登录态恢复 / 退出 | 真实可用 | 页面刷新后用 `/api/auth/me` 恢复当前用户；侧栏底部显示当前用户名并可退出登录 |
| 安全审计说明文字 | 部分可用 | 登录和用户管理操作已有 `audit_logs` 记录；登录页“端到端加密”等安全文案仍是展示性说明，不代表已接入完整企业级安全审计 |

## 管理员用户后台

| 元素 | 状态 | 说明 |
|------|------|------|
| 侧栏“用户管理”入口 | 真实可用 | 仅当前用户 `role=admin` 时显示；非管理员无法看到入口，后端 `/api/admin/*` 也会返回 403 |
| 用户列表 | 真实可用 | 调用 `GET /api/admin/users`，展示用户 id、用户名、角色、启停用状态和创建时间 |
| 新增用户 | 真实可用 | 调用 `POST /api/admin/users`，支持设置用户名、初始密码和 `admin/annotator/viewer` 角色；后端校验用户名唯一和密码长度 |
| 修改角色 / 启停用 / 改密码 | 真实可用 | 调用 `PATCH /api/admin/users/{id}`；后端禁止管理员把自己降级或停用，避免锁死后台 |
| 删除用户 | 真实可用 | 调用 `DELETE /api/admin/users/{id}`；后端禁止删除自己，且用户名下仍有项目时返回 409，避免悬空项目数据 |
| 审计日志 | 真实可用 | 调用 `GET /api/admin/audit-logs`，展示登录成功/失败、用户新增、修改和删除等管理操作 |
| 恢复演示出厂设置 | 真实可用 | 管理员点击危险区按钮后先浏览器确认，再输入 `RESET_DEMO_FACTORY`；前端调用 `POST /api/admin/demo-factory-reset`，后端只保留默认 admin、演示视频项目和一个已按文件名自然顺序生成帧的演示 DICOM 项目，并清空用户、项目帧、标注、任务和私有模板等演示数据；“腹腔镜胆囊切除术”和“头颈部CT分割”系统模板会按内置默认定义重建或覆盖恢复 |

## Dashboard 系统概况

| 元素 | 状态 | 说明 |
|------|------|------|
| WebSocket 连接状态 | 真实可用 | 前端通过 `src/lib/config.ts` 推导或读取 `VITE_WS_PROGRESS_URL`，后端有 `/ws/progress`；Dashboard 卸载或切页导致的主动断开不会触发自动重连，也不会继续输出“Connection closed”噪音 |
| 任务进度 | 真实可用 | 初始数据来自 `GET /api/dashboard/overview`，按 `processing_tasks` queued/running/success/failed/cancelled 任务生成；统计卡片中的处理中任务数只计算 queued/running |
| 任务取消 | 真实可用 | queued/running 任务显示取消按钮，调用 `POST /api/tasks/{task_id}/cancel` |
| 任务重试 | 真实可用 | failed/cancelled 任务显示重试按钮，调用 `POST /api/tasks/{task_id}/retry` 创建新任务 |
| 失败详情 | 真实可用 | 任务详情按钮调用 `GET /api/tasks/{task_id}`，弹窗展示 error、payload、result、Celery ID 和时间 |
| WebSocket 更新任务 | 真实可用 | Celery worker 更新 `processing_tasks` 后发布 Redis `seg:progress`，FastAPI 广播 progress/complete/error/cancelled |
| 项目、任务、标注、系统负载统计 | 真实可用 | 初始数据来自 `GET /api/dashboard/overview`，系统负载按主机 load average 估算 |
| 近期实时流转记录 | 真实可用 | 初始数据来自任务、项目、标注和模板记录；WebSocket status/complete/error 会继续追加 |

## 项目库 ProjectLibrary

| 元素 | 状态 | 说明 |
|------|------|------|
| 项目列表 | 真实可用 | 调用 `GET /api/projects` |
| 项目卡片缩略图 | 真实可用 | 后端返回 MinIO 预签名 `thumbnail_url` 时显示 |
| 点击项目进入工作区 | 真实可用 | 设置 `currentProject` 后切到 `workspace` |
| 新建项目 | 真实可用 | 调用 `POST /api/projects` |
| 导入视频文件 | 真实可用 | 创建项目、上传源视频、刷新项目列表；不会自动拆帧；上传期间显示项目库导入进度条、百分比和已上传字节 |
| 生成帧按钮 | 真实可用 | 仅对已导入源视频且尚无帧、非 parsing 状态的项目显示，调用 `parseMedia(projectId, { parseFps })` |
| 生成帧 FPS 滑块 | 真实可用 | 值传入 `/api/media/parse?parse_fps=...`，决定后台拆帧目标 FPS |
| 项目卡片 FPS 徽标 | 真实可用 | 右上角显示关键帧序列目标 `parse_fps`；原始视频帧率只在卡片底部以“原 xx fps”显示 |
| 导入 DICOM 序列 | 真实可用 | 可上传 `.dcm` 并触发解析；上传前按文件名自然顺序排序，后端解析也保持同一顺序；上传期间显示导入进度条、有效 DICOM 文件数量和已上传字节，上传完成后继续显示解析任务进度直到完成、失败或取消 |
| 项目状态徽标 | 真实可用 | 项目状态统一为 `pending/parsing/ready/error`，前端兼容归一化旧状态值 |
| 删除项目按钮 | 真实可用 | 点击垃圾桶按钮会确认删除，调用 `DELETE /api/projects/{id}`，成功后从项目库移除；若删除的是当前项目，会清空工作区当前项目、帧、mask 和选区 |
| 操作成功/失败提示 | 真实可用 | 使用非阻塞 `TransientNotice` 浮层，自动消失，不会拦截后续按钮、输入框或画布操作 |

## 工作区 VideoWorkspace

| 元素 | 状态 | 说明 |
|------|------|------|
| 当前项目名 | 真实可用 | 读取 `currentProject.name` |
| 顶栏操作提示 | 真实可用 | 保存、导出、传播范围选择等短反馈会自动消失；保存/导出/传播进行中和无帧项目提示会保留到状态变化 |
| 自动加载项目帧 | 真实可用 | 调用 `GET /api/projects/{id}/frames` |
| 无帧项目提示 | 真实可用 | 如果 `video_path` 存在但无帧，只提示回到项目库生成帧，不自动创建拆帧任务 |
| SAM 模型状态徽标 | 真实可用 | 左侧 Sidebar 底部保留紧凑 GPU/CPU 状态徽标；工作区顶栏不再重复显示该徽标，传播权重下拉和自动传播范围摘要只在进入自动传播后显示 |
| 已保存标注回显 | 真实可用 | 加载工作区帧后调用 `GET /api/ai/annotations` 并渲染已保存 mask；回显时保留当前项目帧里尚未保存的 AI/手工 draft mask，避免从 AI 页推送的候选被覆盖 |
| “分割结果导出”按钮 | 真实可用 | 原“导出 JSON 标注集”和“导出 PNG Mask ZIP”已合并为一个入口；点击后可选择整体视频、特定范围帧或当前图片，默认导出范围为当前图片，并勾选导出分开二值 mask、GT_label 黑白图、Pro_label 彩色图和 Mix_label 原图叠加图；选择“特定范围帧”后会进入时间轴范围选择模式，可在播放进度条或视频处理进度条上点击/拖拽选择导出起止帧，也可直接修改起止帧输入框；选择 Mix_label 时可调透明度，默认 0.3，并显示当前/待导出第一帧预览；提交前会保存未归档 mask，然后调用 `GET /api/export/{project_id}/results` 下载 ZIP；浏览器下载名和后端 `Content-Disposition` 均使用 `{项目库项目名}_seg_T_{起始时间戳}-{结束时间戳}_P_{起始项目帧序号}-{结束项目帧序号}.zip`；时间戳格式为 `0h00m00s000ms`，帧序号来自项目抽帧后的 1-based 顺序，不使用原视频帧号；包内固定包含 `annotations_coco.json`、`maskid_GT像素值_类别映射.json` 和 `原始图片/`；选择分开 mask 时包含按帧子目录组织且同类合并的 `分开Mask分割结果/`，选择 GT_label/Pro_label/Mix_label 时分别包含 `GT_label图/`、`Pro_label彩色分割结果/`、`Mix_label重叠覆盖彩色分割结果/`。GT_label 图固定为 8-bit uint8 PNG，背景为 0，语义类别值使用类别真实 maskid，`maskid: 0` 的“待分类”与背景同为 0，Pro_label 中也与背景同为黑色 `[0,0,0]`，缺失 maskid 的旧标注才补下一个可用正整数，正整数 maskid 超出 1-255 会拒绝导出 |
| “导入 GT Mask”按钮 | 真实可用 | 入口已从工作区顶栏移动到左侧工具栏“重叠区域去除”之后，使用紫色图标底色；选择图片后先弹出导入结果预览和未知 maskid 策略选择，可舍弃未知类别或导入为未定义类别；随后调用 `POST /api/ai/import-gt-mask`，后端仅支持 8-bit 二值/灰度 maskid 图和 8-bit RGB 三通道完全相同的 `[X,X,X]` maskid 图，不符合 8-bit 灰度/maskid 图要求时返回错误，16-bit/uint16 GT_label 会被拒绝；尺寸不同会自动最近邻拉伸到当前帧，再按类别/连通域生成高精度 polygon 标注，最后回显到工作区；导入 mask 与普通 mask 一样不显示黄色 seed point，并共用拓扑锚点统计、边缘平滑、编辑、分类和保存链路 |
| 参考帧/起止帧/传播权重/自动传播 | 真实可用 | 当前打开帧即参考帧，前端会使用该帧全部 mask 作为 seed；左侧工具栏橡皮擦下方有彩色 AI 图标“自动传播”入口，点击后进入时间轴范围选择模式，顶栏才显示独立“传播权重”下拉，可在传播前二次选择 SAM 2.1 tiny/small/base+/large 权重，不提供 SAM2/SAM3 家族切换，不影响 AI 智能分割页的单帧推理权重选择；传播权重下拉使用深色背景和青色文字，避免默认灰底白字不可读；播放进度条和视频处理进度条都可点击/拖拽回填传播起始帧和传播结束帧，顶栏会显示当前传播权重以及相对参考帧的向前/向后帧数，再点击“开始传播”提交；用户也可直接改数字框后点击按钮传播。提交后前端把传播权重 id、seed mask、seed 来源 id、未编辑传播结果的原始 seed 签名和前/后方向步骤提交到 `POST /api/ai/propagate/task`，后端先规范化/校验权重 id，再创建 `processing_tasks` 并由 Celery 执行对应 SAM 2.1 video predictor；同一参考帧多个同类别 seed 会按来源 id 分开传播；worker 会在本次目标帧段内按 seed 来源和几何/语义签名做幂等判断，未改变且目标帧已有结果的 seed 直接跳过，已改变、目标帧只部分覆盖或换权重时会先删除本次目标帧段内同源旧自动传播标注再重新传播；历史或外部 seed 若仍带边缘平滑参数，后端仍按完整签名兼容处理；当前前端平滑应用会直接改写 polygon，因此传播以新几何参与签名；中间帧人工新增/修改同一物体后重新传播时，后端会按语义和目标帧空间重叠清理旧传播结果，写入前清理不受旧结果 `propagation_direction` 限制，避免 backward 重传时与旧 forward mask 重叠；传播中顶栏显示任务进度、已处理帧次、删除旧区域数和已保存区域数，前端轮询 `GET /api/tasks/{task_id}` 并刷新已保存标注；任务可取消，若完成后 0 个新区域会明确提示没有生成新 mask 或已跳过未改变 mask |
| 清空片段遮罩 | 已移除 | 顶栏不再提供重复的“清空片段遮罩”；当前帧清空和 DEL 删除只从左侧工具栏或键盘触发，存在传播链时在同一弹窗提供取消/只清当前帧/按帧范围选择/清空所有传播帧 |
| 保存状态按钮 | 真实可用 | 顶栏按钮按当前项目待保存数量显示为“保存 X 个改动”或“已全部保存”；未保存 mask 写入 `POST /api/ai/annotate`，dirty mask 写入 `PATCH /api/ai/annotations/{id}`；保存成功后会重新拉取后端标注，并用 saved annotation 替换本次提交的 draft mask，避免仍显示未保存 |

## CanvasArea 画布

| 元素 | 状态 | 说明 |
|------|------|------|
| 当前帧底图显示 | 真实可用 | `useImage(frameUrl)` 加载当前帧 URL；切换帧或容器尺寸变化时会按 86% 适配比例居中放大显示，默认留出画布边距，不铺满整个画布 |
| 滚轮缩放 | 真实可用 | 改变 Konva Stage scale |
| 拖拽平移 | 真实可用 | activeTool 为 `move` 时 Stage draggable，拖拽结束会回写 React position state，避免 Konva 节点位置和前端状态脱节 |
| 光标坐标显示 | 真实可用 | 根据 pointer position 计算 |
| 正向/反向选点 | 真实可用 | UI 能加点，并按当前帧 `frame.id` 调用 `/api/ai/predict`；结果需点击归档保存才持久化 |
| 框选 | 真实可用 | UI 能画框，并把框坐标归一化后调用后端推理；结果需点击归档保存才持久化 |
| AI 推理中提示 | 真实可用 | 请求期间会显示 |
| 手工多边形/矩形/圆/画笔/橡皮擦 | 真实可用 | 多边形点击取点后可按 Enter 完成，也可在三点后点击首节点闭合；矩形/圆拖拽生成 polygon；多边形/矩形/圆未选语义分类时自动归入黑色 `maskid:0` 的“待分类”；画笔按当前语义分类生成连续圆形笔触并在松开时 union 成 mask，若与选中 mask 连通则自动合并；橡皮擦从选中 mask 中扣除笔触区域；均写入 `Mask.segmentation`，可归档保存 |
| 画布上下文提示 | 真实可用 | 切换到多边形、矩形、圆、画笔、橡皮擦、区域合并/去除、调整多边形等隐性操作工具时，画布左上角显示当前工具的完成/取消/选择顺序提示；提示会在数秒后自动隐藏，避免长期遮挡待编辑图像，工具或操作状态变化时会重新出现 |
| Mask 渲染 | 真实可用 | 前端会把推理、手工绘制、GT 导入和已保存标注转成 Konva `pathData` 渲染；普通 mask 和导入 mask 都不显示黄色 seed point；未选中特定 mask 时，当前帧 mask 会按右侧“语义分类树”拖拽得到的内部覆盖优先级从低到高渲染，使高优先级类别显示在上层；有选中 mask 时保留编辑态置顶行为，方便操作 |
| Mask 透明度 | 真实可用 | 右侧语义分类树上方的“遮罩透明度”滑杆写入全局 `maskPreviewOpacity`，工作区 Canvas 和 AI 智能分割页都会使用该值调整 mask 预览透明度，选中 mask 会在该基础上略微加亮 |
| 传播链跨帧选区跟随 | 真实可用 | 用户选中某个 mask 后切到同一自动传播结果覆盖的其他帧时，`CanvasArea` 会根据 `source_annotation_id`、`source_mask_id` 和 `propagation_seed_key` 查找目标帧对应传播 mask 并自动选中；找不到同链结果时才清空选区 |
| Polygon 逐点编辑 / 删除 | 真实可用 | 点击 mask 后显示 polygon 顶点；多 polygon 或分离区域组成的同一个 mask 会显示所有子区域顶点，不再只显示主区域；按住顶点即可直接拖动并实时重算 `pathData/segmentation/bbox/area`，不需要先单击选中顶点，已保存 mask 标为 dirty；顶点拖拽结束不会触发 Stage 平移，Canvas 当前缩放和位置保持不变；选中顶点后 Delete/Backspace 可删点但保留至少三点；选中 mask 但未选中顶点时 Delete/Backspace 删除整个 mask，左侧 DEL 按钮复用同一链路；已保存 mask 删除前会预检当前后端 annotation id 并只删除仍存在的 id，避免陈旧本地 id 产生 DELETE 404；若删除对象是传播 seed 或传播结果，前端会按 `source_annotation_id`、`source_mask_id` 和 `propagation_seed_key` 同步删除同链自动传播 mask，但不删除其他帧独立 AI 推理/人工 mask |
| 应用分类 | 真实可用 | Canvas 右下角不再提供“应用分类”快捷按钮，避免没选区时误改整帧；右侧语义分类树点击分类时会优先改当前已选 mask，并通过 `source_annotation_id`、`source_mask_id` 和 `propagation_seed_key` 同步更新同一传播链上的前后传播 mask，同时把已选 mask 移到前端渲染最上层方便继续编辑；已保存 mask 会标为 dirty，归档保存时更新后端 |
| 清空遮罩 | 真实可用 | 工作区只通过左侧工具栏触发清空；当前帧有选中 mask 时清选中 mask，没有选中时清当前帧全部 mask；无传播链结果时直接执行，存在传播链结果时弹窗选择取消、只清当前帧、按帧范围选择或清空所有传播帧；按帧范围选择复用时间轴范围选择和最终确认；按范围清空或清空所有传播帧时若目标范围包含人工/AI 标注帧，会二次确认是否删除，选择否会整帧保留 |
| 保存状态计数 | 真实可用 | 底部显示已保存、未保存、待更新数量 |
| 当前图层信息 | 真实可用 | 根据当前选中 mask 显示真实标签/后端 annotation id；未保存 mask 显示“未保存”，未选中时显示“未选择” |

## ToolsPalette 工具栏

| 元素 | 状态 | 说明 |
|------|------|------|
| 工具分组分隔线 | 真实可用 | 拖拽/选择到创建圆为绘制/基础编辑组，画笔/橡皮擦/自动传播为局部与追踪组，区域合并/重叠区域去除/DEL/清空遮罩为布尔与删除组，导入 GT Mask 和 AI 智能分割为外部动作组；组间使用浅灰横线分隔，`data-testid="tool-group-separator"` 位于清空遮罩下方的外部动作组分隔线 |
| 拖拽/选择 | 真实可用 | 控制 Canvas 是否可拖拽 |
| 调整多边形 | 真实可用 | 选中 polygon mask 后显示顶点和边中点；支持按住顶点直接拖动、点击边中点插点、双击边界按位置插点 |
| 多边形/矩形/圆/画笔/橡皮擦 | 真实可用 | 切换 activeTool 后由 `CanvasArea` 生成或编辑可保存的 polygon mask；画笔/橡皮擦在工具栏显示尺寸滑杆 |
| 区域合并/去除 | 真实可用 | 选择工具后点击多个 mask，右下角显示已选数量和操作按钮；合并/去除模式会隐藏 polygon 编辑手柄，避免手柄抢占多选点击；布尔选择态中第一个选中的主区域用黄色实线轮廓，后续参与合并/扣除的区域用红色虚线轮廓，避免主区域和扣除区域看起来像随机阴影差异；使用 `polygon-clipping` 做 union / difference；若选中的主区域和参与区域存在传播帧对应 mask，会先弹窗选择只处理当前帧、处理所有传播帧或按帧范围选择；按帧范围选择会进入和传播一致的时间轴范围选择，点击顶栏确认后再弹最终确认，只处理范围内存在对应传播链的帧；合并会保留主 mask 并移除被合并 mask，且移除次级 mask 时会同步删除其同链自动传播结果；去除会从主 mask 扣除后续选中 mask；内含扣除会保留 hole ring 并用 even-odd 规则渲染 |
| 导入 GT Mask | 真实可用 | 位于“重叠区域去除”之后，点击后打开文件选择器，并在上传前选择未知类别处理策略；该入口不切换 activeTool |
| AI 智能分割跳转入口 | 真实可用 | 切到 AI 智能分割页；不是直接执行推理 |
| AI 正向选点/反向选点/框选 | 不在工作区工具栏显示 | 这些是 AI 智能分割页功能，工作区左侧工具栏不再提供正向选点、反向选点和边界框选按钮 |
| AI 智能分割入口 | 真实可用 | 位于工作区工具栏底部，使用和侧栏一致的 Bot + Sparkles 组合图标；点击后切到 AI 智能分割页 |
| 撤销/重做 | 真实可用 | 绑定 Zustand `maskHistory/maskFuture`，工作区只保留顶栏按钮和快捷键 `Ctrl/Cmd+Z`、`Ctrl/Cmd+Shift+Z`、`Ctrl/Cmd+Y`，AI 页保留自己的按钮；左侧工具栏不再重复放置撤销/重做；输入框聚焦时不拦截快捷键；工作区顶栏撤销图标使用琥珀色、重做图标使用蓝紫色，提高深色顶栏里的识别度 |
| 紧凑/滚动布局 | 真实可用 | 工具按钮使用较紧凑的垂直间距；左侧高度不足时工具栏自身出现纵向滚动，不挤压画布；外层工具栏扩展到 56px，按钮列仍固定 48px，滚动条占用右侧外扩空间，不挤占图标位置；滚动条使用 `seg-scrollbar`，默认低对比融入深色工具区，hover/focus 时才增强为青色提示 |

## FrameTimeline 时间轴

| 元素 | 状态 | 说明 |
|------|------|------|
| 帧缩略图 | 真实可用 | 使用 `frames[].url` |
| 点击缩略图跳帧 | 真实可用 | 调用 `setCurrentFrame(idx)`；非当前帧中，人工/AI 标注帧使用红色边框，自动传播/推理帧使用蓝色边框；同一帧同时有人工/AI 标注和自动传播结果时，红色标注边框优先保留，蓝色传播状态以内描边表达；当前帧仍用青色外框高亮优先，若当前帧同时是人工/AI 标注帧，则在青色外框内增加红色内描边，固定为外层当前帧、内层人工/AI 标注，避免状态颜色互相覆盖 |
| 顶部 range 拖动 | 真实可用 | 改变当前帧 |
| 具体时间显示 | 真实可用 | 根据项目 `parse_fps/original_fps` 显示当前时间和总时长，格式为 `mm:ss.cc` |
| 播放进度条 / 视频处理进度条 | 真实可用 | 播放进度条位于上方，视频处理进度条位于下方；当前帧位置用一条白色竖线贯穿两条进度条，避免和青色播放进度、红/蓝处理状态混淆；视频处理进度条普通状态下可点击跳转到对应帧；根据已保存标注回显的 `mask_data.source`、`propagated_from_frame_id`、`source_annotation_id`、`source_mask_id` 或 `propagation_seed_key` 识别自动传播生成的帧并显示蓝色区段，人工绘制或 AI 智能分割生成的帧显示红色竖线，红/蓝标识也可点击跳转到对应帧；每次自动传播成功处理帧后，工作区会在当前会话记录最近传播范围，并在视频处理进度条上叠加同一蓝色系的纯色片段，按距最新传播的时间顺序逐次变暗，且第 5 次及更早统一为阈值旧记录色，辅助识别第一次、第二次、第 N 次传播；传播历史片段会按当前仍存在的自动传播 mask 自动裁剪或拆分，单独删除传播 mask 后，无任何 mask 的帧不会继续显示红/蓝颜色；未处理背景使用中性灰以和红/蓝/传播历史标记区分；工作区进入自动传播或布尔操作的范围选择模式时，两条进度条显示 amber 选区，并额外用洋红色起始线和黄绿色结束线贯穿两条进度条，表示待处理起止帧，颜色避开附近的青色、红色、蓝色和 amber 元素 |
| 播放/暂停 | 真实可用 | 当前代码按 `parse_fps/original_fps` 推进帧，最多 30fps |
| 方向键切帧 | 真实可用 | 全局监听左右方向键切到上一帧/下一帧；焦点在 input、textarea、select 或 contentEditable 内时不会拦截 |

## OntologyInspector 本体面板

| 元素 | 状态 | 说明 |
|------|------|------|
| 模板选择 | 真实可用 | 读取全局 templates，可切换 activeTemplateId，并会驱动分类树、mask 分类和导出类别信息 |
| 面板滚动条 | 真实可用 | 右侧本体/语义分类面板内容过长时自身滚动；滚动条使用 `seg-scrollbar`，默认低对比融入深色侧栏，hover/focus 时才增强显示 |
| 面板标题 | 已简化 | 原“本体论与属性分类管理树”固定说明栏已移除，右侧面板直接展示模板、透明度和语义分类树 |
| 分类树展示 / 换标签 | 真实可用 | 显示当前模板 classes；点击分类会设为后续新 mask 的 activeClass，如果 Canvas 已选 mask，则同步更新已选 mask 及同一传播链前后帧对应 mask 的标签、颜色和 class 元数据，并把已选 mask 移到前端渲染最上层；当用户在 Canvas 点击已有 mask 时，本面板会按 mask 的 class id / 名称自动切换模板、设置 active class，并滚动/聚焦到对应分类按钮 |
| 添加自定义分类 | 真实可用 | 需要先选择模板；新增分类通过 `PATCH /api/templates/{id}` 写入后端模板 `mapping_rules.classes`，并同步全局模板 store |
| 目标实例属性标题 | 真实可用 | “特定目标实例属性追踪”下方显示当前选中 mask 的 `className/label`，不再跟随全局 active class，避免点过其他分类后标题固定成旧分类 |
| 当前选中区域计数 | 已移除 | 当前交互以单选 mask 为主，计数长期为 1，属于低价值信息，已从实例属性面板删除 |
| 后端拓扑锚点数量 | 真实可用 | 选中 mask 后调用 `POST /api/ai/analyze-mask`，后端按 polygon 的真实顶点数量返回 `topology_anchor_count`；`topology_anchors` 列表只保留最多 64 个抽样点用于调试展示，避免把真实数量误压成十几个；前端会忽略被浏览器中止或已过期的分析请求，避免切换 mask、拖动平滑预览或卸载组件时出现误报 |
| 边缘平滑强度 / 应用边缘平滑 | 真实可用 | 选中 mask 后调整 0-100 平滑强度会先即时更新滑杆数值，再在用户停止拖动约 220ms 后调用 `POST /api/ai/smooth-mask` 生成预览 polygon，避免拖动时连续请求导致卡顿；预览会临时替换当前 mask 显示但不标 dirty；点“应用边缘平滑”后会把平滑 polygon 作为新的实际 mask 几何写入当前 mask 和同传播链前/后对应 mask，整次应用进入同一个撤销/重做历史步骤，并把相关 mask 标记为 dirty/draft；传播链上的 mask 保存时会保留原传播 lineage metadata，不会因为平滑几何同步而在时间轴上变成人工/AI 红色标注帧；应用后平滑强度重置为 0，后续可继续用“调整多边形”编辑新的 polygon；后端平滑使用缓入强度曲线，低强度只做温和切角和轻量去噪，高强度才逐步增加 Chaikin 迭代、切角比例和简化阈值，避免 20% 前后已经过度平滑 |

## AISegmentation 独立 AI 页

| 元素 | 状态 | 说明 |
|------|------|------|
| SAM 2.1 变体选择 / 模型状态 | 真实可用 | AI 页可选 tiny/small/base+/large，调用 `GET /api/ai/models/status?selected_model=<variant>` 展示所选变体和 GPU 状态；只有本地存在 checkpoint 的变体显示可用 |
| 正向/反向点 | 真实可用 | 可在当前项目帧上加点并调用 AI 推理接口；AI 页中点击已有候选 mask 时也会继续添加当前正/反向提示点，点击已有提示点会删除该点；SAM 2.1 框选后会携带原始框和累计正/反点细化同一个候选 mask |
| 边界框选 | 真实可用 | AI 页选择工具后可在画布拖拽蓝色虚线框；执行分割时会随 `/api/ai/predict` 发送 `box`，框选后继续添加正/反点会发送 interactive prompt |
| AI 画布上下文提示 | 真实可用 | 选择正向点、反向点、边界框选或视口控制时，画布左上角提示点击/拖拽、删除提示点和执行推理的操作方式 |
| SAM 3 入口 | 当前禁用 | 因当前系统不提供文本提示，前端不再显示 SAM 3 模型选择、文本输入或 SAM 3 框选入口；后端 `model=sam3` 返回不支持 |
| 语义文本输入 | 当前禁用 | AI 页不再提供文本语义输入；后端收到 `semantic` prompt 会返回 400 |
| 参数开关 | 真实可用 | UI 展示为“局部专注模式（自动裁剪无锚区域）”和“严格除杂模式（自动清理干涉点）”，只是为了让用户更容易理解，不重命名内部字段；`cropMode` 会随 `/api/ai/predict` 发送 `crop_to_prompt`，后端对点/框 prompt 裁剪推理区域并回映射 polygon；`autoDeleteBg` 会发送 `auto_filter_background` 和 `min_score`，后端过滤低分结果和覆盖负向点的结果 |
| AI 遮罩透明度 | 真实可用 | 调节共享的 `maskPreviewOpacity`，AI 页候选 mask 和右侧“遮罩透明度”滑杆联动，只影响预览显示，不改变 mask 几何、分类或保存数据 |
| 执行高精度语义分割 | 真实可用 | 使用当前项目帧和所选 SAM 2.1 变体调用 `/api/ai/predict`；SAM 2.1 需要点/框提示且只采用最高分候选；AI 页只渲染本页最新候选，不显示工作区已有 mask，重复执行会替换上一次 AI 页候选而不是叠加；生成结果写入全局 masks 并自动选中，右侧分类树可立即换标签 |
| 推送至工作区编辑 | 真实可用 | 切回工作区并把工具切到“调整多边形”，保留 AI 页选中的未保存 mask 和当前帧视角；推送前会校验当前 AI 候选 mask 必须已有 `classId` 或 `className`，未选择语义分类时会用右上角 error toast 提示用户先点右侧语义分类树，不允许进入工作区；如果用户直接离开 AI 页，未分类 AI 候选会被清理，避免无语义 mask 进入工作区；工作区回显后端标注时不会覆盖这类 draft mask，也不会强制跳回第一帧 |
| 撤销/重做 | 真实可用 | 绑定全局 mask 历史栈 |
| 删除最近锚点 | 真实可用 | 删除 AI 页最近一次放置的正/反向提示点，不影响已生成候选 mask 或工作区 mask |
| 删除选中候选 | 真实可用 | 删除 AI 页当前选中的本页候选 mask；不会删除工作区已有 mask，Delete/Backspace 也遵循同一范围 |
| 清空全体锚点 | 真实可用 | 清空 AI 页提示点和本页生成的候选 mask，不删除工作区已有 mask |
| 背景图 / 空状态 | 真实可用 | 优先显示当前项目帧；没有项目帧时显示空状态提示，不再回退到外部演示图片 |
| AI 画布初始视图 | 真实可用 | 当前帧在 AI 画布中默认居中，并按 86% 适配比例尽量放大但保留边距 |

## TemplateRegistry 模板库

| 元素 | 状态 | 说明 |
|------|------|------|
| 模板列表 | 真实可用 | 调用 `GET /api/templates` |
| 新建方案 | 真实可用 | 调用 `POST /api/templates` |
| 编辑模板 | 真实可用 | 调用 `PATCH /api/templates/{id}` |
| 删除模板 | 真实可用 | 调用 `DELETE /api/templates/{id}` |
| 添加/删除分类 | 真实可用 | 保存在模板 `mapping_rules.classes` |
| 拖拽排序 | 真实可用 | 模板库详情页、模板编辑弹窗和工作区右侧语义分类树都可拖拽调整内部覆盖优先级，保存时写后端；模板库详情页拖拽会刷新当前详情并同步当前工作区同类 mask 的 `classZIndex`，工作区拖拽也会同步当前同类 mask 的 `classZIndex` 并标记待保存；界面只显示类别稳定 maskid，maskid 不作为排序规范；黑色 `maskid: 0` 的“待分类”保留类固定在最后，不可删除或拖拽上移 |
| JSON 批量导入 | 真实可用 | 前端解析 `[[colors], [names]]` 和 `{colors, names}` 两种格式，并兼容带前缀、代码块、未加引号 keys、单引号、中文逗号/冒号和尾随逗号的粘贴内容；显示导入数量、maskid 起点和缺失颜色提示；导入后加入编辑态，保存模板时落库 |
| mapping rules | 部分可用 | 可存 `rules`，但当前没有运行时映射执行引擎；适合后续用于导入外部标签、别名归一化或跨数据集类别映射 |

## 总体结论

当前前端真实可用的主链路是：JWT 登录、刷新恢复用户、退出登录、Dashboard 当前用户概览、当前用户项目列表、新建项目、上传视频/DICOM、显式生成帧、浏览帧、播放帧、工作区手工绘制、点/框 AI 推理、视频片段传播、GT mask 导入、标注保存/回显、统一分割结果 ZIP 导出、兼容 COCO/PNG mask ZIP 导出、模板 CRUD。

当前最主要的 Mock 或未打通链路是：真正的文本语义分割已因无文本提示入口而暂时禁用；复杂洞结构编辑、骨架/HDBSCAN 级别的 mask 降维增强、任务历史筛选、项目更多菜单、全业务操作审计和 mapping rules 运行时映射执行引擎仍未落地。登录页“端到端加密”等安全文案仍只是 UI 文案；登录和用户管理操作审计已落库并可在管理员后台查看。
