@dungsil/dsh-plugin-ui-setting-skills 0.1.0 → 0.3.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.md +20 -0
- package/assets/light-dark-preview.webp +0 -0
- package/dist/client.js +342 -39
- package/dist/index.js +1071 -6
- package/locale/en.json +17 -3
- package/locale/ko.json +17 -3
- package/locale/zh.json +17 -3
- package/package.json +18 -2
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Skills Settings · 스킬 설정
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
DSH Web 설정에서 로드된 사용자 스킬을 확인하고 DSH에서의 사용 여부를 설정합니다.
|
|
6
|
+
|
|
7
|
+
위 이미지는 최신 빌드의 실제 플러그인 컴포넌트에 예시 스킬 목록·설정 응답과 공식 DSH 테마·UI primitives를 제공한 독립 브라우저 화면을 각각 캡처해 좌측 라이트·우측 다크 모드의 전체 화면을 나란히 이어 붙였습니다. 실제 DSH 세션 화면은 아니며, 스킬 이름·설명·경로와 사용 상태는 예시입니다.
|
|
8
|
+
|
|
9
|
+
## 사용 방법
|
|
10
|
+
|
|
11
|
+
1. DSH 플러그인 설치 화면에서 `@dungsil/dsh-plugin-ui-setting-skills`를 설치하고 활성화합니다.
|
|
12
|
+
2. **설정 → 스킬**을 열고 **공용 스킬** 또는 **DSH 스킬** 탭을 선택합니다.
|
|
13
|
+
3. 스킬을 펼쳐 설명과 파일 경로를 확인합니다. 오른쪽 스위치로 사용 여부를 변경하면 자동 저장합니다. 이 설정은 DSH에만 적용되며 스킬 파일은 수정하지 않습니다.
|
|
14
|
+
|
|
15
|
+
## 동작과 호환성
|
|
16
|
+
|
|
17
|
+
- DSH `0.2.1-rc.5` 이상 `0.3.0` 미만의 Web UI를 지원합니다. 한국어·영어·중국어(간체)와 DSH 테마를 따릅니다.
|
|
18
|
+
- 공용 에이전트 및 DSH 사용자 스킬 경로에서 발견한 목록을 표시합니다. 프로젝트 스킬을 편집하거나 새 스킬을 만드는 화면은 아닙니다.
|
|
19
|
+
- 사고 과정 번역 서비스가 연결되어 있으면 스킬을 펼칠 때 설명을 해당 서비스로 자동 전송해 번역하고 캐시에 저장합니다. 원문으로 전환할 수 있습니다. 번역 서비스가 없으면 원문만 표시합니다.
|
|
20
|
+
- 저장 오류가 나타나면 설정 항목을 닫았다가 다시 열어 저장 상태를 확인한 뒤 다시 시도하세요.
|
|
Binary file
|
package/dist/client.js
CHANGED
|
@@ -1,8 +1,68 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({ id: "@dungsil/dsh-plugin-ui-setting-skills", factory(require) { const module = { exports: {} }; const exports = module.exports;
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
let react = require("react");
|
|
4
|
+
let react_dom_client = require("react-dom/client");
|
|
4
5
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
5
6
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
|
+
// ../../tools/ui/src/client.ts
|
|
8
|
+
/** Each effect owns its sheet, even when another plugin installs the same defaults. */
|
|
9
|
+
function installStyles(css) {
|
|
10
|
+
const style = document.createElement("style");
|
|
11
|
+
style.textContent = css;
|
|
12
|
+
document.head.append(style);
|
|
13
|
+
return () => style.remove();
|
|
14
|
+
}
|
|
15
|
+
// ../../tools/ui/src/settings.tsx
|
|
16
|
+
/** The rc.5 settings shell has no icon slot, so decorate only the matching navigation rows. */
|
|
17
|
+
function installSettingsIcon(id, labels, icon) {
|
|
18
|
+
const attribute = "data-dsh-settings-icon";
|
|
19
|
+
const selector = `[${attribute}="${id}"]`;
|
|
20
|
+
const removeStyles = installStyles(`
|
|
21
|
+
${selector} > svg { display: none; }
|
|
22
|
+
${typeof icon === "string" ? `${selector}::before { content: ''; display: block; flex: none; width: 16px; height: 16px; background: currentColor; mask: url("data:image/svg+xml,${encodeURIComponent(icon)}") center / contain no-repeat; }` : ""}
|
|
23
|
+
${selector} > [data-dsh-settings-icon-glyph] { display: inline-flex; flex: none; }
|
|
24
|
+
`);
|
|
25
|
+
const marked = /* @__PURE__ */ new Map();
|
|
26
|
+
const refresh = () => {
|
|
27
|
+
const rows = new Set(document.querySelectorAll("[data-shortcut-modal=\"settings\"] nav button"));
|
|
28
|
+
for (const [row, dispose] of marked) if (!rows.has(row) || !labels.includes(row.textContent?.trim() ?? "")) {
|
|
29
|
+
row.removeAttribute(attribute);
|
|
30
|
+
dispose();
|
|
31
|
+
marked.delete(row);
|
|
32
|
+
}
|
|
33
|
+
for (const row of rows) if (!marked.has(row) && labels.includes(row.textContent?.trim() ?? "")) {
|
|
34
|
+
row.setAttribute(attribute, id);
|
|
35
|
+
let dispose = () => {};
|
|
36
|
+
if (typeof icon !== "string") {
|
|
37
|
+
const glyph = document.createElement("span");
|
|
38
|
+
glyph.setAttribute("data-dsh-settings-icon-glyph", "");
|
|
39
|
+
glyph.setAttribute("aria-hidden", "true");
|
|
40
|
+
row.prepend(glyph);
|
|
41
|
+
const unmount = icon(glyph);
|
|
42
|
+
dispose = () => {
|
|
43
|
+
unmount();
|
|
44
|
+
glyph.remove();
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
marked.set(row, dispose);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const observer = new MutationObserver(refresh);
|
|
51
|
+
observer.observe(document.body, {
|
|
52
|
+
childList: true,
|
|
53
|
+
subtree: true,
|
|
54
|
+
characterData: true
|
|
55
|
+
});
|
|
56
|
+
refresh();
|
|
57
|
+
return () => {
|
|
58
|
+
observer.disconnect();
|
|
59
|
+
for (const [row, dispose] of marked) {
|
|
60
|
+
row.removeAttribute(attribute);
|
|
61
|
+
dispose();
|
|
62
|
+
}
|
|
63
|
+
removeStyles();
|
|
64
|
+
};
|
|
65
|
+
}
|
|
6
66
|
// locale/en.json
|
|
7
67
|
var en_default = {
|
|
8
68
|
meta: {
|
|
@@ -11,13 +71,27 @@ var en_default = {
|
|
|
11
71
|
},
|
|
12
72
|
ui: {
|
|
13
73
|
"nav": "Skills",
|
|
14
|
-
"description": "
|
|
15
|
-
"agents": "Shared
|
|
74
|
+
"description": "This setting applies only to DSH.",
|
|
75
|
+
"agents": "Shared skills",
|
|
16
76
|
"dsh": "DSH skills",
|
|
17
77
|
"loading": "Loading skills…",
|
|
18
78
|
"empty": "No loaded skills from this source.",
|
|
19
79
|
"error": "Could not load skills. Close and reopen this settings section to try again.",
|
|
20
|
-
"incomplete": "Some skill sources could not be loaded. This list may be incomplete."
|
|
80
|
+
"incomplete": "Some skill sources could not be loaded. This list may be incomplete.",
|
|
81
|
+
"use": "Use",
|
|
82
|
+
"disabled": "Off",
|
|
83
|
+
"openFolder": "Open folder",
|
|
84
|
+
"openingFolder": "Opening folder…",
|
|
85
|
+
"openFolderHint": "Open this skill’s folder in the file manager on the DSH host.",
|
|
86
|
+
"openFolderUnavailable": "Folder opening requires a desktop on the DSH host.",
|
|
87
|
+
"openFolderError": "Could not open the skill folder. Check the file manager on the DSH host and try again.",
|
|
88
|
+
"translate": "View translation",
|
|
89
|
+
"original": "View original",
|
|
90
|
+
"retry": "Retry",
|
|
91
|
+
"translating": "Translating…",
|
|
92
|
+
"translationNotice": "Expanding a skill automatically sends its description to the service selected in reasoning translation settings. Translations are cached using its target language.",
|
|
93
|
+
"translationError": "Could not translate. The original is shown. Paid services require a Session and are unavailable here.",
|
|
94
|
+
"saveError": "Could not save the setting. Close and reopen this section to check the saved state before trying again."
|
|
21
95
|
}
|
|
22
96
|
};
|
|
23
97
|
// locale/ko.json
|
|
@@ -28,13 +102,27 @@ var ko_default = {
|
|
|
28
102
|
},
|
|
29
103
|
ui: {
|
|
30
104
|
"nav": "스킬",
|
|
31
|
-
"description": "
|
|
32
|
-
"agents": "공용
|
|
105
|
+
"description": "이 설정은 DSH에만 적용됩니다.",
|
|
106
|
+
"agents": "공용 스킬",
|
|
33
107
|
"dsh": "DSH 스킬",
|
|
34
108
|
"loading": "스킬을 불러오는 중입니다…",
|
|
35
109
|
"empty": "이 경로에서 로드된 스킬이 없습니다.",
|
|
36
110
|
"error": "스킬을 불러오지 못했습니다. 이 설정 항목을 닫았다가 다시 열어 주세요.",
|
|
37
|
-
"incomplete": "일부 스킬 소스를 불러오지 못해 목록이 완전하지 않을 수 있습니다."
|
|
111
|
+
"incomplete": "일부 스킬 소스를 불러오지 못해 목록이 완전하지 않을 수 있습니다.",
|
|
112
|
+
"use": "사용",
|
|
113
|
+
"disabled": "꺼짐",
|
|
114
|
+
"openFolder": "폴더 열기",
|
|
115
|
+
"openingFolder": "폴더를 여는 중…",
|
|
116
|
+
"openFolderHint": "DSH 실행 호스트의 파일 탐색기에서 이 스킬의 폴더를 엽니다.",
|
|
117
|
+
"openFolderUnavailable": "폴더를 열려면 DSH 실행 호스트에 데스크톱 환경이 필요합니다.",
|
|
118
|
+
"openFolderError": "스킬 폴더를 열지 못했습니다. DSH 실행 호스트의 파일 탐색기를 확인한 후 다시 시도해 주세요.",
|
|
119
|
+
"translate": "번역 보기",
|
|
120
|
+
"original": "원문 보기",
|
|
121
|
+
"retry": "다시 시도",
|
|
122
|
+
"translating": "번역 중…",
|
|
123
|
+
"translationNotice": "스킬을 펼치면 사고 과정 번역 설정에서 선택한 서비스로 설명을 자동 전송합니다. 번역문은 해당 설정의 대상 언어에 따라 캐시에 저장합니다.",
|
|
124
|
+
"translationError": "번역하지 못해 원문을 표시합니다. 유료 서비스는 세션이 필요하므로 이 화면에서 사용할 수 없습니다.",
|
|
125
|
+
"saveError": "설정을 저장하지 못했습니다. 이 설정 항목을 닫았다가 다시 열어 저장 상태를 확인한 후 다시 시도해 주세요."
|
|
38
126
|
}
|
|
39
127
|
};
|
|
40
128
|
// locale/zh.json
|
|
@@ -45,18 +133,34 @@ var zh_default = {
|
|
|
45
133
|
},
|
|
46
134
|
ui: {
|
|
47
135
|
"nav": "技能",
|
|
48
|
-
"description": "
|
|
49
|
-
"agents": "
|
|
136
|
+
"description": "此设置仅适用于 DSH。",
|
|
137
|
+
"agents": "共享技能",
|
|
50
138
|
"dsh": "DSH 技能",
|
|
51
139
|
"loading": "正在加载技能…",
|
|
52
140
|
"empty": "未从此目录加载任何技能。",
|
|
53
141
|
"error": "无法加载技能。请关闭并重新打开此设置项后重试。",
|
|
54
|
-
"incomplete": "部分技能源无法加载,列表可能不完整。"
|
|
142
|
+
"incomplete": "部分技能源无法加载,列表可能不完整。",
|
|
143
|
+
"use": "使用",
|
|
144
|
+
"disabled": "已关闭",
|
|
145
|
+
"openFolder": "打开文件夹",
|
|
146
|
+
"openingFolder": "正在打开文件夹…",
|
|
147
|
+
"openFolderHint": "在 DSH 主机的文件管理器中打开此技能的文件夹。",
|
|
148
|
+
"openFolderUnavailable": "打开文件夹需要 DSH 主机具备桌面环境。",
|
|
149
|
+
"openFolderError": "无法打开技能文件夹。请检查 DSH 主机的文件管理器后重试。",
|
|
150
|
+
"translate": "查看译文",
|
|
151
|
+
"original": "查看原文",
|
|
152
|
+
"retry": "重试",
|
|
153
|
+
"translating": "正在翻译…",
|
|
154
|
+
"translationNotice": "展开技能时,会自动将描述发送至思考过程翻译设置中选择的服务。译文按该设置的目标语言缓存。",
|
|
155
|
+
"translationError": "翻译失败,已显示原文。付费服务需要会话,无法在此页面使用。",
|
|
156
|
+
"saveError": "无法保存设置。请关闭并重新打开此设置项,确认已保存的状态后重试。"
|
|
55
157
|
}
|
|
56
158
|
};
|
|
57
159
|
// src/protocol.ts
|
|
58
160
|
const channel = "/api";
|
|
59
161
|
const method = "dungsil-ui-setting-skills/list";
|
|
162
|
+
const saveMethod = "dungsil-ui-setting-skills/save";
|
|
163
|
+
const translateMethod = "dungsil-ui-setting-skills/translate";
|
|
60
164
|
// src/client.tsx
|
|
61
165
|
const name = "dungsil-ui-setting-skills";
|
|
62
166
|
const inject = [
|
|
@@ -73,24 +177,87 @@ const styles = `
|
|
|
73
177
|
.dsh-skills-settings ul { list-style: none; margin: 0; padding: 0; }
|
|
74
178
|
.dsh-skills-settings code { font-size: 12px; overflow-wrap: anywhere; }
|
|
75
179
|
.dsh-skills-settings [role=alert] { color: var(--dsw-alias-state-error-primary, #b54424); }
|
|
76
|
-
.dsh-skills-settings
|
|
180
|
+
.dsh-skills-settings li { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 16px; border-bottom: .5px solid var(--dsw-alias-border-l2); }
|
|
181
|
+
.dsh-skills-settings details { min-width: 0; }
|
|
77
182
|
.dsh-skills-settings summary { display: flex; align-items: center; gap: 12px; padding: 16px 0; list-style: none; cursor: pointer; border-radius: var(--dsw-radius-md); }
|
|
78
183
|
.dsh-skills-settings summary::-webkit-details-marker { display: none; }
|
|
79
184
|
.dsh-skills-settings summary:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
80
185
|
.dsh-skills-settings summary:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary-new-colorprimary-new-color); outline-offset: 2px; }
|
|
81
186
|
.dsh-skills-settings summary svg { flex: none; }
|
|
82
187
|
.dsh-skills-settings details[open] summary svg { transform: rotate(90deg); }
|
|
83
|
-
.dsh-skills-summary-text { display: flex;
|
|
84
|
-
.dsh-skills-
|
|
85
|
-
.dsh-skills-
|
|
86
|
-
.dsh-skills-
|
|
87
|
-
.dsh-skills-
|
|
188
|
+
.dsh-skills-summary-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
|
|
189
|
+
.dsh-skills-title { display: flex; align-items: baseline; gap: 12px; min-width: 0; }
|
|
190
|
+
.dsh-skills-title strong { min-width: 0; overflow-wrap: anywhere; }
|
|
191
|
+
.dsh-skills-title .dsh-skills-muted { flex: none; font-size: 12px; }
|
|
192
|
+
.dsh-skills-preview { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
88
193
|
.dsh-skills-detail { padding: 0 0 16px 28px; overflow-wrap: anywhere; }
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
194
|
+
.dsh-skills-actions { display: flex; align-items: center; align-self: start; gap: 12px; height: 78px; }
|
|
195
|
+
.dsh-skills-folder { width: 28px; padding: 0; }
|
|
196
|
+
.dsh-skills-open-error { grid-column: 1 / -1; padding: 0 0 16px 28px; }
|
|
197
|
+
.dsh-skills-list-header { display: flex; justify-content: space-between; gap: 16px; padding: 8px 0; border-bottom: .5px solid var(--dsw-alias-border-l2); }
|
|
93
198
|
`;
|
|
199
|
+
function SkillDescription({ skill, connection, language, available, expanded }) {
|
|
200
|
+
const text = localizedCopy(language);
|
|
201
|
+
const [translated, setTranslated] = (0, react.useState)();
|
|
202
|
+
const [original, setOriginal] = (0, react.useState)(false);
|
|
203
|
+
const [pending, setPending] = (0, react.useState)(false);
|
|
204
|
+
const [failed, setFailed] = (0, react.useState)(false);
|
|
205
|
+
const [retry, setRetry] = (0, react.useState)(0);
|
|
206
|
+
(0, react.useEffect)(() => {
|
|
207
|
+
setTranslated(void 0);
|
|
208
|
+
setOriginal(false);
|
|
209
|
+
setPending(false);
|
|
210
|
+
setFailed(false);
|
|
211
|
+
if (!expanded || !available || !skill.path || !skill.description) return;
|
|
212
|
+
const request = new AbortController();
|
|
213
|
+
setPending(true);
|
|
214
|
+
(async () => {
|
|
215
|
+
try {
|
|
216
|
+
const result = await connection.rpc.call(channel, translateMethod, {
|
|
217
|
+
path: skill.path,
|
|
218
|
+
language
|
|
219
|
+
}, request.signal);
|
|
220
|
+
if (request.signal.aborted) return;
|
|
221
|
+
if (!result.ok || typeof result.value !== "string") throw new Error("Could not translate description");
|
|
222
|
+
setTranslated(result.value);
|
|
223
|
+
} catch {
|
|
224
|
+
if (!request.signal.aborted) setFailed(true);
|
|
225
|
+
} finally {
|
|
226
|
+
if (!request.signal.aborted) setPending(false);
|
|
227
|
+
}
|
|
228
|
+
})();
|
|
229
|
+
return () => request.abort();
|
|
230
|
+
}, [
|
|
231
|
+
expanded,
|
|
232
|
+
skill.description,
|
|
233
|
+
skill.path,
|
|
234
|
+
language,
|
|
235
|
+
available,
|
|
236
|
+
connection,
|
|
237
|
+
retry
|
|
238
|
+
]);
|
|
239
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: original || translated === void 0 ? skill.description : translated }), available && skill.path && skill.description && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
240
|
+
pending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
241
|
+
role: "status",
|
|
242
|
+
children: text.translating
|
|
243
|
+
}),
|
|
244
|
+
translated !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
245
|
+
variant: "ghost",
|
|
246
|
+
size: "sm",
|
|
247
|
+
onClick: () => setOriginal((value) => !value),
|
|
248
|
+
children: original ? text.translate : text.original
|
|
249
|
+
}),
|
|
250
|
+
failed && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
251
|
+
role: "alert",
|
|
252
|
+
children: text.translationError
|
|
253
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
254
|
+
variant: "ghost",
|
|
255
|
+
size: "sm",
|
|
256
|
+
onClick: () => setRetry((value) => value + 1),
|
|
257
|
+
children: text.retry
|
|
258
|
+
})] })
|
|
259
|
+
] })] });
|
|
260
|
+
}
|
|
94
261
|
function SkillsSettings({ ctx }) {
|
|
95
262
|
const locale = (0, react.useSyncExternalStore)((listener) => ctx.locale.subscribe(listener), () => ctx.locale.getSnapshot());
|
|
96
263
|
const text = localizedCopy(locale.active);
|
|
@@ -98,6 +265,14 @@ function SkillsSettings({ ctx }) {
|
|
|
98
265
|
const connection = ctx.get("connection");
|
|
99
266
|
const [view, setView] = (0, react.useState)();
|
|
100
267
|
const [error, setError] = (0, react.useState)(false);
|
|
268
|
+
const [saveError, setSaveError] = (0, react.useState)(false);
|
|
269
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
270
|
+
const [opening, setOpening] = (0, react.useState)();
|
|
271
|
+
const [openError, setOpenError] = (0, react.useState)();
|
|
272
|
+
const openingPending = (0, react.useRef)(false);
|
|
273
|
+
const pending = (0, react.useRef)(false);
|
|
274
|
+
const mounted = (0, react.useRef)(false);
|
|
275
|
+
const [expanded, setExpanded] = (0, react.useState)({});
|
|
101
276
|
const [source, setSource] = (0, react.useState)("user-agents");
|
|
102
277
|
const groups = ["user-agents", "user-dsh"].map((value) => ({
|
|
103
278
|
value,
|
|
@@ -106,6 +281,7 @@ function SkillsSettings({ ctx }) {
|
|
|
106
281
|
}));
|
|
107
282
|
(0, react.useEffect)(() => {
|
|
108
283
|
const controller = new AbortController();
|
|
284
|
+
mounted.current = true;
|
|
109
285
|
setView(void 0);
|
|
110
286
|
setError(false);
|
|
111
287
|
(async () => {
|
|
@@ -118,8 +294,48 @@ function SkillsSettings({ ctx }) {
|
|
|
118
294
|
if (!controller.signal.aborted) setError(true);
|
|
119
295
|
}
|
|
120
296
|
})();
|
|
121
|
-
return () =>
|
|
297
|
+
return () => {
|
|
298
|
+
mounted.current = false;
|
|
299
|
+
controller.abort();
|
|
300
|
+
};
|
|
122
301
|
}, [connection]);
|
|
302
|
+
async function save(name, disabled) {
|
|
303
|
+
if (pending.current) return;
|
|
304
|
+
pending.current = true;
|
|
305
|
+
setBusy(true);
|
|
306
|
+
setSaveError(false);
|
|
307
|
+
try {
|
|
308
|
+
const result = await connection.rpc.call(channel, saveMethod, {
|
|
309
|
+
name,
|
|
310
|
+
disabled
|
|
311
|
+
});
|
|
312
|
+
if (!result.ok) throw new Error("Could not save skills");
|
|
313
|
+
const value = result.value;
|
|
314
|
+
if (mounted.current) setView((current) => current && {
|
|
315
|
+
...current,
|
|
316
|
+
disabled: value.disabled
|
|
317
|
+
});
|
|
318
|
+
} catch {
|
|
319
|
+
if (mounted.current) setSaveError(true);
|
|
320
|
+
} finally {
|
|
321
|
+
pending.current = false;
|
|
322
|
+
if (mounted.current) setBusy(false);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async function openFolder(path) {
|
|
326
|
+
if (openingPending.current) return;
|
|
327
|
+
openingPending.current = true;
|
|
328
|
+
setOpening(path);
|
|
329
|
+
setOpenError(void 0);
|
|
330
|
+
try {
|
|
331
|
+
if (!(await connection.rpc.call("/api", "dungsil-ui-setting-skills/open-folder", { path })).ok) throw new Error("Could not open skill folder");
|
|
332
|
+
} catch {
|
|
333
|
+
if (mounted.current) setOpenError(path);
|
|
334
|
+
} finally {
|
|
335
|
+
openingPending.current = false;
|
|
336
|
+
if (mounted.current) setOpening(void 0);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
123
339
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
124
340
|
className: "dsh-skills-settings",
|
|
125
341
|
"aria-label": text.nav,
|
|
@@ -129,6 +345,14 @@ function SkillsSettings({ ctx }) {
|
|
|
129
345
|
className: "dsh-skills-muted",
|
|
130
346
|
children: text.description
|
|
131
347
|
}),
|
|
348
|
+
view && !view.folderOpenAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
349
|
+
className: "dsh-skills-muted",
|
|
350
|
+
children: text.openFolderUnavailable
|
|
351
|
+
}),
|
|
352
|
+
view?.translationAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
353
|
+
className: "dsh-skills-muted",
|
|
354
|
+
children: text.translationNotice
|
|
355
|
+
}),
|
|
132
356
|
!view && !error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
133
357
|
role: "status",
|
|
134
358
|
children: text.loading
|
|
@@ -137,6 +361,10 @@ function SkillsSettings({ ctx }) {
|
|
|
137
361
|
role: "alert",
|
|
138
362
|
children: text.error
|
|
139
363
|
}),
|
|
364
|
+
saveError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
365
|
+
role: "alert",
|
|
366
|
+
children: text.saveError
|
|
367
|
+
}),
|
|
140
368
|
view && !view.complete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
141
369
|
role: "alert",
|
|
142
370
|
children: text.incomplete
|
|
@@ -160,31 +388,106 @@ function SkillsSettings({ ctx }) {
|
|
|
160
388
|
children: group.skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
161
389
|
className: "dsh-skills-muted",
|
|
162
390
|
children: text.empty
|
|
163
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.
|
|
164
|
-
"
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
391
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
392
|
+
className: "dsh-skills-list-header dsh-skills-muted",
|
|
393
|
+
"aria-hidden": "true",
|
|
394
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
395
|
+
text.nav,
|
|
396
|
+
" ",
|
|
397
|
+
group.skills.length
|
|
398
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text.use })]
|
|
399
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { children: group.skills.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
400
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
401
|
+
onToggle: (event) => {
|
|
402
|
+
const open = event.currentTarget.open;
|
|
403
|
+
setExpanded((current) => ({
|
|
404
|
+
...current,
|
|
405
|
+
[skill.path ?? skill.name]: open
|
|
406
|
+
}));
|
|
407
|
+
},
|
|
408
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
|
|
409
|
+
"aria-label": skill.name,
|
|
410
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, {
|
|
411
|
+
size: 16,
|
|
412
|
+
"aria-hidden": "true"
|
|
413
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
414
|
+
className: "dsh-skills-summary-text",
|
|
415
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
416
|
+
className: "dsh-skills-title",
|
|
417
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: skill.name }), view.disabled.includes(skill.name) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
418
|
+
className: "dsh-skills-muted",
|
|
419
|
+
children: text.disabled
|
|
420
|
+
})]
|
|
421
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
422
|
+
className: "dsh-skills-preview dsh-skills-muted",
|
|
423
|
+
"aria-hidden": "true",
|
|
424
|
+
children: skill.description
|
|
425
|
+
})]
|
|
426
|
+
})]
|
|
427
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
428
|
+
className: "dsh-skills-detail",
|
|
429
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillDescription, {
|
|
430
|
+
skill,
|
|
431
|
+
connection,
|
|
432
|
+
language: locale.active,
|
|
433
|
+
available: view.translationAvailable && source === group.value,
|
|
434
|
+
expanded: expanded[skill.path ?? skill.name] ?? false
|
|
435
|
+
}), skill.path && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
436
|
+
className: "dsh-skills-muted",
|
|
437
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: skill.path })
|
|
438
|
+
})]
|
|
174
439
|
})]
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
440
|
+
}),
|
|
441
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
442
|
+
className: "dsh-skills-actions",
|
|
443
|
+
children: [skill.path && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
444
|
+
label: opening === skill.path ? text.openingFolder : text.openFolder,
|
|
445
|
+
side: "top",
|
|
446
|
+
portal: true,
|
|
447
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
448
|
+
className: "dsh-skills-folder",
|
|
449
|
+
variant: "ghost",
|
|
450
|
+
size: "sm",
|
|
451
|
+
"aria-label": `${opening === skill.path ? text.openingFolder : text.openFolder}: ${skill.name}`,
|
|
452
|
+
"aria-busy": opening === skill.path,
|
|
453
|
+
disabled: !view.folderOpenAvailable || opening !== void 0,
|
|
454
|
+
"aria-description": view.folderOpenAvailable ? text.openFolderHint : text.openFolderUnavailable,
|
|
455
|
+
onClick: () => void openFolder(skill.path),
|
|
456
|
+
icon: opening === skill.path ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutlineRegular, {
|
|
457
|
+
size: 16,
|
|
458
|
+
"aria-hidden": "true"
|
|
459
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutlineRegular, {
|
|
460
|
+
size: 16,
|
|
461
|
+
"aria-hidden": "true"
|
|
462
|
+
})
|
|
463
|
+
})
|
|
464
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
465
|
+
label: `${text.use}: ${skill.name}`,
|
|
466
|
+
checked: !view.disabled.includes(skill.name),
|
|
467
|
+
disabled: busy || saveError,
|
|
468
|
+
onChange: (enabled) => void save(skill.name, !enabled)
|
|
469
|
+
})]
|
|
470
|
+
}),
|
|
471
|
+
skill.path && openError === skill.path && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
472
|
+
className: "dsh-skills-open-error",
|
|
473
|
+
role: "alert",
|
|
474
|
+
children: text.openFolderError
|
|
475
|
+
})
|
|
476
|
+
] }, skill.path ?? skill.name)) })] })
|
|
183
477
|
}, group.value))] })
|
|
184
478
|
]
|
|
185
479
|
});
|
|
186
480
|
}
|
|
187
481
|
function apply(ctx) {
|
|
482
|
+
ctx.effect(() => installSettingsIcon(name, [
|
|
483
|
+
en_default.ui.nav,
|
|
484
|
+
ko_default.ui.nav,
|
|
485
|
+
zh_default.ui.nav
|
|
486
|
+
], (container) => {
|
|
487
|
+
const root = (0, react_dom_client.createRoot)(container);
|
|
488
|
+
root.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutlineRegular, { size: 16 }));
|
|
489
|
+
return () => root.unmount();
|
|
490
|
+
}));
|
|
188
491
|
ctx.effect(() => {
|
|
189
492
|
const style = document.createElement("style");
|
|
190
493
|
style.textContent = styles;
|