clay-server 5.4.0 → 5.5.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/agent-behavior-gate.js +223 -0
- package/lib/agent-behavior-schema.js +184 -0
- package/lib/daemon.js +15 -0
- package/lib/issues-mcp-server.js +16 -3
- package/lib/migrate-single-user.js +2 -0
- package/lib/project-issues.js +9 -2
- package/lib/project-log-feedback-delivery.js +3 -0
- package/lib/project-logs-mcp-server.js +23 -2
- package/lib/project-logs.js +40 -6
- package/lib/project-session-document.js +9 -1
- package/lib/project-session-notes.js +19 -8
- package/lib/project-session-pair-prompt.js +5 -4
- package/lib/project-user-knowledge.js +21 -3
- package/lib/project-worker-proposal.js +8 -2
- package/lib/project.js +108 -61
- package/lib/public/app.js +1 -0
- package/lib/public/css/agent-behavior.css +101 -0
- package/lib/public/modules/agent-behavior-nav.js +90 -0
- package/lib/public/modules/agent-behavior-view.js +235 -0
- package/lib/public/modules/agent-behavior.js +200 -0
- package/lib/public/modules/settings-shell-registry.js +1 -1
- package/lib/public/modules/settings-shell-view.js +18 -0
- package/lib/public/modules/sidebar-sessions.js +3 -0
- package/lib/public/modules/split-view-arrange-menu.js +38 -0
- package/lib/public/modules/split-view-create-menu.js +9 -5
- package/lib/public/modules/split-view-member-actions.js +42 -0
- package/lib/public/modules/split-view-picker-actions.js +2 -2
- package/lib/public/modules/split-view-presentation-renderer.js +10 -35
- package/lib/public/modules/split-view-presentations.js +9 -8
- package/lib/public/modules/split-view-sidebar-menu.js +2 -1
- package/lib/public/modules/user-settings-layout.js +8 -1
- package/lib/public/modules/user-settings.js +5 -2
- package/lib/public/modules/view-workspace-state.js +1 -1
- package/lib/public/pane-assets/{app-BDYH6Y7R.js → app-SEMVZ4AY.js} +1 -1
- package/lib/public/pane-assets/{chunk-U5GH2UJM.js → chunk-4YE4RU27.js} +1 -1
- package/lib/public/pane-assets/{chunk-E6NRPGVH.js → chunk-5UBN6GR7.js} +1 -1
- package/lib/public/pane-assets/chunk-6AGSOAOD.js +1 -0
- package/lib/public/pane-assets/chunk-AATAIAXL.js +1 -0
- package/lib/public/pane-assets/{chunk-42C2CZEP.js → chunk-BNNPJJVG.js} +1 -1
- package/lib/public/pane-assets/{chunk-HWLBH6AL.js → chunk-C6P7PICY.js} +1 -1
- package/lib/public/pane-assets/{chunk-KB4T5B2R.js → chunk-CQNPEH2M.js} +1 -1
- package/lib/public/pane-assets/chunk-D3CE5FZL.js +1 -0
- package/lib/public/pane-assets/{chunk-N5SIEIOG.js → chunk-E4VM2IDB.js} +1 -1
- package/lib/public/pane-assets/{chunk-VCNLKOVT.js → chunk-FK3YLGAP.js} +1 -1
- package/lib/public/pane-assets/chunk-GKSLBGVE.js +168 -0
- package/lib/public/pane-assets/chunk-J5PTSKJ6.js +1 -0
- package/lib/public/pane-assets/{chunk-XI2DJM4H.js → chunk-K4PAN77B.js} +1 -1
- package/lib/public/pane-assets/{chunk-UMIYQYGM.js → chunk-M5CWIIIT.js} +1 -1
- package/lib/public/pane-assets/{chunk-KF3LZRJK.js → chunk-QBCM7QEA.js} +1 -1
- package/lib/public/pane-assets/chunk-QPBO6ID4.js +1 -0
- package/lib/public/pane-assets/{chunk-TMUCEZ7C.js → chunk-QWQB2YRF.js} +1 -1
- package/lib/public/pane-assets/chunk-RA6VUJTY.js +1 -0
- package/lib/public/pane-assets/{chunk-TT3NECKK.js → chunk-U7EHYRJZ.js} +1 -1
- package/lib/public/pane-assets/chunk-XOZUM5ET.js +1 -0
- package/lib/public/pane-assets/{chunk-X452YCE5.js → chunk-XYZGVYWW.js} +1 -1
- package/lib/public/pane-assets/{chunk-QRVKAVH7.js → chunk-YR5GH3GI.js} +1 -1
- package/lib/public/pane-assets/{chunk-YE45RR7W.js → chunk-YVVT3CTV.js} +1 -1
- package/lib/public/pane-assets/manifest.json +394 -388
- package/lib/public/pane-assets/{style-VDIGFFLR.css → style-DZX6JAMF.css} +1 -1
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +3 -2
- package/lib/server-agent-behavior-settings.js +72 -0
- package/lib/server-settings.js +2 -0
- package/lib/server.js +9 -0
- package/lib/session-notes-mcp-server.js +27 -7
- package/lib/session-skill-scope.js +2 -2
- package/lib/users-agent-behavior-preferences.js +38 -0
- package/lib/users.js +6 -0
- package/lib/yoke/adapters/claude.js +8 -4
- package/lib/yoke/skill-discovery.js +46 -1
- package/package.json +1 -1
- package/lib/public/pane-assets/chunk-46OOGVHU.js +0 -1
- package/lib/public/pane-assets/chunk-4OJW4FF4.js +0 -1
- package/lib/public/pane-assets/chunk-F7VIE6FJ.js +0 -1
- package/lib/public/pane-assets/chunk-FCJYJLVZ.js +0 -1
- package/lib/public/pane-assets/chunk-FEJDEXOW.js +0 -1
- package/lib/public/pane-assets/chunk-FQQMXMH7.js +0 -168
- package/lib/public/pane-assets/chunk-HEMCIILG.js +0 -1
- package/lib/public/pane-assets/chunk-RV5O66TO.js +0 -1
|
@@ -63,7 +63,24 @@ export function renderPageNav(pages, activePage, onSelect) {
|
|
|
63
63
|
markActivePage(activePage);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// A page may hang a list of in-page links under its own nav item (desktop).
|
|
67
|
+
var subnav = { page: null, node: null };
|
|
68
|
+
var lastActivePage = null;
|
|
69
|
+
export function setPageSubnav(page, node) {
|
|
70
|
+
if (subnav.node && subnav.node.parentNode && subnav.node !== node) subnav.node.parentNode.removeChild(subnav.node);
|
|
71
|
+
subnav = { page: node ? page : null, node: node || null };
|
|
72
|
+
placeSubnav();
|
|
73
|
+
}
|
|
74
|
+
function placeSubnav() {
|
|
75
|
+
var nav = el('settings-page-nav');
|
|
76
|
+
if (!subnav.node) return;
|
|
77
|
+
var active = Array.from(nav.querySelectorAll('button.settings-nav-item')).filter(function (button) { return button.dataset.section === lastActivePage; })[0];
|
|
78
|
+
if (subnav.page === lastActivePage && active) active.after(subnav.node);
|
|
79
|
+
else if (subnav.node.parentNode === nav) nav.removeChild(subnav.node);
|
|
80
|
+
}
|
|
81
|
+
|
|
66
82
|
export function markActivePage(activePage) {
|
|
83
|
+
lastActivePage = activePage;
|
|
67
84
|
Array.from(el('settings-page-nav').querySelectorAll('button')).forEach(function (button) {
|
|
68
85
|
var active = button.dataset.section === activePage;
|
|
69
86
|
button.classList.toggle('active', active);
|
|
@@ -71,6 +88,7 @@ export function markActivePage(activePage) {
|
|
|
71
88
|
});
|
|
72
89
|
var select = el('settings-page-select');
|
|
73
90
|
if (activePage && select.value !== activePage) select.value = activePage;
|
|
91
|
+
placeSubnav();
|
|
74
92
|
}
|
|
75
93
|
|
|
76
94
|
// The selector is a plain select; settings-select upgrades it to the shared
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appendRemoveFromView } from './split-view-member-actions.js';
|
|
1
2
|
import { appendGithubWork } from './session-github.js';
|
|
2
3
|
// sidebar-sessions.js - Session list, search, presence, countdown, CLI picker
|
|
3
4
|
// Extracted from sidebar.js (PR-35)
|
|
@@ -290,6 +291,8 @@ function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData, po
|
|
|
290
291
|
menu.appendChild(renameItem);
|
|
291
292
|
|
|
292
293
|
|
|
294
|
+
appendRemoveFromView(menu, sessionData, currentAnchor);
|
|
295
|
+
|
|
293
296
|
// Session visibility toggle (only the session owner can change)
|
|
294
297
|
if (!isMateWorkspace(store.snap()) && store.get('isMultiUserMode') && sessionData && !sessionData.isMate && sessionData.ownerId && sessionData.ownerId === store.get('myUserId')) {
|
|
295
298
|
var currentVis = (sessionData && sessionData.sessionVisibility) || "private";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { iconHtml } from './icons.js';
|
|
2
|
+
import { appendLayoutOption } from './split-view-layout-icons.js';
|
|
3
|
+
import { SPLIT_SIDES } from './split-view-picker-render.js';
|
|
4
|
+
|
|
5
|
+
export function ensureArrangeMenu(closePresentationArrange) {
|
|
6
|
+
var menu = document.getElementById("split-view-arrange-menu");
|
|
7
|
+
if (menu) return menu;
|
|
8
|
+
menu = document.createElement("div"); menu.id = "split-view-arrange-menu"; menu.className = "split-view-arrange";
|
|
9
|
+
menu.setAttribute("role", "dialog"); menu.setAttribute("aria-label", "Arrange Split view"); menu.hidden = true;
|
|
10
|
+
var header = document.createElement("div"); header.className = "split-view-arrange-header";
|
|
11
|
+
var title = document.createElement("strong"); title.textContent = "Arrange Split view"; header.appendChild(title);
|
|
12
|
+
var close = document.createElement("button"); close.type = "button"; close.setAttribute("aria-label", "Close arrangement menu"); close.innerHTML = iconHtml("x"); header.appendChild(close);
|
|
13
|
+
close.addEventListener("click", function () { closePresentationArrange(); }); menu.appendChild(header);
|
|
14
|
+
var presets = [["balanced", "Balanced"], ["columns", "Columns"], ["rows", "Rows"], ["grid", "Grid"], ["main-left", "Main left"], ["main-right", "Main right"]];
|
|
15
|
+
var presetGrid = document.createElement("div"); presetGrid.className = "split-view-arrange-grid";
|
|
16
|
+
presets.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.preset = item[0]; appendLayoutOption(button, item[1], "preset", item[0]); presetGrid.appendChild(button); });
|
|
17
|
+
menu.appendChild(presetGrid);
|
|
18
|
+
var positions = [["left", "Left half"], ["right", "Right half"], ["top", "Top half"], ["bottom", "Bottom half"], ["top-left", "Top left"], ["top-right", "Top right"], ["bottom-left", "Bottom left"], ["bottom-right", "Bottom right"], ["left-third", "Left third"], ["right-third", "Right third"]];
|
|
19
|
+
var positionGrid = document.createElement("div"); positionGrid.className = "split-view-arrange-grid split-view-arrange-positions";
|
|
20
|
+
positions.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.position = item[0]; appendLayoutOption(button, item[1], "position", item[0]); positionGrid.appendChild(button); });
|
|
21
|
+
menu.appendChild(positionGrid);
|
|
22
|
+
var additions = document.createElement('div'); additions.className = 'split-session-directions';
|
|
23
|
+
SPLIT_SIDES.forEach(function (side) {
|
|
24
|
+
var button = document.createElement('button'); button.type = 'button'; button.dataset.arrangeAddSide = side[0];
|
|
25
|
+
button.innerHTML = iconHtml(side[2]); var label = document.createElement('span'); label.textContent = 'Add ' + side[1] + ' Split'; button.appendChild(label); additions.appendChild(button);
|
|
26
|
+
});
|
|
27
|
+
menu.appendChild(additions);
|
|
28
|
+
var focusButton = document.createElement("button"); focusButton.type = "button"; focusButton.dataset.focusLayout = "true"; menu.appendChild(focusButton);
|
|
29
|
+
var membership = document.createElement("button"); membership.type = "button"; membership.dataset.arrangeMembership = "true";
|
|
30
|
+
membership.textContent = "Choose sessions"; menu.appendChild(membership);
|
|
31
|
+
var context = document.createElement("div"); context.className = "split-view-arrange-context";
|
|
32
|
+
var contextButton = document.createElement("button"); contextButton.type = "button"; contextButton.dataset.arrangeContext = "true";
|
|
33
|
+
contextButton.setAttribute("aria-expanded", "false"); contextButton.setAttribute("aria-controls", "split-view-arrange-context-list");
|
|
34
|
+
var contextTitle = document.createElement("span"); contextTitle.dataset.arrangeContextTitle = "true"; contextButton.appendChild(contextTitle); context.appendChild(contextButton);
|
|
35
|
+
var contextList = document.createElement("div"); contextList.id = "split-view-arrange-context-list"; contextList.hidden = true; contextList.setAttribute("role", "listbox");
|
|
36
|
+
context.appendChild(contextList); menu.insertBefore(context, presetGrid);
|
|
37
|
+
document.body.appendChild(menu); return menu;
|
|
38
|
+
}
|
|
@@ -29,8 +29,8 @@ function position() {
|
|
|
29
29
|
menu.style.left = Math.max(8, Math.min(rect.right - width, innerWidth - width - 8)) + 'px';
|
|
30
30
|
menu.style.top = Math.max(8, Math.min(rect.bottom + 5, innerHeight - menu.offsetHeight - 8)) + 'px';
|
|
31
31
|
}
|
|
32
|
-
function open(anchor) {
|
|
33
|
-
var receipt = pickerReceipt(anchor);
|
|
32
|
+
function open(anchor, side, origin) {
|
|
33
|
+
var receipt = pickerReceipt(anchor, origin);
|
|
34
34
|
if (!receipt || !pickerCurrent(receipt)) { showToast('Wait for this View to finish loading, then try again.', 'warn'); return; }
|
|
35
35
|
var menu = menuElement();
|
|
36
36
|
if (menu && !menu.hidden && menu.clayCreateAnchor === anchor) { close(true); return; }
|
|
@@ -41,8 +41,12 @@ function open(anchor) {
|
|
|
41
41
|
}
|
|
42
42
|
receipt.reservationId = 'split-picker-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);
|
|
43
43
|
menu.clayCreateAnchor = anchor; anchor.setAttribute('aria-expanded', 'true');
|
|
44
|
-
store.set({ splitSessionPicker: { receipt: receipt, side: null, query: '', error: '', priorFocus: store.get('projectSplitViewFocusedOrigin') } });
|
|
45
|
-
|
|
44
|
+
store.set({ splitSessionPicker: { receipt: receipt, side: side || null, query: '', error: '', priorFocus: store.get('projectSplitViewFocusedOrigin') } });
|
|
45
|
+
if (side) store.set({ projectSplitViewFocusedOrigin: null });
|
|
46
|
+
renderPicker(menu); menu.hidden = false;
|
|
47
|
+
if (side) document.dispatchEvent(new CustomEvent('clay-split-picker-layout'));
|
|
48
|
+
position(); menu.querySelector(side ? 'input' : '[data-split-side]').focus();
|
|
49
|
+
if (side) retryOptions();
|
|
46
50
|
}
|
|
47
51
|
function onClick(event) {
|
|
48
52
|
var menu = menuElement(), state = store.get('splitSessionPicker'); if (!menu || menu.hidden || !state) return;
|
|
@@ -76,7 +80,7 @@ function onKey(event) {
|
|
|
76
80
|
items[next].focus();
|
|
77
81
|
}
|
|
78
82
|
export function initSplitViewCreateMenu() {
|
|
79
|
-
document.addEventListener('clay-split-view-open-create', function (event) { if (event.detail && event.detail.anchor) open(event.detail.anchor); });
|
|
83
|
+
document.addEventListener('clay-split-view-open-create', function (event) { if (event.detail && event.detail.anchor) open(event.detail.anchor, event.detail.side, event.detail.origin); });
|
|
80
84
|
document.addEventListener('click', onClick, true); document.addEventListener('keydown', onKey, true);
|
|
81
85
|
document.addEventListener('input', function (event) {
|
|
82
86
|
var state = store.get('splitSessionPicker'); if (!state || !event.target.matches('#split-view-create-menu input')) return;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { mutation } from './view-mutations.js';
|
|
4
|
+
import { remove, validateTree } from './split-view-layout-client.js';
|
|
5
|
+
import { showToast } from './utils.js';
|
|
6
|
+
import { iconHtml } from './icons.js';
|
|
7
|
+
import { closeSessionContextMenu } from './session-context-menu.js';
|
|
8
|
+
|
|
9
|
+
export function removeViewSession(view, origin) {
|
|
10
|
+
var live = (store.get('projectSplitViews') || []).find(function (item) { return item.id === view.id && item.revision === view.revision; });
|
|
11
|
+
if (!live || !live.sessions.some(function (session) { return session.sessionOriginId === origin; })) return false;
|
|
12
|
+
var active = live.id === store.get('projectSplitViewActiveId');
|
|
13
|
+
if (active) {
|
|
14
|
+
try {
|
|
15
|
+
document.querySelectorAll('#split-host .split-pane-frame').forEach(function (frame) { if (frame.clayPane) frame.clayPane.checkDraft(); });
|
|
16
|
+
} catch (error) { showToast(error.message, 'warn'); return false; }
|
|
17
|
+
}
|
|
18
|
+
var origins = live.sessions.map(function (session) { return session.sessionOriginId; }).filter(function (item) { return item !== origin; });
|
|
19
|
+
var tree = remove(live.tree, origin);
|
|
20
|
+
if (!validateTree(tree, origins).ok) return false;
|
|
21
|
+
var nextActive = live.activeSessionOriginId === origin ? origins[0] || null : live.activeSessionOriginId;
|
|
22
|
+
return mutation('project_split_view_update', { id: live.id, sessionOrigins: origins, tree: tree,
|
|
23
|
+
layout: live.layout, activeSessionOriginId: nextActive, active: active }, active ? { closedOrigin: origin, activeOrigin: nextActive } : null);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function appendRemoveFromView(menu, session, currentAnchor) {
|
|
27
|
+
if (!session || session.sessionRole === 'worker') return;
|
|
28
|
+
var view = (store.get('projectSplitViews') || []).find(function (item) {
|
|
29
|
+
return item.sessions.length > 1 && item.sessions.some(function (ref) { return ref.sessionOriginId === session.sessionOriginId; });
|
|
30
|
+
});
|
|
31
|
+
if (!view) return;
|
|
32
|
+
var socket = getWs(), slug = store.get('currentSlug'), viewer = store.get('myUserId'), generation = store.get('projectSplitViewSocketGeneration');
|
|
33
|
+
var button = document.createElement('button'); button.className = 'session-ctx-item';
|
|
34
|
+
button.innerHTML = iconHtml('panel-left-close') + ' <span>Remove from View</span>';
|
|
35
|
+
button.disabled = !!store.get('projectSplitViewPending');
|
|
36
|
+
button.addEventListener('click', function (event) {
|
|
37
|
+
event.stopPropagation(); var anchor = currentAnchor(); closeSessionContextMenu(false);
|
|
38
|
+
if (!anchor || socket !== getWs() || slug !== store.get('currentSlug') || viewer !== store.get('myUserId') || generation !== store.get('projectSplitViewSocketGeneration')) return;
|
|
39
|
+
removeViewSession(view, session.sessionOriginId);
|
|
40
|
+
});
|
|
41
|
+
menu.appendChild(button);
|
|
42
|
+
}
|
|
@@ -11,9 +11,9 @@ import { checkComposerDeparture } from './session-rich-drafts.js';
|
|
|
11
11
|
import { createOptions } from './session-create.js';
|
|
12
12
|
import { showToast } from './utils.js';
|
|
13
13
|
|
|
14
|
-
export function pickerReceipt(anchor) {
|
|
14
|
+
export function pickerReceipt(anchor, targetOrigin) {
|
|
15
15
|
var view = activeSavedView(), pane = anchor && anchor.closest('.split-pane'), frame = pane && pane.querySelector('.split-pane-frame');
|
|
16
|
-
var origin = pane && pane.dataset.presentationOrigin || frame && frame.dataset.sessionOriginId || store.get('sessionOriginId');
|
|
16
|
+
var origin = targetOrigin || pane && pane.dataset.presentationOrigin || frame && frame.dataset.sessionOriginId || store.get('sessionOriginId');
|
|
17
17
|
return view && view.sessions.some(function (session) { return session.sessionOriginId === origin; }) ? { scope: layoutScope(view), origin: origin } : null;
|
|
18
18
|
}
|
|
19
19
|
export function pickerCurrent(receipt) {
|
|
@@ -16,7 +16,8 @@ import { createSplitPane, updateSplitPaneMetadata, updateSplitPaneCloseVisibilit
|
|
|
16
16
|
import { syncPaneHeaderState } from './split-pane-header-state.js';
|
|
17
17
|
import { rectangles, resize, validateTree, listIds } from './split-view-layout-client.js';
|
|
18
18
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
19
|
-
import {
|
|
19
|
+
import { ensureArrangeMenu as buildArrangeMenu } from './split-view-arrange-menu.js';
|
|
20
|
+
import { pickerCapacity } from './split-view-picker-actions.js';
|
|
20
21
|
import { reserveEmptySplit, clearEmptySplit } from './split-session-empty-pane.js';
|
|
21
22
|
|
|
22
23
|
var host = null;
|
|
@@ -51,7 +52,7 @@ function dispatch(name, detail) {
|
|
|
51
52
|
export function initPresentationRenderer(hostElement) {
|
|
52
53
|
host = hostElement;
|
|
53
54
|
app = document.getElementById("app");
|
|
54
|
-
|
|
55
|
+
buildArrangeMenu(closePresentationArrange);
|
|
55
56
|
document.addEventListener('clay-split-picker-layout', renderCurrentGeometry);
|
|
56
57
|
document.addEventListener("click", onClick, true);
|
|
57
58
|
document.addEventListener("keydown", onKeyDown, true);
|
|
@@ -60,7 +61,7 @@ export function initPresentationRenderer(hostElement) {
|
|
|
60
61
|
document.addEventListener("pointercancel", onPointerCancel, true);
|
|
61
62
|
document.addEventListener("lostpointercapture", onPointerCancel, true);
|
|
62
63
|
window.addEventListener("blur", onWindowBlur);
|
|
63
|
-
window.addEventListener("resize", function () { renderCurrentGeometry(); });
|
|
64
|
+
window.addEventListener("resize", function () { closePresentationArrange(false); renderCurrentGeometry(); });
|
|
64
65
|
window.addEventListener("scroll", closePresentationArrange, true);
|
|
65
66
|
if (typeof ResizeObserver === "function") {
|
|
66
67
|
resizeObserver = new ResizeObserver(function () { renderCurrentGeometry(); });
|
|
@@ -136,7 +137,7 @@ export function renderPresentationView(view, tree, sessions) {
|
|
|
136
137
|
if (app) app.classList.add("split-native-hidden");
|
|
137
138
|
reconcileSplitPanes(host, panes, function (pane) { return createSplitPane(pane, function (sessionId) { dispatch("clay-split-view-close-pane", { sessionId: sessionId }); }); }, updateSplitPaneMetadata, paneRetention(host));
|
|
138
139
|
Array.from(host.querySelectorAll(':scope > .split-pane .split-pane-header')).forEach(function (header) { ensureArrangeTrigger(header); });
|
|
139
|
-
|
|
140
|
+
buildArrangeMenu(closePresentationArrange);
|
|
140
141
|
Array.from(host.querySelectorAll(':scope > .split-pane')).forEach(function (node) {
|
|
141
142
|
var target = panes.find(function (pane) { return node.dataset.splitPaneKey === pane.slug + '#' + pane.sessionId; });
|
|
142
143
|
var session = target && sessions.find(function (item) { return item.id === target.sessionId; });
|
|
@@ -278,34 +279,6 @@ export function ensureArrangeTrigger(header) {
|
|
|
278
279
|
if (pane) syncPaneHeaderState(pane);
|
|
279
280
|
}
|
|
280
281
|
|
|
281
|
-
function ensureArrangeMenu() {
|
|
282
|
-
var menu = document.getElementById("split-view-arrange-menu");
|
|
283
|
-
if (menu) return menu;
|
|
284
|
-
menu = document.createElement("div"); menu.id = "split-view-arrange-menu"; menu.className = "split-view-arrange";
|
|
285
|
-
menu.setAttribute("role", "dialog"); menu.setAttribute("aria-label", "Arrange Split view"); menu.hidden = true;
|
|
286
|
-
var header = document.createElement("div"); header.className = "split-view-arrange-header";
|
|
287
|
-
var title = document.createElement("strong"); title.textContent = "Arrange Split view"; header.appendChild(title);
|
|
288
|
-
var close = document.createElement("button"); close.type = "button"; close.setAttribute("aria-label", "Close arrangement menu"); close.innerHTML = iconHtml("x"); header.appendChild(close);
|
|
289
|
-
close.addEventListener("click", function () { closePresentationArrange(); }); menu.appendChild(header);
|
|
290
|
-
var presets = [["balanced", "Balanced"], ["columns", "Columns"], ["rows", "Rows"], ["grid", "Grid"], ["main-left", "Main left"], ["main-right", "Main right"]];
|
|
291
|
-
var presetGrid = document.createElement("div"); presetGrid.className = "split-view-arrange-grid";
|
|
292
|
-
presets.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.preset = item[0]; appendLayoutOption(button, item[1], "preset", item[0]); presetGrid.appendChild(button); });
|
|
293
|
-
menu.appendChild(presetGrid);
|
|
294
|
-
var positions = [["left", "Left half"], ["right", "Right half"], ["top", "Top half"], ["bottom", "Bottom half"], ["top-left", "Top left"], ["top-right", "Top right"], ["bottom-left", "Bottom left"], ["bottom-right", "Bottom right"], ["left-third", "Left third"], ["right-third", "Right third"]];
|
|
295
|
-
var positionGrid = document.createElement("div"); positionGrid.className = "split-view-arrange-grid split-view-arrange-positions";
|
|
296
|
-
positions.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.position = item[0]; appendLayoutOption(button, item[1], "position", item[0]); positionGrid.appendChild(button); });
|
|
297
|
-
menu.appendChild(positionGrid);
|
|
298
|
-
var focusButton = document.createElement("button"); focusButton.type = "button"; focusButton.dataset.focusLayout = "true"; menu.appendChild(focusButton);
|
|
299
|
-
var membership = document.createElement("button"); membership.type = "button"; membership.dataset.arrangeMembership = "true";
|
|
300
|
-
membership.textContent = "Choose sessions"; menu.appendChild(membership);
|
|
301
|
-
var context = document.createElement("div"); context.className = "split-view-arrange-context";
|
|
302
|
-
var contextButton = document.createElement("button"); contextButton.type = "button"; contextButton.dataset.arrangeContext = "true";
|
|
303
|
-
contextButton.setAttribute("aria-expanded", "false"); contextButton.setAttribute("aria-controls", "split-view-arrange-context-list");
|
|
304
|
-
var contextTitle = document.createElement("span"); contextTitle.dataset.arrangeContextTitle = "true"; contextButton.appendChild(contextTitle); context.appendChild(contextButton);
|
|
305
|
-
var contextList = document.createElement("div"); contextList.id = "split-view-arrange-context-list"; contextList.hidden = true; contextList.setAttribute("role", "listbox");
|
|
306
|
-
context.appendChild(contextList); menu.insertBefore(context, presetGrid);
|
|
307
|
-
document.body.appendChild(menu); return menu;
|
|
308
|
-
}
|
|
309
282
|
|
|
310
283
|
function openArrangeMenu(trigger) {
|
|
311
284
|
if (!syncPaneHeaderState(trigger.closest('.split-pane'))) return;
|
|
@@ -313,7 +286,7 @@ function openArrangeMenu(trigger) {
|
|
|
313
286
|
dispatch("clay-split-view-open-create", { anchor: trigger });
|
|
314
287
|
return;
|
|
315
288
|
}
|
|
316
|
-
var menu =
|
|
289
|
+
var menu = buildArrangeMenu(closePresentationArrange);
|
|
317
290
|
if (!menu.hidden && menuTrigger === trigger) { closePresentationArrange(); return; }
|
|
318
291
|
closePresentationArrange(false); menuTrigger = trigger; menu.hidden = false;
|
|
319
292
|
trigger.setAttribute("aria-expanded", "true");
|
|
@@ -340,6 +313,7 @@ function openArrangeMenu(trigger) {
|
|
|
340
313
|
Array.from(menu.querySelectorAll('[data-position*="-"]:not([data-position$="-third"])')).forEach(function (button) {
|
|
341
314
|
button.disabled = count < 3; button.title = count < 3 ? "Quarter layouts require at least three sessions" : "";
|
|
342
315
|
});
|
|
316
|
+
menu.querySelectorAll('[data-arrange-add-side]').forEach(function (button) { button.disabled = !pickerCapacity() || !!store.get('projectSplitViewPending'); });
|
|
343
317
|
var rect = trigger.getBoundingClientRect(), width = Math.min(280, window.innerWidth - 16);
|
|
344
318
|
menu.style.left = Math.max(8, Math.min(rect.left, window.innerWidth - width - 8)) + "px";
|
|
345
319
|
menu.style.top = Math.max(8, Math.min(rect.bottom + 4, window.innerHeight - menu.offsetHeight - 8)) + "px";
|
|
@@ -362,10 +336,11 @@ function onClick(event) {
|
|
|
362
336
|
if (trigger) { event.preventDefault(); event.stopImmediatePropagation(); openArrangeMenu(trigger); return; }
|
|
363
337
|
var menu = event.target.closest('#split-view-arrange-menu');
|
|
364
338
|
if (menu) {
|
|
365
|
-
|
|
339
|
+
var addSide = event.target.closest('[data-arrange-add-side]');
|
|
340
|
+
if (addSide || event.target.closest('[data-arrange-membership]')) {
|
|
366
341
|
event.preventDefault(); event.stopImmediatePropagation();
|
|
367
342
|
var anchor = menuTrigger; closePresentationArrange(false);
|
|
368
|
-
if (anchor && anchor.isConnected) dispatch("clay-split-view-open-create", { anchor: anchor });
|
|
343
|
+
if (anchor && anchor.isConnected) dispatch("clay-split-view-open-create", { anchor: anchor, side: addSide && addSide.dataset.arrangeAddSide, origin: menu.dataset.sourceOrigin });
|
|
369
344
|
return;
|
|
370
345
|
}
|
|
371
346
|
var contextButton = event.target.closest('[data-arrange-context]');
|
|
@@ -19,10 +19,11 @@ import { initPresentationDrag, restorePresentationDrag } from './split-view-pres
|
|
|
19
19
|
import { detachTuiView } from './session-tui-view.js';
|
|
20
20
|
import { checkComposerDeparture } from './session-rich-drafts.js';
|
|
21
21
|
import { showToast } from './utils.js';
|
|
22
|
-
import { preset, listIds, validateTree,
|
|
22
|
+
import { preset, listIds, validateTree, arrangeRegion } from './split-view-layout-client.js';
|
|
23
23
|
import { viewIdForOrigin, updateActiveSession, exactDriverForOrigin, restorablePresentation } from './split-view-presentation-lookup.js';
|
|
24
24
|
import { groupedSessionIds } from './split-group-helpers.js';
|
|
25
25
|
import { handleSplitViewCreateEvent } from './split-view-create-action.js';
|
|
26
|
+
import { removeViewSession } from './split-view-member-actions.js';
|
|
26
27
|
import { initSplitViewCreateMenu } from './split-view-create-menu.js';
|
|
27
28
|
import { currentSingletonPresentation } from './split-view-singleton.js';
|
|
28
29
|
var host = null;
|
|
@@ -333,6 +334,12 @@ function toggleGroupMenu(group, anchor, point) {
|
|
|
333
334
|
}
|
|
334
335
|
function runGroupAction(view, action, root) {
|
|
335
336
|
closeGroupMenus(false);
|
|
337
|
+
if (action === 'add-pane') {
|
|
338
|
+
if (!setActiveView(view.id)) return;
|
|
339
|
+
var anchor = host && host.querySelector('.split-pane:not(.split-pane-cached) [data-action="arrange"]');
|
|
340
|
+
if (anchor) document.dispatchEvent(new CustomEvent('clay-split-view-open-create', { detail: { anchor: anchor, side: 'right', origin: view.activeSessionOriginId || view.sessions[0].sessionOriginId } }));
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
336
343
|
if (action === 'favorite' || action === 'folder') { organizeViewAction(view, action); return; }
|
|
337
344
|
if (action === "rename") { startRename(root, view); return; }
|
|
338
345
|
if (action === "separate") {
|
|
@@ -418,13 +425,7 @@ function onClosePane(event) {
|
|
|
418
425
|
if (!view || !event.detail || !validPresentationClose(event.detail)) return;
|
|
419
426
|
var session = (getCachedSessions() || []).find(function (item) { return item.id === Number(event.detail.sessionId); });
|
|
420
427
|
if (!session || !view.sessions.some(function (item) { return item.sessionOriginId === session.sessionOriginId; })) return;
|
|
421
|
-
|
|
422
|
-
var origins = view.sessions.map(function (item) { return item.sessionOriginId; }).filter(function (origin) { return origin !== session.sessionOriginId; });
|
|
423
|
-
var nextTree = remove(view.tree, session.sessionOriginId);
|
|
424
|
-
if (!validateTree(nextTree, origins).ok) return;
|
|
425
|
-
var nextActive = view.activeSessionOriginId === session.sessionOriginId ? origins[0] || null : view.activeSessionOriginId;
|
|
426
|
-
mutation("project_split_view_update", { id: view.id, sessionOrigins: origins, tree: nextTree,
|
|
427
|
-
layout: view.layout, activeSessionOriginId: nextActive }, { closedOrigin: session.sessionOriginId, activeOrigin: nextActive });
|
|
428
|
+
removeViewSession(view, session.sessionOriginId);
|
|
428
429
|
}
|
|
429
430
|
function onFocus(event) {
|
|
430
431
|
var view = viewExists(store.get('projectSplitViews'), store.get('projectSplitViewActiveId'));
|
|
@@ -25,8 +25,9 @@ export function openSplitViewMenu(group, anchor, point, onAction) {
|
|
|
25
25
|
}
|
|
26
26
|
closeFolderMenu(false); closeSessionContextMenu(false);
|
|
27
27
|
var menu = document.createElement('div'); menu.className = 'session-ctx-menu split-view-menu';
|
|
28
|
-
[['favorite', view.favorite ? 'Remove from Favorites' : 'Add to Favorites', 'star'], ['folder', 'Move to folder', 'folder-input'], ['rename', 'Rename', 'pencil'], ['separate', 'Separate', 'columns-2']].forEach(function (entry) {
|
|
28
|
+
[['add-pane', 'Add pane', 'panel-right-open'], ['favorite', view.favorite ? 'Remove from Favorites' : 'Add to Favorites', 'star'], ['folder', 'Move to folder', 'folder-input'], ['rename', 'Rename', 'pencil'], ['separate', 'Separate', 'columns-2']].forEach(function (entry) {
|
|
29
29
|
var button = document.createElement('button'); button.className = 'session-ctx-item'; button.dataset.splitViewAction = entry[0];
|
|
30
|
+
if (entry[0] === 'add-pane') button.disabled = !view.sessions.length || view.sessions.length + (view.workspace && view.workspace.linkedPair ? view.workspace.linkedPair.workerOrigins.length : 0) >= 8 || !!store.get('projectSplitViewPending');
|
|
30
31
|
button.innerHTML = iconHtml(entry[2]) + ' <span>' + entry[1] + '</span>';
|
|
31
32
|
button.addEventListener('click', function (event) {
|
|
32
33
|
event.stopPropagation(); var root = currentRoot(); closeSessionContextMenu(false);
|
|
@@ -4,6 +4,8 @@ import { store } from './store.js';
|
|
|
4
4
|
import { closeSpeechPicker } from './stt-picker.js';
|
|
5
5
|
import { loadSpeechSettings } from './stt-settings.js';
|
|
6
6
|
import { mountDefaultAi, unmountDefaultAi, requestDefaultAi } from './default-ai.js';
|
|
7
|
+
import { mountAgentBehavior, unmountAgentBehavior } from './agent-behavior-view.js';
|
|
8
|
+
import { resetAgentBehavior } from './agent-behavior.js';
|
|
7
9
|
import { resolvePage } from './settings-shell-registry.js';
|
|
8
10
|
|
|
9
11
|
export function resolveUserSettingsPage(id) { return resolvePage('personal', id); }
|
|
@@ -29,6 +31,9 @@ export function buildUserSettingsLayout() {
|
|
|
29
31
|
document.getElementById('us-auto-continue').closest('label').querySelector('.settings-hint').textContent = 'When a rate-limited conversation becomes eligible to retry, Clay can send a continuation automatically. This may resume work and consume usage without another message from you.';
|
|
30
32
|
heading(ai, 'Command permissions', 'These are personal Claude CLI allow rules, separate from model selection and Clay session approval modes. Managed defaults are shown for reference. Server access restrictions still apply. On an OS-user server these install in your OS home; otherwise they install in the shared server process home and can affect other Claude CLI launches there.');
|
|
31
33
|
ai.appendChild(document.getElementById('us-claude-allow-list').closest('.settings-card'));
|
|
34
|
+
var agent = document.createElement('div'); agent.className = 'us-section'; agent.dataset.section = 'us-agent';
|
|
35
|
+
agent.innerHTML = '<h2>Agent behavior</h2><p class="settings-hint">Choose how your Project Drivers use Clay memory, context, tools and connections. These choices apply to your own Driver conversations.</p><div id="us-agent-behavior"></div>';
|
|
36
|
+
root.querySelector('.us-modal-content').appendChild(agent);
|
|
32
37
|
section('us-mates').remove();
|
|
33
38
|
var experiments = document.createElement('details'); experiments.className = 'us-experiments'; experiments.innerHTML = '<summary>Experimental features</summary><p class="settings-hint">Personal opt-ins for features that may change.</p>';
|
|
34
39
|
experiments.appendChild(document.getElementById('us-capsules-enabled').closest('.settings-card'));chat.appendChild(experiments);section('us-experimental').remove();
|
|
@@ -43,9 +48,11 @@ export function buildUserSettingsLayout() {
|
|
|
43
48
|
export function loadUserSettingsExtras(page) {
|
|
44
49
|
if (page === 'us-integrations') loadPersonalIntegrations();
|
|
45
50
|
if (page === 'us-chat') loadSpeechSettings();
|
|
51
|
+
if (page === 'us-agent') mountAgentBehavior(document.getElementById('us-agent-behavior'));
|
|
46
52
|
if (page === 'us-ai') { mountDefaultAi(document.getElementById('us-default-ai')); requestDefaultAi(); var ws=getWs();if(ws && ws.readyState===1)ws.send(JSON.stringify({type:'get_claude_allow_list'})); }
|
|
47
53
|
}
|
|
48
|
-
export function
|
|
54
|
+
export function leaveAgentBehaviorPage() {unmountAgentBehavior();resetAgentBehavior();}
|
|
55
|
+
export function finishUserSettingsExtras() {leaveAgentBehaviorPage();closeSpeechPicker();unmountDefaultAi(document.getElementById('us-default-ai'));}
|
|
49
56
|
// Returns the draft prompt for leaving one personal page (or every page when
|
|
50
57
|
// pageId is omitted); the shell shows it with the shared confirmation.
|
|
51
58
|
export function personalLeavePrompt(pageId) {
|
|
@@ -6,7 +6,7 @@ import { initPersonalProfile } from './user-settings-profile.js';
|
|
|
6
6
|
import { initPersonalEditors } from './user-settings-editors.js';
|
|
7
7
|
import { initPersonalPreferences } from './user-settings-preferences.js';
|
|
8
8
|
import { showPinForm, hidePinForm, populateAccount, savePin, renderEmailSettings } from './user-settings-account.js';
|
|
9
|
-
import { buildUserSettingsLayout, resolveUserSettingsPage, loadUserSettingsExtras, finishUserSettingsExtras, personalLeavePrompt } from './user-settings-layout.js';
|
|
9
|
+
import { buildUserSettingsLayout, resolveUserSettingsPage, loadUserSettingsExtras, finishUserSettingsExtras, leaveAgentBehaviorPage, personalLeavePrompt } from './user-settings-layout.js';
|
|
10
10
|
import { enhanceSettingsSelects } from './settings-select.js';
|
|
11
11
|
import { loadNotificationSettings } from './notification-settings.js';
|
|
12
12
|
// user-settings.js — Personal settings pane of the unified settings shell
|
|
@@ -37,7 +37,7 @@ export function initUserSettings() {
|
|
|
37
37
|
pane: settingsEl,
|
|
38
38
|
enter: function () { populateAccount(); },
|
|
39
39
|
activate: function (page) { switchSection(page); },
|
|
40
|
-
leave: function () { resetAccountSessions(); finishUserSettingsExtras(); },
|
|
40
|
+
leave: function () { currentPage = null; resetAccountSessions(); finishUserSettingsExtras(); },
|
|
41
41
|
busy: function () { return store.get('speechPopup') ? 'Finish or close the open voice settings editor first.' : ''; },
|
|
42
42
|
leavePrompt: function (kind, pageId) { return personalLeavePrompt(kind === 'page' ? pageId : undefined); }
|
|
43
43
|
});
|
|
@@ -159,8 +159,11 @@ export function isUserSettingsOpen() {
|
|
|
159
159
|
return isSettingsShellOpen() && activeSettingsScope() === 'personal';
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
var currentPage = null;
|
|
162
163
|
function switchSection(sectionName) {
|
|
163
164
|
sectionName = resolveUserSettingsPage(sectionName);
|
|
165
|
+
if (currentPage === 'us-agent' && sectionName !== 'us-agent') leaveAgentBehaviorPage();
|
|
166
|
+
currentPage = sectionName;
|
|
164
167
|
loadUserSettingsExtras(sectionName);
|
|
165
168
|
for (var j = 0; j < sections.length; j++) {
|
|
166
169
|
sections[j].classList.toggle('active', sections[j].dataset.section === sectionName);
|
|
@@ -81,7 +81,7 @@ export function prepareViewMutation(type, message) {
|
|
|
81
81
|
if (type === 'project_split_view_create' || type === 'project_split_view_update') {
|
|
82
82
|
if (!message.workspace) message.workspace = recomposeWorkspace(view && view.workspace, message.tree, message.activeSessionOriginId);
|
|
83
83
|
message.tree = sessionTree(message.workspace);
|
|
84
|
-
message.active =
|
|
84
|
+
message.active = message.active !== false;
|
|
85
85
|
}
|
|
86
86
|
if (type === 'project_split_view_move') {
|
|
87
87
|
var target = views.find(function (item) { return item.id === message.targetViewId; });
|