clay-server 2.46.0 → 2.47.0-beta.2
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/bin/cli.js +10 -18
- package/lib/daemon.js +49 -8
- package/lib/kiro-defaults.js +22 -0
- package/lib/migrate-single-user.js +286 -0
- package/lib/project-connection.js +1 -0
- package/lib/project-notifications.js +1 -1
- package/lib/project-sessions.js +13 -2
- package/lib/project.js +31 -7
- package/lib/public/app.js +6 -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 +11 -10
- package/lib/public/modules/app-panels.js +5 -1
- package/lib/public/modules/app-rendering.js +12 -0
- package/lib/public/modules/input.js +5 -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/sdk-bridge.js +50 -5
- package/lib/sdk-message-processor.js +4 -2
- package/lib/server-auth.js +14 -5
- package/lib/server.js +4 -0
- package/lib/users-preferences.js +3 -1
- package/lib/users.js +14 -0
- package/lib/yoke/adapters/codex.js +7 -4
- package/lib/yoke/adapters/kiro.js +1156 -0
- package/lib/yoke/index.js +56 -4
- package/lib/yoke/kiro-acp-server.js +328 -0
- package/package.json +2 -1
|
@@ -358,18 +358,16 @@
|
|
|
358
358
|
background: rgba(var(--overlay-rgb), 0.06);
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
/*
|
|
362
|
-
|
|
363
|
-
same way the desktop sidebar already does. */
|
|
361
|
+
/* Split "New session" row: main action on the last-used vendor + a chevron
|
|
362
|
+
that expands .mobile-vendor-list below it. Mirrors the desktop sidebar. */
|
|
364
363
|
.mobile-session-new-row {
|
|
365
364
|
display: flex;
|
|
366
365
|
gap: 6px;
|
|
367
366
|
padding: 6px 8px 8px;
|
|
368
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
369
367
|
}
|
|
370
368
|
|
|
371
|
-
.mobile-session-new-row .mobile-session-new-
|
|
372
|
-
flex: 1 1
|
|
369
|
+
.mobile-session-new-row .mobile-session-new-main {
|
|
370
|
+
flex: 1 1 auto;
|
|
373
371
|
width: auto;
|
|
374
372
|
margin-bottom: 0;
|
|
375
373
|
padding: 10px 12px;
|
|
@@ -377,15 +375,61 @@
|
|
|
377
375
|
border-radius: 8px;
|
|
378
376
|
background: rgba(var(--overlay-rgb), 0.03);
|
|
379
377
|
color: var(--text);
|
|
380
|
-
justify-content: center;
|
|
381
378
|
gap: 8px;
|
|
382
379
|
}
|
|
383
|
-
.mobile-session-new-row .mobile-session-new-
|
|
380
|
+
.mobile-session-new-row .mobile-session-new-chevron {
|
|
381
|
+
flex: 0 0 auto;
|
|
382
|
+
width: 44px;
|
|
383
|
+
margin-bottom: 0;
|
|
384
|
+
padding: 10px 0;
|
|
385
|
+
border: 1px solid var(--border-subtle);
|
|
386
|
+
border-radius: 8px;
|
|
387
|
+
background: rgba(var(--overlay-rgb), 0.03);
|
|
388
|
+
color: var(--text-muted);
|
|
389
|
+
justify-content: center;
|
|
390
|
+
gap: 0;
|
|
391
|
+
}
|
|
392
|
+
.mobile-session-new-row .mobile-session-new-chevron.expanded svg {
|
|
393
|
+
transform: rotate(180deg);
|
|
394
|
+
}
|
|
395
|
+
.mobile-session-new-row .mobile-session-new-main:active,
|
|
396
|
+
.mobile-session-new-row .mobile-session-new-chevron:active {
|
|
397
|
+
background: rgba(var(--overlay-rgb), 0.1);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.mobile-vendor-list {
|
|
401
|
+
display: flex;
|
|
402
|
+
flex-direction: column;
|
|
403
|
+
gap: 4px;
|
|
404
|
+
padding: 0 8px 8px;
|
|
384
405
|
border-bottom: 1px solid var(--border-subtle);
|
|
385
406
|
}
|
|
386
|
-
.mobile-
|
|
407
|
+
.mobile-vendor-list.hidden { display: none; }
|
|
408
|
+
|
|
409
|
+
.mobile-vendor-list .mobile-session-new-vendor {
|
|
410
|
+
width: 100%;
|
|
411
|
+
margin-bottom: 0;
|
|
412
|
+
padding: 10px 12px;
|
|
413
|
+
border: 1px solid var(--border-subtle);
|
|
414
|
+
border-radius: 8px;
|
|
415
|
+
background: rgba(var(--overlay-rgb), 0.03);
|
|
416
|
+
color: var(--text);
|
|
417
|
+
font-weight: 500;
|
|
418
|
+
gap: 8px;
|
|
419
|
+
}
|
|
420
|
+
.mobile-vendor-list .mobile-session-new-vendor.active { font-weight: 700; }
|
|
421
|
+
/* Uninstalled vendors stay tappable: the tap opens the vendor's homepage. */
|
|
422
|
+
.mobile-vendor-list .mobile-session-new-vendor.disabled { opacity: 0.45; }
|
|
423
|
+
.mobile-vendor-list .mobile-session-new-vendor:active {
|
|
387
424
|
background: rgba(var(--overlay-rgb), 0.1);
|
|
388
425
|
}
|
|
426
|
+
.mobile-vendor-note {
|
|
427
|
+
margin-left: auto;
|
|
428
|
+
font-size: 12px;
|
|
429
|
+
font-weight: 500;
|
|
430
|
+
color: var(--text-dimmer);
|
|
431
|
+
}
|
|
432
|
+
|
|
389
433
|
.mobile-session-new-icon {
|
|
390
434
|
width: 18px;
|
|
391
435
|
height: 18px;
|
|
@@ -993,7 +993,7 @@
|
|
|
993
993
|
|
|
994
994
|
.session-top-actions {
|
|
995
995
|
display: grid;
|
|
996
|
-
grid-template-columns:
|
|
996
|
+
grid-template-columns: minmax(0, 1fr);
|
|
997
997
|
gap: 2px;
|
|
998
998
|
padding: 0 0 2px;
|
|
999
999
|
}
|
|
@@ -1240,6 +1240,29 @@
|
|
|
1240
1240
|
.session-ctx-item.session-ctx-delete { color: var(--error); }
|
|
1241
1241
|
.session-ctx-item.session-ctx-delete:hover { background: var(--error-8); }
|
|
1242
1242
|
|
|
1243
|
+
/* --- New-session vendor picker --- */
|
|
1244
|
+
.session-new-menu { min-width: 200px; }
|
|
1245
|
+
|
|
1246
|
+
.session-ctx-sep {
|
|
1247
|
+
height: 1px;
|
|
1248
|
+
margin: 4px 0;
|
|
1249
|
+
background: var(--border);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
.session-new-vendor .session-new-vendor-name { flex: 1 1 auto; min-width: 0; }
|
|
1253
|
+
.session-new-vendor.active { color: var(--text); font-weight: 600; }
|
|
1254
|
+
|
|
1255
|
+
/* Uninstalled vendors stay clickable on purpose: the click opens the
|
|
1256
|
+
vendor's homepage instead of creating a session. */
|
|
1257
|
+
.session-new-vendor.disabled { opacity: 0.45; }
|
|
1258
|
+
.session-new-vendor.disabled:hover { opacity: 0.7; }
|
|
1259
|
+
|
|
1260
|
+
.session-new-vendor-note {
|
|
1261
|
+
font-size: 11px;
|
|
1262
|
+
color: var(--text-dimmer);
|
|
1263
|
+
white-space: nowrap;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1243
1266
|
/* --- Session inline rename --- */
|
|
1244
1267
|
.session-rename-input {
|
|
1245
1268
|
width: 100%;
|
|
@@ -49,69 +49,10 @@ body.tui-suspended #tui-resume-bar {
|
|
|
49
49
|
.tui-resume-hint { display: none; }
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
/* Policy
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
display: flex;
|
|
57
|
-
align-items: center;
|
|
58
|
-
gap: 8px;
|
|
59
|
-
flex-shrink: 0;
|
|
60
|
-
padding: 6px 10px;
|
|
61
|
-
margin-bottom: 4px;
|
|
62
|
-
border-radius: 6px;
|
|
63
|
-
background: rgba(80, 250, 123, 0.06);
|
|
64
|
-
border: 1px solid rgba(80, 250, 123, 0.15);
|
|
65
|
-
color: #b8b8b8;
|
|
66
|
-
font-size: 11px;
|
|
67
|
-
line-height: 1.3;
|
|
68
|
-
font-family: "Roboto Mono", Menlo, Monaco, monospace;
|
|
69
|
-
}
|
|
70
|
-
.tui-policy-icon {
|
|
71
|
-
color: #50fa7b;
|
|
72
|
-
font-size: 9px;
|
|
73
|
-
line-height: 1;
|
|
74
|
-
flex-shrink: 0;
|
|
75
|
-
}
|
|
76
|
-
.tui-policy-text {
|
|
77
|
-
flex: 1 1 auto;
|
|
78
|
-
min-width: 0;
|
|
79
|
-
}
|
|
80
|
-
.tui-policy-learn-more {
|
|
81
|
-
flex-shrink: 0;
|
|
82
|
-
padding: 2px 8px;
|
|
83
|
-
border: 1px solid rgba(80, 250, 123, 0.3);
|
|
84
|
-
border-radius: 4px;
|
|
85
|
-
background: transparent;
|
|
86
|
-
color: #50fa7b;
|
|
87
|
-
font: inherit;
|
|
88
|
-
font-size: 10px;
|
|
89
|
-
cursor: pointer;
|
|
90
|
-
transition: background 0.15s, border-color 0.15s;
|
|
91
|
-
}
|
|
92
|
-
.tui-policy-learn-more:hover {
|
|
93
|
-
background: rgba(80, 250, 123, 0.1);
|
|
94
|
-
border-color: rgba(80, 250, 123, 0.5);
|
|
95
|
-
}
|
|
96
|
-
.tui-policy-dismiss {
|
|
97
|
-
flex-shrink: 0;
|
|
98
|
-
width: 18px;
|
|
99
|
-
height: 18px;
|
|
100
|
-
padding: 0;
|
|
101
|
-
border: none;
|
|
102
|
-
border-radius: 4px;
|
|
103
|
-
background: transparent;
|
|
104
|
-
color: #888;
|
|
105
|
-
font-size: 14px;
|
|
106
|
-
line-height: 1;
|
|
107
|
-
cursor: pointer;
|
|
108
|
-
}
|
|
109
|
-
.tui-policy-dismiss:hover {
|
|
110
|
-
background: rgba(255, 255, 255, 0.06);
|
|
111
|
-
color: #ddd;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/* Policy info modal. Themed via CSS vars so it follows light/dark. */
|
|
52
|
+
/* Policy info modal. Themed via CSS vars so it follows light/dark.
|
|
53
|
+
The modal itself is gone; .tui-policy-modal-* rules stay because
|
|
54
|
+
What's New article bodies (lib/whats-new-content.js) still use the
|
|
55
|
+
-links class for their footer links. */
|
|
115
56
|
.tui-policy-modal-backdrop {
|
|
116
57
|
position: fixed;
|
|
117
58
|
inset: 0;
|
package/lib/public/index.html
CHANGED
|
@@ -513,6 +513,10 @@
|
|
|
513
513
|
<img src="/codex-avatar.png" class="vendor-toggle-icon" alt="Codex">
|
|
514
514
|
<span class="vendor-toggle-label">Codex</span>
|
|
515
515
|
</button>
|
|
516
|
+
<button id="vendor-btn-kiro" class="vendor-toggle-btn" data-vendor="kiro">
|
|
517
|
+
<img src="/kiro-avatar.svg" class="vendor-toggle-icon" alt="Kiro">
|
|
518
|
+
<span class="vendor-toggle-label">Kiro CLI</span>
|
|
519
|
+
</button>
|
|
516
520
|
</div>
|
|
517
521
|
<div id="active-vendor-indicator" class="hidden" title="Session vendor">
|
|
518
522
|
<img id="active-vendor-icon" alt="">
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Kiro CLI">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="kiroBg" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#8b5cf6"/>
|
|
5
|
+
<stop offset="1" stop-color="#6d28d9"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="128" height="128" rx="28" fill="url(#kiroBg)"/>
|
|
9
|
+
<g fill="#ffffff">
|
|
10
|
+
<rect x="34" y="34" width="12" height="60" rx="3"/>
|
|
11
|
+
<rect x="52" y="60" width="42" height="12" rx="3" transform="rotate(-45 73 66)"/>
|
|
12
|
+
<rect x="52" y="60" width="42" height="12" rx="3" transform="rotate(45 73 66)"/>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -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';
|
|
@@ -579,6 +579,12 @@ export function processMessage(msg) {
|
|
|
579
579
|
updateSessionPresence(msg.presence || {});
|
|
580
580
|
break;
|
|
581
581
|
|
|
582
|
+
// Project's last-used vendor. Seeds the sidebar's "New session" button
|
|
583
|
+
// on connect and keeps other tabs in sync after someone picks a vendor.
|
|
584
|
+
case "last_vendor":
|
|
585
|
+
store.set({ lastVendor: msg.vendor || "" });
|
|
586
|
+
break;
|
|
587
|
+
|
|
582
588
|
case "cursor_move":
|
|
583
589
|
handleRemoteCursorMove(msg);
|
|
584
590
|
break;
|
|
@@ -696,15 +702,10 @@ export function processMessage(msg) {
|
|
|
696
702
|
}
|
|
697
703
|
}
|
|
698
704
|
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";
|
|
705
|
+
if (msg.vendor && VENDOR_AVATARS[msg.vendor]) {
|
|
706
|
+
_avIcon.src = VENDOR_AVATARS[msg.vendor];
|
|
707
|
+
_avIcon.alt = VENDOR_NAMES[msg.vendor] || msg.vendor;
|
|
708
|
+
_avi.title = (VENDOR_NAMES[msg.vendor] || msg.vendor) + " session";
|
|
708
709
|
_avi.classList.remove("hidden");
|
|
709
710
|
} else {
|
|
710
711
|
_avi.classList.add("hidden");
|
|
@@ -88,6 +88,7 @@ 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 = [
|
|
@@ -108,6 +109,7 @@ var CODEX_WEBSEARCH_OPTIONS = [
|
|
|
108
109
|
var KNOWN_CONTEXT_WINDOWS = {
|
|
109
110
|
"opus-4-6": 1000000,
|
|
110
111
|
"claude-sonnet-4": 1000000,
|
|
112
|
+
"gpt-5.6": 272000,
|
|
111
113
|
"gpt-5.5": 1048576,
|
|
112
114
|
"gpt-5.4": 1048576,
|
|
113
115
|
"gpt-5.3": 1048576,
|
|
@@ -417,7 +419,8 @@ export function initPanels() {
|
|
|
417
419
|
var vendorToggleWrap = $("vendor-toggle-wrap");
|
|
418
420
|
var vendorBtnClaude = $("vendor-btn-claude");
|
|
419
421
|
var vendorBtnCodex = $("vendor-btn-codex");
|
|
420
|
-
var
|
|
422
|
+
var vendorBtnKiro = $("vendor-btn-kiro");
|
|
423
|
+
var vendorBtns = { claude: vendorBtnClaude, codex: vendorBtnCodex, kiro: vendorBtnKiro };
|
|
421
424
|
|
|
422
425
|
function updateVendorToggle() {
|
|
423
426
|
var installed = store.get('installedVendors') || [];
|
|
@@ -446,6 +449,7 @@ export function initPanels() {
|
|
|
446
449
|
|
|
447
450
|
if (vendorBtnClaude) vendorBtnClaude.addEventListener("click", function() { onVendorClick("claude"); });
|
|
448
451
|
if (vendorBtnCodex) vendorBtnCodex.addEventListener("click", function() { onVendorClick("codex"); });
|
|
452
|
+
if (vendorBtnKiro) vendorBtnKiro.addEventListener("click", function() { onVendorClick("kiro"); });
|
|
449
453
|
|
|
450
454
|
// --- Reactive UI sync ---
|
|
451
455
|
store.subscribe(function (state, prev) {
|
|
@@ -17,10 +17,22 @@ import { getScheduledMsgEl } from './app-rate-limit.js';
|
|
|
17
17
|
export var VENDOR_AVATARS = {
|
|
18
18
|
claude: "/claude-code-avatar.png",
|
|
19
19
|
codex: "/codex-avatar.png",
|
|
20
|
+
kiro: "/kiro-avatar.svg",
|
|
20
21
|
};
|
|
21
22
|
export var VENDOR_NAMES = {
|
|
22
23
|
claude: "Claude Code",
|
|
23
24
|
codex: "Codex",
|
|
25
|
+
kiro: "Kiro CLI",
|
|
26
|
+
};
|
|
27
|
+
// Display order for every vendor Clay knows about, installed or not. Pickers
|
|
28
|
+
// render the full list so a missing CLI reads as "not installed yet" rather
|
|
29
|
+
// than "Clay doesn't support it".
|
|
30
|
+
export var VENDOR_ORDER = ["claude", "codex", "kiro"];
|
|
31
|
+
// Where to send the user when they pick a vendor whose CLI isn't installed.
|
|
32
|
+
export var VENDOR_HOMEPAGES = {
|
|
33
|
+
claude: "https://claude.com/product/claude-code",
|
|
34
|
+
codex: "https://openai.com/codex/",
|
|
35
|
+
kiro: "https://kiro.dev/",
|
|
24
36
|
};
|
|
25
37
|
var NEW_MSG_BTN_DEFAULT = "\u2193 Latest";
|
|
26
38
|
var NEW_MSG_BTN_ACTIVITY = "\u2193 New activity";
|
|
@@ -300,9 +300,11 @@ export function sendMessage() {
|
|
|
300
300
|
_vtw2.classList.remove("locked");
|
|
301
301
|
}
|
|
302
302
|
if (_avi && _avIcon) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
303
|
+
var _vendorAvatars = { claude: "/claude-code-avatar.png", codex: "/codex-avatar.png", kiro: "/kiro-avatar.svg" };
|
|
304
|
+
var _vendorNames = { claude: "Claude", codex: "Codex", kiro: "Kiro CLI" };
|
|
305
|
+
_avIcon.src = _vendorAvatars[_committedVendor] || _vendorAvatars.claude;
|
|
306
|
+
_avIcon.alt = _vendorNames[_committedVendor] || _vendorNames.claude;
|
|
307
|
+
_avi.title = (_vendorNames[_committedVendor] || _vendorNames.claude) + " session";
|
|
306
308
|
_avi.classList.remove("hidden");
|
|
307
309
|
}
|
|
308
310
|
} 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();
|