claude-code-kanban 7.23.0 → 7.25.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/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 +491 -44
- package/public/index.html +44 -4
- package/public/style.css +148 -18
- 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>
|
|
@@ -837,11 +841,8 @@
|
|
|
837
841
|
</div>
|
|
838
842
|
<div class="ns-row ns-extra">
|
|
839
843
|
<label class="ns-label" for="ns-prompt">Prompt</label>
|
|
840
|
-
<div class="ns-field ns-prompt-field">
|
|
844
|
+
<div class="ns-field ns-prompt-field mic-field">
|
|
841
845
|
<textarea id="ns-prompt" class="form-input" rows="4" placeholder="Optional. Sent as the first message. Paste or drop images to attach."></textarea>
|
|
842
|
-
<button type="button" id="ns-mic" class="ns-mic" title="Dictate" aria-label="Dictate" aria-pressed="false" hidden>
|
|
843
|
-
<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>
|
|
844
|
-
</button>
|
|
845
846
|
<div id="ns-images" class="ns-images" hidden></div>
|
|
846
847
|
</div>
|
|
847
848
|
</div>
|
|
@@ -883,6 +884,10 @@
|
|
|
883
884
|
</div>
|
|
884
885
|
</div>
|
|
885
886
|
</div>
|
|
887
|
+
<div id="ns-args-row" class="ns-row" hidden>
|
|
888
|
+
<span class="ns-label">Args</span>
|
|
889
|
+
<div class="ns-field"><code id="ns-args" class="ns-hint" title="Claude arguments this session starts with"></code></div>
|
|
890
|
+
</div>
|
|
886
891
|
<div id="ns-error" class="ns-error" hidden></div>
|
|
887
892
|
</div>
|
|
888
893
|
<div class="modal-footer ns-footer">
|
|
@@ -895,6 +900,41 @@
|
|
|
895
900
|
</div>
|
|
896
901
|
</div>
|
|
897
902
|
|
|
903
|
+
<div id="task-modal" class="modal-overlay top-aligned" onclick="closeTaskDialog()">
|
|
904
|
+
<div class="modal task-modal" onclick="event.stopPropagation()" role="dialog" aria-labelledby="task-modal-title">
|
|
905
|
+
<div class="modal-header">
|
|
906
|
+
<h3 class="modal-title" id="task-modal-title">Add task <span id="task-modal-session" class="task-modal-session"></span></h3>
|
|
907
|
+
<span id="task-modal-mic" class="voice-mic" role="status" hidden>
|
|
908
|
+
<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>
|
|
909
|
+
<span id="task-modal-mic-label">Listening</span>
|
|
910
|
+
</span>
|
|
911
|
+
<button class="modal-close" aria-label="Close dialog" onclick="closeTaskDialog()">
|
|
912
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
|
|
913
|
+
</button>
|
|
914
|
+
</div>
|
|
915
|
+
<div class="modal-body ns-body">
|
|
916
|
+
<div class="ns-row">
|
|
917
|
+
<label class="ns-label" for="task-subject">Title</label>
|
|
918
|
+
<div class="ns-field">
|
|
919
|
+
<input type="text" id="task-subject" class="form-input" placeholder="What to do" autocomplete="off" data-bwignore data-1p-ignore data-lpignore="true">
|
|
920
|
+
</div>
|
|
921
|
+
</div>
|
|
922
|
+
<div class="ns-row">
|
|
923
|
+
<label class="ns-label" for="task-description">Description</label>
|
|
924
|
+
<div class="ns-field">
|
|
925
|
+
<textarea id="task-description" class="form-input" rows="4" placeholder="Optional"></textarea>
|
|
926
|
+
</div>
|
|
927
|
+
</div>
|
|
928
|
+
<div id="task-error" class="ns-error" hidden></div>
|
|
929
|
+
</div>
|
|
930
|
+
<div class="modal-footer ns-footer">
|
|
931
|
+
<span class="task-modal-hint"><kbd>Enter</kbd> adds · <kbd>Shift</kbd>+<kbd>Enter</kbd> new line</span>
|
|
932
|
+
<button type="button" class="btn btn-secondary" onclick="closeTaskDialog()">Cancel</button>
|
|
933
|
+
<button type="button" id="task-save" class="btn btn-primary" onclick="saveTaskDialog()" disabled>Add</button>
|
|
934
|
+
</div>
|
|
935
|
+
</div>
|
|
936
|
+
</div>
|
|
937
|
+
|
|
898
938
|
<div id="pane-pop" class="modal-overlay" onclick="closePanePop()">
|
|
899
939
|
<div class="modal pane-pop-dialog" role="dialog" aria-labelledby="pane-pop-title" onclick="event.stopPropagation()">
|
|
900
940
|
<div class="modal-header">
|
package/public/style.css
CHANGED
|
@@ -8675,7 +8675,8 @@ body.light .msg-assistant .msg-text {
|
|
|
8675
8675
|
color: var(--text-muted);
|
|
8676
8676
|
}
|
|
8677
8677
|
|
|
8678
|
-
.confirm-hint kbd
|
|
8678
|
+
.confirm-hint kbd,
|
|
8679
|
+
.task-modal-hint kbd {
|
|
8679
8680
|
font-family: var(--mono);
|
|
8680
8681
|
font-size: 10px;
|
|
8681
8682
|
border: 1px solid var(--border);
|
|
@@ -9107,7 +9108,7 @@ select.form-input option:checked {
|
|
|
9107
9108
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
|
9108
9109
|
}
|
|
9109
9110
|
|
|
9110
|
-
|
|
9111
|
+
.review-pop-input,
|
|
9111
9112
|
.review-card-edit {
|
|
9112
9113
|
width: 100%;
|
|
9113
9114
|
box-sizing: border-box;
|
|
@@ -9182,8 +9183,9 @@ select.form-input option:checked {
|
|
|
9182
9183
|
gap: 6px;
|
|
9183
9184
|
}
|
|
9184
9185
|
|
|
9185
|
-
|
|
9186
|
-
|
|
9186
|
+
/* :where() keeps the specificity as it was, so the button.review-* rules above still win. */
|
|
9187
|
+
#review-pop button:not(:where(.mic-btn)),
|
|
9188
|
+
.review-panel button:not(:where(.mic-btn)) {
|
|
9187
9189
|
padding: 3px 10px;
|
|
9188
9190
|
font: inherit;
|
|
9189
9191
|
color: var(--text-primary);
|
|
@@ -9623,6 +9625,10 @@ select.form-input option:checked {
|
|
|
9623
9625
|
gap: 12px;
|
|
9624
9626
|
}
|
|
9625
9627
|
|
|
9628
|
+
.ns-row[hidden] {
|
|
9629
|
+
display: none;
|
|
9630
|
+
}
|
|
9631
|
+
|
|
9626
9632
|
.new-session-modal.ns-resume .ns-extra {
|
|
9627
9633
|
display: none;
|
|
9628
9634
|
}
|
|
@@ -9797,18 +9803,20 @@ select.form-input option:checked {
|
|
|
9797
9803
|
}
|
|
9798
9804
|
|
|
9799
9805
|
.ns-body textarea.form-input {
|
|
9800
|
-
|
|
9806
|
+
field-sizing: content;
|
|
9807
|
+
min-height: 86px;
|
|
9808
|
+
max-height: 50vh;
|
|
9801
9809
|
}
|
|
9802
9810
|
|
|
9803
|
-
.
|
|
9811
|
+
.mic-field {
|
|
9804
9812
|
position: relative;
|
|
9805
9813
|
}
|
|
9806
9814
|
|
|
9807
|
-
.
|
|
9815
|
+
.mic-field:has(.mic-btn) textarea {
|
|
9808
9816
|
padding-right: 36px;
|
|
9809
9817
|
}
|
|
9810
9818
|
|
|
9811
|
-
.
|
|
9819
|
+
.mic-btn {
|
|
9812
9820
|
position: absolute;
|
|
9813
9821
|
top: 6px;
|
|
9814
9822
|
right: 6px;
|
|
@@ -9825,33 +9833,29 @@ select.form-input option:checked {
|
|
|
9825
9833
|
cursor: pointer;
|
|
9826
9834
|
}
|
|
9827
9835
|
|
|
9828
|
-
.
|
|
9829
|
-
display: none;
|
|
9830
|
-
}
|
|
9831
|
-
|
|
9832
|
-
.ns-mic svg {
|
|
9836
|
+
.mic-btn svg {
|
|
9833
9837
|
width: 15px;
|
|
9834
9838
|
height: 15px;
|
|
9835
9839
|
}
|
|
9836
9840
|
|
|
9837
|
-
.
|
|
9841
|
+
.mic-btn:hover {
|
|
9838
9842
|
color: var(--text-primary);
|
|
9839
9843
|
background: var(--bg-hover);
|
|
9840
9844
|
}
|
|
9841
9845
|
|
|
9842
|
-
.
|
|
9846
|
+
.mic-btn[aria-pressed='true'] {
|
|
9843
9847
|
color: var(--danger);
|
|
9844
|
-
animation:
|
|
9848
|
+
animation: mic-pulse 1.2s ease-in-out infinite;
|
|
9845
9849
|
}
|
|
9846
9850
|
|
|
9847
|
-
@keyframes
|
|
9851
|
+
@keyframes mic-pulse {
|
|
9848
9852
|
50% {
|
|
9849
9853
|
opacity: 0.45;
|
|
9850
9854
|
}
|
|
9851
9855
|
}
|
|
9852
9856
|
|
|
9853
9857
|
@media (prefers-reduced-motion: reduce) {
|
|
9854
|
-
.
|
|
9858
|
+
.mic-btn[aria-pressed='true'] {
|
|
9855
9859
|
animation: none;
|
|
9856
9860
|
}
|
|
9857
9861
|
}
|
|
@@ -9977,6 +9981,132 @@ select.form-input option:checked {
|
|
|
9977
9981
|
}
|
|
9978
9982
|
/* #endregion */
|
|
9979
9983
|
|
|
9984
|
+
/* #region TASK_DIALOG */
|
|
9985
|
+
.modal.task-modal {
|
|
9986
|
+
width: 560px;
|
|
9987
|
+
max-width: calc(100vw - 32px);
|
|
9988
|
+
}
|
|
9989
|
+
|
|
9990
|
+
.task-modal-session {
|
|
9991
|
+
margin-left: 6px;
|
|
9992
|
+
font-size: 12px;
|
|
9993
|
+
font-weight: 400;
|
|
9994
|
+
color: var(--text-tertiary);
|
|
9995
|
+
}
|
|
9996
|
+
|
|
9997
|
+
.task-modal .ns-label {
|
|
9998
|
+
white-space: nowrap;
|
|
9999
|
+
}
|
|
10000
|
+
|
|
10001
|
+
.modal[data-voice] {
|
|
10002
|
+
border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
|
|
10003
|
+
animation: voice-glow 1.6s ease-in-out infinite;
|
|
10004
|
+
}
|
|
10005
|
+
|
|
10006
|
+
@keyframes voice-glow {
|
|
10007
|
+
0%,
|
|
10008
|
+
100% {
|
|
10009
|
+
box-shadow:
|
|
10010
|
+
0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent),
|
|
10011
|
+
0 0 18px color-mix(in srgb, var(--accent) 18%, transparent);
|
|
10012
|
+
}
|
|
10013
|
+
50% {
|
|
10014
|
+
box-shadow:
|
|
10015
|
+
0 0 0 1px color-mix(in srgb, var(--accent) 60%, transparent),
|
|
10016
|
+
0 0 34px color-mix(in srgb, var(--accent) 35%, transparent);
|
|
10017
|
+
}
|
|
10018
|
+
}
|
|
10019
|
+
|
|
10020
|
+
.voice-mic {
|
|
10021
|
+
display: inline-flex;
|
|
10022
|
+
align-items: center;
|
|
10023
|
+
gap: 6px;
|
|
10024
|
+
margin-left: auto;
|
|
10025
|
+
margin-right: 8px;
|
|
10026
|
+
padding: 3px 10px 3px 8px;
|
|
10027
|
+
border-radius: 999px;
|
|
10028
|
+
background: color-mix(in srgb, var(--accent) 14%, transparent);
|
|
10029
|
+
color: var(--accent);
|
|
10030
|
+
font-size: 12px;
|
|
10031
|
+
font-weight: 500;
|
|
10032
|
+
}
|
|
10033
|
+
|
|
10034
|
+
.voice-mic[hidden] {
|
|
10035
|
+
display: none;
|
|
10036
|
+
}
|
|
10037
|
+
|
|
10038
|
+
.voice-mic svg {
|
|
10039
|
+
width: 14px;
|
|
10040
|
+
height: 14px;
|
|
10041
|
+
animation: voice-mic 1.2s ease-in-out infinite;
|
|
10042
|
+
}
|
|
10043
|
+
|
|
10044
|
+
@keyframes voice-mic {
|
|
10045
|
+
50% {
|
|
10046
|
+
opacity: 0.4;
|
|
10047
|
+
}
|
|
10048
|
+
}
|
|
10049
|
+
|
|
10050
|
+
.modal[data-voice] .form-input[readonly] {
|
|
10051
|
+
cursor: default;
|
|
10052
|
+
}
|
|
10053
|
+
|
|
10054
|
+
.task-modal-hint {
|
|
10055
|
+
margin-right: auto;
|
|
10056
|
+
font-size: 11px;
|
|
10057
|
+
color: var(--text-muted);
|
|
10058
|
+
}
|
|
10059
|
+
|
|
10060
|
+
.task-modal .form-input.task-filled {
|
|
10061
|
+
animation: task-filled 0.6s ease-out;
|
|
10062
|
+
}
|
|
10063
|
+
|
|
10064
|
+
@keyframes task-filled {
|
|
10065
|
+
from {
|
|
10066
|
+
border-color: var(--accent);
|
|
10067
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
|
|
10068
|
+
}
|
|
10069
|
+
}
|
|
10070
|
+
|
|
10071
|
+
.task-modal .form-input.task-moved {
|
|
10072
|
+
animation: task-moved 0.35s ease-out;
|
|
10073
|
+
}
|
|
10074
|
+
|
|
10075
|
+
@keyframes task-moved {
|
|
10076
|
+
from {
|
|
10077
|
+
opacity: 0.2;
|
|
10078
|
+
transform: translateY(48px);
|
|
10079
|
+
}
|
|
10080
|
+
}
|
|
10081
|
+
|
|
10082
|
+
.task-modal .form-input.task-titling {
|
|
10083
|
+
background:
|
|
10084
|
+
linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--accent) 14%, transparent) 50%, transparent 100%) 0 0
|
|
10085
|
+
/ 200% 100% var(--bg-elevated);
|
|
10086
|
+
animation: task-titling 1.2s linear infinite;
|
|
10087
|
+
cursor: progress;
|
|
10088
|
+
}
|
|
10089
|
+
|
|
10090
|
+
@keyframes task-titling {
|
|
10091
|
+
from {
|
|
10092
|
+
background-position: 100% 0;
|
|
10093
|
+
}
|
|
10094
|
+
to {
|
|
10095
|
+
background-position: -100% 0;
|
|
10096
|
+
}
|
|
10097
|
+
}
|
|
10098
|
+
|
|
10099
|
+
@media (prefers-reduced-motion: reduce) {
|
|
10100
|
+
.task-modal .form-input.task-filled,
|
|
10101
|
+
.task-modal .form-input.task-moved,
|
|
10102
|
+
.task-modal .form-input.task-titling,
|
|
10103
|
+
.modal[data-voice],
|
|
10104
|
+
.voice-mic svg {
|
|
10105
|
+
animation: none;
|
|
10106
|
+
}
|
|
10107
|
+
}
|
|
10108
|
+
/* #endregion */
|
|
10109
|
+
|
|
9980
10110
|
/* #region TOOL_STATS */
|
|
9981
10111
|
.modal.tool-stats-modal {
|
|
9982
10112
|
width: 90%;
|
|
@@ -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.
|