clay-server 5.3.0-beta.6 → 5.3.0
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-admin-overview.js +22 -11
- package/lib/project-background-tasks.js +76 -0
- package/lib/project-connection.js +1 -1
- package/lib/project-sessions.js +12 -5
- package/lib/project-split-views.js +12 -3
- package/lib/project-view-store.js +7 -5
- package/lib/project-worker-conversation.js +5 -0
- package/lib/public/app.js +7 -0
- package/lib/public/css/contextual-clay.css +2 -1
- package/lib/public/css/mate-conversation.css +1 -1
- package/lib/public/css/pending-message-queue.css +24 -19
- package/lib/public/css/project-admin-overview.css +42 -13
- package/lib/public/css/shared-browser.css +31 -8
- package/lib/public/css/split-view-layout.css +92 -7
- package/lib/public/css/tool-pane.css +4 -7
- package/lib/public/index.html +5 -5
- package/lib/public/modules/agent-message-bubbles.js +96 -0
- package/lib/public/modules/app-header.js +6 -0
- package/lib/public/modules/app-messages.js +26 -9
- package/lib/public/modules/background-task-previews.js +8 -0
- package/lib/public/modules/background-task-viewer.js +97 -0
- package/lib/public/modules/background-tasks-ui.js +34 -125
- package/lib/public/modules/chat-bubble-renderer.js +11 -5
- package/lib/public/modules/contextual-clay-geometry.js +7 -3
- package/lib/public/modules/pane-activation.js +5 -3
- package/lib/public/modules/pane-header-tooltips.js +32 -0
- package/lib/public/modules/pending-message-queue-drag.js +69 -0
- package/lib/public/modules/pending-message-queue.js +3 -33
- package/lib/public/modules/permission-control.js +6 -5
- package/lib/public/modules/project-admin-overview.js +125 -10
- package/lib/public/modules/project-grouping.js +9 -0
- package/lib/public/modules/server-settings-controls.js +33 -4
- package/lib/public/modules/session-folder-toolbar.js +1 -1
- package/lib/public/modules/session-tool-item.js +86 -0
- package/lib/public/modules/settings-select.js +2 -1
- package/lib/public/modules/shared-browser-tabs.js +37 -21
- package/lib/public/modules/sidebar-projects.js +1 -10
- package/lib/public/modules/split-session-empty-pane.js +47 -0
- package/lib/public/modules/split-view-create-menu.js +74 -65
- package/lib/public/modules/split-view-layout-scope.js +4 -0
- package/lib/public/modules/split-view-picker-actions.js +71 -0
- package/lib/public/modules/split-view-picker-render.js +81 -0
- package/lib/public/modules/split-view-presentation-renderer.js +8 -8
- package/lib/public/modules/split-view-worker-activity.js +2 -2
- package/lib/public/modules/split-view-worker-transcript.js +70 -33
- package/lib/public/modules/split-view-workers.js +6 -4
- package/lib/public/modules/terminal-viewport.js +14 -0
- package/lib/public/modules/terminal.js +3 -4
- package/lib/public/modules/theme-palette.js +177 -0
- package/lib/public/modules/theme.js +1 -174
- package/lib/public/modules/tool-pane-chrome.js +7 -1
- package/lib/public/modules/tools.js +5 -89
- package/lib/public/modules/tooltip.js +32 -6
- package/lib/public/modules/wireframe-workbench.js +2 -2
- package/lib/public/modules/worker-message-previews.js +39 -0
- package/lib/public/pane-assets/app-5W6KJXK5.js +1 -0
- package/lib/public/pane-assets/chunk-2IJUTAB6.js +2 -0
- package/lib/public/pane-assets/{chunk-BMHTBRMO.js → chunk-7OVNC5HT.js} +1 -1
- package/lib/public/pane-assets/chunk-BQN7B62H.js +3 -0
- package/lib/public/pane-assets/{chunk-2AIWCI6Y.js → chunk-EMLN2Y7L.js} +1 -1
- package/lib/public/pane-assets/{chunk-VKYBAVWT.js → chunk-ENPAHXLB.js} +1 -1
- package/lib/public/pane-assets/chunk-EPBGGZRT.js +1 -0
- package/lib/public/pane-assets/chunk-EYTE56IA.js +1 -0
- package/lib/public/pane-assets/{chunk-6NYCNWVF.js → chunk-FQTNR4ZS.js} +1 -1
- package/lib/public/pane-assets/chunk-L6LSA5WI.js +1 -0
- package/lib/public/pane-assets/chunk-M57UIGZV.js +1 -0
- package/lib/public/pane-assets/chunk-MGTFTEJP.js +1 -0
- package/lib/public/pane-assets/chunk-MMG6MYCN.js +1 -0
- package/lib/public/pane-assets/chunk-MZLMEXMW.js +1 -0
- package/lib/public/pane-assets/chunk-OG3E5SVA.js +166 -0
- package/lib/public/pane-assets/chunk-RGAFZ3T2.js +1 -0
- package/lib/public/pane-assets/chunk-RNDRURMF.js +1 -0
- package/lib/public/pane-assets/{chunk-PV4PCH2V.js → chunk-SJOT74VW.js} +1 -1
- package/lib/public/pane-assets/{chunk-LDIZLKB2.js → chunk-SJTGHIN5.js} +1 -1
- package/lib/public/pane-assets/chunk-SLMAFKI2.js +1 -0
- package/lib/public/pane-assets/chunk-U3I5KY3B.js +1 -0
- package/lib/public/pane-assets/{chunk-VNAV23CM.js → chunk-UPXU2IPC.js} +1 -1
- package/lib/public/pane-assets/{chunk-TMGSDFJI.js → chunk-VEBYD7TL.js} +1 -1
- package/lib/public/pane-assets/chunk-XBHYNXXT.js +1 -0
- package/lib/public/pane-assets/manifest.json +411 -399
- package/lib/public/pane-assets/style-BSKKK54Q.css +1 -0
- package/lib/sdk-message-processor.js +5 -2
- package/lib/server-storage.js +27 -3
- package/lib/server.js +4 -2
- package/lib/sessions.js +1 -1
- package/lib/storage-usage.js +29 -7
- package/lib/terminal-manager.js +2 -2
- package/lib/worker-message-previews.js +50 -0
- package/lib/ws-schema.js +1 -1
- package/lib/yoke/adapters/claude-worker.js +8 -0
- package/lib/yoke/adapters/claude.js +17 -2
- package/lib/yoke/claude-task-reader.js +94 -0
- package/lib/yoke/interface.js +1 -0
- package/package.json +1 -1
- package/lib/public/pane-assets/app-7UZ727NA.js +0 -1
- package/lib/public/pane-assets/chunk-6MIHIQ2W.js +0 -1
- package/lib/public/pane-assets/chunk-6UJ2ALOB.js +0 -1
- package/lib/public/pane-assets/chunk-6XK2OATZ.js +0 -1
- package/lib/public/pane-assets/chunk-BKIDR2ME.js +0 -1
- package/lib/public/pane-assets/chunk-GEFCFZEN.js +0 -1
- package/lib/public/pane-assets/chunk-GN2NQEVC.js +0 -2
- package/lib/public/pane-assets/chunk-IMLMBPT4.js +0 -166
- package/lib/public/pane-assets/chunk-K5S6AILM.js +0 -1
- package/lib/public/pane-assets/chunk-OWNNLZ36.js +0 -1
- package/lib/public/pane-assets/chunk-P2C3LOWL.js +0 -3
- package/lib/public/pane-assets/chunk-R4LMPTKU.js +0 -1
- package/lib/public/pane-assets/chunk-VWTHIQCE.js +0 -1
- package/lib/public/pane-assets/chunk-WM3K5AFH.js +0 -1
- package/lib/public/pane-assets/chunk-XOB3O327.js +0 -1
- package/lib/public/pane-assets/chunk-XOOUCFYT.js +0 -1
- package/lib/public/pane-assets/style-ZM7OOU3M.css +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Allowlisted display fields and scalar aggregates, never session identities or path/runtime objects.
|
|
2
2
|
function projectMetadata(status, access, users) {
|
|
3
3
|
if (!status || status.isMate || !access || access.error) return null;
|
|
4
4
|
var owner = access.ownerId && users.findUserById(access.ownerId);
|
|
@@ -13,6 +13,12 @@ function projectMetadata(status, access, users) {
|
|
|
13
13
|
administrativeAccess: true,
|
|
14
14
|
ownershipRecovery: access.ownershipRecovery === "required" || !owner,
|
|
15
15
|
runtimeHealth: "Registered",
|
|
16
|
+
icon: typeof status.icon === 'string' ? status.icon : null,
|
|
17
|
+
isWorktree: status.isWorktree === true,
|
|
18
|
+
parentSlug: status.isWorktree === true && typeof status.parentSlug === 'string' ? status.parentSlug : null,
|
|
19
|
+
sessions: Number.isSafeInteger(status.sessions) && status.sessions >= 0 ? status.sessions : null,
|
|
20
|
+
clients: Number.isSafeInteger(status.clients) && status.clients >= 0 ? status.clients : null,
|
|
21
|
+
isProcessing: typeof status.isProcessing === 'boolean' ? status.isProcessing : null,
|
|
16
22
|
};
|
|
17
23
|
}
|
|
18
24
|
|
|
@@ -34,18 +40,23 @@ function page(metadata, projects) {
|
|
|
34
40
|
var accessText = metadata.ownershipRecovery
|
|
35
41
|
? "Ownership needs recovery. The server operator must independently verify or repair the authoritative account-to-OS mapping before ownership repair can succeed. Ambiguous records stay restricted; administrator status cannot claim this project. See the Project ownership and administrator access guide."
|
|
36
42
|
: "Only the project owner can change ownership, visibility and sharing. Administrator status does not grant access to private content.";
|
|
37
|
-
var
|
|
38
|
-
return
|
|
39
|
-
|
|
43
|
+
var navigation = projects.filter(function (project) { return !project.isMate; }).map(function (project) {
|
|
44
|
+
return { slug: project.slug, title: project.title || project.project || project.slug, icon: typeof project.icon === 'string' ? project.icon : null,
|
|
45
|
+
isWorktree: project.isWorktree === true, parentSlug: project.parentSlug || null, contentAccess: project.contentAccess !== false };
|
|
46
|
+
});
|
|
47
|
+
var payload = JSON.stringify({ project: metadata, projects: navigation }).replace(/</g, "\\u003c");
|
|
48
|
+
function fact(label, value) { return '<div><dt>' + escape(label) + '</dt><dd>' + escape(value === null ? 'Unavailable' : String(value)) + '</dd></div>'; }
|
|
40
49
|
return '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>' + escape(metadata.title) + ' · Clay</title>' +
|
|
41
|
-
'<script src="/appearance-bootstrap.js"></script><link rel="stylesheet" href="/style.css"><link rel="stylesheet" href="/css/project-admin-overview.css"></head><body>' +
|
|
42
|
-
'<div id="layout"><header id="top-bar"><a href="/" class="top-bar-brand-link" aria-label="Clay home"><span class="top-bar-brand-wordmark"></span></a></header>' +
|
|
43
|
-
'<div id="layout-body"><nav id="icon-strip" aria-label="Projects"><
|
|
44
|
-
'<main id="main-area" class="project-admin-overview"><div class="project-admin-canvas"><h1>' + escape(metadata.title) + '</h1>' +
|
|
50
|
+
'<script src="/appearance-bootstrap.js"></script><link rel="stylesheet" href="/style.css"><link rel="stylesheet" href="/css/project-admin-overview.css"></head><body class="project-admin-page">' +
|
|
51
|
+
'<div id="layout"><header id="top-bar"><a href="/" class="top-bar-brand-link" aria-label="Clay home"><span class="top-bar-brand-wordmark"></span></a><span class="project-admin-role">Server administration</span></header>' +
|
|
52
|
+
'<div id="layout-body"><nav id="icon-strip" aria-label="Projects"><a class="icon-strip-home" href="/" aria-label="Clay home" data-tip="Clay home"><img class="icon-strip-logo" src="/clay-studio-symbol.png" alt="" width="38" height="38"></a><div class="icon-strip-separator"></div><div class="icon-strip-projects" id="icon-strip-projects"></div></nav>' +
|
|
53
|
+
'<main id="main-area" class="project-admin-overview"><div class="project-admin-canvas"><div class="project-admin-heading"><div><p class="project-admin-eyebrow">Project overview</p><h1>' + escape(metadata.title) + '</h1><p id="project-admin-parent" hidden></p></div><span class="project-admin-access-badge">Content restricted</span></div>' +
|
|
45
54
|
'<div role="tablist" aria-label="Project information"><button type="button" role="tab" aria-selected="true" id="overview-tab" aria-controls="overview-panel">Overview</button><button type="button" role="tab" aria-selected="false" id="access-tab" aria-controls="access-panel">Access</button></div>' +
|
|
46
|
-
'<section id="overview-panel" role="tabpanel" aria-labelledby="overview-tab"><dl
|
|
47
|
-
'<section id="
|
|
48
|
-
'<
|
|
55
|
+
'<section id="overview-panel" role="tabpanel" aria-labelledby="overview-tab"><dl class="project-admin-facts">' + fact('Owner', metadata.ownerName) + fact('Visibility', metadata.visibility) + fact('Registration', metadata.runtimeHealth) + fact('Sessions', metadata.sessions) + fact('Connected clients', metadata.clients) + fact('Agent activity', metadata.isProcessing === null ? null : metadata.isProcessing ? 'Active turn reported' : 'No active turn reported') + '</dl><p class="project-admin-secondary">Session and activity counts reflect this page load.</p>' +
|
|
56
|
+
'<section class="project-admin-storage" aria-labelledby="storage-heading"><div class="project-admin-section-heading"><h2 id="storage-heading">Storage</h2><button type="button" id="project-admin-refresh">Refresh usage</button></div><div id="project-admin-usage" aria-busy="true"><dl class="project-admin-metrics"><div><dt>Project allocated</dt><dd id="usage-bytes">—</dd></div><div><dt>Files</dt><dd id="usage-files">—</dd></div><div><dt>Volume available</dt><dd id="usage-free">—</dd></div><div><dt>Volume capacity</dt><dd id="usage-capacity">—</dd></div></dl><p id="usage-status" role="status">Measuring storage…</p><p class="project-admin-secondary">Allocated filesystem blocks, including dependencies. Nested registered projects are counted separately; hard links shared across projects are excluded. Volume space is shared, not a project quota.</p></div></section>' +
|
|
57
|
+
'<div class="project-admin-notice"><h2>Administrative metadata only</h2><p>' + escape(privacyText) + '</p></div></section>' +
|
|
58
|
+
'<section id="access-panel" role="tabpanel" aria-labelledby="access-tab" hidden><h2>Administrative metadata only</h2><p>' + escape(accessText) + '</p><dl class="project-admin-facts">' + fact('Owner', metadata.ownerName) + fact('Your access', 'Administrative metadata only') + '</dl></section></div></main></div></div>' +
|
|
59
|
+
'<script type="application/json" id="project-admin-data">' + payload + '</script><script type="module" src="/modules/project-admin-overview.js"></script></body></html>';
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
module.exports = { metadataFor: metadataFor, page: page };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Read-only inspection of native background work through its existing query.
|
|
2
|
+
function remember(session, tasks) {
|
|
3
|
+
var generation = session._sdkQueryGeneration || 0;
|
|
4
|
+
var catalog = session.backgroundTaskCatalog;
|
|
5
|
+
if (!catalog || catalog.generation !== generation) catalog = { generation: generation, tasks: [] };
|
|
6
|
+
var next = (tasks || []).slice(0, 64).map(function (task) {
|
|
7
|
+
var old = catalog.tasks.find(function (item) { return item.task_id === task.task_id && String(item.run_id || '') === String(task.run_id || ''); });
|
|
8
|
+
return Object.assign(old || {}, task, { active: true });
|
|
9
|
+
});
|
|
10
|
+
catalog.tasks.forEach(function (old) {
|
|
11
|
+
if (!next.some(function (task) { return task.task_id === old.task_id; })) next.push(Object.assign(old, { active: false }));
|
|
12
|
+
});
|
|
13
|
+
catalog.tasks = next.slice(0, 64); session.backgroundTaskCatalog = catalog;
|
|
14
|
+
return catalog.tasks;
|
|
15
|
+
}
|
|
16
|
+
function observeMessage(session, parsed, send) {
|
|
17
|
+
if (!parsed || parsed.messageRole !== 'assistant' || !Array.isArray(parsed.content)) return false;
|
|
18
|
+
var id = session.taskIdMap && session.taskIdMap[parsed.parentToolUseId];
|
|
19
|
+
var catalog = session.backgroundTaskCatalog;
|
|
20
|
+
if (!id || !catalog || catalog.generation !== (session._sdkQueryGeneration || 0)) return false;
|
|
21
|
+
var task = catalog.tasks.find(function (item) { return item.task_id === id && item.active; });
|
|
22
|
+
if (!task) return false;
|
|
23
|
+
var text = parsed.content.filter(function (part) { return part && part.type === 'text' && typeof part.text === 'string'; }).map(function (part) { return part.text; }).join('\n').slice(0, 1200);
|
|
24
|
+
if (!text.trim()) return false;
|
|
25
|
+
var id = parsed.uuid || String(Date.now());
|
|
26
|
+
var preview = { id: id, text: text, vendor: session.vendor || null, timestamp: task.preview && task.preview.id === id ? task.preview.timestamp : Date.now() };
|
|
27
|
+
if (task.preview && task.preview.id === preview.id && task.preview.text === preview.text) return false;
|
|
28
|
+
task.preview = preview;
|
|
29
|
+
send(session, { type: 'background_task_preview', taskId: task.task_id, runId: task.run_id || null, sessionOriginId: session.sessionOriginId, preview: preview, live: true });
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
function attach(ctx) {
|
|
33
|
+
var pending = new WeakSet();
|
|
34
|
+
function authorized(ws, session) {
|
|
35
|
+
if (!session || ctx.getSessionForWs(ws) !== session || ws.readyState !== 1 || session.destroying) return false;
|
|
36
|
+
var users = ctx.usersModule;
|
|
37
|
+
if (!users.isMultiUser()) return !session.ownerId;
|
|
38
|
+
var user = ws._clayUser;
|
|
39
|
+
return !!(user && users.findUserById(user.id) && users.canAccessProject(user.id, (ctx.getProjectAccess ? ctx.getProjectAccess() : { visibility: "public" })) &&
|
|
40
|
+
users.canAccessSession(user.id, session, (ctx.getProjectAccess ? ctx.getProjectAccess() : { visibility: "public" })));
|
|
41
|
+
}
|
|
42
|
+
async function read(ws, msg) {
|
|
43
|
+
var session = ctx.getSessionForWs(ws), query = session && session.queryInstance;
|
|
44
|
+
var generation = session && session._sdkQueryGeneration;
|
|
45
|
+
var catalog = session && session.backgroundTaskCatalog;
|
|
46
|
+
var task = catalog && catalog.generation === (generation || 0) && catalog.tasks.find(function (item) {
|
|
47
|
+
return item.task_id === msg.taskId && String(item.run_id || '') === String(msg.runId || '');
|
|
48
|
+
});
|
|
49
|
+
var actor = ws._clayUser && ws._clayUser.id, cwd = session && session.cwd, owner = session && session.ownerId;
|
|
50
|
+
var osIdentity = session && ctx.getLinuxUserForSession ? ctx.getLinuxUserForSession(session) : null;
|
|
51
|
+
var requestId = typeof msg.requestId === 'string' ? msg.requestId.slice(0, 120) : null;
|
|
52
|
+
function current() { return authorized(ws, session) && ctx.sm.sessions.get(session.localId) === session &&
|
|
53
|
+
session.sessionOriginId === msg.sessionOriginId && session.queryInstance === query && session._sdkQueryGeneration === generation &&
|
|
54
|
+
session.cwd === cwd && session.ownerId === owner && (!ctx.getLinuxUserForSession || ctx.getLinuxUserForSession(session) === osIdentity) && (ws._clayUser && ws._clayUser.id) === actor && session.backgroundTaskCatalog === catalog &&
|
|
55
|
+
catalog.tasks.indexOf(task) >= 0; }
|
|
56
|
+
function reply(data) { if (authorized(ws, session)) ctx.sendTo(ws, Object.assign({ type: 'background_task_read', requestId: requestId,
|
|
57
|
+
taskId: msg.taskId, runId: msg.runId || null, sessionOriginId: msg.sessionOriginId }, data)); }
|
|
58
|
+
if (!requestId || !task || !current() || !query || msg.type === 'background_task_read' && typeof query.readBackgroundTask !== 'function') {
|
|
59
|
+
reply({ error: 'This task is unavailable in the current session.' }); return;
|
|
60
|
+
}
|
|
61
|
+
var reading = msg.type === 'background_task_read';
|
|
62
|
+
if (reading && pending.has(ws)) { reply({ error: 'A task read is already in progress.' }); return; }
|
|
63
|
+
if (reading) pending.add(ws);
|
|
64
|
+
try {
|
|
65
|
+
var data;
|
|
66
|
+
if (msg.type === 'background_task_stop') {
|
|
67
|
+
if (!task.active || typeof query.stopTask !== 'function') throw new Error('Task is no longer running.');
|
|
68
|
+
await query.stopTask(task.task_id); data = { stopRequested: true };
|
|
69
|
+
} else data = await query.readBackgroundTask({ taskId: task.task_id, runId: task.run_id || null, offset: msg.offset });
|
|
70
|
+
if (current()) reply({ data: data });
|
|
71
|
+
} catch (error) { if (current()) reply({ error: 'Task transcript or output is not available yet.' }); }
|
|
72
|
+
finally { if (reading) pending.delete(ws); }
|
|
73
|
+
}
|
|
74
|
+
return { handleMessage: function (ws, msg) { if (msg.type !== 'background_task_read' && msg.type !== 'background_task_stop') return false; read(ws, msg); return true; } };
|
|
75
|
+
}
|
|
76
|
+
module.exports = { attach: attach, remember: remember, observeMessage: observeMessage };
|
|
@@ -334,7 +334,7 @@ function attachConnection(ctx) {
|
|
|
334
334
|
if (active.isProcessing) {
|
|
335
335
|
sendTo(ws, { type: "status", status: "processing" });
|
|
336
336
|
}
|
|
337
|
-
sendTo(ws, { type: "active_background_tasks", tasks: active.activeBackgroundTasks || [] });
|
|
337
|
+
sendTo(ws, { type: "active_background_tasks", tasks: active.activeBackgroundTasks || [], catalog: active.backgroundTaskCatalog && active.backgroundTaskCatalog.tasks || [] });
|
|
338
338
|
var pendingIds = Object.keys(active.pendingPermissions);
|
|
339
339
|
for (var pi = 0; pi < pendingIds.length; pi++) {
|
|
340
340
|
var p = active.pendingPermissions[pendingIds[pi]];
|
package/lib/project-sessions.js
CHANGED
|
@@ -93,6 +93,7 @@ function formatAskUserAnswerAsMessage(input, answers) {
|
|
|
93
93
|
function attachSessions(ctx) {
|
|
94
94
|
var mateDefaultsToken = {};
|
|
95
95
|
var contextUsage = contextUsageModule.attachContextUsage(ctx);
|
|
96
|
+
var backgroundTaskReader = require("./project-background-tasks").attach(ctx);
|
|
96
97
|
var cwd = ctx.cwd;
|
|
97
98
|
var slug = ctx.slug;
|
|
98
99
|
var isMate = ctx.isMate;
|
|
@@ -606,6 +607,7 @@ function attachSessions(ctx) {
|
|
|
606
607
|
if (workerRemoval.handleMessage(ws, msg)) return true;
|
|
607
608
|
if (serverSettingsMessages.handleServerSettings({ opts: opts, usersModule: usersModule, sendTo: sendTo, send: send }, ws, msg)) return true;
|
|
608
609
|
if (contextUsage.handleMessage(ws, msg)) return true;
|
|
610
|
+
if (backgroundTaskReader.handleMessage(ws, msg)) return true;
|
|
609
611
|
|
|
610
612
|
if (msg.type === "push_subscribe") {
|
|
611
613
|
var _pushUserId = ws._clayUser ? ws._clayUser.id : null;
|
|
@@ -681,6 +683,9 @@ function attachSessions(ctx) {
|
|
|
681
683
|
if (creationRequestId) sendTo(ws, { type: "new_session_result", requestId: creationRequestId, ok: false, error: text });
|
|
682
684
|
else sendTo(ws, { type: "error", text: text });
|
|
683
685
|
};
|
|
686
|
+
if (msg.activate === false && (!creationRequestId || isMate || ws._clayPane || msg.slug !== slug)) {
|
|
687
|
+
creationFailed("Inactive creation requires this exact project and an ordinary session request."); return true;
|
|
688
|
+
}
|
|
684
689
|
// The same request id is created at most once per socket.
|
|
685
690
|
if (creationRequestId) {
|
|
686
691
|
if (!ws._createdRequests) ws._createdRequests = [];
|
|
@@ -723,7 +728,8 @@ function attachSessions(ctx) {
|
|
|
723
728
|
// Reuse an existing blank GUI session instead of stacking another
|
|
724
729
|
// one. A vendor-less blank is acceptable: nothing has happened in it,
|
|
725
730
|
// so stamping the requested vendor is equivalent to a fresh create.
|
|
726
|
-
var
|
|
731
|
+
var inactiveCreation = msg.activate === false && !!creationRequestId && !isMate && !ws._clayPane;
|
|
732
|
+
var reusable = msg.forceNew === true || inactiveCreation ? null : sm.findReusableBlankSession({
|
|
727
733
|
vendor: sessionOpts.vendor || null,
|
|
728
734
|
ownerId: sessionOpts.ownerId || null,
|
|
729
735
|
});
|
|
@@ -734,9 +740,10 @@ function attachSessions(ctx) {
|
|
|
734
740
|
sm.switchSession(reusable.localId, ws);
|
|
735
741
|
newSess = reusable;
|
|
736
742
|
} else {
|
|
737
|
-
newSess = sm.createSession(sessionOpts, ws);
|
|
743
|
+
newSess = inactiveCreation ? sm.createSessionRaw(sessionOpts) : sm.createSession(sessionOpts, ws);
|
|
738
744
|
}
|
|
739
|
-
ws._clayActiveSession = newSess.localId;
|
|
745
|
+
if (!inactiveCreation) ws._clayActiveSession = newSess.localId;
|
|
746
|
+
else { sm.saveSessionFile(newSess); sm.broadcastSessionList(); }
|
|
740
747
|
var folderPlaced = msg.folderId === undefined ? null : sessionFolders.placeNewSession(ws, newSess, msg.folderId, msg.folderSlug);
|
|
741
748
|
if (msg.requestId) {
|
|
742
749
|
var created = { type: "new_session_result", requestId: String(msg.requestId).substring(0, 200), ok: true, sessionId: newSess.localId };
|
|
@@ -758,8 +765,8 @@ function attachSessions(ctx) {
|
|
|
758
765
|
send({ type: "last_vendor", vendor: msg.vendor });
|
|
759
766
|
}
|
|
760
767
|
var nsPresKey = ws._clayUser ? ws._clayUser.id : "_default";
|
|
761
|
-
if (!ws._clayPane) userPresence.setPresence(slug, nsPresKey, userPresence.sessionIdForPersistence(newSess), null);
|
|
762
|
-
if (usersModule.isMultiUser() && !ws._clayPane) {
|
|
768
|
+
if (!inactiveCreation && !ws._clayPane) userPresence.setPresence(slug, nsPresKey, userPresence.sessionIdForPersistence(newSess), null);
|
|
769
|
+
if (!inactiveCreation && usersModule.isMultiUser() && !ws._clayPane) {
|
|
763
770
|
broadcastPresence();
|
|
764
771
|
}
|
|
765
772
|
return true;
|
|
@@ -73,6 +73,13 @@ function attachProjectSplitViews(ctx) {
|
|
|
73
73
|
var accountId = users.isMultiUser() ? ownerId : 'default';
|
|
74
74
|
return typeof users.getSessionFolders === 'function' ? users.getSessionFolders(accountId, ctx.slug).folders.map(function (folder) { return folder.id; }) : [];
|
|
75
75
|
}
|
|
76
|
+
function destinationFolder(ownerId, view, origin) {
|
|
77
|
+
var state = typeof users.getSessionFolders === 'function' ? users.getSessionFolders(users.isMultiUser() ? ownerId : 'default', ctx.slug) : null;
|
|
78
|
+
if (!state) return null;
|
|
79
|
+
// Singleton Views still appear as session rows and use session placement.
|
|
80
|
+
var id = view && view.sessionRefs.length !== 1 ? view.folderId : (state.assignments || {})[view ? view.sessionRefs[0].originId : origin];
|
|
81
|
+
return state.folders.some(function (folder) { return folder.id === id; }) ? id : null;
|
|
82
|
+
}
|
|
76
83
|
function stateFor(ws) {
|
|
77
84
|
var current = actor(ws);
|
|
78
85
|
if (!current) return null;
|
|
@@ -137,10 +144,11 @@ function attachProjectSplitViews(ctx) {
|
|
|
137
144
|
input.liveSessions = refs.sessions;
|
|
138
145
|
if (action === "update") {
|
|
139
146
|
var exact = verifyStoredView(ws, current, input.id);
|
|
140
|
-
|
|
147
|
+
var inheritedFolder = target && target.sessionRefs.length === 1 && input.sessionOrigins.length > 1 ? destinationFolder(current.id, target) : undefined;
|
|
148
|
+
return exact.ok ? store.update(current.id, current.linuxUser, input, refs.refs, inheritedFolder) : exact;
|
|
141
149
|
}
|
|
142
150
|
input.nameMode = "automatic";
|
|
143
|
-
return store.create(current.id, current.linuxUser, input, refs.refs);
|
|
151
|
+
return store.create(current.id, current.linuxUser, input, refs.refs, destinationFolder(current.id, null, input.activeSessionOriginId));
|
|
144
152
|
}
|
|
145
153
|
if (action === "move") {
|
|
146
154
|
var sourceState = store.state(current.id, current.linuxUser);
|
|
@@ -159,7 +167,8 @@ function attachProjectSplitViews(ctx) {
|
|
|
159
167
|
if (!targetResolved.refs) return targetResolved;
|
|
160
168
|
if (input.targetWorkspace && !require('./project-view-pair').authorized(ctx, ws, input.targetWorkspace.linkedPair)) return { ok: false, code: 'pair', error: 'The exact linked Worker pair is unavailable.' };
|
|
161
169
|
input.liveSessions = targetResolved.sessions;
|
|
162
|
-
|
|
170
|
+
var destination = sourceState.views.find(function (view) { return view.id === input.targetViewId; });
|
|
171
|
+
return store.move(current.id, current.linuxUser, input, sourceResolved.refs, targetResolved.refs, destinationFolder(current.id, destination, input.targetSessionOriginId));
|
|
163
172
|
}
|
|
164
173
|
if (action === 'organize' && (input.projectSlug !== ctx.slug || input.folderId !== null && folderIds(current.id).indexOf(input.folderId) < 0)) return { ok: false, code: 'folder', error: 'That project folder is no longer available.' };
|
|
165
174
|
var exactView = verifyStoredView(ws, current, input.id);
|
|
@@ -122,7 +122,7 @@ function createSplitViewsStore(opts) {
|
|
|
122
122
|
});
|
|
123
123
|
return refs.every(function (ref) { return used.indexOf(ref.originId) === -1; });
|
|
124
124
|
}
|
|
125
|
-
function create(ownerId, linuxUser, input, resolvedRefs) {
|
|
125
|
+
function create(ownerId, linuxUser, input, resolvedRefs, destinationFolderId) {
|
|
126
126
|
input = input || {};
|
|
127
127
|
var prepared = prepareRecord(ownerId, linuxUser, input.expectedRevision);
|
|
128
128
|
if (!prepared.ok) return prepared;
|
|
@@ -136,14 +136,14 @@ function createSplitViewsStore(opts) {
|
|
|
136
136
|
if (!uniqueMembership(prepared.record.views, refs.refs)) return { ok: false, error: "A session can belong to only one Split view" };
|
|
137
137
|
var nameMode = input.nameMode === "custom" ? "custom" : "automatic";
|
|
138
138
|
var view = { id: crypto.randomUUID(), name: nameMode === "automatic" ? names.automaticName(input.liveSessions) : name.name, nameMode: nameMode,
|
|
139
|
-
folderId: null, favorite: false, revision: prepared.record.revision + 1, layout: input.layout,
|
|
139
|
+
folderId: destinationFolderId || null, favorite: false, revision: prepared.record.revision + 1, layout: input.layout,
|
|
140
140
|
activeSessionOriginId: input.activeSessionOriginId, tree: clone(input.tree), sessionRefs: refs.refs };
|
|
141
141
|
if (input.workspace !== undefined || refs.refs.length < 2) view.workspace = input.workspace === undefined ? workspace.legacy(view) : clone(input.workspace);
|
|
142
142
|
prepared.record.views.push(view);
|
|
143
143
|
if (input.active === true) prepared.record.activeViewId = view.id;
|
|
144
144
|
return commit(ownerId, prepared.record, view);
|
|
145
145
|
}
|
|
146
|
-
function update(ownerId, linuxUser, input, resolvedRefs) {
|
|
146
|
+
function update(ownerId, linuxUser, input, resolvedRefs, destinationFolderId) {
|
|
147
147
|
input = input || {};
|
|
148
148
|
var prepared = prepareRecord(ownerId, linuxUser, input.expectedRevision);
|
|
149
149
|
if (!prepared.ok) return prepared;
|
|
@@ -168,6 +168,7 @@ function createSplitViewsStore(opts) {
|
|
|
168
168
|
if (names.mode(found.view) === "automatic") found.view.name = names.automaticName(input.liveSessions);
|
|
169
169
|
found.view.layout = input.layout; found.view.activeSessionOriginId = input.activeSessionOriginId;
|
|
170
170
|
found.view.tree = clone(input.tree); found.view.sessionRefs = refs.refs;
|
|
171
|
+
if (destinationFolderId !== undefined) found.view.folderId = destinationFolderId;
|
|
171
172
|
if (input.workspace !== undefined || refs.refs.length < 2) found.view.workspace = input.workspace === undefined ? workspace.legacy(found.view) : clone(input.workspace);
|
|
172
173
|
if (input.active === true) prepared.record.activeViewId = found.view.id;
|
|
173
174
|
return commit(ownerId, prepared.record, found.view);
|
|
@@ -201,7 +202,7 @@ function createSplitViewsStore(opts) {
|
|
|
201
202
|
if (prepared.record.activeViewId === removed.id) prepared.record.activeViewId = null;
|
|
202
203
|
return commit(ownerId, prepared.record, null);
|
|
203
204
|
}
|
|
204
|
-
function move(ownerId, linuxUser, input, resolvedSourceRefs, resolvedTargetRefs) {
|
|
205
|
+
function move(ownerId, linuxUser, input, resolvedSourceRefs, resolvedTargetRefs, destinationFolderId) {
|
|
205
206
|
input = input || {};
|
|
206
207
|
var prepared = prepareRecord(ownerId, linuxUser, input.expectedRevision);
|
|
207
208
|
if (!prepared.ok) return prepared;
|
|
@@ -286,11 +287,12 @@ function createSplitViewsStore(opts) {
|
|
|
286
287
|
if (nextSourceOrigins.length < 2 && !source.view.workspace) { prepared.record.views.splice(source.index, 1); if (prepared.record.activeViewId === source.view.id) prepared.record.activeViewId = null; }
|
|
287
288
|
if (nextSourceOrigins.length >= 2 && names.mode(source.view) === "automatic") source.view.name = names.automaticName(input.sourceLiveSessions.filter(function (session) { return session.sessionOriginId !== input.sourceSessionOriginId; }));
|
|
288
289
|
if (target) {
|
|
290
|
+
if (destinationFolderId !== undefined) target.view.folderId = destinationFolderId;
|
|
289
291
|
target.view.tree = clone(input.tree); target.view.layout = input.layout;
|
|
290
292
|
target.view.activeSessionOriginId = input.activeSessionOriginId; target.view.sessionRefs = targetRefs.refs;
|
|
291
293
|
if (names.mode(target.view) === "automatic") target.view.name = names.automaticName(input.liveSessions);
|
|
292
294
|
} else {
|
|
293
|
-
target = { index: -1, view: { id: crypto.randomUUID(), folderId: null, favorite: false, name: names.automaticName(input.liveSessions), nameMode: "automatic", revision: prepared.record.revision + 1,
|
|
295
|
+
target = { index: -1, view: { id: crypto.randomUUID(), folderId: destinationFolderId || null, favorite: false, name: names.automaticName(input.liveSessions), nameMode: "automatic", revision: prepared.record.revision + 1,
|
|
294
296
|
layout: input.layout, activeSessionOriginId: input.activeSessionOriginId, tree: clone(input.tree), sessionRefs: targetRefs.refs } };
|
|
295
297
|
prepared.record.views.push(target.view);
|
|
296
298
|
}
|
|
@@ -104,6 +104,7 @@ function attachProjectWorkerConversation(ctx) {
|
|
|
104
104
|
if (type === "user_message") return { role: "user", text: textContent(entry.text || entry.content) };
|
|
105
105
|
if (type === "delta" || type === "result" || type === "assistant_message") return { role: "assistant", text: textContent(entry.text || entry.content) };
|
|
106
106
|
if (type === "tool_start" || type === "tool_use") return Object.assign({ role: "tool", text: "Used " + (toolIdentity(entry).toolName || "tool") }, toolIdentity(entry));
|
|
107
|
+
if (type === "tool_executing") return Object.assign({ role: "tool", text: JSON.stringify(entry.input || {}) }, toolIdentity(entry));
|
|
107
108
|
if (type === "tool_result") return Object.assign({ role: "tool", text: textContent(entry.content) || "No recorded tool output.", isError: entry.is_error === true }, toolIdentity(entry));
|
|
108
109
|
return null;
|
|
109
110
|
}
|
|
@@ -187,7 +188,11 @@ function attachProjectWorkerConversation(ctx) {
|
|
|
187
188
|
watches.set(ws, watch);
|
|
188
189
|
if (ws.once) ws.once('close', watch.close);
|
|
189
190
|
}
|
|
191
|
+
var previews = require('./worker-message-previews').attach({ sm: sm, send: send, resolve: function (ws, target) {
|
|
192
|
+
var user = actor(ws); return user && exactPair(ws, target, user);
|
|
193
|
+
} });
|
|
190
194
|
function handleMessage(ws, msg) {
|
|
195
|
+
if (msg && previews.handleMessage(ws, msg)) return true;
|
|
191
196
|
if (!msg || msg.type !== "split_worker_conversation_request") return false;
|
|
192
197
|
var requestId = typeof msg.requestId === "string" ? msg.requestId.slice(0, 160) : "";
|
|
193
198
|
var response = { type: "split_worker_conversation", requestId: requestId, pageBefore: msg.before == null ? null : msg.before,
|
package/lib/public/app.js
CHANGED
|
@@ -501,6 +501,13 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
501
501
|
speechLifecycleBound: false,
|
|
502
502
|
askUserRequests: {},
|
|
503
503
|
activeBackgroundTasks: [],
|
|
504
|
+
backgroundTaskCatalog: [],
|
|
505
|
+
backgroundTaskViewer: null,
|
|
506
|
+
splitSessionPicker: null,
|
|
507
|
+
splitSessionCreation: null,
|
|
508
|
+
agentMessagePreviews: {},
|
|
509
|
+
agentMessageBubble: null,
|
|
510
|
+
workerMessagePreviewSubscription: null,
|
|
504
511
|
sessionVendorBound: false,
|
|
505
512
|
cliSessionId: null,
|
|
506
513
|
isHeadlessMode: false,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/* Anchored shell around the unchanged production conversation renderer. */
|
|
2
2
|
#contextual-clay {
|
|
3
|
+
--contextual-chat-gutter: 80px;
|
|
3
4
|
position: fixed;
|
|
4
5
|
z-index: 160;
|
|
5
6
|
top: var(--contextual-top, 38px);
|
|
6
7
|
left: var(--contextual-left, 64px);
|
|
7
8
|
width: var(--contextual-width, 480px);
|
|
8
9
|
height: var(--contextual-height, 640px);
|
|
9
|
-
max-width: calc(100vw - 16px);
|
|
10
|
+
max-width: min(calc(var(--mate-content-width, 860px) + var(--contextual-chat-gutter)), calc(100vw - 16px));
|
|
10
11
|
max-height: calc(100dvh - 16px);
|
|
11
12
|
display: flex;
|
|
12
13
|
flex-direction: column;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Restore the Home conversation's reading measure inside Mate projects.
|
|
2
2
|
The existing project renderer, queue and composer still own behavior. */
|
|
3
|
+
:root { --mate-content-width: 860px; }
|
|
3
4
|
body.mate-workspace-active {
|
|
4
|
-
--mate-content-width: 860px;
|
|
5
5
|
--content-width: var(--mate-content-width);
|
|
6
6
|
}
|
|
7
7
|
body.mate-workspace-active #messages {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#pending-message-queue {
|
|
2
2
|
margin: 0 7px 8px;
|
|
3
|
-
padding:
|
|
4
|
-
border: 1px solid
|
|
5
|
-
border-radius:
|
|
6
|
-
background:
|
|
7
|
-
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--accent) 70%, transparent);
|
|
3
|
+
padding: 4px;
|
|
4
|
+
border: 1px solid var(--border);
|
|
5
|
+
border-radius: 8px;
|
|
6
|
+
background: var(--bg-alt);
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
#pending-message-queue.hidden { display: none; }
|
|
@@ -14,15 +13,14 @@
|
|
|
14
13
|
align-items: center;
|
|
15
14
|
justify-content: space-between;
|
|
16
15
|
min-height: 24px;
|
|
17
|
-
padding: 0
|
|
16
|
+
padding: 0 5px 2px 8px;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
.pending-message-eyebrow {
|
|
21
20
|
color: var(--text-muted);
|
|
22
21
|
font-size: 10px;
|
|
23
|
-
font-weight:
|
|
24
|
-
letter-spacing: 0.
|
|
25
|
-
text-transform: uppercase;
|
|
22
|
+
font-weight: 550;
|
|
23
|
+
letter-spacing: 0.01em;
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
.pending-message-count {
|
|
@@ -62,7 +60,7 @@
|
|
|
62
60
|
.pending-message-list {
|
|
63
61
|
display: flex;
|
|
64
62
|
flex-direction: column;
|
|
65
|
-
gap:
|
|
63
|
+
gap: 0;
|
|
66
64
|
max-height: min(230px, 28vh);
|
|
67
65
|
overflow-y: auto;
|
|
68
66
|
overscroll-behavior: contain;
|
|
@@ -71,27 +69,30 @@
|
|
|
71
69
|
|
|
72
70
|
.pending-message-chip {
|
|
73
71
|
display: grid;
|
|
74
|
-
grid-template-columns:
|
|
72
|
+
grid-template-columns: 16px 24px minmax(0, 1fr) auto;
|
|
75
73
|
align-items: center;
|
|
76
74
|
flex: 0 0 auto;
|
|
77
75
|
min-height: 46px;
|
|
78
|
-
|
|
79
|
-
border
|
|
80
|
-
|
|
76
|
+
position: relative;
|
|
77
|
+
border: 1px solid transparent;
|
|
78
|
+
border-radius: 5px;
|
|
79
|
+
background: transparent;
|
|
81
80
|
transition: border-color 120ms ease, background 120ms ease, transform 120ms ease;
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
.pending-message-chip
|
|
83
|
+
.pending-message-chip + .pending-message-chip { border-top-color: var(--border-subtle); }
|
|
84
|
+
.pending-message-chip:hover { background: rgba(var(--overlay-rgb), 0.035); }
|
|
85
85
|
.pending-message-chip.is-claimed { background: color-mix(in srgb, var(--accent) 5%, var(--bg)); }
|
|
86
86
|
.pending-message-chip.is-dragging { opacity: 0.62; transform: scale(0.992); }
|
|
87
|
-
.pending-message-chip.is-drop-target {
|
|
87
|
+
.pending-message-chip.is-drop-target::before { content: ""; position: absolute; top: -1px; left: 4px; right: 4px; height: 2px; border-radius: 2px; background: var(--accent); }
|
|
88
|
+
.pending-message-chip.is-drop-target.is-drop-after::before { top: auto; bottom: -1px; }
|
|
89
|
+
#pending-message-queue.is-reordering { cursor: grabbing; user-select: none; }
|
|
88
90
|
|
|
89
91
|
.pending-message-number {
|
|
90
92
|
align-self: stretch;
|
|
91
93
|
display: flex;
|
|
92
94
|
align-items: center;
|
|
93
95
|
justify-content: center;
|
|
94
|
-
border-right: 1px solid var(--border-subtle);
|
|
95
96
|
color: var(--text-dimmer);
|
|
96
97
|
font-size: 10px;
|
|
97
98
|
font-variant-numeric: tabular-nums;
|
|
@@ -119,6 +120,10 @@ button.pending-message-drag:active { cursor: grabbing; }
|
|
|
119
120
|
.pending-message-chip.is-claimed .pending-message-drag .lucide { animation: spin 1.2s linear infinite; }
|
|
120
121
|
|
|
121
122
|
.pending-message-content { min-width: 0; padding: 7px 7px 7px 3px; overflow: visible; }
|
|
123
|
+
.pending-message-content:has(> .pending-message-image-strip) { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 9px; align-items: center; }
|
|
124
|
+
.pending-message-content > .pending-message-image-strip { grid-column: 1; grid-row: 1 / 4; margin: 0; min-height: 0; max-width: 86px; flex-wrap: nowrap; overflow-x: auto; }
|
|
125
|
+
.pending-message-content:has(> .pending-message-image-strip) > :not(.pending-message-image-strip) { grid-column: 2; }
|
|
126
|
+
.pending-message-content > .pending-message-image-strip .pending-message-image-thumb { width: 30px; height: 30px; flex: 0 0 30px; }
|
|
122
127
|
.pending-message-preview { overflow: hidden; color: var(--text); font-size: 12px; line-height: 1.3; text-overflow: ellipsis; white-space: nowrap; }
|
|
123
128
|
.pending-message-image-strip,
|
|
124
129
|
.pending-message-image-editor { display: flex; flex-wrap: wrap; gap: 5px; min-height: 36px; margin-top: 6px; align-items: center; }
|
|
@@ -179,10 +184,10 @@ button.pending-message-drag:active { cursor: grabbing; }
|
|
|
179
184
|
.pending-message-edit-actions button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
180
185
|
|
|
181
186
|
@media (max-width: 600px) {
|
|
182
|
-
#pending-message-queue { margin: 0 3px 6px; padding:
|
|
187
|
+
#pending-message-queue { margin: 0 3px 6px; padding: 4px; border-radius: 8px; }
|
|
183
188
|
.pending-message-head { padding-left: 6px; }
|
|
184
189
|
.pending-message-list { max-height: min(196px, 24vh); }
|
|
185
|
-
.pending-message-chip { grid-template-columns:
|
|
190
|
+
.pending-message-chip { grid-template-columns: 16px 28px minmax(0, 1fr) auto; min-height: 44px; }
|
|
186
191
|
.pending-message-drag { width: 28px; height: 34px; }
|
|
187
192
|
.pending-message-actions button { width: 30px; height: 34px; }
|
|
188
193
|
.pending-message-meta { gap: 5px; }
|
|
@@ -1,13 +1,42 @@
|
|
|
1
|
-
.project-admin-overview { overflow: auto; padding: 8px; }
|
|
2
|
-
.project-admin-canvas {
|
|
3
|
-
.project-admin-
|
|
4
|
-
.project-admin-canvas
|
|
5
|
-
.project-admin-canvas
|
|
6
|
-
.project-admin-
|
|
7
|
-
.project-admin-canvas
|
|
8
|
-
.project-admin-
|
|
9
|
-
.project-admin-
|
|
10
|
-
.project-admin-canvas
|
|
11
|
-
.project-admin-canvas
|
|
12
|
-
.project-admin-
|
|
13
|
-
|
|
1
|
+
#main-area.project-admin-overview { display: block; overflow: auto; min-width: 0; padding: 8px; }
|
|
2
|
+
.project-admin-canvas { min-height: 100%; padding: 24px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg); }
|
|
3
|
+
.project-admin-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; max-width: 1000px; margin-bottom: 22px; }
|
|
4
|
+
.project-admin-canvas h1 { font-size: 22px; font-weight: 600; margin: 0; overflow-wrap: anywhere; }
|
|
5
|
+
.project-admin-canvas h2 { font-size: 13px; font-weight: 600; margin: 0; }
|
|
6
|
+
.project-admin-eyebrow, .project-admin-role { color: var(--text-muted); font-size: 11px; }
|
|
7
|
+
.project-admin-canvas .project-admin-eyebrow { margin: 0 0 5px; font-size: 11px; }
|
|
8
|
+
.project-admin-role { margin-left: auto; padding-right: 16px; }
|
|
9
|
+
.project-admin-access-badge { flex-shrink: 0; border: 1px solid var(--border); border-radius: 5px; padding: 5px 8px; color: var(--text-muted); font-size: 11px; }
|
|
10
|
+
.project-admin-canvas [role="tablist"] { display: flex; gap: 18px; border-bottom: 1px solid var(--border); max-width: 1000px; }
|
|
11
|
+
.project-admin-canvas [role="tab"] { height: 32px; padding: 0 2px; border: 0; border-bottom: 2px solid transparent; border-radius: 0; color: var(--text-muted); background: transparent; cursor: pointer; font: inherit; font-size: 12px; }
|
|
12
|
+
.project-admin-canvas [role="tab"][aria-selected="true"] { color: var(--text); border-bottom-color: var(--accent); }
|
|
13
|
+
.project-admin-page :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
14
|
+
.project-admin-facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 22px 28px; margin: 24px 0 28px; max-width: 1000px; }
|
|
15
|
+
.project-admin-facts dt, .project-admin-metrics dt { font-size: 11px; color: var(--text-muted); margin-bottom: 6px; }
|
|
16
|
+
.project-admin-facts dd { font-size: 13px; margin: 0; overflow-wrap: anywhere; }
|
|
17
|
+
.project-admin-canvas p { line-height: 1.6; font-size: 12px; color: var(--text-secondary); }
|
|
18
|
+
#project-admin-parent { margin: 6px 0 0; font-size: 11px; color: var(--text-muted); }
|
|
19
|
+
.project-admin-storage { border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); padding: 18px 0; max-width: 1000px; }
|
|
20
|
+
.project-admin-section-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 18px; }
|
|
21
|
+
.project-admin-section-heading button { height: 28px; border: 1px solid var(--border); border-radius: 6px; padding: 0 9px; font: inherit; font-size: 11px; background: var(--bg-alt); color: var(--text-secondary); cursor: pointer; }
|
|
22
|
+
.project-admin-section-heading button:hover { background: var(--sidebar-hover); }
|
|
23
|
+
.project-admin-section-heading button:disabled { opacity: .55; cursor: wait; }
|
|
24
|
+
.project-admin-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px; margin: 0 0 16px; }
|
|
25
|
+
.project-admin-metrics dd { font-size: 19px; font-weight: 500; margin: 0; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
|
|
26
|
+
.project-admin-canvas #usage-status { margin: 8px 0 4px; font-size: 11px; }
|
|
27
|
+
.project-admin-canvas .project-admin-secondary { font-size: 11px; color: var(--text-muted); max-width: 720px; margin: 4px 0 0; }
|
|
28
|
+
.project-admin-notice { padding-top: 22px; max-width: 680px; }
|
|
29
|
+
.project-admin-notice h2 { color: var(--text-muted); }
|
|
30
|
+
.project-admin-notice p { margin: 6px 0; }
|
|
31
|
+
.project-admin-rail-header { position: relative; }
|
|
32
|
+
.project-admin-page #icon-strip { padding-bottom: 12px; }
|
|
33
|
+
.project-admin-page #icon-strip .icon-strip-home { margin-bottom: 0; }
|
|
34
|
+
@media (max-width: 700px) {
|
|
35
|
+
.project-admin-canvas { padding: 16px; }
|
|
36
|
+
.project-admin-heading { align-items: flex-start; flex-direction: column; gap: 10px; margin-bottom: 16px; }
|
|
37
|
+
.project-admin-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
|
|
38
|
+
.project-admin-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
39
|
+
.project-admin-metrics dd { font-size: 17px; }
|
|
40
|
+
.project-admin-role { font-size: 10px; }
|
|
41
|
+
.project-admin-page #icon-strip { display: flex; width: 60px; }
|
|
42
|
+
}
|
|
@@ -107,14 +107,37 @@
|
|
|
107
107
|
.shared-browser-pointer i { position: absolute; left: -12px; top: -12px; width: 30px; height: 30px; border: 2px solid #a59dff; border-radius: 50%; opacity: 0; }
|
|
108
108
|
@media (prefers-reduced-motion: reduce) { .shared-browser-pointer { transition: none; } }
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
.shared-browser-
|
|
112
|
-
.shared-browser-tab
|
|
113
|
-
.shared-browser-tab
|
|
114
|
-
.shared-browser-tab
|
|
115
|
-
.shared-browser-tab
|
|
116
|
-
.shared-browser-tab
|
|
117
|
-
.shared-browser-tab
|
|
110
|
+
/* Tabs sit on the pane's top edge; the selected face joins the address toolbar. */
|
|
111
|
+
.shared-browser-tabs { display: flex; align-items: center; gap: 3px; min-width: 0; padding: 4px 6px 0; background: var(--bg-alt); flex: 1 1 auto; }
|
|
112
|
+
.shared-browser-tab-list { display: flex; align-items: stretch; min-width: 0; overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain; }
|
|
113
|
+
.shared-browser-tab-list::-webkit-scrollbar { display: none; }
|
|
114
|
+
.shared-browser-tab { position: relative; display: flex; align-items: center; flex: 0 1 160px; width: 160px; min-width: 84px; max-width: 180px; height: 29px; padding: 0 4px; border-radius: 8px 8px 0 0; color: var(--text-muted); }
|
|
115
|
+
.shared-browser-tab::after { content: ''; position: absolute; right: -1px; top: 7px; height: 14px; width: 1px; background: var(--border); }
|
|
116
|
+
.shared-browser-tab:has([aria-selected="true"]), .shared-browser-tab:hover { background: var(--bg); color: var(--text); }
|
|
117
|
+
.shared-browser-tab:has([aria-selected="true"]) { z-index: 1; }
|
|
118
|
+
.shared-browser-tab:has([aria-selected="true"])::after, .shared-browser-tab:has(+ .shared-browser-tab [aria-selected="true"])::after { display: none; }
|
|
119
|
+
.shared-browser-panel .shared-browser-tab [role="tab"] { flex: 1; min-width: 0; height: 27px; gap: 6px; padding: 0 5px; border-radius: 5px; font-size: 11px; font-weight: 400; color: inherit; }
|
|
120
|
+
.shared-browser-panel .shared-browser-tab [role="tab"]:hover { background: transparent; }
|
|
121
|
+
.shared-browser-tab-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
122
|
+
.shared-browser-tab [role="tab"] svg { width: 12px; height: 12px; color: var(--text-muted); }
|
|
123
|
+
.shared-browser-tab[data-phase="ended"] [role="tab"] svg { opacity: .45; }
|
|
124
|
+
.shared-browser-panel .shared-browser-tab-close { flex: 0 0 18px; width: 18px; height: 18px; padding: 3px; border-radius: 50%; color: var(--text-muted); }
|
|
125
|
+
.shared-browser-panel .shared-browser-tab-close svg { width: 11px; height: 11px; }
|
|
126
|
+
.shared-browser-panel .shared-browser-tab-close:hover { background: rgba(var(--overlay-rgb), .1); color: var(--text); }
|
|
127
|
+
.shared-browser-panel .shared-browser-new-tab { flex: 0 0 23px; width: 23px; height: 23px; margin-bottom: 3px; padding: 4px; border-radius: 50%; color: var(--text-muted); }
|
|
128
|
+
.shared-browser-panel .shared-browser-new-tab:hover { background: rgba(var(--overlay-rgb), .08); }
|
|
129
|
+
.tool-pane-header.browser-pane-header { flex-wrap: nowrap; height: 33px; min-height: 33px; padding: 0 6px; gap: 4px; border-bottom: 0; background: var(--bg-alt); align-items: center; }
|
|
130
|
+
.browser-pane-header > .tool-pane-kind { padding: 0 3px; }
|
|
131
|
+
.browser-pane-header > .tool-pane-kind .split-view-model, .browser-pane-header > .tool-pane-title { display: none; }
|
|
132
|
+
.browser-pane-header > .shared-browser-tabs { align-self: stretch; padding: 4px 0 0; flex-shrink: 1; }
|
|
133
|
+
.browser-pane-header > .split-pane-drag-space { flex: 1 0 12px; min-width: 12px; }
|
|
134
|
+
.browser-pane-header > .shared-browser-window { flex-shrink: 0; }
|
|
135
|
+
.shared-browser-panel .shared-browser-address { min-height: 40px; box-sizing: border-box; padding: 5px 7px; background: var(--bg); }
|
|
136
|
+
.shared-browser-panel .shared-browser-address button { width: 27px; height: 27px; flex-shrink: 0; padding: 5px; border-radius: 50%; }
|
|
137
|
+
.shared-browser-panel .shared-browser-address input { height: 28px; box-sizing: border-box; border-radius: 14px; padding: 4px 11px; border-color: transparent; }
|
|
138
|
+
.shared-browser-panel .shared-browser-address input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
|
|
139
|
+
.browser-settings-open .shared-browser-tabs { display: none; }
|
|
140
|
+
.browser-settings-open .browser-pane-header > .tool-pane-title { display: block; }
|
|
118
141
|
|
|
119
142
|
#shared-browser-toggle-btn[aria-expanded="true"] { color: var(--text); background: rgba(var(--overlay-rgb), .06); }
|
|
120
143
|
|