feat: 文件管理器 + Windows兼容 + SVG主题适配 (v0.6.0)

新增:
- services/web_panel/routes/files.py — 全功能文件管理后端API (11个端点)
- pages/files.html + files.js — 文件管理前端 (浏览/编辑/上传/删除/右键菜单)
- 插件Picker API: window.open+postMessage唤出文件选择器
- 符号链接目录双层面包屑 (逻辑路径+物理路径)
- 文件系统全访问+跨平台 (Linux/Windows/macOS)
- permission_rules.yaml新增4个filemanager.*权限

修复:
- SVG fill=currentColor 日夜模式自适应
- 特殊目录容错 (/dev/fd损坏符号链接/proc)
- 面包屑每层级独立可点击+可编辑路径跳转
- ..行返回上级+data-is-dir补全
- rmlint→lstat回退 损坏符号链接不炸页面

文档:
- 插件开发指南新增第八章(文件管理器集成)
- 开发踩坑记录新增4条(特殊目录/双面包屑/SVG颜色/Windows)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
qinglong
2026-06-11 18:23:32 +08:00
parent 4a989bf50f
commit fd715eac2b
12 changed files with 1015 additions and 9 deletions
+55 -1
View File
@@ -64,6 +64,7 @@
/* ── Reset ── */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
svg{fill:currentColor}
body{
background:var(--bg);color:var(--text);
font-family:"Google Sans",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
@@ -189,7 +190,7 @@ h3{font-size:1rem;font-weight:500;letter-spacing:.15px}
.nav-item:hover{background:rgba(208,188,255,.08);color:var(--text)}
.nav-item.active{background:var(--primary-container);color:var(--md-sys-color-on-primary-container)}
.nav-item .nav-icon{width:24px;height:24px;flex-shrink:0;display:flex;align-items:center;justify-content:center}
.nav-item svg{width:20px;height:20px;flex-shrink:0}
.nav-item svg{width:20px;height:20px;flex-shrink:0;fill:currentColor}
.nav-item .nav-label{overflow:hidden;text-overflow:ellipsis}
.toggle-sidebar{
margin-top:auto;padding:16px;text-align:center;cursor:pointer;
@@ -426,3 +427,56 @@ h3{font-size:1rem;font-weight:500;letter-spacing:.15px}
/* ── Menu reveal animation for login error ── */
.err-msg{transition:opacity .3s}
/* ═══════════════════════════════════════════
File Manager
═══════════════════════════════════════════ */
.fm-container{display:flex;flex-direction:column;height:calc(100vh - var(--topbar-h) - 48px);gap:12px}
.fm-toolbar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px}
.fm-path-bar{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.fm-path-input{width:100%;padding:8px 12px;background:var(--md-sys-color-surface-container-lowest);border:1px solid var(--outline);border-radius:var(--shape-xs);color:var(--text);font-family:"JetBrains Mono",monospace;font-size:12px;outline:none;transition:border-color .2s}
.fm-path-input:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(208,188,255,.15)}
.fm-breadcrumb{display:flex;align-items:center;flex-wrap:wrap;gap:4px;overflow-x:auto;scrollbar-width:none;flex:1;min-width:0}
.fm-breadcrumb::-webkit-scrollbar{display:none}
.fm-crumb{white-space:nowrap;padding:4px 10px;border-radius:var(--shape-full);cursor:pointer;font-size:.82rem;color:var(--text-dim);transition:.15s}
.fm-crumb:hover{background:rgba(208,188,255,.1);color:var(--primary)}
.fm-crumb-sep{color:var(--outline);font-size:.7rem;flex-shrink:0}
.fm-resolved-crumbs{display:flex;align-items:center;gap:4px;font-size:.75rem;opacity:.8;flex-basis:100%}
.fm-actions{display:flex;gap:6px;flex-shrink:0;align-items:center}
.fm-toggle{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--text-dim);cursor:pointer}
.fm-toggle input{margin:0}
.fm-list{flex:1;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--outline) transparent}
.fm-row{display:grid;grid-template-columns:32px 1fr 100px 140px;align-items:center;padding:10px 12px;cursor:pointer;border-radius:var(--shape-xs);transition:background .12s;gap:8px;font-size:.88rem}
.fm-row:hover{background:rgba(208,188,255,.06)}
.fm-row.fm-dir{font-weight:500}
.fm-row.fm-parent{border-bottom:1px solid var(--outline);margin-bottom:2px;font-weight:600}
.fm-icon{font-size:1.2rem;text-align:center}
.fm-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fm-size{text-align:right;color:var(--text-dim);font-size:.8rem;font-variant-numeric:tabular-nums}
.fm-date{text-align:right;color:var(--text-dim);font-size:.78rem}
/* Editor overlay */
.fm-editor-overlay{position:fixed;inset:0;z-index:200;display:flex;flex-direction:column;background:var(--md-sys-color-surface-container-high)}
.fm-editor-header{display:flex;justify-content:space-between;align-items:center;padding:12px 20px;border-bottom:1px solid var(--outline);background:var(--bg-card)}
.fm-editor-header span{font-weight:500;color:var(--primary)}
.fm-editor-header div{display:flex;gap:8px}
.fm-editor-overlay textarea{flex:1;padding:16px 20px;background:var(--md-sys-color-surface-container-lowest);color:var(--text);border:none;outline:none;resize:none;font-family:"JetBrains Mono","Fira Code",monospace;font-size:13px;line-height:1.6;tab-size:4}
/* Dialog overlay */
.fm-dialog-overlay{position:fixed;inset:0;z-index:300;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.5)}
.fm-dialog{background:var(--md-sys-color-surface-container-high);border-radius:var(--shape-md);padding:24px;min-width:320px;max-width:450px;box-shadow:var(--md-sys-elevation-4)}
/* Context menu */
.fm-context{position:fixed;z-index:250;background:var(--md-sys-color-surface-container-high);border-radius:var(--shape-xs);box-shadow:var(--md-sys-elevation-3);min-width:160px;padding:4px 0;overflow:hidden}
.fm-context-item{padding:8px 16px;cursor:pointer;font-size:.82rem;color:var(--text);transition:background .12s}
.fm-context-item:hover{background:rgba(208,188,255,.1)}
.fm-context-item.danger{color:var(--error)}
.fm-context-item.danger:hover{background:rgba(242,184,181,.1)}
.fm-context-sep{height:1px;background:var(--outline);margin:4px 0}
/* Responsive */
@media(max-width:768px){
.fm-row{grid-template-columns:28px 1fr 70px}
.fm-date{display:none}
.fm-actions{flex-wrap:wrap}
}