clay-server 2.32.0 → 2.32.1-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/lib/daemon.js +18 -0
- package/lib/project-email.js +16 -0
- package/lib/project.js +38 -8
- package/lib/public/app.js +11 -0
- package/lib/public/css/command-palette.css +186 -0
- package/lib/public/css/filebrowser.css +73 -49
- package/lib/public/css/icon-strip.css +44 -2
- package/lib/public/css/input.css +10 -5
- package/lib/public/css/mates.css +18 -29
- package/lib/public/css/notifications-center.css +32 -0
- package/lib/public/css/overlays.css +5 -22
- package/lib/public/css/sidebar.css +288 -27
- package/lib/public/index.html +24 -23
- package/lib/public/modules/app-notifications.js +52 -0
- package/lib/public/modules/app-rendering.js +1 -1
- package/lib/public/modules/filebrowser.js +171 -0
- package/lib/public/modules/input.js +12 -11
- package/lib/public/modules/markdown.js +12 -0
- package/lib/public/modules/project-switcher.js +404 -0
- package/lib/public/modules/sidebar.js +14 -10
- package/lib/public/modules/terminal.js +8 -3
- package/lib/public/modules/tool-palette.js +561 -0
- package/lib/sdk-bridge.js +20 -7
- package/lib/server-settings.js +53 -0
- package/lib/sessions.js +4 -2
- package/lib/users-preferences.js +48 -0
- package/lib/users.js +4 -0
- package/lib/yoke/index.js +4 -3
- package/package.json +1 -1
package/lib/daemon.js
CHANGED
|
@@ -737,6 +737,24 @@ var relay = createServer({
|
|
|
737
737
|
console.log("[daemon] Auto-continue on rate limit:", want, "(web)");
|
|
738
738
|
return { ok: true, autoContinueOnRateLimit: want };
|
|
739
739
|
},
|
|
740
|
+
onGetToolPalettes: function () {
|
|
741
|
+
return config.toolPalettes || {};
|
|
742
|
+
},
|
|
743
|
+
onSetToolPalette: function (paletteName, order, hidden) {
|
|
744
|
+
if (paletteName !== "session" && paletteName !== "mate") {
|
|
745
|
+
return { error: "Unknown palette" };
|
|
746
|
+
}
|
|
747
|
+
var safeOrder = Array.isArray(order)
|
|
748
|
+
? order.filter(function (s) { return typeof s === "string"; })
|
|
749
|
+
: [];
|
|
750
|
+
var safeHidden = Array.isArray(hidden)
|
|
751
|
+
? hidden.filter(function (s) { return typeof s === "string"; })
|
|
752
|
+
: [];
|
|
753
|
+
if (!config.toolPalettes) config.toolPalettes = {};
|
|
754
|
+
config.toolPalettes[paletteName] = { order: safeOrder, hidden: safeHidden };
|
|
755
|
+
saveConfig(config);
|
|
756
|
+
return { ok: true, palette: paletteName, order: safeOrder, hidden: safeHidden };
|
|
757
|
+
},
|
|
740
758
|
onSetKeepAwake: function (value) {
|
|
741
759
|
var want = !!value;
|
|
742
760
|
config.keepAwake = want;
|
package/lib/project-email.js
CHANGED
|
@@ -405,12 +405,28 @@ function attachEmail(ctx) {
|
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
+
// Whether any email capability is available for the active user.
|
|
409
|
+
// Used to gate the clay-email MCP so its tools are hidden from the model
|
|
410
|
+
// when the user hasn't registered any accounts and the server SMTP isn't
|
|
411
|
+
// configured (see issue #325 — we don't expose tools the user can't use).
|
|
412
|
+
function hasEmailCapability() {
|
|
413
|
+
try {
|
|
414
|
+
var accounts = emailAccounts.listAccounts(getActiveUserId());
|
|
415
|
+
if (accounts && accounts.length > 0) return true;
|
|
416
|
+
} catch (e) { /* fall through to SMTP check */ }
|
|
417
|
+
try {
|
|
418
|
+
if (smtp.isSmtpConfigured()) return true;
|
|
419
|
+
} catch (e) { /* ignore */ }
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
|
|
408
423
|
return {
|
|
409
424
|
handleEmailMessage: handleEmailMessage,
|
|
410
425
|
getEmailContext: getEmailContext,
|
|
411
426
|
getCheckedEmailAccounts: getCheckedEmailAccounts,
|
|
412
427
|
getEmailDefaults: getEmailDefaults,
|
|
413
428
|
createMcpDeps: createMcpDeps,
|
|
429
|
+
hasEmailCapability: hasEmailCapability,
|
|
414
430
|
destroy: destroy,
|
|
415
431
|
};
|
|
416
432
|
}
|
package/lib/project.js
CHANGED
|
@@ -540,6 +540,32 @@ function createProjectContext(opts) {
|
|
|
540
540
|
return Object.keys(servers).length > 0 ? servers : undefined;
|
|
541
541
|
})();
|
|
542
542
|
|
|
543
|
+
// Gate in-app MCP servers on the underlying capability actually being
|
|
544
|
+
// available. Without this, tools show up in every session's tool list
|
|
545
|
+
// even when the user can't use them, which wastes context and can cause
|
|
546
|
+
// the model to pick the wrong MCP when the user has another one
|
|
547
|
+
// configured (see issue #325).
|
|
548
|
+
//
|
|
549
|
+
// clay-browser -> only when the Chrome extension is connected
|
|
550
|
+
// clay-email -> only when the user has an account or server SMTP
|
|
551
|
+
function getLocalMcpServers() {
|
|
552
|
+
if (!mcpServers) return undefined;
|
|
553
|
+
var extWs = browserState._extensionWs;
|
|
554
|
+
var extConnected = !!(extWs && extWs.readyState === 1);
|
|
555
|
+
var emailAvailable = !!(_email && typeof _email.hasEmailCapability === "function" && _email.hasEmailCapability());
|
|
556
|
+
var keys = Object.keys(mcpServers);
|
|
557
|
+
var filtered = {};
|
|
558
|
+
var hasAny = false;
|
|
559
|
+
for (var i = 0; i < keys.length; i++) {
|
|
560
|
+
var name = keys[i];
|
|
561
|
+
if (name === "clay-browser" && !extConnected) continue;
|
|
562
|
+
if (name === "clay-email" && !emailAvailable) continue;
|
|
563
|
+
filtered[name] = mcpServers[name];
|
|
564
|
+
hasAny = true;
|
|
565
|
+
}
|
|
566
|
+
return hasAny ? filtered : undefined;
|
|
567
|
+
}
|
|
568
|
+
|
|
543
569
|
// --- SDK bridge ---
|
|
544
570
|
var sdk = createSDKBridge({
|
|
545
571
|
cwd: cwd,
|
|
@@ -553,7 +579,7 @@ function createProjectContext(opts) {
|
|
|
553
579
|
mateDisplayName: opts.mateDisplayName || "",
|
|
554
580
|
isMate: isMate,
|
|
555
581
|
dangerouslySkipPermissions: dangerouslySkipPermissions,
|
|
556
|
-
mcpServers:
|
|
582
|
+
mcpServers: getLocalMcpServers,
|
|
557
583
|
getRemoteMcpServers: function () { return _mcp.getMcpServers(); },
|
|
558
584
|
clayPort: serverPort,
|
|
559
585
|
clayTls: serverTls,
|
|
@@ -1125,11 +1151,14 @@ function createProjectContext(opts) {
|
|
|
1125
1151
|
}
|
|
1126
1152
|
}
|
|
1127
1153
|
|
|
1128
|
-
// In-app MCP servers (debate, browser, email)
|
|
1129
|
-
|
|
1130
|
-
|
|
1154
|
+
// In-app MCP servers (debate, browser, email).
|
|
1155
|
+
// Use getLocalMcpServers() so clay-browser is hidden unless the
|
|
1156
|
+
// Chrome extension is currently connected (see issue #325).
|
|
1157
|
+
var localMcp = getLocalMcpServers();
|
|
1158
|
+
if (localMcp) {
|
|
1159
|
+
var inAppNames = Object.keys(localMcp);
|
|
1131
1160
|
for (var i = 0; i < inAppNames.length; i++) {
|
|
1132
|
-
extractServerTools(inAppNames[i],
|
|
1161
|
+
extractServerTools(inAppNames[i], localMcp[inAppNames[i]]);
|
|
1133
1162
|
}
|
|
1134
1163
|
}
|
|
1135
1164
|
|
|
@@ -1147,9 +1176,10 @@ function createProjectContext(opts) {
|
|
|
1147
1176
|
return Promise.resolve(tools);
|
|
1148
1177
|
},
|
|
1149
1178
|
callTool: function (serverName, toolName, args) {
|
|
1150
|
-
// Try in-app servers first
|
|
1151
|
-
|
|
1152
|
-
|
|
1179
|
+
// Try in-app servers first (gated by extension connectivity for clay-browser).
|
|
1180
|
+
var localMcp = getLocalMcpServers();
|
|
1181
|
+
if (localMcp && localMcp[serverName]) {
|
|
1182
|
+
var server = localMcp[serverName];
|
|
1153
1183
|
if (server.instance && server.instance._registeredTools && server.instance._registeredTools[toolName]) {
|
|
1154
1184
|
var handler = server.instance._registeredTools[toolName].handler;
|
|
1155
1185
|
if (typeof handler === "function") {
|
package/lib/public/app.js
CHANGED
|
@@ -40,6 +40,8 @@ import { initPlaybook, openPlaybook, getPlaybooks, getPlaybookForTip, isComplete
|
|
|
40
40
|
import { initSTT } from './modules/stt.js';
|
|
41
41
|
import { initProfile, getProfileLang } from './modules/profile.js';
|
|
42
42
|
import { initUserSettings } from './modules/user-settings.js';
|
|
43
|
+
import { initToolPalettes } from './modules/tool-palette.js';
|
|
44
|
+
import { initProjectSwitcher } from './modules/project-switcher.js';
|
|
43
45
|
import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
44
46
|
import { initSessionSearch, toggleSearch, closeSearch, isSearchOpen, handleFindInSessionResults, onHistoryPrepended as onSessionSearchHistoryPrepended } from './modules/session-search.js';
|
|
45
47
|
import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
@@ -396,6 +398,10 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
396
398
|
get projectList() { return getCachedProjects() || []; },
|
|
397
399
|
availableBuiltins: function () { return store.get('cachedAvailableBuiltins') || []; },
|
|
398
400
|
};
|
|
401
|
+
// Render the customizable tool palettes BEFORE initSidebar so the
|
|
402
|
+
// buttons exist in the DOM when sidebar.js (and later terminal.js /
|
|
403
|
+
// mcp-ui.js / etc.) attach click handlers by ID.
|
|
404
|
+
initToolPalettes();
|
|
399
405
|
initSidebar(sidebarCtx);
|
|
400
406
|
var wsGetter = function () { return _getWsRef(); };
|
|
401
407
|
initMateSidebar(wsGetter);
|
|
@@ -441,6 +447,11 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
441
447
|
},
|
|
442
448
|
});
|
|
443
449
|
|
|
450
|
+
// Project switcher (Cmd/Ctrl+E). Init after command palette so its
|
|
451
|
+
// keydown listener registers later — capture-phase ordering doesn't
|
|
452
|
+
// matter here but it keeps related bootstrap steps adjacent.
|
|
453
|
+
initProjectSwitcher();
|
|
454
|
+
|
|
444
455
|
// --- Connect overlay (animated ASCII logo) ---
|
|
445
456
|
var asciiLogoCanvas = $("ascii-logo-canvas");
|
|
446
457
|
initAsciiLogo(asciiLogoCanvas);
|
|
@@ -371,3 +371,189 @@
|
|
|
371
371
|
display: none;
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
|
+
|
|
375
|
+
/* --- Project switcher (Cmd/Ctrl+E) ---
|
|
376
|
+
macOS Cmd+Tab-style quick switcher: centered modal with a darker
|
|
377
|
+
backdrop than the command palette, projects laid out horizontally
|
|
378
|
+
as icon+label tiles that wrap to multiple rows when there are many.
|
|
379
|
+
Reuses .cmd-palette shell so positioning/animations stay consistent. */
|
|
380
|
+
.project-switcher .cmd-palette-backdrop {
|
|
381
|
+
background: rgba(var(--shadow-rgb), 0.55);
|
|
382
|
+
}
|
|
383
|
+
.project-switcher-dialog {
|
|
384
|
+
top: 50%;
|
|
385
|
+
left: 50%;
|
|
386
|
+
transform: translate(-50%, -50%);
|
|
387
|
+
border-top: 1px solid var(--border);
|
|
388
|
+
border-radius: 16px;
|
|
389
|
+
width: min(820px, 85vw);
|
|
390
|
+
max-height: 80vh;
|
|
391
|
+
background: var(--bg-alt);
|
|
392
|
+
box-shadow: 0 20px 60px rgba(var(--shadow-rgb), 0.5);
|
|
393
|
+
animation: projSwitcherIn 0.12s ease-out;
|
|
394
|
+
}
|
|
395
|
+
@keyframes projSwitcherIn {
|
|
396
|
+
from { opacity: 0; transform: translate(-50%, -50%) scale(0.96); }
|
|
397
|
+
to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
|
|
398
|
+
}
|
|
399
|
+
.project-switcher-header {
|
|
400
|
+
padding: 14px 20px 6px;
|
|
401
|
+
font-size: 11px;
|
|
402
|
+
font-weight: 600;
|
|
403
|
+
color: var(--text-dimmer);
|
|
404
|
+
text-transform: uppercase;
|
|
405
|
+
letter-spacing: 0.5px;
|
|
406
|
+
}
|
|
407
|
+
.project-switcher-dialog .cmd-palette-results {
|
|
408
|
+
padding: 12px 16px;
|
|
409
|
+
display: flex;
|
|
410
|
+
flex-wrap: wrap;
|
|
411
|
+
gap: 8px;
|
|
412
|
+
justify-content: flex-start;
|
|
413
|
+
overflow-y: auto;
|
|
414
|
+
max-height: calc(80vh - 110px);
|
|
415
|
+
}
|
|
416
|
+
.project-switcher-item {
|
|
417
|
+
flex: 0 0 auto;
|
|
418
|
+
width: 96px;
|
|
419
|
+
height: 104px;
|
|
420
|
+
padding: 10px 6px;
|
|
421
|
+
display: flex;
|
|
422
|
+
flex-direction: column;
|
|
423
|
+
align-items: center;
|
|
424
|
+
justify-content: flex-start;
|
|
425
|
+
gap: 8px;
|
|
426
|
+
border: 2px solid transparent;
|
|
427
|
+
border-radius: 12px;
|
|
428
|
+
background: transparent;
|
|
429
|
+
cursor: pointer;
|
|
430
|
+
transition: background 0.12s, border-color 0.12s;
|
|
431
|
+
text-align: center;
|
|
432
|
+
}
|
|
433
|
+
/* Icon-strip-style treatment: rounded bg-alt fill that turns accent
|
|
434
|
+
on hover/active, and a 4-direction drop-shadow + white glow on the
|
|
435
|
+
emoji img for readability on either background. */
|
|
436
|
+
.project-switcher-item .cmd-palette-item-icon {
|
|
437
|
+
width: 44px;
|
|
438
|
+
height: 44px;
|
|
439
|
+
font-size: 28px;
|
|
440
|
+
line-height: 1;
|
|
441
|
+
display: flex;
|
|
442
|
+
align-items: center;
|
|
443
|
+
justify-content: center;
|
|
444
|
+
border-radius: 12px;
|
|
445
|
+
background: var(--bg-alt);
|
|
446
|
+
color: var(--text-secondary);
|
|
447
|
+
flex-shrink: 0;
|
|
448
|
+
transition: background 0.15s, color 0.15s;
|
|
449
|
+
}
|
|
450
|
+
.project-switcher-item .cmd-palette-item-icon .lucide {
|
|
451
|
+
width: 22px;
|
|
452
|
+
height: 22px;
|
|
453
|
+
}
|
|
454
|
+
.project-switcher-item .cmd-palette-item-icon img.emoji {
|
|
455
|
+
width: 26px;
|
|
456
|
+
height: 26px;
|
|
457
|
+
vertical-align: middle;
|
|
458
|
+
margin: 0;
|
|
459
|
+
filter:
|
|
460
|
+
drop-shadow(1px 0 0 rgba(0,0,0,0.25))
|
|
461
|
+
drop-shadow(-1px 0 0 rgba(0,0,0,0.25))
|
|
462
|
+
drop-shadow(0 1px 0 rgba(0,0,0,0.25))
|
|
463
|
+
drop-shadow(0 -1px 0 rgba(0,0,0,0.25))
|
|
464
|
+
drop-shadow(0 0 4px rgba(255,255,255,0.35));
|
|
465
|
+
}
|
|
466
|
+
.project-switcher-item:hover .cmd-palette-item-icon,
|
|
467
|
+
.project-switcher-item.active .cmd-palette-item-icon {
|
|
468
|
+
background: var(--accent);
|
|
469
|
+
color: #fff;
|
|
470
|
+
}
|
|
471
|
+
.project-switcher-avatar {
|
|
472
|
+
width: 100%;
|
|
473
|
+
height: 100%;
|
|
474
|
+
border-radius: 12px;
|
|
475
|
+
object-fit: cover;
|
|
476
|
+
}
|
|
477
|
+
.project-switcher-item .cmd-palette-item-body {
|
|
478
|
+
width: 100%;
|
|
479
|
+
display: block;
|
|
480
|
+
}
|
|
481
|
+
.project-switcher-item .cmd-palette-item-title-row {
|
|
482
|
+
display: flex;
|
|
483
|
+
flex-direction: column;
|
|
484
|
+
align-items: center;
|
|
485
|
+
gap: 2px;
|
|
486
|
+
width: 100%;
|
|
487
|
+
}
|
|
488
|
+
.project-switcher-item .cmd-palette-item-title {
|
|
489
|
+
font-size: 12px;
|
|
490
|
+
font-weight: 500;
|
|
491
|
+
line-height: 1.2;
|
|
492
|
+
color: var(--text-secondary);
|
|
493
|
+
max-width: 100%;
|
|
494
|
+
overflow: hidden;
|
|
495
|
+
text-overflow: ellipsis;
|
|
496
|
+
white-space: nowrap;
|
|
497
|
+
display: block;
|
|
498
|
+
}
|
|
499
|
+
.project-switcher-item:hover {
|
|
500
|
+
background: transparent;
|
|
501
|
+
}
|
|
502
|
+
.project-switcher-item.active {
|
|
503
|
+
border-color: var(--accent);
|
|
504
|
+
background: transparent;
|
|
505
|
+
}
|
|
506
|
+
.project-switcher-item.active .cmd-palette-item-title {
|
|
507
|
+
color: var(--text);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.project-switcher-current {
|
|
511
|
+
font-size: 9px;
|
|
512
|
+
font-weight: 600;
|
|
513
|
+
color: var(--text-dimmer);
|
|
514
|
+
text-transform: uppercase;
|
|
515
|
+
letter-spacing: 0.5px;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* Disabled tile (e.g. worktree with worktreeAccessible: false). Not a
|
|
519
|
+
valid switch target, so skip highlight, skip click, visually dim. */
|
|
520
|
+
.project-switcher-item.disabled {
|
|
521
|
+
opacity: 0.35;
|
|
522
|
+
cursor: not-allowed;
|
|
523
|
+
}
|
|
524
|
+
.project-switcher-item.disabled:hover {
|
|
525
|
+
background: transparent;
|
|
526
|
+
}
|
|
527
|
+
.project-switcher-item.disabled.active {
|
|
528
|
+
border-color: transparent;
|
|
529
|
+
background: transparent;
|
|
530
|
+
}
|
|
531
|
+
.project-switcher-disabled-reason {
|
|
532
|
+
font-size: 9px;
|
|
533
|
+
font-weight: 500;
|
|
534
|
+
color: var(--text-dimmer);
|
|
535
|
+
font-style: italic;
|
|
536
|
+
max-width: 100%;
|
|
537
|
+
overflow: hidden;
|
|
538
|
+
text-overflow: ellipsis;
|
|
539
|
+
white-space: nowrap;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.project-switcher-footer {
|
|
543
|
+
padding: 10px 16px;
|
|
544
|
+
font-size: 11px;
|
|
545
|
+
color: var(--text-dimmer);
|
|
546
|
+
border-top: 1px solid var(--border-subtle);
|
|
547
|
+
display: flex;
|
|
548
|
+
justify-content: center;
|
|
549
|
+
}
|
|
550
|
+
.project-switcher-footer kbd {
|
|
551
|
+
display: inline-block;
|
|
552
|
+
padding: 1px 5px;
|
|
553
|
+
margin: 0 1px;
|
|
554
|
+
background: rgba(var(--overlay-rgb), 0.1);
|
|
555
|
+
border-radius: 4px;
|
|
556
|
+
font-size: 10px;
|
|
557
|
+
font-family: inherit;
|
|
558
|
+
color: var(--text-muted);
|
|
559
|
+
}
|
|
@@ -2,76 +2,51 @@
|
|
|
2
2
|
File Browser
|
|
3
3
|
========================================================================== */
|
|
4
4
|
|
|
5
|
-
/* --- Session actions ---
|
|
5
|
+
/* --- Session actions ---
|
|
6
|
+
Compact icon+caption grid. Tile styles come from .palette-tile in
|
|
7
|
+
sidebar.css so the same tiles render correctly in both the active
|
|
8
|
+
grid here and the hidden-grid when edit-mode moves them. */
|
|
6
9
|
#session-actions {
|
|
7
|
-
display:
|
|
8
|
-
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: repeat(4, 1fr);
|
|
12
|
+
gap: 4px;
|
|
9
13
|
padding: 0 8px 4px;
|
|
10
14
|
}
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
display: flex;
|
|
14
|
-
align-items: center;
|
|
15
|
-
gap: 8px;
|
|
16
|
-
padding: 8px 12px;
|
|
17
|
-
border-radius: 10px;
|
|
18
|
-
border: none;
|
|
19
|
-
background: transparent;
|
|
20
|
-
color: var(--text-muted);
|
|
21
|
-
font-family: inherit;
|
|
22
|
-
font-size: 14px;
|
|
23
|
-
font-weight: 600;
|
|
24
|
-
cursor: pointer;
|
|
25
|
-
margin-bottom: 0;
|
|
26
|
-
transition: background 0.15s, color 0.15s;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
#session-actions button .lucide { width: 16px; height: 16px; flex-shrink: 0; }
|
|
30
|
-
#session-actions button:hover { background: var(--sidebar-hover); color: var(--text-secondary); }
|
|
31
|
-
#session-actions button.active { background: var(--sidebar-hover); color: var(--text); }
|
|
32
|
-
|
|
16
|
+
/* Badge becomes a small corner indicator in icon-only grid */
|
|
33
17
|
.sidebar-badge {
|
|
34
|
-
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 4px;
|
|
20
|
+
right: 4px;
|
|
21
|
+
background: rgba(var(--overlay-rgb), 0.15);
|
|
35
22
|
color: var(--text-dimmer);
|
|
36
|
-
font-size:
|
|
23
|
+
font-size: 9px;
|
|
37
24
|
font-weight: 700;
|
|
38
|
-
min-width:
|
|
39
|
-
height:
|
|
40
|
-
border-radius:
|
|
25
|
+
min-width: 14px;
|
|
26
|
+
height: 14px;
|
|
27
|
+
border-radius: 7px;
|
|
41
28
|
display: inline-flex;
|
|
42
29
|
align-items: center;
|
|
43
30
|
justify-content: center;
|
|
44
|
-
padding: 0
|
|
45
|
-
margin-left: auto;
|
|
31
|
+
padding: 0 3px;
|
|
46
32
|
line-height: 1;
|
|
33
|
+
pointer-events: none;
|
|
47
34
|
}
|
|
48
35
|
.sidebar-badge.hidden { display: none; }
|
|
49
36
|
|
|
37
|
+
/* Sticky-notes badge stays clickable in the icon grid — it toggles the
|
|
38
|
+
archive panel. The old inline "Open Archive" expand label doesn't fit
|
|
39
|
+
a corner dot, so it's gone; the cursor and hover color still signal
|
|
40
|
+
that the badge itself is interactive. */
|
|
50
41
|
#sticky-notes-sidebar-count {
|
|
51
42
|
cursor: pointer;
|
|
52
|
-
|
|
53
|
-
transition:
|
|
54
|
-
}
|
|
55
|
-
#sticky-notes-sidebar-count::after {
|
|
56
|
-
content: "Open Archive";
|
|
57
|
-
display: inline-block;
|
|
58
|
-
max-width: 0;
|
|
59
|
-
overflow: hidden;
|
|
60
|
-
white-space: nowrap;
|
|
61
|
-
vertical-align: middle;
|
|
62
|
-
margin-left: 0;
|
|
63
|
-
opacity: 0;
|
|
64
|
-
transition: max-width 0.2s ease, margin-left 0.2s ease, opacity 0.15s ease;
|
|
43
|
+
pointer-events: auto;
|
|
44
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
65
45
|
}
|
|
66
46
|
#sticky-notes-sidebar-count:hover {
|
|
67
47
|
background: var(--accent);
|
|
68
48
|
color: #fff;
|
|
69
49
|
}
|
|
70
|
-
#sticky-notes-sidebar-count:hover::after {
|
|
71
|
-
max-width: 80px;
|
|
72
|
-
margin-left: 4px;
|
|
73
|
-
opacity: 1;
|
|
74
|
-
}
|
|
75
50
|
|
|
76
51
|
/* --- Panels --- */
|
|
77
52
|
.sidebar-panel { flex: 1; overflow-y: auto; min-height: 0; }
|
|
@@ -95,8 +70,45 @@
|
|
|
95
70
|
margin: 6px 6px 12px;
|
|
96
71
|
display: flex;
|
|
97
72
|
flex-direction: column;
|
|
73
|
+
/* Own the scroll: inner file-tree scrolls, titlebar/search/hint stay
|
|
74
|
+
anchored. Without this the whole panel (inherited overflow-y:auto
|
|
75
|
+
from .sidebar-panel) scrolled past the search bar. */
|
|
76
|
+
overflow: hidden;
|
|
98
77
|
}
|
|
99
78
|
#sidebar-panel-files.hidden { display: none; }
|
|
79
|
+
#sidebar-panel-files #file-tree {
|
|
80
|
+
flex: 1;
|
|
81
|
+
min-height: 0;
|
|
82
|
+
overflow-y: auto;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Keyboard hint footer — pinned below the tree, always visible. */
|
|
86
|
+
.fb-kb-hint {
|
|
87
|
+
flex-shrink: 0;
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
gap: 6px;
|
|
92
|
+
padding: 6px 8px;
|
|
93
|
+
border-top: 1px solid var(--border-subtle);
|
|
94
|
+
font-size: 10px;
|
|
95
|
+
font-weight: 500;
|
|
96
|
+
color: var(--text-dimmer);
|
|
97
|
+
}
|
|
98
|
+
.fb-kb-hint kbd {
|
|
99
|
+
display: inline-block;
|
|
100
|
+
padding: 1px 5px;
|
|
101
|
+
margin: 0 1px;
|
|
102
|
+
background: var(--bg-alt);
|
|
103
|
+
border: 1px solid var(--border-subtle);
|
|
104
|
+
border-radius: 4px;
|
|
105
|
+
font-size: 10px;
|
|
106
|
+
font-family: inherit;
|
|
107
|
+
color: var(--text-muted);
|
|
108
|
+
}
|
|
109
|
+
.fb-kb-hint span {
|
|
110
|
+
opacity: 0.9;
|
|
111
|
+
}
|
|
100
112
|
|
|
101
113
|
/* --- File browser titlebar --- */
|
|
102
114
|
.fb-titlebar {
|
|
@@ -247,6 +259,18 @@
|
|
|
247
259
|
|
|
248
260
|
.file-tree-item:hover { background: var(--sidebar-hover); color: var(--text); }
|
|
249
261
|
.file-tree-item.active { background: var(--sidebar-active); color: var(--text); }
|
|
262
|
+
/* Keyboard focus ring (arrow-key navigation) — separate from .active
|
|
263
|
+
so the currently-opened file stays visually marked even while the
|
|
264
|
+
user is exploring elsewhere with the arrows. */
|
|
265
|
+
.file-tree-item.fb-kb-focus {
|
|
266
|
+
background: var(--sidebar-hover);
|
|
267
|
+
color: var(--text);
|
|
268
|
+
box-shadow: inset 0 0 0 1px var(--accent);
|
|
269
|
+
}
|
|
270
|
+
#file-tree:focus { outline: none; }
|
|
271
|
+
#file-tree:focus .file-tree-item.fb-kb-focus {
|
|
272
|
+
background: var(--sidebar-active);
|
|
273
|
+
}
|
|
250
274
|
.file-tree-item .lucide { width: 14px; height: 14px; flex-shrink: 0; }
|
|
251
275
|
|
|
252
276
|
.file-tree-name {
|
|
@@ -112,6 +112,37 @@
|
|
|
112
112
|
flex-shrink: 0;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
/* Keyboard-shortcut hint above each switchable strip section. Tiny
|
|
116
|
+
pill with the platform-specific chord; clicking opens the switcher
|
|
117
|
+
in that mode, same as the keyboard shortcut itself. */
|
|
118
|
+
.icon-strip-hint {
|
|
119
|
+
flex-shrink: 0;
|
|
120
|
+
display: inline-flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
min-width: 28px;
|
|
124
|
+
padding: 2px 4px;
|
|
125
|
+
margin: 2px 0;
|
|
126
|
+
border: 1px solid var(--border-subtle);
|
|
127
|
+
background: var(--bg-alt);
|
|
128
|
+
color: var(--text-dimmer);
|
|
129
|
+
font-size: 10px;
|
|
130
|
+
font-weight: 600;
|
|
131
|
+
font-family: inherit;
|
|
132
|
+
border-radius: 6px;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
opacity: 0.7;
|
|
135
|
+
transition: opacity 0.15s, color 0.15s, background 0.15s;
|
|
136
|
+
}
|
|
137
|
+
.icon-strip-hint:hover {
|
|
138
|
+
opacity: 1;
|
|
139
|
+
color: var(--text-muted);
|
|
140
|
+
background: var(--bg);
|
|
141
|
+
}
|
|
142
|
+
.icon-strip-hint.hidden {
|
|
143
|
+
display: none;
|
|
144
|
+
}
|
|
145
|
+
|
|
115
146
|
/* --- Scrollable project list area --- */
|
|
116
147
|
.icon-strip-projects {
|
|
117
148
|
width: 100%;
|
|
@@ -763,12 +794,23 @@
|
|
|
763
794
|
height: 22px;
|
|
764
795
|
}
|
|
765
796
|
|
|
766
|
-
/*
|
|
767
|
-
.
|
|
797
|
+
/* Mate section wrapper — pinned just above the user-island at the
|
|
798
|
+
bottom of the strip. Groups the \u2318M hint pill with the users
|
|
799
|
+
list so the hint sits directly above the mate avatars rather than
|
|
800
|
+
floating up in the project section's flex flow. */
|
|
801
|
+
.icon-strip-mate-section {
|
|
768
802
|
position: absolute;
|
|
769
803
|
bottom: 74px; /* above user-island (58px + 8px bottom + 8px gap) */
|
|
770
804
|
left: 50%;
|
|
771
805
|
transform: translateX(-50%);
|
|
806
|
+
display: flex;
|
|
807
|
+
flex-direction: column;
|
|
808
|
+
align-items: center;
|
|
809
|
+
gap: 4px;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/* --- User avatars (circle, inside the mate section) --- */
|
|
813
|
+
.icon-strip-users {
|
|
772
814
|
width: 56px;
|
|
773
815
|
display: flex;
|
|
774
816
|
flex-direction: column;
|
package/lib/public/css/input.css
CHANGED
|
@@ -885,7 +885,10 @@
|
|
|
885
885
|
#ask-mate-btn:hover { background: rgba(var(--overlay-rgb), 0.06); color: var(--text); }
|
|
886
886
|
#ask-mate-btn:active { transform: scale(0.95); }
|
|
887
887
|
|
|
888
|
-
/* Mate avatar overlay on @ button
|
|
888
|
+
/* Mate avatar overlay on @ button.
|
|
889
|
+
Rendered desaturated and dim so it sits quietly in the corner of the
|
|
890
|
+
input area. Regains saturation on hover, so the user only sees the
|
|
891
|
+
actual mate identity when they're actively reaching for the button. */
|
|
889
892
|
.ask-mate-avatar {
|
|
890
893
|
position: absolute;
|
|
891
894
|
bottom: 0;
|
|
@@ -895,12 +898,14 @@
|
|
|
895
898
|
border-radius: 50%;
|
|
896
899
|
border: 1.5px solid var(--input-bg);
|
|
897
900
|
pointer-events: none;
|
|
898
|
-
opacity: 0.
|
|
899
|
-
|
|
901
|
+
opacity: 0.45;
|
|
902
|
+
filter: grayscale(100%);
|
|
903
|
+
transition: opacity 0.3s, transform 0.3s, filter 0.3s;
|
|
900
904
|
}
|
|
901
905
|
|
|
902
906
|
#ask-mate-btn:hover .ask-mate-avatar {
|
|
903
|
-
opacity: 0.
|
|
907
|
+
opacity: 0.9;
|
|
908
|
+
filter: grayscale(0%);
|
|
904
909
|
}
|
|
905
910
|
|
|
906
911
|
.ask-mate-avatar.fade-out {
|
|
@@ -909,7 +914,7 @@
|
|
|
909
914
|
}
|
|
910
915
|
|
|
911
916
|
.ask-mate-avatar.fade-in {
|
|
912
|
-
opacity: 0.
|
|
917
|
+
opacity: 0.45;
|
|
913
918
|
transform: scale(1);
|
|
914
919
|
}
|
|
915
920
|
|
package/lib/public/css/mates.css
CHANGED
|
@@ -588,6 +588,15 @@
|
|
|
588
588
|
border-right: none;
|
|
589
589
|
overflow: hidden;
|
|
590
590
|
flex-shrink: 0;
|
|
591
|
+
-webkit-user-select: none;
|
|
592
|
+
user-select: none;
|
|
593
|
+
}
|
|
594
|
+
/* Inputs inside the mate sidebar remain selectable. */
|
|
595
|
+
#mate-sidebar-column input,
|
|
596
|
+
#mate-sidebar-column textarea,
|
|
597
|
+
#mate-sidebar-column [contenteditable="true"] {
|
|
598
|
+
-webkit-user-select: text;
|
|
599
|
+
user-select: text;
|
|
591
600
|
}
|
|
592
601
|
|
|
593
602
|
#mate-sidebar-column.hidden {
|
|
@@ -1015,38 +1024,18 @@ body.mate-dm-active #layout.sidebar-collapsed .mate-collapsed-info {
|
|
|
1015
1024
|
text-overflow: ellipsis;
|
|
1016
1025
|
}
|
|
1017
1026
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
padding: 8px 8px 4px;
|
|
1027
|
+
/* Mate sidebar tools: icon+caption grid, matches #session-actions.
|
|
1028
|
+
Tile styling comes from .palette-tile in sidebar.css. */
|
|
1029
|
+
#mate-sidebar-tools-wrap {
|
|
1022
1030
|
flex-shrink: 0;
|
|
1023
1031
|
border-bottom: 1px solid var(--border-subtle);
|
|
1032
|
+
padding-bottom: 4px;
|
|
1024
1033
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
padding: 6px 10px;
|
|
1031
|
-
border: none;
|
|
1032
|
-
background: none;
|
|
1033
|
-
color: var(--text-muted);
|
|
1034
|
-
font-size: 13px;
|
|
1035
|
-
font-weight: 600;
|
|
1036
|
-
font-family: inherit;
|
|
1037
|
-
border-radius: 8px;
|
|
1038
|
-
cursor: pointer;
|
|
1039
|
-
transition: background 0.15s, color 0.15s;
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
#mate-sidebar-tools button:hover {
|
|
1043
|
-
background: var(--sidebar-hover);
|
|
1044
|
-
color: var(--text-secondary);
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
#mate-sidebar-tools button svg {
|
|
1048
|
-
width: 16px;
|
|
1049
|
-
height: 16px;
|
|
1034
|
+
#mate-sidebar-tools {
|
|
1035
|
+
display: grid;
|
|
1036
|
+
grid-template-columns: repeat(4, 1fr);
|
|
1037
|
+
gap: 4px;
|
|
1038
|
+
padding: 0 8px 4px;
|
|
1050
1039
|
}
|
|
1051
1040
|
|
|
1052
1041
|
/* Knowledge editor panel (overlays main content) */
|