dsh-comfyui 0.3.0-beta.6 → 0.4.0
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/README.en.md +9 -0
- package/README.md +9 -0
- package/client/client.js +140 -36
- package/client/client.js.map +1 -1
- package/lib/index.js +10 -9
- package/package.json +3 -8
package/README.en.md
CHANGED
|
@@ -16,6 +16,15 @@
|
|
|
16
16
|
<img src="https://img.shields.io/npm/l/dsh-comfyui" alt="license" />
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
|
+
> **Version pairing (pick the right dsh before installing, otherwise dshmarket shows a risk banner)**:
|
|
20
|
+
>
|
|
21
|
+
> | dsh-comfyui | Paired DeepSeek Harness | Notes |
|
|
22
|
+
> | --- | --- | --- |
|
|
23
|
+
> | **0.4.0 (latest, `latest` tag)** | **>= 0.1.2** | From 0.4.0 this plugin uses the new settings service API |
|
|
24
|
+
> | **0.3.x (beta line, `beta` tag)** | **0.1.1** | On older dsh versions stay on the 0.3.x line |
|
|
25
|
+
>
|
|
26
|
+
> Install: `dsh plugin --profile web add dsh-comfyui` (latest 0.4.0) / `dsh plugin --profile web add dsh-comfyui@beta` (0.3.x for older hosts).
|
|
27
|
+
|
|
19
28
|
## Features
|
|
20
29
|
|
|
21
30
|
### Agent tools
|
package/README.md
CHANGED
|
@@ -16,6 +16,15 @@
|
|
|
16
16
|
<img src="https://img.shields.io/npm/l/dsh-comfyui" alt="license" />
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
|
+
> **版本配对(选对 dsh 版本再装,否则 dshmarket 会报风险提示)**:
|
|
20
|
+
>
|
|
21
|
+
> | dsh-comfyui | 配对的 DeepSeek Harness | 说明 |
|
|
22
|
+
> | --- | --- | --- |
|
|
23
|
+
> | **0.4.0(最新,`latest` tag)** | **≥ 0.1.2** | 0.4.0 起使用新版 settings 服务 API |
|
|
24
|
+
> | **0.3.x(beta 线,`beta` tag)** | **0.1.1** | 老版本 dsh 请留在 0.3.x 线 |
|
|
25
|
+
>
|
|
26
|
+
> 安装:`dsh plugin --profile web add dsh-comfyui`(装最新 0.4.0)/ `dsh plugin --profile web add dsh-comfyui@beta`(老宿主装 0.3.x)。
|
|
27
|
+
|
|
19
28
|
## 功能
|
|
20
29
|
|
|
21
30
|
### Agent 工具
|
package/client/client.js
CHANGED
|
@@ -13,7 +13,7 @@ window.__ModuleLoader__.load({
|
|
|
13
13
|
baseUrl: "服务器地址",
|
|
14
14
|
baseUrlHint: "ComfyUI 的 HTTP 地址(默认 http://127.0.0.1:8188)。",
|
|
15
15
|
apiKeyEnv: "API Key 环境变量名",
|
|
16
|
-
apiKeyEnvHint: "
|
|
16
|
+
apiKeyEnvHint: "密钥名:环境变量或凭据存储(默认 COMFYUI_API_KEY)。",
|
|
17
17
|
hasApiKey: "已配置 API Key",
|
|
18
18
|
noApiKey: "未配置 API Key(本地服务通常不需要)",
|
|
19
19
|
save: "保存",
|
|
@@ -25,16 +25,16 @@ window.__ModuleLoader__.load({
|
|
|
25
25
|
testFail: "连接失败:{message}",
|
|
26
26
|
configNotWritable: "设置服务不可用,请通过 cordis.yml 修改配置。",
|
|
27
27
|
mediaHost: "媒体访问地址",
|
|
28
|
-
mediaHostHint: "
|
|
28
|
+
mediaHostHint: "媒体在聊天中显示的完整地址(如 http://192.168.1.5:3080)。留空自动检测;图片不显示时填你访问本页的地址。",
|
|
29
29
|
comfyuiDirs: "ComfyUI 目录",
|
|
30
|
-
comfyuiDirsHint: "本机 ComfyUI
|
|
30
|
+
comfyuiDirsHint: "本机 ComfyUI 安装目录(可多个,支持映射/多实例/便携版)。Agent 据此定位 models、工作流、音色库等文件。",
|
|
31
31
|
comfyuiDirAdd: "+ 添加目录",
|
|
32
32
|
comfyuiDirRemove: "移除该目录",
|
|
33
33
|
comfyuiDirPlaceholder: "如 D:\\ComfyUI",
|
|
34
34
|
skillsDir: "技能包目录",
|
|
35
|
-
skillsDirHint: "
|
|
35
|
+
skillsDirHint: "工作流技能包(SKILL.md 与参考文档)目录。留空 = 数据目录下 skills/;填绝对路径可放到别的盘或同步盘。改后需手动搬移已有目录。当前实际使用:{current}",
|
|
36
36
|
language: "界面语言",
|
|
37
|
-
languageHint: "
|
|
37
|
+
languageHint: "界面语言,切换后页面自动刷新。",
|
|
38
38
|
cardGenerating: "ComfyUI 生成中…",
|
|
39
39
|
cardBackground: "后台任务",
|
|
40
40
|
cardBackgroundDone: "已完成",
|
|
@@ -51,6 +51,7 @@ window.__ModuleLoader__.load({
|
|
|
51
51
|
cardMode: "模式",
|
|
52
52
|
cardListed: "已列出 {runs} 个运行工作流,{graphs} 个图工作流",
|
|
53
53
|
panelTitle: "ComfyUI 面板",
|
|
54
|
+
panelReset: "复位位置与大小",
|
|
54
55
|
close: "关闭",
|
|
55
56
|
lbPrev: "上一张",
|
|
56
57
|
lbNext: "下一张",
|
|
@@ -174,7 +175,7 @@ window.__ModuleLoader__.load({
|
|
|
174
175
|
skillBadgeRequired: "技能包·必读",
|
|
175
176
|
skillBadgeHint: "该工作流挂了技能包:Agent 运行前会读它。",
|
|
176
177
|
skillTitle: "技能包",
|
|
177
|
-
skillIntro: "
|
|
178
|
+
skillIntro: "点击「启用技能包」后,可为这个工作流添加给 Agent 看的额外 skills 说明:什么时候用、参数怎么填、有哪些坑。",
|
|
178
179
|
skillEnable: "启用技能包",
|
|
179
180
|
skillDetach: "停用(保留文件)",
|
|
180
181
|
skillDestroy: "销毁技能包",
|
|
@@ -182,11 +183,10 @@ window.__ModuleLoader__.load({
|
|
|
182
183
|
skillDestroyConfirmButton: "确认销毁",
|
|
183
184
|
skillDropHint: "可把文件拖到这里导入",
|
|
184
185
|
skillRequired: "运行前必读",
|
|
185
|
-
skillRequiredHint: "勾选后,Agent 未读技能包就调用本工作流会被拒绝并提示先读。",
|
|
186
186
|
skillDir: "目录",
|
|
187
187
|
skillSummary: "摘要",
|
|
188
188
|
skillSummaryPlaceholder: "一句话说明这个工作流适合做什么",
|
|
189
|
-
skillSummaryHint: "
|
|
189
|
+
skillSummaryHint: "Agent 靠它决定要不要读正文,等同 skills 里的 description 字段。",
|
|
190
190
|
skillSave: "保存",
|
|
191
191
|
skillSaved: "已保存",
|
|
192
192
|
skillDirty: "未保存",
|
|
@@ -202,6 +202,9 @@ window.__ModuleLoader__.load({
|
|
|
202
202
|
skillReveal: "📂 打开目录",
|
|
203
203
|
skillRevealHint: "在文件管理器中打开这个技能包的文件夹,方便直接拷贝文件进去。打开的是运行 DSH 那台机器上的目录;若你正从另一台机器访问本页面,请按下方显示的路径自行前往。",
|
|
204
204
|
skillRevealed: "已打开",
|
|
205
|
+
skillRefresh: "刷新",
|
|
206
|
+
skillRefreshHint: "重新读取技能包目录,显示 Agent 写入或文件管理器里新增/替换的文件。",
|
|
207
|
+
skillRefreshed: "已刷新",
|
|
205
208
|
skillNewDirPlaceholder: "目录名,如 templates",
|
|
206
209
|
skillNewDirHint: "技能包可以有任意一层子目录。常用:{presets};也可以自己起名(字母/数字/汉字/下划线/连字符)。",
|
|
207
210
|
skillDirCreated: "已创建目录",
|
|
@@ -242,7 +245,7 @@ window.__ModuleLoader__.load({
|
|
|
242
245
|
baseUrl: "Server URL",
|
|
243
246
|
baseUrlHint: "ComfyUI HTTP address (default http://127.0.0.1:8188).",
|
|
244
247
|
apiKeyEnv: "API key env var",
|
|
245
|
-
apiKeyEnvHint: "
|
|
248
|
+
apiKeyEnvHint: "Name of the key: env var or credential store (default COMFYUI_API_KEY).",
|
|
246
249
|
hasApiKey: "API key configured",
|
|
247
250
|
noApiKey: "No API key configured (usually not needed for local servers)",
|
|
248
251
|
save: "Save",
|
|
@@ -254,16 +257,16 @@ window.__ModuleLoader__.load({
|
|
|
254
257
|
testFail: "Connection failed: {message}",
|
|
255
258
|
configNotWritable: "Settings service unavailable — edit cordis.yml instead.",
|
|
256
259
|
mediaHost: "Media base URL",
|
|
257
|
-
mediaHostHint: "
|
|
260
|
+
mediaHostHint: "Base URL for media shown in chat (e.g. http://192.168.1.5:3080). Auto-detected when empty; set it if images do not load over LAN or a reverse proxy.",
|
|
258
261
|
comfyuiDirs: "ComfyUI directories",
|
|
259
|
-
comfyuiDirsHint: "ComfyUI install
|
|
262
|
+
comfyuiDirsHint: "ComfyUI install roots on this machine (multiple allowed: mapped/multiple/portable installs). The agent uses them to locate models, workflows and voice libraries.",
|
|
260
263
|
comfyuiDirAdd: "+ Add directory",
|
|
261
264
|
comfyuiDirRemove: "Remove this directory",
|
|
262
265
|
comfyuiDirPlaceholder: "e.g. D:\\ComfyUI",
|
|
263
266
|
skillsDir: "Skill pack directory",
|
|
264
|
-
skillsDirHint: "
|
|
267
|
+
skillsDirHint: "Directory for workflow skill packs (SKILL.md + reference files). Empty = skills/ under the plugin data dir; an absolute path may target another drive or a synced folder. Existing packs are not moved automatically. Currently in use: {current}",
|
|
265
268
|
language: "Language",
|
|
266
|
-
languageHint: "
|
|
269
|
+
languageHint: "UI language; the page reloads after switching.",
|
|
267
270
|
cardGenerating: "ComfyUI generating…",
|
|
268
271
|
cardBackground: "Background job",
|
|
269
272
|
cardBackgroundDone: "Completed",
|
|
@@ -280,6 +283,7 @@ window.__ModuleLoader__.load({
|
|
|
280
283
|
cardMode: "Mode",
|
|
281
284
|
cardListed: "Listed {runs} runnable workflows, {graphs} graph workflows",
|
|
282
285
|
panelTitle: "ComfyUI Panel",
|
|
286
|
+
panelReset: "Reset position & size",
|
|
283
287
|
close: "Close",
|
|
284
288
|
lbPrev: "Previous image",
|
|
285
289
|
lbNext: "Next image",
|
|
@@ -403,7 +407,7 @@ window.__ModuleLoader__.load({
|
|
|
403
407
|
skillBadgeRequired: "Skill pack (required)",
|
|
404
408
|
skillBadgeHint: "This workflow has a skill pack: the agent reads it before running.",
|
|
405
409
|
skillTitle: "Skill pack",
|
|
406
|
-
skillIntro: "
|
|
410
|
+
skillIntro: "Click \"Create skill pack\" to add extra skill instructions for this workflow: when to use it, how to fill its parameters, and which pitfalls to avoid.",
|
|
407
411
|
skillEnable: "Create skill pack",
|
|
408
412
|
skillDetach: "Detach (keep files)",
|
|
409
413
|
skillDestroy: "Destroy pack",
|
|
@@ -411,11 +415,10 @@ window.__ModuleLoader__.load({
|
|
|
411
415
|
skillDestroyConfirmButton: "Destroy",
|
|
412
416
|
skillDropHint: "Drop files here to import",
|
|
413
417
|
skillRequired: "Required before running",
|
|
414
|
-
skillRequiredHint: "When checked, running this workflow is refused until the agent has loaded the pack.",
|
|
415
418
|
skillDir: "Directory",
|
|
416
419
|
skillSummary: "Summary",
|
|
417
420
|
skillSummaryPlaceholder: "One line on what this workflow is for",
|
|
418
|
-
skillSummaryHint: "
|
|
421
|
+
skillSummaryHint: "The agent uses it to decide whether to read the body — like the description field of a skill.",
|
|
419
422
|
skillSave: "Save",
|
|
420
423
|
skillSaved: "Saved",
|
|
421
424
|
skillDirty: "Unsaved",
|
|
@@ -431,6 +434,9 @@ window.__ModuleLoader__.load({
|
|
|
431
434
|
skillReveal: "📂 Open folder",
|
|
432
435
|
skillRevealHint: "Open this pack folder in the file manager so you can copy files straight in. It opens on the machine running DSH; if you are viewing this page from another machine, use the path shown below instead.",
|
|
433
436
|
skillRevealed: "Opened",
|
|
437
|
+
skillRefresh: "Refresh",
|
|
438
|
+
skillRefreshHint: "Re-read the pack directory to show files added or replaced by the agent or the file manager.",
|
|
439
|
+
skillRefreshed: "Refreshed",
|
|
434
440
|
skillNewDirPlaceholder: "Folder name, e.g. templates",
|
|
435
441
|
skillNewDirHint: "A pack may hold any one-level sub-directory. Common ones: {presets}; custom names work too (letters, digits, CJK, underscore, dash).",
|
|
436
442
|
skillDirCreated: "Folder created",
|
|
@@ -1110,6 +1116,41 @@ window.__ModuleLoader__.load({
|
|
|
1110
1116
|
localStorage.setItem(GEOM_KEY, JSON.stringify(geom));
|
|
1111
1117
|
} catch {}
|
|
1112
1118
|
}
|
|
1119
|
+
/** Default panel width from styles.ts (`.dsc-panel` width var), used only to
|
|
1120
|
+
* estimate the panel extent while clamping a saved position. */
|
|
1121
|
+
const PANEL_WIDTH_FALLBACK = 400;
|
|
1122
|
+
/** Approx. height of the drag-handle band (`.dsc-panel-head`). */
|
|
1123
|
+
const HEAD_BAND = 48;
|
|
1124
|
+
/** Minimum margin kept between the panel and the viewport edge. */
|
|
1125
|
+
const EDGE = 8;
|
|
1126
|
+
/**
|
|
1127
|
+
* Clamp a top-left corner so the drag-handle band always stays inside the
|
|
1128
|
+
* viewport. The failure this prevents: once the header is pushed past the top
|
|
1129
|
+
* edge (or a saved position from a larger monitor no longer fits), there is no
|
|
1130
|
+
* grab surface left and the panel cannot be moved back at all.
|
|
1131
|
+
*/
|
|
1132
|
+
function clampPos(x, y, width, vw, vh) {
|
|
1133
|
+
const maxX = Math.max(EDGE, vw - width - EDGE);
|
|
1134
|
+
const maxY = Math.max(EDGE, vh - HEAD_BAND - EDGE);
|
|
1135
|
+
return {
|
|
1136
|
+
x: Math.min(Math.max(x, EDGE), maxX),
|
|
1137
|
+
y: Math.min(Math.max(y, EDGE), maxY)
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
/** Snap persisted geometry back within reach of the cursor. Geometry that
|
|
1141
|
+
* never carried an explicit position (the CSS-anchored default) is returned
|
|
1142
|
+
* unchanged; geometry already inside the safe area keeps its object identity
|
|
1143
|
+
* so callers can skip pointless re-renders. */
|
|
1144
|
+
function healGeom(geom, vw, vh) {
|
|
1145
|
+
if (geom.x === void 0 || geom.y === void 0) return geom;
|
|
1146
|
+
const pos = clampPos(geom.x, geom.y, geom.width ?? PANEL_WIDTH_FALLBACK, vw, vh);
|
|
1147
|
+
if (pos.x === geom.x && pos.y === geom.y) return geom;
|
|
1148
|
+
return {
|
|
1149
|
+
...geom,
|
|
1150
|
+
x: pos.x,
|
|
1151
|
+
y: pos.y
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1113
1154
|
/** Fallback suggestions when the config route has not answered yet; the
|
|
1114
1155
|
* authoritative list comes from the host (`presetDirs`). */
|
|
1115
1156
|
const SKILL_FALLBACK_DIRS = [
|
|
@@ -1897,7 +1938,7 @@ window.__ModuleLoader__.load({
|
|
|
1897
1938
|
function ComfyUIPanel({ t }) {
|
|
1898
1939
|
const open = usePanelOpen();
|
|
1899
1940
|
const tab = usePanelTab();
|
|
1900
|
-
const [geom, setGeom] = (0, react.useState)(loadGeom);
|
|
1941
|
+
const [geom, setGeom] = (0, react.useState)(() => healGeom(loadGeom(), window.innerWidth, window.innerHeight));
|
|
1901
1942
|
const [dragging, setDragging] = (0, react.useState)(null);
|
|
1902
1943
|
const [lightbox, setLightbox] = (0, react.useState)(null);
|
|
1903
1944
|
const panelRef = (0, react.useRef)(null);
|
|
@@ -1908,9 +1949,18 @@ window.__ModuleLoader__.load({
|
|
|
1908
1949
|
index
|
|
1909
1950
|
});
|
|
1910
1951
|
(0, react.useEffect)(() => {
|
|
1911
|
-
if (open)
|
|
1952
|
+
if (open) {
|
|
1953
|
+
setGeom((prev) => healGeom(prev, window.innerWidth, window.innerHeight));
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1912
1956
|
saveGeom(geom);
|
|
1913
1957
|
}, [open]);
|
|
1958
|
+
(0, react.useEffect)(() => {
|
|
1959
|
+
if (!open) return;
|
|
1960
|
+
const onResize = () => setGeom((prev) => healGeom(prev, window.innerWidth, window.innerHeight));
|
|
1961
|
+
window.addEventListener("resize", onResize);
|
|
1962
|
+
return () => window.removeEventListener("resize", onResize);
|
|
1963
|
+
}, [open]);
|
|
1914
1964
|
const startDrag = (event, mode) => {
|
|
1915
1965
|
if (event.button !== 0) return;
|
|
1916
1966
|
if (mode === "move") {
|
|
@@ -1934,12 +1984,14 @@ window.__ModuleLoader__.load({
|
|
|
1934
1984
|
const moveDrag = (event) => {
|
|
1935
1985
|
const d = dragRef.current;
|
|
1936
1986
|
if (d === null) return;
|
|
1937
|
-
if (dragging === "move")
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1987
|
+
if (dragging === "move") {
|
|
1988
|
+
const pos = clampPos(Math.round(d.origX + event.clientX - d.startX), Math.round(d.origY + event.clientY - d.startY), d.width, window.innerWidth, window.innerHeight);
|
|
1989
|
+
setGeom((prev) => ({
|
|
1990
|
+
...prev,
|
|
1991
|
+
x: pos.x,
|
|
1992
|
+
y: pos.y
|
|
1993
|
+
}));
|
|
1994
|
+
} else if (dragging === "resize") {
|
|
1943
1995
|
const width = Math.max(300, Math.min(720, d.width + event.clientX - d.startX));
|
|
1944
1996
|
const height = Math.max(200, Math.min(Math.max(300, window.innerHeight - 40), d.height + event.clientY - d.startY));
|
|
1945
1997
|
setGeom((prev) => ({
|
|
@@ -1978,11 +2030,22 @@ window.__ModuleLoader__.load({
|
|
|
1978
2030
|
onPointerMove: (event) => moveDrag(event),
|
|
1979
2031
|
onPointerUp: () => endDrag(),
|
|
1980
2032
|
onPointerCancel: () => endDrag()
|
|
1981
|
-
}, (0, react.createElement)("span", { className: "dsc-panel-title" }, (0, react.createElement)("span", { className: "dsc-trigger-glyph" }, (0, react.createElement)(ComfyUIIcon)), (0, react.createElement)("span", null, t("panelTitle"))), (0, react.createElement)("button", {
|
|
2033
|
+
}, (0, react.createElement)("span", { className: "dsc-panel-title" }, (0, react.createElement)("span", { className: "dsc-trigger-glyph" }, (0, react.createElement)(ComfyUIIcon)), (0, react.createElement)("span", null, t("panelTitle"))), (0, react.createElement)("span", { className: "dsc-panel-head-actions" }, (0, react.createElement)("button", {
|
|
2034
|
+
className: "dsc-panel-reset",
|
|
2035
|
+
type: "button",
|
|
2036
|
+
title: t("panelReset"),
|
|
2037
|
+
"aria-label": t("panelReset"),
|
|
2038
|
+
onClick: () => {
|
|
2039
|
+
setGeom({});
|
|
2040
|
+
try {
|
|
2041
|
+
localStorage.removeItem(GEOM_KEY);
|
|
2042
|
+
} catch {}
|
|
2043
|
+
}
|
|
2044
|
+
}, "↺"), (0, react.createElement)("button", {
|
|
1982
2045
|
className: "dsc-panel-close",
|
|
1983
2046
|
"aria-label": t("close"),
|
|
1984
2047
|
onClick: () => panelStore.close()
|
|
1985
|
-
}, "✕")), (0, react.createElement)("div", { className: "dsc-tabs" }, (0, react.createElement)(TabButton, {
|
|
2048
|
+
}, "✕"))), (0, react.createElement)("div", { className: "dsc-tabs" }, (0, react.createElement)(TabButton, {
|
|
1986
2049
|
t,
|
|
1987
2050
|
active: tab === "workflows",
|
|
1988
2051
|
label: t("tabWorkflows"),
|
|
@@ -2470,6 +2533,28 @@ window.__ModuleLoader__.load({
|
|
|
2470
2533
|
setError(cause instanceof Error ? cause.message : String(cause));
|
|
2471
2534
|
}
|
|
2472
2535
|
}, [props.workflow.id, openFile]);
|
|
2536
|
+
/** Re-read the pack listing and — only when nothing is unsaved — the file
|
|
2537
|
+
* currently open, so files added or replaced outside this panel (the agent
|
|
2538
|
+
* writing via comfyui_skill, or the file manager after `打开目录`) show up
|
|
2539
|
+
* without leaving and re-entering the view. */
|
|
2540
|
+
const refreshFiles = async () => {
|
|
2541
|
+
setBusy(true);
|
|
2542
|
+
setNotice(null);
|
|
2543
|
+
try {
|
|
2544
|
+
const data = await getJson(`/comfyui/workflows/skill?id=${encodeURIComponent(props.workflow.id)}`);
|
|
2545
|
+
setEnabled(data.enabled === true);
|
|
2546
|
+
setPack(data.pack ?? null);
|
|
2547
|
+
if (Array.isArray(data.presetDirs) && data.presetDirs.length > 0) setPresetDirs(data.presetDirs);
|
|
2548
|
+
setError(null);
|
|
2549
|
+
props.onChanged();
|
|
2550
|
+
if (data.enabled === true && !dirty) await openFile(active);
|
|
2551
|
+
setNotice(t("skillRefreshed"));
|
|
2552
|
+
} catch (cause) {
|
|
2553
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
2554
|
+
} finally {
|
|
2555
|
+
setBusy(false);
|
|
2556
|
+
}
|
|
2557
|
+
};
|
|
2473
2558
|
(0, react.useEffect)(() => {
|
|
2474
2559
|
refresh();
|
|
2475
2560
|
}, [refresh]);
|
|
@@ -2634,6 +2719,12 @@ window.__ModuleLoader__.load({
|
|
|
2634
2719
|
/** Where a new file may go: the pack's own directories first, then the
|
|
2635
2720
|
* presets it has not used yet. */
|
|
2636
2721
|
const targetDirs = [...packDirs, ...presetDirs.filter((dir) => !packDirs.includes(dir))];
|
|
2722
|
+
/** One row in the pack file list; clicking opens it for editing. */
|
|
2723
|
+
const fileButton = (file) => (0, react.createElement)("button", {
|
|
2724
|
+
key: file.path,
|
|
2725
|
+
className: `dsc-skill-file${file.path === active ? " dsc-skill-file--active" : ""}`,
|
|
2726
|
+
onClick: () => selectFile(file.path)
|
|
2727
|
+
}, (0, react.createElement)("span", { className: "dsc-skill-file-name" }, file.path.includes("/") ? file.path.split("/")[1] : file.path), (0, react.createElement)("span", { className: "dsc-skill-file-size" }, formatBytes(file.size)));
|
|
2637
2728
|
const fileList = (0, react.createElement)("div", {
|
|
2638
2729
|
className: "dsc-skill-files",
|
|
2639
2730
|
onDragOver: (event) => event.preventDefault(),
|
|
@@ -2677,7 +2768,12 @@ window.__ModuleLoader__.load({
|
|
|
2677
2768
|
disabled: busy,
|
|
2678
2769
|
title: t("skillRevealHint"),
|
|
2679
2770
|
onClick: () => void revealDir()
|
|
2680
|
-
}, t("skillReveal"))
|
|
2771
|
+
}, t("skillReveal")), (0, react.createElement)("button", {
|
|
2772
|
+
className: "dsc-btn dsc-btn--sm",
|
|
2773
|
+
disabled: busy,
|
|
2774
|
+
title: t("skillRefreshHint"),
|
|
2775
|
+
onClick: () => void refreshFiles()
|
|
2776
|
+
}, t("skillRefresh"))), creatingDir === null ? null : (0, react.createElement)("div", { className: "dsc-skill-create" }, (0, react.createElement)("input", {
|
|
2681
2777
|
className: "dsc-input dsc-input--sm",
|
|
2682
2778
|
value: creatingDir,
|
|
2683
2779
|
placeholder: t("skillNewDirPlaceholder"),
|
|
@@ -2695,14 +2791,14 @@ window.__ModuleLoader__.load({
|
|
|
2695
2791
|
}, t("skillCreate")), (0, react.createElement)("button", {
|
|
2696
2792
|
className: "dsc-btn dsc-btn--sm",
|
|
2697
2793
|
onClick: () => setCreatingDir(null)
|
|
2698
|
-
}, t("cancel")))), (0, react.createElement)("div", { className: "dsc-skill-drop-hint" }, t("skillDropHint")), buckets.map((bucket) => bucket.items.length === 0 && bucket.label !== "" ? null : (0, react.createElement)("div", {
|
|
2699
|
-
key:
|
|
2794
|
+
}, t("cancel")))), (0, react.createElement)("div", { className: "dsc-skill-drop-hint" }, t("skillDropHint")), buckets.map((bucket) => bucket.items.length === 0 && bucket.label !== "" ? null : bucket.label === "" ? (0, react.createElement)("div", {
|
|
2795
|
+
key: ".",
|
|
2700
2796
|
className: "dsc-skill-bucket"
|
|
2701
|
-
}, bucket.
|
|
2702
|
-
key:
|
|
2703
|
-
className:
|
|
2704
|
-
|
|
2705
|
-
}, (0, react.createElement)("
|
|
2797
|
+
}, bucket.items.map(fileButton)) : (0, react.createElement)("details", {
|
|
2798
|
+
key: bucket.label,
|
|
2799
|
+
className: "dsc-fold dsc-skill-fold",
|
|
2800
|
+
open: true
|
|
2801
|
+
}, (0, react.createElement)("summary", null, `${bucket.label}/`), (0, react.createElement)("div", { className: "dsc-skill-bucket" }, bucket.items.map(fileButton)))), creating === null ? null : (0, react.createElement)("div", { className: "dsc-skill-create" }, (0, react.createElement)("select", {
|
|
2706
2802
|
className: "dsc-input dsc-input--sm",
|
|
2707
2803
|
value: creating.bucket,
|
|
2708
2804
|
onChange: (event) => setCreating({
|
|
@@ -2818,7 +2914,7 @@ window.__ModuleLoader__.load({
|
|
|
2818
2914
|
action: "require",
|
|
2819
2915
|
required: event.target.checked
|
|
2820
2916
|
})
|
|
2821
|
-
}), t("skillRequired"))
|
|
2917
|
+
}), t("skillRequired"))), pack !== null ? (0, react.createElement)("div", {
|
|
2822
2918
|
className: "dsc-skill-dir",
|
|
2823
2919
|
title: pack.dir
|
|
2824
2920
|
}, `${t("skillDir")}: ${pack.dir}`) : null, pendingOpen !== null ? (0, react.createElement)("div", { className: "dsc-dialog" }, (0, react.createElement)("div", { className: "dsc-dialog-head" }, t("skillDirtyPrompt")), (0, react.createElement)("div", { className: "dsc-row" }, (0, react.createElement)("button", {
|
|
@@ -3926,6 +4022,9 @@ select.dsc-input option { background: var(--dsw-alias-bg-layer-1); color: var(--
|
|
|
3926
4022
|
}
|
|
3927
4023
|
.dsc-panel-resize:hover { opacity: 1; }
|
|
3928
4024
|
.dsc-panel-title { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: 14px; }
|
|
4025
|
+
.dsc-panel-head-actions { display: inline-flex; align-items: center; gap: 2px; }
|
|
4026
|
+
.dsc-panel-reset { border: none; background: transparent; color: var(--dsw-alias-label-tertiary); cursor: pointer; font-size: 13px; padding: 4px 6px; line-height: 1; }
|
|
4027
|
+
.dsc-panel-reset:hover { color: var(--dsw-alias-label-primary); }
|
|
3929
4028
|
.dsc-panel-close { border: none; background: transparent; color: var(--dsw-alias-label-secondary); cursor: pointer; font-size: 14px; padding: 4px 8px; }
|
|
3930
4029
|
.dsc-panel-close:hover { color: var(--dsw-alias-label-primary); }
|
|
3931
4030
|
.dsc-tabs { display: flex; border-bottom: 1px solid var(--dsw-alias-border-l1); }
|
|
@@ -3976,7 +4075,12 @@ select.dsc-input option { background: var(--dsw-alias-bg-layer-1); color: var(--
|
|
|
3976
4075
|
.dsc-skill-files { flex: 0 0 190px; min-width: 160px; display: flex; flex-direction: column; gap: 6px; }
|
|
3977
4076
|
.dsc-skill-main { flex: 1 1 300px; min-width: 260px; display: flex; flex-direction: column; gap: 6px; }
|
|
3978
4077
|
.dsc-skill-bucket { display: flex; flex-direction: column; gap: 2px; }
|
|
3979
|
-
|
|
4078
|
+
/* Foldable pack directories: each folder renders as a <details> whose header
|
|
4079
|
+
stays put while its files collapse, keeping crowded packs scannable. */
|
|
4080
|
+
.dsc-skill-fold { border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; margin: 2px 0; }
|
|
4081
|
+
.dsc-skill-fold summary { padding: 5px 10px; font-size: 12px; color: var(--dsw-alias-label-secondary); border-radius: 8px; }
|
|
4082
|
+
.dsc-skill-fold[open] summary { color: var(--dsw-alias-label-primary); border-bottom: 1px solid var(--dsw-alias-border-l1); border-radius: 8px 8px 0 0; }
|
|
4083
|
+
.dsc-skill-fold .dsc-skill-bucket { padding: 4px 6px 6px; }
|
|
3980
4084
|
.dsc-skill-file {
|
|
3981
4085
|
display: flex; align-items: center; justify-content: space-between; gap: 6px;
|
|
3982
4086
|
border: 1px solid transparent; border-radius: 6px; background: transparent;
|