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.
- package/README.en.md +75 -8
- package/README.md +75 -8
- package/docs/UPGRADE-NOTES.md +9 -12
- package/docs/architecture.md +39 -21
- package/lib/api/routes-actions.js +132 -0
- package/lib/client.d.ts +4 -3
- package/lib/client.js +1436 -80
- package/lib/commands/gh.d.ts +6 -1
- package/lib/commands/gh.js +144 -71
- package/lib/executor/git-parser.d.ts +33 -1
- package/lib/executor/git-parser.js +116 -4
- package/lib/executor.d.ts +27 -2
- package/lib/executor.js +189 -5
- package/lib/main.js +28 -4
- package/lib/renderers/agent-task.d.ts +16 -0
- package/lib/renderers/agent-task.js +107 -0
- package/lib/renderers/codespace.d.ts +16 -0
- package/lib/renderers/codespace.js +103 -0
- package/lib/renderers/discussion.d.ts +16 -0
- package/lib/renderers/discussion.js +101 -0
- package/lib/renderers/markdown.d.ts +4 -0
- package/lib/renderers/markdown.js +4 -0
- package/lib/renderers/project.d.ts +20 -0
- package/lib/renderers/project.js +128 -0
- package/lib/renderers/skill.d.ts +30 -0
- package/lib/renderers/skill.js +180 -0
- package/lib/renderers/status.d.ts +5 -0
- package/lib/renderers/status.js +52 -0
- package/lib/telemetry.d.ts +70 -0
- package/lib/telemetry.js +161 -0
- package/lib/tools/agent-task.d.ts +54 -0
- package/lib/tools/agent-task.js +135 -0
- package/lib/tools/codespace.d.ts +51 -0
- package/lib/tools/codespace.js +154 -0
- package/lib/tools/discussion.d.ts +70 -0
- package/lib/tools/discussion.js +195 -0
- package/lib/tools/help.js +109 -9
- package/lib/tools/project.d.ts +80 -0
- package/lib/tools/project.js +241 -0
- package/lib/tools/repo.js +13 -1
- package/lib/tools/skill.d.ts +24 -1
- package/lib/tools/skill.js +115 -5
- package/lib/tools/status.d.ts +3 -2
- package/lib/tools/status.js +9 -4
- package/lib/types.d.ts +5 -0
- package/lib/ui/commit-modal.js +144 -14
- package/lib/ui/help-modal.d.ts +15 -0
- package/lib/ui/help-modal.js +202 -0
- package/lib/ui/storage.d.ts +9 -0
- package/lib/ui/storage.js +24 -0
- package/lib/ui/styles.d.ts +1 -1
- package/lib/ui/styles.js +259 -0
- package/lib/ui/sync-settings-modal.js +1 -1
- package/lib/ui/upgrade-card.js +39 -14
- package/lib/ui/utils.d.ts +35 -0
- package/lib/ui/utils.js +160 -0
- package/lib/ui/workspace-panel.js +30 -5
- package/lib/upgrade-manager.d.ts +5 -1
- package/lib/upgrade-manager.js +24 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- 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
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
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
|
-
:
|
|
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
|
-
:
|
|
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
|
-
:
|
|
1987
|
-
|
|
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
|
-
:
|
|
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"
|
|
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: "
|
|
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",
|
|
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
|
-
|
|
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
|
-
},
|
|
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 工作区与云端协作,提供
|
|
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
|
-
|
|
2616
|
-
|
|
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"
|
|
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
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
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 [
|
|
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
|
-
|
|
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 =
|
|
3031
|
-
var msg = `后端服务未就绪 (${res.status}): ${errText
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
4368
|
+
// 聚焦时若开启了本地 Git 监听,仅局部刷新本地状态,杜绝多余远端请求
|
|
4369
|
+
refreshLocalGit();
|
|
3210
4370
|
}
|
|
3211
4371
|
};
|
|
4372
|
+
var onOnline = () => {
|
|
4373
|
+
// 监听网络由断网恢复连接瞬间,自动静默发起一次全量同步,消除离线警告横幅并拉新
|
|
4374
|
+
loadData(false);
|
|
4375
|
+
};
|
|
3212
4376
|
window.addEventListener("focus", onFocus);
|
|
3213
|
-
|
|
3214
|
-
|
|
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
|
|
3267
|
-
|
|
3268
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3276
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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());
|