dsh-github-flow 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -250,6 +250,16 @@ body[data-ds-dark-theme] .dsh-github-card-subtext {
250
250
  color: #9a6700;
251
251
  border: 1px solid rgba(154, 103, 0, 0.2);
252
252
  }
253
+ .dsh-badge-blue {
254
+ background: rgba(9, 105, 218, 0.12);
255
+ color: #0969da;
256
+ border: 1px solid rgba(9, 105, 218, 0.2);
257
+ }
258
+ .dsh-badge-purple {
259
+ background: rgba(137, 87, 229, 0.12);
260
+ color: #8250df;
261
+ border: 1px solid rgba(137, 87, 229, 0.2);
262
+ }
253
263
  body[data-ds-dark-theme] .dsh-badge-green {
254
264
  background: rgba(46, 160, 67, 0.22);
255
265
  color: #3fb950;
@@ -265,6 +275,63 @@ body[data-ds-dark-theme] .dsh-badge-yellow {
265
275
  color: #d29922;
266
276
  border-color: rgba(210, 153, 34, 0.3);
267
277
  }
278
+ body[data-ds-dark-theme] .dsh-badge-blue {
279
+ background: rgba(56, 139, 253, 0.22);
280
+ color: #58a6ff;
281
+ border-color: rgba(56, 139, 253, 0.3);
282
+ }
283
+ body[data-ds-dark-theme] .dsh-badge-purple {
284
+ background: rgba(163, 113, 247, 0.22);
285
+ color: #bc8cff;
286
+ border-color: rgba(163, 113, 247, 0.3);
287
+ }
288
+
289
+ /* 7.5. 分类筛选药丸栏 */
290
+ .dsh-github-pills {
291
+ display: flex;
292
+ align-items: center;
293
+ gap: 6px;
294
+ flex-wrap: wrap;
295
+ margin-bottom: 12px;
296
+ }
297
+ .dsh-github-pill-btn {
298
+ display: inline-flex;
299
+ align-items: center;
300
+ gap: 4px;
301
+ padding: 4px 10px;
302
+ border-radius: 14px;
303
+ font-size: 11px;
304
+ font-weight: 500;
305
+ border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
306
+ background: var(--dsw-alias-bg-module-platform, #f6f8fa);
307
+ color: var(--dsw-alias-label-secondary, #656d76);
308
+ cursor: pointer;
309
+ transition: all 0.15s ease;
310
+ }
311
+ .dsh-github-pill-btn:hover {
312
+ color: var(--dsw-alias-label-primary, #1f2328);
313
+ border-color: var(--dsw-alias-border-l3, #b0b8c1);
314
+ }
315
+ .dsh-github-pill-btn.active {
316
+ background: var(--dsw-alias-bg-module-platform, rgba(9, 105, 218, 0.1));
317
+ color: var(--dsw-alias-link, #0969da);
318
+ border-color: var(--dsw-alias-link, #0969da);
319
+ font-weight: 600;
320
+ }
321
+ body[data-ds-dark-theme] .dsh-github-pill-btn {
322
+ background: var(--dsw-alias-bg-layer-2, #21262d);
323
+ color: var(--dsw-alias-label-secondary, #8b949e);
324
+ border-color: var(--dsw-alias-border-l1, #30363d);
325
+ }
326
+ body[data-ds-dark-theme] .dsh-github-pill-btn:hover {
327
+ color: var(--dsw-alias-label-primary, #f0f6fc);
328
+ border-color: #8b949e;
329
+ }
330
+ body[data-ds-dark-theme] .dsh-github-pill-btn.active {
331
+ background: rgba(56, 139, 253, 0.18);
332
+ color: #58a6ff;
333
+ border-color: #58a6ff;
334
+ }
268
335
 
269
336
  /* 8. 快捷操作网格 */
270
337
  .dsh-github-quick-actions {
@@ -737,6 +804,131 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
737
804
  margin-top: 10px;
738
805
  line-height: 1.4;
739
806
  }
807
+
808
+ /* 16. 数据同步设置弹窗 (Sync Settings Modal) */
809
+ .dsh-github-modal-overlay {
810
+ position: fixed;
811
+ top: 0;
812
+ left: 0;
813
+ right: 0;
814
+ bottom: 0;
815
+ background: rgba(0, 0, 0, 0.45);
816
+ display: flex;
817
+ align-items: center;
818
+ justify-content: center;
819
+ z-index: 99999;
820
+ backdrop-filter: blur(2px);
821
+ }
822
+ .dsh-github-modal-dialog {
823
+ width: 90%;
824
+ max-width: 480px;
825
+ background: var(--dsw-alias-bg-base, #ffffff);
826
+ border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
827
+ border-radius: 12px;
828
+ box-shadow: 0 16px 32px rgba(0, 0, 0, 0.18);
829
+ padding: 24px;
830
+ box-sizing: border-box;
831
+ color: var(--dsw-alias-label-primary, #1f2328);
832
+ font-family: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
833
+ }
834
+ body[data-ds-dark-theme] .dsh-github-modal-dialog {
835
+ background: var(--dsw-alias-bg-layer-2, #1e1e22);
836
+ border-color: var(--dsw-alias-border-l1, #30363d);
837
+ box-shadow: 0 16px 36px rgba(0, 0, 0, 0.45);
838
+ }
839
+ .dsh-github-radio-card {
840
+ display: flex;
841
+ align-items: flex-start;
842
+ gap: 10px;
843
+ padding: 10px 12px;
844
+ border-radius: 8px;
845
+ border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
846
+ margin-bottom: 8px;
847
+ cursor: pointer;
848
+ transition: all 0.15s ease;
849
+ background: var(--dsw-alias-bg-layer-1, #f6f8fa);
850
+ }
851
+ .dsh-github-radio-card:hover {
852
+ border-color: var(--dsw-alias-link, #0969da);
853
+ }
854
+ .dsh-github-radio-card.active {
855
+ border-color: var(--dsw-alias-link, #0969da);
856
+ background: rgba(9, 105, 218, 0.08);
857
+ }
858
+ body[data-ds-dark-theme] .dsh-github-radio-card {
859
+ background: var(--dsw-alias-bg-base, #161b22);
860
+ border-color: var(--dsw-alias-border-l1, #30363d);
861
+ }
862
+ body[data-ds-dark-theme] .dsh-github-radio-card.active {
863
+ border-color: #58a6ff;
864
+ background: rgba(88, 166, 255, 0.12);
865
+ }
866
+
867
+ /* 下拉菜单及选项在深色/浅色模式下的精准适配 */
868
+ .dsh-github-select {
869
+ width: 100%;
870
+ padding: 8px 12px;
871
+ border-radius: 6px;
872
+ border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
873
+ background: var(--dsw-alias-bg-base, #ffffff);
874
+ color: var(--dsw-alias-label-primary, #1f2328);
875
+ font-size: 13px;
876
+ outline: none;
877
+ cursor: pointer;
878
+ box-sizing: border-box;
879
+ color-scheme: light;
880
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
881
+ }
882
+ .dsh-github-select:focus {
883
+ border-color: var(--dsw-alias-link, #0969da);
884
+ box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.15);
885
+ }
886
+ .dsh-github-select option {
887
+ background-color: #ffffff;
888
+ color: #1f2328;
889
+ padding: 6px 10px;
890
+ }
891
+ body[data-ds-dark-theme] .dsh-github-select {
892
+ border-color: var(--dsw-alias-border-l1, #30363d);
893
+ background: var(--dsw-alias-bg-layer-2, #1e1e22);
894
+ color: var(--dsw-alias-label-primary, #f0f6fc);
895
+ color-scheme: dark;
896
+ }
897
+ body[data-ds-dark-theme] .dsh-github-select:focus {
898
+ border-color: #58a6ff;
899
+ box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.2);
900
+ }
901
+ body[data-ds-dark-theme] .dsh-github-select option {
902
+ background-color: #1e1e22;
903
+ color: #f0f6fc;
904
+ }
905
+
906
+ /* 缓存信息展示框 */
907
+ .dsh-github-cache-info-box {
908
+ padding: 10px 12px;
909
+ border-radius: 6px;
910
+ background: var(--dsw-alias-bg-layer-1, #f6f8fa);
911
+ border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
912
+ margin-bottom: 20px;
913
+ font-size: 12px;
914
+ color: var(--dsw-alias-label-secondary, #656d76);
915
+ transition: all 0.15s ease;
916
+ }
917
+ body[data-ds-dark-theme] .dsh-github-cache-info-box {
918
+ background: var(--dsw-alias-bg-base, #161b22);
919
+ border-color: var(--dsw-alias-border-l1, #30363d);
920
+ color: var(--dsw-alias-label-secondary, #8c8c8c);
921
+ }
922
+
923
+ /* 单选框点强调色 */
924
+ .dsh-github-radio {
925
+ accent-color: var(--dsw-alias-link, #0969da);
926
+ cursor: pointer;
927
+ margin-top: 3px;
928
+ }
929
+ body[data-ds-dark-theme] .dsh-github-radio {
930
+ accent-color: #58a6ff;
931
+ }
740
932
  `;
741
933
  function injectStyles() {
742
934
  var styleId = "dsh-plugin-github-styles";
@@ -882,7 +1074,25 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
882
1074
  }, "🌐 官网安装包 (.msi/.pkg)")
883
1075
  : null, h("button", {
884
1076
  className: "dsh-github-action-btn",
885
- onClick: onRefresh,
1077
+ onClick: function () {
1078
+ setToastMsg("正在快速检测登录状态...");
1079
+ fetch("/api/github/auth?force=true")
1080
+ .then(function (res) { return res.json(); })
1081
+ .then(function (json) {
1082
+ if (json && json.ok && json.auth && json.auth.loggedIn) {
1083
+ setToastMsg("✅ 已识别到 GitHub CLI 登录态,正在拉取数据...");
1084
+ clearStoredGlobalOverview();
1085
+ onRefresh();
1086
+ }
1087
+ else {
1088
+ setToastMsg("⚠️ 暂未检测到登录凭据,请在终端执行 gh auth login 完成授权");
1089
+ onRefresh();
1090
+ }
1091
+ })
1092
+ .catch(function () {
1093
+ onRefresh();
1094
+ });
1095
+ },
886
1096
  title: "重新检测登录与安装状态"
887
1097
  }, "🔄 刷新状态")), toastMsg
888
1098
  ? h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-link, #0969da)", marginTop: "8px" } }, `ℹ️ ${toastMsg}`)
@@ -890,13 +1100,254 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
890
1100
  ? "💡 提示:使用包管理器安装完成后,若未立即识别,可重启 DeepSeek Harness 应用刷新系统环境变量 PATH。"
891
1101
  : "💡 提示:凭证由官方 GitHub CLI 加密保存在操作系统安全凭据库中,授权完毕切回本窗口将自动识别刷新。"));
892
1102
  }
1103
+ // 通用升级通知与一键升级卡片组件(自适应右侧栏紧凑模式与全景大面板模式)
1104
+ function UpgradeNotificationCard(props) {
1105
+ var updateInfo = props.updateInfo;
1106
+ var isRightbar = Boolean(props.isRightbar);
1107
+ if (!updateInfo || !updateInfo.hasUpdate)
1108
+ return null;
1109
+ var storageKey = "dsh_dismissed_update_" + (updateInfo.latestVersion || "latest");
1110
+ var [dismissed, setDismissed] = React.useState(function () {
1111
+ try {
1112
+ return localStorage.getItem(storageKey) === "true";
1113
+ }
1114
+ catch (e) {
1115
+ return false;
1116
+ }
1117
+ });
1118
+ var [status, setStatus] = React.useState("idle"); // 'idle' | 'upgrading' | 'success' | 'failed'
1119
+ var [msg, setMsg] = React.useState("");
1120
+ // 窗口切回或切 Tab 重挂载时,主动对齐后端实际的升级状态,防止状态丢失
1121
+ React.useEffect(function () {
1122
+ fetch("/api/github/upgrade-status")
1123
+ .then(function (res) { return res.json(); })
1124
+ .then(function (json) {
1125
+ if (json && json.ok && json.data) {
1126
+ if (json.data.state === "upgrading") {
1127
+ setStatus("upgrading");
1128
+ setMsg("正在拉取最新版本(约需 5~15 秒)...");
1129
+ }
1130
+ else if (json.data.state === "success") {
1131
+ setStatus("success");
1132
+ setMsg(json.data.message || "升级成功!已安装最新依赖。");
1133
+ }
1134
+ }
1135
+ })
1136
+ .catch(function () { });
1137
+ }, []);
1138
+ if (dismissed)
1139
+ return null;
1140
+ var handleUpgrade = async function () {
1141
+ if (status === "upgrading")
1142
+ return;
1143
+ setStatus("upgrading");
1144
+ setMsg("正在拉取最新版本(约需 5~15 秒)...");
1145
+ try {
1146
+ var res = await fetch("/api/github/upgrade", { method: "POST" });
1147
+ var json = await res.json().catch(function () { return {}; });
1148
+ if (res.ok && json.ok) {
1149
+ setStatus("success");
1150
+ setMsg(json.message || "升级成功!已安装最新依赖。");
1151
+ }
1152
+ else {
1153
+ setStatus("failed");
1154
+ setMsg(json.message || "升级命令执行失败,请在宿主终端手动执行。");
1155
+ }
1156
+ }
1157
+ catch (err) {
1158
+ setStatus("failed");
1159
+ setMsg("无法连接后端执行升级: " + (err.message || err));
1160
+ }
1161
+ };
1162
+ var handleDismiss = function () {
1163
+ try {
1164
+ localStorage.setItem(storageKey, "true");
1165
+ }
1166
+ catch (e) { }
1167
+ setDismissed(true);
1168
+ };
1169
+ var handleCopy = function () {
1170
+ var cmd = updateInfo.upgradeCommand || "pnpm dsh plugin add dsh-github-flow@latest";
1171
+ if (navigator.clipboard) {
1172
+ navigator.clipboard.writeText(cmd);
1173
+ alert("已复制升级命令到剪贴板:\n" + cmd);
1174
+ }
1175
+ else {
1176
+ prompt("请复制升级命令:", cmd);
1177
+ }
1178
+ };
1179
+ var handleReload = function () {
1180
+ window.location.reload();
1181
+ };
1182
+ var h = React.createElement;
1183
+ // 1. 右侧栏紧凑型模式
1184
+ if (isRightbar) {
1185
+ return h("div", {
1186
+ className: "dsh-github-upgrade-strip",
1187
+ style: {
1188
+ padding: "10px 12px",
1189
+ marginBottom: "12px",
1190
+ borderRadius: "8px",
1191
+ background: status === "success"
1192
+ ? "var(--dsw-alias-state-success-subtle, rgba(46, 160, 67, 0.12))"
1193
+ : "var(--dsw-alias-bg-module-platform, rgba(9, 105, 218, 0.08))",
1194
+ border: "1px solid " + (status === "success"
1195
+ ? "var(--dsw-alias-state-success-border, rgba(46, 160, 67, 0.35))"
1196
+ : "var(--dsw-alias-border-l2, rgba(9, 105, 218, 0.25))"),
1197
+ fontSize: "12px",
1198
+ },
1199
+ }, h("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "6px" } }, h("span", { style: { fontWeight: 600, color: "var(--dsw-alias-label-primary, #1f2328)" } }, status === "success" ? "🎉 升级完成" : "🚀 发现新版 v" + updateInfo.latestVersion), h("button", {
1200
+ onClick: handleDismiss,
1201
+ title: "不再提醒此版本",
1202
+ style: { background: "none", border: "none", cursor: "pointer", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", fontSize: "14px", padding: 0 },
1203
+ }, "✕")), h("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "8px", lineHeight: "1.4" } }, msg || (status === "success"
1204
+ ? "需重启 DSH 服务彻底生效后端工具,或刷新网页预览新界面。"
1205
+ : "当前: v" + updateInfo.currentVersion + ",推荐升级以获取最新功能。")), h("div", { style: { display: "flex", gap: "6px", flexWrap: "wrap" } }, status === "success"
1206
+ ? h("button", {
1207
+ className: "dsh-github-btn-sm",
1208
+ onClick: handleReload,
1209
+ style: { flex: 1, justifyContent: "center", background: "#2ea043", color: "#fff", borderColor: "#2ea043" },
1210
+ }, "🔄 刷新页面")
1211
+ : h("button", {
1212
+ className: "dsh-github-btn-sm",
1213
+ disabled: status === "upgrading",
1214
+ onClick: handleUpgrade,
1215
+ style: {
1216
+ flex: 1,
1217
+ justifyContent: "center",
1218
+ background: "var(--dsw-alias-link, #0969da)",
1219
+ color: "#fff",
1220
+ borderColor: "var(--dsw-alias-link, #0969da)",
1221
+ opacity: status === "upgrading" ? 0.7 : 1,
1222
+ },
1223
+ }, status === "upgrading" ? "升级中..." : "⚡ 一键升级"), h("button", {
1224
+ className: "dsh-github-btn-sm",
1225
+ onClick: handleCopy,
1226
+ style: { fontSize: "11px", padding: "4px 8px" },
1227
+ }, "📋 复制命令")));
1228
+ }
1229
+ // 2. 全景驾驶舱大面板横幅模式
1230
+ return h("div", {
1231
+ className: "dsh-github-upgrade-banner",
1232
+ style: {
1233
+ display: "flex",
1234
+ alignItems: "center",
1235
+ justifyContent: "space-between",
1236
+ padding: "12px 18px",
1237
+ marginBottom: "16px",
1238
+ borderRadius: "8px",
1239
+ background: status === "success"
1240
+ ? "linear-gradient(90deg, rgba(46, 160, 67, 0.15) 0%, rgba(9, 105, 218, 0.12) 100%)"
1241
+ : "linear-gradient(90deg, rgba(9, 105, 218, 0.12) 0%, rgba(137, 87, 229, 0.12) 100%)",
1242
+ border: "1px solid " + (status === "success" ? "rgba(46, 160, 67, 0.35)" : "rgba(9, 105, 218, 0.3)"),
1243
+ fontSize: "13px",
1244
+ },
1245
+ }, h("div", { style: { display: "flex", alignItems: "center", gap: "10px" } }, h("span", { style: { fontSize: "20px" } }, status === "success" ? "🎉" : "🚀"), h("div", null, h("div", { style: { fontWeight: 600, color: "var(--dsw-alias-label-primary, #1f2328)" } }, status === "success"
1246
+ ? "升级完成!新版本 v" + updateInfo.latestVersion + " 已写入"
1247
+ : "发现 dsh-github-flow 新版本 v" + updateInfo.latestVersion + "(当前版本: v" + updateInfo.currentVersion + ")"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, msg || (status === "success"
1248
+ ? "💡 提示:DSH 后端驻留了旧版内存,需重启 DSH 服务彻底生效后端工具;若仅预览界面改动可先刷新网页。"
1249
+ : "包含最新 GitHub 工具特性与性能优化,点击右侧可一键自动升级。")))), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 } }, status === "success"
1250
+ ? h("button", {
1251
+ className: "dsh-github-btn-sm",
1252
+ onClick: handleReload,
1253
+ style: { background: "#2ea043", color: "#fff", borderColor: "#2ea043", padding: "6px 14px", fontWeight: 600 },
1254
+ }, "🔄 立即刷新页面")
1255
+ : h("button", {
1256
+ className: "dsh-github-btn-sm",
1257
+ disabled: status === "upgrading",
1258
+ onClick: handleUpgrade,
1259
+ style: {
1260
+ background: "var(--dsw-alias-link, #0969da)",
1261
+ color: "#fff",
1262
+ borderColor: "var(--dsw-alias-link, #0969da)",
1263
+ padding: "6px 14px",
1264
+ fontWeight: 600,
1265
+ opacity: status === "upgrading" ? 0.7 : 1,
1266
+ },
1267
+ }, status === "upgrading" ? "正在自动升级中..." : "⚡ 一键自动升级"), h("button", {
1268
+ className: "dsh-github-btn-sm",
1269
+ onClick: handleCopy,
1270
+ style: { padding: "6px 12px" },
1271
+ }, "📋 复制命令"), h("button", {
1272
+ onClick: handleDismiss,
1273
+ title: "不再提醒此版本",
1274
+ style: { background: "none", border: "none", cursor: "pointer", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", fontSize: "16px", padding: "0 4px" },
1275
+ }, "✕")));
1276
+ }
1277
+ // 数据新鲜度与同步状态微标组件 (自适应全景大面板与右侧栏紧凑模式)
1278
+ function SyncStatusBadge(props) {
1279
+ var syncStatus = props.syncStatus;
1280
+ var lastSyncedAt = props.lastSyncedAt;
1281
+ var isRightbar = Boolean(props.isRightbar);
1282
+ var text = "";
1283
+ var bg = "rgba(101, 109, 118, 0.1)";
1284
+ var color = "var(--dsw-alias-label-secondary, #656d76)";
1285
+ var formatTimeAgo = function (ts) {
1286
+ if (!ts)
1287
+ return "刚刚";
1288
+ var diffSec = Math.floor((Date.now() - ts) / 1000);
1289
+ if (diffSec < 60)
1290
+ return "刚刚";
1291
+ var diffMin = Math.floor(diffSec / 60);
1292
+ if (diffMin < 60)
1293
+ return diffMin + "分钟前";
1294
+ var diffHour = Math.floor(diffMin / 60);
1295
+ if (diffHour < 24)
1296
+ return diffHour + "小时前";
1297
+ var diffDay = Math.floor(diffHour / 24);
1298
+ return diffDay + "天前";
1299
+ };
1300
+ if (syncStatus === "syncing") {
1301
+ text = isRightbar ? "⏳ 同步中..." : "⏳ 后台同步中...";
1302
+ bg = "rgba(9, 105, 218, 0.12)";
1303
+ color = "var(--dsw-alias-link, #0969da)";
1304
+ }
1305
+ else if (syncStatus === "fresh") {
1306
+ var timeStr = lastSyncedAt ? new Date(lastSyncedAt).toLocaleTimeString() : "";
1307
+ text = "✅ " + (isRightbar ? "最新" : "已是最新") + (timeStr ? " (" + timeStr + ")" : "");
1308
+ bg = "rgba(46, 160, 67, 0.12)";
1309
+ color = "var(--dsw-alias-state-success, #2ea043)";
1310
+ }
1311
+ else if (syncStatus === "cached") {
1312
+ text = "📦 本地快照 (" + formatTimeAgo(lastSyncedAt) + ")";
1313
+ bg = "rgba(101, 109, 118, 0.12)";
1314
+ color = "var(--dsw-alias-label-secondary, #656d76)";
1315
+ }
1316
+ else if (syncStatus === "failed") {
1317
+ text = isRightbar ? "⚠️ 同步超时" : "⚠️ 同步超时,已保留本地缓存";
1318
+ bg = "rgba(207, 34, 46, 0.1)";
1319
+ color = "var(--dsw-alias-state-error-primary, #cf222e)";
1320
+ }
1321
+ if (!text)
1322
+ return null;
1323
+ return React.createElement("span", {
1324
+ className: "dsh-github-sync-badge",
1325
+ style: {
1326
+ fontSize: isRightbar ? "10px" : "11px",
1327
+ padding: isRightbar ? "2px 6px" : "3px 8px",
1328
+ borderRadius: "12px",
1329
+ background: bg,
1330
+ color: color,
1331
+ marginLeft: isRightbar ? "6px" : "10px",
1332
+ fontWeight: 500,
1333
+ display: "inline-flex",
1334
+ alignItems: "center",
1335
+ whiteSpace: "nowrap",
1336
+ },
1337
+ }, text);
1338
+ }
893
1339
  // 渲染 GitHub 仓库信息与操作视图的核心函数
894
- function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false) {
1340
+ function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false, updateInfo = null, syncStatus = "", lastSyncedAt = 0) {
895
1341
  var auth = data ? data.auth : null;
896
1342
  var repo = data ? data.repo : null;
897
1343
  var prs = (data && data.pullRequests) || [];
1344
+ var issues = (data && data.issues) || [];
898
1345
  var runs = (data && data.runs) || [];
899
- var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(isRightbar ? 26 : 30), h("h2", null, isRightbar ? "GitHub 仓库" : "GitHub 工作台")), h("button", {
1346
+ var effectiveUpdateInfo = updateInfo || data?.updateInfo;
1347
+ var upgradeBannerEl = effectiveUpdateInfo && effectiveUpdateInfo.hasUpdate
1348
+ ? h(UpgradeNotificationCard, { updateInfo: effectiveUpdateInfo, isRightbar })
1349
+ : 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", {
900
1351
  className: "dsh-github-refresh-btn",
901
1352
  onClick: onRefresh,
902
1353
  disabled: loading,
@@ -943,7 +1394,17 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
943
1394
  }
944
1395
  },
945
1396
  }, "🚀 快速创建 PR"),
946
- // 4. 📝 新建 Issue (官方 Issue 模板页直达)
1397
+ // 4. 查看所有 Issues (官方 Issues 列表直达)
1398
+ h("button", {
1399
+ className: "dsh-github-action-btn",
1400
+ title: "在系统默认浏览器中查看当前仓库所有 Issues",
1401
+ onClick: () => {
1402
+ if (repo?.url) {
1403
+ window.open(`${repo.url}/issues`, "_blank", "noopener,noreferrer");
1404
+ }
1405
+ },
1406
+ }, "📋 查看所有 Issues"),
1407
+ // 5. 📝 新建 Issue (官方 Issue 模板页直达)
947
1408
  h("button", {
948
1409
  className: "dsh-github-action-btn",
949
1410
  title: "在系统默认浏览器中打开 GitHub 官方新建 Issue 页面",
@@ -959,6 +1420,33 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
959
1420
  : h("div", { className: "dsh-github-list" }, prs.map((pr) => h("div", { key: pr.number, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: pr.url, target: "_blank" }, `#${pr.number} ${pr.title}`), h("div", { className: "dsh-github-item-meta" }, h("span", null, `${pr.headRefName} → ${pr.baseRefName}`), pr.mergeable === "CONFLICTING"
960
1421
  ? h("span", { className: "dsh-github-badge dsh-badge-red" }, "代码冲突")
961
1422
  : h("span", { className: "dsh-github-badge dsh-badge-green" }, "可合并"))))));
1423
+ // 仓库 Issues 列表
1424
+ var issueSectionEl = h("div", { className: "dsh-github-section" }, h("div", { className: "dsh-github-section-header" }, h("h3", null, `📋 仓库 Issues (${issues.length})`), repo?.url
1425
+ ? h("a", {
1426
+ href: `${repo.url}/issues`,
1427
+ target: "_blank",
1428
+ style: { fontSize: "11px", color: "var(--dsw-alias-link, #0969da)", textDecoration: "none" },
1429
+ }, "全部 ↗")
1430
+ : null), issues.length === 0
1431
+ ? h("div", { className: "dsh-github-empty" }, "当前无开放中的 Issue")
1432
+ : h("div", { className: "dsh-github-list" }, issues.map((issue) => {
1433
+ var cats = issue.categories || [];
1434
+ var badges = [];
1435
+ if (cats.includes("reported")) {
1436
+ badges.push(h("span", { key: "rep", className: "dsh-github-badge dsh-badge-purple" }, "用户反馈"));
1437
+ }
1438
+ if (cats.includes("assigned")) {
1439
+ badges.push(h("span", { key: "asn", className: "dsh-github-badge dsh-badge-blue" }, "已分配"));
1440
+ }
1441
+ if (cats.includes("created")) {
1442
+ badges.push(h("span", { key: "crt", className: "dsh-github-badge dsh-badge-green" }, "我发起的"));
1443
+ }
1444
+ return h("div", { key: issue.number, className: "dsh-github-item" }, h("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" } }, h("a", { className: "dsh-github-item-title", href: issue.url, target: "_blank" }, `#${issue.number} ${issue.title}`), ...badges), h("div", { className: "dsh-github-item-meta" }, h("span", null, `由 ${issue.author?.login || "未知"} 提出`), issue.assignees && issue.assignees.length > 0
1445
+ ? h("span", null, `指派: ${issue.assignees.map((a) => a.login).join(", ")}`)
1446
+ : null, issue.labels && issue.labels.length > 0
1447
+ ? issue.labels.map((lbl) => h("span", { key: lbl.name, className: "dsh-github-badge dsh-badge-yellow" }, lbl.name))
1448
+ : null));
1449
+ })));
962
1450
  // Actions 流水线
963
1451
  var runSectionEl = h("div", { className: "dsh-github-section" }, h("div", { className: "dsh-github-section-header" }, h("h3", null, `⚡ 最近 Actions 流水线 (${runs.length})`)), runs.length === 0
964
1452
  ? h("div", { className: "dsh-github-empty" }, "暂无工作流记录")
@@ -970,23 +1458,202 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
970
1458
  var authGuideEl = !auth?.loggedIn
971
1459
  ? h(AuthGuideCard, { auth, onRefresh, isRightbar })
972
1460
  : null;
973
- return [headerEl, authGuideEl, repoCardEl, actionsBarEl, prSectionEl, runSectionEl].filter(Boolean);
1461
+ return [headerEl, upgradeBannerEl, authGuideEl, repoCardEl, actionsBarEl, prSectionEl, issueSectionEl, runSectionEl].filter(Boolean);
1462
+ }
1463
+ // 全局持久化缓存与偏好配置键名
1464
+ var GLOBAL_OVERVIEW_CACHE_KEY = "dsh_github_global_overview_snapshot_v1";
1465
+ var SYNC_SETTINGS_STORAGE_KEY = "dsh_github_sync_settings_v1";
1466
+ var DEFAULT_SYNC_SETTINGS = {
1467
+ strategy: "swr", // 'swr' (秒开展示旧数据,后台静默拉取) | 'manual' (仅手动刷新) | 'interval' (定时轮询)
1468
+ intervalMs: 300000, // 默认 5 分钟 (300,000ms)
1469
+ };
1470
+ function getLocalSyncSettings() {
1471
+ try {
1472
+ var raw = localStorage.getItem(SYNC_SETTINGS_STORAGE_KEY);
1473
+ if (!raw)
1474
+ return DEFAULT_SYNC_SETTINGS;
1475
+ var parsed = JSON.parse(raw);
1476
+ return Object.assign({}, DEFAULT_SYNC_SETTINGS, parsed);
1477
+ }
1478
+ catch (e) {
1479
+ return DEFAULT_SYNC_SETTINGS;
1480
+ }
1481
+ }
1482
+ function saveLocalSyncSettings(settings) {
1483
+ try {
1484
+ localStorage.setItem(SYNC_SETTINGS_STORAGE_KEY, JSON.stringify(settings));
1485
+ // 跨组件事件通知:确保主页设置被修改后,右侧栏及所有实例即时同步新规则
1486
+ if (typeof window !== "undefined") {
1487
+ window.dispatchEvent(new CustomEvent("dsh-github-sync-settings-change", { detail: settings }));
1488
+ }
1489
+ }
1490
+ catch (e) { }
1491
+ }
1492
+ function getStoredGlobalOverview() {
1493
+ try {
1494
+ var raw = localStorage.getItem(GLOBAL_OVERVIEW_CACHE_KEY);
1495
+ if (!raw)
1496
+ return null;
1497
+ var parsed = JSON.parse(raw);
1498
+ // 毒丸缓存自愈清洗:如果缓存中的快照显示「未登录」或未就绪,立即将其从 localStorage 彻底抹除!
1499
+ if (!parsed || !parsed.data || !parsed.data.auth || !parsed.data.auth.loggedIn) {
1500
+ localStorage.removeItem(GLOBAL_OVERVIEW_CACHE_KEY);
1501
+ return null;
1502
+ }
1503
+ if (typeof parsed.savedAt === "number") {
1504
+ return parsed;
1505
+ }
1506
+ return null;
1507
+ }
1508
+ catch (e) {
1509
+ return null;
1510
+ }
1511
+ }
1512
+ function saveStoredGlobalOverview(data) {
1513
+ // 毒丸拦截守卫:绝不持久化存储未登录状态、空数据或异常快照!
1514
+ if (!data || !data.auth || !data.auth.loggedIn) {
1515
+ return;
1516
+ }
1517
+ try {
1518
+ localStorage.setItem(GLOBAL_OVERVIEW_CACHE_KEY, JSON.stringify({
1519
+ data: data,
1520
+ savedAt: Date.now(),
1521
+ }));
1522
+ }
1523
+ catch (e) { }
1524
+ }
1525
+ function clearStoredGlobalOverview() {
1526
+ try {
1527
+ localStorage.removeItem(GLOBAL_OVERVIEW_CACHE_KEY);
1528
+ // 彻底清空所有右侧栏工作区历史快照,确保全局一致
1529
+ if (typeof localStorage !== "undefined") {
1530
+ for (var i = localStorage.length - 1; i >= 0; i--) {
1531
+ var k = localStorage.key(i);
1532
+ if (k && k.indexOf("dsh_github_rightbar_snapshot_") === 0) {
1533
+ localStorage.removeItem(k);
1534
+ }
1535
+ }
1536
+ }
1537
+ if (typeof window !== "undefined") {
1538
+ window.dispatchEvent(new CustomEvent("dsh-github-cache-cleared"));
1539
+ }
1540
+ }
1541
+ catch (e) { }
1542
+ }
1543
+ function getCacheStorageMeta() {
1544
+ try {
1545
+ var raw = localStorage.getItem(GLOBAL_OVERVIEW_CACHE_KEY);
1546
+ if (!raw)
1547
+ return { exists: false, sizeKb: 0, savedAt: 0 };
1548
+ var parsed = JSON.parse(raw);
1549
+ var sizeKb = Math.round((raw.length * 2) / 1024 * 10) / 10;
1550
+ return { exists: true, sizeKb: sizeKb, savedAt: parsed?.savedAt || 0 };
1551
+ }
1552
+ catch (e) {
1553
+ return { exists: false, sizeKb: 0, savedAt: 0 };
1554
+ }
1555
+ }
1556
+ // 右侧栏专属本地持久化快照工具
1557
+ function getRightbarStorageKey(cwd) {
1558
+ var safe = (cwd || "default").replace(/[^a-zA-Z0-9_-]/g, "_");
1559
+ return "dsh_github_rightbar_snapshot_" + safe;
1560
+ }
1561
+ function getStoredRightbarSnapshot(cwd) {
1562
+ try {
1563
+ var key = getRightbarStorageKey(cwd);
1564
+ var raw = localStorage.getItem(key);
1565
+ if (!raw)
1566
+ return null;
1567
+ var parsed = JSON.parse(raw);
1568
+ // 毒丸拦截:若快照未登录,物理删除
1569
+ if (!parsed || !parsed.data || !parsed.data.auth || !parsed.data.auth.loggedIn) {
1570
+ localStorage.removeItem(key);
1571
+ return null;
1572
+ }
1573
+ return { data: parsed.data, savedAt: typeof parsed.savedAt === "number" ? parsed.savedAt : 0 };
1574
+ }
1575
+ catch (e) {
1576
+ return null;
1577
+ }
1578
+ }
1579
+ function saveStoredRightbarData(cwd, data) {
1580
+ if (!data || !data.auth || !data.auth.loggedIn)
1581
+ return;
1582
+ try {
1583
+ var key = getRightbarStorageKey(cwd);
1584
+ localStorage.setItem(key, JSON.stringify({
1585
+ data: data,
1586
+ savedAt: Date.now(),
1587
+ }));
1588
+ }
1589
+ catch (e) { }
974
1590
  }
975
1591
  // 1. 右侧栏专属视图 (Right Sidebar Tab Body)
976
1592
  function GithubRightbarBody(props) {
977
- var [loading, setLoading] = React.useState(true);
978
- var [data, setData] = React.useState(null);
979
- var [error, setError] = React.useState(null);
980
- // 核心:直接从会话 Props 拿到真实物理工作区路径!
1593
+ var sessionId = props ? props.sessionId : null;
981
1594
  var cwd = null;
982
- if (props && props.useSessions && props.sessionId) {
1595
+ if (props && typeof props.useSessions === "function" && sessionId) {
983
1596
  try {
984
- cwd = props.useSessions((sessions) => sessions?.byId?.[props.sessionId]?.cwd);
1597
+ cwd = props.useSessions((sessions) => sessions?.byId?.[sessionId]?.cwd) || null;
985
1598
  }
986
1599
  catch (e) { }
987
1600
  }
1601
+ // 核心原则:右侧栏与全景主页使用同一套在“主页-设置”中配置的缓存与刷新规则事实源
1602
+ var initialSettings = React.useMemo(() => getLocalSyncSettings(), []);
1603
+ var [syncSettings, setSyncSettings] = React.useState(initialSettings);
1604
+ // SWR 秒开机制:初始数据直接从本地快照读取,0ms 瞬间展示!
1605
+ var initialSnapshot = React.useMemo(() => getStoredRightbarSnapshot(cwd), [cwd]);
1606
+ var [data, setData] = React.useState(initialSnapshot?.data || null);
1607
+ var [syncStatus, setSyncStatus] = React.useState(initialSnapshot ? "cached" : "loading");
1608
+ var [lastSyncedAt, setLastSyncedAt] = React.useState(initialSnapshot?.savedAt || 0);
1609
+ var [loading, setLoading] = React.useState(false);
1610
+ var [error, setError] = React.useState(null);
1611
+ var [updateInfo, setUpdateInfo] = React.useState(null);
1612
+ // 跨组件事件监听:当主页修改了同步设置或清空缓存时,右侧栏即时无感对齐
1613
+ React.useEffect(function () {
1614
+ var onSettingsChange = function (e) {
1615
+ if (e && e.detail) {
1616
+ setSyncSettings(e.detail);
1617
+ }
1618
+ };
1619
+ var onCacheCleared = function () {
1620
+ setData(null);
1621
+ setLastSyncedAt(0);
1622
+ setSyncStatus("loading");
1623
+ };
1624
+ window.addEventListener("dsh-github-sync-settings-change", onSettingsChange);
1625
+ window.addEventListener("dsh-github-cache-cleared", onCacheCleared);
1626
+ return function () {
1627
+ window.removeEventListener("dsh-github-sync-settings-change", onSettingsChange);
1628
+ window.removeEventListener("dsh-github-cache-cleared", onCacheCleared);
1629
+ };
1630
+ }, []);
1631
+ // gh 数据拉取完毕后,闲时 1.5 秒异步检测升级,绝对不抢占主业务首屏资源
1632
+ React.useEffect(() => {
1633
+ if (!data)
1634
+ return;
1635
+ var timer = setTimeout(function () {
1636
+ fetch("/api/github/check-update")
1637
+ .then(function (res) { return res.json(); })
1638
+ .then(function (json) {
1639
+ if (json && json.ok && json.data) {
1640
+ setUpdateInfo(json.data);
1641
+ }
1642
+ })
1643
+ .catch(function () { });
1644
+ }, 1500);
1645
+ return function () { clearTimeout(timer); };
1646
+ }, [data]);
1647
+ // 遵循全局规则的数据拉取核心函数 (仅依赖标量 cwd 与 sessionId,坚决去除 props 引用依赖,彻底终结死循环)
988
1648
  var loadData = React.useCallback(async (forceRefresh = false) => {
989
- setLoading(true);
1649
+ // 规则执行:若主页设置为「纯手动模式 (manual)」,且非用户点击强制刷新,且本地已有数据,坚决不打后台网络!
1650
+ if (!forceRefresh && syncSettings.strategy === "manual" && data) {
1651
+ return;
1652
+ }
1653
+ if (forceRefresh || !data) {
1654
+ setLoading(true);
1655
+ }
1656
+ setSyncStatus(data ? "syncing" : "loading");
990
1657
  setError(null);
991
1658
  try {
992
1659
  if (forceRefresh) {
@@ -994,8 +1661,8 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
994
1661
  }
995
1662
  var url = "/api/github/overview";
996
1663
  var params = [];
997
- if (props && props.sessionId) {
998
- params.push("sessionId=" + encodeURIComponent(props.sessionId));
1664
+ if (sessionId) {
1665
+ params.push("sessionId=" + encodeURIComponent(sessionId));
999
1666
  }
1000
1667
  if (cwd) {
1001
1668
  params.push("cwd=" + encodeURIComponent(cwd));
@@ -1006,39 +1673,82 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
1006
1673
  var res = await fetch(url);
1007
1674
  if (!res.ok) {
1008
1675
  var errText = await res.text().catch(() => "");
1009
- setError(`后端服务未就绪 (${res.status}): ${errText || res.statusText}`);
1676
+ var msg = `后端服务未就绪 (${res.status}): ${errText || res.statusText}`;
1677
+ if (data) {
1678
+ setSyncStatus("failed");
1679
+ }
1680
+ else {
1681
+ setError(msg);
1682
+ }
1010
1683
  return;
1011
1684
  }
1012
1685
  var json = await res.json();
1013
1686
  if (json.ok && json.data) {
1014
1687
  setData(json.data);
1688
+ saveStoredRightbarData(cwd, json.data); // 写入持久化快照
1689
+ setLastSyncedAt(Date.now());
1690
+ setSyncStatus("fresh");
1691
+ if (json.data.updateInfo) {
1692
+ setUpdateInfo(json.data.updateInfo);
1693
+ }
1694
+ }
1695
+ else if (data) {
1696
+ setSyncStatus("failed");
1015
1697
  }
1016
1698
  else {
1017
1699
  setError(json.error || "获取状态失败");
1018
1700
  }
1019
1701
  }
1020
1702
  catch (err) {
1021
- setError(err.message || "网络异常");
1703
+ if (data) {
1704
+ setSyncStatus("failed");
1705
+ }
1706
+ else {
1707
+ setError(err.message || "网络异常");
1708
+ }
1022
1709
  }
1023
1710
  finally {
1024
1711
  setLoading(false);
1025
1712
  }
1026
- }, [cwd, props]);
1713
+ }, [cwd, sessionId, data, syncSettings.strategy]);
1714
+ // 挂载与策略调度:与主页规则 100% 保持一致
1027
1715
  React.useEffect(() => {
1028
- loadData(false);
1029
- }, [loadData]);
1030
- // 窗口切回自动探测自愈
1716
+ if (syncSettings.strategy === "swr") {
1717
+ var timer = setTimeout(function () { loadData(false); }, 300);
1718
+ return function () { clearTimeout(timer); };
1719
+ }
1720
+ else if (!data) {
1721
+ loadData(false);
1722
+ }
1723
+ }, [cwd, sessionId, syncSettings.strategy]);
1724
+ // 定时轮询调度:与主页完全一致
1725
+ React.useEffect(() => {
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]);
1733
+ // 窗口切回自动探测自愈:优先轻量快速探针,检测到已登录立即自愈
1031
1734
  React.useEffect(() => {
1032
1735
  var onFocus = () => {
1033
1736
  if (!data?.auth?.loggedIn) {
1034
- loadData(false);
1737
+ fetch("/api/github/auth?force=true")
1738
+ .then(function (res) { return res.json(); })
1739
+ .then(function (json) {
1740
+ if (json && json.ok && json.auth && json.auth.loggedIn) {
1741
+ loadData(true);
1742
+ }
1743
+ })
1744
+ .catch(function () { });
1035
1745
  }
1036
1746
  };
1037
1747
  window.addEventListener("focus", onFocus);
1038
1748
  return () => window.removeEventListener("focus", onFocus);
1039
1749
  }, [data, loadData]);
1040
1750
  var h = React.createElement;
1041
- return h("div", { className: "dsh-github-rightbar-container" }, ...renderGithubRepoView(h, data, loading, error, () => loadData(true), true));
1751
+ return h("div", { className: "dsh-github-rightbar-container" }, ...renderGithubRepoView(h, data, loading, error, () => loadData(true), true, updateInfo, syncStatus, lastSyncedAt));
1042
1752
  }
1043
1753
  // 2. 右侧栏 Tab 标题组件
1044
1754
  function GithubRightbarTitle() {
@@ -1059,75 +1769,282 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
1059
1769
  className: "dsh-github-icon-inner" + (active ? " active" : "")
1060
1770
  }, renderOctocat(18, "currentColor")));
1061
1771
  }
1772
+ // 全景驾驶舱「⚙️ 数据同步与缓存设置」模态弹窗组件
1773
+ function GlobalSyncSettingsModal(props) {
1774
+ if (!props.isOpen)
1775
+ return null;
1776
+ var [strategy, setStrategy] = React.useState(props.currentSettings.strategy || "swr");
1777
+ var [intervalMs, setIntervalMs] = React.useState(props.currentSettings.intervalMs || 300000);
1778
+ var [cacheMeta, setCacheMeta] = React.useState(getCacheStorageMeta());
1779
+ var [clearTip, setClearTip] = React.useState("");
1780
+ // 键盘 Escape 快捷关闭支持
1781
+ React.useEffect(function () {
1782
+ var onKeyDown = function (e) {
1783
+ if (e.key === "Escape" || e.keyCode === 27) {
1784
+ props.onClose();
1785
+ }
1786
+ };
1787
+ window.addEventListener("keydown", onKeyDown);
1788
+ return function () { window.removeEventListener("keydown", onKeyDown); };
1789
+ }, [props.onClose]);
1790
+ var h = React.createElement;
1791
+ var handleSave = function () {
1792
+ var updated = {
1793
+ strategy: strategy,
1794
+ intervalMs: Number(intervalMs),
1795
+ };
1796
+ saveLocalSyncSettings(updated);
1797
+ props.onSave(updated);
1798
+ props.onClose();
1799
+ };
1800
+ var handleClear = function () {
1801
+ clearStoredGlobalOverview();
1802
+ props.onClearCache();
1803
+ setCacheMeta({ exists: false, sizeKb: 0, savedAt: 0 });
1804
+ setClearTip("本地缓存已彻底清空!");
1805
+ setTimeout(function () { setClearTip(""); }, 2000);
1806
+ };
1807
+ return h("div", { className: "dsh-github-modal-overlay", onClick: props.onClose }, h("div", {
1808
+ className: "dsh-github-modal-dialog",
1809
+ onClick: function (e) { e.stopPropagation(); },
1810
+ }, h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" } }, h("h3", { style: { margin: 0, fontSize: "16px", fontWeight: 600 } }, "⚙️ 数据同步与缓存偏好"), h("button", {
1811
+ style: { background: "none", border: "none", cursor: "pointer", fontSize: "18px", color: "var(--dsw-alias-label-secondary, #656d76)" },
1812
+ onClick: props.onClose,
1813
+ }, "✕")), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "14px", lineHeight: "1.5" } }, "配置 GitHub 全景驾驶舱的数据拉取节奏。启用秒开模式可利用本地持久化缓存消除每次打开时的漫长等待。"),
1814
+ // 刷新策略选择
1815
+ h("div", { style: { marginBottom: "16px" } }, h("div", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "8px" } }, "数据刷新策略"),
1816
+ // 策略 A: SWR 秒开
1817
+ h("div", {
1818
+ className: "dsh-github-radio-card" + (strategy === "swr" ? " active" : ""),
1819
+ onClick: function () { setStrategy("swr"); },
1820
+ }, h("input", { type: "radio", className: "dsh-github-radio", checked: strategy === "swr", readOnly: true }), h("div", null, h("div", { style: { fontWeight: 600, fontSize: "13px" } }, "⚡ 秒开展示旧数据 (SWR, 强烈推荐)"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "打开驾驶舱立即展示上次本地缓存(0ms秒开),后台静默同步最新状态。"))),
1821
+ // 策略 B: 纯手动刷新
1822
+ h("div", {
1823
+ className: "dsh-github-radio-card" + (strategy === "manual" ? " active" : ""),
1824
+ 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
+ // 策略 C: 定时轮询
1827
+ h("div", {
1828
+ className: "dsh-github-radio-card" + (strategy === "interval" ? " active" : ""),
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 }, "从不自动刷新 (仅手动)"))),
1837
+ // 本地缓存信息与清理
1838
+ h("div", {
1839
+ className: "dsh-github-cache-info-box",
1840
+ }, h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" } }, h("span", null, cacheMeta.exists
1841
+ ? `📦 本地缓存: 约 ${cacheMeta.sizeKb} KB (上次保存: ${new Date(cacheMeta.savedAt).toLocaleTimeString()})`
1842
+ : "📦 暂无本地持久化缓存"), cacheMeta.exists ? h("button", {
1843
+ className: "dsh-github-btn-sm",
1844
+ style: { padding: "3px 8px", fontSize: "11px", color: "var(--dsw-alias-state-error-primary, #cf222e)" },
1845
+ onClick: handleClear,
1846
+ }, "🗑️ 清空缓存") : null), clearTip ? h("div", { style: { color: "var(--dsw-alias-state-success, #2ea043)", marginTop: "4px", fontWeight: 500 } }, clearTip) : null),
1847
+ // 操作按钮
1848
+ h("div", { style: { display: "flex", justifyContent: "flex-end", gap: "8px" } }, h("button", {
1849
+ className: "dsh-github-btn-sm",
1850
+ style: { padding: "6px 14px" },
1851
+ onClick: props.onClose,
1852
+ }, "取消"), h("button", {
1853
+ className: "dsh-github-btn-sm",
1854
+ style: {
1855
+ padding: "6px 16px",
1856
+ background: "var(--dsw-alias-link, #0969da)",
1857
+ color: "#ffffff",
1858
+ borderColor: "var(--dsw-alias-link, #0969da)",
1859
+ fontWeight: 600,
1860
+ },
1861
+ onClick: handleSave,
1862
+ }, "保存并应用"))));
1863
+ }
1062
1864
  function GithubWorkspacePanel() {
1063
- var [loading, setLoading] = React.useState(true);
1064
- var [data, setData] = React.useState(null);
1865
+ // 1. 读取本地偏好配置与历史持久化快照 (0ms 纯内存读取)
1866
+ var initialSettings = React.useMemo(() => getLocalSyncSettings(), []);
1867
+ var initialSnapshot = React.useMemo(() => getStoredGlobalOverview(), []);
1868
+ var [syncSettings, setSyncSettings] = React.useState(initialSettings);
1869
+ var [data, setData] = React.useState(initialSnapshot?.data || null);
1870
+ var [loading, setLoading] = React.useState(!initialSnapshot);
1871
+ var [syncStatus, setSyncStatus] = React.useState(initialSnapshot ? "cached" : "loading");
1872
+ var [lastSyncedAt, setLastSyncedAt] = React.useState(initialSnapshot?.savedAt || 0);
1873
+ var [isSettingsModalOpen, setIsSettingsModalOpen] = React.useState(false);
1065
1874
  var [error, setError] = React.useState(null);
1875
+ var [updateInfo, setUpdateInfo] = React.useState(null);
1066
1876
  var [activeTab, setActiveTab] = React.useState("workspaces"); // 'workspaces' | 'repos' | 'mywork'
1067
1877
  var [searchQuery, setSearchQuery] = React.useState("");
1878
+ var [issueFilter, setIssueFilter] = React.useState("all"); // 'all' | 'reported' | 'assigned' | 'created'
1879
+ // gh 业务数据就绪后,闲时 1.5 秒异步静默检测升级,绝不影响首屏
1880
+ React.useEffect(() => {
1881
+ if (!data)
1882
+ return;
1883
+ var timer = setTimeout(function () {
1884
+ fetch("/api/github/check-update")
1885
+ .then(function (res) { return res.json(); })
1886
+ .then(function (json) {
1887
+ if (json && json.ok && json.data) {
1888
+ setUpdateInfo(json.data);
1889
+ }
1890
+ })
1891
+ .catch(function () { });
1892
+ }, 1500);
1893
+ return function () { clearTimeout(timer); };
1894
+ }, [data]);
1068
1895
  var loadData = React.useCallback(async (forceRefresh = false) => {
1896
+ // 如果是纯手动模式,且非显式强制刷新,且本地已有旧数据,直接跳过后台拉取
1897
+ if (!forceRefresh && syncSettings.strategy === "manual" && data) {
1898
+ return;
1899
+ }
1069
1900
  setLoading(true);
1901
+ setSyncStatus(data ? "syncing" : "loading");
1070
1902
  setError(null);
1071
- // 全局驾驶舱要 spawn 十几次 gh,冷启动可能超过 10 秒。
1072
- // 不设显式超时的话浏览器会抛一个与真实原因无关的网络错误,
1073
- // 早先这里会把所有失败都归因成「后端服务未就绪或接口不存在」——归因错误比没有报错更糟。
1074
1903
  var controller = new AbortController();
1075
1904
  var timedOut = false;
1076
- var timer = setTimeout(function () { timedOut = true; controller.abort(); }, 30000);
1905
+ var timer = setTimeout(function () { timedOut = true; controller.abort(); }, 45000);
1077
1906
  try {
1078
1907
  if (forceRefresh)
1079
1908
  await fetch("/api/github/refresh", { method: "POST" }).catch(() => { });
1080
1909
  var res = await fetch("/api/github/global-overview", { signal: controller.signal });
1081
1910
  if (!res.ok) {
1082
1911
  var errText = await res.text().catch(() => "");
1083
- setError(`获取全局数据失败(HTTP ${res.status})${errText ? ": " + errText : ""}`);
1912
+ var failMsg = `获取全局数据失败(HTTP ${res.status})${errText ? ": " + errText : ""}`;
1913
+ if (data) {
1914
+ // 容灾保障:有本地旧数据时绝不红屏崩溃,平滑降级展示离线快照
1915
+ setSyncStatus("failed");
1916
+ }
1917
+ else {
1918
+ setError(failMsg);
1919
+ }
1084
1920
  return;
1085
1921
  }
1086
1922
  var json = await res.json();
1087
1923
  if (json.ok && json.data) {
1088
1924
  setData(json.data);
1089
- // 工作区矩阵单独失败时不能静默:否则界面显示「暂无工作区」,把故障伪装成空状态
1925
+ saveStoredGlobalOverview(json.data); // 写入持久化快照
1926
+ setLastSyncedAt(Date.now());
1927
+ setSyncStatus("fresh");
1928
+ if (json.data.updateInfo) {
1929
+ setUpdateInfo(json.data.updateInfo);
1930
+ }
1090
1931
  if (json.data.workspaceError) {
1091
1932
  setError("工作区列表读取失败:" + json.data.workspaceError);
1092
1933
  }
1093
1934
  }
1094
1935
  else {
1095
- setError(json.error || "获取全局数据失败");
1936
+ if (data) {
1937
+ setSyncStatus("failed");
1938
+ }
1939
+ else {
1940
+ setError(json.error || "获取全局数据失败");
1941
+ }
1096
1942
  }
1097
1943
  }
1098
1944
  catch (err) {
1099
- if (timedOut || err.name === "AbortError") {
1100
- setError("获取超时(30 秒):本机 gh 命令响应过慢,通常是网络或工作区数量较多所致。可稍后重试,或减少同时打开的工作区。");
1945
+ if (data) {
1946
+ // 后台网络故障时,旧数据依然完整可用
1947
+ setSyncStatus("failed");
1101
1948
  }
1102
1949
  else {
1103
- setError("无法连接后端服务:" + (err.message || err));
1950
+ if (timedOut || err.name === "AbortError") {
1951
+ setError("获取超时(30 秒):本机 gh 命令响应过慢,通常是网络或工作区数量较多所致。可稍后重试,或减少同时打开的工作区。");
1952
+ }
1953
+ else {
1954
+ setError("无法连接后端服务:" + (err.message || err));
1955
+ }
1104
1956
  }
1105
1957
  }
1106
1958
  finally {
1107
1959
  clearTimeout(timer);
1108
1960
  setLoading(false);
1109
1961
  }
1110
- }, []);
1111
- React.useEffect(() => { loadData(false); }, [loadData]);
1112
- // 窗口切回自动探测自愈
1962
+ }, [data, syncSettings]);
1963
+ // 挂载加载调度:SWR 模式下初次延迟 300ms 后台平滑发起静默同步
1964
+ React.useEffect(() => {
1965
+ if (syncSettings.strategy === "swr") {
1966
+ var timer = setTimeout(function () { loadData(false); }, 300);
1967
+ return function () { clearTimeout(timer); };
1968
+ }
1969
+ else if (!data) {
1970
+ loadData(false);
1971
+ }
1972
+ }, [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]);
1982
+ // 窗口切回自动探测自愈:优先轻量快速探针,检测到已登录立即自愈
1113
1983
  React.useEffect(() => {
1114
1984
  var onFocus = () => {
1115
1985
  if (!data?.auth?.loggedIn) {
1116
- loadData(false);
1986
+ fetch("/api/github/auth?force=true")
1987
+ .then(function (res) { return res.json(); })
1988
+ .then(function (json) {
1989
+ if (json && json.ok && json.auth && json.auth.loggedIn) {
1990
+ clearStoredGlobalOverview();
1991
+ loadData(true);
1992
+ }
1993
+ })
1994
+ .catch(function () { });
1117
1995
  }
1118
1996
  };
1119
1997
  window.addEventListener("focus", onFocus);
1120
1998
  return () => window.removeEventListener("focus", onFocus);
1121
1999
  }, [data, loadData]);
1122
2000
  var h = React.createElement;
1123
- var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(32), h("h2", null, "GitHub 全景驾驶舱")), h("button", {
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 })), h("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, h("button", {
1124
2002
  className: "dsh-github-refresh-btn",
1125
2003
  onClick: () => loadData(true),
1126
2004
  disabled: loading,
1127
- }, loading ? "刷新中..." : "🔄 刷新全局数据"));
2005
+ }, loading ? "同步中..." : "🔄 刷新全局数据"), h("button", {
2006
+ className: "dsh-github-btn-sm",
2007
+ onClick: () => setIsSettingsModalOpen(true),
2008
+ title: "配置数据拉取节奏与本地缓存偏好",
2009
+ style: { padding: "6px 12px" },
2010
+ }, "⚙️ 同步设置")));
1128
2011
  if (loading && !data) {
1129
2012
  return h("div", { className: "dsh-github-panel" }, headerEl, h("div", { className: "dsh-github-empty" }, "正在加载全局 GitHub 账号与工作区数据..."));
1130
2013
  }
2014
+ var effectiveUpdateInfo = updateInfo || data?.updateInfo;
2015
+ var upgradeBannerEl = effectiveUpdateInfo && effectiveUpdateInfo.hasUpdate
2016
+ ? h(UpgradeNotificationCard, { updateInfo: effectiveUpdateInfo, isRightbar: false })
2017
+ : null;
2018
+ var settingsModalEl = h(GlobalSyncSettingsModal, {
2019
+ isOpen: isSettingsModalOpen,
2020
+ onClose: () => setIsSettingsModalOpen(false),
2021
+ currentSettings: syncSettings,
2022
+ onSave: (newSettings) => {
2023
+ setSyncSettings(newSettings);
2024
+ },
2025
+ onClearCache: () => {
2026
+ setSyncStatus("loading");
2027
+ setData(null);
2028
+ setLastSyncedAt(0);
2029
+ },
2030
+ });
2031
+ // 错误拦截阻断:若无本地数据且请求失败,直接呈现错误卡片,杜绝把接口异常伪装成「未安装 CLI」
2032
+ if (!data && error) {
2033
+ return h("div", { className: "dsh-github-panel" }, headerEl, upgradeBannerEl, h("div", {
2034
+ style: {
2035
+ padding: "24px",
2036
+ borderRadius: "8px",
2037
+ background: "rgba(248, 81, 73, 0.08)",
2038
+ border: "1px solid rgba(248, 81, 73, 0.3)",
2039
+ margin: "16px 0",
2040
+ textAlign: "center",
2041
+ },
2042
+ }, h("div", { style: { fontSize: "16px", fontWeight: 600, color: "#f85149", marginBottom: "8px" } }, "⚠️ 后端服务加载异常"), h("div", { style: { fontSize: "13px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "16px" } }, error), h("button", {
2043
+ className: "dsh-github-btn-sm",
2044
+ onClick: () => loadData(true),
2045
+ style: { padding: "6px 18px", background: "var(--dsw-alias-link, #0969da)", color: "#ffffff", borderColor: "var(--dsw-alias-link, #0969da)" },
2046
+ }, "🔄 重新请求")), settingsModalEl);
2047
+ }
1131
2048
  var auth = data ? data.auth : null;
1132
2049
  var userRepos = (data && data.userRepos) || [];
1133
2050
  var workspaceMatrix = (data && data.workspaceMatrix) || [];
@@ -1149,7 +2066,7 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
1149
2066
  ? h(AuthGuideCard, { auth, onRefresh: () => loadData(true), isRightbar: false })
1150
2067
  : null;
1151
2068
  // Tab 栏
1152
- var tabsEl = h("div", { className: "dsh-github-tabs" }, h("button", { className: `dsh-github-tab-btn ${activeTab === 'workspaces' ? 'active' : ''}`, onClick: () => setActiveTab('workspaces') }, `📂 本地工作区矩阵 (${workspaceMatrix.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'repos' ? 'active' : ''}`, onClick: () => setActiveTab('repos') }, `☁️ 我的 GitHub 仓库 (${userRepos.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'mywork' ? 'active' : ''}`, onClick: () => setActiveTab('mywork') }, `📋 个人待办 (PR: ${myPrs.length} · Issue: ${myIssues.length})`));
2069
+ var tabsEl = h("div", { className: "dsh-github-tabs" }, h("button", { className: `dsh-github-tab-btn ${activeTab === 'workspaces' ? 'active' : ''}`, onClick: () => setActiveTab('workspaces') }, `📂 本地工作区矩阵 (${workspaceMatrix.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'repos' ? 'active' : ''}`, onClick: () => setActiveTab('repos') }, `☁️ 我的 GitHub 仓库 (${userRepos.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'mywork' ? 'active' : ''}`, onClick: () => setActiveTab('mywork') }, `📋 任务与待办 (PR: ${myPrs.length} · Issue: ${myIssues.length})`));
1153
2070
  // 内容区
1154
2071
  var contentEl = null;
1155
2072
  if (activeTab === 'workspaces') {
@@ -1178,10 +2095,52 @@ body[data-ds-dark-theme] .dsh-github-steps-list {
1178
2095
  contentEl = h("div", { className: "dsh-github-empty" }, "当前尚未认证登录 GitHub 账号,无法读取个人待办 PR 与 Issues。请先完成上方的安装或登录指引。");
1179
2096
  }
1180
2097
  else {
1181
- contentEl = h("div", null, h("h3", { className: "dsh-github-subheading" }, `🔀 我发起的 PR (${myPrs.length})`), myPrs.length === 0 ? h("div", { className: "dsh-github-empty", style: { marginBottom: "16px" } }, "暂无开放中的 PR") : h("div", { className: "dsh-github-list", style: { marginBottom: "16px" } }, myPrs.map((pr) => h("div", { key: pr.number, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: pr.url, target: "_blank" }, `[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`)))), h("h3", { className: "dsh-github-subheading" }, `📝 我的待办 Issues (${myIssues.length})`), myIssues.length === 0 ? h("div", { className: "dsh-github-empty" }, "暂无开放中的 Issue") : h("div", { className: "dsh-github-list" }, myIssues.map((is) => h("div", { key: is.number, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: is.url, target: "_blank" }, `[${is.repository?.nameWithOwner || 'Repo'}] #${is.number} ${is.title}`)))));
2098
+ var issueStats = (data && data.issueStats) || {
2099
+ total: myIssues.length,
2100
+ reported: myIssues.filter((i) => (i.categories || []).includes('reported')).length,
2101
+ assigned: myIssues.filter((i) => (i.categories || []).includes('assigned')).length,
2102
+ created: myIssues.filter((i) => (i.categories || []).includes('created')).length,
2103
+ };
2104
+ var filteredIssues = myIssues.filter((is) => {
2105
+ if (issueFilter === 'all')
2106
+ return true;
2107
+ var cats = is.categories || [];
2108
+ return cats.includes(issueFilter);
2109
+ });
2110
+ var issuePillsEl = h("div", { className: "dsh-github-pills" }, h("button", {
2111
+ className: `dsh-github-pill-btn ${issueFilter === 'all' ? 'active' : ''}`,
2112
+ onClick: () => setIssueFilter('all'),
2113
+ }, `全部 (${issueStats.total || myIssues.length})`), h("button", {
2114
+ className: `dsh-github-pill-btn ${issueFilter === 'reported' ? 'active' : ''}`,
2115
+ onClick: () => setIssueFilter('reported'),
2116
+ }, `📥 用户提起的 (${issueStats.reported || 0})`), h("button", {
2117
+ className: `dsh-github-pill-btn ${issueFilter === 'assigned' ? 'active' : ''}`,
2118
+ onClick: () => setIssueFilter('assigned'),
2119
+ }, `🎯 分配给我的 (${issueStats.assigned || 0})`), h("button", {
2120
+ className: `dsh-github-pill-btn ${issueFilter === 'created' ? 'active' : ''}`,
2121
+ onClick: () => setIssueFilter('created'),
2122
+ }, `✍️ 我创建的 (${issueStats.created || 0})`));
2123
+ contentEl = h("div", null, h("h3", { className: "dsh-github-subheading" }, `🔀 我发起的 PR (${myPrs.length})`), myPrs.length === 0 ? h("div", { className: "dsh-github-empty", style: { marginBottom: "16px" } }, "暂无开放中的 PR") : h("div", { className: "dsh-github-list", style: { marginBottom: "16px" } }, myPrs.map((pr) => h("div", { key: pr.number, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: pr.url, target: "_blank" }, `[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`)))), h("h3", { className: "dsh-github-subheading" }, `📝 任务与待办 Issues (${filteredIssues.length})`), issuePillsEl, filteredIssues.length === 0 ? h("div", { className: "dsh-github-empty" }, "当前分类下暂无开放中的 Issue") : h("div", { className: "dsh-github-list" }, filteredIssues.map((is) => {
2124
+ var cats = is.categories || [];
2125
+ var badges = [];
2126
+ if (cats.includes("reported")) {
2127
+ badges.push(h("span", { key: "rep", className: "dsh-github-badge dsh-badge-purple" }, "用户提单"));
2128
+ }
2129
+ if (cats.includes("assigned")) {
2130
+ badges.push(h("span", { key: "asn", className: "dsh-github-badge dsh-badge-blue" }, "分配待办"));
2131
+ }
2132
+ if (cats.includes("created")) {
2133
+ badges.push(h("span", { key: "crt", className: "dsh-github-badge dsh-badge-green" }, "我发起的"));
2134
+ }
2135
+ return h("div", { key: is.url || is.number, className: "dsh-github-item" }, h("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" } }, h("a", { className: "dsh-github-item-title", href: is.url, target: "_blank" }, `[${is.repository?.nameWithOwner || 'Repo'}] #${is.number} ${is.title}`), ...badges), h("div", { className: "dsh-github-item-meta" }, is.author ? h("span", null, `由 @${is.author.login} 提出`) : null, is.assignees && is.assignees.length > 0
2136
+ ? h("span", null, `指派: ${is.assignees.map((a) => '@' + a.login).join(', ')}`)
2137
+ : null, is.labels && is.labels.length > 0
2138
+ ? is.labels.map((lbl) => h("span", { key: lbl.name, className: "dsh-github-badge dsh-badge-yellow" }, lbl.name))
2139
+ : null, is.updatedAt ? h("span", null, `更新于: ${new Date(is.updatedAt).toLocaleDateString()}`) : null));
2140
+ })));
1182
2141
  }
1183
2142
  }
1184
- return h("div", { className: "dsh-github-panel" }, headerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl);
2143
+ return h("div", { className: "dsh-github-panel" }, headerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl);
1185
2144
  }
1186
2145
  var inject = ["slots", "sidebarRightTabs", "sessions", "layout", "theme"];
1187
2146
  function apply(ctx) {