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/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) => h("div", { key: pr.number, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: pr.url, target: "_blank" }, `#${pr.number} ${pr.title}`), h("div", { className: "dsh-github-item-meta" }, h("span", null, `${pr.headRefName} → ${pr.baseRefName}`), pr.mergeable === "CONFLICTING"
1697
- ? h("span", { className: "dsh-github-badge dsh-badge-red" }, "代码冲突")
1698
- : h("span", { className: "dsh-github-badge dsh-badge-green" }, "可合并"))))));
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", { className: "dsh-github-item-title", href: r.url, target: "_blank" }, r.name), h("div", { className: "dsh-github-item-meta" }, h("span", null, `分支: ${r.headBranch}`), r.conclusion === "success"
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
- return [headerEl, upgradeBannerEl, authGuideEl, repoCardEl, actionsBarEl, prSectionEl, issueSectionEl, runSectionEl].filter(Boolean);
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)), h("div", { style: { display: "flex", gap: "8px" } }, ws.repo
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" }, h("a", { className: "dsh-github-item-title", href: pr.url, target: "_blank" }, `[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`)))), h("h3", { className: "dsh-github-subheading" }, `📝 任务与待办 Issues (${filteredIssues.length})`), issuePillsEl, filteredIssues.length === 0 ? h("div", { className: "dsh-github-empty" }, "当前分类下暂无开放中的 Issue") : h("div", { className: "dsh-github-list" }, filteredIssues.map((is) => {
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")) {