From 5c3b157159a00e092fac5b53221938213611d21e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Fri, 5 Jun 2026 17:15:50 +0800 Subject: [PATCH] Add files via upload --- web/static/css/style.css | 74 ++++++++++++++++++++++++++++++++++++++ web/static/i18n/en-US.json | 3 ++ web/static/i18n/zh-CN.json | 3 ++ web/static/js/monitor.js | 9 +++-- web/static/js/skills.js | 21 ++++++++--- web/templates/index.html | 3 +- 6 files changed, 105 insertions(+), 8 deletions(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index 6cd1fb3f..5900c44c 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -5556,6 +5556,80 @@ header { padding-bottom: 0; } +/* Skill 包内文件树:区分不可点击的文件夹与可点击的文件 */ +#skill-package-tree { + flex: 0 0 240px; + max-height: 440px; + overflow: auto; + border: 1px solid var(--border-color); + border-radius: 6px; + padding: 6px 4px; + font-size: 13px; + line-height: 1.4; +} + +.skill-package-tree-hint { + display: block; + font-size: 12px; + color: var(--text-muted); + margin: 4px 0 8px; + line-height: 1.45; +} + +.skill-tree-row { + display: flex; + align-items: flex-start; + gap: 6px; + padding: 5px 8px; + border-radius: 4px; + margin-bottom: 1px; + min-width: 0; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 12px; + word-break: break-all; + line-height: 1.35; +} + +.skill-tree-dir { + color: var(--text-muted); + cursor: default; + user-select: none; + font-weight: 500; + opacity: 0.88; +} + +.skill-tree-dir .skill-tree-icon { + opacity: 0.65; +} + +.skill-tree-file { + color: var(--text-primary); + cursor: pointer; + transition: background 0.15s ease; +} + +.skill-tree-file:hover { + background: rgba(0, 102, 255, 0.08); +} + +.skill-tree-file.is-selected { + font-weight: 600; + background: rgba(99, 102, 241, 0.12); + color: var(--accent-color); +} + +.skill-tree-icon { + flex-shrink: 0; + width: 1.15em; + text-align: center; + line-height: 1.35; +} + +.skill-tree-label { + min-width: 0; + flex: 1; +} + .pagination-fixed { background: var(--bg-primary); margin-top: 0; diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index 894eec8c..d1a40773 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -2257,6 +2257,9 @@ "descriptionPlaceholder": "Short description", "descriptionHint": "Maps to the description field in SKILL.md YAML (when creating/editing SKILL.md)", "packageFiles": "Package files", + "packageFilesHint": "Click a file to edit; folders are labels only and cannot be opened", + "folderHint": "Folder (not editable)", + "clickToEdit": "Click to edit this file", "editingFile": "Editing", "newFile": "New file", "newFilePlaceholder": "Relative path, e.g. FORMS.md or scripts/extra.sh", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index 129da128..616472a9 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -2246,6 +2246,9 @@ "descriptionPlaceholder": "Skill的简短描述", "descriptionHint": "对应 SKILL.md 中 YAML 的 description 字段(创建/编辑 SKILL.md 时使用)", "packageFiles": "包内文件", + "packageFilesHint": "点击文件进行编辑;文件夹仅作分组展示,不可点击", + "folderHint": "文件夹(不可编辑)", + "clickToEdit": "点击编辑此文件", "editingFile": "正在编辑", "newFile": "新建文件", "newFilePlaceholder": "新文件路径,如 FORMS.md 或 scripts/extra.sh", diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js index 2cc3ec96..84089de5 100644 --- a/web/static/js/monitor.js +++ b/web/static/js/monitor.js @@ -3637,10 +3637,15 @@ function buildMcpTimelineSvg(points, rangeKey) { const tickIdx = points.length <= 2 ? points.map((_, i) => i) : [0, Math.floor((points.length - 1) / 2), points.length - 1]; - const xLabels = tickIdx.map((idx) => { + const xLabels = tickIdx.map((idx, ti) => { const c = coords[idx]; const label = formatMcpTimelineLabel(c.p.t, rangeKey, locale); - return `${escapeHtml(label)}`; + let anchor = 'middle'; + if (tickIdx.length > 1) { + if (ti === 0) anchor = 'start'; + else if (ti === tickIdx.length - 1) anchor = 'end'; + } + return `${escapeHtml(label)}`; }).join(''); const dots = coords.map((c) => { diff --git a/web/static/js/skills.js b/web/static/js/skills.js index c8ea1456..3fd95a83 100644 --- a/web/static/js/skills.js +++ b/web/static/js/skills.js @@ -468,6 +468,11 @@ function showAddSkillModal() { modal.style.display = 'flex'; } +function skillPackagePathDepth(path) { + if (!path) return 0; + return (String(path).replace(/\/$/, '').match(/\//g) || []).length; +} + function renderSkillPackageTree() { const el = document.getElementById('skill-package-tree'); if (!el) return; @@ -479,13 +484,19 @@ function renderSkillPackageTree() { } el.innerHTML = rows.map(f => { const path = f.path || ''; + const indent = 8 + skillPackagePathDepth(path) * 14; if (f.is_dir) { - return `
${escapeHtml(path)}/
`; + const dirLabel = path.endsWith('/') ? path : path + '/'; + return `
` + + `` + + `${escapeHtml(dirLabel)}` + + `
`; } - const sel = path === skillActivePath - ? 'font-weight:600;background:rgba(99,102,241,0.12);' - : ''; - return `
${escapeHtml(path)}
`; + const selected = path === skillActivePath ? ' is-selected' : ''; + return `
` + + `` + + `${escapeHtml(path)}` + + `
`; }).join(''); el.querySelectorAll('[data-skill-tree-path]').forEach(node => { node.addEventListener('click', () => { diff --git a/web/templates/index.html b/web/templates/index.html index 2366d3fa..c65e50c0 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -3555,8 +3555,9 @@