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/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.7.0 (sha256 904e53d87ec4). Copied by npm run sdk:sync in claude-code-hub. Do not edit.
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
- return { comboOf, keyParts };
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 = {