clay-server 4.3.0-beta.7 → 4.3.0-beta.9
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/project-sessions.js +4 -0
- package/lib/public/css/mobile-nav.css +13 -10
- package/lib/public/css/sidebar.css +61 -10
- package/lib/public/modules/session-creation-cta.js +33 -0
- package/lib/public/modules/sidebar-mobile.js +41 -30
- package/lib/public/modules/sidebar-sessions.js +45 -41
- package/package.json +1 -1
package/lib/project-sessions.js
CHANGED
|
@@ -989,6 +989,8 @@ function attachSessions(ctx) {
|
|
|
989
989
|
// TUI session: kill the underlying PTY before deleting the session
|
|
990
990
|
// record so the `claude` process is reaped and not left orphaned.
|
|
991
991
|
var dsTarget = sm.sessions.get(msg.id);
|
|
992
|
+
if (usersModule.isMultiUser() && (!ws._clayUser ||
|
|
993
|
+
!usersModule.canAccessSession(ws._clayUser.id, dsTarget, getProjectAccess()))) return true;
|
|
992
994
|
if (dsTarget && dsTarget.mode === "tui" && typeof dsTarget.terminalId === "number" && tm) {
|
|
993
995
|
try { tm.close(dsTarget.terminalId); } catch (e) {}
|
|
994
996
|
}
|
|
@@ -1001,6 +1003,8 @@ function attachSessions(ctx) {
|
|
|
1001
1003
|
if (msg.type === "rename_session") {
|
|
1002
1004
|
if (msg.id && sm.sessions.has(msg.id) && msg.title) {
|
|
1003
1005
|
var s = sm.sessions.get(msg.id);
|
|
1006
|
+
if (usersModule.isMultiUser() && (!ws._clayUser ||
|
|
1007
|
+
!usersModule.canAccessSession(ws._clayUser.id, s, getProjectAccess()))) return true;
|
|
1004
1008
|
s.title = String(msg.title).substring(0, 100);
|
|
1005
1009
|
s.titleProvisional = false;
|
|
1006
1010
|
s.titleAutoGenerated = false;
|
|
@@ -358,8 +358,7 @@
|
|
|
358
358
|
background: rgba(var(--overlay-rgb), 0.06);
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
/*
|
|
362
|
-
that expands .mobile-vendor-list below it. Mirrors the desktop sidebar. */
|
|
361
|
+
/* Primary creation action + a labeled provider selector. */
|
|
363
362
|
.mobile-session-new-row {
|
|
364
363
|
display: flex;
|
|
365
364
|
gap: 6px;
|
|
@@ -377,26 +376,30 @@
|
|
|
377
376
|
color: var(--text);
|
|
378
377
|
gap: 8px;
|
|
379
378
|
}
|
|
380
|
-
.mobile-session-new-row .mobile-session-new-
|
|
381
|
-
flex: 0
|
|
382
|
-
width:
|
|
379
|
+
.mobile-session-new-row .mobile-session-new-provider {
|
|
380
|
+
flex: 0 1 132px;
|
|
381
|
+
width: 132px;
|
|
382
|
+
min-width: 104px;
|
|
383
383
|
margin-bottom: 0;
|
|
384
|
-
padding:
|
|
384
|
+
padding: 6px 9px;
|
|
385
385
|
border: 1px solid var(--border-subtle);
|
|
386
386
|
border-radius: 8px;
|
|
387
387
|
background: rgba(var(--overlay-rgb), 0.03);
|
|
388
388
|
color: var(--text-muted);
|
|
389
|
-
|
|
390
|
-
gap:
|
|
389
|
+
display: grid;
|
|
390
|
+
gap: 2px;
|
|
391
391
|
}
|
|
392
|
-
.mobile-session-new-row .mobile-session-new-
|
|
392
|
+
.mobile-session-new-row .mobile-session-new-provider.expanded .session-create-provider-chevron svg {
|
|
393
393
|
transform: rotate(180deg);
|
|
394
394
|
}
|
|
395
395
|
.mobile-session-new-row .mobile-session-new-main:active,
|
|
396
|
-
.mobile-session-new-row .mobile-session-new-
|
|
396
|
+
.mobile-session-new-row .mobile-session-new-provider:active {
|
|
397
397
|
background: rgba(var(--overlay-rgb), 0.1);
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
+
.mobile-session-new-row .session-create-provider-label { font-size: 9px; }
|
|
401
|
+
.mobile-session-new-row .session-create-provider-name { font-size: 12px; }
|
|
402
|
+
|
|
400
403
|
.mobile-vendor-list {
|
|
401
404
|
display: flex;
|
|
402
405
|
flex-direction: column;
|
|
@@ -1262,26 +1262,77 @@
|
|
|
1262
1262
|
opacity: 1;
|
|
1263
1263
|
}
|
|
1264
1264
|
|
|
1265
|
-
/*
|
|
1265
|
+
/* Primary creation action + a visibly grouped provider selector. */
|
|
1266
1266
|
.session-top-action-split {
|
|
1267
1267
|
display: flex;
|
|
1268
|
+
flex-wrap: wrap;
|
|
1268
1269
|
align-items: stretch;
|
|
1269
|
-
gap:
|
|
1270
|
+
gap: 6px;
|
|
1270
1271
|
min-width: 0;
|
|
1271
1272
|
}
|
|
1272
1273
|
|
|
1273
1274
|
.session-top-action-split .split-main {
|
|
1274
|
-
flex: 1 1
|
|
1275
|
+
flex: 1 1 110px;
|
|
1275
1276
|
width: auto;
|
|
1277
|
+
min-width: 110px;
|
|
1278
|
+
border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
|
|
1279
|
+
background: color-mix(in srgb, var(--accent) 13%, transparent);
|
|
1280
|
+
color: var(--text);
|
|
1281
|
+
font-weight: 600;
|
|
1282
|
+
white-space: nowrap;
|
|
1276
1283
|
}
|
|
1277
1284
|
|
|
1278
|
-
.session-top-action-split .split-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
+
.session-top-action-split .split-main:hover {
|
|
1286
|
+
border-color: color-mix(in srgb, var(--accent) 50%, transparent);
|
|
1287
|
+
background: color-mix(in srgb, var(--accent) 19%, transparent);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
.session-top-action-split .split-provider {
|
|
1291
|
+
flex: 1 1 104px;
|
|
1292
|
+
width: auto;
|
|
1293
|
+
min-width: 104px;
|
|
1294
|
+
padding: 5px 8px;
|
|
1295
|
+
border: 1px solid var(--border-subtle);
|
|
1296
|
+
background: color-mix(in srgb, var(--sidebar-hover) 48%, transparent);
|
|
1297
|
+
border-radius: 9px;
|
|
1298
|
+
display: grid;
|
|
1299
|
+
gap: 1px;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
.session-create-provider-label {
|
|
1303
|
+
display: block;
|
|
1304
|
+
color: var(--text-dimmer);
|
|
1305
|
+
font-size: 9px;
|
|
1306
|
+
font-weight: 600;
|
|
1307
|
+
line-height: 1;
|
|
1308
|
+
letter-spacing: 0.03em;
|
|
1309
|
+
text-transform: uppercase;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
.session-create-provider-choice {
|
|
1313
|
+
display: flex;
|
|
1314
|
+
align-items: center;
|
|
1315
|
+
gap: 5px;
|
|
1316
|
+
min-width: 0;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
.session-create-provider-name {
|
|
1320
|
+
overflow: hidden;
|
|
1321
|
+
color: var(--text-secondary);
|
|
1322
|
+
font-size: 11px;
|
|
1323
|
+
font-weight: 600;
|
|
1324
|
+
line-height: 1.2;
|
|
1325
|
+
text-overflow: ellipsis;
|
|
1326
|
+
white-space: nowrap;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
.session-create-provider-chevron {
|
|
1330
|
+
display: inline-flex;
|
|
1331
|
+
margin-left: auto;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
.session-create-provider[aria-expanded="true"] .session-create-provider-chevron svg {
|
|
1335
|
+
transform: rotate(180deg);
|
|
1285
1336
|
}
|
|
1286
1337
|
|
|
1287
1338
|
.session-favorites-section {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Shared, presentation-only session creation controls for desktop and mobile.
|
|
2
|
+
|
|
3
|
+
import { iconHtml } from './icons.js';
|
|
4
|
+
|
|
5
|
+
export function renderSessionCreationCta(options) {
|
|
6
|
+
var row = document.createElement("div");
|
|
7
|
+
row.className = options.rowClass + " session-create-cta";
|
|
8
|
+
|
|
9
|
+
var createButton = document.createElement("button");
|
|
10
|
+
createButton.className = options.createClass + " session-create-primary";
|
|
11
|
+
createButton.type = "button";
|
|
12
|
+
createButton.textContent = "Create session";
|
|
13
|
+
createButton.title = options.createTitle;
|
|
14
|
+
createButton.setAttribute("aria-label", options.createTitle);
|
|
15
|
+
createButton.addEventListener("click", options.onCreate);
|
|
16
|
+
row.appendChild(createButton);
|
|
17
|
+
|
|
18
|
+
var providerButton = document.createElement("button");
|
|
19
|
+
providerButton.className = options.providerClass + " session-create-provider";
|
|
20
|
+
providerButton.type = "button";
|
|
21
|
+
providerButton.title = options.providerTitle;
|
|
22
|
+
providerButton.setAttribute("aria-label", "AI provider: " + options.vendorName + ". " + options.providerTitle);
|
|
23
|
+
providerButton.setAttribute("aria-expanded", "false");
|
|
24
|
+
providerButton.setAttribute("aria-controls", options.menuId);
|
|
25
|
+
providerButton.innerHTML = '<span class="session-create-provider-label">AI provider</span>' +
|
|
26
|
+
'<span class="session-create-provider-choice"><img src="' + options.vendorAvatar +
|
|
27
|
+
'" class="' + options.iconClass + ' session-create-provider-icon" alt=""><span class="session-create-provider-name">' +
|
|
28
|
+
options.vendorName + '</span>' + iconHtml("chevron-down", "session-create-provider-chevron") + '</span>';
|
|
29
|
+
providerButton.addEventListener("click", options.onProvider);
|
|
30
|
+
row.appendChild(providerButton);
|
|
31
|
+
|
|
32
|
+
return { element: row, createButton: createButton, providerButton: providerButton };
|
|
33
|
+
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from './sidebar-sessions.js';
|
|
17
17
|
import { VENDOR_AVATARS, VENDOR_NAMES, VENDOR_ORDER, VENDOR_HOMEPAGES, isExperimentalVendor } from './app-rendering.js';
|
|
18
18
|
import { defaultVendorUnavailable, resolvePreferredVendor, saveDefaultVendor } from './default-vendor.js';
|
|
19
|
+
import { renderSessionCreationCta } from './session-creation-cta.js';
|
|
19
20
|
import {
|
|
20
21
|
getCachedProjectList,
|
|
21
22
|
getCachedCurrentSlug,
|
|
@@ -671,42 +672,49 @@ function createMobileLoopGroup(loopId, children, groupKey) {
|
|
|
671
672
|
}
|
|
672
673
|
|
|
673
674
|
// Helper: render sorted sessions into a container with date groups (with loop session grouping)
|
|
674
|
-
function renderMobileSessionsInto(container) {
|
|
675
|
-
// Mirrors the desktop sidebar's split button: one "New session" action on
|
|
676
|
-
// the project's last-used vendor, plus a chevron that expands the full
|
|
677
|
-
// vendor list. The list expands inline rather than as a fixed-position
|
|
678
|
-
// popover because it lives inside a scrolling bottom sheet.
|
|
675
|
+
export function renderMobileSessionsInto(container) {
|
|
679
676
|
var mobileDefaultVendor = resolvePreferredVendor(store.get('installedVendors') || []) || resolveDefaultVendor();
|
|
680
677
|
var mobileInstalled = store.get('installedVendors') || [];
|
|
681
678
|
var mobileDefaultState = store.get('defaultVendorState') || {};
|
|
682
679
|
|
|
683
|
-
var newRow = document.createElement("div");
|
|
684
|
-
newRow.className = "mobile-session-new-row";
|
|
685
|
-
|
|
686
|
-
var mainBtn = document.createElement("button");
|
|
687
|
-
mainBtn.className = "mobile-session-new mobile-session-new-main";
|
|
688
|
-
mainBtn.title = "New " + (VENDOR_NAMES[mobileDefaultVendor] || mobileDefaultVendor) + " session";
|
|
689
|
-
if (defaultVendorUnavailable(mobileInstalled)) mainBtn.title += " (saved default is unavailable; using installed priority)";
|
|
690
|
-
mainBtn.innerHTML = '<img src="' + (VENDOR_AVATARS[mobileDefaultVendor] || VENDOR_AVATARS.claude) +
|
|
691
|
-
'" class="mobile-session-new-icon" alt=""><span>New ' + (VENDOR_NAMES[mobileDefaultVendor] || mobileDefaultVendor) + ' session</span>';
|
|
692
|
-
mainBtn.addEventListener("click", function () {
|
|
693
|
-
startNewSession(mobileDefaultVendor);
|
|
694
|
-
closeMobileSheet();
|
|
695
|
-
});
|
|
696
|
-
newRow.appendChild(mainBtn);
|
|
697
|
-
|
|
698
680
|
var vendorList = document.createElement("div");
|
|
699
681
|
vendorList.className = "mobile-vendor-list hidden";
|
|
700
|
-
|
|
701
|
-
var
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
682
|
+
vendorList.id = "mobile-session-provider-menu";
|
|
683
|
+
var mobileVendorName = VENDOR_NAMES[mobileDefaultVendor] || mobileDefaultVendor;
|
|
684
|
+
var mobileCreateTitle = "Create a " + mobileVendorName + " session";
|
|
685
|
+
if (defaultVendorUnavailable(mobileInstalled)) mobileCreateTitle += " (saved default is unavailable; using installed priority)";
|
|
686
|
+
var controls;
|
|
687
|
+
controls = renderSessionCreationCta({
|
|
688
|
+
rowClass: "mobile-session-new-row",
|
|
689
|
+
createClass: "mobile-session-new mobile-session-new-main",
|
|
690
|
+
providerClass: "mobile-session-new mobile-session-new-provider",
|
|
691
|
+
iconClass: "mobile-session-new-icon",
|
|
692
|
+
vendorName: mobileVendorName,
|
|
693
|
+
vendorAvatar: VENDOR_AVATARS[mobileDefaultVendor] || VENDOR_AVATARS.claude,
|
|
694
|
+
createTitle: mobileCreateTitle,
|
|
695
|
+
providerTitle: "Choose an AI provider",
|
|
696
|
+
menuId: vendorList.id,
|
|
697
|
+
onCreate: function () { startNewSession(mobileDefaultVendor); closeMobileSheet(); },
|
|
698
|
+
onProvider: function () {
|
|
699
|
+
var opening = vendorList.classList.contains("hidden");
|
|
700
|
+
vendorList.classList.toggle("hidden", !opening);
|
|
701
|
+
controls.providerButton.classList.toggle("expanded", opening);
|
|
702
|
+
controls.providerButton.setAttribute("aria-expanded", opening ? "true" : "false");
|
|
703
|
+
if (opening) {
|
|
704
|
+
var first = vendorList.querySelector("button:not(:disabled)");
|
|
705
|
+
if (first) first.focus();
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
var newRow = controls.element;
|
|
710
|
+
vendorList.addEventListener("keydown", function (e) {
|
|
711
|
+
if (e.key !== "Escape") return;
|
|
712
|
+
e.preventDefault();
|
|
713
|
+
vendorList.classList.add("hidden");
|
|
714
|
+
controls.providerButton.classList.remove("expanded");
|
|
715
|
+
controls.providerButton.setAttribute("aria-expanded", "false");
|
|
716
|
+
controls.providerButton.focus();
|
|
708
717
|
});
|
|
709
|
-
newRow.appendChild(chevronBtn);
|
|
710
718
|
|
|
711
719
|
if (defaultVendorUnavailable(mobileInstalled) && mobileDefaultState.preference) {
|
|
712
720
|
var mobileFallbackNote = document.createElement("div");
|
|
@@ -727,7 +735,7 @@ function renderMobileSessionsInto(container) {
|
|
|
727
735
|
if (!isInstalled) vBtn.classList.add("disabled");
|
|
728
736
|
if (vendor === mobileDefaultVendor) vBtn.classList.add("active");
|
|
729
737
|
vBtn.innerHTML = '<img src="' + VENDOR_AVATARS[vendor] + '" class="mobile-session-new-icon" alt="">' +
|
|
730
|
-
'<span>' + name + '</span>' +
|
|
738
|
+
'<span>' + (isInstalled ? "Create with " : "Learn about ") + name + '</span>' +
|
|
731
739
|
(isExperimentalVendor(vendor) ? '<span class="vendor-experimental-badge" aria-label="Experimental integration" title="Experimental integration; not yet validated through direct use testing">🧪</span>' : '') +
|
|
732
740
|
(isInstalled ? "" : '<span class="mobile-vendor-note">Not installed</span>');
|
|
733
741
|
vBtn.addEventListener("click", function () {
|
|
@@ -735,6 +743,9 @@ function renderMobileSessionsInto(container) {
|
|
|
735
743
|
window.open(VENDOR_HOMEPAGES[vendor], "_blank", "noopener");
|
|
736
744
|
return;
|
|
737
745
|
}
|
|
746
|
+
vendorList.classList.add("hidden");
|
|
747
|
+
controls.providerButton.classList.remove("expanded");
|
|
748
|
+
controls.providerButton.setAttribute("aria-expanded", "false");
|
|
738
749
|
startNewSession(vendor);
|
|
739
750
|
closeMobileSheet();
|
|
740
751
|
});
|
|
@@ -17,6 +17,7 @@ import { openGroup, separateGroup } from './split-view.js';
|
|
|
17
17
|
import { groupedSessionIds } from './split-group-helpers.js';
|
|
18
18
|
import { defaultVendorUnavailable, resolvePreferredVendor, saveDefaultVendor } from './default-vendor.js';
|
|
19
19
|
import { openPairDialog } from './split-pair-ui.js';
|
|
20
|
+
import { renderSessionCreationCta } from './session-creation-cta.js';
|
|
20
21
|
import {
|
|
21
22
|
hierarchyItemMatches,
|
|
22
23
|
prepareSidebarHierarchy,
|
|
@@ -53,10 +54,11 @@ var armedDeleteTimer = null;
|
|
|
53
54
|
store.subscribe(function (state, previous) {
|
|
54
55
|
if (state.defaultVendorState !== previous.defaultVendorState || state.installedVendors !== previous.installedVendors) {
|
|
55
56
|
var reopenNewMenu = !!(sessionCtxMenu && sessionCtxMenuAnchor);
|
|
57
|
+
var sessionList = getSessionListEl();
|
|
56
58
|
if (reopenNewMenu) closeSessionCtxMenu();
|
|
57
|
-
if (
|
|
58
|
-
if (reopenNewMenu) {
|
|
59
|
-
var freshAnchor =
|
|
59
|
+
if (sessionList) renderSessionList();
|
|
60
|
+
if (reopenNewMenu && sessionList) {
|
|
61
|
+
var freshAnchor = sessionList.querySelector(".session-top-action.split-provider");
|
|
60
62
|
if (freshAnchor) showNewSessionMenu(freshAnchor, resolveDefaultVendor());
|
|
61
63
|
}
|
|
62
64
|
}
|
|
@@ -356,58 +358,49 @@ export function startNewSession(vendor, extra) {
|
|
|
356
358
|
});
|
|
357
359
|
}
|
|
358
360
|
|
|
359
|
-
function renderSessionTopActions() {
|
|
361
|
+
export function renderSessionTopActions() {
|
|
360
362
|
var wrap = document.createElement("div");
|
|
361
363
|
wrap.className = "session-top-actions";
|
|
362
364
|
|
|
363
|
-
// Single split button. The main half creates a session with the project's
|
|
364
|
-
// last-used vendor (for Claude the server applies the claudeOpenMode pref);
|
|
365
|
-
// the chevron opens the vendor picker plus alternate launch modes.
|
|
366
365
|
var defaultVendor = resolveDefaultVendor();
|
|
367
|
-
|
|
368
|
-
var
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
chevron.innerHTML = iconHtml("chevron-down");
|
|
389
|
-
chevron.addEventListener("click", function (e) {
|
|
390
|
-
e.preventDefault();
|
|
391
|
-
e.stopPropagation();
|
|
392
|
-
if (sessionCtxMenu) { closeSessionCtxMenu(); return; }
|
|
393
|
-
showNewSessionMenu(chevron, defaultVendor);
|
|
366
|
+
var vendorName = VENDOR_NAMES[defaultVendor] || defaultVendor;
|
|
367
|
+
var createTitle = "Create a " + vendorName + " session";
|
|
368
|
+
if (defaultVendorUnavailable(store.get('installedVendors') || [])) createTitle += " (saved default is unavailable; using installed priority)";
|
|
369
|
+
var controls;
|
|
370
|
+
controls = renderSessionCreationCta({
|
|
371
|
+
rowClass: "session-top-action-split",
|
|
372
|
+
createClass: "session-top-action split-main",
|
|
373
|
+
providerClass: "session-top-action split-provider",
|
|
374
|
+
iconClass: "session-top-action-icon",
|
|
375
|
+
vendorName: vendorName,
|
|
376
|
+
vendorAvatar: VENDOR_AVATARS[defaultVendor] || VENDOR_AVATARS.claude,
|
|
377
|
+
createTitle: createTitle,
|
|
378
|
+
providerTitle: "Choose an AI provider or launch mode",
|
|
379
|
+
menuId: "session-new-menu",
|
|
380
|
+
onCreate: function () { startNewSession(defaultVendor); },
|
|
381
|
+
onProvider: function (e) {
|
|
382
|
+
e.preventDefault();
|
|
383
|
+
e.stopPropagation();
|
|
384
|
+
if (sessionCtxMenu) { closeSessionCtxMenu(); return; }
|
|
385
|
+
showNewSessionMenu(controls.providerButton, defaultVendor);
|
|
386
|
+
},
|
|
394
387
|
});
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
wrap.appendChild(cell);
|
|
388
|
+
wrap.appendChild(controls.element);
|
|
398
389
|
return wrap;
|
|
399
390
|
}
|
|
400
391
|
|
|
401
|
-
//
|
|
402
|
-
// element/var so the global document click handler closes it.
|
|
392
|
+
// Provider disclosure anchored to the grouped provider control. Reuses the
|
|
393
|
+
// session-ctx-menu element/var so the global document click handler closes it.
|
|
403
394
|
function showNewSessionMenu(anchorBtn, defaultVendor) {
|
|
404
395
|
closeSessionCtxMenu();
|
|
405
396
|
|
|
406
397
|
var menu = document.createElement("div");
|
|
407
398
|
menu.className = "session-ctx-menu session-new-menu";
|
|
399
|
+
menu.id = "session-new-menu";
|
|
408
400
|
var installed = store.get('installedVendors') || [];
|
|
409
401
|
var defaultVendorState = store.get('defaultVendorState') || {};
|
|
410
402
|
sessionCtxMenuAnchor = anchorBtn;
|
|
403
|
+
anchorBtn.setAttribute("aria-expanded", "true");
|
|
411
404
|
|
|
412
405
|
if (defaultVendorUnavailable(installed) && defaultVendorState.preference) {
|
|
413
406
|
var fallbackNote = document.createElement("div");
|
|
@@ -430,7 +423,7 @@ function showNewSessionMenu(anchorBtn, defaultVendor) {
|
|
|
430
423
|
if (vendor === defaultVendor) item.classList.add("active");
|
|
431
424
|
item.type = "button";
|
|
432
425
|
item.innerHTML = '<img src="' + VENDOR_AVATARS[vendor] + '" class="session-top-action-icon" alt="">' +
|
|
433
|
-
'<span class="session-new-vendor-name">' + name + '</span>' +
|
|
426
|
+
'<span class="session-new-vendor-name">' + (isInstalled ? "Create with " : "Learn about ") + name + '</span>' +
|
|
434
427
|
(isExperimentalVendor(vendor) ? '<span class="vendor-experimental-badge" aria-label="Experimental integration" title="Experimental integration; not yet validated through direct use testing">🧪</span>' : '') +
|
|
435
428
|
(isInstalled ? "" : '<span class="session-new-vendor-note">Not installed</span>' + iconHtml("external-link"));
|
|
436
429
|
item.title = isInstalled
|
|
@@ -526,6 +519,12 @@ function showNewSessionMenu(anchorBtn, defaultVendor) {
|
|
|
526
519
|
|
|
527
520
|
document.body.appendChild(menu);
|
|
528
521
|
sessionCtxMenu = menu;
|
|
522
|
+
menu.addEventListener("keydown", function (e) {
|
|
523
|
+
if (e.key !== "Escape") return;
|
|
524
|
+
e.preventDefault();
|
|
525
|
+
e.stopPropagation();
|
|
526
|
+
closeSessionCtxMenu(true);
|
|
527
|
+
});
|
|
529
528
|
refreshIcons();
|
|
530
529
|
|
|
531
530
|
requestAnimationFrame(function () {
|
|
@@ -542,6 +541,8 @@ function showNewSessionMenu(anchorBtn, defaultVendor) {
|
|
|
542
541
|
if (menuRect.bottom > window.innerHeight - 8) {
|
|
543
542
|
menu.style.top = (btnRect.top - menuRect.height - 2) + "px";
|
|
544
543
|
}
|
|
544
|
+
var firstMenuItem = menu.querySelector("button:not(:disabled)");
|
|
545
|
+
if (firstMenuItem) firstMenuItem.focus();
|
|
545
546
|
});
|
|
546
547
|
}
|
|
547
548
|
|
|
@@ -718,13 +719,16 @@ export function getExpandedLoopRuns() {
|
|
|
718
719
|
|
|
719
720
|
// --- Context menu ---
|
|
720
721
|
|
|
721
|
-
function closeSessionCtxMenu() {
|
|
722
|
+
function closeSessionCtxMenu(restoreFocus) {
|
|
723
|
+
var anchor = sessionCtxMenuAnchor;
|
|
724
|
+
if (anchor) anchor.setAttribute("aria-expanded", "false");
|
|
722
725
|
sessionCtxMenuAnchor = null;
|
|
723
726
|
if (sessionCtxMenu) {
|
|
724
727
|
sessionCtxMenu.remove();
|
|
725
728
|
sessionCtxMenu = null;
|
|
726
729
|
sessionCtxSessionId = null;
|
|
727
730
|
}
|
|
731
|
+
if (restoreFocus && anchor && document.contains(anchor)) anchor.focus();
|
|
728
732
|
}
|
|
729
733
|
|
|
730
734
|
function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData) {
|
package/package.json
CHANGED