feat(files): tree rename (inline) + delete (confirmed) + submenu visual tether

资源管理器补全:文件树右键菜单新增「重命名」(行内编辑:预填+全选、
Enter/失焦提交、Esc 取消、IME 组合键守卫)与「删除」(Modal 确认,文件/
目录文案区分,永久删除——宿主无回收站)。根行(工作区根)不提供两项,
服务端 fs.rename/fs.remove 双保险拒绝;目标已存在 409;link-aware——
symlink 行只改/删链接本身(lstat 判定),fence 校验对解析后目标进行。

落定收尾:pruneTree 清旧前缀缓存 + 收起 stale expanded + 单层重载父目录;
tab 调解走 service(rename→updateTab 改路径改标题、delete→closeTab 关
受影响 tab,覆盖两侧分栏/底栏/浮动窗口,经 tree-mutations.ts)。

二级菜单视觉关系(layout.css,仍限 data-dsh-sidebar-submenu 作用域):
父行在子菜单打开期间保持高亮(宿主已渲染 aria-expanded="true"),
卡片间距 10px→4px(悬停走廊同步收窄),翻转规则一并 4px。

i18n 新增 7 key × 20 语言文件(locales.spec 的 key-set-parity 门禁)。
测试:fs-operations 追加 12 例(rename/remove 全失败面);新增
file-tree-rename-delete.spec.tsx 8 例(菜单可见性/重命名流/删除流/错误条);
menu-flip.spec.tsx 契约扩展覆盖 tether 规则。设计文档
docs/plans/2026-09-04-file-tree-rename-delete-design.md。
This commit is contained in:
Menghuan1918
2026-09-04 22:08:02 +08:00
parent 76b57508f4
commit e39ea92ade
34 changed files with 1070 additions and 16 deletions
+77
View File
@@ -1038,6 +1038,83 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
white-space: nowrap;
}
/* ── Inline rename editor ─────────────────────────────────────────────────
The row swaps its name span for a plain input (same row metrics: 34px
height, 6px gap, xxs type); only tokens, no hardcoded colors. */
.explorerRenaming {
cursor: text;
}
.explorerRenameInput {
box-sizing: border-box;
flex: 1 1 auto;
min-width: 0;
height: 24px;
padding: 0 6px;
border: 1px solid var(--dsw-alias-state-business-primary);
border-radius: 6px;
background: var(--dsw-alias-bg-layer-1);
color: var(--dsw-alias-label-primary);
font: var(--dsw-font-xxs-12);
}
.explorerRenameInput:focus {
outline: none;
border-color: var(--dsw-alias-state-business-primary);
}
/* The dismissable mutation-failure strip above the root row (rename/delete
refusals — raw server text, same policy as the fence notice). */
.explorerActionError {
display: flex;
align-items: flex-start;
gap: 6px;
box-sizing: border-box;
width: 100%;
margin-bottom: 4px;
padding: 6px 8px;
border: 1px solid var(--dsw-alias-state-error-primary);
border-radius: 8px;
background: var(--dsw-alias-bg-layer-1);
}
.explorerActionErrorText {
flex: 1 1 auto;
min-width: 0;
overflow-wrap: anywhere;
font: var(--dsw-font-xxs-12);
color: var(--dsw-alias-state-error-primary);
}
.explorerActionErrorClose {
flex: none;
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
padding: 0;
border: none;
border-radius: 5px;
background: transparent;
color: var(--dsw-alias-label-tertiary);
cursor: pointer;
}
.explorerActionErrorClose:hover {
background: var(--dsw-alias-interactive-bg-hover);
color: var(--dsw-alias-label-primary);
}
/* The delete-confirmation modal's description paragraph (the git lens's
.gitConfirmDesc sibling — same quiet single-question look). */
.explorerConfirmDesc {
margin: 0;
font: var(--dsw-font-s-14);
color: var(--dsw-alias-label-primary);
overflow-wrap: anywhere;
}
/* The hover-revealed @-reference button at the row's far right (and the
transient "copied" label that replaces it after a copy). The only auto
margin in the row absorbs the filename's spare width, so a short name