@linxin666/dsh-client-ui-community-plugins 0.2.0 → 0.2.2
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.i18n.yaml +2 -2
- package/README.md +8 -4
- package/README.zh.md +8 -4
- package/lib/client.js +714 -68
- package/lib/client.js.map +1 -1
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -5,10 +5,11 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
8
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
10
|
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
10
11
|
//#region \0dsh-css:packages/dsh-community-plugins/src/client/settings-card.module.css.mjs
|
|
11
|
-
const css$1 = ".hX6Abq_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.hX6Abq_card:hover{border-color:var(--dsw-alias-label-dimmed)}.hX6Abq_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.hX6Abq_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.hX6Abq_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.hX6Abq_headerStatic{border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.hX6Abq_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.hX6Abq_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.hX6Abq_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.hX6Abq_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.hX6Abq_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.hX6Abq_chevronOpen{transform:rotate(180deg)}.hX6Abq_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.hX6Abq_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.hX6Abq_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.hX6Abq_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.hX6Abq_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.hX6Abq_discard,.hX6Abq_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.hX6Abq_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.hX6Abq_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.hX6Abq_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.hX6Abq_discard:disabled,.hX6Abq_save:disabled{opacity:.4;cursor:default}.hX6Abq_discard:focus-visible,.hX6Abq_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.hX6Abq_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.hX6Abq_field+.hX6Abq_field{border-top:1px solid var(--dsw-alias-border-l2)}.hX6Abq_head{align-items:center;gap:8px;display:flex}.hX6Abq_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.hX6Abq_badges{align-items:center;gap:8px;display:inline-flex}.hX6Abq_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.hX6Abq_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.hX6Abq_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.hX6Abq_reset:disabled{cursor:default}.hX6Abq_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.hX6Abq_input,.hX6Abq_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.hX6Abq_input:focus-visible,.hX6Abq_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.hX6Abq_input:disabled,.hX6Abq_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.hX6Abq_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.hX6Abq_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.hX6Abq_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.hX6Abq_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.hX6Abq_card,.hX6Abq_header,.hX6Abq_chevron,.hX6Abq_chevronOpen,.hX6Abq_discard,.hX6Abq_save{transition:none}}";
|
|
12
|
+
const css$1 = ".hX6Abq_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.hX6Abq_card:hover{border-color:var(--dsw-alias-label-dimmed)}.hX6Abq_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.hX6Abq_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.hX6Abq_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.hX6Abq_headerStatic{border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.hX6Abq_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.hX6Abq_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.hX6Abq_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.hX6Abq_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.hX6Abq_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.hX6Abq_chevronOpen{transform:rotate(180deg)}.hX6Abq_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.hX6Abq_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.hX6Abq_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.hX6Abq_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.hX6Abq_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.hX6Abq_discard,.hX6Abq_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.hX6Abq_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.hX6Abq_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.hX6Abq_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.hX6Abq_discard:disabled,.hX6Abq_save:disabled{opacity:.4;cursor:default}.hX6Abq_discard:focus-visible,.hX6Abq_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.hX6Abq_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.hX6Abq_field+.hX6Abq_field{border-top:1px solid var(--dsw-alias-border-l2)}.hX6Abq_head{align-items:center;gap:8px;display:flex}.hX6Abq_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.hX6Abq_badges{align-items:center;gap:8px;display:inline-flex}.hX6Abq_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.hX6Abq_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.hX6Abq_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.hX6Abq_reset:disabled{cursor:default}.hX6Abq_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.hX6Abq_input,.hX6Abq_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.hX6Abq_input:focus-visible,.hX6Abq_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.hX6Abq_input:disabled,.hX6Abq_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.hX6Abq_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.hX6Abq_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.hX6Abq_selectWrap{position:relative}.hX6Abq_selectButton{appearance:none;text-align:left;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;width:100%;display:flex}.hX6Abq_selectLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.hX6Abq_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.hX6Abq_selectChevronOpen{transform:rotate(180deg)}.hX6Abq_selectPopup{z-index:40;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);max-height:240px;box-shadow:0 8px 24px var(--dsw-alias-bg-mask-2);opacity:0;border-radius:8px;flex-direction:column;padding:4px;transition:opacity .1s,transform .1s;display:flex;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto;transform:translateY(-4px)}.hX6Abq_selectPopupOpen{opacity:1;transform:none}.hX6Abq_selectPopupClose{opacity:0;pointer-events:none;transform:translateY(-4px)}.hX6Abq_selectOption{color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;border-radius:6px;padding:6px 10px;font-size:13px;line-height:1.5;overflow:hidden}.hX6Abq_selectOption:hover,.hX6Abq_selectOptionActive{background:var(--dsw-alias-interactive-bg-hover)}.hX6Abq_selectOptionSelected{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 10%, transparent);font-weight:500}.hX6Abq_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.hX6Abq_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.hX6Abq_card,.hX6Abq_header,.hX6Abq_chevron,.hX6Abq_chevronOpen,.hX6Abq_discard,.hX6Abq_save,.hX6Abq_selectChevron,.hX6Abq_selectChevronOpen,.hX6Abq_selectPopup{transition:none}}";
|
|
12
13
|
const tagId$1 = "@linxin666/dsh-client-ui-community-plugins/settings-card.module.css";
|
|
13
14
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
14
15
|
const tag = document.createElement("style");
|
|
@@ -45,7 +46,18 @@ window.__ModuleLoader__.load({
|
|
|
45
46
|
"readOnly": "hX6Abq_readOnly",
|
|
46
47
|
"reset": "hX6Abq_reset",
|
|
47
48
|
"save": "hX6Abq_save",
|
|
48
|
-
"select": "hX6Abq_select"
|
|
49
|
+
"select": "hX6Abq_select",
|
|
50
|
+
"selectButton": "hX6Abq_selectButton",
|
|
51
|
+
"selectChevron": "hX6Abq_selectChevron",
|
|
52
|
+
"selectChevronOpen": "hX6Abq_selectChevronOpen",
|
|
53
|
+
"selectLabel": "hX6Abq_selectLabel",
|
|
54
|
+
"selectOption": "hX6Abq_selectOption",
|
|
55
|
+
"selectOptionActive": "hX6Abq_selectOptionActive",
|
|
56
|
+
"selectOptionSelected": "hX6Abq_selectOptionSelected",
|
|
57
|
+
"selectPopup": "hX6Abq_selectPopup",
|
|
58
|
+
"selectPopupClose": "hX6Abq_selectPopupClose",
|
|
59
|
+
"selectPopupOpen": "hX6Abq_selectPopupOpen",
|
|
60
|
+
"selectWrap": "hX6Abq_selectWrap"
|
|
49
61
|
};
|
|
50
62
|
//#endregion
|
|
51
63
|
//#region src/client/PluginSettingsCard.tsx
|
|
@@ -179,6 +191,175 @@ window.__ModuleLoader__.load({
|
|
|
179
191
|
}) : null]
|
|
180
192
|
});
|
|
181
193
|
}
|
|
194
|
+
const NON_SKIN_BODY_MARKERS = /* @__PURE__ */ new Set(["dshSkinCenter", "dshSidebarCollapsed"]);
|
|
195
|
+
function isSkinActive() {
|
|
196
|
+
return Object.keys(document.body.dataset).some((key) => key.startsWith("dsh") && !NON_SKIN_BODY_MARKERS.has(key));
|
|
197
|
+
}
|
|
198
|
+
const SELECT_CLOSE_MS = 100;
|
|
199
|
+
/**
|
|
200
|
+
* The shared dual-mode select control. While an appearance skin is active it
|
|
201
|
+
* renders the legacy native `<select>` untouched, so element-level skin
|
|
202
|
+
* selectors keep working; under the default appearance it renders a
|
|
203
|
+
* self-drawn `role="listbox"` popup whose open/close is transition-animated.
|
|
204
|
+
* Staged cards reach it through BooleanField/ChoiceField; immediate-apply
|
|
205
|
+
* editors (the side-card prefs) bind it directly through onEdit.
|
|
206
|
+
* 双模式下拉框:皮肤激活时用原生 select,默认外观用自绘动画弹层。
|
|
207
|
+
*/
|
|
208
|
+
function SelectField(props) {
|
|
209
|
+
const { id, options, value } = props;
|
|
210
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
211
|
+
const [closing, setClosing] = (0, react.useState)(false);
|
|
212
|
+
const [phase, setPhase] = (0, react.useState)("initial");
|
|
213
|
+
const [activeIndex, setActiveIndex] = (0, react.useState)(0);
|
|
214
|
+
const closeTimer = (0, react.useRef)(void 0);
|
|
215
|
+
const wrapRef = (0, react.useRef)(null);
|
|
216
|
+
const popupRef = (0, react.useRef)(null);
|
|
217
|
+
const currentIndex = () => {
|
|
218
|
+
const index = options.findIndex((option) => option.value === value);
|
|
219
|
+
return index >= 0 ? index : 0;
|
|
220
|
+
};
|
|
221
|
+
const close = (0, react.useCallback)(() => {
|
|
222
|
+
if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
|
|
223
|
+
setClosing(true);
|
|
224
|
+
closeTimer.current = setTimeout(() => {
|
|
225
|
+
setClosing(false);
|
|
226
|
+
setOpen(false);
|
|
227
|
+
}, SELECT_CLOSE_MS);
|
|
228
|
+
}, []);
|
|
229
|
+
const openPopup = () => {
|
|
230
|
+
if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
|
|
231
|
+
setActiveIndex(currentIndex());
|
|
232
|
+
setPhase("initial");
|
|
233
|
+
setClosing(false);
|
|
234
|
+
setOpen(true);
|
|
235
|
+
};
|
|
236
|
+
const commit = (index) => {
|
|
237
|
+
const option = options[index];
|
|
238
|
+
if (option) props.onEdit(option.value);
|
|
239
|
+
close();
|
|
240
|
+
};
|
|
241
|
+
const onTriggerClick = () => {
|
|
242
|
+
if (props.disabled) return;
|
|
243
|
+
if (open && !closing) close();
|
|
244
|
+
else openPopup();
|
|
245
|
+
};
|
|
246
|
+
const onKeyDown = (event) => {
|
|
247
|
+
if (props.disabled) return;
|
|
248
|
+
const count = options.length;
|
|
249
|
+
switch (event.key) {
|
|
250
|
+
case "ArrowDown":
|
|
251
|
+
case "ArrowUp":
|
|
252
|
+
case "Enter":
|
|
253
|
+
case " ":
|
|
254
|
+
event.preventDefault();
|
|
255
|
+
if (!open) openPopup();
|
|
256
|
+
else if (!closing) if (event.key === "ArrowDown") setActiveIndex((index) => (index + 1) % count);
|
|
257
|
+
else if (event.key === "ArrowUp") setActiveIndex((index) => (index - 1 + count) % count);
|
|
258
|
+
else commit(activeIndex);
|
|
259
|
+
break;
|
|
260
|
+
case "Escape":
|
|
261
|
+
if (open) {
|
|
262
|
+
event.preventDefault();
|
|
263
|
+
event.stopPropagation();
|
|
264
|
+
close();
|
|
265
|
+
}
|
|
266
|
+
break;
|
|
267
|
+
case "Tab":
|
|
268
|
+
if (open) close();
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
(0, react.useEffect)(() => () => {
|
|
273
|
+
if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
|
|
274
|
+
}, []);
|
|
275
|
+
(0, react.useLayoutEffect)(() => {
|
|
276
|
+
if (open && !closing && phase === "initial") {
|
|
277
|
+
popupRef.current?.offsetHeight;
|
|
278
|
+
setPhase("open");
|
|
279
|
+
}
|
|
280
|
+
}, [
|
|
281
|
+
open,
|
|
282
|
+
closing,
|
|
283
|
+
phase
|
|
284
|
+
]);
|
|
285
|
+
(0, react.useEffect)(() => {
|
|
286
|
+
if (!open) return;
|
|
287
|
+
const onPointerDown = (event) => {
|
|
288
|
+
const target = event.target;
|
|
289
|
+
if (target instanceof Node && !wrapRef.current?.contains(target)) close();
|
|
290
|
+
};
|
|
291
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
292
|
+
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
293
|
+
}, [open, close]);
|
|
294
|
+
(0, react.useEffect)(() => {
|
|
295
|
+
if (props.disabled && open) close();
|
|
296
|
+
}, [
|
|
297
|
+
props.disabled,
|
|
298
|
+
open,
|
|
299
|
+
close
|
|
300
|
+
]);
|
|
301
|
+
if (isSkinActive()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
302
|
+
id,
|
|
303
|
+
className: settings_card_module_css_default.select,
|
|
304
|
+
value,
|
|
305
|
+
disabled: props.disabled,
|
|
306
|
+
onChange: (event) => {
|
|
307
|
+
props.onEdit(event.target.value);
|
|
308
|
+
},
|
|
309
|
+
children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
310
|
+
value: option.value,
|
|
311
|
+
children: option.label
|
|
312
|
+
}, option.value))
|
|
313
|
+
});
|
|
314
|
+
const label = options.find((option) => option.value === value)?.label ?? "";
|
|
315
|
+
const popupClass = closing ? `${settings_card_module_css_default.selectPopup} ${settings_card_module_css_default.selectPopupClose}` : phase === "open" ? `${settings_card_module_css_default.selectPopup} ${settings_card_module_css_default.selectPopupOpen}` : settings_card_module_css_default.selectPopup;
|
|
316
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
317
|
+
className: settings_card_module_css_default.selectWrap,
|
|
318
|
+
ref: wrapRef,
|
|
319
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
320
|
+
type: "button",
|
|
321
|
+
id,
|
|
322
|
+
className: `${settings_card_module_css_default.select} ${settings_card_module_css_default.selectButton}`,
|
|
323
|
+
disabled: props.disabled,
|
|
324
|
+
"aria-haspopup": "listbox",
|
|
325
|
+
"aria-expanded": open,
|
|
326
|
+
"aria-activedescendant": open ? `${id}-o${activeIndex}` : void 0,
|
|
327
|
+
"aria-invalid": props.invalid || void 0,
|
|
328
|
+
onClick: onTriggerClick,
|
|
329
|
+
onKeyDown,
|
|
330
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
331
|
+
className: settings_card_module_css_default.selectLabel,
|
|
332
|
+
children: label
|
|
333
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
334
|
+
width: "14",
|
|
335
|
+
height: "14",
|
|
336
|
+
viewBox: "0 0 14 14",
|
|
337
|
+
fill: "none",
|
|
338
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
339
|
+
className: open ? `${settings_card_module_css_default.selectChevron} ${settings_card_module_css_default.selectChevronOpen}` : settings_card_module_css_default.selectChevron,
|
|
340
|
+
"aria-hidden": "true",
|
|
341
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
342
|
+
d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
|
|
343
|
+
fill: "currentColor"
|
|
344
|
+
})
|
|
345
|
+
})]
|
|
346
|
+
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
347
|
+
className: popupClass,
|
|
348
|
+
role: "listbox",
|
|
349
|
+
ref: popupRef,
|
|
350
|
+
children: options.map((option, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
351
|
+
id: `${id}-o${index}`,
|
|
352
|
+
role: "option",
|
|
353
|
+
"aria-selected": option.value === value,
|
|
354
|
+
className: `${settings_card_module_css_default.selectOption}${option.value === value ? ` ${settings_card_module_css_default.selectOptionSelected}` : ""}${index === activeIndex && !closing ? ` ${settings_card_module_css_default.selectOptionActive}` : ""}`,
|
|
355
|
+
onClick: () => {
|
|
356
|
+
commit(index);
|
|
357
|
+
},
|
|
358
|
+
children: option.label
|
|
359
|
+
}, option.value))
|
|
360
|
+
}) : null]
|
|
361
|
+
});
|
|
362
|
+
}
|
|
182
363
|
/** A staged boolean field: 继承 / 开 / 关. */
|
|
183
364
|
function BooleanField(props) {
|
|
184
365
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -204,28 +385,26 @@ window.__ModuleLoader__.load({
|
|
|
204
385
|
})]
|
|
205
386
|
}) : null]
|
|
206
387
|
}),
|
|
207
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
388
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
|
|
208
389
|
id: props.id,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
disabled: props.disabled,
|
|
212
|
-
onChange: (event) => {
|
|
213
|
-
props.onEdit(event.target.value);
|
|
214
|
-
},
|
|
215
|
-
children: [
|
|
216
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
390
|
+
options: [
|
|
391
|
+
{
|
|
217
392
|
value: "",
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
393
|
+
label: props.inheritLabel
|
|
394
|
+
},
|
|
395
|
+
{
|
|
221
396
|
value: "true",
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
397
|
+
label: props.onLabel
|
|
398
|
+
},
|
|
399
|
+
{
|
|
225
400
|
value: "false",
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
]
|
|
401
|
+
label: props.offLabel
|
|
402
|
+
}
|
|
403
|
+
],
|
|
404
|
+
value: props.text,
|
|
405
|
+
disabled: props.disabled,
|
|
406
|
+
invalid: props.invalid,
|
|
407
|
+
onEdit: props.onEdit
|
|
229
408
|
}),
|
|
230
409
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
231
410
|
className: settings_card_module_css_default.hint,
|
|
@@ -548,7 +727,6 @@ window.__ModuleLoader__.load({
|
|
|
548
727
|
"repo": "https://github.com/v833/dsh-chat-summary",
|
|
549
728
|
"description": "总结当前对话并导出为 Markdown / DOCX / PDF,可选 LLM 智能总结(用户自配 API Key)。",
|
|
550
729
|
"descriptionEn": "Summarize the current conversation and export it as Markdown / DOCX / PDF, with optional LLM summarization using your own API key.",
|
|
551
|
-
"npm": "@linxin666/dsh-client-ui-chat-summary",
|
|
552
730
|
"category": "knowledge"
|
|
553
731
|
},
|
|
554
732
|
{
|
|
@@ -604,16 +782,6 @@ window.__ModuleLoader__.load({
|
|
|
604
782
|
"npm": "dsh-mnemon",
|
|
605
783
|
"category": "knowledge"
|
|
606
784
|
},
|
|
607
|
-
{
|
|
608
|
-
"id": "dsh-plugin-hub",
|
|
609
|
-
"name": "插件中心(dsh-plugin-hub)",
|
|
610
|
-
"nameEn": "Plugin Hub",
|
|
611
|
-
"author": "Noob-stupid",
|
|
612
|
-
"repo": "https://github.com/Noob-stupid/dsh-plugin-hub",
|
|
613
|
-
"description": "插件管理面板:已安装插件一键启用/停用,内置 GitHub dsh-plugin 插件市场(官方/聚合识别、子包浏览、一键安装与本地 AI 兜底修复、删除卸载)。",
|
|
614
|
-
"descriptionEn": "Plugin management panel: one-click enable/disable, a GitHub dsh-plugin marketplace with official/aggregate detection, subpackage browsing, one-click installs with local-AI fallback repair, and uninstall.",
|
|
615
|
-
"category": "tools"
|
|
616
|
-
},
|
|
617
785
|
{
|
|
618
786
|
"id": "dsh-genui",
|
|
619
787
|
"name": "GenUI 生成式 UI",
|
|
@@ -675,6 +843,7 @@ window.__ModuleLoader__.load({
|
|
|
675
843
|
"repo": "https://github.com/Qinling-Melon-Farmers/dsh-memoir",
|
|
676
844
|
"description": "项目持久化记忆与会话经验沉淀:memoir_record / memoir_read 工具(BM25 相关度排序检索)、每轮工作自动蒸馏、记忆诊断面板与跨项目全局检索,纯本地零外部依赖。",
|
|
677
845
|
"descriptionEn": "Project persistent memory and session-lessons distillation: memoir_record / memoir_read tools with BM25-ranked retrieval, per-turn auto-distill, a memory diagnostics panel and cross-project search - fully local, no external dependency.",
|
|
846
|
+
"npm": "dsh-memoir",
|
|
678
847
|
"category": "knowledge"
|
|
679
848
|
},
|
|
680
849
|
{
|
|
@@ -700,14 +869,162 @@ window.__ModuleLoader__.load({
|
|
|
700
869
|
"category": "tools"
|
|
701
870
|
},
|
|
702
871
|
{
|
|
703
|
-
"id": "dsh-
|
|
704
|
-
"name": "
|
|
705
|
-
"nameEn": "
|
|
706
|
-
"author": "
|
|
707
|
-
"repo": "https://github.com/
|
|
708
|
-
"description": "
|
|
709
|
-
"descriptionEn": "
|
|
872
|
+
"id": "dsh-friendly-steps",
|
|
873
|
+
"name": "过程精简 Friendly Steps",
|
|
874
|
+
"nameEn": "Friendly Steps",
|
|
875
|
+
"author": "dongwenxiu83-web",
|
|
876
|
+
"repo": "https://github.com/dongwenxiu83-web/dsh-friendly-steps",
|
|
877
|
+
"description": "面向文字工作者的过程精简:纯 CSS 折叠 Think / 工具调用等技术过程行,右下角浮条「已完成 N 步」一键展开收起,失败步骤红色计数反馈;不注入 React 列表、无全量扫描,零侵入。",
|
|
878
|
+
"descriptionEn": "Writer-friendly process collapse: hides Think / tool-call rows via pure CSS behind a floating 'N steps done' pill with red failure counts and one-click expand/collapse. No foreign nodes in React lists, no full-DOM scans — zero interference.",
|
|
879
|
+
"category": "ui"
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
"id": "dsh-full-remote",
|
|
883
|
+
"name": "全功能远程访问",
|
|
884
|
+
"nameEn": "Full Remote Access",
|
|
885
|
+
"author": "JUANWANG-BUAA",
|
|
886
|
+
"repo": "https://github.com/JUANWANG-BUAA/dsh-full-remote",
|
|
887
|
+
"description": "令牌门控反向代理:公网隧道或局域网下,在手机等设备上远程使用 DeepSeek Harness,设置、凭据与文件访问保持可用,支持按设备会话。",
|
|
888
|
+
"descriptionEn": "Token-gated reverse proxy for remote access: use DeepSeek Harness over public tunnels or LAN from phones and other devices, with settings, credentials and file access kept working and per-device sessions.",
|
|
889
|
+
"npm": "dsh-full-remote",
|
|
890
|
+
"category": "integration"
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
"id": "dsh-palate",
|
|
894
|
+
"name": "Palate 品味训练",
|
|
895
|
+
"nameEn": "Palate Design Taste",
|
|
896
|
+
"author": "guo6x",
|
|
897
|
+
"repo": "https://github.com/guo6x/dsh-palate",
|
|
898
|
+
"description": "会长大的眼:可积累的设计品味语料库(好/坏例子 + 编码原则),喂得越多评审越准;6 个 palate_* 工具(review/add/learn/list/principles/stats)+ 成长面板,node:sqlite + 人可读 Markdown 镜像,零运行时依赖。",
|
|
899
|
+
"descriptionEn": "An eye that grows: an accumulated design-taste corpus (good/bad examples plus codified principles) that sharpens with every example you feed; six palate_* tools (review/add/learn/list/principles/stats) plus a growth panel, node:sqlite with human-readable Markdown mirrors, zero runtime dependencies.",
|
|
900
|
+
"category": "knowledge"
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
"id": "dsh-archive-manager",
|
|
904
|
+
"name": "归档管理",
|
|
905
|
+
"nameEn": "Archive Manager",
|
|
906
|
+
"author": "z953218350",
|
|
907
|
+
"repo": "https://github.com/z953218350/dsh-archive-manager",
|
|
908
|
+
"description": "设置页 Codex 风格会话归档管理:按项目分组、搜索筛选、预览对话内容、一键恢复与彻底物理删除,内置 zstd 解压引擎。",
|
|
909
|
+
"descriptionEn": "Codex-style archived session manager in settings: group by project, search and filter, preview conversation history, restore to sidebar, and permanently delete from disk; built-in zstd decompression.",
|
|
910
|
+
"npm": "@mlgbnb/dsh-archive-manager",
|
|
911
|
+
"category": "utility"
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
"id": "dsh-history-tree",
|
|
915
|
+
"name": "历史树",
|
|
916
|
+
"nameEn": "History Tree",
|
|
917
|
+
"author": "z953218350",
|
|
918
|
+
"repo": "https://github.com/z953218350/dsh-history-tree",
|
|
919
|
+
"description": "在聊天消息左侧提供 Codex 风格对话轮次时间线点阵与悬浮历史概览卡片,支持鱼眼放大动效与点击直达对应轮次。",
|
|
920
|
+
"descriptionEn": "Codex-style conversation turn timeline rail and hover overview cards beside chat messages, with fish-eye magnification and click-to-jump navigation.",
|
|
921
|
+
"npm": "dsh-history-tree",
|
|
922
|
+
"category": "ui"
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
"id": "dsh-gzip",
|
|
926
|
+
"name": "dsh-gzip",
|
|
927
|
+
"nameEn": "dsh-gzip",
|
|
928
|
+
"author": "wingsky-1",
|
|
929
|
+
"repo": "https://github.com/wingsky-1/dsh-plugin-hub/tree/main/packages/dsh-gzip",
|
|
930
|
+
"description": "/api 响应 gzip 压缩插件:为远程 / 低带宽链路开启 /api 响应压缩,解决会话历史因大响应无压缩而超时加载失败的问题;SSE / zip / 已编码响应自动豁免,无全局副作用。",
|
|
931
|
+
"descriptionEn": "API response gzip plugin for DSH Web GUI: enables gzip on /api responses for remote / low-bandwidth links, fixing session-history loads that fail on the timeout of large uncompressed responses; SSE, zip and already-encoded responses are passed through untouched, with no global side effects.",
|
|
932
|
+
"npm": "@wingsky-1/dsh-gzip"
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
"id": "dsh-wsl-bridge",
|
|
936
|
+
"name": "WSL Windows 桥",
|
|
937
|
+
"nameEn": "WSL Windows Bridge",
|
|
938
|
+
"author": "ch1bug",
|
|
939
|
+
"repo": "https://github.com/ch1bug/dsh-wsl-bridge",
|
|
940
|
+
"description": "让 WSL 里的 agent 访问 Windows 侧:读写 C:\\ 文件、运行 .exe、Explorer 打开、wslpath 路径互转与盘符枚举(win_ls/read/write/run/open/path/drives)。",
|
|
941
|
+
"descriptionEn": "Lets a WSL agent reach the Windows host: read/write C:\\ files, run .exe programs, open in Explorer, convert paths via wslpath and list drives (win_ls/read/write/run/open/path/drives).",
|
|
942
|
+
"category": "integration"
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
"id": "dsh-mimo-agent-tools",
|
|
946
|
+
"name": "MiMo 多模态工具",
|
|
947
|
+
"nameEn": "MiMo Multimodal Tools",
|
|
948
|
+
"author": "ch1bug",
|
|
949
|
+
"repo": "https://github.com/ch1bug/dsh-mimo-agent-tools",
|
|
950
|
+
"description": "把小米 MiMo API 封装为 agent 工具:联网搜索、图像/音频/视频理解、语音转写、语音合成与音色克隆(mimo_search/vision/audio/video/asr/tts/voiceclone)。",
|
|
951
|
+
"descriptionEn": "Turns the Xiaomi MiMo API into agent tools: web search, image/audio/video understanding, speech-to-text, text-to-speech and voice cloning (mimo_search/vision/audio/video/asr/tts/voiceclone).",
|
|
952
|
+
"category": "tools"
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
"id": "dsh-skill-fuzzy",
|
|
956
|
+
"name": "技能模糊搜索",
|
|
957
|
+
"nameEn": "Skill Fuzzy Search",
|
|
958
|
+
"author": "ch1bug",
|
|
959
|
+
"repo": "https://github.com/ch1bug/dsh-skill-fuzzy",
|
|
960
|
+
"description": "Codex 风格模糊技能搜索:/bug、/diag、/regression 都能在 / 技能菜单里命中 diagnosing-bugs(名称子序列 + 描述连续子串匹配)。",
|
|
961
|
+
"descriptionEn": "Codex-style fuzzy skill search: /bug, /diag and /regression all find diagnosing-bugs in the / skill menu (name subsequence + description substring matching).",
|
|
710
962
|
"category": "ui"
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
"id": "dsh-secret-redactor",
|
|
966
|
+
"name": "敏感信息脱敏",
|
|
967
|
+
"nameEn": "Secret Redactor",
|
|
968
|
+
"author": "DamonKoy",
|
|
969
|
+
"repo": "https://github.com/DamonKoy/dsh-plugins/tree/main/packages/dsh-secret-redactor",
|
|
970
|
+
"description": "自动掩码工具输出中的 API key、token、JWT、私钥与配置密钥。",
|
|
971
|
+
"descriptionEn": "Auto-masks API keys, tokens, JWTs and private keys in tool results shown to the model."
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
"id": "dsh-approve-for-me",
|
|
975
|
+
"name": "自动审批审核",
|
|
976
|
+
"nameEn": "Approve for Me",
|
|
977
|
+
"author": "DamonKoy",
|
|
978
|
+
"repo": "https://github.com/DamonKoy/dsh-plugins/tree/main/packages/dsh-approve-for-me",
|
|
979
|
+
"description": "只读工具自动放行、危险命令自动拒绝,可配置全自动模式。",
|
|
980
|
+
"descriptionEn": "Auto-approves read-only tools, auto-denies dangerous commands, optional full-auto mode."
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
"id": "dsh-mcp-client-v2",
|
|
984
|
+
"name": "MCP 客户端 v2",
|
|
985
|
+
"nameEn": "MCP Client v2",
|
|
986
|
+
"author": "DamonKoy",
|
|
987
|
+
"repo": "https://github.com/DamonKoy/dsh-plugins/tree/main/packages/dsh-mcp-client-v2",
|
|
988
|
+
"description": "MCP 客户端增强:分页工具发现、非阻塞启动、工具搜索。",
|
|
989
|
+
"descriptionEn": "Enhanced MCP client: paginated tool discovery, non-blocking startup, tool search."
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
"id": "dsh-memories",
|
|
993
|
+
"name": "项目记忆",
|
|
994
|
+
"nameEn": "Project Memories",
|
|
995
|
+
"author": "DamonKoy",
|
|
996
|
+
"repo": "https://github.com/DamonKoy/dsh-plugins/tree/main/packages/dsh-memories",
|
|
997
|
+
"description": "按项目作用域持久化的键值记忆,支持增删查列与搜索。",
|
|
998
|
+
"descriptionEn": "Project-scoped persistent key-value memories with set/get/list/delete/search."
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
"id": "dsh-system-proxy",
|
|
1002
|
+
"name": "系统代理",
|
|
1003
|
+
"nameEn": "System Proxy",
|
|
1004
|
+
"author": "DamonKoy",
|
|
1005
|
+
"repo": "https://github.com/DamonKoy/dsh-plugins/tree/main/packages/dsh-system-proxy",
|
|
1006
|
+
"description": "检测系统代理(scutil/环境变量/PAC)并为子进程导出代理环境变量。",
|
|
1007
|
+
"descriptionEn": "Detects system proxy (scutil/env/PAC) and exports proxy env for child processes."
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
"id": "dsh-usage-cost",
|
|
1011
|
+
"name": "用量成本提醒",
|
|
1012
|
+
"nameEn": "Usage & Cost",
|
|
1013
|
+
"author": "DamonKoy",
|
|
1014
|
+
"repo": "https://github.com/DamonKoy/dsh-plugins/tree/main/packages/dsh-usage-cost",
|
|
1015
|
+
"description": "统计会话与每日 token 用量和估算成本,超预算提醒。",
|
|
1016
|
+
"descriptionEn": "Tracks token usage and estimated cost per session/day with budget alerts."
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
"id": "dsh-notifier",
|
|
1020
|
+
"name": "dsh-notifier",
|
|
1021
|
+
"nameEn": "dsh-notifier",
|
|
1022
|
+
"author": "wingsky-1",
|
|
1023
|
+
"repo": "https://github.com/wingsky-1/dsh-plugin-hub/tree/main/packages/dsh-notifier",
|
|
1024
|
+
"description": "审批/完成/错误事件通知:浏览器 Notification + 系统原生 toast(Windows PowerShell WinRT / Linux·macOS notify-send,零依赖);提示音可配、错误同类合并、免打扰时段、通知历史与配置面板;非安全上下文自动降级横幅。",
|
|
1025
|
+
"descriptionEn": "Approval / completion / error event notifications: browser Notification API plus native OS toasts (Windows PowerShell WinRT, Linux and macOS notify-send, zero dependencies); configurable sound, error merging, quiet hours, notification history and a settings panel; falls back to in-page banners outside secure contexts.",
|
|
1026
|
+
"npm": "@wingsky-1/dsh-notifier",
|
|
1027
|
+
"category": "utility"
|
|
711
1028
|
}
|
|
712
1029
|
];
|
|
713
1030
|
//#endregion
|
|
@@ -742,8 +1059,106 @@ window.__ModuleLoader__.load({
|
|
|
742
1059
|
return true;
|
|
743
1060
|
}
|
|
744
1061
|
//#endregion
|
|
1062
|
+
//#region src/client/plugin-manager-bridge.ts
|
|
1063
|
+
let snapshot = {
|
|
1064
|
+
face: null,
|
|
1065
|
+
version: 0
|
|
1066
|
+
};
|
|
1067
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1068
|
+
/** Replace the held face and notify subscribers. */
|
|
1069
|
+
function setFace(face) {
|
|
1070
|
+
snapshot = {
|
|
1071
|
+
face,
|
|
1072
|
+
version: snapshot.version + 1
|
|
1073
|
+
};
|
|
1074
|
+
for (const listener of listeners) listener();
|
|
1075
|
+
}
|
|
1076
|
+
/** Current bridge snapshot (cached reference, safe for useSyncExternalStore). */
|
|
1077
|
+
function getPluginManagerSnapshot() {
|
|
1078
|
+
return snapshot;
|
|
1079
|
+
}
|
|
1080
|
+
/** Subscribe to face changes; returns the unsubscribe function. */
|
|
1081
|
+
function subscribePluginManager(listener) {
|
|
1082
|
+
listeners.add(listener);
|
|
1083
|
+
return () => {
|
|
1084
|
+
listeners.delete(listener);
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Bridge the optional 'pluginManager' service into the module store. Uses
|
|
1089
|
+
* ctx.inject (NOT the plugin's module-level inject array) so the service
|
|
1090
|
+
* stays optional: the inner callback runs when the sibling plugin provides
|
|
1091
|
+
* the face and is disposed when it goes away, which clears the store.
|
|
1092
|
+
* @param ctx - the client root context.
|
|
1093
|
+
*/
|
|
1094
|
+
function bridgePluginManager(ctx) {
|
|
1095
|
+
ctx.inject(["pluginManager"], (inner) => {
|
|
1096
|
+
inner.effect(() => {
|
|
1097
|
+
setFace(inner.pluginManager ?? null);
|
|
1098
|
+
return () => {
|
|
1099
|
+
setFace(null);
|
|
1100
|
+
};
|
|
1101
|
+
}, "community-plugins: pluginManager bridge");
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
//#endregion
|
|
1105
|
+
//#region src/client/install-source.ts
|
|
1106
|
+
/**
|
|
1107
|
+
* The spec handed to pluginManager.install(): the npm package name when the
|
|
1108
|
+
* entry is published there, else the repository URL (git install).
|
|
1109
|
+
* @param entry - the community index entry.
|
|
1110
|
+
* @returns the install spec.
|
|
1111
|
+
*/
|
|
1112
|
+
function installSpec(entry) {
|
|
1113
|
+
return entry.npm ?? entry.repo;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Normalize a git remote for comparison: lowercase, drop the transport
|
|
1117
|
+
* prefix ('git+' wrapper, 'https://', 'http://', 'git://', 'ssh://git@', the
|
|
1118
|
+
* scp-style 'git@host:' userinfo, and the pnpm host shorthands 'github:' /
|
|
1119
|
+
* 'gitlab:' / 'bitbucket:' that package.json records for shorthand installs),
|
|
1120
|
+
* then strip trailing slashes and one trailing '.git'
|
|
1121
|
+
* suffix (repeated, so 'x/.git/' and 'x.git' converge). Conservative on
|
|
1122
|
+
* purpose: no path rewriting beyond those steps, so distinct repositories
|
|
1123
|
+
* never collapse onto each other.
|
|
1124
|
+
* @param url - the repository URL as recorded by either side.
|
|
1125
|
+
* @returns the comparison key.
|
|
1126
|
+
*/
|
|
1127
|
+
function normalizeGitUrl(url) {
|
|
1128
|
+
let key = url.trim().toLowerCase();
|
|
1129
|
+
key = key.replace(/^git[+]/, "");
|
|
1130
|
+
key = key.replace(/^(github|gitlab):/, "$1.com/");
|
|
1131
|
+
key = key.replace(/^bitbucket:/, "bitbucket.org/");
|
|
1132
|
+
key = key.replace(/^(?:https?|git):[/][/]/, "");
|
|
1133
|
+
key = key.replace(/^ssh:[/][/]git@/, "");
|
|
1134
|
+
key = key.replace(/^git@([^/:]+):/, "$1/");
|
|
1135
|
+
for (;;) {
|
|
1136
|
+
const next = key.replace(/[/]+$/, "").replace(/[.]git$/, "");
|
|
1137
|
+
if (next === key) return key;
|
|
1138
|
+
key = next;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Match one community entry against the installed snapshot. npm-published
|
|
1143
|
+
* entries match on the installed row's package identity (id/name): the npm
|
|
1144
|
+
* source spec records the dependency RANGE ('^0.3.2') or an install-time
|
|
1145
|
+
* spec, not necessarily the bare package name, so it is only a fallback.
|
|
1146
|
+
* Repository entries match on the normalized git URL. An entry with an npm
|
|
1147
|
+
* field ONLY matches npm installs — a same-repository git install is a
|
|
1148
|
+
* different source and stays unmatched (the card then offers install, which
|
|
1149
|
+
* the host reconciles).
|
|
1150
|
+
* @param entry - the community index entry.
|
|
1151
|
+
* @param installed - the installed snapshot from the pluginManager service.
|
|
1152
|
+
* @returns the matching installed row, or null.
|
|
1153
|
+
*/
|
|
1154
|
+
function entryInstalled(entry, installed) {
|
|
1155
|
+
if (entry.npm !== void 0) return installed.find((item) => item.source.kind === "npm" && (item.id === entry.npm || item.name === entry.npm || item.source.spec === entry.npm)) ?? null;
|
|
1156
|
+
const key = normalizeGitUrl(entry.repo);
|
|
1157
|
+
return installed.find((item) => item.source.kind === "git" && normalizeGitUrl(item.source.spec) === key) ?? null;
|
|
1158
|
+
}
|
|
1159
|
+
//#endregion
|
|
745
1160
|
//#region \0dsh-css:packages/dsh-community-plugins/src/client/community.module.css.mjs
|
|
746
|
-
const css = ".z0J_8a_sectionList{margin:0;padding:0;list-style:none}.z0J_8a_market{flex-direction:column;gap:10px;margin-top:12px;display:flex}.z0J_8a_toolbar{display:flex}.z0J_8a_search{min-width:0;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;flex:1;padding:6px 10px;font-size:13px;line-height:1.5}.z0J_8a_search::placeholder{color:var(--dsw-alias-label-tertiary)}.z0J_8a_search:focus{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.z0J_8a_search:disabled{opacity:.5}.z0J_8a_filters{flex-wrap:wrap;gap:6px;display:flex}.z0J_8a_pill{font:inherit;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;border-radius:999px;padding:2px 10px;font-size:12px;line-height:1.6}.z0J_8a_pill:hover:enabled{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.z0J_8a_pill:disabled{opacity:.5;cursor:default}.z0J_8a_pillActive{color:var(--dsw-alias-bg-layer-3);background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);font-weight:600}.z0J_8a_pillActive:hover:enabled{color:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-label-primary)}.z0J_8a_resultCount{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.4}.z0J_8a_grid{grid-template-columns:repeat(auto-fill,minmax(256px,1fr));gap:8px;margin:0;padding:0;list-style:none;display:grid}.z0J_8a_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;gap:6px;min-width:0;padding:12px;display:flex}.z0J_8a_card:hover{border-color:var(--dsw-alias-label-dimmed)}.z0J_8a_cardHead{justify-content:space-between;align-items:baseline;gap:8px;min-width:0;display:flex}.z0J_8a_cardName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.z0J_8a_badge{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);white-space:nowrap;border-radius:999px;flex:none;padding:0 8px;font-size:11px;line-height:1.6}.z0J_8a_badgePublished{color:var(--dsw-alias-link-primary,var(--dsw-alias-button-info-fill));border-color:var(--dsw-alias-link-primary,var(--dsw-alias-button-info-fill))}.z0J_8a_cardMeta{min-width:0;color:var(--dsw-alias-label-tertiary);align-items:center;gap:6px;margin:0;font-size:12px;line-height:1.4;display:flex}.z0J_8a_cardCategory{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.z0J_8a_cardDot{flex:none}.z0J_8a_cardAuthor{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.z0J_8a_cardDescription{color:var(--dsw-alias-label-secondary);-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;margin:0;font-size:13px;line-height:1.45;display:-webkit-box;overflow:hidden}.z0J_8a_cardFooter{flex-direction:column;gap:6px;margin-top:auto;padding-top:6px;display:flex}.z0J_8a_cardTop{justify-content:space-between;align-items:center;gap:8px;min-width:0;display:flex}.z0J_8a_cardLink{color:var(--dsw-alias-link-primary,var(--dsw-alias-button-info-fill));font-size:13px;text-decoration:none}.z0J_8a_cardLink:hover{text-decoration:underline}.z0J_8a_cardCommand{box-sizing:border-box;width:100%;min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);white-space:nowrap;text-overflow:ellipsis;border-radius:4px;padding:2px 6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;overflow:hidden}.z0J_8a_installButton{font:inherit;color:var(--dsw-alias-bg-layer-3);background:var(--dsw-alias-button-info-fill);border:1px solid var(--dsw-alias-button-info-fill);cursor:pointer;white-space:nowrap;border-radius:6px;flex:none;padding:4px 12px;font-size:12px;font-weight:600;line-height:1.5}.z0J_8a_installButton:hover:enabled{opacity:.88}.z0J_8a_installButton:disabled{opacity:.5;cursor:default}.z0J_8a_installButtonCopied{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed);background:0 0}.z0J_8a_installNote{color:var(--dsw-alias-label-tertiary);margin:8px 2px 0;font-size:12px;line-height:1.4}.z0J_8a_empty{color:var(--dsw-alias-label-tertiary);margin:0;padding:8px 2px;font-size:13px}.z0J_8a_off{color:var(--dsw-alias-label-tertiary);margin:8px 2px 0;font-size:13px}.z0J_8a_notice{color:var(--dsw-alias-label-tertiary);margin:10px 2px 0;font-size:12px;line-height:1.4}";
|
|
1161
|
+
const css = ".z0J_8a_sectionList{margin:0;padding:0;list-style:none}.z0J_8a_market{flex-direction:column;gap:10px;margin-top:12px;display:flex}.z0J_8a_toolbar{display:flex}.z0J_8a_search{min-width:0;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;flex:1;padding:6px 10px;font-size:13px;line-height:1.5}.z0J_8a_search::placeholder{color:var(--dsw-alias-label-tertiary)}.z0J_8a_search:focus{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.z0J_8a_search:disabled{opacity:.5}.z0J_8a_filters{flex-wrap:wrap;gap:6px;display:flex}.z0J_8a_pill{font:inherit;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;border-radius:999px;padding:2px 10px;font-size:12px;line-height:1.6}.z0J_8a_pill:hover:enabled{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.z0J_8a_pill:disabled{opacity:.5;cursor:default}.z0J_8a_pillActive{color:var(--dsw-alias-bg-layer-3);background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);font-weight:600}.z0J_8a_pillActive:hover:enabled{color:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-label-primary)}.z0J_8a_resultCount{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.4}.z0J_8a_grid{grid-template-columns:repeat(auto-fill,minmax(256px,1fr));gap:8px;margin:0;padding:0;list-style:none;display:grid}.z0J_8a_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;gap:6px;min-width:0;padding:12px;display:flex}.z0J_8a_card:hover{border-color:var(--dsw-alias-label-dimmed)}.z0J_8a_cardHead{justify-content:space-between;align-items:baseline;gap:8px;min-width:0;display:flex}.z0J_8a_cardBadges{flex:none;align-items:center;gap:4px;min-width:0;display:flex}.z0J_8a_cardName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.z0J_8a_badge{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);white-space:nowrap;border-radius:999px;flex:none;padding:0 8px;font-size:11px;line-height:1.6}.z0J_8a_badgePublished{color:var(--dsw-alias-link-primary,var(--dsw-alias-button-info-fill));border-color:var(--dsw-alias-link-primary,var(--dsw-alias-button-info-fill))}.z0J_8a_badgeInstalled{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed);background:var(--dsw-alias-bg-layer-2)}.z0J_8a_cardMeta{min-width:0;color:var(--dsw-alias-label-tertiary);align-items:center;gap:6px;margin:0;font-size:12px;line-height:1.4;display:flex}.z0J_8a_cardCategory{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.z0J_8a_cardDot{flex:none}.z0J_8a_cardAuthor{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.z0J_8a_cardDescription{color:var(--dsw-alias-label-secondary);-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;margin:0;font-size:13px;line-height:1.45;display:-webkit-box;overflow:hidden}.z0J_8a_cardFooter{flex-direction:column;gap:6px;margin-top:auto;padding-top:6px;display:flex}.z0J_8a_cardTop{justify-content:space-between;align-items:center;gap:8px;min-width:0;display:flex}.z0J_8a_cardLink{color:var(--dsw-alias-link-primary,var(--dsw-alias-button-info-fill));font-size:13px;text-decoration:none}.z0J_8a_cardLink:hover{text-decoration:underline}.z0J_8a_cardCommand{box-sizing:border-box;width:100%;min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);white-space:nowrap;text-overflow:ellipsis;border-radius:4px;padding:2px 6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;overflow:hidden}.z0J_8a_installButton{font:inherit;color:var(--dsw-alias-bg-layer-3);background:var(--dsw-alias-button-info-fill);border:1px solid var(--dsw-alias-button-info-fill);cursor:pointer;white-space:nowrap;border-radius:6px;flex:none;padding:4px 12px;font-size:12px;font-weight:600;line-height:1.5}.z0J_8a_installButton:hover:enabled{opacity:.88}.z0J_8a_installButton:disabled{opacity:.5;cursor:default}.z0J_8a_installButtonCopied{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed);background:0 0}.z0J_8a_installButtonSecondary{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2);background:0 0}.z0J_8a_installButtonSecondary:hover:enabled{border-color:var(--dsw-alias-label-dimmed)}.z0J_8a_cardActions{flex:none;align-items:center;gap:6px;display:flex}.z0J_8a_progress{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.4}.z0J_8a_error{color:var(--dsw-alias-label-error,#c53030);overflow-wrap:anywhere;margin:0;font-size:12px;line-height:1.4}.z0J_8a_confirmBody{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px;line-height:1.5}.z0J_8a_modalActions{justify-content:flex-end;gap:8px;margin-top:16px;display:flex}.z0J_8a_dangerButton{background:var(--dsw-alias-label-error,#c53030);border-color:var(--dsw-alias-label-error,#c53030)}.z0J_8a_installNote{color:var(--dsw-alias-label-tertiary);margin:8px 2px 0;font-size:12px;line-height:1.4}.z0J_8a_empty{color:var(--dsw-alias-label-tertiary);margin:0;padding:8px 2px;font-size:13px}.z0J_8a_off{color:var(--dsw-alias-label-tertiary);margin:8px 2px 0;font-size:13px}.z0J_8a_notice{color:var(--dsw-alias-label-tertiary);margin:10px 2px 0;font-size:12px;line-height:1.4}";
|
|
747
1162
|
const tagId = "@linxin666/dsh-client-ui-community-plugins/community.module.css";
|
|
748
1163
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
749
1164
|
const tag = document.createElement("style");
|
|
@@ -754,9 +1169,12 @@ window.__ModuleLoader__.load({
|
|
|
754
1169
|
}
|
|
755
1170
|
var community_module_css_default = {
|
|
756
1171
|
"badge": "z0J_8a_badge",
|
|
1172
|
+
"badgeInstalled": "z0J_8a_badgeInstalled",
|
|
757
1173
|
"badgePublished": "z0J_8a_badgePublished",
|
|
758
1174
|
"card": "z0J_8a_card",
|
|
1175
|
+
"cardActions": "z0J_8a_cardActions",
|
|
759
1176
|
"cardAuthor": "z0J_8a_cardAuthor",
|
|
1177
|
+
"cardBadges": "z0J_8a_cardBadges",
|
|
760
1178
|
"cardCategory": "z0J_8a_cardCategory",
|
|
761
1179
|
"cardCommand": "z0J_8a_cardCommand",
|
|
762
1180
|
"cardDescription": "z0J_8a_cardDescription",
|
|
@@ -767,17 +1185,23 @@ window.__ModuleLoader__.load({
|
|
|
767
1185
|
"cardMeta": "z0J_8a_cardMeta",
|
|
768
1186
|
"cardName": "z0J_8a_cardName",
|
|
769
1187
|
"cardTop": "z0J_8a_cardTop",
|
|
1188
|
+
"confirmBody": "z0J_8a_confirmBody",
|
|
1189
|
+
"dangerButton": "z0J_8a_dangerButton",
|
|
770
1190
|
"empty": "z0J_8a_empty",
|
|
1191
|
+
"error": "z0J_8a_error",
|
|
771
1192
|
"filters": "z0J_8a_filters",
|
|
772
1193
|
"grid": "z0J_8a_grid",
|
|
773
1194
|
"installButton": "z0J_8a_installButton",
|
|
774
1195
|
"installButtonCopied": "z0J_8a_installButtonCopied",
|
|
1196
|
+
"installButtonSecondary": "z0J_8a_installButtonSecondary",
|
|
775
1197
|
"installNote": "z0J_8a_installNote",
|
|
776
1198
|
"market": "z0J_8a_market",
|
|
1199
|
+
"modalActions": "z0J_8a_modalActions",
|
|
777
1200
|
"notice": "z0J_8a_notice",
|
|
778
1201
|
"off": "z0J_8a_off",
|
|
779
1202
|
"pill": "z0J_8a_pill",
|
|
780
1203
|
"pillActive": "z0J_8a_pillActive",
|
|
1204
|
+
"progress": "z0J_8a_progress",
|
|
781
1205
|
"resultCount": "z0J_8a_resultCount",
|
|
782
1206
|
"search": "z0J_8a_search",
|
|
783
1207
|
"sectionList": "z0J_8a_sectionList",
|
|
@@ -830,6 +1254,12 @@ window.__ModuleLoader__.load({
|
|
|
830
1254
|
function installCommand(entry) {
|
|
831
1255
|
return `dsh plugin --profile web add ${entry.npm ?? entry.repo}`;
|
|
832
1256
|
}
|
|
1257
|
+
/** Progress polling cadence while an install is in flight. */
|
|
1258
|
+
const PROGRESS_POLL_MS = 500;
|
|
1259
|
+
/** Extract a displayable reason from an install/uninstall rejection. */
|
|
1260
|
+
function messageOf(reason) {
|
|
1261
|
+
return reason instanceof Error ? reason.message : String(reason);
|
|
1262
|
+
}
|
|
833
1263
|
/**
|
|
834
1264
|
* Localized entry copy read through the card's `t`. Falls back to the raw
|
|
835
1265
|
* entry field when the key is missing — the dictionary only carries the
|
|
@@ -862,6 +1292,102 @@ window.__ModuleLoader__.load({
|
|
|
862
1292
|
const [query, setQuery] = (0, react.useState)("");
|
|
863
1293
|
const [category, setCategory] = (0, react.useState)(null);
|
|
864
1294
|
const [copiedId, setCopiedId] = (0, react.useState)(null);
|
|
1295
|
+
const bridge = (0, react.useSyncExternalStore)(subscribePluginManager, getPluginManagerSnapshot);
|
|
1296
|
+
const face = props.pluginManager !== void 0 ? props.pluginManager : bridge.face;
|
|
1297
|
+
const faceLoopback = face !== null && face.isLoopback;
|
|
1298
|
+
const [installed, setInstalled] = (0, react.useState)(null);
|
|
1299
|
+
const [pending, setPending] = (0, react.useState)(null);
|
|
1300
|
+
const [progress, setProgress] = (0, react.useState)(null);
|
|
1301
|
+
const [errors, setErrors] = (0, react.useState)({});
|
|
1302
|
+
const [uninstallTarget, setUninstallTarget] = (0, react.useState)(null);
|
|
1303
|
+
(0, react.useEffect)(() => {
|
|
1304
|
+
if (face === null || !face.isLoopback) {
|
|
1305
|
+
setInstalled(null);
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
let alive = true;
|
|
1309
|
+
const refresh = () => {
|
|
1310
|
+
face.list().then((list) => {
|
|
1311
|
+
if (alive) setInstalled(list);
|
|
1312
|
+
}, () => {});
|
|
1313
|
+
};
|
|
1314
|
+
refresh();
|
|
1315
|
+
const unsubscribe = face.onChange(refresh);
|
|
1316
|
+
return () => {
|
|
1317
|
+
alive = false;
|
|
1318
|
+
unsubscribe();
|
|
1319
|
+
};
|
|
1320
|
+
}, [face, faceLoopback]);
|
|
1321
|
+
(0, react.useEffect)(() => {
|
|
1322
|
+
if (face === null || pending?.kind !== "install") {
|
|
1323
|
+
setProgress(null);
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
let alive = true;
|
|
1327
|
+
const poll = () => {
|
|
1328
|
+
face.status().then((item) => {
|
|
1329
|
+
if (alive) setProgress(item);
|
|
1330
|
+
}, () => {});
|
|
1331
|
+
};
|
|
1332
|
+
poll();
|
|
1333
|
+
const timer = setInterval(poll, PROGRESS_POLL_MS);
|
|
1334
|
+
return () => {
|
|
1335
|
+
alive = false;
|
|
1336
|
+
clearInterval(timer);
|
|
1337
|
+
};
|
|
1338
|
+
}, [face, pending?.kind === "install"]);
|
|
1339
|
+
const clearError = (id) => {
|
|
1340
|
+
setErrors((previous) => {
|
|
1341
|
+
if (!(id in previous)) return previous;
|
|
1342
|
+
const next = { ...previous };
|
|
1343
|
+
delete next[id];
|
|
1344
|
+
return next;
|
|
1345
|
+
});
|
|
1346
|
+
};
|
|
1347
|
+
const onInstall = (entry) => {
|
|
1348
|
+
if (face === null || !face.isLoopback || pending !== null) return;
|
|
1349
|
+
clearError(entry.id);
|
|
1350
|
+
setPending({
|
|
1351
|
+
kind: "install",
|
|
1352
|
+
id: entry.id
|
|
1353
|
+
});
|
|
1354
|
+
face.install(installSpec(entry)).then(() => {
|
|
1355
|
+
face.list().then(setInstalled, () => {});
|
|
1356
|
+
}, (reason) => {
|
|
1357
|
+
setErrors((previous) => ({
|
|
1358
|
+
...previous,
|
|
1359
|
+
[entry.id]: t("installFailed", { reason: messageOf(reason) })
|
|
1360
|
+
}));
|
|
1361
|
+
}).finally(() => {
|
|
1362
|
+
setPending(null);
|
|
1363
|
+
});
|
|
1364
|
+
};
|
|
1365
|
+
const onUninstallConfirm = () => {
|
|
1366
|
+
const target = uninstallTarget;
|
|
1367
|
+
if (face === null || target === null || pending !== null) return;
|
|
1368
|
+
const item = entryInstalled(target, installed ?? []);
|
|
1369
|
+
clearError(target.id);
|
|
1370
|
+
if (item === null) {
|
|
1371
|
+
setUninstallTarget(null);
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
setPending({
|
|
1375
|
+
kind: "uninstall",
|
|
1376
|
+
id: target.id
|
|
1377
|
+
});
|
|
1378
|
+
face.uninstall(item.id).then((list) => {
|
|
1379
|
+
setInstalled(list);
|
|
1380
|
+
setUninstallTarget(null);
|
|
1381
|
+
}, (reason) => {
|
|
1382
|
+
setErrors((previous) => ({
|
|
1383
|
+
...previous,
|
|
1384
|
+
[target.id]: t("uninstallFailed", { reason: messageOf(reason) })
|
|
1385
|
+
}));
|
|
1386
|
+
setUninstallTarget(null);
|
|
1387
|
+
}).finally(() => {
|
|
1388
|
+
setPending(null);
|
|
1389
|
+
});
|
|
1390
|
+
};
|
|
865
1391
|
const copyCommand = (id, command) => {
|
|
866
1392
|
const mark = () => {
|
|
867
1393
|
setCopiedId(id);
|
|
@@ -885,6 +1411,7 @@ window.__ModuleLoader__.load({
|
|
|
885
1411
|
mark();
|
|
886
1412
|
};
|
|
887
1413
|
const disabled = !state.writable;
|
|
1414
|
+
const mutationsDisabled = disabled || pending !== null;
|
|
888
1415
|
const visible = state.enabled.text !== "false";
|
|
889
1416
|
const fieldProps = {
|
|
890
1417
|
overriddenLabel: t("settings.overridden"),
|
|
@@ -1017,6 +1544,10 @@ window.__ModuleLoader__.load({
|
|
|
1017
1544
|
const copied = copiedId === plugin.id;
|
|
1018
1545
|
const name = entryCopy(t, `name.${plugin.id}`, plugin.nameEn || plugin.name);
|
|
1019
1546
|
const description = entryCopy(t, `desc.${plugin.id}`, plugin.descriptionEn ?? plugin.description ?? "");
|
|
1547
|
+
const installedItem = faceLoopback ? entryInstalled(plugin, installed ?? []) : null;
|
|
1548
|
+
const isInstalling = pending?.kind === "install" && pending.id === plugin.id;
|
|
1549
|
+
const isUninstalling = pending?.kind === "uninstall" && pending.id === plugin.id;
|
|
1550
|
+
const error = errors[plugin.id];
|
|
1020
1551
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
1021
1552
|
className: community_module_css_default.card,
|
|
1022
1553
|
children: [
|
|
@@ -1026,10 +1557,16 @@ window.__ModuleLoader__.load({
|
|
|
1026
1557
|
className: community_module_css_default.cardName,
|
|
1027
1558
|
title: name,
|
|
1028
1559
|
children: name
|
|
1029
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1030
|
-
className:
|
|
1031
|
-
|
|
1032
|
-
|
|
1560
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1561
|
+
className: community_module_css_default.cardBadges,
|
|
1562
|
+
children: [installedItem !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1563
|
+
className: `${community_module_css_default.badge} ${community_module_css_default.badgeInstalled}`,
|
|
1564
|
+
children: t("badge.installed")
|
|
1565
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1566
|
+
className: plugin.npm ? `${community_module_css_default.badge} ${community_module_css_default.badgePublished}` : community_module_css_default.badge,
|
|
1567
|
+
title: plugin.npm ?? plugin.repo,
|
|
1568
|
+
children: plugin.npm ? t("badge.published") : t("badge.source")
|
|
1569
|
+
})]
|
|
1033
1570
|
})]
|
|
1034
1571
|
}),
|
|
1035
1572
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -1057,28 +1594,64 @@ window.__ModuleLoader__.load({
|
|
|
1057
1594
|
}) : null,
|
|
1058
1595
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1059
1596
|
className: community_module_css_default.cardFooter,
|
|
1060
|
-
children: [
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1597
|
+
children: [
|
|
1598
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1599
|
+
className: community_module_css_default.cardTop,
|
|
1600
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1601
|
+
className: community_module_css_default.cardLink,
|
|
1602
|
+
href: plugin.repo,
|
|
1603
|
+
target: "_blank",
|
|
1604
|
+
rel: "noreferrer",
|
|
1605
|
+
children: t("repository")
|
|
1606
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1607
|
+
className: community_module_css_default.cardActions,
|
|
1608
|
+
children: [
|
|
1609
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1610
|
+
type: "button",
|
|
1611
|
+
className: copied ? `${community_module_css_default.installButton} ${community_module_css_default.installButtonCopied}` : faceLoopback ? `${community_module_css_default.installButton} ${community_module_css_default.installButtonSecondary}` : community_module_css_default.installButton,
|
|
1612
|
+
title: command,
|
|
1613
|
+
onClick: () => {
|
|
1614
|
+
copyCommand(plugin.id, command);
|
|
1615
|
+
},
|
|
1616
|
+
children: copied ? t("copied") : t("install")
|
|
1617
|
+
}),
|
|
1618
|
+
faceLoopback && installedItem === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1619
|
+
type: "button",
|
|
1620
|
+
className: community_module_css_default.installButton,
|
|
1621
|
+
disabled: mutationsDisabled,
|
|
1622
|
+
onClick: () => {
|
|
1623
|
+
onInstall(plugin);
|
|
1624
|
+
},
|
|
1625
|
+
children: isInstalling ? t("installing") : t("installNow")
|
|
1626
|
+
}) : null,
|
|
1627
|
+
faceLoopback && installedItem !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1628
|
+
type: "button",
|
|
1629
|
+
className: `${community_module_css_default.installButton} ${community_module_css_default.installButtonSecondary}`,
|
|
1630
|
+
disabled: mutationsDisabled,
|
|
1631
|
+
onClick: () => {
|
|
1632
|
+
setUninstallTarget(plugin);
|
|
1633
|
+
},
|
|
1634
|
+
children: isUninstalling ? t("uninstalling") : t("uninstall")
|
|
1635
|
+
}) : null
|
|
1636
|
+
]
|
|
1637
|
+
})]
|
|
1638
|
+
}),
|
|
1639
|
+
isInstalling ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1640
|
+
className: community_module_css_default.progress,
|
|
1641
|
+
role: "status",
|
|
1642
|
+
children: progress !== null && progress.kind !== "idle" ? t(`progress.${progress.stage}`) : t("installing")
|
|
1643
|
+
}) : null,
|
|
1644
|
+
error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1645
|
+
className: community_module_css_default.error,
|
|
1646
|
+
role: "alert",
|
|
1647
|
+
children: error
|
|
1648
|
+
}) : null,
|
|
1649
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
1650
|
+
className: community_module_css_default.cardCommand,
|
|
1071
1651
|
title: command,
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
children: copied ? t("copied") : t("install")
|
|
1076
|
-
})]
|
|
1077
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
1078
|
-
className: community_module_css_default.cardCommand,
|
|
1079
|
-
title: command,
|
|
1080
|
-
children: command
|
|
1081
|
-
})]
|
|
1652
|
+
children: command
|
|
1653
|
+
})
|
|
1654
|
+
]
|
|
1082
1655
|
})
|
|
1083
1656
|
]
|
|
1084
1657
|
}, plugin.id);
|
|
@@ -1090,6 +1663,15 @@ window.__ModuleLoader__.load({
|
|
|
1090
1663
|
role: "status",
|
|
1091
1664
|
children: t("off")
|
|
1092
1665
|
}),
|
|
1666
|
+
face === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1667
|
+
className: community_module_css_default.installNote,
|
|
1668
|
+
role: "note",
|
|
1669
|
+
children: t("managerHint")
|
|
1670
|
+
}) : face.isLoopback ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1671
|
+
className: community_module_css_default.installNote,
|
|
1672
|
+
role: "note",
|
|
1673
|
+
children: t("remoteHint")
|
|
1674
|
+
}),
|
|
1093
1675
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1094
1676
|
className: community_module_css_default.installNote,
|
|
1095
1677
|
role: "note",
|
|
@@ -1099,13 +1681,41 @@ window.__ModuleLoader__.load({
|
|
|
1099
1681
|
className: community_module_css_default.notice,
|
|
1100
1682
|
role: "note",
|
|
1101
1683
|
children: t("notice")
|
|
1684
|
+
}),
|
|
1685
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1686
|
+
open: uninstallTarget !== null,
|
|
1687
|
+
onClose: () => {
|
|
1688
|
+
if (pending === null) setUninstallTarget(null);
|
|
1689
|
+
},
|
|
1690
|
+
title: t("uninstallConfirm.title"),
|
|
1691
|
+
closeLabel: t("cancel"),
|
|
1692
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1693
|
+
className: community_module_css_default.confirmBody,
|
|
1694
|
+
children: t("uninstallConfirm.body", { name: uninstallTarget === null ? "" : entryCopy(t, `name.${uninstallTarget.id}`, uninstallTarget.nameEn || uninstallTarget.name) })
|
|
1695
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1696
|
+
className: community_module_css_default.modalActions,
|
|
1697
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1698
|
+
variant: "outline",
|
|
1699
|
+
disabled: pending !== null,
|
|
1700
|
+
onClick: () => {
|
|
1701
|
+
setUninstallTarget(null);
|
|
1702
|
+
},
|
|
1703
|
+
children: t("cancel")
|
|
1704
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1705
|
+
variant: "primary",
|
|
1706
|
+
className: community_module_css_default.dangerButton,
|
|
1707
|
+
disabled: pending !== null,
|
|
1708
|
+
onClick: onUninstallConfirm,
|
|
1709
|
+
children: pending?.kind === "uninstall" ? t("uninstalling") : t("uninstallConfirm.confirm")
|
|
1710
|
+
})]
|
|
1711
|
+
})]
|
|
1102
1712
|
})
|
|
1103
1713
|
]
|
|
1104
1714
|
});
|
|
1105
1715
|
}
|
|
1106
1716
|
/** Render the community plugin index as a first-level settings page. */
|
|
1107
1717
|
function CommunityPluginsSection(props) {
|
|
1108
|
-
const { t, useCommunityPluginsCard, save, discard, edit, resetField, plugins } = props;
|
|
1718
|
+
const { t, useCommunityPluginsCard, save, discard, edit, resetField, plugins, pluginManager } = props;
|
|
1109
1719
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1110
1720
|
className: community_module_css_default.sectionList,
|
|
1111
1721
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommunityPluginsCard, {
|
|
@@ -1115,7 +1725,8 @@ window.__ModuleLoader__.load({
|
|
|
1115
1725
|
discard,
|
|
1116
1726
|
edit,
|
|
1117
1727
|
resetField,
|
|
1118
|
-
plugins
|
|
1728
|
+
plugins,
|
|
1729
|
+
pluginManager
|
|
1119
1730
|
})
|
|
1120
1731
|
});
|
|
1121
1732
|
}
|
|
@@ -1154,6 +1765,23 @@ window.__ModuleLoader__.load({
|
|
|
1154
1765
|
"copy": "复制",
|
|
1155
1766
|
"copied": "已复制",
|
|
1156
1767
|
"install": "复制安装命令",
|
|
1768
|
+
"installNow": "安装",
|
|
1769
|
+
"installing": "安装中…",
|
|
1770
|
+
"uninstall": "卸载",
|
|
1771
|
+
"uninstalling": "卸载中…",
|
|
1772
|
+
"cancel": "取消",
|
|
1773
|
+
"badge.installed": "已安装 · 重启后生效",
|
|
1774
|
+
"progress.fetch": "获取插件信息",
|
|
1775
|
+
"progress.download": "下载中",
|
|
1776
|
+
"progress.extract": "解压中",
|
|
1777
|
+
"progress.write": "写入中",
|
|
1778
|
+
"uninstallConfirm.title": "卸载插件",
|
|
1779
|
+
"uninstallConfirm.body": "确定要卸载 {name} 吗?移除将在重启 dsh web 后完全生效。",
|
|
1780
|
+
"uninstallConfirm.confirm": "确认卸载",
|
|
1781
|
+
"installFailed": "安装失败:{reason}",
|
|
1782
|
+
"uninstallFailed": "卸载失败:{reason}",
|
|
1783
|
+
"managerHint": "未检测到「插件管理器」插件:安装 @linxin666/dsh-client-ui-plugin-manager 后,可在本机浏览器中直接从这里安装与卸载。",
|
|
1784
|
+
"remoteHint": "当前是远程浏览器,界面内安装不可用;请在宿主机器的终端执行安装命令。",
|
|
1157
1785
|
"noMatch": "没有匹配的社区插件。",
|
|
1158
1786
|
"search.placeholder": "搜索名称、作者或简介",
|
|
1159
1787
|
"search.label": "搜索社区插件",
|
|
@@ -1168,7 +1796,7 @@ window.__ModuleLoader__.load({
|
|
|
1168
1796
|
"category.integration": "集成与分享",
|
|
1169
1797
|
"category.security": "安全与运维",
|
|
1170
1798
|
"category.utility": "实用工具",
|
|
1171
|
-
"installHint": "
|
|
1799
|
+
"installHint": "在本机浏览器且已安装「插件管理器」插件时,可直接点击卡片上的「安装 / 卸载」;否则复制安装命令到终端执行。安装与卸载在重启 dsh web 后生效,插件自带的开关与配置(若有)会出现在插件配置里。",
|
|
1172
1800
|
"empty": "暂无社区插件登记。",
|
|
1173
1801
|
"off": "社区插件索引已关闭。",
|
|
1174
1802
|
"notice": "条目由贡献者自行登记,与 dsh-web-ui 的发布内容无关;使用前请自行评估。"
|
|
@@ -1199,6 +1827,23 @@ window.__ModuleLoader__.load({
|
|
|
1199
1827
|
"copy": "Copy",
|
|
1200
1828
|
"copied": "Copied",
|
|
1201
1829
|
"install": "Copy install command",
|
|
1830
|
+
"installNow": "Install",
|
|
1831
|
+
"installing": "Installing…",
|
|
1832
|
+
"uninstall": "Uninstall",
|
|
1833
|
+
"uninstalling": "Uninstalling…",
|
|
1834
|
+
"cancel": "Cancel",
|
|
1835
|
+
"badge.installed": "Installed · restart to take effect",
|
|
1836
|
+
"progress.fetch": "Fetching plugin info",
|
|
1837
|
+
"progress.download": "Downloading",
|
|
1838
|
+
"progress.extract": "Extracting",
|
|
1839
|
+
"progress.write": "Writing",
|
|
1840
|
+
"uninstallConfirm.title": "Uninstall plugin",
|
|
1841
|
+
"uninstallConfirm.body": "Uninstall {name}? The removal fully takes effect after restarting dsh web.",
|
|
1842
|
+
"uninstallConfirm.confirm": "Confirm uninstall",
|
|
1843
|
+
"installFailed": "Install failed: {reason}",
|
|
1844
|
+
"uninstallFailed": "Uninstall failed: {reason}",
|
|
1845
|
+
"managerHint": "The Plugin manager plugin is not detected: install @linxin666/dsh-client-ui-plugin-manager to install and uninstall entries directly from here in a local browser.",
|
|
1846
|
+
"remoteHint": "This is a remote browser, so in-GUI install is unavailable; run the install command in a terminal on the host machine.",
|
|
1202
1847
|
"noMatch": "No matching community plugin found.",
|
|
1203
1848
|
"search.placeholder": "Search name, author or description",
|
|
1204
1849
|
"search.label": "Search community plugins",
|
|
@@ -1213,7 +1858,7 @@ window.__ModuleLoader__.load({
|
|
|
1213
1858
|
"category.integration": "Integration & Sharing",
|
|
1214
1859
|
"category.security": "Security & Ops",
|
|
1215
1860
|
"category.utility": "Utilities",
|
|
1216
|
-
"installHint": "
|
|
1861
|
+
"installHint": "With the Plugin manager plugin present and a local browser, use the Install / Uninstall buttons on each card; otherwise copy the install command into a terminal. Installs and uninstalls take effect after restarting dsh web; once installed, the plugin provides its own switch and config (if any) in the plugin configuration section.",
|
|
1217
1862
|
"empty": "No community plugins registered yet.",
|
|
1218
1863
|
"off": "The community plugin index is turned off.",
|
|
1219
1864
|
"notice": "Entries are contributed by their authors and are separate from dsh-web-ui releases; evaluate before use."
|
|
@@ -1253,6 +1898,7 @@ window.__ModuleLoader__.load({
|
|
|
1253
1898
|
zh,
|
|
1254
1899
|
en
|
|
1255
1900
|
}), "community-plugins: dictionaries");
|
|
1901
|
+
bridgePluginManager(ctx);
|
|
1256
1902
|
const controller = new CommunityPluginsCardController((ctx.get("webUiSettings") ?? ctx.settingsScope).bind({ namespace: COMMUNITY_PLUGINS_NS }));
|
|
1257
1903
|
ctx.slots.inject("settings.section", () => {
|
|
1258
1904
|
const unregister = ctx.slots.register({
|