feat(files): 行尾悬停露出常用操作图标,面包屑带登记目录类型

学 MP 文件管理:鼠标悬停或键盘焦点所在行在行尾浮出 2–3 个图标按钮(未入库视频 / 目录:整理、识别、刮削;已入库:打开作品、刷新元数据、重命名;其余:重命名、移动到),带 tooltip 与 aria-label,被状态阻止的灰显写原因;浮出时让出修改时间列;焦点行可 Tab 进入、Esc 回列表;窄屏仍只用 ⋯ 菜单。
面包屑前加「下载目录 / 媒体库」说明段。顺带修正 Shift+F10 菜单定位按行键找行时被 CSS.escape 改坏的问题。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-06 12:06:27 +08:00
1 parent d98899a5f8
commit 67f05895d3
6 files changed
+165 -39

No files matched your search

+36 -4
View File
@@ -429,13 +429,23 @@ export function menuModel(list: FileEntry[], root: Pick<FileRoot, "kind" | "writ
return out;
}
/** 行内 hover 操作(桌面,§5.1):按状态只露 2 个 */
export function hoverOps(e: FileEntry): [MenuKey, MenuKey] {
if (e.asset) return ["rename", "openMedia"];
if (opApplies("organize", e)) return ["organize", "identify"];
/**
* 行内 hover / 键盘聚焦时浮出的常用操作(桌面,§5.1;照 MP 的悬停图标,但只露 2–3 个,其余留在 ⋯ 菜单):
* 已入库 → 打开作品、刷新元数据、重命名;未入库视频 / 目录 → 整理、识别、刮削;
* 其余能识别的(临时文件里的视频等)→ 识别、重命名、移动到;别的文件 → 重命名、移动到。
*/
export function hoverOps(e: FileEntry): MenuKey[] {
if (e.asset) return ["openMedia", "scrape", "rename"];
if (opApplies("organize", e)) return ["organize", "identify", "writeNfo"];
if (opApplies("identify", e)) return ["identify", "rename", "move"];
return ["rename", "move"];
}
/** 行内图标按钮的文字(tooltip / aria-label):菜单里的"…"表示还要再选一步,图标上不带 */
export function hoverOpLabel(op: MenuKey): string {
return OP_LABEL[op].replace("…", "");
}
export const OP_LABEL: Record<MenuKey, string> = {
open: "打开",
organize: "整理…",
@@ -470,6 +480,28 @@ export function entrySubline(e: FileEntry): { text: string; tone: "link" | "hint
return null;
}
export const ROOT_KIND_LABEL: Record<FileRoot["kind"], string> = { download: "下载目录", library: "媒体库" };
export type Crumb = { label: string; path: string };
/**
* 面包屑(照 MP「本地 > MEDIA > 亚洲剧集」):登记目录类型(下载目录 / 媒体库,只作说明)+ 登记目录名 + 各级子目录。
* group 不可点;crumbs 每段可点(path 为登记目录内相对路径,根为 "")。
*/
export function breadcrumbItems(
root: Pick<FileRoot, "name" | "kind">,
path: string,
): { group: string; crumbs: Crumb[] } {
const segs = segments(path);
return {
group: ROOT_KIND_LABEL[root.kind],
crumbs: [
{ label: root.name, path: "" },
...segs.map((s, i) => ({ label: s, path: segs.slice(0, i + 1).join("/") })),
],
};
}
export type Loc = { root: string; path: string };
/** 初始位置(§2 #2):上次位置(根在线)> 第一个在线下载目录 > 第一个在线库根 > 第一个 */