dsh-github-flow 0.4.0 → 0.5.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 (62) hide show
  1. package/README.en.md +75 -8
  2. package/README.md +75 -8
  3. package/docs/UPGRADE-NOTES.md +9 -12
  4. package/docs/architecture.md +39 -21
  5. package/lib/api/routes-actions.js +132 -0
  6. package/lib/client.d.ts +4 -3
  7. package/lib/client.js +1436 -80
  8. package/lib/commands/gh.d.ts +6 -1
  9. package/lib/commands/gh.js +144 -71
  10. package/lib/executor/git-parser.d.ts +33 -1
  11. package/lib/executor/git-parser.js +116 -4
  12. package/lib/executor.d.ts +27 -2
  13. package/lib/executor.js +189 -5
  14. package/lib/main.js +28 -4
  15. package/lib/renderers/agent-task.d.ts +16 -0
  16. package/lib/renderers/agent-task.js +107 -0
  17. package/lib/renderers/codespace.d.ts +16 -0
  18. package/lib/renderers/codespace.js +103 -0
  19. package/lib/renderers/discussion.d.ts +16 -0
  20. package/lib/renderers/discussion.js +101 -0
  21. package/lib/renderers/markdown.d.ts +4 -0
  22. package/lib/renderers/markdown.js +4 -0
  23. package/lib/renderers/project.d.ts +20 -0
  24. package/lib/renderers/project.js +128 -0
  25. package/lib/renderers/skill.d.ts +30 -0
  26. package/lib/renderers/skill.js +180 -0
  27. package/lib/renderers/status.d.ts +5 -0
  28. package/lib/renderers/status.js +52 -0
  29. package/lib/telemetry.d.ts +70 -0
  30. package/lib/telemetry.js +161 -0
  31. package/lib/tools/agent-task.d.ts +54 -0
  32. package/lib/tools/agent-task.js +135 -0
  33. package/lib/tools/codespace.d.ts +51 -0
  34. package/lib/tools/codespace.js +154 -0
  35. package/lib/tools/discussion.d.ts +70 -0
  36. package/lib/tools/discussion.js +195 -0
  37. package/lib/tools/help.js +109 -9
  38. package/lib/tools/project.d.ts +80 -0
  39. package/lib/tools/project.js +241 -0
  40. package/lib/tools/repo.js +13 -1
  41. package/lib/tools/skill.d.ts +24 -1
  42. package/lib/tools/skill.js +115 -5
  43. package/lib/tools/status.d.ts +3 -2
  44. package/lib/tools/status.js +9 -4
  45. package/lib/types.d.ts +5 -0
  46. package/lib/ui/commit-modal.js +144 -14
  47. package/lib/ui/help-modal.d.ts +15 -0
  48. package/lib/ui/help-modal.js +202 -0
  49. package/lib/ui/storage.d.ts +9 -0
  50. package/lib/ui/storage.js +24 -0
  51. package/lib/ui/styles.d.ts +1 -1
  52. package/lib/ui/styles.js +259 -0
  53. package/lib/ui/sync-settings-modal.js +1 -1
  54. package/lib/ui/upgrade-card.js +39 -14
  55. package/lib/ui/utils.d.ts +35 -0
  56. package/lib/ui/utils.js +160 -0
  57. package/lib/ui/workspace-panel.js +30 -5
  58. package/lib/upgrade-manager.d.ts +5 -1
  59. package/lib/upgrade-manager.js +24 -1
  60. package/lib/version.d.ts +1 -1
  61. package/lib/version.js +1 -1
  62. package/package.json +2 -2
package/lib/client.js CHANGED
@@ -1227,6 +1227,244 @@ color: var(--dsw-alias-state-success, #1a7f37);
1227
1227
  body[data-ds-dark-theme] .dsh-github-branch-item.active {
1228
1228
  color: #3fb950;
1229
1229
  }
1230
+ .dsh-github-help-dialog {
1231
+ width: 920px;
1232
+ max-width: 92vw;
1233
+ height: 85vh;
1234
+ max-height: 85vh;
1235
+ display: flex;
1236
+ flex-direction: column;
1237
+ border-radius: 12px;
1238
+ background: var(--dsw-alias-bg-base, #ffffff);
1239
+ border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
1240
+ box-shadow: 0 24px 64px rgba(0, 0, 0, 0.22);
1241
+ color: var(--dsw-alias-label-primary, #1f2328);
1242
+ font-family: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
1243
+ overflow: hidden;
1244
+ box-sizing: border-box;
1245
+ }
1246
+ body[data-ds-dark-theme] .dsh-github-help-dialog {
1247
+ background: var(--dsw-alias-bg-layer-2, #1c2128);
1248
+ border-color: var(--dsw-alias-border-l1, #30363d);
1249
+ color: var(--dsw-alias-label-primary, #f0f6fc);
1250
+ box-shadow: 0 24px 64px rgba(0, 0, 0, 0.65);
1251
+ }
1252
+ .dsh-github-help-header {
1253
+ padding: 16px 22px;
1254
+ border-bottom: 1px solid var(--dsw-alias-border-l2, #d0d7de);
1255
+ display: flex;
1256
+ align-items: center;
1257
+ justify-content: space-between;
1258
+ flex-shrink: 0;
1259
+ background: var(--dsw-alias-bg-base, #ffffff);
1260
+ }
1261
+ body[data-ds-dark-theme] .dsh-github-help-header {
1262
+ background: var(--dsw-alias-bg-layer-2, #1c2128);
1263
+ border-bottom-color: var(--dsw-alias-border-l1, #30363d);
1264
+ }
1265
+ .dsh-github-help-nav {
1266
+ display: flex;
1267
+ gap: 8px;
1268
+ padding: 10px 22px;
1269
+ background: var(--dsw-alias-bg-module-platform, rgba(9, 105, 218, 0.05));
1270
+ border-bottom: 1px solid var(--dsw-alias-border-l2, #d0d7de);
1271
+ flex-shrink: 0;
1272
+ }
1273
+ body[data-ds-dark-theme] .dsh-github-help-nav {
1274
+ background: rgba(56, 139, 253, 0.08);
1275
+ border-bottom-color: var(--dsw-alias-border-l1, #30363d);
1276
+ }
1277
+ .dsh-github-help-tab-btn {
1278
+ padding: 6px 14px;
1279
+ font-size: 13px;
1280
+ font-weight: 500;
1281
+ border-radius: 6px;
1282
+ background: transparent;
1283
+ color: var(--dsw-alias-label-secondary, #656d76);
1284
+ border: 1px solid transparent;
1285
+ cursor: pointer;
1286
+ transition: all 0.15s ease;
1287
+ }
1288
+ body[data-ds-dark-theme] .dsh-github-help-tab-btn {
1289
+ color: var(--dsw-alias-label-secondary, #8b949e);
1290
+ }
1291
+ .dsh-github-help-tab-btn:hover {
1292
+ background: rgba(0, 0, 0, 0.05);
1293
+ }
1294
+ body[data-ds-dark-theme] .dsh-github-help-tab-btn:hover {
1295
+ background: rgba(255, 255, 255, 0.08);
1296
+ color: #f0f6fc;
1297
+ }
1298
+ .dsh-github-help-tab-btn.active {
1299
+ font-weight: 600;
1300
+ background: var(--dsw-alias-link, #0969da);
1301
+ color: #ffffff !important;
1302
+ border-color: var(--dsw-alias-link, #0969da);
1303
+ }
1304
+ body[data-ds-dark-theme] .dsh-github-help-tab-btn.active {
1305
+ background: #1f6feb;
1306
+ border-color: #1f6feb;
1307
+ }
1308
+ .dsh-github-help-scroll-body {
1309
+ flex: 1;
1310
+ overflow-y: auto;
1311
+ padding: 22px;
1312
+ box-sizing: border-box;
1313
+ }
1314
+ .dsh-github-help-scroll-body::-webkit-scrollbar {
1315
+ width: 6px;
1316
+ }
1317
+ .dsh-github-help-scroll-body::-webkit-scrollbar-thumb {
1318
+ background: rgba(0, 0, 0, 0.15);
1319
+ border-radius: 3px;
1320
+ }
1321
+ body[data-ds-dark-theme] .dsh-github-help-scroll-body::-webkit-scrollbar-thumb {
1322
+ background: #30363d;
1323
+ }
1324
+ .dsh-github-help-card {
1325
+ padding: 16px 18px;
1326
+ margin-bottom: 14px;
1327
+ border-radius: 8px;
1328
+ background: var(--dsw-alias-bg-module-platform, rgba(9, 105, 218, 0.04));
1329
+ border: 1px solid var(--dsw-alias-border-l2, rgba(9, 105, 218, 0.18));
1330
+ transition: border-color 0.15s ease;
1331
+ }
1332
+ body[data-ds-dark-theme] .dsh-github-help-card {
1333
+ background: rgba(56, 139, 253, 0.05);
1334
+ border-color: rgba(56, 139, 253, 0.18);
1335
+ }
1336
+ .dsh-github-help-card:hover {
1337
+ border-color: rgba(9, 105, 218, 0.35);
1338
+ }
1339
+ body[data-ds-dark-theme] .dsh-github-help-card:hover {
1340
+ border-color: rgba(56, 139, 253, 0.4);
1341
+ }
1342
+ .dsh-github-prompt-box {
1343
+ padding: 10px 14px;
1344
+ margin-bottom: 8px;
1345
+ border-radius: 6px;
1346
+ background: #f6f8fa;
1347
+ border: 1px solid #d0d7de;
1348
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
1349
+ font-size: 12px;
1350
+ line-height: 1.5;
1351
+ color: var(--dsw-alias-label-primary, #1f2328);
1352
+ word-break: break-all;
1353
+ }
1354
+ body[data-ds-dark-theme] .dsh-github-prompt-box {
1355
+ background: #0d1117;
1356
+ border-color: #30363d;
1357
+ color: #79c0ff;
1358
+ }
1359
+ .dsh-github-help-step-badge {
1360
+ width: 28px;
1361
+ height: 28px;
1362
+ border-radius: 50%;
1363
+ background: var(--dsw-alias-link, #0969da);
1364
+ color: #ffffff;
1365
+ display: flex;
1366
+ align-items: center;
1367
+ justify-content: center;
1368
+ font-weight: 700;
1369
+ font-size: 14px;
1370
+ flex-shrink: 0;
1371
+ }
1372
+ body[data-ds-dark-theme] .dsh-github-help-step-badge {
1373
+ background: #1f6feb;
1374
+ box-shadow: 0 0 10px rgba(31, 111, 235, 0.35);
1375
+ }
1376
+ .dsh-github-help-footer {
1377
+ padding: 12px 22px;
1378
+ border-top: 1px solid var(--dsw-alias-border-l2, #d0d7de);
1379
+ display: flex;
1380
+ justify-content: space-between;
1381
+ align-items: center;
1382
+ background: var(--dsw-alias-bg-base, #ffffff);
1383
+ flex-shrink: 0;
1384
+ }
1385
+ body[data-ds-dark-theme] .dsh-github-help-footer {
1386
+ background: var(--dsw-alias-bg-layer-2, #1c2128);
1387
+ border-top-color: var(--dsw-alias-border-l1, #30363d);
1388
+ }
1389
+ .dsh-github-help-title {
1390
+ font-weight: 600;
1391
+ font-size: 14px;
1392
+ color: var(--dsw-alias-label-primary, #1f2328);
1393
+ }
1394
+ body[data-ds-dark-theme] .dsh-github-help-title {
1395
+ color: var(--dsw-alias-label-primary, #f0f6fc);
1396
+ }
1397
+ .dsh-github-help-desc {
1398
+ font-size: 12px;
1399
+ color: var(--dsw-alias-label-secondary, #656d76);
1400
+ line-height: 1.5;
1401
+ }
1402
+ body[data-ds-dark-theme] .dsh-github-help-desc {
1403
+ color: var(--dsw-alias-label-secondary, #8b949e);
1404
+ }
1405
+ .dsh-github-dirty-item {
1406
+ display: flex;
1407
+ align-items: center;
1408
+ gap: 8px;
1409
+ padding: 4px 6px;
1410
+ border-radius: 6px;
1411
+ cursor: pointer;
1412
+ transition: background-color 0.15s ease;
1413
+ user-select: none;
1414
+ }
1415
+ .dsh-github-dirty-item:hover {
1416
+ background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.05));
1417
+ }
1418
+ body[data-ds-dark-theme] .dsh-github-dirty-item:hover {
1419
+ background: rgba(255, 255, 255, 0.08);
1420
+ }
1421
+ .dsh-github-dirty-item:active {
1422
+ background: var(--dsw-alias-interactive-bg-active, rgba(0, 0, 0, 0.08));
1423
+ }
1424
+ body[data-ds-dark-theme] .dsh-github-dirty-item:active {
1425
+ background: rgba(255, 255, 255, 0.12);
1426
+ }
1427
+ .dsh-github-inline-diff-view {
1428
+ animation: dsh-github-fade-in 0.15s ease-out;
1429
+ }
1430
+ @keyframes dsh-github-fade-in {
1431
+ from { opacity: 0; transform: translateY(2px); }
1432
+ to { opacity: 1; transform: translateY(0); }
1433
+ }
1434
+ body[data-ds-dark-theme] .dsh-github-inline-diff-content {
1435
+ background-color: var(--dsw-alias-bg-layer-2, #1c2128) !important;
1436
+ border-color: var(--dsw-alias-border-l1, #30363d) !important;
1437
+ }
1438
+ .dsh-github-stat-list {
1439
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1440
+ background: var(--dsw-alias-bg-module-platform, #f6f8fa);
1441
+ }
1442
+ body[data-ds-dark-theme] .dsh-github-stat-list {
1443
+ border-color: var(--dsw-alias-border-l1, #30363d) !important;
1444
+ background: var(--dsw-alias-bg-layer-2, #1c2128) !important;
1445
+ }
1446
+ .dsh-github-stat-row {
1447
+ transition: background-color 0.15s ease;
1448
+ }
1449
+ .dsh-github-stat-row:hover {
1450
+ background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.04));
1451
+ }
1452
+ body[data-ds-dark-theme] .dsh-github-stat-row:hover {
1453
+ background: rgba(255, 255, 255, 0.06);
1454
+ }
1455
+ .dsh-github-header-cards-collapsed {
1456
+ display: none !important;
1457
+ }
1458
+ .dsh-github-btn-sm.active {
1459
+ background: var(--dsw-alias-state-attention-subtle, rgba(234, 179, 8, 0.15)) !important;
1460
+ color: var(--dsw-alias-state-attention-primary, #9a6700) !important;
1461
+ border-color: var(--dsw-alias-state-attention-primary, #d29922) !important;
1462
+ }
1463
+ body[data-ds-dark-theme] .dsh-github-btn-sm.active {
1464
+ background: rgba(234, 179, 8, 0.2) !important;
1465
+ color: #f0b72f !important;
1466
+ border-color: #d29922 !important;
1467
+ }
1230
1468
  `;
1231
1469
  function injectStyles() {
1232
1470
  var styleId = "dsh-plugin-github-styles";
@@ -1309,6 +1547,166 @@ function eventLabel(event) {
1309
1547
  return "🔀 PR";
1310
1548
  return e || "";
1311
1549
  }
1550
+ /**
1551
+ * 带有阶梯退避重试(Backoff Retry)的通用异步拉取函数
1552
+ *
1553
+ * 核心设计理由与约束:
1554
+ * 1. 应对代理节点不稳定、TLS 握手卡顿、短时网络抖动(半秒至两秒微断流);
1555
+ * 2. 阶梯退避时序:第 1 次失败等 1s (1000ms),第 2 次等 2s (2000ms),第 3 次等 4s (4000ms),附加 0~200ms Jitter;
1556
+ * 3. 智能区分软硬错误:401/403/404/400 确定性错误坚决不重试,仅在网络超时、不可达、或带有 networkError 时重试;
1557
+ * 4. 严格响应外部 AbortSignal,组件卸载或用户手动打断时立即退出等待,绝无内存泄漏。
1558
+ */
1559
+ async function fetchWithRetry(url, options = {}, config = {}) {
1560
+ var maxRetries = config.maxRetries !== undefined ? config.maxRetries : 3;
1561
+ var baseDelayMs = config.baseDelayMs !== undefined ? config.baseDelayMs : 1000;
1562
+ var signal = config.signal || options.signal;
1563
+ var attempt = 0;
1564
+ while (true) {
1565
+ attempt++;
1566
+ var currentRes = null;
1567
+ var currentJson = null;
1568
+ var failureReason = "";
1569
+ var shouldRetry = false;
1570
+ try {
1571
+ if (signal && signal.aborted) {
1572
+ throw new Error("Aborted");
1573
+ }
1574
+ currentRes = await fetch(url, { ...options, signal });
1575
+ // 硬错误守卫:客户端确定性错误(如未授权、权限拒绝、不存在、参数错误)坚决不重试
1576
+ if (currentRes.status === 401 || currentRes.status === 403 || currentRes.status === 404 || currentRes.status === 400) {
1577
+ currentJson = await currentRes.json().catch(() => ({}));
1578
+ return { res: currentRes, json: currentJson };
1579
+ }
1580
+ if (!currentRes.ok) {
1581
+ failureReason = "HTTP " + currentRes.status + " " + currentRes.statusText;
1582
+ shouldRetry = true;
1583
+ }
1584
+ else {
1585
+ currentJson = await currentRes.json().catch(() => null);
1586
+ // 业务层软错误检测(后端返回了 200,但标记了 networkError 或 partialFailure)
1587
+ var isNetworkError = Boolean(currentJson && (currentJson.partialFailure ||
1588
+ currentJson.data?.partialFailure ||
1589
+ currentJson.data?.networkError ||
1590
+ currentJson.data?.auth?.networkError));
1591
+ if (isNetworkError) {
1592
+ failureReason = currentJson.error || currentJson.data?.networkErrorReason || "云端接口网络抖动";
1593
+ shouldRetry = true;
1594
+ }
1595
+ else {
1596
+ // 成功获取完整且无网络错误的数据
1597
+ return { res: currentRes, json: currentJson };
1598
+ }
1599
+ }
1600
+ }
1601
+ catch (err) {
1602
+ if ((signal && signal.aborted) || err.name === "AbortError") {
1603
+ throw err;
1604
+ }
1605
+ failureReason = err.message || "网络连接中断或超时";
1606
+ shouldRetry = true;
1607
+ }
1608
+ // 判断是否已耗尽重试次数
1609
+ if (!shouldRetry || attempt > maxRetries) {
1610
+ if (currentRes) {
1611
+ return { res: currentRes, json: currentJson || {} };
1612
+ }
1613
+ throw new Error(failureReason || "网络请求失败");
1614
+ }
1615
+ // 计算阶梯退避等待时间:baseDelayMs * 2^(attempt - 1) + Jitter 自适应微扰
1616
+ var exponentialDelay = baseDelayMs * Math.pow(2, attempt - 1);
1617
+ var maxJitter = Math.min(200, Math.floor(exponentialDelay * 0.2));
1618
+ var jitter = Math.floor(Math.random() * (maxJitter + 1));
1619
+ var totalDelay = exponentialDelay + jitter;
1620
+ if (config.onRetry) {
1621
+ config.onRetry(attempt, totalDelay, failureReason);
1622
+ }
1623
+ // 可中断的睡眠等待
1624
+ await new Promise((resolve, reject) => {
1625
+ var timer = setTimeout(resolve, totalDelay);
1626
+ if (signal) {
1627
+ signal.addEventListener("abort", () => {
1628
+ clearTimeout(timer);
1629
+ reject(new Error("Aborted"));
1630
+ }, { once: true });
1631
+ }
1632
+ });
1633
+ }
1634
+ }
1635
+ function parseGitStatText(statText) {
1636
+ if (!statText || typeof statText !== "string")
1637
+ return null;
1638
+ var lines = statText.split("\n");
1639
+ var files = [];
1640
+ var summary = null;
1641
+ for (var i = 0; i < lines.length; i++) {
1642
+ var line = lines[i].trim();
1643
+ if (!line)
1644
+ continue;
1645
+ var summaryMatch = line.match(/(\d+)\s+files?\s+changed(?:,\s+(\d+)\s+insertions?\(\+\))?(?:,\s+(\d+)\s+deletions?\(-\))?/i);
1646
+ if (summaryMatch) {
1647
+ summary = {
1648
+ filesChanged: parseInt(summaryMatch[1], 10),
1649
+ insertions: parseInt(summaryMatch[2] || "0", 10),
1650
+ deletions: parseInt(summaryMatch[3] || "0", 10),
1651
+ };
1652
+ continue;
1653
+ }
1654
+ var pipeIdx = line.indexOf("|");
1655
+ if (pipeIdx > 0) {
1656
+ var left = line.slice(0, pipeIdx).trim();
1657
+ var right = line.slice(pipeIdx + 1).trim();
1658
+ if (left.startsWith('"') && left.endsWith('"') && left.length >= 2) {
1659
+ left = left.slice(1, -1);
1660
+ }
1661
+ if (/^Bin\b/i.test(right)) {
1662
+ files.push({ path: left, total: 0, additions: 0, deletions: 0, isBinary: true, rawRight: right });
1663
+ }
1664
+ else {
1665
+ var numMatch = right.match(/^(\d+)(.*)$/);
1666
+ if (numMatch) {
1667
+ var total = parseInt(numMatch[1], 10);
1668
+ var graph = numMatch[2].trim();
1669
+ var plusCount = 0;
1670
+ var minusCount = 0;
1671
+ for (var j = 0; j < graph.length; j++) {
1672
+ if (graph[j] === "+")
1673
+ plusCount++;
1674
+ else if (graph[j] === "-")
1675
+ minusCount++;
1676
+ }
1677
+ var additions = 0;
1678
+ var deletions = 0;
1679
+ if (plusCount === 0 && minusCount === 0) {
1680
+ additions = total;
1681
+ }
1682
+ else if (minusCount === 0) {
1683
+ additions = total;
1684
+ deletions = 0;
1685
+ }
1686
+ else if (plusCount === 0) {
1687
+ additions = 0;
1688
+ deletions = total;
1689
+ }
1690
+ else {
1691
+ if (plusCount + minusCount === total) {
1692
+ additions = plusCount;
1693
+ deletions = minusCount;
1694
+ }
1695
+ else {
1696
+ var addExact = Math.round((total * plusCount) / (plusCount + minusCount));
1697
+ additions = addExact;
1698
+ deletions = Math.max(0, total - addExact);
1699
+ }
1700
+ }
1701
+ files.push({ path: left, total, additions, deletions, isBinary: false });
1702
+ }
1703
+ }
1704
+ }
1705
+ }
1706
+ if (files.length === 0 && !summary)
1707
+ return null;
1708
+ return { files, summary };
1709
+ }
1312
1710
 
1313
1711
  // ---- [Bundled: icons.js] ----
1314
1712
  /**
@@ -1500,6 +1898,30 @@ function saveStoredRightbarData(cwd, data) {
1500
1898
  }
1501
1899
  catch (e) { }
1502
1900
  }
1901
+ const HEADER_CARDS_STORAGE_KEY = "dsh_github_header_cards_visible_v1";
1902
+ /**
1903
+ * 读取右侧栏上方仓库卡片与快捷操作栏的折叠/展开偏好(默认为 true 展开)
1904
+ */
1905
+ function getStoredHeaderCardsVisible() {
1906
+ try {
1907
+ var raw = localStorage.getItem(HEADER_CARDS_STORAGE_KEY);
1908
+ if (raw === null || raw === undefined)
1909
+ return true;
1910
+ return raw !== "false";
1911
+ }
1912
+ catch (e) {
1913
+ return true;
1914
+ }
1915
+ }
1916
+ /**
1917
+ * 持久化保存右侧栏上方仓库卡片与快捷操作栏的展开/折叠偏好
1918
+ */
1919
+ function saveStoredHeaderCardsVisible(visible) {
1920
+ try {
1921
+ localStorage.setItem(HEADER_CARDS_STORAGE_KEY, visible ? "true" : "false");
1922
+ }
1923
+ catch (e) { }
1924
+ }
1503
1925
 
1504
1926
  // ---- [Bundled: branch-selector.js] ----
1505
1927
  /**
@@ -1882,14 +2304,28 @@ function UpgradeNotificationCard(props) {
1882
2304
  }
1883
2305
  else {
1884
2306
  setStatus("failed");
1885
- var logTail = "";
1886
- if (json.log && typeof json.log === "string") {
1887
- var lines = json.log.trim().split("\n").filter(function (l) { return l.trim(); });
1888
- if (lines.length > 0) {
1889
- logTail = "\n\n📝 日志末尾: " + lines.slice(-4).join(" ⏎ ").slice(0, 400);
2307
+ var logText = String(json.log || "");
2308
+ var isAgeViolation = json.errorType === "MINIMUM_RELEASE_AGE_VIOLATION" ||
2309
+ logText.includes("ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION") ||
2310
+ logText.includes("minimumReleaseAge") ||
2311
+ (typeof json.message === "string" && (json.message.includes("24 小时安全保护期") || json.message.includes("防投毒")));
2312
+ if (isAgeViolation) {
2313
+ // 命中 DSH 内置包管理器 24 小时防投毒策略:收起英文报错堆栈,呈现全中文专业自愈指引
2314
+ setMsg("🛡️ 触发 DSH 新版本 24 小时安全保护期(npm 供应链防投毒策略)。检测到目标版本刚刚发布未满 24 小时,DSH 内置包管理器暂缓了自动升级以保障系统安全。\n\n" +
2315
+ "【推荐操作】:\n" +
2316
+ "1. 立即尝鲜:请先【完全退出关闭 DSH 应用程序】,然后在系统终端执行手动安装命令(点击右侧「📋 复制命令」获取);\n" +
2317
+ "2. 稍后升级:无需任何操作,发版满 24 小时后直接在此点击「⚡ 一键自动升级」即可平滑完成。");
2318
+ }
2319
+ else {
2320
+ var logTail = "";
2321
+ if (json.log && typeof json.log === "string") {
2322
+ var lines = json.log.trim().split("\n").filter(function (l) { return l.trim(); });
2323
+ if (lines.length > 0) {
2324
+ logTail = "\n\n📝 日志末尾: " + lines.slice(-4).join(" ⏎ ").slice(0, 400);
2325
+ }
1890
2326
  }
2327
+ setMsg((json.message || "升级命令执行失败,请在宿主终端手动执行。") + logTail);
1891
2328
  }
1892
- setMsg((json.message || "升级命令执行失败,请在宿主终端手动执行。") + logTail);
1893
2329
  }
1894
2330
  }
1895
2331
  catch (err) {
@@ -1918,6 +2354,7 @@ function UpgradeNotificationCard(props) {
1918
2354
  window.location.reload();
1919
2355
  };
1920
2356
  var h = React.createElement;
2357
+ var isAgeViolation = msg.includes("24 小时安全保护期") || msg.includes("防投毒");
1921
2358
  if (isRightbar) {
1922
2359
  return h("div", {
1923
2360
  className: "dsh-github-upgrade-strip",
@@ -1927,13 +2364,17 @@ function UpgradeNotificationCard(props) {
1927
2364
  borderRadius: "8px",
1928
2365
  background: status === "success"
1929
2366
  ? "var(--dsw-alias-state-success-subtle, rgba(46, 160, 67, 0.12))"
1930
- : "var(--dsw-alias-bg-module-platform, rgba(9, 105, 218, 0.08))",
2367
+ : (isAgeViolation
2368
+ ? "rgba(217, 119, 6, 0.12)"
2369
+ : "var(--dsw-alias-bg-module-platform, rgba(9, 105, 218, 0.08))"),
1931
2370
  border: "1px solid " + (status === "success"
1932
2371
  ? "var(--dsw-alias-state-success-border, rgba(46, 160, 67, 0.35))"
1933
- : "var(--dsw-alias-border-l2, rgba(9, 105, 218, 0.25))"),
2372
+ : (isAgeViolation
2373
+ ? "rgba(217, 119, 6, 0.35)"
2374
+ : "var(--dsw-alias-border-l2, rgba(9, 105, 218, 0.25))")),
1934
2375
  fontSize: "12px",
1935
2376
  },
1936
- }, h("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "6px" } }, h("span", { style: { fontWeight: 600, color: "var(--dsw-alias-label-primary, #1f2328)" } }, status === "success" ? "🎉 升级完成" : "🚀 发现新版 v" + updateInfo.latestVersion), h("button", {
2377
+ }, h("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "6px" } }, h("span", { style: { fontWeight: 600, color: "var(--dsw-alias-label-primary, #1f2328)" } }, status === "success" ? "🎉 升级完成" : (isAgeViolation ? "🛡️ 处于 24h 安全保护期" : "🚀 发现新版 v" + updateInfo.latestVersion)), h("button", {
1937
2378
  onClick: handleDismiss,
1938
2379
  title: "不再提醒此版本",
1939
2380
  style: { background: "none", border: "none", cursor: "pointer", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", fontSize: "14px", padding: 0 },
@@ -1983,13 +2424,17 @@ function UpgradeNotificationCard(props) {
1983
2424
  borderRadius: "8px",
1984
2425
  background: status === "success"
1985
2426
  ? "linear-gradient(90deg, rgba(46, 160, 67, 0.15) 0%, rgba(9, 105, 218, 0.12) 100%)"
1986
- : "linear-gradient(90deg, rgba(9, 105, 218, 0.12) 0%, rgba(137, 87, 229, 0.12) 100%)",
1987
- border: "1px solid " + (status === "success" ? "rgba(46, 160, 67, 0.35)" : "rgba(9, 105, 218, 0.3)"),
2427
+ : (isAgeViolation
2428
+ ? "linear-gradient(90deg, rgba(217, 119, 6, 0.15) 0%, rgba(245, 158, 11, 0.12) 100%)"
2429
+ : "linear-gradient(90deg, rgba(9, 105, 218, 0.12) 0%, rgba(137, 87, 229, 0.12) 100%)"),
2430
+ border: "1px solid " + (status === "success" ? "rgba(46, 160, 67, 0.35)" : (isAgeViolation ? "rgba(217, 119, 6, 0.35)" : "rgba(9, 105, 218, 0.3)")),
1988
2431
  fontSize: "13px",
1989
2432
  },
1990
- }, h("div", { style: { display: "flex", alignItems: "center", gap: "10px" } }, h("span", { style: { fontSize: "20px" } }, status === "success" ? "🎉" : "🚀"), h("div", null, h("div", { style: { fontWeight: 600, color: "var(--dsw-alias-label-primary, #1f2328)" } }, status === "success"
2433
+ }, h("div", { style: { display: "flex", alignItems: "center", gap: "10px" } }, h("span", { style: { fontSize: "20px" } }, status === "success" ? "🎉" : (isAgeViolation ? "🛡️" : "🚀")), h("div", null, h("div", { style: { fontWeight: 600, color: "var(--dsw-alias-label-primary, #1f2328)" } }, status === "success"
1991
2434
  ? "升级完成!新版本 v" + updateInfo.latestVersion + " 已写入"
1992
- : "发现 dsh-github-flow 新版本 v" + updateInfo.latestVersion + "(当前版本: v" + updateInfo.currentVersion + ")"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, msg || (status === "success"
2435
+ : (isAgeViolation
2436
+ ? "🛡️ 触发 DSH 新版本 24 小时安全保护期 (供应链防投毒策略)"
2437
+ : "发现 dsh-github-flow 新版本 v" + updateInfo.latestVersion + "(当前版本: v" + updateInfo.currentVersion + ")")), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, msg || (status === "success"
1993
2438
  ? "💡 提示:DSH 后端驻留了旧版内存,需重启 DSH 服务彻底生效后端工具;若仅预览界面改动可先刷新网页。"
1994
2439
  : "包含最新 GitHub 工具特性与性能优化,点击右侧可一键自动升级。")))), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 } }, status === "success"
1995
2440
  ? h("button", {
@@ -2021,7 +2466,9 @@ function UpgradeNotificationCard(props) {
2021
2466
  fontWeight: 600,
2022
2467
  } : {}),
2023
2468
  },
2024
- }, status === "failed" ? "📋 复制命令手动升级" : "📋 复制命令"), h("button", {
2469
+ }, status === "failed"
2470
+ ? (isAgeViolation ? "📋 复制命令 (需先退出DSH)" : "📋 复制命令手动升级")
2471
+ : "📋 复制命令"), h("button", {
2025
2472
  onClick: handleDismiss,
2026
2473
  title: "不再提醒此版本",
2027
2474
  style: { background: "none", border: "none", cursor: "pointer", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", fontSize: "16px", padding: "0 4px" },
@@ -2189,13 +2636,16 @@ function CommitDetailModal(props) {
2189
2636
  if (!effectiveBody && !effectiveStat && fetchedDetail) {
2190
2637
  effectiveStat = fetchedDetail;
2191
2638
  }
2639
+ var parsedStat = React.useMemo(() => {
2640
+ return effectiveStat && typeof parseGitStatText === "function" ? parseGitStatText(effectiveStat) : null;
2641
+ }, [effectiveStat]);
2192
2642
  return h("div", {
2193
2643
  className: "dsh-github-modal-overlay",
2194
2644
  onClick: props.onClose,
2195
2645
  style: { zIndex: 100000 },
2196
2646
  }, h("div", {
2197
2647
  className: "dsh-github-modal-dialog",
2198
- style: { width: "94%", maxWidth: "460px", maxHeight: "84vh", display: "flex", flexDirection: "column", overflow: "hidden" },
2648
+ style: { width: "94%", maxWidth: "520px", maxHeight: "86vh", display: "flex", flexDirection: "column", overflow: "hidden" },
2199
2649
  onClick: function (e) { e.stopPropagation(); },
2200
2650
  }, 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", {
2201
2651
  style: { background: "none", border: "none", cursor: "pointer", fontSize: "18px", color: "var(--dsw-alias-label-secondary, #656d76)", padding: "0 4px" },
@@ -2218,22 +2668,149 @@ function CommitDetailModal(props) {
2218
2668
  },
2219
2669
  }, effectiveBody)
2220
2670
  : null, effectiveStat
2221
- ? h("div", null, h("div", { style: { fontSize: "11px", fontWeight: 600, marginBottom: "4px", opacity: 0.8 } }, "📁 文件变更统计:"), h("pre", {
2222
- className: "dsh-github-commit-pre",
2671
+ ? h("div", { style: { display: "flex", flexDirection: "column", gap: "6px" } }, h("div", {
2223
2672
  style: {
2673
+ display: "flex",
2674
+ alignItems: "center",
2675
+ justifyContent: "space-between",
2224
2676
  fontSize: "11px",
2225
- lineHeight: "1.5",
2226
- whiteSpace: "pre-wrap",
2227
- wordBreak: "break-word",
2228
- margin: 0,
2229
- maxHeight: "34vh",
2230
- overflowY: "auto",
2231
- padding: "8px 10px",
2232
- borderRadius: "6px",
2233
- background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
2234
- border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
2677
+ fontWeight: 600,
2235
2678
  },
2236
- }, effectiveStat))
2679
+ }, h("div", { style: { opacity: 0.85, display: "flex", alignItems: "center", gap: "6px" } }, "📁 文件变更统计", parsedStat?.summary
2680
+ ? h("span", { style: { opacity: 0.65, fontWeight: 400 } }, `(${parsedStat.summary.filesChanged} 个文件)`)
2681
+ : parsedStat?.files
2682
+ ? h("span", { style: { opacity: 0.65, fontWeight: 400 } }, `(${parsedStat.files.length} 个文件)`)
2683
+ : null), parsedStat?.summary
2684
+ ? h("div", { style: { display: "flex", gap: "6px", fontFamily: "monospace", fontWeight: 600 } }, parsedStat.summary.insertions > 0
2685
+ ? h("span", {
2686
+ style: {
2687
+ color: "var(--dsw-alias-state-success-primary, #1a7f37)",
2688
+ backgroundColor: "var(--dsw-alias-state-success-subtle, rgba(46,160,67,0.12))",
2689
+ padding: "1px 6px",
2690
+ borderRadius: "10px",
2691
+ },
2692
+ }, `+${parsedStat.summary.insertions}`)
2693
+ : null, parsedStat.summary.deletions > 0
2694
+ ? h("span", {
2695
+ style: {
2696
+ color: "var(--dsw-alias-state-error-primary, #cf222e)",
2697
+ backgroundColor: "var(--dsw-alias-state-error-subtle, rgba(248,81,73,0.12))",
2698
+ padding: "1px 6px",
2699
+ borderRadius: "10px",
2700
+ },
2701
+ }, `-${parsedStat.summary.deletions}`)
2702
+ : null)
2703
+ : null), parsedStat && parsedStat.files && parsedStat.files.length > 0
2704
+ ? h("div", {
2705
+ className: "dsh-github-stat-list",
2706
+ style: {
2707
+ borderRadius: "6px",
2708
+ border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
2709
+ background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
2710
+ maxHeight: "36vh",
2711
+ overflowY: "auto",
2712
+ display: "flex",
2713
+ flexDirection: "column",
2714
+ },
2715
+ }, parsedStat.files.map(function (f, idx) {
2716
+ var diffBlocks = [];
2717
+ if (!f.isBinary && f.total > 0) {
2718
+ var totalRatio = f.additions + f.deletions;
2719
+ var addBlocks = totalRatio > 0 ? Math.round((f.additions / totalRatio) * 5) : 0;
2720
+ var delBlocks = totalRatio > 0 ? Math.round((f.deletions / totalRatio) * 5) : 0;
2721
+ if (f.additions > 0 && addBlocks === 0)
2722
+ addBlocks = 1;
2723
+ if (f.deletions > 0 && delBlocks === 0)
2724
+ delBlocks = 1;
2725
+ while (addBlocks + delBlocks > 5) {
2726
+ if (addBlocks > delBlocks)
2727
+ addBlocks--;
2728
+ else
2729
+ delBlocks--;
2730
+ }
2731
+ var neutralBlocks = Math.max(0, 5 - addBlocks - delBlocks);
2732
+ for (var bi = 0; bi < addBlocks; bi++) {
2733
+ diffBlocks.push(h("span", { key: "a" + bi, style: { width: "3px", height: "8px", borderRadius: "1px", backgroundColor: "var(--dsw-alias-state-success-primary, #1a7f37)" } }));
2734
+ }
2735
+ for (var bj = 0; bj < delBlocks; bj++) {
2736
+ diffBlocks.push(h("span", { key: "d" + bj, style: { width: "3px", height: "8px", borderRadius: "1px", backgroundColor: "var(--dsw-alias-state-error-primary, #cf222e)" } }));
2737
+ }
2738
+ for (var bk = 0; bk < neutralBlocks; bk++) {
2739
+ diffBlocks.push(h("span", { key: "n" + bk, style: { width: "3px", height: "8px", borderRadius: "1px", backgroundColor: "var(--dsw-alias-border-subtle, rgba(0,0,0,0.15))" } }));
2740
+ }
2741
+ }
2742
+ return h("div", {
2743
+ key: idx,
2744
+ className: "dsh-github-stat-row",
2745
+ style: {
2746
+ display: "flex",
2747
+ alignItems: "center",
2748
+ justifyContent: "space-between",
2749
+ padding: "6px 10px",
2750
+ fontSize: "11px",
2751
+ lineHeight: "1.4",
2752
+ borderBottom: idx < parsedStat.files.length - 1 ? "1px solid var(--dsw-alias-border-subtle, rgba(0,0,0,0.06))" : "none",
2753
+ gap: "8px",
2754
+ },
2755
+ title: f.path + (f.isBinary ? " (二进制文件)" : ` (+${f.additions}, -${f.deletions})`),
2756
+ }, h("div", {
2757
+ style: {
2758
+ fontFamily: "ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, monospace",
2759
+ overflow: "hidden",
2760
+ textOverflow: "ellipsis",
2761
+ whiteSpace: "nowrap",
2762
+ flex: 1,
2763
+ minWidth: 0,
2764
+ color: "var(--dsw-alias-label-primary, #1f2328)",
2765
+ },
2766
+ }, f.path), h("div", {
2767
+ style: {
2768
+ display: "flex",
2769
+ alignItems: "center",
2770
+ gap: "6px",
2771
+ flexShrink: 0,
2772
+ fontFamily: "monospace",
2773
+ fontWeight: 600,
2774
+ },
2775
+ }, f.isBinary
2776
+ ? h("span", { style: { opacity: 0.6, fontSize: "10px" } }, "二进制")
2777
+ : [
2778
+ f.additions > 0
2779
+ ? h("span", {
2780
+ key: "add",
2781
+ style: { color: "var(--dsw-alias-state-success-primary, #1a7f37)" },
2782
+ }, `+${f.additions}`)
2783
+ : null,
2784
+ f.deletions > 0
2785
+ ? h("span", {
2786
+ key: "del",
2787
+ style: { color: "var(--dsw-alias-state-error-primary, #cf222e)" },
2788
+ }, `-${f.deletions}`)
2789
+ : null,
2790
+ diffBlocks.length > 0
2791
+ ? h("div", {
2792
+ key: "bar",
2793
+ style: { display: "flex", gap: "1px", marginLeft: "2px", alignItems: "center" },
2794
+ }, ...diffBlocks)
2795
+ : null,
2796
+ ]));
2797
+ }))
2798
+ : h("pre", {
2799
+ className: "dsh-github-commit-pre",
2800
+ style: {
2801
+ fontSize: "11px",
2802
+ lineHeight: "1.5",
2803
+ whiteSpace: "pre-wrap",
2804
+ wordBreak: "break-word",
2805
+ margin: 0,
2806
+ maxHeight: "34vh",
2807
+ overflowY: "auto",
2808
+ padding: "8px 10px",
2809
+ borderRadius: "6px",
2810
+ background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
2811
+ border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
2812
+ },
2813
+ }, effectiveStat))
2237
2814
  : null, !effectiveBody && !effectiveStat
2238
2815
  ? (fetchingDetail
2239
2816
  ? h("div", { style: { fontSize: "12px", opacity: 0.7, padding: "8px 0" } }, "⏳ 正在获取详细变更说明…")
@@ -2509,7 +3086,7 @@ function GlobalSyncSettingsModal(props) {
2509
3086
  ? "var(--dsw-alias-state-error-primary, #cf222e)"
2510
3087
  : "var(--dsw-alias-label-secondary, #656d76)",
2511
3088
  },
2512
- }, updateMsg.text) : null)) : activeSection === "about" ? h("div", null, h("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" } }, renderOctocatBadge(40), h("div", null, h("div", { style: { fontSize: "15px", fontWeight: 600 } }, "DSH GitHub Flow"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "当前版本: v" + (aboutVersion || "…")))), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "14px", lineHeight: "1.6" } }, "让 DeepSeek Harness Agent 拥有操作 GitHub 的原生手脚。原生复用宿主机 gh CLI 安全凭据,深度打通本地 Git 工作区与云端协作,提供 9 大领域 Agent 工具、人类 Slash 指令、Web 全景驾驶舱与一键跨平台升级。"), h("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "14px" } }, h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "仓库地址"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "github.com/ItQianChen/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "npm 包"), h("a", { href: "https://www.npmjs.com/package/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "npmjs.com/package/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "开源协议"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow/blob/master/LICENSE", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "MIT License"))), h("div", {
3089
+ }, updateMsg.text) : null)) : activeSection === "about" ? h("div", null, h("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" } }, renderOctocatBadge(40), h("div", null, h("div", { style: { fontSize: "15px", fontWeight: 600 } }, "DSH GitHub Flow"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "当前版本: v" + (aboutVersion || "…")))), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "14px", lineHeight: "1.6" } }, "让 DeepSeek Harness Agent 拥有操作 GitHub 的原生手脚。原生复用宿主机 gh CLI 安全凭据,深度打通本地 Git 工作区与云端协作,提供 18 大领域 Agent 工具、人类 Slash 指令、Web 全景驾驶舱与一键跨平台升级。"), h("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "14px" } }, h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "仓库地址"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "github.com/ItQianChen/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "npm 包"), h("a", { href: "https://www.npmjs.com/package/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "npmjs.com/package/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "开源协议"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow/blob/master/LICENSE", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "MIT License"))), h("div", {
2513
3090
  className: "dsh-github-about-star-box",
2514
3091
  style: {
2515
3092
  display: "flex",
@@ -2593,7 +3170,212 @@ function GlobalSyncSettingsModal(props) {
2593
3170
  }, activeSection === "sync" ? "取消" : "完成"))));
2594
3171
  }
2595
3172
 
3173
+ // ---- [Bundled: help-modal.js] ----
3174
+ /**
3175
+ * 独立沉浸式指南大窗口组件 (Dedicated Large Reader Modal)
3176
+ *
3177
+ * 核心设计理由与交互契约:
3178
+ * 1. 彻底从设置小窗口剥离,打造 920px 宽屏、85vh 高度的沉浸式阅读视界;
3179
+ * 2. 深度适配 DSH 主题系统,全套样式基于 CSS 类与主题变量,支持深色 (data-ds-dark-theme) 与浅色无缝自适应;
3180
+ * 3. 拒绝技术自嗨清单,从用户行动视角组织:3分钟快速上手、Agent实战Prompt、双界面图解与常见排错;
3181
+ * 4. 宽屏卡片排列,配备 Prompt 一键复制到剪贴板与短暂反馈气泡;
3182
+ * 5. 监听键盘 Esc 键与遮罩点击秒关,复制完指令直接回到聊天输入框发送,零打扰、不中断工作流。
3183
+ */
3184
+ function GithubHelpModal(props) {
3185
+ if (!props.isOpen)
3186
+ return null;
3187
+ var [activeTab, setActiveTab] = React.useState(props.initialTab || "quickstart");
3188
+ var [copiedIdx, setCopiedIdx] = React.useState(-1);
3189
+ React.useEffect(function () {
3190
+ if (props.isOpen && props.initialTab) {
3191
+ setActiveTab(props.initialTab);
3192
+ }
3193
+ }, [props.isOpen, props.initialTab]);
3194
+ React.useEffect(function () {
3195
+ var onKeyDown = function (e) {
3196
+ if (e.key === "Escape" || e.keyCode === 27) {
3197
+ props.onClose();
3198
+ }
3199
+ };
3200
+ window.addEventListener("keydown", onKeyDown);
3201
+ return function () { window.removeEventListener("keydown", onKeyDown); };
3202
+ }, [props.onClose]);
3203
+ var handleCopy = function (text, idx) {
3204
+ if (navigator.clipboard) {
3205
+ navigator.clipboard.writeText(text);
3206
+ setCopiedIdx(idx);
3207
+ setTimeout(function () { setCopiedIdx(-1); }, 2000);
3208
+ }
3209
+ else {
3210
+ prompt("请复制提示词:", text);
3211
+ }
3212
+ };
3213
+ var h = React.createElement;
3214
+ // 1. 顶部 Tab 导航项
3215
+ var tabs = [
3216
+ { id: "quickstart", label: "🚀 3分钟极速上手" },
3217
+ { id: "prompts", label: "🤖 Agent 提示词实战" },
3218
+ { id: "panels", label: "🖥️ 两大界面操作图解" },
3219
+ { id: "faq", label: "🔑 常见自愈排错" },
3220
+ ];
3221
+ // 2. 渲染各 Tab 独立视图
3222
+ var renderContent = function () {
3223
+ if (activeTab === "quickstart") {
3224
+ return h("div", null, h("div", { className: "dsh-github-help-desc", style: { fontSize: "13px", marginBottom: "16px" } }, "欢迎使用 DSH GitHub Flow!本插件让 DeepSeek Harness Agent 具备直接操作 GitHub 仓库的能力,同时为你提供沉浸式的本地与云端协作工作台。跟着下面 3 步即可快速跑通你的第一个人机协作研发闭环:"), [
3225
+ {
3226
+ step: "1",
3227
+ title: "确认 GitHub CLI 登录状态",
3228
+ desc: "本插件直接复用宿主机已有的安全登录态,绝不触碰任何明文 Token。",
3229
+ actionText: "操作方式:在 DSH 任意聊天框输入 /gh 直接按回车;或查看右侧栏抽屉顶部的账号徽标。若显示未登录,在系统终端执行单行命令 gh auth login 即可完成授权。",
3230
+ },
3231
+ {
3232
+ step: "2",
3233
+ title: "打开你的代码仓库",
3234
+ desc: "在 DSH 中打开任何包含 .git 的项目文件夹,插件将自动毫秒级绑定当前仓库上下文。",
3235
+ actionText: "无需每次告诉 Agent 仓库叫什么名字,插件底层已自动完成 CWD 工作区绑定,提单与审查均精准面向当前项目。",
3236
+ },
3237
+ {
3238
+ step: "3",
3239
+ title: "直接向 Agent 下达工作指令",
3240
+ desc: "像和资深研发搭档协作一样,在聊天输入框中直接吩咐 Agent 处理日常 GitHub 任务。",
3241
+ actionText: "初次体验试一试直接对 Agent 说:\n👉 \"查看当前仓库有哪些开放的 PR,总结下改动要点\"",
3242
+ },
3243
+ ].map(function (s, i) {
3244
+ return h("div", { key: i, className: "dsh-github-help-card", style: { display: "flex", gap: "14px", alignItems: "flex-start" } }, h("div", { className: "dsh-github-help-step-badge" }, s.step), h("div", { style: { flex: 1 } }, h("div", { className: "dsh-github-help-title", style: { marginBottom: "4px" } }, s.title), h("div", { className: "dsh-github-help-desc", style: { marginBottom: "6px" } }, s.desc), h("div", { style: { fontSize: "12px", lineHeight: "1.6", whiteSpace: "pre-line", color: "inherit" } }, s.actionText)));
3245
+ }));
3246
+ }
3247
+ if (activeTab === "prompts") {
3248
+ var promptCards = [
3249
+ {
3250
+ title: "🔀 场景一:自动创建 PR 并发起代码审查",
3251
+ desc: "本地写完代码或实现新功能后,无需打开浏览器手动点点点,让 Agent 自主完成端到端提单。",
3252
+ prompt: "帮我把当前分支创建一个 PR,标题简明概括改动要点,附带详细描述",
3253
+ aiAction: "Agent 自动检查本地当前分支、比对与主干的 diff、推送到远程仓库并创建 GitHub PR,附带优雅的改动说明与关联标记。",
3254
+ },
3255
+ {
3256
+ title: "🚨 场景二:CI / Actions 门禁报错秒级排查与自动修复",
3257
+ desc: "构建挂了不用在网页反复翻几十页日志,直接吩咐 Agent 定位报错步骤并就地改代码。",
3258
+ prompt: "检查当前仓库最近一次 CI 失败的原因,精确定位报错步骤并帮我尝试修复代码",
3259
+ aiAction: "Agent 自动调用失败日志探测工具,秒级提取具体挂掉的构建步骤输出,精准定位语法/单测异常并就地修改本地代码。",
3260
+ },
3261
+ {
3262
+ title: "📌 场景三:早会与每日任务待办盘点",
3263
+ desc: "晨会前或开始工作时,一键拉取名下所有关联事项,做到心中有数。",
3264
+ prompt: "帮我看看今天在 GitHub 上分配给我的 Issue 待办与等待我评审的 PR,汇总近期动态",
3265
+ aiAction: "Agent 自动在账号维度跨仓库聚合所有分配给你的议题、等待评审的拉取请求以及提及你的动态,输出整洁紧凑的待办看板。",
3266
+ },
3267
+ {
3268
+ title: "🛡️ 场景四:代码差异 (Diff) 安全审查与重构建议",
3269
+ desc: "合并前把好最后一关,让大模型充当资深 Tech Lead 开展多维度代码走查。",
3270
+ prompt: "审查当前分支与主干的代码差异 (diff),检查是否存在潜在 Bug 或安全隐患并给出重构建议",
3271
+ aiAction: "Agent 自动获取改动文件与行级差异,结合工程最佳实践逐项排查边界条件、内存泄漏、空指针与安全漏洞。",
3272
+ },
3273
+ {
3274
+ title: "💬 场景五:团队方案提案与开源 RFC 研讨",
3275
+ desc: "需要和团队讨论架构思路或征集想法时,直接将讨论沉淀在 GitHub Discussions 知识库中。",
3276
+ prompt: "在当前仓库发起一个关于 '优化前端构建性能' 的 RFC 讨论方案",
3277
+ aiAction: "Agent 自动调用 Discussions 工具在指定分类下发起主题,格式化排版技术方案正文,并返回可供团队直达探讨的链接。",
3278
+ },
3279
+ ];
3280
+ return h("div", null, h("div", { className: "dsh-github-help-desc", style: { fontSize: "13px", marginBottom: "14px" } }, "以下精选了开发者日常研发中最高频的 5 大真实协作场景。点击卡片右侧的「📋 一键复制」按钮,直接粘贴到聊天输入框发给 Agent 即可:"), promptCards.map(function (c, idx) {
3281
+ var isCopied = copiedIdx === idx;
3282
+ return h("div", { key: idx, className: "dsh-github-help-card" }, h("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "8px" } }, h("div", { className: "dsh-github-help-title" }, c.title), h("button", {
3283
+ className: "dsh-github-btn-sm",
3284
+ onClick: function () { handleCopy(c.prompt, idx); },
3285
+ style: {
3286
+ padding: "4px 10px",
3287
+ fontSize: "12px",
3288
+ background: isCopied ? "#2ea043" : "var(--dsw-alias-link, #0969da)",
3289
+ color: "#ffffff",
3290
+ borderColor: isCopied ? "#2ea043" : "var(--dsw-alias-link, #0969da)",
3291
+ fontWeight: 600,
3292
+ transition: "all 0.2s ease",
3293
+ },
3294
+ }, isCopied ? "✅ 已复制到剪贴板!" : "📋 一键复制 Prompt")), h("div", { className: "dsh-github-help-desc", style: { marginBottom: "10px" } }, c.desc), h("div", { className: "dsh-github-prompt-box" }, `"${c.prompt}"`), h("div", { style: { fontSize: "11px", color: "var(--dsw-alias-state-attention-primary, #d29922)", lineHeight: "1.4" } }, "💡 " + c.aiAction));
3295
+ }));
3296
+ }
3297
+ if (activeTab === "panels") {
3298
+ return h("div", null, h("div", { className: "dsh-github-help-desc", style: { fontSize: "13px", marginBottom: "16px" } }, "插件为人类开发者量身打造了两套互补的可视化工作台,消灭频繁在 VS Code、终端与浏览器切屏的上下文丢失痛点:"), [
3299
+ {
3300
+ title: "1. 全景驾驶舱 (Main Dashboard) —— 掌控全局资产",
3301
+ items: [
3302
+ "打开入口:点击 DSH 最左侧主导航栏的 Octocat 猫咪徽标,瞬间全屏展开;",
3303
+ "SWR 0ms 瞬间秒开:采用本地持久化快照体系,打开界面零转圈、零等待;",
3304
+ "名下仓库一览:一屏透视名下所有 GitHub 仓库、默认分支与 Star 状态,支持一键切换工作区;",
3305
+ "三维待办 Issue 矩阵:智能划分「📥 用户提单(反馈到我名下仓库)」、「🎯 指派待办(分配给我)」与「✍️ 我发起的」,一键药丸筛选;",
3306
+ "全局配置中心:右上方提供数据同步节奏、网络代理与更新源的集中配置弹窗。",
3307
+ ],
3308
+ },
3309
+ {
3310
+ title: "2. 会话右侧栏面板 (Sidebar Panel) —— 深度协同当前工作区",
3311
+ items: [
3312
+ "打开入口:位于每个会话工作区的右侧抽屉栏,深度锁定当前会话的物理 Git 仓库;",
3313
+ "未提交改动 (Dirty Tab):实时监听本地未提交文件列表与改动数量,开发改代码一目了然;",
3314
+ "分支查看与安全切换:点击分支名称下拉秒级搜索本地/远程分支;具备未提交变更安全拦截,杜绝代码被冲掉;",
3315
+ "Git 提交时间线 (Git Tab):清晰三段式呈现待推送(领先)、待拉取(落后)与最近提交,点击任意提交行可弹出 Commit 完整详情弹窗;",
3316
+ "动静解耦架构:修改本地代码只定向查询本地 .git 核心元数据(耗时仅 5ms),绝对不发起远端网络请求,杜绝网络风暴。",
3317
+ ],
3318
+ },
3319
+ ].map(function (sec, idx) {
3320
+ return h("div", { key: idx, className: "dsh-github-help-card" }, h("div", { className: "dsh-github-help-title", style: { marginBottom: "8px" } }, sec.title), h("ul", { style: { margin: 0, paddingLeft: "18px", fontSize: "12px", lineHeight: "1.7", color: "var(--dsw-alias-label-secondary, #8b949e)" } }, sec.items.map(function (it, i) { return h("li", { key: i }, it); })));
3321
+ }));
3322
+ }
3323
+ if (activeTab === "faq") {
3324
+ var faqs = [
3325
+ {
3326
+ q: "Q1: 界面提示「未登录 GitHub」或「Authentication Required」怎么办?",
3327
+ a: "插件直接复用宿主系统的 GitHub CLI 登录态。请打开系统终端运行命令 `gh auth login`,依次选择 GitHub.com ➔ HTTPS ➔ 网页回车授权,一分钟内即可完成登录。登录完成后刷新界面即可自动识别。",
3328
+ },
3329
+ {
3330
+ q: "Q2: 提示「缺少权限 (Scope)」或「SCOPE_REQUIRED」怎么办?",
3331
+ a: "当调用项目看板(Projects v2)或云端开发机(Codespaces)时,GitHub 要求专有的 Token Scope。在系统终端执行对应的一键自愈命令完成增量授权即可(无需重新登录):\n- 项目看板:`gh auth refresh -s read:project`\n- 云端开发机:`gh auth refresh -h github.com -s codespace`",
3332
+ },
3333
+ {
3334
+ q: "Q3: 点击一键升级提示「处于 24h 安全保护期 (防投毒策略)」怎么办?",
3335
+ a: "这是 DSH 官方内置包管理器针对刚发布未满 24 小时新版本的供应链防投毒安全锁机制。\n- 立即体验:请先【完全退出关闭 DSH 应用程序】,然后在系统终端执行复制的手动安装命令;\n- 稍后升级:无需任何操作,新版本发版满 24 小时后,再次点击一键升级即可自动安装。",
3336
+ },
3337
+ {
3338
+ q: "Q4: 使用代理节点时偶尔出现网络波动怎么办?",
3339
+ a: "插件内置了 1s ➔ 2s ➔ 4s 的阶梯指数退避重试机制。代理节点若发生短时抖动断流,系统会自动在后台静默发起自愈重试,在节点恢复后瞬间平滑拉取最新数据,不会立刻亮出大红横幅打扰你。同时电脑联网恢复瞬间也会自动触发静默更新。",
3340
+ },
3341
+ ];
3342
+ return h("div", null, h("div", { className: "dsh-github-help-desc", style: { fontSize: "13px", marginBottom: "14px" } }, "常见环境、权限与网络问题的排查与自愈指引:"), faqs.map(function (f, idx) {
3343
+ return h("div", { key: idx, className: "dsh-github-help-card" }, h("div", { className: "dsh-github-help-title", style: { marginBottom: "6px", fontSize: "13px" } }, f.q), h("div", { className: "dsh-github-help-desc", style: { lineHeight: "1.6", whiteSpace: "pre-line" } }, f.a));
3344
+ }));
3345
+ }
3346
+ return null;
3347
+ };
3348
+ return h("div", { className: "dsh-github-modal-overlay", onClick: props.onClose }, h("div", {
3349
+ className: "dsh-github-help-dialog",
3350
+ onClick: function (e) { e.stopPropagation(); },
3351
+ },
3352
+ // 顶部标题栏
3353
+ h("div", { className: "dsh-github-help-header" }, h("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, h("span", { style: { fontSize: "20px" } }, "📖"), h("h3", { style: { margin: 0, fontSize: "16px", fontWeight: 600, color: "inherit" } }, "DSH GitHub Flow · 使用指南与实战手册")), h("button", {
3354
+ style: { background: "none", border: "none", cursor: "pointer", fontSize: "18px", color: "var(--dsw-alias-label-secondary, #8b949e)", padding: "0 4px" },
3355
+ onClick: props.onClose,
3356
+ title: "关闭 (Esc)",
3357
+ }, "✕")),
3358
+ // 顶部四分类 Segment 导航栏
3359
+ h("div", { className: "dsh-github-help-nav" }, tabs.map(function (t) {
3360
+ var isActive = activeTab === t.id;
3361
+ return h("button", {
3362
+ key: t.id,
3363
+ className: "dsh-github-help-tab-btn" + (isActive ? " active" : ""),
3364
+ onClick: function () { setActiveTab(t.id); },
3365
+ }, t.label);
3366
+ })),
3367
+ // 主内容滚动区
3368
+ h("div", { className: "dsh-github-help-scroll-body" }, renderContent()),
3369
+ // 底部工具栏
3370
+ h("div", { className: "dsh-github-help-footer" }, h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary, #8c959f)" } }, "💡 提示:按键盘 Esc 键可随时快速关闭大窗口返回会话"), h("button", {
3371
+ className: "dsh-github-btn-sm",
3372
+ style: { padding: "6px 16px", fontWeight: 600 },
3373
+ onClick: props.onClose,
3374
+ }, "关闭窗口"))));
3375
+ }
3376
+
2596
3377
 
3378
+ var currentPluginCtx = null;
2597
3379
  /**
2598
3380
  * 按容器记录的 Tab 键应用互斥显示状态(hidden 页 + active 按钮)。
2599
3381
  * 模块级纯 DOM 操作:渲染函数(switchTab 点击路径)与宿主组件的 effect
@@ -2601,6 +3383,11 @@ function GlobalSyncSettingsModal(props) {
2601
3383
  * 重设 hidden/active,用户手动切的 Tab 会被一次后台数据同步拽回默认页。
2602
3384
  */
2603
3385
  function applyTabState(container, tabKey) {
3386
+ container.setAttribute("data-active-tab", tabKey);
3387
+ var rightbar = container.closest(".dsh-github-rightbar-container");
3388
+ if (rightbar) {
3389
+ rightbar.setAttribute("data-active-tab", tabKey);
3390
+ }
2604
3391
  var pages = container.querySelectorAll("[data-tab-page]");
2605
3392
  pages.forEach(function (page) {
2606
3393
  page.hidden = page.getAttribute("data-tab-page") !== tabKey;
@@ -2612,8 +3399,234 @@ function GlobalSyncSettingsModal(props) {
2612
3399
  btn.setAttribute("aria-selected", active ? "true" : "false");
2613
3400
  });
2614
3401
  }
2615
- // 渲染 GitHub 仓库信息与操作视图的核心函数
2616
- function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false, updateInfo = null, syncStatus = "", lastSyncedAt = 0, onOpenSettings = null, cwd = null, onSelectCommit = null, gitLogPagination = null, onShowToast = null) {
3402
+ /**
3403
+ * 未提交文件原生二级 Git Diff 下钻视图组件(右侧栏内就地渲染,零模态遮罩)
3404
+ */
3405
+ function UncommittedDiffView(props) {
3406
+ var f = props.file;
3407
+ var h = React.createElement;
3408
+ var [diffData, setDiffData] = React.useState(null);
3409
+ var [loading, setLoading] = React.useState(true);
3410
+ var [error, setError] = React.useState(null);
3411
+ var [copied, setCopied] = React.useState(false);
3412
+ React.useEffect(function () {
3413
+ var onKeyDown = function (e) {
3414
+ if (e.key === "Escape" || e.keyCode === 27) {
3415
+ props.onBack();
3416
+ }
3417
+ };
3418
+ window.addEventListener("keydown", onKeyDown);
3419
+ return function () { window.removeEventListener("keydown", onKeyDown); };
3420
+ }, [props.onBack]);
3421
+ React.useEffect(function () {
3422
+ setLoading(true);
3423
+ setError(null);
3424
+ setDiffData(null);
3425
+ var fetchDiff = async function () {
3426
+ var primaryUrl = "/api/github/file-diff?file=" + encodeURIComponent(f.path) + (props.cwd ? "&cwd=" + encodeURIComponent(props.cwd) : "");
3427
+ var tryParseResponse = async function (res) {
3428
+ var text = await res.text().catch(function () { return ""; });
3429
+ var json = null;
3430
+ if (text && (text.trim().startsWith("{") || text.trim().startsWith("["))) {
3431
+ try {
3432
+ json = JSON.parse(text);
3433
+ }
3434
+ catch (e) { }
3435
+ }
3436
+ return { ok: res.ok, status: res.status, text: text, json: json };
3437
+ };
3438
+ try {
3439
+ var r1 = await fetch(primaryUrl);
3440
+ var p1 = await tryParseResponse(r1);
3441
+ if (p1.ok && p1.json && p1.json.ok && p1.json.data) {
3442
+ setDiffData(p1.json.data);
3443
+ setLoading(false);
3444
+ return;
3445
+ }
3446
+ // 若主路由遇到 404 (后端常驻进程未重启),自动降级请求常驻的 /api/github/action 端点
3447
+ if (p1.status === 404) {
3448
+ var r2 = await fetch("/api/github/action", {
3449
+ method: "POST",
3450
+ headers: { "Content-Type": "application/json" },
3451
+ body: JSON.stringify({
3452
+ action: "get_file_diff",
3453
+ file: f.path,
3454
+ cwd: props.cwd || undefined,
3455
+ }),
3456
+ }).catch(function () { return null; });
3457
+ if (r2) {
3458
+ var p2 = await tryParseResponse(r2);
3459
+ if (p2.ok && p2.json && p2.json.data) {
3460
+ setDiffData(p2.json.data);
3461
+ setLoading(false);
3462
+ return;
3463
+ }
3464
+ }
3465
+ setLoading(false);
3466
+ setError("后端对比接口未激活 (404)。请重启 DSH 桌面主程序以载入最新对比引擎;您也可点击上方「在 DSH 中打开」直接预览文件源码。");
3467
+ return;
3468
+ }
3469
+ setLoading(false);
3470
+ setError(p1.json?.error || p1.text || ("查询差异失败 (HTTP " + p1.status + ")"));
3471
+ }
3472
+ catch (err) {
3473
+ setLoading(false);
3474
+ setError(err?.message || "网络请求异常");
3475
+ }
3476
+ };
3477
+ fetchDiff();
3478
+ }, [f.path, props.cwd]);
3479
+ var copyDiff = function () {
3480
+ if (!diffData?.diff)
3481
+ return;
3482
+ if (navigator?.clipboard?.writeText) {
3483
+ navigator.clipboard.writeText(diffData.diff).then(function () {
3484
+ setCopied(true);
3485
+ setTimeout(function () { setCopied(false); }, 2000);
3486
+ }).catch(function () { });
3487
+ }
3488
+ };
3489
+ var statusColor = f.status === "??" ? "var(--dsw-alias-state-success-primary, #1a7f37)"
3490
+ : f.status.indexOf("D") >= 0 ? "var(--dsw-alias-state-error-primary, #cf222e)"
3491
+ : "var(--dsw-alias-state-attention-primary, #9a6700)";
3492
+ var statusText = f.status === "??" ? "未跟踪新增"
3493
+ : f.status.indexOf("D") >= 0 ? "已删除"
3494
+ : f.status.indexOf("R") >= 0 ? "已重命名"
3495
+ : "已修改";
3496
+ return h("div", {
3497
+ className: "dsh-github-inline-diff-view",
3498
+ style: {
3499
+ display: "flex",
3500
+ flexDirection: "column",
3501
+ gap: "8px",
3502
+ minHeight: "260px",
3503
+ padding: "4px 0",
3504
+ },
3505
+ },
3506
+ // 顶部操作与状态栏
3507
+ h("div", {
3508
+ style: {
3509
+ display: "flex",
3510
+ alignItems: "center",
3511
+ justifyContent: "space-between",
3512
+ flexWrap: "wrap",
3513
+ gap: "6px",
3514
+ paddingBottom: "8px",
3515
+ borderBottom: "1px solid var(--dsw-alias-border-subtle, rgba(0,0,0,0.1))",
3516
+ },
3517
+ }, h("div", { style: { display: "flex", alignItems: "center", gap: "6px" } }, h("button", {
3518
+ className: "dsh-github-btn-sm",
3519
+ style: { padding: "3px 8px", cursor: "pointer", fontWeight: 600, fontSize: "11px" },
3520
+ onClick: props.onBack,
3521
+ title: "返回未提交文件清单 (Esc)",
3522
+ }, "← 返回列表"), h("span", {
3523
+ className: "dsh-github-badge",
3524
+ style: {
3525
+ backgroundColor: statusColor + "22",
3526
+ color: statusColor,
3527
+ fontWeight: 600,
3528
+ fontSize: "11px",
3529
+ },
3530
+ }, `${f.status} ${statusText}`), diffData && diffData.additions !== undefined
3531
+ ? h("div", { style: { display: "flex", gap: "4px", fontSize: "11px", fontFamily: "monospace" } }, h("span", { style: { color: "var(--dsw-alias-state-success-primary, #1a7f37)", fontWeight: 600 } }, `+${diffData.additions}`), h("span", { style: { color: "var(--dsw-alias-state-error-primary, #cf222e)", fontWeight: 600 } }, `-${diffData.deletions}`))
3532
+ : null), h("div", { style: { display: "flex", alignItems: "center", gap: "6px" } }, diffData?.diff
3533
+ ? h("button", {
3534
+ className: "dsh-github-btn-sm",
3535
+ style: { padding: "3px 8px", fontSize: "11px", cursor: "pointer" },
3536
+ onClick: copyDiff,
3537
+ title: "复制 Unified Diff 原文",
3538
+ }, copied ? "✓ 已复制" : "📋 复制 Diff")
3539
+ : null, props.onOpenInDsh
3540
+ ? h("button", {
3541
+ className: "dsh-github-btn-sm",
3542
+ style: { padding: "3px 8px", fontSize: "11px", cursor: "pointer" },
3543
+ onClick: function () {
3544
+ if (props.onOpenInDsh)
3545
+ props.onOpenInDsh(f.path);
3546
+ },
3547
+ title: "在 DSH 侧边栏打开查看该文件完整源码",
3548
+ }, "📄 在 DSH 中打开")
3549
+ : null)),
3550
+ // 文件路径栏
3551
+ h("div", {
3552
+ style: {
3553
+ fontSize: "11px",
3554
+ fontFamily: "monospace",
3555
+ color: "var(--dsw-alias-label-secondary, #656d76)",
3556
+ wordBreak: "break-all",
3557
+ padding: "3px 6px",
3558
+ backgroundColor: "var(--dsw-alias-bg-subtle, rgba(0,0,0,0.03))",
3559
+ borderRadius: "4px",
3560
+ },
3561
+ title: f.path,
3562
+ }, f.path),
3563
+ // Diff 主体内容
3564
+ loading
3565
+ ? h("div", { style: { padding: "32px 0", textAlign: "center", opacity: 0.65, fontSize: "12px" } }, "⏳ 正在计算 Git 代码比对差异...")
3566
+ : error
3567
+ ? h("div", {
3568
+ style: {
3569
+ padding: "16px",
3570
+ color: "var(--dsw-alias-label-primary, #1f2328)",
3571
+ fontSize: "12px",
3572
+ lineHeight: "1.6",
3573
+ backgroundColor: "var(--dsw-alias-bg-subtle, rgba(0,0,0,0.03))",
3574
+ borderRadius: "6px",
3575
+ },
3576
+ }, h("div", { style: { color: "var(--dsw-alias-state-error-primary, #cf222e)", fontWeight: 600, marginBottom: "6px" } }, `⚠️ ${error}`), h("div", { style: { fontSize: "11px", opacity: 0.75 } }, "💡 提示:重启 DSH 桌面主程序即可激活本地比对路由;在此期间您也可点击上方「📄 在 DSH 中打开」直接预览文件源码。"))
3577
+ : diffData?.isBinary
3578
+ ? h("div", { style: { padding: "32px 0", textAlign: "center", opacity: 0.8, fontSize: "12px" } }, "📦 该文件为二进制格式(图片或编译产物),无法直接展示文本差异。")
3579
+ : h("div", {
3580
+ className: "dsh-github-inline-diff-content",
3581
+ style: {
3582
+ margin: 0,
3583
+ padding: "4px 0",
3584
+ fontFamily: "ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, monospace",
3585
+ fontSize: "11px",
3586
+ lineHeight: "1.5",
3587
+ overflowX: "auto",
3588
+ maxHeight: "68vh",
3589
+ borderRadius: "6px",
3590
+ border: "1px solid var(--dsw-alias-border-subtle, rgba(0,0,0,0.08))",
3591
+ backgroundColor: "var(--dsw-alias-bg-canvas, #ffffff)",
3592
+ },
3593
+ }, (diffData?.diff || "").split("\n").map(function (line, idx) {
3594
+ var isAdd = line.startsWith("+") && !line.startsWith("+++");
3595
+ var isDel = line.startsWith("-") && !line.startsWith("---");
3596
+ var isHunk = line.startsWith("@@");
3597
+ var isHeader = line.startsWith("diff --git") || line.startsWith("index ") || line.startsWith("---") || line.startsWith("+++");
3598
+ var lineStyle = {
3599
+ display: "flex",
3600
+ padding: "0 8px",
3601
+ whiteSpace: "pre",
3602
+ minWidth: "100%",
3603
+ boxSizing: "border-box",
3604
+ };
3605
+ if (isAdd) {
3606
+ lineStyle.backgroundColor = "var(--dsw-alias-state-success-subtle, rgba(46,160,67,0.15))";
3607
+ lineStyle.color = "var(--dsw-alias-state-success-primary, #1a7f37)";
3608
+ }
3609
+ else if (isDel) {
3610
+ lineStyle.backgroundColor = "var(--dsw-alias-state-error-subtle, rgba(248,81,73,0.15))";
3611
+ lineStyle.color = "var(--dsw-alias-state-error-primary, #cf222e)";
3612
+ }
3613
+ else if (isHunk) {
3614
+ lineStyle.backgroundColor = "var(--dsw-alias-bg-subtle, rgba(0,0,0,0.04))";
3615
+ lineStyle.color = "var(--dsw-alias-label-secondary, #656d76)";
3616
+ lineStyle.fontWeight = 600;
3617
+ lineStyle.margin = "2px 0";
3618
+ }
3619
+ else if (isHeader) {
3620
+ lineStyle.color = "var(--dsw-alias-label-muted, #8c959f)";
3621
+ lineStyle.fontSize = "10px";
3622
+ }
3623
+ else {
3624
+ lineStyle.color = "var(--dsw-alias-label-primary, #1f2328)";
3625
+ }
3626
+ return h("div", { key: idx, style: lineStyle }, line || " ");
3627
+ })));
3628
+ }
3629
+ function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false, updateInfo = null, syncStatus = "", lastSyncedAt = 0, onOpenSettings = null, cwd = null, onSelectCommit = null, gitLogPagination = null, onShowToast = null, onOpenHelp = null, onOpenFile = null, dirtyPagination = null, activeDiffFile = null, onSelectDiffFile = null, onCloseDiff = null, onOpenInDsh = null, headerCardsVisible = true, onToggleHeaderCards = null) {
2617
3630
  var auth = data ? data.auth : null;
2618
3631
  var repo = data ? data.repo : null;
2619
3632
  var prs = (data && data.pullRequests) || [];
@@ -2637,20 +3650,56 @@ function GlobalSyncSettingsModal(props) {
2637
3650
  },
2638
3651
  })
2639
3652
  : null;
2640
- var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(isRightbar ? 26 : 30), h("h2", null, isRightbar ? "GitHub 仓库" : "GitHub 工作台"), renderPluginBadge(h), syncStatus ? h(SyncStatusBadge, { syncStatus, lastSyncedAt, isRightbar }) : null), h("div", { style: { display: "flex", alignItems: "center", gap: "6px" } }, h("button", {
3653
+ var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(isRightbar ? 26 : 30), h("h2", null, isRightbar ? "GitHub 仓库" : "GitHub 工作台"), renderPluginBadge(h), syncStatus ? h(SyncStatusBadge, { syncStatus, lastSyncedAt, isRightbar }) : null), h("div", { style: { display: "flex", alignItems: "center", gap: "6px" } }, isRightbar && onToggleHeaderCards ? h("button", {
3654
+ className: "dsh-github-btn-sm" + (!headerCardsVisible ? " active" : ""),
3655
+ onClick: onToggleHeaderCards,
3656
+ title: headerCardsVisible ? "收起上方仓库信息与快捷操作栏(增加下方工作区高度)" : "展开上方仓库信息与快捷操作栏",
3657
+ style: {
3658
+ padding: "4px 8px",
3659
+ fontSize: "11px",
3660
+ fontWeight: 600,
3661
+ display: "inline-flex",
3662
+ alignItems: "center",
3663
+ gap: "3px",
3664
+ cursor: "pointer",
3665
+ },
3666
+ "aria-expanded": headerCardsVisible ? "true" : "false",
3667
+ }, headerCardsVisible ? "🔼 收起" : "🔽 展开") : null, h("button", {
2641
3668
  className: "dsh-github-refresh-btn",
2642
3669
  onClick: onRefresh,
2643
3670
  disabled: loading,
2644
- }, loading ? "刷新中..." : "🔄 刷新"), onOpenSettings ? h("button", {
3671
+ }, loading ? "刷新中..." : "🔄 刷新"), (onOpenHelp || onOpenSettings) ? h("button", {
3672
+ className: "dsh-github-btn-sm",
3673
+ onClick: () => (onOpenHelp ? onOpenHelp() : (onOpenSettings && onOpenSettings("help"))),
3674
+ title: "打开使用帮助与实战手册大窗口(Esc 可退出)",
3675
+ style: { padding: "4px 8px", fontSize: "12px" },
3676
+ }, "❓") : null, onOpenSettings ? h("button", {
2645
3677
  className: "dsh-github-btn-sm",
2646
- onClick: onOpenSettings,
3678
+ onClick: () => onOpenSettings("sync"),
2647
3679
  title: "配置数据同步节奏、本地缓存、网络代理与插件信息",
2648
3680
  style: { padding: "4px 10px" },
2649
3681
  }, "⚙️") : null));
2650
3682
  var gitSt = data && data.gitStatus;
2651
3683
  var relInfo = data && data.releaseInfo;
2652
3684
  var branchPr = data && data.branchPr;
2653
- var repoCardEl = h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "当前工作区 GitHub 仓库"), h("div", { className: "dsh-github-card-value" }, repo
3685
+ var repoCardEl = h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title", style: { display: "flex", justifyContent: "space-between", alignItems: "center" } }, h("span", null, "当前工作区 GitHub 仓库"), repo && repo.stargazerCount !== undefined
3686
+ ? h("a", {
3687
+ href: `${repo.url}/stargazers`,
3688
+ target: "_blank",
3689
+ className: "dsh-github-badge",
3690
+ style: {
3691
+ textDecoration: "none",
3692
+ display: "inline-flex",
3693
+ alignItems: "center",
3694
+ gap: "3px",
3695
+ fontWeight: 600,
3696
+ fontSize: "11px",
3697
+ cursor: "pointer",
3698
+ padding: "1px 6px",
3699
+ },
3700
+ title: `当前仓库 Star 数量: ${repo.stargazerCount}(点击前往 GitHub 查看)`,
3701
+ }, "⭐ ", String(repo.stargazerCount))
3702
+ : null), h("div", { className: "dsh-github-card-value" }, repo
2654
3703
  ? h("a", { href: repo.url, target: "_blank", className: "dsh-github-repo-link" }, repo.nameWithOwner)
2655
3704
  : h("span", { style: { color: "var(--dsw-alias-state-error-primary, #cf222e)" } }, "未检测到关联的 GitHub 仓库"), repo
2656
3705
  ? h(BranchSelectorDropdown, {
@@ -2762,25 +3811,86 @@ function GlobalSyncSettingsModal(props) {
2762
3811
  ? h("a", { className: "dsh-github-item-title", href: branchPr.url, target: "_blank" }, `PR #${branchPr.number} ${branchPr.title}`)
2763
3812
  : 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)
2764
3813
  : null);
2765
- 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
2766
- ? h("div", { className: "dsh-github-empty" }, "工作区干净,没有未提交的变更 ✓")
2767
- : h("div", { style: { fontSize: "12px", lineHeight: "1.9" } }, (gitSt.dirtyFiles || []).map(function (f, i) {
2768
- return h("div", { key: i, style: { display: "flex", gap: "8px", padding: "2px 4px", borderRadius: "4px" } }, h("span", {
2769
- style: {
2770
- minWidth: "24px",
2771
- textAlign: "center",
2772
- opacity: 0.75,
2773
- fontSize: "11px",
2774
- fontFamily: "monospace",
2775
- color: f.status === "??" ? "var(--dsw-alias-state-success-primary, #1a7f37)"
2776
- : f.status.indexOf("D") >= 0 ? "var(--dsw-alias-state-error-primary, #cf222e)"
2777
- : f.status.indexOf("M") >= 0 || f.status.indexOf("R") >= 0 ? "var(--dsw-alias-state-attention-primary, #9a6700)"
2778
- : "inherit",
2779
- },
2780
- }, f.status === "??" ? "??" : f.status), h("span", { style: { wordBreak: "break-all" } }, f.path));
2781
- }), gitSt.dirtyCount > (gitSt.dirtyFiles || []).length
2782
- ? h("div", { style: { opacity: 0.6, marginTop: "6px" } }, `…共 ${gitSt.dirtyCount} 个变更(仅列前 ${(gitSt.dirtyFiles || []).length})`)
2783
- : null));
3814
+ var baseDirty = (gitSt && gitSt.dirtyFiles) || [];
3815
+ var extraDirty = (dirtyPagination && dirtyPagination.extraDirtyFiles) || [];
3816
+ var allDirtyFiles = [];
3817
+ var seenDirtyPaths = new Set();
3818
+ baseDirty.concat(extraDirty).forEach(function (f) {
3819
+ if (f && f.path && !seenDirtyPaths.has(f.path)) {
3820
+ seenDirtyPaths.add(f.path);
3821
+ allDirtyFiles.push(f);
3822
+ }
3823
+ });
3824
+ var visibleDirtyLimit = (dirtyPagination && dirtyPagination.visibleCount) || 15;
3825
+ var displayedDirty = allDirtyFiles.slice(0, visibleDirtyLimit);
3826
+ var totalDirtyCount = gitSt ? gitSt.dirtyCount : allDirtyFiles.length;
3827
+ var hasMoreDirtyToLoad = displayedDirty.length < totalDirtyCount;
3828
+ var dirtyHeaderTitle = gitSt && totalDirtyCount > displayedDirty.length
3829
+ ? `📁 未提交清单 (${displayedDirty.length} / ${totalDirtyCount})`
3830
+ : `📁 未提交清单 (${gitSt ? gitSt.dirtyCount : 0})`;
3831
+ var dirtySectionEl = activeDiffFile
3832
+ ? h(UncommittedDiffView, {
3833
+ file: activeDiffFile,
3834
+ onBack: onCloseDiff || (() => { }),
3835
+ cwd: cwd,
3836
+ onOpenInDsh: onOpenInDsh,
3837
+ })
3838
+ : h("div", { className: "dsh-github-section" }, h("div", { className: "dsh-github-section-header" }, h("h3", null, dirtyHeaderTitle)), !gitSt || gitSt.dirtyCount === 0
3839
+ ? h("div", { className: "dsh-github-empty" }, "工作区干净,没有未提交的变更 ✓")
3840
+ : h("div", { style: { fontSize: "12px", lineHeight: "1.9" } }, displayedDirty.map(function (f, i) {
3841
+ var statusColor = f.status === "??" ? "var(--dsw-alias-state-success-primary, #1a7f37)"
3842
+ : f.status.indexOf("D") >= 0 ? "var(--dsw-alias-state-error-primary, #cf222e)"
3843
+ : f.status.indexOf("M") >= 0 || f.status.indexOf("R") >= 0 ? "var(--dsw-alias-state-attention-primary, #9a6700)"
3844
+ : "inherit";
3845
+ var statusText = f.status === "??" ? "未跟踪新增"
3846
+ : f.status.indexOf("D") >= 0 ? "已删除"
3847
+ : f.status.indexOf("R") >= 0 ? "已重命名"
3848
+ : "已修改";
3849
+ return h("div", {
3850
+ key: f.path || i,
3851
+ className: "dsh-github-dirty-item",
3852
+ title: `${f.path} (${statusText} [${f.status}])\n点击查看代码改动对比 (Unified Diff)`,
3853
+ onClick: function () {
3854
+ if (typeof onSelectDiffFile === "function") {
3855
+ onSelectDiffFile(f);
3856
+ }
3857
+ },
3858
+ }, h("span", {
3859
+ style: {
3860
+ minWidth: "24px",
3861
+ textAlign: "center",
3862
+ opacity: 0.85,
3863
+ fontSize: "11px",
3864
+ fontFamily: "monospace",
3865
+ color: statusColor,
3866
+ fontWeight: 600,
3867
+ },
3868
+ }, f.status === "??" ? "??" : f.status), h("span", { style: { wordBreak: "break-all", flex: 1, fontSize: "12px" } }, f.path));
3869
+ }), hasMoreDirtyToLoad
3870
+ ? h("div", { style: { marginTop: "10px", textAlign: "center" } }, dirtyPagination?.loadingMore
3871
+ ? h("div", { style: { fontSize: "11px", opacity: 0.75, padding: "6px 0" } }, "⏳ 正在加载更多未提交变更…")
3872
+ : h("div", {
3873
+ style: {
3874
+ fontSize: "11px",
3875
+ opacity: 0.7,
3876
+ padding: "6px 0",
3877
+ display: "flex",
3878
+ alignItems: "center",
3879
+ justifyContent: "center",
3880
+ gap: "6px",
3881
+ cursor: "pointer",
3882
+ borderRadius: "4px",
3883
+ },
3884
+ title: "向下滑动滚轮或点击加载更多",
3885
+ onClick: function () {
3886
+ if (dirtyPagination && typeof dirtyPagination.onLoadMore === "function") {
3887
+ dirtyPagination.onLoadMore();
3888
+ }
3889
+ },
3890
+ }, h("span", null, "⬇️ 向下滑动自动加载"), h("span", { style: { fontFamily: "monospace" } }, `(${displayedDirty.length} / ${totalDirtyCount})`)))
3891
+ : totalDirtyCount > 15
3892
+ ? h("div", { style: { textAlign: "center", fontSize: "11px", opacity: 0.5, padding: "6px 0", marginTop: "8px" } }, `✓ 已加载全部 ${totalDirtyCount} 个未提交变更`)
3893
+ : null));
2784
3894
  // 快捷操作栏 (仅在检测到关联仓库时展示,未绑定仓库时不展示虚假按钮)
2785
3895
  var actionsBarEl = repo ? h("div", { className: "dsh-github-quick-actions" }, h("button", {
2786
3896
  className: "dsh-github-action-btn",
@@ -2907,6 +4017,10 @@ function GlobalSyncSettingsModal(props) {
2907
4017
  var switchTab = function (container, tabKey) {
2908
4018
  container.setAttribute("data-active-tab", tabKey);
2909
4019
  applyTabState(container, tabKey);
4020
+ var rightbar = container.closest(".dsh-github-rightbar-container");
4021
+ if (rightbar) {
4022
+ rightbar.setAttribute("data-active-tab", tabKey);
4023
+ }
2910
4024
  };
2911
4025
  var tabsBarEl = h("div", { className: "dsh-github-tabs", style: { marginBottom: "12px" } }, ...TAB_DEFS.map(function (t) {
2912
4026
  var labelText = t.count !== null ? `${t.label} (${t.count})` : t.label;
@@ -2923,7 +4037,15 @@ function GlobalSyncSettingsModal(props) {
2923
4037
  }, t.icon ? [t.icon, " ", labelText] : labelText);
2924
4038
  }));
2925
4039
  var tabbedContentEl = h("div", { "data-active-tab": defaultTab }, tabsBarEl, 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);
2926
- return [headerEl, networkAlertBannerEl, upgradeBannerEl, authGuideEl, repoCardEl, actionsBarEl, tabbedContentEl].filter(Boolean);
4040
+ return [
4041
+ headerEl,
4042
+ networkAlertBannerEl,
4043
+ upgradeBannerEl,
4044
+ authGuideEl,
4045
+ headerCardsVisible ? repoCardEl : null,
4046
+ headerCardsVisible ? actionsBarEl : null,
4047
+ tabbedContentEl,
4048
+ ].filter(Boolean);
2927
4049
  }
2928
4050
  // 1. 右侧栏专属视图 (Right Sidebar Tab Body)
2929
4051
  function GithubRightbarBody(props) {
@@ -3025,10 +4147,19 @@ function GlobalSyncSettingsModal(props) {
3025
4147
  if (params.length > 0) {
3026
4148
  url += "?" + params.join("&");
3027
4149
  }
3028
- var res = await fetch(url, { signal: controller.signal });
4150
+ // 接入 1s/2s/4s 阶梯退避重试,平滑吸收代理节点与网络的偶发抖动断流
4151
+ var { res, json } = await fetchWithRetry(url, {}, {
4152
+ signal: controller.signal,
4153
+ maxRetries: 3,
4154
+ baseDelayMs: 1000,
4155
+ onRetry: (attempt) => {
4156
+ setSyncStatus("syncing");
4157
+ setError(`网络抖动,正在自愈重试 (${attempt}/3)...`);
4158
+ },
4159
+ });
3029
4160
  if (!res.ok) {
3030
- var errText = await res.text().catch(() => "");
3031
- var msg = `后端服务未就绪 (${res.status}): ${errText || res.statusText}`;
4161
+ var errText = (typeof json === "string" ? json : JSON.stringify(json)) || res.statusText;
4162
+ var msg = `后端服务未就绪 (${res.status}): ${errText}`;
3032
4163
  setSyncStatus("failed");
3033
4164
  setError(msg);
3034
4165
  if (forceRefresh) {
@@ -3036,7 +4167,6 @@ function GlobalSyncSettingsModal(props) {
3036
4167
  }
3037
4168
  return;
3038
4169
  }
3039
- var json = await res.json();
3040
4170
  if (json.ok && json.data) {
3041
4171
  var isFail = Boolean(json.partialFailure ||
3042
4172
  json.data.partialFailure ||
@@ -3150,6 +4280,34 @@ function GlobalSyncSettingsModal(props) {
3150
4280
  clearTimeout(cloudTimerRef.current);
3151
4281
  };
3152
4282
  }, [cwd, sessionId, syncSettings.strategy, syncSettings.cloudSyncIntervalMs, loadData]);
4283
+ // 纯本地 Git 状态局部刷新:只更新未提交改动(Dirty)、分支与提交记录,彻底切断连带远端网络风暴
4284
+ var refreshLocalGit = React.useCallback(async () => {
4285
+ if (!cwd)
4286
+ return;
4287
+ try {
4288
+ var res = await fetch("/api/github/local-git-status?cwd=" + encodeURIComponent(String(cwd)));
4289
+ if (!res.ok)
4290
+ return;
4291
+ var json = await res.json().catch(() => null);
4292
+ if (json && json.ok && json.data) {
4293
+ setData((prev) => {
4294
+ if (!prev)
4295
+ return prev;
4296
+ var next = {
4297
+ ...prev,
4298
+ gitStatus: json.data.gitStatus !== undefined ? json.data.gitStatus : prev.gitStatus,
4299
+ commitLog: json.data.commitLog !== undefined ? json.data.commitLog : prev.commitLog,
4300
+ branches: json.data.branches !== undefined ? json.data.branches : prev.branches,
4301
+ };
4302
+ saveStoredRightbarData(cwd, next);
4303
+ return next;
4304
+ });
4305
+ }
4306
+ }
4307
+ catch {
4308
+ // 纯本地静默更新,网络异常或组件卸载不打扰用户
4309
+ }
4310
+ }, [cwd]);
3153
4311
  React.useEffect(() => {
3154
4312
  if (!syncSettings.localGitWatch || !cwd || !data?.repo)
3155
4313
  return;
@@ -3169,7 +4327,8 @@ function GlobalSyncSettingsModal(props) {
3169
4327
  if (aborted)
3170
4328
  break;
3171
4329
  if (json && json.changed) {
3172
- loadData(false);
4330
+ // 仅局部刷新本地未提交文件、分支与提交记录,零网络流量,彻底切断连带远端网络风暴!
4331
+ refreshLocalGit();
3173
4332
  }
3174
4333
  }
3175
4334
  catch (err) {
@@ -3184,7 +4343,7 @@ function GlobalSyncSettingsModal(props) {
3184
4343
  aborted = true;
3185
4344
  watchController.abort();
3186
4345
  };
3187
- }, [cwd, data?.repo, syncSettings.localGitWatch, loadData]);
4346
+ }, [cwd, data?.repo, syncSettings.localGitWatch, refreshLocalGit]);
3188
4347
  React.useEffect(() => {
3189
4348
  var container = containerRef && containerRef.current;
3190
4349
  if (!container)
@@ -3206,22 +4365,49 @@ function GlobalSyncSettingsModal(props) {
3206
4365
  .catch(function () { });
3207
4366
  }
3208
4367
  else if (syncSettings.localGitWatch && data?.repo) {
3209
- loadData(false);
4368
+ // 聚焦时若开启了本地 Git 监听,仅局部刷新本地状态,杜绝多余远端请求
4369
+ refreshLocalGit();
3210
4370
  }
3211
4371
  };
4372
+ var onOnline = () => {
4373
+ // 监听网络由断网恢复连接瞬间,自动静默发起一次全量同步,消除离线警告横幅并拉新
4374
+ loadData(false);
4375
+ };
3212
4376
  window.addEventListener("focus", onFocus);
3213
- return () => window.removeEventListener("focus", onFocus);
3214
- }, [data, loadData, syncSettings.localGitWatch]);
4377
+ window.addEventListener("online", onOnline);
4378
+ return () => {
4379
+ window.removeEventListener("focus", onFocus);
4380
+ window.removeEventListener("online", onOnline);
4381
+ };
4382
+ }, [data, loadData, refreshLocalGit, syncSettings.localGitWatch]);
3215
4383
  var [aboutModalOpen, setAboutModalOpen] = React.useState(false);
3216
4384
  var [rightbarModalOpen, setRightbarModalOpen] = React.useState(false);
3217
4385
  var [rightbarModalSection, setRightbarModalSection] = React.useState("sync");
4386
+ var [helpModalOpen, setHelpModalOpen] = React.useState(false);
3218
4387
  var [selectedCommit, setSelectedCommit] = React.useState(null);
3219
4388
  var [extraCommits, setExtraCommits] = React.useState([]);
3220
4389
  var [loadingMore, setLoadingMore] = React.useState(false);
3221
4390
  var [hasMoreCommits, setHasMoreCommits] = React.useState(true);
4391
+ var [visibleDirtyCount, setVisibleDirtyCount] = React.useState(15);
4392
+ var [extraDirtyFiles, setExtraDirtyFiles] = React.useState([]);
4393
+ var [loadingMoreDirty, setLoadingMoreDirty] = React.useState(false);
4394
+ var [hasMoreDirty, setHasMoreDirty] = React.useState(true);
4395
+ var [activeDiffFile, setActiveDiffFile] = React.useState(null);
4396
+ var [headerCardsVisible, setHeaderCardsVisible] = React.useState(() => getStoredHeaderCardsVisible());
4397
+ var toggleHeaderCards = React.useCallback(() => {
4398
+ setHeaderCardsVisible(function (prev) {
4399
+ var next = !prev;
4400
+ saveStoredHeaderCardsVisible(next);
4401
+ return next;
4402
+ });
4403
+ }, []);
3222
4404
  React.useEffect(function () {
3223
4405
  setExtraCommits([]);
3224
4406
  setHasMoreCommits(true);
4407
+ setExtraDirtyFiles([]);
4408
+ setVisibleDirtyCount(15);
4409
+ setHasMoreDirty(true);
4410
+ setActiveDiffFile(null);
3225
4411
  }, [cwd, data]);
3226
4412
  var loadMoreCommits = function () {
3227
4413
  if (loadingMore || !hasMoreCommits)
@@ -3258,22 +4444,136 @@ function GlobalSyncSettingsModal(props) {
3258
4444
  setLoadingMore(false);
3259
4445
  });
3260
4446
  };
4447
+ var loadMoreDirtyFiles = function () {
4448
+ if (loadingMoreDirty)
4449
+ return;
4450
+ var total = data?.gitStatus?.dirtyCount || 0;
4451
+ var baseFiles = data?.gitStatus?.dirtyFiles || [];
4452
+ var loadedCount = baseFiles.length + extraDirtyFiles.length;
4453
+ // 若当前已持有的文件列表中还有未展示的内容,按 15 条步长递增展示窗口(0ms 纯前端瞬时响应)
4454
+ if (visibleDirtyCount < loadedCount) {
4455
+ setVisibleDirtyCount(function (prev) {
4456
+ return Math.min(prev + 15, loadedCount);
4457
+ });
4458
+ return;
4459
+ }
4460
+ // 若当前已展示数已经达到总数,说明全量全部展示完毕,无需发起请求
4461
+ if (visibleDirtyCount >= total && total > 0) {
4462
+ setHasMoreDirty(false);
4463
+ return;
4464
+ }
4465
+ // 仅在当前已知条数小于总数时向后端增量拉取后续条目
4466
+ setLoadingMoreDirty(true);
4467
+ var skip = loadedCount;
4468
+ var limit = 15;
4469
+ var url = "/api/github/uncommitted-files?limit=" + limit + "&skip=" + skip + (cwd ? "&cwd=" + encodeURIComponent(cwd) : "");
4470
+ var fetchWithFallback = async function () {
4471
+ try {
4472
+ var r = await fetch(url);
4473
+ if (r.status === 404) {
4474
+ // 自动无缝降级走常驻端点 /api/github/action
4475
+ r = await fetch("/api/github/action", {
4476
+ method: "POST",
4477
+ headers: { "Content-Type": "application/json" },
4478
+ body: JSON.stringify({
4479
+ action: "get_uncommitted_files",
4480
+ limit: limit,
4481
+ skip: skip,
4482
+ cwd: cwd || undefined,
4483
+ }),
4484
+ });
4485
+ }
4486
+ if (!r.ok) {
4487
+ setLoadingMoreDirty(false);
4488
+ return;
4489
+ }
4490
+ var j = await r.json().catch(function () { return null; });
4491
+ setLoadingMoreDirty(false);
4492
+ if (j && j.ok && j.data && Array.isArray(j.data.items)) {
4493
+ if (j.data.items.length === 0) {
4494
+ setHasMoreDirty(false);
4495
+ }
4496
+ else {
4497
+ setExtraDirtyFiles(function (prev) {
4498
+ return prev.concat(j.data.items);
4499
+ });
4500
+ setVisibleDirtyCount(function (prev) {
4501
+ return prev + j.data.items.length;
4502
+ });
4503
+ if (!j.data.hasMore || (loadedCount + j.data.items.length) >= total) {
4504
+ setHasMoreDirty(false);
4505
+ }
4506
+ }
4507
+ }
4508
+ else {
4509
+ setHasMoreDirty(false);
4510
+ }
4511
+ }
4512
+ catch (e) {
4513
+ setLoadingMoreDirty(false);
4514
+ }
4515
+ };
4516
+ fetchWithFallback();
4517
+ };
3261
4518
  React.useEffect(function () {
3262
4519
  var el = containerRef && containerRef.current;
3263
4520
  if (!el)
3264
4521
  return;
3265
4522
  var onScroll = function () {
3266
- var activeTab = el.getAttribute("data-active-tab");
3267
- if (activeTab && activeTab !== "git")
3268
- return;
3269
- var threshold = 80;
4523
+ var activeBtn = el.querySelector(".dsh-github-tab-btn.active");
4524
+ var activeTab = activeBtn ? activeBtn.getAttribute("data-tab-btn") : (el.getAttribute("data-active-tab") || "git");
4525
+ var threshold = 120;
3270
4526
  if (el.scrollHeight - el.scrollTop - el.clientHeight < threshold) {
3271
- loadMoreCommits();
4527
+ if (activeTab === "git") {
4528
+ loadMoreCommits();
4529
+ }
4530
+ else if (activeTab === "dirty") {
4531
+ loadMoreDirtyFiles();
4532
+ }
4533
+ }
4534
+ };
4535
+ // 监听鼠标滚轮向下滚事件(即使用户收起卡片导致内容暂未撑出滚动条,只要向下滚轮也能瞬间响应!)
4536
+ var onWheel = function (e) {
4537
+ if (!e || e.deltaY <= 0)
4538
+ return;
4539
+ var activeBtn = el.querySelector(".dsh-github-tab-btn.active");
4540
+ var activeTab = activeBtn ? activeBtn.getAttribute("data-tab-btn") : (el.getAttribute("data-active-tab") || "git");
4541
+ var threshold = 120;
4542
+ var atBottom = (el.scrollHeight - el.scrollTop - el.clientHeight < threshold) || (el.scrollHeight <= el.clientHeight + 40);
4543
+ if (atBottom) {
4544
+ if (activeTab === "git") {
4545
+ loadMoreCommits();
4546
+ }
4547
+ else if (activeTab === "dirty") {
4548
+ loadMoreDirtyFiles();
4549
+ }
3272
4550
  }
3273
4551
  };
3274
4552
  el.addEventListener("scroll", onScroll, { passive: true });
3275
- return function () { el.removeEventListener("scroll", onScroll); };
3276
- }, [data, extraCommits, loadingMore, hasMoreCommits, cwd]);
4553
+ el.addEventListener("wheel", onWheel, { passive: true });
4554
+ return function () {
4555
+ el.removeEventListener("scroll", onScroll);
4556
+ el.removeEventListener("wheel", onWheel);
4557
+ };
4558
+ }, [data, extraCommits, loadingMore, hasMoreCommits, cwd, extraDirtyFiles, loadingMoreDirty, hasMoreDirty, visibleDirtyCount, headerCardsVisible]);
4559
+ // 视口充盈智能自适应:当用户点击收起卡片导致页面变矮、未能产生滚动条时,自动将后续条目拉取补齐!
4560
+ React.useEffect(function () {
4561
+ var el = containerRef && containerRef.current;
4562
+ if (!el)
4563
+ return;
4564
+ var timer = setTimeout(function () {
4565
+ var activeBtn = el.querySelector(".dsh-github-tab-btn.active");
4566
+ var activeTab = activeBtn ? activeBtn.getAttribute("data-tab-btn") : (el.getAttribute("data-active-tab") || "git");
4567
+ if (activeTab === "dirty") {
4568
+ var total = data?.gitStatus?.dirtyCount || 0;
4569
+ var hasMore = total > 0 && visibleDirtyCount < total;
4570
+ if (hasMore && el.scrollHeight <= el.clientHeight + 60) {
4571
+ loadMoreDirtyFiles();
4572
+ }
4573
+ }
4574
+ }, 120);
4575
+ return function () { clearTimeout(timer); };
4576
+ }, [headerCardsVisible, visibleDirtyCount, data]);
3277
4577
  React.useEffect(() => {
3278
4578
  var onOpenAbout = () => {
3279
4579
  var el = containerRef && containerRef.current;
@@ -3302,8 +4602,13 @@ function GlobalSyncSettingsModal(props) {
3302
4602
  window.removeEventListener("dsh-github-open-proxy", onOpenProxy);
3303
4603
  };
3304
4604
  }, []);
3305
- var openRightbarSettings = () => {
3306
- setRightbarModalSection("sync");
4605
+ var openRightbarSettings = (section) => {
4606
+ if (section) {
4607
+ setRightbarModalSection(section);
4608
+ }
4609
+ else {
4610
+ setRightbarModalSection("sync");
4611
+ }
3307
4612
  setRightbarModalOpen(true);
3308
4613
  };
3309
4614
  var h = React.createElement;
@@ -3324,12 +4629,34 @@ function GlobalSyncSettingsModal(props) {
3324
4629
  },
3325
4630
  }, h("span", null, toastMsg), h("button", { onClick: () => setToastMsg(null), style: { background: "none", border: "none", cursor: "pointer", color: "inherit", fontSize: "14px" } }, "✕"))
3326
4631
  : null;
4632
+ var openFileInDsh = function (f) {
4633
+ if (!f || !f.path)
4634
+ return;
4635
+ try {
4636
+ var rel = f.path.replace(/\\/g, "/");
4637
+ var address = "dsh-resource://file/session/" + encodeURIComponent(sessionId || "") + "/" + encodeURIComponent(rel);
4638
+ var activeCtx = props?.ctx || currentPluginCtx;
4639
+ if (activeCtx && activeCtx.sidebarRight && typeof activeCtx.sidebarRight.openResource === "function") {
4640
+ activeCtx.sidebarRight.openResource(address);
4641
+ }
4642
+ else {
4643
+ window.dispatchEvent(new CustomEvent("dsh-open-file", { detail: { path: f.path, cwd: cwd, sessionId: sessionId } }));
4644
+ }
4645
+ }
4646
+ catch (e) { }
4647
+ };
3327
4648
  return h("div", { className: "dsh-github-rightbar-container", ref: containerRef }, toastEl, ...renderGithubRepoView(h, data, loading, error, () => loadData(true), true, updateInfo, syncStatus, lastSyncedAt, openRightbarSettings, cwd, (c) => setSelectedCommit(c), {
3328
4649
  extraCommits: extraCommits,
3329
4650
  loadingMore: loadingMore,
3330
4651
  hasMore: hasMoreCommits,
3331
4652
  onLoadMore: loadMoreCommits,
3332
- }, showToast), h(GlobalSyncSettingsModal, {
4653
+ }, showToast, () => setHelpModalOpen(true), openFileInDsh, {
4654
+ visibleCount: visibleDirtyCount,
4655
+ extraDirtyFiles: extraDirtyFiles,
4656
+ loadingMore: loadingMoreDirty,
4657
+ hasMore: hasMoreDirty,
4658
+ onLoadMore: loadMoreDirtyFiles,
4659
+ }, activeDiffFile, (f) => setActiveDiffFile(f), () => setActiveDiffFile(null), openFileInDsh, headerCardsVisible, toggleHeaderCards), h(GlobalSyncSettingsModal, {
3333
4660
  isOpen: aboutModalOpen,
3334
4661
  initialSection: "about",
3335
4662
  onClose: () => setAboutModalOpen(false),
@@ -3347,6 +4674,9 @@ function GlobalSyncSettingsModal(props) {
3347
4674
  setLastSyncedAt(0);
3348
4675
  setSyncStatus("loading");
3349
4676
  },
4677
+ }), h(GithubHelpModal, {
4678
+ isOpen: helpModalOpen,
4679
+ onClose: () => setHelpModalOpen(false),
3350
4680
  }), selectedCommit
3351
4681
  ? h(CommitDetailModal, {
3352
4682
  commit: selectedCommit,
@@ -3374,7 +4704,7 @@ function GlobalSyncSettingsModal(props) {
3374
4704
  className: "dsh-github-icon-inner" + (active ? " active" : "")
3375
4705
  }, renderOctocat(18, "currentColor")));
3376
4706
  }
3377
- var inject = ["slots", "sidebarRightTabs", "sessions", "layout", "theme"];
4707
+ var inject = ["slots", "sidebarRightTabs", "sessions", "layout", "theme", "sidebarRight"];
3378
4708
 
3379
4709
  // ---- [Bundled: workspace-panel.js] ----
3380
4710
  /**
@@ -3390,6 +4720,7 @@ function GithubWorkspacePanel() {
3390
4720
  var [lastSyncedAt, setLastSyncedAt] = React.useState(initialSnapshot?.savedAt || 0);
3391
4721
  var [isSettingsModalOpen, setIsSettingsModalOpen] = React.useState(false);
3392
4722
  var [settingsInitialSection, setSettingsInitialSection] = React.useState("sync");
4723
+ var [isHelpModalOpen, setIsHelpModalOpen] = React.useState(false);
3393
4724
  var [error, setError] = React.useState(null);
3394
4725
  var [updateInfo, setUpdateInfo] = React.useState(null);
3395
4726
  var [activeTab, setActiveTab] = React.useState("workspaces");
@@ -3483,9 +4814,18 @@ function GithubWorkspacePanel() {
3483
4814
  if (forceRefresh) {
3484
4815
  url += "?force=true";
3485
4816
  }
3486
- var res = await fetch(url, { signal: controller.signal });
4817
+ // 接入 1s/2s/4s 阶梯退避重试,平滑吸收代理节点与网络的偶发抖动断流
4818
+ var { res, json } = await fetchWithRetry(url, {}, {
4819
+ signal: controller.signal,
4820
+ maxRetries: 3,
4821
+ baseDelayMs: 1000,
4822
+ onRetry: (attempt) => {
4823
+ setSyncStatus("syncing");
4824
+ setError(`网络抖动,正在自愈重试 (${attempt}/3)...`);
4825
+ },
4826
+ });
3487
4827
  if (!res.ok) {
3488
- var errText = await res.text().catch(() => "");
4828
+ var errText = (typeof json === "string" ? json : JSON.stringify(json)) || res.statusText;
3489
4829
  var failMsg = `获取全局数据失败(HTTP ${res.status})${errText ? ": " + errText : ""}`;
3490
4830
  setSyncStatus("failed");
3491
4831
  setError(failMsg);
@@ -3493,7 +4833,6 @@ function GithubWorkspacePanel() {
3493
4833
  showToast("❌ 刷新失败:" + failMsg);
3494
4834
  return;
3495
4835
  }
3496
- var json = await res.json();
3497
4836
  if (json.ok && json.data) {
3498
4837
  var isFail = Boolean(json.partialFailure ||
3499
4838
  json.data.partialFailure ||
@@ -3620,8 +4959,16 @@ function GithubWorkspacePanel() {
3620
4959
  .catch(function () { });
3621
4960
  }
3622
4961
  };
4962
+ var onOnline = () => {
4963
+ // 监听网络由断网恢复连接瞬间,自动静默发起一次全量同步,消除离线警告横幅并拉新
4964
+ loadData(false);
4965
+ };
3623
4966
  window.addEventListener("focus", onFocus);
3624
- return () => window.removeEventListener("focus", onFocus);
4967
+ window.addEventListener("online", onOnline);
4968
+ return () => {
4969
+ window.removeEventListener("focus", onFocus);
4970
+ window.removeEventListener("online", onOnline);
4971
+ };
3625
4972
  }, [data, loadData]);
3626
4973
  var h = React.createElement;
3627
4974
  var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(32), h("h2", null, "GitHub 全景驾驶舱"), renderPluginBadge(h), h(SyncStatusBadge, { syncStatus, lastSyncedAt }), proxyActive && proxyActive.enabled
@@ -3646,6 +4993,11 @@ function GithubWorkspacePanel() {
3646
4993
  onClick: () => loadData(true),
3647
4994
  disabled: loading,
3648
4995
  }, loading ? "同步中..." : "🔄 刷新全局数据"), h("button", {
4996
+ className: "dsh-github-btn-sm",
4997
+ onClick: () => setIsHelpModalOpen(true),
4998
+ title: "打开使用帮助与实战手册大窗口(Esc 可退出)",
4999
+ style: { padding: "6px 12px" },
5000
+ }, "📖 使用指南"), h("button", {
3649
5001
  className: "dsh-github-btn-sm",
3650
5002
  onClick: openSettingsDefault,
3651
5003
  title: "配置数据同步节奏、本地缓存、网络代理与插件信息",
@@ -3839,11 +5191,15 @@ function GithubWorkspacePanel() {
3839
5191
  },
3840
5192
  })
3841
5193
  : null;
3842
- return h("div", { className: "dsh-github-panel", ref: panelRef }, toastEl, headerEl, networkAlertBannerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl);
5194
+ return h("div", { className: "dsh-github-panel", ref: panelRef }, toastEl, headerEl, networkAlertBannerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl, h(GithubHelpModal, {
5195
+ isOpen: isHelpModalOpen,
5196
+ onClose: () => setIsHelpModalOpen(false),
5197
+ }));
3843
5198
  }
3844
5199
 
3845
5200
 
3846
5201
  function apply(ctx) {
5202
+ currentPluginCtx = ctx;
3847
5203
  if (typeof window !== "undefined" && window.document) {
3848
5204
  var sweep = () => {
3849
5205
  document.querySelectorAll(".dsh-github-right-card, .dsh-github-mounted-card, #dsh-github-floating-drawer").forEach((el) => el.remove());