dsh-github-flow 0.2.1 → 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 +38 -11
- package/README.md +37 -10
- package/docs/UPGRADE-NOTES.md +9 -9
- package/docs/architecture.md +53 -23
- package/docs/images/2.png +0 -0
- package/docs/npm-publish-guide.md +12 -1
- package/lib/api/routes-overview.js +104 -1
- package/lib/client.js +583 -14
- package/lib/executor.d.ts +118 -1
- package/lib/executor.js +357 -0
- package/lib/main.js +1 -0
- package/lib/renderers/markdown.d.ts +6 -0
- package/lib/renderers/markdown.js +109 -2
- package/lib/tools/help.js +132 -11
- package/lib/tools/issue.d.ts +9 -0
- package/lib/tools/issue.js +88 -4
- package/lib/tools/pr.d.ts +4 -0
- package/lib/tools/pr.js +50 -3
- package/lib/tools/repo.d.ts +8 -0
- package/lib/tools/repo.js +127 -3
- 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 +73 -0
- 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 重启时客户端加载崩溃并触发插件批量卸载——
|
|
@@ -1140,6 +1239,48 @@ body[data-ds-dark-theme] .dsh-github-primary-btn:hover {
|
|
|
1140
1239
|
body[data-ds-dark-theme] .dsh-github-radio {
|
|
1141
1240
|
accent-color: #58a6ff;
|
|
1142
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
|
+
}
|
|
1143
1284
|
`;
|
|
1144
1285
|
function injectStyles() {
|
|
1145
1286
|
var styleId = "dsh-plugin-github-styles";
|
|
@@ -1168,6 +1309,18 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1168
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"
|
|
1169
1310
|
}));
|
|
1170
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
|
+
}
|
|
1171
1324
|
// 现代暗黑与浅色自适应科技风勋章图标
|
|
1172
1325
|
function renderOctocatBadge(size = 32) {
|
|
1173
1326
|
return React.createElement("div", {
|
|
@@ -1605,7 +1758,7 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1605
1758
|
}, text);
|
|
1606
1759
|
}
|
|
1607
1760
|
// 渲染 GitHub 仓库信息与操作视图的核心函数
|
|
1608
|
-
function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false, updateInfo = null, syncStatus = "", lastSyncedAt = 0, onOpenSettings = null) {
|
|
1761
|
+
function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false, updateInfo = null, syncStatus = "", lastSyncedAt = 0, onOpenSettings = null, cwd = null, onSelectCommit = null, gitLogPagination = null) {
|
|
1609
1762
|
var auth = data ? data.auth : null;
|
|
1610
1763
|
var repo = data ? data.repo : null;
|
|
1611
1764
|
var prs = (data && data.pullRequests) || [];
|
|
@@ -1628,7 +1781,10 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1628
1781
|
title: "配置数据同步节奏、本地缓存、网络代理与插件信息",
|
|
1629
1782
|
style: { padding: "4px 10px" },
|
|
1630
1783
|
}, "⚙️") : null));
|
|
1631
|
-
//
|
|
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;
|
|
1632
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
|
|
1633
1789
|
? h("a", { href: repo.url, target: "_blank", className: "dsh-github-repo-link" }, repo.nameWithOwner)
|
|
1634
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
|
|
@@ -1638,6 +1794,138 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1638
1794
|
: null, !repo
|
|
1639
1795
|
? h("div", { className: "dsh-github-card-subtext" }, "若本地已有 git 仓库,请运行 `git remote add github <url>` 或 `git remote add origin <url>` 关联远程仓库。")
|
|
1640
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));
|
|
1641
1929
|
// 快捷操作栏 (方案 1: 官方黄金功能直达)
|
|
1642
1930
|
var actionsBarEl = h("div", { className: "dsh-github-quick-actions" },
|
|
1643
1931
|
// 1. 浏览器打开仓库主页
|
|
@@ -1693,9 +1981,28 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1693
1981
|
// PR 列表
|
|
1694
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
|
|
1695
1983
|
? h("div", { className: "dsh-github-empty" }, "当前无开放中的 PR")
|
|
1696
|
-
: h("div", { className: "dsh-github-list" }, prs.map((pr) =>
|
|
1697
|
-
|
|
1698
|
-
|
|
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
|
+
})));
|
|
1699
2006
|
// 仓库 Issues 列表
|
|
1700
2007
|
var issueSectionEl = h("div", { className: "dsh-github-section" }, h("div", { className: "dsh-github-section-header" }, h("h3", null, `📋 仓库 Issues (${issues.length})`), repo?.url
|
|
1701
2008
|
? h("a", {
|
|
@@ -1726,15 +2033,72 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
1726
2033
|
// Actions 流水线
|
|
1727
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
|
|
1728
2035
|
? h("div", { className: "dsh-github-empty" }, "暂无工作流记录")
|
|
1729
|
-
: 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"
|
|
1730
2042
|
? h("span", { className: "dsh-github-badge dsh-badge-green" }, "✓ 成功")
|
|
1731
2043
|
: r.conclusion === "failure"
|
|
1732
2044
|
? h("span", { className: "dsh-github-badge dsh-badge-red" }, "✕ 失败")
|
|
1733
|
-
: 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))));
|
|
1734
2054
|
var authGuideEl = !auth?.loggedIn
|
|
1735
2055
|
? h(AuthGuideCard, { auth, onRefresh, isRightbar })
|
|
1736
2056
|
: null;
|
|
1737
|
-
|
|
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);
|
|
1738
2102
|
}
|
|
1739
2103
|
// 全局持久化缓存与偏好配置键名
|
|
1740
2104
|
var GLOBAL_OVERVIEW_CACHE_KEY = "dsh_github_global_overview_snapshot_v1";
|
|
@@ -2024,6 +2388,17 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2024
2388
|
}, [cwd, sessionId, syncSettings.strategy]);
|
|
2025
2389
|
// (原定时轮询调度已随 'interval' 策略一并移除:前端 setInterval 受浏览器
|
|
2026
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]);
|
|
2027
2402
|
// 窗口切回自动探测自愈:优先轻量快速探针,检测到已登录立即自愈
|
|
2028
2403
|
React.useEffect(() => {
|
|
2029
2404
|
var onFocus = () => {
|
|
@@ -2046,7 +2421,68 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2046
2421
|
var [aboutModalOpen, setAboutModalOpen] = React.useState(false);
|
|
2047
2422
|
var [rightbarModalOpen, setRightbarModalOpen] = React.useState(false);
|
|
2048
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);
|
|
2049
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]);
|
|
2050
2486
|
React.useEffect(() => {
|
|
2051
2487
|
// 多宿主同挂载防叠层:全景主页与右侧栏可能同时在 DOM 中,徽标事件若被
|
|
2052
2488
|
// 两处同时响应会出现双层弹窗。只有「自身容器可见」的宿主才响应——
|
|
@@ -2068,7 +2504,12 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2068
2504
|
setRightbarModalOpen(true);
|
|
2069
2505
|
};
|
|
2070
2506
|
var h = React.createElement;
|
|
2071
|
-
return h("div", { className: "dsh-github-rightbar-container", ref: containerRef }, ...renderGithubRepoView(h, data, loading, error, () => loadData(true), true, updateInfo, syncStatus, lastSyncedAt, openRightbarSettings),
|
|
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
|
+
}),
|
|
2072
2513
|
// 弹窗一:插件徽标触发的「关于」单页展示
|
|
2073
2514
|
h(GlobalSyncSettingsModal, {
|
|
2074
2515
|
isOpen: aboutModalOpen,
|
|
@@ -2090,7 +2531,15 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2090
2531
|
setLastSyncedAt(0);
|
|
2091
2532
|
setSyncStatus("loading");
|
|
2092
2533
|
},
|
|
2093
|
-
})
|
|
2534
|
+
}),
|
|
2535
|
+
// 弹窗三:点击 Git 提交记录行的详情模态弹窗(与设置弹窗同构,在右侧栏容器内天然居中)
|
|
2536
|
+
selectedCommit
|
|
2537
|
+
? h(CommitDetailModal, {
|
|
2538
|
+
commit: selectedCommit,
|
|
2539
|
+
onClose: function () { setSelectedCommit(null); },
|
|
2540
|
+
cwd: cwd,
|
|
2541
|
+
})
|
|
2542
|
+
: null);
|
|
2094
2543
|
}
|
|
2095
2544
|
// 2. 右侧栏 Tab 标题组件
|
|
2096
2545
|
function GithubRightbarTitle() {
|
|
@@ -2111,6 +2560,97 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2111
2560
|
className: "dsh-github-icon-inner" + (active ? " active" : "")
|
|
2112
2561
|
}, renderOctocat(18, "currentColor")));
|
|
2113
2562
|
}
|
|
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
|
+
}
|
|
2114
2654
|
// 全景驾驶舱「⚙️ 设置」模态弹窗组件:左侧分区导航(数据同步 / 网络代理 / 更新设置 / 关于)+ 右侧内容区。
|
|
2115
2655
|
// 为什么改多分区:弹窗承载能力从单一「同步偏好」扩展为多项插件设置(后续可继续加),
|
|
2116
2656
|
// 单页滚动堆叠会让用户找不到入口;分区导航让每项设置拥有独立心智空间与保存动作。
|
|
@@ -2754,7 +3294,12 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2754
3294
|
? h("span", { className: "dsh-github-badge dsh-badge-green" }, "Active")
|
|
2755
3295
|
: !auth?.installed
|
|
2756
3296
|
? h("span", { className: "dsh-github-badge dsh-badge-red" }, "Missing")
|
|
2757
|
-
: 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" }, "无待办 ✓")));
|
|
2758
3303
|
// 安装与登录引导组件(未登录或未安装时显式展开)
|
|
2759
3304
|
var authGuideEl = !auth?.loggedIn
|
|
2760
3305
|
? h(AuthGuideCard, { auth, onRefresh: () => loadData(true), isRightbar: false })
|
|
@@ -2766,7 +3311,22 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2766
3311
|
if (activeTab === 'workspaces') {
|
|
2767
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
|
|
2768
3313
|
? h("span", { className: "dsh-github-ws-repo-link" }, renderOctocat(13, "currentColor"), `关联远程: ${ws.repo.nameWithOwner} (${ws.repo.defaultBranch})`)
|
|
2769
|
-
: 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
|
|
2770
3330
|
? h("a", { className: "dsh-github-btn-sm", href: ws.repo.url, target: "_blank" }, "在 GitHub 打开")
|
|
2771
3331
|
: null))));
|
|
2772
3332
|
}
|
|
@@ -2781,7 +3341,9 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2781
3341
|
placeholder: "🔍 搜索我的 GitHub 仓库...",
|
|
2782
3342
|
value: searchQuery,
|
|
2783
3343
|
onChange: (e) => setSearchQuery(e.target.value)
|
|
2784
|
-
}), 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)))));
|
|
2785
3347
|
}
|
|
2786
3348
|
}
|
|
2787
3349
|
else {
|
|
@@ -2814,7 +3376,14 @@ body[data-ds-dark-theme] .dsh-github-radio {
|
|
|
2814
3376
|
className: `dsh-github-pill-btn ${issueFilter === 'created' ? 'active' : ''}`,
|
|
2815
3377
|
onClick: () => setIssueFilter('created'),
|
|
2816
3378
|
}, `✍️ 我创建的 (${issueStats.created || 0})`));
|
|
2817
|
-
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) => {
|
|
2818
3387
|
var cats = is.categories || [];
|
|
2819
3388
|
var badges = [];
|
|
2820
3389
|
if (cats.includes("reported")) {
|