clay-server 2.47.0-beta.1 → 2.47.0-beta.3
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/codex-defaults.js +12 -2
- package/lib/daemon.js +38 -8
- package/lib/kiro-defaults.js +22 -0
- package/lib/project-connection.js +3 -1
- package/lib/project-notifications.js +3 -1
- package/lib/project-sessions.js +28 -9
- package/lib/project.js +42 -10
- package/lib/public/app.js +8 -0
- package/lib/public/css/mobile-nav.css +53 -9
- package/lib/public/css/sidebar.css +24 -1
- package/lib/public/css/tui-attention.css +4 -63
- package/lib/public/index.html +4 -0
- package/lib/public/kiro-avatar.svg +14 -0
- package/lib/public/modules/app-messages.js +14 -12
- package/lib/public/modules/app-notifications.js +12 -4
- package/lib/public/modules/app-panels.js +10 -4
- package/lib/public/modules/app-rate-limit.js +41 -13
- package/lib/public/modules/app-rendering.js +14 -0
- package/lib/public/modules/input.js +4 -3
- package/lib/public/modules/mate-sidebar.js +3 -3
- package/lib/public/modules/session-tui-view.js +4 -40
- package/lib/public/modules/sidebar-mates.js +1 -1
- package/lib/public/modules/sidebar-mobile.js +56 -23
- package/lib/public/modules/sidebar-sessions.js +126 -63
- package/lib/public/modules/tools.js +1 -1
- package/lib/public/modules/tui-grab.js +12 -3
- package/lib/sdk-bridge.js +90 -21
- package/lib/sdk-message-processor.js +4 -4
- package/lib/server.js +4 -0
- package/lib/users-preferences.js +3 -1
- package/lib/yoke/adapters/claude.js +25 -0
- package/lib/yoke/adapters/codex.js +34 -21
- package/lib/yoke/adapters/kiro.js +1170 -0
- package/lib/yoke/index.js +70 -6
- package/lib/yoke/kiro-acp-server.js +328 -0
- package/lib/yoke/vendor-registry.js +55 -0
- package/package.json +4 -3
|
@@ -42,7 +42,7 @@ import { handleMcpServersState } from './mcp-ui.js';
|
|
|
42
42
|
import { handleLoopRegistryUpdated, handleScheduleRunStarted, handleScheduleRunFinished, handleLoopScheduled, isSchedulerOpen, enterCraftingMode, exitCraftingMode, handleLoopRegistryFiles } from './scheduler.js';
|
|
43
43
|
|
|
44
44
|
// --- App module imports ---
|
|
45
|
-
import { scrollToBottom, addToMessages, addUserMessage, addSystemMessage, removeMatePreThinking, appendDelta, finalizeAssistantBlock, addConflictMessage, addContextOverflowMessage, showSuggestionChips, armStickyBottom } from './app-rendering.js';
|
|
45
|
+
import { scrollToBottom, addToMessages, addUserMessage, addSystemMessage, removeMatePreThinking, appendDelta, finalizeAssistantBlock, addConflictMessage, addContextOverflowMessage, showSuggestionChips, armStickyBottom, VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
46
46
|
import { setActivity, startUrgentBlink, stopUrgentBlink, blinkSessionDot, updateCrossProjectBlink } from './app-favicon.js';
|
|
47
47
|
import { setStatus } from './app-connection.js';
|
|
48
48
|
import { handleWhatsNewState, handleWhatsNewSeenResult, setKnownEntries as setWhatsNewKnownEntries } from './whats-new.js';
|
|
@@ -289,7 +289,7 @@ export function processMessage(msg) {
|
|
|
289
289
|
// host lives on document.body (it's position: fixed), so it
|
|
290
290
|
// survives project navigation unless we detach explicitly here.
|
|
291
291
|
detachTuiView();
|
|
292
|
-
store.set({ projectName: msg.project || msg.cwd });
|
|
292
|
+
store.set({ projectName: msg.project || msg.cwd, vendorInfo: msg.vendors || {} });
|
|
293
293
|
if (msg.cwd) store.set({ cwd: msg.cwd });
|
|
294
294
|
if (msg.slug) store.set({ currentSlug: msg.slug });
|
|
295
295
|
try { var _is = store.snap(); localStorage.setItem("clay-project-name-" + (_is.currentSlug || "default"), _is.projectName); } catch (e) {}
|
|
@@ -464,6 +464,7 @@ export function processMessage(msg) {
|
|
|
464
464
|
if (msg.vendor && !store.get('vendorSelectionLocked')) _miUpdate.currentVendor = msg.vendor;
|
|
465
465
|
if (msg.availableVendors) _miUpdate.availableVendors = msg.availableVendors;
|
|
466
466
|
if (msg.installedVendors) _miUpdate.installedVendors = msg.installedVendors;
|
|
467
|
+
if (msg.capabilities) _miUpdate.vendorCapabilities = msg.capabilities;
|
|
467
468
|
store.set(_miUpdate);
|
|
468
469
|
updateSettingsModels(_modelVal, msg.models || []);
|
|
469
470
|
break;
|
|
@@ -579,6 +580,12 @@ export function processMessage(msg) {
|
|
|
579
580
|
updateSessionPresence(msg.presence || {});
|
|
580
581
|
break;
|
|
581
582
|
|
|
583
|
+
// Project's last-used vendor. Seeds the sidebar's "New session" button
|
|
584
|
+
// on connect and keeps other tabs in sync after someone picks a vendor.
|
|
585
|
+
case "last_vendor":
|
|
586
|
+
store.set({ lastVendor: msg.vendor || "" });
|
|
587
|
+
break;
|
|
588
|
+
|
|
582
589
|
case "cursor_move":
|
|
583
590
|
handleRemoteCursorMove(msg);
|
|
584
591
|
break;
|
|
@@ -696,15 +703,10 @@ export function processMessage(msg) {
|
|
|
696
703
|
}
|
|
697
704
|
}
|
|
698
705
|
if (_avi && _avIcon) {
|
|
699
|
-
if (msg.vendor
|
|
700
|
-
_avIcon.src =
|
|
701
|
-
_avIcon.alt =
|
|
702
|
-
_avi.title = "
|
|
703
|
-
_avi.classList.remove("hidden");
|
|
704
|
-
} else if (msg.vendor === "codex") {
|
|
705
|
-
_avIcon.src = "/codex-avatar.png";
|
|
706
|
-
_avIcon.alt = "Codex";
|
|
707
|
-
_avi.title = "Codex session";
|
|
706
|
+
if (msg.vendor && VENDOR_AVATARS[msg.vendor]) {
|
|
707
|
+
_avIcon.src = VENDOR_AVATARS[msg.vendor];
|
|
708
|
+
_avIcon.alt = VENDOR_NAMES[msg.vendor] || msg.vendor;
|
|
709
|
+
_avi.title = (VENDOR_NAMES[msg.vendor] || msg.vendor) + " session";
|
|
708
710
|
_avi.classList.remove("hidden");
|
|
709
711
|
} else {
|
|
710
712
|
_avi.classList.add("hidden");
|
|
@@ -1358,7 +1360,7 @@ export function processMessage(msg) {
|
|
|
1358
1360
|
var _lm = store.get('pendingLoginModal');
|
|
1359
1361
|
store.set({ pendingLoginModal: null });
|
|
1360
1362
|
openTuiModal(msg.id, _lm.slug, {
|
|
1361
|
-
sessionTitle: (_lm.vendor
|
|
1363
|
+
sessionTitle: (VENDOR_NAMES[_lm.vendor] || "Claude Code") + " login",
|
|
1362
1364
|
projectName: _lm.slug,
|
|
1363
1365
|
compact: true,
|
|
1364
1366
|
});
|
|
@@ -19,6 +19,14 @@ var bannerContainer = null;
|
|
|
19
19
|
var bellBtn = null;
|
|
20
20
|
var badgeEl = null;
|
|
21
21
|
|
|
22
|
+
function getVendorLoginCommand(vendor) {
|
|
23
|
+
var vendors = store.get('vendorInfo') || {};
|
|
24
|
+
var info = vendors[vendor];
|
|
25
|
+
if (info && info.loginCommand) return info.loginCommand;
|
|
26
|
+
var fallbacks = { codex: "codex login --device-auth", claude: "claude login" };
|
|
27
|
+
return fallbacks[vendor] || fallbacks.claude;
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
// --- Pending TUI attention tracking ---
|
|
23
31
|
// Mirrors the icon-shake / favicon-blink behavior the SDK side already gets
|
|
24
32
|
// from `pendingPermissions` on project status broadcasts. The notification
|
|
@@ -252,7 +260,7 @@ function showBanner(notif, autoDismissMs) {
|
|
|
252
260
|
removeBanner(banner);
|
|
253
261
|
dismissNotif(notif.id);
|
|
254
262
|
var authMeta = notif.meta || {};
|
|
255
|
-
startLoginInModal(authMeta.loginCommand || (
|
|
263
|
+
startLoginInModal(authMeta.loginCommand || getVendorLoginCommand(authMeta.vendor || "claude"), authMeta.vendor || "claude");
|
|
256
264
|
showLoginReminderBanner();
|
|
257
265
|
});
|
|
258
266
|
}
|
|
@@ -332,7 +340,7 @@ function startLoginInModal(loginCommand, vendor) {
|
|
|
332
340
|
if (authReminderVisible) return;
|
|
333
341
|
var ws = getWs();
|
|
334
342
|
if (!ws || ws.readyState !== 1) return;
|
|
335
|
-
var cmd = loginCommand || (vendor
|
|
343
|
+
var cmd = loginCommand || getVendorLoginCommand(vendor);
|
|
336
344
|
var slug = currentProjectSlug();
|
|
337
345
|
if (!slug) { startLoginCommand(cmd); return; }
|
|
338
346
|
store.set({ pendingLoginModal: { slug: slug, vendor: vendor || "claude" } });
|
|
@@ -366,7 +374,7 @@ export function autoStartLoginIfNeeded(msg) {
|
|
|
366
374
|
if (authReminderVisible) return false;
|
|
367
375
|
var vendor = msg.vendor || "claude";
|
|
368
376
|
var cmd = msg.loginCommand
|
|
369
|
-
|| (vendor
|
|
377
|
+
|| getVendorLoginCommand(vendor);
|
|
370
378
|
startLoginInModal(cmd, vendor);
|
|
371
379
|
showLoginReminderBanner();
|
|
372
380
|
return true;
|
|
@@ -426,7 +434,7 @@ function showLoginReminderBanner() {
|
|
|
426
434
|
export function showAuthRequiredBanner(msg) {
|
|
427
435
|
if (!bannerContainer) return;
|
|
428
436
|
var vendor = (msg && (msg.vendor || (msg.meta && msg.meta.vendor))) || "claude";
|
|
429
|
-
var loginCommand = (msg && (msg.loginCommand || (msg.meta && msg.meta.loginCommand))) || (vendor
|
|
437
|
+
var loginCommand = (msg && (msg.loginCommand || (msg.meta && msg.meta.loginCommand))) || getVendorLoginCommand(vendor);
|
|
430
438
|
activeAuthRequiredMsg = Object.assign({}, msg || {}, {
|
|
431
439
|
id: (msg && msg.id) || ("_auth_" + Date.now()),
|
|
432
440
|
type: "auth_required",
|
|
@@ -88,11 +88,12 @@ var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
|
88
88
|
var EFFORT_LEVELS_BY_VENDOR = {
|
|
89
89
|
claude: ["low", "medium", "high", "xhigh", "max"],
|
|
90
90
|
codex: ["minimal", "low", "medium", "high", "xhigh"],
|
|
91
|
+
kiro: ["low", "medium", "high", "xhigh", "max"],
|
|
91
92
|
};
|
|
92
93
|
var THINKING_OPTIONS = ["disabled", "adaptive", "budget"];
|
|
93
94
|
var CODEX_APPROVAL_OPTIONS = [
|
|
94
95
|
{ value: "never", label: "Auto" },
|
|
95
|
-
{ value: "
|
|
96
|
+
{ value: "untrusted", label: "Untrusted" },
|
|
96
97
|
{ value: "on-request", label: "Ask" },
|
|
97
98
|
];
|
|
98
99
|
var CODEX_SANDBOX_OPTIONS = [
|
|
@@ -418,7 +419,8 @@ export function initPanels() {
|
|
|
418
419
|
var vendorToggleWrap = $("vendor-toggle-wrap");
|
|
419
420
|
var vendorBtnClaude = $("vendor-btn-claude");
|
|
420
421
|
var vendorBtnCodex = $("vendor-btn-codex");
|
|
421
|
-
var
|
|
422
|
+
var vendorBtnKiro = $("vendor-btn-kiro");
|
|
423
|
+
var vendorBtns = { claude: vendorBtnClaude, codex: vendorBtnCodex, kiro: vendorBtnKiro };
|
|
422
424
|
|
|
423
425
|
function updateVendorToggle() {
|
|
424
426
|
var installed = store.get('installedVendors') || [];
|
|
@@ -447,6 +449,7 @@ export function initPanels() {
|
|
|
447
449
|
|
|
448
450
|
if (vendorBtnClaude) vendorBtnClaude.addEventListener("click", function() { onVendorClick("claude"); });
|
|
449
451
|
if (vendorBtnCodex) vendorBtnCodex.addEventListener("click", function() { onVendorClick("codex"); });
|
|
452
|
+
if (vendorBtnKiro) vendorBtnKiro.addEventListener("click", function() { onVendorClick("kiro"); });
|
|
450
453
|
|
|
451
454
|
// --- Reactive UI sync ---
|
|
452
455
|
store.subscribe(function (state, prev) {
|
|
@@ -638,11 +641,14 @@ export function updateConfigChip() {
|
|
|
638
641
|
rebuildModeList();
|
|
639
642
|
rebuildEffortBar();
|
|
640
643
|
|
|
641
|
-
//
|
|
644
|
+
// MODE remains Claude-specific until adapter modes become capabilities.
|
|
642
645
|
var isClaude = vendor === "claude";
|
|
643
|
-
// MODE, THINKING, BETA are Claude-only
|
|
644
646
|
if (configModeList && configModeList.parentElement) configModeList.parentElement.style.display = isClaude ? "" : "none";
|
|
645
647
|
rebuildThinkingSection();
|
|
648
|
+
// capabilities.thinking means "emits a thinking stream" (true for every
|
|
649
|
+
// vendor), NOT "accepts thinking config". The adaptive/extended toggle and
|
|
650
|
+
// budget only feed Claude queries (sm.currentThinking), so the section
|
|
651
|
+
// stays Claude-only until a dedicated thinkingConfig capability exists.
|
|
646
652
|
if (configThinkingSection) configThinkingSection.style.display = isClaude ? "" : "none";
|
|
647
653
|
// BETA section deprecated (1M context is now standard)
|
|
648
654
|
if (configBetaSection) configBetaSection.style.display = "none";
|
|
@@ -23,20 +23,37 @@ var fastModeIndicatorEl = null;
|
|
|
23
23
|
// --- Internal helpers ---
|
|
24
24
|
|
|
25
25
|
function getVendorUsageMeta(vendor) {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
var vendors = store.get('vendorInfo') || {};
|
|
27
|
+
var info = vendors[vendor];
|
|
28
|
+
if (info && info.usageDashboard) return info.usageDashboard;
|
|
29
|
+
var fallbacks = {
|
|
30
|
+
codex: {
|
|
28
31
|
icon: "/codex-avatar.png",
|
|
29
32
|
alt: "Codex",
|
|
30
33
|
href: "https://chatgpt.com/admin/usage",
|
|
31
34
|
title: "Check usage on ChatGPT",
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
},
|
|
36
|
+
claude: {
|
|
37
|
+
icon: "/claude-code-avatar.png",
|
|
38
|
+
alt: "Claude Code",
|
|
39
|
+
href: "https://claude.ai/settings/usage",
|
|
40
|
+
title: "Check usage on claude.ai",
|
|
41
|
+
},
|
|
39
42
|
};
|
|
43
|
+
return fallbacks[vendor] || fallbacks.claude;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function vendorTracksRateLimits(vendor) {
|
|
47
|
+
var vendors = store.get('vendorInfo') || {};
|
|
48
|
+
var info = vendors[vendor];
|
|
49
|
+
if (info) return info.rateLimitTracking !== false;
|
|
50
|
+
var legacyTrackedVendors = ["claude", "codex"];
|
|
51
|
+
return legacyTrackedVendors.indexOf(vendor) !== -1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function vendorSupportsScheduledMessages(vendor) {
|
|
55
|
+
var scheduledMessageVendors = ["claude"];
|
|
56
|
+
return scheduledMessageVendors.indexOf(vendor) !== -1;
|
|
40
57
|
}
|
|
41
58
|
|
|
42
59
|
function rateLimitTypeLabel(type) {
|
|
@@ -178,8 +195,13 @@ function tickRateLimitUsage() {
|
|
|
178
195
|
|
|
179
196
|
export function initRateLimit() {
|
|
180
197
|
store.subscribe(function(state, prev) {
|
|
181
|
-
if (state.currentVendor !== prev.currentVendor && state.currentVendor
|
|
182
|
-
clearScheduleDelay();
|
|
198
|
+
if (state.currentVendor !== prev.currentVendor && state.currentVendor) {
|
|
199
|
+
if (!vendorSupportsScheduledMessages(state.currentVendor)) clearScheduleDelay();
|
|
200
|
+
}
|
|
201
|
+
if (state.currentVendor !== prev.currentVendor || state.vendorInfo !== prev.vendorInfo) {
|
|
202
|
+
if (rateLimitUsageEl) {
|
|
203
|
+
rateLimitUsageEl.style.display = vendorTracksRateLimits(state.currentVendor || "claude") ? "" : "none";
|
|
204
|
+
}
|
|
183
205
|
}
|
|
184
206
|
});
|
|
185
207
|
}
|
|
@@ -203,7 +225,7 @@ export function handleRateLimitEvent(msg) {
|
|
|
203
225
|
if (rateLimitResetTimer) clearTimeout(rateLimitResetTimer);
|
|
204
226
|
// Auto-switch input to schedule mode: any message typed will be queued for after reset
|
|
205
227
|
var delayUntilReset = msg.resetsAt - Date.now();
|
|
206
|
-
if (delayUntilReset > 0 && (store.get('currentVendor') || "claude")
|
|
228
|
+
if (delayUntilReset > 0 && vendorSupportsScheduledMessages(store.get('currentVendor') || "claude")) {
|
|
207
229
|
setScheduleDelayMs(delayUntilReset + 60000); // +1min buffer after reset
|
|
208
230
|
}
|
|
209
231
|
rateLimitResetTimer = setTimeout(function () {
|
|
@@ -222,6 +244,11 @@ export function handleRateLimitEvent(msg) {
|
|
|
222
244
|
}
|
|
223
245
|
|
|
224
246
|
export function updateRateLimitUsage(msg) {
|
|
247
|
+
var activeVendor = store.get('currentVendor') || "claude";
|
|
248
|
+
if (!vendorTracksRateLimits(activeVendor)) {
|
|
249
|
+
if (rateLimitUsageEl) rateLimitUsageEl.style.display = "none";
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
225
252
|
if (msg.rateLimitType && msg.resetsAt) {
|
|
226
253
|
rateLimitResetState[msg.rateLimitType] = { resetsAt: msg.resetsAt, status: msg.status };
|
|
227
254
|
}
|
|
@@ -238,6 +265,7 @@ export function updateRateLimitUsage(msg) {
|
|
|
238
265
|
var ref = document.getElementById("skip-perms-pill");
|
|
239
266
|
topBarActions.insertBefore(rateLimitUsageEl, ref);
|
|
240
267
|
}
|
|
268
|
+
rateLimitUsageEl.style.display = "";
|
|
241
269
|
|
|
242
270
|
// Build label from available reset times
|
|
243
271
|
var parts = [];
|
|
@@ -251,7 +279,7 @@ export function updateRateLimitUsage(msg) {
|
|
|
251
279
|
}
|
|
252
280
|
|
|
253
281
|
var label = parts.length > 0 ? parts.join(" · ") : "Check usage";
|
|
254
|
-
var vendor =
|
|
282
|
+
var vendor = activeVendor;
|
|
255
283
|
var meta = getVendorUsageMeta(vendor);
|
|
256
284
|
rateLimitUsageEl.href = meta.href;
|
|
257
285
|
rateLimitUsageEl.title = meta.title;
|
|
@@ -14,13 +14,27 @@ import { sendMessage, hasSendableContent } from './input.js';
|
|
|
14
14
|
import { getChatLayout } from './theme.js';
|
|
15
15
|
import { getScheduledMsgEl } from './app-rate-limit.js';
|
|
16
16
|
|
|
17
|
+
// Keep these client constants aligned with lib/yoke/vendor-registry.js. The
|
|
18
|
+
// browser cannot import the server's CommonJS registry directly.
|
|
17
19
|
export var VENDOR_AVATARS = {
|
|
18
20
|
claude: "/claude-code-avatar.png",
|
|
19
21
|
codex: "/codex-avatar.png",
|
|
22
|
+
kiro: "/kiro-avatar.svg",
|
|
20
23
|
};
|
|
21
24
|
export var VENDOR_NAMES = {
|
|
22
25
|
claude: "Claude Code",
|
|
23
26
|
codex: "Codex",
|
|
27
|
+
kiro: "Kiro CLI",
|
|
28
|
+
};
|
|
29
|
+
// Display order for every vendor Clay knows about, installed or not. Pickers
|
|
30
|
+
// render the full list so a missing CLI reads as "not installed yet" rather
|
|
31
|
+
// than "Clay doesn't support it".
|
|
32
|
+
export var VENDOR_ORDER = ["claude", "codex", "kiro"];
|
|
33
|
+
// Where to send the user when they pick a vendor whose CLI isn't installed.
|
|
34
|
+
export var VENDOR_HOMEPAGES = {
|
|
35
|
+
claude: "https://claude.com/product/claude-code",
|
|
36
|
+
codex: "https://openai.com/codex/",
|
|
37
|
+
kiro: "https://kiro.dev/",
|
|
24
38
|
};
|
|
25
39
|
var NEW_MSG_BTN_DEFAULT = "\u2193 Latest";
|
|
26
40
|
var NEW_MSG_BTN_ACTIVITY = "\u2193 New activity";
|
|
@@ -5,6 +5,7 @@ import { checkForMention, showMentionMenu, hideMentionMenu, isMentionMenuVisible
|
|
|
5
5
|
import { store } from './store.js';
|
|
6
6
|
import { mateAvatarUrl } from './avatar.js';
|
|
7
7
|
import { tuiIsActive, tuiSubmitText } from './session-tui-view.js';
|
|
8
|
+
import { VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
8
9
|
|
|
9
10
|
var ctx;
|
|
10
11
|
|
|
@@ -300,9 +301,9 @@ export function sendMessage() {
|
|
|
300
301
|
_vtw2.classList.remove("locked");
|
|
301
302
|
}
|
|
302
303
|
if (_avi && _avIcon) {
|
|
303
|
-
_avIcon.src = _committedVendor
|
|
304
|
-
_avIcon.alt = _committedVendor
|
|
305
|
-
_avi.title = (_committedVendor
|
|
304
|
+
_avIcon.src = VENDOR_AVATARS[_committedVendor] || VENDOR_AVATARS.claude;
|
|
305
|
+
_avIcon.alt = VENDOR_NAMES[_committedVendor] || VENDOR_NAMES.claude;
|
|
306
|
+
_avi.title = (VENDOR_NAMES[_committedVendor] || VENDOR_NAMES.claude) + " session";
|
|
306
307
|
_avi.classList.remove("hidden");
|
|
307
308
|
}
|
|
308
309
|
} else if (_vtw2) {
|
|
@@ -158,9 +158,9 @@ export function showMateSidebar(mateId, mateData) {
|
|
|
158
158
|
if (mateVendorWrap) {
|
|
159
159
|
var available = store.get('availableVendors') || [];
|
|
160
160
|
var mateVendor = mateData.vendor || "claude";
|
|
161
|
-
var vendorIcons = { claude: "/claude-code-avatar.png", codex: "/codex-avatar.png" };
|
|
162
|
-
var vendorNames = { claude: "Claude Code", codex: "Codex" };
|
|
163
|
-
var vendorKeys = ["claude", "codex"];
|
|
161
|
+
var vendorIcons = { claude: "/claude-code-avatar.png", codex: "/codex-avatar.png", kiro: "/kiro-avatar.svg" };
|
|
162
|
+
var vendorNames = { claude: "Claude Code", codex: "Codex", kiro: "Kiro CLI" };
|
|
163
|
+
var vendorKeys = ["claude", "codex", "kiro"];
|
|
164
164
|
mateVendorWrap.innerHTML = "";
|
|
165
165
|
for (var vi = 0; vi < vendorKeys.length; vi++) {
|
|
166
166
|
var vk = vendorKeys[vi];
|
|
@@ -20,15 +20,9 @@ import { getTerminalTheme } from './theme.js';
|
|
|
20
20
|
import { getTerminalFontFamily, getTerminalFontSize, onTerminalFontChange } from './terminal-prefs.js';
|
|
21
21
|
import { showHeaderTuiFont, hideHeaderTuiFont } from './header-tui-font.js';
|
|
22
22
|
import { attachTuiGrab, detachTuiGrab } from './tui-grab.js';
|
|
23
|
-
import { openArticle as openWhatsNewArticle } from './whats-new-article.js';
|
|
24
23
|
import { createKeyToolbar, TERMINAL_TOOLBAR_HTML } from './terminal-toolbar.js';
|
|
25
24
|
import { refreshIcons, iconHtml } from './icons.js';
|
|
26
25
|
|
|
27
|
-
// Stable id of the canonical "Why TUI mode?" article in
|
|
28
|
-
// lib/whats-new-content.js. The TUI policy notice's "Learn more" button
|
|
29
|
-
// opens that article in the blog viewer instead of a one-off modal.
|
|
30
|
-
var TUI_POLICY_ARTICLE_ID = "2026-06-tui-default";
|
|
31
|
-
|
|
32
26
|
// Claude TUI session terminal colors follow Clay's active theme via
|
|
33
27
|
// getTerminalTheme(). Live theme switches are wired through
|
|
34
28
|
// setTuiSessionTheme() below, which theme.js calls from applyTheme.
|
|
@@ -44,15 +38,6 @@ var isTouchDevice = "ontouchstart" in window;
|
|
|
44
38
|
// touch devices and forward its send into the PTY (input.js -> tuiSubmitText).
|
|
45
39
|
// xterm is render-only on mobile.
|
|
46
40
|
|
|
47
|
-
// The TUI policy notice's "Learn more" button opens the canonical
|
|
48
|
-
// "Why TUI mode?" entry in the What's New blog viewer. The full
|
|
49
|
-
// authoritative text lives in lib/whats-new-content.js so there's a
|
|
50
|
-
// single source of truth.
|
|
51
|
-
function openPolicyArticle(e) {
|
|
52
|
-
if (e && e.stopPropagation) e.stopPropagation();
|
|
53
|
-
openWhatsNewArticle(TUI_POLICY_ARTICLE_ID);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
41
|
var xterm = null; // xterm.js instance
|
|
57
42
|
var fitAddon = null;
|
|
58
43
|
var webglAddon = null;
|
|
@@ -81,7 +66,7 @@ var resizeDebounce = null;
|
|
|
81
66
|
// fitAddon.fit() reads clientWidth - otherwise fit() can compute
|
|
82
67
|
// cols against the pre-resize container width.
|
|
83
68
|
// 2. A second rAF + second fit() pass converges any further layout
|
|
84
|
-
// adjustments (scrollbar appear/disappear,
|
|
69
|
+
// adjustments (scrollbar appear/disappear, the key toolbar
|
|
85
70
|
// reflowing on narrower widths, etc.). This mirrors the two-pass
|
|
86
71
|
// fit used on attach (fitNow + setTimeout(fitNow, 50)).
|
|
87
72
|
// 3. WebGL renderer caches a font atlas keyed on cell metrics. When
|
|
@@ -128,7 +113,7 @@ function ensureHostEl() {
|
|
|
128
113
|
hostEl.style.boxSizing = "border-box";
|
|
129
114
|
// 4px inset on all sides so the terminal doesn't kiss the edges of the
|
|
130
115
|
// content area. fitAddon computes against the xterm container's
|
|
131
|
-
// clientWidth/clientHeight (which exclude padding + the
|
|
116
|
+
// clientWidth/clientHeight (which exclude padding + the key toolbar),
|
|
132
117
|
// so cols/rows stay correct.
|
|
133
118
|
hostEl.style.padding = "4px";
|
|
134
119
|
|
|
@@ -148,30 +133,9 @@ function ensureHostEl() {
|
|
|
148
133
|
});
|
|
149
134
|
}
|
|
150
135
|
|
|
151
|
-
//
|
|
152
|
-
// SDK usage into a separate billing bucket on 2026-06-15; running
|
|
153
|
-
// `claude` in a real terminal keeps usage in the Interactive bucket).
|
|
154
|
-
var noticeEl = document.createElement("div");
|
|
155
|
-
noticeEl.className = "tui-policy-notice";
|
|
156
|
-
noticeEl.innerHTML =
|
|
157
|
-
'<span class="tui-policy-icon">●</span>' +
|
|
158
|
-
'<span class="tui-policy-text">TUI is the default Claude mode in Clay because of Anthropic\'s new billing policy.</span>' +
|
|
159
|
-
'<button type="button" class="tui-policy-learn-more">Learn more</button>' +
|
|
160
|
-
'<button type="button" class="tui-policy-dismiss" aria-label="Dismiss this notice for the rest of the page">×</button>';
|
|
161
|
-
noticeEl.querySelector(".tui-policy-learn-more").addEventListener("click", openPolicyArticle);
|
|
162
|
-
noticeEl.querySelector(".tui-policy-dismiss").addEventListener("click", function () {
|
|
163
|
-
// Session-only dismiss: hides the notice for the current page load.
|
|
164
|
-
// Reloading restores it on purpose so the policy context doesn't
|
|
165
|
-
// get lost forever - a user who comes back next month should be
|
|
166
|
-
// re-reminded that TUI is a billing workaround.
|
|
167
|
-
noticeEl.style.display = "none";
|
|
168
|
-
if (typeof fitNow === "function") fitNow();
|
|
169
|
-
});
|
|
170
|
-
hostEl.appendChild(noticeEl);
|
|
171
|
-
|
|
172
|
-
// Dedicated xterm container so the banner sits above the terminal
|
|
136
|
+
// Dedicated xterm container so the toolbar sits above the terminal
|
|
173
137
|
// rather than xterm mounting at the host root (which would have it
|
|
174
|
-
// side-by-side with the
|
|
138
|
+
// side-by-side with the toolbar under flex layout).
|
|
175
139
|
xtermContainerEl = document.createElement("div");
|
|
176
140
|
xtermContainerEl.className = "tui-xterm-container";
|
|
177
141
|
xtermContainerEl.style.flex = "1 1 auto";
|
|
@@ -457,7 +457,7 @@ export function renderUserStrip(allUsers, onlineUserIds, myUserId, dmFavorites,
|
|
|
457
457
|
// Tooltip
|
|
458
458
|
var displayName = mp.displayName || mate.name || "New Mate";
|
|
459
459
|
var mateVendor = mate.vendor || "claude";
|
|
460
|
-
var vendorLabels = { claude: "Claude Code", codex: "OpenAI Codex" };
|
|
460
|
+
var vendorLabels = { claude: "Claude Code", codex: "OpenAI Codex", kiro: "Kiro CLI" };
|
|
461
461
|
el.addEventListener("mouseenter", function () {
|
|
462
462
|
var html = '<div style="font-weight:600">' + escapeHtml(displayName);
|
|
463
463
|
if (mate.primary) {
|
|
@@ -11,8 +11,11 @@ import { getMateSessions } from './mate-sidebar.js';
|
|
|
11
11
|
import { openProjectSettings } from './project-settings.js';
|
|
12
12
|
import {
|
|
13
13
|
getCachedSessions,
|
|
14
|
-
getDateGroup
|
|
14
|
+
getDateGroup,
|
|
15
|
+
resolveDefaultVendor,
|
|
16
|
+
startNewSession
|
|
15
17
|
} from './sidebar-sessions.js';
|
|
18
|
+
import { VENDOR_AVATARS, VENDOR_NAMES, VENDOR_ORDER, VENDOR_HOMEPAGES } from './app-rendering.js';
|
|
16
19
|
import {
|
|
17
20
|
getCachedProjectList,
|
|
18
21
|
getCachedCurrentSlug,
|
|
@@ -698,36 +701,66 @@ function renderMateMobileActions(container) {
|
|
|
698
701
|
|
|
699
702
|
// Helper: render sorted sessions into a container with date groups (with loop session grouping)
|
|
700
703
|
function renderMobileSessionsInto(container) {
|
|
701
|
-
//
|
|
702
|
-
//
|
|
703
|
-
//
|
|
704
|
+
// Mirrors the desktop sidebar's split button: one "New session" action on
|
|
705
|
+
// the project's last-used vendor, plus a chevron that expands the full
|
|
706
|
+
// vendor list. The list expands inline rather than as a fixed-position
|
|
707
|
+
// popover because it lives inside a scrolling bottom sheet.
|
|
708
|
+
var mobileDefaultVendor = resolveDefaultVendor();
|
|
709
|
+
var mobileInstalled = store.get('installedVendors') || [];
|
|
710
|
+
|
|
704
711
|
var newRow = document.createElement("div");
|
|
705
712
|
newRow.className = "mobile-session-new-row";
|
|
706
713
|
|
|
707
|
-
var
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
getWs().send(JSON.stringify({ type: "new_session", vendor: "claude" }));
|
|
714
|
-
}
|
|
714
|
+
var mainBtn = document.createElement("button");
|
|
715
|
+
mainBtn.className = "mobile-session-new mobile-session-new-main";
|
|
716
|
+
mainBtn.innerHTML = '<img src="' + (VENDOR_AVATARS[mobileDefaultVendor] || VENDOR_AVATARS.claude) +
|
|
717
|
+
'" class="mobile-session-new-icon" alt=""><span>New ' + (VENDOR_NAMES[mobileDefaultVendor] || mobileDefaultVendor) + ' session</span>';
|
|
718
|
+
mainBtn.addEventListener("click", function () {
|
|
719
|
+
startNewSession(mobileDefaultVendor);
|
|
715
720
|
closeMobileSheet();
|
|
716
721
|
});
|
|
717
|
-
newRow.appendChild(
|
|
718
|
-
|
|
719
|
-
var
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
722
|
+
newRow.appendChild(mainBtn);
|
|
723
|
+
|
|
724
|
+
var vendorList = document.createElement("div");
|
|
725
|
+
vendorList.className = "mobile-vendor-list hidden";
|
|
726
|
+
|
|
727
|
+
var chevronBtn = document.createElement("button");
|
|
728
|
+
chevronBtn.className = "mobile-session-new mobile-session-new-chevron";
|
|
729
|
+
chevronBtn.setAttribute("aria-label", "Choose a vendor");
|
|
730
|
+
chevronBtn.innerHTML = iconHtml("chevron-down");
|
|
731
|
+
chevronBtn.addEventListener("click", function () {
|
|
732
|
+
vendorList.classList.toggle("hidden");
|
|
733
|
+
chevronBtn.classList.toggle("expanded");
|
|
727
734
|
});
|
|
728
|
-
newRow.appendChild(
|
|
735
|
+
newRow.appendChild(chevronBtn);
|
|
736
|
+
|
|
737
|
+
// Every known vendor is listed. Uninstalled ones stay visible but disabled,
|
|
738
|
+
// and tapping one opens the vendor's homepage.
|
|
739
|
+
for (var vi = 0; vi < VENDOR_ORDER.length; vi++) {
|
|
740
|
+
(function (vendor) {
|
|
741
|
+
var isInstalled = mobileInstalled.indexOf(vendor) !== -1;
|
|
742
|
+
var name = VENDOR_NAMES[vendor] || vendor;
|
|
743
|
+
var vBtn = document.createElement("button");
|
|
744
|
+
vBtn.className = "mobile-session-new mobile-session-new-vendor";
|
|
745
|
+
if (!isInstalled) vBtn.classList.add("disabled");
|
|
746
|
+
if (vendor === mobileDefaultVendor) vBtn.classList.add("active");
|
|
747
|
+
vBtn.innerHTML = '<img src="' + VENDOR_AVATARS[vendor] + '" class="mobile-session-new-icon" alt="">' +
|
|
748
|
+
'<span>' + name + '</span>' +
|
|
749
|
+
(isInstalled ? "" : '<span class="mobile-vendor-note">Not installed</span>');
|
|
750
|
+
vBtn.addEventListener("click", function () {
|
|
751
|
+
if (!isInstalled) {
|
|
752
|
+
window.open(VENDOR_HOMEPAGES[vendor], "_blank", "noopener");
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
startNewSession(vendor);
|
|
756
|
+
closeMobileSheet();
|
|
757
|
+
});
|
|
758
|
+
vendorList.appendChild(vBtn);
|
|
759
|
+
})(VENDOR_ORDER[vi]);
|
|
760
|
+
}
|
|
729
761
|
|
|
730
762
|
container.appendChild(newRow);
|
|
763
|
+
container.appendChild(vendorList);
|
|
731
764
|
|
|
732
765
|
// Partition: loop sessions vs normal sessions (same logic as desktop renderSessionList)
|
|
733
766
|
var sessions = getCachedSessions();
|