chore: align mvp design governance
- 统一 local-first MVP 后阶段架构口径,补充 process 执行总序和 Reasonix 协作记录 - 归档已完成的 design checklist,标注参考型 process,更新 AGENTS/REASONIX/架构文档 - 补充文件树/主编辑器下载与上下文菜单相关实现、bug 记录和 smoke 脚本 验证:git diff --check;codegraph sync .;cargo test -p mnote-web;node --check scripts/task476-filetree-editor-context-menu-download-smoke.js
This commit is contained in:
+2
@@ -2,6 +2,8 @@
|
||||
|
||||
> 更新时间:2026-05-19
|
||||
>
|
||||
> 当前状态:`process-reference`。本文是参考实现采用矩阵,不是当前执行 checklist;具体实现任务应拆到对应 domain 的 process/done 文档。
|
||||
>
|
||||
> 关联背景:
|
||||
> - `/mnt/Data1T/mnote/CURRENT_ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||||
|
||||
-135
@@ -1,135 +0,0 @@
|
||||
# 5-15 本地 Markdown 路径身份统一 Checklist v1
|
||||
|
||||
## 背景
|
||||
|
||||
当前本地 Markdown 主链正在对齐 VS Code 文件模型:普通 `.md` 文件的运行时身份应来自文件资源地址,而不是 frontmatter 或 MNote 内部稳定 ID。
|
||||
|
||||
VS Code 参考:
|
||||
|
||||
- `design/05-editor-mainline/reference-code/vscode/src/vs/platform/files/common/fileService.ts`
|
||||
- `createFile(resource)` 以 `URI` 创建文件。
|
||||
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files/browser/fileCommands.ts`
|
||||
- 新建文件后 `openEditor({ resource: saveUri })`。
|
||||
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts`
|
||||
- `matches()` 使用 `isEqual(otherInput.resource, this.resource)`。
|
||||
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files/browser/editors/fileEditorHandler.ts`
|
||||
- working copy 与 editor 是否同一打开项,比较 `workingCopy.resource` 与 `editor.resource`。
|
||||
|
||||
本阶段目标不是引入新的稳定语义 ID,而是先消除普通本地 Markdown runtime 中的多 ID 竞争。
|
||||
|
||||
## 身份原则
|
||||
|
||||
- 普通本地 Markdown 的真实身份是 `sourceKind + rootUri + relativePath`。
|
||||
- 对外 `documentId` 继续使用路径型短句柄:`local-md:<encoded relative_path>`。
|
||||
- `mnote_id` 只作为已有 frontmatter 元数据保留,不参与普通本地 Markdown 的 `documentId` 决策。
|
||||
- `.mnote/page-ids.json` 只允许作为历史兼容/迁移辅助,不参与普通本地 Markdown 的主身份生成。
|
||||
- 不主动修改、删除或重写用户 Markdown frontmatter。
|
||||
|
||||
## 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/local_search_index.rs`
|
||||
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
|
||||
- 必要时更新直接相关测试:
|
||||
- `rust/crates/mnote-web/src/routes/search.rs`
|
||||
- `rust/crates/mnote-web/src/routes/tree.rs`
|
||||
- `rust/crates/mnote-web/src/routes/documents.rs`
|
||||
|
||||
不要修改用户数据目录,不要清理仓库中无关脏文件,不要做 git commit。
|
||||
|
||||
## P0 Checklist
|
||||
|
||||
### 1. 搜索索引 documentId 与树形列表统一
|
||||
|
||||
- [ ] `local_search_index.rs` 中 `index_markdown_file()` 永远按 `relative_path` 生成 `local-md:<encoded relative_path>`。
|
||||
- [ ] frontmatter `mnote_id` 不再生成 `local-mdid:*`。
|
||||
- [ ] 搜索结果、最近修改、反链接口返回的 Markdown `documentId` 与文件树一致。
|
||||
- [ ] 含 `mnote_id` 的 Markdown 搜索测试必须断言仍返回路径型 ID,例如 `local-md:docs~2Fchild.md`。
|
||||
|
||||
验收:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_index -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web search_local_index -- --nocapture
|
||||
```
|
||||
|
||||
### 2. 清理本地 Markdown 身份生成的误导参数
|
||||
|
||||
- [ ] `local_markdown_page_id(relative_path, _mnote_id, _metadata)` 不再保留无用参数签名。
|
||||
- [ ] 普通调用点直接使用 `local_markdown_path_page_id(relative_path)` 或新的清晰命名函数。
|
||||
- [ ] 如果必须保留 legacy resolver,函数名必须包含 `legacy` / `compat`,并且不能参与普通本地 Markdown 主链。
|
||||
|
||||
验收:
|
||||
|
||||
```bash
|
||||
rg -n "local_markdown_page_id\\(|local-mdid|mnote_id|page-ids" rust/crates/mnote-web/src/routes/local_folder_source.rs rust/crates/mnote-web/src/routes/local_search_index.rs
|
||||
```
|
||||
|
||||
期望:
|
||||
|
||||
- `local_markdown_page_id(` 不应再出现在普通主链调用中。
|
||||
- `local_search_index.rs` 不应再出现 `local-mdid` 生成逻辑。
|
||||
- `mnote_id` 可以继续出现在解析、兼容测试或“保留 frontmatter”测试中,但不能参与普通 `documentId` 生成。
|
||||
|
||||
### 3. 不污染用户文件
|
||||
|
||||
- [ ] 新建本地 Markdown 不写 `mnote_id` frontmatter。
|
||||
- [ ] 复制本地 Markdown 不改写 `mnote_id`。
|
||||
- [ ] 保存已有 Markdown 时保留原 frontmatter,但不新增身份字段。
|
||||
|
||||
验收:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_tree_command_local_folder_lifecycle -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_aggregate_accepts_path_id_when_frontmatter_has_mnote_id -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_save -- --nocapture
|
||||
```
|
||||
|
||||
## P1 Checklist
|
||||
|
||||
### 4. 路径型身份覆盖打开/保存/搜索一致性
|
||||
|
||||
- [ ] 同一个含 `mnote_id` 的 `.md` 文件,在文件树、页面 aggregate、搜索索引中返回同一个 `local-md:<encoded relative_path>`。
|
||||
- [ ] `documents.content.get` / 页面打开不再需要 `local-mdid:*` 才能命中文件。
|
||||
- [ ] rename/move 后新路径返回新的路径型 `documentId`,旧路径型 ID 不作为稳定页面 ID 保留。
|
||||
|
||||
验收:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_identity_uses_path_even_when_frontmatter_has_mnote_id -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_aggregate_accepts_path_id_when_frontmatter_has_mnote_id -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_lifecycle -- --nocapture
|
||||
```
|
||||
|
||||
## 最终验收
|
||||
|
||||
```bash
|
||||
cargo fmt --check --all --manifest-path rust/Cargo.toml
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_index -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web search_local_index -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_identity_uses_path_even_when_frontmatter_has_mnote_id -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_aggregate_accepts_path_id_when_frontmatter_has_mnote_id -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_lifecycle -- --nocapture
|
||||
```
|
||||
|
||||
若涉及浏览器可见行为,Codex 复核阶段必须使用真实浏览器验证并将截图放在 `tmp/` 下。
|
||||
|
||||
## 执行状态(2026-05-19)
|
||||
|
||||
- [x] P0-1 搜索索引 `documentId` 已统一为 `local-md:<encoded relative_path>`,不再由 frontmatter `mnote_id` 生成 `local-mdid:*`。
|
||||
- [x] P0-2 普通本地 Markdown 主链已移除 `local_markdown_page_id(relative_path, _mnote_id, _metadata)` 误导签名,文件树、page aggregate、find-by-id 改用路径型 ID。
|
||||
- [x] P0-3 新建、复制、rename/move/restore/purge 普通本地 Markdown 不再写 `.mnote/page-ids.json`,也不再写入或改写 `mnote_id`。
|
||||
- [x] P1-4 rename/move 后返回新路径型 `documentId`,旧 ID 不再作为稳定页面 ID 保留。
|
||||
- [x] 追加清理:删除 `ParsedLocalMarkdownPage.mnote_id` 字段,避免后续代码重新把 frontmatter `mnote_id` 当身份入口。
|
||||
|
||||
已通过验收:
|
||||
|
||||
```bash
|
||||
cargo fmt --check --all --manifest-path rust/Cargo.toml
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_index -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web search_local_index -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_identity_uses_path_even_when_frontmatter_has_mnote_id -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_aggregate_accepts_path_id_when_frontmatter_has_mnote_id -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_id_initialization_returns_path_id_without_writing_page_ids -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder -- --nocapture
|
||||
```
|
||||
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
# 5-16 Editor Group 与 Resource Tab 安全收口 Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:`mnote-web` 主编辑区 runtime / resource tab
|
||||
|
||||
## 1. 目标
|
||||
|
||||
把当前浏览器内存版 resource tab 收口为轻量 editor group 模型的第一阶段:不引入完整 VSCode 多 group,不改变 Page Aggregate 事实源,只解决打开、激活、关闭和脏资源防护的最小闭环。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/pages/document.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/styles.rs`
|
||||
- `scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- 可新增一个聚焦 smoke,例如 `scripts/task460-resource-tab-close-dirty-smoke.js`
|
||||
|
||||
## 3. Checklist
|
||||
|
||||
- [ ] 定义轻量 `MainEditorTabEntry` / `mainEditorTabRegistry` 语义,至少包含 `id`、`kind`、`title`、`dirty`、`saving`、`hasExternalConflict`、`lastActiveAt`。
|
||||
- [x] 保持 page tab 为固定 tab,但 resource tab 激活/关闭应走同一套 registry 状态更新。
|
||||
- [x] `closeResourceTab` 关闭前检查 resource session:
|
||||
- [x] `saving=true` 时禁止直接关闭或等待保存完成。
|
||||
- [x] `hasExternalConflict=true` 时禁止静默关闭。
|
||||
- [x] `dirty=true` 时触发保存或确认流程;不得直接释放 session。
|
||||
- [x] 关闭 active resource tab 后按 MRU 回到上一个 resource tab;没有可用 resource tab 时才回到 page tab。
|
||||
- [x] resource tab 激活时更新稳定 `aria-selected`、`tabindex` 和 panel hidden 状态。
|
||||
- [ ] 打开失败时保留 error placeholder tab,允许用户关闭或重试,不要留下半注册状态。
|
||||
|
||||
## 4. 验收
|
||||
|
||||
- [x] 打开两个 resource tab,切到第二个并关闭后,应回到第一个,而不是无条件回 page tab。
|
||||
- [x] 编辑 resource markdown 后立即点关闭,不应静默丢弃未保存内容。
|
||||
- [x] 保存中关闭不会释放 editor session。
|
||||
- [ ] 打开失败的 resource tab 有明确错误占位,并可关闭。
|
||||
- [x] 现有 `task457-main-editor-resource-tab-smoke.js` 继续通过。
|
||||
- [x] 新增 close dirty / MRU smoke 通过。
|
||||
|
||||
## 4.1 本轮执行记录
|
||||
|
||||
- 已合入:resource tab MRU、关闭 guard、`aria-selected` / `tabindex` 同步、关闭后回退到上一个 resource tab。
|
||||
- 已新增:`scripts/task460-resource-tab-close-dirty-smoke.js` 覆盖近期本地输入后的关闭阻止。
|
||||
- 未完成:正式 `MainEditorTabEntry` 模型、打开失败 placeholder tab。
|
||||
|
||||
## 5. 非目标
|
||||
|
||||
- 不实现完整 VSCode preview / pinned / sticky / drag reorder。
|
||||
- 不实现 tab 持久化恢复。
|
||||
- 不改变 secondary pane 布局。
|
||||
- 不把 `resourceTabRegistry` 移到后端持久状态。
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
# 5-17 Resource Editor Kind 与 Smoke 矩阵 Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:资源打开 resolver / Office / Markdown/Text/Code/Image/PDF smoke
|
||||
|
||||
## 1. 目标
|
||||
|
||||
把资源类型识别和打开策略从分散 if/else 收口为轻量 resolver,并补齐当前缺失的类型 smoke。默认目标是主编辑区 resource tab;右侧边栏和新窗口必须作为显式目标保留。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_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/onlyoffice.rs`
|
||||
- `scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- `scripts/task459-local-markdown-attachment-tab-smoke.js`
|
||||
- 可新增 resource kind smoke
|
||||
|
||||
## 3. Checklist
|
||||
|
||||
- [ ] 定义轻量 resource editor resolver:
|
||||
- [ ] 输入:`name`、`mimeType`、`href`、`assetId`、`rootUri`、`path`、`openTarget`。
|
||||
- [ ] 输出:`editorKind`、`badgeKind`、`defaultOpenMode`、`editable`、`viewerUrl`。
|
||||
- [x] `.md` / `.markdown` / `.txt` / 常见 code 文件继续使用 tiptap resource editor。
|
||||
- [ ] Office 文件默认在主编辑区 tab 内 iframe 打开,显式新窗口仍可打开。
|
||||
- [x] PDF 使用独立 `pdf` badge kind,不再复用 ppt。
|
||||
- [x] image 使用 image badge kind 和 img viewer。
|
||||
- [x] unknown file 有明确 fallback:下载/新窗口/错误占位,不应无响应。
|
||||
- [ ] `active-tab`、`side`、`new-window` 三个 target 在同一 resolver 后执行,不再散落在 attachment capture 与 filetree click 中。
|
||||
|
||||
## 4. Smoke 矩阵
|
||||
|
||||
- [x] `.md` 上传/文件树点击 -> 主编辑区 tiptap tab,可编辑并写回资源文件。
|
||||
- [x] `.txt` -> 主编辑区 tiptap tab,可编辑并写回。
|
||||
- [x] `.json` 或 `.ts` -> 主编辑区 tiptap/code-like tab,可编辑并写回。
|
||||
- [ ] `.docx` -> 主编辑区 Office iframe,DocumentServer 可回源。
|
||||
- [ ] `.pptx` / `.xlsx` -> badge 颜色分别正确。
|
||||
- [x] `.pdf` -> 主编辑区 iframe,badge 为 pdf 独立颜色。
|
||||
- [x] image -> 主编辑区 img viewer。
|
||||
- [x] unknown binary -> 明确 fallback,不破坏 page tab 切换。
|
||||
|
||||
## 4.1 本轮执行记录
|
||||
|
||||
- 已合入:PDF 独立 badge kind、file fallback badge、resource kind smoke 覆盖 markdown/text/code/image/pdf 与未知类型不破坏 tab 回退。
|
||||
- 已验证:`scripts/task461-resource-kind-smoke.js` 与 `uploaded_attachment_rows_use_colored_file_badges` 通过。
|
||||
- 未完成:正式 resource editor resolver 抽象、Office DocumentServer 回源修复、`active-tab` / `side` / `new-window` 统一 resolver 后执行。
|
||||
|
||||
## 5. 非目标
|
||||
|
||||
- 不引入 Monaco / CodeMirror。
|
||||
- 不实现云端资源写回。
|
||||
- 不重构 OnlyOffice DocumentServer 部署。
|
||||
- 不改变 `.md` 资源仍用 tiptap 编辑的产品口径。
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
# 5-18 Main Editor Tab State 与错误占位 Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:主编辑区 tab runtime / `web_shell.rs`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P0-1 的最小轻量 editor group 状态:在不引入完整 VSCode 多 group 的前提下,让 page/resource tab 共享一个明确的浏览器内存状态模型,并补上 resource 打开失败时的错误占位 tab。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/pages/document.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/styles.rs`
|
||||
- `scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- 可新增 `scripts/task462-resource-tab-error-placeholder-smoke.js`
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不引入完整 VSCode DI、多 editor group grid、preview/sticky 持久化。
|
||||
- 不改变 Page Aggregate 或 local Markdown 事实源。
|
||||
- 不修改 resource resolver、OnlyOffice、File Tree lifecycle;这些由其它 checklist 处理。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] 定义轻量 main editor tab 状态,至少能表达 `id`、`kind`、`title`、`dirty`、`saving`、`hasExternalConflict`、`lastActiveAt`、`status`。
|
||||
- [x] page tab 与 resource tab 激活统一通过同一状态更新入口。
|
||||
- [x] resource tab close guard 读取同一状态入口,避免散落检查。
|
||||
- [x] 打开 resource 失败时创建或保留 error placeholder tab。
|
||||
- [x] error placeholder tab 有明确错误 UI,可关闭,不留下半注册 panel/session。
|
||||
- [x] 更新 Rust 字符串 contract test,确保关键 runtime marker 存在。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `node --check scripts/task462-resource-tab-error-placeholder-smoke.js`
|
||||
- [x] `node scripts/task462-resource-tab-error-placeholder-smoke.js`
|
||||
- [x] `node scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- [x] `cargo test -p mnote-web web_shell -- --test-threads=1`
|
||||
|
||||
## 6. 本轮执行记录
|
||||
|
||||
- 已完成:
|
||||
- `createResourceSession` 已补 `lastActiveAt`,resource session 继续持有 `dirty` / `saving` / `hasExternalConflict` / `status` 等关闭保护状态。
|
||||
- page tab 与 resource tab 继续统一通过 `activateMainEditorTab` 激活,并在激活 resource tab 时更新 MRU 与 `lastActiveAt`。
|
||||
- `closeResourceTab` 继续通过 `resourceTabCloseGuardReason` 读取关闭保护状态。
|
||||
- `openResourceInActiveTab` 失败时保留 resource tab,并通过 `.mnote-resource-tab-error` 渲染可见错误占位。
|
||||
- 新增 `scripts/task462-resource-tab-error-placeholder-smoke.js`,已验证失败资源会出现错误占位、可关闭、连续失败不会产生失控 tab。
|
||||
- `web_shell.rs` 与 `styles.rs` 已补关键 runtime / CSS contract。
|
||||
- 未完成:
|
||||
- `lastActiveAt` 时间戳尚未用于 tab 排序(当前 MRU 队列 `resourceTabMru` 控制切换顺序)。
|
||||
- `dirty`/`saving`/`hasExternalConflict` 状态在 resource tab close guard 中读出但无 UI 确认弹窗(仅 console.warn + 禁止关闭)。
|
||||
- 风险:无
|
||||
|
||||
## 7. 非目标
|
||||
|
||||
- 不做 tab 持久化恢复。
|
||||
- 不做 tab 拖拽排序、context menu 或 close others。
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
# 5-19 Resource Open Resolver 与 OnlyOffice 回源 Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:资源打开 resolver / attachment capture / OnlyOffice URL 配置
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P0-3:把资源类型识别、badge、默认打开目标、显式 `active-tab / side / new-window` 目标收口到轻量 resolver,并优先修复本地上传 `.docx` 在主编辑区 tab 内无法回源的问题。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- `rust/crates/mnote-web/src/routes/onlyoffice.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/styles.rs`
|
||||
- `scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- `scripts/task461-resource-kind-smoke.js`
|
||||
- 可新增 OnlyOffice/resolver smoke
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不引入 Monaco / CodeMirror。
|
||||
- 不重构 OnlyOffice DocumentServer 部署。
|
||||
- 不改变 `.md` / `.txt` / code 资源继续用 tiptap resource editor 的口径。
|
||||
- 不处理 File Tree open target 合同;该项由 4-39 处理。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] 定义轻量 resource open resolver,输入包含 `name/mimeType/href/assetId/rootUri/path/openTarget`。
|
||||
- [x] resolver 输出 `editorKind/badgeKind/defaultOpenMode/editable/viewerUrl` 或等价结构。
|
||||
- [x] `tree.asset.open`、attachment click capture、显式 new-window 复用同一 resolver 判断。
|
||||
- [x] 默认打开目标为主编辑区 `active-tab`;`new-window` 显式保留;`side` 不回归。
|
||||
- [x] `.docx/.pptx/.xlsx` 默认进入主编辑区 Office iframe,显式 new-window 打开 `/onlyoffice`。
|
||||
- [x] OnlyOffice iframe 的 document URL 对 local-folder 文件使用 Rust 可访问的 `/api/local-folder/files/open` 或 `/api/onlyoffice/proxy` 回源 URL,避免 DocumentServer 访问浏览器不可达 URL。
|
||||
- [x] 如果本机 DocumentServer 不可用,smoke 至少断言 iframe config URL / document URL / callback URL 正确,不把外部服务不可用误判为 resolver 失败。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `node --check scripts/task457-main-editor-resource-tab-smoke.js && node --check scripts/task461-resource-kind-smoke.js`
|
||||
- [x] `node scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- [x] `node scripts/task461-resource-kind-smoke.js`
|
||||
- [x] 新增或更新 Office smoke,覆盖 `.docx` 主编辑区 tab iframe 与显式 new-window。
|
||||
- [x] `cargo test -p mnote-web onlyoffice -- --test-threads=1`
|
||||
|
||||
## 6. 本轮执行记录
|
||||
|
||||
- 已完成:
|
||||
- `resolveResourceOpen` 已集中输出 `editorKind` / `badgeKind` / `defaultOpenMode` / `editable` / `viewerUrl` / `openTarget`。
|
||||
- 本地 Office 资源默认进入主编辑区 iframe,显式 `new-window` 仍打开 `/onlyoffice`。
|
||||
- `buildOnlyOfficeOpenUrl` 会把相对 `fileUrl` 绝对化,local-folder Office iframe 携带 `http://127.0.0.1:3000/api/local-folder/files/open?...` 这类 Rust 可访问回源 URL。
|
||||
- 新增 `scripts/task463-onlyoffice-resolver-smoke.js`,已覆盖 `.docx/.pptx/.xlsx` badge、主编辑区 iframe、显式 new-window、OnlyOffice 页面脚本结构。
|
||||
- 未完成:
|
||||
- 未验证真实外部 DocumentServer 协同编辑会话,只验证 MNote 侧 URL / iframe / proxy / callback 配置。
|
||||
- 风险:无
|
||||
|
||||
## 7. 非目标
|
||||
|
||||
- 不修复外部 DocumentServer 服务部署本身。
|
||||
- 不实现云端资源写回。
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
> 更新时间:2026-04-19
|
||||
>
|
||||
> 当前状态:`process-reference`。本文只作为 tiptap / Notion-like 行为模型参考,不再作为当前执行 checklist;默认主编辑器已是页面内 `leptos-tiptap` island。
|
||||
>
|
||||
> 这份文档已经按当前进展修正:
|
||||
>
|
||||
> 现在的问题不再是“这套模板能不能迁进来”,而是:
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
# 5-20 Resource Tab Close Confirm Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:主编辑区 resource tab close guard / `web_shell.rs`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P0-2 的可见关闭防护:resource tab 已能检测 `dirty/saving/hasExternalConflict`,但当前只是 `console.warn` 并阻止关闭。需要补上用户可见的最小确认/提示 UI,避免“点击无反应”。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/styles.rs`
|
||||
- `scripts/task460-resource-tab-close-dirty-smoke.js`
|
||||
- 可新增一个聚焦 smoke
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不引入完整 VSCode working copy service。
|
||||
- 不改变 resource markdown / text / code 的保存事实源。
|
||||
- 不实现完整 discard/undo 体系;本轮只做可见提示和明确阻止。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] `dirty` 关闭时显示明确提示,说明资源有未保存更改。
|
||||
- [x] `saving` 关闭时显示明确提示,说明正在保存。
|
||||
- [x] `hasExternalConflict` 关闭时显示明确提示,说明存在外部冲突。
|
||||
- [x] tab 上保留 `data-mnote-resource-close-guard` 与可测试状态。
|
||||
- [x] 提示 UI 可被后续关闭尝试更新,不堆积多个弹层。
|
||||
- [x] 新增或更新 smoke 覆盖点击关闭后出现提示,而不是静默无响应。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `node --check scripts/task460-resource-tab-close-dirty-smoke.js`
|
||||
- [x] `node scripts/task460-resource-tab-close-dirty-smoke.js`
|
||||
- [x] `cargo test -p mnote-web web_shell -- --test-threads=1`
|
||||
|
||||
## 5.1 本轮执行记录
|
||||
|
||||
- 已合入:`showResourceTabCloseGuardNotice`,对 `dirty` / `saving` / `hasExternalConflict` 输出可见提示。
|
||||
- 已验证:`task460` 断言可见提示存在且重复点击不堆叠。
|
||||
- 保留约束:仍只阻止关闭,不实现 discard/undo。
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不做真正丢弃更改按钮。
|
||||
- 不做保存队列或 retry 策略。
|
||||
@@ -1,49 +0,0 @@
|
||||
# 5-21 Editor Tab URL / Active Row / Canonical Identity Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:主编辑区 tab runtime / sidebar active state / resource identity
|
||||
|
||||
## 1. 目标
|
||||
|
||||
在 P0 清零后推进相邻 P1:resource tab 激活目前主要改 DOM 状态,不稳定同步 URL、sidebar active row 与 canonical identity。先做轻量收口,让用户在 resource tab / page tab 之间切换时有可测试的 active resource 状态。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- `scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- 可新增聚焦 smoke
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不实现 tab 持久化恢复。
|
||||
- 不改变 Page Aggregate / local Markdown 事实源。
|
||||
- 不实现完整 side group。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] resource tab 使用统一 canonical identity,优先 `resource:file:<rootUri>:<path>`。
|
||||
- [x] 激活 resource tab 时写入可测试 URL state 或 history state,不触发整页导航。
|
||||
- [x] 切回 page tab 时清除 active resource state。
|
||||
- [x] sidebar file tree 对应 resource row 能显示 active 状态或至少暴露稳定 DOM 标记。
|
||||
- [x] page tab / resource tab 切换后不会卡死在 resource tab,`task457` 继续覆盖。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `node --check scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- [x] `node scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- [x] `cargo test -p mnote-web web_shell -- --test-threads=1`
|
||||
|
||||
## 5.1 本轮执行记录
|
||||
|
||||
- 已合入:resource tab 激活时写入 `resourceTab=<canonical identity>` URL state,切回 page tab 或关闭全部资源 tab 时清除。
|
||||
- 已合入:对应 filetree row 已渲染时只设置 `data-active=true`,不污染 `data-selected` 多选状态。
|
||||
- 已验证:`task457` 覆盖 resource tab、Office tab、new-window、关闭回退和 URL state 清理。
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不做跨刷新恢复。
|
||||
- 不做多 editor group。
|
||||
@@ -1,52 +0,0 @@
|
||||
# 5-22 Mindmap Main Editor Tab Kind Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:main editor resource tab / mindmap editor kind
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P1:mindmap 仍以独立 object shell 进入主 pane,而不是 main editor group 内的 editor kind。目标是把 File Tree 中的 mindmap 资源默认打开到主编辑区 tab,并保留显式对象页 / 新窗口兼容入口。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- `scripts/task169-mindmap-realtime-smoke.js`
|
||||
- 可新增聚焦 smoke:`scripts/task47*-mindmap-resource-tab-smoke.js`
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不把 mindmap 数据保存到 `page.body.save`。
|
||||
- 不删除 `/mindmap/:documentId/:mindmapId` object shell 兼容路由。
|
||||
- 不破坏已存在的 mindmap realtime / long text edit smoke。
|
||||
- 不让 mindmap tab 和 page tab 使用同一个 DOM identity。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] resource open resolver 识别 `kind=mindmap` / `objectKind=mindmap`。
|
||||
- [x] 点击 File Tree mindmap 资源默认创建 main editor resource tab,而不是整页跳 `/mindmap/...`。
|
||||
- [x] mindmap tab 使用稳定 canonical identity,如 `resource:mindmap:<documentId>:<mindmapId>` 或 local file canonical identity。
|
||||
- [x] tab panel 内挂载既有 mindmap runtime,并保持 `data-mnote-object-editor="mindmap"` / 可观测标记。
|
||||
- [x] 显式新窗口 / object shell 入口仍可用。
|
||||
- [x] smoke 覆盖从 File Tree 打开 mindmap tab、切回 page、再切回 mindmap。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `cargo test -p mnote-web web_shell -- --test-threads=1`
|
||||
- [x] `node scripts/task169-mindmap-realtime-smoke.js`
|
||||
- [ ] 如新增 smoke:`node scripts/task47*-mindmap-resource-tab-smoke.js`
|
||||
|
||||
## 5.1 本轮执行记录
|
||||
|
||||
- 已合入:`kind=mindmap` resource tab,使用 `resource:mindmap:<documentId>:<mindmapId>` identity,并复用现有 `/mindmap/...` bootstrap + runtime mount。
|
||||
- 已保留:旧 object shell 作为 runtime 不可用或显式路径 fallback。
|
||||
- 本轮更新:`task169` 已从旧 object shell 口径切到 main editor mindmap resource tab 口径,断言 File Tree 打开 mindmap 后进入 `data-mnote-tab-kind="mindmap"` 的 resource tab,并覆盖切回 page tab 后再切回 mindmap tab。
|
||||
- 已退役:`task169` 内旧 topic/runtime realtime 深测不再作为必过主链,结果中写入 `retiredRealtimeTopicEditing`,避免旧 object shell 时代的交互假设干扰当前 P1 resource tab 验收。
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不实现 mindmap 多实例协同编辑模型。
|
||||
- 不实现 tab 持久化恢复。
|
||||
@@ -1,50 +0,0 @@
|
||||
# 5-23 Editor Side Target / Secondary Pane Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:document pane runtime / side target
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P1:secondary pane 与 main tab 目前是两套概念。目标是先建立轻量 side target 合同,让 `openTarget=side` 能稳定映射到当前 secondary pane,而不是继续分散在“右侧边栏打开”和 resource tab 特例里。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- `scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- 可新增聚焦 smoke:`scripts/task47*-side-target-smoke.js`
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不实现完整多 editor group grid。
|
||||
- 不改变 primary page / resource tab 的默认 active tab 行为。
|
||||
- 不把 secondary pane 等同为 active resource tab。
|
||||
- 不删除现有 `secondaryDocumentId` query 兼容。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] `resolveResourceOpen` / document pane runtime 对外暴露 `openTarget=side` 的稳定返回。
|
||||
- [x] 页面文档的 side target 继续打开 secondary pane,并更新/清理既有 secondary query。
|
||||
- [x] 资源类 side target 有明确行为:当前阶段可先显示 unsupported/error placeholder,但必须可观测且不误开新窗口。
|
||||
- [x] 关闭 secondary pane 不影响 active resource tab URL state。
|
||||
- [x] smoke 覆盖 active tab 与 secondary pane 可同时存在、互不清状态。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `cargo test -p mnote-web web_shell -- --test-threads=1`
|
||||
- [x] `node scripts/task457-main-editor-resource-tab-smoke.js`
|
||||
- [x] 如新增 smoke:`node scripts/task47*-side-target-smoke.js`
|
||||
|
||||
## 5.1 本轮执行记录
|
||||
|
||||
- 已合入:`openResourceAsSideTarget`,资源侧栏目标先显示可观测 unsupported placeholder,不再误开新窗口或污染 active resource tab。
|
||||
- 已合入:`tree.page.open` + `openTarget=side` 的 document side target wrapper,复用现有 `openSecondaryDocument` / `secondaryDocumentId` 合同。
|
||||
- 已合入:资源类 `openTarget=side` 使用 secondary pane unsupported placeholder 时清理 `secondaryDocumentId` / `secondarySourceKind` / `secondaryRootUri`,保留 active resource tab 的 `resourceTab` URL state。
|
||||
- 验证记录:`node --check scripts/task472-side-target-secondary-pane-smoke.js`、`node --check scripts/task457-main-editor-resource-tab-smoke.js`、`cargo test -p mnote-web web_shell -- --test-threads=1`、`node scripts/task472-side-target-secondary-pane-smoke.js`、`node scripts/task457-main-editor-resource-tab-smoke.js`。
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不实现 split editor layout、drag editor between groups、MRU across groups。
|
||||
@@ -1,51 +0,0 @@
|
||||
# 5-24 Open Editors Lightweight View Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-21
|
||||
>
|
||||
> Owner:main editor tab / sidebar open editors parity
|
||||
|
||||
## 1. 目标
|
||||
|
||||
参考 Sidex / VSCode `OpenEditorsView`,为 MNote 做轻量版“打开的编辑器”视图或可观测模型。当前阶段不要求完整 UI,但至少要让 page/resource tabs 的 active、title、kind、dirty guard 状态可被统一读取和测试。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/styles.rs`
|
||||
- 可新增 `scripts/task47*-open-editors-*.js`
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不引入完整 VSCode editor group service。
|
||||
- 不实现多 editor group grid。
|
||||
- 不改变 Page Aggregate / local Markdown 事实源。
|
||||
- 不破坏现有 resource tab 打开、关闭和 URL state。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] 暴露轻量 open editors snapshot:page tab + resource tabs。
|
||||
- [x] snapshot 至少包含 `objectIdentity`、`title`、`kind`、`active`、`dirtyGuard`。
|
||||
- [x] active tab 切换时 snapshot 更新。
|
||||
- [x] resource tab close / create 时 snapshot 更新。
|
||||
- [ ] 如做 UI,保持低干扰,不新增复杂 sidebar 分组。
|
||||
- [x] 补测试或 smoke,验证可读取 active editor 列表。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- `cargo test -p mnote-web web_shell -- --test-threads=1`
|
||||
- 如新增 smoke:`node scripts/task47*-open-editors-*.js`
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不做 drag editor between groups。
|
||||
- 不做完整 dirty working copy service。
|
||||
|
||||
## 7. 执行记录
|
||||
|
||||
- 2026-05-21:Codex 补齐 `mnote.open_editors_snapshot.v1` 轻量合同,暴露 `window.__mnoteDocumentPaneRuntime.getOpenEditorsSnapshot()`,并同步 `window.__mnoteOpenEditorsSnapshot` / `data-mnote-open-editors-count` / `data-mnote-active-editor`。
|
||||
- 2026-05-21:snapshot 覆盖 page tab 与 resource tabs;resource tab 条目包含 `objectIdentity`、`title`、`kind`、`badgeKind`、`active`、`dirtyGuard`、`assetId`、`path`。
|
||||
- 2026-05-21:未新增 sidebar UI,当前阶段先保持为可观测模型。
|
||||
- 2026-05-21:`cargo test -p mnote-web web_shell -- --test-threads=1` 通过。
|
||||
- Reasonix Worker A 本轮只停留在探索,未产生可合入 diff;最终由 Codex 本地实现。
|
||||
@@ -1,74 +0,0 @@
|
||||
# 5-25 Editor Tab Keyboard and MRU Checklist v1
|
||||
|
||||
> 状态:process
|
||||
>
|
||||
> 日期:2026-05-21
|
||||
>
|
||||
> Owner:main editor tab keyboard / MRU behavior
|
||||
|
||||
## 1. 目标
|
||||
|
||||
参考 Sidex / VSCode `MultiEditorTabsControl`,补 MNote main tab 的基础键盘和 MRU 行为。当前只做轻量交互,不照搬完整 tab control。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/styles.rs`
|
||||
- 可新增聚焦 smoke
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不新增全局快捷键与浏览器默认冲突。
|
||||
- 不实现完整 tab overflow 复杂布局。
|
||||
- 不改变 resource tab identity。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] tab strip 支持左右箭头 roving focus。
|
||||
- [x] `Enter` / `Space` 激活当前 focused tab。
|
||||
- [x] 关闭 active resource tab 后回到 MRU 或 page tab,行为稳定。
|
||||
- [x] tab 上保留可测试 DOM 状态。
|
||||
- [x] 补测试或 smoke。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- `cargo test -p mnote-web web_shell -- --test-threads=1`
|
||||
- 如新增 smoke:`node scripts/task47*-editor-tab-keyboard-*.js`
|
||||
|
||||
## 6. 执行记录
|
||||
|
||||
> 引用 proposal:`.codex/reasonix-proposals/2026-05-21-tab-keyboard-mru-proposal.md`
|
||||
|
||||
### 已确认
|
||||
|
||||
- `resourceTabMru` 数组 + `touchResourceTabMru` / `lastActiveResourceTabKey` / `removeFromResourceTabMru` 逻辑完整,MRU 核心逻辑无需改动。
|
||||
- `activateMainEditorTab` 已正确维护 roving tabindex(active → `tabindex="0"`, others → `tabindex="-1"`)。
|
||||
- `createResourceTabDom` 中 resource tab 初始 `tabindex="-1"` 符合预期。
|
||||
|
||||
### 已实现
|
||||
|
||||
- [x] 新增 `bindMainEditorTabStrip()` 函数(keydown 事件委托 + collectTabs + 箭头/Home/End/Enter/Space)。
|
||||
- [x] 修改 `closeResourceTab()` 末尾,关闭后 focus 迁移到新激活的 tab。
|
||||
- [x] 在 `styles.rs` 补充 `:focus-visible` 样式。
|
||||
- [x] 更新 Rust contract test 断言。
|
||||
- [x] `cargo test -p mnote-web web_shell -- --test-threads=1` 通过。
|
||||
|
||||
### 后续可选
|
||||
|
||||
- [ ] 如后续需要浏览器级回归,可新增 `scripts/task475-editor-tab-keyboard-mru-smoke.js`,覆盖多 tab 键盘切换与关闭后 focus。
|
||||
|
||||
### 非预期发现
|
||||
|
||||
- `closeResourceTab` 在 activate 后未显式调用 `focus()`,导致 keyboard focus 丢失。需修复。
|
||||
- tab strip 无 `:focus-visible` 样式,键盘用户在箭头导航后看不到 focus 环。
|
||||
|
||||
### Codex 实施记录
|
||||
|
||||
- 2026-05-21:Reasonix Worker B 按要求只输出 proposal,未直接改源码。
|
||||
- 2026-05-21:Codex 按 proposal 的最小安全集实现键盘 roving focus、Enter/Space 激活、关闭后 MRU/page tab focus 迁移。
|
||||
- 2026-05-21:未新增全局快捷键;事件只绑定在 `[data-mnote-main-tab-strip]` 内。
|
||||
|
||||
## 7. 非目标
|
||||
|
||||
- 不做拖拽 tab 排序。
|
||||
- 不做 close others / close saved 复杂菜单。
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
> 更新时间:2026-05-18
|
||||
>
|
||||
> 当前状态:`process-reference`。本文作为 Page Aggregate 单一真源长期方案保留;当前执行进度与剩余项以 `5-6` 及 MVP 后阶段 checklist 为准。
|
||||
>
|
||||
> 2026-05-18 口径补充:
|
||||
> - 页面聚合的默认 source 已开始向 local-first workspace 收口;页面正文、标题、设置和上传资源不应再默认把 Convex 视作主数据层。
|
||||
> - 本文中涉及 Convex 的表述只应理解为 local-first 与 Convex-backed 两种 `WorkspaceSource` 的过渡兼容背景,不应再作为默认产品形态解释。
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
|
||||
> 更新时间:2026-04-30
|
||||
>
|
||||
> 当前状态:`process-reference`。本文只保留 Wolai 体验复刻目标和取证基线;持续执行与安全边界以 `08-wolai-aline-test-flow` 和 `wolai-aline` skill 为准。
|
||||
|
||||
> **Wolai-aline 执行口径更新(2026-04-30):** 本文只保留体验复刻任务拆解和产品目标。所有 Wolai 对标测试、浏览器取证、编辑权限、安全边界、subagent 使用、截图复核和 smoke 补齐,统一以 `/home/lix/.codex/skills/wolai-aline` 与 `/mnt/Data1T/mnote/design/08-wolai-aline-test-flow/process/wolai-aline-test-flow-v1.md` 为准。若本文旧段落与该 skill 冲突,以 skill 为准。
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user