claude-code-kanban 7.22.0 → 7.24.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 +20 -1
- package/lib/show.js +15 -0
- package/lib/terminal-client.js +8 -3
- package/lib/terminal-host.js +2 -1
- package/lib/terminal.js +47 -14
- package/package.json +2 -1
- package/public/app.js +470 -45
- package/public/index.html +43 -0
- package/public/style.css +146 -8
- package/public/vendor/claude-hub-sdk.js +2 -2
- package/server.js +43 -13
package/public/index.html
CHANGED
|
@@ -814,6 +814,10 @@
|
|
|
814
814
|
<div class="modal new-session-modal" onclick="event.stopPropagation()" role="dialog" aria-labelledby="ns-title">
|
|
815
815
|
<div class="modal-header">
|
|
816
816
|
<h3 class="modal-title" id="ns-title">New session</h3>
|
|
817
|
+
<span id="ns-voice" class="voice-mic" role="status" hidden>
|
|
818
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="2" width="6" height="12" rx="3"/><path d="M5 10a7 7 0 0 0 14 0M12 17v5"/></svg>
|
|
819
|
+
<span>Listening</span>
|
|
820
|
+
</span>
|
|
817
821
|
<button class="modal-close" aria-label="Close dialog" onclick="closeNewSession()">
|
|
818
822
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
|
|
819
823
|
</button>
|
|
@@ -883,6 +887,10 @@
|
|
|
883
887
|
</div>
|
|
884
888
|
</div>
|
|
885
889
|
</div>
|
|
890
|
+
<div id="ns-args-row" class="ns-row" hidden>
|
|
891
|
+
<span class="ns-label">Args</span>
|
|
892
|
+
<div class="ns-field"><code id="ns-args" class="ns-hint" title="Claude arguments this session starts with"></code></div>
|
|
893
|
+
</div>
|
|
886
894
|
<div id="ns-error" class="ns-error" hidden></div>
|
|
887
895
|
</div>
|
|
888
896
|
<div class="modal-footer ns-footer">
|
|
@@ -895,6 +903,41 @@
|
|
|
895
903
|
</div>
|
|
896
904
|
</div>
|
|
897
905
|
|
|
906
|
+
<div id="task-modal" class="modal-overlay top-aligned" onclick="closeTaskDialog()">
|
|
907
|
+
<div class="modal task-modal" onclick="event.stopPropagation()" role="dialog" aria-labelledby="task-modal-title">
|
|
908
|
+
<div class="modal-header">
|
|
909
|
+
<h3 class="modal-title" id="task-modal-title">Add task <span id="task-modal-session" class="task-modal-session"></span></h3>
|
|
910
|
+
<span id="task-modal-mic" class="voice-mic" role="status" hidden>
|
|
911
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="2" width="6" height="12" rx="3"/><path d="M5 10a7 7 0 0 0 14 0M12 17v5"/></svg>
|
|
912
|
+
<span id="task-modal-mic-label">Listening</span>
|
|
913
|
+
</span>
|
|
914
|
+
<button class="modal-close" aria-label="Close dialog" onclick="closeTaskDialog()">
|
|
915
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
|
|
916
|
+
</button>
|
|
917
|
+
</div>
|
|
918
|
+
<div class="modal-body ns-body">
|
|
919
|
+
<div class="ns-row">
|
|
920
|
+
<label class="ns-label" for="task-subject">Title</label>
|
|
921
|
+
<div class="ns-field">
|
|
922
|
+
<input type="text" id="task-subject" class="form-input" placeholder="What to do" autocomplete="off" data-bwignore data-1p-ignore data-lpignore="true">
|
|
923
|
+
</div>
|
|
924
|
+
</div>
|
|
925
|
+
<div class="ns-row">
|
|
926
|
+
<label class="ns-label" for="task-description">Description</label>
|
|
927
|
+
<div class="ns-field">
|
|
928
|
+
<textarea id="task-description" class="form-input" rows="4" placeholder="Optional"></textarea>
|
|
929
|
+
</div>
|
|
930
|
+
</div>
|
|
931
|
+
<div id="task-error" class="ns-error" hidden></div>
|
|
932
|
+
</div>
|
|
933
|
+
<div class="modal-footer ns-footer">
|
|
934
|
+
<span class="task-modal-hint"><kbd>Enter</kbd> adds · <kbd>Shift</kbd>+<kbd>Enter</kbd> new line</span>
|
|
935
|
+
<button type="button" class="btn btn-secondary" onclick="closeTaskDialog()">Cancel</button>
|
|
936
|
+
<button type="button" id="task-save" class="btn btn-primary" onclick="saveTaskDialog()" disabled>Add</button>
|
|
937
|
+
</div>
|
|
938
|
+
</div>
|
|
939
|
+
</div>
|
|
940
|
+
|
|
898
941
|
<div id="pane-pop" class="modal-overlay" onclick="closePanePop()">
|
|
899
942
|
<div class="modal pane-pop-dialog" role="dialog" aria-labelledby="pane-pop-title" onclick="event.stopPropagation()">
|
|
900
943
|
<div class="modal-header">
|
package/public/style.css
CHANGED
|
@@ -69,9 +69,10 @@
|
|
|
69
69
|
/* Tokens derived from theme colors live on body, where color themes set those colors;
|
|
70
70
|
on :root they would resolve to the defaults before a theme applies. */
|
|
71
71
|
body {
|
|
72
|
-
/*
|
|
73
|
-
|
|
74
|
-
--session-active-bg: var(--bg-
|
|
72
|
+
/* bg-deep and bg-elevated sit within a few shades of the dark sidebar, so dark themes raise
|
|
73
|
+
the open session to the hover tone; light themes use bg-elevated. */
|
|
74
|
+
--session-active-bg: var(--bg-hover);
|
|
75
|
+
--session-active-border: color-mix(in srgb, var(--text-primary) 30%, transparent);
|
|
75
76
|
--ansi-black: var(--text-muted);
|
|
76
77
|
--ansi-white: var(--text-secondary);
|
|
77
78
|
--ansi-bright-black: var(--text-tertiary);
|
|
@@ -1059,7 +1060,7 @@ body.booting .boot-splash {
|
|
|
1059
1060
|
|
|
1060
1061
|
.session-item.active {
|
|
1061
1062
|
background: var(--session-active-bg);
|
|
1062
|
-
border-color: var(--border);
|
|
1063
|
+
border-color: var(--session-active-border);
|
|
1063
1064
|
}
|
|
1064
1065
|
|
|
1065
1066
|
.session-item.kb-selected,
|
|
@@ -4125,14 +4126,16 @@ code.ct .ct-has[aria-expanded='false'] .ct-name::after {
|
|
|
4125
4126
|
}
|
|
4126
4127
|
|
|
4127
4128
|
/* Stops above the prompt rows, as the expanded show card does. */
|
|
4128
|
-
.dock-addon.expanded
|
|
4129
|
+
.dock-addon.expanded,
|
|
4130
|
+
.show-card.docked.expanded {
|
|
4129
4131
|
left: 0;
|
|
4130
4132
|
bottom: calc(7.2 * var(--show-term-font, 13px));
|
|
4131
4133
|
width: auto;
|
|
4132
4134
|
border-bottom: 1px solid var(--border);
|
|
4133
4135
|
}
|
|
4134
4136
|
|
|
4135
|
-
.dock-addon.expanded .show-dock-edge
|
|
4137
|
+
.dock-addon.expanded .show-dock-edge,
|
|
4138
|
+
.show-card.docked.expanded .show-dock-edge {
|
|
4136
4139
|
display: none;
|
|
4137
4140
|
}
|
|
4138
4141
|
|
|
@@ -7994,6 +7997,7 @@ body.light {
|
|
|
7994
7997
|
--bg-elevated: #fbfaf9;
|
|
7995
7998
|
--bg-hover: #d2d0cc;
|
|
7996
7999
|
--session-active-bg: var(--bg-elevated);
|
|
8000
|
+
--session-active-border: var(--border);
|
|
7997
8001
|
--border: #cfcbc4;
|
|
7998
8002
|
--text-primary: #0a0a0a;
|
|
7999
8003
|
--text-secondary: #2c2c2c;
|
|
@@ -8671,7 +8675,8 @@ body.light .msg-assistant .msg-text {
|
|
|
8671
8675
|
color: var(--text-muted);
|
|
8672
8676
|
}
|
|
8673
8677
|
|
|
8674
|
-
.confirm-hint kbd
|
|
8678
|
+
.confirm-hint kbd,
|
|
8679
|
+
.task-modal-hint kbd {
|
|
8675
8680
|
font-family: var(--mono);
|
|
8676
8681
|
font-size: 10px;
|
|
8677
8682
|
border: 1px solid var(--border);
|
|
@@ -9619,6 +9624,10 @@ select.form-input option:checked {
|
|
|
9619
9624
|
gap: 12px;
|
|
9620
9625
|
}
|
|
9621
9626
|
|
|
9627
|
+
.ns-row[hidden] {
|
|
9628
|
+
display: none;
|
|
9629
|
+
}
|
|
9630
|
+
|
|
9622
9631
|
.new-session-modal.ns-resume .ns-extra {
|
|
9623
9632
|
display: none;
|
|
9624
9633
|
}
|
|
@@ -9793,7 +9802,9 @@ select.form-input option:checked {
|
|
|
9793
9802
|
}
|
|
9794
9803
|
|
|
9795
9804
|
.ns-body textarea.form-input {
|
|
9796
|
-
|
|
9805
|
+
field-sizing: content;
|
|
9806
|
+
min-height: 86px;
|
|
9807
|
+
max-height: 50vh;
|
|
9797
9808
|
}
|
|
9798
9809
|
|
|
9799
9810
|
.ns-prompt-field {
|
|
@@ -9973,6 +9984,132 @@ select.form-input option:checked {
|
|
|
9973
9984
|
}
|
|
9974
9985
|
/* #endregion */
|
|
9975
9986
|
|
|
9987
|
+
/* #region TASK_DIALOG */
|
|
9988
|
+
.modal.task-modal {
|
|
9989
|
+
width: 560px;
|
|
9990
|
+
max-width: calc(100vw - 32px);
|
|
9991
|
+
}
|
|
9992
|
+
|
|
9993
|
+
.task-modal-session {
|
|
9994
|
+
margin-left: 6px;
|
|
9995
|
+
font-size: 12px;
|
|
9996
|
+
font-weight: 400;
|
|
9997
|
+
color: var(--text-tertiary);
|
|
9998
|
+
}
|
|
9999
|
+
|
|
10000
|
+
.task-modal .ns-label {
|
|
10001
|
+
white-space: nowrap;
|
|
10002
|
+
}
|
|
10003
|
+
|
|
10004
|
+
.modal[data-voice] {
|
|
10005
|
+
border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
|
|
10006
|
+
animation: voice-glow 1.6s ease-in-out infinite;
|
|
10007
|
+
}
|
|
10008
|
+
|
|
10009
|
+
@keyframes voice-glow {
|
|
10010
|
+
0%,
|
|
10011
|
+
100% {
|
|
10012
|
+
box-shadow:
|
|
10013
|
+
0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent),
|
|
10014
|
+
0 0 18px color-mix(in srgb, var(--accent) 18%, transparent);
|
|
10015
|
+
}
|
|
10016
|
+
50% {
|
|
10017
|
+
box-shadow:
|
|
10018
|
+
0 0 0 1px color-mix(in srgb, var(--accent) 60%, transparent),
|
|
10019
|
+
0 0 34px color-mix(in srgb, var(--accent) 35%, transparent);
|
|
10020
|
+
}
|
|
10021
|
+
}
|
|
10022
|
+
|
|
10023
|
+
.voice-mic {
|
|
10024
|
+
display: inline-flex;
|
|
10025
|
+
align-items: center;
|
|
10026
|
+
gap: 6px;
|
|
10027
|
+
margin-left: auto;
|
|
10028
|
+
margin-right: 8px;
|
|
10029
|
+
padding: 3px 10px 3px 8px;
|
|
10030
|
+
border-radius: 999px;
|
|
10031
|
+
background: color-mix(in srgb, var(--accent) 14%, transparent);
|
|
10032
|
+
color: var(--accent);
|
|
10033
|
+
font-size: 12px;
|
|
10034
|
+
font-weight: 500;
|
|
10035
|
+
}
|
|
10036
|
+
|
|
10037
|
+
.voice-mic[hidden] {
|
|
10038
|
+
display: none;
|
|
10039
|
+
}
|
|
10040
|
+
|
|
10041
|
+
.voice-mic svg {
|
|
10042
|
+
width: 14px;
|
|
10043
|
+
height: 14px;
|
|
10044
|
+
animation: voice-mic 1.2s ease-in-out infinite;
|
|
10045
|
+
}
|
|
10046
|
+
|
|
10047
|
+
@keyframes voice-mic {
|
|
10048
|
+
50% {
|
|
10049
|
+
opacity: 0.4;
|
|
10050
|
+
}
|
|
10051
|
+
}
|
|
10052
|
+
|
|
10053
|
+
.modal[data-voice] .form-input[readonly] {
|
|
10054
|
+
cursor: default;
|
|
10055
|
+
}
|
|
10056
|
+
|
|
10057
|
+
.task-modal-hint {
|
|
10058
|
+
margin-right: auto;
|
|
10059
|
+
font-size: 11px;
|
|
10060
|
+
color: var(--text-muted);
|
|
10061
|
+
}
|
|
10062
|
+
|
|
10063
|
+
.task-modal .form-input.task-filled {
|
|
10064
|
+
animation: task-filled 0.6s ease-out;
|
|
10065
|
+
}
|
|
10066
|
+
|
|
10067
|
+
@keyframes task-filled {
|
|
10068
|
+
from {
|
|
10069
|
+
border-color: var(--accent);
|
|
10070
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
|
|
10071
|
+
}
|
|
10072
|
+
}
|
|
10073
|
+
|
|
10074
|
+
.task-modal .form-input.task-moved {
|
|
10075
|
+
animation: task-moved 0.35s ease-out;
|
|
10076
|
+
}
|
|
10077
|
+
|
|
10078
|
+
@keyframes task-moved {
|
|
10079
|
+
from {
|
|
10080
|
+
opacity: 0.2;
|
|
10081
|
+
transform: translateY(48px);
|
|
10082
|
+
}
|
|
10083
|
+
}
|
|
10084
|
+
|
|
10085
|
+
.task-modal .form-input.task-titling {
|
|
10086
|
+
background:
|
|
10087
|
+
linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--accent) 14%, transparent) 50%, transparent 100%) 0 0
|
|
10088
|
+
/ 200% 100% var(--bg-elevated);
|
|
10089
|
+
animation: task-titling 1.2s linear infinite;
|
|
10090
|
+
cursor: progress;
|
|
10091
|
+
}
|
|
10092
|
+
|
|
10093
|
+
@keyframes task-titling {
|
|
10094
|
+
from {
|
|
10095
|
+
background-position: 100% 0;
|
|
10096
|
+
}
|
|
10097
|
+
to {
|
|
10098
|
+
background-position: -100% 0;
|
|
10099
|
+
}
|
|
10100
|
+
}
|
|
10101
|
+
|
|
10102
|
+
@media (prefers-reduced-motion: reduce) {
|
|
10103
|
+
.task-modal .form-input.task-filled,
|
|
10104
|
+
.task-modal .form-input.task-moved,
|
|
10105
|
+
.task-modal .form-input.task-titling,
|
|
10106
|
+
.modal[data-voice],
|
|
10107
|
+
.voice-mic svg {
|
|
10108
|
+
animation: none;
|
|
10109
|
+
}
|
|
10110
|
+
}
|
|
10111
|
+
/* #endregion */
|
|
10112
|
+
|
|
9976
10113
|
/* #region TOOL_STATS */
|
|
9977
10114
|
.modal.tool-stats-modal {
|
|
9978
10115
|
width: 90%;
|
|
@@ -10526,6 +10663,7 @@ select.form-input option:checked {
|
|
|
10526
10663
|
--bg-elevated: #fbfaf9;
|
|
10527
10664
|
--bg-hover: #d2d0cc;
|
|
10528
10665
|
--session-active-bg: var(--bg-elevated);
|
|
10666
|
+
--session-active-border: var(--border);
|
|
10529
10667
|
--border: #cfcbc4;
|
|
10530
10668
|
--text-primary: #0a0a0a;
|
|
10531
10669
|
--text-secondary: #2c2c2c;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// claude-hub-sdk 1.
|
|
1
|
+
// claude-hub-sdk 1.9.0 (sha256 537745aa3415). Copied by npm run sdk:sync in claude-code-hub. Do not edit.
|
|
2
2
|
// Claude Code Hub SDK: the one rule that names a key press, shared by the hub page, client.js and
|
|
3
3
|
// stub.js. The hub serves it to its page as /sdk/keys.js; mount() and sdk:sync put it in front of
|
|
4
4
|
// client.js and stub.js, so an app still loads one file.
|
|
@@ -18,7 +18,7 @@ var ClaudeHubKeys = (() => {
|
|
|
18
18
|
function comboOf(e) {
|
|
19
19
|
const lower = typeof e.key === 'string' ? e.key.toLowerCase() : '';
|
|
20
20
|
const m = !e.getModifierState?.('AltGraph') && /^(?:Key|Digit)([A-Z1-9])$/.exec(e.code || '');
|
|
21
|
-
const key = /^[a-z1-9]$/.test(lower) ? lower : m ? m[1].toLowerCase() : e.key;
|
|
21
|
+
const key = /^[a-z1-9]$/.test(lower) ? lower : m ? m[1].toLowerCase() : e.key === ' ' ? 'Space' : e.key;
|
|
22
22
|
const mods = [e.ctrlKey && 'ctrl', e.altKey && 'alt', e.shiftKey && 'shift', e.metaKey && 'meta'];
|
|
23
23
|
return [...mods, key].filter(Boolean).join('+');
|
|
24
24
|
}
|
package/server.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
if (process.argv.includes("--install") || process.argv.includes("--uninstall")) {
|
|
4
|
+
const { runInstall, runUninstall } = require("./install");
|
|
5
|
+
const yes = process.argv.includes("--yes");
|
|
6
|
+
(process.argv.includes("--install") ? runInstall({ yes }) : runUninstall())
|
|
7
|
+
.then(() => process.exit(0))
|
|
8
|
+
.catch(e => { console.error(e.message); process.exit(1); });
|
|
9
|
+
} else if (!require("./cli").runCli(process.argv)) {
|
|
10
|
+
startServer();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Not indented: the formatter is off for this file, and a reindent would rewrite every line.
|
|
14
|
+
// The requires sit inside, so a CLI call does not load the server's modules.
|
|
15
|
+
function startServer() {
|
|
16
|
+
|
|
3
17
|
// #region SETUP
|
|
4
18
|
const express = require('express');
|
|
5
19
|
const path = require('node:path');
|
|
@@ -79,19 +93,6 @@ const { foldAgentLines } = require('./lib/agent-log');
|
|
|
79
93
|
const { projectMatcher, normalizeProjectPath, sessionProjectKey } = require('./public/project-match');
|
|
80
94
|
const { getParentVerdict, setParentVerdict, peekParentVerdict } = require('./lib/parent-cache');
|
|
81
95
|
|
|
82
|
-
if (process.argv.includes("--install") || process.argv.includes("--uninstall")) {
|
|
83
|
-
const { runInstall, runUninstall } = require("./install");
|
|
84
|
-
const yes = process.argv.includes("--yes");
|
|
85
|
-
(process.argv.includes("--install") ? runInstall({ yes }) : runUninstall())
|
|
86
|
-
.then(() => process.exit(0))
|
|
87
|
-
.catch(e => { console.error(e.message); process.exit(1); });
|
|
88
|
-
} else if (!require("./cli").runCli(process.argv)) {
|
|
89
|
-
startServer();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Not indented: the formatter is off for this file, and a reindent would rewrite every line.
|
|
93
|
-
function startServer() {
|
|
94
|
-
|
|
95
96
|
const app = express();
|
|
96
97
|
const PORT = parseInt(getArgValue('port') || process.env.PORT || '3541', 10);
|
|
97
98
|
|
|
@@ -3392,6 +3393,35 @@ app.post('/api/terminal/launched/:id', (req, res) => {
|
|
|
3392
3393
|
res.status(204).end();
|
|
3393
3394
|
});
|
|
3394
3395
|
|
|
3396
|
+
// The hub's session.paste action: text into a session's input box, with Enter when `submit`.
|
|
3397
|
+
// claude queues a prompt sent while it works. Refused (409) only on a dialog, where the keys would answer it:
|
|
3398
|
+
// the screen check misses a permission prompt with no "Enter to confirm", so the waiting marker is checked too.
|
|
3399
|
+
app.post('/api/terminal/paste/:id', terminalRoute(async (req, res) => {
|
|
3400
|
+
if (!terminal.authorized(req.get('x-terminal-token'))) return res.status(401).json({ error: 'invalid terminal token' });
|
|
3401
|
+
const { text, submit } = req.body || {};
|
|
3402
|
+
if (typeof text !== 'string' || !text.trim()) return res.status(400).json({ error: 'text is required' });
|
|
3403
|
+
const id = req.params.id;
|
|
3404
|
+
if (!terminal.isRunning(id)) return res.status(404).json({ error: 'no running terminal for this session' });
|
|
3405
|
+
if (isWaitingOnUser(id) || !(await terminal.paste(id, text, submit === true))) {
|
|
3406
|
+
return res.status(409).json({ error: 'the session shows a dialog' });
|
|
3407
|
+
}
|
|
3408
|
+
res.status(204).end();
|
|
3409
|
+
}));
|
|
3410
|
+
|
|
3411
|
+
// The hub's session.live action: dictated words typed into the input box as they come.
|
|
3412
|
+
// `from` is what the last call typed; the box loses what differs and gets the rest.
|
|
3413
|
+
app.post('/api/terminal/type/:id', terminalRoute(async (req, res) => {
|
|
3414
|
+
if (!terminal.authorized(req.get('x-terminal-token'))) return res.status(401).json({ error: 'invalid terminal token' });
|
|
3415
|
+
const { from = '', text, submit } = req.body || {};
|
|
3416
|
+
if (typeof text !== 'string' || typeof from !== 'string') return res.status(400).json({ error: 'text is required' });
|
|
3417
|
+
const id = req.params.id;
|
|
3418
|
+
if (!terminal.isRunning(id)) return res.status(404).json({ error: 'no running terminal for this session' });
|
|
3419
|
+
if (isWaitingOnUser(id) || !(await terminal.type(id, from, text, submit === true))) {
|
|
3420
|
+
return res.status(409).json({ error: 'the session shows a dialog' });
|
|
3421
|
+
}
|
|
3422
|
+
res.status(204).end();
|
|
3423
|
+
}));
|
|
3424
|
+
|
|
3395
3425
|
// The New session dialog's "Stay here": the PTY starts headless and the board keeps its view.
|
|
3396
3426
|
// `show` is `claude-code-kanban new` from a shell outside the board: a board tab adds its card
|
|
3397
3427
|
// and attaches, and with no tab to claim it the PTY is ended, because no one would see it.
|