# 4-37 [done] 垃圾箱改为居中弹窗工作台 v1

> 更新时间：2026-05-15
>
> 背景：
> - 用户建议：垃圾箱应做成弹窗，而不是单一页面。
> - 当前 `/trash` 是完整页面入口；在本地文件夹 / 云空间切换异常时，用户会通过垃圾箱“找回”原空间页面，这进一步暴露垃圾箱作为导航页面会改变当前工作区上下文。

## 1. 设计目标

把垃圾箱从“离开当前工作区的独立页面”逐步改为“当前工作区内的居中弹窗工作台”。

弹窗形态以 Wolai 居中对话框为准：遮罩覆盖页面，工作台面板在视口中心显示，左右和上下都保留留白；不要做成靠右侧贴边的 drawer / 抽屉。

目标体验：

- 用户点击左下角“垃圾箱”时，不离开当前页面和当前文件树上下文。
- 垃圾箱以居中 modal 打开，不贴右侧边缘，不改变当前 URL。
- 弹窗内展示页面垃圾箱与资源垃圾箱。
- 恢复、彻底删除、清空操作完成后，当前 Sidebar / File Tree / Page Tree 实时同步。
- 关闭弹窗后，用户回到原页面、原选中 row、原滚动位置。

## 2. 非目标

- 本稿不改变删除、恢复、彻底删除的底层命令语义。
- 本稿不一次性废弃 `/trash`；`/trash` 可作为兼容、直接链接和测试入口保留。
- 本稿不扩大到全局搜索、模板中心等其它 footer 入口。

## 3. 最小实现切片

P0：入口形态

- 左下角“垃圾箱”从普通导航改为打开弹窗。
- 弹窗复用现有 `mnote-trash-workbench` DOM 与 API。
- 打开弹窗不改变当前 URL；如需要可用 `?trash=1` 或 history state，但默认不跳整页。

P1：上下文保持

- 弹窗打开前记录当前 `sourceKind`、`workspaceId`、`rootUri`、active row、scrollTop。
- 关闭弹窗后恢复原 File Tree / Page Tree 视图状态。
- 本地文件夹场景下，垃圾箱只展示本地 `.mnote/trash` 能力；Convex 云空间展示 Convex trash。

P2：实时同步

- 页面 restore / purge / empty 后，当前 Sidebar 通过 tree event 或本地 apply 更新。
- 资源 restore / purge / empty 后，File Tree 对应资源 row 同步恢复或消失。

## 4. 验收标准

- 点击“垃圾箱”不会离开当前文档 URL。
- 垃圾箱面板在视口中心显示，左右居中偏差不超过 8px，并保留可见的上下留白。
- 弹窗内能恢复页面、彻底删除页面、清空页面垃圾箱。
- 弹窗内能恢复/彻底删除/清空附件、mindmap、table 等资源。
- 关闭弹窗后，原页面仍打开，File Tree 滚动位置和选中 row 不被无关重置。
- `/trash` 兼容入口仍可访问并通过现有 smoke。

## 5. 当前状态

当前状态：`done`

2026-05-15 已完成最小可用实现：

- 左下角“垃圾箱”入口保留 `href="/trash"` 作为兼容 fallback，但主壳点击会拦截为 `data-mnote-action="open-trash-modal"`。
- `PageLayout` 新增垃圾箱 modal：打开后 fetch `/trash?workspaceId=...`，解析并挂载现有 `mnote-trash-workbench`，不改变当前 URL。
- modal 关闭时会关闭 workbench EventSource，并恢复 File Tree `scrollTop`；不改变原页面、active row 或当前文档 URL。
- `/trash` 独立页面继续保留，作为直接链接、兼容入口和现有 smoke 的验证对象。
- 本地文件夹上下文下，modal 保持在本地 source，不跳回 Convex；当前先展示 `.mnote/trash` / `trash-index.json` 的本地垃圾箱说明，后续如需完整 local trash list 可继续扩展。
- 2026-05-15 根据用户参考图修正 modal 形态：从右侧 drawer 改为居中对话框，并在 smoke 中增加居中与上下留白断言，避免后续重复退回侧栏抽屉形态。

代码落点：

- `rust/crates/mnote-web/src/workspace_shell.rs`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- `rust/crates/mnote-web/src/ssr/styles.rs`
- `rust/crates/mnote-web/src/routes/gateway.rs`
- `scripts/task442-trash-modal-workbench-smoke.js`

已通过验证：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web workspace_shell -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web trash_entry -- --nocapture
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task442-trash-modal-workbench-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js
```

结果文件：

- `tmp/task442-trash-modal-workbench-smoke/result.json`
- `tmp/task432-filetree-trash-page-dual-browser-no-refresh-smoke/result.json`
