claude-code-kanban 7.18.0 → 7.20.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/lib/ask.js +215 -0
- package/package.json +1 -1
- package/plugin/plugins/claude-code-kanban/.claude-plugin/plugin.json +1 -1
- package/plugin/plugins/claude-code-kanban/hooks/ask.ts +163 -0
- package/plugin/plugins/claude-code-kanban/hooks/index.ts +2 -0
- package/plugin/plugins/claude-code-kanban/skills/ask/SKILL.md +44 -0
- package/plugin/plugins/claude-code-kanban/skills/ask/references/playground.md +25 -0
- package/plugin/plugins/claude-code-kanban/tests/ask.test.ts +100 -0
- package/plugin/plugins/claude-code-kanban/tests/show.test.ts +1 -1
- package/public/app.js +286 -56
- package/public/index.html +24 -3
- package/public/style.css +176 -1
- package/public/vendor/claude-hub-sdk.js +18 -3
- package/server.js +11 -0
package/public/style.css
CHANGED
|
@@ -4460,6 +4460,181 @@ body.show-resizing iframe {
|
|
|
4460
4460
|
|
|
4461
4461
|
/* #endregion */
|
|
4462
4462
|
|
|
4463
|
+
/* #region ASK */
|
|
4464
|
+
.ask-modal {
|
|
4465
|
+
position: absolute;
|
|
4466
|
+
inset: 0;
|
|
4467
|
+
z-index: 5;
|
|
4468
|
+
display: flex;
|
|
4469
|
+
align-items: center;
|
|
4470
|
+
justify-content: center;
|
|
4471
|
+
background: color-mix(in srgb, var(--bg-deep) 55%, transparent);
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4474
|
+
.ask-modal[hidden] {
|
|
4475
|
+
display: none;
|
|
4476
|
+
}
|
|
4477
|
+
|
|
4478
|
+
.ask-box {
|
|
4479
|
+
display: flex;
|
|
4480
|
+
flex-direction: column;
|
|
4481
|
+
box-sizing: border-box;
|
|
4482
|
+
width: min(560px, calc(100% - 32px));
|
|
4483
|
+
max-height: calc(100% - 32px);
|
|
4484
|
+
background: var(--bg-surface);
|
|
4485
|
+
border: 1px solid var(--border);
|
|
4486
|
+
border-radius: 10px;
|
|
4487
|
+
box-shadow: 0 12px 32px -12px rgba(0, 0, 0, 0.45);
|
|
4488
|
+
overflow: hidden;
|
|
4489
|
+
}
|
|
4490
|
+
|
|
4491
|
+
.ask-box[data-size='lg'] {
|
|
4492
|
+
width: min(960px, calc(100% - 32px));
|
|
4493
|
+
}
|
|
4494
|
+
|
|
4495
|
+
.ask-box[data-size='full'] {
|
|
4496
|
+
width: calc(100% - 32px);
|
|
4497
|
+
height: calc(100% - 32px);
|
|
4498
|
+
}
|
|
4499
|
+
|
|
4500
|
+
.ask-head,
|
|
4501
|
+
.ask-foot {
|
|
4502
|
+
display: flex;
|
|
4503
|
+
flex: none;
|
|
4504
|
+
align-items: center;
|
|
4505
|
+
gap: 8px;
|
|
4506
|
+
padding: 6px 8px 6px 12px;
|
|
4507
|
+
color: var(--text-tertiary);
|
|
4508
|
+
font-size: 11px;
|
|
4509
|
+
}
|
|
4510
|
+
|
|
4511
|
+
.ask-head {
|
|
4512
|
+
border-bottom: 1px solid var(--border);
|
|
4513
|
+
}
|
|
4514
|
+
|
|
4515
|
+
.ask-foot {
|
|
4516
|
+
justify-content: space-between;
|
|
4517
|
+
border-top: 1px solid var(--border);
|
|
4518
|
+
}
|
|
4519
|
+
|
|
4520
|
+
.ask-dot {
|
|
4521
|
+
display: inline-grid;
|
|
4522
|
+
place-items: center;
|
|
4523
|
+
flex: none;
|
|
4524
|
+
width: 16px;
|
|
4525
|
+
height: 16px;
|
|
4526
|
+
border-radius: 50%;
|
|
4527
|
+
background: var(--accent);
|
|
4528
|
+
color: #fff;
|
|
4529
|
+
font-size: 10px;
|
|
4530
|
+
font-weight: 700;
|
|
4531
|
+
}
|
|
4532
|
+
|
|
4533
|
+
.ask-title {
|
|
4534
|
+
flex: 1;
|
|
4535
|
+
min-width: 0;
|
|
4536
|
+
overflow: hidden;
|
|
4537
|
+
color: var(--text-primary);
|
|
4538
|
+
font-size: 12px;
|
|
4539
|
+
font-weight: 600;
|
|
4540
|
+
text-overflow: ellipsis;
|
|
4541
|
+
white-space: nowrap;
|
|
4542
|
+
}
|
|
4543
|
+
|
|
4544
|
+
.ask-x {
|
|
4545
|
+
display: inline-grid;
|
|
4546
|
+
place-items: center;
|
|
4547
|
+
width: 22px;
|
|
4548
|
+
height: 22px;
|
|
4549
|
+
padding: 0;
|
|
4550
|
+
border: 0;
|
|
4551
|
+
border-radius: 4px;
|
|
4552
|
+
background: transparent;
|
|
4553
|
+
color: var(--text-tertiary);
|
|
4554
|
+
cursor: pointer;
|
|
4555
|
+
}
|
|
4556
|
+
|
|
4557
|
+
.ask-x svg {
|
|
4558
|
+
width: 14px;
|
|
4559
|
+
height: 14px;
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4562
|
+
.ask-foot button {
|
|
4563
|
+
padding: 3px 12px;
|
|
4564
|
+
border: 1px solid var(--border);
|
|
4565
|
+
border-radius: 5px;
|
|
4566
|
+
background: transparent;
|
|
4567
|
+
color: var(--text-secondary);
|
|
4568
|
+
font: inherit;
|
|
4569
|
+
cursor: pointer;
|
|
4570
|
+
}
|
|
4571
|
+
|
|
4572
|
+
.ask-x:hover,
|
|
4573
|
+
.ask-foot button:hover {
|
|
4574
|
+
background: var(--bg-hover);
|
|
4575
|
+
color: var(--text-primary);
|
|
4576
|
+
}
|
|
4577
|
+
|
|
4578
|
+
.ask-body {
|
|
4579
|
+
display: flex;
|
|
4580
|
+
flex: 1 1 auto;
|
|
4581
|
+
flex-direction: column;
|
|
4582
|
+
min-height: 0;
|
|
4583
|
+
}
|
|
4584
|
+
|
|
4585
|
+
.ask-frame {
|
|
4586
|
+
display: block;
|
|
4587
|
+
width: 100%;
|
|
4588
|
+
border: 0;
|
|
4589
|
+
background: var(--bg-surface);
|
|
4590
|
+
}
|
|
4591
|
+
|
|
4592
|
+
.ask-box[data-size='full'] .ask-frame {
|
|
4593
|
+
flex: 1;
|
|
4594
|
+
}
|
|
4595
|
+
|
|
4596
|
+
.ask-missing {
|
|
4597
|
+
margin: 0;
|
|
4598
|
+
padding: 16px;
|
|
4599
|
+
color: var(--text-tertiary);
|
|
4600
|
+
}
|
|
4601
|
+
|
|
4602
|
+
.ask-reopen {
|
|
4603
|
+
max-width: 240px;
|
|
4604
|
+
border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
|
|
4605
|
+
color: var(--text-secondary);
|
|
4606
|
+
}
|
|
4607
|
+
|
|
4608
|
+
.ask-reopen-n {
|
|
4609
|
+
overflow: hidden;
|
|
4610
|
+
text-overflow: ellipsis;
|
|
4611
|
+
white-space: nowrap;
|
|
4612
|
+
}
|
|
4613
|
+
|
|
4614
|
+
.ask-reopen .ask-dot {
|
|
4615
|
+
width: 14px;
|
|
4616
|
+
height: 14px;
|
|
4617
|
+
font-size: 9px;
|
|
4618
|
+
animation: ask-pulse 1.6s ease-out infinite;
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4621
|
+
@keyframes ask-pulse {
|
|
4622
|
+
0% {
|
|
4623
|
+
box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent);
|
|
4624
|
+
}
|
|
4625
|
+
100% {
|
|
4626
|
+
box-shadow: 0 0 0 6px transparent;
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
4629
|
+
|
|
4630
|
+
@media (prefers-reduced-motion: reduce) {
|
|
4631
|
+
.ask-reopen .ask-dot {
|
|
4632
|
+
animation: none;
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
|
|
4636
|
+
/* #endregion */
|
|
4637
|
+
|
|
4463
4638
|
/* #region PANES */
|
|
4464
4639
|
.pane-strip {
|
|
4465
4640
|
display: none;
|
|
@@ -5058,7 +5233,7 @@ body.show-resizing iframe {
|
|
|
5058
5233
|
color: var(--accent);
|
|
5059
5234
|
}
|
|
5060
5235
|
|
|
5061
|
-
.pane-view.frame-focus .pane-keys {
|
|
5236
|
+
.pane-view.frame-focus:not(.keys-bridged) .pane-keys {
|
|
5062
5237
|
display: flex;
|
|
5063
5238
|
}
|
|
5064
5239
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// claude-hub-sdk 1.
|
|
1
|
+
// claude-hub-sdk 1.8.0 (sha256 9a67dd2aa534). 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.
|
|
@@ -33,7 +33,21 @@ var ClaudeHubKeys = (() => {
|
|
|
33
33
|
return combo.split('+').map((k) => mods[k] ?? named[k] ?? (k.length === 1 ? k.toUpperCase() : k));
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// A host claim (protocol §5.2) names a press by its physical code, or by its character when the layout
|
|
37
|
+
// moves the key: {ctrlKey?, altKey?, shiftKey?, metaKey?, code, key: 'Unidentified'} or {…, key, code: ''}.
|
|
38
|
+
// A press matches a claim when one of its two signatures is the claim's first. AltGr types a
|
|
39
|
+
// character, so its press matches by character only. cck's frames use the same format.
|
|
40
|
+
function keySigs(e) {
|
|
41
|
+
const mods = [e.ctrlKey && 'ctrl', e.altKey && 'alt', e.shiftKey && 'shift', e.metaKey && 'meta']
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.join('+');
|
|
44
|
+
return [
|
|
45
|
+
e.code && !e.getModifierState?.('AltGraph') && `${mods}|c:${e.code}`,
|
|
46
|
+
typeof e.key === 'string' && e.key !== 'Unidentified' && `${mods}|k:${e.key.toLowerCase()}`,
|
|
47
|
+
].filter(Boolean);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { comboOf, keyParts, keySigs };
|
|
37
51
|
})();
|
|
38
52
|
|
|
39
53
|
if (typeof module === 'object' && module.exports) module.exports = ClaudeHubKeys;
|
|
@@ -69,9 +83,10 @@ if (typeof module === 'object' && module.exports) module.exports = ClaudeHubKeys
|
|
|
69
83
|
bindTheme: () => off,
|
|
70
84
|
handle() {},
|
|
71
85
|
publish() {},
|
|
72
|
-
invoke(action, params = {}) {
|
|
86
|
+
invoke(action, params = {}, opts = {}) {
|
|
73
87
|
const target = standaloneFn(action)?.(params);
|
|
74
88
|
if (!target) return Promise.resolve({ ok: false, reason: 'unhandled' });
|
|
89
|
+
if (opts.open === 'pane') return Promise.resolve({ ok: true, handledBy: 'standalone', url: target });
|
|
75
90
|
win.open(target, '_blank', 'noopener');
|
|
76
91
|
return Promise.resolve({ ok: true, handledBy: 'standalone' });
|
|
77
92
|
},
|
package/server.js
CHANGED
|
@@ -51,6 +51,7 @@ const { createTerminalClient } = require('./lib/terminal-client');
|
|
|
51
51
|
const { kanbotOn, kanbotModel, createKanbot, findCostCli, readCostServer } = require('./lib/kanbot');
|
|
52
52
|
const { AGENT_ID_RE } = require('./cli');
|
|
53
53
|
const { createShowStore, mountShowRoutes, showBodyParser, SHOW_PATH } = require('./lib/show');
|
|
54
|
+
const { createAskStore, mountAskRoutes, askBodyParser, ASK_PATH } = require('./lib/ask');
|
|
54
55
|
const { readLiveSessions, isPidAlive, noteReplacedSessions } = require('./lib/live-sessions');
|
|
55
56
|
const { createProcStats } = require('./lib/proc-stats');
|
|
56
57
|
const { createUserGroupStore } = require('./lib/user-groups');
|
|
@@ -666,6 +667,7 @@ app.param('name', (_req, res, next, val) => {
|
|
|
666
667
|
});
|
|
667
668
|
|
|
668
669
|
app.use(SHOW_PATH, showBodyParser());
|
|
670
|
+
app.use(ASK_PATH, askBodyParser());
|
|
669
671
|
// Parse JSON bodies. A review of 50 comments at the field caps is about 450 kB.
|
|
670
672
|
app.use(express.json({ limit: '1mb' }));
|
|
671
673
|
// #endregion
|
|
@@ -3326,6 +3328,7 @@ let savedTerminals = terminalsFile.load() || {};
|
|
|
3326
3328
|
function saveTerminals() {
|
|
3327
3329
|
const kept = new Set(Object.values(savedTerminals.terminalIds || {}));
|
|
3328
3330
|
const showSessions = show.prune((id) => kept.has(id) || terminal.hasTerminal(id));
|
|
3331
|
+
ask.prune((id) => terminal.hasTerminal(id));
|
|
3329
3332
|
terminalsFile.save({ ...savedTerminals, showSessions });
|
|
3330
3333
|
}
|
|
3331
3334
|
|
|
@@ -3460,6 +3463,14 @@ mountShowRoutes(app, {
|
|
|
3460
3463
|
broadcast,
|
|
3461
3464
|
onRemoved: (sessionId, paths) => panes.removeShow(sessionId, paths),
|
|
3462
3465
|
});
|
|
3466
|
+
const ask = createAskStore();
|
|
3467
|
+
mountAskRoutes(app, {
|
|
3468
|
+
store: ask,
|
|
3469
|
+
authorized: (token) => terminal.authorized(token),
|
|
3470
|
+
hasTerminal: (id) => terminal.hasTerminal(id),
|
|
3471
|
+
readPreviewFile,
|
|
3472
|
+
broadcast,
|
|
3473
|
+
});
|
|
3463
3474
|
|
|
3464
3475
|
// Served from node_modules, never a CDN: any script on this page can use the token.
|
|
3465
3476
|
const XTERM_FILES = {
|