clay-server 4.0.0-beta.2 → 4.0.0-beta.21
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/background-task-timing.js +105 -0
- package/lib/capsule-display-floor.js +51 -0
- package/lib/capsule-frame-server.js +255 -0
- package/lib/capsule-mate-turn.js +105 -0
- package/lib/capsule-pig-logic.js +300 -0
- package/lib/capsule-server-runtimes.js +39 -0
- package/lib/capsules/pig/display.js +198 -0
- package/lib/capsules/pig/manifest.json +9 -0
- package/lib/capsules/pig/ui.json +84 -0
- package/lib/config.js +25 -1
- package/lib/daemon-projects.js +13 -8
- package/lib/daemon.js +127 -14
- package/lib/git-cli.js +95 -1
- package/lib/knowledge-import.js +436 -0
- package/lib/knowledge-record-store.js +188 -0
- package/lib/knowledge-search.js +240 -0
- package/lib/mate-knowledge-mcp-server.js +151 -0
- package/lib/mate-knowledge-migration.js +283 -0
- package/lib/mate-knowledge-service.js +488 -0
- package/lib/mate-knowledge-sync.js +482 -0
- package/lib/migrate-single-user.js +2 -0
- package/lib/notes-lifecycle.js +149 -0
- package/lib/notes.js +60 -3
- package/lib/os-users.js +3 -1
- package/lib/project-capsule-catalog.js +9 -1
- package/lib/project-capsule-turn.js +96 -0
- package/lib/project-connection.js +14 -1
- package/lib/project-http.js +15 -1
- package/lib/project-knowledge.js +22 -0
- package/lib/project-log-context.js +172 -0
- package/lib/project-log-feedback-delivery.js +118 -0
- package/lib/project-logs-comments.js +137 -0
- package/lib/project-logs-context-state.js +42 -0
- package/lib/project-logs-mcp-server.js +328 -0
- package/lib/project-logs-query.js +185 -0
- package/lib/project-logs-root.js +75 -0
- package/lib/project-logs-schema.js +259 -0
- package/lib/project-logs-service.js +463 -0
- package/lib/project-logs-snapshot.js +205 -0
- package/lib/project-logs-store.js +495 -0
- package/lib/project-logs-versioning.js +132 -0
- package/lib/project-logs.js +400 -0
- package/lib/project-mate-interaction.js +14 -0
- package/lib/project-mate-knowledge.js +110 -0
- package/lib/project-memory.js +22 -0
- package/lib/project-message-delivery.js +70 -0
- package/lib/project-pair-lifecycle.js +460 -0
- package/lib/project-session-handoff.js +1 -1
- package/lib/project-session-notes.js +64 -13
- package/lib/project-session-pair.js +159 -146
- package/lib/project-session-spawn.js +1 -1
- package/lib/project-sessions.js +58 -13
- package/lib/project-user-message.js +53 -4
- package/lib/project-vendor-login.js +20 -3
- package/lib/project-worker-permission.js +430 -0
- package/lib/project-worker-proposal.js +208 -62
- package/lib/project.js +178 -6
- package/lib/public/app.js +58 -35
- package/lib/public/css/admin.css +2 -2
- package/lib/public/css/capsule-ui.css +38 -0
- package/lib/public/css/command-palette.css +19 -9
- package/lib/public/css/git-placard.css +154 -0
- package/lib/public/css/home-hub.css +4 -0
- package/lib/public/css/home-session-actions.css +66 -0
- package/lib/public/css/home-sidebar.css +66 -0
- package/lib/public/css/input.css +163 -28
- package/lib/public/css/mobile-nav.css +80 -0
- package/lib/public/css/notifications-center.css +67 -0
- package/lib/public/css/pane.css +52 -0
- package/lib/public/css/project-logs.css +198 -0
- package/lib/public/css/scheduler.css +1 -1
- package/lib/public/css/session-actions.css +16 -101
- package/lib/public/css/sidebar.css +103 -0
- package/lib/public/css/sticky-notes.css +237 -229
- package/lib/public/css/worker-proposal.css +34 -1
- package/lib/public/index.html +45 -11
- package/lib/public/modules/admin.js +8 -8
- package/lib/public/modules/app-connection.js +109 -4
- package/lib/public/modules/app-home-hub.js +19 -7
- package/lib/public/modules/app-message-cards.js +2 -1
- package/lib/public/modules/app-messages.js +84 -7
- package/lib/public/modules/app-misc.js +2 -14
- package/lib/public/modules/app-notifications.js +11 -7
- package/lib/public/modules/app-panels.js +46 -4
- package/lib/public/modules/app-projects.js +13 -8
- package/lib/public/modules/app-rendering.js +1 -0
- package/lib/public/modules/background-tasks-ui.js +139 -16
- package/lib/public/modules/capsule-preference.js +44 -0
- package/lib/public/modules/chat-bubble-renderer.js +1 -1
- package/lib/public/modules/clay-log-links.js +70 -0
- package/lib/public/modules/command-palette.js +52 -15
- package/lib/public/modules/context-sources.js +0 -3
- package/lib/public/modules/git-agent-sessions.js +85 -0
- package/lib/public/modules/git-panel.js +111 -84
- package/lib/public/modules/git-placard.js +161 -0
- package/lib/public/modules/home-chat-empty-state.js +39 -0
- package/lib/public/modules/home-chat-identity.js +9 -0
- package/lib/public/modules/home-conversations-sheet.js +102 -48
- package/lib/public/modules/home-mate-chat.js +15 -35
- package/lib/public/modules/home-session-actions.js +6 -0
- package/lib/public/modules/home-sidebar-chat-list.js +101 -41
- package/lib/public/modules/home-sidebar.js +7 -0
- package/lib/public/modules/home-tool-frame.js +182 -0
- package/lib/public/modules/home-tools.js +138 -1
- package/lib/public/modules/input.js +38 -7
- package/lib/public/modules/markdown.js +2 -0
- package/lib/public/modules/mate-sidebar.js +0 -8
- package/lib/public/modules/message-delivery.js +97 -0
- package/lib/public/modules/paste-modal.js +84 -0
- package/lib/public/modules/project-logs-render.js +405 -0
- package/lib/public/modules/project-logs.js +461 -0
- package/lib/public/modules/project-removal-target.js +26 -0
- package/lib/public/modules/scheduler.js +17 -3
- package/lib/public/modules/session-actions.js +29 -105
- package/lib/public/modules/session-hierarchy.js +54 -0
- package/lib/public/modules/sidebar-mobile.js +40 -15
- package/lib/public/modules/sidebar-session-hierarchy.js +214 -0
- package/lib/public/modules/sidebar-sessions.js +77 -11
- package/lib/public/modules/sidebar.js +17 -5
- package/lib/public/modules/split-group-helpers.js +8 -0
- package/lib/public/modules/split-view.js +5 -2
- package/lib/public/modules/sticky-note-markdown.js +5 -0
- package/lib/public/modules/sticky-notes-browser.js +326 -0
- package/lib/public/modules/sticky-notes-card.js +360 -0
- package/lib/public/modules/sticky-notes-editor.js +291 -0
- package/lib/public/modules/sticky-notes-shared.js +90 -0
- package/lib/public/modules/sticky-notes.js +170 -946
- package/lib/public/modules/tool-palette-order.js +143 -0
- package/lib/public/modules/tool-palette-overlays.js +159 -0
- package/lib/public/modules/tool-palette.js +21 -188
- package/lib/public/modules/tools.js +21 -1
- package/lib/public/modules/update-snooze.js +169 -0
- package/lib/public/modules/user-settings.js +4 -0
- package/lib/public/modules/worker-pane-lock.js +176 -0
- package/lib/public/modules/worker-proposal-state.js +16 -0
- package/lib/public/modules/worker-proposal.js +49 -12
- package/lib/public/style.css +3 -1
- package/lib/sdk-bridge.js +132 -46
- package/lib/sdk-message-processor.js +60 -14
- package/lib/server-experimental-settings.js +49 -0
- package/lib/server-global-ws.js +11 -0
- package/lib/server-home-chat-events.js +31 -1
- package/lib/server-home-chat.js +27 -4
- package/lib/server-home-clay-entry.js +1 -1
- package/lib/server-home-clay-session-links.js +34 -5
- package/lib/server-settings.js +5 -0
- package/lib/server-tools.js +127 -9
- package/lib/server.js +124 -18
- package/lib/session-driver-eligibility.js +38 -0
- package/lib/session-driver-orchestration.js +31 -0
- package/lib/session-hygiene.js +3 -0
- package/lib/session-notes-mcp-server.js +25 -5
- package/lib/session-pair-factory.js +247 -0
- package/lib/session-pair-mcp-server.js +46 -8
- package/lib/session-pair-prompts.js +79 -0
- package/lib/session-pair-turn-control.js +153 -0
- package/lib/session-provenance.js +119 -0
- package/lib/session-spawn-mcp-server.js +1 -1
- package/lib/session-split-groups.js +4 -1
- package/lib/session-title-policy.js +60 -0
- package/lib/session-visibility.js +39 -0
- package/lib/sessions.js +61 -11
- package/lib/tools-registry.js +89 -10
- package/lib/update-snooze.js +437 -0
- package/lib/users-experimental-preferences.js +30 -0
- package/lib/users-permissions.js +2 -2
- package/lib/users.js +22 -16
- package/lib/workspace-query-access.js +102 -0
- package/lib/workspace-query-service.js +24 -17
- package/lib/worktree.js +25 -38
- package/lib/ws-schema.js +38 -7
- package/lib/yoke/adapters/claude-worker.js +16 -6
- package/lib/yoke/adapters/claude.js +81 -4
- package/lib/yoke/adapters/codex.js +63 -15
- package/lib/yoke/codex-background-tasks.js +8 -2
- package/lib/yoke/vendor-registry.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// tool-palette-order.js — the tool registry and the pure ordering rules.
|
|
2
|
+
//
|
|
3
|
+
// Extracted from tool-palette.js, which owns the DOM. Everything here is
|
|
4
|
+
// pure data and pure functions over id lists: what tools exist, and what a
|
|
5
|
+
// stored preference means today. Keeping it separate keeps the palette module
|
|
6
|
+
// under the size limit and makes the ordering rules directly testable without
|
|
7
|
+
// a browser.
|
|
8
|
+
//
|
|
9
|
+
// Two kinds of rule act on a stored preference, in this order:
|
|
10
|
+
// - legacy remap: an id that was replaced by another tool
|
|
11
|
+
// - retirement: an id whose tool was withdrawn with no replacement
|
|
12
|
+
//
|
|
13
|
+
// Neither rule ever overrides a live choice. A preference naming a tool that
|
|
14
|
+
// still exists is honored exactly as saved; registry order is only the default
|
|
15
|
+
// for a fresh palette and the append order for tools a saved list predates.
|
|
16
|
+
|
|
17
|
+
// Registry order = default order for users who haven't customized. Users
|
|
18
|
+
// with saved preferences keep their own order (applyPreferences uses
|
|
19
|
+
// saved order first, then appends any registry entries the user's saved
|
|
20
|
+
// list doesn't mention). So changes here only affect fresh palettes.
|
|
21
|
+
var SESSION_TOOLS = [
|
|
22
|
+
{ id: "file-browser-btn", icon: "folder-tree", label: "File browser" },
|
|
23
|
+
{ id: "terminal-sidebar-btn", icon: "square-terminal", label: "Terminal", countId: "terminal-sidebar-count" },
|
|
24
|
+
{ id: "sticky-notes-sidebar-btn", icon: "sticky-note", label: "Sticky Notes", countId: "sticky-notes-sidebar-count" },
|
|
25
|
+
{ id: "project-logs-btn", icon: "notebook-tabs", label: "Logs", countId: "project-logs-count" },
|
|
26
|
+
{ id: "loop-tool-btn", icon: "repeat", label: "Loop" },
|
|
27
|
+
{ id: "mcp-btn", icon: "cable", label: "MCP Servers", countId: "mcp-sidebar-count" },
|
|
28
|
+
{ id: "skills-btn", icon: "puzzle", label: "Skills" },
|
|
29
|
+
{ id: "scheduler-btn", icon: "calendar-clock", label: "Scheduled Tasks" },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
var MATE_TOOLS = [
|
|
33
|
+
{ id: "mate-memory-btn", icon: "brain", label: "Memory", countId: "mate-memory-count" },
|
|
34
|
+
{ id: "mate-knowledge-btn", icon: "book-open", label: "Knowledge", countId: "mate-knowledge-count" },
|
|
35
|
+
{ id: "mate-sticky-notes-btn", icon: "sticky-note", label: "Sticky Notes" },
|
|
36
|
+
{ id: "mate-debate-btn", icon: "mic", label: "Debate" },
|
|
37
|
+
{ id: "mate-mcp-btn", icon: "cable", label: "MCP Servers", countId: "mate-mcp-sidebar-count" },
|
|
38
|
+
{ id: "mate-skills-btn", icon: "puzzle", label: "Skills" },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
export var PALETTES = {
|
|
42
|
+
session: {
|
|
43
|
+
tools: SESSION_TOOLS,
|
|
44
|
+
activeContainerId: "session-actions",
|
|
45
|
+
hiddenSectionId: "session-actions-hidden",
|
|
46
|
+
},
|
|
47
|
+
mate: {
|
|
48
|
+
tools: MATE_TOOLS,
|
|
49
|
+
activeContainerId: "mate-sidebar-tools",
|
|
50
|
+
hiddenSectionId: "mate-sidebar-tools-hidden",
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Tools that were replaced in the session palette. A saved preference naming
|
|
55
|
+
// the old id would otherwise silently drop out (its element no longer exists)
|
|
56
|
+
// and the replacement would append to the end of the palette instead of
|
|
57
|
+
// inheriting the slot the user had chosen.
|
|
58
|
+
//
|
|
59
|
+
// There is no active remap. "scheduler-btn" used to map to "project-logs-btn"
|
|
60
|
+
// from when Logs took the Scheduled Tasks slot; that remap is retired because
|
|
61
|
+
// Scheduled Tasks is a live tool again, so a stored "scheduler-btn" once more
|
|
62
|
+
// means exactly what it says and rewriting it would destroy the user's own
|
|
63
|
+
// choice. The mechanism stays as the seam for the next replacement.
|
|
64
|
+
var LEGACY_SESSION_TOOL_IDS = {};
|
|
65
|
+
|
|
66
|
+
// Tools withdrawn from the session palette with no replacement. Git moved to
|
|
67
|
+
// the always-visible repository placard below the tool strip, so it no longer
|
|
68
|
+
// consumes an icon slot. A saved preference naming it is dropped rather than
|
|
69
|
+
// left to linger in stored order forever.
|
|
70
|
+
var RETIRED_SESSION_TOOL_IDS = ["git-sidebar-btn"];
|
|
71
|
+
|
|
72
|
+
function indexOfId(list, id) {
|
|
73
|
+
for (var i = 0; i < list.length; i++) {
|
|
74
|
+
if (list[i] === id) return i;
|
|
75
|
+
}
|
|
76
|
+
return -1;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Rewrite a saved preference so a replaced tool keeps its slot.
|
|
80
|
+
//
|
|
81
|
+
// If the user already has an explicit preference for the replacement, that
|
|
82
|
+
// choice wins and the legacy id is simply dropped: their position, and whether
|
|
83
|
+
// they hid it, are never overridden. Otherwise the legacy id is replaced in
|
|
84
|
+
// place, at the exact index it occupied, in whichever list it appeared in.
|
|
85
|
+
//
|
|
86
|
+
// Exported for tests; returns new arrays and never mutates the input.
|
|
87
|
+
export function normalizeToolPreferences(name, prefs) {
|
|
88
|
+
var order = (prefs && prefs.order) || [];
|
|
89
|
+
var hidden = (prefs && prefs.hidden) || [];
|
|
90
|
+
var result = { order: order.slice(), hidden: hidden.slice(), migrated: false };
|
|
91
|
+
if (name !== "session") return result;
|
|
92
|
+
|
|
93
|
+
var legacyIds = Object.keys(LEGACY_SESSION_TOOL_IDS);
|
|
94
|
+
for (var i = 0; i < legacyIds.length; i++) {
|
|
95
|
+
var legacyId = legacyIds[i];
|
|
96
|
+
var replacementId = LEGACY_SESSION_TOOL_IDS[legacyId];
|
|
97
|
+
var inOrder = indexOfId(result.order, legacyId) !== -1;
|
|
98
|
+
var inHidden = indexOfId(result.hidden, legacyId) !== -1;
|
|
99
|
+
if (!inOrder && !inHidden) continue;
|
|
100
|
+
|
|
101
|
+
var explicit = indexOfId(result.order, replacementId) !== -1
|
|
102
|
+
|| indexOfId(result.hidden, replacementId) !== -1;
|
|
103
|
+
result.order = rewriteList(result.order, legacyId, replacementId, explicit);
|
|
104
|
+
result.hidden = rewriteList(result.hidden, legacyId, replacementId, explicit);
|
|
105
|
+
result.migrated = true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (var r = 0; r < RETIRED_SESSION_TOOL_IDS.length; r++) {
|
|
109
|
+
var retiredId = RETIRED_SESSION_TOOL_IDS[r];
|
|
110
|
+
if (indexOfId(result.order, retiredId) === -1 && indexOfId(result.hidden, retiredId) === -1) continue;
|
|
111
|
+
result.order = dropId(result.order, retiredId);
|
|
112
|
+
result.hidden = dropId(result.hidden, retiredId);
|
|
113
|
+
result.migrated = true;
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function dropId(list, id) {
|
|
119
|
+
var out = [];
|
|
120
|
+
for (var i = 0; i < list.length; i++) {
|
|
121
|
+
if (list[i] !== id) out.push(list[i]);
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Drop the legacy id when the replacement is already chosen explicitly;
|
|
127
|
+
// otherwise swap it in place. Any duplicate of the replacement introduced by
|
|
128
|
+
// the swap is removed, keeping the first occurrence.
|
|
129
|
+
function rewriteList(list, legacyId, replacementId, dropLegacy) {
|
|
130
|
+
var out = [];
|
|
131
|
+
var seen = {};
|
|
132
|
+
for (var i = 0; i < list.length; i++) {
|
|
133
|
+
var id = list[i];
|
|
134
|
+
if (id === legacyId) {
|
|
135
|
+
if (dropLegacy) continue;
|
|
136
|
+
id = replacementId;
|
|
137
|
+
}
|
|
138
|
+
if (seen[id]) continue;
|
|
139
|
+
seen[id] = true;
|
|
140
|
+
out.push(id);
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// tool-palette-overlays.js — transient overlays that sit on top of the palette.
|
|
2
|
+
//
|
|
3
|
+
// Two independent, self-contained pieces extracted from tool-palette.js so
|
|
4
|
+
// that module stays under the size limit: the right-click context menu, and
|
|
5
|
+
// the Cmd/Ctrl+O hotkey pick mode. Neither owns palette state; the menu takes
|
|
6
|
+
// its items (and their actions) from the caller, and pick mode works purely
|
|
7
|
+
// off the rendered tiles. Behavior is unchanged by the move.
|
|
8
|
+
|
|
9
|
+
import { PALETTES } from './tool-palette-order.js';
|
|
10
|
+
|
|
11
|
+
// --- Right-click context menu ---
|
|
12
|
+
|
|
13
|
+
var _openMenu = null;
|
|
14
|
+
|
|
15
|
+
export function openPaletteContextMenu(x, y, items) {
|
|
16
|
+
closePaletteContextMenu();
|
|
17
|
+
var menu = document.createElement('div');
|
|
18
|
+
menu.className = 'tool-palette-ctx-menu';
|
|
19
|
+
for (var i = 0; i < items.length; i++) {
|
|
20
|
+
(function (item) {
|
|
21
|
+
var btn = document.createElement('button');
|
|
22
|
+
btn.type = 'button';
|
|
23
|
+
btn.className = 'tool-palette-ctx-item';
|
|
24
|
+
btn.textContent = item.label;
|
|
25
|
+
btn.addEventListener('click', function (e) {
|
|
26
|
+
e.stopPropagation();
|
|
27
|
+
closePaletteContextMenu();
|
|
28
|
+
item.action();
|
|
29
|
+
});
|
|
30
|
+
menu.appendChild(btn);
|
|
31
|
+
})(items[i]);
|
|
32
|
+
}
|
|
33
|
+
document.body.appendChild(menu);
|
|
34
|
+
// Clamp to viewport.
|
|
35
|
+
var rect = menu.getBoundingClientRect();
|
|
36
|
+
var px = x, py = y;
|
|
37
|
+
if (px + rect.width > window.innerWidth - 4) px = window.innerWidth - rect.width - 4;
|
|
38
|
+
if (py + rect.height > window.innerHeight - 4) py = window.innerHeight - rect.height - 4;
|
|
39
|
+
menu.style.left = px + 'px';
|
|
40
|
+
menu.style.top = py + 'px';
|
|
41
|
+
_openMenu = menu;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function closePaletteContextMenu() {
|
|
45
|
+
if (_openMenu && _openMenu.parentNode) _openMenu.parentNode.removeChild(_openMenu);
|
|
46
|
+
_openMenu = null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
document.addEventListener('click', function () { closePaletteContextMenu(); });
|
|
50
|
+
document.addEventListener('keydown', function (e) {
|
|
51
|
+
if (e.key === 'Escape') closePaletteContextMenu();
|
|
52
|
+
});
|
|
53
|
+
window.addEventListener('blur', function () { closePaletteContextMenu(); });
|
|
54
|
+
window.addEventListener('resize', function () { closePaletteContextMenu(); });
|
|
55
|
+
|
|
56
|
+
// --- Hotkey overlay (Cmd/Ctrl + O) ---
|
|
57
|
+
//
|
|
58
|
+
// Press Cmd/Ctrl + O to reveal a hotkey badge on each visible palette
|
|
59
|
+
// tile. Then press the shown digit (1..9, 0) or letter (a..z) to
|
|
60
|
+
// activate that tool. Escape or clicking away dismisses. Inspired by
|
|
61
|
+
// Vimium / Superhuman-style "pick a tile with one keystroke" flows.
|
|
62
|
+
|
|
63
|
+
var _pickActive = false;
|
|
64
|
+
var HOTKEYS = (function () {
|
|
65
|
+
var k = [];
|
|
66
|
+
for (var i = 1; i <= 9; i++) k.push(String(i));
|
|
67
|
+
k.push('0');
|
|
68
|
+
for (var c = 97; c <= 122; c++) k.push(String.fromCharCode(c));
|
|
69
|
+
return k;
|
|
70
|
+
})();
|
|
71
|
+
|
|
72
|
+
function getVisiblePaletteName() {
|
|
73
|
+
// offsetParent is null when the element (or an ancestor) is hidden.
|
|
74
|
+
var mateActive = document.getElementById('mate-sidebar-tools');
|
|
75
|
+
if (mateActive && mateActive.offsetParent !== null) return 'mate';
|
|
76
|
+
var sessionActive = document.getElementById('session-actions');
|
|
77
|
+
if (sessionActive && sessionActive.offsetParent !== null) return 'session';
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function enterToolPickMode() {
|
|
82
|
+
if (_pickActive) return;
|
|
83
|
+
var name = getVisiblePaletteName();
|
|
84
|
+
if (!name) return;
|
|
85
|
+
var active = document.getElementById(PALETTES[name].activeContainerId);
|
|
86
|
+
if (!active) return;
|
|
87
|
+
var tiles = active.querySelectorAll('[data-tool-id]');
|
|
88
|
+
if (tiles.length === 0) return;
|
|
89
|
+
for (var i = 0; i < tiles.length && i < HOTKEYS.length; i++) {
|
|
90
|
+
var key = HOTKEYS[i];
|
|
91
|
+
tiles[i].dataset.hotkey = key;
|
|
92
|
+
var badge = document.createElement('span');
|
|
93
|
+
badge.className = 'tool-palette-hotkey-badge';
|
|
94
|
+
badge.textContent = key.toUpperCase();
|
|
95
|
+
tiles[i].appendChild(badge);
|
|
96
|
+
}
|
|
97
|
+
_pickActive = true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function exitToolPickMode() {
|
|
101
|
+
if (!_pickActive) return;
|
|
102
|
+
_pickActive = false;
|
|
103
|
+
var badges = document.querySelectorAll('.tool-palette-hotkey-badge');
|
|
104
|
+
for (var i = 0; i < badges.length; i++) {
|
|
105
|
+
if (badges[i].parentNode) badges[i].parentNode.removeChild(badges[i]);
|
|
106
|
+
}
|
|
107
|
+
var marked = document.querySelectorAll('[data-hotkey]');
|
|
108
|
+
for (var j = 0; j < marked.length; j++) {
|
|
109
|
+
delete marked[j].dataset.hotkey;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Capture phase so we reliably preempt typing and browser shortcuts
|
|
114
|
+
// when focus is in an input.
|
|
115
|
+
document.addEventListener('keydown', function (e) {
|
|
116
|
+
// Pick mode: consume the next printable key.
|
|
117
|
+
if (_pickActive) {
|
|
118
|
+
if (e.key === 'Escape') {
|
|
119
|
+
e.preventDefault();
|
|
120
|
+
exitToolPickMode();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Ignore modifier-only keys; react to single-character keys.
|
|
124
|
+
if (e.key.length !== 1) return;
|
|
125
|
+
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
126
|
+
var key = e.key.toLowerCase();
|
|
127
|
+
var tile = document.querySelector('[data-hotkey="' + key + '"]');
|
|
128
|
+
if (tile) {
|
|
129
|
+
e.preventDefault();
|
|
130
|
+
exitToolPickMode();
|
|
131
|
+
tile.click();
|
|
132
|
+
} else {
|
|
133
|
+
// Unknown key exits pick mode without triggering a tile.
|
|
134
|
+
exitToolPickMode();
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Enter pick mode on Cmd/Ctrl + O (letter O). Intercepted globally
|
|
140
|
+
// including inside text inputs — the browser's native Cmd+O is a
|
|
141
|
+
// file-picker dialog Clay never wants, so taking it over costs the
|
|
142
|
+
// user nothing and is especially useful when jumping to a tool from
|
|
143
|
+
// the session search box.
|
|
144
|
+
if ((e.metaKey || e.ctrlKey) && !e.altKey && !e.shiftKey && e.key && e.key.toLowerCase() === 'o') {
|
|
145
|
+
if (!getVisiblePaletteName()) return;
|
|
146
|
+
e.preventDefault();
|
|
147
|
+
enterToolPickMode();
|
|
148
|
+
}
|
|
149
|
+
}, true);
|
|
150
|
+
|
|
151
|
+
// Dismiss the overlay on click-away, blur, or viewport changes so it
|
|
152
|
+
// never lingers invisibly.
|
|
153
|
+
document.addEventListener('click', function () { if (_pickActive) exitToolPickMode(); });
|
|
154
|
+
window.addEventListener('blur', function () { if (_pickActive) exitToolPickMode(); });
|
|
155
|
+
window.addEventListener('resize', function () { if (_pickActive) exitToolPickMode(); });
|
|
156
|
+
|
|
157
|
+
export function isToolPickActive() {
|
|
158
|
+
return _pickActive;
|
|
159
|
+
}
|
|
@@ -14,44 +14,13 @@
|
|
|
14
14
|
// An in-flight save is debounced so rapid drag reorders don't spam.
|
|
15
15
|
|
|
16
16
|
import { refreshIcons } from './icons.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
{ id: "terminal-sidebar-btn", icon: "square-terminal", label: "Terminal", countId: "terminal-sidebar-count" },
|
|
25
|
-
{ id: "sticky-notes-sidebar-btn", icon: "sticky-note", label: "Sticky Notes", countId: "sticky-notes-sidebar-count" },
|
|
26
|
-
{ id: "scheduler-btn", icon: "calendar-clock", label: "Scheduled Tasks" },
|
|
27
|
-
{ id: "loop-tool-btn", icon: "repeat", label: "Loop" },
|
|
28
|
-
{ id: "git-sidebar-btn", icon: "git-branch", label: "Git", countId: "git-sidebar-count" },
|
|
29
|
-
{ id: "mcp-btn", icon: "cable", label: "MCP Servers", countId: "mcp-sidebar-count" },
|
|
30
|
-
{ id: "skills-btn", icon: "puzzle", label: "Skills" },
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
var MATE_TOOLS = [
|
|
34
|
-
{ id: "mate-memory-btn", icon: "brain", label: "Memory", countId: "mate-memory-count" },
|
|
35
|
-
{ id: "mate-knowledge-btn", icon: "book-open", label: "Knowledge", countId: "mate-knowledge-count" },
|
|
36
|
-
{ id: "mate-sticky-notes-btn", icon: "sticky-note", label: "Sticky Notes" },
|
|
37
|
-
{ id: "mate-scheduler-btn", icon: "calendar-clock", label: "Scheduled Tasks" },
|
|
38
|
-
{ id: "mate-debate-btn", icon: "mic", label: "Debate" },
|
|
39
|
-
{ id: "mate-mcp-btn", icon: "cable", label: "MCP Servers", countId: "mate-mcp-sidebar-count" },
|
|
40
|
-
{ id: "mate-skills-btn", icon: "puzzle", label: "Skills" },
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
var PALETTES = {
|
|
44
|
-
session: {
|
|
45
|
-
tools: SESSION_TOOLS,
|
|
46
|
-
activeContainerId: "session-actions",
|
|
47
|
-
hiddenSectionId: "session-actions-hidden",
|
|
48
|
-
},
|
|
49
|
-
mate: {
|
|
50
|
-
tools: MATE_TOOLS,
|
|
51
|
-
activeContainerId: "mate-sidebar-tools",
|
|
52
|
-
hiddenSectionId: "mate-sidebar-tools-hidden",
|
|
53
|
-
},
|
|
54
|
-
};
|
|
17
|
+
import { PALETTES, normalizeToolPreferences } from './tool-palette-order.js';
|
|
18
|
+
import {
|
|
19
|
+
enterToolPickMode,
|
|
20
|
+
exitToolPickMode,
|
|
21
|
+
isToolPickActive,
|
|
22
|
+
openPaletteContextMenu
|
|
23
|
+
} from './tool-palette-overlays.js';
|
|
55
24
|
|
|
56
25
|
var _saveTimers = {};
|
|
57
26
|
var _draggingEl = null;
|
|
@@ -86,7 +55,7 @@ export function initToolPalettes() {
|
|
|
86
55
|
// handler below, which would immediately tear down the pick-mode
|
|
87
56
|
// we just entered.
|
|
88
57
|
e.stopPropagation();
|
|
89
|
-
if (
|
|
58
|
+
if (isToolPickActive()) exitToolPickMode();
|
|
90
59
|
else enterToolPickMode();
|
|
91
60
|
});
|
|
92
61
|
}
|
|
@@ -303,152 +272,6 @@ function moveToActive(name, toolId) {
|
|
|
303
272
|
// attached in buildToolButton before any external module attaches
|
|
304
273
|
// handlers by ID. See the stopImmediatePropagation logic there.)
|
|
305
274
|
|
|
306
|
-
// --- Right-click context menu ---
|
|
307
|
-
|
|
308
|
-
var _openMenu = null;
|
|
309
|
-
|
|
310
|
-
function openPaletteContextMenu(x, y, items) {
|
|
311
|
-
closePaletteContextMenu();
|
|
312
|
-
var menu = document.createElement('div');
|
|
313
|
-
menu.className = 'tool-palette-ctx-menu';
|
|
314
|
-
for (var i = 0; i < items.length; i++) {
|
|
315
|
-
(function (item) {
|
|
316
|
-
var btn = document.createElement('button');
|
|
317
|
-
btn.type = 'button';
|
|
318
|
-
btn.className = 'tool-palette-ctx-item';
|
|
319
|
-
btn.textContent = item.label;
|
|
320
|
-
btn.addEventListener('click', function (e) {
|
|
321
|
-
e.stopPropagation();
|
|
322
|
-
closePaletteContextMenu();
|
|
323
|
-
item.action();
|
|
324
|
-
});
|
|
325
|
-
menu.appendChild(btn);
|
|
326
|
-
})(items[i]);
|
|
327
|
-
}
|
|
328
|
-
document.body.appendChild(menu);
|
|
329
|
-
// Clamp to viewport.
|
|
330
|
-
var rect = menu.getBoundingClientRect();
|
|
331
|
-
var px = x, py = y;
|
|
332
|
-
if (px + rect.width > window.innerWidth - 4) px = window.innerWidth - rect.width - 4;
|
|
333
|
-
if (py + rect.height > window.innerHeight - 4) py = window.innerHeight - rect.height - 4;
|
|
334
|
-
menu.style.left = px + 'px';
|
|
335
|
-
menu.style.top = py + 'px';
|
|
336
|
-
_openMenu = menu;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function closePaletteContextMenu() {
|
|
340
|
-
if (_openMenu && _openMenu.parentNode) _openMenu.parentNode.removeChild(_openMenu);
|
|
341
|
-
_openMenu = null;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
document.addEventListener('click', function () { closePaletteContextMenu(); });
|
|
345
|
-
document.addEventListener('keydown', function (e) {
|
|
346
|
-
if (e.key === 'Escape') closePaletteContextMenu();
|
|
347
|
-
});
|
|
348
|
-
window.addEventListener('blur', function () { closePaletteContextMenu(); });
|
|
349
|
-
window.addEventListener('resize', function () { closePaletteContextMenu(); });
|
|
350
|
-
|
|
351
|
-
// --- Hotkey overlay (Cmd/Ctrl + O) ---
|
|
352
|
-
//
|
|
353
|
-
// Press Cmd/Ctrl + O to reveal a hotkey badge on each visible palette
|
|
354
|
-
// tile. Then press the shown digit (1..9, 0) or letter (a..z) to
|
|
355
|
-
// activate that tool. Escape or clicking away dismisses. Inspired by
|
|
356
|
-
// Vimium / Superhuman-style "pick a tile with one keystroke" flows.
|
|
357
|
-
|
|
358
|
-
var _pickActive = false;
|
|
359
|
-
var HOTKEYS = (function () {
|
|
360
|
-
var k = [];
|
|
361
|
-
for (var i = 1; i <= 9; i++) k.push(String(i));
|
|
362
|
-
k.push('0');
|
|
363
|
-
for (var c = 97; c <= 122; c++) k.push(String.fromCharCode(c));
|
|
364
|
-
return k;
|
|
365
|
-
})();
|
|
366
|
-
|
|
367
|
-
function getVisiblePaletteName() {
|
|
368
|
-
// offsetParent is null when the element (or an ancestor) is hidden.
|
|
369
|
-
var mateActive = document.getElementById('mate-sidebar-tools');
|
|
370
|
-
if (mateActive && mateActive.offsetParent !== null) return 'mate';
|
|
371
|
-
var sessionActive = document.getElementById('session-actions');
|
|
372
|
-
if (sessionActive && sessionActive.offsetParent !== null) return 'session';
|
|
373
|
-
return null;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function enterToolPickMode() {
|
|
377
|
-
if (_pickActive) return;
|
|
378
|
-
var name = getVisiblePaletteName();
|
|
379
|
-
if (!name) return;
|
|
380
|
-
var active = document.getElementById(PALETTES[name].activeContainerId);
|
|
381
|
-
if (!active) return;
|
|
382
|
-
var tiles = active.querySelectorAll('[data-tool-id]');
|
|
383
|
-
if (tiles.length === 0) return;
|
|
384
|
-
for (var i = 0; i < tiles.length && i < HOTKEYS.length; i++) {
|
|
385
|
-
var key = HOTKEYS[i];
|
|
386
|
-
tiles[i].dataset.hotkey = key;
|
|
387
|
-
var badge = document.createElement('span');
|
|
388
|
-
badge.className = 'tool-palette-hotkey-badge';
|
|
389
|
-
badge.textContent = key.toUpperCase();
|
|
390
|
-
tiles[i].appendChild(badge);
|
|
391
|
-
}
|
|
392
|
-
_pickActive = true;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function exitToolPickMode() {
|
|
396
|
-
if (!_pickActive) return;
|
|
397
|
-
_pickActive = false;
|
|
398
|
-
var badges = document.querySelectorAll('.tool-palette-hotkey-badge');
|
|
399
|
-
for (var i = 0; i < badges.length; i++) {
|
|
400
|
-
if (badges[i].parentNode) badges[i].parentNode.removeChild(badges[i]);
|
|
401
|
-
}
|
|
402
|
-
var marked = document.querySelectorAll('[data-hotkey]');
|
|
403
|
-
for (var j = 0; j < marked.length; j++) {
|
|
404
|
-
delete marked[j].dataset.hotkey;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Capture phase so we reliably preempt typing and browser shortcuts
|
|
409
|
-
// when focus is in an input.
|
|
410
|
-
document.addEventListener('keydown', function (e) {
|
|
411
|
-
// Pick mode: consume the next printable key.
|
|
412
|
-
if (_pickActive) {
|
|
413
|
-
if (e.key === 'Escape') {
|
|
414
|
-
e.preventDefault();
|
|
415
|
-
exitToolPickMode();
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
// Ignore modifier-only keys; react to single-character keys.
|
|
419
|
-
if (e.key.length !== 1) return;
|
|
420
|
-
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
421
|
-
var key = e.key.toLowerCase();
|
|
422
|
-
var tile = document.querySelector('[data-hotkey="' + key + '"]');
|
|
423
|
-
if (tile) {
|
|
424
|
-
e.preventDefault();
|
|
425
|
-
exitToolPickMode();
|
|
426
|
-
tile.click();
|
|
427
|
-
} else {
|
|
428
|
-
// Unknown key exits pick mode without triggering a tile.
|
|
429
|
-
exitToolPickMode();
|
|
430
|
-
}
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Enter pick mode on Cmd/Ctrl + O (letter O). Intercepted globally
|
|
435
|
-
// including inside text inputs — the browser's native Cmd+O is a
|
|
436
|
-
// file-picker dialog Clay never wants, so taking it over costs the
|
|
437
|
-
// user nothing and is especially useful when jumping to a tool from
|
|
438
|
-
// the session search box.
|
|
439
|
-
if ((e.metaKey || e.ctrlKey) && !e.altKey && !e.shiftKey && e.key && e.key.toLowerCase() === 'o') {
|
|
440
|
-
if (!getVisiblePaletteName()) return;
|
|
441
|
-
e.preventDefault();
|
|
442
|
-
enterToolPickMode();
|
|
443
|
-
}
|
|
444
|
-
}, true);
|
|
445
|
-
|
|
446
|
-
// Dismiss the overlay on click-away, blur, or viewport changes so it
|
|
447
|
-
// never lingers invisibly.
|
|
448
|
-
document.addEventListener('click', function () { if (_pickActive) exitToolPickMode(); });
|
|
449
|
-
window.addEventListener('blur', function () { if (_pickActive) exitToolPickMode(); });
|
|
450
|
-
window.addEventListener('resize', function () { if (_pickActive) exitToolPickMode(); });
|
|
451
|
-
|
|
452
275
|
function getDragAfterElement(container, x, y) {
|
|
453
276
|
var tiles = Array.prototype.slice.call(
|
|
454
277
|
container.querySelectorAll('[data-tool-id]:not(.dragging)')
|
|
@@ -495,13 +318,17 @@ function applyPreferences(name, prefs) {
|
|
|
495
318
|
var hiddenGrid = hiddenSection ? hiddenSection.querySelector('.tool-palette-hidden-grid') : null;
|
|
496
319
|
if (!active || !hiddenGrid) return;
|
|
497
320
|
|
|
321
|
+
var normalized = normalizeToolPreferences(name, prefs);
|
|
322
|
+
|
|
498
323
|
var hiddenSet = {};
|
|
499
|
-
var hiddenList =
|
|
324
|
+
var hiddenList = normalized.hidden;
|
|
500
325
|
for (var i = 0; i < hiddenList.length; i++) hiddenSet[hiddenList[i]] = true;
|
|
501
326
|
|
|
502
|
-
var orderList =
|
|
327
|
+
var orderList = normalized.order;
|
|
503
328
|
// Any tools not in the stored order are appended in registry order so
|
|
504
|
-
// newly-added tools surface automatically for existing users.
|
|
329
|
+
// newly-added tools surface automatically for existing users. This is how
|
|
330
|
+
// a restored tool reaches a saved palette that predates it: no rewrite, no
|
|
331
|
+
// server state, just the normal new-registry append.
|
|
505
332
|
var placed = {};
|
|
506
333
|
for (var j = 0; j < orderList.length; j++) {
|
|
507
334
|
var id = orderList[j];
|
|
@@ -525,6 +352,12 @@ function applyPreferences(name, prefs) {
|
|
|
525
352
|
hiddenGrid.appendChild(hbtn);
|
|
526
353
|
}
|
|
527
354
|
}
|
|
355
|
+
|
|
356
|
+
// Write the rewritten preference back once, through the normal save path, so
|
|
357
|
+
// the migration survives reloads and reaches the user's other devices. The
|
|
358
|
+
// save reads the DOM this function just arranged, so it records exactly what
|
|
359
|
+
// is on screen.
|
|
360
|
+
if (normalized.migrated) queueSave(name);
|
|
528
361
|
}
|
|
529
362
|
|
|
530
363
|
function queueSave(name) {
|
|
@@ -203,6 +203,23 @@ export function toolActivityText(name, input) {
|
|
|
203
203
|
return "Running " + name + "...";
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
// The sublabel for a call that has finished. `toolActivityText` is written in
|
|
207
|
+
// the present progressive for a call in flight, so re-asserting it after a
|
|
208
|
+
// result has arrived leaves a finished card claiming to still be running —
|
|
209
|
+
// which is how a failed custom tool ended up reading "Running
|
|
210
|
+
// send_to_partner..." beside its own error output.
|
|
211
|
+
//
|
|
212
|
+
// Only ever used where a result exists, so a genuinely in-flight call is never
|
|
213
|
+
// relabelled. A failure states so plainly; a success keeps whatever specific
|
|
214
|
+
// description the activity text produced ("Reading foo.js"), because that still
|
|
215
|
+
// describes the call accurately, and only the generic present-progressive
|
|
216
|
+
// fallback is replaced.
|
|
217
|
+
export function toolTerminalText(name, input, isError) {
|
|
218
|
+
if (isError) return name + " failed";
|
|
219
|
+
var activity = toolActivityText(name, input);
|
|
220
|
+
return activity === "Running " + name + "..." ? name : activity;
|
|
221
|
+
}
|
|
222
|
+
|
|
206
223
|
function shortPath(p) {
|
|
207
224
|
if (!p) return "";
|
|
208
225
|
var parts = p.split("/");
|
|
@@ -2136,9 +2153,12 @@ export function updateToolResult(id, content, isError, images) {
|
|
|
2136
2153
|
var tool = tools[id];
|
|
2137
2154
|
if (!tool) return;
|
|
2138
2155
|
|
|
2156
|
+
// Re-render the sublabel from the now-complete input (streaming can have
|
|
2157
|
+
// built it from a partial one), but in its terminal form: a result has
|
|
2158
|
+
// arrived, so this call is no longer running.
|
|
2139
2159
|
var subtitleText = tool.el.querySelector(".tool-subtitle-text");
|
|
2140
2160
|
if (subtitleText && tool.input) {
|
|
2141
|
-
subtitleText.textContent =
|
|
2161
|
+
subtitleText.textContent = toolTerminalText(tool.name, tool.input, isError);
|
|
2142
2162
|
}
|
|
2143
2163
|
|
|
2144
2164
|
var resultBlock = document.createElement("div");
|