@qzsy/dsh-git-panel 0.1.1 → 0.2.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.md +5 -2
- package/lib/client.js +529 -269
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
在右侧栏的引导页(`+` → 卡片)里多一张 **Git** 卡片,点开就是 Git 分区:
|
|
10
10
|
|
|
11
|
-
-
|
|
11
|
+
- **顶部就是提交框**:不再有独立工具条;仓库动作全部并进了分支图顶栏(见下)。
|
|
12
12
|
- **提交框**:提交信息输入框,`Ctrl/⌘+Enter` 直接提交;主按钮右侧的小箭头里有「提交(已暂存)」「提交全部改动」「修订上一次提交」「跳过校验钩子提交」「提交后推送」;右侧 `⋯` 里是「撤销上一次提交」「暂存改动」「恢复暂存的改动」。
|
|
13
13
|
- **CHANGES 分区**:分组列出 冲突 / 已暂存 / 未暂存 / 未跟踪,每行带**文件类型图标**(代码 / 配置 / 样式 / 文档 / 图片 / 音视频 / 字体 / 归档 / 脚本 / 锁文件 / 普通文件)与**改动类型图标**(修改 / 新增 / 未跟踪 / 删除 / 重命名 / 复制 / 类型变更 / 冲突,颜色与虚线圈区分暂存与否)、`+/-` 行数;悬停出现暂存、取消暂存、丢弃、看改动、打开文件五个动作;分区标题右侧是全部暂存 / 全部取消暂存 / 全部丢弃 / 预览全部改动。
|
|
14
|
-
- **GRAPH 分区**:泳道图(合并节点画空心圈)+ 分支/标签/远端徽标 + 摘要 + 作者 + 相对时间;点一行展开提交说明面板(作者、时间、hash、正文、文件清单与 `+/-`),并给出「查看这次提交的改动」「复制 hash
|
|
14
|
+
- **GRAPH 分区**:泳道图(合并节点画空心圈)+ 分支/标签/远端徽标 + 摘要 + 作者 + 相对时间;点一行展开提交说明面板(作者、时间、hash、正文、文件清单与 `+/-`),并给出「查看这次提交的改动」「复制 hash」「切换到这个分支」。
|
|
15
|
+
- **分支图顶栏**:左边是折叠热区(chevron + 「分支图」+ 计数),右边是仓库动作——当前分支胶囊(内含 `↑ahead / ↓behind`,点开向上弹出本地分支菜单)、「当前分支 / 全部引用」切换、抓取、同步、刷新。折叠热区只覆盖左侧标签簇,不会误触右侧按钮;面板窄于 300px 时自动隐藏「抓取」(同步已经包含 fetch)。
|
|
15
16
|
- **改动预览标签页**:点文件行 → 新标签显示这个文件的统一 diff(带双行号),标题就是这个文件的路径;未跟踪的新文件没有 index 版本,由 Host 直接合成「整篇新增」的补丁,所以看到的是全绿;「预览全部改动」把已暂存、未暂存、新文件拼成一张总 diff。
|
|
16
17
|
- **打开文件标签页**:文件行的「打开文件」走侧栏自己的 `openResource`(`dsh-resource://file/session/...`),落进任何文件链接都会用的那个预览器。
|
|
17
18
|
|
|
19
|
+
样式按「Apple 语义色 + 源码控制图布局」重做:表面/文字/分隔线全部取宿主的 `--dsw-alias-*` 语义 token,亮色与暗色主题自动跟随,不再假定深色;行高、计数、状态芯片、弹层和 diff 行号都按同一套 4/8pt 间距重排。
|
|
20
|
+
|
|
18
21
|
所有丢弃、撤销提交都要点一次确认才执行——不会一点就掉改动。「丢弃」的确认是面板底部的一次性浮层(不随列表滚走,Esc 或点空白处取消);确认后真的执行:已跟踪文件回到 HEAD,未跟踪与「已暂存的新文件」连内容一起删掉,列表随即刷新。
|
|
19
22
|
|
|
20
23
|
## 结构
|
package/lib/client.js
CHANGED
|
@@ -123,6 +123,7 @@ window.__ModuleLoader__.load({
|
|
|
123
123
|
'branch.behind': '落后 {count}',
|
|
124
124
|
'branch.switch': '切换分支',
|
|
125
125
|
'branch.current': '当前分支',
|
|
126
|
+
'branch.local': '本地分支',
|
|
126
127
|
'status.refresh': '刷新',
|
|
127
128
|
'status.fetch': '抓取',
|
|
128
129
|
'status.pull': '拉取',
|
|
@@ -159,7 +160,7 @@ window.__ModuleLoader__.load({
|
|
|
159
160
|
'confirm.discard': '丢弃',
|
|
160
161
|
'confirm.cancel': '取消',
|
|
161
162
|
'discard.done': '已丢弃 {count} 个文件的改动',
|
|
162
|
-
'commit.placeholder': '提交信息({mod}+↵
|
|
163
|
+
'commit.placeholder': '提交信息({mod}+↵ 提交)',
|
|
163
164
|
'commit.draft': '按当前改动生成提交信息草稿',
|
|
164
165
|
'commit.draft.one': '更新 {name}',
|
|
165
166
|
'commit.draft.many': '更新 {name} 等 {count} 个文件',
|
|
@@ -235,6 +236,7 @@ window.__ModuleLoader__.load({
|
|
|
235
236
|
'branch.behind': '{count} behind',
|
|
236
237
|
'branch.switch': 'Switch branch',
|
|
237
238
|
'branch.current': 'Current branch',
|
|
239
|
+
'branch.local': 'Local branches',
|
|
238
240
|
'status.refresh': 'Refresh',
|
|
239
241
|
'status.fetch': 'Fetch',
|
|
240
242
|
'status.pull': 'Pull',
|
|
@@ -271,7 +273,7 @@ window.__ModuleLoader__.load({
|
|
|
271
273
|
'confirm.discard': 'Discard',
|
|
272
274
|
'confirm.cancel': 'Cancel',
|
|
273
275
|
'discard.done': 'Discarded changes in {count} files',
|
|
274
|
-
'commit.placeholder': 'Message ({mod}+↵ to commit
|
|
276
|
+
'commit.placeholder': 'Message ({mod}+↵ to commit)',
|
|
275
277
|
'commit.draft': 'Draft a commit message from the changes',
|
|
276
278
|
'commit.draft.one': 'Update {name}',
|
|
277
279
|
'commit.draft.many': 'Update {name} and {count} more files',
|
|
@@ -325,181 +327,302 @@ window.__ModuleLoader__.load({
|
|
|
325
327
|
/* ------------------------------------------------------------------ */
|
|
326
328
|
|
|
327
329
|
const CSS = `
|
|
328
|
-
/*
|
|
330
|
+
/* ---------------------------------------------------------------------------
|
|
331
|
+
A Git pane in the Apple surface language, laid out like the Source Control
|
|
332
|
+
Graph: one header row per pane, a compact list, lane artwork on the left.
|
|
333
|
+
|
|
334
|
+
Colour comes from the host app's semantic tokens, so the panel follows the
|
|
335
|
+
app's light and dark themes; the value after each comma is the dark-theme
|
|
336
|
+
fallback used when the panel renders outside the app shell.
|
|
337
|
+
--------------------------------------------------------------------------- */
|
|
329
338
|
.gp-root{
|
|
330
339
|
--gp-font:-apple-system,BlinkMacSystemFont,"SF Pro Text","Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
|
|
331
340
|
--gp-mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,monospace;
|
|
332
|
-
|
|
333
|
-
--gp-
|
|
334
|
-
--gp-
|
|
335
|
-
--gp-
|
|
336
|
-
--gp-
|
|
337
|
-
--gp-
|
|
338
|
-
--gp-
|
|
339
|
-
--gp-
|
|
340
|
-
--gp-
|
|
341
|
-
--gp-
|
|
342
|
-
--gp-
|
|
343
|
-
--gp-
|
|
344
|
-
--gp-
|
|
345
|
-
|
|
346
|
-
--gp-
|
|
347
|
-
--gp-
|
|
348
|
-
--gp-
|
|
349
|
-
--gp-purple:#
|
|
350
|
-
--gp-
|
|
341
|
+
|
|
342
|
+
--gp-bg:var(--dsw-alias-bg-base,#151517);
|
|
343
|
+
--gp-surface:var(--dsw-alias-bg-layer-1,#1b1b1d);
|
|
344
|
+
--gp-surface-2:var(--dsw-alias-bg-layer-2,#202023);
|
|
345
|
+
--gp-surface-3:var(--dsw-alias-bg-layer-3,#26262a);
|
|
346
|
+
--gp-label:var(--dsw-alias-label-primary,#f5f5f7);
|
|
347
|
+
--gp-label-2:var(--dsw-alias-label-secondary,#c7c7cc);
|
|
348
|
+
--gp-label-3:var(--dsw-alias-label-tertiary,#8e8e93);
|
|
349
|
+
--gp-label-4:var(--dsw-alias-label-caption,#5f5f66);
|
|
350
|
+
--gp-separator:var(--dsw-alias-border-l2,rgba(255,255,255,.12));
|
|
351
|
+
--gp-hairline:var(--dsw-alias-border-l1,rgba(255,255,255,.07));
|
|
352
|
+
--gp-hover:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.07));
|
|
353
|
+
--gp-press:var(--dsw-alias-interactive-bg-active,rgba(255,255,255,.12));
|
|
354
|
+
|
|
355
|
+
--gp-green:var(--dsw-alias-state-success-primary,#30d158);
|
|
356
|
+
--gp-red:var(--dsw-alias-state-error-primary,#ff453a);
|
|
357
|
+
--gp-orange:#b26a00;
|
|
358
|
+
--gp-purple:#8b3fd1;
|
|
359
|
+
--gp-blue:#1f6feb;
|
|
360
|
+
--gp-code-add:var(--dsw-alias-code-diff-added,rgba(48,209,88,.14));
|
|
361
|
+
--gp-code-del:var(--dsw-alias-code-diff-deleted,rgba(255,69,58,.15));
|
|
362
|
+
|
|
363
|
+
--gp-file-code:#a87c1f;
|
|
364
|
+
--gp-file-data:#7a5cc4;
|
|
365
|
+
--gp-file-style:#1c7fa8;
|
|
366
|
+
--gp-file-doc:#3f74a8;
|
|
367
|
+
--gp-file-image:#9a55b8;
|
|
368
|
+
--gp-file-media:#b8457d;
|
|
369
|
+
--gp-file-font:#8a7420;
|
|
370
|
+
--gp-file-archive:#6b7079;
|
|
371
|
+
--gp-file-lock:#6b6c73;
|
|
372
|
+
--gp-file-shell:#2f7d4a;
|
|
373
|
+
|
|
374
|
+
--gp-accent:#2f6fb3;
|
|
375
|
+
--gp-accent-hover:#255d97;
|
|
376
|
+
--gp-accent-ink:#ffffff;
|
|
377
|
+
--gp-accent-soft:rgba(47,111,179,.12);
|
|
378
|
+
--gp-ring:0 0 0 3px rgba(47,111,179,.3);
|
|
379
|
+
|
|
380
|
+
--gp-radius-s:6px;
|
|
381
|
+
--gp-radius-m:9px;
|
|
382
|
+
--gp-radius-l:12px;
|
|
383
|
+
--gp-ease:cubic-bezier(.32,.72,0,1);
|
|
384
|
+
|
|
351
385
|
display:flex;flex-direction:column;height:100%;min-height:0;position:relative;
|
|
386
|
+
container-type:inline-size;
|
|
352
387
|
font-family:var(--gp-font);font-size:12.5px;line-height:1.45;letter-spacing:-.006em;
|
|
353
388
|
color:var(--gp-label);background:transparent;
|
|
354
389
|
-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
|
|
355
390
|
}
|
|
356
|
-
.gp-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
391
|
+
body[data-ds-dark-theme] .gp-root{
|
|
392
|
+
--gp-orange:#ffb340;
|
|
393
|
+
--gp-purple:#c08cff;
|
|
394
|
+
--gp-blue:#66a9ff;
|
|
395
|
+
--gp-file-code:#e0c07a;
|
|
396
|
+
--gp-file-data:#b9a3e3;
|
|
397
|
+
--gp-file-style:#6fc7e8;
|
|
398
|
+
--gp-file-doc:#7fa6cf;
|
|
399
|
+
--gp-file-image:#c08ed6;
|
|
400
|
+
--gp-file-media:#e08ab0;
|
|
401
|
+
--gp-file-font:#d8c98a;
|
|
402
|
+
--gp-file-archive:#9aa3ad;
|
|
403
|
+
--gp-file-lock:#a9a9b3;
|
|
404
|
+
--gp-file-shell:#8fd39a;
|
|
405
|
+
--gp-accent:#9dbcd8;
|
|
406
|
+
--gp-accent-hover:#b2cbe2;
|
|
407
|
+
--gp-accent-ink:#0d1319;
|
|
408
|
+
--gp-accent-soft:rgba(157,188,216,.16);
|
|
409
|
+
--gp-ring:0 0 0 3px rgba(157,188,216,.3);
|
|
410
|
+
}
|
|
411
|
+
.gp-root ::-webkit-scrollbar{width:9px;height:9px}
|
|
412
|
+
.gp-root ::-webkit-scrollbar-track{background:transparent}
|
|
413
|
+
.gp-root ::-webkit-scrollbar-thumb{background:var(--dsw-alias-scrollbar-bg-l2,#3b3b40);border-radius:999px;border:2px solid transparent;background-clip:content-box}
|
|
414
|
+
.gp-root ::-webkit-scrollbar-thumb:hover{background:var(--dsw-alias-scrollbar-hover-l2,#4a4a50);background-clip:content-box}
|
|
415
|
+
|
|
416
|
+
.gp-changes,.gp-graph-list,.gp-diff-body{overflow:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-scrollbar-bg-l2,#3b3b40) transparent}
|
|
417
|
+
|
|
418
|
+
/* --- review --------------------------------------------------------------- */
|
|
419
|
+
.gp-review{flex:none;display:flex;flex-direction:column;gap:6px;padding:8px 12px 6px}
|
|
420
|
+
.gp-review-head{display:flex;align-items:center;gap:6px;align-self:flex-start;font-size:10.5px;font-weight:650;letter-spacing:.05em;text-transform:uppercase;color:var(--gp-label-3);cursor:pointer;user-select:none;padding:2px 6px;margin-left:-6px;border-radius:var(--gp-radius-s);transition:background .12s var(--gp-ease),color .12s var(--gp-ease)}
|
|
421
|
+
.gp-review-head:hover{background:var(--gp-hover);color:var(--gp-label-2)}
|
|
422
|
+
.gp-review-head:focus-visible{outline:none;box-shadow:var(--gp-ring)}
|
|
423
|
+
.gp-review-btn{width:100%;height:28px;display:inline-flex;align-items:center;justify-content:center;gap:6px;border:.5px solid var(--gp-separator);border-radius:var(--gp-radius-s);background:transparent;color:var(--gp-label-2);font:inherit;font-size:12px;font-weight:560;cursor:pointer;transition:background .12s var(--gp-ease),color .12s var(--gp-ease),border-color .12s var(--gp-ease)}
|
|
424
|
+
.gp-review-btn:hover:not(:disabled){background:var(--gp-hover);border-color:var(--gp-label-4);color:var(--gp-label)}
|
|
425
|
+
.gp-review-btn:active:not(:disabled){transform:scale(.995)}
|
|
360
426
|
.gp-review-btn:disabled{opacity:.42;cursor:default}
|
|
361
|
-
.gp-review-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
.gp-
|
|
365
|
-
.gp-resizer:hover,.gp-resizer:active{background:var(--gp-fill-3)}
|
|
366
|
-
.gp-graph-panel{flex:none;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin}
|
|
367
|
-
.gp-toolbar{display:flex;align-items:center;gap:6px;flex:none;padding:8px 10px;border-bottom:.5px solid var(--gp-separator)}
|
|
368
|
-
.gp-toolbar .gp-spacer{flex:1}
|
|
369
|
-
.gp-branch{display:inline-flex;align-items:center;gap:5px;min-width:0;max-width:100%;height:22px;padding:0 8px;border-radius:6px;background:var(--gp-fill-3);color:var(--gp-label);font-family:var(--gp-mono);font-size:11px;letter-spacing:0;border:none;cursor:pointer;transition:background .12s ease-out}
|
|
370
|
-
.gp-branch:hover{background:var(--gp-fill-2)}
|
|
371
|
-
.gp-branch span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
372
|
-
.gp-tag{font-size:10.5px;line-height:16px;padding:0 6px;border-radius:999px;background:var(--gp-fill-3);color:var(--gp-label-2)}
|
|
373
|
-
.gp-btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;height:24px;min-width:24px;padding:0 6px;border-radius:6px;border:none;background:transparent;color:var(--gp-label-2);font:inherit;font-size:11.5px;cursor:pointer;transition:background .12s ease-out,color .12s ease-out}
|
|
374
|
-
.gp-btn:hover:not(:disabled){background:var(--gp-fill-3);color:var(--gp-label)}
|
|
375
|
-
.gp-btn:active:not(:disabled){background:var(--gp-fill-2)}
|
|
376
|
-
.gp-btn:disabled{opacity:.38;cursor:default}
|
|
377
|
-
.gp-btn svg{width:13px;height:13px;flex:none}
|
|
378
|
-
.gp-btn-danger:hover:not(:disabled){background:rgba(255,69,58,.18);color:var(--gp-red)}
|
|
379
|
-
.gp-primary{height:28px;padding:0 12px;border-radius:7px;border:none;background:var(--gp-accent);color:var(--gp-accent-ink);font:inherit;font-size:12.5px;font-weight:590;letter-spacing:-.01em;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:background .12s ease-out,transform .06s ease-out}
|
|
380
|
-
.gp-primary:hover:not(:disabled){background:var(--gp-accent-hover)}
|
|
381
|
-
.gp-primary:active:not(:disabled){transform:scale(.985)}
|
|
382
|
-
.gp-primary:disabled{background:var(--gp-accent-dim);color:var(--gp-label-3);cursor:default}
|
|
383
|
-
.gp-compose{flex:none;display:flex;flex-direction:column;gap:8px;padding:10px}
|
|
384
|
-
.gp-input{box-sizing:border-box;width:100%;min-height:36px;max-height:132px;resize:none;field-sizing:content;padding:8px 10px;border-radius:var(--gp-radius);border:.5px solid rgba(255,255,255,.07);background:#2e2e2e;color:rgba(255,255,255,.94);font:inherit;font-size:12.5px;line-height:1.45;outline:none;transition:border-color .12s ease-out,box-shadow .12s ease-out}
|
|
385
|
-
.gp-input:focus{border-color:var(--gp-accent);box-shadow:0 0 0 3.5px rgba(128,161,192,.28)}
|
|
386
|
-
.gp-input::placeholder{color:var(--gp-label-3)}
|
|
387
|
-
.gp-compose .gp-input-wrap{position:relative;display:flex}
|
|
388
|
-
.gp-input-wrap .gp-input{padding-right:34px}
|
|
389
|
-
.gp-input-ai{position:absolute;top:5px;right:5px;width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;border-radius:6px;background:transparent;color:var(--gp-label-3);cursor:pointer;transition:background .12s ease-out,color .12s ease-out}
|
|
390
|
-
.gp-input-ai:hover:not(:disabled){background:var(--gp-fill-3);color:var(--gp-label)}
|
|
391
|
-
.gp-input-ai:disabled{opacity:.35;cursor:default}
|
|
392
|
-
.gp-input-ai svg{width:14px;height:14px;flex:none}
|
|
393
|
-
.gp-actions{display:flex;align-items:center;gap:6px}
|
|
394
|
-
.gp-actions > .gp-primary{flex:1}
|
|
395
|
-
.gp-actions .gp-primary{width:100%;height:32px;justify-content:center;gap:6px;font-size:13px;border-radius:8px}
|
|
396
|
-
.gp-actions .gp-primary svg{width:14px;height:14px;flex:none}
|
|
397
|
-
.gp-actions .gp-primary-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
398
|
-
.gp-menu-wrap{position:relative}
|
|
399
|
-
.gp-menu{position:absolute;z-index:40;top:calc(100% + 6px);right:0;min-width:200px;padding:5px;border-radius:10px;border:.5px solid rgba(255,255,255,.1);background:rgba(40,40,44,.86);backdrop-filter:saturate(180%) blur(24px);-webkit-backdrop-filter:saturate(180%) blur(24px);box-shadow:0 12px 32px rgba(0,0,0,.48),0 0 0 .5px rgba(0,0,0,.5);display:flex;flex-direction:column;gap:1px}
|
|
400
|
-
.gp-menu button{display:flex;width:100%;align-items:center;gap:7px;height:28px;padding:0 8px;border:none;border-radius:6px;background:transparent;color:var(--gp-label);font:inherit;font-size:12.5px;text-align:left;cursor:pointer;transition:background .1s ease-out}
|
|
401
|
-
.gp-menu button:hover:not(:disabled){background:var(--gp-accent)}
|
|
402
|
-
.gp-menu button:hover:not(:disabled){color:var(--gp-accent-ink)}
|
|
403
|
-
.gp-menu button:disabled{opacity:.4;cursor:default}
|
|
427
|
+
.gp-review-btn svg{width:13px;height:13px;flex:none}
|
|
428
|
+
|
|
429
|
+
/* --- changes -------------------------------------------------------------- */
|
|
430
|
+
.gp-changes{flex:1;min-height:90px;padding:2px 0 6px}
|
|
404
431
|
.gp-section{flex:none}
|
|
405
|
-
.gp-section-head{display:flex;align-items:center;gap:
|
|
406
|
-
.gp-section-head
|
|
407
|
-
.gp-section-
|
|
408
|
-
.gp-section-
|
|
409
|
-
.gp-
|
|
432
|
+
.gp-section-head{display:flex;align-items:center;gap:2px;padding:0 6px;height:30px}
|
|
433
|
+
.gp-section-head-graph{height:34px;padding:0 6px 0 4px;border-top:.5px solid var(--gp-hairline);border-bottom:.5px solid var(--gp-hairline);gap:2px}
|
|
434
|
+
.gp-section-toggle{display:flex;align-items:center;gap:6px;min-width:0;height:24px;padding:0 5px;border-radius:var(--gp-radius-s);cursor:pointer;user-select:none;color:inherit;background:transparent;transition:background .12s var(--gp-ease)}
|
|
435
|
+
.gp-section-toggle:hover{background:var(--gp-hover)}
|
|
436
|
+
.gp-section-toggle:focus-visible{outline:none;box-shadow:var(--gp-ring)}
|
|
437
|
+
.gp-section-title{font-size:10.5px;font-weight:650;letter-spacing:.045em;text-transform:uppercase;color:var(--gp-label-3);white-space:nowrap}
|
|
438
|
+
.gp-section-count{min-width:17px;height:15px;padding:0 5px;border-radius:999px;background:var(--gp-hover);color:var(--gp-label-3);font-size:10px;font-weight:600;line-height:15px;text-align:center;font-variant-numeric:tabular-nums}
|
|
439
|
+
.gp-chevron{width:11px;height:11px;flex:none;color:var(--gp-label-3);transition:transform .2s var(--gp-ease)}
|
|
410
440
|
.gp-chevron.open{transform:rotate(90deg)}
|
|
411
|
-
.gp-section-actions{margin-left:auto;display:flex;align-items:center;gap:
|
|
412
|
-
|
|
413
|
-
.
|
|
414
|
-
.gp-
|
|
415
|
-
|
|
416
|
-
.
|
|
417
|
-
|
|
441
|
+
.gp-section-actions{position:relative;margin-left:auto;display:flex;align-items:center;gap:1px;flex:none}
|
|
442
|
+
/* The branch popover belongs to the header's action cluster, not to the chip:
|
|
443
|
+
anchored to the chip it would hang off the panel's left edge. */
|
|
444
|
+
.gp-section-actions .gp-menu-wrap{position:static}
|
|
445
|
+
/* A narrow pane gives the branch chip the room fetch would otherwise take;
|
|
446
|
+
sync already covers fetch + pull + push. */
|
|
447
|
+
@container (max-width: 300px){
|
|
448
|
+
.gp-section-head-graph .gp-btn-fetch{display:none}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.gp-row{display:flex;align-items:center;gap:8px;height:32px;margin:0 6px;padding:0 6px 0 8px;border-radius:7px;cursor:pointer;position:relative;transition:background .12s var(--gp-ease)}
|
|
452
|
+
.gp-row:hover{background:var(--gp-hover)}
|
|
453
|
+
.gp-row:active{background:var(--gp-press)}
|
|
454
|
+
.gp-row:focus-visible{outline:none;box-shadow:var(--gp-ring)}
|
|
455
|
+
.gp-row .gp-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;letter-spacing:-.01em}
|
|
418
456
|
.gp-row .gp-name .gp-dir{color:var(--gp-label-3)}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
aria-label carry the word. */
|
|
422
|
-
.gp-change{flex:none;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:5.5px;background:var(--gp-fill-4);border:.5px solid transparent;color:var(--gp-label-2);transition:background .12s ease-out}
|
|
423
|
-
.gp-change svg{width:12px;height:12px;flex:none}
|
|
424
|
-
.gp-change.M{color:var(--gp-orange);background:rgba(255,159,10,.13);border-color:rgba(255,159,10,.3)}
|
|
425
|
-
.gp-change.A{color:var(--gp-green);background:rgba(48,209,88,.13);border-color:rgba(48,209,88,.3)}
|
|
426
|
-
.gp-change.U{color:var(--gp-green);background:rgba(48,209,88,.06);border:.5px dashed rgba(48,209,88,.55)}
|
|
427
|
-
.gp-change.D{color:var(--gp-red);background:rgba(255,69,58,.13);border-color:rgba(255,69,58,.3)}
|
|
428
|
-
.gp-change.R,.gp-change.C{color:var(--gp-blue);background:rgba(10,132,255,.14);border-color:rgba(10,132,255,.32)}
|
|
429
|
-
.gp-change.T{color:var(--gp-purple);background:rgba(191,90,242,.14);border-color:rgba(191,90,242,.32)}
|
|
430
|
-
.gp-change.CONFLICT{color:var(--gp-red);background:rgba(255,69,58,.2);border-color:rgba(255,69,58,.45)}
|
|
431
|
-
.gp-ftype{flex:none;display:inline-flex;align-items:center;justify-content:center;width:16px;color:var(--gp-label-3)}
|
|
432
|
-
.gp-ftype svg{width:14px;height:14px;flex:none}
|
|
433
|
-
.gp-ftype.code{color:#e0c07a}
|
|
434
|
-
.gp-ftype.style{color:#6fc7e8}
|
|
435
|
-
.gp-ftype.doc{color:#7fa6cf}
|
|
436
|
-
.gp-ftype.image{color:#c08ed6}
|
|
437
|
-
.gp-ftype.media{color:#e08ab0}
|
|
438
|
-
.gp-ftype.font{color:#d8c98a}
|
|
439
|
-
.gp-ftype.archive{color:#9aa3ad}
|
|
440
|
-
.gp-ftype.lock{color:#a9a9b3}
|
|
441
|
-
.gp-ftype.data{color:#8fcfa0}
|
|
442
|
-
.gp-ftype.shell{color:#8fd39a}
|
|
443
|
-
.gp-row-conflict .gp-change{color:var(--gp-red)}
|
|
444
|
-
.gp-counts{display:flex;gap:5px;flex:none;font-size:10.5px;font-variant-numeric:tabular-nums;color:var(--gp-label-3);font-family:var(--gp-mono)}
|
|
457
|
+
.gp-row-conflict .gp-name{color:var(--gp-red)}
|
|
458
|
+
.gp-counts{display:flex;gap:6px;flex:none;font-size:10.5px;font-variant-numeric:tabular-nums;color:var(--gp-label-3);font-family:var(--gp-mono)}
|
|
445
459
|
.gp-add{color:var(--gp-green)}
|
|
446
460
|
.gp-del{color:var(--gp-red)}
|
|
447
461
|
.gp-row-actions{display:none;align-items:center;gap:1px;flex:none}
|
|
448
462
|
.gp-row:hover .gp-row-actions{display:flex}
|
|
449
463
|
.gp-row:hover .gp-counts{display:none}
|
|
450
464
|
.gp-row:hover .gp-change{display:none}
|
|
465
|
+
|
|
466
|
+
/* --- change-kind chips ---------------------------------------------------- */
|
|
467
|
+
.gp-change{flex:none;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:5.5px;background:var(--gp-hover);border:.5px solid transparent;color:var(--gp-label-2)}
|
|
468
|
+
.gp-change svg{width:12px;height:12px;flex:none}
|
|
469
|
+
.gp-change.M{color:var(--gp-orange);background:color-mix(in srgb,var(--gp-orange) 13%,transparent);border-color:color-mix(in srgb,var(--gp-orange) 28%,transparent)}
|
|
470
|
+
.gp-change.A{color:var(--gp-green);background:color-mix(in srgb,var(--gp-green) 13%,transparent);border-color:color-mix(in srgb,var(--gp-green) 28%,transparent)}
|
|
471
|
+
.gp-change.U{color:var(--gp-green);background:color-mix(in srgb,var(--gp-green) 6%,transparent);border:.5px dashed color-mix(in srgb,var(--gp-green) 52%,transparent)}
|
|
472
|
+
.gp-change.D{color:var(--gp-red);background:color-mix(in srgb,var(--gp-red) 13%,transparent);border-color:color-mix(in srgb,var(--gp-red) 28%,transparent)}
|
|
473
|
+
.gp-change.R,.gp-change.C{color:var(--gp-blue);background:color-mix(in srgb,var(--gp-blue) 13%,transparent);border-color:color-mix(in srgb,var(--gp-blue) 28%,transparent)}
|
|
474
|
+
.gp-change.T{color:var(--gp-purple);background:color-mix(in srgb,var(--gp-purple) 13%,transparent);border-color:color-mix(in srgb,var(--gp-purple) 28%,transparent)}
|
|
475
|
+
.gp-change.CONFLICT{color:var(--gp-red);background:color-mix(in srgb,var(--gp-red) 18%,transparent);border-color:color-mix(in srgb,var(--gp-red) 42%,transparent)}
|
|
476
|
+
|
|
477
|
+
/* --- file families -------------------------------------------------------- */
|
|
478
|
+
.gp-ftype{flex:none;display:inline-flex;align-items:center;justify-content:center;width:16px;color:var(--gp-label-4)}
|
|
479
|
+
.gp-ftype svg{width:14px;height:14px;flex:none}
|
|
480
|
+
.gp-ftype.code{color:var(--gp-file-code)}
|
|
481
|
+
.gp-ftype.data{color:var(--gp-file-data)}
|
|
482
|
+
.gp-ftype.style{color:var(--gp-file-style)}
|
|
483
|
+
.gp-ftype.doc{color:var(--gp-file-doc)}
|
|
484
|
+
.gp-ftype.image{color:var(--gp-file-image)}
|
|
485
|
+
.gp-ftype.media{color:var(--gp-file-media)}
|
|
486
|
+
.gp-ftype.font{color:var(--gp-file-font)}
|
|
487
|
+
.gp-ftype.archive{color:var(--gp-file-archive)}
|
|
488
|
+
.gp-ftype.lock{color:var(--gp-file-lock)}
|
|
489
|
+
.gp-ftype.shell{color:var(--gp-file-shell)}
|
|
490
|
+
|
|
491
|
+
/* --- buttons -------------------------------------------------------------- */
|
|
492
|
+
.gp-btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;height:24px;min-width:24px;padding:0 5px;border-radius:var(--gp-radius-s);border:none;background:transparent;color:var(--gp-label-3);font:inherit;font-size:11.5px;cursor:pointer;transition:background .12s var(--gp-ease),color .12s var(--gp-ease)}
|
|
493
|
+
.gp-btn:hover:not(:disabled){background:var(--gp-hover);color:var(--gp-label)}
|
|
494
|
+
.gp-btn:active:not(:disabled){background:var(--gp-press)}
|
|
495
|
+
.gp-btn:focus-visible{outline:none;box-shadow:var(--gp-ring)}
|
|
496
|
+
.gp-btn:disabled{opacity:.36;cursor:default}
|
|
497
|
+
.gp-btn svg{width:13px;height:13px;flex:none}
|
|
498
|
+
.gp-btn.active{background:var(--gp-accent-soft);color:var(--gp-accent)}
|
|
499
|
+
.gp-btn-danger:hover:not(:disabled){background:color-mix(in srgb,var(--gp-red) 16%,transparent);color:var(--gp-red)}
|
|
500
|
+
|
|
501
|
+
.gp-primary{height:30px;padding:0 12px;border-radius:8px;border:none;background:var(--gp-accent);color:var(--gp-accent-ink);font:inherit;font-size:12.5px;font-weight:600;letter-spacing:-.01em;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:6px;transition:background .12s var(--gp-ease),transform .08s var(--gp-ease)}
|
|
502
|
+
.gp-primary:hover:not(:disabled){background:var(--gp-accent-hover)}
|
|
503
|
+
.gp-primary:active:not(:disabled){transform:scale(.99)}
|
|
504
|
+
.gp-primary:focus-visible{outline:none;box-shadow:var(--gp-ring),0 0 0 1px var(--gp-bg)}
|
|
505
|
+
.gp-primary:disabled{background:color-mix(in srgb,var(--gp-label) 5%,transparent);color:var(--gp-label-4);cursor:default}
|
|
506
|
+
.gp-primary svg{width:14px;height:14px;flex:none}
|
|
507
|
+
|
|
508
|
+
/* --- commit box ----------------------------------------------------------- */
|
|
509
|
+
.gp-compose{flex:none;display:flex;flex-direction:column;gap:8px;padding:12px 12px 10px;border-bottom:.5px solid var(--gp-hairline)}
|
|
510
|
+
.gp-input{box-sizing:border-box;width:100%;min-height:38px;max-height:140px;resize:none;field-sizing:content;padding:9px 10px;border-radius:10px;border:.5px solid var(--gp-hairline);background:color-mix(in srgb,var(--gp-label) 4%,transparent);color:var(--gp-label);font:inherit;font-size:12.5px;line-height:1.45;outline:none;transition:border-color .12s var(--gp-ease),box-shadow .12s var(--gp-ease),background .12s var(--gp-ease)}
|
|
511
|
+
.gp-input::placeholder{color:var(--gp-label-4)}
|
|
512
|
+
.gp-input:hover{border-color:var(--gp-separator)}
|
|
513
|
+
.gp-input:focus{border-color:color-mix(in srgb,var(--gp-accent) 60%,transparent);box-shadow:var(--gp-ring);background:color-mix(in srgb,var(--gp-label) 7%,transparent)}
|
|
514
|
+
.gp-input-wrap{position:relative;display:flex}
|
|
515
|
+
.gp-input-wrap .gp-input{padding-right:34px}
|
|
516
|
+
.gp-input-ai{position:absolute;top:6px;right:6px;width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;border-radius:var(--gp-radius-s);background:transparent;color:var(--gp-label-4);cursor:pointer;transition:background .12s var(--gp-ease),color .12s var(--gp-ease)}
|
|
517
|
+
.gp-input-ai:hover:not(:disabled){background:var(--gp-hover);color:var(--gp-label-2)}
|
|
518
|
+
.gp-input-ai:focus-visible{outline:none;box-shadow:var(--gp-ring)}
|
|
519
|
+
.gp-input-ai:disabled{opacity:.32;cursor:default}
|
|
520
|
+
.gp-input-ai svg{width:14px;height:14px;flex:none}
|
|
521
|
+
.gp-actions{display:flex;align-items:center;gap:6px}
|
|
522
|
+
.gp-actions .gp-primary{width:100%}
|
|
523
|
+
.gp-primary-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
524
|
+
|
|
525
|
+
/* --- branch chip + popover menu ------------------------------------------- */
|
|
526
|
+
.gp-menu-wrap{position:relative;display:inline-flex;min-width:0}
|
|
527
|
+
.gp-branch{display:inline-flex;align-items:center;gap:5px;min-width:0;max-width:132px;height:24px;padding:0 8px;border-radius:7px;background:var(--gp-hover);border:.5px solid var(--gp-hairline);color:var(--gp-label);font-family:var(--gp-mono);font-size:11px;letter-spacing:0;cursor:pointer;transition:background .12s var(--gp-ease),border-color .12s var(--gp-ease)}
|
|
528
|
+
.gp-branch:hover{background:var(--gp-press)}
|
|
529
|
+
.gp-branch:focus-visible{outline:none;box-shadow:var(--gp-ring)}
|
|
530
|
+
.gp-branch svg{width:11px;height:11px;flex:none;color:var(--gp-label-3)}
|
|
531
|
+
.gp-branch-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
532
|
+
.gp-branch-meta{display:flex;gap:3px;flex:none;font-size:9.5px;color:var(--gp-label-3);font-variant-numeric:tabular-nums}
|
|
533
|
+
.gp-branch-meta .up{color:var(--gp-green)}
|
|
534
|
+
.gp-branch-meta .down{color:var(--gp-orange)}
|
|
535
|
+
.gp-branch-waiting{cursor:default;color:var(--gp-label-3)}
|
|
536
|
+
.gp-menu{position:absolute;z-index:50;top:calc(100% + 6px);right:0;min-width:216px;max-height:min(60vh,340px);overflow:auto;padding:5px;border-radius:11px;background:color-mix(in srgb,var(--gp-surface-3) 90%,transparent);backdrop-filter:saturate(180%) blur(30px);-webkit-backdrop-filter:saturate(180%) blur(30px);border:.5px solid var(--gp-hairline);box-shadow:0 18px 44px rgba(0,0,0,.42),0 0 0 .5px rgba(0,0,0,.18);animation:gp-pop .14s var(--gp-ease);display:flex;flex-direction:column;gap:1px}
|
|
537
|
+
.gp-menu.up{top:auto;bottom:calc(100% + 6px);animation-name:gp-pop-up}
|
|
538
|
+
.gp-menu-title{padding:4px 8px 3px;font-size:9.5px;font-weight:650;letter-spacing:.05em;text-transform:uppercase;color:var(--gp-label-4)}
|
|
539
|
+
.gp-menu button{display:flex;width:100%;align-items:center;gap:8px;height:28px;padding:0 8px;border:none;border-radius:var(--gp-radius-s);background:transparent;color:var(--gp-label);font:inherit;font-size:12px;text-align:left;cursor:pointer;transition:background .1s var(--gp-ease),color .1s var(--gp-ease)}
|
|
540
|
+
.gp-menu button:hover:not(:disabled){background:var(--gp-accent);color:var(--gp-accent-ink)}
|
|
541
|
+
.gp-menu button:hover:not(:disabled) svg{color:currentColor}
|
|
542
|
+
.gp-menu button:disabled{color:var(--gp-label-3);cursor:default}
|
|
543
|
+
.gp-menu button svg{width:12px;height:12px;flex:none;color:var(--gp-label-3)}
|
|
544
|
+
.gp-menu button span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
545
|
+
.gp-menu-check{margin-left:auto;display:inline-flex;color:var(--gp-accent)}
|
|
546
|
+
.gp-menu-check svg{color:currentColor}
|
|
547
|
+
|
|
548
|
+
/* --- graph ---------------------------------------------------------------- */
|
|
549
|
+
.gp-resizer{flex:none;position:relative;height:10px;margin:-3px 0;cursor:row-resize;background:transparent;user-select:none;touch-action:none}
|
|
550
|
+
.gp-resizer:after{content:"";position:absolute;left:50%;top:50%;width:38px;height:3px;margin:-1.5px 0 0 -19px;border-radius:2px;background:var(--gp-separator);opacity:0;transition:opacity .15s var(--gp-ease)}
|
|
551
|
+
.gp-resizer:hover:after,.gp-resizer:active:after{opacity:1}
|
|
552
|
+
.gp-graph-panel{flex:none;display:flex;flex-direction:column;min-height:0;position:relative;overflow:visible}
|
|
553
|
+
.gp-graph-list{flex:1;min-height:0;padding:2px 0 6px}
|
|
451
554
|
.gp-graph{position:relative}
|
|
452
|
-
.gp-graph-row{display:flex;align-items:center;gap:8px;min-height:38px;padding:0 8px 0
|
|
453
|
-
.gp-graph-row:hover{background:var(--gp-
|
|
454
|
-
.gp-graph-row
|
|
455
|
-
.gp-graph-
|
|
456
|
-
.gp-graph-
|
|
457
|
-
.gp-graph-row
|
|
555
|
+
.gp-graph-row{display:flex;align-items:center;gap:8px;min-height:38px;margin:0 6px;padding:0 8px 0 2px;border-radius:8px;cursor:pointer;position:relative;transition:background .12s var(--gp-ease)}
|
|
556
|
+
.gp-graph-row:hover{background:var(--gp-hover)}
|
|
557
|
+
.gp-graph-row:focus-visible{outline:none;box-shadow:var(--gp-ring)}
|
|
558
|
+
.gp-graph-row.selected{background:var(--gp-accent-soft);box-shadow:inset 2px 0 0 var(--gp-accent)}
|
|
559
|
+
.gp-graph-canvas{flex:none;display:block;overflow:visible;transition:filter .15s var(--gp-ease)}
|
|
560
|
+
.gp-graph-row:hover .gp-graph-canvas{filter:brightness(1.12)}
|
|
561
|
+
.gp-graph-row.selected .gp-graph-canvas{filter:brightness(1.2) saturate(1.05)}
|
|
458
562
|
.gp-graph-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
|
|
459
|
-
.gp-graph-subject{display:flex;align-items:center;gap:
|
|
460
|
-
.gp-graph-subject .gp-subject-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px}
|
|
461
|
-
.gp-ref{flex:none;max-width:
|
|
462
|
-
.gp-ref.head{background:
|
|
463
|
-
.gp-ref.tag{background:
|
|
464
|
-
.gp-ref.remote{background:
|
|
465
|
-
.gp-graph-meta{display:flex;align-items:center;gap:
|
|
466
|
-
.gp-graph-meta
|
|
467
|
-
.gp-
|
|
468
|
-
.gp-
|
|
469
|
-
.gp-
|
|
470
|
-
.gp-
|
|
471
|
-
.gp-detail-
|
|
472
|
-
.gp-
|
|
473
|
-
.gp-
|
|
563
|
+
.gp-graph-subject{display:flex;align-items:center;gap:5px;min-width:0}
|
|
564
|
+
.gp-graph-subject .gp-subject-text{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;letter-spacing:-.01em}
|
|
565
|
+
.gp-ref{flex:none;max-width:96px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;height:17px;padding:0 6px;border-radius:999px;background:var(--gp-hover);color:var(--gp-label-2);font-size:9.5px;font-weight:600;line-height:17px;letter-spacing:.01em}
|
|
566
|
+
.gp-ref.head{background:color-mix(in srgb,var(--gp-accent) 24%,transparent);color:var(--gp-accent)}
|
|
567
|
+
.gp-ref.tag{background:color-mix(in srgb,var(--gp-orange) 20%,transparent);color:var(--gp-orange)}
|
|
568
|
+
.gp-ref.remote{background:color-mix(in srgb,var(--gp-purple) 20%,transparent);color:var(--gp-purple)}
|
|
569
|
+
.gp-graph-meta{display:flex;align-items:center;gap:6px;min-width:0;overflow:hidden;white-space:nowrap;font-size:10.5px;color:var(--gp-label-4);font-variant-numeric:tabular-nums}
|
|
570
|
+
.gp-graph-meta > span{flex:none;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
571
|
+
.gp-graph-meta > span:not(.gp-hash){flex:0 1 auto}
|
|
572
|
+
.gp-graph-meta > span + span:before{content:"·";margin-right:6px;color:var(--gp-label-4)}
|
|
573
|
+
.gp-graph-meta .gp-hash{font-family:var(--gp-mono);font-size:10px;color:var(--gp-label-3);background:var(--gp-hover);border-radius:4px;padding:0 4px;line-height:15px}
|
|
574
|
+
.gp-detail{margin:2px 6px 8px;padding:10px 12px;border-radius:var(--gp-radius-m);background:color-mix(in srgb,var(--gp-label) 4%,transparent);border:.5px solid var(--gp-hairline);animation:gp-fade .16s var(--gp-ease)}
|
|
575
|
+
.gp-detail-body{white-space:pre-wrap;word-break:break-word;font-size:11.5px;color:var(--gp-label-2);margin:6px 0 8px}
|
|
576
|
+
.gp-kv{display:flex;gap:8px;font-size:11px;color:var(--gp-label-3);line-height:1.7}
|
|
577
|
+
.gp-kv b{font-weight:600;color:var(--gp-label-2)}
|
|
578
|
+
.gp-detail-actions{display:flex;gap:1px;margin:6px 0 0 -5px}
|
|
579
|
+
.gp-detail-files{margin-top:8px;padding-top:8px;border-top:.5px solid var(--gp-hairline);display:flex;flex-direction:column;gap:1px;max-height:168px;overflow:auto}
|
|
580
|
+
.gp-detail-file{display:flex;align-items:center;gap:7px;height:24px;padding:0 4px;border-radius:5px;font-size:11px;color:var(--gp-label-2)}
|
|
581
|
+
.gp-detail-file:hover{background:var(--gp-hover)}
|
|
474
582
|
.gp-detail-file .gp-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
.gp-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
.gp-
|
|
482
|
-
.gp-
|
|
483
|
-
.gp-
|
|
484
|
-
|
|
583
|
+
|
|
584
|
+
/* --- status note / error / toast ------------------------------------------ */
|
|
585
|
+
.gp-note{padding:18px 14px;color:var(--gp-label-3);font-size:11.5px;text-align:center;line-height:1.5}
|
|
586
|
+
.gp-error{margin:8px 12px 0;padding:9px 10px;border-radius:10px;background:color-mix(in srgb,var(--gp-red) 10%,transparent);border:.5px solid color-mix(in srgb,var(--gp-red) 24%,transparent);color:var(--gp-label);font-size:11.5px;display:flex;gap:8px;align-items:flex-start;animation:gp-fade .16s var(--gp-ease)}
|
|
587
|
+
.gp-error-icon{flex:none;display:inline-flex;margin-top:1px;color:var(--gp-red)}
|
|
588
|
+
.gp-error-icon svg{width:13px;height:13px}
|
|
589
|
+
.gp-error span.gp-error-text{flex:1;min-width:0;word-break:break-word;white-space:pre-wrap}
|
|
590
|
+
.gp-toast{margin:8px 12px 0;padding:8px 10px;border-radius:9px;background:var(--gp-hover);color:var(--gp-label-2);font-size:11.5px;display:flex;gap:7px;align-items:center;animation:gp-fade .16s var(--gp-ease)}
|
|
591
|
+
.gp-toast svg{width:13px;height:13px;flex:none;color:var(--gp-green)}
|
|
592
|
+
|
|
593
|
+
/* --- confirm overlay ------------------------------------------------------ */
|
|
594
|
+
.gp-confirm-scrim{position:absolute;inset:0;z-index:60;display:flex;align-items:flex-end;padding:10px;background:color-mix(in srgb,#000 36%,transparent);animation:gp-fade .16s var(--gp-ease)}
|
|
595
|
+
.gp-confirm{flex:1;padding:10px 12px;border-radius:var(--gp-radius-l);background:color-mix(in srgb,var(--gp-surface-3) 92%,transparent);backdrop-filter:saturate(180%) blur(28px);-webkit-backdrop-filter:saturate(180%) blur(28px);border:.5px solid var(--gp-hairline);box-shadow:0 18px 44px rgba(0,0,0,.45),0 0 0 .5px rgba(0,0,0,.2);font-size:11.5px;display:flex;gap:8px;align-items:center;animation:gp-rise .2s var(--gp-ease)}
|
|
596
|
+
.gp-confirm span{flex:1;min-width:0;line-height:1.5}
|
|
597
|
+
.gp-confirm .gp-primary{height:26px;padding:0 10px;font-size:11.5px;flex:none}
|
|
598
|
+
|
|
599
|
+
/* --- diff page ------------------------------------------------------------ */
|
|
485
600
|
.gp-diff{display:flex;flex-direction:column;height:100%;min-height:0;font-size:11.5px}
|
|
486
|
-
.gp-diff-head{display:flex;align-items:center;gap:
|
|
601
|
+
.gp-diff-head{display:flex;align-items:center;gap:8px;padding:0 12px;height:40px;flex:none;border-bottom:.5px solid var(--gp-hairline)}
|
|
487
602
|
.gp-diff-head .gp-path{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--gp-mono);font-size:11.5px}
|
|
488
|
-
.gp-diff-body{flex:1;min-height:0;
|
|
489
|
-
.gp-diff-line{display:flex;
|
|
490
|
-
.gp-diff-line
|
|
491
|
-
.gp-diff-line .gp-
|
|
492
|
-
.gp-diff-line .gp-
|
|
493
|
-
.gp-diff-line.
|
|
603
|
+
.gp-diff-body{flex:1;min-height:0;font-family:var(--gp-mono);font-size:11.5px;line-height:18px}
|
|
604
|
+
.gp-diff-line{display:flex;box-sizing:border-box;width:max-content;min-width:100%;white-space:pre}
|
|
605
|
+
.gp-diff-line.meta,.gp-diff-line.hunk{padding-left:10px}
|
|
606
|
+
.gp-diff-line.meta .gp-ln,.gp-diff-line.hunk .gp-ln{width:0;padding:0}
|
|
607
|
+
.gp-diff-line.meta .gp-sign,.gp-diff-line.hunk .gp-sign{width:0}
|
|
608
|
+
.gp-diff-line .gp-ln{flex:none;width:40px;padding-right:8px;text-align:right;color:var(--gp-label-4);user-select:none;font-variant-numeric:tabular-nums;font-size:10.5px}
|
|
609
|
+
.gp-diff-line .gp-sign{flex:none;width:14px;text-align:center}
|
|
610
|
+
.gp-diff-line .gp-code{flex:1;padding-right:16px;white-space:pre}
|
|
611
|
+
.gp-diff-line.add{background:var(--gp-code-add)}
|
|
494
612
|
.gp-diff-line.add .gp-sign{color:var(--gp-green)}
|
|
495
|
-
.gp-diff-line.del{background:
|
|
613
|
+
.gp-diff-line.del{background:var(--gp-code-del)}
|
|
496
614
|
.gp-diff-line.del .gp-sign{color:var(--gp-red)}
|
|
497
|
-
.gp-diff-line.hunk{background:var(--gp-
|
|
498
|
-
.gp-diff-line.meta{color:var(--gp-label-
|
|
499
|
-
|
|
615
|
+
.gp-diff-line.hunk{background:var(--gp-surface-2);color:var(--gp-label-3);box-shadow:inset 2px 0 0 color-mix(in srgb,var(--gp-blue) 45%,transparent)}
|
|
616
|
+
.gp-diff-line.meta{color:var(--gp-label-4)}
|
|
617
|
+
|
|
618
|
+
/* --- misc ----------------------------------------------------------------- */
|
|
619
|
+
.gp-tag{display:inline-flex;align-items:center;height:17px;padding:0 6px;border-radius:999px;background:var(--gp-hover);color:var(--gp-label-3);font-size:9.5px;font-weight:600}
|
|
500
620
|
.gp-spinner{display:inline-block;width:11px;height:11px;border:1.5px solid currentColor;border-right-color:transparent;border-radius:50%;animation:gp-spin .7s linear infinite}
|
|
501
621
|
@keyframes gp-spin{to{transform:rotate(360deg)}}
|
|
502
|
-
|
|
622
|
+
@keyframes gp-pop{from{opacity:0;transform:scale(.97) translateY(-3px)}to{opacity:1;transform:none}}
|
|
623
|
+
@keyframes gp-pop-up{from{opacity:0;transform:scale(.97) translateY(3px)}to{opacity:1;transform:none}}
|
|
624
|
+
@keyframes gp-rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
|
|
625
|
+
@keyframes gp-fade{from{opacity:0}to{opacity:1}}
|
|
503
626
|
`
|
|
504
627
|
|
|
505
628
|
/* ------------------------------------------------------------------ */
|
|
@@ -944,6 +1067,27 @@ window.__ModuleLoader__.load({
|
|
|
944
1067
|
|
|
945
1068
|
const ChevronIcon = (props) => h('svg', { ...iconProps, width: 12, height: 12, className: props?.className }, h('path', { d: 'M6 3.5 10.5 8 6 12.5' }))
|
|
946
1069
|
const RefreshIcon = () => h('svg', iconProps, h('path', { d: 'M13.5 8a5.5 5.5 0 1 1-1.9-4.2' }), h('path', { d: 'M13.5 2v3.2h-3.2' }))
|
|
1070
|
+
/** Two arrows around a circle: fetch + pull + push, one verb. */
|
|
1071
|
+
const SyncIcon = () =>
|
|
1072
|
+
h(
|
|
1073
|
+
'svg',
|
|
1074
|
+
glyphProps,
|
|
1075
|
+
h('path', { d: 'M2.6 8a5.4 5.4 0 0 1 9.2-3.8' }),
|
|
1076
|
+
h('path', { d: 'M13.4 8a5.4 5.4 0 0 1-9.2 3.8' }),
|
|
1077
|
+
h('path', { d: 'M12.4 1.6v2.9H9.5' }),
|
|
1078
|
+
h('path', { d: 'M3.6 14.4v-2.9h2.9' }),
|
|
1079
|
+
)
|
|
1080
|
+
/** The graph's refs filter: a lane that forks. */
|
|
1081
|
+
const GraphRefsIcon = () =>
|
|
1082
|
+
h(
|
|
1083
|
+
'svg',
|
|
1084
|
+
glyphProps,
|
|
1085
|
+
h('circle', { cx: 4, cy: 3.4, r: 1.5 }),
|
|
1086
|
+
h('circle', { cx: 4, cy: 12.6, r: 1.5 }),
|
|
1087
|
+
h('circle', { cx: 11.8, cy: 8, r: 1.5 }),
|
|
1088
|
+
h('path', { d: 'M4 4.9v6.2' }),
|
|
1089
|
+
h('path', { d: 'M5.3 7.1c2.5 0 3.5.4 5 1' }),
|
|
1090
|
+
)
|
|
947
1091
|
const PlusIcon = () => h('svg', iconProps, h('path', { d: 'M8 3v10M3 8h10' }))
|
|
948
1092
|
const MinusIcon = () => h('svg', iconProps, h('path', { d: 'M3 8h10' }))
|
|
949
1093
|
const UndoIcon = () => h('svg', iconProps, h('path', { d: 'M3 8a5 5 0 1 1 1.6 3.7' }), h('path', { d: 'M3 3.6V8h4.4' }))
|
|
@@ -1224,14 +1368,15 @@ window.__ModuleLoader__.load({
|
|
|
1224
1368
|
/* ------------------------------------------------------------------ */
|
|
1225
1369
|
|
|
1226
1370
|
/** A ghost icon button with an accessible label. */
|
|
1227
|
-
function IconButton({ label, onClick, disabled, danger, children }) {
|
|
1371
|
+
function IconButton({ label, onClick, disabled, danger, active, className, children }) {
|
|
1228
1372
|
return h(
|
|
1229
1373
|
'button',
|
|
1230
1374
|
{
|
|
1231
1375
|
type: 'button',
|
|
1232
|
-
className: `gp-btn${danger === true ? ' gp-btn-danger' : ''}`,
|
|
1376
|
+
className: `gp-btn${danger === true ? ' gp-btn-danger' : ''}${active === true ? ' active' : ''}${typeof className === 'string' ? ` ${className}` : ''}`,
|
|
1233
1377
|
title: label,
|
|
1234
1378
|
'aria-label': label,
|
|
1379
|
+
'aria-pressed': active === undefined ? undefined : active === true,
|
|
1235
1380
|
disabled: disabled === true,
|
|
1236
1381
|
onClick: (event) => {
|
|
1237
1382
|
event.stopPropagation()
|
|
@@ -1242,15 +1387,40 @@ window.__ModuleLoader__.load({
|
|
|
1242
1387
|
)
|
|
1243
1388
|
}
|
|
1244
1389
|
|
|
1245
|
-
/**
|
|
1246
|
-
|
|
1390
|
+
/**
|
|
1391
|
+
* One collapsible section header.
|
|
1392
|
+
*
|
|
1393
|
+
* The collapse hit area is the label cluster only (chevron, title, count),
|
|
1394
|
+
* not the whole row: the right side now hosts real controls — the branch
|
|
1395
|
+
* menu and the repo verbs on the graph header — and a full-row toggle
|
|
1396
|
+
* would fire when the user aimed at them.
|
|
1397
|
+
*/
|
|
1398
|
+
function SectionHeader({ title, count, expanded, onToggle, actions, t, variant }) {
|
|
1399
|
+
const label = expanded === true ? t('changes.collapse') : t('changes.expand')
|
|
1247
1400
|
return h(
|
|
1248
1401
|
'div',
|
|
1249
|
-
{ className:
|
|
1250
|
-
h(
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1402
|
+
{ className: `gp-section-head${variant === 'graph' ? ' gp-section-head-graph' : ''}` },
|
|
1403
|
+
h(
|
|
1404
|
+
'div',
|
|
1405
|
+
{
|
|
1406
|
+
className: 'gp-section-toggle',
|
|
1407
|
+
role: 'button',
|
|
1408
|
+
tabIndex: 0,
|
|
1409
|
+
'aria-expanded': expanded === true,
|
|
1410
|
+
title: label,
|
|
1411
|
+
onClick: onToggle,
|
|
1412
|
+
onKeyDown: (event) => {
|
|
1413
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
1414
|
+
event.preventDefault()
|
|
1415
|
+
onToggle?.()
|
|
1416
|
+
}
|
|
1417
|
+
},
|
|
1418
|
+
},
|
|
1419
|
+
h(ChevronIcon, { className: `gp-chevron${expanded ? ' open' : ''}` }),
|
|
1420
|
+
h('span', { className: 'gp-section-title' }, title),
|
|
1421
|
+
count === undefined ? null : h('span', { className: 'gp-section-count' }, String(count)),
|
|
1422
|
+
),
|
|
1423
|
+
h('div', { className: 'gp-section-actions' }, actions),
|
|
1254
1424
|
)
|
|
1255
1425
|
}
|
|
1256
1426
|
|
|
@@ -1279,7 +1449,12 @@ window.__ModuleLoader__.load({
|
|
|
1279
1449
|
h(FileTypeIcon, { path: entry.path }),
|
|
1280
1450
|
h('span', { className: 'gp-name' }, dir === '' ? null : h('span', { className: 'gp-dir' }, dir), name),
|
|
1281
1451
|
showCounts
|
|
1282
|
-
? h(
|
|
1452
|
+
? h(
|
|
1453
|
+
'span',
|
|
1454
|
+
{ className: 'gp-counts' },
|
|
1455
|
+
entry.additions > 0 ? h('span', { className: 'gp-add' }, `+${entry.additions}`) : null,
|
|
1456
|
+
entry.deletions > 0 ? h('span', { className: 'gp-del' }, `-${entry.deletions}`) : null,
|
|
1457
|
+
)
|
|
1283
1458
|
: null,
|
|
1284
1459
|
h(ChangeBadge, { entry, t, group }),
|
|
1285
1460
|
h(
|
|
@@ -1855,6 +2030,13 @@ window.__ModuleLoader__.load({
|
|
|
1855
2030
|
|
|
1856
2031
|
const sectionToggle = (name) => () => setExpanded((current) => ({ ...current, [name]: current[name] !== true }))
|
|
1857
2032
|
|
|
2033
|
+
/** One refresh verb for the whole pane: status and history together. */
|
|
2034
|
+
const refreshAll = useCallback(() => {
|
|
2035
|
+
setStatusVersion((value2) => value2 + 1)
|
|
2036
|
+
setGraphVersion((value2) => value2 + 1)
|
|
2037
|
+
status.refresh?.(true)
|
|
2038
|
+
}, [status])
|
|
2039
|
+
|
|
1858
2040
|
if (cwd === undefined) {
|
|
1859
2041
|
return h('div', { className: 'gp-root' }, h('div', { className: 'gp-note' }, t('empty.noRepo')))
|
|
1860
2042
|
}
|
|
@@ -1874,35 +2056,16 @@ window.__ModuleLoader__.load({
|
|
|
1874
2056
|
return h(
|
|
1875
2057
|
'div',
|
|
1876
2058
|
{ className: 'gp-root' },
|
|
1877
|
-
h(
|
|
1878
|
-
'div',
|
|
1879
|
-
{ className: 'gp-toolbar' },
|
|
1880
|
-
repo === undefined
|
|
1881
|
-
? h('span', { className: 'gp-branch' }, h('span', { className: 'gp-spinner' }), h('span', null, t('empty.loading')))
|
|
1882
|
-
: h(
|
|
1883
|
-
'div',
|
|
1884
|
-
{ className: 'gp-menu-wrap' },
|
|
1885
|
-
h(BranchMenu, { t, repo, busy, onCheckout: (name) => run('checkout', { name }) }),
|
|
1886
|
-
),
|
|
1887
|
-
repo !== undefined && repo.detached === true ? h('span', { className: 'gp-tag' }, t('branch.detached')) : null,
|
|
1888
|
-
repo !== undefined && repo.ahead > 0 ? h('span', { className: 'gp-tag' }, fill(t('branch.ahead'), { count: repo.ahead })) : null,
|
|
1889
|
-
repo !== undefined && repo.behind > 0 ? h('span', { className: 'gp-tag' }, fill(t('branch.behind'), { count: repo.behind })) : null,
|
|
1890
|
-
h('span', { className: 'gp-spacer' }),
|
|
1891
|
-
repo === undefined
|
|
1892
|
-
? null
|
|
1893
|
-
: h(IconButton, { label: t('status.fetch'), disabled: busy, onClick: () => run('network', { mode: 'fetch' }, { graph: false }) }, h(CloudIcon, null)),
|
|
1894
|
-
repo === undefined ? null : h(IconButton, { label: t('status.sync'), disabled: busy, onClick: () => run('network', { mode: 'sync' }, { graph: false }) }, h(RefreshIcon, null)),
|
|
1895
|
-
h(IconButton, { label: t('status.refresh'), disabled: busy, onClick: () => status.refresh?.(true) }, h(RefreshIcon, null)),
|
|
1896
|
-
),
|
|
1897
2059
|
error === undefined
|
|
1898
2060
|
? null
|
|
1899
2061
|
: h(
|
|
1900
2062
|
'div',
|
|
1901
|
-
{ className: 'gp-error' },
|
|
1902
|
-
h('span',
|
|
2063
|
+
{ className: 'gp-error', role: 'alert' },
|
|
2064
|
+
h('span', { className: 'gp-error-icon' }, h(ChangeConflictIcon, null)),
|
|
2065
|
+
h('span', { className: 'gp-error-text' }, `${t('error.title')}:${error.message}`),
|
|
1903
2066
|
h(IconButton, { label: t('error.dismiss'), onClick: () => setError(undefined) }, h('span', { 'aria-hidden': 'true' }, '✕')),
|
|
1904
2067
|
),
|
|
1905
|
-
notice === undefined ? null : h('div', { className: 'gp-
|
|
2068
|
+
notice === undefined ? null : h('div', { className: 'gp-toast', role: 'status' }, h(CheckIcon, null), h('span', null, notice)),
|
|
1906
2069
|
h(CommitBox, {
|
|
1907
2070
|
t,
|
|
1908
2071
|
status: repo,
|
|
@@ -1912,7 +2075,9 @@ window.__ModuleLoader__.load({
|
|
|
1912
2075
|
h(
|
|
1913
2076
|
'div',
|
|
1914
2077
|
{ className: 'gp-changes' },
|
|
1915
|
-
|
|
2078
|
+
repo === undefined
|
|
2079
|
+
? h('div', { className: 'gp-note' }, t('empty.loading'))
|
|
2080
|
+
: sections.map((section) =>
|
|
1916
2081
|
h(
|
|
1917
2082
|
'div',
|
|
1918
2083
|
{ className: 'gp-section', key: section.id },
|
|
@@ -1967,7 +2132,7 @@ window.__ModuleLoader__.load({
|
|
|
1967
2132
|
className: 'gp-review-btn',
|
|
1968
2133
|
disabled: busy || (repo?.staged?.length ?? 0) + (repo?.changes?.length ?? 0) + (repo?.untracked?.length ?? 0) === 0,
|
|
1969
2134
|
onClick: findIssues,
|
|
1970
|
-
}, t('review.find')),
|
|
2135
|
+
}, h(SparkleIcon, null), t('review.find')),
|
|
1971
2136
|
),
|
|
1972
2137
|
expanded.graph === false
|
|
1973
2138
|
? null
|
|
@@ -1984,91 +2149,127 @@ window.__ModuleLoader__.load({
|
|
|
1984
2149
|
className: 'gp-graph-panel',
|
|
1985
2150
|
style: expanded.graph === false ? null : { height: `${graphHeight}px` },
|
|
1986
2151
|
},
|
|
1987
|
-
h(
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2152
|
+
h(SectionHeader, {
|
|
2153
|
+
t,
|
|
2154
|
+
variant: 'graph',
|
|
2155
|
+
title: t('graph.title'),
|
|
2156
|
+
count: graphRows.rows.length,
|
|
2157
|
+
expanded: expanded.graph,
|
|
2158
|
+
onToggle: sectionToggle('graph'),
|
|
2159
|
+
actions: [
|
|
2160
|
+
repo === undefined
|
|
2161
|
+
? h('span', { className: 'gp-branch gp-branch-waiting', key: 'branch' }, h('span', { className: 'gp-spinner' }), h('span', { className: 'gp-branch-name' }, t('empty.loading')))
|
|
2162
|
+
: h(BranchMenu, {
|
|
2163
|
+
key: 'branch',
|
|
2164
|
+
t,
|
|
2165
|
+
repo,
|
|
2166
|
+
busy,
|
|
2167
|
+
placement: 'up',
|
|
2168
|
+
onCheckout: (name) => run('checkout', { name }),
|
|
2169
|
+
}),
|
|
2170
|
+
h(
|
|
2171
|
+
IconButton,
|
|
2172
|
+
{
|
|
2173
|
+
key: 'refs',
|
|
2174
|
+
label: graphAll ? t('graph.head') : t('graph.all'),
|
|
2175
|
+
active: graphAll,
|
|
2176
|
+
onClick: () => setGraphAll((value2) => !value2),
|
|
2177
|
+
},
|
|
2178
|
+
h(GraphRefsIcon, null),
|
|
2179
|
+
),
|
|
2180
|
+
repo === undefined
|
|
2181
|
+
? null
|
|
2182
|
+
: h(
|
|
2183
|
+
IconButton,
|
|
2184
|
+
{
|
|
2185
|
+
key: 'fetch',
|
|
2186
|
+
label: t('status.fetch'),
|
|
2187
|
+
className: 'gp-btn-fetch',
|
|
2188
|
+
disabled: busy,
|
|
2189
|
+
onClick: () => run('network', { mode: 'fetch' }, { graph: false }),
|
|
2190
|
+
},
|
|
2191
|
+
h(CloudIcon, null),
|
|
2192
|
+
),
|
|
2193
|
+
repo === undefined
|
|
2194
|
+
? null
|
|
2195
|
+
: h(IconButton, { key: 'sync', label: t('status.sync'), disabled: busy, onClick: () => run('network', { mode: 'sync' }, { graph: false }) }, h(SyncIcon, null)),
|
|
2196
|
+
h(IconButton, { key: 'refresh', label: t('status.refresh'), disabled: busy, onClick: refreshAll }, h(RefreshIcon, null)),
|
|
2197
|
+
],
|
|
2198
|
+
}),
|
|
2199
|
+
expanded.graph !== true
|
|
2200
|
+
? null
|
|
2201
|
+
: h(
|
|
2202
|
+
'div',
|
|
2203
|
+
{ className: 'gp-graph-list' },
|
|
2204
|
+
graph.loading === true && graphRows.rows.length === 0
|
|
2205
|
+
? h('div', { className: 'gp-note' }, t('graph.loading'))
|
|
2206
|
+
: graphRows.rows.length === 0
|
|
2207
|
+
? h('div', { className: 'gp-note' }, t('graph.empty'))
|
|
2208
|
+
: h(
|
|
2209
|
+
'div',
|
|
2210
|
+
{ className: 'gp-graph' },
|
|
2211
|
+
graphRows.rows.map((row) =>
|
|
2014
2212
|
h(
|
|
2015
|
-
|
|
2016
|
-
{
|
|
2017
|
-
className: `gp-graph-row${selected === row.commit.hash ? ' selected' : ''}`,
|
|
2018
|
-
onClick: () => selectCommit(row.commit),
|
|
2019
|
-
role: 'button',
|
|
2020
|
-
tabIndex: 0,
|
|
2021
|
-
onKeyDown: (event) => (event.key === 'Enter' ? selectCommit(row.commit) : undefined),
|
|
2022
|
-
},
|
|
2023
|
-
h(GraphCanvas, { row, laneCount: graphRows.laneCount }),
|
|
2213
|
+
React.Fragment,
|
|
2214
|
+
{ key: row.commit.hash },
|
|
2024
2215
|
h(
|
|
2025
2216
|
'div',
|
|
2026
|
-
{
|
|
2217
|
+
{
|
|
2218
|
+
className: `gp-graph-row${selected === row.commit.hash ? ' selected' : ''}`,
|
|
2219
|
+
onClick: () => selectCommit(row.commit),
|
|
2220
|
+
role: 'button',
|
|
2221
|
+
tabIndex: 0,
|
|
2222
|
+
onKeyDown: (event) => (event.key === 'Enter' ? selectCommit(row.commit) : undefined),
|
|
2223
|
+
},
|
|
2224
|
+
h(GraphCanvas, { row, laneCount: graphRows.laneCount }),
|
|
2027
2225
|
h(
|
|
2028
2226
|
'div',
|
|
2029
|
-
{ className: 'gp-graph-
|
|
2030
|
-
h(
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
h(
|
|
2037
|
-
|
|
2038
|
-
|
|
2227
|
+
{ className: 'gp-graph-main' },
|
|
2228
|
+
h(
|
|
2229
|
+
'div',
|
|
2230
|
+
{ className: 'gp-graph-subject' },
|
|
2231
|
+
h(Refs, { refs: row.commit.refs, t }),
|
|
2232
|
+
h('span', { className: 'gp-subject-text', title: row.commit.subject }, row.commit.subject === '' ? '(无提交说明)' : row.commit.subject),
|
|
2233
|
+
),
|
|
2234
|
+
h(
|
|
2235
|
+
'div',
|
|
2236
|
+
{ className: 'gp-graph-meta' },
|
|
2237
|
+
h('span', { className: 'gp-hash' }, row.commit.short),
|
|
2238
|
+
h('span', null, row.commit.author),
|
|
2239
|
+
h('span', null, relativeTime(row.commit.at)),
|
|
2240
|
+
),
|
|
2039
2241
|
),
|
|
2040
2242
|
),
|
|
2243
|
+
selected === row.commit.hash
|
|
2244
|
+
? h(CommitDetail, {
|
|
2245
|
+
t,
|
|
2246
|
+
detail,
|
|
2247
|
+
loading: detailLoading,
|
|
2248
|
+
copied,
|
|
2249
|
+
onOpenDiff: (commit) => tabActions?.openTab(DIFF_KIND, { params: { hash: commit.hash, cwd: repo?.cwd } }),
|
|
2250
|
+
onCopy: (commit) => {
|
|
2251
|
+
try {
|
|
2252
|
+
navigator.clipboard?.writeText?.(commit.hash)
|
|
2253
|
+
} catch {}
|
|
2254
|
+
setCopied(true)
|
|
2255
|
+
setTimeout(() => setCopied(false), 1600)
|
|
2256
|
+
},
|
|
2257
|
+
onCheckout:
|
|
2258
|
+
row.commit.refs.some((ref) => ref.kind === 'branch' || ref.kind === 'head')
|
|
2259
|
+
? () => {
|
|
2260
|
+
const ref = row.commit.refs.find((entry) => entry.kind === 'branch')
|
|
2261
|
+
if (ref !== undefined) run('checkout', { name: ref.name })
|
|
2262
|
+
}
|
|
2263
|
+
: undefined,
|
|
2264
|
+
})
|
|
2265
|
+
: null,
|
|
2041
2266
|
),
|
|
2042
|
-
selected === row.commit.hash
|
|
2043
|
-
? h(CommitDetail, {
|
|
2044
|
-
t,
|
|
2045
|
-
detail,
|
|
2046
|
-
loading: detailLoading,
|
|
2047
|
-
copied,
|
|
2048
|
-
onOpenDiff: (commit) => tabActions?.openTab(DIFF_KIND, { params: { hash: commit.hash, cwd: repo?.cwd } }),
|
|
2049
|
-
onCopy: (commit) => {
|
|
2050
|
-
try {
|
|
2051
|
-
navigator.clipboard?.writeText?.(commit.hash)
|
|
2052
|
-
} catch {}
|
|
2053
|
-
setCopied(true)
|
|
2054
|
-
setTimeout(() => setCopied(false), 1600)
|
|
2055
|
-
},
|
|
2056
|
-
onCheckout:
|
|
2057
|
-
row.commit.refs.some((ref) => ref.kind === 'branch' || ref.kind === 'head')
|
|
2058
|
-
? () => {
|
|
2059
|
-
const ref = row.commit.refs.find((entry) => entry.kind === 'branch')
|
|
2060
|
-
if (ref !== undefined) run('checkout', { name: ref.name })
|
|
2061
|
-
}
|
|
2062
|
-
: undefined,
|
|
2063
|
-
})
|
|
2064
|
-
: null,
|
|
2065
2267
|
),
|
|
2268
|
+
graph.value?.truncated === true ? h('div', { className: 'gp-note' }, fill(t('graph.truncated'), { count: graphRows.rows.length })) : null,
|
|
2066
2269
|
),
|
|
2067
|
-
|
|
2068
|
-
),
|
|
2069
|
-
),
|
|
2270
|
+
),
|
|
2070
2271
|
),
|
|
2071
|
-
|
|
2272
|
+
confirming === undefined
|
|
2072
2273
|
? null
|
|
2073
2274
|
: h(ConfirmBar, {
|
|
2074
2275
|
t,
|
|
@@ -2154,11 +2355,37 @@ window.__ModuleLoader__.load({
|
|
|
2154
2355
|
)
|
|
2155
2356
|
}
|
|
2156
2357
|
|
|
2157
|
-
/**
|
|
2158
|
-
|
|
2358
|
+
/**
|
|
2359
|
+
* What the branch chip says: the checked-out name (or the short hash when
|
|
2360
|
+
* detached) plus the divergence the old toolbar used to show as three
|
|
2361
|
+
* separate tags.
|
|
2362
|
+
* @param {object} repo - status payload.
|
|
2363
|
+
* @returns {{label: string, detached: boolean, ahead: number, behind: number}} chip facts.
|
|
2364
|
+
*/
|
|
2365
|
+
function branchSummaryOf(repo) {
|
|
2366
|
+
const detached = repo?.detached === true
|
|
2367
|
+
const raw = detached ? String(repo?.oid ?? '') : String(repo?.branch ?? '')
|
|
2368
|
+
const label = detached ? raw.slice(0, 7) || 'HEAD' : raw
|
|
2369
|
+
return {
|
|
2370
|
+
label,
|
|
2371
|
+
detached,
|
|
2372
|
+
ahead: Number(repo?.ahead) || 0,
|
|
2373
|
+
behind: Number(repo?.behind) || 0,
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
/**
|
|
2378
|
+
* The branch chip and its checkout menu.
|
|
2379
|
+
*
|
|
2380
|
+
* It lives in the graph header now, which sits at the bottom of the pane,
|
|
2381
|
+
* so the menu opens upward (`placement="up"`); a downward popover would be
|
|
2382
|
+
* clipped by the sidebar.
|
|
2383
|
+
*/
|
|
2384
|
+
function BranchMenu({ t, repo, busy, onCheckout, placement }) {
|
|
2159
2385
|
const [open, setOpen] = useState(false)
|
|
2160
2386
|
const [branches, setBranches] = useState(undefined)
|
|
2161
2387
|
const ref = useRef(null)
|
|
2388
|
+
const summary = branchSummaryOf(repo)
|
|
2162
2389
|
|
|
2163
2390
|
useEffect(() => {
|
|
2164
2391
|
if (!open || branches !== undefined) return undefined
|
|
@@ -2173,24 +2400,54 @@ window.__ModuleLoader__.load({
|
|
|
2173
2400
|
const onDown = (event) => {
|
|
2174
2401
|
if (ref.current !== null && !ref.current.contains(event.target)) setOpen(false)
|
|
2175
2402
|
}
|
|
2403
|
+
const onKey = (event) => {
|
|
2404
|
+
if (event.key === 'Escape') setOpen(false)
|
|
2405
|
+
}
|
|
2176
2406
|
document.addEventListener('mousedown', onDown)
|
|
2177
|
-
|
|
2407
|
+
document.addEventListener('keydown', onKey)
|
|
2408
|
+
return () => {
|
|
2409
|
+
document.removeEventListener('mousedown', onDown)
|
|
2410
|
+
document.removeEventListener('keydown', onKey)
|
|
2411
|
+
}
|
|
2178
2412
|
}, [open])
|
|
2179
2413
|
|
|
2180
|
-
const
|
|
2414
|
+
const diverged = summary.ahead + summary.behind
|
|
2415
|
+
const detail = [
|
|
2416
|
+
`${t('branch.current')}: ${summary.label}`,
|
|
2417
|
+
summary.detached ? t('branch.detached') : '',
|
|
2418
|
+
summary.ahead > 0 ? fill(t('branch.ahead'), { count: summary.ahead }) : '',
|
|
2419
|
+
summary.behind > 0 ? fill(t('branch.behind'), { count: summary.behind }) : '',
|
|
2420
|
+
].filter((part) => part !== '').join(' · ')
|
|
2421
|
+
|
|
2181
2422
|
return h(
|
|
2182
2423
|
'div',
|
|
2183
2424
|
{ className: 'gp-menu-wrap', ref },
|
|
2184
2425
|
h(
|
|
2185
2426
|
'button',
|
|
2186
|
-
{
|
|
2187
|
-
|
|
2188
|
-
|
|
2427
|
+
{
|
|
2428
|
+
type: 'button',
|
|
2429
|
+
className: 'gp-branch',
|
|
2430
|
+
title: detail,
|
|
2431
|
+
'aria-haspopup': 'menu',
|
|
2432
|
+
'aria-expanded': open === true,
|
|
2433
|
+
onClick: () => setOpen((value) => !value),
|
|
2434
|
+
},
|
|
2435
|
+
h(BranchIcon, { size: 11 }),
|
|
2436
|
+
h('span', { className: 'gp-branch-name' }, summary.label),
|
|
2437
|
+
summary.detached === true || diverged === 0
|
|
2438
|
+
? null
|
|
2439
|
+
: h(
|
|
2440
|
+
'span',
|
|
2441
|
+
{ className: 'gp-branch-meta' },
|
|
2442
|
+
summary.ahead > 0 ? h('span', { className: 'up' }, `↑${summary.ahead}`) : null,
|
|
2443
|
+
summary.behind > 0 ? h('span', { className: 'down' }, `↓${summary.behind}`) : null,
|
|
2444
|
+
),
|
|
2189
2445
|
),
|
|
2190
2446
|
open
|
|
2191
2447
|
? h(
|
|
2192
2448
|
'div',
|
|
2193
|
-
{ className:
|
|
2449
|
+
{ className: `gp-menu${placement === 'up' ? ' up' : ''}`, role: 'menu' },
|
|
2450
|
+
h('div', { className: 'gp-menu-title' }, t('branch.local')),
|
|
2194
2451
|
(branches?.local ?? []).map((branch) =>
|
|
2195
2452
|
h(
|
|
2196
2453
|
'button',
|
|
@@ -2206,10 +2463,10 @@ window.__ModuleLoader__.load({
|
|
|
2206
2463
|
},
|
|
2207
2464
|
h(BranchIcon, { size: 12 }),
|
|
2208
2465
|
h('span', null, branch.name),
|
|
2209
|
-
branch.current === true ? h('span', { className: 'gp-
|
|
2466
|
+
branch.current === true ? h('span', { className: 'gp-menu-check' }, h(CheckIcon, null)) : null,
|
|
2210
2467
|
),
|
|
2211
2468
|
),
|
|
2212
|
-
(branches?.local ?? []).length === 0 ? h('div', { className: 'gp-note' }, t('
|
|
2469
|
+
(branches?.local ?? []).length === 0 ? h('div', { className: 'gp-note' }, t('empty.loading')) : null,
|
|
2213
2470
|
)
|
|
2214
2471
|
: null,
|
|
2215
2472
|
)
|
|
@@ -2314,6 +2571,7 @@ window.__ModuleLoader__.load({
|
|
|
2314
2571
|
h(
|
|
2315
2572
|
'div',
|
|
2316
2573
|
{ className: 'gp-diff-head' },
|
|
2574
|
+
typeof request.path === 'string' ? h(FileTypeIcon, { path: request.path }) : null,
|
|
2317
2575
|
h('span', { className: 'gp-path', title: request.path ?? request.hash ?? t('diff.title') }, request.path ?? request.hash ?? t('diff.title')),
|
|
2318
2576
|
mode === undefined ? null : h('span', { className: 'gp-tag' }, mode),
|
|
2319
2577
|
parsed.additions > 0 ? h('span', { className: 'gp-add' }, `+${parsed.additions}`) : null,
|
|
@@ -2477,6 +2735,8 @@ window.__ModuleLoader__.load({
|
|
|
2477
2735
|
__test: {
|
|
2478
2736
|
buildReviewPrompt,
|
|
2479
2737
|
ChangeBadge,
|
|
2738
|
+
branchSummaryOf,
|
|
2739
|
+
messages: { zh, en },
|
|
2480
2740
|
changeKindOf,
|
|
2481
2741
|
diffRequestFor,
|
|
2482
2742
|
discardRequestFor,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qzsy/dsh-git-panel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A Git section for the right Sidebar: workspace changes, a commit box, stage/discard, a diff preview tab, and a commit graph — modelled on the Git module of the local Cursor IDE.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|