@xia-sc/dsh-git 0.5.2 → 0.6.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/LICENSE +21 -0
- package/README.en.md +51 -8
- package/README.md +34 -8
- package/lib/client.js +748 -255
- package/lib/index.js +27 -7
- package/package.json +3 -2
package/lib/client.js
CHANGED
|
@@ -38,6 +38,7 @@ window.__ModuleLoader__.load({
|
|
|
38
38
|
"panel.title": "Git",
|
|
39
39
|
"panel.collapse": "收起",
|
|
40
40
|
"refresh.aria": "刷新 Git 状态",
|
|
41
|
+
"refresh.busy": "刷新中…",
|
|
41
42
|
"branch.aria": "当前 Git 分支",
|
|
42
43
|
"switch.aria": "切换分支",
|
|
43
44
|
"switch.dirtyWarn": "有 {count} 个未提交文件(如 {paths}),切换分支可能被 git 拒绝,请先提交或暂存",
|
|
@@ -72,12 +73,17 @@ window.__ModuleLoader__.load({
|
|
|
72
73
|
"generate.noProvider": "尚未配置模型,请先在 设置 > 模型 中添加",
|
|
73
74
|
"generate.noModel": "该提供方没有可用模型",
|
|
74
75
|
"generate.empty": "模型没有返回提交信息",
|
|
76
|
+
"generate.truncated": "模型还没写出提交信息就用完了 token 上限(思考模型会先花掉这份预算),再点一次通常即可",
|
|
75
77
|
"generate.cancelled": "生成已取消",
|
|
76
78
|
"generate.failed": "生成失败",
|
|
77
79
|
"commit.placeholder": "提交信息(可多行,Ctrl+Enter 提交)…",
|
|
78
80
|
"commit.empty": "请输入提交信息",
|
|
79
|
-
"changes.title": "
|
|
81
|
+
"changes.title": "变更",
|
|
80
82
|
"changes.none": "工作区干净",
|
|
83
|
+
// Section footer: the list previews its first N rows and this says how
|
|
84
|
+
// many more there are, instead of a bare "▾ 13".
|
|
85
|
+
"section.more": "显示全部 {count} 项",
|
|
86
|
+
"section.less": "只看前几项",
|
|
81
87
|
"diff.title": "差异",
|
|
82
88
|
"diff.open": "查看差异",
|
|
83
89
|
"diff.close": "关闭差异",
|
|
@@ -109,6 +115,17 @@ window.__ModuleLoader__.load({
|
|
|
109
115
|
"busy.checkout": "切换中…",
|
|
110
116
|
"busy.createBranch": "创建分支中…",
|
|
111
117
|
"output.ok": "操作成功",
|
|
118
|
+
"output.failed": "操作失败",
|
|
119
|
+
"output.detail": "详情",
|
|
120
|
+
"output.detailHide": "收起",
|
|
121
|
+
"output.fetch": "已拉取更新",
|
|
122
|
+
"output.pull": "已拉取合并",
|
|
123
|
+
"output.push": "已推送",
|
|
124
|
+
"output.stage": "已暂存全部",
|
|
125
|
+
"output.commit": "已提交",
|
|
126
|
+
"output.checkout": "已切换到 {branch}",
|
|
127
|
+
"output.createBranch": "已创建并切换到 {branch}",
|
|
128
|
+
"output.generate": "已生成提交信息",
|
|
112
129
|
"dock.aria": "打开 Git 面板",
|
|
113
130
|
"status.modified": "修改",
|
|
114
131
|
"status.added": "新增",
|
|
@@ -125,6 +142,7 @@ window.__ModuleLoader__.load({
|
|
|
125
142
|
"panel.title": "Git",
|
|
126
143
|
"panel.collapse": "Collapse",
|
|
127
144
|
"refresh.aria": "Refresh Git status",
|
|
145
|
+
"refresh.busy": "Refreshing…",
|
|
128
146
|
"branch.aria": "Current git branch",
|
|
129
147
|
"switch.aria": "Switch branch",
|
|
130
148
|
"switch.dirtyWarn": "{count} uncommitted file(s) (e.g. {paths}) may block the switch — commit or stash first",
|
|
@@ -159,12 +177,15 @@ window.__ModuleLoader__.load({
|
|
|
159
177
|
"generate.noProvider": "No model is configured — add one in Settings > Models",
|
|
160
178
|
"generate.noModel": "That provider advertises no model",
|
|
161
179
|
"generate.empty": "The model returned no commit message",
|
|
180
|
+
"generate.truncated": "The model hit its token cap before writing anything (a reasoning model spends that budget thinking first) — trying again usually works",
|
|
162
181
|
"generate.cancelled": "Draft cancelled",
|
|
163
182
|
"generate.failed": "Draft failed",
|
|
164
183
|
"commit.placeholder": "Commit message (multi-line allowed, Ctrl+Enter commits)…",
|
|
165
184
|
"commit.empty": "Enter a commit message",
|
|
166
|
-
"changes.title": "Changes
|
|
185
|
+
"changes.title": "Changes",
|
|
167
186
|
"changes.none": "Working tree clean",
|
|
187
|
+
"section.more": "Show all {count}",
|
|
188
|
+
"section.less": "Show fewer",
|
|
168
189
|
"diff.title": "Diff",
|
|
169
190
|
"diff.open": "View diff",
|
|
170
191
|
"diff.close": "Close diff",
|
|
@@ -196,6 +217,17 @@ window.__ModuleLoader__.load({
|
|
|
196
217
|
"busy.checkout": "Switching…",
|
|
197
218
|
"busy.createBranch": "Creating branch…",
|
|
198
219
|
"output.ok": "Operation succeeded",
|
|
220
|
+
"output.failed": "Operation failed",
|
|
221
|
+
"output.detail": "Details",
|
|
222
|
+
"output.detailHide": "Hide",
|
|
223
|
+
"output.fetch": "Fetched",
|
|
224
|
+
"output.pull": "Pulled",
|
|
225
|
+
"output.push": "Pushed",
|
|
226
|
+
"output.stage": "Staged everything",
|
|
227
|
+
"output.commit": "Committed",
|
|
228
|
+
"output.checkout": "Switched to {branch}",
|
|
229
|
+
"output.createBranch": "Created and switched to {branch}",
|
|
230
|
+
"output.generate": "Draft ready",
|
|
199
231
|
"dock.aria": "Open Git panel",
|
|
200
232
|
"status.modified": "modified",
|
|
201
233
|
"status.added": "added",
|
|
@@ -245,8 +277,8 @@ window.__ModuleLoader__.load({
|
|
|
245
277
|
display: "flex",
|
|
246
278
|
alignItems: "center",
|
|
247
279
|
gap: 8,
|
|
248
|
-
padding: "8px
|
|
249
|
-
borderBottom: "1px solid var(--dsw-alias-border-
|
|
280
|
+
padding: "8px 10px",
|
|
281
|
+
borderBottom: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.22))",
|
|
250
282
|
flex: "none",
|
|
251
283
|
cursor: "move",
|
|
252
284
|
userSelect: "none",
|
|
@@ -260,11 +292,39 @@ window.__ModuleLoader__.load({
|
|
|
260
292
|
fontSize: 13,
|
|
261
293
|
fontWeight: 600,
|
|
262
294
|
color: "var(--dsw-alias-label-primary, #e5e7eb)",
|
|
295
|
+
flex: "none",
|
|
296
|
+
whiteSpace: "nowrap"
|
|
297
|
+
},
|
|
298
|
+
// The workspace path takes the header's slack: it is the one field whose
|
|
299
|
+
// truncation must never move the buttons beside it.
|
|
300
|
+
cwdPath: {
|
|
263
301
|
flex: "1",
|
|
264
302
|
minWidth: 0,
|
|
265
303
|
overflow: "hidden",
|
|
266
304
|
textOverflow: "ellipsis",
|
|
267
|
-
whiteSpace: "nowrap"
|
|
305
|
+
whiteSpace: "nowrap",
|
|
306
|
+
fontSize: 11,
|
|
307
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)",
|
|
308
|
+
textAlign: "right"
|
|
309
|
+
},
|
|
310
|
+
// Square ghost icon button: the panel's own chrome (refresh / collapse).
|
|
311
|
+
iconButton: {
|
|
312
|
+
flex: "none",
|
|
313
|
+
display: "inline-flex",
|
|
314
|
+
alignItems: "center",
|
|
315
|
+
justifyContent: "center",
|
|
316
|
+
width: 24,
|
|
317
|
+
height: 24,
|
|
318
|
+
padding: 0,
|
|
319
|
+
border: "1px solid transparent",
|
|
320
|
+
borderRadius: 6,
|
|
321
|
+
background: "transparent",
|
|
322
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)",
|
|
323
|
+
font: "inherit",
|
|
324
|
+
fontSize: 13,
|
|
325
|
+
lineHeight: "20px",
|
|
326
|
+
boxSizing: "border-box",
|
|
327
|
+
cursor: "pointer"
|
|
268
328
|
},
|
|
269
329
|
body: {
|
|
270
330
|
flex: "1",
|
|
@@ -275,9 +335,36 @@ window.__ModuleLoader__.load({
|
|
|
275
335
|
row: {
|
|
276
336
|
display: "flex",
|
|
277
337
|
alignItems: "center",
|
|
278
|
-
gap:
|
|
338
|
+
gap: 6,
|
|
279
339
|
margin: "6px 0"
|
|
280
340
|
},
|
|
341
|
+
// One action row inside a card: the gap is owned by the card, not the row.
|
|
342
|
+
rowTight: {
|
|
343
|
+
display: "flex",
|
|
344
|
+
alignItems: "center",
|
|
345
|
+
gap: 6,
|
|
346
|
+
minWidth: 0
|
|
347
|
+
},
|
|
348
|
+
// Equal-width action buttons: a row of auto-width labels was the messy
|
|
349
|
+
// part of the old layout — every group now shares the row evenly.
|
|
350
|
+
grow: {
|
|
351
|
+
flex: "1 1 0",
|
|
352
|
+
minWidth: 0,
|
|
353
|
+
padding: "0 6px",
|
|
354
|
+
textAlign: "center"
|
|
355
|
+
},
|
|
356
|
+
// Card grouping the whole commit flow (stage → draft → message → commit).
|
|
357
|
+
card: {
|
|
358
|
+
margin: "6px 0",
|
|
359
|
+
padding: 8,
|
|
360
|
+
display: "flex",
|
|
361
|
+
flexDirection: "column",
|
|
362
|
+
gap: 6,
|
|
363
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.22))",
|
|
364
|
+
borderRadius: 8,
|
|
365
|
+
background: "var(--dsw-alias-bg-layer-1, rgba(127,127,127,0.06))",
|
|
366
|
+
minWidth: 0
|
|
367
|
+
},
|
|
281
368
|
warn: {
|
|
282
369
|
display: "flex",
|
|
283
370
|
alignItems: "center",
|
|
@@ -285,18 +372,22 @@ window.__ModuleLoader__.load({
|
|
|
285
372
|
margin: "6px 0",
|
|
286
373
|
padding: "6px 8px",
|
|
287
374
|
borderRadius: 8,
|
|
288
|
-
background: "var(--dsw-alias-state-
|
|
289
|
-
border: "1px solid var(--dsw-alias-state-
|
|
290
|
-
color: "var(--dsw-alias-state-
|
|
375
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-warn-primary, #f0a020) 14%, transparent)",
|
|
376
|
+
border: "1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary, #f0a020) 40%, transparent)",
|
|
377
|
+
color: "var(--dsw-alias-state-warn-primary, #f0a020)",
|
|
291
378
|
fontSize: 12,
|
|
292
379
|
lineHeight: "16px"
|
|
293
380
|
},
|
|
381
|
+
// The branch chip — only for the detached-HEAD notice, so it carries the
|
|
382
|
+
// warning colour rather than the accent.
|
|
294
383
|
chip: {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
borderRadius: 999,
|
|
384
|
+
flex: "none",
|
|
385
|
+
maxWidth: "62%",
|
|
298
386
|
padding: "0 8px",
|
|
299
|
-
|
|
387
|
+
borderRadius: 6,
|
|
388
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-warn-primary, #f0a020) 16%, transparent)",
|
|
389
|
+
color: "var(--dsw-alias-state-warn-primary, #f0a020)",
|
|
390
|
+
fontWeight: 600,
|
|
300
391
|
fontSize: 12,
|
|
301
392
|
lineHeight: "20px",
|
|
302
393
|
overflow: "hidden",
|
|
@@ -304,40 +395,58 @@ window.__ModuleLoader__.load({
|
|
|
304
395
|
whiteSpace: "nowrap"
|
|
305
396
|
},
|
|
306
397
|
dirty: {
|
|
398
|
+
flex: "none",
|
|
307
399
|
color: "var(--dsw-alias-state-warn-primary, #f0a020)",
|
|
308
400
|
fontWeight: 600,
|
|
309
401
|
fontSize: 12
|
|
310
402
|
},
|
|
311
403
|
meta: {
|
|
312
|
-
color: "var(--dsw-alias-label-
|
|
313
|
-
fontSize:
|
|
404
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)",
|
|
405
|
+
fontSize: 11
|
|
314
406
|
},
|
|
315
407
|
button: {
|
|
316
|
-
background: "var(--dsw-alias-
|
|
408
|
+
background: "color-mix(in srgb, var(--dsw-alias-label-primary, #e5e7eb) 8%, transparent)",
|
|
317
409
|
border: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.3))",
|
|
318
|
-
borderRadius:
|
|
319
|
-
color: "var(--dsw-alias-label-
|
|
410
|
+
borderRadius: 7,
|
|
411
|
+
color: "var(--dsw-alias-label-primary, #e5e7eb)",
|
|
320
412
|
font: "inherit",
|
|
321
413
|
fontSize: 12,
|
|
322
|
-
lineHeight: "
|
|
414
|
+
lineHeight: "24px",
|
|
415
|
+
height: 26,
|
|
323
416
|
padding: "0 10px",
|
|
417
|
+
boxSizing: "border-box",
|
|
324
418
|
cursor: "pointer",
|
|
325
|
-
flex: "none"
|
|
419
|
+
flex: "none",
|
|
420
|
+
whiteSpace: "nowrap",
|
|
421
|
+
overflow: "hidden",
|
|
422
|
+
textOverflow: "ellipsis"
|
|
326
423
|
},
|
|
327
424
|
buttonPrimary: {
|
|
328
|
-
|
|
425
|
+
// The harness's own primary button: the label token IS the fill, with
|
|
426
|
+
// `label-primary-foreground` as the text on top. It reads near-black on
|
|
427
|
+
// light and near-white on dark (an inverted contrast button).
|
|
428
|
+
// `--dsw-alias-brand-primary` is that same contrast colour, NOT an
|
|
429
|
+
// accent — pairing it with `#fff` text made 推送 a white block with
|
|
430
|
+
// invisible text in the dark theme, and 提交 an empty grey box.
|
|
431
|
+
background: "var(--dsw-alias-button-primary-fill, var(--dsw-alias-label-primary, #e5e7eb))",
|
|
329
432
|
border: "1px solid transparent",
|
|
330
|
-
borderRadius:
|
|
331
|
-
color: "#
|
|
433
|
+
borderRadius: 7,
|
|
434
|
+
color: "var(--dsw-alias-label-primary-foreground, var(--dsw-alias-bg-base, #1e1e1e))",
|
|
332
435
|
font: "inherit",
|
|
333
436
|
fontSize: 12,
|
|
334
|
-
|
|
437
|
+
fontWeight: 600,
|
|
438
|
+
lineHeight: "24px",
|
|
439
|
+
height: 26,
|
|
335
440
|
padding: "0 10px",
|
|
441
|
+
boxSizing: "border-box",
|
|
336
442
|
cursor: "pointer",
|
|
337
|
-
flex: "none"
|
|
443
|
+
flex: "none",
|
|
444
|
+
whiteSpace: "nowrap",
|
|
445
|
+
overflow: "hidden",
|
|
446
|
+
textOverflow: "ellipsis"
|
|
338
447
|
},
|
|
339
448
|
buttonDisabled: {
|
|
340
|
-
opacity: 0.
|
|
449
|
+
opacity: 0.4,
|
|
341
450
|
cursor: "default"
|
|
342
451
|
},
|
|
343
452
|
select: {
|
|
@@ -345,14 +454,16 @@ window.__ModuleLoader__.load({
|
|
|
345
454
|
// theme (dark list on dark pages) instead of the OS default
|
|
346
455
|
// white; option/optgroup styles above reinforce it.
|
|
347
456
|
colorScheme: "light dark",
|
|
348
|
-
background: "var(--dsw-alias-bg-layer-
|
|
349
|
-
border: "1px solid var(--dsw-alias-
|
|
350
|
-
borderRadius:
|
|
351
|
-
color: "var(--dsw-alias-label-
|
|
457
|
+
background: "var(--dsw-alias-bg-layer-2, rgba(127,127,127,0.1))",
|
|
458
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.3))",
|
|
459
|
+
borderRadius: 7,
|
|
460
|
+
color: "var(--dsw-alias-label-primary, #e5e7eb)",
|
|
352
461
|
font: "inherit",
|
|
353
462
|
fontSize: 12,
|
|
354
|
-
lineHeight: "
|
|
355
|
-
|
|
463
|
+
lineHeight: "24px",
|
|
464
|
+
height: 26,
|
|
465
|
+
padding: "0 6px",
|
|
466
|
+
boxSizing: "border-box",
|
|
356
467
|
maxWidth: 220,
|
|
357
468
|
cursor: "pointer",
|
|
358
469
|
flex: "1",
|
|
@@ -360,14 +471,16 @@ window.__ModuleLoader__.load({
|
|
|
360
471
|
outline: "none"
|
|
361
472
|
},
|
|
362
473
|
input: {
|
|
363
|
-
background: "
|
|
364
|
-
border: "1px solid var(--dsw-alias-
|
|
365
|
-
borderRadius:
|
|
474
|
+
background: "var(--dsw-alias-bg-layer-2, rgba(127,127,127,0.1))",
|
|
475
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.3))",
|
|
476
|
+
borderRadius: 7,
|
|
366
477
|
color: "var(--dsw-alias-label-primary, #e5e7eb)",
|
|
367
478
|
font: "inherit",
|
|
368
479
|
fontSize: 12,
|
|
369
|
-
lineHeight: "
|
|
370
|
-
|
|
480
|
+
lineHeight: "24px",
|
|
481
|
+
height: 26,
|
|
482
|
+
padding: "0 8px",
|
|
483
|
+
boxSizing: "border-box",
|
|
371
484
|
flex: "1",
|
|
372
485
|
minWidth: 0,
|
|
373
486
|
outline: "none"
|
|
@@ -376,47 +489,123 @@ window.__ModuleLoader__.load({
|
|
|
376
489
|
// silently strips every line feed, so a drafted subject+body would lose
|
|
377
490
|
// its body before the request was ever sent.
|
|
378
491
|
textarea: {
|
|
379
|
-
background: "
|
|
380
|
-
border: "1px solid var(--dsw-alias-
|
|
381
|
-
borderRadius:
|
|
492
|
+
background: "var(--dsw-alias-bg-layer-2, rgba(127,127,127,0.1))",
|
|
493
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.3))",
|
|
494
|
+
borderRadius: 7,
|
|
382
495
|
color: "var(--dsw-alias-label-primary, #e5e7eb)",
|
|
383
496
|
font: "inherit",
|
|
384
497
|
fontSize: 12,
|
|
385
|
-
lineHeight: "
|
|
498
|
+
lineHeight: "17px",
|
|
386
499
|
padding: "4px 8px",
|
|
500
|
+
minHeight: 46,
|
|
387
501
|
flex: "1",
|
|
388
502
|
minWidth: 0,
|
|
503
|
+
boxSizing: "border-box",
|
|
389
504
|
outline: "none",
|
|
390
505
|
resize: "vertical",
|
|
391
506
|
overflowY: "auto"
|
|
392
507
|
},
|
|
393
|
-
|
|
508
|
+
// ── list sections (changes / recent commits) ─────────────────────
|
|
509
|
+
// A section is a card: a full-width header toggle (chevron + label +
|
|
510
|
+
// count) and its rows. Collapsing hides the rows outright — the old
|
|
511
|
+
// header doubled as "show more" for a preview, so its chevron pointed
|
|
512
|
+
// the wrong way for a list that was already on screen.
|
|
513
|
+
section: {
|
|
514
|
+
marginTop: 8,
|
|
515
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.22))",
|
|
516
|
+
borderRadius: 8,
|
|
517
|
+
background: "var(--dsw-alias-bg-layer-1, rgba(127,127,127,0.06))",
|
|
518
|
+
overflow: "hidden"
|
|
519
|
+
},
|
|
520
|
+
sectionHeader: {
|
|
521
|
+
display: "flex",
|
|
522
|
+
alignItems: "center",
|
|
523
|
+
gap: 6,
|
|
524
|
+
width: "100%",
|
|
525
|
+
padding: "6px 8px",
|
|
526
|
+
background: "transparent",
|
|
527
|
+
border: "none",
|
|
528
|
+
color: "var(--dsw-alias-label-primary, #e5e7eb)",
|
|
529
|
+
font: "inherit",
|
|
394
530
|
fontSize: 12,
|
|
395
531
|
fontWeight: 600,
|
|
396
|
-
|
|
397
|
-
|
|
532
|
+
lineHeight: "18px",
|
|
533
|
+
boxSizing: "border-box",
|
|
398
534
|
cursor: "pointer",
|
|
399
|
-
|
|
535
|
+
textAlign: "left"
|
|
536
|
+
},
|
|
537
|
+
sectionChevron: {
|
|
538
|
+
flex: "none",
|
|
539
|
+
width: 10,
|
|
540
|
+
fontSize: 10,
|
|
541
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)"
|
|
542
|
+
},
|
|
543
|
+
sectionLabel: {
|
|
544
|
+
flex: "1",
|
|
545
|
+
minWidth: 0,
|
|
546
|
+
overflow: "hidden",
|
|
547
|
+
textOverflow: "ellipsis",
|
|
548
|
+
whiteSpace: "nowrap"
|
|
549
|
+
},
|
|
550
|
+
sectionCount: {
|
|
551
|
+
flex: "none",
|
|
552
|
+
padding: "0 6px",
|
|
553
|
+
borderRadius: 999,
|
|
554
|
+
background: "color-mix(in srgb, var(--dsw-alias-label-primary, #e5e7eb) 10%, transparent)",
|
|
555
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)",
|
|
556
|
+
fontSize: 11,
|
|
557
|
+
fontWeight: 500,
|
|
558
|
+
lineHeight: "16px"
|
|
559
|
+
},
|
|
560
|
+
sectionBody: {
|
|
561
|
+
padding: "0 4px 4px"
|
|
562
|
+
},
|
|
563
|
+
sectionEmpty: {
|
|
564
|
+
padding: "2px 6px 6px",
|
|
565
|
+
fontSize: 12,
|
|
566
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)"
|
|
567
|
+
},
|
|
568
|
+
sectionMore: {
|
|
569
|
+
display: "block",
|
|
570
|
+
width: "100%",
|
|
571
|
+
padding: "2px 0 4px",
|
|
572
|
+
background: "transparent",
|
|
573
|
+
border: "none",
|
|
574
|
+
color: "var(--dsw-alias-link, #4f8cff)",
|
|
575
|
+
font: "inherit",
|
|
576
|
+
fontSize: 11,
|
|
577
|
+
cursor: "pointer",
|
|
578
|
+
textAlign: "center"
|
|
400
579
|
},
|
|
401
580
|
changeRow: {
|
|
402
581
|
display: "flex",
|
|
403
582
|
alignItems: "center",
|
|
404
583
|
gap: 8,
|
|
405
584
|
fontSize: 12,
|
|
406
|
-
lineHeight: "
|
|
407
|
-
|
|
408
|
-
padding: "1px 6px",
|
|
409
|
-
margin: "0 -6px",
|
|
585
|
+
lineHeight: "18px",
|
|
586
|
+
padding: "3px 6px",
|
|
410
587
|
borderRadius: 6,
|
|
411
588
|
minWidth: 0,
|
|
412
589
|
cursor: "pointer"
|
|
413
590
|
},
|
|
414
591
|
changePath: {
|
|
592
|
+
flex: "1",
|
|
593
|
+
minWidth: 0,
|
|
415
594
|
overflow: "hidden",
|
|
416
595
|
textOverflow: "ellipsis",
|
|
417
596
|
whiteSpace: "nowrap",
|
|
418
|
-
|
|
419
|
-
|
|
597
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
598
|
+
color: "var(--dsw-alias-label-primary, #e5e7eb)"
|
|
599
|
+
},
|
|
600
|
+
// Per-status chip; `color-mix(currentColor)` gives every status the same
|
|
601
|
+
// tint shape from whichever state colour it carries.
|
|
602
|
+
changeTag: {
|
|
603
|
+
flex: "none",
|
|
604
|
+
padding: "0 6px",
|
|
605
|
+
borderRadius: 4,
|
|
606
|
+
fontSize: 11,
|
|
607
|
+
lineHeight: "16px",
|
|
608
|
+
background: "color-mix(in srgb, currentColor 15%, transparent)"
|
|
420
609
|
},
|
|
421
610
|
// A change row is the diff viewer's navigation: it has to read as a
|
|
422
611
|
// control, and to show which file the right-hand pane is showing.
|
|
@@ -425,14 +614,12 @@ window.__ModuleLoader__.load({
|
|
|
425
614
|
alignItems: "center",
|
|
426
615
|
gap: 8,
|
|
427
616
|
fontSize: 12,
|
|
428
|
-
lineHeight: "
|
|
429
|
-
|
|
430
|
-
padding: "1px 6px",
|
|
431
|
-
margin: "0 -6px",
|
|
617
|
+
lineHeight: "18px",
|
|
618
|
+
padding: "3px 6px",
|
|
432
619
|
borderRadius: 6,
|
|
433
620
|
minWidth: 0,
|
|
434
621
|
cursor: "pointer",
|
|
435
|
-
background: "var(--dsw-alias-
|
|
622
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-business-primary, #4f8cff) 15%, transparent)"
|
|
436
623
|
},
|
|
437
624
|
// ── two-pane (changes + diff) layout ────────────────────────────
|
|
438
625
|
// `bodySplit` replaces `body` while a file is selected: the left pane
|
|
@@ -504,9 +691,9 @@ window.__ModuleLoader__.load({
|
|
|
504
691
|
lineHeight: "18px",
|
|
505
692
|
padding: "0 8px",
|
|
506
693
|
borderRadius: 999,
|
|
507
|
-
border: "1px solid var(--dsw-alias-
|
|
694
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.25))",
|
|
508
695
|
background: "transparent",
|
|
509
|
-
color: "var(--dsw-alias-label-
|
|
696
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)",
|
|
510
697
|
font: "inherit",
|
|
511
698
|
fontSize: 11,
|
|
512
699
|
cursor: "pointer"
|
|
@@ -517,7 +704,7 @@ window.__ModuleLoader__.load({
|
|
|
517
704
|
padding: "0 8px",
|
|
518
705
|
borderRadius: 999,
|
|
519
706
|
border: "1px solid transparent",
|
|
520
|
-
background: "var(--dsw-alias-
|
|
707
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-business-primary, #4f8cff) 16%, transparent)",
|
|
521
708
|
color: "var(--dsw-alias-label-primary, #e5e7eb)",
|
|
522
709
|
font: "inherit",
|
|
523
710
|
fontSize: 11,
|
|
@@ -547,7 +734,7 @@ window.__ModuleLoader__.load({
|
|
|
547
734
|
width: 34,
|
|
548
735
|
textAlign: "right",
|
|
549
736
|
paddingRight: 6,
|
|
550
|
-
color: "var(--dsw-alias-label-
|
|
737
|
+
color: "var(--dsw-alias-label-tertiary, #6b7280)",
|
|
551
738
|
userSelect: "none",
|
|
552
739
|
WebkitUserSelect: "none"
|
|
553
740
|
},
|
|
@@ -571,7 +758,7 @@ window.__ModuleLoader__.load({
|
|
|
571
758
|
color: "var(--dsw-alias-state-business-primary, #4f8cff)"
|
|
572
759
|
},
|
|
573
760
|
diffMetaRow: {
|
|
574
|
-
color: "var(--dsw-alias-label-
|
|
761
|
+
color: "var(--dsw-alias-label-tertiary, #6b7280)"
|
|
575
762
|
},
|
|
576
763
|
diffNotice: {
|
|
577
764
|
margin: "6px 10px",
|
|
@@ -579,7 +766,7 @@ window.__ModuleLoader__.load({
|
|
|
579
766
|
borderRadius: 8,
|
|
580
767
|
fontSize: 12,
|
|
581
768
|
lineHeight: "18px",
|
|
582
|
-
background: "var(--dsw-alias-
|
|
769
|
+
background: "var(--dsw-alias-bg-layer-2, rgba(127,127,127,0.12))",
|
|
583
770
|
color: "var(--dsw-alias-label-secondary, #c8ccd2)"
|
|
584
771
|
},
|
|
585
772
|
statusTag: {
|
|
@@ -588,40 +775,86 @@ window.__ModuleLoader__.load({
|
|
|
588
775
|
lineHeight: "16px",
|
|
589
776
|
padding: "0 6px",
|
|
590
777
|
borderRadius: 4,
|
|
591
|
-
background: "var(--dsw-alias-
|
|
778
|
+
background: "color-mix(in srgb, var(--dsw-alias-label-primary, #e5e7eb) 10%, transparent)",
|
|
779
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)"
|
|
592
780
|
},
|
|
593
781
|
logRow: {
|
|
594
782
|
display: "flex",
|
|
595
783
|
alignItems: "center",
|
|
596
784
|
gap: 8,
|
|
597
785
|
fontSize: 12,
|
|
598
|
-
lineHeight: "
|
|
599
|
-
|
|
600
|
-
|
|
786
|
+
lineHeight: "18px",
|
|
787
|
+
padding: "3px 6px",
|
|
788
|
+
borderRadius: 6,
|
|
601
789
|
minWidth: 0
|
|
602
790
|
},
|
|
603
791
|
logSha: {
|
|
604
792
|
flex: "none",
|
|
605
|
-
|
|
606
|
-
|
|
793
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
794
|
+
fontSize: 11,
|
|
795
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)"
|
|
796
|
+
},
|
|
797
|
+
logSubject: {
|
|
798
|
+
flex: "1",
|
|
799
|
+
minWidth: 0,
|
|
800
|
+
overflow: "hidden",
|
|
801
|
+
textOverflow: "ellipsis",
|
|
802
|
+
whiteSpace: "nowrap",
|
|
803
|
+
color: "var(--dsw-alias-label-primary, #e5e7eb)"
|
|
607
804
|
},
|
|
608
805
|
output: {
|
|
609
|
-
|
|
806
|
+
// Sits at the top of the body, so the breathing room belongs below it.
|
|
807
|
+
// The card itself is one localized summary line tall; git's own words
|
|
808
|
+
// live in `outputDetail`, behind the toggle.
|
|
809
|
+
marginBottom: 8,
|
|
610
810
|
padding: "6px 10px",
|
|
611
811
|
borderRadius: 8,
|
|
612
812
|
fontSize: 12,
|
|
813
|
+
lineHeight: "18px"
|
|
814
|
+
},
|
|
815
|
+
outputLine: {
|
|
816
|
+
display: "flex",
|
|
817
|
+
alignItems: "center",
|
|
818
|
+
gap: 8
|
|
819
|
+
},
|
|
820
|
+
outputSummary: {
|
|
821
|
+
flex: "1",
|
|
822
|
+
minWidth: 0,
|
|
823
|
+
overflow: "hidden",
|
|
824
|
+
textOverflow: "ellipsis",
|
|
825
|
+
whiteSpace: "nowrap"
|
|
826
|
+
},
|
|
827
|
+
outputToggle: {
|
|
828
|
+
flex: "none",
|
|
829
|
+
padding: "0 6px",
|
|
830
|
+
height: 20,
|
|
831
|
+
border: "1px solid currentColor",
|
|
832
|
+
borderRadius: 6,
|
|
833
|
+
background: "transparent",
|
|
834
|
+
color: "inherit",
|
|
835
|
+
font: "inherit",
|
|
836
|
+
fontSize: 11,
|
|
613
837
|
lineHeight: "18px",
|
|
838
|
+
cursor: "pointer"
|
|
839
|
+
},
|
|
840
|
+
outputDetail: {
|
|
841
|
+
marginTop: 6,
|
|
842
|
+
paddingTop: 6,
|
|
843
|
+
borderTop: "1px solid color-mix(in srgb, currentColor 30%, transparent)",
|
|
844
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
845
|
+
fontSize: 11,
|
|
846
|
+
lineHeight: "16px",
|
|
614
847
|
wordBreak: "break-word",
|
|
615
848
|
maxHeight: 160,
|
|
616
849
|
overflowY: "auto",
|
|
617
850
|
whiteSpace: "pre-wrap"
|
|
618
851
|
},
|
|
619
852
|
outputOk: {
|
|
620
|
-
background: "var(--dsw-alias-state-success-
|
|
853
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-success-primary, #34c759) 14%, transparent)",
|
|
621
854
|
color: "var(--dsw-alias-state-success-primary, #34c759)"
|
|
622
855
|
},
|
|
623
856
|
outputError: {
|
|
624
|
-
background: "var(--dsw-alias-state-error-
|
|
857
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-error-primary, #ef4444) 14%, transparent)",
|
|
625
858
|
color: "var(--dsw-alias-state-error-primary, #ef4444)"
|
|
626
859
|
},
|
|
627
860
|
dockWrap: {
|
|
@@ -631,7 +864,7 @@ window.__ModuleLoader__.load({
|
|
|
631
864
|
maxWidth: "100%",
|
|
632
865
|
fontSize: 12,
|
|
633
866
|
lineHeight: "20px",
|
|
634
|
-
color: "var(--dsw-alias-label-
|
|
867
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)",
|
|
635
868
|
whiteSpace: "nowrap",
|
|
636
869
|
overflow: "hidden",
|
|
637
870
|
textOverflow: "ellipsis",
|
|
@@ -645,9 +878,206 @@ window.__ModuleLoader__.load({
|
|
|
645
878
|
return text.length > max ? text.slice(0, max - 1) + "…" : text;
|
|
646
879
|
}
|
|
647
880
|
|
|
881
|
+
/**
|
|
882
|
+
* Strip ANSI/VT escape sequences from git's human-readable output.
|
|
883
|
+
*
|
|
884
|
+
* `git push` forwards the remote's own sideband banner verbatim — Gitee,
|
|
885
|
+
* for one, decorates "Powered by GITEE.COM" with SGR codes, and its first
|
|
886
|
+
* sequence is even truncated (`ESC[0`) right before a complete one. A DOM
|
|
887
|
+
* is not a terminal: the ESC byte has no glyph, so only the parameters
|
|
888
|
+
* survived and the banner read `Powered by [0[01;33mGITEE.COM [0[m…`.
|
|
889
|
+
*
|
|
890
|
+
* Only `message` text passes through here. Diff bodies and paths must not:
|
|
891
|
+
* a file may legitimately contain ESC bytes and the pane shows it verbatim.
|
|
892
|
+
*
|
|
893
|
+
* @param text - a git message string (or anything else).
|
|
894
|
+
* @returns the text with escape sequences removed.
|
|
895
|
+
*/
|
|
896
|
+
function stripAnsiEscapes(text) {
|
|
897
|
+
return (text === undefined || text === null ? "" : String(text))
|
|
898
|
+
// OSC (window title / hyperlink): ESC ] … BEL or ESC \
|
|
899
|
+
.replace(/\u001B\][^\u0007\u001B]*(?:\u0007|\u001B\\)/g, "")
|
|
900
|
+
// A complete CSI sequence: ESC [ params/intermediates final byte
|
|
901
|
+
.replace(/\u001B\[[0-9;?]*[ -/]*[@-~]/g, "")
|
|
902
|
+
// A truncated CSI (the remote's malformed banner): ESC [ params only
|
|
903
|
+
.replace(/\u001B\[[0-9;?]*/g, "")
|
|
904
|
+
// Any other two-character escape, then any surviving ESC.
|
|
905
|
+
.replace(/\u001B[@-Z\\-_]/g, "")
|
|
906
|
+
.replace(/\u001B/g, "");
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/** Sanitize one RPC result's human-readable text, leaving data untouched. */
|
|
910
|
+
function sanitizeResult(result) {
|
|
911
|
+
if (result === null || typeof result !== "object") return result;
|
|
912
|
+
var message = function (holder) {
|
|
913
|
+
return holder !== null && typeof holder === "object" && typeof holder.message === "string"
|
|
914
|
+
? Object.assign({}, holder, { message: stripAnsiEscapes(holder.message) })
|
|
915
|
+
: holder;
|
|
916
|
+
};
|
|
917
|
+
var value = message(result.value);
|
|
918
|
+
var error = message(result.error);
|
|
919
|
+
if (value === result.value && error === result.error) return result;
|
|
920
|
+
return Object.assign({}, result, { value: value, error: error });
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/** Longest one-line summary an operation card shows before folding. */
|
|
924
|
+
var OUTPUT_SUMMARY_MAX = 160;
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Word one operation result the default way: a summary the panel can
|
|
928
|
+
* localize from `action` (the store holds no translator), plus git's own
|
|
929
|
+
* output as collapsible detail.
|
|
930
|
+
*
|
|
931
|
+
* git's output is often several lines of advice — the LF/CRLF warning on
|
|
932
|
+
* every staged file, a remote's push banner — and showing it as the
|
|
933
|
+
* notification buries the answer to "did it work?". So success keeps git's
|
|
934
|
+
* words behind the toggle, and a failure shows the head of the diagnostic
|
|
935
|
+
* with the whole thing one click away.
|
|
936
|
+
*
|
|
937
|
+
* @param action - the operation name (`push`, `stage`, …).
|
|
938
|
+
* @param res - the raw RPC result.
|
|
939
|
+
* @param params - interpolation for the localized summary (e.g. `{ branch }`).
|
|
940
|
+
* @returns `{ kind, action, params, text, detail }`.
|
|
941
|
+
*/
|
|
942
|
+
function summarizeResult(action, res, params) {
|
|
943
|
+
if (res && res.ok === true) {
|
|
944
|
+
var raw = res.value && typeof res.value.message === "string" ? res.value.message.trim() : "";
|
|
945
|
+
return { kind: "ok", action: action, params: params || null, text: null, detail: raw };
|
|
946
|
+
}
|
|
947
|
+
var message = res && res.error && res.error.message ? String(res.error.message).trim() : "";
|
|
948
|
+
return {
|
|
949
|
+
kind: "error",
|
|
950
|
+
action: action,
|
|
951
|
+
params: params || null,
|
|
952
|
+
text: message === "" ? null : shortText(message, OUTPUT_SUMMARY_MAX),
|
|
953
|
+
detail: message.length > OUTPUT_SUMMARY_MAX ? message : ""
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* One operation-feedback card: the localized summary line and, when git
|
|
959
|
+
* said anything, git's own output behind a toggle. Pure and exported, so
|
|
960
|
+
* the render test can drive both states (SSR cannot click).
|
|
961
|
+
*
|
|
962
|
+
* @param result - `state.lastResult` (`{ kind, action, params, text, detail }`).
|
|
963
|
+
* @param t - the locale translator.
|
|
964
|
+
* @param open - whether the detail block is expanded.
|
|
965
|
+
* @param onToggle - called when the detail toggle is clicked.
|
|
966
|
+
* @returns the card element, or null without a result.
|
|
967
|
+
*/
|
|
968
|
+
function outputView(result, t, open, onToggle) {
|
|
969
|
+
if (!result) return null;
|
|
970
|
+
var summary = result.text !== undefined && result.text !== null && String(result.text) !== ""
|
|
971
|
+
? String(result.text)
|
|
972
|
+
: result.action
|
|
973
|
+
? t("output." + result.action, result.params || undefined)
|
|
974
|
+
: t(result.kind === "error" ? "output.failed" : "output.ok");
|
|
975
|
+
var detail = typeof result.detail === "string" ? result.detail.trim() : "";
|
|
976
|
+
var line = [
|
|
977
|
+
h("span", { key: "text", style: S.outputSummary, title: summary }, summary)
|
|
978
|
+
];
|
|
979
|
+
if (detail !== "") {
|
|
980
|
+
line.push(h("button", {
|
|
981
|
+
key: "toggle",
|
|
982
|
+
type: "button",
|
|
983
|
+
"data-dsh-git": "output-toggle",
|
|
984
|
+
style: S.outputToggle,
|
|
985
|
+
"aria-expanded": open ? "true" : "false",
|
|
986
|
+
onClick: onToggle
|
|
987
|
+
}, (open ? "▴ " : "▾ ") + t(open ? "output.detailHide" : "output.detail")));
|
|
988
|
+
}
|
|
989
|
+
var children = [h("div", { key: "line", style: S.outputLine }, line)];
|
|
990
|
+
if (open && detail !== "") {
|
|
991
|
+
children.push(h("div", { key: "detail", "data-dsh-git": "output-detail", style: S.outputDetail }, detail));
|
|
992
|
+
}
|
|
993
|
+
return h("div", {
|
|
994
|
+
key: "output",
|
|
995
|
+
"data-dsh-git": "output",
|
|
996
|
+
style: Object.assign({}, S.output, result.kind === "ok" ? S.outputOk : S.outputError)
|
|
997
|
+
}, children);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/** Rows a collapsed-by-default list shows before its "show all" footer. */
|
|
1001
|
+
var CHANGES_PREVIEW = 8;
|
|
1002
|
+
/** Same, for the recent-commit list (shorter rows, less to say). */
|
|
1003
|
+
var LOG_PREVIEW = 5;
|
|
1004
|
+
|
|
1005
|
+
/** The state colour one porcelain status carries, in theme tokens. */
|
|
1006
|
+
function changeTagStyle(status) {
|
|
1007
|
+
var color = status === "added" || status === "untracked"
|
|
1008
|
+
? "var(--dsw-alias-state-success-primary, #34c759)"
|
|
1009
|
+
: status === "deleted" || status === "conflict"
|
|
1010
|
+
? "var(--dsw-alias-state-error-primary, #ef4444)"
|
|
1011
|
+
: status === "renamed" || status === "copied"
|
|
1012
|
+
// `state-business-primary` is the real accent (deepseek blue);
|
|
1013
|
+
// `brand-primary` is the inverted contrast colour and would make
|
|
1014
|
+
// this chip white-on-white in the dark theme.
|
|
1015
|
+
? "var(--dsw-alias-state-business-primary, #4f8cff)"
|
|
1016
|
+
: status === "modified" || status === "changed"
|
|
1017
|
+
? "var(--dsw-alias-state-warn-primary, #f0a020)"
|
|
1018
|
+
: "var(--dsw-alias-label-secondary, #c8ccd2)";
|
|
1019
|
+
// `changeTag`'s background is `color-mix(currentColor…)`, so the tint
|
|
1020
|
+
// follows whichever colour the status carries.
|
|
1021
|
+
return Object.assign({}, S.changeTag, { color: color });
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* One collapsible list section: a full-width header toggle (chevron +
|
|
1026
|
+
* label + count) and, while open, its rows plus a footer that expands the
|
|
1027
|
+
* preview. Two separate states on purpose — the header collapses the
|
|
1028
|
+
* section, the footer widens it — because one flag doing both made the
|
|
1029
|
+
* chevron point the wrong way for a list that was already on screen.
|
|
1030
|
+
*
|
|
1031
|
+
* @param spec - `{ hook, label, count, open, all, cap, empty, rows, onToggle, onMore }`.
|
|
1032
|
+
* @param t - the locale translator.
|
|
1033
|
+
* @returns the section element.
|
|
1034
|
+
*/
|
|
1035
|
+
function sectionView(spec, t) {
|
|
1036
|
+
var children = [h("button", {
|
|
1037
|
+
key: "header",
|
|
1038
|
+
type: "button",
|
|
1039
|
+
"data-dsh-git": spec.hook + "-header",
|
|
1040
|
+
style: S.sectionHeader,
|
|
1041
|
+
"aria-expanded": spec.open ? "true" : "false",
|
|
1042
|
+
onClick: spec.onToggle
|
|
1043
|
+
}, [
|
|
1044
|
+
h("span", { key: "chevron", style: S.sectionChevron }, spec.open ? "▾" : "▸"),
|
|
1045
|
+
h("span", { key: "label", style: S.sectionLabel }, spec.label),
|
|
1046
|
+
h("span", { key: "count", style: S.sectionCount }, String(spec.count))
|
|
1047
|
+
])];
|
|
1048
|
+
if (spec.open) {
|
|
1049
|
+
var previewed = spec.all || spec.count <= spec.cap;
|
|
1050
|
+
children.push(h("div", {
|
|
1051
|
+
key: "body",
|
|
1052
|
+
"data-dsh-git": spec.hook + "-body",
|
|
1053
|
+
style: S.sectionBody
|
|
1054
|
+
}, spec.count === 0
|
|
1055
|
+
? h("div", { key: "empty", style: S.sectionEmpty }, spec.empty)
|
|
1056
|
+
: (previewed ? spec.rows : spec.rows.slice(0, spec.cap))));
|
|
1057
|
+
if (!previewed) {
|
|
1058
|
+
children.push(h("button", {
|
|
1059
|
+
key: "more",
|
|
1060
|
+
type: "button",
|
|
1061
|
+
"data-dsh-git": spec.hook + "-more",
|
|
1062
|
+
style: S.sectionMore,
|
|
1063
|
+
onClick: spec.onMore
|
|
1064
|
+
}, t("section.more", { count: spec.count })));
|
|
1065
|
+
} else if (spec.all && spec.count > spec.cap) {
|
|
1066
|
+
children.push(h("button", {
|
|
1067
|
+
key: "more",
|
|
1068
|
+
type: "button",
|
|
1069
|
+
"data-dsh-git": spec.hook + "-more",
|
|
1070
|
+
style: S.sectionMore,
|
|
1071
|
+
onClick: spec.onMore
|
|
1072
|
+
}, t("section.less")));
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
return h("div", { key: spec.hook, "data-dsh-git": spec.hook, style: S.section }, children);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
648
1078
|
/** Call one /dsh-git-rpc endpoint. */
|
|
649
1079
|
function rpc(ctx, endpoint, args) {
|
|
650
|
-
return ctx.connection.rpc.call("/dsh-git-rpc", endpoint, { args: args || {} });
|
|
1080
|
+
return ctx.connection.rpc.call("/dsh-git-rpc", endpoint, { args: args || {} }).then(sanitizeResult);
|
|
651
1081
|
}
|
|
652
1082
|
|
|
653
1083
|
/**
|
|
@@ -702,6 +1132,7 @@ window.__ModuleLoader__.load({
|
|
|
702
1132
|
commits: [],
|
|
703
1133
|
error: null,
|
|
704
1134
|
busy: null,
|
|
1135
|
+
refreshing: false,
|
|
705
1136
|
lastResult: null,
|
|
706
1137
|
panelOpen: false
|
|
707
1138
|
};
|
|
@@ -763,7 +1194,19 @@ window.__ModuleLoader__.load({
|
|
|
763
1194
|
|
|
764
1195
|
function doRefresh(cwd) {
|
|
765
1196
|
var mySeq = ++seq;
|
|
766
|
-
|
|
1197
|
+
// Re-reading the SAME workspace keeps what is already on screen.
|
|
1198
|
+
// Flipping the body to "loading" for a few hundred milliseconds
|
|
1199
|
+
// collapsed the whole workbench and snapped it back — that is the
|
|
1200
|
+
// jump you saw after clicking ↻ or any action button. Only a first
|
|
1201
|
+
// load (or a workspace switch) has nothing to keep.
|
|
1202
|
+
var keep = cwd !== null && cwd === state.cwd && state.phase === "ready";
|
|
1203
|
+
emit(Object.assign({}, state, {
|
|
1204
|
+
sessionId: sessionId,
|
|
1205
|
+
cwd: cwd,
|
|
1206
|
+
phase: keep ? state.phase : (cwd ? "loading" : "idle"),
|
|
1207
|
+
refreshing: keep,
|
|
1208
|
+
error: keep ? state.error : null
|
|
1209
|
+
}));
|
|
767
1210
|
if (!cwd) return Promise.resolve();
|
|
768
1211
|
return Promise.all([
|
|
769
1212
|
verbs.status(cwd),
|
|
@@ -823,17 +1266,20 @@ window.__ModuleLoader__.load({
|
|
|
823
1266
|
* Run one action; on success force a fresh load; surfaces lastResult.
|
|
824
1267
|
* `describe` lets a caller own the wording of both outcomes (used by
|
|
825
1268
|
* generation, where the success value is the message itself and a
|
|
826
|
-
* failure code maps to a localized string).
|
|
827
|
-
*
|
|
1269
|
+
* failure code maps to a localized string). Without it the result is
|
|
1270
|
+
* summarized by {@link summarizeResult}: a localized one-liner plus
|
|
1271
|
+
* git's own output as detail. `params` interpolates that summary (e.g.
|
|
1272
|
+
* `{ branch }`); the panel localizes it, since the store holds no `t`.
|
|
1273
|
+
* Resolves with the raw RPC result so the caller can also act on it.
|
|
1274
|
+
* @param name - the operation name (`push`, `commit`, …).
|
|
1275
|
+
* @param run - performs the call.
|
|
1276
|
+
* @param describe - optional wording override for both outcomes.
|
|
1277
|
+
* @param params - optional interpolation for the default summary.
|
|
828
1278
|
*/
|
|
829
|
-
function act(name, run, describe) {
|
|
1279
|
+
function act(name, run, describe, params) {
|
|
830
1280
|
emit(Object.assign({}, state, { busy: name, lastResult: null }));
|
|
831
1281
|
return run().then(function (res) {
|
|
832
|
-
var result = describe
|
|
833
|
-
? describe(res)
|
|
834
|
-
: res && res.ok === true
|
|
835
|
-
? { kind: "ok", text: res.value && res.value.message ? String(res.value.message) : null }
|
|
836
|
-
: { kind: "error", text: res && res.error && res.error.message ? String(res.error.message) : "operation failed" };
|
|
1282
|
+
var result = describe ? describe(res) : summarizeResult(name, res, params);
|
|
837
1283
|
emit(Object.assign({}, state, { busy: null, lastResult: result }));
|
|
838
1284
|
if (res && res.ok === true) {
|
|
839
1285
|
var cwd = state.cwd;
|
|
@@ -841,10 +1287,10 @@ window.__ModuleLoader__.load({
|
|
|
841
1287
|
}
|
|
842
1288
|
return res;
|
|
843
1289
|
}).catch(function (error) {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
}));
|
|
1290
|
+
// A transport failure is worded exactly like a git failure: the
|
|
1291
|
+
// same summary/detail split, so the card never grows a second shape.
|
|
1292
|
+
var failure = { ok: false, error: { message: error && error.message ? String(error.message) : String(error) } };
|
|
1293
|
+
emit(Object.assign({}, state, { busy: null, lastResult: summarizeResult(name, failure, params) }));
|
|
848
1294
|
});
|
|
849
1295
|
}
|
|
850
1296
|
|
|
@@ -870,7 +1316,7 @@ window.__ModuleLoader__.load({
|
|
|
870
1316
|
};
|
|
871
1317
|
var OPTGROUP_STYLE = {
|
|
872
1318
|
background: "var(--dsw-alias-bg-base, #1e1e1e)",
|
|
873
|
-
color: "var(--dsw-alias-label-
|
|
1319
|
+
color: "var(--dsw-alias-label-secondary, #c8ccd2)",
|
|
874
1320
|
fontWeight: 600
|
|
875
1321
|
};
|
|
876
1322
|
|
|
@@ -1021,6 +1467,32 @@ window.__ModuleLoader__.load({
|
|
|
1021
1467
|
var PANEL_MARGIN = 24;
|
|
1022
1468
|
/** Grab width of the panel's right-edge resize handle. */
|
|
1023
1469
|
var PANEL_RESIZE_GRIP = 6;
|
|
1470
|
+
/**
|
|
1471
|
+
* Height the panel takes while the diff pane is open: a fixed height, not a
|
|
1472
|
+
* cap. With a cap the panel's height followed its content, so the moment the
|
|
1473
|
+
* diff arrived the pane grew and — anchored by `bottom` — the whole panel
|
|
1474
|
+
* jumped (the flash you saw when opening a diff). The `min()` keeps the panel
|
|
1475
|
+
* on screen in a short window (168px is the panel's bottom offset).
|
|
1476
|
+
*/
|
|
1477
|
+
var DIFF_PANEL_HEIGHT = "min(72vh, calc(100vh - 184px))";
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* The panel's box in either mode: the compact single column, or workbench +
|
|
1481
|
+
* diff with a height that cannot move once the pane is open.
|
|
1482
|
+
* @param selected - the change the diff pane shows, or null.
|
|
1483
|
+
* @param diffWidth - width of the diff pane.
|
|
1484
|
+
* @returns the panel's inline style.
|
|
1485
|
+
*/
|
|
1486
|
+
function panelBoxStyle(selected, diffWidth) {
|
|
1487
|
+
var box = Object.assign({}, S.panel, {
|
|
1488
|
+
width: selected === null ? S.panel.width : DIFF_LEFT_PANE_WIDTH + PANEL_BORDER_WIDTH + diffWidth
|
|
1489
|
+
});
|
|
1490
|
+
if (selected !== null) {
|
|
1491
|
+
box.height = DIFF_PANEL_HEIGHT;
|
|
1492
|
+
box.maxHeight = DIFF_PANEL_HEIGHT;
|
|
1493
|
+
}
|
|
1494
|
+
return box;
|
|
1495
|
+
}
|
|
1024
1496
|
|
|
1025
1497
|
/** The last path segment, for chip-sized text. */
|
|
1026
1498
|
function baseName(file) {
|
|
@@ -1371,12 +1843,31 @@ window.__ModuleLoader__.load({
|
|
|
1371
1843
|
var generateModeRef = React.useState("staged");
|
|
1372
1844
|
var generateMode = generateModeRef[0];
|
|
1373
1845
|
var setGenerateMode = generateModeRef[1];
|
|
1374
|
-
|
|
1846
|
+
// Sections start OPEN: a Git panel that hides the user's own changes
|
|
1847
|
+
// behind a chevron is a panel that looks empty. Collapsing is the
|
|
1848
|
+
// explicit choice, and widening a long list past its preview is a
|
|
1849
|
+
// separate one (`…All`), so the chevron always tells the truth.
|
|
1850
|
+
var changesOpenRef = React.useState(true);
|
|
1375
1851
|
var changesOpen = changesOpenRef[0];
|
|
1376
1852
|
var setChangesOpen = changesOpenRef[1];
|
|
1377
|
-
var
|
|
1853
|
+
var changesAllRef = React.useState(false);
|
|
1854
|
+
var changesAll = changesAllRef[0];
|
|
1855
|
+
var setChangesAll = changesAllRef[1];
|
|
1856
|
+
var logOpenRef = React.useState(true);
|
|
1378
1857
|
var logOpen = logOpenRef[0];
|
|
1379
1858
|
var setLogOpen = logOpenRef[1];
|
|
1859
|
+
var logAllRef = React.useState(false);
|
|
1860
|
+
var logAll = logAllRef[0];
|
|
1861
|
+
var setLogAll = logAllRef[1];
|
|
1862
|
+
// Whether the last operation's git output is expanded. A new result
|
|
1863
|
+
// always starts collapsed: the summary line is the notification, the
|
|
1864
|
+
// raw output is opt-in.
|
|
1865
|
+
var outputOpenRef = React.useState(false);
|
|
1866
|
+
var outputOpen = outputOpenRef[0];
|
|
1867
|
+
var setOutputOpen = outputOpenRef[1];
|
|
1868
|
+
React.useEffect(function () {
|
|
1869
|
+
setOutputOpen(false);
|
|
1870
|
+
}, [state.lastResult]);
|
|
1380
1871
|
|
|
1381
1872
|
// Branch the user picked while the tree is dirty: the pre-check
|
|
1382
1873
|
// shows a warning instead of switching immediately; the switch only
|
|
@@ -1629,11 +2120,7 @@ window.__ModuleLoader__.load({
|
|
|
1629
2120
|
// while nothing is selected, and the two-pane width (workbench + diff)
|
|
1630
2121
|
// once a file is open. Dragging the panel's right edge is what resizes
|
|
1631
2122
|
// the diff, so the panel grows and shrinks with it.
|
|
1632
|
-
var
|
|
1633
|
-
var panelStyle = Object.assign({}, S.panel, {
|
|
1634
|
-
width: panelWidth,
|
|
1635
|
-
maxHeight: selected === null ? S.panel.maxHeight : "72vh"
|
|
1636
|
-
});
|
|
2123
|
+
var panelStyle = panelBoxStyle(selected, diffWidth);
|
|
1637
2124
|
if (dragPos) {
|
|
1638
2125
|
panelStyle = Object.assign(panelStyle, {
|
|
1639
2126
|
left: dragPos.left,
|
|
@@ -1667,20 +2154,24 @@ window.__ModuleLoader__.load({
|
|
|
1667
2154
|
"data-dsh-git": "panel-drag"
|
|
1668
2155
|
}, [
|
|
1669
2156
|
h("span", { key: "title", style: S.title }, "⎇ " + t("panel.title")),
|
|
1670
|
-
h("span", { key: "cwd", style: S.
|
|
2157
|
+
h("span", { key: "cwd", style: S.cwdPath, title: cwd }, shortText(cwd, 80)),
|
|
1671
2158
|
h("button", {
|
|
1672
2159
|
key: "refresh",
|
|
1673
2160
|
type: "button",
|
|
1674
|
-
|
|
1675
|
-
|
|
2161
|
+
// A re-read keeps the whole workbench on screen and reports itself
|
|
2162
|
+
// here instead of collapsing the body to a "loading" line and
|
|
2163
|
+
// snapping back — that flash was the jump.
|
|
2164
|
+
style: Object.assign({}, S.iconButton, busy !== null || state.refreshing ? S.buttonDisabled : null),
|
|
2165
|
+
disabled: busy !== null || state.refreshing === true,
|
|
1676
2166
|
onClick: function () { store.refresh(state.cwd); },
|
|
1677
2167
|
"aria-label": t("refresh.aria"),
|
|
1678
|
-
|
|
1679
|
-
|
|
2168
|
+
"aria-busy": state.refreshing === true ? "true" : "false",
|
|
2169
|
+
title: state.refreshing === true ? t("refresh.busy") : t("refresh.aria")
|
|
2170
|
+
}, state.refreshing === true ? "⋯" : "↻"),
|
|
1680
2171
|
h("button", {
|
|
1681
2172
|
key: "collapse",
|
|
1682
2173
|
type: "button",
|
|
1683
|
-
style: S.
|
|
2174
|
+
style: S.iconButton,
|
|
1684
2175
|
onClick: function () { store.setPanelOpen(false); },
|
|
1685
2176
|
"aria-label": t("panel.collapse"),
|
|
1686
2177
|
title: t("panel.collapse")
|
|
@@ -1688,6 +2179,16 @@ window.__ModuleLoader__.load({
|
|
|
1688
2179
|
]);
|
|
1689
2180
|
|
|
1690
2181
|
var bodyChildren = [];
|
|
2182
|
+
// Operation feedback sits at the very TOP of the body. A push banner is
|
|
2183
|
+
// several lines long, and at the bottom of a 400px panel it landed under
|
|
2184
|
+
// the change list and the log — below the fold on any repo with a few
|
|
2185
|
+
// files, which is exactly where nobody looks after clicking a button.
|
|
2186
|
+
if (state.lastResult) {
|
|
2187
|
+
bodyChildren.push(outputView(state.lastResult, t, outputOpen, function () { setOutputOpen(!outputOpen); }));
|
|
2188
|
+
}
|
|
2189
|
+
if (busy) {
|
|
2190
|
+
bodyChildren.push(h("div", { key: "busy", style: S.meta }, busyLabel));
|
|
2191
|
+
}
|
|
1691
2192
|
if (state.phase === "loading" || state.phase === "idle") {
|
|
1692
2193
|
bodyChildren.push(h("div", { key: "loading", style: S.meta }, t("loading")));
|
|
1693
2194
|
} else if (state.phase === "error") {
|
|
@@ -1695,15 +2196,21 @@ window.__ModuleLoader__.load({
|
|
|
1695
2196
|
} else if (!state.repo) {
|
|
1696
2197
|
bodyChildren.push(h("div", { key: "norepo", style: S.meta }, t("notRepo")));
|
|
1697
2198
|
} else {
|
|
1698
|
-
// status line
|
|
2199
|
+
// status line — the branch itself lives in the select right below,
|
|
2200
|
+
// so this row carries only what the select cannot say: pending
|
|
2201
|
+
// changes and how the branch sits against its upstream.
|
|
1699
2202
|
var statusLine = [];
|
|
1700
2203
|
var branchText = state.branch;
|
|
1701
2204
|
if (!branchText && state.detached) {
|
|
1702
2205
|
branchText = t("detached") + (state.oid ? " " + state.oid.slice(0, 7) : "");
|
|
1703
2206
|
}
|
|
1704
|
-
|
|
2207
|
+
if (state.detached) {
|
|
2208
|
+
statusLine.push(h("span", { key: "chip", style: S.chip, title: branchText }, branchText));
|
|
2209
|
+
}
|
|
1705
2210
|
if (state.dirty > 0) {
|
|
1706
|
-
statusLine.push(h("span", { key: "dirty", style: S.dirty, title: t("dirty", { count: state.dirty }) }, "● " + state.dirty));
|
|
2211
|
+
statusLine.push(h("span", { key: "dirty", style: S.dirty, title: t("dirty", { count: state.dirty }) }, "● " + t("dirty", { count: state.dirty })));
|
|
2212
|
+
} else {
|
|
2213
|
+
statusLine.push(h("span", { key: "clean", style: S.meta }, "✓ " + t("changes.none")));
|
|
1707
2214
|
}
|
|
1708
2215
|
if (state.ahead > 0 || state.behind > 0) {
|
|
1709
2216
|
statusLine.push(h("span", { key: "ab", style: S.meta }, "↑" + state.ahead + " ↓" + state.behind));
|
|
@@ -1728,7 +2235,7 @@ window.__ModuleLoader__.load({
|
|
|
1728
2235
|
var value = event.target.value;
|
|
1729
2236
|
if (!value || value === state.branch) { setPendingBranch(null); return; }
|
|
1730
2237
|
if (state.dirty > 0) { setPendingBranch(value); return; }
|
|
1731
|
-
store.act("checkout", function () { return store.verbs.checkout(state.cwd, value); });
|
|
2238
|
+
store.act("checkout", function () { return store.verbs.checkout(state.cwd, value); }, null, { branch: value });
|
|
1732
2239
|
},
|
|
1733
2240
|
"aria-label": t("switch.aria")
|
|
1734
2241
|
}, selectChildren),
|
|
@@ -1799,7 +2306,7 @@ window.__ModuleLoader__.load({
|
|
|
1799
2306
|
if (nameText === "" || baseText === "" || busy !== null) return;
|
|
1800
2307
|
setNewBranchName("");
|
|
1801
2308
|
setNewBranchOpen(false);
|
|
1802
|
-
store.act("createBranch", function () { return store.verbs.createBranch(state.cwd, nameText, baseText); });
|
|
2309
|
+
store.act("createBranch", function () { return store.verbs.createBranch(state.cwd, nameText, baseText); }, null, { branch: nameText });
|
|
1803
2310
|
}
|
|
1804
2311
|
}
|
|
1805
2312
|
if (pendingBranch !== null && state.dirty > 0) {
|
|
@@ -1817,100 +2324,102 @@ window.__ModuleLoader__.load({
|
|
|
1817
2324
|
onClick: function () {
|
|
1818
2325
|
var target = pendingBranch;
|
|
1819
2326
|
setPendingBranch(null);
|
|
1820
|
-
store.act("checkout", function () { return store.verbs.checkout(state.cwd, target); });
|
|
2327
|
+
store.act("checkout", function () { return store.verbs.checkout(state.cwd, target); }, null, { branch: target });
|
|
1821
2328
|
}
|
|
1822
2329
|
}, busy === "checkout" ? t("busy.checkout") : t("switch.force"))
|
|
1823
2330
|
]));
|
|
1824
2331
|
}
|
|
1825
2332
|
|
|
1826
|
-
// network
|
|
2333
|
+
// network actions — three equal columns. Auto-width labels used to
|
|
2334
|
+
// give each button a different width for the same job, which is what
|
|
2335
|
+
// made this row read as a pile rather than a toolbar.
|
|
1827
2336
|
bodyChildren.push(h("div", { key: "net", style: S.row }, [
|
|
1828
2337
|
h("button", {
|
|
1829
2338
|
key: "fetch",
|
|
1830
2339
|
type: "button",
|
|
1831
|
-
style: Object.assign({}, S.button, busy !== null ? S.buttonDisabled : null),
|
|
2340
|
+
style: Object.assign({}, S.button, S.grow, busy !== null ? S.buttonDisabled : null),
|
|
1832
2341
|
disabled: busy !== null,
|
|
1833
2342
|
onClick: function () { store.act("fetch", function () { return store.verbs.fetch(state.cwd); }); }
|
|
1834
2343
|
}, busy === "fetch" ? t("busy.fetch") : t("action.fetch")),
|
|
1835
2344
|
h("button", {
|
|
1836
2345
|
key: "pull",
|
|
1837
2346
|
type: "button",
|
|
1838
|
-
style: Object.assign({}, S.button, busy !== null ? S.buttonDisabled : null),
|
|
2347
|
+
style: Object.assign({}, S.button, S.grow, busy !== null ? S.buttonDisabled : null),
|
|
1839
2348
|
disabled: busy !== null,
|
|
1840
2349
|
onClick: function () { store.act("pull", function () { return store.verbs.pull(state.cwd); }); }
|
|
1841
2350
|
}, busy === "pull" ? t("busy.pull") : t("action.pull")),
|
|
1842
2351
|
h("button", {
|
|
1843
2352
|
key: "push",
|
|
1844
2353
|
type: "button",
|
|
1845
|
-
style: Object.assign({}, S.buttonPrimary, busy !== null ? S.buttonDisabled : null),
|
|
2354
|
+
style: Object.assign({}, S.buttonPrimary, S.grow, busy !== null ? S.buttonDisabled : null),
|
|
1846
2355
|
disabled: busy !== null,
|
|
1847
2356
|
onClick: function () { store.act("push", function () { return store.verbs.push(state.cwd); }); }
|
|
1848
2357
|
}, busy === "push" ? t("busy.push") : t("action.push"))
|
|
1849
2358
|
]));
|
|
1850
2359
|
|
|
1851
|
-
// commit
|
|
1852
|
-
//
|
|
1853
|
-
//
|
|
1854
|
-
// message row.
|
|
2360
|
+
// commit flow, one card: stage all → draft basis + draft → message →
|
|
2361
|
+
// commit. Grouping it keeps the 400px column readable and puts the
|
|
2362
|
+
// primary action (提交) in the same surface as the text it sends.
|
|
1855
2363
|
var noChanges = state.dirty === 0;
|
|
1856
|
-
bodyChildren.push(h("div", { key: "
|
|
1857
|
-
h("
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
2364
|
+
bodyChildren.push(h("div", { key: "commitCard", style: S.card }, [
|
|
2365
|
+
h("div", { key: "tools", style: S.rowTight }, [
|
|
2366
|
+
h("button", {
|
|
2367
|
+
key: "stage",
|
|
2368
|
+
type: "button",
|
|
2369
|
+
style: Object.assign({}, S.button, S.grow, busy !== null || noChanges ? S.buttonDisabled : null),
|
|
2370
|
+
disabled: busy !== null || noChanges,
|
|
2371
|
+
title: noChanges ? t("changes.none") : t("action.stage"),
|
|
2372
|
+
onClick: function () { store.act("stage", function () { return store.verbs.stage(state.cwd); }); }
|
|
2373
|
+
}, busy === "stage" ? t("busy.stage") : t("action.stage")),
|
|
2374
|
+
h("select", {
|
|
2375
|
+
key: "mode",
|
|
2376
|
+
style: Object.assign({}, S.select, S.grow, { maxWidth: "none" }),
|
|
2377
|
+
value: generateMode,
|
|
2378
|
+
disabled: busy !== null,
|
|
2379
|
+
title: t("generate.mode.title"),
|
|
2380
|
+
"aria-label": t("generate.mode.title"),
|
|
2381
|
+
onChange: function (event) { setGenerateMode(event.target.value); }
|
|
2382
|
+
}, [
|
|
2383
|
+
h("option", { key: "staged", value: "staged", style: OPTION_STYLE }, t("generate.mode.staged")),
|
|
2384
|
+
h("option", { key: "unstaged", value: "unstaged", style: OPTION_STYLE }, t("generate.mode.unstaged")),
|
|
2385
|
+
h("option", { key: "all", value: "all", style: OPTION_STYLE }, t("generate.mode.all"))
|
|
2386
|
+
]),
|
|
2387
|
+
h("button", {
|
|
2388
|
+
key: "generate",
|
|
2389
|
+
type: "button",
|
|
2390
|
+
style: Object.assign({}, S.button, S.grow, busy !== null || noChanges ? S.buttonDisabled : null),
|
|
2391
|
+
disabled: busy !== null || noChanges,
|
|
2392
|
+
title: noChanges ? t("changes.none") : t("action.generate"),
|
|
2393
|
+
onClick: doGenerate
|
|
2394
|
+
}, busy === "generate" ? t("busy.generate") : "✨ " + t("action.generate"))
|
|
1877
2395
|
]),
|
|
1878
|
-
h("
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
placeholder: t("commit.placeholder"),
|
|
1895
|
-
disabled: busy !== null,
|
|
1896
|
-
onChange: function (event) { setMessage(event.target.value); },
|
|
1897
|
-
// Enter inserts a newline (a message may have a body); the
|
|
1898
|
-
// explicit commit chord is Ctrl/Cmd+Enter.
|
|
1899
|
-
onKeyDown: function (event) {
|
|
1900
|
-
if (event.key === "Enter" && (event.ctrlKey || event.metaKey) && message.trim() !== "" && busy === null) {
|
|
1901
|
-
event.preventDefault();
|
|
1902
|
-
doCommit();
|
|
2396
|
+
h("div", { key: "message", style: S.rowTight }, [
|
|
2397
|
+
h("textarea", {
|
|
2398
|
+
key: "msg",
|
|
2399
|
+
rows: 2,
|
|
2400
|
+
style: S.textarea,
|
|
2401
|
+
value: message,
|
|
2402
|
+
placeholder: t("commit.placeholder"),
|
|
2403
|
+
disabled: busy !== null,
|
|
2404
|
+
onChange: function (event) { setMessage(event.target.value); },
|
|
2405
|
+
// Enter inserts a newline (a message may have a body); the
|
|
2406
|
+
// explicit commit chord is Ctrl/Cmd+Enter.
|
|
2407
|
+
onKeyDown: function (event) {
|
|
2408
|
+
if (event.key === "Enter" && (event.ctrlKey || event.metaKey) && message.trim() !== "" && busy === null) {
|
|
2409
|
+
event.preventDefault();
|
|
2410
|
+
doCommit();
|
|
2411
|
+
}
|
|
1903
2412
|
}
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
2413
|
+
}),
|
|
2414
|
+
h("button", {
|
|
2415
|
+
key: "commit",
|
|
2416
|
+
type: "button",
|
|
2417
|
+
style: Object.assign({}, S.buttonPrimary, { alignSelf: "flex-end", padding: "0 14px" }, busy !== null || message.trim() === "" ? S.buttonDisabled : null),
|
|
2418
|
+
disabled: busy !== null || message.trim() === "",
|
|
2419
|
+
title: message.trim() === "" ? t("commit.empty") : undefined,
|
|
2420
|
+
onClick: doCommit
|
|
2421
|
+
}, busy === "commit" ? t("busy.commit") : t("action.commit"))
|
|
2422
|
+
])
|
|
1914
2423
|
]));
|
|
1915
2424
|
|
|
1916
2425
|
function doCommit() {
|
|
@@ -1935,6 +2444,7 @@ window.__ModuleLoader__.load({
|
|
|
1935
2444
|
"no-provider": t("generate.noProvider"),
|
|
1936
2445
|
"no-model": t("generate.noModel"),
|
|
1937
2446
|
"llm-empty": t("generate.empty"),
|
|
2447
|
+
"llm-truncated": t("generate.truncated"),
|
|
1938
2448
|
"cancelled": t("generate.cancelled")
|
|
1939
2449
|
};
|
|
1940
2450
|
var text = known[details.code];
|
|
@@ -1961,90 +2471,67 @@ window.__ModuleLoader__.load({
|
|
|
1961
2471
|
});
|
|
1962
2472
|
}
|
|
1963
2473
|
|
|
1964
|
-
// changes (one row per file) —
|
|
1965
|
-
//
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
var
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
1987
|
-
if (event.preventDefault) event.preventDefault();
|
|
1988
|
-
selectChange(change);
|
|
1989
|
-
}
|
|
2474
|
+
// changes (one row per file) — the diff viewer's navigation:
|
|
2475
|
+
// clicking a row opens that file's diff on the right. The chip
|
|
2476
|
+
// trails the path so paths line up in one column and the eye can
|
|
2477
|
+
// scan them; the status only has to say which kind of change it is.
|
|
2478
|
+
var changesRows = state.changes.map(function (change, index) {
|
|
2479
|
+
var file = change.file !== undefined ? change.file : change.path;
|
|
2480
|
+
var origFile = change.origFile !== undefined ? change.origFile : null;
|
|
2481
|
+
var isActive = selected !== null && selected.file === file && selected.origFile === origFile;
|
|
2482
|
+
return h("div", {
|
|
2483
|
+
key: index,
|
|
2484
|
+
style: isActive ? S.changeRowActive : S.changeRow,
|
|
2485
|
+
role: "button",
|
|
2486
|
+
tabIndex: 0,
|
|
2487
|
+
title: change.path,
|
|
2488
|
+
"aria-label": t("diff.open") + " " + change.path,
|
|
2489
|
+
"data-dsh-git": "change-row",
|
|
2490
|
+
"data-active": isActive === true ? "true" : "false",
|
|
2491
|
+
onClick: function () { selectChange(change); },
|
|
2492
|
+
onKeyDown: function (event) {
|
|
2493
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
2494
|
+
if (event.preventDefault) event.preventDefault();
|
|
2495
|
+
selectChange(change);
|
|
1990
2496
|
}
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
style: S.button,
|
|
2030
|
-
onClick: function () { setLogOpen(!logOpen); }
|
|
2031
|
-
}, logOpen ? "▴" : "▾ " + (state.commits.length - logShown.length))
|
|
2032
|
-
]);
|
|
2033
|
-
}
|
|
2034
|
-
}
|
|
2035
|
-
bodyChildren.push(h("div", { key: "logTitle", style: S.sectionTitle, onClick: function () { setLogOpen(!logOpen); } }, (logOpen ? "▾ " : "▸ ") + t("log.title")));
|
|
2036
|
-
bodyChildren.push(h("div", { key: "logBody" }, logRows));
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
|
-
// last operation output
|
|
2040
|
-
if (state.lastResult) {
|
|
2041
|
-
bodyChildren.push(h("div", {
|
|
2042
|
-
key: "output",
|
|
2043
|
-
style: Object.assign({}, S.output, state.lastResult.kind === "ok" ? S.outputOk : S.outputError)
|
|
2044
|
-
}, state.lastResult.text || t("output.ok")));
|
|
2045
|
-
}
|
|
2046
|
-
if (busy) {
|
|
2047
|
-
bodyChildren.push(h("div", { key: "busy", style: S.meta }, busyLabel));
|
|
2497
|
+
}
|
|
2498
|
+
}, [
|
|
2499
|
+
h("span", { key: "path", style: S.changePath }, change.path),
|
|
2500
|
+
h("span", { key: "tag", style: changeTagStyle(change.status) }, t("status." + change.status))
|
|
2501
|
+
]);
|
|
2502
|
+
});
|
|
2503
|
+
bodyChildren.push(sectionView({
|
|
2504
|
+
hook: "changes",
|
|
2505
|
+
label: t("changes.title"),
|
|
2506
|
+
count: state.changes.length,
|
|
2507
|
+
open: changesOpen,
|
|
2508
|
+
all: changesAll,
|
|
2509
|
+
cap: CHANGES_PREVIEW,
|
|
2510
|
+
empty: "✓ " + t("changes.none"),
|
|
2511
|
+
rows: changesRows,
|
|
2512
|
+
onToggle: function () { setChangesOpen(!changesOpen); },
|
|
2513
|
+
onMore: function () { setChangesAll(!changesAll); }
|
|
2514
|
+
}, t));
|
|
2515
|
+
|
|
2516
|
+
// recent commits — same card, one row per commit
|
|
2517
|
+
var logRows = state.commits.map(function (commit, index) {
|
|
2518
|
+
return h("div", { key: index, style: S.logRow, title: commit.subject }, [
|
|
2519
|
+
h("span", { key: "sha", style: S.logSha }, commit.sha),
|
|
2520
|
+
h("span", { key: "subj", style: S.logSubject }, commit.subject)
|
|
2521
|
+
]);
|
|
2522
|
+
});
|
|
2523
|
+
bodyChildren.push(sectionView({
|
|
2524
|
+
hook: "log",
|
|
2525
|
+
label: t("log.title"),
|
|
2526
|
+
count: state.commits.length,
|
|
2527
|
+
open: logOpen,
|
|
2528
|
+
all: logAll,
|
|
2529
|
+
cap: LOG_PREVIEW,
|
|
2530
|
+
empty: t("log.none"),
|
|
2531
|
+
rows: logRows,
|
|
2532
|
+
onToggle: function () { setLogOpen(!logOpen); },
|
|
2533
|
+
onMore: function () { setLogAll(!logAll); }
|
|
2534
|
+
}, t));
|
|
2048
2535
|
}
|
|
2049
2536
|
|
|
2050
2537
|
// Everything the open diff depends on, as one comparable string: a
|
|
@@ -2212,7 +2699,7 @@ window.__ModuleLoader__.load({
|
|
|
2212
2699
|
maxWidth: 220,
|
|
2213
2700
|
padding: "1px 10px",
|
|
2214
2701
|
borderRadius: 999,
|
|
2215
|
-
border: "1px solid var(--dsw-alias-
|
|
2702
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.25))",
|
|
2216
2703
|
background: "var(--dsw-alias-bg-layer-1, rgba(127,127,127,0.12))",
|
|
2217
2704
|
color: "var(--dsw-alias-label-secondary, #c8ccd2)",
|
|
2218
2705
|
font: "inherit",
|
|
@@ -2279,7 +2766,13 @@ window.__ModuleLoader__.load({
|
|
|
2279
2766
|
parseDiffSide: parseDiffSide,
|
|
2280
2767
|
diffRowView: diffRowView,
|
|
2281
2768
|
GitDiffPane: GitDiffPane,
|
|
2282
|
-
DIFF_ROW: DIFF_ROW
|
|
2769
|
+
DIFF_ROW: DIFF_ROW,
|
|
2770
|
+
stripAnsiEscapes: stripAnsiEscapes,
|
|
2771
|
+
outputView: outputView,
|
|
2772
|
+
sectionView: sectionView,
|
|
2773
|
+
changeTagStyle: changeTagStyle,
|
|
2774
|
+
panelBoxStyle: panelBoxStyle,
|
|
2775
|
+
DIFF_PANEL_HEIGHT: DIFF_PANEL_HEIGHT
|
|
2283
2776
|
};
|
|
2284
2777
|
return module.exports;
|
|
2285
2778
|
}
|