@linxin666/dsh-client-ui-community-plugins 0.2.1 → 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 +7 -3
- package/README.zh.md +7 -3
- package/lib/client.js +371 -51
- package/lib/client.js.map +1 -1
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -5,6 +5,7 @@ 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
|
|
@@ -726,7 +727,6 @@ window.__ModuleLoader__.load({
|
|
|
726
727
|
"repo": "https://github.com/v833/dsh-chat-summary",
|
|
727
728
|
"description": "总结当前对话并导出为 Markdown / DOCX / PDF,可选 LLM 智能总结(用户自配 API Key)。",
|
|
728
729
|
"descriptionEn": "Summarize the current conversation and export it as Markdown / DOCX / PDF, with optional LLM summarization using your own API key.",
|
|
729
|
-
"npm": "@linxin666/dsh-client-ui-chat-summary",
|
|
730
730
|
"category": "knowledge"
|
|
731
731
|
},
|
|
732
732
|
{
|
|
@@ -782,16 +782,6 @@ window.__ModuleLoader__.load({
|
|
|
782
782
|
"npm": "dsh-mnemon",
|
|
783
783
|
"category": "knowledge"
|
|
784
784
|
},
|
|
785
|
-
{
|
|
786
|
-
"id": "dsh-plugin-hub",
|
|
787
|
-
"name": "插件中心(dsh-plugin-hub)",
|
|
788
|
-
"nameEn": "Plugin Hub",
|
|
789
|
-
"author": "Noob-stupid",
|
|
790
|
-
"repo": "https://github.com/Noob-stupid/dsh-plugin-hub",
|
|
791
|
-
"description": "插件管理面板:已安装插件一键启用/停用,内置 GitHub dsh-plugin 插件市场(官方/聚合识别、子包浏览、一键安装与本地 AI 兜底修复、删除卸载)。",
|
|
792
|
-
"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.",
|
|
793
|
-
"category": "tools"
|
|
794
|
-
},
|
|
795
785
|
{
|
|
796
786
|
"id": "dsh-genui",
|
|
797
787
|
"name": "GenUI 生成式 UI",
|
|
@@ -878,16 +868,6 @@ window.__ModuleLoader__.load({
|
|
|
878
868
|
"npm": "@linxin666/dsh-client-ui-skill-explorer",
|
|
879
869
|
"category": "tools"
|
|
880
870
|
},
|
|
881
|
-
{
|
|
882
|
-
"id": "dsh-dream-skin",
|
|
883
|
-
"name": "dsh-dream-skin",
|
|
884
|
-
"nameEn": "dsh-dream-skin",
|
|
885
|
-
"author": "RevolutionLA",
|
|
886
|
-
"repo": "https://github.com/RevolutionLA/dsh-dream-skin",
|
|
887
|
-
"description": "DSH Web 一键换肤插件:8 套原创主题、背景壁纸(透明度/模糊/渐变/URL)、每用户强调色、主题包导入导出与分享链接、收藏与随机,纯原生 token 系统。",
|
|
888
|
-
"descriptionEn": "One-command skinning for DSH Web: 8 original themes, wallpaper (opacity/blur/gradient/URL), per-user accent, shareable theme packs with favorites and surprise-me. Purely native token system.",
|
|
889
|
-
"category": "ui"
|
|
890
|
-
},
|
|
891
871
|
{
|
|
892
872
|
"id": "dsh-friendly-steps",
|
|
893
873
|
"name": "过程精简 Friendly Steps",
|
|
@@ -1034,6 +1014,17 @@ window.__ModuleLoader__.load({
|
|
|
1034
1014
|
"repo": "https://github.com/DamonKoy/dsh-plugins/tree/main/packages/dsh-usage-cost",
|
|
1035
1015
|
"description": "统计会话与每日 token 用量和估算成本,超预算提醒。",
|
|
1036
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"
|
|
1037
1028
|
}
|
|
1038
1029
|
];
|
|
1039
1030
|
//#endregion
|
|
@@ -1068,8 +1059,106 @@ window.__ModuleLoader__.load({
|
|
|
1068
1059
|
return true;
|
|
1069
1060
|
}
|
|
1070
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
|
|
1071
1160
|
//#region \0dsh-css:packages/dsh-community-plugins/src/client/community.module.css.mjs
|
|
1072
|
-
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}";
|
|
1073
1162
|
const tagId = "@linxin666/dsh-client-ui-community-plugins/community.module.css";
|
|
1074
1163
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1075
1164
|
const tag = document.createElement("style");
|
|
@@ -1080,9 +1169,12 @@ window.__ModuleLoader__.load({
|
|
|
1080
1169
|
}
|
|
1081
1170
|
var community_module_css_default = {
|
|
1082
1171
|
"badge": "z0J_8a_badge",
|
|
1172
|
+
"badgeInstalled": "z0J_8a_badgeInstalled",
|
|
1083
1173
|
"badgePublished": "z0J_8a_badgePublished",
|
|
1084
1174
|
"card": "z0J_8a_card",
|
|
1175
|
+
"cardActions": "z0J_8a_cardActions",
|
|
1085
1176
|
"cardAuthor": "z0J_8a_cardAuthor",
|
|
1177
|
+
"cardBadges": "z0J_8a_cardBadges",
|
|
1086
1178
|
"cardCategory": "z0J_8a_cardCategory",
|
|
1087
1179
|
"cardCommand": "z0J_8a_cardCommand",
|
|
1088
1180
|
"cardDescription": "z0J_8a_cardDescription",
|
|
@@ -1093,17 +1185,23 @@ window.__ModuleLoader__.load({
|
|
|
1093
1185
|
"cardMeta": "z0J_8a_cardMeta",
|
|
1094
1186
|
"cardName": "z0J_8a_cardName",
|
|
1095
1187
|
"cardTop": "z0J_8a_cardTop",
|
|
1188
|
+
"confirmBody": "z0J_8a_confirmBody",
|
|
1189
|
+
"dangerButton": "z0J_8a_dangerButton",
|
|
1096
1190
|
"empty": "z0J_8a_empty",
|
|
1191
|
+
"error": "z0J_8a_error",
|
|
1097
1192
|
"filters": "z0J_8a_filters",
|
|
1098
1193
|
"grid": "z0J_8a_grid",
|
|
1099
1194
|
"installButton": "z0J_8a_installButton",
|
|
1100
1195
|
"installButtonCopied": "z0J_8a_installButtonCopied",
|
|
1196
|
+
"installButtonSecondary": "z0J_8a_installButtonSecondary",
|
|
1101
1197
|
"installNote": "z0J_8a_installNote",
|
|
1102
1198
|
"market": "z0J_8a_market",
|
|
1199
|
+
"modalActions": "z0J_8a_modalActions",
|
|
1103
1200
|
"notice": "z0J_8a_notice",
|
|
1104
1201
|
"off": "z0J_8a_off",
|
|
1105
1202
|
"pill": "z0J_8a_pill",
|
|
1106
1203
|
"pillActive": "z0J_8a_pillActive",
|
|
1204
|
+
"progress": "z0J_8a_progress",
|
|
1107
1205
|
"resultCount": "z0J_8a_resultCount",
|
|
1108
1206
|
"search": "z0J_8a_search",
|
|
1109
1207
|
"sectionList": "z0J_8a_sectionList",
|
|
@@ -1156,6 +1254,12 @@ window.__ModuleLoader__.load({
|
|
|
1156
1254
|
function installCommand(entry) {
|
|
1157
1255
|
return `dsh plugin --profile web add ${entry.npm ?? entry.repo}`;
|
|
1158
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
|
+
}
|
|
1159
1263
|
/**
|
|
1160
1264
|
* Localized entry copy read through the card's `t`. Falls back to the raw
|
|
1161
1265
|
* entry field when the key is missing — the dictionary only carries the
|
|
@@ -1188,6 +1292,102 @@ window.__ModuleLoader__.load({
|
|
|
1188
1292
|
const [query, setQuery] = (0, react.useState)("");
|
|
1189
1293
|
const [category, setCategory] = (0, react.useState)(null);
|
|
1190
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
|
+
};
|
|
1191
1391
|
const copyCommand = (id, command) => {
|
|
1192
1392
|
const mark = () => {
|
|
1193
1393
|
setCopiedId(id);
|
|
@@ -1211,6 +1411,7 @@ window.__ModuleLoader__.load({
|
|
|
1211
1411
|
mark();
|
|
1212
1412
|
};
|
|
1213
1413
|
const disabled = !state.writable;
|
|
1414
|
+
const mutationsDisabled = disabled || pending !== null;
|
|
1214
1415
|
const visible = state.enabled.text !== "false";
|
|
1215
1416
|
const fieldProps = {
|
|
1216
1417
|
overriddenLabel: t("settings.overridden"),
|
|
@@ -1343,6 +1544,10 @@ window.__ModuleLoader__.load({
|
|
|
1343
1544
|
const copied = copiedId === plugin.id;
|
|
1344
1545
|
const name = entryCopy(t, `name.${plugin.id}`, plugin.nameEn || plugin.name);
|
|
1345
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];
|
|
1346
1551
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
1347
1552
|
className: community_module_css_default.card,
|
|
1348
1553
|
children: [
|
|
@@ -1352,10 +1557,16 @@ window.__ModuleLoader__.load({
|
|
|
1352
1557
|
className: community_module_css_default.cardName,
|
|
1353
1558
|
title: name,
|
|
1354
1559
|
children: name
|
|
1355
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1356
|
-
className:
|
|
1357
|
-
|
|
1358
|
-
|
|
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
|
+
})]
|
|
1359
1570
|
})]
|
|
1360
1571
|
}),
|
|
1361
1572
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -1383,28 +1594,64 @@ window.__ModuleLoader__.load({
|
|
|
1383
1594
|
}) : null,
|
|
1384
1595
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1385
1596
|
className: community_module_css_default.cardFooter,
|
|
1386
|
-
children: [
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
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,
|
|
1397
1651
|
title: command,
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
children: copied ? t("copied") : t("install")
|
|
1402
|
-
})]
|
|
1403
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
1404
|
-
className: community_module_css_default.cardCommand,
|
|
1405
|
-
title: command,
|
|
1406
|
-
children: command
|
|
1407
|
-
})]
|
|
1652
|
+
children: command
|
|
1653
|
+
})
|
|
1654
|
+
]
|
|
1408
1655
|
})
|
|
1409
1656
|
]
|
|
1410
1657
|
}, plugin.id);
|
|
@@ -1416,6 +1663,15 @@ window.__ModuleLoader__.load({
|
|
|
1416
1663
|
role: "status",
|
|
1417
1664
|
children: t("off")
|
|
1418
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
|
+
}),
|
|
1419
1675
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1420
1676
|
className: community_module_css_default.installNote,
|
|
1421
1677
|
role: "note",
|
|
@@ -1425,13 +1681,41 @@ window.__ModuleLoader__.load({
|
|
|
1425
1681
|
className: community_module_css_default.notice,
|
|
1426
1682
|
role: "note",
|
|
1427
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
|
+
})]
|
|
1428
1712
|
})
|
|
1429
1713
|
]
|
|
1430
1714
|
});
|
|
1431
1715
|
}
|
|
1432
1716
|
/** Render the community plugin index as a first-level settings page. */
|
|
1433
1717
|
function CommunityPluginsSection(props) {
|
|
1434
|
-
const { t, useCommunityPluginsCard, save, discard, edit, resetField, plugins } = props;
|
|
1718
|
+
const { t, useCommunityPluginsCard, save, discard, edit, resetField, plugins, pluginManager } = props;
|
|
1435
1719
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1436
1720
|
className: community_module_css_default.sectionList,
|
|
1437
1721
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommunityPluginsCard, {
|
|
@@ -1441,7 +1725,8 @@ window.__ModuleLoader__.load({
|
|
|
1441
1725
|
discard,
|
|
1442
1726
|
edit,
|
|
1443
1727
|
resetField,
|
|
1444
|
-
plugins
|
|
1728
|
+
plugins,
|
|
1729
|
+
pluginManager
|
|
1445
1730
|
})
|
|
1446
1731
|
});
|
|
1447
1732
|
}
|
|
@@ -1480,6 +1765,23 @@ window.__ModuleLoader__.load({
|
|
|
1480
1765
|
"copy": "复制",
|
|
1481
1766
|
"copied": "已复制",
|
|
1482
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": "当前是远程浏览器,界面内安装不可用;请在宿主机器的终端执行安装命令。",
|
|
1483
1785
|
"noMatch": "没有匹配的社区插件。",
|
|
1484
1786
|
"search.placeholder": "搜索名称、作者或简介",
|
|
1485
1787
|
"search.label": "搜索社区插件",
|
|
@@ -1494,7 +1796,7 @@ window.__ModuleLoader__.load({
|
|
|
1494
1796
|
"category.integration": "集成与分享",
|
|
1495
1797
|
"category.security": "安全与运维",
|
|
1496
1798
|
"category.utility": "实用工具",
|
|
1497
|
-
"installHint": "
|
|
1799
|
+
"installHint": "在本机浏览器且已安装「插件管理器」插件时,可直接点击卡片上的「安装 / 卸载」;否则复制安装命令到终端执行。安装与卸载在重启 dsh web 后生效,插件自带的开关与配置(若有)会出现在插件配置里。",
|
|
1498
1800
|
"empty": "暂无社区插件登记。",
|
|
1499
1801
|
"off": "社区插件索引已关闭。",
|
|
1500
1802
|
"notice": "条目由贡献者自行登记,与 dsh-web-ui 的发布内容无关;使用前请自行评估。"
|
|
@@ -1525,6 +1827,23 @@ window.__ModuleLoader__.load({
|
|
|
1525
1827
|
"copy": "Copy",
|
|
1526
1828
|
"copied": "Copied",
|
|
1527
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.",
|
|
1528
1847
|
"noMatch": "No matching community plugin found.",
|
|
1529
1848
|
"search.placeholder": "Search name, author or description",
|
|
1530
1849
|
"search.label": "Search community plugins",
|
|
@@ -1539,7 +1858,7 @@ window.__ModuleLoader__.load({
|
|
|
1539
1858
|
"category.integration": "Integration & Sharing",
|
|
1540
1859
|
"category.security": "Security & Ops",
|
|
1541
1860
|
"category.utility": "Utilities",
|
|
1542
|
-
"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.",
|
|
1543
1862
|
"empty": "No community plugins registered yet.",
|
|
1544
1863
|
"off": "The community plugin index is turned off.",
|
|
1545
1864
|
"notice": "Entries are contributed by their authors and are separate from dsh-web-ui releases; evaluate before use."
|
|
@@ -1579,6 +1898,7 @@ window.__ModuleLoader__.load({
|
|
|
1579
1898
|
zh,
|
|
1580
1899
|
en
|
|
1581
1900
|
}), "community-plugins: dictionaries");
|
|
1901
|
+
bridgePluginManager(ctx);
|
|
1582
1902
|
const controller = new CommunityPluginsCardController((ctx.get("webUiSettings") ?? ctx.settingsScope).bind({ namespace: COMMUNITY_PLUGINS_NS }));
|
|
1583
1903
|
ctx.slots.inject("settings.section", () => {
|
|
1584
1904
|
const unregister = ctx.slots.register({
|