@co0ontty/wand 1.25.3 → 1.26.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 +12 -0
- package/dist/git-quick-commit.d.ts +17 -1
- package/dist/git-quick-commit.js +248 -32
- package/dist/npm-update-utils.d.ts +51 -0
- package/dist/npm-update-utils.js +171 -0
- package/dist/server-session-routes.js +58 -1
- package/dist/server.js +14 -17
- package/dist/structured-session-manager.js +58 -2
- package/dist/tui/commands.d.ts +4 -0
- package/dist/tui/commands.js +16 -7
- package/dist/types.d.ts +32 -0
- package/dist/web-ui/content/scripts.js +634 -77
- package/dist/web-ui/content/styles.css +285 -0
- package/dist/ws-broadcast.js +36 -13
- package/package.json +1 -1
|
@@ -13537,11 +13537,296 @@
|
|
|
13537
13537
|
margin-top: 4px;
|
|
13538
13538
|
}
|
|
13539
13539
|
|
|
13540
|
+
/* ── Quick-commit V2 layout ───────────────────────────────────── */
|
|
13541
|
+
.quick-commit-modal .modal-body {
|
|
13542
|
+
display: flex;
|
|
13543
|
+
flex-direction: column;
|
|
13544
|
+
gap: 14px;
|
|
13545
|
+
}
|
|
13546
|
+
.qc-section {
|
|
13547
|
+
display: flex;
|
|
13548
|
+
flex-direction: column;
|
|
13549
|
+
gap: 10px;
|
|
13550
|
+
padding: 12px 14px;
|
|
13551
|
+
background: rgba(255, 255, 255, 0.6);
|
|
13552
|
+
border: 1px solid rgba(125, 91, 57, 0.10);
|
|
13553
|
+
border-radius: 14px;
|
|
13554
|
+
box-shadow: 0 1px 2px rgba(125, 91, 57, 0.04);
|
|
13555
|
+
}
|
|
13556
|
+
.qc-section--repo {
|
|
13557
|
+
background: linear-gradient(180deg, rgba(255, 250, 244, 0.7) 0%, rgba(255, 247, 238, 0.7) 100%);
|
|
13558
|
+
}
|
|
13559
|
+
.qc-section--empty {
|
|
13560
|
+
background: rgba(245, 245, 245, 0.5);
|
|
13561
|
+
}
|
|
13562
|
+
.qc-section-head {
|
|
13563
|
+
display: flex;
|
|
13564
|
+
align-items: baseline;
|
|
13565
|
+
justify-content: space-between;
|
|
13566
|
+
gap: 8px;
|
|
13567
|
+
}
|
|
13568
|
+
.qc-section-title {
|
|
13569
|
+
font-size: 0.78rem;
|
|
13570
|
+
font-weight: 700;
|
|
13571
|
+
text-transform: uppercase;
|
|
13572
|
+
letter-spacing: 0.06em;
|
|
13573
|
+
color: var(--text-muted);
|
|
13574
|
+
}
|
|
13575
|
+
.qc-section-meta {
|
|
13576
|
+
font-size: 0.75rem;
|
|
13577
|
+
color: var(--text-muted);
|
|
13578
|
+
font-family: var(--font-mono, monospace);
|
|
13579
|
+
overflow: hidden;
|
|
13580
|
+
text-overflow: ellipsis;
|
|
13581
|
+
white-space: nowrap;
|
|
13582
|
+
min-width: 0;
|
|
13583
|
+
}
|
|
13584
|
+
.qc-section-actions {
|
|
13585
|
+
display: flex;
|
|
13586
|
+
justify-content: flex-end;
|
|
13587
|
+
align-items: center;
|
|
13588
|
+
gap: 8px;
|
|
13589
|
+
margin-top: 2px;
|
|
13590
|
+
flex-wrap: wrap;
|
|
13591
|
+
}
|
|
13592
|
+
.qc-section-actions--secondary {
|
|
13593
|
+
justify-content: space-between;
|
|
13594
|
+
}
|
|
13595
|
+
|
|
13596
|
+
/* Empty state */
|
|
13597
|
+
.qc-empty-state {
|
|
13598
|
+
display: flex;
|
|
13599
|
+
align-items: center;
|
|
13600
|
+
gap: 12px;
|
|
13601
|
+
padding: 6px 4px;
|
|
13602
|
+
}
|
|
13603
|
+
.qc-empty-icon {
|
|
13604
|
+
flex-shrink: 0;
|
|
13605
|
+
width: 32px;
|
|
13606
|
+
height: 32px;
|
|
13607
|
+
border-radius: 50%;
|
|
13608
|
+
background: rgba(60, 160, 90, 0.14);
|
|
13609
|
+
color: rgba(40, 130, 70, 0.95);
|
|
13610
|
+
display: flex;
|
|
13611
|
+
align-items: center;
|
|
13612
|
+
justify-content: center;
|
|
13613
|
+
font-weight: 700;
|
|
13614
|
+
font-size: 1rem;
|
|
13615
|
+
}
|
|
13616
|
+
.qc-empty-title {
|
|
13617
|
+
font-size: 0.9rem;
|
|
13618
|
+
font-weight: 600;
|
|
13619
|
+
color: var(--text-primary);
|
|
13620
|
+
}
|
|
13621
|
+
.qc-empty-sub {
|
|
13622
|
+
font-size: 0.78rem;
|
|
13623
|
+
color: var(--text-muted);
|
|
13624
|
+
}
|
|
13625
|
+
|
|
13626
|
+
/* HEAD card (last commit info) */
|
|
13627
|
+
.qc-head-card {
|
|
13628
|
+
display: flex;
|
|
13629
|
+
align-items: center;
|
|
13630
|
+
gap: 10px;
|
|
13631
|
+
padding: 8px 12px;
|
|
13632
|
+
background: rgba(255, 255, 255, 0.7);
|
|
13633
|
+
border: 1px solid rgba(125, 91, 57, 0.08);
|
|
13634
|
+
border-radius: 10px;
|
|
13635
|
+
min-height: 36px;
|
|
13636
|
+
}
|
|
13637
|
+
.qc-head-label {
|
|
13638
|
+
flex-shrink: 0;
|
|
13639
|
+
font-size: 0.7rem;
|
|
13640
|
+
font-weight: 700;
|
|
13641
|
+
letter-spacing: 0.05em;
|
|
13642
|
+
color: var(--text-muted);
|
|
13643
|
+
text-transform: uppercase;
|
|
13644
|
+
}
|
|
13645
|
+
.qc-head-text {
|
|
13646
|
+
flex: 1 1 auto;
|
|
13647
|
+
min-width: 0;
|
|
13648
|
+
font-family: var(--font-mono, monospace);
|
|
13649
|
+
font-size: 0.78rem;
|
|
13650
|
+
color: var(--text-primary);
|
|
13651
|
+
overflow: hidden;
|
|
13652
|
+
text-overflow: ellipsis;
|
|
13653
|
+
white-space: nowrap;
|
|
13654
|
+
background: transparent;
|
|
13655
|
+
padding: 0;
|
|
13656
|
+
}
|
|
13657
|
+
|
|
13658
|
+
/* Status chips (ahead/behind/tags) */
|
|
13659
|
+
.qc-status-chips {
|
|
13660
|
+
display: flex;
|
|
13661
|
+
flex-wrap: wrap;
|
|
13662
|
+
gap: 6px;
|
|
13663
|
+
}
|
|
13664
|
+
.qc-chip {
|
|
13665
|
+
display: inline-flex;
|
|
13666
|
+
align-items: center;
|
|
13667
|
+
gap: 4px;
|
|
13668
|
+
padding: 3px 9px;
|
|
13669
|
+
border-radius: 999px;
|
|
13670
|
+
font-size: 0.72rem;
|
|
13671
|
+
font-weight: 600;
|
|
13672
|
+
line-height: 1.4;
|
|
13673
|
+
background: rgba(120, 120, 120, 0.08);
|
|
13674
|
+
color: var(--text-secondary);
|
|
13675
|
+
border: 1px solid rgba(120, 120, 120, 0.12);
|
|
13676
|
+
white-space: nowrap;
|
|
13677
|
+
}
|
|
13678
|
+
.qc-chip--ahead {
|
|
13679
|
+
background: rgba(60, 130, 200, 0.14);
|
|
13680
|
+
color: rgba(40, 100, 170, 0.95);
|
|
13681
|
+
border-color: rgba(60, 130, 200, 0.28);
|
|
13682
|
+
}
|
|
13683
|
+
.qc-chip--behind {
|
|
13684
|
+
background: rgba(180, 100, 60, 0.14);
|
|
13685
|
+
color: rgba(160, 90, 50, 0.95);
|
|
13686
|
+
border-color: rgba(180, 100, 60, 0.28);
|
|
13687
|
+
}
|
|
13688
|
+
.qc-chip--tag {
|
|
13689
|
+
background: rgba(140, 100, 200, 0.14);
|
|
13690
|
+
color: rgba(110, 70, 180, 0.95);
|
|
13691
|
+
border-color: rgba(140, 100, 200, 0.28);
|
|
13692
|
+
}
|
|
13693
|
+
.qc-chip--warn {
|
|
13694
|
+
background: rgba(220, 150, 30, 0.14);
|
|
13695
|
+
color: rgba(180, 110, 20, 0.95);
|
|
13696
|
+
border-color: rgba(220, 150, 30, 0.32);
|
|
13697
|
+
}
|
|
13698
|
+
.qc-chip--clean {
|
|
13699
|
+
background: rgba(60, 160, 90, 0.12);
|
|
13700
|
+
color: rgba(40, 130, 70, 0.95);
|
|
13701
|
+
border-color: rgba(60, 160, 90, 0.24);
|
|
13702
|
+
}
|
|
13703
|
+
|
|
13704
|
+
/* Tag HEAD inline drawer */
|
|
13705
|
+
.qc-tag-head-panel {
|
|
13706
|
+
display: flex;
|
|
13707
|
+
flex-direction: column;
|
|
13708
|
+
gap: 8px;
|
|
13709
|
+
padding: 10px 12px;
|
|
13710
|
+
background: rgba(255, 255, 255, 0.85);
|
|
13711
|
+
border: 1px solid rgba(140, 100, 200, 0.18);
|
|
13712
|
+
border-radius: 10px;
|
|
13713
|
+
}
|
|
13714
|
+
.qc-tag-head-row {
|
|
13715
|
+
display: flex;
|
|
13716
|
+
gap: 6px;
|
|
13717
|
+
align-items: stretch;
|
|
13718
|
+
}
|
|
13719
|
+
.qc-tag-head-row .field-input { flex: 1 1 auto; min-width: 0; }
|
|
13720
|
+
.qc-tag-head-push {
|
|
13721
|
+
display: flex;
|
|
13722
|
+
align-items: center;
|
|
13723
|
+
gap: 6px;
|
|
13724
|
+
font-size: 0.78rem;
|
|
13725
|
+
color: var(--text-secondary);
|
|
13726
|
+
cursor: pointer;
|
|
13727
|
+
user-select: none;
|
|
13728
|
+
}
|
|
13729
|
+
.qc-tag-head-actions {
|
|
13730
|
+
display: flex;
|
|
13731
|
+
justify-content: flex-end;
|
|
13732
|
+
gap: 6px;
|
|
13733
|
+
}
|
|
13734
|
+
|
|
13735
|
+
/* Split button (primary action: commit / commit & push) */
|
|
13736
|
+
.qc-split-button {
|
|
13737
|
+
position: relative;
|
|
13738
|
+
display: inline-flex;
|
|
13739
|
+
align-items: stretch;
|
|
13740
|
+
isolation: isolate;
|
|
13741
|
+
}
|
|
13742
|
+
.qc-split-main {
|
|
13743
|
+
border-top-right-radius: 0;
|
|
13744
|
+
border-bottom-right-radius: 0;
|
|
13745
|
+
padding-right: 12px;
|
|
13746
|
+
}
|
|
13747
|
+
.qc-split-caret {
|
|
13748
|
+
border-top-left-radius: 0;
|
|
13749
|
+
border-bottom-left-radius: 0;
|
|
13750
|
+
padding: 0 8px;
|
|
13751
|
+
border-left: 1px solid rgba(255, 255, 255, 0.25);
|
|
13752
|
+
min-width: 26px;
|
|
13753
|
+
display: inline-flex;
|
|
13754
|
+
align-items: center;
|
|
13755
|
+
justify-content: center;
|
|
13756
|
+
}
|
|
13757
|
+
.qc-split-caret svg {
|
|
13758
|
+
transition: transform 0.16s ease;
|
|
13759
|
+
}
|
|
13760
|
+
.qc-split-caret.is-active svg {
|
|
13761
|
+
transform: rotate(180deg);
|
|
13762
|
+
}
|
|
13763
|
+
.qc-split-button--secondary .qc-split-caret {
|
|
13764
|
+
border-left-color: rgba(125, 91, 57, 0.12);
|
|
13765
|
+
}
|
|
13766
|
+
|
|
13767
|
+
/* Dropdown menu (used by primary action + push) */
|
|
13768
|
+
.qc-dropdown-menu {
|
|
13769
|
+
position: absolute;
|
|
13770
|
+
top: calc(100% + 6px);
|
|
13771
|
+
right: 0;
|
|
13772
|
+
min-width: 220px;
|
|
13773
|
+
max-width: 320px;
|
|
13774
|
+
background: rgba(255, 255, 255, 0.98);
|
|
13775
|
+
backdrop-filter: blur(12px);
|
|
13776
|
+
-webkit-backdrop-filter: blur(12px);
|
|
13777
|
+
border: 1px solid rgba(125, 91, 57, 0.16);
|
|
13778
|
+
border-radius: 12px;
|
|
13779
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06);
|
|
13780
|
+
padding: 4px;
|
|
13781
|
+
z-index: 20;
|
|
13782
|
+
display: flex;
|
|
13783
|
+
flex-direction: column;
|
|
13784
|
+
gap: 2px;
|
|
13785
|
+
animation: qcDropdownIn 0.14s ease-out;
|
|
13786
|
+
}
|
|
13787
|
+
.qc-dropdown-menu--right { right: 0; left: auto; }
|
|
13788
|
+
@keyframes qcDropdownIn {
|
|
13789
|
+
from { opacity: 0; transform: translateY(-4px); }
|
|
13790
|
+
to { opacity: 1; transform: translateY(0); }
|
|
13791
|
+
}
|
|
13792
|
+
.qc-dropdown-item {
|
|
13793
|
+
all: unset;
|
|
13794
|
+
display: flex;
|
|
13795
|
+
flex-direction: column;
|
|
13796
|
+
gap: 1px;
|
|
13797
|
+
padding: 8px 10px;
|
|
13798
|
+
border-radius: 8px;
|
|
13799
|
+
cursor: pointer;
|
|
13800
|
+
transition: background 0.12s ease;
|
|
13801
|
+
}
|
|
13802
|
+
.qc-dropdown-item:hover:not(.is-disabled) {
|
|
13803
|
+
background: rgba(197, 101, 61, 0.08);
|
|
13804
|
+
}
|
|
13805
|
+
.qc-dropdown-item.is-selected {
|
|
13806
|
+
background: rgba(197, 101, 61, 0.12);
|
|
13807
|
+
}
|
|
13808
|
+
.qc-dropdown-item.is-disabled {
|
|
13809
|
+
opacity: 0.5;
|
|
13810
|
+
cursor: not-allowed;
|
|
13811
|
+
}
|
|
13812
|
+
.qc-dropdown-item-title {
|
|
13813
|
+
font-size: 0.84rem;
|
|
13814
|
+
font-weight: 600;
|
|
13815
|
+
color: var(--text-primary);
|
|
13816
|
+
}
|
|
13817
|
+
.qc-dropdown-item-desc {
|
|
13818
|
+
font-size: 0.72rem;
|
|
13819
|
+
color: var(--text-muted);
|
|
13820
|
+
line-height: 1.4;
|
|
13821
|
+
}
|
|
13822
|
+
|
|
13540
13823
|
@media (max-width: 720px) {
|
|
13541
13824
|
.topbar-git-branch { max-width: 8em; }
|
|
13542
13825
|
.topbar-git-badge { padding: 0 8px; font-size: 0.7rem; }
|
|
13543
13826
|
.quick-commit-modal { max-width: 95vw; }
|
|
13544
13827
|
.qc-files-wrap { max-height: 160px; }
|
|
13828
|
+
.qc-section { padding: 10px 12px; }
|
|
13829
|
+
.qc-dropdown-menu { min-width: 180px; }
|
|
13545
13830
|
}
|
|
13546
13831
|
|
|
13547
13832
|
/* ============================================================ */
|
package/dist/ws-broadcast.js
CHANGED
|
@@ -78,23 +78,46 @@ export class WsBroadcastManager {
|
|
|
78
78
|
if (event.type === "output") {
|
|
79
79
|
const existing = this.outputDebounceCache.get(event.sessionId);
|
|
80
80
|
if (existing) {
|
|
81
|
-
clearTimeout(existing.timer);
|
|
82
|
-
// Merge prev + cur. Cur takes precedence for identically-named fields,
|
|
83
|
-
// but fields only present on prev (e.g. chunk while cur carries
|
|
84
|
-
// messages, or messages while cur carries chunk) survive — the old
|
|
85
|
-
// implementation silently dropped them.
|
|
86
81
|
const prevData = existing.event.data ?? {};
|
|
87
82
|
const curData = event.data ?? {};
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
83
|
+
// 跨"事件形状"不能简单 shallow-merge:
|
|
84
|
+
// - 全量事件(带 messages)+ 增量事件(带 lastMessage,不带 messages)
|
|
85
|
+
// 合并后变成 { messages, incremental: true, lastMessage }。客户端
|
|
86
|
+
// reducer 看到 incremental 就只读 lastMessage,把权威的 messages 丢掉,
|
|
87
|
+
// 表现是"刷新页面才出来的消失文字"。
|
|
88
|
+
// - 反过来:增量在前,全量在后,cur 覆盖 prev 后 incremental 仍为 true
|
|
89
|
+
// 而 messages 来自 cur——这种顺序原本是安全的,但风险一致就一起处理。
|
|
90
|
+
// 形状不一致时 flush 上一条立即广播,新事件单独开窗口。这样客户端永远
|
|
91
|
+
// 不会在一条 WS 消息里同时看到 messages 和 lastMessage 两种语义。
|
|
92
|
+
const prevHasMessages = "messages" in prevData && prevData.messages !== undefined;
|
|
93
|
+
const prevHasLastMsg = "lastMessage" in prevData && prevData.lastMessage !== undefined;
|
|
94
|
+
const curHasMessages = "messages" in curData && curData.messages !== undefined;
|
|
95
|
+
const curHasLastMsg = "lastMessage" in curData && curData.lastMessage !== undefined;
|
|
96
|
+
const shapeMismatch = (prevHasMessages && curHasLastMsg && !curHasMessages) ||
|
|
97
|
+
(prevHasLastMsg && curHasMessages && !curHasLastMsg);
|
|
98
|
+
if (shapeMismatch) {
|
|
99
|
+
clearTimeout(existing.timer);
|
|
100
|
+
this.outputDebounceCache.delete(event.sessionId);
|
|
101
|
+
this.broadcast(existing.event);
|
|
102
|
+
// Fall through to schedule cur on a fresh debounce window
|
|
93
103
|
}
|
|
94
|
-
else
|
|
95
|
-
|
|
104
|
+
else {
|
|
105
|
+
clearTimeout(existing.timer);
|
|
106
|
+
// Merge prev + cur. Cur takes precedence for identically-named fields,
|
|
107
|
+
// but fields only present on prev (e.g. chunk while cur carries
|
|
108
|
+
// messages, or messages while cur carries chunk) survive — the old
|
|
109
|
+
// implementation silently dropped them.
|
|
110
|
+
const merged = { ...prevData, ...curData };
|
|
111
|
+
const prevChunk = prevData.chunk;
|
|
112
|
+
const curChunk = curData.chunk;
|
|
113
|
+
if (prevChunk && curChunk) {
|
|
114
|
+
merged.chunk = prevChunk + curChunk;
|
|
115
|
+
}
|
|
116
|
+
else if (prevChunk && !curChunk) {
|
|
117
|
+
merged.chunk = prevChunk;
|
|
118
|
+
}
|
|
119
|
+
event = { ...event, data: merged };
|
|
96
120
|
}
|
|
97
|
-
event = { ...event, data: merged };
|
|
98
121
|
}
|
|
99
122
|
const timer = setTimeout(() => {
|
|
100
123
|
this.outputDebounceCache.delete(event.sessionId);
|