dsh-github-flow 0.3.0 → 0.4.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.
Files changed (94) hide show
  1. package/README.en.md +220 -143
  2. package/README.md +202 -124
  3. package/docs/UPGRADE-NOTES.md +14 -8
  4. package/docs/architecture.md +111 -129
  5. package/lib/api/routes-actions.js +117 -0
  6. package/lib/api/routes-overview.js +94 -2
  7. package/lib/cache.d.ts +4 -0
  8. package/lib/cache.js +14 -0
  9. package/lib/client.d.ts +13 -1
  10. package/lib/client.js +2906 -2475
  11. package/lib/executor/git-parser.d.ts +25 -0
  12. package/lib/executor/git-parser.js +143 -0
  13. package/lib/executor/platform-guide.d.ts +14 -0
  14. package/lib/executor/platform-guide.js +40 -0
  15. package/lib/executor/process-utils.d.ts +51 -0
  16. package/lib/executor/process-utils.js +162 -0
  17. package/lib/executor/version-utils.d.ts +12 -0
  18. package/lib/executor/version-utils.js +27 -0
  19. package/lib/executor.d.ts +45 -48
  20. package/lib/executor.js +184 -353
  21. package/lib/git-watcher.d.ts +36 -0
  22. package/lib/git-watcher.js +91 -0
  23. package/lib/main.js +10 -0
  24. package/lib/renderers/cache.d.ts +8 -0
  25. package/lib/renderers/cache.js +66 -0
  26. package/lib/renderers/common.d.ts +24 -0
  27. package/lib/renderers/common.js +88 -0
  28. package/lib/renderers/gist.d.ts +15 -0
  29. package/lib/renderers/gist.js +99 -0
  30. package/lib/renderers/issue.d.ts +4 -0
  31. package/lib/renderers/issue.js +70 -0
  32. package/lib/renderers/label.d.ts +4 -0
  33. package/lib/renderers/label.js +48 -0
  34. package/lib/renderers/markdown.d.ts +15 -32
  35. package/lib/renderers/markdown.js +15 -624
  36. package/lib/renderers/pr.d.ts +4 -0
  37. package/lib/renderers/pr.js +107 -0
  38. package/lib/renderers/release.d.ts +4 -0
  39. package/lib/renderers/release.js +74 -0
  40. package/lib/renderers/repo.d.ts +4 -0
  41. package/lib/renderers/repo.js +149 -0
  42. package/lib/renderers/ruleset.d.ts +4 -0
  43. package/lib/renderers/ruleset.js +74 -0
  44. package/lib/renderers/run.d.ts +4 -0
  45. package/lib/renderers/run.js +79 -0
  46. package/lib/renderers/skill.d.ts +4 -0
  47. package/lib/renderers/skill.js +61 -0
  48. package/lib/renderers/status.d.ts +23 -0
  49. package/lib/renderers/status.js +124 -0
  50. package/lib/tools/cache.d.ts +64 -0
  51. package/lib/tools/cache.js +186 -0
  52. package/lib/tools/gist.d.ts +155 -0
  53. package/lib/tools/gist.js +190 -0
  54. package/lib/tools/help.js +107 -6
  55. package/lib/tools/image-utils.d.ts +8 -0
  56. package/lib/tools/image-utils.js +34 -0
  57. package/lib/tools/issue.d.ts +8 -1
  58. package/lib/tools/issue.js +26 -4
  59. package/lib/tools/label.d.ts +80 -0
  60. package/lib/tools/label.js +237 -0
  61. package/lib/tools/pr.d.ts +8 -0
  62. package/lib/tools/pr.js +48 -3
  63. package/lib/tools/ruleset.d.ts +80 -0
  64. package/lib/tools/ruleset.js +177 -0
  65. package/lib/tools/status.d.ts +39 -0
  66. package/lib/tools/status.js +53 -0
  67. package/lib/types.d.ts +39 -0
  68. package/lib/ui/auth-guide-card.d.ts +4 -0
  69. package/lib/ui/auth-guide-card.js +161 -0
  70. package/lib/ui/branch-selector.d.ts +10 -0
  71. package/lib/ui/branch-selector.js +163 -0
  72. package/lib/ui/commit-modal.d.ts +8 -0
  73. package/lib/ui/commit-modal.js +89 -0
  74. package/lib/ui/icons.d.ts +7 -0
  75. package/lib/ui/icons.js +53 -0
  76. package/lib/ui/network-banner.d.ts +10 -0
  77. package/lib/ui/network-banner.js +54 -0
  78. package/lib/ui/storage.d.ts +26 -0
  79. package/lib/ui/storage.js +134 -0
  80. package/lib/ui/styles.d.ts +6 -0
  81. package/lib/ui/styles.js +1285 -0
  82. package/lib/ui/sync-settings-modal.d.ts +11 -0
  83. package/lib/ui/sync-settings-modal.js +350 -0
  84. package/lib/ui/sync-status-badge.d.ts +8 -0
  85. package/lib/ui/sync-status-badge.js +64 -0
  86. package/lib/ui/upgrade-card.d.ts +7 -0
  87. package/lib/ui/upgrade-card.js +197 -0
  88. package/lib/ui/utils.d.ts +10 -0
  89. package/lib/ui/utils.js +66 -0
  90. package/lib/ui/workspace-panel.d.ts +4 -0
  91. package/lib/ui/workspace-panel.js +464 -0
  92. package/lib/version.d.ts +1 -1
  93. package/lib/version.js +1 -1
  94. package/package.json +1 -1
@@ -0,0 +1,53 @@
1
+ import { renderStatusMarkdown, parseStatusOutput } from '../renderers/markdown.js';
2
+ export function createStatusTool(executor) {
3
+ return {
4
+ name: 'github_status',
5
+ description: '聚合展示当前登录用户的跨仓库全局待办与动态大盘:包括指派给我的 Issues、指派给我的 PR、等待我评审的 PR、近期 @提及 以及订阅仓库的最新动态。无需指定单仓,账号维度一站式概览。',
6
+ parameters: {
7
+ type: 'object',
8
+ properties: {
9
+ action: {
10
+ type: 'string',
11
+ enum: ['view'],
12
+ description: '操作类型,默认为 view(查看全局待办看板)。',
13
+ },
14
+ org: {
15
+ type: 'string',
16
+ description: '组织名称(可选,限定仅汇报指定组织内部的待办与动态)。',
17
+ },
18
+ exclude: {
19
+ type: 'array',
20
+ items: { type: 'string' },
21
+ description: '要排除的仓库名称列表 [owner/repo](可选,支持排除噪音仓库)。',
22
+ },
23
+ },
24
+ },
25
+ output: {
26
+ schema: { type: 'object' },
27
+ render(args, value) {
28
+ return [{ type: 'text', text: renderStatusMarkdown(args, value) }];
29
+ },
30
+ },
31
+ async execute(args, execContext) {
32
+ const cwd = execContext?.cwd || process.cwd();
33
+ const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
34
+ const statusCmd = ['status'];
35
+ if (args?.org) {
36
+ statusCmd.push('-o', String(args.org));
37
+ }
38
+ if (args?.exclude && Array.isArray(args.exclude)) {
39
+ for (const ex of args.exclude) {
40
+ statusCmd.push('-e', String(ex));
41
+ }
42
+ }
43
+ const res = await executor.run(statusCmd, { cwd, sessionId, rawText: true });
44
+ if (!res.ok)
45
+ throw new Error(res.error);
46
+ const parsed = parseStatusOutput(res.rawOutput || '');
47
+ return {
48
+ parsed,
49
+ rawText: res.rawOutput || '',
50
+ };
51
+ },
52
+ };
53
+ }
package/lib/types.d.ts CHANGED
@@ -37,6 +37,15 @@ export interface PluginConfig {
37
37
  */
38
38
  proxyUrl?: string;
39
39
  }
40
+ /** 数据同步与自动化感知偏好配置 */
41
+ export interface SyncSettings {
42
+ /** 刷新策略三选一:'swr' 秒开并在首屏拉新 | 'auto' SWR秒开+云端定时轮询 | 'manual' 仅手动点击拉取 */
43
+ strategy: 'swr' | 'auto' | 'manual';
44
+ /** 云端定时同步周期(毫秒,不少于 300,000 即 5 分钟,默认 600,000 即 10 分钟) */
45
+ cloudSyncIntervalMs: number;
46
+ /** 是否开启本地 .git 监控(感知代码修改、暂存与分支切换,仅右侧栏当前工作区生效,默认开启,用户可取消勾选) */
47
+ localGitWatch: boolean;
48
+ }
40
49
  export interface GhExecutionOptions {
41
50
  /** 命令执行的工作目录,优先使用当前 Agent 会话所在的 Workspace 物理路径 */
42
51
  cwd?: string;
@@ -91,6 +100,12 @@ export interface AuthStatus {
91
100
  scopes?: string[];
92
101
  installGuide?: AuthInstallGuide;
93
102
  raw?: string;
103
+ /** 网络是否发生异常(超时、DNS解析失败、连接被拒、代理阻断等) */
104
+ networkError?: boolean;
105
+ /** 网络异常的人类可读原因或排查建议 */
106
+ networkErrorReason?: string;
107
+ /** 本地是否已检测到持久化凭据(如 hosts.yml 或 Keyring) */
108
+ hasLocalToken?: boolean;
94
109
  }
95
110
  export interface RepoMetadata {
96
111
  nameWithOwner: string;
@@ -102,6 +117,20 @@ export interface RepoMetadata {
102
117
  isPrivate?: boolean;
103
118
  url?: string;
104
119
  }
120
+ /** Git 分支详情项(供分支切换器下拉列表消费) */
121
+ export interface GitBranchItem {
122
+ name: string;
123
+ isCurrent: boolean;
124
+ isRemote: boolean;
125
+ upstream?: string;
126
+ shortSha: string;
127
+ subject: string;
128
+ relativeTime: string;
129
+ }
130
+ export interface GitBranchesResult {
131
+ current: string;
132
+ branches: GitBranchItem[];
133
+ }
105
134
  /**
106
135
  * 本地 Git 工作区状态快照(ahead/behind 是 remote-tracking ref 的「上次 fetch」视图,
107
136
  * 非实时网络状态——UI 渲染时必须带上这一语义提示,否则会误导用户)。
@@ -220,6 +249,10 @@ export interface GitHubOverviewData {
220
249
  runs: WorkflowRunItem[];
221
250
  lastUpdated: string;
222
251
  error?: string;
252
+ /** 网络是否发生异常(超时、连接拒绝、代理阻断等) */
253
+ networkError?: boolean;
254
+ /** 网络异常的原因或提示 */
255
+ networkErrorReason?: string;
223
256
  /** 插件版本与升级信息(由内存缓存快速读取,不阻塞首屏业务) */
224
257
  updateInfo?: UpdateInfo;
225
258
  /** 本地 Git 状态(ahead/behind/dirty,remote-tracking 快照语义);非 Git 工作区为 undefined */
@@ -241,6 +274,8 @@ export interface GitHubOverviewData {
241
274
  url?: string;
242
275
  isDraft?: boolean;
243
276
  } | null;
277
+ /** 当前工作区的 Git 分支列表(供右侧栏分支选择器 0ms 秒开) */
278
+ branches?: GitBranchesResult | null;
244
279
  }
245
280
  export interface UserRepoItem {
246
281
  name: string;
@@ -301,6 +336,10 @@ export interface GlobalOverviewData {
301
336
  issueStats?: IssueStats;
302
337
  lastUpdated: string;
303
338
  error?: string;
339
+ /** 网络是否发生异常(超时、连接拒绝、代理阻断等) */
340
+ networkError?: boolean;
341
+ /** 网络异常的原因或提示 */
342
+ networkErrorReason?: string;
304
343
  /** 工作区矩阵读取失败的原因。区分「没有工作区」与「读不到工作区」,避免把故障显示成空状态 */
305
344
  workspaceError?: string;
306
345
  /** 插件版本与升级信息(由内存缓存快速读取,不阻塞首屏业务) */
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 认证与安装引导卡片组件(响应式自适应中央大面板与右侧栏)
3
+ */
4
+ export declare function AuthGuideCard(props: any): any;
@@ -0,0 +1,161 @@
1
+ /**
2
+ * 认证与安装引导卡片组件(响应式自适应中央大面板与右侧栏)
3
+ */
4
+ export function AuthGuideCard(props) {
5
+ var auth = props ? props.auth : null;
6
+ var onRefresh = props ? props.onRefresh : () => { };
7
+ var isRightbar = Boolean(props && props.isRightbar);
8
+ var [copied, setCopied] = React.useState(false);
9
+ var [launching, setLaunching] = React.useState(false);
10
+ var [toastMsg, setToastMsg] = React.useState(null);
11
+ var isNetwork = Boolean(auth?.networkError);
12
+ var notInstalled = !isNetwork && Boolean(auth && auth.installed === false);
13
+ var notLoggedIn = !isNetwork && !notInstalled && Boolean(!auth?.loggedIn);
14
+ var guide = auth?.installGuide;
15
+ var defaultCmd = isNetwork
16
+ ? "gh auth status"
17
+ : notInstalled
18
+ ? (guide?.command || "winget install --id GitHub.cli")
19
+ : "gh auth login";
20
+ var copyCmd = (cmd) => {
21
+ try {
22
+ if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
23
+ navigator.clipboard.writeText(cmd);
24
+ }
25
+ else {
26
+ var input = document.createElement("textarea");
27
+ input.value = cmd;
28
+ document.body.appendChild(input);
29
+ input.select();
30
+ document.execCommand("copy");
31
+ document.body.removeChild(input);
32
+ }
33
+ setCopied(true);
34
+ setTimeout(() => setCopied(false), 2000);
35
+ }
36
+ catch (e) {
37
+ console.warn("复制失败:", e);
38
+ }
39
+ };
40
+ var launchTerminal = async (cmd) => {
41
+ setLaunching(true);
42
+ setToastMsg(null);
43
+ try {
44
+ var res = await fetch("/api/github/action", {
45
+ method: "POST",
46
+ headers: { "Content-Type": "application/json" },
47
+ body: JSON.stringify({ action: "open_terminal", command: cmd })
48
+ });
49
+ var json = await res.json();
50
+ if (json.ok) {
51
+ setToastMsg("已唤起系统终端,请在弹出窗口中完成操作;若未弹出,可手动打开终端执行上方命令");
52
+ setTimeout(() => setToastMsg(null), 6000);
53
+ }
54
+ else {
55
+ setToastMsg("❌ " + (json.message || "唤起失败,请手动打开终端执行上方命令"));
56
+ }
57
+ }
58
+ catch (e) {
59
+ setToastMsg("无法连接后台服务,请手动打开终端执行上方命令");
60
+ }
61
+ finally {
62
+ setLaunching(false);
63
+ }
64
+ };
65
+ var openDownload = async () => {
66
+ var url = guide?.downloadUrl || "https://cli.github.com/";
67
+ try {
68
+ await fetch("/api/github/action", {
69
+ method: "POST",
70
+ headers: { "Content-Type": "application/json" },
71
+ body: JSON.stringify({ action: "open_browser", url })
72
+ });
73
+ }
74
+ catch (e) {
75
+ window.open(url, "_blank", "noopener,noreferrer");
76
+ }
77
+ };
78
+ var openProxySettings = () => {
79
+ try {
80
+ window.dispatchEvent(new CustomEvent("dsh-github-open-proxy"));
81
+ }
82
+ catch (e) { }
83
+ };
84
+ var h = React.createElement;
85
+ return h("div", {
86
+ className: "dsh-github-guide-card " + (isNetwork ? "warning" : notInstalled ? "danger" : "warning"),
87
+ style: isRightbar ? { padding: "12px", marginBottom: "12px" } : undefined
88
+ }, h("div", { className: "dsh-github-guide-header" }, isNetwork
89
+ ? "🌐 无法连接 GitHub 服务器"
90
+ : notInstalled
91
+ ? "⚠️ 未检测到 GitHub CLI (gh)"
92
+ : "🔑 GitHub 账号尚未登录", h("span", { className: "dsh-github-badge " + (isNetwork ? "dsh-badge-yellow" : notInstalled ? "dsh-badge-red" : "dsh-badge-yellow") }, isNetwork ? "网络受限" : notInstalled ? "未安装" : "未认证")), h("div", { className: "dsh-github-guide-desc" }, isNetwork
93
+ ? (auth?.networkErrorReason || "检测到本机已安装 GitHub CLI,但当前无法连接至 api.github.com(网络超时、DNS 无法解析或被系统代理阻断)。")
94
+ : notInstalled
95
+ ? (guide?.description || "DSH GitHub Flow 插件依赖官方 GitHub CLI 工具与云端进行安全交互,当前系统 PATH 中未检测到该工具。")
96
+ : "检测到本机已安装 GitHub CLI,请完成账号授权以解锁仓库管理、PR 审查和 Issues 待办。"), h("div", { className: "dsh-github-cmd-box" }, h("code", null, defaultCmd), h("button", {
97
+ className: "dsh-github-copy-btn " + (copied ? "copied" : ""),
98
+ onClick: () => copyCmd(defaultCmd),
99
+ title: "复制命令到剪贴板"
100
+ }, copied ? "✓ 已复制" : "📋 复制命令")), !notInstalled && !isRightbar
101
+ ? (isNetwork
102
+ ? h("ol", { className: "dsh-github-steps-list" }, h("li", null, "检查系统本地网络连接或 DNS 解析,确认能正常访问 github.com;"), h("li", null, "若使用了网络代理工具,可在下方点击「配置网络代理」填写 HTTP 或 SOCKS5 代理;"), h("li", null, "配置或恢复网络后,切回本面板或点击「重试连接」即可自动同步数据。"))
103
+ : h("ol", { className: "dsh-github-steps-list" }, h("li", null, "打开系统终端(PowerShell / CMD / Terminal),运行上述命令;"), h("li", null, "按提示依次选择「GitHub.com」➜「HTTPS」➜「Login with a web browser」;"), h("li", null, "按回车打开浏览器确认授权后,切回本页面即可自动恢复。")))
104
+ : null, h("div", { className: "dsh-github-guide-actions" }, isNetwork
105
+ ? h("button", {
106
+ className: "dsh-github-action-btn",
107
+ onClick: onRefresh,
108
+ title: "重新发起网络连接校验"
109
+ }, "🔄 重试网络连接")
110
+ : null, isNetwork
111
+ ? h("button", {
112
+ className: "dsh-github-action-btn",
113
+ onClick: openProxySettings,
114
+ title: "打开网络代理设置,配置 HTTP/SOCKS5 代理服务器"
115
+ }, "⚙️ 配置网络代理")
116
+ : null, h("button", {
117
+ className: "dsh-github-action-btn",
118
+ onClick: () => launchTerminal(defaultCmd),
119
+ disabled: launching,
120
+ title: "自动唤起系统终端并执行该命令"
121
+ }, launching ? "正在唤起..." : (isNetwork ? "💻 终端排查状态" : notInstalled ? "💻 唤起终端安装" : "💻 唤起终端登录")), notInstalled
122
+ ? h("button", {
123
+ className: "dsh-github-action-btn",
124
+ onClick: openDownload,
125
+ title: "打开 GitHub CLI 官方下载主页获取安装包"
126
+ }, "🌐 官网安装包 (.msi/.pkg)")
127
+ : null, !isNetwork
128
+ ? h("button", {
129
+ className: "dsh-github-action-btn",
130
+ onClick: function () {
131
+ setToastMsg("正在快速检测登录状态...");
132
+ var probeController = new AbortController();
133
+ var probeTimer = setTimeout(function () { probeController.abort(); }, 10000);
134
+ fetch("/api/github/auth?force=true", { signal: probeController.signal })
135
+ .then(function (res) { return res.json(); })
136
+ .then(function (json) {
137
+ clearTimeout(probeTimer);
138
+ if (json && json.ok && json.auth && json.auth.loggedIn) {
139
+ setToastMsg("✅ 已识别到 GitHub CLI 登录态,正在拉取数据...");
140
+ clearStoredGlobalOverview();
141
+ onRefresh();
142
+ }
143
+ else {
144
+ setToastMsg("⚠️ 暂未检测到登录凭据,请在终端执行 gh auth login 完成授权");
145
+ onRefresh();
146
+ }
147
+ })
148
+ .catch(function () {
149
+ clearTimeout(probeTimer);
150
+ setToastMsg("❌ 检测超时或后端不可达,请稍后重试");
151
+ onRefresh();
152
+ });
153
+ },
154
+ title: "重新检测登录与安装状态"
155
+ }, "🔄 刷新状态")
156
+ : null), toastMsg
157
+ ? h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-link, #0969da)", marginTop: "8px" } }, `ℹ️ ${toastMsg}`)
158
+ : null, h("div", { className: "dsh-github-guide-tip" }, notInstalled
159
+ ? "💡 提示:使用包管理器安装完成后,若未立即识别,可重启 DeepSeek Harness 应用刷新系统环境变量 PATH。"
160
+ : "💡 提示:凭证由官方 GitHub CLI 加密保存在操作系统安全凭据库中,授权完毕切回本窗口将自动识别刷新。"));
161
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 分支查看与快速切换下拉选择器组件
3
+ */
4
+ export declare function BranchSelectorDropdown(props: {
5
+ currentBranch: string;
6
+ cwd: string | null;
7
+ branchesData?: any;
8
+ onBranchSwitched?: (branch: string) => void;
9
+ showToast?: (msg: string) => void;
10
+ }): any;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * 分支查看与快速切换下拉选择器组件
3
+ */
4
+ export function BranchSelectorDropdown(props) {
5
+ var currentBranch = props.currentBranch || "main";
6
+ var cwd = props.cwd;
7
+ var branchesData = props.branchesData;
8
+ var onBranchSwitched = props.onBranchSwitched || (() => { });
9
+ var showToast = props.showToast || (() => { });
10
+ var [isOpen, setIsOpen] = React.useState(false);
11
+ var [loading, setLoading] = React.useState(false);
12
+ var [switching, setSwitching] = React.useState(false);
13
+ var [branches, setBranches] = React.useState([]);
14
+ var [search, setSearch] = React.useState("");
15
+ var [fetchError, setFetchError] = React.useState(null);
16
+ var containerRef = React.useRef(null);
17
+ React.useEffect(() => {
18
+ if (!isOpen)
19
+ return;
20
+ var handleClickOutside = (e) => {
21
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
22
+ setIsOpen(false);
23
+ }
24
+ };
25
+ document.addEventListener("mousedown", handleClickOutside);
26
+ return () => document.removeEventListener("mousedown", handleClickOutside);
27
+ }, [isOpen]);
28
+ var toggleOpen = () => {
29
+ if (isOpen) {
30
+ setIsOpen(false);
31
+ return;
32
+ }
33
+ setIsOpen(true);
34
+ setSearch("");
35
+ setFetchError(null);
36
+ if (branchesData && Array.isArray(branchesData.branches) && branchesData.branches.length > 0) {
37
+ setBranches(branchesData.branches);
38
+ setLoading(false);
39
+ return;
40
+ }
41
+ setLoading(true);
42
+ var url = "/api/github/branches" + (cwd ? "?cwd=" + encodeURIComponent(cwd) : "");
43
+ fetch(url)
44
+ .then(async (res) => {
45
+ if (!res.ok) {
46
+ var errText = await res.text().catch(() => "");
47
+ if (res.status === 404 || errText.includes("not found")) {
48
+ throw new Error("后端分支接口未注册 (404)。请重启 DSH 桌面端以加载新后端服务。");
49
+ }
50
+ throw new Error(`服务响应异常 (${res.status}): ${errText || res.statusText}`);
51
+ }
52
+ return res.json();
53
+ })
54
+ .then((json) => {
55
+ setLoading(false);
56
+ if (json && json.ok && json.data && Array.isArray(json.data.branches)) {
57
+ setBranches(json.data.branches);
58
+ }
59
+ else {
60
+ setFetchError(json.error || "获取分支列表失败");
61
+ }
62
+ })
63
+ .catch((err) => {
64
+ setLoading(false);
65
+ setFetchError(err.message || "读取分支失败");
66
+ });
67
+ };
68
+ var handleSwitch = async (targetBranch) => {
69
+ if (targetBranch === currentBranch || switching)
70
+ return;
71
+ setSwitching(true);
72
+ try {
73
+ var res = await fetch("/api/github/branch-switch", {
74
+ method: "POST",
75
+ headers: { "Content-Type": "application/json" },
76
+ body: JSON.stringify({ cwd, branch: targetBranch }),
77
+ }).catch(() => null);
78
+ if (!res || !res.ok) {
79
+ res = await fetch("/api/github/action", {
80
+ method: "POST",
81
+ headers: { "Content-Type": "application/json" },
82
+ body: JSON.stringify({ action: "switch_branch", cwd, branch: targetBranch }),
83
+ });
84
+ }
85
+ if (!res.ok) {
86
+ var errText = await res.text().catch(() => "");
87
+ showToast(`❌ 切换失败:${errText || res.statusText}`);
88
+ return;
89
+ }
90
+ var json = await res.json();
91
+ if (json && json.ok) {
92
+ setIsOpen(false);
93
+ showToast(`✅ 已切换到分支 ${json.currentBranch || targetBranch}`);
94
+ onBranchSwitched(json.currentBranch || targetBranch);
95
+ }
96
+ else {
97
+ showToast(`❌ 切换失败:${json.error || json.message || "未知错误"}`);
98
+ }
99
+ }
100
+ catch (e) {
101
+ showToast(`❌ 切换分支网络请求失败:${e.message || e}`);
102
+ }
103
+ finally {
104
+ setSwitching(false);
105
+ }
106
+ };
107
+ var filtered = branches.filter((b) => !search || b.name.toLowerCase().includes(search.toLowerCase()));
108
+ var localBranches = filtered.filter((b) => !b.isRemote);
109
+ var remoteBranches = filtered.filter((b) => b.isRemote);
110
+ var h = React.createElement;
111
+ return h("div", {
112
+ ref: containerRef,
113
+ style: { position: "relative", display: "inline-block" },
114
+ }, h("button", {
115
+ type: "button",
116
+ className: "dsh-github-branch-btn",
117
+ onClick: toggleOpen,
118
+ title: `当前分支: ${currentBranch}(点击可切换分支)`,
119
+ }, renderGitIcon(12, "currentColor"), h("span", { style: { maxWidth: "160px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, currentBranch), h("span", { style: { fontSize: "9px", opacity: 0.7, marginLeft: "1px" } }, "▾")), isOpen
120
+ ? h("div", { className: "dsh-github-branch-dropdown" }, h("div", { className: "dsh-github-branch-search" }, h("input", {
121
+ className: "dsh-github-branch-input",
122
+ type: "text",
123
+ placeholder: "搜索分支 (输入关键词)...",
124
+ value: search,
125
+ autoFocus: true,
126
+ onChange: (e) => setSearch(e.target.value),
127
+ })), h("div", { className: "dsh-github-branch-list" }, loading
128
+ ? h("div", { style: { padding: "12px", textAlign: "center", fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "⏳ 读取分支中...")
129
+ : fetchError
130
+ ? h("div", { style: { padding: "12px", textAlign: "center", fontSize: "11px", color: "var(--dsw-alias-state-error-primary, #cf222e)" } }, `⚠️ ${fetchError}`)
131
+ : [
132
+ localBranches.length > 0
133
+ ? h("div", { key: "hdr-local", style: { padding: "4px 12px", fontSize: "10px", fontWeight: 600, color: "var(--dsw-alias-label-tertiary, #8c8c8c)", textTransform: "uppercase" } }, "本地分支")
134
+ : null,
135
+ ...localBranches.map((b) => h("div", {
136
+ key: b.name,
137
+ className: "dsh-github-branch-item" + (b.isCurrent ? " active" : ""),
138
+ onClick: () => handleSwitch(b.name),
139
+ title: b.subject ? `${b.name}\n${b.subject} (${b.relativeTime})` : b.name,
140
+ }, h("div", { style: { display: "flex", alignItems: "center", gap: "6px", minWidth: 0 } }, b.isCurrent
141
+ ? h("span", { style: { color: "var(--dsw-alias-state-success, #2ea043)", fontWeight: 700 } }, "✓")
142
+ : h("span", { style: { width: "10px" } }), h("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, b.name)), b.relativeTime
143
+ ? h("span", { style: { fontSize: "10px", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", flexShrink: 0, marginLeft: "6px" } }, b.relativeTime)
144
+ : null)),
145
+ remoteBranches.length > 0
146
+ ? h("div", { key: "hdr-remote", style: { padding: "6px 12px 4px", fontSize: "10px", fontWeight: 600, color: "var(--dsw-alias-label-tertiary, #8c8c8c)", textTransform: "uppercase", borderTop: "1px solid var(--dsw-alias-border-l2, rgba(0,0,0,0.06))", marginTop: "4px" } }, "远程分支 (点击检出)")
147
+ : null,
148
+ ...remoteBranches.map((b) => h("div", {
149
+ key: b.name,
150
+ className: "dsh-github-branch-item",
151
+ onClick: () => handleSwitch(b.name),
152
+ title: b.subject ? `${b.name}\n${b.subject} (${b.relativeTime})` : b.name,
153
+ }, h("div", { style: { display: "flex", alignItems: "center", gap: "6px", minWidth: 0 } }, h("span", { style: { width: "10px" } }), h("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.85 } }, b.name)), b.relativeTime
154
+ ? h("span", { style: { fontSize: "10px", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", flexShrink: 0, marginLeft: "6px" } }, b.relativeTime)
155
+ : null)),
156
+ filtered.length === 0 && !loading
157
+ ? h("div", { style: { padding: "12px", textAlign: "center", fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "未匹配到任何分支")
158
+ : null,
159
+ ].filter(Boolean)), switching
160
+ ? h("div", { style: { padding: "6px", textAlign: "center", fontSize: "11px", background: "rgba(9, 105, 218, 0.1)", color: "var(--dsw-alias-link, #0969da)", fontWeight: 500 } }, "正在切换分支...")
161
+ : null)
162
+ : null);
163
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 提交详情模态弹窗组件(与设置弹窗同构)
3
+ */
4
+ export declare function CommitDetailModal(props: {
5
+ commit: any;
6
+ onClose: () => void;
7
+ cwd?: string | null;
8
+ }): any;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * 提交详情模态弹窗组件(与设置弹窗同构)
3
+ */
4
+ export function CommitDetailModal(props) {
5
+ if (!props.commit)
6
+ return null;
7
+ var c = props.commit;
8
+ var h = React.createElement;
9
+ var [fetchedDetail, setFetchedDetail] = React.useState(null);
10
+ var [fetchingDetail, setFetchingDetail] = React.useState(false);
11
+ React.useEffect(function () {
12
+ var onKeyDown = function (e) {
13
+ if (e.key === "Escape" || e.keyCode === 27)
14
+ props.onClose();
15
+ };
16
+ window.addEventListener("keydown", onKeyDown);
17
+ return function () { window.removeEventListener("keydown", onKeyDown); };
18
+ }, [props.onClose]);
19
+ React.useEffect(function () {
20
+ setFetchedDetail(null);
21
+ if (c.body || c.stat)
22
+ return;
23
+ setFetchingDetail(true);
24
+ var url = "/api/github/commit-detail?sha=" + encodeURIComponent(c.sha) + (props.cwd ? "&cwd=" + encodeURIComponent(props.cwd) : "");
25
+ fetch(url)
26
+ .then(function (r) { return r.json(); })
27
+ .then(function (j) {
28
+ setFetchingDetail(false);
29
+ if (j && j.ok && j.detail)
30
+ setFetchedDetail(j.detail);
31
+ })
32
+ .catch(function () { setFetchingDetail(false); });
33
+ }, [c.sha, props.cwd]);
34
+ var effectiveBody = c.body;
35
+ var effectiveStat = c.stat;
36
+ if (!effectiveBody && !effectiveStat && fetchedDetail) {
37
+ effectiveStat = fetchedDetail;
38
+ }
39
+ return h("div", {
40
+ className: "dsh-github-modal-overlay",
41
+ onClick: props.onClose,
42
+ style: { zIndex: 100000 },
43
+ }, h("div", {
44
+ className: "dsh-github-modal-dialog",
45
+ style: { width: "94%", maxWidth: "460px", maxHeight: "84vh", display: "flex", flexDirection: "column", overflow: "hidden" },
46
+ onClick: function (e) { e.stopPropagation(); },
47
+ }, 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", {
48
+ style: { background: "none", border: "none", cursor: "pointer", fontSize: "18px", color: "var(--dsw-alias-label-secondary, #656d76)", padding: "0 4px" },
49
+ onClick: props.onClose,
50
+ title: "关闭 (Esc)",
51
+ }, "✕")), h("div", {
52
+ className: "dsh-github-modal-body",
53
+ style: { padding: "16px", overflowY: "auto", flex: 1, minHeight: 0, display: "flex", flexDirection: "column", gap: "12px", boxSizing: "border-box" },
54
+ }, 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
55
+ ? h("div", {
56
+ style: {
57
+ fontSize: "12px",
58
+ lineHeight: "1.6",
59
+ whiteSpace: "pre-wrap",
60
+ wordBreak: "break-word",
61
+ padding: "10px 12px",
62
+ borderRadius: "6px",
63
+ background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
64
+ border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
65
+ },
66
+ }, effectiveBody)
67
+ : null, effectiveStat
68
+ ? h("div", null, h("div", { style: { fontSize: "11px", fontWeight: 600, marginBottom: "4px", opacity: 0.8 } }, "📁 文件变更统计:"), h("pre", {
69
+ className: "dsh-github-commit-pre",
70
+ style: {
71
+ fontSize: "11px",
72
+ lineHeight: "1.5",
73
+ whiteSpace: "pre-wrap",
74
+ wordBreak: "break-word",
75
+ margin: 0,
76
+ maxHeight: "34vh",
77
+ overflowY: "auto",
78
+ padding: "8px 10px",
79
+ borderRadius: "6px",
80
+ background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
81
+ border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
82
+ },
83
+ }, effectiveStat))
84
+ : null, !effectiveBody && !effectiveStat
85
+ ? (fetchingDetail
86
+ ? h("div", { style: { fontSize: "12px", opacity: 0.7, padding: "8px 0" } }, "⏳ 正在获取详细变更说明…")
87
+ : h("div", { style: { fontSize: "11px", opacity: 0.6, fontStyle: "italic", padding: "6px 0" } }, "(该提交未包含多行正文;重启 DSH 桌面端可同步最新格式并直接呈现变更统计)"))
88
+ : null)));
89
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 官方高清 SVG 矢量组件与图标徽章
3
+ */
4
+ export declare function renderOctocat(size?: number, color?: string): any;
5
+ export declare function renderGitIcon(size?: number, color?: string): any;
6
+ export declare function renderOctocatBadge(size?: number): any;
7
+ export declare function renderPluginBadge(hFactory: any): any;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 官方高清 SVG 矢量组件与图标徽章
3
+ */
4
+ export function renderOctocat(size = 18, color = "currentColor") {
5
+ return React.createElement("svg", {
6
+ viewBox: "0 0 16 16",
7
+ width: size,
8
+ height: size,
9
+ fill: color,
10
+ style: { flexShrink: 0, display: "inline-block", verticalAlign: "middle" }
11
+ }, React.createElement("path", {
12
+ fillRule: "evenodd",
13
+ clipRule: "evenodd",
14
+ 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"
15
+ }));
16
+ }
17
+ export function renderGitIcon(size = 14, color = "#1296db") {
18
+ return React.createElement("svg", {
19
+ viewBox: "0 0 1024 1024",
20
+ width: size,
21
+ height: size,
22
+ style: { flexShrink: 0, display: "inline-block", verticalAlign: "middle" }
23
+ }, React.createElement("path", {
24
+ fill: color,
25
+ 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"
26
+ }));
27
+ }
28
+ export function renderOctocatBadge(size = 32) {
29
+ return React.createElement("div", {
30
+ className: "dsh-octocat-badge",
31
+ style: {
32
+ width: size + "px",
33
+ height: size + "px",
34
+ borderRadius: Math.round(size * 0.25) + "px",
35
+ }
36
+ }, renderOctocat(Math.round(size * 0.58), "#ffffff"));
37
+ }
38
+ export function renderPluginBadge(hFactory) {
39
+ var h = hFactory;
40
+ return h("button", {
41
+ className: "dsh-github-plugin-badge",
42
+ type: "button",
43
+ title: "本界面由第三方插件 dsh-github-flow 提供(非 DSH / GitHub 官方)。点击查看关于。",
44
+ onClick: function (e) {
45
+ e.stopPropagation();
46
+ e.preventDefault();
47
+ try {
48
+ window.dispatchEvent(new CustomEvent("dsh-github-open-about"));
49
+ }
50
+ catch (err) { }
51
+ },
52
+ }, "插件");
53
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 网络连接异常与离线状态醒目横幅组件
3
+ */
4
+ export declare function NetworkAlertBanner(props: {
5
+ isRightbar?: boolean;
6
+ networkErrorReason?: string | null;
7
+ lastSyncedAt?: number;
8
+ onRefresh?: () => void;
9
+ onOpenProxy?: () => void;
10
+ }): any;