clay-server 5.1.0-beta.1 → 5.1.0-beta.3
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/LICENSES/README.md +4 -0
- package/LICENSES/dicebear-avatars.md +16 -0
- package/bin/cli.js +9 -91
- package/lib/builtin-mates.js +7 -7
- package/lib/daemon-project-access.js +7 -1
- package/lib/daemon-settings.js +11 -0
- package/lib/daemon.js +10 -37
- package/lib/dicebear-avatar.js +62 -0
- package/lib/knowledge-db-service.js +175 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/knowledge-document-index.js +49 -0
- package/lib/knowledge-draft-store.js +56 -0
- package/lib/knowledge-file-store.js +199 -0
- package/lib/knowledge-markdown.js +128 -0
- package/lib/linear-store.js +35 -8
- package/lib/mate-identity-file.js +39 -0
- package/lib/mate-knowledge-mcp-server.js +29 -6
- package/lib/mate-knowledge-service.js +23 -2
- package/lib/mate-knowledge-sync.js +37 -22
- package/lib/mate-ready-creation.js +2 -1
- package/lib/mate-workspace-color.js +4 -0
- package/lib/mates-identity.js +4 -4
- package/lib/mates-prompts.js +4 -3
- package/lib/mates.js +30 -11
- package/lib/os-users.js +5 -16
- package/lib/project-connection.js +36 -9
- package/lib/project-debate-proposal.js +6 -0
- package/lib/project-debate-workspace.js +43 -0
- package/lib/project-debate.js +4 -2
- package/lib/project-filesystem.js +21 -39
- package/lib/project-knowledge.js +315 -136
- package/lib/project-mate-creation-proposal.js +1 -0
- package/lib/project-mate-interaction.js +7 -6
- package/lib/project-server-settings.js +31 -0
- package/lib/project-sessions.js +92 -182
- package/lib/project-user-knowledge.js +60 -0
- package/lib/project.js +62 -21
- package/lib/public/app.js +79 -71
- package/lib/public/clay-studio-wordmark-bold.svg +20 -0
- package/lib/public/css/clay-workspace.css +105 -0
- package/lib/public/css/debates-workbench.css +47 -0
- package/lib/public/css/home-debate-live.css +26 -0
- package/lib/public/css/home-hub.css +3 -1
- package/lib/public/css/home-mate-model-picker.css +15 -131
- package/lib/public/css/home-mate-settings.css +168 -16
- package/lib/public/css/icon-strip.css +249 -32
- package/lib/public/css/input.css +3 -1
- package/lib/public/css/mate-conversation.css +176 -0
- package/lib/public/css/mate-knowledge-workbench.css +118 -0
- package/lib/public/css/mate-workspace-color.css +16 -0
- package/lib/public/css/mates.css +28 -28
- package/lib/public/css/menus.css +3 -0
- package/lib/public/css/messages.css +1 -114
- package/lib/public/css/mobile-nav.css +16 -16
- package/lib/public/css/overlays.css +15 -0
- package/lib/public/css/project-logs.css +7 -0
- package/lib/public/css/project-mate-creation.css +105 -0
- package/lib/public/css/project-settings-modal.css +56 -0
- package/lib/public/css/right-workbench.css +3 -3
- package/lib/public/css/server-settings-system.css +125 -0
- package/lib/public/css/session-folders.css +80 -21
- package/lib/public/css/session-github.css +25 -16
- package/lib/public/css/settings-ui.css +247 -22
- package/lib/public/css/shared-browser.css +7 -3
- package/lib/public/css/sidebar.css +183 -107
- package/lib/public/css/title-bar.css +156 -1
- package/lib/public/css/worker-delegation.css +47 -0
- package/lib/public/css/worker-proposal.css +72 -150
- package/lib/public/css/you.css +74 -0
- package/lib/public/index.html +206 -548
- package/lib/public/modules/account-island.js +232 -0
- package/lib/public/modules/admin-settings-email.js +197 -0
- package/lib/public/modules/admin-settings-invitations.js +183 -0
- package/lib/public/modules/admin-settings-projects.js +198 -0
- package/lib/public/modules/admin-settings-ui.js +100 -0
- package/lib/public/modules/admin-settings-users.js +331 -0
- package/lib/public/modules/admin.js +19 -949
- package/lib/public/modules/app-cursors.js +19 -43
- package/lib/public/modules/app-debate-ui.js +4 -3
- package/lib/public/modules/app-dm.js +0 -2
- package/lib/public/modules/app-header.js +106 -0
- package/lib/public/modules/app-message-router.js +12 -3
- package/lib/public/modules/app-messages.js +47 -61
- package/lib/public/modules/app-notifications.js +4 -7
- package/lib/public/modules/app-projects.js +5 -38
- package/lib/public/modules/app-rate-limit.js +70 -12
- package/lib/public/modules/app-rendering.js +12 -13
- package/lib/public/modules/avatar.js +30 -9
- package/lib/public/modules/chat-render-runtime.js +16 -4
- package/lib/public/modules/command-palette.js +2 -15
- package/lib/public/modules/context-sources.js +27 -18
- package/lib/public/modules/cursor-sharing-preference.js +3 -3
- package/lib/public/modules/debate-controls.js +299 -0
- package/lib/public/modules/debate.js +2 -0
- package/lib/public/modules/debates-workbench.js +339 -0
- package/lib/public/modules/default-ai.js +88 -65
- package/lib/public/modules/header-tui-font.js +10 -20
- package/lib/public/modules/home-debate-controls.js +18 -289
- package/lib/public/modules/home-mate-avatar-editor.js +228 -0
- package/lib/public/modules/home-mate-color-picker.js +66 -0
- package/lib/public/modules/home-mate-creation.js +1 -1
- package/lib/public/modules/home-mate-instructions.js +90 -0
- package/lib/public/modules/home-mate-model-picker.js +80 -271
- package/lib/public/modules/home-mate-profile-editor.js +72 -0
- package/lib/public/modules/home-mate-settings.js +118 -125
- package/lib/public/modules/home-shell.js +3 -0
- package/lib/public/modules/home-surface-boot.js +1 -9
- package/lib/public/modules/icon-strip-tooltip.js +95 -0
- package/lib/public/modules/knowledge-db-edit-state.js +35 -0
- package/lib/public/modules/knowledge-db-workbench.js +234 -0
- package/lib/public/modules/knowledge-history-state.js +55 -0
- package/lib/public/modules/knowledge-markdown-editor.js +57 -0
- package/lib/public/modules/knowledge-preview-links.js +70 -0
- package/lib/public/modules/knowledge-workbench-shell.js +21 -0
- package/lib/public/modules/linear-settings.js +3 -2
- package/lib/public/modules/markdown.js +5 -6
- package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
- package/lib/public/modules/mate-knowledge-workbench.js +500 -0
- package/lib/public/modules/mate-knowledge.js +11 -3
- package/lib/public/modules/mate-management.js +54 -10
- package/lib/public/modules/mate-sidebar.js +11 -1
- package/lib/public/modules/mate-workspace-color.js +33 -0
- package/lib/public/modules/mention.js +5 -13
- package/lib/public/modules/notification-settings.js +81 -0
- package/lib/public/modules/notifications.js +44 -28
- package/lib/public/modules/personal-settings-dialog.js +51 -0
- package/lib/public/modules/profile.js +66 -24
- package/lib/public/modules/project-chat-identity.js +79 -0
- package/lib/public/modules/project-debate-planning-workspace.js +250 -0
- package/lib/public/modules/project-debate-workspace.js +185 -0
- package/lib/public/modules/project-header-identity.js +127 -0
- package/lib/public/modules/project-logs-render.js +9 -4
- package/lib/public/modules/project-mate-creation-workspace.js +414 -0
- package/lib/public/modules/project-mate-navigation.js +238 -0
- package/lib/public/modules/project-session-navigation.js +29 -0
- package/lib/public/modules/project-settings-environment.js +106 -0
- package/lib/public/modules/project-settings.js +72 -628
- package/lib/public/modules/project-switcher.js +6 -8
- package/lib/public/modules/projects-hub-view.js +5 -6
- package/lib/public/modules/projects-hub.js +2 -6
- package/lib/public/modules/qrcode.js +27 -8
- package/lib/public/modules/search-clay-chat.js +8 -11
- package/lib/public/modules/server-settings-controls.js +316 -0
- package/lib/public/modules/server-settings-environment.js +194 -0
- package/lib/public/modules/server-settings-navigation.js +39 -0
- package/lib/public/modules/server-settings-state.js +54 -0
- package/lib/public/modules/server-settings.js +116 -788
- package/lib/public/modules/server-storage-settings.js +84 -0
- package/lib/public/modules/session-activity-tooltip.js +12 -0
- package/lib/public/modules/session-create-form.js +86 -14
- package/lib/public/modules/session-create.js +50 -7
- package/lib/public/modules/session-github.js +2 -1
- package/lib/public/modules/session-list-visibility.js +41 -0
- package/lib/public/modules/session-presence-ui.js +3 -0
- package/lib/public/modules/session-title-marquee.js +51 -0
- package/lib/public/modules/settings-env-values.js +34 -0
- package/lib/public/modules/settings-select.js +121 -0
- package/lib/public/modules/shared-browser.js +22 -12
- package/lib/public/modules/sidebar-mates.js +3 -50
- package/lib/public/modules/sidebar-mobile.js +63 -60
- package/lib/public/modules/sidebar-projects.js +84 -97
- package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
- package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
- package/lib/public/modules/sidebar-sessions.js +49 -50
- package/lib/public/modules/sidebar.js +11 -13
- package/lib/public/modules/stt-picker.js +14 -5
- package/lib/public/modules/terminal-prefs.js +22 -0
- package/lib/public/modules/theme.js +88 -52
- package/lib/public/modules/thinking-lifecycle.js +7 -6
- package/lib/public/modules/tool-palette-order.js +4 -3
- package/lib/public/modules/tool-palette.js +13 -3
- package/lib/public/modules/tools.js +22 -22
- package/lib/public/modules/user-settings-account.js +318 -0
- package/lib/public/modules/user-settings-appearance.js +55 -0
- package/lib/public/modules/user-settings-editors.js +24 -0
- package/lib/public/modules/user-settings-integrations.js +104 -0
- package/lib/public/modules/user-settings-layout.js +80 -0
- package/lib/public/modules/user-settings-preferences.js +48 -0
- package/lib/public/modules/user-settings-profile.js +53 -0
- package/lib/public/modules/user-settings-themes.js +38 -0
- package/lib/public/modules/user-settings-voice.js +75 -0
- package/lib/public/modules/user-settings.js +53 -480
- package/lib/public/modules/worker-proposal.js +42 -8
- package/lib/public/modules/worker-runtime-picker.js +115 -0
- package/lib/public/modules/you-activity.js +121 -0
- package/lib/public/modules/you.js +251 -0
- package/lib/public/style.css +20 -8
- package/lib/public/vendor/codemirror/LICENSE +21 -0
- package/lib/public/vendor/codemirror/codemirror.css +344 -0
- package/lib/public/vendor/codemirror/codemirror.js +9884 -0
- package/lib/public/vendor/codemirror/continuelist.js +101 -0
- package/lib/public/vendor/codemirror/markdown.js +886 -0
- package/lib/public/vendor/codemirror/show-hint.css +37 -0
- package/lib/public/vendor/codemirror/show-hint.js +523 -0
- package/lib/sdk-bridge.js +15 -3
- package/lib/sdk-message-processor.js +16 -4
- package/lib/sdk-quota-snapshot.js +37 -0
- package/lib/server-admin.js +11 -0
- package/lib/server-appearance-settings.js +71 -0
- package/lib/server-home-chat-events.js +1 -1
- package/lib/server-home-chat.js +25 -3
- package/lib/server-home-debate-planning.js +4 -1
- package/lib/server-home-debates.js +2 -0
- package/lib/server-home-mate-creation.js +4 -1
- package/lib/server-linear.js +14 -8
- package/lib/server-mate-instructions.js +44 -0
- package/lib/server-mates.js +14 -4
- package/lib/server-notification-settings.js +67 -0
- package/lib/server-palette.js +1 -1
- package/lib/server-settings.js +69 -1
- package/lib/server-storage.js +57 -0
- package/lib/server-user-knowledge.js +119 -0
- package/lib/server.js +31 -3
- package/lib/session-visibility.js +7 -2
- package/lib/sessions.js +35 -10
- package/lib/startup-projects.js +40 -0
- package/lib/storage-native-walk.js +85 -0
- package/lib/storage-usage.js +144 -0
- package/lib/user-knowledge-service.js +167 -0
- package/lib/users-permissions.js +7 -0
- package/lib/users-preferences.js +0 -74
- package/lib/users-project-mate-filter-preferences.js +39 -0
- package/lib/users.js +6 -4
- package/lib/workspace-query-access.js +4 -1
- package/lib/ws-schema.js +52 -4
- package/lib/yoke/acp-process-manager.js +2 -2
- package/lib/yoke/adapters/codex.js +12 -52
- package/lib/yoke/codex-app-server.js +2 -2
- package/lib/yoke/codex-model-catalog.js +52 -0
- package/lib/yoke/codex-rate-limits.js +26 -0
- package/package.json +12 -3
|
@@ -33,13 +33,14 @@ var MATE_TOOLS = [
|
|
|
33
33
|
{ id: "mate-memory-btn", icon: "brain", label: "Memory", countId: "mate-memory-count" },
|
|
34
34
|
{ id: "mate-knowledge-btn", icon: "book-open", label: "Knowledge", countId: "mate-knowledge-count" },
|
|
35
35
|
{ id: "mate-sticky-notes-btn", icon: "sticky-note", label: "Sticky Notes" },
|
|
36
|
-
{ id: "mate-
|
|
36
|
+
{ id: "mate-debates-btn", icon: "messages-square", label: "Debates" },
|
|
37
37
|
{ id: "mate-mcp-btn", icon: "cable", label: "MCP / Skills", countId: "mate-mcp-sidebar-count" },
|
|
38
38
|
];
|
|
39
39
|
|
|
40
40
|
export var PALETTES = {
|
|
41
41
|
session: {
|
|
42
42
|
tools: SESSION_TOOLS,
|
|
43
|
+
fixedTools: [{ id: "you-button", icon: "user-round", label: "Me", countId: "me-unread-badge", fixed: true, scope: "clay" }],
|
|
43
44
|
activeContainerId: "session-actions",
|
|
44
45
|
hiddenSectionId: "session-actions-hidden",
|
|
45
46
|
},
|
|
@@ -57,13 +58,13 @@ export var PALETTES = {
|
|
|
57
58
|
//
|
|
58
59
|
// Skills now shares the MCP / Skills entry; explicit MCP placement wins.
|
|
59
60
|
var LEGACY_SESSION_TOOL_IDS = { "skills-btn": "mcp-btn" };
|
|
60
|
-
var LEGACY_MATE_TOOL_IDS = { "mate-skills-btn": "mate-mcp-btn" };
|
|
61
|
+
var LEGACY_MATE_TOOL_IDS = { "mate-skills-btn": "mate-mcp-btn", "mate-debate-btn": "mate-debates-btn" };
|
|
61
62
|
|
|
62
63
|
// Tools withdrawn from the session palette with no replacement. Git moved to
|
|
63
64
|
// the always-visible repository placard below the tool strip, so it no longer
|
|
64
65
|
// consumes an icon slot. A saved preference naming it is dropped rather than
|
|
65
66
|
// left to linger in stored order forever.
|
|
66
|
-
var RETIRED_SESSION_TOOL_IDS = ["git-sidebar-btn", "loop-tool-btn"];
|
|
67
|
+
var RETIRED_SESSION_TOOL_IDS = ["git-sidebar-btn", "loop-tool-btn", "debates-btn"];
|
|
67
68
|
|
|
68
69
|
function indexOfId(list, id) {
|
|
69
70
|
for (var i = 0; i < list.length; i++) {
|
|
@@ -37,6 +37,8 @@ function buildPalette(name) {
|
|
|
37
37
|
var active = document.getElementById(palette.activeContainerId);
|
|
38
38
|
if (!active) return;
|
|
39
39
|
|
|
40
|
+
(palette.fixedTools || []).forEach(function (tool) { active.appendChild(buildToolButton(tool, name)); });
|
|
41
|
+
|
|
40
42
|
// Create buttons with stable IDs and append in registry order (default).
|
|
41
43
|
for (var i = 0; i < palette.tools.length; i++) {
|
|
42
44
|
active.appendChild(buildToolButton(palette.tools[i], name));
|
|
@@ -73,8 +75,11 @@ function buildToolButton(tool, paletteName) {
|
|
|
73
75
|
btn.setAttribute('aria-label', tool.label);
|
|
74
76
|
btn.dataset.toolId = tool.id;
|
|
75
77
|
btn.dataset.palette = paletteName;
|
|
76
|
-
//
|
|
78
|
+
// Ordinary tools can be reordered; fixed tools retain their first slots.
|
|
77
79
|
btn.draggable = true;
|
|
80
|
+
if (tool.fixed) { btn.draggable = false; btn.dataset.fixed = 'true'; }
|
|
81
|
+
if (tool.scope) btn.dataset.scope = tool.scope;
|
|
82
|
+
if (tool.id === 'you-button') { btn.classList.add('you-button'); btn.setAttribute('aria-pressed', 'false'); }
|
|
78
83
|
|
|
79
84
|
var icon = document.createElement('i');
|
|
80
85
|
icon.setAttribute('data-lucide', tool.icon);
|
|
@@ -89,10 +94,12 @@ function buildToolButton(tool, paletteName) {
|
|
|
89
94
|
var count = document.createElement('span');
|
|
90
95
|
count.id = tool.countId;
|
|
91
96
|
count.className = 'sidebar-badge hidden';
|
|
97
|
+
if (tool.id === 'you-button') { count.className = 'sidebar-badge me-unread-badge'; count.hidden = true; }
|
|
92
98
|
btn.appendChild(count);
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
btn.addEventListener('dragstart', function (e) {
|
|
102
|
+
if (tool.fixed) { e.preventDefault(); return; }
|
|
96
103
|
var container = btn.parentNode;
|
|
97
104
|
if (!container || container.id !== PALETTES[paletteName].activeContainerId) {
|
|
98
105
|
e.preventDefault();
|
|
@@ -122,6 +129,7 @@ function getDragAfterElement(container, x, y) {
|
|
|
122
129
|
var closest = null;
|
|
123
130
|
var closestDist = Number.POSITIVE_INFINITY;
|
|
124
131
|
for (var i = 0; i < tiles.length; i++) {
|
|
132
|
+
if (tiles[i].dataset.fixed === 'true') continue;
|
|
125
133
|
var rect = tiles[i].getBoundingClientRect();
|
|
126
134
|
// Pointer is "before" the tile if it's left of the tile's horizontal
|
|
127
135
|
// midpoint on the same row (or on a higher row).
|
|
@@ -164,7 +172,7 @@ function applyPreferences(name, prefs) {
|
|
|
164
172
|
function place(id) {
|
|
165
173
|
if (placed[id]) return;
|
|
166
174
|
var btn = document.getElementById(id);
|
|
167
|
-
if (!btn) return;
|
|
175
|
+
if (!btn || btn.dataset.fixed === 'true') return;
|
|
168
176
|
active.appendChild(btn);
|
|
169
177
|
placed[id] = true;
|
|
170
178
|
}
|
|
@@ -201,7 +209,9 @@ function savePreferences(name) {
|
|
|
201
209
|
|
|
202
210
|
var order = [];
|
|
203
211
|
var activeTiles = active.querySelectorAll('[data-tool-id]');
|
|
204
|
-
for (var i = 0; i < activeTiles.length; i++)
|
|
212
|
+
for (var i = 0; i < activeTiles.length; i++) {
|
|
213
|
+
if (activeTiles[i].dataset.fixed !== 'true') order.push(activeTiles[i].dataset.toolId);
|
|
214
|
+
}
|
|
205
215
|
|
|
206
216
|
// Every tool is visible, so the explicit order is the whole preference.
|
|
207
217
|
fetch('/api/user/tool-palettes', {
|
|
@@ -11,6 +11,7 @@ import { VENDOR_NAMES } from './app-rendering.js';
|
|
|
11
11
|
import { startThinkingSegment, appendThinkingText, stopThinkingSegment, resetThinkingTurn, clearThinkingState, saveThinkingState, restoreThinkingState, updateThinkingTokenEstimate } from './thinking-lifecycle.js';
|
|
12
12
|
import { buildElicitationCard, markElicitationCard, showElicitationError } from './elicitation-form.js';
|
|
13
13
|
import { findAskUserCard, getAskUserRequestKey, hasAskUserRequest, rememberAskUserRequest, markAskUserRequestAnswered, saveAskUserState, restoreAskUserState, resetAskUserState } from './ask-user-state.js';
|
|
14
|
+
import { getProjectAssistantPlaceholder, resolveProjectAssistantIdentity, applyProjectAssistantIdentity } from './project-chat-identity.js';
|
|
14
15
|
|
|
15
16
|
var ctx;
|
|
16
17
|
|
|
@@ -142,11 +143,11 @@ function updateToolGroupHeader(group) {
|
|
|
142
143
|
refreshIcons();
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
// Show group header only when 2+ visible tools (or always in
|
|
146
|
+
// Show group header only when 2+ visible tools (or always in a Mate conversation)
|
|
146
147
|
var header = group.el.querySelector(".tool-group-header");
|
|
147
148
|
var isMate = group.el.classList.contains("mate-tool-group");
|
|
148
149
|
if (isMate) {
|
|
149
|
-
// Mate
|
|
150
|
+
// Mate conversation: hide entire group when no tools, show collapsed when tools exist
|
|
150
151
|
if (group.toolCount === 0) {
|
|
151
152
|
group.el.style.display = "none";
|
|
152
153
|
} else {
|
|
@@ -248,12 +249,13 @@ export function renderAskUserQuestion(toolId, input) {
|
|
|
248
249
|
container.dataset.askUserRequestKey = requestKey;
|
|
249
250
|
rememberAskUserRequest(toolId);
|
|
250
251
|
|
|
251
|
-
// Mate
|
|
252
|
+
// Mate conversation: wrap in avatar + content layout (same as msg-assistant)
|
|
252
253
|
var mateContentWrap = null;
|
|
253
|
-
if (
|
|
254
|
+
if (resolveProjectAssistantIdentity().kind === 'mate') {
|
|
254
255
|
container.classList.add("mate-ask-user");
|
|
255
|
-
var
|
|
256
|
-
var
|
|
256
|
+
var identity = resolveProjectAssistantIdentity();
|
|
257
|
+
var mateName = identity.name;
|
|
258
|
+
var mateAvatar = identity.avatarUrl;
|
|
257
259
|
|
|
258
260
|
var avi = document.createElement("img");
|
|
259
261
|
avi.className = "dm-bubble-avatar";
|
|
@@ -393,7 +395,7 @@ export function renderAskUserQuestion(toolId, input) {
|
|
|
393
395
|
actions.appendChild(skipBtn);
|
|
394
396
|
container.appendChild(actions);
|
|
395
397
|
|
|
396
|
-
// Mate
|
|
398
|
+
// Mate conversation: move all content into the bubble content wrapper
|
|
397
399
|
if (mateContentWrap) {
|
|
398
400
|
var children = [];
|
|
399
401
|
for (var ci = 0; ci < container.childNodes.length; ci++) {
|
|
@@ -420,12 +422,7 @@ export function disableMainInput() {
|
|
|
420
422
|
|
|
421
423
|
export function enableMainInput() {
|
|
422
424
|
ctx.inputEl.disabled = false;
|
|
423
|
-
|
|
424
|
-
ctx.inputEl.placeholder = "Message " + document.body.dataset.mateName + "...";
|
|
425
|
-
} else {
|
|
426
|
-
var _v = store.get('currentVendor') || "claude";
|
|
427
|
-
ctx.inputEl.placeholder = "Message " + (VENDOR_NAMES[_v] || VENDOR_NAMES.claude) + "...";
|
|
428
|
-
}
|
|
425
|
+
ctx.inputEl.placeholder = getProjectAssistantPlaceholder(store.snap(), store.get('currentVendor'));
|
|
429
426
|
}
|
|
430
427
|
|
|
431
428
|
function submitAskUserAnswer(container, toolId, questions, answers, multiSelections) {
|
|
@@ -557,8 +554,8 @@ export function renderPermissionRequest(requestId, toolName, toolInput, decision
|
|
|
557
554
|
return;
|
|
558
555
|
}
|
|
559
556
|
|
|
560
|
-
// Channel layout or Mate
|
|
561
|
-
if ((
|
|
557
|
+
// Channel layout or Mate conversation: conversational "Can I ...?" style
|
|
558
|
+
if (resolveProjectAssistantIdentity().kind === "mate" || getChatLayout() === "channel") {
|
|
562
559
|
renderConversationalPermission(requestId, toolName, toolInput, mateId, vendor);
|
|
563
560
|
applyPermissionHints(pendingPermissions[requestId], hints);
|
|
564
561
|
return;
|
|
@@ -824,10 +821,11 @@ function matePermissionInfo(toolName, toolInput) {
|
|
|
824
821
|
}
|
|
825
822
|
|
|
826
823
|
function resolvePermissionIdentity(mateId, vendor) {
|
|
827
|
-
// Mate
|
|
828
|
-
if (
|
|
829
|
-
var
|
|
830
|
-
var
|
|
824
|
+
// Use the Mate bound to this session, including legacy DM targets.
|
|
825
|
+
if (resolveProjectAssistantIdentity().kind === 'mate') {
|
|
826
|
+
var identity = resolveProjectAssistantIdentity();
|
|
827
|
+
var name = identity.name;
|
|
828
|
+
var avatar = identity.avatarUrl;
|
|
831
829
|
// Override if specific mateId provided (e.g. @mention)
|
|
832
830
|
if (mateId && ctx.getMateById) {
|
|
833
831
|
var mentionMate = ctx.getMateById(mateId);
|
|
@@ -1577,7 +1575,7 @@ function updateTodoSticky() {
|
|
|
1577
1575
|
export function startThinking() {
|
|
1578
1576
|
ctx.finalizeAssistantBlock();
|
|
1579
1577
|
var el = startThinkingSegment();
|
|
1580
|
-
if (
|
|
1578
|
+
if (resolveProjectAssistantIdentity().kind !== 'mate') {
|
|
1581
1579
|
ctx.setActivity("thinking");
|
|
1582
1580
|
}
|
|
1583
1581
|
return el;
|
|
@@ -1605,7 +1603,8 @@ export function createToolItem(id, name) {
|
|
|
1605
1603
|
toolGroupCounter++;
|
|
1606
1604
|
var groupEl = document.createElement("div");
|
|
1607
1605
|
groupEl.className = "tool-group";
|
|
1608
|
-
var
|
|
1606
|
+
var identity = resolveProjectAssistantIdentity();
|
|
1607
|
+
var isMateToolGroup = identity.kind === 'mate';
|
|
1609
1608
|
if (isMateToolGroup) groupEl.classList.add("mate-tool-group");
|
|
1610
1609
|
groupEl.dataset.groupId = "g" + toolGroupCounter;
|
|
1611
1610
|
|
|
@@ -1619,7 +1618,7 @@ export function createToolItem(id, name) {
|
|
|
1619
1618
|
'<div class="tool-group-items"></div>';
|
|
1620
1619
|
|
|
1621
1620
|
if (isMateToolGroup) {
|
|
1622
|
-
var mateAvatar =
|
|
1621
|
+
var mateAvatar = identity.avatarUrl;
|
|
1623
1622
|
groupEl.innerHTML =
|
|
1624
1623
|
'<img class="dm-bubble-avatar dm-bubble-avatar-mate" src="' + escapeHtml(mateAvatar) + '" alt="">' +
|
|
1625
1624
|
'<div class="dm-bubble-content">' + toolGroupInner + '</div>';
|
|
@@ -1627,6 +1626,7 @@ export function createToolItem(id, name) {
|
|
|
1627
1626
|
groupEl.innerHTML = toolGroupInner;
|
|
1628
1627
|
}
|
|
1629
1628
|
|
|
1629
|
+
applyProjectAssistantIdentity(groupEl, identity);
|
|
1630
1630
|
groupEl.querySelector(".tool-group-header").addEventListener("click", function () {
|
|
1631
1631
|
groupEl.classList.toggle("collapsed");
|
|
1632
1632
|
if (currentToolGroup) currentToolGroup.userToggled = true;
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { refreshIcons } from './icons.js';
|
|
2
|
+
import { confirmSettingsAction as showConfirmDialog } from './server-settings-state.js';
|
|
3
|
+
import { store } from './store.js';
|
|
4
|
+
import { getWs } from './ws-ref.js';
|
|
5
|
+
import { showToast } from './utils.js';
|
|
6
|
+
import { getChatLayout, setChatLayout } from './theme.js';
|
|
7
|
+
import { applyTerminalFont } from './terminal-prefs.js';
|
|
8
|
+
import { userAvatarUrl } from './avatar.js';
|
|
9
|
+
import { syncCapsulePreference } from './capsule-preference.js';
|
|
10
|
+
import { getEmailAccountListCache } from './context-sources.js';
|
|
11
|
+
function applyProjectMateDmsClass(enabled) { document.body.classList.toggle('project-mate-dms-disabled', !enabled); }
|
|
12
|
+
export function showPinForm() {
|
|
13
|
+
var pinForm = document.getElementById('us-pin-form');
|
|
14
|
+
if (!pinForm) return;
|
|
15
|
+
if(!pinForm.classList.contains('hidden'))return;
|
|
16
|
+
pinForm.classList.remove('hidden');
|
|
17
|
+
var pinCurrentRow = document.getElementById('us-pin-current-row');
|
|
18
|
+
var pinInput = document.getElementById('us-pin-input');
|
|
19
|
+
var pinConfirmInput = document.getElementById('us-pin-confirm-input');
|
|
20
|
+
var pinCurrentInput = document.getElementById('us-pin-current-input');
|
|
21
|
+
var pinSave = document.getElementById('us-pin-save');
|
|
22
|
+
var pinMsg = document.getElementById('us-pin-msg');
|
|
23
|
+
var pinFormTitle = document.getElementById('us-pin-form-title');
|
|
24
|
+
if (pinFormTitle) {
|
|
25
|
+
pinFormTitle.textContent = store.get('userPinEnabled') ? 'Change PIN' : 'Set PIN';
|
|
26
|
+
}
|
|
27
|
+
if (pinCurrentRow) {
|
|
28
|
+
pinCurrentRow.classList.toggle('hidden', !store.get('userPinEnabled'));
|
|
29
|
+
}
|
|
30
|
+
if (pinInput) {
|
|
31
|
+
pinInput.value = '';
|
|
32
|
+
}
|
|
33
|
+
if (pinConfirmInput) pinConfirmInput.value = '';
|
|
34
|
+
if (pinCurrentInput) pinCurrentInput.value = '';
|
|
35
|
+
if (pinSave) pinSave.disabled = true;
|
|
36
|
+
if (pinSave) pinSave.textContent = 'Save';
|
|
37
|
+
if (pinMsg) {
|
|
38
|
+
pinMsg.textContent = '';
|
|
39
|
+
pinMsg.className = 'settings-hint settings-pin-error hidden';
|
|
40
|
+
}
|
|
41
|
+
if (store.get('userPinEnabled') && pinCurrentInput) {
|
|
42
|
+
pinCurrentInput.focus();
|
|
43
|
+
} else if (pinInput) {
|
|
44
|
+
pinInput.focus();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function hidePinForm(force) {
|
|
49
|
+
var fields = document.querySelectorAll('#us-pin-form input');
|
|
50
|
+
if (!force && Array.from(fields).some(function (input) {return !!input.value;})) {
|
|
51
|
+
showConfirmDialog('Discard the unsaved PIN change?', 'Discard', function () {hidePinForm(true);});return;
|
|
52
|
+
}
|
|
53
|
+
var pinForm = document.getElementById('us-pin-form');
|
|
54
|
+
if (!pinForm) return;
|
|
55
|
+
pinForm.classList.add('hidden');
|
|
56
|
+
var pinCurrentRow = document.getElementById('us-pin-current-row');
|
|
57
|
+
var pinInput = document.getElementById('us-pin-input');
|
|
58
|
+
var pinConfirmInput = document.getElementById('us-pin-confirm-input');
|
|
59
|
+
var pinCurrentInput = document.getElementById('us-pin-current-input');
|
|
60
|
+
var pinSave = document.getElementById('us-pin-save');
|
|
61
|
+
var pinMsg = document.getElementById('us-pin-msg');
|
|
62
|
+
if (pinCurrentRow) {
|
|
63
|
+
pinCurrentRow.classList.remove('hidden');
|
|
64
|
+
}
|
|
65
|
+
if (pinInput) pinInput.value = '';
|
|
66
|
+
if (pinConfirmInput) pinConfirmInput.value = '';
|
|
67
|
+
if (pinCurrentInput) pinCurrentInput.value = '';
|
|
68
|
+
if (pinSave) pinSave.disabled = true;
|
|
69
|
+
if (pinSave) pinSave.textContent = 'Save';
|
|
70
|
+
if (pinMsg) {
|
|
71
|
+
pinMsg.textContent = '';
|
|
72
|
+
pinMsg.className = 'settings-hint settings-pin-error hidden';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// --- Account population ---
|
|
77
|
+
|
|
78
|
+
export function populateAccount() {
|
|
79
|
+
fetch('/api/profile').then(function (r) {
|
|
80
|
+
if (!r.ok) return null;
|
|
81
|
+
return r.json();
|
|
82
|
+
}).then(function (data) {
|
|
83
|
+
if (!data) return;
|
|
84
|
+
var displayName = data.name || data.displayName || data.username || 'Clay User';
|
|
85
|
+
var avatarImg = document.getElementById('us-account-avatar-img');
|
|
86
|
+
var avatarFallback = document.getElementById('us-account-avatar-fallback');
|
|
87
|
+
var accountName = document.getElementById('us-account-name');
|
|
88
|
+
var accountSubline = document.getElementById('us-account-subline');
|
|
89
|
+
var langSelect = document.getElementById('us-lang-select');
|
|
90
|
+
var pinSetBtnEl = document.getElementById('us-pin-set-btn');
|
|
91
|
+
var pinFormTitle = document.getElementById('us-pin-form-title');
|
|
92
|
+
if (accountName) {
|
|
93
|
+
accountName.textContent = displayName;
|
|
94
|
+
}
|
|
95
|
+
if (accountSubline) {
|
|
96
|
+
var parts = [];
|
|
97
|
+
if (data.username) parts.push('@' + data.username);
|
|
98
|
+
if (data.role) parts.push(data.role.charAt(0).toUpperCase() + data.role.slice(1));
|
|
99
|
+
accountSubline.textContent = parts.length ? parts.join(' · ') : 'Local profile';
|
|
100
|
+
}
|
|
101
|
+
store.set({userPinEnabled:!!data.pinEnabled});
|
|
102
|
+
if (pinSetBtnEl) {
|
|
103
|
+
pinSetBtnEl.textContent = store.get('userPinEnabled') ? 'Change PIN' : 'Set PIN';
|
|
104
|
+
}
|
|
105
|
+
if (pinFormTitle) {
|
|
106
|
+
pinFormTitle.textContent = store.get('userPinEnabled') ? 'Change PIN' : 'Set PIN';
|
|
107
|
+
}
|
|
108
|
+
if (langSelect) {
|
|
109
|
+
langSelect.value = store.get('speechLang') || 'en-US';
|
|
110
|
+
langSelect.dispatchEvent(new Event('settings-sync'));
|
|
111
|
+
}
|
|
112
|
+
if (avatarImg && avatarFallback) {
|
|
113
|
+
var avatarUrl = getCurrentUserAvatarSrc(data);
|
|
114
|
+
avatarImg.alt = displayName;
|
|
115
|
+
avatarImg.classList.remove('hidden');
|
|
116
|
+
avatarFallback.classList.remove('hidden');
|
|
117
|
+
avatarFallback.textContent = avatarInitials(displayName);
|
|
118
|
+
avatarImg.onload = function () {
|
|
119
|
+
avatarFallback.classList.add('hidden');
|
|
120
|
+
};
|
|
121
|
+
avatarImg.onerror = function () {
|
|
122
|
+
avatarFallback.classList.remove('hidden');
|
|
123
|
+
};
|
|
124
|
+
avatarImg.src = avatarUrl;
|
|
125
|
+
if (avatarImg.complete && avatarImg.naturalWidth > 0) {
|
|
126
|
+
avatarFallback.classList.add('hidden');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Hide account section in single-user mode (no username)
|
|
130
|
+
var accountNav = document.getElementById('user-settings').querySelector('[data-section="us-account"]');
|
|
131
|
+
if (accountNav) accountNav.style.display = '';
|
|
132
|
+
if (pinSetBtnEl) pinSetBtnEl.disabled = !data.username;
|
|
133
|
+
document.getElementById('us-claude-allow-save').disabled=!data.username;
|
|
134
|
+
if(!data.username)document.getElementById('us-claude-allow-status').textContent='Personal command rules require a signed-in account.';
|
|
135
|
+
// Terminal font: seed in-memory prefs from server. Header-mounted
|
|
136
|
+
// controls (header-tui-font.js) listen to applyTerminalFont and
|
|
137
|
+
// sync their UI - we don't touch any DOM here.
|
|
138
|
+
if (data.terminalFont && typeof data.terminalFont === "object") {
|
|
139
|
+
applyTerminalFont(data.terminalFont.family, data.terminalFont.size);
|
|
140
|
+
}
|
|
141
|
+
// Auto-continue toggle
|
|
142
|
+
var acToggle = document.getElementById('us-auto-continue');
|
|
143
|
+
if (acToggle) acToggle.checked = !!data.autoContinueOnRateLimit;
|
|
144
|
+
// Theme picker self-syncs active selection via theme.js's applyTheme
|
|
145
|
+
// change callbacks (updatePickerActive). Nothing to do here.
|
|
146
|
+
// Layout switcher: sync from server response
|
|
147
|
+
// Sync mate onboarding state from server
|
|
148
|
+
if (data.mateOnboardingShown) {
|
|
149
|
+
store.set({mateOnboardingShown:true});
|
|
150
|
+
}
|
|
151
|
+
// Keep project Mate DM shortcuts available for legacy profiles too.
|
|
152
|
+
var matesOn = true;
|
|
153
|
+
var matesToggle = document.getElementById('us-mates-enabled');
|
|
154
|
+
if (matesToggle) matesToggle.checked = matesOn;
|
|
155
|
+
store.set({ projectMateDmsEnabled: matesOn });
|
|
156
|
+
applyProjectMateDmsClass(matesOn);
|
|
157
|
+
syncCapsulePreference(data.capsulesEnabled === true);
|
|
158
|
+
if (data.chatLayout) {
|
|
159
|
+
setChatLayout(data.chatLayout); // update local cache + CSS
|
|
160
|
+
}
|
|
161
|
+
var lSwitcher = document.getElementById('us-layout-switcher');
|
|
162
|
+
if (lSwitcher) {
|
|
163
|
+
var currentLayout = getChatLayout();
|
|
164
|
+
var lBtns = lSwitcher.querySelectorAll('.layout-option');
|
|
165
|
+
for (var li = 0; li < lBtns.length; li++) {
|
|
166
|
+
lBtns[li].classList.toggle('selected', lBtns[li].dataset.layout === currentLayout);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
store.set({userSettingsProfile:data});
|
|
170
|
+
}).catch(function () {showToast('Could not load your settings. Close and reopen to retry.');});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function avatarInitials(value) {
|
|
174
|
+
var text = (value || '').trim();
|
|
175
|
+
if (!text) return '?';
|
|
176
|
+
var parts = text.split(/\s+/);
|
|
177
|
+
var first = parts[0].charAt(0);
|
|
178
|
+
var second = parts.length > 1 ? parts[1].charAt(0) : parts[0].charAt(1);
|
|
179
|
+
var out = first + (second || '');
|
|
180
|
+
return out.toUpperCase();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function getCurrentUserAvatarSrc(data) {
|
|
184
|
+
var islandAvatar = document.querySelector('.user-island-avatar img');
|
|
185
|
+
if (islandAvatar && islandAvatar.src) return islandAvatar.src;
|
|
186
|
+
if (data && data.avatarCustom) return data.avatarCustom;
|
|
187
|
+
return userAvatarUrl(data, 96);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function savePin(pin) {
|
|
191
|
+
var pinInput = document.getElementById('us-pin-input');
|
|
192
|
+
var pinConfirmInput = document.getElementById('us-pin-confirm-input');
|
|
193
|
+
var pinCurrentInput = document.getElementById('us-pin-current-input');
|
|
194
|
+
var pinSave = document.getElementById('us-pin-save');
|
|
195
|
+
var pinMsg = document.getElementById('us-pin-msg');
|
|
196
|
+
var currentPin = pinCurrentInput ? pinCurrentInput.value.trim() : '';
|
|
197
|
+
var confirmPin = pinConfirmInput ? pinConfirmInput.value.trim() : '';
|
|
198
|
+
var newPin = pinInput ? pinInput.value.trim() : '';
|
|
199
|
+
|
|
200
|
+
if (!/^\d{6}$/.test(newPin)) {
|
|
201
|
+
if (pinMsg) {
|
|
202
|
+
pinMsg.textContent = 'PIN must be exactly 6 digits.';
|
|
203
|
+
pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
|
|
204
|
+
pinMsg.classList.remove('hidden');
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (confirmPin !== newPin) {
|
|
209
|
+
if (pinMsg) {
|
|
210
|
+
pinMsg.textContent = 'PINs do not match.';
|
|
211
|
+
pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
|
|
212
|
+
pinMsg.classList.remove('hidden');
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (store.get('userPinEnabled') && !/^\d{6}$/.test(currentPin)) {
|
|
217
|
+
if (pinMsg) {
|
|
218
|
+
pinMsg.textContent = 'Current PIN is required.';
|
|
219
|
+
pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
|
|
220
|
+
pinMsg.classList.remove('hidden');
|
|
221
|
+
}
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
pinSave.disabled = true;
|
|
226
|
+
pinSave.textContent = 'Saving\u2026';
|
|
227
|
+
|
|
228
|
+
fetch('/api/user/pin', {
|
|
229
|
+
method: 'PUT',
|
|
230
|
+
headers: { 'Content-Type': 'application/json' },
|
|
231
|
+
body: JSON.stringify({ currentPin: currentPin, newPin: newPin }),
|
|
232
|
+
}).then(function (r) { return r.json(); }).then(function (data) {
|
|
233
|
+
if (data.ok) {
|
|
234
|
+
showToast('PIN changed');
|
|
235
|
+
hidePinForm(true);
|
|
236
|
+
store.set({userPinEnabled:true});
|
|
237
|
+
} else {
|
|
238
|
+
pinSave.disabled = false;
|
|
239
|
+
pinSave.textContent = 'Save';
|
|
240
|
+
if (pinMsg) {
|
|
241
|
+
pinMsg.textContent = data.error || 'Could not change your PIN. Please try again.';
|
|
242
|
+
pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
|
|
243
|
+
pinMsg.classList.remove('hidden');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}).catch(function () {
|
|
247
|
+
pinSave.disabled = false;
|
|
248
|
+
pinSave.textContent = 'Save';
|
|
249
|
+
if (pinMsg) {
|
|
250
|
+
pinMsg.textContent = 'Connection lost. Check your network and try again.';
|
|
251
|
+
pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
|
|
252
|
+
pinMsg.classList.remove('hidden');
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// --- Email settings ---
|
|
258
|
+
|
|
259
|
+
export function renderEmailSettings() {
|
|
260
|
+
var listEl = document.getElementById('us-email-list');
|
|
261
|
+
if (!listEl) return;
|
|
262
|
+
listEl.innerHTML = '';
|
|
263
|
+
|
|
264
|
+
var accounts = getEmailAccountListCache();
|
|
265
|
+
if (!accounts || accounts.length === 0) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
for (var i = 0; i < accounts.length; i++) {
|
|
270
|
+
var acc = accounts[i];
|
|
271
|
+
var row = document.createElement('div');
|
|
272
|
+
row.className = 'us-integration-email-row';
|
|
273
|
+
row.innerHTML =
|
|
274
|
+
'<div class="us-voice-row-controls"><div class="us-integration-brand">' + providerIcon(acc.provider) + '</div>' +
|
|
275
|
+
'<div class="us-voice-provider-copy"><strong>' + escHtml(acc.email) + '</strong><small>Email · ' + escHtml(({gmail:'Gmail',outlook:'Outlook Mail',yahoo:'Yahoo Mail',custom:'Custom email'})[acc.provider] || 'Custom email') + (acc.label ? ' · ' + escHtml(acc.label) : '') + '</small></div>' +
|
|
276
|
+
'<span class="us-integration-state">Account saved</span><button type="button" class="settings-btn-sm" aria-expanded="false">Manage</button></div>' +
|
|
277
|
+
'<div class="us-email-manage" hidden><p class="settings-hint">This personal mailbox can be used to read, search and send email. Disconnecting keeps your mail at the provider.</p><button type="button" class="settings-btn-sm" data-account-id="' + escHtml(acc.id) + '">Disconnect</button></div>';
|
|
278
|
+
var manageBtn = row.querySelector('[aria-expanded]');
|
|
279
|
+
manageBtn.setAttribute('aria-label', 'Manage ' + acc.email);
|
|
280
|
+
manageBtn.addEventListener('click', function () {
|
|
281
|
+
var panel = this.closest('.us-integration-email-row').querySelector('.us-email-manage');
|
|
282
|
+
panel.hidden = !panel.hidden; this.setAttribute('aria-expanded', String(!panel.hidden));
|
|
283
|
+
});
|
|
284
|
+
var removeBtn = row.querySelector('[data-account-id]');
|
|
285
|
+
removeBtn.addEventListener('click', function () {
|
|
286
|
+
var accountId = this.getAttribute('data-account-id');
|
|
287
|
+
showConfirmDialog('Disconnect this personal mailbox from Clay? Existing mail at the provider is kept.', 'Disconnect', function () {
|
|
288
|
+
var ws=getWs();
|
|
289
|
+
if(ws && ws.readyState===1)ws.send(JSON.stringify({type:'email_account_remove',accountId:accountId}));
|
|
290
|
+
else showToast('Disconnected from Clay. Reconnect and retry.');
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
listEl.appendChild(row);
|
|
295
|
+
}
|
|
296
|
+
refreshIcons();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
var PROVIDER_ICON_PATHS = {
|
|
301
|
+
gmail: '/icons/email/gmail.svg',
|
|
302
|
+
outlook: '/icons/email/outlook.svg',
|
|
303
|
+
yahoo: '/icons/email/yahoo.svg',
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
function providerIcon(provider) {
|
|
307
|
+
var src = PROVIDER_ICON_PATHS[provider];
|
|
308
|
+
if (src) {
|
|
309
|
+
return '<img src="' + src + '" class="us-email-provider-icon" alt="' + provider + '">';
|
|
310
|
+
}
|
|
311
|
+
return '<i data-lucide="mail" class="us-email-provider-icon-fallback"></i>';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function escHtml(str) {
|
|
315
|
+
var d = document.createElement('div');
|
|
316
|
+
d.textContent = str;
|
|
317
|
+
return d.innerHTML;
|
|
318
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { initPersonalThemes } from './user-settings-themes.js';
|
|
3
|
+
import { TERMINAL_FONT_OPTIONS, getTerminalFontFamily, getTerminalFontSize, saveTerminalFont, onTerminalFontChange } from './terminal-prefs.js';
|
|
4
|
+
import { enhanceSettingsSelects } from './settings-select.js';
|
|
5
|
+
|
|
6
|
+
export function initPersonalAppearance() {
|
|
7
|
+
var theme = document.getElementById('us-theme-picker').closest('.settings-card');
|
|
8
|
+
initPersonalThemes(theme);
|
|
9
|
+
|
|
10
|
+
var layout = document.getElementById('us-layout-switcher');
|
|
11
|
+
layout.closest('.settings-card').classList.add('us-layout-group');
|
|
12
|
+
layout.setAttribute('role', 'group'); layout.setAttribute('aria-label', 'Conversation layout');
|
|
13
|
+
layout.querySelectorAll('button').forEach(function (button) {
|
|
14
|
+
var bubble = button.dataset.layout === 'bubble';
|
|
15
|
+
button.querySelector('.layout-option-desc').textContent = bubble ? 'Centered messages in separate bubbles.' : 'Full-width conversation with avatars.';
|
|
16
|
+
var preview = document.createElement('span'); preview.className = 'us-layout-preview us-layout-preview-' + button.dataset.layout; preview.setAttribute('aria-hidden', 'true');
|
|
17
|
+
preview.innerHTML = '<i></i><i></i><i></i>'; button.prepend(preview);
|
|
18
|
+
});
|
|
19
|
+
layout.querySelectorAll('button').forEach(function (button) { button.setAttribute('aria-pressed', button.classList.contains('selected')); });
|
|
20
|
+
addTerminalTypography(theme.closest('.us-section'));
|
|
21
|
+
enhanceSettingsSelects(theme);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function addTerminalTypography(section) {
|
|
25
|
+
var content = document.createElement('section'); content.className = 'us-typography settings-card';
|
|
26
|
+
content.innerHTML = '<h3>Terminal typography</h3><p class="settings-hint">Used by existing and new terminals. Also available from the terminal toolbar.</p><div class="us-font-controls"><div><label class="settings-label" for="us-terminal-family">Font</label><select id="us-terminal-family" class="settings-select"></select></div><div><label class="settings-label" for="us-terminal-size">Size</label><div class="us-font-size"><input id="us-terminal-size" class="us-text-input" type="number" min="9" max="32" step="1" aria-label="Terminal font size"><span>px</span></div></div></div><div class="us-font-preview" aria-hidden="true">Aa Bb 0123 · ~/projects/clay</div><p class="settings-status" role="status" id="us-terminal-status"></p><button type="button" class="settings-btn-sm" id="us-terminal-retry" hidden>Retry save</button>';
|
|
27
|
+
section.appendChild(content);
|
|
28
|
+
var family = content.querySelector('select'); var size = content.querySelector('input');
|
|
29
|
+
var preview = content.querySelector('.us-font-preview'); var status = content.querySelector('[role=status]'); var retry = content.querySelector('#us-terminal-retry');
|
|
30
|
+
TERMINAL_FONT_OPTIONS.forEach(function (font) {
|
|
31
|
+
var option = document.createElement('option'); option.value = font.family; option.textContent = font.label; family.appendChild(option);
|
|
32
|
+
});
|
|
33
|
+
function sync() {
|
|
34
|
+
if (store.get('userTerminalDirty')) return;
|
|
35
|
+
var value = getTerminalFontFamily();
|
|
36
|
+
if (!Array.from(family.options).some(function (option) { return option.value === value; })) {
|
|
37
|
+
var option = document.createElement('option'); option.value = value; option.textContent = 'Custom font'; family.appendChild(option);
|
|
38
|
+
}
|
|
39
|
+
family.value = value; size.value = getTerminalFontSize();
|
|
40
|
+
preview.style.fontFamily = value; preview.style.fontSize = size.value + 'px';
|
|
41
|
+
family.dispatchEvent(new Event('settings-sync'));
|
|
42
|
+
}
|
|
43
|
+
async function save() {
|
|
44
|
+
var value = Number(size.value); store.set({ userTerminalDirty: true });
|
|
45
|
+
if (!Number.isInteger(value) || value < 9 || value > 32) { status.textContent = 'Choose a whole size from 9 to 32.'; status.dataset.state = 'error'; return; }
|
|
46
|
+
family.disabled = true; size.disabled = true; retry.hidden = true; status.textContent = 'Saving…'; status.dataset.state = '';
|
|
47
|
+
try {
|
|
48
|
+
await saveTerminalFont(family.value, value); store.set({ userTerminalDirty: false }); sync(); status.textContent = 'Saved.';
|
|
49
|
+
} catch (error) { status.textContent = error.message; status.dataset.state = 'error'; retry.hidden = false; }
|
|
50
|
+
finally { family.disabled = false; size.disabled = false; family.dispatchEvent(new Event('settings-sync')); }
|
|
51
|
+
}
|
|
52
|
+
family.onchange = save; size.onchange = save; retry.onclick = save;
|
|
53
|
+
size.oninput = function () { store.set({ userTerminalDirty: true }); };
|
|
54
|
+
onTerminalFontChange(sync); sync(); enhanceSettingsSelects(content);
|
|
55
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { confirmSettingsAction as showConfirmDialog } from './server-settings-state.js';
|
|
3
|
+
|
|
4
|
+
function wrapEditor(content, title, summary) {
|
|
5
|
+
var group=document.createElement('section');group.className='us-config-group';
|
|
6
|
+
content.before(group);
|
|
7
|
+
var heading=document.createElement('div');heading.className='us-editor-heading';
|
|
8
|
+
var label=document.createElement('h3');label.textContent=title;
|
|
9
|
+
var edit=document.createElement('button');edit.type='button';edit.className='settings-btn-sm';edit.textContent='Edit';
|
|
10
|
+
var state=document.createElement('p');state.className='us-editor-summary';state.textContent=summary();
|
|
11
|
+
heading.append(label,edit);group.append(heading,state);
|
|
12
|
+
var help=content.querySelector('.settings-hint');var copy=null;if(help && title!=='Personal allow rules'){copy=help.cloneNode(true);group.appendChild(copy);}
|
|
13
|
+
group.appendChild(content);content.hidden=true;
|
|
14
|
+
store.subscribe(function(next,previous){if(next.userSettingsProfile!==previous.userSettingsProfile)state.textContent=summary();});
|
|
15
|
+
var done=document.createElement('button');done.type='button';done.className='settings-btn-sm';done.textContent='Done';content.appendChild(done);
|
|
16
|
+
edit.onclick=function(){if(copy)copy.hidden=true;content.hidden=false;state.hidden=true;edit.hidden=true;var first=content.querySelector('button,input,textarea,select');if(first)first.focus();};
|
|
17
|
+
done.onclick=function(){if(copy)copy.hidden=false;content.hidden=true;state.hidden=false;edit.hidden=false;state.textContent=summary();edit.focus();};
|
|
18
|
+
return {group:group,state:state,edit:edit,done:done,content:content};
|
|
19
|
+
}
|
|
20
|
+
export function initPersonalEditors() {
|
|
21
|
+
var allow=document.getElementById('us-claude-allow-list');var permission=wrapEditor(allow.closest('.settings-card'),'Personal allow rules',function(){var count=(store.get('userAllowSaved') || []).length;return count+' personal rules. Managed defaults apply separately.';});
|
|
22
|
+
permission.done.textContent='Cancel';permission.done.onclick=function(){function cancel(){allow.value=(store.get('userAllowSaved') || []).join('\n');store.set({userAllowDirty:false});permission.content.hidden=true;permission.state.hidden=false;permission.edit.hidden=false;permission.edit.focus();}if(store.get('userAllowDirty'))showConfirmDialog('Discard unsaved command rules?', 'Discard',cancel);else cancel();};
|
|
23
|
+
store.subscribe(function(state,previous){if(state.userAllowSaved!==previous.userAllowSaved){permission.state.textContent=(state.userAllowSaved || []).length+' personal rules. Managed defaults apply separately.';if(previous.userAllowDirty && !state.userAllowDirty){permission.content.hidden=true;permission.state.hidden=false;permission.edit.hidden=false;permission.edit.focus();}}});
|
|
24
|
+
}
|