dsh-github-flow 0.2.0 → 0.3.0
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 +68 -22
- package/README.md +69 -23
- package/docs/UPGRADE-NOTES.md +93 -0
- package/docs/architecture.md +76 -31
- 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 +27 -5
- package/lib/api/routes-actions.d.ts +18 -4
- package/lib/api/routes-actions.js +143 -19
- package/lib/api/routes-lifecycle.d.ts +1 -1
- package/lib/api/routes-lifecycle.js +53 -2
- package/lib/api/routes-overview.js +104 -1
- package/lib/client.js +991 -55
- package/lib/commands/gh.js +11 -2
- package/lib/executor.d.ts +118 -1
- package/lib/executor.js +479 -12
- package/lib/main.js +10 -0
- package/lib/renderers/markdown.d.ts +12 -1
- package/lib/renderers/markdown.js +150 -16
- package/lib/tools/help.js +144 -11
- package/lib/tools/image-utils.d.ts +79 -0
- package/lib/tools/image-utils.js +247 -0
- package/lib/tools/issue.d.ts +18 -4
- package/lib/tools/issue.js +161 -6
- package/lib/tools/pr.d.ts +12 -4
- package/lib/tools/pr.js +128 -7
- package/lib/tools/repo.d.ts +20 -4
- package/lib/tools/repo.js +196 -4
- package/lib/tools/run.d.ts +8 -0
- package/lib/tools/run.js +104 -3
- package/lib/tools/secret.d.ts +5 -0
- package/lib/tools/secret.js +108 -23
- package/lib/types.d.ts +85 -1
- package/lib/update-settings.d.ts +18 -0
- package/lib/update-settings.js +60 -0
- package/lib/updater.d.ts +32 -1
- package/lib/updater.js +117 -9
- package/lib/upgrade-manager.d.ts +70 -1
- package/lib/upgrade-manager.js +346 -46
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -5,6 +5,105 @@ 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
|
+
// ============================================================
|
|
9
|
+
// 面板公共工具函数(纯函数,可被单元测试直接覆盖)
|
|
10
|
+
// ============================================================
|
|
11
|
+
/**
|
|
12
|
+
* ISO 时间 → 人性化相对时间("刚刚"/"N分钟前"/"N小时前"/"N天前"/日期回退)。
|
|
13
|
+
* 为什么需要:PR/Issue/Run 的时间字段原始 ISO 串在窄面板不可读,相对时间
|
|
14
|
+
* 一眼判断「还活跃吗」;hover 场景由调用方把绝对时间放进 title。
|
|
15
|
+
*/
|
|
16
|
+
function formatRelativeTime(iso) {
|
|
17
|
+
if (!iso || typeof iso !== "string")
|
|
18
|
+
return "";
|
|
19
|
+
var t = Date.parse(iso);
|
|
20
|
+
if (isNaN(t))
|
|
21
|
+
return "";
|
|
22
|
+
var diff = Date.now() - t;
|
|
23
|
+
if (diff < 0)
|
|
24
|
+
return "刚刚"; // 时钟轻微漂移容错
|
|
25
|
+
var MIN = 60 * 1000, HOUR = 60 * MIN, DAY = 24 * HOUR;
|
|
26
|
+
if (diff < MIN)
|
|
27
|
+
return "刚刚";
|
|
28
|
+
if (diff < HOUR)
|
|
29
|
+
return Math.floor(diff / MIN) + "分钟前";
|
|
30
|
+
if (diff < DAY)
|
|
31
|
+
return Math.floor(diff / HOUR) + "小时前";
|
|
32
|
+
if (diff < 7 * DAY)
|
|
33
|
+
return Math.floor(diff / DAY) + "天前";
|
|
34
|
+
var d = new Date(t);
|
|
35
|
+
return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* statusCheckRollup 聚合为单枚举 CI 徽章状态("pass" | "fail" | "pending" | null)。
|
|
39
|
+
* 归约顺序刻意 fail > pending > pass:用户扫一眼要看到的是「有没有问题」,
|
|
40
|
+
* 而非「有多少通过」——一个 fail 淹没在 9 个 pass 里就是漏报。
|
|
41
|
+
*/
|
|
42
|
+
function rollupCiStatus(checks) {
|
|
43
|
+
if (!Array.isArray(checks) || checks.length === 0)
|
|
44
|
+
return null;
|
|
45
|
+
var hasFail = false, hasPending = false;
|
|
46
|
+
for (var i = 0; i < checks.length; i++) {
|
|
47
|
+
var c = checks[i] || {};
|
|
48
|
+
var status = String(c.status || "").toLowerCase();
|
|
49
|
+
var conclusion = String(c.conclusion || "").toLowerCase();
|
|
50
|
+
if (conclusion === "failure" || conclusion === "timed_out" || conclusion === "cancelled")
|
|
51
|
+
hasFail = true;
|
|
52
|
+
else if (conclusion === "success" || conclusion === "skipped" || conclusion === "neutral") { /* 通过族 */ }
|
|
53
|
+
else if (status === "in_progress" || status === "queued" || conclusion === "pending" || (!conclusion && status))
|
|
54
|
+
hasPending = true;
|
|
55
|
+
}
|
|
56
|
+
if (hasFail)
|
|
57
|
+
return "fail";
|
|
58
|
+
if (hasPending)
|
|
59
|
+
return "pending";
|
|
60
|
+
return "pass";
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* reviewDecision → 展示态(徽章文案与色彩键)。无判定返回 null 不渲染。
|
|
64
|
+
*/
|
|
65
|
+
function reviewDecisionBadge(decision) {
|
|
66
|
+
var d = String(decision || "").toUpperCase();
|
|
67
|
+
if (d === "APPROVED")
|
|
68
|
+
return { text: "✔ 已批准", kind: "green" };
|
|
69
|
+
if (d === "CHANGES_REQUESTED")
|
|
70
|
+
return { text: "✋ 需修改", kind: "red" };
|
|
71
|
+
if (d === "REVIEW_REQUIRED")
|
|
72
|
+
return { text: "👀 待审查", kind: "yellow" };
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Actions event → 触发方式短标签。
|
|
77
|
+
*/
|
|
78
|
+
function eventLabel(event) {
|
|
79
|
+
var e = String(event || "");
|
|
80
|
+
if (e === "push")
|
|
81
|
+
return "⇡ push";
|
|
82
|
+
if (e === "schedule")
|
|
83
|
+
return "⏰ 定时";
|
|
84
|
+
if (e === "workflow_dispatch")
|
|
85
|
+
return "▶ 手动";
|
|
86
|
+
if (e === "pull_request")
|
|
87
|
+
return "🔀 PR";
|
|
88
|
+
return e || "";
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 按容器记录的 Tab 键应用互斥显示状态(hidden 页 + active 按钮)。
|
|
92
|
+
* 模块级纯 DOM 操作:渲染函数(switchTab 点击路径)与宿主组件的 effect
|
|
93
|
+
* (SWR 重渲染校正路径)共用——后者是必须的:React 重渲染会按 VDOM 初值
|
|
94
|
+
* 重设 hidden/active,用户手动切的 Tab 会被一次后台数据同步拽回默认页。
|
|
95
|
+
*/
|
|
96
|
+
function applyTabState(container, tabKey) {
|
|
97
|
+
var pages = container.querySelectorAll("[data-tab-page]");
|
|
98
|
+
pages.forEach(function (page) {
|
|
99
|
+
page.hidden = page.getAttribute("data-tab-page") !== tabKey;
|
|
100
|
+
});
|
|
101
|
+
var btns = container.querySelectorAll("[data-tab-btn]");
|
|
102
|
+
btns.forEach(function (btn) {
|
|
103
|
+
var on = btn.getAttribute("data-tab-btn") === tabKey;
|
|
104
|
+
btn.className = "dsh-github-tab-btn" + (on ? " active" : "");
|
|
105
|
+
});
|
|
106
|
+
}
|
|
8
107
|
// 面板样式(内联维护):DSH 客户端加载器以经典脚本语义执行本产物(build.mjs 剥除
|
|
9
108
|
// export 声明即为佐证),模块级 import 在该语境是 SyntaxError。曾尝试把 CSS 拆为
|
|
10
109
|
// 独立模块经 import 引入,导致 DSH 重启时客户端加载崩溃并触发插件批量卸载——
|
|
@@ -290,6 +389,40 @@ body[data-ds-dark-theme] .dsh-badge-purple {
|
|
|
290
389
|
border-color: rgba(163, 113, 247, 0.3);
|
|
291
390
|
}
|
|
292
391
|
|
|
392
|
+
/* 插件身份徽标:标识界面由第三方插件 dsh-github-flow 提供(非 DSH/GitHub 官方)。
|
|
393
|
+
* 为什么需要:界面以 GitHub 官方 Octocat 做功能图标,新用户易误认为官方功能;
|
|
394
|
+
* 徽标点击跳插件仓库,与设置「关于」页形成身份闭环。 */
|
|
395
|
+
.dsh-github-plugin-badge {
|
|
396
|
+
display: inline-flex;
|
|
397
|
+
align-items: center;
|
|
398
|
+
font-size: 10px;
|
|
399
|
+
font-weight: 500;
|
|
400
|
+
padding: 1px 7px;
|
|
401
|
+
border-radius: 10px;
|
|
402
|
+
line-height: 14px;
|
|
403
|
+
vertical-align: middle;
|
|
404
|
+
margin-left: 8px;
|
|
405
|
+
text-decoration: none !important;
|
|
406
|
+
background: rgba(125, 133, 144, 0.12);
|
|
407
|
+
color: #656d76;
|
|
408
|
+
border: 1px solid rgba(125, 133, 144, 0.25);
|
|
409
|
+
cursor: pointer;
|
|
410
|
+
transition: all 0.15s ease;
|
|
411
|
+
}
|
|
412
|
+
.dsh-github-plugin-badge:hover {
|
|
413
|
+
background: rgba(125, 133, 144, 0.2);
|
|
414
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
415
|
+
}
|
|
416
|
+
body[data-ds-dark-theme] .dsh-github-plugin-badge {
|
|
417
|
+
background: rgba(139, 148, 158, 0.18);
|
|
418
|
+
color: #8b949e;
|
|
419
|
+
border-color: rgba(139, 148, 158, 0.3);
|
|
420
|
+
}
|
|
421
|
+
body[data-ds-dark-theme] .dsh-github-plugin-badge:hover {
|
|
422
|
+
background: rgba(139, 148, 158, 0.28);
|
|
423
|
+
color: #f0f6fc;
|
|
424
|
+
}
|
|
425
|
+
|
|
293
426
|
/* 7.5. 分类筛选药丸栏 */
|
|
294
427
|
.dsh-github-pills {
|
|
295
428
|
display: flex;
|
|
@@ -1058,6 +1191,45 @@ body[data-ds-dark-theme] .dsh-github-cache-info-box {
|
|
|
1058
1191
|
color: var(--dsw-alias-label-secondary, #8c8c8c);
|
|
1059
1192
|
}
|
|
1060
1193
|
|
|
1194
|
+
/* 关于页求 Star 卡片:与 cache-info-box 同款双主题底色(浅色纸面/暗色深底) */
|
|
1195
|
+
.dsh-github-about-star-box {
|
|
1196
|
+
padding: 12px 14px;
|
|
1197
|
+
border-radius: 8px;
|
|
1198
|
+
background: var(--dsw-alias-bg-layer-1, #f6f8fa);
|
|
1199
|
+
border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
|
|
1200
|
+
}
|
|
1201
|
+
body[data-ds-dark-theme] .dsh-github-about-star-box {
|
|
1202
|
+
background: var(--dsw-alias-bg-base, #161b22);
|
|
1203
|
+
border-color: var(--dsw-alias-border-l1, #30363d);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/* 主操作按钮(链接色底):链接色底 + 白字,暗色用亮蓝(对齐 radio/链接的暗色强调色 58a6ff) */
|
|
1207
|
+
.dsh-github-primary-btn {
|
|
1208
|
+
padding: 5px 14px;
|
|
1209
|
+
font-weight: 600;
|
|
1210
|
+
white-space: nowrap;
|
|
1211
|
+
background: var(--dsw-alias-link, #0969da);
|
|
1212
|
+
border-color: var(--dsw-alias-link, #0969da);
|
|
1213
|
+
color: #ffffff;
|
|
1214
|
+
}
|
|
1215
|
+
.dsh-github-primary-btn:hover {
|
|
1216
|
+
background: var(--dsw-alias-link, #0969da);
|
|
1217
|
+
border-color: var(--dsw-alias-link, #0969da);
|
|
1218
|
+
color: #ffffff;
|
|
1219
|
+
opacity: 0.88;
|
|
1220
|
+
}
|
|
1221
|
+
body[data-ds-dark-theme] .dsh-github-primary-btn {
|
|
1222
|
+
background: #58a6ff;
|
|
1223
|
+
border-color: #58a6ff;
|
|
1224
|
+
color: #0d1117;
|
|
1225
|
+
}
|
|
1226
|
+
body[data-ds-dark-theme] .dsh-github-primary-btn:hover {
|
|
1227
|
+
background: #58a6ff;
|
|
1228
|
+
border-color: #58a6ff;
|
|
1229
|
+
color: #0d1117;
|
|
1230
|
+
opacity: 0.88;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1061
1233
|
/* 单选框点强调色 */
|
|
1062
1234
|
.dsh-github-radio {
|
|
1063
1235
|
accent-color: var(--dsw-alias-link, #0969da);
|
|
@@ -1067,6 +1239,48 @@ body[data-ds-dark-theme] .dsh-github-cache-info-box {
|
|
|
1067
1239
|
body[data-ds-dark-theme] .dsh-github-radio {
|
|
1068
1240
|
accent-color: #58a6ff;
|
|
1069
1241
|
}
|
|
1242
|
+
|
|
1243
|
+
/* 17. 提交详情原生模态弹窗 (<dialog>) */
|
|
1244
|
+
#dsh-github-commit-dialog {
|
|
1245
|
+
max-width: 600px;
|
|
1246
|
+
width: calc(100vw - 48px);
|
|
1247
|
+
border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
|
|
1248
|
+
border-radius: 12px;
|
|
1249
|
+
padding: 0;
|
|
1250
|
+
box-sizing: border-box;
|
|
1251
|
+
background: var(--dsw-alias-bg-base, #ffffff);
|
|
1252
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
1253
|
+
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.22);
|
|
1254
|
+
font-family: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
|
|
1255
|
+
}
|
|
1256
|
+
#dsh-github-commit-dialog::backdrop {
|
|
1257
|
+
background: rgba(0, 0, 0, 0.45);
|
|
1258
|
+
backdrop-filter: blur(2px);
|
|
1259
|
+
}
|
|
1260
|
+
body[data-ds-dark-theme] #dsh-github-commit-dialog {
|
|
1261
|
+
background: var(--dsw-alias-bg-layer-2, #1e1e22);
|
|
1262
|
+
border-color: var(--dsw-alias-border-l1, #30363d);
|
|
1263
|
+
color: var(--dsw-alias-label-primary, #ececec);
|
|
1264
|
+
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.5);
|
|
1265
|
+
}
|
|
1266
|
+
.dsh-github-commit-row {
|
|
1267
|
+
transition: background 0.15s ease;
|
|
1268
|
+
}
|
|
1269
|
+
.dsh-github-commit-row:hover {
|
|
1270
|
+
background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.05));
|
|
1271
|
+
}
|
|
1272
|
+
body[data-ds-dark-theme] .dsh-github-commit-row:hover {
|
|
1273
|
+
background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.08));
|
|
1274
|
+
}
|
|
1275
|
+
body[data-ds-dark-theme] .dsh-github-commit-pre {
|
|
1276
|
+
background: var(--dsw-alias-bg-base, #151517) !important;
|
|
1277
|
+
color: #ececec !important;
|
|
1278
|
+
border: 1px solid #30363d !important;
|
|
1279
|
+
}
|
|
1280
|
+
.dsh-github-dialog-close-btn:hover {
|
|
1281
|
+
background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.08)) !important;
|
|
1282
|
+
opacity: 1 !important;
|
|
1283
|
+
}
|
|
1070
1284
|
`;
|
|
1071
1285
|
function injectStyles() {
|
|
1072
1286
|
var styleId = "dsh-plugin-github-styles";
|
|
@@ -1095,6 +1309,18 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1095
1309
|
d: "M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.72 6.56 11.52C4.78 11.32 2.92 10.63 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.32 9.47 11.52C9.76 11.77 10.01 12.25 10.01 13C10.01 14.08 10 14.95 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z"
|
|
1096
1310
|
}));
|
|
1097
1311
|
}
|
|
1312
|
+
// Git 官方菱形分支矢量组件 (viewBox 0 0 1024 1024,经典 #1296db 配色)
|
|
1313
|
+
function renderGitIcon(size = 14, color = "#1296db") {
|
|
1314
|
+
return React.createElement("svg", {
|
|
1315
|
+
viewBox: "0 0 1024 1024",
|
|
1316
|
+
width: size,
|
|
1317
|
+
height: size,
|
|
1318
|
+
style: { flexShrink: 0, display: "inline-block", verticalAlign: "middle" }
|
|
1319
|
+
}, React.createElement("path", {
|
|
1320
|
+
fill: color,
|
|
1321
|
+
d: "M943.078 472.342L551.076 80.34c-21.859-21.859-57.626-21.859-79.485 0L79.589 472.342c-21.859 21.859-21.859 57.626 0 79.485l392.002 392.002c21.859 21.859 57.626 21.859 79.485 0l392.002-392.002c21.858-21.858 21.858-57.626 0-79.485z m-249.083 113.12c-37.939 0-68.693-30.755-68.693-68.693a68.532 68.532 0 0 1 4.136-23.521l-86.88-86.88v237.369c21.391 11.639 35.908 34.313 35.908 60.378 0 37.939-30.755 68.693-68.693 68.693s-68.693-30.755-68.693-68.693c0-27.314 15.944-50.903 39.03-61.973v-250.25c-23.022-10.642-39.03-33.879-39.03-60.907 0-7.808 1.401-15.272 3.853-22.243L344.061 207.871l44.159-44.159L490.738 266.23c5.58-1.503 11.418-2.378 17.473-2.378 37.076 0 67.132 30.056 67.132 67.132 0 6.055-0.875 11.893-2.378 17.473l102.241 102.241a68.68 68.68 0 0 1 18.789-2.623c37.939 0 68.693 30.755 68.693 68.693s-30.754 68.694-68.693 68.694z"
|
|
1322
|
+
}));
|
|
1323
|
+
}
|
|
1098
1324
|
// 现代暗黑与浅色自适应科技风勋章图标
|
|
1099
1325
|
function renderOctocatBadge(size = 32) {
|
|
1100
1326
|
return React.createElement("div", {
|
|
@@ -1106,6 +1332,24 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1106
1332
|
}
|
|
1107
1333
|
}, renderOctocat(Math.round(size * 0.58), "#ffffff"));
|
|
1108
1334
|
}
|
|
1335
|
+
// 插件身份徽标(h 工厂注入):标注界面由第三方插件 dsh-github-flow 提供。
|
|
1336
|
+
// 点击不直接跳仓库,而是派发全局事件打开「设置-关于」弹窗(仓库/协议/Star
|
|
1337
|
+
// 都在里面),由各宿主面板监听并挂载弹窗——避免从任意界面突兀跳浏览器。
|
|
1338
|
+
function renderPluginBadge(h) {
|
|
1339
|
+
return h("button", {
|
|
1340
|
+
className: "dsh-github-plugin-badge",
|
|
1341
|
+
type: "button",
|
|
1342
|
+
title: "本界面由第三方插件 dsh-github-flow 提供(非 DSH / GitHub 官方)。点击查看关于。",
|
|
1343
|
+
onClick: function (e) {
|
|
1344
|
+
e.stopPropagation();
|
|
1345
|
+
e.preventDefault();
|
|
1346
|
+
try {
|
|
1347
|
+
window.dispatchEvent(new CustomEvent("dsh-github-open-about"));
|
|
1348
|
+
}
|
|
1349
|
+
catch (err) { /* 事件派发失败不阻断点击语义 */ }
|
|
1350
|
+
},
|
|
1351
|
+
}, "插件");
|
|
1352
|
+
}
|
|
1109
1353
|
// 认证与安装引导卡片组件(响应式自适应中央大面板与右侧栏)
|
|
1110
1354
|
function AuthGuideCard(props) {
|
|
1111
1355
|
var auth = props ? props.auth : null;
|
|
@@ -1150,15 +1394,17 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1150
1394
|
});
|
|
1151
1395
|
var json = await res.json();
|
|
1152
1396
|
if (json.ok) {
|
|
1153
|
-
|
|
1154
|
-
|
|
1397
|
+
// 措辞留余地:start 的成败偶受系统会话策略影响,未弹出时用户仍有手动出路
|
|
1398
|
+
setToastMsg("已唤起系统终端,请在弹出窗口中完成操作;若未弹出,可手动打开终端执行上方命令");
|
|
1399
|
+
setTimeout(() => setToastMsg(null), 6000);
|
|
1155
1400
|
}
|
|
1156
1401
|
else {
|
|
1157
|
-
|
|
1402
|
+
// 失败如实展示:后端带回真实原因(如未找到终端/命令失败),并给出手动命令
|
|
1403
|
+
setToastMsg("❌ " + (json.message || "唤起失败,请手动打开终端执行上方命令"));
|
|
1158
1404
|
}
|
|
1159
1405
|
}
|
|
1160
1406
|
catch (e) {
|
|
1161
|
-
setToastMsg("
|
|
1407
|
+
setToastMsg("无法连接后台服务,请手动打开终端执行上方命令");
|
|
1162
1408
|
}
|
|
1163
1409
|
finally {
|
|
1164
1410
|
setLaunching(false);
|
|
@@ -1214,9 +1460,14 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1214
1460
|
className: "dsh-github-action-btn",
|
|
1215
1461
|
onClick: function () {
|
|
1216
1462
|
setToastMsg("正在快速检测登录状态...");
|
|
1217
|
-
|
|
1463
|
+
// 探测超时保护:后端 auth 探针本身 10s 超时,前端同样 10s 兜底——
|
|
1464
|
+
// 无超时时挂起会让 toast 永远停在"正在检测"且自愈刷新不触发
|
|
1465
|
+
var probeController = new AbortController();
|
|
1466
|
+
var probeTimer = setTimeout(function () { probeController.abort(); }, 10000);
|
|
1467
|
+
fetch("/api/github/auth?force=true", { signal: probeController.signal })
|
|
1218
1468
|
.then(function (res) { return res.json(); })
|
|
1219
1469
|
.then(function (json) {
|
|
1470
|
+
clearTimeout(probeTimer);
|
|
1220
1471
|
if (json && json.ok && json.auth && json.auth.loggedIn) {
|
|
1221
1472
|
setToastMsg("✅ 已识别到 GitHub CLI 登录态,正在拉取数据...");
|
|
1222
1473
|
clearStoredGlobalOverview();
|
|
@@ -1228,6 +1479,8 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1228
1479
|
}
|
|
1229
1480
|
})
|
|
1230
1481
|
.catch(function () {
|
|
1482
|
+
clearTimeout(probeTimer);
|
|
1483
|
+
setToastMsg("❌ 检测超时或后端不可达,请稍后重试");
|
|
1231
1484
|
onRefresh();
|
|
1232
1485
|
});
|
|
1233
1486
|
},
|
|
@@ -1289,7 +1542,16 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1289
1542
|
}
|
|
1290
1543
|
else {
|
|
1291
1544
|
setStatus("failed");
|
|
1292
|
-
|
|
1545
|
+
// 失败原因透传:后端 log 的末尾几行含真实报错(如 'dsh' 不是内部命令),
|
|
1546
|
+
// 只给「退出码异常」这类笼统话术用户无从下手
|
|
1547
|
+
var logTail = "";
|
|
1548
|
+
if (json.log && typeof json.log === "string") {
|
|
1549
|
+
var lines = json.log.trim().split("\n").filter(function (l) { return l.trim(); });
|
|
1550
|
+
if (lines.length > 0) {
|
|
1551
|
+
logTail = "\n\n📝 日志末尾: " + lines.slice(-4).join(" ⏎ ").slice(0, 400);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
setMsg((json.message || "升级命令执行失败,请在宿主终端手动执行。") + logTail);
|
|
1293
1555
|
}
|
|
1294
1556
|
}
|
|
1295
1557
|
catch (err) {
|
|
@@ -1305,7 +1567,9 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1305
1567
|
setDismissed(true);
|
|
1306
1568
|
};
|
|
1307
1569
|
var handleCopy = function () {
|
|
1308
|
-
|
|
1570
|
+
// 兜底命令也必须带 --profile(官方 CLI 必填)——正常路径 upgradeCommand
|
|
1571
|
+
// 由后端按真实 profile 生成,此默认值仅在后端数据缺失时兜底
|
|
1572
|
+
var cmd = updateInfo.upgradeCommand || "dsh plugin --profile desktop add dsh-github-flow@latest";
|
|
1309
1573
|
if (navigator.clipboard) {
|
|
1310
1574
|
navigator.clipboard.writeText(cmd);
|
|
1311
1575
|
alert("已复制升级命令到剪贴板:\n" + cmd);
|
|
@@ -1361,7 +1625,17 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1361
1625
|
}, status === "upgrading" ? "升级中..." : "⚡ 一键升级"), h("button", {
|
|
1362
1626
|
className: "dsh-github-btn-sm",
|
|
1363
1627
|
onClick: handleCopy,
|
|
1364
|
-
style: {
|
|
1628
|
+
style: {
|
|
1629
|
+
fontSize: "11px",
|
|
1630
|
+
padding: "4px 8px",
|
|
1631
|
+
// 失败态高亮:与驾驶舱横幅同一引导逻辑(失败文案指向复制动作)
|
|
1632
|
+
...(status === "failed" ? {
|
|
1633
|
+
background: "var(--dsw-alias-link, #0969da)",
|
|
1634
|
+
color: "#ffffff",
|
|
1635
|
+
borderColor: "var(--dsw-alias-link, #0969da)",
|
|
1636
|
+
fontWeight: 600,
|
|
1637
|
+
} : {}),
|
|
1638
|
+
},
|
|
1365
1639
|
}, "📋 复制命令")));
|
|
1366
1640
|
}
|
|
1367
1641
|
// 2. 全景驾驶舱大面板横幅模式
|
|
@@ -1405,8 +1679,17 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1405
1679
|
}, status === "upgrading" ? "正在自动升级中..." : "⚡ 一键自动升级"), h("button", {
|
|
1406
1680
|
className: "dsh-github-btn-sm",
|
|
1407
1681
|
onClick: handleCopy,
|
|
1408
|
-
style: {
|
|
1409
|
-
|
|
1682
|
+
style: {
|
|
1683
|
+
padding: "6px 12px",
|
|
1684
|
+
// 失败态高亮复制按钮:失败文案引导的动作就是它,视觉权重同步提升
|
|
1685
|
+
...(status === "failed" ? {
|
|
1686
|
+
background: "var(--dsw-alias-link, #0969da)",
|
|
1687
|
+
color: "#ffffff",
|
|
1688
|
+
borderColor: "var(--dsw-alias-link, #0969da)",
|
|
1689
|
+
fontWeight: 600,
|
|
1690
|
+
} : {}),
|
|
1691
|
+
},
|
|
1692
|
+
}, status === "failed" ? "📋 复制命令手动升级" : "📋 复制命令"), h("button", {
|
|
1410
1693
|
onClick: handleDismiss,
|
|
1411
1694
|
title: "不再提醒此版本",
|
|
1412
1695
|
style: { background: "none", border: "none", cursor: "pointer", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", fontSize: "16px", padding: "0 4px" },
|
|
@@ -1475,7 +1758,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1475
1758
|
}, text);
|
|
1476
1759
|
}
|
|
1477
1760
|
// 渲染 GitHub 仓库信息与操作视图的核心函数
|
|
1478
|
-
function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false, updateInfo = null, syncStatus = "", lastSyncedAt = 0) {
|
|
1761
|
+
function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false, updateInfo = null, syncStatus = "", lastSyncedAt = 0, onOpenSettings = null, cwd = null, onSelectCommit = null, gitLogPagination = null) {
|
|
1479
1762
|
var auth = data ? data.auth : null;
|
|
1480
1763
|
var repo = data ? data.repo : null;
|
|
1481
1764
|
var prs = (data && data.pullRequests) || [];
|
|
@@ -1485,12 +1768,23 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1485
1768
|
var upgradeBannerEl = effectiveUpdateInfo && effectiveUpdateInfo.hasUpdate
|
|
1486
1769
|
? h(UpgradeNotificationCard, { updateInfo: effectiveUpdateInfo, isRightbar })
|
|
1487
1770
|
: null;
|
|
1488
|
-
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", {
|
|
1771
|
+
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", {
|
|
1489
1772
|
className: "dsh-github-refresh-btn",
|
|
1490
1773
|
onClick: onRefresh,
|
|
1491
1774
|
disabled: loading,
|
|
1492
|
-
}, loading ? "刷新中..." : "🔄 刷新")
|
|
1493
|
-
//
|
|
1775
|
+
}, loading ? "刷新中..." : "🔄 刷新"),
|
|
1776
|
+
// 设置按钮:与全景主页复用同一设置弹窗(传入回调时渲染——目前仅右侧栏)。
|
|
1777
|
+
// 右侧栏此前没有设置入口,用户必须切到主页才能改同步/代理/更新设置。
|
|
1778
|
+
onOpenSettings ? h("button", {
|
|
1779
|
+
className: "dsh-github-btn-sm",
|
|
1780
|
+
onClick: onOpenSettings,
|
|
1781
|
+
title: "配置数据同步节奏、本地缓存、网络代理与插件信息",
|
|
1782
|
+
style: { padding: "4px 10px" },
|
|
1783
|
+
}, "⚙️") : null));
|
|
1784
|
+
// 仓库基础卡片(Tab 化后回归纯身份信息——git/PR/Release 细节移入「Git 记录」Tab 页)
|
|
1785
|
+
var gitSt = data && data.gitStatus;
|
|
1786
|
+
var relInfo = data && data.releaseInfo;
|
|
1787
|
+
var branchPr = data && data.branchPr;
|
|
1494
1788
|
var repoCardEl = h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "当前工作区 GitHub 仓库"), h("div", { className: "dsh-github-card-value" }, repo
|
|
1495
1789
|
? h("a", { href: repo.url, target: "_blank", className: "dsh-github-repo-link" }, repo.nameWithOwner)
|
|
1496
1790
|
: h("span", { style: { color: "var(--dsw-alias-state-error-primary, #cf222e)" } }, "未检测到关联的 GitHub 仓库"), repo ? h("span", { className: "dsh-github-badge dsh-badge-green" }, repo.defaultBranch) : null), repo && repo.description
|
|
@@ -1500,6 +1794,138 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1500
1794
|
: null, !repo
|
|
1501
1795
|
? h("div", { className: "dsh-github-card-subtext" }, "若本地已有 git 仓库,请运行 `git remote add github <url>` 或 `git remote add origin <url>` 关联远程仓库。")
|
|
1502
1796
|
: null);
|
|
1797
|
+
// ============ 「Git 记录」Tab 页:提交记录 + 本地↔远程差异 ============
|
|
1798
|
+
// 用户核心诉求:具体的提交内容(谁/何时/什么信息)+ 远程与本地的区别。
|
|
1799
|
+
// 三段视图:待推送(本地领先)/ 待拉取(远程领先,快照口径)/ 最近提交;
|
|
1800
|
+
// 分支同步摘要行保留为上下文锚点。
|
|
1801
|
+
var commitLog = data && data.commitLog;
|
|
1802
|
+
var renderCommitRow = function (c, tone) {
|
|
1803
|
+
return h("div", {
|
|
1804
|
+
style: { display: "flex", alignItems: "baseline", gap: "8px", padding: "3px 6px", fontSize: "12px", cursor: "pointer", borderRadius: "4px" },
|
|
1805
|
+
className: "dsh-github-commit-row",
|
|
1806
|
+
// 行级悬浮:完整 subject + 作者 + 时间 + 点击提示
|
|
1807
|
+
title: `${c.subject}\n— ${c.author} · ${c.relative} · ${c.sha}\n(点击查看提交详情)`,
|
|
1808
|
+
onClick: function () {
|
|
1809
|
+
if (typeof onSelectCommit === "function") {
|
|
1810
|
+
onSelectCommit(c);
|
|
1811
|
+
}
|
|
1812
|
+
},
|
|
1813
|
+
}, h("span", {
|
|
1814
|
+
style: {
|
|
1815
|
+
fontFamily: "monospace",
|
|
1816
|
+
fontSize: "11px",
|
|
1817
|
+
color: tone === "ahead" ? "var(--dsw-alias-state-success-primary, #1a7f37)"
|
|
1818
|
+
: tone === "behind" ? "var(--dsw-alias-state-attention-primary, #9a6700)"
|
|
1819
|
+
: "var(--dsw-alias-label-secondary, #656d76)",
|
|
1820
|
+
flexShrink: 0,
|
|
1821
|
+
},
|
|
1822
|
+
}, c.sha),
|
|
1823
|
+
// subject 单行截断:溢出省略号,全文走悬浮与点击弹窗
|
|
1824
|
+
h("span", {
|
|
1825
|
+
style: {
|
|
1826
|
+
flex: 1,
|
|
1827
|
+
minWidth: 0,
|
|
1828
|
+
overflow: "hidden",
|
|
1829
|
+
textOverflow: "ellipsis",
|
|
1830
|
+
whiteSpace: "nowrap",
|
|
1831
|
+
},
|
|
1832
|
+
}, c.subject), h("span", { style: { fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)", whiteSpace: "nowrap", flexShrink: 0 } }, `${c.author} · ${c.relative}`));
|
|
1833
|
+
};
|
|
1834
|
+
var gitSectionEl = h("div", { className: "dsh-github-section" },
|
|
1835
|
+
// section header + Release 右上角小徽章(flex 两端对齐;hover title 显详情,
|
|
1836
|
+
// 点击直达 Release 页——从尾部大块降为角落锚点,节省一整行卡片空间)
|
|
1837
|
+
h("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "8px" } }, h("h3", { style: { margin: 0, display: "flex", alignItems: "center", gap: "6px" } }, renderGitIcon(16, "#1296db"), "Git 记录"), relInfo
|
|
1838
|
+
? h(relInfo.url ? "a" : "span", {
|
|
1839
|
+
...(relInfo.url ? { href: relInfo.url, target: "_blank" } : {}),
|
|
1840
|
+
className: "dsh-github-badge" + (relInfo.isPrerelease ? " dsh-badge-purple" : " dsh-badge-green"),
|
|
1841
|
+
style: { fontSize: "10px", textDecoration: "none", flexShrink: 0 },
|
|
1842
|
+
title: `最新 Release: ${relInfo.tagName}${relInfo.isPrerelease ? "(预发布)" : ""}${formatRelativeTime(relInfo.publishedAt) ? ` · ${formatRelativeTime(relInfo.publishedAt)}发布` : ""}\n点击打开 Release 页`,
|
|
1843
|
+
}, `🏷️ ${relInfo.tagName}`)
|
|
1844
|
+
: null),
|
|
1845
|
+
// 分支同步摘要(上下文锚点,含快照语义提示)
|
|
1846
|
+
gitSt
|
|
1847
|
+
? h("div", {
|
|
1848
|
+
style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap", fontSize: "13px", marginBottom: "8px" },
|
|
1849
|
+
title: "ahead/behind 相对上次 git fetch 的快照(非实时)",
|
|
1850
|
+
}, h("span", { style: { fontWeight: 600, display: "inline-flex", alignItems: "center", gap: "5px" } }, renderGitIcon(13, "#1296db"), gitSt.branch), gitSt.hasUpstream
|
|
1851
|
+
? h("span", null, `${gitSt.ahead > 0 ? "↑" + gitSt.ahead + " " : ""}${gitSt.behind > 0 ? "↓" + gitSt.behind : ""}${gitSt.ahead === 0 && gitSt.behind === 0 ? "✓ 与远程同步" : ""}`)
|
|
1852
|
+
: h("span", { style: { opacity: 0.7 } }, "↑? ↓?(无 upstream)"), gitSt.dirtyCount > 0
|
|
1853
|
+
? h("span", null, `· ${gitSt.dirtyCount} 文件未提交`)
|
|
1854
|
+
: h("span", null, "· 工作区干净"))
|
|
1855
|
+
: null,
|
|
1856
|
+
// 段 1:本地领先远程(待推送的具体提交)
|
|
1857
|
+
commitLog && commitLog.aheadCommits && commitLog.aheadCommits.length > 0
|
|
1858
|
+
? h("div", { style: { marginBottom: "10px" } }, h("div", { style: { fontSize: "12px", fontWeight: 600, marginBottom: "4px", color: "var(--dsw-alias-state-success-primary, #1a7f37)" } }, `↑ 本地领先远程 ${commitLog.aheadCommits.length} 个提交(待 push)`), commitLog.aheadCommits.map(function (c, i) { return h("div", { key: "a" + i }, renderCommitRow(c, "ahead")); }))
|
|
1859
|
+
: null,
|
|
1860
|
+
// 段 2:远程领先本地(待拉取的具体提交——上次 fetch 快照口径)
|
|
1861
|
+
commitLog && commitLog.behindCommits && commitLog.behindCommits.length > 0
|
|
1862
|
+
? h("div", { style: { marginBottom: "10px" } }, h("div", { style: { fontSize: "12px", fontWeight: 600, marginBottom: "4px", color: "var(--dsw-alias-state-attention-primary, #9a6700)" } }, `↓ 远程领先本地 ${commitLog.behindCommits.length} 个提交(待 pull · 快照口径)`), commitLog.behindCommits.map(function (c, i) { return h("div", { key: "b" + i }, renderCommitRow(c, "behind")); }))
|
|
1863
|
+
: null,
|
|
1864
|
+
// 段 3:最近提交(常态锚定——即使完全同步也有内容可看;支持分页加载更多)
|
|
1865
|
+
(() => {
|
|
1866
|
+
var baseRecent = (commitLog && commitLog.recent) || [];
|
|
1867
|
+
var extraRecent = (gitLogPagination && gitLogPagination.extraCommits) || [];
|
|
1868
|
+
var allRecentCommits = baseRecent.concat(extraRecent);
|
|
1869
|
+
var totalCommits = commitLog && commitLog.totalCount;
|
|
1870
|
+
var loadingMore = Boolean(gitLogPagination && gitLogPagination.loadingMore);
|
|
1871
|
+
var hasMore = Boolean(gitLogPagination && gitLogPagination.hasMore);
|
|
1872
|
+
var onLoadMore = gitLogPagination ? gitLogPagination.onLoadMore : null;
|
|
1873
|
+
if (allRecentCommits.length === 0)
|
|
1874
|
+
return null;
|
|
1875
|
+
return h("div", null, h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "6px" } }, h("div", { style: { fontSize: "12px", fontWeight: 600, opacity: 0.85 } }, "🕘 最近提交"), totalCommits
|
|
1876
|
+
? h("div", { style: { fontSize: "11px", opacity: 0.6 } }, `${allRecentCommits.length} / ${totalCommits}`)
|
|
1877
|
+
: null), allRecentCommits.map(function (c, i) { return h("div", { key: "r" + (c.sha || i) }, renderCommitRow(c, "recent")); }),
|
|
1878
|
+
// 下滑或点击加载更多
|
|
1879
|
+
h("div", { style: { marginTop: "10px", textAlign: "center" } }, loadingMore
|
|
1880
|
+
? h("div", { style: { fontSize: "11px", opacity: 0.75, padding: "6px 0" } }, "⏳ 正在加载更多提交…")
|
|
1881
|
+
: (hasMore && onLoadMore)
|
|
1882
|
+
? h("button", {
|
|
1883
|
+
className: "dsh-github-btn-sm",
|
|
1884
|
+
style: {
|
|
1885
|
+
cursor: "pointer",
|
|
1886
|
+
padding: "4px 14px",
|
|
1887
|
+
borderRadius: "14px",
|
|
1888
|
+
border: "1px solid var(--dsw-alias-border-l2, #d0d7de)",
|
|
1889
|
+
background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
|
|
1890
|
+
color: "inherit",
|
|
1891
|
+
fontSize: "11px",
|
|
1892
|
+
lineHeight: "1.6",
|
|
1893
|
+
},
|
|
1894
|
+
onClick: onLoadMore,
|
|
1895
|
+
}, `⬇️ 下滑或点击加载更多 (${allRecentCommits.length}${totalCommits ? " / " + totalCommits : ""})`)
|
|
1896
|
+
: (allRecentCommits.length >= 15)
|
|
1897
|
+
? h("div", { style: { fontSize: "11px", opacity: 0.5, padding: "4px 0" } }, `✓ 已加载全部 ${allRecentCommits.length} 条提交`)
|
|
1898
|
+
: null));
|
|
1899
|
+
})(), !commitLog || ((commitLog.aheadCommits || []).length === 0 && (commitLog.behindCommits || []).length === 0 && (commitLog.recent || []).length === 0)
|
|
1900
|
+
? h("div", { className: "dsh-github-empty" }, "无提交记录")
|
|
1901
|
+
: null,
|
|
1902
|
+
// 分支关联 PR 与 Release 锚定(保留)
|
|
1903
|
+
branchPr
|
|
1904
|
+
? h("div", { className: "dsh-github-item", style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap", marginTop: "8px" } }, h("span", null, "🔀"), branchPr.url
|
|
1905
|
+
? h("a", { className: "dsh-github-item-title", href: branchPr.url, target: "_blank" }, `PR #${branchPr.number} ${branchPr.title}`)
|
|
1906
|
+
: h("span", { className: "dsh-github-item-title" }, `PR #${branchPr.number} ${branchPr.title}`), branchPr.isDraft ? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "DRAFT") : null)
|
|
1907
|
+
: null);
|
|
1908
|
+
// ============ 「未提交清单」Tab 页(全宽列表,原 details 折叠平移) ============
|
|
1909
|
+
var dirtySectionEl = h("div", { className: "dsh-github-section" }, h("div", { className: "dsh-github-section-header" }, h("h3", null, `📁 未提交清单 (${gitSt ? gitSt.dirtyCount : 0})`)), !gitSt || gitSt.dirtyCount === 0
|
|
1910
|
+
? h("div", { className: "dsh-github-empty" }, "工作区干净,没有未提交的变更 ✓")
|
|
1911
|
+
: h("div", { style: { fontSize: "12px", lineHeight: "1.9" } }, (gitSt.dirtyFiles || []).map(function (f, i) {
|
|
1912
|
+
return h("div", { key: i, style: { display: "flex", gap: "8px", padding: "2px 4px", borderRadius: "4px" } }, h("span", {
|
|
1913
|
+
style: {
|
|
1914
|
+
minWidth: "24px",
|
|
1915
|
+
textAlign: "center",
|
|
1916
|
+
opacity: 0.75,
|
|
1917
|
+
fontSize: "11px",
|
|
1918
|
+
fontFamily: "monospace",
|
|
1919
|
+
// 状态码配色:?? 新增(绿) / D 删除(红) / M 修改(黄) / 其他中性
|
|
1920
|
+
color: f.status === "??" ? "var(--dsw-alias-state-success-primary, #1a7f37)"
|
|
1921
|
+
: f.status.indexOf("D") >= 0 ? "var(--dsw-alias-state-error-primary, #cf222e)"
|
|
1922
|
+
: f.status.indexOf("M") >= 0 || f.status.indexOf("R") >= 0 ? "var(--dsw-alias-state-attention-primary, #9a6700)"
|
|
1923
|
+
: "inherit",
|
|
1924
|
+
},
|
|
1925
|
+
}, f.status === "??" ? "??" : f.status), h("span", { style: { wordBreak: "break-all" } }, f.path));
|
|
1926
|
+
}), gitSt.dirtyCount > (gitSt.dirtyFiles || []).length
|
|
1927
|
+
? h("div", { style: { opacity: 0.6, marginTop: "6px" } }, `…共 ${gitSt.dirtyCount} 个变更(仅列前 ${(gitSt.dirtyFiles || []).length})`)
|
|
1928
|
+
: null));
|
|
1503
1929
|
// 快捷操作栏 (方案 1: 官方黄金功能直达)
|
|
1504
1930
|
var actionsBarEl = h("div", { className: "dsh-github-quick-actions" },
|
|
1505
1931
|
// 1. 浏览器打开仓库主页
|
|
@@ -1555,9 +1981,28 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1555
1981
|
// PR 列表
|
|
1556
1982
|
var prSectionEl = h("div", { className: "dsh-github-section" }, h("div", { className: "dsh-github-section-header" }, h("h3", null, `🔀 Pull Requests (${prs.length})`)), prs.length === 0
|
|
1557
1983
|
? h("div", { className: "dsh-github-empty" }, "当前无开放中的 PR")
|
|
1558
|
-
: h("div", { className: "dsh-github-list" }, prs.map((pr) =>
|
|
1559
|
-
|
|
1560
|
-
|
|
1984
|
+
: h("div", { className: "dsh-github-list" }, prs.map((pr) => {
|
|
1985
|
+
// CI 聚合(归约顺序 fail>pending>pass——问题优先于通过)与 Review 判定
|
|
1986
|
+
var ci = rollupCiStatus(pr.statusCheckRollup);
|
|
1987
|
+
var rv = reviewDecisionBadge(pr.reviewDecision);
|
|
1988
|
+
var relTime = formatRelativeTime(pr.updatedAt);
|
|
1989
|
+
return h("div", { key: pr.number, className: "dsh-github-item" }, h("a", {
|
|
1990
|
+
className: "dsh-github-item-title",
|
|
1991
|
+
href: pr.url,
|
|
1992
|
+
target: "_blank",
|
|
1993
|
+
title: relTime ? `更新于 ${pr.updatedAt}` : undefined,
|
|
1994
|
+
}, `${pr.isDraft ? "⚪ " : ""}#${pr.number} ${pr.title}`), h("div", { className: "dsh-github-item-meta" }, h("span", null, `${pr.headRefName} → ${pr.baseRefName}`), pr.mergeable === "CONFLICTING"
|
|
1995
|
+
? h("span", { className: "dsh-github-badge dsh-badge-red" }, "代码冲突")
|
|
1996
|
+
: h("span", { className: "dsh-github-badge dsh-badge-green" }, "可合并"), ci === "fail"
|
|
1997
|
+
? h("span", { className: "dsh-github-badge dsh-badge-red" }, "❌ CI 失败")
|
|
1998
|
+
: ci === "pending"
|
|
1999
|
+
? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "⏳ CI 运行中")
|
|
2000
|
+
: ci === "pass"
|
|
2001
|
+
? h("span", { className: "dsh-github-badge dsh-badge-green" }, "✅ CI 通过")
|
|
2002
|
+
: null, rv
|
|
2003
|
+
? h("span", { className: `dsh-github-badge dsh-badge-${rv.kind}` }, rv.text)
|
|
2004
|
+
: null, relTime ? h("span", null, relTime) : null));
|
|
2005
|
+
})));
|
|
1561
2006
|
// 仓库 Issues 列表
|
|
1562
2007
|
var issueSectionEl = h("div", { className: "dsh-github-section" }, h("div", { className: "dsh-github-section-header" }, h("h3", null, `📋 仓库 Issues (${issues.length})`), repo?.url
|
|
1563
2008
|
? h("a", {
|
|
@@ -1588,15 +2033,72 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1588
2033
|
// Actions 流水线
|
|
1589
2034
|
var runSectionEl = h("div", { className: "dsh-github-section" }, h("div", { className: "dsh-github-section-header" }, h("h3", null, `⚡ 最近 Actions 流水线 (${runs.length})`)), runs.length === 0
|
|
1590
2035
|
? h("div", { className: "dsh-github-empty" }, "暂无工作流记录")
|
|
1591
|
-
: h("div", { className: "dsh-github-list" }, runs.map((r) => h("div", { key: r.databaseId, className: "dsh-github-item" }, h("a", {
|
|
2036
|
+
: h("div", { className: "dsh-github-list" }, runs.map((r) => h("div", { key: r.databaseId, className: "dsh-github-item" }, h("a", {
|
|
2037
|
+
className: "dsh-github-item-title",
|
|
2038
|
+
href: r.url,
|
|
2039
|
+
target: "_blank",
|
|
2040
|
+
title: r.createdAt ? `始于 ${r.createdAt}` : undefined,
|
|
2041
|
+
}, r.name), h("div", { className: "dsh-github-item-meta" }, h("span", null, `分支: ${r.headBranch}`), r.event ? h("span", null, eventLabel(r.event)) : null, formatRelativeTime(r.createdAt) ? h("span", null, formatRelativeTime(r.createdAt)) : null, r.conclusion === "success"
|
|
1592
2042
|
? h("span", { className: "dsh-github-badge dsh-badge-green" }, "✓ 成功")
|
|
1593
2043
|
: r.conclusion === "failure"
|
|
1594
2044
|
? h("span", { className: "dsh-github-badge dsh-badge-red" }, "✕ 失败")
|
|
1595
|
-
: h("span", { className: "dsh-github-badge dsh-badge-yellow" }, r.status))
|
|
2045
|
+
: h("span", { className: "dsh-github-badge dsh-badge-yellow" }, r.status)),
|
|
2046
|
+
// 失败自愈引导:把面板从「展示」升级为「引导 Agent 定位」——
|
|
2047
|
+
// run_id 直接嵌入文案,Agent 看到即可发起 log_failed 调用
|
|
2048
|
+
r.conclusion === "failure"
|
|
2049
|
+
? h("div", {
|
|
2050
|
+
className: "dsh-github-card-subtext",
|
|
2051
|
+
style: { fontSize: "11px", marginTop: "2px" },
|
|
2052
|
+
}, `💡 让 Agent 调用 github_run(action:'log_failed', run_id:'${r.databaseId}') 提取失败日志定位原因`)
|
|
2053
|
+
: null))));
|
|
1596
2054
|
var authGuideEl = !auth?.loggedIn
|
|
1597
2055
|
? h(AuthGuideCard, { auth, onRefresh, isRightbar })
|
|
1598
2056
|
: null;
|
|
1599
|
-
|
|
2057
|
+
// ============ 内容区 Tab 化(五页互斥,DOM 级状态) ============
|
|
2058
|
+
// 为什么 Tab 状态存 DOM 而非 React state / 模块变量:renderGithubRepoView
|
|
2059
|
+
// 是普通渲染函数(非组件,不能 useState);模块级可变状态在多宿主双挂载时
|
|
2060
|
+
// 会串扰。data-active-tab 属性挂在每实例容器上,各实例独立记忆各自 Tab。
|
|
2061
|
+
var TAB_DEFS = [
|
|
2062
|
+
{ key: "git", label: "Git", count: null, icon: renderGitIcon(13, "#1296db") },
|
|
2063
|
+
{ key: "dirty", label: "📁 未提交", count: gitSt ? gitSt.dirtyCount : null },
|
|
2064
|
+
{ key: "prs", label: "🔀 PR", count: prs.length },
|
|
2065
|
+
{ key: "issues", label: "📋 Issues", count: issues.length },
|
|
2066
|
+
{ key: "runs", label: "⚡ Actions", count: runs.length },
|
|
2067
|
+
].filter(function (t) {
|
|
2068
|
+
// 无 gitStatus(非 Git 工作区/探测失败)时 Git 与未提交两页隐藏——
|
|
2069
|
+
// 数据缺失是常态而非禁用态,隐藏比置灰诚实
|
|
2070
|
+
if ((t.key === "git" || t.key === "dirty") && !gitSt)
|
|
2071
|
+
return false;
|
|
2072
|
+
return true;
|
|
2073
|
+
});
|
|
2074
|
+
// 默认 Tab:Git 记录(工作区状态是会话最常查的上下文);非 Git 工作区
|
|
2075
|
+
// 降级为 PR,空 PR + 有 Issue 再降 Issues
|
|
2076
|
+
var defaultTab = gitSt ? "git" : (prs.length === 0 && issues.length > 0 ? "issues" : "prs");
|
|
2077
|
+
var switchTab = function (container, tabKey) {
|
|
2078
|
+
// data-active-tab 是每实例的 Tab 记忆载体(DOM 属性);互斥显示逻辑
|
|
2079
|
+
// 提升为模块级 applyTabState(与宿主组件的 SWR 校正 effect 共用)
|
|
2080
|
+
container.setAttribute("data-active-tab", tabKey);
|
|
2081
|
+
applyTabState(container, tabKey);
|
|
2082
|
+
};
|
|
2083
|
+
var tabsBarEl = h("div", { className: "dsh-github-tabs", style: { marginBottom: "12px" } }, ...TAB_DEFS.map(function (t) {
|
|
2084
|
+
var labelText = t.count !== null ? `${t.label} (${t.count})` : t.label;
|
|
2085
|
+
return h("button", {
|
|
2086
|
+
key: t.key,
|
|
2087
|
+
// React 无受控状态——className 初值按默认 Tab 计算,切换由 switchTab 直接改 DOM
|
|
2088
|
+
className: "dsh-github-tab-btn" + (t.key === defaultTab ? " active" : ""),
|
|
2089
|
+
"data-tab-btn": t.key,
|
|
2090
|
+
style: t.icon ? { display: "inline-flex", alignItems: "center", gap: "5px" } : undefined,
|
|
2091
|
+
onClick: function (e) {
|
|
2092
|
+
var container = e.target.closest("[data-active-tab]");
|
|
2093
|
+
if (container)
|
|
2094
|
+
switchTab(container, t.key);
|
|
2095
|
+
},
|
|
2096
|
+
}, t.icon ? [t.icon, " ", labelText] : labelText);
|
|
2097
|
+
}));
|
|
2098
|
+
var tabbedContentEl = h("div", { "data-active-tab": defaultTab }, tabsBarEl,
|
|
2099
|
+
// 五个页面:hidden 初值按默认 Tab;原三区块内部渲染零改动,仅加包裹属性
|
|
2100
|
+
h("div", { "data-tab-page": "prs", hidden: defaultTab !== "prs" }, prSectionEl), h("div", { "data-tab-page": "issues", hidden: defaultTab !== "issues" }, issueSectionEl), h("div", { "data-tab-page": "runs", hidden: defaultTab !== "runs" }, runSectionEl), gitSt ? h("div", { "data-tab-page": "git", hidden: defaultTab !== "git" }, gitSectionEl) : null, gitSt ? h("div", { "data-tab-page": "dirty", hidden: defaultTab !== "dirty" }, dirtySectionEl) : null);
|
|
2101
|
+
return [headerEl, upgradeBannerEl, authGuideEl, repoCardEl, actionsBarEl, tabbedContentEl].filter(Boolean);
|
|
1600
2102
|
}
|
|
1601
2103
|
// 全局持久化缓存与偏好配置键名
|
|
1602
2104
|
var GLOBAL_OVERVIEW_CACHE_KEY = "dsh_github_global_overview_snapshot_v1";
|
|
@@ -1776,10 +2278,10 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1776
2278
|
window.removeEventListener("dsh-github-cache-cleared", onCacheCleared);
|
|
1777
2279
|
};
|
|
1778
2280
|
}, []);
|
|
1779
|
-
// gh
|
|
2281
|
+
// 检查更新与 gh 环境链路(安装/登录/业务数据)零依赖,挂载即并行发起:
|
|
2282
|
+
// 原先门控在 [data] 之后会串行等 5~19 秒,且 gh 冷启动失败时 data 恒空导致横幅永不出。
|
|
2283
|
+
// 仅 300ms 微延迟避让首屏渲染;并发由服务端 inFlightPromise + 24h 缓存合流去重。
|
|
1780
2284
|
React.useEffect(() => {
|
|
1781
|
-
if (!data)
|
|
1782
|
-
return;
|
|
1783
2285
|
var timer = setTimeout(function () {
|
|
1784
2286
|
fetch("/api/github/check-update")
|
|
1785
2287
|
.then(function (res) { return res.json(); })
|
|
@@ -1789,9 +2291,9 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1789
2291
|
}
|
|
1790
2292
|
})
|
|
1791
2293
|
.catch(function () { });
|
|
1792
|
-
},
|
|
2294
|
+
}, 300);
|
|
1793
2295
|
return function () { clearTimeout(timer); };
|
|
1794
|
-
}, [
|
|
2296
|
+
}, []);
|
|
1795
2297
|
// 遵循全局规则的数据拉取核心函数 (仅依赖标量 cwd 与 sessionId,坚决去除 props 引用依赖,彻底终结死循环)
|
|
1796
2298
|
var loadData = React.useCallback(async (forceRefresh = false) => {
|
|
1797
2299
|
// 规则执行:若主页设置为「纯手动模式 (manual)」,且非用户点击强制刷新,且本地已有数据,坚决不打后台网络!
|
|
@@ -1803,9 +2305,15 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1803
2305
|
}
|
|
1804
2306
|
setSyncStatus(data ? "syncing" : "loading");
|
|
1805
2307
|
setError(null);
|
|
2308
|
+
// 与全局面板同规格的超时保护:任一 fetch 挂起(如系统代理劫持回环请求)时
|
|
2309
|
+
// 45 秒 abort,loading 必然清除——未安装 gh 的用户能拿到明确的错误出口
|
|
2310
|
+
// 而非永久转圈。此前本函数完全没有 AbortController,挂起即永久 loading。
|
|
2311
|
+
var controller = new AbortController();
|
|
2312
|
+
var timedOut = false;
|
|
2313
|
+
var timer = setTimeout(function () { timedOut = true; controller.abort(); }, 45000);
|
|
1806
2314
|
try {
|
|
1807
2315
|
if (forceRefresh) {
|
|
1808
|
-
await fetch("/api/github/refresh", { method: "POST" }).catch(() => { });
|
|
2316
|
+
await fetch("/api/github/refresh", { method: "POST", signal: controller.signal }).catch(() => { });
|
|
1809
2317
|
}
|
|
1810
2318
|
var url = "/api/github/overview";
|
|
1811
2319
|
var params = [];
|
|
@@ -1818,7 +2326,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1818
2326
|
if (params.length > 0) {
|
|
1819
2327
|
url += "?" + params.join("&");
|
|
1820
2328
|
}
|
|
1821
|
-
var res = await fetch(url);
|
|
2329
|
+
var res = await fetch(url, { signal: controller.signal });
|
|
1822
2330
|
if (!res.ok) {
|
|
1823
2331
|
var errText = await res.text().catch(() => "");
|
|
1824
2332
|
var msg = `后端服务未就绪 (${res.status}): ${errText || res.statusText}`;
|
|
@@ -1849,13 +2357,20 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1849
2357
|
}
|
|
1850
2358
|
catch (err) {
|
|
1851
2359
|
if (data) {
|
|
2360
|
+
// 后台网络故障时,旧数据依然完整可用
|
|
1852
2361
|
setSyncStatus("failed");
|
|
1853
2362
|
}
|
|
1854
2363
|
else {
|
|
1855
|
-
|
|
2364
|
+
if (timedOut || err.name === "AbortError") {
|
|
2365
|
+
setError("获取超时(45 秒):本机 gh 命令响应过慢或网络代理劫持了回环请求。可稍后重试,或在「⚙️ 设置 → 🌐 网络代理」确认代理已放行 127.0.0.1。");
|
|
2366
|
+
}
|
|
2367
|
+
else {
|
|
2368
|
+
setError(err.message || "网络异常");
|
|
2369
|
+
}
|
|
1856
2370
|
}
|
|
1857
2371
|
}
|
|
1858
2372
|
finally {
|
|
2373
|
+
clearTimeout(timer);
|
|
1859
2374
|
setLoading(false);
|
|
1860
2375
|
}
|
|
1861
2376
|
}, [cwd, sessionId, data, syncSettings.strategy]);
|
|
@@ -1873,6 +2388,17 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1873
2388
|
}, [cwd, sessionId, syncSettings.strategy]);
|
|
1874
2389
|
// (原定时轮询调度已随 'interval' 策略一并移除:前端 setInterval 受浏览器
|
|
1875
2390
|
// 后台节流且随组件卸载销毁,无法兑现「定时自动更新」承诺)
|
|
2391
|
+
// Tab 状态校正:SWR 后台同步触发 data 重渲染时,renderGithubRepoView 的
|
|
2392
|
+
// hidden/active 按默认 Tab 重算——用户手动切换的选择会被拽回。此 effect
|
|
2393
|
+
// 在每次 data 变化后按容器记录的 data-active-tab(用户最后选择)重新应用。
|
|
2394
|
+
React.useEffect(() => {
|
|
2395
|
+
var container = containerRef.current;
|
|
2396
|
+
if (!container)
|
|
2397
|
+
return;
|
|
2398
|
+
var active = container.getAttribute("data-active-tab");
|
|
2399
|
+
if (active)
|
|
2400
|
+
applyTabState(container, active);
|
|
2401
|
+
}, [data]);
|
|
1876
2402
|
// 窗口切回自动探测自愈:优先轻量快速探针,检测到已登录立即自愈
|
|
1877
2403
|
React.useEffect(() => {
|
|
1878
2404
|
var onFocus = () => {
|
|
@@ -1890,8 +2416,130 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1890
2416
|
window.addEventListener("focus", onFocus);
|
|
1891
2417
|
return () => window.removeEventListener("focus", onFocus);
|
|
1892
2418
|
}, [data, loadData]);
|
|
2419
|
+
// 设置弹窗(右侧栏独立实例):插件徽标 → 直开「关于」分区;⚙️ 按钮 → 默认
|
|
2420
|
+
// 「数据同步」。与全景主页复用同一弹窗组件,同步设置经事件总线实时对齐两侧。
|
|
2421
|
+
var [aboutModalOpen, setAboutModalOpen] = React.useState(false);
|
|
2422
|
+
var [rightbarModalOpen, setRightbarModalOpen] = React.useState(false);
|
|
2423
|
+
var [rightbarModalSection, setRightbarModalSection] = React.useState("sync");
|
|
2424
|
+
var [selectedCommit, setSelectedCommit] = React.useState(null);
|
|
2425
|
+
var [extraCommits, setExtraCommits] = React.useState([]);
|
|
2426
|
+
var [loadingMore, setLoadingMore] = React.useState(false);
|
|
2427
|
+
var [hasMoreCommits, setHasMoreCommits] = React.useState(true);
|
|
2428
|
+
var containerRef = React.useRef(null);
|
|
2429
|
+
// 工作区路径切换或刷新数据时重置分页状态
|
|
2430
|
+
React.useEffect(function () {
|
|
2431
|
+
setExtraCommits([]);
|
|
2432
|
+
setHasMoreCommits(true);
|
|
2433
|
+
}, [cwd, data]);
|
|
2434
|
+
var loadMoreCommits = function () {
|
|
2435
|
+
if (loadingMore || !hasMoreCommits)
|
|
2436
|
+
return;
|
|
2437
|
+
var baseCount = (data?.commitLog?.recent?.length || 0);
|
|
2438
|
+
var currentCount = baseCount + extraCommits.length;
|
|
2439
|
+
var total = data?.commitLog?.totalCount;
|
|
2440
|
+
if (total !== undefined && currentCount >= total) {
|
|
2441
|
+
setHasMoreCommits(false);
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
setLoadingMore(true);
|
|
2445
|
+
var url = "/api/github/git-log?limit=15&skip=" + currentCount + (cwd ? "&cwd=" + encodeURIComponent(cwd) : "");
|
|
2446
|
+
fetch(url)
|
|
2447
|
+
.then(function (r) { return r.json(); })
|
|
2448
|
+
.then(function (j) {
|
|
2449
|
+
setLoadingMore(false);
|
|
2450
|
+
if (j && j.ok && j.data && Array.isArray(j.data.commits)) {
|
|
2451
|
+
if (j.data.commits.length === 0) {
|
|
2452
|
+
setHasMoreCommits(false);
|
|
2453
|
+
}
|
|
2454
|
+
else {
|
|
2455
|
+
setExtraCommits(function (prev) { return prev.concat(j.data.commits); });
|
|
2456
|
+
if (j.data.totalCount !== undefined && (currentCount + j.data.commits.length) >= j.data.totalCount) {
|
|
2457
|
+
setHasMoreCommits(false);
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
else {
|
|
2462
|
+
setHasMoreCommits(false);
|
|
2463
|
+
}
|
|
2464
|
+
})
|
|
2465
|
+
.catch(function () {
|
|
2466
|
+
setLoadingMore(false);
|
|
2467
|
+
});
|
|
2468
|
+
};
|
|
2469
|
+
// 监听右侧栏容器滚动触底:下滑时自动加载更多提交
|
|
2470
|
+
React.useEffect(function () {
|
|
2471
|
+
var el = containerRef.current;
|
|
2472
|
+
if (!el)
|
|
2473
|
+
return;
|
|
2474
|
+
var onScroll = function () {
|
|
2475
|
+
var activeTab = el.getAttribute("data-active-tab");
|
|
2476
|
+
if (activeTab && activeTab !== "git")
|
|
2477
|
+
return;
|
|
2478
|
+
var threshold = 80;
|
|
2479
|
+
if (el.scrollHeight - el.scrollTop - el.clientHeight < threshold) {
|
|
2480
|
+
loadMoreCommits();
|
|
2481
|
+
}
|
|
2482
|
+
};
|
|
2483
|
+
el.addEventListener("scroll", onScroll, { passive: true });
|
|
2484
|
+
return function () { el.removeEventListener("scroll", onScroll); };
|
|
2485
|
+
}, [data, extraCommits, loadingMore, hasMoreCommits, cwd]);
|
|
2486
|
+
React.useEffect(() => {
|
|
2487
|
+
// 多宿主同挂载防叠层:全景主页与右侧栏可能同时在 DOM 中,徽标事件若被
|
|
2488
|
+
// 两处同时响应会出现双层弹窗。只有「自身容器可见」的宿主才响应——
|
|
2489
|
+
// 徽标所在面板必然是用户当前看到的那个。
|
|
2490
|
+
var onOpenAbout = () => {
|
|
2491
|
+
var el = containerRef.current;
|
|
2492
|
+
if (!el)
|
|
2493
|
+
return;
|
|
2494
|
+
var rect = el.getBoundingClientRect();
|
|
2495
|
+
var visible = rect.width > 0 && rect.height > 0;
|
|
2496
|
+
if (visible)
|
|
2497
|
+
setAboutModalOpen(true);
|
|
2498
|
+
};
|
|
2499
|
+
window.addEventListener("dsh-github-open-about", onOpenAbout);
|
|
2500
|
+
return () => window.removeEventListener("dsh-github-open-about", onOpenAbout);
|
|
2501
|
+
}, []);
|
|
2502
|
+
var openRightbarSettings = () => {
|
|
2503
|
+
setRightbarModalSection("sync");
|
|
2504
|
+
setRightbarModalOpen(true);
|
|
2505
|
+
};
|
|
1893
2506
|
var h = React.createElement;
|
|
1894
|
-
return h("div", { className: "dsh-github-rightbar-container" }, ...renderGithubRepoView(h, data, loading, error, () => loadData(true), true, updateInfo, syncStatus, lastSyncedAt))
|
|
2507
|
+
return h("div", { className: "dsh-github-rightbar-container", ref: containerRef }, ...renderGithubRepoView(h, data, loading, error, () => loadData(true), true, updateInfo, syncStatus, lastSyncedAt, openRightbarSettings, cwd, (c) => setSelectedCommit(c), {
|
|
2508
|
+
extraCommits: extraCommits,
|
|
2509
|
+
loadingMore: loadingMore,
|
|
2510
|
+
hasMore: hasMoreCommits,
|
|
2511
|
+
onLoadMore: loadMoreCommits,
|
|
2512
|
+
}),
|
|
2513
|
+
// 弹窗一:插件徽标触发的「关于」单页展示
|
|
2514
|
+
h(GlobalSyncSettingsModal, {
|
|
2515
|
+
isOpen: aboutModalOpen,
|
|
2516
|
+
initialSection: "about",
|
|
2517
|
+
onClose: () => setAboutModalOpen(false),
|
|
2518
|
+
currentSettings: syncSettings,
|
|
2519
|
+
onSave: function () { },
|
|
2520
|
+
onClearCache: function () { setData(null); },
|
|
2521
|
+
}),
|
|
2522
|
+
// 弹窗二:⚙️ 按钮触发的完整设置(与主页同一弹窗组件,分区可自由切换)
|
|
2523
|
+
h(GlobalSyncSettingsModal, {
|
|
2524
|
+
isOpen: rightbarModalOpen,
|
|
2525
|
+
initialSection: rightbarModalSection,
|
|
2526
|
+
onClose: () => setRightbarModalOpen(false),
|
|
2527
|
+
currentSettings: syncSettings,
|
|
2528
|
+
onSave: function (newSettings) { setSyncSettings(newSettings); },
|
|
2529
|
+
onClearCache: function () {
|
|
2530
|
+
setData(null);
|
|
2531
|
+
setLastSyncedAt(0);
|
|
2532
|
+
setSyncStatus("loading");
|
|
2533
|
+
},
|
|
2534
|
+
}),
|
|
2535
|
+
// 弹窗三:点击 Git 提交记录行的详情模态弹窗(与设置弹窗同构,在右侧栏容器内天然居中)
|
|
2536
|
+
selectedCommit
|
|
2537
|
+
? h(CommitDetailModal, {
|
|
2538
|
+
commit: selectedCommit,
|
|
2539
|
+
onClose: function () { setSelectedCommit(null); },
|
|
2540
|
+
cwd: cwd,
|
|
2541
|
+
})
|
|
2542
|
+
: null);
|
|
1895
2543
|
}
|
|
1896
2544
|
// 2. 右侧栏 Tab 标题组件
|
|
1897
2545
|
function GithubRightbarTitle() {
|
|
@@ -1907,22 +2555,142 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1907
2555
|
const active = Boolean(props && props.active);
|
|
1908
2556
|
return React.createElement("div", {
|
|
1909
2557
|
className: "dsh-github-icon" + (active ? " active" : ""),
|
|
1910
|
-
title: "GitHub
|
|
2558
|
+
title: "GitHub 全景驾驶舱(dsh-github-flow 插件)",
|
|
1911
2559
|
}, React.createElement("div", {
|
|
1912
2560
|
className: "dsh-github-icon-inner" + (active ? " active" : "")
|
|
1913
2561
|
}, renderOctocat(18, "currentColor")));
|
|
1914
2562
|
}
|
|
1915
|
-
//
|
|
2563
|
+
// 提交详情模态弹窗组件(与设置弹窗同构:使用相同的 modal-overlay 与 modal-dialog,
|
|
2564
|
+
// 在右侧栏宿主内天然居中展示,不突破右侧容器边界)。
|
|
2565
|
+
// 数据源优先来自本地 git 预取(c.sha/fullSha/author/date/subject/body/stat,0ms 纯本地秒开),
|
|
2566
|
+
// 缺失时静默向后端拉取 git show 原文补充,弹窗内完整支持上下滚动浏览超长提交说明。
|
|
2567
|
+
function CommitDetailModal(props) {
|
|
2568
|
+
if (!props.commit)
|
|
2569
|
+
return null;
|
|
2570
|
+
var c = props.commit;
|
|
2571
|
+
var h = React.createElement;
|
|
2572
|
+
var [fetchedDetail, setFetchedDetail] = React.useState(null);
|
|
2573
|
+
var [fetchingDetail, setFetchingDetail] = React.useState(false);
|
|
2574
|
+
React.useEffect(function () {
|
|
2575
|
+
var onKeyDown = function (e) {
|
|
2576
|
+
if (e.key === "Escape" || e.keyCode === 27)
|
|
2577
|
+
props.onClose();
|
|
2578
|
+
};
|
|
2579
|
+
window.addEventListener("keydown", onKeyDown);
|
|
2580
|
+
return function () { window.removeEventListener("keydown", onKeyDown); };
|
|
2581
|
+
}, [props.onClose]);
|
|
2582
|
+
// 兜底补全机制:若内存中无 body/stat(如 DSH 尚未重启加载新解析逻辑),静默单查
|
|
2583
|
+
React.useEffect(function () {
|
|
2584
|
+
setFetchedDetail(null);
|
|
2585
|
+
if (c.body || c.stat)
|
|
2586
|
+
return;
|
|
2587
|
+
setFetchingDetail(true);
|
|
2588
|
+
var url = "/api/github/commit-detail?sha=" + encodeURIComponent(c.sha) + (props.cwd ? "&cwd=" + encodeURIComponent(props.cwd) : "");
|
|
2589
|
+
fetch(url)
|
|
2590
|
+
.then(function (r) { return r.json(); })
|
|
2591
|
+
.then(function (j) {
|
|
2592
|
+
setFetchingDetail(false);
|
|
2593
|
+
if (j && j.ok && j.detail)
|
|
2594
|
+
setFetchedDetail(j.detail);
|
|
2595
|
+
})
|
|
2596
|
+
.catch(function () { setFetchingDetail(false); });
|
|
2597
|
+
}, [c.sha, props.cwd]);
|
|
2598
|
+
var effectiveBody = c.body;
|
|
2599
|
+
var effectiveStat = c.stat;
|
|
2600
|
+
if (!effectiveBody && !effectiveStat && fetchedDetail) {
|
|
2601
|
+
effectiveStat = fetchedDetail;
|
|
2602
|
+
}
|
|
2603
|
+
return h("div", {
|
|
2604
|
+
className: "dsh-github-modal-overlay",
|
|
2605
|
+
onClick: props.onClose,
|
|
2606
|
+
style: { zIndex: 100000 },
|
|
2607
|
+
}, h("div", {
|
|
2608
|
+
className: "dsh-github-modal-dialog",
|
|
2609
|
+
style: { width: "94%", maxWidth: "460px", maxHeight: "84vh", display: "flex", flexDirection: "column", overflow: "hidden" },
|
|
2610
|
+
onClick: function (e) { e.stopPropagation(); },
|
|
2611
|
+
}, h("div", { className: "dsh-github-modal-header", style: { flexShrink: 0 } }, h("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, renderGitIcon(15, "#1296db"), h("h3", { style: { margin: 0, fontSize: "14px", fontWeight: 600 } }, "提交详情")), h("button", {
|
|
2612
|
+
style: { background: "none", border: "none", cursor: "pointer", fontSize: "18px", color: "var(--dsw-alias-label-secondary, #656d76)", padding: "0 4px" },
|
|
2613
|
+
onClick: props.onClose,
|
|
2614
|
+
title: "关闭 (Esc)",
|
|
2615
|
+
}, "✕")), h("div", {
|
|
2616
|
+
className: "dsh-github-modal-body",
|
|
2617
|
+
style: { padding: "16px", overflowY: "auto", flex: 1, minHeight: 0, display: "flex", flexDirection: "column", gap: "12px", boxSizing: "border-box" },
|
|
2618
|
+
}, h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: "11px", opacity: 0.85 } }, h("span", { style: { fontFamily: "monospace", fontWeight: 600, userSelect: "all" }, title: "完整 SHA: " + (c.fullSha || c.sha) }, c.fullSha || c.sha), h("span", null, c.relative)), h("div", { style: { fontSize: "13px", fontWeight: 600, color: "var(--dsw-alias-label-primary, #1f2328)", wordBreak: "break-word", lineHeight: "1.45" } }, c.subject), h("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, `提交者: ${c.author}${c.email ? ` <${c.email}>` : ""}${c.date ? ` · ${c.date}` : ""}`), effectiveBody
|
|
2619
|
+
? h("div", {
|
|
2620
|
+
style: {
|
|
2621
|
+
fontSize: "12px",
|
|
2622
|
+
lineHeight: "1.6",
|
|
2623
|
+
whiteSpace: "pre-wrap",
|
|
2624
|
+
wordBreak: "break-word",
|
|
2625
|
+
padding: "10px 12px",
|
|
2626
|
+
borderRadius: "6px",
|
|
2627
|
+
background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
|
|
2628
|
+
border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
|
|
2629
|
+
},
|
|
2630
|
+
}, effectiveBody)
|
|
2631
|
+
: null, effectiveStat
|
|
2632
|
+
? h("div", null, h("div", { style: { fontSize: "11px", fontWeight: 600, marginBottom: "4px", opacity: 0.8 } }, "📁 文件变更统计:"), h("pre", {
|
|
2633
|
+
className: "dsh-github-commit-pre",
|
|
2634
|
+
style: {
|
|
2635
|
+
fontSize: "11px",
|
|
2636
|
+
lineHeight: "1.5",
|
|
2637
|
+
whiteSpace: "pre-wrap",
|
|
2638
|
+
wordBreak: "break-word",
|
|
2639
|
+
margin: 0,
|
|
2640
|
+
maxHeight: "34vh",
|
|
2641
|
+
overflowY: "auto",
|
|
2642
|
+
padding: "8px 10px",
|
|
2643
|
+
borderRadius: "6px",
|
|
2644
|
+
background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
|
|
2645
|
+
border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
|
|
2646
|
+
},
|
|
2647
|
+
}, effectiveStat))
|
|
2648
|
+
: null, !effectiveBody && !effectiveStat
|
|
2649
|
+
? (fetchingDetail
|
|
2650
|
+
? h("div", { style: { fontSize: "12px", opacity: 0.7, padding: "8px 0" } }, "⏳ 正在获取详细变更说明…")
|
|
2651
|
+
: h("div", { style: { fontSize: "11px", opacity: 0.6, fontStyle: "italic", padding: "6px 0" } }, "(该提交未包含多行正文;重启 DSH 桌面端可同步最新格式并直接呈现变更统计)"))
|
|
2652
|
+
: null)));
|
|
2653
|
+
}
|
|
2654
|
+
// 全景驾驶舱「⚙️ 设置」模态弹窗组件:左侧分区导航(数据同步 / 网络代理 / 更新设置 / 关于)+ 右侧内容区。
|
|
1916
2655
|
// 为什么改多分区:弹窗承载能力从单一「同步偏好」扩展为多项插件设置(后续可继续加),
|
|
1917
2656
|
// 单页滚动堆叠会让用户找不到入口;分区导航让每项设置拥有独立心智空间与保存动作。
|
|
2657
|
+
// initialSection:宿主可指定初始分区——插件徽标点击时直开「关于」,单独展示身份信息。
|
|
1918
2658
|
function GlobalSyncSettingsModal(props) {
|
|
1919
2659
|
if (!props.isOpen)
|
|
1920
2660
|
return null;
|
|
1921
|
-
// ---- 当前激活分区:sync
|
|
1922
|
-
var [activeSection, setActiveSection] = React.useState("sync");
|
|
2661
|
+
// ---- 当前激活分区:sync / proxy / update / about ----
|
|
2662
|
+
var [activeSection, setActiveSection] = React.useState(props.initialSection || "sync");
|
|
1923
2663
|
var [strategy, setStrategy] = React.useState(props.currentSettings.strategy || "swr");
|
|
1924
2664
|
var [cacheMeta, setCacheMeta] = React.useState(getCacheStorageMeta());
|
|
1925
2665
|
var [clearTip, setClearTip] = React.useState("");
|
|
2666
|
+
// ---- 更新设置分区状态 ----
|
|
2667
|
+
// 打开弹窗时从后端拉取真实模式(权威源是 update-settings.json 而非 localStorage),
|
|
2668
|
+
// 与代理分区同范式:避免多窗口/外部手改文件导致 UI 与实际生效配置漂移。
|
|
2669
|
+
var [updateMode, setUpdateMode] = React.useState("official");
|
|
2670
|
+
var [updateBusy, setUpdateBusy] = React.useState(false);
|
|
2671
|
+
var [updateMsg, setUpdateMsg] = React.useState(null);
|
|
2672
|
+
// ---- 关于分区:当前运行版本号(弹窗打开时轻量拉取,失败静默回落占位)----
|
|
2673
|
+
var [aboutVersion, setAboutVersion] = React.useState(null);
|
|
2674
|
+
React.useEffect(function () {
|
|
2675
|
+
if (!props.isOpen)
|
|
2676
|
+
return;
|
|
2677
|
+
fetch("/api/github/update-settings")
|
|
2678
|
+
.then(function (r) { return r.json(); })
|
|
2679
|
+
.then(function (j) {
|
|
2680
|
+
if (j && j.ok && j.data && j.data.mode) {
|
|
2681
|
+
setUpdateMode(j.data.mode);
|
|
2682
|
+
}
|
|
2683
|
+
})
|
|
2684
|
+
.catch(function () { });
|
|
2685
|
+
// 版本号:check-update 命中 24h 缓存时零成本;失败静默(关于页显示占位版本)
|
|
2686
|
+
fetch("/api/github/check-update")
|
|
2687
|
+
.then(function (r) { return r.json(); })
|
|
2688
|
+
.then(function (j) {
|
|
2689
|
+
if (j && j.ok && j.data && j.data.currentVersion)
|
|
2690
|
+
setAboutVersion(j.data.currentVersion);
|
|
2691
|
+
})
|
|
2692
|
+
.catch(function () { });
|
|
2693
|
+
}, [props.isOpen]);
|
|
1926
2694
|
// ---- 网络代理分区状态 ----
|
|
1927
2695
|
// 打开弹窗时从后端拉取真实代理状态(权威源是 proxy.json 而非 localStorage),
|
|
1928
2696
|
// 避免多窗口/外部手改文件导致的 UI 与实际生效配置漂移。
|
|
@@ -1960,6 +2728,55 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1960
2728
|
window.addEventListener("keydown", onKeyDown);
|
|
1961
2729
|
return function () { window.removeEventListener("keydown", onKeyDown); };
|
|
1962
2730
|
}, [props.onClose]);
|
|
2731
|
+
// initialSection 同步:弹窗实例常驻(return null 在 hooks 后,不卸载),
|
|
2732
|
+
// useState 初值只在首挂载取一次——宿主切换入口重开时(如齿轮→关→徽标),
|
|
2733
|
+
// 必须显式把 activeSection 对齐到新的 initialSection,否则停留在旧分区。
|
|
2734
|
+
React.useEffect(function () {
|
|
2735
|
+
if (props.isOpen && props.initialSection) {
|
|
2736
|
+
setActiveSection(props.initialSection);
|
|
2737
|
+
}
|
|
2738
|
+
}, [props.isOpen, props.initialSection]);
|
|
2739
|
+
// 更新源保存:POST 后端(写盘 + 热切换 checker + 立即用新源真探)。
|
|
2740
|
+
// 返回的 probe 结果如实展示:探测失败提示网络问题而非假报「已生效无异常」。
|
|
2741
|
+
var handleUpdateSave = function () {
|
|
2742
|
+
setUpdateBusy(true);
|
|
2743
|
+
setUpdateMsg(null);
|
|
2744
|
+
fetch("/api/github/update-settings", {
|
|
2745
|
+
method: "POST",
|
|
2746
|
+
headers: { "Content-Type": "application/json" },
|
|
2747
|
+
body: JSON.stringify({ mode: updateMode }),
|
|
2748
|
+
})
|
|
2749
|
+
.then(function (r) { return r.json(); })
|
|
2750
|
+
.then(function (j) {
|
|
2751
|
+
setUpdateBusy(false);
|
|
2752
|
+
if (j && j.ok && j.data) {
|
|
2753
|
+
var d = j.data;
|
|
2754
|
+
var text = "";
|
|
2755
|
+
if (d.mode === "off") {
|
|
2756
|
+
text = "✅ 已关闭自动检查更新(/gh upgrade 与一键升级仍可用),无需重启";
|
|
2757
|
+
}
|
|
2758
|
+
else if (d.probe && d.probe.latestVersion) {
|
|
2759
|
+
text = "✅ 已切换至" + d.description + ",当前最新版本 v" + d.probe.latestVersion
|
|
2760
|
+
+ (d.probe.hasUpdate ? "(有可用更新)" : "(已是最新)") + ",无需重启";
|
|
2761
|
+
}
|
|
2762
|
+
else {
|
|
2763
|
+
// 探测失败如实告知:设置已生效但新源暂不可达(可能是网络或源同步问题)
|
|
2764
|
+
text = "✅ 已切换至" + d.description + ",但新源暂时不可达(版本探测失败,稍后会自动重试);设置已生效,无需重启";
|
|
2765
|
+
}
|
|
2766
|
+
if (d.persisted === false) {
|
|
2767
|
+
text += ";⚠️ 设置写盘失败,重启后需重新选择";
|
|
2768
|
+
}
|
|
2769
|
+
setUpdateMsg({ kind: "ok", text: text });
|
|
2770
|
+
}
|
|
2771
|
+
else {
|
|
2772
|
+
setUpdateMsg({ kind: "err", text: (j && j.message) || "保存失败" });
|
|
2773
|
+
}
|
|
2774
|
+
})
|
|
2775
|
+
.catch(function (e) {
|
|
2776
|
+
setUpdateBusy(false);
|
|
2777
|
+
setUpdateMsg({ kind: "err", text: "网络请求失败: " + (e && e.message ? e.message : e) });
|
|
2778
|
+
});
|
|
2779
|
+
};
|
|
1963
2780
|
var h = React.createElement;
|
|
1964
2781
|
var handleSave = function () {
|
|
1965
2782
|
var updated = {
|
|
@@ -2051,7 +2868,13 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2051
2868
|
}, "🔄 数据同步"), h("button", {
|
|
2052
2869
|
className: "dsh-github-settings-nav-item" + (activeSection === "proxy" ? " active" : ""),
|
|
2053
2870
|
onClick: function () { setActiveSection("proxy"); },
|
|
2054
|
-
}, "🌐 网络代理")
|
|
2871
|
+
}, "🌐 网络代理"), h("button", {
|
|
2872
|
+
className: "dsh-github-settings-nav-item" + (activeSection === "update" ? " active" : ""),
|
|
2873
|
+
onClick: function () { setActiveSection("update"); },
|
|
2874
|
+
}, "⬆️ 更新设置"), h("button", {
|
|
2875
|
+
className: "dsh-github-settings-nav-item" + (activeSection === "about" ? " active" : ""),
|
|
2876
|
+
onClick: function () { setActiveSection("about"); },
|
|
2877
|
+
}, "ℹ️ 关于")), 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 全景驾驶舱的数据拉取节奏。启用秒开模式可利用本地持久化缓存消除每次打开时的漫长等待。"),
|
|
2055
2878
|
// 刷新策略选择
|
|
2056
2879
|
h("div", { style: { marginBottom: "16px" } }, h("div", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "8px" } }, "数据刷新策略"),
|
|
2057
2880
|
// 策略 A: SWR 秒开
|
|
@@ -2076,7 +2899,63 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2076
2899
|
className: "dsh-github-btn-sm",
|
|
2077
2900
|
style: { padding: "3px 8px", fontSize: "11px", color: "var(--dsw-alias-state-error-primary, #cf222e)" },
|
|
2078
2901
|
onClick: handleClear,
|
|
2079
|
-
}, "🗑️ 清空缓存") : null), clearTip ? h("div", { style: { color: "var(--dsw-alias-state-success, #2ea043)", marginTop: "4px", fontWeight: 500 } }, clearTip) : null)) : h("div", null,
|
|
2902
|
+
}, "🗑️ 清空缓存") : null), clearTip ? h("div", { style: { color: "var(--dsw-alias-state-success, #2ea043)", marginTop: "4px", fontWeight: 500 } }, clearTip) : null)) : activeSection === "update" ? h("div", null,
|
|
2903
|
+
// ⬆️ 更新设置分区:选择版本检测的 npm 源(或关闭自动检查)。
|
|
2904
|
+
// 检测与升级流量均遵循「🌐 网络代理」分区设置——代理是全局的。
|
|
2905
|
+
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" } }, "更新源"),
|
|
2906
|
+
// 源 A: npm 官方(默认)
|
|
2907
|
+
h("div", {
|
|
2908
|
+
className: "dsh-github-radio-card" + (updateMode === "official" ? " active" : ""),
|
|
2909
|
+
onClick: function () { setUpdateMode("official"); },
|
|
2910
|
+
}, 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" } }, "版本全球首发最及时,刚发布的新版立即可见;国内直连可能较慢。"))),
|
|
2911
|
+
// 源 B: 国内镜像
|
|
2912
|
+
h("div", {
|
|
2913
|
+
className: "dsh-github-radio-card" + (updateMode === "mirror" ? " active" : ""),
|
|
2914
|
+
onClick: function () { setUpdateMode("mirror"); },
|
|
2915
|
+
}, 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" } }, "阿里云镜像,国内访问快;同步存在分钟级延迟,刚发布的新版可能暂时查不到。"))),
|
|
2916
|
+
// 源 C: 不检查更新
|
|
2917
|
+
h("div", {
|
|
2918
|
+
className: "dsh-github-radio-card" + (updateMode === "off" ? " active" : ""),
|
|
2919
|
+
onClick: function () { setUpdateMode("off"); },
|
|
2920
|
+
}, 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 命令与一键升级仍然可用。")))),
|
|
2921
|
+
// 保存按钮 + 结果反馈(按钮复用 star-btn 的双主题链接色样式:内联色
|
|
2922
|
+
// 会覆盖 btn-sm 暗色规则,且暗色下亮蓝底刺眼——与关于页修复同源)
|
|
2923
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" } }, h("button", {
|
|
2924
|
+
className: "dsh-github-btn-sm dsh-github-primary-btn",
|
|
2925
|
+
disabled: updateBusy,
|
|
2926
|
+
onClick: handleUpdateSave,
|
|
2927
|
+
}, updateBusy ? "验证中..." : "💾 保存并全局应用"), updateMsg ? h("span", {
|
|
2928
|
+
style: {
|
|
2929
|
+
fontSize: "12px",
|
|
2930
|
+
lineHeight: "1.5",
|
|
2931
|
+
color: updateMsg.kind === "ok"
|
|
2932
|
+
? "var(--dsw-alias-state-success, #2ea043)"
|
|
2933
|
+
: updateMsg.kind === "err"
|
|
2934
|
+
? "var(--dsw-alias-state-error-primary, #cf222e)"
|
|
2935
|
+
: "var(--dsw-alias-label-secondary, #656d76)",
|
|
2936
|
+
},
|
|
2937
|
+
}, updateMsg.text) : null)) : activeSection === "about" ? h("div", null,
|
|
2938
|
+
// ℹ️ 关于分区:项目信息/仓库地址/开源协议/求 Star。纯静态展示,零状态零请求。
|
|
2939
|
+
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 全景驾驶舱与一键跨平台升级。"),
|
|
2940
|
+
// 信息条目
|
|
2941
|
+
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"))),
|
|
2942
|
+
// 求 Star 卡片
|
|
2943
|
+
// 主题兼容:不用内联色——暗色主题的覆盖只能作用于 class 选择器;
|
|
2944
|
+
// 且此前的 canvas-subtle/border-muted 并非宿主真实令牌(全项目无他处使用),
|
|
2945
|
+
// 永远回落亮色值导致暗色下一块刺眼亮底。改用 cache-info-box 同款双主题 class。
|
|
2946
|
+
h("div", {
|
|
2947
|
+
className: "dsh-github-about-star-box",
|
|
2948
|
+
style: {
|
|
2949
|
+
display: "flex",
|
|
2950
|
+
alignItems: "center",
|
|
2951
|
+
gap: "10px",
|
|
2952
|
+
},
|
|
2953
|
+
}, h("span", { style: { fontSize: "18px" } }, "⭐"), h("div", { style: { flex: 1, fontSize: "12px", lineHeight: "1.5" } }, "如果这个插件对你的日常研发有帮助,欢迎到 GitHub 仓库点个 Star 支持一下——你的 Star 是持续迭代的动力!也欢迎提交 Issue 与 PR。"), h("a", {
|
|
2954
|
+
href: "https://github.com/ItQianChen/dsh-github-flow",
|
|
2955
|
+
target: "_blank",
|
|
2956
|
+
rel: "noopener noreferrer",
|
|
2957
|
+
className: "dsh-github-btn-sm dsh-github-primary-btn",
|
|
2958
|
+
}, "🌟 去点 Star"))) : h("div", null,
|
|
2080
2959
|
// 🌐 网络代理分区:设置保存即全局生效(驾驶舱/右侧栏/Agent 工具/升级链路),
|
|
2081
2960
|
// 未启用时回落系统环境代理或直连(「默认走系统设置」语义)。
|
|
2082
2961
|
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", {
|
|
@@ -2137,8 +3016,8 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2137
3016
|
: "var(--dsw-alias-label-secondary, #656d76)",
|
|
2138
3017
|
},
|
|
2139
3018
|
}, proxyMsg.text) : null))),
|
|
2140
|
-
// 底部操作条:sync 分区提供主保存动作;proxy 分区有自己的「保存并全局应用」,
|
|
2141
|
-
//
|
|
3019
|
+
// 底部操作条:sync 分区提供主保存动作;proxy/update 分区有自己的「保存并全局应用」,
|
|
3020
|
+
// 底部统一为「完成」关闭弹窗,避免多个保存按钮的语义冲突。
|
|
2142
3021
|
h("div", { className: "dsh-github-modal-footer" }, activeSection === "sync" ? h("button", {
|
|
2143
3022
|
className: "dsh-github-btn-sm",
|
|
2144
3023
|
style: {
|
|
@@ -2164,7 +3043,10 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2164
3043
|
var [loading, setLoading] = React.useState(!initialSnapshot);
|
|
2165
3044
|
var [syncStatus, setSyncStatus] = React.useState(initialSnapshot ? "cached" : "loading");
|
|
2166
3045
|
var [lastSyncedAt, setLastSyncedAt] = React.useState(initialSnapshot?.savedAt || 0);
|
|
3046
|
+
// 弹窗开闭 + 初始分区分离:插件徽标点击直开「关于」分区(initialSection=about),
|
|
3047
|
+
// 而齿轮按钮仍默认打开「数据同步」——同一弹窗两种入口语义。
|
|
2167
3048
|
var [isSettingsModalOpen, setIsSettingsModalOpen] = React.useState(false);
|
|
3049
|
+
var [settingsInitialSection, setSettingsInitialSection] = React.useState("sync");
|
|
2168
3050
|
var [error, setError] = React.useState(null);
|
|
2169
3051
|
var [updateInfo, setUpdateInfo] = React.useState(null);
|
|
2170
3052
|
var [activeTab, setActiveTab] = React.useState("workspaces"); // 'workspaces' | 'repos' | 'mywork'
|
|
@@ -2187,10 +3069,9 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2187
3069
|
.catch(function () { });
|
|
2188
3070
|
}, []);
|
|
2189
3071
|
React.useEffect(function () { refreshProxyBadge(); }, [refreshProxyBadge]);
|
|
2190
|
-
// gh
|
|
3072
|
+
// 检查更新与 gh 业务数据零依赖,挂载即并行发起(与右侧栏同策略):
|
|
3073
|
+
// 并发由服务端 inFlightPromise + 24h 缓存合流去重,无请求风暴风险。
|
|
2191
3074
|
React.useEffect(() => {
|
|
2192
|
-
if (!data)
|
|
2193
|
-
return;
|
|
2194
3075
|
var timer = setTimeout(function () {
|
|
2195
3076
|
fetch("/api/github/check-update")
|
|
2196
3077
|
.then(function (res) { return res.json(); })
|
|
@@ -2200,9 +3081,31 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2200
3081
|
}
|
|
2201
3082
|
})
|
|
2202
3083
|
.catch(function () { });
|
|
2203
|
-
},
|
|
3084
|
+
}, 300);
|
|
2204
3085
|
return function () { clearTimeout(timer); };
|
|
2205
|
-
}, [
|
|
3086
|
+
}, []);
|
|
3087
|
+
// 插件徽标事件:标题上的「插件」徽标点击 → 直开设置弹窗的「关于」分区。
|
|
3088
|
+
// 可见性守卫:右侧栏与主页可能同挂载,只有自身容器可见的宿主响应(防双层弹窗)。
|
|
3089
|
+
var panelRef = React.useRef(null);
|
|
3090
|
+
React.useEffect(() => {
|
|
3091
|
+
var onOpenAbout = () => {
|
|
3092
|
+
var el = panelRef.current;
|
|
3093
|
+
if (!el)
|
|
3094
|
+
return;
|
|
3095
|
+
var rect = el.getBoundingClientRect();
|
|
3096
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
3097
|
+
setSettingsInitialSection("about");
|
|
3098
|
+
setIsSettingsModalOpen(true);
|
|
3099
|
+
}
|
|
3100
|
+
};
|
|
3101
|
+
window.addEventListener("dsh-github-open-about", onOpenAbout);
|
|
3102
|
+
return () => window.removeEventListener("dsh-github-open-about", onOpenAbout);
|
|
3103
|
+
}, []);
|
|
3104
|
+
// 齿轮按钮:默认打开「数据同步」分区(既有语义不变)
|
|
3105
|
+
var openSettingsDefault = () => {
|
|
3106
|
+
setSettingsInitialSection("sync");
|
|
3107
|
+
setIsSettingsModalOpen(true);
|
|
3108
|
+
};
|
|
2206
3109
|
var loadData = React.useCallback(async (forceRefresh = false) => {
|
|
2207
3110
|
// 如果是纯手动模式,且非显式强制刷新,且本地已有旧数据,直接跳过后台拉取
|
|
2208
3111
|
if (!forceRefresh && syncSettings.strategy === "manual" && data) {
|
|
@@ -2215,8 +3118,10 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2215
3118
|
var timedOut = false;
|
|
2216
3119
|
var timer = setTimeout(function () { timedOut = true; controller.abort(); }, 45000);
|
|
2217
3120
|
try {
|
|
3121
|
+
// refresh 预置请求必须共享同一 signal:它一旦挂起(如系统代理劫持回环请求),
|
|
3122
|
+
// 后续 overview fetch 永远执行不到,loading 永不清除——无 gh 用户正是被此卡死。
|
|
2218
3123
|
if (forceRefresh)
|
|
2219
|
-
await fetch("/api/github/refresh", { method: "POST" }).catch(() => { });
|
|
3124
|
+
await fetch("/api/github/refresh", { method: "POST", signal: controller.signal }).catch(() => { });
|
|
2220
3125
|
var res = await fetch("/api/github/global-overview", { signal: controller.signal });
|
|
2221
3126
|
if (!res.ok) {
|
|
2222
3127
|
var errText = await res.text().catch(() => "");
|
|
@@ -2259,7 +3164,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2259
3164
|
}
|
|
2260
3165
|
else {
|
|
2261
3166
|
if (timedOut || err.name === "AbortError") {
|
|
2262
|
-
setError("获取超时(
|
|
3167
|
+
setError("获取超时(45 秒):本机 gh 命令响应过慢或网络代理劫持了回环请求。可稍后重试,或在「⚙️ 设置 → 🌐 网络代理」确认代理已放行 127.0.0.1。");
|
|
2263
3168
|
}
|
|
2264
3169
|
else {
|
|
2265
3170
|
setError("无法连接后端服务:" + (err.message || err));
|
|
@@ -2303,7 +3208,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2303
3208
|
return () => window.removeEventListener("focus", onFocus);
|
|
2304
3209
|
}, [data, loadData]);
|
|
2305
3210
|
var h = React.createElement;
|
|
2306
|
-
var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(32), h("h2", null, "GitHub 全景驾驶舱"), h(SyncStatusBadge, { syncStatus, lastSyncedAt }),
|
|
3211
|
+
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 }),
|
|
2307
3212
|
// 代理生效徽章:启用时可见,hover 展示代理地址,让用户随时确认「当前确实在走代理」
|
|
2308
3213
|
proxyActive && proxyActive.enabled
|
|
2309
3214
|
? h("span", {
|
|
@@ -2328,12 +3233,12 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2328
3233
|
disabled: loading,
|
|
2329
3234
|
}, loading ? "同步中..." : "🔄 刷新全局数据"), h("button", {
|
|
2330
3235
|
className: "dsh-github-btn-sm",
|
|
2331
|
-
onClick:
|
|
2332
|
-
title: "
|
|
3236
|
+
onClick: openSettingsDefault,
|
|
3237
|
+
title: "配置数据同步节奏、本地缓存、网络代理与插件信息",
|
|
2333
3238
|
style: { padding: "6px 12px" },
|
|
2334
3239
|
}, "⚙️ 设置")));
|
|
2335
3240
|
if (loading && !data) {
|
|
2336
|
-
return h("div", { className: "dsh-github-panel" }, headerEl, h("div", { className: "dsh-github-empty" }, "正在加载全局 GitHub 账号与工作区数据..."));
|
|
3241
|
+
return h("div", { className: "dsh-github-panel", ref: panelRef }, headerEl, h("div", { className: "dsh-github-empty" }, "正在加载全局 GitHub 账号与工作区数据..."));
|
|
2337
3242
|
}
|
|
2338
3243
|
var effectiveUpdateInfo = updateInfo || data?.updateInfo;
|
|
2339
3244
|
var upgradeBannerEl = effectiveUpdateInfo && effectiveUpdateInfo.hasUpdate
|
|
@@ -2341,6 +3246,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2341
3246
|
: null;
|
|
2342
3247
|
var settingsModalEl = h(GlobalSyncSettingsModal, {
|
|
2343
3248
|
isOpen: isSettingsModalOpen,
|
|
3249
|
+
initialSection: settingsInitialSection,
|
|
2344
3250
|
onClose: () => {
|
|
2345
3251
|
setIsSettingsModalOpen(false);
|
|
2346
3252
|
// 弹窗内可能刚保存过代理配置,关闭时同步刷新标题栏徽章
|
|
@@ -2358,7 +3264,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2358
3264
|
});
|
|
2359
3265
|
// 错误拦截阻断:若无本地数据且请求失败,直接呈现错误卡片,杜绝把接口异常伪装成「未安装 CLI」
|
|
2360
3266
|
if (!data && error) {
|
|
2361
|
-
return h("div", { className: "dsh-github-panel" }, headerEl, upgradeBannerEl, h("div", {
|
|
3267
|
+
return h("div", { className: "dsh-github-panel", ref: panelRef }, headerEl, upgradeBannerEl, h("div", {
|
|
2362
3268
|
style: {
|
|
2363
3269
|
padding: "24px",
|
|
2364
3270
|
borderRadius: "8px",
|
|
@@ -2388,7 +3294,12 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2388
3294
|
? h("span", { className: "dsh-github-badge dsh-badge-green" }, "Active")
|
|
2389
3295
|
: !auth?.installed
|
|
2390
3296
|
? h("span", { className: "dsh-github-badge dsh-badge-red" }, "Missing")
|
|
2391
|
-
: h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "Required"))), h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "账号云端仓库"), h("div", { className: "dsh-github-card-value" }, `${userRepos.length} 个 Repositories`)), h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "本地工作区联动"), h("div", { className: "dsh-github-card-value" }, `${workspaceMatrix.length} 个工作区 (${workspaceMatrix.filter((w) => w.repo).length} 个已关联)`))
|
|
3297
|
+
: h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "Required"))), h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "账号云端仓库"), h("div", { className: "dsh-github-card-value" }, `${userRepos.length} 个 Repositories`)), h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "本地工作区联动"), h("div", { className: "dsh-github-card-value" }, `${workspaceMatrix.length} 个工作区 (${workspaceMatrix.filter((w) => w.repo).length} 个已关联)`)),
|
|
3298
|
+
// 待办概览卡:issueStats 三分类(数据已有零成本)——第一屏直接呈现工作量,
|
|
3299
|
+
// 无待办时正向反馈「无待办 ✓」而非空白
|
|
3300
|
+
h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "📥 待处理 Issues"), (data && data.issueStats && (data.issueStats.reported || data.issueStats.assigned || data.issueStats.created))
|
|
3301
|
+
? h("div", { className: "dsh-github-card-value", style: { fontSize: "14px" } }, `📥 用户提单 ${data.issueStats.reported || 0} · 🎯 分配给我 ${data.issueStats.assigned || 0} · ✍️ 我创建 ${data.issueStats.created || 0}`)
|
|
3302
|
+
: h("div", { className: "dsh-github-card-value" }, "无待办 ✓")));
|
|
2392
3303
|
// 安装与登录引导组件(未登录或未安装时显式展开)
|
|
2393
3304
|
var authGuideEl = !auth?.loggedIn
|
|
2394
3305
|
? h(AuthGuideCard, { auth, onRefresh: () => loadData(true), isRightbar: false })
|
|
@@ -2400,7 +3311,22 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2400
3311
|
if (activeTab === 'workspaces') {
|
|
2401
3312
|
contentEl = h("div", { className: "dsh-github-list" }, workspaceMatrix.map((ws) => h("div", { key: ws.id, className: "dsh-github-card dsh-github-workspace-card" }, h("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, h("div", { className: "dsh-github-ws-title" }, ws.title), h("div", { className: "dsh-github-ws-path" }, ws.path), h("div", { style: { fontSize: "12px", marginTop: "4px", display: "flex", gap: "8px", alignItems: "center" } }, ws.repo
|
|
2402
3313
|
? h("span", { className: "dsh-github-ws-repo-link" }, renderOctocat(13, "currentColor"), `关联远程: ${ws.repo.nameWithOwner} (${ws.repo.defaultBranch})`)
|
|
2403
|
-
: h("span", { className: "dsh-github-ws-path" }, ws.hasGit ? "本地有 Git,未关联 GitHub 远程" : "本地无 Git 仓库"), ws.repo ? h("span", { className: "dsh-github-badge dsh-badge-green" }, "已关联") : null)
|
|
3314
|
+
: h("span", { className: "dsh-github-ws-path" }, ws.hasGit ? "本地有 Git,未关联 GitHub 远程" : "本地无 Git 仓库"), ws.repo ? h("span", { className: "dsh-github-badge dsh-badge-green" }, "已关联") : null),
|
|
3315
|
+
// git 本地状态摘要(ahead/behind 快照语义 + dirty;探测失败/非 Git 不渲染)
|
|
3316
|
+
ws.gitStatus
|
|
3317
|
+
? h("div", {
|
|
3318
|
+
style: { fontSize: "11px", display: "flex", gap: "8px", alignItems: "center", opacity: 0.85 },
|
|
3319
|
+
title: "ahead/behind 相对上次 git fetch 的快照(非实时)",
|
|
3320
|
+
}, h("span", { style: { display: "inline-flex", alignItems: "center", gap: "4px" } }, renderGitIcon(12, "#1296db"), ws.gitStatus.branch), ws.gitStatus.hasUpstream
|
|
3321
|
+
? h("span", null, `${ws.gitStatus.ahead > 0 ? "↑" + ws.gitStatus.ahead + " " : ""}${ws.gitStatus.behind > 0 ? "↓" + ws.gitStatus.behind : ""}${ws.gitStatus.ahead === 0 && ws.gitStatus.behind === 0 ? "✓ 同步" : ""}`)
|
|
3322
|
+
: h("span", null, "↑? ↓?"), ws.gitStatus.dirtyCount > 0
|
|
3323
|
+
? h("span", null, `${ws.gitStatus.dirtyCount} 文件未提交`)
|
|
3324
|
+
: h("span", null, "干净"))
|
|
3325
|
+
: null, ws.gitStatus && ws.gitStatus.dirtyFiles && ws.gitStatus.dirtyFiles.length > 0
|
|
3326
|
+
? h("details", { style: { fontSize: "11px" } }, h("summary", { style: { cursor: "pointer", opacity: 0.8 } }, `未提交清单(${ws.gitStatus.dirtyFiles.length})`), h("div", { style: { padding: "4px 8px", borderRadius: "6px", background: "var(--dsw-alias-bg-module-platform, #f6f8fa)", lineHeight: "1.6", marginTop: "4px" } }, ws.gitStatus.dirtyFiles.map(function (f, i) {
|
|
3327
|
+
return h("div", { key: i }, h("span", { style: { opacity: 0.7, marginRight: "6px" } }, f.status === "??" ? "??" : f.status), f.path);
|
|
3328
|
+
})))
|
|
3329
|
+
: null), h("div", { style: { display: "flex", gap: "8px" } }, ws.repo
|
|
2404
3330
|
? h("a", { className: "dsh-github-btn-sm", href: ws.repo.url, target: "_blank" }, "在 GitHub 打开")
|
|
2405
3331
|
: null))));
|
|
2406
3332
|
}
|
|
@@ -2415,7 +3341,9 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2415
3341
|
placeholder: "🔍 搜索我的 GitHub 仓库...",
|
|
2416
3342
|
value: searchQuery,
|
|
2417
3343
|
onChange: (e) => setSearchQuery(e.target.value)
|
|
2418
|
-
}), h("div", { className: "dsh-github-list" }, filteredRepos.map((r) => h("div", { key: r.nameWithOwner, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: r.url, target: "_blank" }, r.nameWithOwner), r.description ? h("div", { className: "dsh-github-item-desc" }, r.description) : null, h("div", { className: "dsh-github-item-meta" }, h("span", null, `默认分支: ${r.defaultBranch}`), r.isPrivate ? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "Private") : h("span", { className: "dsh-github-badge dsh-badge-green" }, "Public"), h("span", null, `⭐ ${r.stargazerCount}`))
|
|
3344
|
+
}), h("div", { className: "dsh-github-list" }, filteredRepos.map((r) => h("div", { key: r.nameWithOwner, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: r.url, target: "_blank" }, r.nameWithOwner), r.description ? h("div", { className: "dsh-github-item-desc" }, r.description) : null, h("div", { className: "dsh-github-item-meta" }, h("span", null, `默认分支: ${r.defaultBranch}`), r.isPrivate ? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "Private") : h("span", { className: "dsh-github-badge dsh-badge-green" }, "Public"), h("span", null, `⭐ ${r.stargazerCount}`), formatRelativeTime(r.updatedAt)
|
|
3345
|
+
? h("span", { title: `更新于 ${r.updatedAt}` }, `活跃于 ${formatRelativeTime(r.updatedAt)}`)
|
|
3346
|
+
: null)))));
|
|
2419
3347
|
}
|
|
2420
3348
|
}
|
|
2421
3349
|
else {
|
|
@@ -2448,7 +3376,14 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2448
3376
|
className: `dsh-github-pill-btn ${issueFilter === 'created' ? 'active' : ''}`,
|
|
2449
3377
|
onClick: () => setIssueFilter('created'),
|
|
2450
3378
|
}, `✍️ 我创建的 (${issueStats.created || 0})`));
|
|
2451
|
-
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"
|
|
3379
|
+
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", style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" } }, h("a", {
|
|
3380
|
+
className: "dsh-github-item-title",
|
|
3381
|
+
href: pr.url,
|
|
3382
|
+
target: "_blank",
|
|
3383
|
+
title: pr.updatedAt ? `更新于 ${pr.updatedAt}` : undefined,
|
|
3384
|
+
}, `${pr.isDraft ? "⚪ " : ""}[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`), pr.isDraft ? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "DRAFT") : null, formatRelativeTime(pr.updatedAt)
|
|
3385
|
+
? h("span", { style: { fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)", whiteSpace: "nowrap" } }, formatRelativeTime(pr.updatedAt))
|
|
3386
|
+
: null))), 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) => {
|
|
2452
3387
|
var cats = is.categories || [];
|
|
2453
3388
|
var badges = [];
|
|
2454
3389
|
if (cats.includes("reported")) {
|
|
@@ -2468,7 +3403,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2468
3403
|
})));
|
|
2469
3404
|
}
|
|
2470
3405
|
}
|
|
2471
|
-
return h("div", { className: "dsh-github-panel" }, headerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl);
|
|
3406
|
+
return h("div", { className: "dsh-github-panel", ref: panelRef }, headerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl);
|
|
2472
3407
|
}
|
|
2473
3408
|
var inject = ["slots", "sidebarRightTabs", "sessions", "layout", "theme"];
|
|
2474
3409
|
function apply(ctx) {
|
|
@@ -2500,6 +3435,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2500
3435
|
id: "dsh-github-flow",
|
|
2501
3436
|
kind: "github",
|
|
2502
3437
|
priority: "plugin",
|
|
3438
|
+
// 标题保持简洁,身份由面板内徽标标注(Tab 体系不支持内嵌徽标元素)
|
|
2503
3439
|
title: () => "GitHub 仓库",
|
|
2504
3440
|
guide: [
|
|
2505
3441
|
{
|
|
@@ -2507,7 +3443,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2507
3443
|
commandId: "workspace.github",
|
|
2508
3444
|
order: 40,
|
|
2509
3445
|
title: () => "GitHub 仓库",
|
|
2510
|
-
description: () => "查看与操作当前工作区目录下的 GitHub
|
|
3446
|
+
description: () => "查看与操作当前工作区目录下的 GitHub 仓库(由 dsh-github-flow 插件提供)",
|
|
2511
3447
|
icon: GithubArtwork,
|
|
2512
3448
|
},
|
|
2513
3449
|
],
|