dsh-github-flow 0.1.8 → 0.2.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.en.md +162 -58
- package/README.md +167 -60
- package/docs/UPGRADE-NOTES.md +93 -0
- package/docs/architecture.md +47 -17
- package/docs/images/1.png +0 -0
- package/docs/images/2.png +0 -0
- package/docs/images/3.png +0 -0
- package/docs/images/4.png +0 -0
- package/docs/npm-publish-guide.md +26 -3
- package/lib/api/route-helpers.d.ts +47 -0
- package/lib/api/route-helpers.js +134 -0
- package/lib/api/routes-actions.d.ts +23 -0
- package/lib/api/routes-actions.js +239 -0
- package/lib/api/routes-lifecycle.d.ts +9 -0
- package/lib/api/routes-lifecycle.js +126 -0
- package/lib/api/routes-overview.d.ts +11 -0
- package/lib/api/routes-overview.js +227 -0
- package/lib/api/routes-proxy.d.ts +18 -0
- package/lib/api/routes-proxy.js +63 -0
- package/lib/api/routes.d.ts +16 -6
- package/lib/api/routes.js +14 -504
- package/lib/client.js +785 -90
- package/lib/commands/gh.js +11 -2
- package/lib/executor.d.ts +17 -0
- package/lib/executor.js +146 -8
- package/lib/main.d.ts +1 -0
- package/lib/main.js +49 -9
- package/lib/proxy.d.ts +87 -0
- package/lib/proxy.js +275 -0
- package/lib/renderers/markdown.d.ts +29 -0
- package/lib/renderers/markdown.js +520 -0
- package/lib/tools/api.d.ts +4 -0
- package/lib/tools/api.js +14 -4
- package/lib/tools/help.d.ts +31 -0
- package/lib/tools/help.js +224 -0
- package/lib/tools/image-utils.d.ts +79 -0
- package/lib/tools/image-utils.js +247 -0
- package/lib/tools/issue.d.ts +40 -4
- package/lib/tools/issue.js +167 -19
- package/lib/tools/pr.d.ts +29 -4
- package/lib/tools/pr.js +144 -18
- package/lib/tools/release.d.ts +89 -0
- package/lib/tools/release.js +219 -0
- package/lib/tools/repo.d.ts +24 -4
- package/lib/tools/repo.js +114 -14
- package/lib/tools/run.d.ts +22 -1
- package/lib/tools/run.js +74 -14
- package/lib/tools/secret.d.ts +60 -0
- package/lib/tools/secret.js +175 -0
- package/lib/tools/skill.d.ts +63 -0
- package/lib/tools/skill.js +146 -0
- package/lib/types.d.ts +24 -1
- package/lib/update-settings.d.ts +18 -0
- package/lib/update-settings.js +60 -0
- package/lib/updater.d.ts +40 -1
- package/lib/updater.js +126 -9
- package/lib/upgrade-manager.d.ts +74 -1
- package/lib/upgrade-manager.js +356 -46
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +3 -2
- package/lib/client/icons.d.ts +0 -13
- package/lib/client/icons.js +0 -41
- package/lib/client/index.d.ts +0 -3
- package/lib/client/index.js +0 -36
- package/lib/client/panel.d.ts +0 -1
- package/lib/client/panel.js +0 -102
- package/lib/client/style.d.ts +0 -1
- package/lib/client/style.js +0 -555
package/lib/client.js
CHANGED
|
@@ -5,6 +5,10 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
var React = require("react");
|
|
8
|
+
// 面板样式(内联维护):DSH 客户端加载器以经典脚本语义执行本产物(build.mjs 剥除
|
|
9
|
+
// export 声明即为佐证),模块级 import 在该语境是 SyntaxError。曾尝试把 CSS 拆为
|
|
10
|
+
// 独立模块经 import 引入,导致 DSH 重启时客户端加载崩溃并触发插件批量卸载——
|
|
11
|
+
// 已回滚。样式与组件的分离改由「同文件内分区注释」维持,不再跨文件。
|
|
8
12
|
var GITHUB_PANEL_CSS = `
|
|
9
13
|
/* ==========================================================================
|
|
10
14
|
DSH GitHub Flow - 主题自适应样式系统 (Light / Dark 动态联动)
|
|
@@ -286,6 +290,40 @@ body[data-ds-dark-theme] .dsh-badge-purple {
|
|
|
286
290
|
border-color: rgba(163, 113, 247, 0.3);
|
|
287
291
|
}
|
|
288
292
|
|
|
293
|
+
/* 插件身份徽标:标识界面由第三方插件 dsh-github-flow 提供(非 DSH/GitHub 官方)。
|
|
294
|
+
* 为什么需要:界面以 GitHub 官方 Octocat 做功能图标,新用户易误认为官方功能;
|
|
295
|
+
* 徽标点击跳插件仓库,与设置「关于」页形成身份闭环。 */
|
|
296
|
+
.dsh-github-plugin-badge {
|
|
297
|
+
display: inline-flex;
|
|
298
|
+
align-items: center;
|
|
299
|
+
font-size: 10px;
|
|
300
|
+
font-weight: 500;
|
|
301
|
+
padding: 1px 7px;
|
|
302
|
+
border-radius: 10px;
|
|
303
|
+
line-height: 14px;
|
|
304
|
+
vertical-align: middle;
|
|
305
|
+
margin-left: 8px;
|
|
306
|
+
text-decoration: none !important;
|
|
307
|
+
background: rgba(125, 133, 144, 0.12);
|
|
308
|
+
color: #656d76;
|
|
309
|
+
border: 1px solid rgba(125, 133, 144, 0.25);
|
|
310
|
+
cursor: pointer;
|
|
311
|
+
transition: all 0.15s ease;
|
|
312
|
+
}
|
|
313
|
+
.dsh-github-plugin-badge:hover {
|
|
314
|
+
background: rgba(125, 133, 144, 0.2);
|
|
315
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
316
|
+
}
|
|
317
|
+
body[data-ds-dark-theme] .dsh-github-plugin-badge {
|
|
318
|
+
background: rgba(139, 148, 158, 0.18);
|
|
319
|
+
color: #8b949e;
|
|
320
|
+
border-color: rgba(139, 148, 158, 0.3);
|
|
321
|
+
}
|
|
322
|
+
body[data-ds-dark-theme] .dsh-github-plugin-badge:hover {
|
|
323
|
+
background: rgba(139, 148, 158, 0.28);
|
|
324
|
+
color: #f0f6fc;
|
|
325
|
+
}
|
|
326
|
+
|
|
289
327
|
/* 7.5. 分类筛选药丸栏 */
|
|
290
328
|
.dsh-github-pills {
|
|
291
329
|
display: flex;
|
|
@@ -805,7 +843,7 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
|
|
|
805
843
|
line-height: 1.4;
|
|
806
844
|
}
|
|
807
845
|
|
|
808
|
-
/* 16.
|
|
846
|
+
/* 16. 设置弹窗 (Settings Modal):左侧分区导航 + 右侧内容区 */
|
|
809
847
|
.dsh-github-modal-overlay {
|
|
810
848
|
position: fixed;
|
|
811
849
|
top: 0;
|
|
@@ -820,14 +858,18 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
|
|
|
820
858
|
backdrop-filter: blur(2px);
|
|
821
859
|
}
|
|
822
860
|
.dsh-github-modal-dialog {
|
|
823
|
-
width:
|
|
824
|
-
max-width:
|
|
861
|
+
width: 92%;
|
|
862
|
+
max-width: 640px;
|
|
863
|
+
max-height: 86vh;
|
|
864
|
+
display: flex;
|
|
865
|
+
flex-direction: column;
|
|
825
866
|
background: var(--dsw-alias-bg-base, #ffffff);
|
|
826
867
|
border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
|
|
827
868
|
border-radius: 12px;
|
|
828
869
|
box-shadow: 0 16px 32px rgba(0, 0, 0, 0.18);
|
|
829
|
-
padding:
|
|
870
|
+
padding: 0;
|
|
830
871
|
box-sizing: border-box;
|
|
872
|
+
overflow: hidden;
|
|
831
873
|
color: var(--dsw-alias-label-primary, #1f2328);
|
|
832
874
|
font-family: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
|
|
833
875
|
}
|
|
@@ -836,6 +878,105 @@ body[data-ds-dark-theme] .dsh-github-modal-dialog {
|
|
|
836
878
|
border-color: var(--dsw-alias-border-l1, #30363d);
|
|
837
879
|
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.45);
|
|
838
880
|
}
|
|
881
|
+
/* 弹窗头部(标题 + 关闭) */
|
|
882
|
+
.dsh-github-modal-header {
|
|
883
|
+
display: flex;
|
|
884
|
+
justify-content: space-between;
|
|
885
|
+
align-items: center;
|
|
886
|
+
padding: 18px 22px 14px;
|
|
887
|
+
border-bottom: 1px solid var(--dsw-alias-border-l2, #d0d7de);
|
|
888
|
+
flex-shrink: 0;
|
|
889
|
+
}
|
|
890
|
+
body[data-ds-dark-theme] .dsh-github-modal-header {
|
|
891
|
+
border-bottom-color: var(--dsw-alias-border-l1, #30363d);
|
|
892
|
+
}
|
|
893
|
+
/* 分区导航 + 内容 双栏骨架:窄屏(<560px)自动折叠为顶部横向页签 */
|
|
894
|
+
.dsh-github-modal-body {
|
|
895
|
+
display: flex;
|
|
896
|
+
min-height: 0;
|
|
897
|
+
flex: 1;
|
|
898
|
+
}
|
|
899
|
+
.dsh-github-settings-nav {
|
|
900
|
+
flex-shrink: 0;
|
|
901
|
+
width: 148px;
|
|
902
|
+
padding: 14px 10px;
|
|
903
|
+
border-right: 1px solid var(--dsw-alias-border-l2, #d0d7de);
|
|
904
|
+
display: flex;
|
|
905
|
+
flex-direction: column;
|
|
906
|
+
gap: 4px;
|
|
907
|
+
box-sizing: border-box;
|
|
908
|
+
}
|
|
909
|
+
body[data-ds-dark-theme] .dsh-github-settings-nav {
|
|
910
|
+
border-right-color: var(--dsw-alias-border-l1, #30363d);
|
|
911
|
+
}
|
|
912
|
+
.dsh-github-settings-nav-item {
|
|
913
|
+
display: flex;
|
|
914
|
+
align-items: center;
|
|
915
|
+
gap: 8px;
|
|
916
|
+
padding: 8px 10px;
|
|
917
|
+
border-radius: 6px;
|
|
918
|
+
border: none;
|
|
919
|
+
background: none;
|
|
920
|
+
cursor: pointer;
|
|
921
|
+
font-size: 13px;
|
|
922
|
+
text-align: left;
|
|
923
|
+
color: var(--dsw-alias-label-secondary, #656d76);
|
|
924
|
+
transition: background 0.12s ease, color 0.12s ease;
|
|
925
|
+
}
|
|
926
|
+
.dsh-github-settings-nav-item:hover {
|
|
927
|
+
background: var(--dsw-alias-bg-layer-1, #f6f8fa);
|
|
928
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
929
|
+
}
|
|
930
|
+
.dsh-github-settings-nav-item.active {
|
|
931
|
+
background: rgba(9, 105, 218, 0.1);
|
|
932
|
+
color: var(--dsw-alias-link, #0969da);
|
|
933
|
+
font-weight: 600;
|
|
934
|
+
}
|
|
935
|
+
body[data-ds-dark-theme] .dsh-github-settings-nav-item:hover {
|
|
936
|
+
background: rgba(240, 246, 252, 0.06);
|
|
937
|
+
color: var(--dsw-alias-label-primary, #f0f6fc);
|
|
938
|
+
}
|
|
939
|
+
body[data-ds-dark-theme] .dsh-github-settings-nav-item.active {
|
|
940
|
+
background: rgba(88, 166, 255, 0.15);
|
|
941
|
+
color: #58a6ff;
|
|
942
|
+
}
|
|
943
|
+
.dsh-github-settings-content {
|
|
944
|
+
flex: 1;
|
|
945
|
+
min-width: 0;
|
|
946
|
+
padding: 18px 22px 20px;
|
|
947
|
+
overflow-y: auto;
|
|
948
|
+
box-sizing: border-box;
|
|
949
|
+
}
|
|
950
|
+
@media (max-width: 560px) {
|
|
951
|
+
.dsh-github-modal-body {
|
|
952
|
+
flex-direction: column;
|
|
953
|
+
}
|
|
954
|
+
.dsh-github-settings-nav {
|
|
955
|
+
width: 100%;
|
|
956
|
+
flex-direction: row;
|
|
957
|
+
border-right: none;
|
|
958
|
+
border-bottom: 1px solid var(--dsw-alias-border-l2, #d0d7e0);
|
|
959
|
+
overflow-x: auto;
|
|
960
|
+
}
|
|
961
|
+
body[data-ds-dark-theme] .dsh-github-settings-nav {
|
|
962
|
+
border-bottom-color: var(--dsw-alias-border-l1, #30363d);
|
|
963
|
+
}
|
|
964
|
+
.dsh-github-settings-nav-item {
|
|
965
|
+
white-space: nowrap;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
/* 弹窗底部操作条 */
|
|
969
|
+
.dsh-github-modal-footer {
|
|
970
|
+
display: flex;
|
|
971
|
+
justify-content: flex-end;
|
|
972
|
+
gap: 8px;
|
|
973
|
+
padding: 14px 22px;
|
|
974
|
+
border-top: 1px solid var(--dsw-alias-border-l2, #d0d7de);
|
|
975
|
+
flex-shrink: 0;
|
|
976
|
+
}
|
|
977
|
+
body[data-ds-dark-theme] .dsh-github-modal-footer {
|
|
978
|
+
border-top-color: var(--dsw-alias-border-l1, #30363d);
|
|
979
|
+
}
|
|
839
980
|
.dsh-github-radio-card {
|
|
840
981
|
display: flex;
|
|
841
982
|
align-items: flex-start;
|
|
@@ -903,6 +1044,37 @@ body[data-ds-dark-theme] .dsh-github-select option {
|
|
|
903
1044
|
color: #f0f6fc;
|
|
904
1045
|
}
|
|
905
1046
|
|
|
1047
|
+
/* 代理设置弹窗的文本输入框(浅色/深色双模式,设计变量对齐 select 保持视觉一致) */
|
|
1048
|
+
.dsh-github-input {
|
|
1049
|
+
padding: 7px 12px;
|
|
1050
|
+
border-radius: 6px;
|
|
1051
|
+
border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
|
|
1052
|
+
background: var(--dsw-alias-bg-base, #ffffff);
|
|
1053
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
1054
|
+
font-size: 13px;
|
|
1055
|
+
outline: none;
|
|
1056
|
+
box-sizing: border-box;
|
|
1057
|
+
color-scheme: light;
|
|
1058
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
1059
|
+
}
|
|
1060
|
+
.dsh-github-input:focus {
|
|
1061
|
+
border-color: var(--dsw-alias-link, #0969da);
|
|
1062
|
+
box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.15);
|
|
1063
|
+
}
|
|
1064
|
+
.dsh-github-input::placeholder {
|
|
1065
|
+
color: var(--dsw-alias-label-tertiary, #8c959f);
|
|
1066
|
+
}
|
|
1067
|
+
body[data-ds-dark-theme] .dsh-github-input {
|
|
1068
|
+
border-color: var(--dsw-alias-border-l1, #30363d);
|
|
1069
|
+
background: var(--dsw-alias-bg-layer-2, #1e1e22);
|
|
1070
|
+
color: var(--dsw-alias-label-primary, #f0f6fc);
|
|
1071
|
+
color-scheme: dark;
|
|
1072
|
+
}
|
|
1073
|
+
body[data-ds-dark-theme] .dsh-github-input:focus {
|
|
1074
|
+
border-color: #58a6ff;
|
|
1075
|
+
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.2);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
906
1078
|
/* 缓存信息展示框 */
|
|
907
1079
|
.dsh-github-cache-info-box {
|
|
908
1080
|
padding: 10px 12px;
|
|
@@ -920,6 +1092,45 @@ body[data-ds-dark-theme] .dsh-github-cache-info-box {
|
|
|
920
1092
|
color: var(--dsw-alias-label-secondary, #8c8c8c);
|
|
921
1093
|
}
|
|
922
1094
|
|
|
1095
|
+
/* 关于页求 Star 卡片:与 cache-info-box 同款双主题底色(浅色纸面/暗色深底) */
|
|
1096
|
+
.dsh-github-about-star-box {
|
|
1097
|
+
padding: 12px 14px;
|
|
1098
|
+
border-radius: 8px;
|
|
1099
|
+
background: var(--dsw-alias-bg-layer-1, #f6f8fa);
|
|
1100
|
+
border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
|
|
1101
|
+
}
|
|
1102
|
+
body[data-ds-dark-theme] .dsh-github-about-star-box {
|
|
1103
|
+
background: var(--dsw-alias-bg-base, #161b22);
|
|
1104
|
+
border-color: var(--dsw-alias-border-l1, #30363d);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/* 主操作按钮(链接色底):链接色底 + 白字,暗色用亮蓝(对齐 radio/链接的暗色强调色 58a6ff) */
|
|
1108
|
+
.dsh-github-primary-btn {
|
|
1109
|
+
padding: 5px 14px;
|
|
1110
|
+
font-weight: 600;
|
|
1111
|
+
white-space: nowrap;
|
|
1112
|
+
background: var(--dsw-alias-link, #0969da);
|
|
1113
|
+
border-color: var(--dsw-alias-link, #0969da);
|
|
1114
|
+
color: #ffffff;
|
|
1115
|
+
}
|
|
1116
|
+
.dsh-github-primary-btn:hover {
|
|
1117
|
+
background: var(--dsw-alias-link, #0969da);
|
|
1118
|
+
border-color: var(--dsw-alias-link, #0969da);
|
|
1119
|
+
color: #ffffff;
|
|
1120
|
+
opacity: 0.88;
|
|
1121
|
+
}
|
|
1122
|
+
body[data-ds-dark-theme] .dsh-github-primary-btn {
|
|
1123
|
+
background: #58a6ff;
|
|
1124
|
+
border-color: #58a6ff;
|
|
1125
|
+
color: #0d1117;
|
|
1126
|
+
}
|
|
1127
|
+
body[data-ds-dark-theme] .dsh-github-primary-btn:hover {
|
|
1128
|
+
background: #58a6ff;
|
|
1129
|
+
border-color: #58a6ff;
|
|
1130
|
+
color: #0d1117;
|
|
1131
|
+
opacity: 0.88;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
923
1134
|
/* 单选框点强调色 */
|
|
924
1135
|
.dsh-github-radio {
|
|
925
1136
|
accent-color: var(--dsw-alias-link, #0969da);
|
|
@@ -968,6 +1179,24 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
968
1179
|
}
|
|
969
1180
|
}, renderOctocat(Math.round(size * 0.58), "#ffffff"));
|
|
970
1181
|
}
|
|
1182
|
+
// 插件身份徽标(h 工厂注入):标注界面由第三方插件 dsh-github-flow 提供。
|
|
1183
|
+
// 点击不直接跳仓库,而是派发全局事件打开「设置-关于」弹窗(仓库/协议/Star
|
|
1184
|
+
// 都在里面),由各宿主面板监听并挂载弹窗——避免从任意界面突兀跳浏览器。
|
|
1185
|
+
function renderPluginBadge(h) {
|
|
1186
|
+
return h("button", {
|
|
1187
|
+
className: "dsh-github-plugin-badge",
|
|
1188
|
+
type: "button",
|
|
1189
|
+
title: "本界面由第三方插件 dsh-github-flow 提供(非 DSH / GitHub 官方)。点击查看关于。",
|
|
1190
|
+
onClick: function (e) {
|
|
1191
|
+
e.stopPropagation();
|
|
1192
|
+
e.preventDefault();
|
|
1193
|
+
try {
|
|
1194
|
+
window.dispatchEvent(new CustomEvent("dsh-github-open-about"));
|
|
1195
|
+
}
|
|
1196
|
+
catch (err) { /* 事件派发失败不阻断点击语义 */ }
|
|
1197
|
+
},
|
|
1198
|
+
}, "插件");
|
|
1199
|
+
}
|
|
971
1200
|
// 认证与安装引导卡片组件(响应式自适应中央大面板与右侧栏)
|
|
972
1201
|
function AuthGuideCard(props) {
|
|
973
1202
|
var auth = props ? props.auth : null;
|
|
@@ -1012,15 +1241,17 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1012
1241
|
});
|
|
1013
1242
|
var json = await res.json();
|
|
1014
1243
|
if (json.ok) {
|
|
1015
|
-
|
|
1016
|
-
|
|
1244
|
+
// 措辞留余地:start 的成败偶受系统会话策略影响,未弹出时用户仍有手动出路
|
|
1245
|
+
setToastMsg("已唤起系统终端,请在弹出窗口中完成操作;若未弹出,可手动打开终端执行上方命令");
|
|
1246
|
+
setTimeout(() => setToastMsg(null), 6000);
|
|
1017
1247
|
}
|
|
1018
1248
|
else {
|
|
1019
|
-
|
|
1249
|
+
// 失败如实展示:后端带回真实原因(如未找到终端/命令失败),并给出手动命令
|
|
1250
|
+
setToastMsg("❌ " + (json.message || "唤起失败,请手动打开终端执行上方命令"));
|
|
1020
1251
|
}
|
|
1021
1252
|
}
|
|
1022
1253
|
catch (e) {
|
|
1023
|
-
setToastMsg("
|
|
1254
|
+
setToastMsg("无法连接后台服务,请手动打开终端执行上方命令");
|
|
1024
1255
|
}
|
|
1025
1256
|
finally {
|
|
1026
1257
|
setLaunching(false);
|
|
@@ -1076,9 +1307,14 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1076
1307
|
className: "dsh-github-action-btn",
|
|
1077
1308
|
onClick: function () {
|
|
1078
1309
|
setToastMsg("正在快速检测登录状态...");
|
|
1079
|
-
|
|
1310
|
+
// 探测超时保护:后端 auth 探针本身 10s 超时,前端同样 10s 兜底——
|
|
1311
|
+
// 无超时时挂起会让 toast 永远停在"正在检测"且自愈刷新不触发
|
|
1312
|
+
var probeController = new AbortController();
|
|
1313
|
+
var probeTimer = setTimeout(function () { probeController.abort(); }, 10000);
|
|
1314
|
+
fetch("/api/github/auth?force=true", { signal: probeController.signal })
|
|
1080
1315
|
.then(function (res) { return res.json(); })
|
|
1081
1316
|
.then(function (json) {
|
|
1317
|
+
clearTimeout(probeTimer);
|
|
1082
1318
|
if (json && json.ok && json.auth && json.auth.loggedIn) {
|
|
1083
1319
|
setToastMsg("✅ 已识别到 GitHub CLI 登录态,正在拉取数据...");
|
|
1084
1320
|
clearStoredGlobalOverview();
|
|
@@ -1090,6 +1326,8 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1090
1326
|
}
|
|
1091
1327
|
})
|
|
1092
1328
|
.catch(function () {
|
|
1329
|
+
clearTimeout(probeTimer);
|
|
1330
|
+
setToastMsg("❌ 检测超时或后端不可达,请稍后重试");
|
|
1093
1331
|
onRefresh();
|
|
1094
1332
|
});
|
|
1095
1333
|
},
|
|
@@ -1151,7 +1389,16 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1151
1389
|
}
|
|
1152
1390
|
else {
|
|
1153
1391
|
setStatus("failed");
|
|
1154
|
-
|
|
1392
|
+
// 失败原因透传:后端 log 的末尾几行含真实报错(如 'dsh' 不是内部命令),
|
|
1393
|
+
// 只给「退出码异常」这类笼统话术用户无从下手
|
|
1394
|
+
var logTail = "";
|
|
1395
|
+
if (json.log && typeof json.log === "string") {
|
|
1396
|
+
var lines = json.log.trim().split("\n").filter(function (l) { return l.trim(); });
|
|
1397
|
+
if (lines.length > 0) {
|
|
1398
|
+
logTail = "\n\n📝 日志末尾: " + lines.slice(-4).join(" ⏎ ").slice(0, 400);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
setMsg((json.message || "升级命令执行失败,请在宿主终端手动执行。") + logTail);
|
|
1155
1402
|
}
|
|
1156
1403
|
}
|
|
1157
1404
|
catch (err) {
|
|
@@ -1167,7 +1414,9 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1167
1414
|
setDismissed(true);
|
|
1168
1415
|
};
|
|
1169
1416
|
var handleCopy = function () {
|
|
1170
|
-
|
|
1417
|
+
// 兜底命令也必须带 --profile(官方 CLI 必填)——正常路径 upgradeCommand
|
|
1418
|
+
// 由后端按真实 profile 生成,此默认值仅在后端数据缺失时兜底
|
|
1419
|
+
var cmd = updateInfo.upgradeCommand || "dsh plugin --profile desktop add dsh-github-flow@latest";
|
|
1171
1420
|
if (navigator.clipboard) {
|
|
1172
1421
|
navigator.clipboard.writeText(cmd);
|
|
1173
1422
|
alert("已复制升级命令到剪贴板:\n" + cmd);
|
|
@@ -1223,7 +1472,17 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1223
1472
|
}, status === "upgrading" ? "升级中..." : "⚡ 一键升级"), h("button", {
|
|
1224
1473
|
className: "dsh-github-btn-sm",
|
|
1225
1474
|
onClick: handleCopy,
|
|
1226
|
-
style: {
|
|
1475
|
+
style: {
|
|
1476
|
+
fontSize: "11px",
|
|
1477
|
+
padding: "4px 8px",
|
|
1478
|
+
// 失败态高亮:与驾驶舱横幅同一引导逻辑(失败文案指向复制动作)
|
|
1479
|
+
...(status === "failed" ? {
|
|
1480
|
+
background: "var(--dsw-alias-link, #0969da)",
|
|
1481
|
+
color: "#ffffff",
|
|
1482
|
+
borderColor: "var(--dsw-alias-link, #0969da)",
|
|
1483
|
+
fontWeight: 600,
|
|
1484
|
+
} : {}),
|
|
1485
|
+
},
|
|
1227
1486
|
}, "📋 复制命令")));
|
|
1228
1487
|
}
|
|
1229
1488
|
// 2. 全景驾驶舱大面板横幅模式
|
|
@@ -1267,8 +1526,17 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1267
1526
|
}, status === "upgrading" ? "正在自动升级中..." : "⚡ 一键自动升级"), h("button", {
|
|
1268
1527
|
className: "dsh-github-btn-sm",
|
|
1269
1528
|
onClick: handleCopy,
|
|
1270
|
-
style: {
|
|
1271
|
-
|
|
1529
|
+
style: {
|
|
1530
|
+
padding: "6px 12px",
|
|
1531
|
+
// 失败态高亮复制按钮:失败文案引导的动作就是它,视觉权重同步提升
|
|
1532
|
+
...(status === "failed" ? {
|
|
1533
|
+
background: "var(--dsw-alias-link, #0969da)",
|
|
1534
|
+
color: "#ffffff",
|
|
1535
|
+
borderColor: "var(--dsw-alias-link, #0969da)",
|
|
1536
|
+
fontWeight: 600,
|
|
1537
|
+
} : {}),
|
|
1538
|
+
},
|
|
1539
|
+
}, status === "failed" ? "📋 复制命令手动升级" : "📋 复制命令"), h("button", {
|
|
1272
1540
|
onClick: handleDismiss,
|
|
1273
1541
|
title: "不再提醒此版本",
|
|
1274
1542
|
style: { background: "none", border: "none", cursor: "pointer", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", fontSize: "16px", padding: "0 4px" },
|
|
@@ -1337,7 +1605,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1337
1605
|
}, text);
|
|
1338
1606
|
}
|
|
1339
1607
|
// 渲染 GitHub 仓库信息与操作视图的核心函数
|
|
1340
|
-
function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false, updateInfo = null, syncStatus = "", lastSyncedAt = 0) {
|
|
1608
|
+
function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false, updateInfo = null, syncStatus = "", lastSyncedAt = 0, onOpenSettings = null) {
|
|
1341
1609
|
var auth = data ? data.auth : null;
|
|
1342
1610
|
var repo = data ? data.repo : null;
|
|
1343
1611
|
var prs = (data && data.pullRequests) || [];
|
|
@@ -1347,11 +1615,19 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1347
1615
|
var upgradeBannerEl = effectiveUpdateInfo && effectiveUpdateInfo.hasUpdate
|
|
1348
1616
|
? h(UpgradeNotificationCard, { updateInfo: effectiveUpdateInfo, isRightbar })
|
|
1349
1617
|
: null;
|
|
1350
|
-
var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(isRightbar ? 26 : 30), h("h2", null, isRightbar ? "GitHub 仓库" : "GitHub 工作台"), syncStatus ? h(SyncStatusBadge, { syncStatus, lastSyncedAt, isRightbar }) : null), h("button", {
|
|
1618
|
+
var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(isRightbar ? 26 : 30), h("h2", null, isRightbar ? "GitHub 仓库" : "GitHub 工作台"), renderPluginBadge(h), syncStatus ? h(SyncStatusBadge, { syncStatus, lastSyncedAt, isRightbar }) : null), h("div", { style: { display: "flex", alignItems: "center", gap: "6px" } }, h("button", {
|
|
1351
1619
|
className: "dsh-github-refresh-btn",
|
|
1352
1620
|
onClick: onRefresh,
|
|
1353
1621
|
disabled: loading,
|
|
1354
|
-
}, loading ? "刷新中..." : "🔄 刷新")
|
|
1622
|
+
}, loading ? "刷新中..." : "🔄 刷新"),
|
|
1623
|
+
// 设置按钮:与全景主页复用同一设置弹窗(传入回调时渲染——目前仅右侧栏)。
|
|
1624
|
+
// 右侧栏此前没有设置入口,用户必须切到主页才能改同步/代理/更新设置。
|
|
1625
|
+
onOpenSettings ? h("button", {
|
|
1626
|
+
className: "dsh-github-btn-sm",
|
|
1627
|
+
onClick: onOpenSettings,
|
|
1628
|
+
title: "配置数据同步节奏、本地缓存、网络代理与插件信息",
|
|
1629
|
+
style: { padding: "4px 10px" },
|
|
1630
|
+
}, "⚙️") : null));
|
|
1355
1631
|
// 仓库基础卡片
|
|
1356
1632
|
var repoCardEl = h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "当前工作区 GitHub 仓库"), h("div", { className: "dsh-github-card-value" }, repo
|
|
1357
1633
|
? h("a", { href: repo.url, target: "_blank", className: "dsh-github-repo-link" }, repo.nameWithOwner)
|
|
@@ -1463,9 +1739,12 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1463
1739
|
// 全局持久化缓存与偏好配置键名
|
|
1464
1740
|
var GLOBAL_OVERVIEW_CACHE_KEY = "dsh_github_global_overview_snapshot_v1";
|
|
1465
1741
|
var SYNC_SETTINGS_STORAGE_KEY = "dsh_github_sync_settings_v1";
|
|
1742
|
+
// 同步策略收敛为两态:'swr'(打开秒开旧数据+后台静默拉新)| 'manual'(仅手动刷新)。
|
|
1743
|
+
// 旧的第三态 'interval'(前端 setInterval 轮询)已删除:浏览器对后台标签页节流
|
|
1744
|
+
// 定时器、组件卸载即销毁,该模式在真实环境几乎从不按承诺工作(停留短于间隔则
|
|
1745
|
+
// 永不触发);其「打开即新鲜」的诉求由 SWR 挂载自愈覆盖,「持续同步」点刷新即可。
|
|
1466
1746
|
var DEFAULT_SYNC_SETTINGS = {
|
|
1467
|
-
strategy: "swr", // 'swr'
|
|
1468
|
-
intervalMs: 300000, // 默认 5 分钟 (300,000ms)
|
|
1747
|
+
strategy: "swr", // 'swr' | 'manual'
|
|
1469
1748
|
};
|
|
1470
1749
|
function getLocalSyncSettings() {
|
|
1471
1750
|
try {
|
|
@@ -1473,7 +1752,14 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1473
1752
|
if (!raw)
|
|
1474
1753
|
return DEFAULT_SYNC_SETTINGS;
|
|
1475
1754
|
var parsed = JSON.parse(raw);
|
|
1476
|
-
|
|
1755
|
+
var merged = Object.assign({}, DEFAULT_SYNC_SETTINGS, parsed);
|
|
1756
|
+
// 旧版本迁移:曾选过 'interval' 的用户自动回落 SWR(最接近原意图),
|
|
1757
|
+
// 并立即覆写存储防止每次读取都走迁移分支。
|
|
1758
|
+
if (merged.strategy === "interval") {
|
|
1759
|
+
merged.strategy = "swr";
|
|
1760
|
+
saveLocalSyncSettings(merged);
|
|
1761
|
+
}
|
|
1762
|
+
return merged;
|
|
1477
1763
|
}
|
|
1478
1764
|
catch (e) {
|
|
1479
1765
|
return DEFAULT_SYNC_SETTINGS;
|
|
@@ -1628,10 +1914,10 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1628
1914
|
window.removeEventListener("dsh-github-cache-cleared", onCacheCleared);
|
|
1629
1915
|
};
|
|
1630
1916
|
}, []);
|
|
1631
|
-
// gh
|
|
1917
|
+
// 检查更新与 gh 环境链路(安装/登录/业务数据)零依赖,挂载即并行发起:
|
|
1918
|
+
// 原先门控在 [data] 之后会串行等 5~19 秒,且 gh 冷启动失败时 data 恒空导致横幅永不出。
|
|
1919
|
+
// 仅 300ms 微延迟避让首屏渲染;并发由服务端 inFlightPromise + 24h 缓存合流去重。
|
|
1632
1920
|
React.useEffect(() => {
|
|
1633
|
-
if (!data)
|
|
1634
|
-
return;
|
|
1635
1921
|
var timer = setTimeout(function () {
|
|
1636
1922
|
fetch("/api/github/check-update")
|
|
1637
1923
|
.then(function (res) { return res.json(); })
|
|
@@ -1641,9 +1927,9 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1641
1927
|
}
|
|
1642
1928
|
})
|
|
1643
1929
|
.catch(function () { });
|
|
1644
|
-
},
|
|
1930
|
+
}, 300);
|
|
1645
1931
|
return function () { clearTimeout(timer); };
|
|
1646
|
-
}, [
|
|
1932
|
+
}, []);
|
|
1647
1933
|
// 遵循全局规则的数据拉取核心函数 (仅依赖标量 cwd 与 sessionId,坚决去除 props 引用依赖,彻底终结死循环)
|
|
1648
1934
|
var loadData = React.useCallback(async (forceRefresh = false) => {
|
|
1649
1935
|
// 规则执行:若主页设置为「纯手动模式 (manual)」,且非用户点击强制刷新,且本地已有数据,坚决不打后台网络!
|
|
@@ -1655,9 +1941,15 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1655
1941
|
}
|
|
1656
1942
|
setSyncStatus(data ? "syncing" : "loading");
|
|
1657
1943
|
setError(null);
|
|
1944
|
+
// 与全局面板同规格的超时保护:任一 fetch 挂起(如系统代理劫持回环请求)时
|
|
1945
|
+
// 45 秒 abort,loading 必然清除——未安装 gh 的用户能拿到明确的错误出口
|
|
1946
|
+
// 而非永久转圈。此前本函数完全没有 AbortController,挂起即永久 loading。
|
|
1947
|
+
var controller = new AbortController();
|
|
1948
|
+
var timedOut = false;
|
|
1949
|
+
var timer = setTimeout(function () { timedOut = true; controller.abort(); }, 45000);
|
|
1658
1950
|
try {
|
|
1659
1951
|
if (forceRefresh) {
|
|
1660
|
-
await fetch("/api/github/refresh", { method: "POST" }).catch(() => { });
|
|
1952
|
+
await fetch("/api/github/refresh", { method: "POST", signal: controller.signal }).catch(() => { });
|
|
1661
1953
|
}
|
|
1662
1954
|
var url = "/api/github/overview";
|
|
1663
1955
|
var params = [];
|
|
@@ -1670,7 +1962,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1670
1962
|
if (params.length > 0) {
|
|
1671
1963
|
url += "?" + params.join("&");
|
|
1672
1964
|
}
|
|
1673
|
-
var res = await fetch(url);
|
|
1965
|
+
var res = await fetch(url, { signal: controller.signal });
|
|
1674
1966
|
if (!res.ok) {
|
|
1675
1967
|
var errText = await res.text().catch(() => "");
|
|
1676
1968
|
var msg = `后端服务未就绪 (${res.status}): ${errText || res.statusText}`;
|
|
@@ -1701,17 +1993,26 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1701
1993
|
}
|
|
1702
1994
|
catch (err) {
|
|
1703
1995
|
if (data) {
|
|
1996
|
+
// 后台网络故障时,旧数据依然完整可用
|
|
1704
1997
|
setSyncStatus("failed");
|
|
1705
1998
|
}
|
|
1706
1999
|
else {
|
|
1707
|
-
|
|
2000
|
+
if (timedOut || err.name === "AbortError") {
|
|
2001
|
+
setError("获取超时(45 秒):本机 gh 命令响应过慢或网络代理劫持了回环请求。可稍后重试,或在「⚙️ 设置 → 🌐 网络代理」确认代理已放行 127.0.0.1。");
|
|
2002
|
+
}
|
|
2003
|
+
else {
|
|
2004
|
+
setError(err.message || "网络异常");
|
|
2005
|
+
}
|
|
1708
2006
|
}
|
|
1709
2007
|
}
|
|
1710
2008
|
finally {
|
|
2009
|
+
clearTimeout(timer);
|
|
1711
2010
|
setLoading(false);
|
|
1712
2011
|
}
|
|
1713
2012
|
}, [cwd, sessionId, data, syncSettings.strategy]);
|
|
1714
|
-
// 挂载与策略调度:与主页规则 100%
|
|
2013
|
+
// 挂载与策略调度:与主页规则 100% 保持一致。
|
|
2014
|
+
// SWR 分支不判 data:挂载 300ms 后台静默拉新,昨天的持久化快照当场自愈——
|
|
2015
|
+
// 「打开就能看到最新」是 SWR 的定义,不依赖用户停留时长。
|
|
1715
2016
|
React.useEffect(() => {
|
|
1716
2017
|
if (syncSettings.strategy === "swr") {
|
|
1717
2018
|
var timer = setTimeout(function () { loadData(false); }, 300);
|
|
@@ -1721,15 +2022,8 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1721
2022
|
loadData(false);
|
|
1722
2023
|
}
|
|
1723
2024
|
}, [cwd, sessionId, syncSettings.strategy]);
|
|
1724
|
-
//
|
|
1725
|
-
|
|
1726
|
-
if (syncSettings.strategy !== "interval" || !syncSettings.intervalMs || syncSettings.intervalMs <= 0)
|
|
1727
|
-
return;
|
|
1728
|
-
var timer = setInterval(function () {
|
|
1729
|
-
loadData(false);
|
|
1730
|
-
}, syncSettings.intervalMs);
|
|
1731
|
-
return function () { clearInterval(timer); };
|
|
1732
|
-
}, [syncSettings.strategy, syncSettings.intervalMs, loadData]);
|
|
2025
|
+
// (原定时轮询调度已随 'interval' 策略一并移除:前端 setInterval 受浏览器
|
|
2026
|
+
// 后台节流且随组件卸载销毁,无法兑现「定时自动更新」承诺)
|
|
1733
2027
|
// 窗口切回自动探测自愈:优先轻量快速探针,检测到已登录立即自愈
|
|
1734
2028
|
React.useEffect(() => {
|
|
1735
2029
|
var onFocus = () => {
|
|
@@ -1747,8 +2041,56 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1747
2041
|
window.addEventListener("focus", onFocus);
|
|
1748
2042
|
return () => window.removeEventListener("focus", onFocus);
|
|
1749
2043
|
}, [data, loadData]);
|
|
2044
|
+
// 设置弹窗(右侧栏独立实例):插件徽标 → 直开「关于」分区;⚙️ 按钮 → 默认
|
|
2045
|
+
// 「数据同步」。与全景主页复用同一弹窗组件,同步设置经事件总线实时对齐两侧。
|
|
2046
|
+
var [aboutModalOpen, setAboutModalOpen] = React.useState(false);
|
|
2047
|
+
var [rightbarModalOpen, setRightbarModalOpen] = React.useState(false);
|
|
2048
|
+
var [rightbarModalSection, setRightbarModalSection] = React.useState("sync");
|
|
2049
|
+
var containerRef = React.useRef(null);
|
|
2050
|
+
React.useEffect(() => {
|
|
2051
|
+
// 多宿主同挂载防叠层:全景主页与右侧栏可能同时在 DOM 中,徽标事件若被
|
|
2052
|
+
// 两处同时响应会出现双层弹窗。只有「自身容器可见」的宿主才响应——
|
|
2053
|
+
// 徽标所在面板必然是用户当前看到的那个。
|
|
2054
|
+
var onOpenAbout = () => {
|
|
2055
|
+
var el = containerRef.current;
|
|
2056
|
+
if (!el)
|
|
2057
|
+
return;
|
|
2058
|
+
var rect = el.getBoundingClientRect();
|
|
2059
|
+
var visible = rect.width > 0 && rect.height > 0;
|
|
2060
|
+
if (visible)
|
|
2061
|
+
setAboutModalOpen(true);
|
|
2062
|
+
};
|
|
2063
|
+
window.addEventListener("dsh-github-open-about", onOpenAbout);
|
|
2064
|
+
return () => window.removeEventListener("dsh-github-open-about", onOpenAbout);
|
|
2065
|
+
}, []);
|
|
2066
|
+
var openRightbarSettings = () => {
|
|
2067
|
+
setRightbarModalSection("sync");
|
|
2068
|
+
setRightbarModalOpen(true);
|
|
2069
|
+
};
|
|
1750
2070
|
var h = React.createElement;
|
|
1751
|
-
return h("div", { className: "dsh-github-rightbar-container" }, ...renderGithubRepoView(h, data, loading, error, () => loadData(true), true, updateInfo, syncStatus, lastSyncedAt)
|
|
2071
|
+
return h("div", { className: "dsh-github-rightbar-container", ref: containerRef }, ...renderGithubRepoView(h, data, loading, error, () => loadData(true), true, updateInfo, syncStatus, lastSyncedAt, openRightbarSettings),
|
|
2072
|
+
// 弹窗一:插件徽标触发的「关于」单页展示
|
|
2073
|
+
h(GlobalSyncSettingsModal, {
|
|
2074
|
+
isOpen: aboutModalOpen,
|
|
2075
|
+
initialSection: "about",
|
|
2076
|
+
onClose: () => setAboutModalOpen(false),
|
|
2077
|
+
currentSettings: syncSettings,
|
|
2078
|
+
onSave: function () { },
|
|
2079
|
+
onClearCache: function () { setData(null); },
|
|
2080
|
+
}),
|
|
2081
|
+
// 弹窗二:⚙️ 按钮触发的完整设置(与主页同一弹窗组件,分区可自由切换)
|
|
2082
|
+
h(GlobalSyncSettingsModal, {
|
|
2083
|
+
isOpen: rightbarModalOpen,
|
|
2084
|
+
initialSection: rightbarModalSection,
|
|
2085
|
+
onClose: () => setRightbarModalOpen(false),
|
|
2086
|
+
currentSettings: syncSettings,
|
|
2087
|
+
onSave: function (newSettings) { setSyncSettings(newSettings); },
|
|
2088
|
+
onClearCache: function () {
|
|
2089
|
+
setData(null);
|
|
2090
|
+
setLastSyncedAt(0);
|
|
2091
|
+
setSyncStatus("loading");
|
|
2092
|
+
},
|
|
2093
|
+
}));
|
|
1752
2094
|
}
|
|
1753
2095
|
// 2. 右侧栏 Tab 标题组件
|
|
1754
2096
|
function GithubRightbarTitle() {
|
|
@@ -1764,19 +2106,78 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1764
2106
|
const active = Boolean(props && props.active);
|
|
1765
2107
|
return React.createElement("div", {
|
|
1766
2108
|
className: "dsh-github-icon" + (active ? " active" : ""),
|
|
1767
|
-
title: "GitHub
|
|
2109
|
+
title: "GitHub 全景驾驶舱(dsh-github-flow 插件)",
|
|
1768
2110
|
}, React.createElement("div", {
|
|
1769
2111
|
className: "dsh-github-icon-inner" + (active ? " active" : "")
|
|
1770
2112
|
}, renderOctocat(18, "currentColor")));
|
|
1771
2113
|
}
|
|
1772
|
-
// 全景驾驶舱「⚙️
|
|
2114
|
+
// 全景驾驶舱「⚙️ 设置」模态弹窗组件:左侧分区导航(数据同步 / 网络代理 / 更新设置 / 关于)+ 右侧内容区。
|
|
2115
|
+
// 为什么改多分区:弹窗承载能力从单一「同步偏好」扩展为多项插件设置(后续可继续加),
|
|
2116
|
+
// 单页滚动堆叠会让用户找不到入口;分区导航让每项设置拥有独立心智空间与保存动作。
|
|
2117
|
+
// initialSection:宿主可指定初始分区——插件徽标点击时直开「关于」,单独展示身份信息。
|
|
1773
2118
|
function GlobalSyncSettingsModal(props) {
|
|
1774
2119
|
if (!props.isOpen)
|
|
1775
2120
|
return null;
|
|
2121
|
+
// ---- 当前激活分区:sync / proxy / update / about ----
|
|
2122
|
+
var [activeSection, setActiveSection] = React.useState(props.initialSection || "sync");
|
|
1776
2123
|
var [strategy, setStrategy] = React.useState(props.currentSettings.strategy || "swr");
|
|
1777
|
-
var [intervalMs, setIntervalMs] = React.useState(props.currentSettings.intervalMs || 300000);
|
|
1778
2124
|
var [cacheMeta, setCacheMeta] = React.useState(getCacheStorageMeta());
|
|
1779
2125
|
var [clearTip, setClearTip] = React.useState("");
|
|
2126
|
+
// ---- 更新设置分区状态 ----
|
|
2127
|
+
// 打开弹窗时从后端拉取真实模式(权威源是 update-settings.json 而非 localStorage),
|
|
2128
|
+
// 与代理分区同范式:避免多窗口/外部手改文件导致 UI 与实际生效配置漂移。
|
|
2129
|
+
var [updateMode, setUpdateMode] = React.useState("official");
|
|
2130
|
+
var [updateBusy, setUpdateBusy] = React.useState(false);
|
|
2131
|
+
var [updateMsg, setUpdateMsg] = React.useState(null);
|
|
2132
|
+
// ---- 关于分区:当前运行版本号(弹窗打开时轻量拉取,失败静默回落占位)----
|
|
2133
|
+
var [aboutVersion, setAboutVersion] = React.useState(null);
|
|
2134
|
+
React.useEffect(function () {
|
|
2135
|
+
if (!props.isOpen)
|
|
2136
|
+
return;
|
|
2137
|
+
fetch("/api/github/update-settings")
|
|
2138
|
+
.then(function (r) { return r.json(); })
|
|
2139
|
+
.then(function (j) {
|
|
2140
|
+
if (j && j.ok && j.data && j.data.mode) {
|
|
2141
|
+
setUpdateMode(j.data.mode);
|
|
2142
|
+
}
|
|
2143
|
+
})
|
|
2144
|
+
.catch(function () { });
|
|
2145
|
+
// 版本号:check-update 命中 24h 缓存时零成本;失败静默(关于页显示占位版本)
|
|
2146
|
+
fetch("/api/github/check-update")
|
|
2147
|
+
.then(function (r) { return r.json(); })
|
|
2148
|
+
.then(function (j) {
|
|
2149
|
+
if (j && j.ok && j.data && j.data.currentVersion)
|
|
2150
|
+
setAboutVersion(j.data.currentVersion);
|
|
2151
|
+
})
|
|
2152
|
+
.catch(function () { });
|
|
2153
|
+
}, [props.isOpen]);
|
|
2154
|
+
// ---- 网络代理分区状态 ----
|
|
2155
|
+
// 打开弹窗时从后端拉取真实代理状态(权威源是 proxy.json 而非 localStorage),
|
|
2156
|
+
// 避免多窗口/外部手改文件导致的 UI 与实际生效配置漂移。
|
|
2157
|
+
var [proxyEnabled, setProxyEnabled] = React.useState(false);
|
|
2158
|
+
var [proxyUrl, setProxyUrl] = React.useState("");
|
|
2159
|
+
var [proxyNoProxy, setProxyNoProxy] = React.useState("localhost,127.0.0.1");
|
|
2160
|
+
var [proxyApplyNpm, setProxyApplyNpm] = React.useState(true);
|
|
2161
|
+
var [proxyBusy, setProxyBusy] = React.useState(false);
|
|
2162
|
+
var [proxyMsg, setProxyMsg] = React.useState(null);
|
|
2163
|
+
var [proxyProbe, setProxyProbe] = React.useState(null);
|
|
2164
|
+
React.useEffect(function () {
|
|
2165
|
+
if (!props.isOpen)
|
|
2166
|
+
return;
|
|
2167
|
+
fetch("/api/github/proxy")
|
|
2168
|
+
.then(function (r) { return r.json(); })
|
|
2169
|
+
.then(function (j) {
|
|
2170
|
+
if (j && j.ok && j.state) {
|
|
2171
|
+
setProxyEnabled(Boolean(j.state.config && j.state.config.url));
|
|
2172
|
+
setProxyUrl((j.state.config && j.state.config.url) || "");
|
|
2173
|
+
setProxyNoProxy((j.state.config && j.state.config.noProxy) || "localhost,127.0.0.1");
|
|
2174
|
+
setProxyApplyNpm(j.state.config ? j.state.config.applyToPackageManager !== false : true);
|
|
2175
|
+
if (j.state.lastProbe)
|
|
2176
|
+
setProxyProbe(j.state.lastProbe);
|
|
2177
|
+
}
|
|
2178
|
+
})
|
|
2179
|
+
.catch(function () { });
|
|
2180
|
+
}, [props.isOpen]);
|
|
1780
2181
|
// 键盘 Escape 快捷关闭支持
|
|
1781
2182
|
React.useEffect(function () {
|
|
1782
2183
|
var onKeyDown = function (e) {
|
|
@@ -1787,11 +2188,59 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1787
2188
|
window.addEventListener("keydown", onKeyDown);
|
|
1788
2189
|
return function () { window.removeEventListener("keydown", onKeyDown); };
|
|
1789
2190
|
}, [props.onClose]);
|
|
2191
|
+
// initialSection 同步:弹窗实例常驻(return null 在 hooks 后,不卸载),
|
|
2192
|
+
// useState 初值只在首挂载取一次——宿主切换入口重开时(如齿轮→关→徽标),
|
|
2193
|
+
// 必须显式把 activeSection 对齐到新的 initialSection,否则停留在旧分区。
|
|
2194
|
+
React.useEffect(function () {
|
|
2195
|
+
if (props.isOpen && props.initialSection) {
|
|
2196
|
+
setActiveSection(props.initialSection);
|
|
2197
|
+
}
|
|
2198
|
+
}, [props.isOpen, props.initialSection]);
|
|
2199
|
+
// 更新源保存:POST 后端(写盘 + 热切换 checker + 立即用新源真探)。
|
|
2200
|
+
// 返回的 probe 结果如实展示:探测失败提示网络问题而非假报「已生效无异常」。
|
|
2201
|
+
var handleUpdateSave = function () {
|
|
2202
|
+
setUpdateBusy(true);
|
|
2203
|
+
setUpdateMsg(null);
|
|
2204
|
+
fetch("/api/github/update-settings", {
|
|
2205
|
+
method: "POST",
|
|
2206
|
+
headers: { "Content-Type": "application/json" },
|
|
2207
|
+
body: JSON.stringify({ mode: updateMode }),
|
|
2208
|
+
})
|
|
2209
|
+
.then(function (r) { return r.json(); })
|
|
2210
|
+
.then(function (j) {
|
|
2211
|
+
setUpdateBusy(false);
|
|
2212
|
+
if (j && j.ok && j.data) {
|
|
2213
|
+
var d = j.data;
|
|
2214
|
+
var text = "";
|
|
2215
|
+
if (d.mode === "off") {
|
|
2216
|
+
text = "✅ 已关闭自动检查更新(/gh upgrade 与一键升级仍可用),无需重启";
|
|
2217
|
+
}
|
|
2218
|
+
else if (d.probe && d.probe.latestVersion) {
|
|
2219
|
+
text = "✅ 已切换至" + d.description + ",当前最新版本 v" + d.probe.latestVersion
|
|
2220
|
+
+ (d.probe.hasUpdate ? "(有可用更新)" : "(已是最新)") + ",无需重启";
|
|
2221
|
+
}
|
|
2222
|
+
else {
|
|
2223
|
+
// 探测失败如实告知:设置已生效但新源暂不可达(可能是网络或源同步问题)
|
|
2224
|
+
text = "✅ 已切换至" + d.description + ",但新源暂时不可达(版本探测失败,稍后会自动重试);设置已生效,无需重启";
|
|
2225
|
+
}
|
|
2226
|
+
if (d.persisted === false) {
|
|
2227
|
+
text += ";⚠️ 设置写盘失败,重启后需重新选择";
|
|
2228
|
+
}
|
|
2229
|
+
setUpdateMsg({ kind: "ok", text: text });
|
|
2230
|
+
}
|
|
2231
|
+
else {
|
|
2232
|
+
setUpdateMsg({ kind: "err", text: (j && j.message) || "保存失败" });
|
|
2233
|
+
}
|
|
2234
|
+
})
|
|
2235
|
+
.catch(function (e) {
|
|
2236
|
+
setUpdateBusy(false);
|
|
2237
|
+
setUpdateMsg({ kind: "err", text: "网络请求失败: " + (e && e.message ? e.message : e) });
|
|
2238
|
+
});
|
|
2239
|
+
};
|
|
1790
2240
|
var h = React.createElement;
|
|
1791
2241
|
var handleSave = function () {
|
|
1792
2242
|
var updated = {
|
|
1793
2243
|
strategy: strategy,
|
|
1794
|
-
intervalMs: Number(intervalMs),
|
|
1795
2244
|
};
|
|
1796
2245
|
saveLocalSyncSettings(updated);
|
|
1797
2246
|
props.onSave(updated);
|
|
@@ -1804,13 +2253,88 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1804
2253
|
setClearTip("本地缓存已彻底清空!");
|
|
1805
2254
|
setTimeout(function () { setClearTip(""); }, 2000);
|
|
1806
2255
|
};
|
|
2256
|
+
// 代理保存:POST 后端后立即全局生效(gh 子进程/升级链路 env + npm 探测 dispatcher)。
|
|
2257
|
+
// 保存成功即提示「无需重启」,与「设置完成之后全局生效」的语义对齐。
|
|
2258
|
+
var handleProxySave = function () {
|
|
2259
|
+
setProxyBusy(true);
|
|
2260
|
+
setProxyMsg(null);
|
|
2261
|
+
fetch("/api/github/proxy", {
|
|
2262
|
+
method: "POST",
|
|
2263
|
+
headers: { "Content-Type": "application/json" },
|
|
2264
|
+
body: JSON.stringify({
|
|
2265
|
+
url: proxyEnabled ? proxyUrl.trim() : "",
|
|
2266
|
+
noProxy: proxyNoProxy,
|
|
2267
|
+
applyToPackageManager: proxyApplyNpm,
|
|
2268
|
+
}),
|
|
2269
|
+
})
|
|
2270
|
+
.then(function (r) { return r.json(); })
|
|
2271
|
+
.then(function (j) {
|
|
2272
|
+
setProxyBusy(false);
|
|
2273
|
+
if (j && j.ok) {
|
|
2274
|
+
setProxyMsg({
|
|
2275
|
+
kind: "ok",
|
|
2276
|
+
text: j.state && j.state.config && j.state.config.url
|
|
2277
|
+
? "🌐 代理已保存并全局生效(驾驶舱 / 右侧栏 / Agent 工具 / 升级链路),无需重启"
|
|
2278
|
+
: "已切换为系统默认网络(直连或系统环境代理),无需重启",
|
|
2279
|
+
});
|
|
2280
|
+
if (j.state && j.state.lastProbe)
|
|
2281
|
+
setProxyProbe(j.state.lastProbe);
|
|
2282
|
+
}
|
|
2283
|
+
else {
|
|
2284
|
+
setProxyMsg({ kind: "err", text: (j && j.message) || "保存失败" });
|
|
2285
|
+
}
|
|
2286
|
+
})
|
|
2287
|
+
.catch(function (e) {
|
|
2288
|
+
setProxyBusy(false);
|
|
2289
|
+
setProxyMsg({ kind: "err", text: "网络请求失败: " + (e && e.message ? e.message : e) });
|
|
2290
|
+
});
|
|
2291
|
+
};
|
|
2292
|
+
// 连通性探测:验证经当前配置访问 GitHub API 的可达性(2.5s 超时由后端控制)
|
|
2293
|
+
var handleProxyProbe = function () {
|
|
2294
|
+
setProxyBusy(true);
|
|
2295
|
+
setProxyMsg(null);
|
|
2296
|
+
fetch("/api/github/proxy/probe", { method: "POST" })
|
|
2297
|
+
.then(function (r) { return r.json(); })
|
|
2298
|
+
.then(function (j) {
|
|
2299
|
+
setProxyBusy(false);
|
|
2300
|
+
if (j && j.ok && j.probe) {
|
|
2301
|
+
setProxyProbe(j.probe);
|
|
2302
|
+
setProxyMsg(j.probe.ok
|
|
2303
|
+
? { kind: "ok", text: "✅ GitHub API 可达 (" + j.probe.latencyMs + "ms)" }
|
|
2304
|
+
: { kind: "err", text: "❌ " + (j.probe.error || "探测失败") });
|
|
2305
|
+
}
|
|
2306
|
+
else {
|
|
2307
|
+
setProxyMsg({ kind: "err", text: (j && j.message) || "探测失败" });
|
|
2308
|
+
}
|
|
2309
|
+
})
|
|
2310
|
+
.catch(function (e) {
|
|
2311
|
+
setProxyBusy(false);
|
|
2312
|
+
setProxyMsg({ kind: "err", text: "探测请求失败: " + (e && e.message ? e.message : e) });
|
|
2313
|
+
});
|
|
2314
|
+
};
|
|
1807
2315
|
return h("div", { className: "dsh-github-modal-overlay", onClick: props.onClose }, h("div", {
|
|
1808
2316
|
className: "dsh-github-modal-dialog",
|
|
1809
2317
|
onClick: function (e) { e.stopPropagation(); },
|
|
1810
|
-
},
|
|
2318
|
+
},
|
|
2319
|
+
// 弹窗头部:标题 + 关闭按钮
|
|
2320
|
+
h("div", { className: "dsh-github-modal-header" }, h("h3", { style: { margin: 0, fontSize: "16px", fontWeight: 600 } }, "⚙️ 设置"), h("button", {
|
|
1811
2321
|
style: { background: "none", border: "none", cursor: "pointer", fontSize: "18px", color: "var(--dsw-alias-label-secondary, #656d76)" },
|
|
1812
2322
|
onClick: props.onClose,
|
|
1813
|
-
}, "✕")),
|
|
2323
|
+
}, "✕")),
|
|
2324
|
+
// 双栏骨架:左侧分区导航 + 右侧内容区(窄屏自动折叠为横向页签)
|
|
2325
|
+
h("div", { className: "dsh-github-modal-body" }, h("div", { className: "dsh-github-settings-nav" }, h("button", {
|
|
2326
|
+
className: "dsh-github-settings-nav-item" + (activeSection === "sync" ? " active" : ""),
|
|
2327
|
+
onClick: function () { setActiveSection("sync"); },
|
|
2328
|
+
}, "🔄 数据同步"), h("button", {
|
|
2329
|
+
className: "dsh-github-settings-nav-item" + (activeSection === "proxy" ? " active" : ""),
|
|
2330
|
+
onClick: function () { setActiveSection("proxy"); },
|
|
2331
|
+
}, "🌐 网络代理"), h("button", {
|
|
2332
|
+
className: "dsh-github-settings-nav-item" + (activeSection === "update" ? " active" : ""),
|
|
2333
|
+
onClick: function () { setActiveSection("update"); },
|
|
2334
|
+
}, "⬆️ 更新设置"), h("button", {
|
|
2335
|
+
className: "dsh-github-settings-nav-item" + (activeSection === "about" ? " active" : ""),
|
|
2336
|
+
onClick: function () { setActiveSection("about"); },
|
|
2337
|
+
}, "ℹ️ 关于")), h("div", { className: "dsh-github-settings-content" }, activeSection === "sync" ? h("div", null, h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "14px", lineHeight: "1.5" } }, "配置 GitHub 全景驾驶舱的数据拉取节奏。启用秒开模式可利用本地持久化缓存消除每次打开时的漫长等待。"),
|
|
1814
2338
|
// 刷新策略选择
|
|
1815
2339
|
h("div", { style: { marginBottom: "16px" } }, h("div", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "8px" } }, "数据刷新策略"),
|
|
1816
2340
|
// 策略 A: SWR 秒开
|
|
@@ -1822,18 +2346,10 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1822
2346
|
h("div", {
|
|
1823
2347
|
className: "dsh-github-radio-card" + (strategy === "manual" ? " active" : ""),
|
|
1824
2348
|
onClick: function () { setStrategy("manual"); },
|
|
1825
|
-
}, h("input", { type: "radio", className: "dsh-github-radio", checked: strategy === "manual", readOnly: true }), h("div", null, h("div", { style: { fontWeight: 600, fontSize: "13px" } }, "📋 仅在手动点击时拉取 (Manual Only)"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "打开只浏览本地缓存,绝不自动触发后台 gh
|
|
1826
|
-
//
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
onClick: function () { setStrategy("interval"); },
|
|
1830
|
-
}, h("input", { type: "radio", className: "dsh-github-radio", checked: strategy === "interval", readOnly: true }), h("div", null, h("div", { style: { fontWeight: 600, fontSize: "13px" } }, "⏱️ 定时自动轮询 (Auto Poll)"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "驾驶舱打开期间,按照下方设定的时间间隔在后台自动轮询。")))),
|
|
1831
|
-
// 自动拉取间隔下拉
|
|
1832
|
-
h("div", { style: { marginBottom: "18px" } }, h("div", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "6px" } }, "后台自动拉取间隔"), h("select", {
|
|
1833
|
-
className: "dsh-github-select",
|
|
1834
|
-
value: intervalMs,
|
|
1835
|
-
onChange: function (e) { setIntervalMs(Number(e.target.value)); },
|
|
1836
|
-
}, h("option", { value: 60000 }, "1 分钟 (高频刷新)"), h("option", { value: 300000 }, "5 分钟 (默认推荐)"), h("option", { value: 900000 }, "15 分钟"), h("option", { value: 1800000 }, "30 分钟 (推荐节能)"), h("option", { value: 3600000 }, "1 小时"), h("option", { value: 0 }, "从不自动刷新 (仅手动)"))),
|
|
2349
|
+
}, h("input", { type: "radio", className: "dsh-github-radio", checked: strategy === "manual", readOnly: true }), h("div", null, h("div", { style: { fontWeight: 600, fontSize: "13px" } }, "📋 仅在手动点击时拉取 (Manual Only)"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "打开只浏览本地缓存,绝不自动触发后台 gh 进程,最省电、最省资源;想更新时点击「🔄 刷新」即可。")))
|
|
2350
|
+
// (第三种「定时自动轮询」策略已移除:前端定时器受浏览器后台节流且随
|
|
2351
|
+
// 组件卸载销毁,无法兑现定时更新承诺;SWR 挂载自愈已覆盖「打开即新鲜」)
|
|
2352
|
+
),
|
|
1837
2353
|
// 本地缓存信息与清理
|
|
1838
2354
|
h("div", {
|
|
1839
2355
|
className: "dsh-github-cache-info-box",
|
|
@@ -1843,13 +2359,126 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1843
2359
|
className: "dsh-github-btn-sm",
|
|
1844
2360
|
style: { padding: "3px 8px", fontSize: "11px", color: "var(--dsw-alias-state-error-primary, #cf222e)" },
|
|
1845
2361
|
onClick: handleClear,
|
|
1846
|
-
}, "🗑️ 清空缓存") : null), clearTip ? h("div", { style: { color: "var(--dsw-alias-state-success, #2ea043)", marginTop: "4px", fontWeight: 500 } }, clearTip) : null),
|
|
1847
|
-
//
|
|
1848
|
-
|
|
2362
|
+
}, "🗑️ 清空缓存") : null), clearTip ? h("div", { style: { color: "var(--dsw-alias-state-success, #2ea043)", marginTop: "4px", fontWeight: 500 } }, clearTip) : null)) : activeSection === "update" ? h("div", null,
|
|
2363
|
+
// ⬆️ 更新设置分区:选择版本检测的 npm 源(或关闭自动检查)。
|
|
2364
|
+
// 检测与升级流量均遵循「🌐 网络代理」分区设置——代理是全局的。
|
|
2365
|
+
h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "14px", lineHeight: "1.5" } }, "选择插件新版本检测所用的 npm 源。检测与升级流量均遵循「🌐 网络代理」分区设置:启用 http/https 代理时自动经代理访问;socks5 代理下 npm 检测为直连(gh 与升级子进程链路仍走代理)。无论选择哪个源,安装与升级流程本身不受影响。"), h("div", { style: { marginBottom: "16px" } }, h("div", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "8px" } }, "更新源"),
|
|
2366
|
+
// 源 A: npm 官方(默认)
|
|
2367
|
+
h("div", {
|
|
2368
|
+
className: "dsh-github-radio-card" + (updateMode === "official" ? " active" : ""),
|
|
2369
|
+
onClick: function () { setUpdateMode("official"); },
|
|
2370
|
+
}, h("input", { type: "radio", className: "dsh-github-radio", checked: updateMode === "official", readOnly: true }), h("div", null, h("div", { style: { fontWeight: 600, fontSize: "13px" } }, "🌍 npm 官方源 (推荐)"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "版本全球首发最及时,刚发布的新版立即可见;国内直连可能较慢。"))),
|
|
2371
|
+
// 源 B: 国内镜像
|
|
2372
|
+
h("div", {
|
|
2373
|
+
className: "dsh-github-radio-card" + (updateMode === "mirror" ? " active" : ""),
|
|
2374
|
+
onClick: function () { setUpdateMode("mirror"); },
|
|
2375
|
+
}, h("input", { type: "radio", className: "dsh-github-radio", checked: updateMode === "mirror", readOnly: true }), h("div", null, h("div", { style: { fontWeight: 600, fontSize: "13px" } }, "🇨🇳 国内镜像 (npmmirror)"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "阿里云镜像,国内访问快;同步存在分钟级延迟,刚发布的新版可能暂时查不到。"))),
|
|
2376
|
+
// 源 C: 不检查更新
|
|
2377
|
+
h("div", {
|
|
2378
|
+
className: "dsh-github-radio-card" + (updateMode === "off" ? " active" : ""),
|
|
2379
|
+
onClick: function () { setUpdateMode("off"); },
|
|
2380
|
+
}, h("input", { type: "radio", className: "dsh-github-radio", checked: updateMode === "off", readOnly: true }), h("div", null, h("div", { style: { fontWeight: 600, fontSize: "13px" } }, "⛔ 不检查更新"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "关闭自动检查与新版提示横幅;/gh upgrade 命令与一键升级仍然可用。")))),
|
|
2381
|
+
// 保存按钮 + 结果反馈(按钮复用 star-btn 的双主题链接色样式:内联色
|
|
2382
|
+
// 会覆盖 btn-sm 暗色规则,且暗色下亮蓝底刺眼——与关于页修复同源)
|
|
2383
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" } }, h("button", {
|
|
2384
|
+
className: "dsh-github-btn-sm dsh-github-primary-btn",
|
|
2385
|
+
disabled: updateBusy,
|
|
2386
|
+
onClick: handleUpdateSave,
|
|
2387
|
+
}, updateBusy ? "验证中..." : "💾 保存并全局应用"), updateMsg ? h("span", {
|
|
2388
|
+
style: {
|
|
2389
|
+
fontSize: "12px",
|
|
2390
|
+
lineHeight: "1.5",
|
|
2391
|
+
color: updateMsg.kind === "ok"
|
|
2392
|
+
? "var(--dsw-alias-state-success, #2ea043)"
|
|
2393
|
+
: updateMsg.kind === "err"
|
|
2394
|
+
? "var(--dsw-alias-state-error-primary, #cf222e)"
|
|
2395
|
+
: "var(--dsw-alias-label-secondary, #656d76)",
|
|
2396
|
+
},
|
|
2397
|
+
}, updateMsg.text) : null)) : activeSection === "about" ? h("div", null,
|
|
2398
|
+
// ℹ️ 关于分区:项目信息/仓库地址/开源协议/求 Star。纯静态展示,零状态零请求。
|
|
2399
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" } }, renderOctocatBadge(40), h("div", null, h("div", { style: { fontSize: "15px", fontWeight: 600 } }, "DSH GitHub Flow"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "当前版本: v" + (aboutVersion || "…")))), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "14px", lineHeight: "1.6" } }, "让 DeepSeek Harness Agent 拥有操作 GitHub 的原生手脚。原生复用宿主机 gh CLI 安全凭据,深度打通本地 Git 工作区与云端协作,提供 9 大领域 Agent 工具、人类 Slash 指令、Web 全景驾驶舱与一键跨平台升级。"),
|
|
2400
|
+
// 信息条目
|
|
2401
|
+
h("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "14px" } }, h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "仓库地址"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "github.com/ItQianChen/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "npm 包"), h("a", { href: "https://www.npmjs.com/package/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "npmjs.com/package/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "开源协议"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow/blob/master/LICENSE", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "MIT License"))),
|
|
2402
|
+
// 求 Star 卡片
|
|
2403
|
+
// 主题兼容:不用内联色——暗色主题的覆盖只能作用于 class 选择器;
|
|
2404
|
+
// 且此前的 canvas-subtle/border-muted 并非宿主真实令牌(全项目无他处使用),
|
|
2405
|
+
// 永远回落亮色值导致暗色下一块刺眼亮底。改用 cache-info-box 同款双主题 class。
|
|
2406
|
+
h("div", {
|
|
2407
|
+
className: "dsh-github-about-star-box",
|
|
2408
|
+
style: {
|
|
2409
|
+
display: "flex",
|
|
2410
|
+
alignItems: "center",
|
|
2411
|
+
gap: "10px",
|
|
2412
|
+
},
|
|
2413
|
+
}, h("span", { style: { fontSize: "18px" } }, "⭐"), h("div", { style: { flex: 1, fontSize: "12px", lineHeight: "1.5" } }, "如果这个插件对你的日常研发有帮助,欢迎到 GitHub 仓库点个 Star 支持一下——你的 Star 是持续迭代的动力!也欢迎提交 Issue 与 PR。"), h("a", {
|
|
2414
|
+
href: "https://github.com/ItQianChen/dsh-github-flow",
|
|
2415
|
+
target: "_blank",
|
|
2416
|
+
rel: "noopener noreferrer",
|
|
2417
|
+
className: "dsh-github-btn-sm dsh-github-primary-btn",
|
|
2418
|
+
}, "🌟 去点 Star"))) : h("div", null,
|
|
2419
|
+
// 🌐 网络代理分区:设置保存即全局生效(驾驶舱/右侧栏/Agent 工具/升级链路),
|
|
2420
|
+
// 未启用时回落系统环境代理或直连(「默认走系统设置」语义)。
|
|
2421
|
+
h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "14px", lineHeight: "1.5" } }, "设置后插件的 GitHub 数据链路(本页面、右侧栏、Agent 工具调用、版本升级)全部经代理访问;未启用时默认使用系统环境代理或直连。保存后立即生效,无需重启。"), h("label", { style: { display: "flex", alignItems: "center", gap: "8px", marginBottom: "10px", cursor: "pointer", fontSize: "13px" } }, h("input", {
|
|
2422
|
+
type: "checkbox",
|
|
2423
|
+
checked: proxyEnabled,
|
|
2424
|
+
onChange: function (e) { setProxyEnabled(Boolean(e.target.checked)); },
|
|
2425
|
+
}), "启用代理"), proxyEnabled ? h("div", null, h("div", { style: { fontSize: "12px", marginBottom: "4px" } }, "代理地址"), h("input", {
|
|
2426
|
+
className: "dsh-github-input",
|
|
2427
|
+
style: { width: "100%", boxSizing: "border-box", padding: "6px 10px", fontSize: "13px", marginBottom: "10px" },
|
|
2428
|
+
placeholder: "http://127.0.0.1:7890 或 socks5h://127.0.0.1:1080(DNS 由代理解析)",
|
|
2429
|
+
value: proxyUrl,
|
|
2430
|
+
onChange: function (e) { setProxyUrl(e.target.value); },
|
|
2431
|
+
}), h("div", { style: { fontSize: "12px", marginBottom: "4px" } }, "NO_PROXY 排除列表(逗号分隔,localhost 与 127.0.0.1 强制包含)"), h("input", {
|
|
2432
|
+
className: "dsh-github-input",
|
|
2433
|
+
style: { width: "100%", boxSizing: "border-box", padding: "6px 10px", fontSize: "12px", marginBottom: "10px" },
|
|
2434
|
+
value: proxyNoProxy,
|
|
2435
|
+
onChange: function (e) { setProxyNoProxy(e.target.value); },
|
|
2436
|
+
}), h("label", { style: { display: "flex", alignItems: "center", gap: "8px", marginBottom: "8px", cursor: "pointer", fontSize: "12px" } }, h("input", {
|
|
2437
|
+
type: "checkbox",
|
|
2438
|
+
checked: proxyApplyNpm,
|
|
2439
|
+
onChange: function (e) { setProxyApplyNpm(Boolean(e.target.checked)); },
|
|
2440
|
+
}), "同时应用于 npm 版本探测与一键升级")) : null,
|
|
2441
|
+
// 操作与反馈行:探测/保存按钮 + 结果胶囊
|
|
2442
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" } }, h("button", {
|
|
1849
2443
|
className: "dsh-github-btn-sm",
|
|
1850
|
-
style: { padding: "
|
|
1851
|
-
|
|
1852
|
-
|
|
2444
|
+
style: { padding: "5px 12px", fontSize: "12px" },
|
|
2445
|
+
disabled: proxyBusy || !proxyEnabled,
|
|
2446
|
+
onClick: handleProxyProbe,
|
|
2447
|
+
title: "经当前配置探测 GitHub API 连通性(约 2.5 秒)",
|
|
2448
|
+
}, "🧪 测试连通性"), h("button", {
|
|
2449
|
+
className: "dsh-github-btn-sm",
|
|
2450
|
+
style: {
|
|
2451
|
+
padding: "5px 14px",
|
|
2452
|
+
fontSize: "12px",
|
|
2453
|
+
background: "var(--dsw-alias-link, #0969da)",
|
|
2454
|
+
color: "#ffffff",
|
|
2455
|
+
borderColor: "var(--dsw-alias-link, #0969da)",
|
|
2456
|
+
fontWeight: 600,
|
|
2457
|
+
},
|
|
2458
|
+
disabled: proxyBusy,
|
|
2459
|
+
onClick: handleProxySave,
|
|
2460
|
+
}, proxyBusy ? "处理中..." : "💾 保存并全局应用"), proxyProbe ? h("span", {
|
|
2461
|
+
style: {
|
|
2462
|
+
fontSize: "12px",
|
|
2463
|
+
fontWeight: 500,
|
|
2464
|
+
color: proxyProbe.ok ? "var(--dsw-alias-state-success, #2ea043)" : "var(--dsw-alias-state-error-primary, #cf222e)",
|
|
2465
|
+
},
|
|
2466
|
+
title: proxyProbe.error || "",
|
|
2467
|
+
}, proxyProbe.ok ? "✅ " + proxyProbe.latencyMs + "ms" : "❌ 探测失败") : null), proxyMsg ? h("div", {
|
|
2468
|
+
style: {
|
|
2469
|
+
marginTop: "8px",
|
|
2470
|
+
fontSize: "12px",
|
|
2471
|
+
lineHeight: "1.5",
|
|
2472
|
+
color: proxyMsg.kind === "ok"
|
|
2473
|
+
? "var(--dsw-alias-state-success, #2ea043)"
|
|
2474
|
+
: proxyMsg.kind === "err"
|
|
2475
|
+
? "var(--dsw-alias-state-error-primary, #cf222e)"
|
|
2476
|
+
: "var(--dsw-alias-label-secondary, #656d76)",
|
|
2477
|
+
},
|
|
2478
|
+
}, proxyMsg.text) : null))),
|
|
2479
|
+
// 底部操作条:sync 分区提供主保存动作;proxy/update 分区有自己的「保存并全局应用」,
|
|
2480
|
+
// 底部统一为「完成」关闭弹窗,避免多个保存按钮的语义冲突。
|
|
2481
|
+
h("div", { className: "dsh-github-modal-footer" }, activeSection === "sync" ? h("button", {
|
|
1853
2482
|
className: "dsh-github-btn-sm",
|
|
1854
2483
|
style: {
|
|
1855
2484
|
padding: "6px 16px",
|
|
@@ -1859,7 +2488,11 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1859
2488
|
fontWeight: 600,
|
|
1860
2489
|
},
|
|
1861
2490
|
onClick: handleSave,
|
|
1862
|
-
}, "
|
|
2491
|
+
}, "保存同步设置") : null, h("button", {
|
|
2492
|
+
className: "dsh-github-btn-sm",
|
|
2493
|
+
style: { padding: "6px 14px" },
|
|
2494
|
+
onClick: props.onClose,
|
|
2495
|
+
}, activeSection === "sync" ? "取消" : "完成"))));
|
|
1863
2496
|
}
|
|
1864
2497
|
function GithubWorkspacePanel() {
|
|
1865
2498
|
// 1. 读取本地偏好配置与历史持久化快照 (0ms 纯内存读取)
|
|
@@ -1870,16 +2503,35 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1870
2503
|
var [loading, setLoading] = React.useState(!initialSnapshot);
|
|
1871
2504
|
var [syncStatus, setSyncStatus] = React.useState(initialSnapshot ? "cached" : "loading");
|
|
1872
2505
|
var [lastSyncedAt, setLastSyncedAt] = React.useState(initialSnapshot?.savedAt || 0);
|
|
2506
|
+
// 弹窗开闭 + 初始分区分离:插件徽标点击直开「关于」分区(initialSection=about),
|
|
2507
|
+
// 而齿轮按钮仍默认打开「数据同步」——同一弹窗两种入口语义。
|
|
1873
2508
|
var [isSettingsModalOpen, setIsSettingsModalOpen] = React.useState(false);
|
|
2509
|
+
var [settingsInitialSection, setSettingsInitialSection] = React.useState("sync");
|
|
1874
2510
|
var [error, setError] = React.useState(null);
|
|
1875
2511
|
var [updateInfo, setUpdateInfo] = React.useState(null);
|
|
1876
2512
|
var [activeTab, setActiveTab] = React.useState("workspaces"); // 'workspaces' | 'repos' | 'mywork'
|
|
1877
2513
|
var [searchQuery, setSearchQuery] = React.useState("");
|
|
1878
2514
|
var [issueFilter, setIssueFilter] = React.useState("all"); // 'all' | 'reported' | 'assigned' | 'created'
|
|
1879
|
-
//
|
|
2515
|
+
// 代理生效状态(标题栏 🌐 徽章数据源):挂载时读一次 + 弹窗关闭时刷新
|
|
2516
|
+
//(保存发生在弹窗内,关闭时机足以让徽章与最新配置对齐,无需轮询)
|
|
2517
|
+
var [proxyActive, setProxyActive] = React.useState(null);
|
|
2518
|
+
var refreshProxyBadge = React.useCallback(function () {
|
|
2519
|
+
fetch("/api/github/proxy")
|
|
2520
|
+
.then(function (r) { return r.json(); })
|
|
2521
|
+
.then(function (j) {
|
|
2522
|
+
if (j && j.ok && j.state && j.state.config) {
|
|
2523
|
+
setProxyActive({ enabled: Boolean(j.state.config.url), url: j.state.config.url || "" });
|
|
2524
|
+
}
|
|
2525
|
+
else {
|
|
2526
|
+
setProxyActive({ enabled: false, url: "" });
|
|
2527
|
+
}
|
|
2528
|
+
})
|
|
2529
|
+
.catch(function () { });
|
|
2530
|
+
}, []);
|
|
2531
|
+
React.useEffect(function () { refreshProxyBadge(); }, [refreshProxyBadge]);
|
|
2532
|
+
// 检查更新与 gh 业务数据零依赖,挂载即并行发起(与右侧栏同策略):
|
|
2533
|
+
// 并发由服务端 inFlightPromise + 24h 缓存合流去重,无请求风暴风险。
|
|
1880
2534
|
React.useEffect(() => {
|
|
1881
|
-
if (!data)
|
|
1882
|
-
return;
|
|
1883
2535
|
var timer = setTimeout(function () {
|
|
1884
2536
|
fetch("/api/github/check-update")
|
|
1885
2537
|
.then(function (res) { return res.json(); })
|
|
@@ -1889,9 +2541,31 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1889
2541
|
}
|
|
1890
2542
|
})
|
|
1891
2543
|
.catch(function () { });
|
|
1892
|
-
},
|
|
2544
|
+
}, 300);
|
|
1893
2545
|
return function () { clearTimeout(timer); };
|
|
1894
|
-
}, [
|
|
2546
|
+
}, []);
|
|
2547
|
+
// 插件徽标事件:标题上的「插件」徽标点击 → 直开设置弹窗的「关于」分区。
|
|
2548
|
+
// 可见性守卫:右侧栏与主页可能同挂载,只有自身容器可见的宿主响应(防双层弹窗)。
|
|
2549
|
+
var panelRef = React.useRef(null);
|
|
2550
|
+
React.useEffect(() => {
|
|
2551
|
+
var onOpenAbout = () => {
|
|
2552
|
+
var el = panelRef.current;
|
|
2553
|
+
if (!el)
|
|
2554
|
+
return;
|
|
2555
|
+
var rect = el.getBoundingClientRect();
|
|
2556
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
2557
|
+
setSettingsInitialSection("about");
|
|
2558
|
+
setIsSettingsModalOpen(true);
|
|
2559
|
+
}
|
|
2560
|
+
};
|
|
2561
|
+
window.addEventListener("dsh-github-open-about", onOpenAbout);
|
|
2562
|
+
return () => window.removeEventListener("dsh-github-open-about", onOpenAbout);
|
|
2563
|
+
}, []);
|
|
2564
|
+
// 齿轮按钮:默认打开「数据同步」分区(既有语义不变)
|
|
2565
|
+
var openSettingsDefault = () => {
|
|
2566
|
+
setSettingsInitialSection("sync");
|
|
2567
|
+
setIsSettingsModalOpen(true);
|
|
2568
|
+
};
|
|
1895
2569
|
var loadData = React.useCallback(async (forceRefresh = false) => {
|
|
1896
2570
|
// 如果是纯手动模式,且非显式强制刷新,且本地已有旧数据,直接跳过后台拉取
|
|
1897
2571
|
if (!forceRefresh && syncSettings.strategy === "manual" && data) {
|
|
@@ -1904,8 +2578,10 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1904
2578
|
var timedOut = false;
|
|
1905
2579
|
var timer = setTimeout(function () { timedOut = true; controller.abort(); }, 45000);
|
|
1906
2580
|
try {
|
|
2581
|
+
// refresh 预置请求必须共享同一 signal:它一旦挂起(如系统代理劫持回环请求),
|
|
2582
|
+
// 后续 overview fetch 永远执行不到,loading 永不清除——无 gh 用户正是被此卡死。
|
|
1907
2583
|
if (forceRefresh)
|
|
1908
|
-
await fetch("/api/github/refresh", { method: "POST" }).catch(() => { });
|
|
2584
|
+
await fetch("/api/github/refresh", { method: "POST", signal: controller.signal }).catch(() => { });
|
|
1909
2585
|
var res = await fetch("/api/github/global-overview", { signal: controller.signal });
|
|
1910
2586
|
if (!res.ok) {
|
|
1911
2587
|
var errText = await res.text().catch(() => "");
|
|
@@ -1948,7 +2624,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1948
2624
|
}
|
|
1949
2625
|
else {
|
|
1950
2626
|
if (timedOut || err.name === "AbortError") {
|
|
1951
|
-
setError("获取超时(
|
|
2627
|
+
setError("获取超时(45 秒):本机 gh 命令响应过慢或网络代理劫持了回环请求。可稍后重试,或在「⚙️ 设置 → 🌐 网络代理」确认代理已放行 127.0.0.1。");
|
|
1952
2628
|
}
|
|
1953
2629
|
else {
|
|
1954
2630
|
setError("无法连接后端服务:" + (err.message || err));
|
|
@@ -1960,7 +2636,9 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1960
2636
|
setLoading(false);
|
|
1961
2637
|
}
|
|
1962
2638
|
}, [data, syncSettings]);
|
|
1963
|
-
// 挂载加载调度:SWR 模式下初次延迟 300ms
|
|
2639
|
+
// 挂载加载调度:SWR 模式下初次延迟 300ms 后台平滑发起静默同步。
|
|
2640
|
+
// 无论本地是否有旧快照都拉:昨天的持久化快照在挂载后 300ms 内自愈,
|
|
2641
|
+
// 「打开驾驶舱即新鲜」由 SWR 定义本身保证,而非依赖用户停留或手动刷新。
|
|
1964
2642
|
React.useEffect(() => {
|
|
1965
2643
|
if (syncSettings.strategy === "swr") {
|
|
1966
2644
|
var timer = setTimeout(function () { loadData(false); }, 300);
|
|
@@ -1970,15 +2648,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1970
2648
|
loadData(false);
|
|
1971
2649
|
}
|
|
1972
2650
|
}, [syncSettings.strategy]);
|
|
1973
|
-
//
|
|
1974
|
-
React.useEffect(() => {
|
|
1975
|
-
if (syncSettings.strategy !== "interval" || !syncSettings.intervalMs || syncSettings.intervalMs <= 0)
|
|
1976
|
-
return;
|
|
1977
|
-
var timer = setInterval(function () {
|
|
1978
|
-
loadData(false);
|
|
1979
|
-
}, syncSettings.intervalMs);
|
|
1980
|
-
return function () { clearInterval(timer); };
|
|
1981
|
-
}, [syncSettings.strategy, syncSettings.intervalMs, loadData]);
|
|
2651
|
+
// (原定时轮询调度已随 'interval' 策略一并移除)
|
|
1982
2652
|
// 窗口切回自动探测自愈:优先轻量快速探针,检测到已登录立即自愈
|
|
1983
2653
|
React.useEffect(() => {
|
|
1984
2654
|
var onFocus = () => {
|
|
@@ -1998,18 +2668,37 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1998
2668
|
return () => window.removeEventListener("focus", onFocus);
|
|
1999
2669
|
}, [data, loadData]);
|
|
2000
2670
|
var h = React.createElement;
|
|
2001
|
-
var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(32), h("h2", null, "GitHub 全景驾驶舱"), h(SyncStatusBadge, { syncStatus, lastSyncedAt })
|
|
2671
|
+
var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(32), h("h2", null, "GitHub 全景驾驶舱"), renderPluginBadge(h), h(SyncStatusBadge, { syncStatus, lastSyncedAt }),
|
|
2672
|
+
// 代理生效徽章:启用时可见,hover 展示代理地址,让用户随时确认「当前确实在走代理」
|
|
2673
|
+
proxyActive && proxyActive.enabled
|
|
2674
|
+
? h("span", {
|
|
2675
|
+
style: {
|
|
2676
|
+
display: "inline-flex",
|
|
2677
|
+
alignItems: "center",
|
|
2678
|
+
gap: "4px",
|
|
2679
|
+
fontSize: "11px",
|
|
2680
|
+
fontWeight: 500,
|
|
2681
|
+
padding: "2px 8px",
|
|
2682
|
+
borderRadius: "999px",
|
|
2683
|
+
background: "rgba(9, 105, 218, 0.1)",
|
|
2684
|
+
border: "1px solid rgba(9, 105, 218, 0.35)",
|
|
2685
|
+
color: "var(--dsw-alias-link, #0969da)",
|
|
2686
|
+
cursor: "default",
|
|
2687
|
+
},
|
|
2688
|
+
title: "插件网络代理已启用: " + proxyActive.url + "(GitHub 数据链路与工具调用经此代理;点击 ⚙️ 可修改)",
|
|
2689
|
+
}, "🌐 代理")
|
|
2690
|
+
: null), h("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, h("button", {
|
|
2002
2691
|
className: "dsh-github-refresh-btn",
|
|
2003
2692
|
onClick: () => loadData(true),
|
|
2004
2693
|
disabled: loading,
|
|
2005
2694
|
}, loading ? "同步中..." : "🔄 刷新全局数据"), h("button", {
|
|
2006
2695
|
className: "dsh-github-btn-sm",
|
|
2007
|
-
onClick:
|
|
2008
|
-
title: "
|
|
2696
|
+
onClick: openSettingsDefault,
|
|
2697
|
+
title: "配置数据同步节奏、本地缓存、网络代理与插件信息",
|
|
2009
2698
|
style: { padding: "6px 12px" },
|
|
2010
|
-
}, "⚙️
|
|
2699
|
+
}, "⚙️ 设置")));
|
|
2011
2700
|
if (loading && !data) {
|
|
2012
|
-
return h("div", { className: "dsh-github-panel" }, headerEl, h("div", { className: "dsh-github-empty" }, "正在加载全局 GitHub 账号与工作区数据..."));
|
|
2701
|
+
return h("div", { className: "dsh-github-panel", ref: panelRef }, headerEl, h("div", { className: "dsh-github-empty" }, "正在加载全局 GitHub 账号与工作区数据..."));
|
|
2013
2702
|
}
|
|
2014
2703
|
var effectiveUpdateInfo = updateInfo || data?.updateInfo;
|
|
2015
2704
|
var upgradeBannerEl = effectiveUpdateInfo && effectiveUpdateInfo.hasUpdate
|
|
@@ -2017,7 +2706,12 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2017
2706
|
: null;
|
|
2018
2707
|
var settingsModalEl = h(GlobalSyncSettingsModal, {
|
|
2019
2708
|
isOpen: isSettingsModalOpen,
|
|
2020
|
-
|
|
2709
|
+
initialSection: settingsInitialSection,
|
|
2710
|
+
onClose: () => {
|
|
2711
|
+
setIsSettingsModalOpen(false);
|
|
2712
|
+
// 弹窗内可能刚保存过代理配置,关闭时同步刷新标题栏徽章
|
|
2713
|
+
refreshProxyBadge();
|
|
2714
|
+
},
|
|
2021
2715
|
currentSettings: syncSettings,
|
|
2022
2716
|
onSave: (newSettings) => {
|
|
2023
2717
|
setSyncSettings(newSettings);
|
|
@@ -2030,7 +2724,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2030
2724
|
});
|
|
2031
2725
|
// 错误拦截阻断:若无本地数据且请求失败,直接呈现错误卡片,杜绝把接口异常伪装成「未安装 CLI」
|
|
2032
2726
|
if (!data && error) {
|
|
2033
|
-
return h("div", { className: "dsh-github-panel" }, headerEl, upgradeBannerEl, h("div", {
|
|
2727
|
+
return h("div", { className: "dsh-github-panel", ref: panelRef }, headerEl, upgradeBannerEl, h("div", {
|
|
2034
2728
|
style: {
|
|
2035
2729
|
padding: "24px",
|
|
2036
2730
|
borderRadius: "8px",
|
|
@@ -2140,7 +2834,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2140
2834
|
})));
|
|
2141
2835
|
}
|
|
2142
2836
|
}
|
|
2143
|
-
return h("div", { className: "dsh-github-panel" }, headerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl);
|
|
2837
|
+
return h("div", { className: "dsh-github-panel", ref: panelRef }, headerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl);
|
|
2144
2838
|
}
|
|
2145
2839
|
var inject = ["slots", "sidebarRightTabs", "sessions", "layout", "theme"];
|
|
2146
2840
|
function apply(ctx) {
|
|
@@ -2172,6 +2866,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2172
2866
|
id: "dsh-github-flow",
|
|
2173
2867
|
kind: "github",
|
|
2174
2868
|
priority: "plugin",
|
|
2869
|
+
// 标题保持简洁,身份由面板内徽标标注(Tab 体系不支持内嵌徽标元素)
|
|
2175
2870
|
title: () => "GitHub 仓库",
|
|
2176
2871
|
guide: [
|
|
2177
2872
|
{
|
|
@@ -2179,7 +2874,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2179
2874
|
commandId: "workspace.github",
|
|
2180
2875
|
order: 40,
|
|
2181
2876
|
title: () => "GitHub 仓库",
|
|
2182
|
-
description: () => "查看与操作当前工作区目录下的 GitHub
|
|
2877
|
+
description: () => "查看与操作当前工作区目录下的 GitHub 仓库(由 dsh-github-flow 插件提供)",
|
|
2183
2878
|
icon: GithubArtwork,
|
|
2184
2879
|
},
|
|
2185
2880
|
],
|