dsh-github-flow 0.1.0 → 0.1.2

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/api/routes.js CHANGED
@@ -1,230 +1,262 @@
1
1
  const cacheMap = new Map();
2
- export function registerApiRoutes(ctx, executor, config) {
3
- const webServer = ctx.get ? ctx.get('webServer') : ctx.webServer;
4
- if (!webServer || typeof webServer.register !== 'function')
2
+ const CACHE_TTL_MS = 15_000;
3
+ export function registerApiRoutes(webServerService, executor, workspaceRegistry) {
4
+ if (!webServerService || typeof webServerService.register !== 'function')
5
5
  return;
6
- const cacheTtlMs = config?.cacheTtlMs || 15_000;
7
- const listLimit = String(config?.defaultListLimit || 20);
8
6
  function sendJson(res, status, data) {
9
7
  res.statusCode = status;
10
8
  res.setHeader('Content-Type', 'application/json; charset=utf-8');
11
9
  res.setHeader('Cache-Control', 'no-store');
12
10
  res.end(JSON.stringify(data));
13
11
  }
14
- // 接入 Cordis effect 管理生命周期,卸载或重载时注销所有路由
15
- const registerWithEffect = (fn) => {
16
- if (ctx.effect) {
17
- return ctx.effect(() => {
18
- const disposers = fn();
19
- return () => {
20
- disposers.forEach((dispose) => {
21
- if (typeof dispose === 'function')
22
- dispose();
23
- });
24
- };
25
- }, 'github-flow: api routes');
26
- }
27
- else {
28
- fn();
29
- }
30
- };
31
- registerWithEffect(() => {
32
- const disposers = [];
33
- // 1. 获取全局与当前工作区仓库的 GitHub 概览
34
- disposers.push(webServer.register({
35
- kind: 'exact',
36
- path: '/api/github/overview',
37
- handler: async (req, res) => {
38
- if (req.method !== 'GET') {
39
- sendJson(res, 405, { ok: false, message: '仅支持 GET 请求' });
40
- return;
41
- }
42
- try {
43
- const url = new URL(req.url || '', 'http://127.0.0.1');
44
- const cwd = url.searchParams.get('cwd') || process.cwd();
45
- const now = Date.now();
46
- const cached = cacheMap.get(cwd);
47
- if (cached && now - cached.time < cacheTtlMs) {
48
- sendJson(res, 200, { ok: true, data: cached.data, fromCache: true, cwd });
49
- return;
50
- }
51
- const auth = await executor.checkAuth(cwd);
52
- const repo = await executor.getRepoMetadata(cwd);
53
- let pullRequests = [];
54
- let issues = [];
55
- let runs = [];
56
- if (repo && auth.loggedIn) {
57
- // 并发拉取当前工作区仓库的 PRs、Issues 和 Runs
58
- const [prsRes, issuesRes, runsRes] = await Promise.all([
59
- executor.run([
60
- 'pr',
61
- 'list',
62
- '--json',
63
- 'number,title,state,author,headRefName,baseRefName,isDraft,mergeable,reviewDecision,statusCheckRollup,url,updatedAt',
64
- '-L',
65
- '10',
66
- ], { cwd, timeoutMs: 12_000 }),
67
- executor.run(['issue', 'list', '--json', 'number,title,state,author,labels,assignees,url,updatedAt', '-L', '10'], { cwd, timeoutMs: 12_000 }),
68
- executor.run(['run', 'list', '--json', 'databaseId,name,status,conclusion,event,headBranch,url,createdAt', '-L', '5'], { cwd, timeoutMs: 12_000 }),
69
- ]);
70
- if (prsRes.ok && Array.isArray(prsRes.data))
71
- pullRequests = prsRes.data;
72
- if (issuesRes.ok && Array.isArray(issuesRes.data))
73
- issues = issuesRes.data;
74
- if (runsRes.ok && Array.isArray(runsRes.data))
75
- runs = runsRes.data;
76
- }
77
- const freshData = {
78
- auth,
79
- repo: repo || undefined,
80
- pullRequests,
81
- issues,
82
- runs,
83
- lastUpdated: new Date().toISOString(),
84
- };
85
- cacheMap.set(cwd, { data: freshData, time: now });
86
- sendJson(res, 200, { ok: true, data: freshData, fromCache: false, cwd });
87
- }
88
- catch (err) {
89
- sendJson(res, 500, { ok: false, error: err.message || '获取 GitHub 概览失败' });
90
- }
91
- },
92
- }));
93
- // 2. 强制刷新缓存
94
- disposers.push(webServer.register({
95
- kind: 'exact',
96
- path: '/api/github/refresh',
97
- handler: async (req, res) => {
98
- cacheMap.clear();
99
- sendJson(res, 200, { ok: true, message: '缓存已清空,下次读取将拉取最新数据' });
100
- },
101
- }));
102
- // 3. 右侧栏快捷操作代理接口
103
- disposers.push(webServer.register({
104
- kind: 'exact',
105
- path: '/api/github/action',
106
- handler: async (req, res) => {
107
- if (req.method !== 'POST') {
108
- sendJson(res, 405, { ok: false, message: '仅支持 POST 请求' });
109
- return;
110
- }
111
- let body = '';
112
- req.on('data', (chunk) => {
113
- body += chunk;
114
- });
115
- req.on('end', async () => {
12
+ // 1. 获取全局与当前仓库的 GitHub 概览
13
+ webServerService.register({
14
+ kind: 'exact',
15
+ path: '/api/github/overview',
16
+ handler: async (req, res) => {
17
+ if (req.method !== 'GET') {
18
+ sendJson(res, 405, { ok: false, message: '仅支持 GET 请求' });
19
+ return;
20
+ }
21
+ try {
22
+ const url = new URL(req.url || '', 'http://127.0.0.1');
23
+ let cwd = url.searchParams.get('cwd');
24
+ const sessionId = url.searchParams.get('sessionId');
25
+ // 1. 优先通过 sessionId 从 workspace.json 逆向解析物理路径
26
+ if (!cwd && sessionId) {
116
27
  try {
117
- const payload = body ? JSON.parse(body) : {};
118
- const cwd = payload.cwd || process.cwd();
119
- switch (payload.action) {
120
- case 'create_pr': {
121
- const cmd = ['pr', 'create', '--title', payload.title || 'Update', '--body', payload.body || 'Created from DSH Rightbar'];
122
- if (payload.draft)
123
- cmd.push('--draft');
124
- const r = await executor.run(cmd, { cwd });
125
- if (!r.ok)
126
- throw new Error(r.error);
127
- if (ctx.emit) {
128
- ctx.emit('github/pr:create', cwd, { title: payload.title || 'Update', url: r.rawOutput || '' });
28
+ const fs = await import('node:fs');
29
+ const path = await import('node:path');
30
+ const os = await import('node:os');
31
+ const wsJsonPath = path.join(os.homedir(), '.dsh', 'storages', 'workspace.json');
32
+ if (fs.existsSync(wsJsonPath)) {
33
+ const wsData = JSON.parse(fs.readFileSync(wsJsonPath, 'utf8'));
34
+ const workspaces = wsData.tables?.workspaces || {};
35
+ for (const wsId of Object.keys(workspaces)) {
36
+ const item = workspaces[wsId];
37
+ if (item.sessionIds && item.sessionIds.includes(sessionId)) {
38
+ cwd = item.path;
39
+ break;
129
40
  }
130
- sendJson(res, 200, { ok: true, message: 'PR 创建成功', url: r.rawOutput });
131
- break;
132
41
  }
133
- case 'create_issue': {
134
- const cmd = ['issue', 'create', '--title', payload.title || 'New Issue', '--body', payload.body || 'Created from DSH Rightbar'];
135
- const r = await executor.run(cmd, { cwd });
136
- if (!r.ok)
137
- throw new Error(r.error);
138
- if (ctx.emit) {
139
- ctx.emit('github/issue:create', cwd, { title: payload.title || 'New Issue', url: r.rawOutput || '' });
42
+ }
43
+ }
44
+ catch (e) { }
45
+ }
46
+ // 2. 若仍未获取,从 workspace.json 自动匹配最近活跃的工作区
47
+ if (!cwd) {
48
+ try {
49
+ const fs = await import('node:fs');
50
+ const path = await import('node:path');
51
+ const os = await import('node:os');
52
+ const wsJsonPath = path.join(os.homedir(), '.dsh', 'storages', 'workspace.json');
53
+ if (fs.existsSync(wsJsonPath)) {
54
+ const wsData = JSON.parse(fs.readFileSync(wsJsonPath, 'utf8'));
55
+ const workspaces = wsData.tables?.workspaces || {};
56
+ let latestItem = null;
57
+ for (const wsId of Object.keys(workspaces)) {
58
+ const item = workspaces[wsId];
59
+ if (!latestItem || new Date(item.updatedAt || 0) > new Date(latestItem.updatedAt || 0)) {
60
+ latestItem = item;
140
61
  }
141
- sendJson(res, 200, { ok: true, message: 'Issue 创建成功', url: r.rawOutput });
142
- break;
143
62
  }
144
- case 'open_browser': {
145
- // 浏览器外链在 Client 端原生调用 window.open,服务端安全闭环响应
146
- sendJson(res, 200, { ok: true, message: '外链已通过客户端安全打开' });
147
- break;
63
+ if (latestItem && latestItem.path) {
64
+ cwd = latestItem.path;
148
65
  }
149
- default:
150
- sendJson(res, 400, { ok: false, message: `未知的操作类型: ${payload.action}` });
151
66
  }
152
67
  }
153
- catch (err) {
154
- sendJson(res, 500, { ok: false, error: err.message || '操作执行失败' });
155
- }
156
- });
157
- },
158
- }));
159
- // 4. 获取全局视角的驾驶舱概览数据 (不绑定单一项目)
160
- disposers.push(webServer.register({
161
- kind: 'exact',
162
- path: '/api/github/global-overview',
163
- handler: async (req, res) => {
164
- if (req.method !== 'GET') {
165
- sendJson(res, 405, { ok: false, message: '仅支持 GET 请求' });
68
+ catch (e) { }
69
+ }
70
+ if (!cwd) {
71
+ cwd = process.cwd();
72
+ }
73
+ const now = Date.now();
74
+ const cached = cacheMap.get(cwd);
75
+ if (cached && now - cached.time < CACHE_TTL_MS) {
76
+ sendJson(res, 200, { ok: true, data: cached.data, fromCache: true, cwd });
166
77
  return;
167
78
  }
79
+ const auth = await executor.checkAuth(cwd);
80
+ const repo = await executor.getRepoMetadata(cwd);
81
+ let pullRequests = [];
82
+ let issues = [];
83
+ let runs = [];
84
+ if (repo && auth.loggedIn) {
85
+ // 并发拉取当前文件夹仓库的 PRs、Issues 和 Runs
86
+ const [prsRes, issuesRes, runsRes] = await Promise.all([
87
+ executor.run(['pr', 'list', '--json', 'number,title,state,author,headRefName,baseRefName,isDraft,mergeable,reviewDecision,statusCheckRollup,url,updatedAt', '-L', '10'], { cwd, timeoutMs: 12_000 }),
88
+ executor.run(['issue', 'list', '--json', 'number,title,state,author,labels,assignees,url,updatedAt', '-L', '10'], { cwd, timeoutMs: 12_000 }),
89
+ executor.run(['run', 'list', '--json', 'databaseId,name,status,conclusion,event,headBranch,url,createdAt', '-L', '5'], { cwd, timeoutMs: 12_000 }),
90
+ ]);
91
+ if (prsRes.ok && Array.isArray(prsRes.data))
92
+ pullRequests = prsRes.data;
93
+ if (issuesRes.ok && Array.isArray(issuesRes.data))
94
+ issues = issuesRes.data;
95
+ if (runsRes.ok && Array.isArray(runsRes.data))
96
+ runs = runsRes.data;
97
+ }
98
+ const freshData = {
99
+ auth,
100
+ repo: repo || undefined,
101
+ pullRequests,
102
+ issues,
103
+ runs,
104
+ lastUpdated: new Date().toISOString(),
105
+ };
106
+ cacheMap.set(cwd, { data: freshData, time: now });
107
+ sendJson(res, 200, { ok: true, data: freshData, fromCache: false, cwd });
108
+ }
109
+ catch (err) {
110
+ sendJson(res, 500, { ok: false, error: err.message || '获取 GitHub 概览失败' });
111
+ }
112
+ },
113
+ });
114
+ // 2. 强制刷新缓存
115
+ webServerService.register({
116
+ kind: 'exact',
117
+ path: '/api/github/refresh',
118
+ handler: async (req, res) => {
119
+ cacheMap.clear();
120
+ sendJson(res, 200, { ok: true, message: '缓存已清空,下次读取将拉取最新数据' });
121
+ },
122
+ });
123
+ // 3. 右侧栏快捷操作代理接口
124
+ webServerService.register({
125
+ kind: 'exact',
126
+ path: '/api/github/action',
127
+ handler: async (req, res) => {
128
+ if (req.method !== 'POST') {
129
+ sendJson(res, 405, { ok: false, message: '仅支持 POST 请求' });
130
+ return;
131
+ }
132
+ let body = '';
133
+ req.on('data', (chunk) => { body += chunk; });
134
+ req.on('end', async () => {
168
135
  try {
169
- const auth = await executor.checkAuth();
170
- let userRepos = [];
171
- let myPrs = [];
172
- let myIssues = [];
173
- let workspaceMatrix = [];
174
- if (auth.loggedIn) {
175
- // 并发拉取全局概览数据
176
- const [reposRes, prsRes, issuesRes] = await Promise.all([
177
- executor.run(['repo', 'list', '--limit', listLimit, '--json', 'name,nameWithOwner,description,defaultBranchRef,isPrivate,stargazerCount,updatedAt,url'], { timeoutMs: 12_000 }),
178
- executor.run(['search', 'prs', '--author=@me', '--state=open', '--limit', '10', '--json', 'number,title,repository,url,updatedAt'], { timeoutMs: 12_000 }),
179
- executor.run(['search', 'issues', '--author=@me', '--state=open', '--limit', '10', '--json', 'number,title,repository,url,updatedAt'], { timeoutMs: 12_000 }),
180
- ]);
181
- if (reposRes.ok && Array.isArray(reposRes.data)) {
182
- userRepos = reposRes.data.map((r) => ({
183
- name: r.name || '',
184
- nameWithOwner: r.nameWithOwner || '',
185
- description: r.description || '',
186
- defaultBranch: r.defaultBranchRef?.name || 'main',
187
- isPrivate: Boolean(r.isPrivate),
188
- stargazerCount: r.stargazerCount || 0,
189
- updatedAt: r.updatedAt || '',
190
- url: r.url || `https://github.com/${r.nameWithOwner}`,
191
- }));
136
+ const payload = body ? JSON.parse(body) : {};
137
+ const cwd = payload.cwd || process.cwd();
138
+ switch (payload.action) {
139
+ case 'create_pr': {
140
+ const cmd = ['pr', 'create', '--title', payload.title || 'Update', '--body', payload.body || 'Created from DSH Rightbar'];
141
+ if (payload.draft)
142
+ cmd.push('--draft');
143
+ const r = await executor.run(cmd, { cwd });
144
+ if (!r.ok)
145
+ throw new Error(r.error);
146
+ sendJson(res, 200, { ok: true, message: 'PR 创建成功', url: r.rawOutput });
147
+ break;
192
148
  }
193
- if (prsRes.ok && Array.isArray(prsRes.data)) {
194
- myPrs = prsRes.data;
149
+ case 'create_issue': {
150
+ const cmd = ['issue', 'create', '--title', payload.title || 'New Issue', '--body', payload.body || 'Created from DSH Rightbar'];
151
+ const r = await executor.run(cmd, { cwd });
152
+ if (!r.ok)
153
+ throw new Error(r.error);
154
+ sendJson(res, 200, { ok: true, message: 'Issue 创建成功', url: r.rawOutput });
155
+ break;
195
156
  }
196
- if (issuesRes.ok && Array.isArray(issuesRes.data)) {
197
- myIssues = issuesRes.data;
157
+ case 'open_browser': {
158
+ if (!payload.url)
159
+ throw new Error('缺少 url 参数');
160
+ const { exec } = await import('node:child_process');
161
+ const openCmd = process.platform === 'win32' ? `start "" "${payload.url}"` : (process.platform === 'darwin' ? `open "${payload.url}"` : `xdg-open "${payload.url}"`);
162
+ exec(openCmd);
163
+ sendJson(res, 200, { ok: true, message: '已在浏览器打开' });
164
+ break;
198
165
  }
166
+ default:
167
+ sendJson(res, 400, { ok: false, message: `未知的操作类型: ${payload.action}` });
199
168
  }
200
- // 安全解析工作区矩阵:优先通过官方工作区服务获取,无服务时仅展示当前运行工作区,绝不私自侵入宿主私有文件
201
- const currentCwd = process.cwd();
202
- const currentRepo = await executor.getRepoMetadata(currentCwd);
203
- workspaceMatrix = [
204
- {
205
- id: 'current',
206
- title: '当前活动工作区',
207
- path: currentCwd,
208
- hasGit: Boolean(currentRepo),
209
- repo: currentRepo || undefined,
210
- sessionCount: 1,
211
- },
212
- ];
213
- const globalData = {
214
- auth,
215
- userRepos,
216
- workspaceMatrix,
217
- myPrs,
218
- myIssues,
219
- lastUpdated: new Date().toISOString(),
220
- };
221
- sendJson(res, 200, { ok: true, data: globalData });
222
169
  }
223
170
  catch (err) {
224
- sendJson(res, 500, { ok: false, error: err.message || '获取全局驾驶舱数据失败' });
171
+ sendJson(res, 500, { ok: false, error: err.message || '操作执行失败' });
172
+ }
173
+ });
174
+ },
175
+ });
176
+ // 4. 获取全局视角的驾驶舱概览数据 (不绑定单一项目)
177
+ webServerService.register({
178
+ kind: 'exact',
179
+ path: '/api/github/global-overview',
180
+ handler: async (req, res) => {
181
+ if (req.method !== 'GET') {
182
+ sendJson(res, 405, { ok: false, message: '仅支持 GET 请求' });
183
+ return;
184
+ }
185
+ try {
186
+ const auth = await executor.checkAuth();
187
+ let userRepos = [];
188
+ let myPrs = [];
189
+ let myIssues = [];
190
+ let workspaceMatrix = [];
191
+ if (auth.loggedIn) {
192
+ // 并发执行:1. 账号下仓库列表 2. 个人 PRs 3. 个人 Issues
193
+ const [reposRes, prsRes, issuesRes] = await Promise.all([
194
+ executor.run(['repo', 'list', '--limit', '20', '--json', 'name,nameWithOwner,description,defaultBranchRef,isPrivate,stargazerCount,updatedAt,url'], { timeoutMs: 12_000 }),
195
+ executor.run(['search', 'prs', '--author=@me', '--state=open', '--limit', '10', '--json', 'number,title,repository,url,updatedAt'], { timeoutMs: 12_000 }),
196
+ executor.run(['search', 'issues', '--author=@me', '--state=open', '--limit', '10', '--json', 'number,title,repository,url,updatedAt'], { timeoutMs: 12_000 }),
197
+ ]);
198
+ if (reposRes.ok && Array.isArray(reposRes.data)) {
199
+ userRepos = reposRes.data.map((r) => ({
200
+ name: r.name || '',
201
+ nameWithOwner: r.nameWithOwner || '',
202
+ description: r.description || '',
203
+ defaultBranch: r.defaultBranchRef?.name || 'main',
204
+ isPrivate: Boolean(r.isPrivate),
205
+ stargazerCount: r.stargazerCount || 0,
206
+ updatedAt: r.updatedAt || '',
207
+ url: r.url || `https://github.com/${r.nameWithOwner}`,
208
+ }));
209
+ }
210
+ if (prsRes.ok && Array.isArray(prsRes.data)) {
211
+ myPrs = prsRes.data;
212
+ }
213
+ if (issuesRes.ok && Array.isArray(issuesRes.data)) {
214
+ myIssues = issuesRes.data;
215
+ }
216
+ }
217
+ // 4. 解析本地所有工作区与 GitHub 关联矩阵
218
+ try {
219
+ const fs = await import('node:fs');
220
+ const path = await import('node:path');
221
+ const os = await import('node:os');
222
+ const wsJsonPath = path.join(os.homedir(), '.dsh', 'storages', 'workspace.json');
223
+ if (fs.existsSync(wsJsonPath)) {
224
+ const wsData = JSON.parse(fs.readFileSync(wsJsonPath, 'utf8'));
225
+ const workspaces = wsData.tables?.workspaces || {};
226
+ const wsEntries = Object.keys(workspaces).map((id) => ({ id, ...workspaces[id] }));
227
+ // 并发探测每个工作区路径的 Git Remote
228
+ const matrixResults = await Promise.all(wsEntries.map(async (ws) => {
229
+ const hasGit = fs.existsSync(path.join(ws.path, '.git'));
230
+ let repoMeta = undefined;
231
+ if (hasGit) {
232
+ repoMeta = (await executor.getRepoMetadata(ws.path)) || undefined;
233
+ }
234
+ return {
235
+ id: ws.id,
236
+ title: ws.title || path.basename(ws.path),
237
+ path: ws.path,
238
+ hasGit,
239
+ repo: repoMeta,
240
+ sessionCount: (ws.sessionIds && ws.sessionIds.length) || 0,
241
+ };
242
+ }));
243
+ workspaceMatrix = matrixResults;
244
+ }
225
245
  }
226
- },
227
- }));
228
- return disposers;
246
+ catch (e) { }
247
+ const globalData = {
248
+ auth,
249
+ userRepos,
250
+ workspaceMatrix,
251
+ myPrs,
252
+ myIssues,
253
+ lastUpdated: new Date().toISOString(),
254
+ };
255
+ sendJson(res, 200, { ok: true, data: globalData });
256
+ }
257
+ catch (err) {
258
+ sendJson(res, 500, { ok: false, error: err.message || '获取全局驾驶舱数据失败' });
259
+ }
260
+ },
229
261
  });
230
262
  }
@@ -1 +1 @@
1
- export declare const GITHUB_PANEL_CSS = "\n.dsh-github-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary, #8c8c8c);\n transition: color 0.15s ease;\n}\n.dsh-github-icon:hover {\n color: var(--dsw-alias-label-primary, #ffffff);\n}\n.dsh-github-icon svg {\n width: 18px;\n height: 18px;\n fill: currentColor;\n}\n\n/* \u4E3B\u9762\u677F\u5BB9\u5668 */\n.dsh-github-panel {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n overflow-y: auto;\n padding: 24px 32px;\n box-sizing: border-box;\n background: var(--dsw-alias-bg-primary, #1e1e20);\n color: var(--dsw-alias-label-primary, #ececec);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n\n/* \u9876\u90E8\u6807\u9898\u4E0E\u5DE5\u5177\u680F */\n.dsh-github-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n padding-bottom: 16px;\n border-bottom: 1px solid var(--dsw-alias-border-l1, #333336);\n}\n.dsh-github-title {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.dsh-github-title h2 {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n}\n.dsh-github-refresh-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n font-size: 13px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l1, #444);\n background: var(--dsw-alias-bg-secondary, #2a2a2e);\n color: var(--dsw-alias-label-primary, #fff);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.dsh-github-refresh-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover, #3a3a3e);\n}\n\n/* \u6982\u89C8\u5361\u7247\u533A */\n.dsh-github-overview-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n}\n.dsh-github-card {\n padding: 16px 20px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, #333336);\n background: var(--dsw-alias-bg-secondary, #252528);\n}\n.dsh-github-card-title {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--dsw-alias-label-tertiary, #888);\n margin-bottom: 8px;\n}\n.dsh-github-card-value {\n font-size: 16px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.dsh-github-badge {\n display: inline-block;\n font-size: 11px;\n padding: 2px 8px;\n border-radius: 12px;\n font-weight: 500;\n}\n.dsh-badge-green { background: rgba(46, 160, 67, 0.2); color: #3fb950; }\n.dsh-badge-red { background: rgba(248, 81, 73, 0.2); color: #f85149; }\n.dsh-badge-yellow { background: rgba(210, 153, 34, 0.2); color: #d29922; }\n.dsh-badge-purple { background: rgba(163, 113, 247, 0.2); color: #a371f7; }\n\n/* \u6A21\u5757\u5206\u680F\u533A */\n.dsh-github-section {\n margin-bottom: 28px;\n}\n.dsh-github-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 12px;\n}\n.dsh-github-section-header h3 {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n}\n\n/* \u5217\u8868\u6761\u76EE */\n.dsh-github-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.dsh-github-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l1, #333336);\n background: var(--dsw-alias-bg-secondary, #252528);\n transition: border-color 0.15s ease;\n}\n.dsh-github-item:hover {\n border-color: var(--dsw-alias-border-l2, #555);\n}\n.dsh-github-item-main {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.dsh-github-item-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary, #fff);\n text-decoration: none;\n}\n.dsh-github-item-title:hover {\n text-decoration: underline;\n}\n.dsh-github-item-meta {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, #8c8c8c);\n display: flex;\n gap: 12px;\n}\n.dsh-github-item-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.dsh-github-btn-sm {\n font-size: 12px;\n padding: 4px 10px;\n border-radius: 4px;\n border: 1px solid var(--dsw-alias-border-l1, #444);\n background: transparent;\n color: var(--dsw-alias-label-primary, #fff);\n cursor: pointer;\n}\n.dsh-github-btn-sm:hover {\n background: var(--dsw-alias-interactive-bg-hover, #333);\n}\n.dsh-github-empty {\n padding: 24px;\n text-align: center;\n font-size: 13px;\n color: var(--dsw-alias-label-tertiary, #666);\n border: 1px dashed var(--dsw-alias-border-l1, #333);\n border-radius: 6px;\n}\n";
1
+ export declare const GITHUB_PANEL_CSS = "\n/* ==========================================================================\n DSH GitHub Flow - \u4E3B\u9898\u81EA\u9002\u5E94\u6837\u5F0F\u7CFB\u7EDF (Light / Dark \u52A8\u6001\u8054\u52A8)\n \u9075\u5FAA DSH \u539F\u751F DSW (DeepSeek Web) \u8BBE\u8BA1\u8BED\u8A00\u89C4\u8303\n ========================================================================== */\n\n/* 1. \u4FA7\u8FB9\u680F\u56FE\u6807\u4E0E\u5916\u58F3 */\n.dsh-github-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary, #656d76);\n transition: color 0.15s ease;\n}\n.dsh-github-icon:hover {\n color: var(--dsw-alias-label-primary, #1f2328);\n}\n.dsh-github-icon-inner {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border-radius: 7px;\n background: transparent;\n color: inherit;\n border: 1px solid transparent;\n transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n}\n.dsh-github-icon-inner.active {\n background: var(--dsw-alias-bg-module-platform, rgba(9, 105, 218, 0.08));\n color: var(--dsw-alias-link, #0969da);\n border-color: var(--dsw-alias-border-l2, rgba(9, 105, 218, 0.2));\n}\n\nbody[data-ds-dark-theme] .dsh-github-icon {\n color: var(--dsw-alias-label-secondary, #9ca3af);\n}\nbody[data-ds-dark-theme] .dsh-github-icon:hover {\n color: var(--dsw-alias-label-primary, #ffffff);\n}\nbody[data-ds-dark-theme] .dsh-github-icon-inner.active {\n background: linear-gradient(135deg, rgba(46, 160, 67, 0.3) 0%, rgba(31, 111, 235, 0.45) 100%);\n color: #ffffff;\n box-shadow: 0 0 12px rgba(31, 111, 235, 0.4);\n border-color: rgba(88, 166, 255, 0.45);\n}\n\n/* 2. Octocat \u79D1\u6280\u52CB\u7AE0 Badge */\n.dsh-octocat-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n background: linear-gradient(135deg, #1f6feb 0%, #0969da 60%, #8957e5 100%);\n box-shadow: 0 2px 6px rgba(9, 105, 218, 0.25);\n flex-shrink: 0;\n}\nbody[data-ds-dark-theme] .dsh-octocat-badge {\n background: linear-gradient(135deg, #2ea043 0%, #1f6feb 60%, #8957e5 100%);\n box-shadow: 0 2px 8px rgba(31, 111, 235, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n\n/* 3. \u5BB9\u5668\u89C6\u56FE\uFF08\u4E3B\u9762\u677F\u4E0E\u53F3\u4FA7\u62BD\u5C49\u680F\uFF09 */\n.dsh-github-panel {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n overflow-y: auto;\n padding: 24px 32px;\n box-sizing: border-box;\n background: var(--dsw-alias-bg-base, #ffffff);\n color: var(--dsw-alias-label-primary, #1f2328);\n font-family: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n}\n\n.dsh-github-rightbar-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n overflow-y: auto;\n padding: 16px 18px;\n box-sizing: border-box;\n background: var(--dsw-alias-bg-base, #ffffff);\n color: var(--dsw-alias-label-primary, #1f2328);\n font-family: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n}\n\nbody[data-ds-dark-theme] .dsh-github-panel,\nbody[data-ds-dark-theme] .dsh-github-rightbar-container {\n background: var(--dsw-alias-bg-base, #151517);\n color: var(--dsw-alias-label-primary, #ececec);\n}\n\n/* 4. \u5934\u90E8\u533A\u57DF */\n.dsh-github-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 20px;\n padding-bottom: 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, #e5e7eb);\n}\nbody[data-ds-dark-theme] .dsh-github-header {\n border-bottom-color: var(--dsw-alias-border-l1, #2c2c2e);\n}\n.dsh-github-title {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.dsh-github-title h2 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, #1f2328);\n}\nbody[data-ds-dark-theme] .dsh-github-title h2 {\n color: var(--dsw-alias-label-primary, #f0f6fc);\n}\n\n/* 5. \u5237\u65B0\u6309\u94AE */\n.dsh-github-refresh-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n font-size: 12px;\n font-weight: 500;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);\n background: var(--dsw-alias-bg-module-platform, #f6f8fa);\n color: var(--dsw-alias-label-primary, #1f2328);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.dsh-github-refresh-btn:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #ebeef2);\n border-color: var(--dsw-alias-border-l3, #d0d7de);\n}\n.dsh-github-refresh-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\nbody[data-ds-dark-theme] .dsh-github-refresh-btn {\n border-color: var(--dsw-alias-border-l1, #38383c);\n background: var(--dsw-alias-bg-layer-2, #242528);\n color: var(--dsw-alias-label-primary, #f0f6fc);\n}\nbody[data-ds-dark-theme] .dsh-github-refresh-btn:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #2f3034);\n border-color: rgba(255, 255, 255, 0.25);\n}\n\n/* 6. \u6982\u89C8\u5361\u7247\u4E0E\u7F51\u683C */\n.dsh-github-overview-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 14px;\n margin-bottom: 20px;\n}\n.dsh-github-card {\n padding: 14px 16px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);\n background: var(--dsw-alias-bg-layer-1, #fcfcfd);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n margin-bottom: 12px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\nbody[data-ds-dark-theme] .dsh-github-card {\n border-color: var(--dsw-alias-border-l1, #303034);\n background: var(--dsw-alias-bg-layer-2, #212124);\n box-shadow: none;\n}\n.dsh-github-card-title {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--dsw-alias-label-tertiary, #656d76);\n margin-bottom: 6px;\n font-weight: 500;\n}\nbody[data-ds-dark-theme] .dsh-github-card-title {\n color: var(--dsw-alias-label-tertiary, #888);\n}\n.dsh-github-card-value {\n font-size: 15px;\n font-weight: 600;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n color: var(--dsw-alias-label-primary, #1f2328);\n}\nbody[data-ds-dark-theme] .dsh-github-card-value {\n color: var(--dsw-alias-label-primary, #f0f6fc);\n}\n.dsh-github-card-desc {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, #656d76);\n margin-top: 4px;\n line-height: 1.4;\n}\nbody[data-ds-dark-theme] .dsh-github-card-desc {\n color: var(--dsw-alias-label-secondary, #bbb);\n}\n.dsh-github-card-subtext {\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary, #8c959f);\n margin-top: 6px;\n}\nbody[data-ds-dark-theme] .dsh-github-card-subtext {\n color: var(--dsw-alias-label-tertiary, #888);\n}\n\n/* 7. \u5FBD\u7AE0\uFF08Badges\uFF09 */\n.dsh-github-badge {\n display: inline-flex;\n align-items: center;\n font-size: 11px;\n padding: 2px 8px;\n border-radius: 12px;\n font-weight: 500;\n line-height: 16px;\n}\n.dsh-badge-green {\n background: rgba(46, 160, 67, 0.12);\n color: #1a7f37;\n border: 1px solid rgba(46, 160, 67, 0.2);\n}\n.dsh-badge-red {\n background: rgba(207, 34, 46, 0.12);\n color: #cf222e;\n border: 1px solid rgba(207, 34, 46, 0.2);\n}\n.dsh-badge-yellow {\n background: rgba(154, 103, 0, 0.12);\n color: #9a6700;\n border: 1px solid rgba(154, 103, 0, 0.2);\n}\nbody[data-ds-dark-theme] .dsh-badge-green {\n background: rgba(46, 160, 67, 0.22);\n color: #3fb950;\n border-color: rgba(46, 160, 67, 0.3);\n}\nbody[data-ds-dark-theme] .dsh-badge-red {\n background: rgba(248, 81, 73, 0.22);\n color: #f85149;\n border-color: rgba(248, 81, 73, 0.3);\n}\nbody[data-ds-dark-theme] .dsh-badge-yellow {\n background: rgba(210, 153, 34, 0.22);\n color: #d29922;\n border-color: rgba(210, 153, 34, 0.3);\n}\n\n/* 8. \u5FEB\u6377\u64CD\u4F5C\u7F51\u683C */\n.dsh-github-quick-actions {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 8px;\n margin-bottom: 16px;\n}\n.dsh-github-action-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);\n background: var(--dsw-alias-bg-module-platform, #f6f8fa);\n color: var(--dsw-alias-label-primary, #1f2328);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.dsh-github-action-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover, #ebeef2);\n border-color: var(--dsw-alias-link, #0969da);\n color: var(--dsw-alias-link, #0969da);\n}\nbody[data-ds-dark-theme] .dsh-github-action-btn {\n border-color: var(--dsw-alias-border-l1, #444);\n background: var(--dsw-alias-bg-layer-2, #28282b);\n color: var(--dsw-alias-label-primary, #fff);\n}\nbody[data-ds-dark-theme] .dsh-github-action-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover, #343438);\n border-color: #58a6ff;\n color: #58a6ff;\n}\n\n/* 9. \u6A21\u5757\u5206\u533A\u4E0E\u5217\u8868 */\n.dsh-github-section {\n margin-bottom: 20px;\n}\n.dsh-github-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n}\n.dsh-github-section-header h3 {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary, #656d76);\n}\nbody[data-ds-dark-theme] .dsh-github-section-header h3 {\n color: var(--dsw-alias-label-secondary, #ccc);\n}\n.dsh-github-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.dsh-github-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 10px 14px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);\n background: var(--dsw-alias-bg-layer-1, #fcfcfd);\n transition: border-color 0.15s ease, background 0.15s ease;\n}\n.dsh-github-item:hover {\n border-color: var(--dsw-alias-border-l3, #d0d7de);\n background: var(--dsw-alias-interactive-bg-hover, #f6f8fa);\n}\nbody[data-ds-dark-theme] .dsh-github-item {\n border-color: var(--dsw-alias-border-l1, #333336);\n background: var(--dsw-alias-bg-layer-2, #252528);\n}\nbody[data-ds-dark-theme] .dsh-github-item:hover {\n border-color: var(--dsw-alias-border-l2, #555);\n background: var(--dsw-alias-interactive-bg-hover, #2c2c30);\n}\n.dsh-github-item-title {\n font-size: 13px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary, #1f2328);\n text-decoration: none;\n transition: color 0.15s ease;\n}\n.dsh-github-item-title:hover {\n color: var(--dsw-alias-link, #0969da);\n text-decoration: underline;\n}\nbody[data-ds-dark-theme] .dsh-github-item-title {\n color: var(--dsw-alias-label-primary, #f0f6fc);\n}\nbody[data-ds-dark-theme] .dsh-github-item-title:hover {\n color: #58a6ff;\n}\n.dsh-github-item-desc {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, #656d76);\n line-height: 1.4;\n}\nbody[data-ds-dark-theme] .dsh-github-item-desc {\n color: var(--dsw-alias-label-secondary, #bbb);\n}\n.dsh-github-item-meta {\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary, #656d76);\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n}\nbody[data-ds-dark-theme] .dsh-github-item-meta {\n color: var(--dsw-alias-label-secondary, #8c8c8c);\n}\n\n/* 10. \u5C0F\u6309\u94AE\u6837\u5F0F\uFF08\u5728 GitHub \u6253\u5F00\u7B49\uFF09 */\n.dsh-github-btn-sm {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 500;\n padding: 4px 10px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, #d0d7de);\n background: var(--dsw-alias-bg-module-platform, #f6f8fa);\n color: var(--dsw-alias-label-primary, #1f2328);\n cursor: pointer;\n text-decoration: none !important;\n transition: all 0.15s ease;\n}\n.dsh-github-btn-sm:hover {\n background: var(--dsw-alias-interactive-bg-hover, #ebeef2);\n border-color: var(--dsw-alias-border-l3, #b0b8c1);\n color: var(--dsw-alias-link, #0969da);\n}\nbody[data-ds-dark-theme] .dsh-github-btn-sm {\n border-color: var(--dsw-alias-border-l1, #444);\n background: var(--dsw-alias-bg-layer-2, #26272b);\n color: var(--dsw-alias-label-primary, #f0f6fc);\n}\nbody[data-ds-dark-theme] .dsh-github-btn-sm:hover {\n background: var(--dsw-alias-interactive-bg-hover, #33343a);\n border-color: rgba(255, 255, 255, 0.25);\n color: #58a6ff;\n}\n\n/* 11. \u7A7A\u72B6\u6001 */\n.dsh-github-empty {\n padding: 24px;\n text-align: center;\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary, #656d76);\n border: 1px dashed var(--dsw-alias-border-l2, #d0d7de);\n border-radius: 8px;\n background: rgba(0, 0, 0, 0.01);\n}\nbody[data-ds-dark-theme] .dsh-github-empty {\n color: var(--dsw-alias-label-tertiary, #888);\n border-color: var(--dsw-alias-border-l1, #38383c);\n background: rgba(255, 255, 255, 0.01);\n}\n\n/* 12. Tabs \u5BFC\u822A\u680F */\n.dsh-github-tabs {\n display: flex;\n gap: 8px;\n margin-bottom: 18px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, #e5e7eb);\n padding-bottom: 8px;\n}\nbody[data-ds-dark-theme] .dsh-github-tabs {\n border-bottom-color: var(--dsw-alias-border-l1, #303034);\n}\n.dsh-github-tab-btn {\n padding: 6px 14px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #656d76);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.dsh-github-tab-btn:hover {\n color: var(--dsw-alias-label-primary, #1f2328);\n background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.05));\n}\n.dsh-github-tab-btn.active {\n color: var(--dsw-alias-link, #0969da);\n background: rgba(9, 105, 218, 0.1);\n font-weight: 600;\n}\nbody[data-ds-dark-theme] .dsh-github-tab-btn {\n color: var(--dsw-alias-label-secondary, #8c8c8c);\n}\nbody[data-ds-dark-theme] .dsh-github-tab-btn:hover {\n color: var(--dsw-alias-label-primary, #fff);\n background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.06));\n}\nbody[data-ds-dark-theme] .dsh-github-tab-btn.active {\n color: #58a6ff;\n background: rgba(88, 166, 255, 0.15);\n}\n\n/* 13. \u641C\u7D22\u8F93\u5165\u6846 */\n.dsh-github-search-input {\n width: 100%;\n max-width: 320px;\n padding: 6px 12px;\n font-size: 12px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, #d0d7de);\n background: var(--dsw-alias-bg-base, #ffffff);\n color: var(--dsw-alias-label-primary, #1f2328);\n outline: none;\n margin-bottom: 14px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n.dsh-github-search-input:focus {\n border-color: var(--dsw-alias-link, #0969da);\n box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.15);\n}\nbody[data-ds-dark-theme] .dsh-github-search-input {\n border-color: var(--dsw-alias-border-l1, #444);\n background: var(--dsw-alias-bg-layer-2, #212124);\n color: var(--dsw-alias-label-primary, #f0f6fc);\n}\nbody[data-ds-dark-theme] .dsh-github-search-input:focus {\n border-color: #58a6ff;\n box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.2);\n}\n\n/* 14. \u5DE5\u4F5C\u533A\u77E9\u9635\u6761\u76EE\u4E13\u5C5E\u7C7B */\n.dsh-github-workspace-card {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n}\n.dsh-github-ws-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, #1f2328);\n}\nbody[data-ds-dark-theme] .dsh-github-ws-title {\n color: var(--dsw-alias-label-primary, #f0f6fc);\n}\n.dsh-github-ws-path {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary, #656d76);\n font-family: var(--ds-font-family-code, monospace);\n}\nbody[data-ds-dark-theme] .dsh-github-ws-path {\n color: var(--dsw-alias-label-tertiary, #888);\n}\n.dsh-github-ws-repo-link {\n color: var(--dsw-alias-link, #0969da);\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-weight: 500;\n}\nbody[data-ds-dark-theme] .dsh-github-ws-repo-link {\n color: #58a6ff;\n}\n.dsh-github-repo-link {\n color: var(--dsw-alias-link, #0969da);\n text-decoration: none;\n font-weight: 600;\n}\n.dsh-github-repo-link:hover {\n text-decoration: underline;\n}\nbody[data-ds-dark-theme] .dsh-github-repo-link {\n color: #58a6ff;\n}\n.dsh-github-subheading {\n font-size: 14px;\n font-weight: 600;\n margin: 0 0 10px 0;\n color: var(--dsw-alias-label-primary, #1f2328);\n}\nbody[data-ds-dark-theme] .dsh-github-subheading {\n color: var(--dsw-alias-label-primary, #f0f6fc);\n}\n";