dsh-plugin 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/client/client.js +547 -178
- package/client/client.js.map +1 -1
- package/lib/services/loader.js +12 -5
- package/lib/services/task-queue.js +8 -3
- package/lib/types/services/install-types.d.ts +2 -0
- package/lib/types/services/loader.d.ts +6 -4
- package/package.json +1 -1
- package/src/client/components/AboutModal.tsx +72 -0
- package/src/client/components/CatalogHeader.tsx +30 -5
- package/src/client/components/HubUpdateModal.tsx +84 -0
- package/src/client/components/NotificationsModal.tsx +21 -8
- package/src/client/components/PluginCard.tsx +3 -0
- package/src/client/components/PluginHubSection.tsx +62 -6
- package/src/client/components/modals.tsx +31 -10
- package/src/client/hooks/useCatalog.ts +82 -14
- package/src/client/hooks/useTaskQueue.ts +39 -26
- package/src/client/lib/catalog.ts +32 -0
- package/src/client/lib/renderMarkdown.ts +116 -0
- package/src/client/locales.ts +41 -0
- package/src/client/styles/Header.module.css +83 -3
- package/src/client/styles/Modal.module.css +274 -0
- package/src/client/types.ts +2 -0
- package/src/server/services/install-types.ts +2 -0
- package/src/server/services/loader.ts +12 -5
- package/src/server/services/task-queue.ts +8 -3
|
@@ -20,12 +20,13 @@
|
|
|
20
20
|
padding: 2px 2px 0;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/* 头部右上角控件组:语言切换 + GitHub
|
|
23
|
+
/* 头部右上角控件组:语言切换 + GitHub 源码图标,右侧一列对齐,auto 把它顶到标题行最右 */
|
|
24
24
|
.headerRight {
|
|
25
25
|
flex-shrink: 0;
|
|
26
26
|
display: inline-flex;
|
|
27
27
|
align-items: center;
|
|
28
28
|
gap: 2px;
|
|
29
|
+
margin-left: auto;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
/* 语言切换按钮:与 GitHub 图标同风格(次级灰低调、悬浮品牌紫),文字显示目标语言 */
|
|
@@ -75,11 +76,36 @@
|
|
|
75
76
|
display: block;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
/*
|
|
79
|
+
/* 「关注我们」入口:GitHub 图标后,与语言切换按钮同风格 —— 普通文字、无边框,
|
|
80
|
+
悬浮才转品牌紫 + 浅紫底,提示可点(打开平台介绍 + 用户反馈群二维码弹窗) */
|
|
81
|
+
.aboutBtn {
|
|
82
|
+
flex-shrink: 0;
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
justify-content: center;
|
|
86
|
+
height: 24px;
|
|
87
|
+
padding: 0 8px;
|
|
88
|
+
font-size: 12px;
|
|
89
|
+
font-weight: 600;
|
|
90
|
+
line-height: 1;
|
|
91
|
+
color: var(--hub-text-tertiary);
|
|
92
|
+
background: transparent;
|
|
93
|
+
border: none;
|
|
94
|
+
border-radius: 6px;
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
user-select: none;
|
|
97
|
+
transition: color 0.15s ease, background-color 0.15s ease;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.aboutBtn:hover {
|
|
101
|
+
color: var(--hub-purple-1);
|
|
102
|
+
background-color: var(--hub-purple-tint);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* 头部第一行:logo + 标题(官网链接)紧接着版本号入口,右侧控件组靠 auto 顶到最右 */
|
|
79
106
|
.headerTitleRow {
|
|
80
107
|
display: flex;
|
|
81
108
|
align-items: center;
|
|
82
|
-
justify-content: space-between;
|
|
83
109
|
gap: 8px;
|
|
84
110
|
min-width: 0;
|
|
85
111
|
}
|
|
@@ -117,6 +143,60 @@
|
|
|
117
143
|
color: var(--hub-text-secondary);
|
|
118
144
|
}
|
|
119
145
|
|
|
146
|
+
/* 版本号入口按钮:常驻可点(打开版本信息/更新记录弹窗),无边框、字号与标题协调,
|
|
147
|
+
hover 加品牌色下划线提示可点 —— 无论有无更新都可查看更新内容;
|
|
148
|
+
有更新时版本号后紧跟红色「可更新」徽标(同一按钮内的 span,不分开成两个元素) */
|
|
149
|
+
.versionBtn {
|
|
150
|
+
flex-shrink: 0;
|
|
151
|
+
display: inline-flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
gap: 5px;
|
|
154
|
+
height: 20px;
|
|
155
|
+
padding: 0 6px;
|
|
156
|
+
border: none;
|
|
157
|
+
border-radius: 5px;
|
|
158
|
+
font-size: 12px;
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
line-height: 1;
|
|
161
|
+
color: var(--hub-text-secondary);
|
|
162
|
+
background: transparent;
|
|
163
|
+
cursor: pointer;
|
|
164
|
+
user-select: none;
|
|
165
|
+
transition: color 120ms ease;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* 悬浮只提示整组可点,但版本号本身颜色保持不变;交互反馈交给「可更新」徽标单独变深红 */
|
|
169
|
+
.versionBtn:hover {
|
|
170
|
+
color: var(--hub-text-secondary);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* 版本号旁的「可更新」徽标:默认红底白字(醒目),悬浮轻微加深 + 投影提升一点反馈感,
|
|
174
|
+
紧跟版本号,提示可查看并直接升级。作为同一按钮内文字,点击由外层按钮统一处理,本身无需独立事件。 */
|
|
175
|
+
.hubUpdateBadge {
|
|
176
|
+
flex-shrink: 0;
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
height: 18px;
|
|
180
|
+
padding: 0 7px;
|
|
181
|
+
border-radius: 999px;
|
|
182
|
+
font-size: 11px;
|
|
183
|
+
font-weight: 700;
|
|
184
|
+
line-height: 1;
|
|
185
|
+
letter-spacing: 0.02em;
|
|
186
|
+
color: #ffffff;
|
|
187
|
+
background: var(--hub-danger);
|
|
188
|
+
box-shadow: 0 1px 4px rgba(209, 36, 47, 0.4);
|
|
189
|
+
user-select: none;
|
|
190
|
+
transition: background 120ms ease, box-shadow 120ms ease, transform 120ms ease;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* 悬浮:轻微变深红 + 一点投影提升,只动徽标本身,版本号不动 */
|
|
194
|
+
.hubUpdateBadge:hover {
|
|
195
|
+
background: var(--hub-danger-hover);
|
|
196
|
+
box-shadow: 0 2px 8px rgba(209, 36, 47, 0.5);
|
|
197
|
+
transform: translateY(-1px);
|
|
198
|
+
}
|
|
199
|
+
|
|
120
200
|
/* 品牌 logo 图标:与标题文字垂直居中对齐 */
|
|
121
201
|
.logoIcon {
|
|
122
202
|
flex-shrink: 0;
|
|
@@ -113,6 +113,28 @@
|
|
|
113
113
|
padding: 6px 10px;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/* 可能需要命令行辅助的插件提示(webInstallable=false):中性说明,仍允许尝试一键安装 */
|
|
117
|
+
.cliOnlyHint {
|
|
118
|
+
font-size: 12px;
|
|
119
|
+
line-height: 18px;
|
|
120
|
+
color: var(--hub-text-secondary);
|
|
121
|
+
background: var(--hub-bg-hover);
|
|
122
|
+
border-radius: 6px;
|
|
123
|
+
padding: 6px 10px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* 安装失败引导:红色醒目,提示复制命令到 dsh 终端手动执行(原生依赖构建脚本需人工放行) */
|
|
127
|
+
.failedCopyHint {
|
|
128
|
+
font-size: 12px;
|
|
129
|
+
line-height: 18px;
|
|
130
|
+
font-weight: 500;
|
|
131
|
+
color: var(--hub-danger-text);
|
|
132
|
+
background: var(--hub-danger-tint);
|
|
133
|
+
border: 1px solid var(--hub-danger-border-soft);
|
|
134
|
+
border-radius: 6px;
|
|
135
|
+
padding: 6px 10px;
|
|
136
|
+
}
|
|
137
|
+
|
|
116
138
|
.modalRow {
|
|
117
139
|
display: flex;
|
|
118
140
|
align-items: baseline;
|
|
@@ -916,6 +938,17 @@
|
|
|
916
938
|
background: var(--hub-danger-tint);
|
|
917
939
|
}
|
|
918
940
|
|
|
941
|
+
/* 成功通知卡片(时间戳已并入头部行):删除按钮不再用 auto margin 顶到最右,
|
|
942
|
+
由时间戳的 margin-left:auto 把「时间 + 删除」整体推到行尾 —— 图标文字与
|
|
943
|
+
时间垂直居中,卡片只有一行内容,整体居中不顶头 */
|
|
944
|
+
.noticeRowOk .noticeRemove {
|
|
945
|
+
margin-left: 0;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
.noticeRowOk .noticeTime {
|
|
949
|
+
margin-left: auto;
|
|
950
|
+
}
|
|
951
|
+
|
|
919
952
|
/* 记录内的复制按钮:纯灰文字、无边框,低调隐蔽,点击即可复制该条完整错误日志 */
|
|
920
953
|
.failCopy {
|
|
921
954
|
flex-shrink: 0;
|
|
@@ -1004,3 +1037,244 @@
|
|
|
1004
1037
|
border: 1px solid var(--hub-warning-border);
|
|
1005
1038
|
border-radius: 6px;
|
|
1006
1039
|
}
|
|
1040
|
+
|
|
1041
|
+
/* —— 「关注我们」弹窗 —— */
|
|
1042
|
+
|
|
1043
|
+
/* 关注弹窗:比普通弹窗略宽,给 Markdown 介绍 + 反馈群二维码留出阅读空间;
|
|
1044
|
+
max-width 覆盖普通弹窗的 calc(100vw - 32px),窄屏时仍不贴边 */
|
|
1045
|
+
.aboutModal {
|
|
1046
|
+
width: 460px;
|
|
1047
|
+
max-width: calc(100vw - 48px);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/* Markdown 内容区:固定最大高度内滚动,介绍文字 + 二维码不把弹窗撑爆;
|
|
1051
|
+
样式沿用 hubUpdateNotes 的 12px 基线,仅二维码图片单独约束最大尺寸 */
|
|
1052
|
+
.aboutContent {
|
|
1053
|
+
flex: 1 1 auto;
|
|
1054
|
+
min-height: 0;
|
|
1055
|
+
max-height: 340px;
|
|
1056
|
+
overflow-y: auto;
|
|
1057
|
+
font-size: 12px;
|
|
1058
|
+
line-height: 18px;
|
|
1059
|
+
color: var(--hub-text-secondary);
|
|
1060
|
+
background: var(--hub-bg-2);
|
|
1061
|
+
border: 1px solid var(--hub-border-2);
|
|
1062
|
+
border-radius: 8px;
|
|
1063
|
+
padding: 10px 12px;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
.aboutContent > *:first-child {
|
|
1067
|
+
margin-top: 0;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
.aboutContent > *:last-child {
|
|
1071
|
+
margin-bottom: 0;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
.aboutContent h2,
|
|
1075
|
+
.aboutContent h3,
|
|
1076
|
+
.aboutContent h4,
|
|
1077
|
+
.aboutContent h5 {
|
|
1078
|
+
margin: 10px 0 4px;
|
|
1079
|
+
font-size: 13px;
|
|
1080
|
+
font-weight: 600;
|
|
1081
|
+
line-height: 20px;
|
|
1082
|
+
color: var(--hub-text-primary);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
.aboutContent h2 {
|
|
1086
|
+
font-size: 14px;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
.aboutContent p {
|
|
1090
|
+
margin: 4px 0;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
.aboutContent ul,
|
|
1094
|
+
.aboutContent ol {
|
|
1095
|
+
margin: 4px 0;
|
|
1096
|
+
padding-left: 18px;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
.aboutContent li {
|
|
1100
|
+
margin: 2px 0;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
.aboutContent code {
|
|
1104
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
1105
|
+
font-size: 11px;
|
|
1106
|
+
background: var(--hub-bg-hover);
|
|
1107
|
+
border-radius: 4px;
|
|
1108
|
+
padding: 1px 4px;
|
|
1109
|
+
color: var(--hub-text-primary);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
.aboutContent pre {
|
|
1113
|
+
margin: 6px 0;
|
|
1114
|
+
background: var(--hub-bg-1);
|
|
1115
|
+
border: 1px solid var(--hub-border-2);
|
|
1116
|
+
border-radius: 6px;
|
|
1117
|
+
padding: 8px 10px;
|
|
1118
|
+
overflow-x: auto;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
.aboutContent pre code {
|
|
1122
|
+
background: none;
|
|
1123
|
+
padding: 0;
|
|
1124
|
+
border-radius: 0;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
.aboutContent blockquote {
|
|
1128
|
+
margin: 6px 0;
|
|
1129
|
+
padding: 2px 10px;
|
|
1130
|
+
border-left: 3px solid var(--hub-brand);
|
|
1131
|
+
background: var(--hub-bg-hover);
|
|
1132
|
+
border-radius: 0 6px 6px 0;
|
|
1133
|
+
color: var(--hub-text-tertiary);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
.aboutContent a {
|
|
1137
|
+
color: var(--hub-brand);
|
|
1138
|
+
text-decoration: none;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
.aboutContent a:hover {
|
|
1142
|
+
color: var(--hub-brand-hover);
|
|
1143
|
+
text-decoration: underline;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/* 内容里的图片(反馈群二维码):居中、最大高度受控,不撑破弹窗:
|
|
1147
|
+
当图片自身更窄时收缩到实际宽度(不强行拉满铺平) */
|
|
1148
|
+
.aboutContent img {
|
|
1149
|
+
display: block;
|
|
1150
|
+
margin: 8px auto;
|
|
1151
|
+
max-width: 100%;
|
|
1152
|
+
max-height: 220px;
|
|
1153
|
+
border-radius: 8px;
|
|
1154
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/* 更新时间小字:弱化展示在内容区下方 */
|
|
1158
|
+
.aboutMeta {
|
|
1159
|
+
font-size: 11px;
|
|
1160
|
+
line-height: 16px;
|
|
1161
|
+
color: var(--hub-text-tertiary);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/* —— Hub 自我更新说明弹窗 —— */
|
|
1165
|
+
|
|
1166
|
+
/* 更新说明弹窗:比普通弹窗略宽,给变更记录留出阅读空间 */
|
|
1167
|
+
.hubUpdateModal {
|
|
1168
|
+
width: 520px;
|
|
1169
|
+
max-width: calc(100vw - 48px);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/* 版本 / 发布时间元信息行:小号标签并排,弱化但可读 */
|
|
1173
|
+
.hubUpdateMeta {
|
|
1174
|
+
display: flex;
|
|
1175
|
+
align-items: center;
|
|
1176
|
+
flex-wrap: wrap;
|
|
1177
|
+
gap: 4px 14px;
|
|
1178
|
+
font-size: 12px;
|
|
1179
|
+
line-height: 18px;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
.hubUpdateMetaItem {
|
|
1183
|
+
color: var(--hub-text-tertiary);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/* Markdown 变更记录区:固定最大高度内滚动,长更新记录不把弹窗撑爆;
|
|
1187
|
+
子元素(标题/列表/代码块/引用)继承 12px 基线,仅标题加大加粗 */
|
|
1188
|
+
.hubUpdateNotes {
|
|
1189
|
+
flex: 1 1 auto;
|
|
1190
|
+
min-height: 0;
|
|
1191
|
+
max-height: 300px;
|
|
1192
|
+
overflow-y: auto;
|
|
1193
|
+
font-size: 12px;
|
|
1194
|
+
line-height: 18px;
|
|
1195
|
+
color: var(--hub-text-secondary);
|
|
1196
|
+
background: var(--hub-bg-2);
|
|
1197
|
+
border: 1px solid var(--hub-border-2);
|
|
1198
|
+
border-radius: 8px;
|
|
1199
|
+
padding: 10px 12px;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
.hubUpdateNotes > *:first-child {
|
|
1203
|
+
margin-top: 0;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
.hubUpdateNotes > *:last-child {
|
|
1207
|
+
margin-bottom: 0;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
.hubUpdateNotes h2,
|
|
1211
|
+
.hubUpdateNotes h3,
|
|
1212
|
+
.hubUpdateNotes h4,
|
|
1213
|
+
.hubUpdateNotes h5 {
|
|
1214
|
+
margin: 10px 0 4px;
|
|
1215
|
+
font-size: 13px;
|
|
1216
|
+
font-weight: 600;
|
|
1217
|
+
line-height: 20px;
|
|
1218
|
+
color: var(--hub-text-primary);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
.hubUpdateNotes h2 {
|
|
1222
|
+
font-size: 14px;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.hubUpdateNotes p {
|
|
1226
|
+
margin: 4px 0;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
.hubUpdateNotes ul,
|
|
1230
|
+
.hubUpdateNotes ol {
|
|
1231
|
+
margin: 4px 0;
|
|
1232
|
+
padding-left: 18px;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
.hubUpdateNotes li {
|
|
1236
|
+
margin: 2px 0;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
.hubUpdateNotes code {
|
|
1240
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
1241
|
+
font-size: 11px;
|
|
1242
|
+
background: var(--hub-bg-hover);
|
|
1243
|
+
border-radius: 4px;
|
|
1244
|
+
padding: 1px 4px;
|
|
1245
|
+
color: var(--hub-text-primary);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
.hubUpdateNotes pre {
|
|
1249
|
+
margin: 6px 0;
|
|
1250
|
+
background: var(--hub-bg-1);
|
|
1251
|
+
border: 1px solid var(--hub-border-2);
|
|
1252
|
+
border-radius: 6px;
|
|
1253
|
+
padding: 8px 10px;
|
|
1254
|
+
overflow-x: auto;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
.hubUpdateNotes pre code {
|
|
1258
|
+
background: none;
|
|
1259
|
+
padding: 0;
|
|
1260
|
+
border-radius: 0;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
.hubUpdateNotes blockquote {
|
|
1264
|
+
margin: 6px 0;
|
|
1265
|
+
padding: 2px 10px;
|
|
1266
|
+
border-left: 3px solid var(--hub-brand);
|
|
1267
|
+
background: var(--hub-bg-hover);
|
|
1268
|
+
border-radius: 0 6px 6px 0;
|
|
1269
|
+
color: var(--hub-text-tertiary);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
.hubUpdateNotes a {
|
|
1273
|
+
color: var(--hub-brand);
|
|
1274
|
+
text-decoration: none;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
.hubUpdateNotes a:hover {
|
|
1278
|
+
color: var(--hub-brand-hover);
|
|
1279
|
+
text-decoration: underline;
|
|
1280
|
+
}
|
package/src/client/types.ts
CHANGED
|
@@ -47,6 +47,8 @@ export interface HubPlugin {
|
|
|
47
47
|
features?: string[]
|
|
48
48
|
description?: string
|
|
49
49
|
source?: { repo?: string; npmPackage?: string }
|
|
50
|
+
/** 能否网页一键安装:false = 仅支持命令行/git/本地构建安装(缺省视为 true 可安装) */
|
|
51
|
+
install?: { webInstallable?: boolean }
|
|
50
52
|
compatibility?: { status?: string }
|
|
51
53
|
dates?: { repoUpdatedAt?: string; addedAt?: string }
|
|
52
54
|
stats?: { stargazers_count?: number; forks_count?: number }
|
|
@@ -28,6 +28,8 @@ export interface InstallTask {
|
|
|
28
28
|
progress: number
|
|
29
29
|
/** Newest output lines first (consumer shows the tail). */
|
|
30
30
|
lines: string[]
|
|
31
|
+
/** 完成后是否仍需宿主重启才生效:卸载时 loader 已即时移除 → false;否则 true(弹窗据此给重启选项) */
|
|
32
|
+
needsRestart: boolean
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
/** 子进程启动参数(复用宿主 dsh 入口,或回退到 PATH 上的 `dsh`)。 */
|
|
@@ -60,13 +60,15 @@ function nameMatches(entryName: string | undefined, target: string): boolean {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* 卸载成功后从运行中 loader
|
|
64
|
-
* 加载已卸载的 client bundle,也无需重启)。做法:按 name 扫描
|
|
63
|
+
* 卸载成功后从运行中 loader 停用指定包的条目。做法:按 name 扫描
|
|
65
64
|
* `loader.entries()`(含嵌套子树),对每个匹配条目
|
|
66
65
|
* `entry.update({ disabled: true })` 做 live-disable —— fiber 被 dispose,
|
|
67
66
|
* client-modules 对账后不再把它写进 `__DSH_BOOT__`,页面刷新即恢复。
|
|
68
|
-
* 返回 true =
|
|
69
|
-
*
|
|
67
|
+
* 返回 true = 无需重启(仅当宿主从未加载过它:磁盘已干净);
|
|
68
|
+
* 其余一律 false = 需要重启:要么 disable 失败需「待重启清理」兜底,
|
|
69
|
+
* 要么 disable 成功但该插件曾在 loader 中存活——带 UI 的插件(侧边栏面板等
|
|
70
|
+
* 宿主启动时渲染的槽位)disable 后不会主动摘除,不重启面板一直残留。
|
|
71
|
+
* 宿主关键包(@deepseek-ai/* 与本插件自身)一律跳过。
|
|
70
72
|
*/
|
|
71
73
|
export async function removeLoadedEntry(loader: LoaderHandle, name: string): Promise<boolean> {
|
|
72
74
|
if (name === 'dsh-plugin' || name.startsWith('@deepseek-ai/')) return false
|
|
@@ -95,8 +97,13 @@ export async function removeLoadedEntry(loader: LoaderHandle, name: string): Pro
|
|
|
95
97
|
if (!found) {
|
|
96
98
|
// 运行中 loader 无该条目:宿主从未加载过它(装完未重启),磁盘已干净,无需重启
|
|
97
99
|
console.log(`[hub-uninstall] no live entry for ${name}; nothing to disable`)
|
|
100
|
+
return true
|
|
98
101
|
}
|
|
99
|
-
|
|
102
|
+
// 找到并停用了 live entry:逻辑层已卸载(刷新页面不再加载其 client bundle),
|
|
103
|
+
// 但带 UI 的插件(侧边栏面板等)是宿主启动时渲染的槽位,disable 不会主动摘除,
|
|
104
|
+
// 不重启面板会一直残留 → 返回「需要重启」,弹窗给出「立即重启」,面板立即消失
|
|
105
|
+
console.log(`[hub-uninstall] disabled live entry for ${name}; UI may persist until restart`)
|
|
106
|
+
return false
|
|
100
107
|
})()
|
|
101
108
|
const result = await Promise.race([
|
|
102
109
|
removal.catch((error) => {
|
|
@@ -170,7 +170,7 @@ export function startPluginMutation(options: {
|
|
|
170
170
|
/** 入队前已尝试的安装方式(npm registry 反查等):失败提 Issue 时如实展示;实际执行的命令由 spawn 时追加 */
|
|
171
171
|
attempts?: string[]
|
|
172
172
|
}): InstallTask {
|
|
173
|
-
const task: InstallTask = { id: nextTaskId, target: options.target, displayTarget: options.displayTarget, action: options.action, status: 'pending', timedOut: false, exitCode: null, progress: 0, lines: [], attempts: options.attempts ?? [] }
|
|
173
|
+
const task: InstallTask = { id: nextTaskId, target: options.target, displayTarget: options.displayTarget, action: options.action, status: 'pending', timedOut: false, exitCode: null, progress: 0, lines: [], attempts: options.attempts ?? [], needsRestart: false }
|
|
174
174
|
nextTaskId = nextTaskId >= Number.MAX_SAFE_INTEGER ? 1 : nextTaskId + 1
|
|
175
175
|
tasks.set(task.id, task)
|
|
176
176
|
if (tasks.size > MAX_TASKS) {
|
|
@@ -345,14 +345,19 @@ function spawnMutation(options: {
|
|
|
345
345
|
if (task.status === 'done') {
|
|
346
346
|
task.progress = 100
|
|
347
347
|
if (action === 'remove' && uninstallLoader) {
|
|
348
|
-
// 卸载成功 → 主动从运行中 loader
|
|
349
|
-
//
|
|
348
|
+
// 卸载成功 → 主动从运行中 loader 停用该包条目;
|
|
349
|
+
// 仅当宿主从未加载过它(磁盘已干净)才判「无需重启」;
|
|
350
|
+
// 只要曾在 loader 中存活(停用过 live entry)就仍要求重启——
|
|
351
|
+
// 带 UI 的插件(侧边栏面板等宿主启动时渲染的槽位)disable 后不会
|
|
352
|
+
// 主动消失,重启才能立即摘除面板
|
|
350
353
|
const removed = await removeLoadedEntry(uninstallLoader, target)
|
|
354
|
+
task.needsRestart = !removed
|
|
351
355
|
if (removed) clearPendingRestart(displayTarget ?? target)
|
|
352
356
|
else addPendingRestart(displayTarget ?? target, 'uninstall')
|
|
353
357
|
} else {
|
|
354
358
|
// 安装成功(或卸载但无 loader 引用)→ 登记待重启:
|
|
355
359
|
// 插件要宿主重启才会挂载/卸载干净,重启前一直提醒;展示目标用 displayTarget(owner/repo)
|
|
360
|
+
task.needsRestart = true
|
|
356
361
|
addPendingRestart(displayTarget ?? target, action === 'add' ? 'install' : 'uninstall')
|
|
357
362
|
}
|
|
358
363
|
}
|