claude-code-kanban 7.1.2 → 7.3.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/hub-app.json +1 -0
- package/lib/panes.js +9 -1
- package/lib/terminal.js +54 -14
- package/package.json +1 -1
- package/public/app.js +773 -88
- package/public/index.html +18 -8
- package/public/style.css +300 -10
- package/public/terminal-frame.js +6 -4
- package/public/vendor/claude-hub-sdk.js +40 -6
- package/server.js +41 -2
package/public/index.html
CHANGED
|
@@ -293,6 +293,9 @@
|
|
|
293
293
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
294
294
|
</button>
|
|
295
295
|
<div class="pane-tools">
|
|
296
|
+
<button id="pane-split-btn" class="icon-btn pane-split-btn" title="Split with the tab before it (|)" aria-label="Split panes" aria-pressed="false">
|
|
297
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="16" rx="2"/><line x1="12" y1="4" x2="12" y2="20"/></svg>
|
|
298
|
+
</button>
|
|
296
299
|
<button id="pane-reload" class="icon-btn pane-tool" title="Reload pane" aria-label="Reload pane">
|
|
297
300
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7L21 8"/><polyline points="21 3 21 8 16 8"/></svg>
|
|
298
301
|
</button>
|
|
@@ -302,6 +305,8 @@
|
|
|
302
305
|
</div>
|
|
303
306
|
</div>
|
|
304
307
|
|
|
308
|
+
<div id="pane-split" class="pane-split">
|
|
309
|
+
<div id="board-side" class="board-side">
|
|
305
310
|
<div class="kanban" id="main-content">
|
|
306
311
|
<div id="owner-filter-bar" class="owner-filter-bar">
|
|
307
312
|
<select id="owner-filter" class="filter-dropdown" onchange="filterByOwner(this.value)" aria-label="Filter by team member">
|
|
@@ -335,6 +340,18 @@
|
|
|
335
340
|
<div id="completed-tasks" class="column-tasks" role="list" data-status="completed" ondragover="onColumnDragOver(event)" ondragleave="onColumnDragLeave(event)" ondrop="onColumnDrop(event)"></div>
|
|
336
341
|
</div>
|
|
337
342
|
</div>
|
|
343
|
+
<div id="agent-footer" class="agent-footer">
|
|
344
|
+
<div class="agent-footer-header" onclick="toggleAgentFooter()">
|
|
345
|
+
<span id="agent-footer-label">Agents</span>
|
|
346
|
+
<button class="agent-footer-toggle" id="agent-footer-toggle" aria-label="Toggle agent panel">▾</button>
|
|
347
|
+
</div>
|
|
348
|
+
<div class="agent-footer-content" id="agent-footer-content"></div>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
<div id="pane-divider" class="pane-divider" title="Drag to resize · drag to an edge to unsplit · double-click to reset"></div>
|
|
352
|
+
<div id="pane-views" class="pane-views"></div>
|
|
353
|
+
<div id="pane-drop" class="pane-drop"><div data-side="l"></div><div data-side="r"></div></div>
|
|
354
|
+
</div>
|
|
338
355
|
<div id="terminal-pane" class="terminal-pane">
|
|
339
356
|
<div class="terminal-bar">
|
|
340
357
|
<span id="terminal-status" class="terminal-status"></span>
|
|
@@ -353,14 +370,6 @@
|
|
|
353
370
|
<div id="terminal-host" class="terminal-host"></div>
|
|
354
371
|
<textarea id="terminal-key-proxy" class="terminal-key-proxy" tabindex="-1" aria-hidden="true"></textarea>
|
|
355
372
|
</div>
|
|
356
|
-
<div id="pane-views" class="pane-views"></div>
|
|
357
|
-
<div id="agent-footer" class="agent-footer">
|
|
358
|
-
<div class="agent-footer-header" onclick="toggleAgentFooter()">
|
|
359
|
-
<span id="agent-footer-label">Agents</span>
|
|
360
|
-
<button class="agent-footer-toggle" id="agent-footer-toggle" aria-label="Toggle agent panel">▾</button>
|
|
361
|
-
</div>
|
|
362
|
-
<div class="agent-footer-content" id="agent-footer-content"></div>
|
|
363
|
-
</div>
|
|
364
373
|
</div>
|
|
365
374
|
</main>
|
|
366
375
|
|
|
@@ -840,6 +849,7 @@
|
|
|
840
849
|
</div>
|
|
841
850
|
<div class="modal-footer ns-footer">
|
|
842
851
|
<label class="ns-check ns-stay" title="Start in the background and keep the current view"><input type="checkbox" id="ns-stay"> Stay here</label>
|
|
852
|
+
<label class="ns-check ns-edit" title="Open a shell in the folder with the claude command typed in. Press Enter to run it."><input type="checkbox" id="ns-edit"> Edit before run</label>
|
|
843
853
|
<button type="button" class="btn btn-secondary" onclick="closeNewSession()">Cancel</button>
|
|
844
854
|
<button type="button" id="ns-start" class="btn btn-primary" onclick="startNewSession()" disabled>Start</button>
|
|
845
855
|
</div>
|
package/public/style.css
CHANGED
|
@@ -368,7 +368,9 @@ body::before {
|
|
|
368
368
|
transition: background 0.15s;
|
|
369
369
|
}
|
|
370
370
|
.panel-resize-handle:hover,
|
|
371
|
-
.panel-resize-handle.dragging
|
|
371
|
+
.panel-resize-handle.dragging,
|
|
372
|
+
.pane-divider:hover,
|
|
373
|
+
.pane-divider.dragging {
|
|
372
374
|
background: var(--accent-dim);
|
|
373
375
|
}
|
|
374
376
|
|
|
@@ -3461,8 +3463,7 @@ code.language-tree .hljs-title {
|
|
|
3461
3463
|
display: none;
|
|
3462
3464
|
}
|
|
3463
3465
|
|
|
3464
|
-
.session-view.pane-mode .
|
|
3465
|
-
.session-view.pane-mode .agent-footer {
|
|
3466
|
+
.session-view.pane-mode .board-side {
|
|
3466
3467
|
display: none;
|
|
3467
3468
|
}
|
|
3468
3469
|
|
|
@@ -3530,6 +3531,51 @@ code.language-tree .hljs-title {
|
|
|
3530
3531
|
color: var(--text-primary);
|
|
3531
3532
|
}
|
|
3532
3533
|
|
|
3534
|
+
.pane-tab.shown {
|
|
3535
|
+
background: var(--bg-deep);
|
|
3536
|
+
border-color: var(--border);
|
|
3537
|
+
color: var(--text-secondary);
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
.pane-tab.dragging {
|
|
3541
|
+
opacity: 0.5;
|
|
3542
|
+
}
|
|
3543
|
+
|
|
3544
|
+
.pane-pair {
|
|
3545
|
+
display: flex;
|
|
3546
|
+
flex: 0 1 auto;
|
|
3547
|
+
align-items: stretch;
|
|
3548
|
+
min-width: 0;
|
|
3549
|
+
border: 1px solid var(--border);
|
|
3550
|
+
border-bottom: 0;
|
|
3551
|
+
border-radius: 7px 7px 0 0;
|
|
3552
|
+
background: var(--bg-elevated);
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3555
|
+
.pane-pair.on {
|
|
3556
|
+
border-color: var(--accent-dim);
|
|
3557
|
+
}
|
|
3558
|
+
|
|
3559
|
+
.pane-pair .pane-tab {
|
|
3560
|
+
min-width: 80px;
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3563
|
+
.pane-pair .pane-tab.on,
|
|
3564
|
+
.pane-pair .pane-tab.shown {
|
|
3565
|
+
border-color: transparent;
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
.pane-pair-sep {
|
|
3569
|
+
flex: none;
|
|
3570
|
+
width: 1px;
|
|
3571
|
+
margin: 5px 0;
|
|
3572
|
+
background: var(--border);
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3575
|
+
.pane-strip.drop-hot {
|
|
3576
|
+
background: var(--accent-dim);
|
|
3577
|
+
}
|
|
3578
|
+
|
|
3533
3579
|
.pane-tab.pinned {
|
|
3534
3580
|
flex: none;
|
|
3535
3581
|
min-width: 0;
|
|
@@ -3605,13 +3651,19 @@ code.language-tree .hljs-title {
|
|
|
3605
3651
|
}
|
|
3606
3652
|
|
|
3607
3653
|
.pane-add,
|
|
3608
|
-
.pane-tool
|
|
3654
|
+
.pane-tool,
|
|
3655
|
+
.pane-split-btn {
|
|
3609
3656
|
width: 26px;
|
|
3610
3657
|
height: 26px;
|
|
3611
3658
|
border-color: transparent;
|
|
3612
3659
|
}
|
|
3613
3660
|
|
|
3661
|
+
.pane-split-btn.on {
|
|
3662
|
+
color: var(--accent);
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3614
3665
|
.pane-add svg,
|
|
3666
|
+
.pane-split-btn svg,
|
|
3615
3667
|
.pane-tool svg {
|
|
3616
3668
|
width: 14px;
|
|
3617
3669
|
height: 14px;
|
|
@@ -3629,7 +3681,8 @@ code.language-tree .hljs-title {
|
|
|
3629
3681
|
margin: 0 0 3px auto;
|
|
3630
3682
|
}
|
|
3631
3683
|
|
|
3632
|
-
.pane-tool:disabled
|
|
3684
|
+
.pane-tool:disabled,
|
|
3685
|
+
.pane-split-btn:disabled {
|
|
3633
3686
|
opacity: 0.35;
|
|
3634
3687
|
cursor: default;
|
|
3635
3688
|
background: none;
|
|
@@ -3658,6 +3711,178 @@ code.language-tree .hljs-title {
|
|
|
3658
3711
|
display: flex;
|
|
3659
3712
|
}
|
|
3660
3713
|
|
|
3714
|
+
.pane-split {
|
|
3715
|
+
--pane-split: 50%;
|
|
3716
|
+
position: relative;
|
|
3717
|
+
flex: 1;
|
|
3718
|
+
display: flex;
|
|
3719
|
+
min-height: 0;
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3722
|
+
.board-side {
|
|
3723
|
+
flex: 1;
|
|
3724
|
+
display: flex;
|
|
3725
|
+
flex-direction: column;
|
|
3726
|
+
min-width: 0;
|
|
3727
|
+
container: board / inline-size;
|
|
3728
|
+
}
|
|
3729
|
+
|
|
3730
|
+
.board-side > .kanban {
|
|
3731
|
+
min-height: 0;
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3734
|
+
/* 936px is where the full layout stops fitting: 3 × 280px columns, 2 × 24px gaps, 2 × 24px padding. */
|
|
3735
|
+
@container board (max-width: 936px) {
|
|
3736
|
+
.kanban {
|
|
3737
|
+
gap: 12px;
|
|
3738
|
+
padding: 16px;
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3741
|
+
.kanban-column {
|
|
3742
|
+
min-width: 140px;
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
.column-header {
|
|
3746
|
+
gap: 6px;
|
|
3747
|
+
padding-bottom: 12px;
|
|
3748
|
+
margin-bottom: 12px;
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3751
|
+
.column-title {
|
|
3752
|
+
font-size: 12px;
|
|
3753
|
+
letter-spacing: 0.03em;
|
|
3754
|
+
white-space: nowrap;
|
|
3755
|
+
overflow: hidden;
|
|
3756
|
+
text-overflow: ellipsis;
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
.column-tasks {
|
|
3760
|
+
padding-right: 0;
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
|
|
3764
|
+
.session-view.terminal-mode .pane-split {
|
|
3765
|
+
display: none;
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3768
|
+
.pane-divider,
|
|
3769
|
+
.pane-drop {
|
|
3770
|
+
display: none;
|
|
3771
|
+
}
|
|
3772
|
+
|
|
3773
|
+
.pane-divider {
|
|
3774
|
+
background: linear-gradient(to right, transparent 2px, var(--border) 2px 4px, transparent 4px);
|
|
3775
|
+
transition: background 0.15s;
|
|
3776
|
+
}
|
|
3777
|
+
|
|
3778
|
+
.session-view.split-mode:not(.terminal-mode) .pane-views {
|
|
3779
|
+
display: contents;
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3782
|
+
.session-view.split-mode .pane-split > .board-side.side-l,
|
|
3783
|
+
.session-view.split-mode .pane-split > .board-side.side-r,
|
|
3784
|
+
.session-view.split-mode .pane-view {
|
|
3785
|
+
position: absolute;
|
|
3786
|
+
top: 0;
|
|
3787
|
+
bottom: 0;
|
|
3788
|
+
background: var(--bg-deep);
|
|
3789
|
+
}
|
|
3790
|
+
|
|
3791
|
+
.session-view.split-mode .side-l {
|
|
3792
|
+
left: 0;
|
|
3793
|
+
right: calc(100% - var(--pane-split) + 3px);
|
|
3794
|
+
}
|
|
3795
|
+
|
|
3796
|
+
.session-view.split-mode .side-r {
|
|
3797
|
+
left: calc(var(--pane-split) + 3px);
|
|
3798
|
+
right: 0;
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
.pane-split > .message-panel.log-l,
|
|
3802
|
+
.pane-split > .message-panel.log-r {
|
|
3803
|
+
position: absolute;
|
|
3804
|
+
top: 0;
|
|
3805
|
+
bottom: 0;
|
|
3806
|
+
left: auto;
|
|
3807
|
+
height: auto;
|
|
3808
|
+
z-index: 3;
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
.pane-split > .message-panel.log-l {
|
|
3812
|
+
right: calc(100% - var(--pane-split) + 3px);
|
|
3813
|
+
width: min(var(--message-panel-width, 540px), calc(var(--pane-split) - 3px));
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3816
|
+
.pane-split > .message-panel.log-r {
|
|
3817
|
+
right: 0;
|
|
3818
|
+
width: min(var(--message-panel-width, 540px), calc(100% - var(--pane-split) - 3px));
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
.pane-split > .message-panel.full-width.log-l {
|
|
3822
|
+
left: 0;
|
|
3823
|
+
width: auto;
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
.pane-split > .message-panel.full-width.log-r {
|
|
3827
|
+
left: calc(var(--pane-split) + 3px);
|
|
3828
|
+
width: auto;
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3831
|
+
.session-view.split-mode .pane-focus::after {
|
|
3832
|
+
content: '';
|
|
3833
|
+
position: absolute;
|
|
3834
|
+
top: 0;
|
|
3835
|
+
left: 0;
|
|
3836
|
+
right: 0;
|
|
3837
|
+
height: 2px;
|
|
3838
|
+
background: var(--accent);
|
|
3839
|
+
pointer-events: none;
|
|
3840
|
+
z-index: 3;
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3843
|
+
.session-view.split-mode:not(.terminal-mode) .pane-divider {
|
|
3844
|
+
display: block;
|
|
3845
|
+
position: absolute;
|
|
3846
|
+
top: 0;
|
|
3847
|
+
bottom: 0;
|
|
3848
|
+
left: calc(var(--pane-split) - 3px);
|
|
3849
|
+
width: 6px;
|
|
3850
|
+
cursor: col-resize;
|
|
3851
|
+
z-index: 4;
|
|
3852
|
+
}
|
|
3853
|
+
|
|
3854
|
+
.pane-split.resizing {
|
|
3855
|
+
cursor: col-resize;
|
|
3856
|
+
}
|
|
3857
|
+
|
|
3858
|
+
.pane-split.resizing iframe {
|
|
3859
|
+
pointer-events: none;
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3862
|
+
.pane-split.collapse-l .side-l,
|
|
3863
|
+
.pane-split.collapse-r .side-r {
|
|
3864
|
+
opacity: 0.35;
|
|
3865
|
+
}
|
|
3866
|
+
|
|
3867
|
+
.pane-split.dropping .pane-drop {
|
|
3868
|
+
display: flex;
|
|
3869
|
+
position: absolute;
|
|
3870
|
+
inset: 0;
|
|
3871
|
+
z-index: 5;
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
.pane-drop > div {
|
|
3875
|
+
flex: 1;
|
|
3876
|
+
margin: 8px;
|
|
3877
|
+
border-radius: 8px;
|
|
3878
|
+
}
|
|
3879
|
+
|
|
3880
|
+
.pane-drop > div.hot {
|
|
3881
|
+
background: var(--accent-dim);
|
|
3882
|
+
outline: 2px dashed var(--accent);
|
|
3883
|
+
outline-offset: -2px;
|
|
3884
|
+
}
|
|
3885
|
+
|
|
3661
3886
|
.pane-view:has(> .review-panel) {
|
|
3662
3887
|
flex-direction: row;
|
|
3663
3888
|
}
|
|
@@ -3684,6 +3909,49 @@ code.language-tree .hljs-title {
|
|
|
3684
3909
|
background: #fff;
|
|
3685
3910
|
}
|
|
3686
3911
|
|
|
3912
|
+
.pane-url-bar {
|
|
3913
|
+
position: absolute;
|
|
3914
|
+
right: 12px;
|
|
3915
|
+
bottom: 12px;
|
|
3916
|
+
display: flex;
|
|
3917
|
+
align-items: center;
|
|
3918
|
+
gap: 2px;
|
|
3919
|
+
padding: 3px;
|
|
3920
|
+
border: 1px solid var(--border);
|
|
3921
|
+
border-radius: 8px;
|
|
3922
|
+
background: var(--bg-elevated);
|
|
3923
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
|
|
3924
|
+
font-family: var(--font-mono);
|
|
3925
|
+
font-size: 12px;
|
|
3926
|
+
opacity: 0;
|
|
3927
|
+
pointer-events: none;
|
|
3928
|
+
transition: opacity 0.15s;
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
.pane-view:hover > .pane-url-bar,
|
|
3932
|
+
.pane-view.frame-focus > .pane-url-bar {
|
|
3933
|
+
opacity: 1;
|
|
3934
|
+
pointer-events: auto;
|
|
3935
|
+
}
|
|
3936
|
+
|
|
3937
|
+
.pane-url-bar .icon-btn {
|
|
3938
|
+
width: auto;
|
|
3939
|
+
min-width: 26px;
|
|
3940
|
+
height: 24px;
|
|
3941
|
+
padding: 0 6px;
|
|
3942
|
+
border-color: transparent;
|
|
3943
|
+
font: inherit;
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
.pane-url-bar .pane-keys {
|
|
3947
|
+
display: none;
|
|
3948
|
+
color: var(--accent);
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3951
|
+
.pane-view.frame-focus .pane-keys {
|
|
3952
|
+
display: flex;
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3687
3955
|
.pane-doc {
|
|
3688
3956
|
flex: 1;
|
|
3689
3957
|
min-height: 0;
|
|
@@ -5270,6 +5538,7 @@ code.language-tree .hljs-title {
|
|
|
5270
5538
|
|
|
5271
5539
|
.linked-doc-list,
|
|
5272
5540
|
.scratch-file-list,
|
|
5541
|
+
.zen-bookmark-list,
|
|
5273
5542
|
.artifact-list {
|
|
5274
5543
|
list-style: none;
|
|
5275
5544
|
padding: 0;
|
|
@@ -5281,6 +5550,7 @@ code.language-tree .hljs-title {
|
|
|
5281
5550
|
|
|
5282
5551
|
.linked-doc-item,
|
|
5283
5552
|
.scratch-file-item,
|
|
5553
|
+
.zen-bookmark-item,
|
|
5284
5554
|
.artifact-item {
|
|
5285
5555
|
display: flex;
|
|
5286
5556
|
align-items: baseline;
|
|
@@ -5290,6 +5560,7 @@ code.language-tree .hljs-title {
|
|
|
5290
5560
|
|
|
5291
5561
|
.linked-doc-link,
|
|
5292
5562
|
.scratch-file-link,
|
|
5563
|
+
.zen-bookmark-link,
|
|
5293
5564
|
.artifact-link {
|
|
5294
5565
|
color: var(--accent-text);
|
|
5295
5566
|
text-decoration: underline;
|
|
@@ -5347,6 +5618,12 @@ code.language-tree .hljs-title {
|
|
|
5347
5618
|
font-size: 11px;
|
|
5348
5619
|
}
|
|
5349
5620
|
|
|
5621
|
+
.zen-bookmark-who {
|
|
5622
|
+
color: var(--text-muted);
|
|
5623
|
+
flex-shrink: 0;
|
|
5624
|
+
}
|
|
5625
|
+
|
|
5626
|
+
.zen-bookmark-link,
|
|
5350
5627
|
.scratch-file-link {
|
|
5351
5628
|
background: none;
|
|
5352
5629
|
border: none;
|
|
@@ -7099,7 +7376,7 @@ select.form-input option:checked {
|
|
|
7099
7376
|
|
|
7100
7377
|
/* #region NEW_SESSION */
|
|
7101
7378
|
.modal.new-session-modal {
|
|
7102
|
-
width:
|
|
7379
|
+
width: 640px;
|
|
7103
7380
|
max-width: calc(100vw - 32px);
|
|
7104
7381
|
overflow: visible;
|
|
7105
7382
|
}
|
|
@@ -7228,7 +7505,8 @@ select.form-input option:checked {
|
|
|
7228
7505
|
accent-color: var(--accent);
|
|
7229
7506
|
}
|
|
7230
7507
|
|
|
7231
|
-
.ns-inline .form-input:disabled
|
|
7508
|
+
.ns-inline .form-input:disabled,
|
|
7509
|
+
.ns-check:has(input:disabled) {
|
|
7232
7510
|
opacity: 0.5;
|
|
7233
7511
|
cursor: not-allowed;
|
|
7234
7512
|
}
|
|
@@ -7311,15 +7589,27 @@ select.form-input option:checked {
|
|
|
7311
7589
|
border-top: 1px solid var(--border);
|
|
7312
7590
|
}
|
|
7313
7591
|
|
|
7314
|
-
.ns-stay
|
|
7315
|
-
|
|
7592
|
+
.ns-stay,
|
|
7593
|
+
.ns-edit {
|
|
7316
7594
|
color: var(--text-tertiary);
|
|
7317
7595
|
}
|
|
7318
7596
|
|
|
7319
|
-
.
|
|
7597
|
+
.ns-edit {
|
|
7598
|
+
margin-right: auto;
|
|
7599
|
+
}
|
|
7600
|
+
|
|
7601
|
+
.new-session-modal.ns-resume .ns-stay,
|
|
7602
|
+
.new-session-modal.ns-resume .ns-edit {
|
|
7320
7603
|
visibility: hidden;
|
|
7321
7604
|
}
|
|
7322
7605
|
|
|
7606
|
+
.ns-footer .btn-primary {
|
|
7607
|
+
min-width: 0;
|
|
7608
|
+
overflow: hidden;
|
|
7609
|
+
text-overflow: ellipsis;
|
|
7610
|
+
white-space: nowrap;
|
|
7611
|
+
}
|
|
7612
|
+
|
|
7323
7613
|
.ns-footer .btn-primary:disabled {
|
|
7324
7614
|
opacity: 0.5;
|
|
7325
7615
|
cursor: not-allowed;
|
package/public/terminal-frame.js
CHANGED
|
@@ -11,12 +11,14 @@
|
|
|
11
11
|
const modsOf = (e) =>
|
|
12
12
|
[e.ctrlKey && 'ctrl', e.altKey && 'alt', e.shiftKey && 'shift', e.metaKey && 'meta'].filter(Boolean).join('+');
|
|
13
13
|
|
|
14
|
-
// A claim names a key by its physical code, or by its character when the layout moves it.
|
|
14
|
+
// A claim names a key by its physical code, or by its character when the layout moves it. A press
|
|
15
|
+
// that AltGr turns into a character (Polish AltGr+S is 'ś') is matched by its character only.
|
|
15
16
|
function keySigs(e) {
|
|
16
17
|
const mods = modsOf(e);
|
|
17
|
-
return [
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
return [
|
|
19
|
+
e.code && !e.getModifierState?.('AltGraph') && `${mods}|c:${e.code}`,
|
|
20
|
+
typeof e.key === 'string' && `${mods}|k:${e.key.toLowerCase()}`,
|
|
21
|
+
].filter(Boolean);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
const claimSig = (c) => keySigs(c)[0];
|
|
@@ -1,18 +1,51 @@
|
|
|
1
|
-
// claude-hub-sdk 1.
|
|
2
|
-
// Claude Code Hub SDK
|
|
3
|
-
//
|
|
4
|
-
|
|
1
|
+
// claude-hub-sdk 1.4.0 (sha256 7d559a8888ce). Copied by npm run sdk:sync in claude-code-hub. Do not edit.
|
|
2
|
+
// Claude Code Hub SDK: the one rule that names a key press, shared by the hub page, client.js and
|
|
3
|
+
// stub.js. The hub serves it to its page as /sdk/keys.js; mount() and sdk:sync put it in front of
|
|
4
|
+
// client.js and stub.js, so an app still loads one file.
|
|
5
|
+
// var, not const: the hub page and an app page each load it as a classic script, and a second
|
|
6
|
+
// const declaration in the same page would throw.
|
|
7
|
+
var ClaudeHubKeys = (() => {
|
|
5
8
|
'use strict';
|
|
6
9
|
|
|
7
|
-
//
|
|
10
|
+
// Modifiers in ctrl, alt, shift, meta order, joined by '+' to the key. macOS composes
|
|
11
|
+
// Option+<key> into a character (Option+1 is '¡', Option+P is 'π') and holding Control does not
|
|
12
|
+
// undo it, so e.key alone cannot name these presses there. e.code is the physical key, which is
|
|
13
|
+
// wrong for non-US layouts, hence only as a fallback. No fallback under AltGraph: Chromium sets it
|
|
14
|
+
// when AltGr types a character, and that press is text. Takes a real KeyboardEvent or a forwarded
|
|
15
|
+
// {key, code} payload; a payload without code degrades to key. A payload needs no AltGraph: the
|
|
16
|
+
// SDK forwards only a press whose key already names a bound combo.
|
|
17
|
+
// Self-contained on purpose: cck puts its source text into a sandboxed frame.
|
|
8
18
|
function comboOf(e) {
|
|
9
19
|
const lower = typeof e.key === 'string' ? e.key.toLowerCase() : '';
|
|
10
|
-
const m = /^(?:Key|Digit)([A-Z1-9])$/.exec(e.code || '');
|
|
20
|
+
const m = !e.getModifierState?.('AltGraph') && /^(?:Key|Digit)([A-Z1-9])$/.exec(e.code || '');
|
|
11
21
|
const key = /^[a-z1-9]$/.test(lower) ? lower : m ? m[1].toLowerCase() : e.key;
|
|
12
22
|
const mods = [e.ctrlKey && 'ctrl', e.altKey && 'alt', e.shiftKey && 'shift', e.metaKey && 'meta'];
|
|
13
23
|
return [...mods, key].filter(Boolean).join('+');
|
|
14
24
|
}
|
|
15
25
|
|
|
26
|
+
// The keys of a combo as a help row shows them: ['Ctrl', 'Alt', 'P'], or ['⌃', '⌥', 'P'] on macOS.
|
|
27
|
+
// The {n} of a numbered combo reads 1…9.
|
|
28
|
+
function keyParts(combo, mac) {
|
|
29
|
+
const mods = mac
|
|
30
|
+
? { ctrl: '⌃', alt: '⌥', shift: '⇧', meta: '⌘' }
|
|
31
|
+
: { ctrl: 'Ctrl', alt: 'Alt', shift: 'Shift', meta: 'Win' };
|
|
32
|
+
const named = { '{n}': '1…9', ArrowLeft: '←', ArrowRight: '→', ArrowUp: '↑', ArrowDown: '↓' };
|
|
33
|
+
return combo.split('+').map((k) => mods[k] ?? named[k] ?? (k.length === 1 ? k.toUpperCase() : k));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { comboOf, keyParts };
|
|
37
|
+
})();
|
|
38
|
+
|
|
39
|
+
if (typeof module === 'object' && module.exports) module.exports = ClaudeHubKeys;
|
|
40
|
+
|
|
41
|
+
// Claude Code Hub SDK stub: what an app serves when no hub runs it. Under a hub, the hub hands the
|
|
42
|
+
// app its real SDK (client.js) in place of this file. The API must match client.js; a test checks it.
|
|
43
|
+
((root) => {
|
|
44
|
+
'use strict';
|
|
45
|
+
|
|
46
|
+
// keys.js comes first in the synced file. Required as a module, stub.js loads it itself.
|
|
47
|
+
const { comboOf } = typeof ClaudeHubKeys === 'object' ? ClaudeHubKeys : require('./keys');
|
|
48
|
+
|
|
16
49
|
function createClaudeHub(win) {
|
|
17
50
|
let hub = null;
|
|
18
51
|
const off = () => {};
|
|
@@ -43,6 +76,7 @@
|
|
|
43
76
|
can: (action) => !!standaloneFn(action),
|
|
44
77
|
forwards: () => false,
|
|
45
78
|
forwardCombos: () => [],
|
|
79
|
+
keyLabel: () => null,
|
|
46
80
|
closeGuard() {},
|
|
47
81
|
openExternal(url) {
|
|
48
82
|
win.open(url, '_blank', 'noopener');
|
package/server.js
CHANGED
|
@@ -4015,6 +4015,12 @@ const panesFileStamp = () => {
|
|
|
4015
4015
|
}
|
|
4016
4016
|
};
|
|
4017
4017
|
let panesStamp;
|
|
4018
|
+
// File panes render the file once; this watch tells the boards when one changes. An editor
|
|
4019
|
+
// or an agent often saves several times in a row, so each path waits for a quiet 200 ms.
|
|
4020
|
+
const paneFileWatcher = chokidar.watch([], { ignoreInitial: true });
|
|
4021
|
+
const paneFileTimers = new Map();
|
|
4022
|
+
let paneFilesWatched = new Set();
|
|
4023
|
+
let paneFileWatchQueued = false;
|
|
4018
4024
|
const panes = createPaneStore({
|
|
4019
4025
|
load: () => {
|
|
4020
4026
|
const stamp = panesFileStamp();
|
|
@@ -4026,7 +4032,37 @@ const panes = createPaneStore({
|
|
|
4026
4032
|
writeJsonAtomic(PANES_FILE, data);
|
|
4027
4033
|
panesStamp = panesFileStamp();
|
|
4028
4034
|
},
|
|
4029
|
-
onChange: (sessionId, layout) =>
|
|
4035
|
+
onChange: (sessionId, layout) => {
|
|
4036
|
+
broadcast({ type: 'pane:changed', sessionId, layout });
|
|
4037
|
+
queuePaneFileWatch();
|
|
4038
|
+
},
|
|
4039
|
+
});
|
|
4040
|
+
|
|
4041
|
+
// Deferred: the store's first load reports changes before `panes` is assigned, and a reload
|
|
4042
|
+
// reports each changed session, which one sync covers.
|
|
4043
|
+
function queuePaneFileWatch() {
|
|
4044
|
+
if (paneFileWatchQueued) return;
|
|
4045
|
+
paneFileWatchQueued = true;
|
|
4046
|
+
setImmediate(() => {
|
|
4047
|
+
paneFileWatchQueued = false;
|
|
4048
|
+
const next = panes.fileTargets();
|
|
4049
|
+
const added = [...next].filter((p) => !paneFilesWatched.has(p));
|
|
4050
|
+
const gone = [...paneFilesWatched].filter((p) => !next.has(p));
|
|
4051
|
+
if (added.length) paneFileWatcher.add(added);
|
|
4052
|
+
if (gone.length) paneFileWatcher.unwatch(gone);
|
|
4053
|
+
paneFilesWatched = next;
|
|
4054
|
+
});
|
|
4055
|
+
}
|
|
4056
|
+
paneFileWatcher.on('all', (event, filePath) => {
|
|
4057
|
+
if (event !== 'add' && event !== 'change' && event !== 'unlink') return;
|
|
4058
|
+
clearTimeout(paneFileTimers.get(filePath));
|
|
4059
|
+
paneFileTimers.set(
|
|
4060
|
+
filePath,
|
|
4061
|
+
setTimeout(() => {
|
|
4062
|
+
paneFileTimers.delete(filePath);
|
|
4063
|
+
broadcast({ type: 'pane:file-changed', path: filePath });
|
|
4064
|
+
}, 200),
|
|
4065
|
+
);
|
|
4030
4066
|
});
|
|
4031
4067
|
chokidar.watch(PANES_FILE, { ignoreInitial: true }).on('all', (event) => {
|
|
4032
4068
|
if (event === 'add' || event === 'change') panes.reload();
|
|
@@ -4143,7 +4179,8 @@ function parseReviewBody(body) {
|
|
|
4143
4179
|
selector: code(c?.selector, 500),
|
|
4144
4180
|
}));
|
|
4145
4181
|
if (items.some((c) => !c.comment)) throw previewError(400, 'every comment needs text');
|
|
4146
|
-
|
|
4182
|
+
const locate = oneLine(source.locate, 600) || null;
|
|
4183
|
+
return { kind: source.kind, label, path: source.kind === 'file' ? source.path : null, locate, items };
|
|
4147
4184
|
}
|
|
4148
4185
|
|
|
4149
4186
|
function formatReviewMarkdown(src, items) {
|
|
@@ -4152,6 +4189,7 @@ function formatReviewMarkdown(src, items) {
|
|
|
4152
4189
|
'',
|
|
4153
4190
|
`Each comment is the user's instruction about the quoted text.${src.path ? ' A quote missing from the source means the source changed after the review: say so.' : ''}`,
|
|
4154
4191
|
];
|
|
4192
|
+
if (src.locate) parts.push('', src.locate);
|
|
4155
4193
|
items.forEach((c, i) => {
|
|
4156
4194
|
const where = [c.line && `line ${c.line}`, c.heading && `under "${c.heading}"`].filter(Boolean).join(', ');
|
|
4157
4195
|
parts.push('', `## ${i + 1}${where ? ` (${where})` : ''}`, '');
|
|
@@ -4491,6 +4529,7 @@ async function runRetention() {
|
|
|
4491
4529
|
const opts = { known: scan?.ids, maxAgeMs: retentionMs(CLAUDE_DIR) };
|
|
4492
4530
|
const markers = dispatched.prune(opts);
|
|
4493
4531
|
const layouts = panes.prune(opts);
|
|
4532
|
+
if (layouts) queuePaneFileWatch();
|
|
4494
4533
|
const reviews = await pruneSessionDirs(REVIEW_DIR, opts);
|
|
4495
4534
|
const contexts = await pruneContextStatus(CONTEXT_STATUS_DIR, opts);
|
|
4496
4535
|
const wts = worktrees.prune(scan?.dirs);
|