@mcpware/claude-code-organizer 0.2.2 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +3 -3
- package/.github/workflows/publish.yml +54 -0
- package/.mcp.json +9 -0
- package/README.ja.md +15 -0
- package/README.ko.md +15 -0
- package/README.md +15 -0
- package/README.zh-CN.md +15 -0
- package/README.zh-TW.md +15 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/organize/SKILL.md +31 -0
- package/src/scanner.mjs +47 -1
- package/src/ui/app.js +112 -2
- package/src/ui/style.css +19 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-organizer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Organize all your Claude Code memories, skills, MCP servers, and hooks — view by scope hierarchy, move between scopes via drag-and-drop",
|
|
5
5
|
"author": {
|
|
6
|
-
"name": "
|
|
7
|
-
"url": "https://github.com/
|
|
6
|
+
"name": "ithiria894",
|
|
7
|
+
"url": "https://github.com/ithiria894"
|
|
8
8
|
},
|
|
9
9
|
"homepage": "https://github.com/mcpware/claude-code-organizer",
|
|
10
10
|
"repository": "https://github.com/mcpware/claude-code-organizer",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write # MCP Registry OIDC auth
|
|
13
|
+
contents: write # Create GitHub Release
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v5
|
|
18
|
+
|
|
19
|
+
- name: Setup Node.js
|
|
20
|
+
uses: actions/setup-node@v5
|
|
21
|
+
with:
|
|
22
|
+
node-version: "lts/*"
|
|
23
|
+
registry-url: "https://registry.npmjs.org"
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: |
|
|
27
|
+
if [ -f package-lock.json ]; then
|
|
28
|
+
npm ci --ignore-scripts
|
|
29
|
+
else
|
|
30
|
+
npm install --ignore-scripts
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
- name: Build (if applicable)
|
|
34
|
+
run: npm run build --if-present
|
|
35
|
+
|
|
36
|
+
- name: Publish to npm
|
|
37
|
+
run: npm publish --access public
|
|
38
|
+
env:
|
|
39
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
40
|
+
|
|
41
|
+
- name: Create GitHub Release
|
|
42
|
+
uses: softprops/action-gh-release@v2
|
|
43
|
+
with:
|
|
44
|
+
generate_release_notes: true
|
|
45
|
+
|
|
46
|
+
- name: Install mcp-publisher
|
|
47
|
+
run: |
|
|
48
|
+
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
|
|
49
|
+
|
|
50
|
+
- name: Authenticate to MCP Registry (OIDC)
|
|
51
|
+
run: ./mcp-publisher login github-oidc
|
|
52
|
+
|
|
53
|
+
- name: Publish to MCP Registry
|
|
54
|
+
run: ./mcp-publisher publish
|
package/.mcp.json
ADDED
package/README.ja.md
CHANGED
|
@@ -43,6 +43,21 @@ Claude Code に「これ覚えて」って言ったのに、**違うスコープ
|
|
|
43
43
|
- **依存ゼロ** — 純粋な Node.js ビルトインモジュールのみ、SortableJS は CDN 経由
|
|
44
44
|
- **ガチのファイル移動** — `~/.claude/` 内のファイルを実際に移動する。閲覧専用ツールじゃない
|
|
45
45
|
|
|
46
|
+
## なぜビジュアルダッシュボード?
|
|
47
|
+
|
|
48
|
+
Claude Code は CLI でファイルの一覧表示や移動ができる。じゃあなぜこのツールが必要?
|
|
49
|
+
|
|
50
|
+
| やりたいこと | CLI / Skill | ビジュアルダッシュボード |
|
|
51
|
+
|-------------|:-----------:|:-------------------:|
|
|
52
|
+
| **全体像** — 全スコープのメモリ・スキル・MCPサーバーを一度に見る | 長いテキスト出力をスクロール | スコープツリーで一目瞭然 |
|
|
53
|
+
| **スコープ横断の把握** — Global vs Workspace vs Project の継承関係を理解 | 複数コマンドを実行して頭の中で組み立てる | インデント付きツリー階層 |
|
|
54
|
+
| **スコープ間でアイテム移動** | 正確なパスを覚えてコマンド入力 | ドラッグ&ドロップ |
|
|
55
|
+
| **中身をプレビュー** | ファイルを1つずつ `cat` | クリック → サイドパネル |
|
|
56
|
+
| **横断検索** | `grep` + 手動フィルタリング | リアルタイム検索 + カテゴリフィルター |
|
|
57
|
+
| **何を持っているか把握** | ディレクトリごとに自分でファイル数を数える | スコープ×カテゴリごとの自動カウント |
|
|
58
|
+
|
|
59
|
+
テキスト出力では得られない**全体像**が手に入る — スコープツリー全体が見えて、置き場所の間違いを即座に発見、ドラッグで修正。コマンドを覚える必要もパスを打つ必要もない。
|
|
60
|
+
|
|
46
61
|
## クイックスタート
|
|
47
62
|
|
|
48
63
|
```bash
|
package/README.ko.md
CHANGED
|
@@ -43,6 +43,21 @@ Claude Code한테 "이거 기억해"라고 했는데, **엉뚱한 스코프에
|
|
|
43
43
|
- **의존성 제로** — 순수 Node.js 내장 모듈, SortableJS는 CDN으로
|
|
44
44
|
- **진짜 파일 이동** — `~/.claude/` 안의 파일을 실제로 옮깁니다. 보기만 하는 뷰어가 아닙니다
|
|
45
45
|
|
|
46
|
+
## 왜 비주얼 대시보드인가?
|
|
47
|
+
|
|
48
|
+
Claude Code는 CLI로도 파일 목록 확인과 이동이 가능합니다. 그런데 왜 이 도구가 필요할까요?
|
|
49
|
+
|
|
50
|
+
| 하고 싶은 것 | CLI / Skill | 비주얼 대시보드 |
|
|
51
|
+
|-------------|:-----------:|:-------------:|
|
|
52
|
+
| **전체 그림** — 모든 스코프의 메모리, 스킬, MCP 서버를 한 번에 보기 | 긴 텍스트 출력 스크롤 | 스코프 트리로 한눈에 |
|
|
53
|
+
| **스코프 간 인식** — Global vs Workspace vs Project 상속 관계 파악 | 여러 명령어 실행 후 머릿속으로 조합 | 들여쓰기된 트리 계층 |
|
|
54
|
+
| **스코프 간 항목 이동** | 정확한 경로 기억 + 명령어 입력 | 드래그 앤 드롭 |
|
|
55
|
+
| **내용 미리보기** | 파일 하나씩 `cat` | 클릭 → 사이드 패널 |
|
|
56
|
+
| **전체 검색** | `grep` + 수동 필터링 | 실시간 검색 + 카테고리 필터 |
|
|
57
|
+
| **보유 현황 파악** | 디렉토리별 파일 수를 직접 세기 | 스코프×카테고리별 자동 카운트 |
|
|
58
|
+
|
|
59
|
+
텍스트 출력으로는 얻을 수 없는 **전체 그림**을 제공합니다 — 스코프 트리 전체가 보이고, 잘못 배치된 항목을 즉시 발견하고, 드래그로 수정. 명령어를 외울 필요도 경로를 입력할 필요도 없습니다.
|
|
60
|
+
|
|
46
61
|
## 빠른 시작
|
|
47
62
|
|
|
48
63
|
```bash
|
package/README.md
CHANGED
|
@@ -43,6 +43,21 @@ You have a deploy skill sitting in global, but it only makes sense for one repo.
|
|
|
43
43
|
- **Zero dependencies** — Pure Node.js built-in modules, SortableJS via CDN
|
|
44
44
|
- **Real file moves** — Actually moves files in `~/.claude/`, not just a viewer
|
|
45
45
|
|
|
46
|
+
## Why a Visual Dashboard?
|
|
47
|
+
|
|
48
|
+
Claude Code can already list and move files via CLI. So why does this tool exist?
|
|
49
|
+
|
|
50
|
+
| What you need | CLI / Skill | Visual Dashboard |
|
|
51
|
+
|---------------|:-----------:|:----------------:|
|
|
52
|
+
| **Big picture** — see every memory, skill, MCP server across all scopes at once | Scroll through long text output | Scope tree, one glance |
|
|
53
|
+
| **Cross-scope awareness** — understand Global vs Workspace vs Project inheritance | Run multiple commands, piece it together | Tree hierarchy with indentation |
|
|
54
|
+
| **Move items between scopes** | Remember exact paths, type commands | Drag-and-drop |
|
|
55
|
+
| **Preview content** | `cat` each file one by one | Click → side panel |
|
|
56
|
+
| **Search across everything** | `grep` with manual filtering | Real-time search + category filters |
|
|
57
|
+
| **Understand what you have** | Count files per directory yourself | Automatic counts per category per scope |
|
|
58
|
+
|
|
59
|
+
The dashboard gives you the **big picture that text output can't** — you see the full scope tree, spot misplaced items instantly, and fix them with a drag. No commands to memorize, no paths to type.
|
|
60
|
+
|
|
46
61
|
## Quick Start
|
|
47
62
|
|
|
48
63
|
```bash
|
package/README.zh-CN.md
CHANGED
|
@@ -43,6 +43,21 @@
|
|
|
43
43
|
- **零依赖** — 纯 Node.js 内置模块,SortableJS 走 CDN
|
|
44
44
|
- **真·文件移动** — 直接操作 `~/.claude/` 目录里的文件,不是什么只读查看器
|
|
45
45
|
|
|
46
|
+
## 为什么需要可视化仪表盘?
|
|
47
|
+
|
|
48
|
+
Claude Code 用 CLI 就能列出和移动文件。那为什么还要这个工具?
|
|
49
|
+
|
|
50
|
+
| 你想做的事 | CLI / Skill | 可视化仪表盘 |
|
|
51
|
+
|-----------|:-----------:|:----------:|
|
|
52
|
+
| **全局视野** — 一次看到所有作用域的记忆、技能、MCP 服务器 | 滚动一大段文本输出 | 作用域树,一目了然 |
|
|
53
|
+
| **跨作用域感知** — 理解 Global vs Workspace vs Project 的继承关系 | 跑多条命令,脑内拼凑 | 带缩进的树状层级 |
|
|
54
|
+
| **跨作用域移动** | 记住准确路径,敲命令 | 拖拽搞定 |
|
|
55
|
+
| **预览内容** | 一个个 `cat` 文件 | 点击 → 侧边面板 |
|
|
56
|
+
| **全局搜索** | `grep` + 手动筛选 | 实时搜索 + 分类过滤 |
|
|
57
|
+
| **了解你有什么** | 自己数每个目录里的文件 | 按作用域×类别自动统计 |
|
|
58
|
+
|
|
59
|
+
仪表盘给你**文字输出无法提供的全局视野** — 完整的作用域树一览无余,一眼发现放错位置的东西,拖一下就修好。不用背命令,不用打路径。
|
|
60
|
+
|
|
46
61
|
## 快速上手
|
|
47
62
|
|
|
48
63
|
```bash
|
package/README.zh-TW.md
CHANGED
|
@@ -43,6 +43,21 @@
|
|
|
43
43
|
- **零相依** — 純 Node.js 內建模組,SortableJS 走 CDN
|
|
44
44
|
- **真・檔案搬移** — 直接動 `~/.claude/` 裡的檔案,不是只能看的 viewer
|
|
45
45
|
|
|
46
|
+
## 點解要用視覺化儀表板?
|
|
47
|
+
|
|
48
|
+
Claude Code 用 CLI 都可以 list 同搬 file。咁點解仲要呢個工具?
|
|
49
|
+
|
|
50
|
+
| 你想做嘅嘢 | CLI / Skill | 視覺化儀表板 |
|
|
51
|
+
|-----------|:-----------:|:----------:|
|
|
52
|
+
| **全局視野** — 一次睇晒所有 scope 嘅記憶、技能、MCP 伺服器 | Scroll 一大段 text output | Scope tree,一眼睇晒 |
|
|
53
|
+
| **跨 scope 認知** — 理解 Global vs Workspace vs Project 嘅繼承關係 | 行幾條 command,自己喺腦入面砌 | 有縮排嘅 tree 層級 |
|
|
54
|
+
| **跨 scope 搬移** | 記住準確路徑,打 command | 拖拉搞掂 |
|
|
55
|
+
| **預覽內容** | 逐個 `cat` 檔案 | 撳一下 → 側邊面板 |
|
|
56
|
+
| **全局搜尋** | `grep` + 自己 filter | 即時搜尋 + 分類篩選 |
|
|
57
|
+
| **了解你有咩** | 自己數每個目錄幾多個 file | 按 scope × 類別自動統計 |
|
|
58
|
+
|
|
59
|
+
儀表板俾你**text output 做唔到嘅全局視野** — 成個 scope tree 一覽無遺,一眼就見到擺錯位嘅嘢,拖一下就搞掂。唔使背 command,唔使打 path。
|
|
60
|
+
|
|
46
61
|
## 快速開始
|
|
47
62
|
|
|
48
63
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcpware/claude-code-organizer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Organize all your Claude Code memories, skills, MCP servers, and hooks — view by scope hierarchy, move between scopes via drag-and-drop",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/mcpware/claude-code-organizer",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.2.
|
|
9
|
+
"version": "0.2.5",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@mcpware/claude-code-organizer",
|
|
14
|
-
"version": "0.2.
|
|
14
|
+
"version": "0.2.5",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: organize
|
|
3
|
+
description: Open the Claude Code Organizer dashboard — view and manage all memories, skills, MCP servers, hooks, and configs across scopes
|
|
4
|
+
argument-hint: [--port <number>]
|
|
5
|
+
allowed-tools: [Bash, Read]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Organize — Claude Code Dashboard
|
|
9
|
+
|
|
10
|
+
Launch the Claude Code Organizer dashboard to visually manage all your customizations.
|
|
11
|
+
|
|
12
|
+
## What to do
|
|
13
|
+
|
|
14
|
+
1. Run the organizer server:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @mcpware/claude-code-organizer $ARGUMENTS
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
2. Tell the user the dashboard is opening in their browser at `http://localhost:3847` (or the next available port).
|
|
21
|
+
|
|
22
|
+
3. If the user provides `--port <number>`, pass it through as the argument.
|
|
23
|
+
|
|
24
|
+
## What this does
|
|
25
|
+
|
|
26
|
+
Opens a drag-and-drop web dashboard showing:
|
|
27
|
+
- All memories, skills, MCP servers, hooks, configs, and plugins
|
|
28
|
+
- Organized by scope hierarchy (Global → Workspace → Project)
|
|
29
|
+
- Move items between scopes via drag-and-drop
|
|
30
|
+
- Search, filter, and preview any item
|
|
31
|
+
- Delete items with undo support
|
package/src/scanner.mjs
CHANGED
|
@@ -175,6 +175,44 @@ async function discoverScopes() {
|
|
|
175
175
|
return scopes;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
// ── Skill bundle detection (via skills-lock.json) ───────────────────
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Load skill bundle info from skills-lock.json files.
|
|
182
|
+
* Returns a Map of skillName → { source, sourceType }.
|
|
183
|
+
*
|
|
184
|
+
* Checks:
|
|
185
|
+
* 1. Project-level: <repoDir>/skills-lock.json (version 1)
|
|
186
|
+
* 2. Global: ~/.agents/.skill-lock.json (version 3)
|
|
187
|
+
*/
|
|
188
|
+
async function loadSkillBundles(repoDir) {
|
|
189
|
+
const bundles = new Map();
|
|
190
|
+
|
|
191
|
+
// Paths to check (project-level first, then global)
|
|
192
|
+
const lockPaths = [];
|
|
193
|
+
if (repoDir) lockPaths.push(join(repoDir, "skills-lock.json"));
|
|
194
|
+
lockPaths.push(join(HOME, ".agents", ".skill-lock.json"));
|
|
195
|
+
|
|
196
|
+
for (const lockPath of lockPaths) {
|
|
197
|
+
const content = await safeReadFile(lockPath);
|
|
198
|
+
if (!content) continue;
|
|
199
|
+
try {
|
|
200
|
+
const lock = JSON.parse(content);
|
|
201
|
+
const skills = lock.skills || {};
|
|
202
|
+
for (const [name, entry] of Object.entries(skills)) {
|
|
203
|
+
if (!bundles.has(name) && entry.source) {
|
|
204
|
+
bundles.set(name, {
|
|
205
|
+
source: entry.source,
|
|
206
|
+
sourceType: entry.sourceType || "unknown",
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch {}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return bundles;
|
|
214
|
+
}
|
|
215
|
+
|
|
178
216
|
// ── Item scanners ────────────────────────────────────────────────────
|
|
179
217
|
|
|
180
218
|
async function scanMemories(scope) {
|
|
@@ -224,10 +262,14 @@ async function scanSkills(scope) {
|
|
|
224
262
|
if (await exists(dir)) skillDirs.push(dir);
|
|
225
263
|
}
|
|
226
264
|
|
|
265
|
+
// Load bundle info from skills-lock.json
|
|
266
|
+
const bundleMap = await loadSkillBundles(scope.repoDir);
|
|
267
|
+
|
|
227
268
|
for (const skillsRoot of skillDirs) {
|
|
228
269
|
const entries = await readdir(skillsRoot, { withFileTypes: true });
|
|
229
270
|
for (const entry of entries) {
|
|
230
|
-
|
|
271
|
+
// Support both real directories and symlinks pointing to directories
|
|
272
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
231
273
|
// Skip "private" directory (usually copies of global skills)
|
|
232
274
|
if (entry.name === "private") continue;
|
|
233
275
|
|
|
@@ -268,6 +310,9 @@ async function scanSkills(scope) {
|
|
|
268
310
|
if (fs) totalSize += fs.size;
|
|
269
311
|
}
|
|
270
312
|
|
|
313
|
+
// Bundle detection from skills-lock.json
|
|
314
|
+
const bundleInfo = bundleMap.get(entry.name);
|
|
315
|
+
|
|
271
316
|
items.push({
|
|
272
317
|
category: "skill",
|
|
273
318
|
scopeId: scope.id,
|
|
@@ -280,6 +325,7 @@ async function scanSkills(scope) {
|
|
|
280
325
|
fileCount,
|
|
281
326
|
mtime: s ? s.mtime.toISOString().slice(0, 10) : "",
|
|
282
327
|
path: skillDir,
|
|
328
|
+
bundle: bundleInfo?.source || null,
|
|
283
329
|
});
|
|
284
330
|
}
|
|
285
331
|
}
|
package/src/ui/app.js
CHANGED
|
@@ -192,7 +192,61 @@ function renderScope(scope, depth) {
|
|
|
192
192
|
// Render each category
|
|
193
193
|
for (const [cat, catItems] of Object.entries(categories)) {
|
|
194
194
|
const catConfig = CATEGORIES[cat] || { icon: "📄", label: cat.toUpperCase(), group: null };
|
|
195
|
-
|
|
195
|
+
|
|
196
|
+
// Skills: group by bundle if any items have bundle info
|
|
197
|
+
if (cat === "skill") {
|
|
198
|
+
const bundled = {}; // source → items[]
|
|
199
|
+
const unbundled = []; // items without bundle
|
|
200
|
+
for (const item of catItems) {
|
|
201
|
+
if (item.bundle) {
|
|
202
|
+
(bundled[item.bundle] ??= []).push(item);
|
|
203
|
+
} else {
|
|
204
|
+
unbundled.push(item);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const hasBundles = Object.keys(bundled).length > 0;
|
|
209
|
+
|
|
210
|
+
html += `
|
|
211
|
+
<div class="cat-hdr" data-cat="${esc(cat)}">
|
|
212
|
+
<span class="cat-tog">▼</span>
|
|
213
|
+
<span class="cat-ico">${catConfig.icon}</span>
|
|
214
|
+
<span class="cat-nm">${catConfig.label}</span>
|
|
215
|
+
<span class="cat-cnt">${catItems.length}</span>
|
|
216
|
+
</div>
|
|
217
|
+
<div class="cat-body" data-cat="${esc(cat)}">`;
|
|
218
|
+
|
|
219
|
+
// Render each bundle as a collapsible sub-group
|
|
220
|
+
for (const [source, bundleItems] of Object.entries(bundled)) {
|
|
221
|
+
const bundleName = source.split("/").pop(); // "pbakaus/impeccable" → "impeccable"
|
|
222
|
+
const bundleLabel = source; // full "owner/repo"
|
|
223
|
+
html += `
|
|
224
|
+
<div class="bundle-hdr" data-bundle="${esc(source)}">
|
|
225
|
+
<span class="bundle-tog">▼</span>
|
|
226
|
+
<span class="bundle-ico">📦</span>
|
|
227
|
+
<span class="bundle-nm">${esc(bundleName)}</span>
|
|
228
|
+
<span class="bundle-src">${esc(bundleLabel)}</span>
|
|
229
|
+
<span class="bundle-cnt">${bundleItems.length}</span>
|
|
230
|
+
</div>
|
|
231
|
+
<div class="bundle-body" data-bundle="${esc(source)}">
|
|
232
|
+
<div class="sortable-zone" data-scope="${esc(scope.id)}" data-group="${catConfig.group || 'none'}">
|
|
233
|
+
${bundleItems.map(item => renderItem(item)).join("")}
|
|
234
|
+
</div>
|
|
235
|
+
</div>`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Render unbundled skills flat (no group header)
|
|
239
|
+
if (unbundled.length > 0) {
|
|
240
|
+
html += `
|
|
241
|
+
<div class="sortable-zone" data-scope="${esc(scope.id)}" data-group="${catConfig.group || 'none'}">
|
|
242
|
+
${unbundled.map(item => renderItem(item)).join("")}
|
|
243
|
+
</div>`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
html += `</div>`;
|
|
247
|
+
} else {
|
|
248
|
+
// Non-skill categories: render flat as before
|
|
249
|
+
html += `
|
|
196
250
|
<div class="cat-hdr" data-cat="${esc(cat)}">
|
|
197
251
|
<span class="cat-tog">▼</span>
|
|
198
252
|
<span class="cat-ico">${catConfig.icon}</span>
|
|
@@ -204,6 +258,7 @@ function renderScope(scope, depth) {
|
|
|
204
258
|
${catItems.map(item => renderItem(item)).join("")}
|
|
205
259
|
</div>
|
|
206
260
|
</div>`;
|
|
261
|
+
}
|
|
207
262
|
}
|
|
208
263
|
|
|
209
264
|
// Render child scopes
|
|
@@ -281,6 +336,15 @@ function saveExpandState() {
|
|
|
281
336
|
expandState.cats.add(catKey);
|
|
282
337
|
}
|
|
283
338
|
});
|
|
339
|
+
// Save bundle expand state
|
|
340
|
+
document.querySelectorAll(".bundle-hdr").forEach(hdr => {
|
|
341
|
+
const body = hdr.nextElementSibling;
|
|
342
|
+
const scopeId = hdr.closest(".scope-block")?.querySelector(".scope-hdr")?.dataset.scopeId || "";
|
|
343
|
+
const bundleKey = `${scopeId}::bundle::${hdr.dataset.bundle}`;
|
|
344
|
+
if (body && !body.classList.contains("c")) {
|
|
345
|
+
expandState.cats.add(bundleKey);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
284
348
|
}
|
|
285
349
|
|
|
286
350
|
function restoreExpandState() {
|
|
@@ -384,6 +448,24 @@ function initSortable() {
|
|
|
384
448
|
});
|
|
385
449
|
});
|
|
386
450
|
|
|
451
|
+
// Bundle toggle — default collapsed
|
|
452
|
+
document.querySelectorAll(".bundle-hdr").forEach(hdr => {
|
|
453
|
+
const body = hdr.nextElementSibling;
|
|
454
|
+
const tog = hdr.querySelector(".bundle-tog");
|
|
455
|
+
// Default collapsed
|
|
456
|
+
const scopeId = hdr.closest(".scope-block")?.querySelector(".scope-hdr")?.dataset.scopeId || "";
|
|
457
|
+
const bundleKey = `${scopeId}::bundle::${hdr.dataset.bundle}`;
|
|
458
|
+
if (!expandState.cats.has(bundleKey)) {
|
|
459
|
+
body?.classList.add("c");
|
|
460
|
+
tog?.classList.add("c");
|
|
461
|
+
}
|
|
462
|
+
hdr.addEventListener("click", (e) => {
|
|
463
|
+
e.stopPropagation();
|
|
464
|
+
body.classList.toggle("c");
|
|
465
|
+
tog.classList.toggle("c");
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
387
469
|
// Item click → detail panel
|
|
388
470
|
document.querySelectorAll(".item-row").forEach(row => {
|
|
389
471
|
row.addEventListener("click", (e) => {
|
|
@@ -633,6 +715,19 @@ function setupExpandToggle() {
|
|
|
633
715
|
tog?.classList.add("c");
|
|
634
716
|
}
|
|
635
717
|
});
|
|
718
|
+
|
|
719
|
+
// Also expand/collapse bundles
|
|
720
|
+
document.querySelectorAll(".bundle-hdr").forEach(hdr => {
|
|
721
|
+
const body = hdr.nextElementSibling;
|
|
722
|
+
const tog = hdr.querySelector(".bundle-tog");
|
|
723
|
+
if (allExpanded) {
|
|
724
|
+
body?.classList.remove("c");
|
|
725
|
+
tog?.classList.remove("c");
|
|
726
|
+
} else {
|
|
727
|
+
body?.classList.add("c");
|
|
728
|
+
tog?.classList.add("c");
|
|
729
|
+
}
|
|
730
|
+
});
|
|
636
731
|
});
|
|
637
732
|
}
|
|
638
733
|
|
|
@@ -670,7 +765,22 @@ function setupSearch() {
|
|
|
670
765
|
row.style.display = (!q || text.includes(q)) ? "" : "none";
|
|
671
766
|
});
|
|
672
767
|
|
|
673
|
-
//
|
|
768
|
+
// 2a. Hide bundle groups where all items are hidden
|
|
769
|
+
document.querySelectorAll(".bundle-hdr").forEach(bundleHdr => {
|
|
770
|
+
const bundleBody = bundleHdr.nextElementSibling;
|
|
771
|
+
if (!bundleBody) return;
|
|
772
|
+
const rows = bundleBody.querySelectorAll(".item-row");
|
|
773
|
+
const anyVisible = rows.length === 0 || [...rows].some(r => r.style.display !== "none");
|
|
774
|
+
bundleHdr.style.display = anyVisible ? "" : "none";
|
|
775
|
+
bundleBody.style.display = anyVisible ? "" : "none";
|
|
776
|
+
// When searching, auto-expand visible bundles
|
|
777
|
+
if (q && anyVisible) {
|
|
778
|
+
bundleBody.classList.remove("c");
|
|
779
|
+
bundleHdr.querySelector(".bundle-tog")?.classList.remove("c");
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
// 2b. Hide category sections where all items are hidden
|
|
674
784
|
document.querySelectorAll(".cat-hdr").forEach(catHdr => {
|
|
675
785
|
const catBody = catHdr.nextElementSibling;
|
|
676
786
|
if (!catBody) return;
|
package/src/ui/style.css
CHANGED
|
@@ -128,6 +128,25 @@ body {
|
|
|
128
128
|
.cat-body { padding:0; }
|
|
129
129
|
.cat-body.c { display:none; }
|
|
130
130
|
|
|
131
|
+
/* ══════════════════════════════════════
|
|
132
|
+
BUNDLE SUB-GROUP — Inside skill category
|
|
133
|
+
══════════════════════════════════════ */
|
|
134
|
+
.bundle-hdr {
|
|
135
|
+
display:flex; align-items:center; gap:8px;
|
|
136
|
+
padding:6px 12px; margin:3px 0 2px 64px; cursor:pointer;
|
|
137
|
+
background:var(--surface); border:1px solid var(--border-light);
|
|
138
|
+
border-radius:7px; user-select:none; transition:all 0.15s;
|
|
139
|
+
}
|
|
140
|
+
.bundle-hdr:hover { background:var(--hover); border-color:var(--border); }
|
|
141
|
+
.bundle-tog { width:12px; text-align:center; color:var(--text-faint); font-size:0.55rem; transition:transform 0.15s; flex-shrink:0; }
|
|
142
|
+
.bundle-tog.c { transform:rotate(-90deg); }
|
|
143
|
+
.bundle-ico { font-size:0.75rem; }
|
|
144
|
+
.bundle-nm { font-size:0.68rem; color:var(--text-secondary); font-weight:600; }
|
|
145
|
+
.bundle-src { font-size:0.58rem; color:var(--text-faint); font-family:'SF Mono','Cascadia Code','Fira Code','Courier New',monospace; }
|
|
146
|
+
.bundle-cnt { font-size:0.58rem; color:var(--text-muted); margin-left:auto; font-weight:500; }
|
|
147
|
+
.bundle-body { padding:0; }
|
|
148
|
+
.bundle-body.c { display:none; }
|
|
149
|
+
|
|
131
150
|
/* ══════════════════════════════════════
|
|
132
151
|
ITEM ROW — Least prominent
|
|
133
152
|
══════════════════════════════════════ */
|