@civilization/deepseek-harness-skill-mcp 0.1.5 → 0.1.6
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/lib/client.js +165 -57
- package/package.json +1 -1
- package/src/client/controller.js +9 -0
- package/src/client/index.jsx +48 -18
- package/src/client/locales.js +8 -4
- package/src/client/styles.css +2 -0
package/lib/client.js
CHANGED
|
@@ -30,6 +30,17 @@ module.exports = __toCommonJS(index_exports);
|
|
|
30
30
|
var import_react = require("react");
|
|
31
31
|
|
|
32
32
|
// src/client/controller.js
|
|
33
|
+
var skillCatalogMutations = /* @__PURE__ */ new Set([
|
|
34
|
+
"add-skill",
|
|
35
|
+
"update-skill-source",
|
|
36
|
+
"create-skill",
|
|
37
|
+
"update-skill",
|
|
38
|
+
"delete-skill",
|
|
39
|
+
"set-skill-invocation"
|
|
40
|
+
]);
|
|
41
|
+
function refreshesSlashCatalog(endpoint, options = {}) {
|
|
42
|
+
return options.refreshSlashCatalog === true || skillCatalogMutations.has(endpoint);
|
|
43
|
+
}
|
|
33
44
|
var ExtensionsController = class {
|
|
34
45
|
state = { extensions: [], sources: [], skills: [], revision: "", loading: false, saving: false, error: "", saved: false };
|
|
35
46
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -110,10 +121,16 @@ var zh = {
|
|
|
110
121
|
create: "\u521B\u5EFA",
|
|
111
122
|
edit: "\u7F16\u8F91",
|
|
112
123
|
delete: "\u5220\u9664",
|
|
124
|
+
confirmDelete: "\u786E\u8BA4\u5220\u9664",
|
|
125
|
+
deleting: "\u5220\u9664\u4E2D\u2026",
|
|
113
126
|
saving: "\u4FDD\u5B58\u4E2D\u2026",
|
|
114
127
|
loading: "\u52A0\u8F7D\u4E2D\u2026",
|
|
115
|
-
|
|
116
|
-
|
|
128
|
+
deleteExtensionTitle: "\u79FB\u9664 Skill \u6765\u6E90",
|
|
129
|
+
deleteMcpTitle: "\u5220\u9664 MCP \u670D\u52A1\u5668",
|
|
130
|
+
deleteSkillTitle: "\u6C38\u4E45\u5220\u9664 Skill",
|
|
131
|
+
deleteExtensionConfirm: "\u5C06\u4ECE\u5F53\u524D\u914D\u7F6E\u4E2D\u79FB\u9664\u201C{name}\u201D\uFF0C\u76EE\u5F55\u4E2D\u7684 Skill \u6587\u4EF6\u4F1A\u4FDD\u7559\u3002",
|
|
132
|
+
deleteMcpConfirm: "\u5C06\u5220\u9664 MCP \u670D\u52A1\u5668\u201C{name}\u201D\u53CA\u5176\u8FDE\u63A5\u914D\u7F6E\u3002",
|
|
133
|
+
deleteSkillConfirm: "\u5C06\u6C38\u4E45\u5220\u9664\u201C{name}\u201D\u53CA\u5176\u76EE\u5F55\u5185\u7684\u8D44\u6E90\u6587\u4EF6\u3002\u6B64\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500\u3002",
|
|
117
134
|
enabled: "\u914D\u7F6E\u5DF2\u542F\u7528",
|
|
118
135
|
disabled: "\u5DF2\u7981\u7528",
|
|
119
136
|
enable: "\u542F\u7528",
|
|
@@ -214,10 +231,16 @@ var en = {
|
|
|
214
231
|
create: "Create",
|
|
215
232
|
edit: "Edit",
|
|
216
233
|
delete: "Delete",
|
|
234
|
+
confirmDelete: "Delete",
|
|
235
|
+
deleting: "Deleting\u2026",
|
|
217
236
|
saving: "Saving\u2026",
|
|
218
237
|
loading: "Loading\u2026",
|
|
219
|
-
|
|
220
|
-
|
|
238
|
+
deleteExtensionTitle: "Remove Skill source",
|
|
239
|
+
deleteMcpTitle: "Delete MCP server",
|
|
240
|
+
deleteSkillTitle: "Permanently delete Skill",
|
|
241
|
+
deleteExtensionConfirm: "Remove \u201C{name}\u201D from this configuration. Skill files in the directory will be kept.",
|
|
242
|
+
deleteMcpConfirm: "Delete MCP server \u201C{name}\u201D and its connection configuration.",
|
|
243
|
+
deleteSkillConfirm: "Permanently delete \u201C{name}\u201D and the resource files in its directory. This cannot be undone.",
|
|
221
244
|
enabled: "Configured enabled",
|
|
222
245
|
disabled: "Disabled",
|
|
223
246
|
enable: "Enable",
|
|
@@ -290,7 +313,7 @@ var en = {
|
|
|
290
313
|
};
|
|
291
314
|
|
|
292
315
|
// src/client/styles.css
|
|
293
|
-
var styles_default = ".dsh-ext{color:var(--dsw-alias-label-primary);max-width:840px;padding:4px 0 28px;font:inherit}\n.dsh-ext-heading,.dsh-ext-card>header{display:flex;justify-content:space-between;align-items:center;gap:16px}\n.dsh-ext h2{font-size:22px;letter-spacing:-.4px;margin:0 0 8px}.dsh-ext h3{font-size:15px;margin:0;display:flex;align-items:center;gap:8px}\n.dsh-ext p,.dsh-ext small{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.6}.dsh-ext-heading p{margin:0}\n.dsh-ext button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:inherit;padding:8px 13px;white-space:nowrap}\n.dsh-ext button:hover{background:var(--dsw-alias-bg-layer-3)}.dsh-ext button:disabled{opacity:.5;cursor:default}\n.dsh-ext :is(button,input,select,textarea):focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}\n.dsh-ext-card{border:1px solid var(--dsw-alias-border-l4);border-radius:16px;margin-top:20px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}\n.dsh-ext-card>header{padding:18px 20px;border-bottom:1px solid var(--dsw-alias-border-l4)}.dsh-ext-count{font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary)}\n.dsh-ext-empty{padding:32px 20px}.dsh-ext-empty strong{font-size:14px;font-weight:500}.dsh-ext-empty p{margin:6px 0 0}\n.dsh-ext ul{list-style:none;padding:0;margin:0}.dsh-ext li{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:16px 20px;border-bottom:1px solid var(--dsw-alias-border-l4)}.dsh-ext li:last-child{border:0}\n.dsh-ext-details{display:flex;flex-direction:column;gap:5px;min-width:0}.dsh-ext-details strong{font-size:14px}.dsh-ext-details code{font-size:12px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}\n.dsh-ext-runtime{display:flex;align-items:center;flex-wrap:wrap;gap:7px;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsh-ext-status{width:7px;height:7px;border-radius:50%;background:#858585;box-shadow:0 0 0 3px color-mix(in srgb,#858585 16%,transparent)}\n.dsh-ext-status-available{background:#38a169;box-shadow:0 0 0 3px color-mix(in srgb,#38a169 18%,transparent)}.dsh-ext-status-waiting{background:#d69e2e;box-shadow:0 0 0 3px color-mix(in srgb,#d69e2e 18%,transparent)}\n.dsh-ext-runtime details{width:100%;margin-top:2px}.dsh-ext-runtime summary{width:max-content;cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none}.dsh-ext-runtime summary:hover{color:var(--dsw-alias-label-primary)}\n.dsh-ext-runtime .dsh-ext-tools{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.dsh-ext-runtime .dsh-ext-tools li{display:block;border:0;padding:4px 8px;border-radius:6px;background:var(--dsw-alias-bg-layer-3)}\n.dsh-ext-form{padding:20px;border-top:1px solid var(--dsw-alias-border-l4)}.dsh-ext fieldset{border:0;padding:0;margin:0;display:grid;grid-template-columns:1fr 1fr;gap:16px}.dsh-ext label{display:flex;flex-direction:column;gap:7px;font-size:13px}\n.dsh-ext input,.dsh-ext select,.dsh-ext textarea{box-sizing:border-box;min-width:0;width:100%;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l4);border-radius:8px;padding:10px}.dsh-ext textarea{resize:vertical}\n.dsh-ext-actions{display:flex;justify-content:flex-end;gap:8px;grid-column:1/-1}.dsh-ext fieldset>p{grid-column:1/-1;margin:0}.dsh-ext-notice{padding:12px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:10px}.dsh-ext-footnote{padding:0 4px}\n.dsh-ext-row-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end}.dsh-ext .dsh-ext-danger{color:#e98282}.dsh-ext-inline-form{width:100%;padding:14px 0 2px;border:0}.dsh-skill-edit-form{margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1)}\n.dsh-ext-heading-actions{display:flex;gap:8px;align-items:center}.dsh-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.dsh-skill-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:20px}.dsh-skill-stats>div{display:flex;flex-direction:column;gap:4px;padding:14px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:12px;background:var(--dsw-alias-bg-layer-2)}.dsh-skill-stats strong{font-size:20px}.dsh-skill-stats span{font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsh-skill-search{margin-top:14px}.dsh-skill-search input{padding-left:13px}.dsh-skill-manager>.dsh-ext-form{margin-top:14px;border:1px solid var(--dsw-alias-border-l4);border-radius:14px;background:var(--dsw-alias-bg-layer-2)}\n.dsh-skill-filters{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:10px}.dsh-skill-filters label{gap:5px;font-size:11px;color:var(--dsw-alias-label-tertiary)}.dsh-skill-filters select{padding:8px 10px;color:var(--dsw-alias-label-primary)}\n.dsh-skill-source-manager{margin-top:14px}.dsh-skill-source-manager>summary{cursor:pointer;padding:14px 18px;font-size:14px;font-weight:600;user-select:none}.dsh-skill-source-manager[open]>summary{border-bottom:1px solid var(--dsw-alias-border-l4)}\n.dsh-skill-source-manager li{padding:11px 18px;flex-wrap:wrap}.dsh-skill-source-title{display:flex;align-items:flex-start;gap:11px;min-width:0}.dsh-skill-source-title>.dsh-ext-status{margin-top:6px;flex:0 0 auto}.dsh-skill-source-title>div{display:flex;min-width:0;flex-direction:column;gap:3px}.dsh-skill-source-title code{font-size:11px;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}.dsh-skill-source-title small{font-size:11px;color:#e98282}\n.dsh-skill-library{margin-top:14px}.dsh-skill-group-editor{display:flex;align-items:flex-end;gap:8px}.dsh-skill-group-editor label{flex:1}.dsh-skill-group-editor input{padding:7px 9px}.dsh-skill-group-editor button{padding:7px 10px}\n.dsh-skill-row{display:block!important;padding:15px 18px!important}.dsh-skill-row-main{min-width:0}.dsh-skill-name{display:flex;align-items:center;gap:9px}.dsh-skill-name strong{font-size:14px}.dsh-skill-row-main>p{margin:6px 0 3px}.dsh-skill-row-main>code{font-size:11px;color:var(--dsw-alias-label-tertiary)}\n.dsh-skill-badge{font-size:11px;padding:2px 7px;border-radius:999px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary)}.dsh-skill-badge-group{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}.dsh-skill-badge-invalid{color:#e98282}.dsh-skill-badge-shadowed{color:#a98af8}.dsh-ext-status-invalid{background:#e05252;box-shadow:0 0 0 3px color-mix(in srgb,#e05252 18%,transparent)}.dsh-ext-status-shadowed{background:#8b5cf6;box-shadow:0 0 0 3px color-mix(in srgb,#8b5cf6 18%,transparent)}.dsh-ext-status-manual{background:#d69e2e;box-shadow:0 0 0 3px color-mix(in srgb,#d69e2e 18%,transparent)}\n.dsh-skill-row details{margin-top:9px}.dsh-skill-row summary{width:max-content;cursor:pointer;font-size:12px;color:var(--dsw-alias-label-secondary)}.dsh-skill-detail{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1)}.dsh-skill-detail>p{grid-column:1/-1;margin:0}.dsh-skill-detail p strong,.dsh-skill-detail>div>strong{display:block;margin-bottom:5px;color:var(--dsw-alias-label-secondary);font-size:12px}\n.dsh-skill-invocation{display:flex;flex-direction:column;gap:7px}.dsh-skill-invocation label,.dsh-skill-create-flags label{display:flex;flex-direction:row;align-items:center;gap:5px}.dsh-ext input[type=checkbox]{width:auto;min-width:auto}.dsh-skill-detail ul,.dsh-skill-detail li{display:block!important;margin:0;padding:0!important;border:0!important}.dsh-skill-resources{display:flex!important;flex-wrap:wrap!important;gap:5px}.dsh-skill-resources li{padding:3px 6px!important;border-radius:5px!important;background:var(--dsw-alias-bg-layer-3)!important}\n.dsh-ext-span{grid-column:1/-1}.dsh-skill-create-flags{display:flex;flex-direction:column;gap:7px;font-size:13px}.dsh-skill-create-flags>span{margin-bottom:1px}.dsh-skill-source>.dsh-ext-notice{margin:12px 18px}.dsh-skill-source>.dsh-ext-empty{padding:18px}\n@media(max-width:600px){.dsh-ext fieldset,.dsh-skill-detail,.dsh-skill-filters{grid-template-columns:1fr}.dsh-ext-heading{align-items:flex-start;flex-direction:column}.dsh-ext-heading-actions{flex-wrap:wrap}.dsh-ext-card>header{padding:14px}.dsh-ext li{padding:14px}.dsh-skill-stats{grid-template-columns:repeat(2,1fr)}.dsh-skill-group-editor{align-items:stretch;flex-direction:column}}\n";
|
|
316
|
+
var styles_default = ".dsh-ext{color:var(--dsw-alias-label-primary);max-width:840px;padding:4px 0 28px;font:inherit}\n.dsh-ext-heading,.dsh-ext-card>header{display:flex;justify-content:space-between;align-items:center;gap:16px}\n.dsh-ext h2{font-size:22px;letter-spacing:-.4px;margin:0 0 8px}.dsh-ext h3{font-size:15px;margin:0;display:flex;align-items:center;gap:8px}\n.dsh-ext p,.dsh-ext small{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.6}.dsh-ext-heading p{margin:0}\n.dsh-ext button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:inherit;padding:8px 13px;white-space:nowrap}\n.dsh-ext button:hover{background:var(--dsw-alias-bg-layer-3)}.dsh-ext button:disabled{opacity:.5;cursor:default}\n.dsh-ext :is(button,input,select,textarea):focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}\n.dsh-ext-card{border:1px solid var(--dsw-alias-border-l4);border-radius:16px;margin-top:20px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}\n.dsh-ext-card>header{padding:18px 20px;border-bottom:1px solid var(--dsw-alias-border-l4)}.dsh-ext-count{font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary)}\n.dsh-ext-empty{padding:32px 20px}.dsh-ext-empty strong{font-size:14px;font-weight:500}.dsh-ext-empty p{margin:6px 0 0}\n.dsh-ext ul{list-style:none;padding:0;margin:0}.dsh-ext li{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:16px 20px;border-bottom:1px solid var(--dsw-alias-border-l4)}.dsh-ext li:last-child{border:0}\n.dsh-ext-details{display:flex;flex-direction:column;gap:5px;min-width:0}.dsh-ext-details strong{font-size:14px}.dsh-ext-details code{font-size:12px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}\n.dsh-ext-runtime{display:flex;align-items:center;flex-wrap:wrap;gap:7px;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsh-ext-status{width:7px;height:7px;border-radius:50%;background:#858585;box-shadow:0 0 0 3px color-mix(in srgb,#858585 16%,transparent)}\n.dsh-ext-status-available{background:#38a169;box-shadow:0 0 0 3px color-mix(in srgb,#38a169 18%,transparent)}.dsh-ext-status-waiting{background:#d69e2e;box-shadow:0 0 0 3px color-mix(in srgb,#d69e2e 18%,transparent)}\n.dsh-ext-runtime details{width:100%;margin-top:2px}.dsh-ext-runtime summary{width:max-content;cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none}.dsh-ext-runtime summary:hover{color:var(--dsw-alias-label-primary)}\n.dsh-ext-runtime .dsh-ext-tools{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.dsh-ext-runtime .dsh-ext-tools li{display:block;border:0;padding:4px 8px;border-radius:6px;background:var(--dsw-alias-bg-layer-3)}\n.dsh-ext-form{padding:20px;border-top:1px solid var(--dsw-alias-border-l4)}.dsh-ext fieldset{border:0;padding:0;margin:0;display:grid;grid-template-columns:1fr 1fr;gap:16px}.dsh-ext label{display:flex;flex-direction:column;gap:7px;font-size:13px}\n.dsh-ext input,.dsh-ext select,.dsh-ext textarea{box-sizing:border-box;min-width:0;width:100%;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l4);border-radius:8px;padding:10px}.dsh-ext textarea{resize:vertical}\n.dsh-ext-actions{display:flex;justify-content:flex-end;gap:8px;grid-column:1/-1}.dsh-ext fieldset>p{grid-column:1/-1;margin:0}.dsh-ext-notice{padding:12px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:10px}.dsh-ext-footnote{padding:0 4px}\n.dsh-ext-row-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end}.dsh-ext .dsh-ext-danger{color:#e98282}.dsh-ext-inline-form{width:100%;padding:14px 0 2px;border:0}.dsh-skill-edit-form{margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1)}\n.dsh-confirm{box-sizing:border-box;width:min(430px,calc(100vw - 32px));padding:22px;border:1px solid var(--dsw-alias-border-l4);border-radius:16px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);box-shadow:0 20px 60px rgba(0,0,0,.28);font:inherit}.dsh-confirm::backdrop{background:rgba(10,12,16,.48);backdrop-filter:blur(3px)}\n.dsh-confirm-icon{display:grid;place-items:center;width:34px;height:34px;margin-bottom:16px;border-radius:10px;background:color-mix(in srgb,#e05252 14%,transparent);color:#e98282;font-size:19px;font-weight:700}.dsh-confirm-copy h3{margin:0;font-size:16px}.dsh-confirm-copy p{margin:8px 0 0;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.65}.dsh-confirm-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:22px}.dsh-confirm button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:inherit;padding:8px 14px}.dsh-confirm button:hover{background:var(--dsw-alias-bg-layer-3)}.dsh-confirm button:disabled{opacity:.5;cursor:default}.dsh-confirm button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.dsh-confirm .dsh-confirm-delete{border-color:color-mix(in srgb,#e05252 45%,var(--dsw-alias-border-l4));background:#b83232;color:#fff}.dsh-confirm .dsh-confirm-delete:hover{background:#c63b3b}\n.dsh-ext-heading-actions{display:flex;gap:8px;align-items:center}.dsh-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.dsh-skill-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:20px}.dsh-skill-stats>div{display:flex;flex-direction:column;gap:4px;padding:14px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:12px;background:var(--dsw-alias-bg-layer-2)}.dsh-skill-stats strong{font-size:20px}.dsh-skill-stats span{font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsh-skill-search{margin-top:14px}.dsh-skill-search input{padding-left:13px}.dsh-skill-manager>.dsh-ext-form{margin-top:14px;border:1px solid var(--dsw-alias-border-l4);border-radius:14px;background:var(--dsw-alias-bg-layer-2)}\n.dsh-skill-filters{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:10px}.dsh-skill-filters label{gap:5px;font-size:11px;color:var(--dsw-alias-label-tertiary)}.dsh-skill-filters select{padding:8px 10px;color:var(--dsw-alias-label-primary)}\n.dsh-skill-source-manager{margin-top:14px}.dsh-skill-source-manager>summary{cursor:pointer;padding:14px 18px;font-size:14px;font-weight:600;user-select:none}.dsh-skill-source-manager[open]>summary{border-bottom:1px solid var(--dsw-alias-border-l4)}\n.dsh-skill-source-manager li{padding:11px 18px;flex-wrap:wrap}.dsh-skill-source-title{display:flex;align-items:flex-start;gap:11px;min-width:0}.dsh-skill-source-title>.dsh-ext-status{margin-top:6px;flex:0 0 auto}.dsh-skill-source-title>div{display:flex;min-width:0;flex-direction:column;gap:3px}.dsh-skill-source-title code{font-size:11px;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}.dsh-skill-source-title small{font-size:11px;color:#e98282}\n.dsh-skill-library{margin-top:14px}.dsh-skill-group-editor{display:flex;align-items:flex-end;gap:8px}.dsh-skill-group-editor label{flex:1}.dsh-skill-group-editor input{padding:7px 9px}.dsh-skill-group-editor button{padding:7px 10px}\n.dsh-skill-row{display:block!important;padding:15px 18px!important}.dsh-skill-row-main{min-width:0}.dsh-skill-name{display:flex;align-items:center;gap:9px}.dsh-skill-name strong{font-size:14px}.dsh-skill-row-main>p{margin:6px 0 3px}.dsh-skill-row-main>code{font-size:11px;color:var(--dsw-alias-label-tertiary)}\n.dsh-skill-badge{font-size:11px;padding:2px 7px;border-radius:999px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary)}.dsh-skill-badge-group{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}.dsh-skill-badge-invalid{color:#e98282}.dsh-skill-badge-shadowed{color:#a98af8}.dsh-ext-status-invalid{background:#e05252;box-shadow:0 0 0 3px color-mix(in srgb,#e05252 18%,transparent)}.dsh-ext-status-shadowed{background:#8b5cf6;box-shadow:0 0 0 3px color-mix(in srgb,#8b5cf6 18%,transparent)}.dsh-ext-status-manual{background:#d69e2e;box-shadow:0 0 0 3px color-mix(in srgb,#d69e2e 18%,transparent)}\n.dsh-skill-row details{margin-top:9px}.dsh-skill-row summary{width:max-content;cursor:pointer;font-size:12px;color:var(--dsw-alias-label-secondary)}.dsh-skill-detail{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1)}.dsh-skill-detail>p{grid-column:1/-1;margin:0}.dsh-skill-detail p strong,.dsh-skill-detail>div>strong{display:block;margin-bottom:5px;color:var(--dsw-alias-label-secondary);font-size:12px}\n.dsh-skill-invocation{display:flex;flex-direction:column;gap:7px}.dsh-skill-invocation label,.dsh-skill-create-flags label{display:flex;flex-direction:row;align-items:center;gap:5px}.dsh-ext input[type=checkbox]{width:auto;min-width:auto}.dsh-skill-detail ul,.dsh-skill-detail li{display:block!important;margin:0;padding:0!important;border:0!important}.dsh-skill-resources{display:flex!important;flex-wrap:wrap!important;gap:5px}.dsh-skill-resources li{padding:3px 6px!important;border-radius:5px!important;background:var(--dsw-alias-bg-layer-3)!important}\n.dsh-ext-span{grid-column:1/-1}.dsh-skill-create-flags{display:flex;flex-direction:column;gap:7px;font-size:13px}.dsh-skill-create-flags>span{margin-bottom:1px}.dsh-skill-source>.dsh-ext-notice{margin:12px 18px}.dsh-skill-source>.dsh-ext-empty{padding:18px}\n@media(max-width:600px){.dsh-ext fieldset,.dsh-skill-detail,.dsh-skill-filters{grid-template-columns:1fr}.dsh-ext-heading{align-items:flex-start;flex-direction:column}.dsh-ext-heading-actions{flex-wrap:wrap}.dsh-ext-card>header{padding:14px}.dsh-ext li{padding:14px}.dsh-skill-stats{grid-template-columns:repeat(2,1fr)}.dsh-skill-group-editor{align-items:stretch;flex-direction:column}}\n";
|
|
294
317
|
|
|
295
318
|
// src/client/index.jsx
|
|
296
319
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -321,7 +344,7 @@ function apply(ctx) {
|
|
|
321
344
|
const t = ctx.locale.bind(namespace);
|
|
322
345
|
const request = async (endpoint, args, options) => {
|
|
323
346
|
const ok = await controller.request(endpoint, args, options);
|
|
324
|
-
if (ok && (endpoint
|
|
347
|
+
if (ok && refreshesSlashCatalog(endpoint, options)) refreshSlashCatalog();
|
|
325
348
|
return ok;
|
|
326
349
|
};
|
|
327
350
|
const face = { hooks: { manager: controller }, request };
|
|
@@ -368,7 +391,7 @@ function SkillSection({ t, useManager, request }) {
|
|
|
368
391
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-ext-heading-actions", children: [
|
|
369
392
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy || !state.revision, onClick: () => setForm("source"), children: t("add") }),
|
|
370
393
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy || sources.length === 0, onClick: () => setForm("skill"), children: t("newSkill") }),
|
|
371
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy, onClick: () => request("list"), children: t("refresh") })
|
|
394
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy, onClick: () => request("list", {}, { refreshSlashCatalog: true }), children: t("refresh") })
|
|
372
395
|
] })
|
|
373
396
|
] }),
|
|
374
397
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageState, { state, t }),
|
|
@@ -446,51 +469,70 @@ function SkillSection({ t, useManager, request }) {
|
|
|
446
469
|
}
|
|
447
470
|
function SkillSources({ sources, skills, t, busy, request }) {
|
|
448
471
|
const [editing, setEditing] = (0, import_react.useState)("");
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `dsh-ext-status ${source.enabled ? "dsh-ext-status-available" : ""}`, "aria-hidden": "true" }),
|
|
458
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
459
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
|
|
460
|
-
source.id,
|
|
461
|
-
" ",
|
|
462
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-ext-count", children: skills.filter((skill) => skill.sourceId === source.id).length })
|
|
463
|
-
] }),
|
|
464
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: source.location }),
|
|
465
|
-
source.issue && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: t(`issue_${source.issue}`) })
|
|
466
|
-
] })
|
|
472
|
+
const [confirming, setConfirming] = (0, import_react.useState)("");
|
|
473
|
+
const target = sources.find((source) => source.id === confirming);
|
|
474
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
475
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "dsh-ext-card dsh-skill-source-manager", children: [
|
|
476
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("summary", { children: [
|
|
477
|
+
t("skillSources"),
|
|
478
|
+
" ",
|
|
479
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-ext-count", children: sources.length })
|
|
467
480
|
] }),
|
|
468
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
469
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
470
|
-
|
|
471
|
-
"
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
481
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: sources.map((source) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
|
|
482
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-skill-source-title", children: [
|
|
483
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `dsh-ext-status ${source.enabled ? "dsh-ext-status-available" : ""}`, "aria-hidden": "true" }),
|
|
484
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
485
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
|
|
486
|
+
source.id,
|
|
487
|
+
" ",
|
|
488
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-ext-count", children: skills.filter((skill) => skill.sourceId === source.id).length })
|
|
489
|
+
] }),
|
|
490
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: source.location }),
|
|
491
|
+
source.issue && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: t(`issue_${source.issue}`) })
|
|
492
|
+
] })
|
|
493
|
+
] }),
|
|
494
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-ext-row-actions", children: [
|
|
495
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy, onClick: () => setEditing(editing === source.id ? "" : source.id), children: t("edit") }),
|
|
496
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
497
|
+
"button",
|
|
498
|
+
{
|
|
499
|
+
type: "button",
|
|
500
|
+
role: "switch",
|
|
501
|
+
"aria-checked": source.enabled,
|
|
502
|
+
disabled: busy,
|
|
503
|
+
onClick: () => request("enable", { id: source.id, enabled: !source.enabled }, { refreshSlashCatalog: true }),
|
|
504
|
+
children: t(source.enabled ? "disable" : "enable")
|
|
505
|
+
}
|
|
506
|
+
),
|
|
507
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dsh-ext-danger", type: "button", disabled: busy, onClick: () => setConfirming(source.id), children: t("delete") })
|
|
508
|
+
] }),
|
|
509
|
+
editing === source.id && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkillSourceForm, { source, t, busy, onCancel: () => setEditing(""), onSave: async (args) => {
|
|
510
|
+
if (await request("update-skill-source", args)) setEditing("");
|
|
511
|
+
} })
|
|
512
|
+
] }, source.id)) })
|
|
513
|
+
] }),
|
|
514
|
+
target && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
515
|
+
ConfirmDialog,
|
|
516
|
+
{
|
|
517
|
+
title: t("deleteExtensionTitle"),
|
|
518
|
+
message: t("deleteExtensionConfirm").replace("{name}", target.id),
|
|
519
|
+
t,
|
|
520
|
+
busy,
|
|
521
|
+
onCancel: () => setConfirming(""),
|
|
522
|
+
onConfirm: async () => {
|
|
523
|
+
if (await request("delete-extension", { id: target.id }, { refreshSlashCatalog: true })) {
|
|
524
|
+
setEditing("");
|
|
525
|
+
setConfirming("");
|
|
479
526
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}, children: t("delete") })
|
|
484
|
-
] }),
|
|
485
|
-
editing === source.id && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkillSourceForm, { source, t, busy, onCancel: () => setEditing(""), onSave: async (args) => {
|
|
486
|
-
if (await request("update-skill-source", args)) setEditing("");
|
|
487
|
-
} })
|
|
488
|
-
] }, source.id)) })
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
)
|
|
489
530
|
] });
|
|
490
531
|
}
|
|
491
532
|
function McpSection({ t, useManager, request }) {
|
|
492
533
|
const state = useManager((value) => value);
|
|
493
534
|
const [form, setForm] = (0, import_react.useState)("");
|
|
535
|
+
const [confirming, setConfirming] = (0, import_react.useState)("");
|
|
494
536
|
const busy = state.loading || state.saving;
|
|
495
537
|
const rows = state.extensions.filter((row) => row.kind === "mcp");
|
|
496
538
|
useLiveRefresh(request);
|
|
@@ -539,16 +581,30 @@ function McpSection({ t, useManager, request }) {
|
|
|
539
581
|
children: t(row.enabled ? "disable" : "enable")
|
|
540
582
|
}
|
|
541
583
|
),
|
|
542
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dsh-ext-danger", type: "button", disabled: busy, onClick:
|
|
543
|
-
if (window.confirm(t("deleteExtensionConfirm")) && await request("delete-extension", { id: row.id })) setForm("");
|
|
544
|
-
}, children: t("delete") })
|
|
584
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dsh-ext-danger", type: "button", disabled: busy, onClick: () => setConfirming(row.id), children: t("delete") })
|
|
545
585
|
] })
|
|
546
586
|
] }, row.id)) }),
|
|
547
587
|
form === "new" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(McpForm, { t, busy, onCancel: () => setForm(""), onSave: async (args) => {
|
|
548
588
|
if (await request("add-mcp", args)) setForm("");
|
|
549
589
|
} })
|
|
550
590
|
] }),
|
|
551
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-ext-footnote", children: t("mcpFootnote") })
|
|
591
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-ext-footnote", children: t("mcpFootnote") }),
|
|
592
|
+
confirming && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
593
|
+
ConfirmDialog,
|
|
594
|
+
{
|
|
595
|
+
title: t("deleteMcpTitle"),
|
|
596
|
+
message: t("deleteMcpConfirm").replace("{name}", confirming),
|
|
597
|
+
t,
|
|
598
|
+
busy,
|
|
599
|
+
onCancel: () => setConfirming(""),
|
|
600
|
+
onConfirm: async () => {
|
|
601
|
+
if (await request("delete-extension", { id: confirming })) {
|
|
602
|
+
setForm("");
|
|
603
|
+
setConfirming("");
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
)
|
|
552
608
|
] });
|
|
553
609
|
}
|
|
554
610
|
function useLiveRefresh(request) {
|
|
@@ -582,6 +638,7 @@ function skillState(skill) {
|
|
|
582
638
|
}
|
|
583
639
|
function SkillRow({ skill, source, groups, t, busy, request }) {
|
|
584
640
|
const [editing, setEditing] = (0, import_react.useState)(false);
|
|
641
|
+
const [confirming, setConfirming] = (0, import_react.useState)(false);
|
|
585
642
|
const state = skillState(skill);
|
|
586
643
|
const invocation = skill.modelInvocable && skill.userInvocable ? "both" : skill.modelInvocable ? "modelOnly" : skill.userInvocable ? "manualOnly" : "none";
|
|
587
644
|
const statusLabel = !skill.valid ? "skillStatusInvalid" : !skill.sourceEnabled ? "skillStatusSourceDisabled" : skill.shadowed ? "skillStatusShadowed" : "";
|
|
@@ -639,21 +696,72 @@ function SkillRow({ skill, source, groups, t, busy, request }) {
|
|
|
639
696
|
] }),
|
|
640
697
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-ext-row-actions", children: [
|
|
641
698
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy || !skill.valid, onClick: () => setEditing(!editing), children: t("edit") }),
|
|
642
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dsh-ext-danger", type: "button", disabled: busy || !skill.valid, onClick: () =>
|
|
643
|
-
if (window.confirm(t("deleteSkillConfirm"))) void request("delete-skill", {
|
|
644
|
-
sourceId: skill.sourceId,
|
|
645
|
-
relativePath: skill.relativePath,
|
|
646
|
-
skillRevision: skill.revision
|
|
647
|
-
});
|
|
648
|
-
}, children: t("delete") })
|
|
699
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dsh-ext-danger", type: "button", disabled: busy || !skill.valid, onClick: () => setConfirming(true), children: t("delete") })
|
|
649
700
|
] })
|
|
650
701
|
] })
|
|
651
702
|
] }),
|
|
652
703
|
editing && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkillEditForm, { skill, t, busy, onCancel: () => setEditing(false), onSave: async (args) => {
|
|
653
704
|
if (await request("update-skill", args)) setEditing(false);
|
|
654
|
-
} })
|
|
705
|
+
} }),
|
|
706
|
+
confirming && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
707
|
+
ConfirmDialog,
|
|
708
|
+
{
|
|
709
|
+
title: t("deleteSkillTitle"),
|
|
710
|
+
message: t("deleteSkillConfirm").replace("{name}", skill.name),
|
|
711
|
+
t,
|
|
712
|
+
busy,
|
|
713
|
+
onCancel: () => setConfirming(false),
|
|
714
|
+
onConfirm: async () => {
|
|
715
|
+
await request("delete-skill", {
|
|
716
|
+
sourceId: skill.sourceId,
|
|
717
|
+
relativePath: skill.relativePath,
|
|
718
|
+
skillRevision: skill.revision
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
)
|
|
655
723
|
] }) });
|
|
656
724
|
}
|
|
725
|
+
function ConfirmDialog({ title, message, t, busy, onConfirm, onCancel }) {
|
|
726
|
+
const dialog = (0, import_react.useRef)(null);
|
|
727
|
+
const cancel = (0, import_react.useRef)(null);
|
|
728
|
+
const titleId = (0, import_react.useId)();
|
|
729
|
+
const messageId = (0, import_react.useId)();
|
|
730
|
+
(0, import_react.useEffect)(() => {
|
|
731
|
+
dialog.current.showModal();
|
|
732
|
+
cancel.current.focus();
|
|
733
|
+
return () => {
|
|
734
|
+
if (dialog.current?.open) dialog.current.close();
|
|
735
|
+
};
|
|
736
|
+
}, []);
|
|
737
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
738
|
+
"dialog",
|
|
739
|
+
{
|
|
740
|
+
ref: dialog,
|
|
741
|
+
className: "dsh-confirm",
|
|
742
|
+
"aria-labelledby": titleId,
|
|
743
|
+
"aria-describedby": messageId,
|
|
744
|
+
onCancel: (event) => {
|
|
745
|
+
event.preventDefault();
|
|
746
|
+
if (!busy) onCancel();
|
|
747
|
+
},
|
|
748
|
+
onMouseDown: (event) => {
|
|
749
|
+
if (event.target === event.currentTarget && !busy) onCancel();
|
|
750
|
+
},
|
|
751
|
+
children: [
|
|
752
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-confirm-icon", "aria-hidden": "true", children: "!" }),
|
|
753
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-confirm-copy", children: [
|
|
754
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { id: titleId, children: title }),
|
|
755
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: messageId, children: message })
|
|
756
|
+
] }),
|
|
757
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-confirm-actions", children: [
|
|
758
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ref: cancel, type: "button", disabled: busy, onClick: onCancel, children: t("cancel") }),
|
|
759
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dsh-confirm-delete", type: "button", disabled: busy, onClick: () => void onConfirm(), children: t(busy ? "deleting" : "confirmDelete") })
|
|
760
|
+
] })
|
|
761
|
+
]
|
|
762
|
+
}
|
|
763
|
+
);
|
|
764
|
+
}
|
|
657
765
|
function SkillEditForm({ skill, t, busy, onSave, onCancel }) {
|
|
658
766
|
const prefix = (0, import_react.useId)();
|
|
659
767
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", { className: "dsh-skill-edit-form", onSubmit: (event) => {
|
package/package.json
CHANGED
package/src/client/controller.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
/** One observable per mounted plugin; drafts stay in the settings component. */
|
|
2
|
+
const skillCatalogMutations = new Set([
|
|
3
|
+
'add-skill', 'update-skill-source', 'create-skill', 'update-skill', 'delete-skill', 'set-skill-invocation',
|
|
4
|
+
])
|
|
5
|
+
|
|
6
|
+
/** Keep broad connection cache invalidation away from ordinary page reads and MCP-only writes. */
|
|
7
|
+
export function refreshesSlashCatalog(endpoint, options = {}) {
|
|
8
|
+
return options.refreshSlashCatalog === true || skillCatalogMutations.has(endpoint)
|
|
9
|
+
}
|
|
10
|
+
|
|
2
11
|
export class ExtensionsController {
|
|
3
12
|
state = { extensions: [], sources: [], skills: [], revision: '', loading: false, saving: false, error: '', saved: false }
|
|
4
13
|
listeners = new Set()
|
package/src/client/index.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Skill and MCP settings sections registered through the public Slots and Connection services. */
|
|
2
|
-
import { useEffect, useId, useState } from 'react'
|
|
3
|
-
import { ExtensionsController } from './controller.js'
|
|
2
|
+
import { useEffect, useId, useRef, useState } from 'react'
|
|
3
|
+
import { ExtensionsController, refreshesSlashCatalog } from './controller.js'
|
|
4
4
|
import { zh, en } from './locales.js'
|
|
5
5
|
import css from './styles.css'
|
|
6
6
|
|
|
@@ -27,7 +27,7 @@ export function apply(ctx) {
|
|
|
27
27
|
const t = ctx.locale.bind(namespace)
|
|
28
28
|
const request = async (endpoint, args, options) => {
|
|
29
29
|
const ok = await controller.request(endpoint, args, options)
|
|
30
|
-
if (ok && (endpoint
|
|
30
|
+
if (ok && refreshesSlashCatalog(endpoint, options)) refreshSlashCatalog()
|
|
31
31
|
return ok
|
|
32
32
|
}
|
|
33
33
|
const face = { hooks: { manager: controller }, request }
|
|
@@ -70,7 +70,7 @@ export function SkillSection({ t, useManager, request }) {
|
|
|
70
70
|
<header className="dsh-ext-heading"><div><h2>{t('navSkill')}</h2><p>{t('skillIntro')}</p></div>
|
|
71
71
|
<div className="dsh-ext-heading-actions"><button type="button" disabled={busy || !state.revision} onClick={() => setForm('source')}>{t('add')}</button>
|
|
72
72
|
<button type="button" disabled={busy || sources.length === 0} onClick={() => setForm('skill')}>{t('newSkill')}</button>
|
|
73
|
-
<button type="button" disabled={busy} onClick={() => request('list')}>{t('refresh')}</button></div></header>
|
|
73
|
+
<button type="button" disabled={busy} onClick={() => request('list', {}, { refreshSlashCatalog: true })}>{t('refresh')}</button></div></header>
|
|
74
74
|
<PageState state={state} t={t} />
|
|
75
75
|
<div className="dsh-skill-stats">
|
|
76
76
|
{Object.entries(counts).map(([key, value]) => <div key={key}><strong>{value}</strong><span>{t(`stat_${key}`)}</span></div>)}
|
|
@@ -107,25 +107,29 @@ export function SkillSection({ t, useManager, request }) {
|
|
|
107
107
|
|
|
108
108
|
function SkillSources({ sources, skills, t, busy, request }) {
|
|
109
109
|
const [editing, setEditing] = useState('')
|
|
110
|
-
|
|
110
|
+
const [confirming, setConfirming] = useState('')
|
|
111
|
+
const target = sources.find(source => source.id === confirming)
|
|
112
|
+
return <><details className="dsh-ext-card dsh-skill-source-manager"><summary>{t('skillSources')} <span className="dsh-ext-count">{sources.length}</span></summary>
|
|
111
113
|
<ul>{sources.map(source => <li key={source.id}><div className="dsh-skill-source-title"><span className={`dsh-ext-status ${source.enabled ? 'dsh-ext-status-available' : ''}`} aria-hidden="true" />
|
|
112
114
|
<div><strong>{source.id} <span className="dsh-ext-count">{skills.filter(skill => skill.sourceId === source.id).length}</span></strong><code>{source.location}</code>
|
|
113
115
|
{source.issue && <small>{t(`issue_${source.issue}`)}</small>}</div></div>
|
|
114
116
|
<div className="dsh-ext-row-actions"><button type="button" disabled={busy} onClick={() => setEditing(editing === source.id ? '' : source.id)}>{t('edit')}</button>
|
|
115
117
|
<button type="button" role="switch" aria-checked={source.enabled} disabled={busy}
|
|
116
|
-
onClick={() => request('enable', { id: source.id, enabled: !source.enabled })}>{t(source.enabled ? 'disable' : 'enable')}</button>
|
|
117
|
-
<button className="dsh-ext-danger" type="button" disabled={busy} onClick={
|
|
118
|
-
if (window.confirm(t('deleteExtensionConfirm')) && await request('delete-extension', { id: source.id })) setEditing('')
|
|
119
|
-
}}>{t('delete')}</button></div>
|
|
118
|
+
onClick={() => request('enable', { id: source.id, enabled: !source.enabled }, { refreshSlashCatalog: true })}>{t(source.enabled ? 'disable' : 'enable')}</button>
|
|
119
|
+
<button className="dsh-ext-danger" type="button" disabled={busy} onClick={() => setConfirming(source.id)}>{t('delete')}</button></div>
|
|
120
120
|
{editing === source.id && <SkillSourceForm source={source} t={t} busy={busy} onCancel={() => setEditing('')} onSave={async args => {
|
|
121
121
|
if (await request('update-skill-source', args)) setEditing('')
|
|
122
122
|
}} />}</li>)}</ul>
|
|
123
|
-
</details>
|
|
123
|
+
</details>{target && <ConfirmDialog title={t('deleteExtensionTitle')} message={t('deleteExtensionConfirm').replace('{name}', target.id)}
|
|
124
|
+
t={t} busy={busy} onCancel={() => setConfirming('')} onConfirm={async () => {
|
|
125
|
+
if (await request('delete-extension', { id: target.id }, { refreshSlashCatalog: true })) { setEditing(''); setConfirming('') }
|
|
126
|
+
}} />}</>
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
export function McpSection({ t, useManager, request }) {
|
|
127
130
|
const state = useManager(value => value)
|
|
128
131
|
const [form, setForm] = useState('')
|
|
132
|
+
const [confirming, setConfirming] = useState('')
|
|
129
133
|
const busy = state.loading || state.saving
|
|
130
134
|
const rows = state.extensions.filter(row => row.kind === 'mcp')
|
|
131
135
|
useLiveRefresh(request)
|
|
@@ -144,14 +148,16 @@ export function McpSection({ t, useManager, request }) {
|
|
|
144
148
|
<div className="dsh-ext-row-actions"><button type="button" disabled={busy} onClick={() => setForm(form === row.id ? '' : row.id)}>{t('edit')}</button>
|
|
145
149
|
<button type="button" role="switch" aria-checked={row.enabled} aria-label={`${t(row.enabled ? 'disable' : 'enable')} ${row.id}`} disabled={busy}
|
|
146
150
|
onClick={() => request('enable', { id: row.id, enabled: !row.enabled })}>{t(row.enabled ? 'disable' : 'enable')}</button>
|
|
147
|
-
<button className="dsh-ext-danger" type="button" disabled={busy} onClick={
|
|
148
|
-
if (window.confirm(t('deleteExtensionConfirm')) && await request('delete-extension', { id: row.id })) setForm('')
|
|
149
|
-
}}>{t('delete')}</button></div></li>)}</ul>
|
|
151
|
+
<button className="dsh-ext-danger" type="button" disabled={busy} onClick={() => setConfirming(row.id)}>{t('delete')}</button></div></li>)}</ul>
|
|
150
152
|
{form === 'new' && <McpForm t={t} busy={busy} onCancel={() => setForm('')} onSave={async args => {
|
|
151
153
|
if (await request('add-mcp', args)) setForm('')
|
|
152
154
|
}} />}
|
|
153
155
|
</section>
|
|
154
156
|
<p className="dsh-ext-footnote">{t('mcpFootnote')}</p>
|
|
157
|
+
{confirming && <ConfirmDialog title={t('deleteMcpTitle')} message={t('deleteMcpConfirm').replace('{name}', confirming)}
|
|
158
|
+
t={t} busy={busy} onCancel={() => setConfirming('')} onConfirm={async () => {
|
|
159
|
+
if (await request('delete-extension', { id: confirming })) { setForm(''); setConfirming('') }
|
|
160
|
+
}} />}
|
|
155
161
|
</section>
|
|
156
162
|
}
|
|
157
163
|
|
|
@@ -188,6 +194,7 @@ function skillState(skill) {
|
|
|
188
194
|
|
|
189
195
|
function SkillRow({ skill, source, groups, t, busy, request }) {
|
|
190
196
|
const [editing, setEditing] = useState(false)
|
|
197
|
+
const [confirming, setConfirming] = useState(false)
|
|
191
198
|
const state = skillState(skill)
|
|
192
199
|
const invocation = skill.modelInvocable && skill.userInvocable ? 'both'
|
|
193
200
|
: skill.modelInvocable ? 'modelOnly' : skill.userInvocable ? 'manualOnly' : 'none'
|
|
@@ -214,18 +221,41 @@ function SkillRow({ skill, source, groups, t, busy, request }) {
|
|
|
214
221
|
? <ul className="dsh-skill-resources">{skill.resources.map(path => <li key={path}><code>{path}</code></li>)}</ul>
|
|
215
222
|
: <p>{t('noResources')}</p>}</div>
|
|
216
223
|
<div className="dsh-ext-row-actions"><button type="button" disabled={busy || !skill.valid} onClick={() => setEditing(!editing)}>{t('edit')}</button>
|
|
217
|
-
<button className="dsh-ext-danger" type="button" disabled={busy || !skill.valid} onClick={() => {
|
|
218
|
-
if (window.confirm(t('deleteSkillConfirm'))) void request('delete-skill', {
|
|
219
|
-
sourceId: skill.sourceId, relativePath: skill.relativePath, skillRevision: skill.revision,
|
|
220
|
-
})
|
|
221
|
-
}}>{t('delete')}</button></div>
|
|
224
|
+
<button className="dsh-ext-danger" type="button" disabled={busy || !skill.valid} onClick={() => setConfirming(true)}>{t('delete')}</button></div>
|
|
222
225
|
</div></details>
|
|
223
226
|
{editing && <SkillEditForm skill={skill} t={t} busy={busy} onCancel={() => setEditing(false)} onSave={async args => {
|
|
224
227
|
if (await request('update-skill', args)) setEditing(false)
|
|
225
228
|
}} />}
|
|
229
|
+
{confirming && <ConfirmDialog title={t('deleteSkillTitle')} message={t('deleteSkillConfirm').replace('{name}', skill.name)}
|
|
230
|
+
t={t} busy={busy} onCancel={() => setConfirming(false)} onConfirm={async () => {
|
|
231
|
+
await request('delete-skill', {
|
|
232
|
+
sourceId: skill.sourceId, relativePath: skill.relativePath, skillRevision: skill.revision,
|
|
233
|
+
})
|
|
234
|
+
}} />}
|
|
226
235
|
</div></li>
|
|
227
236
|
}
|
|
228
237
|
|
|
238
|
+
function ConfirmDialog({ title, message, t, busy, onConfirm, onCancel }) {
|
|
239
|
+
const dialog = useRef(null)
|
|
240
|
+
const cancel = useRef(null)
|
|
241
|
+
const titleId = useId()
|
|
242
|
+
const messageId = useId()
|
|
243
|
+
useEffect(() => {
|
|
244
|
+
dialog.current.showModal()
|
|
245
|
+
cancel.current.focus()
|
|
246
|
+
return () => { if (dialog.current?.open) dialog.current.close() }
|
|
247
|
+
}, [])
|
|
248
|
+
return <dialog ref={dialog} className="dsh-confirm" aria-labelledby={titleId} aria-describedby={messageId}
|
|
249
|
+
onCancel={event => { event.preventDefault(); if (!busy) onCancel() }} onMouseDown={event => {
|
|
250
|
+
if (event.target === event.currentTarget && !busy) onCancel()
|
|
251
|
+
}}>
|
|
252
|
+
<div className="dsh-confirm-icon" aria-hidden="true">!</div>
|
|
253
|
+
<div className="dsh-confirm-copy"><h3 id={titleId}>{title}</h3><p id={messageId}>{message}</p></div>
|
|
254
|
+
<div className="dsh-confirm-actions"><button ref={cancel} type="button" disabled={busy} onClick={onCancel}>{t('cancel')}</button>
|
|
255
|
+
<button className="dsh-confirm-delete" type="button" disabled={busy} onClick={() => void onConfirm()}>{t(busy ? 'deleting' : 'confirmDelete')}</button></div>
|
|
256
|
+
</dialog>
|
|
257
|
+
}
|
|
258
|
+
|
|
229
259
|
function SkillEditForm({ skill, t, busy, onSave, onCancel }) {
|
|
230
260
|
const prefix = useId()
|
|
231
261
|
return <form className="dsh-skill-edit-form" onSubmit={event => {
|
package/src/client/locales.js
CHANGED
|
@@ -7,8 +7,10 @@ export const zh = {
|
|
|
7
7
|
emptyMcp: '还没有添加 MCP 服务器', emptyMcpHint: '通过本地命令或 HTTP 地址连接外部工具。',
|
|
8
8
|
id: '标识', directory: 'Skill 路径', transport: '配置类型', stdio: 'stdio', http: 'HTTP',
|
|
9
9
|
command: '命令', args: '参数(每行一个)', cwd: '工作目录(可选)', url: '服务器地址',
|
|
10
|
-
cancel: '取消', save: '添加', saveChanges: '保存修改', create: '创建', edit: '编辑', delete: '删除', saving: '保存中…', loading: '加载中…',
|
|
11
|
-
|
|
10
|
+
cancel: '取消', save: '添加', saveChanges: '保存修改', create: '创建', edit: '编辑', delete: '删除', confirmDelete: '确认删除', deleting: '删除中…', saving: '保存中…', loading: '加载中…',
|
|
11
|
+
deleteExtensionTitle: '移除 Skill 来源', deleteMcpTitle: '删除 MCP 服务器', deleteSkillTitle: '永久删除 Skill',
|
|
12
|
+
deleteExtensionConfirm: '将从当前配置中移除“{name}”,目录中的 Skill 文件会保留。', deleteMcpConfirm: '将删除 MCP 服务器“{name}”及其连接配置。',
|
|
13
|
+
deleteSkillConfirm: '将永久删除“{name}”及其目录内的资源文件。此操作无法撤销。',
|
|
12
14
|
enabled: '配置已启用', disabled: '已禁用', enable: '启用', disable: '禁用',
|
|
13
15
|
saved: '更改已保存。', failed: '操作失败,请检查输入、连接或服务端日志后重试。',
|
|
14
16
|
conflict: '配置已被修改,请刷新后重试。未提交的表单内容已保留。',
|
|
@@ -43,8 +45,10 @@ export const en = {
|
|
|
43
45
|
emptyMcp: 'No MCP servers yet', emptyMcpHint: 'Connect external tools with a local command or HTTP endpoint.',
|
|
44
46
|
id: 'Identifier', directory: 'Skill path', transport: 'Type', stdio: 'stdio', http: 'HTTP',
|
|
45
47
|
command: 'Command', args: 'Arguments (one per line)', cwd: 'Working directory (optional)', url: 'Server URL',
|
|
46
|
-
cancel: 'Cancel', save: 'Add', saveChanges: 'Save changes', create: 'Create', edit: 'Edit', delete: 'Delete', saving: 'Saving…', loading: 'Loading…',
|
|
47
|
-
|
|
48
|
+
cancel: 'Cancel', save: 'Add', saveChanges: 'Save changes', create: 'Create', edit: 'Edit', delete: 'Delete', confirmDelete: 'Delete', deleting: 'Deleting…', saving: 'Saving…', loading: 'Loading…',
|
|
49
|
+
deleteExtensionTitle: 'Remove Skill source', deleteMcpTitle: 'Delete MCP server', deleteSkillTitle: 'Permanently delete Skill',
|
|
50
|
+
deleteExtensionConfirm: 'Remove “{name}” from this configuration. Skill files in the directory will be kept.', deleteMcpConfirm: 'Delete MCP server “{name}” and its connection configuration.',
|
|
51
|
+
deleteSkillConfirm: 'Permanently delete “{name}” and the resource files in its directory. This cannot be undone.',
|
|
48
52
|
enabled: 'Configured enabled', disabled: 'Disabled', enable: 'Enable', disable: 'Disable',
|
|
49
53
|
saved: 'Changes saved.', failed: 'Operation failed. Check input, connection or server logs and retry.',
|
|
50
54
|
conflict: 'Configuration changed. Refresh and retry; your draft has been retained.',
|
package/src/client/styles.css
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
.dsh-ext input,.dsh-ext select,.dsh-ext textarea{box-sizing:border-box;min-width:0;width:100%;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l4);border-radius:8px;padding:10px}.dsh-ext textarea{resize:vertical}
|
|
20
20
|
.dsh-ext-actions{display:flex;justify-content:flex-end;gap:8px;grid-column:1/-1}.dsh-ext fieldset>p{grid-column:1/-1;margin:0}.dsh-ext-notice{padding:12px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:10px}.dsh-ext-footnote{padding:0 4px}
|
|
21
21
|
.dsh-ext-row-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end}.dsh-ext .dsh-ext-danger{color:#e98282}.dsh-ext-inline-form{width:100%;padding:14px 0 2px;border:0}.dsh-skill-edit-form{margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1)}
|
|
22
|
+
.dsh-confirm{box-sizing:border-box;width:min(430px,calc(100vw - 32px));padding:22px;border:1px solid var(--dsw-alias-border-l4);border-radius:16px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);box-shadow:0 20px 60px rgba(0,0,0,.28);font:inherit}.dsh-confirm::backdrop{background:rgba(10,12,16,.48);backdrop-filter:blur(3px)}
|
|
23
|
+
.dsh-confirm-icon{display:grid;place-items:center;width:34px;height:34px;margin-bottom:16px;border-radius:10px;background:color-mix(in srgb,#e05252 14%,transparent);color:#e98282;font-size:19px;font-weight:700}.dsh-confirm-copy h3{margin:0;font-size:16px}.dsh-confirm-copy p{margin:8px 0 0;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.65}.dsh-confirm-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:22px}.dsh-confirm button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:inherit;padding:8px 14px}.dsh-confirm button:hover{background:var(--dsw-alias-bg-layer-3)}.dsh-confirm button:disabled{opacity:.5;cursor:default}.dsh-confirm button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.dsh-confirm .dsh-confirm-delete{border-color:color-mix(in srgb,#e05252 45%,var(--dsw-alias-border-l4));background:#b83232;color:#fff}.dsh-confirm .dsh-confirm-delete:hover{background:#c63b3b}
|
|
22
24
|
.dsh-ext-heading-actions{display:flex;gap:8px;align-items:center}.dsh-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
23
25
|
.dsh-skill-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:20px}.dsh-skill-stats>div{display:flex;flex-direction:column;gap:4px;padding:14px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:12px;background:var(--dsw-alias-bg-layer-2)}.dsh-skill-stats strong{font-size:20px}.dsh-skill-stats span{font-size:12px;color:var(--dsw-alias-label-tertiary)}
|
|
24
26
|
.dsh-skill-search{margin-top:14px}.dsh-skill-search input{padding-left:13px}.dsh-skill-manager>.dsh-ext-form{margin-top:14px;border:1px solid var(--dsw-alias-border-l4);border-radius:14px;background:var(--dsw-alias-bg-layer-2)}
|