clideck 1.31.19 → 1.31.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clideck",
3
- "version": "1.31.19",
3
+ "version": "1.31.21",
4
4
  "description": "One screen for all your AI coding agents — run, monitor, and manage multiple CLI agents from a single browser tab",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -9,7 +9,9 @@
9
9
  "scripts": {
10
10
  "start": "node server.js",
11
11
  "build:css": "tailwindcss -i src/input.css -o public/tailwind.css --minify",
12
- "prepublishOnly": "npm run build:css"
12
+ "prepublishOnly": "npm run build:css",
13
+ "smoke:providers": "node tests/providers/run.js",
14
+ "smoke:menu": "node tests/providers/menu-detection.test.js"
13
15
  },
14
16
  "keywords": [
15
17
  "terminal",
package/public/js/app.js CHANGED
@@ -236,7 +236,7 @@ function connect() {
236
236
  break;
237
237
  }
238
238
  case 'telemetry.autosetup.result': {
239
- const toast = document.querySelector(msg.commandId ? `[data-command-id="${msg.commandId}"]` : `[data-setup-preset="${msg.presetId}"]`);
239
+ const toast = document.querySelector(msg.commandId ? `.telemetry-setup-toast[data-command-id="${msg.commandId}"]` : `.telemetry-setup-toast[data-setup-preset="${msg.presetId}"]`);
240
240
  if (!toast) break;
241
241
  const actionsEl = toast.querySelector('.setup-actions');
242
242
  if (msg.success) {
@@ -554,7 +554,7 @@ function showTelemetrySetup(commandId, sessionId) {
554
554
  toast.dataset.setupPreset = preset.presetId;
555
555
  if (sessionId) toast.dataset.sessionId = sessionId;
556
556
  toast.dataset.commandId = commandId;
557
- toast.className = 'fixed bottom-5 right-5 z-[500] w-[360px] bg-slate-800/95 backdrop-blur-sm border border-slate-700/60 rounded-xl shadow-2xl shadow-black/60';
557
+ toast.className = 'telemetry-setup-toast fixed bottom-5 right-5 z-[500] w-[360px] bg-slate-800/95 backdrop-blur-sm border border-slate-700/60 rounded-xl shadow-2xl shadow-black/60';
558
558
  toast.style.opacity = '0';
559
559
  toast.style.transform = 'translateY(12px)';
560
560
  toast.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
@@ -213,17 +213,16 @@ export function openCreator() {
213
213
  card.id = 'session-creator';
214
214
  card.className = 'p-3 border-b border-slate-700/50 bg-slate-800/30';
215
215
  card.innerHTML = `
216
- ${(state.cfg.projects?.length) ? `
217
216
  <div class="text-[10px] font-semibold uppercase tracking-wider text-slate-500 mb-1.5">Select project</div>
218
217
  <input type="hidden" id="creator-project" value="">
219
218
  <button type="button" id="creator-project-trigger" class="w-full px-3 py-1.5 text-xs bg-slate-900 border border-slate-700 rounded-md text-slate-400 text-left flex items-center justify-between outline-none hover:border-slate-500 transition-colors cursor-pointer mb-2">
220
219
  <span id="creator-project-label">Select project</span>
221
220
  <span class="text-slate-600 ml-2">&#9662;</span>
222
- </button>` : ''}
221
+ </button>
223
222
  <div class="text-[10px] font-semibold uppercase tracking-wider text-slate-500 mb-1.5">Session name</div>
224
223
  <input id="creator-name" type="text" maxlength="35" placeholder="Session name"
225
224
  class="w-full px-3 py-2 text-sm bg-slate-900 border border-slate-700 rounded-md text-slate-200 placeholder-slate-500 outline-none focus:border-blue-500 transition-colors mb-2">
226
- <div id="creator-cwd-wrap" class="flex items-center gap-1.5 mb-2 ${(state.cfg.projects?.length) ? 'hidden' : ''}">
225
+ <div id="creator-cwd-wrap" class="flex items-center gap-1.5 mb-2 hidden">
227
226
  <input id="creator-cwd" type="text" value="${esc(defaultPath)}" placeholder="Working directory"
228
227
  class="flex-1 px-3 py-1.5 text-xs bg-slate-900 border border-slate-700 rounded-md text-slate-400 placeholder-slate-600 outline-none focus:border-blue-500 transition-colors font-mono">
229
228
  <button id="creator-browse" class="flex-shrink-0 w-7 h-7 flex items-center justify-center rounded-md border border-slate-700 text-slate-500 hover:text-slate-300 hover:bg-slate-700 transition-colors" title="Browse">
@@ -4,6 +4,7 @@
4
4
  import { handleTerminalKey } from './prompts.js';
5
5
 
6
6
  const registry = new Map(); // normalized combo → { pluginId, callback }
7
+ const MAX_OSC52_BYTES = 512 * 1024;
7
8
 
8
9
  // Normalize a KeyboardEvent into a canonical combo string
9
10
  function normalizeEvent(e) {
@@ -84,10 +85,35 @@ export function unregisterAllForPlugin(pluginId) {
84
85
  }
85
86
  }
86
87
 
88
+ function decodeOsc52(data) {
89
+ const parts = String(data || '').split(';');
90
+ if (parts.length < 2) return '';
91
+ const encoded = parts.slice(1).join(';').trim();
92
+ if (!encoded || encoded === '?') return '';
93
+ if (encoded.length > MAX_OSC52_BYTES) return '';
94
+ try {
95
+ const binary = atob(encoded);
96
+ const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0));
97
+ return new TextDecoder().decode(bytes);
98
+ } catch {
99
+ return '';
100
+ }
101
+ }
102
+
103
+ function attachClipboardOscHandler(term) {
104
+ if (!term.parser?.registerOscHandler) return;
105
+ term.parser.registerOscHandler(52, (data) => {
106
+ const text = decodeOsc52(data);
107
+ if (!text || !navigator.clipboard?.writeText) return false;
108
+ return navigator.clipboard.writeText(text).then(() => true, () => false);
109
+ });
110
+ }
111
+
87
112
  // Attach to an xterm terminal instance — xterm's hidden textarea is an input,
88
113
  // so we bypass the isInput check and dispatch directly.
89
114
  // Prompt autocomplete (// trigger) runs first, then hotkey dispatch.
90
115
  export function attachToTerminal(term, presetId) {
116
+ attachClipboardOscHandler(term);
91
117
  term.attachCustomKeyEventHandler((e) => {
92
118
  if (e.type === 'keydown'
93
119
  && e.ctrlKey
@@ -781,7 +781,11 @@ export function select(id) {
781
781
  const dot = document.querySelector(`.group[data-id="${id}"] .unread-dot`);
782
782
  if (dot) dot.classList.add('hidden');
783
783
  updateUnreadBadge();
784
- if (state.filter.tab === 'unread') setTab('all');
784
+ if (state.filter.tab === 'unread') {
785
+ const hasMoreUnread = [...state.terms].some(([otherId, other]) => otherId !== id && other.unread);
786
+ if (hasMoreUnread) applyFilter();
787
+ else setTab('all');
788
+ }
785
789
  }
786
790
  entry.term.scrollToBottom();
787
791
  if (!document.querySelector('[contenteditable="true"]')) entry.term.focus();
package/session-ask.js CHANGED
@@ -202,7 +202,7 @@ async function askSession(payload, sessionsApi, cfg = {}) {
202
202
 
203
203
  const [targetId, target] = findTarget(sessions, callerId, caller, payload.target, cfg);
204
204
  if (target.working) {
205
- throw jsonError(`Target session "${target.name}" is busy. CliDeck ask only sends to idle sessions. Try again later, choose another idle session, or ask the user how to proceed.`, 409);
205
+ throw jsonError(`Target agent "${target.name}" is busy right now. CliDeck ask only sends to idle agents and does not queue requests because the context can become stale. If you need this specific agent, try again later or set a reminder to retry. You can also ask another idle agent.`, 409);
206
206
  }
207
207
 
208
208
  const message = String(payload.message || '').trim();