feat(tree): checkpoint resource lifecycle work

提交当前顶层 mnote Git 工作区,范围集中在 04-tree-domain 的 resource/trash/filetree 生命周期、mnote-web resource_trash 路由、Convex/Next 兼容接口、sidebar/file-tree 客户端适配、smoke 脚本与对应设计/bug 记录。

不包含被 ignore 的 design/05-editor-mainline/reference-code/leptos-tiptap 嵌套仓库改动。新增 smoke 的测试密码改为运行时读取 MNOTE_E2E_PASSWORD,避免提交明文 credential assignment。
This commit is contained in:
lix-2026
2026-05-16 07:38:45 +08:00
parent 274779c0d8
commit 384da4e44c
85 changed files with 12570 additions and 394 deletions
+13 -2
View File
@@ -3,7 +3,7 @@
import type { FileTreeRow } from "./types";
import { parseFileTreeRowId } from "./types";
export type FileTreeClipboardAction = "copy";
export type FileTreeClipboardAction = "copy" | "cut";
export type FileTreeClipboardPayloadV1 = {
type: "mnote-file-tree";
@@ -45,7 +45,7 @@ export function decodeFileTreeClipboardPayload(text: string): FileTreeClipboardP
const raw = decodeBase64(base64);
const parsed = JSON.parse(raw) as Partial<FileTreeClipboardPayloadV1>;
if (parsed?.type !== "mnote-file-tree" || parsed.version !== 1) return null;
if (parsed.action !== "copy") return null;
if (parsed.action !== "copy" && parsed.action !== "cut") return null;
if (!Array.isArray(parsed.rowIds) || parsed.rowIds.some((id) => typeof id !== "string")) return null;
return parsed as FileTreeClipboardPayloadV1;
} catch {
@@ -78,6 +78,17 @@ export async function readFileTreeClipboardPayload(): Promise<FileTreeClipboardP
return decodeFileTreeClipboardPayload(text);
}
export async function clearFileTreeClipboardPayload(): Promise<void> {
memoryClipboardText = null;
if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
try {
await navigator.clipboard.writeText("");
} catch {
// ignore, memory fallback 已清空
}
}
}
export function isTextInputTarget(target: EventTarget | null): boolean {
const el = target as HTMLElement | null;
if (!el) return false;