clay-server 5.1.0-beta.1 → 6.0.0-beta.1
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/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-knowledge-mcp-server.js +29 -6
- package/lib/mate-knowledge-service.js +23 -2
- package/lib/mate-knowledge-sync.js +36 -21
- package/lib/mate-workspace-color.js +4 -0
- package/lib/mates.js +18 -0
- 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 +1 -0
- 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 +2 -2
- 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 +59 -18
- 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/messages.css +1 -114
- package/lib/public/css/mobile-nav.css +16 -16
- package/lib/public/css/overlays.css +8 -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 +2 -0
- 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 +203 -547
- 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 +25 -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 +10 -7
- 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 +12 -3
- 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 +56 -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 +34 -9
- package/lib/startup-projects.js +40 -0
- package/lib/storage-usage.js +127 -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/adapters/codex.js +8 -45
- package/lib/yoke/codex-model-catalog.js +40 -0
- package/lib/yoke/codex-rate-limits.js +26 -0
- package/package.json +12 -3
|
@@ -12,7 +12,6 @@ import { dismissOverlayPanels, closeSidebar, updatePageTitle, spawnDustParticles
|
|
|
12
12
|
import { showConfirm } from './app-misc.js';
|
|
13
13
|
import { getUpcomingSchedules } from './scheduler.js';
|
|
14
14
|
import { refreshMobileChatSheet } from './sidebar-mobile.js';
|
|
15
|
-
import { VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
16
15
|
import { openGroup, openWorkerWithDriver, separateGroup } from './split-view.js';
|
|
17
16
|
import { groupedSessionIds } from './split-group-helpers.js';
|
|
18
17
|
import { resolvePreferredVendor } from './default-vendor.js';
|
|
@@ -24,6 +23,7 @@ import { renderFolderToolbar, captureFolderInputs, folderToolbarNeedsRender } fr
|
|
|
24
23
|
import { captureFolderCollapseAnimations } from './session-folder-collapse.js';
|
|
25
24
|
import { getListSearchQuery, getListSearchMatchIds, applyListSearchResults, filterItemsBySearch } from './session-list-search.js';
|
|
26
25
|
import { expectCreatedSession } from './session-create.js';
|
|
26
|
+
import { isMateWorkspace } from './project-mate-navigation.js';
|
|
27
27
|
import { describeSessionDelete } from './session-delete-scope.js';
|
|
28
28
|
import { renderSessionPresence, setSessionPresence } from './session-presence-ui.js';
|
|
29
29
|
import {
|
|
@@ -31,6 +31,9 @@ import {
|
|
|
31
31
|
renderDesktopDriverHierarchy,
|
|
32
32
|
renderDesktopOrphanHierarchy
|
|
33
33
|
} from './sidebar-session-hierarchy.js';
|
|
34
|
+
import { ordinarySessionListSessions } from './session-list-visibility.js';
|
|
35
|
+
import { formatSessionActivity } from './session-activity-tooltip.js';
|
|
36
|
+
import { bindSessionTitleMarquee } from './session-title-marquee.js';
|
|
34
37
|
import { summarizeSidebarItems } from './sidebar-session-counts.js';
|
|
35
38
|
import {
|
|
36
39
|
cancelSessionInlineRename,
|
|
@@ -272,7 +275,7 @@ function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData) {
|
|
|
272
275
|
|
|
273
276
|
|
|
274
277
|
// Session visibility toggle (only the session owner can change)
|
|
275
|
-
if (store.get('isMultiUserMode') && sessionData && sessionData.ownerId && sessionData.ownerId === store.get('myUserId')) {
|
|
278
|
+
if (!isMateWorkspace(store.snap()) && store.get('isMultiUserMode') && sessionData && !sessionData.isMate && sessionData.ownerId && sessionData.ownerId === store.get('myUserId')) {
|
|
276
279
|
var currentVis = (sessionData && sessionData.sessionVisibility) || "private";
|
|
277
280
|
var isPrivate = currentVis === "private";
|
|
278
281
|
var visItem = document.createElement("button");
|
|
@@ -514,6 +517,17 @@ function isSessionVisibleBySearch(sessionId) {
|
|
|
514
517
|
return getListSearchMatchIds().has(sessionId);
|
|
515
518
|
}
|
|
516
519
|
|
|
520
|
+
function appendSessionTitleLine(el, textSpan) {
|
|
521
|
+
var processingDot = textSpan.querySelector('.session-processing');
|
|
522
|
+
if (processingDot) processingDot.remove();
|
|
523
|
+
var titleLine = document.createElement('span');
|
|
524
|
+
titleLine.className = 'session-item-title-line';
|
|
525
|
+
titleLine.appendChild(textSpan);
|
|
526
|
+
if (processingDot) titleLine.appendChild(processingDot);
|
|
527
|
+
el.appendChild(titleLine);
|
|
528
|
+
return titleLine;
|
|
529
|
+
}
|
|
530
|
+
|
|
517
531
|
// --- Loop child / run / group rendering ---
|
|
518
532
|
|
|
519
533
|
function renderLoopChild(s) {
|
|
@@ -539,7 +553,7 @@ function renderLoopChild(s) {
|
|
|
539
553
|
textHtml += '<span class="session-loop-role-badge' + roleCls + '">' + roleName + iterSuffix + '</span>';
|
|
540
554
|
}
|
|
541
555
|
textSpan.innerHTML = textHtml;
|
|
542
|
-
el
|
|
556
|
+
appendSessionTitleLine(el, textSpan);
|
|
543
557
|
appendSessionCloseButton(el, s);
|
|
544
558
|
|
|
545
559
|
el.addEventListener("click", (function (id) {
|
|
@@ -665,7 +679,7 @@ function renderLoopGroup(loopId, children, groupKey) {
|
|
|
665
679
|
textHtml += '<span class="session-loop-count' + countClass + '">' + countLabel + '</span>';
|
|
666
680
|
}
|
|
667
681
|
textSpan.innerHTML = textHtml;
|
|
668
|
-
el
|
|
682
|
+
appendSessionTitleLine(el, textSpan);
|
|
669
683
|
|
|
670
684
|
// More button (ellipsis)
|
|
671
685
|
var moreBtn = document.createElement("button");
|
|
@@ -768,7 +782,7 @@ function renderLoopRun(parentGk, startedAtKey, sessions, isRalph) {
|
|
|
768
782
|
textHtml += '<span class="session-loop-run-time">' + escapeHtml(timeLabel) + '</span>';
|
|
769
783
|
textHtml += '<span class="session-loop-count' + (isRalph ? "" : " scheduled") + '">' + sessions.length + '</span>';
|
|
770
784
|
textSpan.innerHTML = textHtml;
|
|
771
|
-
el
|
|
785
|
+
appendSessionTitleLine(el, textSpan);
|
|
772
786
|
|
|
773
787
|
// Click row -> switch to latest session of this run
|
|
774
788
|
el.addEventListener("click", (function (id) {
|
|
@@ -799,26 +813,24 @@ function renderLoopRun(parentGk, startedAtKey, sessions, isRalph) {
|
|
|
799
813
|
|
|
800
814
|
function renderSessionItem(s, options) {
|
|
801
815
|
var itemOptions = options || {};
|
|
816
|
+
var displaySession = s;
|
|
817
|
+
if (itemOptions.promotedActive || itemOptions.promotedProcessing || itemOptions.promotedUnread !== undefined) {
|
|
818
|
+
displaySession = Object.assign({}, s, {
|
|
819
|
+
active: !!(s.active || itemOptions.promotedActive),
|
|
820
|
+
isProcessing: !!(s.isProcessing || itemOptions.promotedProcessing),
|
|
821
|
+
unread: (s.unread || 0) + (itemOptions.promotedUnread || 0),
|
|
822
|
+
});
|
|
823
|
+
}
|
|
802
824
|
var el = document.createElement("div");
|
|
803
825
|
var isMatch = getListSearchMatchIds() !== null && getListSearchMatchIds().has(s.id);
|
|
804
|
-
el.className = "session-item" + (
|
|
826
|
+
el.className = "session-item" + (displaySession.active ? " active" : "") + (isMatch ? " search-match" : "");
|
|
805
827
|
if (itemOptions.worker) el.classList.add("session-worker-item");
|
|
806
828
|
el.dataset.sessionId = s.id;
|
|
807
829
|
|
|
808
|
-
// Keep agent identity separate from the title so each row has a stable
|
|
809
|
-
// visual anchor instead of mixing a tiny inline logo into the text.
|
|
810
|
-
var itemVendor = s.vendor || "claude";
|
|
811
|
-
var vendorIcon = document.createElement("img");
|
|
812
|
-
vendorIcon.className = "session-vendor-icon session-vendor-mark";
|
|
813
|
-
vendorIcon.src = VENDOR_AVATARS[itemVendor] || VENDOR_AVATARS.claude;
|
|
814
|
-
vendorIcon.alt = "";
|
|
815
|
-
vendorIcon.title = VENDOR_NAMES[itemVendor] || itemVendor;
|
|
816
|
-
el.appendChild(vendorIcon);
|
|
817
|
-
|
|
818
830
|
var textSpan = document.createElement("span");
|
|
819
831
|
textSpan.className = "session-item-text";
|
|
820
832
|
var textHtml = "";
|
|
821
|
-
if (
|
|
833
|
+
if (displaySession.isProcessing) {
|
|
822
834
|
textHtml += '<span class="session-processing"></span>';
|
|
823
835
|
}
|
|
824
836
|
if (s.backgroundTaskCount) {
|
|
@@ -832,8 +844,12 @@ function renderSessionItem(s, options) {
|
|
|
832
844
|
}
|
|
833
845
|
textHtml += '<span class="session-item-title">' + highlightMatch(s.title || "New Session", getListSearchQuery()) + '</span>';
|
|
834
846
|
textSpan.innerHTML = textHtml;
|
|
835
|
-
|
|
836
|
-
|
|
847
|
+
textSpan.setAttribute("aria-label", s.title || "New Session");
|
|
848
|
+
var titleEl = textSpan.querySelector(".session-item-title");
|
|
849
|
+
var activityTitle = formatSessionActivity(s.lastActivity);
|
|
850
|
+
if (titleEl && activityTitle) titleEl.title = activityTitle;
|
|
851
|
+
var titleLine = appendSessionTitleLine(el, textSpan);
|
|
852
|
+
appendGithubWork(el, s, titleLine);
|
|
837
853
|
|
|
838
854
|
if (itemOptions.worker) {
|
|
839
855
|
var workerLabel = document.createElement("span");
|
|
@@ -844,13 +860,6 @@ function renderSessionItem(s, options) {
|
|
|
844
860
|
|
|
845
861
|
var trailing = document.createElement("span");
|
|
846
862
|
trailing.className = "session-row-trailing";
|
|
847
|
-
var ageText = relativeTime(s.lastActivity || 0);
|
|
848
|
-
if (ageText) {
|
|
849
|
-
var age = document.createElement("span");
|
|
850
|
-
age.className = "session-item-age";
|
|
851
|
-
age.textContent = ageText;
|
|
852
|
-
trailing.appendChild(age);
|
|
853
|
-
}
|
|
854
863
|
|
|
855
864
|
// Right-click / long-press: context menu
|
|
856
865
|
el.addEventListener("contextmenu", (function(id, title, cliSid, anchor, sData) {
|
|
@@ -865,8 +874,8 @@ function renderSessionItem(s, options) {
|
|
|
865
874
|
var unreadBadge = document.createElement("span");
|
|
866
875
|
unreadBadge.className = "session-unread-badge";
|
|
867
876
|
unreadBadge.dataset.sessionId = s.id;
|
|
868
|
-
if (
|
|
869
|
-
unreadBadge.textContent =
|
|
877
|
+
if (displaySession.unread > 0) {
|
|
878
|
+
unreadBadge.textContent = displaySession.unread > 99 ? "99+" : String(displaySession.unread);
|
|
870
879
|
unreadBadge.classList.add("has-unread");
|
|
871
880
|
}
|
|
872
881
|
el.appendChild(unreadBadge);
|
|
@@ -877,6 +886,7 @@ function renderSessionItem(s, options) {
|
|
|
877
886
|
if (isFavoriteSession(s)) el.classList.add("is-favorite");
|
|
878
887
|
}
|
|
879
888
|
appendSessionCloseButton(actions, s);
|
|
889
|
+
el.style.setProperty("--session-actions-width", (actions.childNodes.length * 18 + Math.max(0, actions.childNodes.length - 1) * 4 + 21) + "px");
|
|
880
890
|
if (actions.childNodes.length) trailing.appendChild(actions);
|
|
881
891
|
if (trailing.childNodes.length) el.appendChild(trailing);
|
|
882
892
|
|
|
@@ -898,6 +908,7 @@ function renderSessionItem(s, options) {
|
|
|
898
908
|
|
|
899
909
|
// Presence avatars (multi-user)
|
|
900
910
|
renderSessionPresence(el, String(s.id));
|
|
911
|
+
bindSessionTitleMarquee(el, textSpan.querySelector(".session-item-title"), actions);
|
|
901
912
|
if (!itemOptions.worker) setupFolderDrag(el, s);
|
|
902
913
|
|
|
903
914
|
return el;
|
|
@@ -911,14 +922,7 @@ function renderSplitGroupItem(group, members) {
|
|
|
911
922
|
|
|
912
923
|
var text = document.createElement("span");
|
|
913
924
|
text.className = "session-item-text split-group-text";
|
|
914
|
-
var html = '<span class="split-group-glyph" aria-hidden="true">' + iconHtml("columns-2") + '</span
|
|
915
|
-
for (var i = 0; i < members.length; i++) {
|
|
916
|
-
var vendor = members[i].vendor || "claude";
|
|
917
|
-
html += '<img class="session-vendor-icon split-group-vendor" src="' +
|
|
918
|
-
(VENDOR_AVATARS[vendor] || VENDOR_AVATARS.claude) + '" alt="" title="' +
|
|
919
|
-
escapeHtml(VENDOR_NAMES[vendor] || vendor) + '">';
|
|
920
|
-
}
|
|
921
|
-
html += "</span>";
|
|
925
|
+
var html = '<span class="split-group-glyph" aria-hidden="true">' + iconHtml("columns-2") + '</span>';
|
|
922
926
|
var splitProcessing = false;
|
|
923
927
|
var splitBackgroundTaskCount = 0;
|
|
924
928
|
for (var si = 0; si < members.length; si++) {
|
|
@@ -929,7 +933,7 @@ function renderSplitGroupItem(group, members) {
|
|
|
929
933
|
if (splitBackgroundTaskCount) html += '<span class="session-background-task-count">' + splitBackgroundTaskCount + '</span>';
|
|
930
934
|
html += '<span class="split-group-name">' + highlightMatch(group.name || "Split", getListSearchQuery()) + "</span>";
|
|
931
935
|
text.innerHTML = html;
|
|
932
|
-
el
|
|
936
|
+
appendSessionTitleLine(el, text);
|
|
933
937
|
|
|
934
938
|
var unread = 0;
|
|
935
939
|
for (var ui = 0; ui < members.length; ui++) unread += members[ui].unread || 0;
|
|
@@ -938,7 +942,6 @@ function renderSplitGroupItem(group, members) {
|
|
|
938
942
|
badge.dataset.splitGroupId = group.id;
|
|
939
943
|
if (unread > 0) badge.textContent = unread > 99 ? "99+" : String(unread);
|
|
940
944
|
el.appendChild(badge);
|
|
941
|
-
|
|
942
945
|
el.addEventListener("click", function () {
|
|
943
946
|
if (openGroup(group)) {
|
|
944
947
|
dismissOverlayPanels();
|
|
@@ -994,22 +997,23 @@ export function renderSessionList(sessions) {
|
|
|
994
997
|
|
|
995
998
|
// Partition: loop sessions vs normal sessions
|
|
996
999
|
// Group by loopId + date so all runs of the same task on the same day are merged
|
|
1000
|
+
var visibleSessions = ordinarySessionListSessions(cachedSessions);
|
|
997
1001
|
var splitGroups = store.get('splitGroups') || [];
|
|
998
1002
|
var groupedIds = groupedSessionIds(splitGroups);
|
|
999
|
-
var hierarchy = prepareSidebarHierarchy(
|
|
1003
|
+
var hierarchy = prepareSidebarHierarchy(visibleSessions);
|
|
1000
1004
|
var hierarchyByDriver = hierarchy.byDriver;
|
|
1001
1005
|
var hierarchySessionIds = hierarchy.sessionIds;
|
|
1002
1006
|
hierarchySessionIds.forEach(function (id) { groupedIds.delete(id); });
|
|
1003
1007
|
var sessionsById = new Map();
|
|
1004
|
-
for (var si = 0; si <
|
|
1008
|
+
for (var si = 0; si < visibleSessions.length; si++) sessionsById.set(visibleSessions[si].id, visibleSessions[si]);
|
|
1005
1009
|
var loopGroups = {}; // groupKey -> [sessions]
|
|
1006
1010
|
var normalSessions = [];
|
|
1007
|
-
for (var i = 0; i <
|
|
1008
|
-
var s =
|
|
1011
|
+
for (var i = 0; i < visibleSessions.length; i++) {
|
|
1012
|
+
var s = visibleSessions[i];
|
|
1009
1013
|
if (s.sessionRole === "worker") continue;
|
|
1010
1014
|
if (groupedIds.has(s.id)) continue;
|
|
1011
|
-
if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph"
|
|
1012
|
-
// Task crafting sessions live in the scheduler calendar, not the main list
|
|
1015
|
+
if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph") {
|
|
1016
|
+
// Task crafting sessions live in the scheduler calendar, not the main list.
|
|
1013
1017
|
continue;
|
|
1014
1018
|
} else if (s.loop && s.loop.loopId) {
|
|
1015
1019
|
var startedAt = s.loop.startedAt || 0;
|
|
@@ -1029,11 +1033,6 @@ export function renderSessionList(sessions) {
|
|
|
1029
1033
|
if (driverRoot) items.push({ type: "driver-hierarchy", root: driverRoot, data: driverRoot.driver, lastActivity: driverRoot.lastActivity });
|
|
1030
1034
|
else items.push({ type: "session", data: normalSessions[j], lastActivity: normalSessions[j].lastActivity || 0 });
|
|
1031
1035
|
}
|
|
1032
|
-
if (hierarchy.orphans.length) {
|
|
1033
|
-
var orphanActivity = 0;
|
|
1034
|
-
for (var oi = 0; oi < hierarchy.orphans.length; oi++) orphanActivity = Math.max(orphanActivity, hierarchy.orphans[oi].lastActivity || 0);
|
|
1035
|
-
items.push({ type: "orphan-workers", workers: hierarchy.orphans, lastActivity: orphanActivity });
|
|
1036
|
-
}
|
|
1037
1036
|
for (var sg = 0; sg < splitGroups.length; sg++) {
|
|
1038
1037
|
var splitGroup = splitGroups[sg];
|
|
1039
1038
|
var splitMembers = [];
|
|
@@ -1083,7 +1082,7 @@ export function renderSessionList(sessions) {
|
|
|
1083
1082
|
if (it.type === "loop") return renderLoopGroup(it.loopId, it.children, it.groupKey) || document.createElement("div");
|
|
1084
1083
|
if (it.type === "split-group") return renderSplitGroupItem(it.group, it.members);
|
|
1085
1084
|
if (it.type === "driver-hierarchy") return renderDesktopDriverHierarchy(it.root, renderSessionItem, rerender, matchIds);
|
|
1086
|
-
if (it.type === "orphan-workers") return
|
|
1085
|
+
if (it.type === "orphan-workers") return document.createElement("div");
|
|
1087
1086
|
return renderSessionItem(it.data);
|
|
1088
1087
|
},
|
|
1089
1088
|
};
|
|
@@ -62,6 +62,14 @@ export function closeSidebar() {
|
|
|
62
62
|
ctx.sidebarOverlay.classList.remove("visible");
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export function toggleSidebarCollapse() {
|
|
66
|
+
if (!ctx) return;
|
|
67
|
+
var layout = ctx.$("layout");
|
|
68
|
+
var collapsed = layout.classList.toggle("sidebar-collapsed");
|
|
69
|
+
try { localStorage.setItem("sidebar-collapsed", collapsed ? "1" : ""); } catch (e) {}
|
|
70
|
+
setTimeout(function () { syncResizeHandles(); }, 210);
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
export function initSidebar(_ctx) {
|
|
66
74
|
ctx = _ctx;
|
|
67
75
|
|
|
@@ -87,13 +95,6 @@ export function initSidebar(_ctx) {
|
|
|
87
95
|
ctx.sidebarOverlay.addEventListener("click", closeSidebar);
|
|
88
96
|
|
|
89
97
|
// --- Desktop sidebar collapse/expand ---
|
|
90
|
-
function toggleSidebarCollapse() {
|
|
91
|
-
var layout = ctx.$("layout");
|
|
92
|
-
var collapsed = layout.classList.toggle("sidebar-collapsed");
|
|
93
|
-
try { localStorage.setItem("sidebar-collapsed", collapsed ? "1" : ""); } catch (e) {}
|
|
94
|
-
setTimeout(function () { syncUserIslandWidth(); syncResizeHandle(); }, 210);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
98
|
if (ctx.sidebarToggleBtn) ctx.sidebarToggleBtn.addEventListener("click", toggleSidebarCollapse);
|
|
98
99
|
if (ctx.sidebarExpandBtn) ctx.sidebarExpandBtn.addEventListener("click", toggleSidebarCollapse);
|
|
99
100
|
var mateSidebarToggle = document.getElementById("mate-sidebar-toggle-btn");
|
|
@@ -245,12 +246,9 @@ export function initSidebar(_ctx) {
|
|
|
245
246
|
|
|
246
247
|
function syncUserIslandWidth() {
|
|
247
248
|
if (!userIsland) return;
|
|
248
|
-
var
|
|
249
|
-
var
|
|
250
|
-
|
|
251
|
-
if (!col) return;
|
|
252
|
-
var rect = col.getBoundingClientRect();
|
|
253
|
-
userIsland.style.width = (rect.right - 8 - 8) + "px";
|
|
249
|
+
var iconStrip = document.getElementById("icon-strip");
|
|
250
|
+
var stripWidth = iconStrip ? iconStrip.getBoundingClientRect().width : 0;
|
|
251
|
+
userIsland.style.width = (stripWidth > 0 ? stripWidth : 72) + "px";
|
|
254
252
|
}
|
|
255
253
|
|
|
256
254
|
// --- Sidebar resize handle ---
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { confirmSettingsAction as showConfirmDialog } from './server-settings-state.js';
|
|
2
|
+
import { enhanceSettingsSelects } from './settings-select.js';
|
|
1
3
|
import { store } from './store.js';
|
|
2
4
|
import { escapeHtml } from './utils.js';
|
|
3
5
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
@@ -5,7 +7,10 @@ import { speechLanguages, voiceModel, loadSpeechSettings, saveSpeechSettings, sa
|
|
|
5
7
|
import { stopSTT } from './stt-controller.js';
|
|
6
8
|
import { browserSpeechInfo, compareSpeechModels } from './stt-browser.js';
|
|
7
9
|
|
|
8
|
-
export function closeSpeechPicker() {
|
|
10
|
+
export function closeSpeechPicker(force) {
|
|
11
|
+
if(document.querySelector('.admin-modal-overlay, #settings-confirm') && !force)return;
|
|
12
|
+
var input = document.querySelector("#speech-api-key");
|
|
13
|
+
if (!force && input && input.value) {showConfirmDialog("Discard the unsaved voice API key?", "Discard", function () {closeSpeechPicker(true);});return;}
|
|
9
14
|
var state = store.get('speechPopup');
|
|
10
15
|
store.set({ speechPopup: null });
|
|
11
16
|
if (state && state.anchor.isConnected) { state.anchor.setAttribute('aria-expanded', 'false'); state.anchor.focus(); }
|
|
@@ -59,8 +64,10 @@ function render() {
|
|
|
59
64
|
popup.style.left = Math.max(8, Math.min(bounds.right - 324, window.innerWidth - 332)) + 'px';
|
|
60
65
|
popup.style.bottom = Math.max(8, window.innerHeight - bounds.top + 8) + 'px';
|
|
61
66
|
popup.style.maxHeight = Math.max(150, bounds.top - 16) + 'px';
|
|
67
|
+
if (state.anchor.closest('#user-settings')) {popup.style.bottom = 'auto';popup.style.top = 'max(16px, calc(50dvh - 260px))';popup.style.maxHeight = 'calc(100dvh - 48px)';}
|
|
62
68
|
popup.addEventListener('click', function (event) {
|
|
63
69
|
var button = event.target.closest('button'); if (!button || button.disabled) return;
|
|
70
|
+
if (popup.querySelector("#speech-api-key") && popup.querySelector("#speech-api-key").value && (button.dataset.mode || button.dataset.provider || button.dataset.model)) {showConfirmDialog('Discard the unsaved voice API key?', 'Discard', function () {form.elements.key.value = '';button.click();});return;}
|
|
64
71
|
if (button.dataset.mode) patch({ mode: button.dataset.mode, provider: null, keyModel: null });
|
|
65
72
|
else if (button.dataset.provider) {
|
|
66
73
|
var keyModel = button.parentElement.querySelector('[data-model]').dataset.model;
|
|
@@ -80,7 +87,7 @@ function render() {
|
|
|
80
87
|
var form = popup.querySelector('form');
|
|
81
88
|
if (form && previousProvider === state.provider) form.elements.key.value = unsavedKey;
|
|
82
89
|
if (form) form.addEventListener('submit', function (event) {
|
|
83
|
-
event.preventDefault(); var key = form.elements.key.value;
|
|
90
|
+
event.preventDefault(); var key = form.elements.key.value;
|
|
84
91
|
save({ provider: state.provider, key: key }, false);
|
|
85
92
|
});
|
|
86
93
|
var select = popup.querySelector('select');
|
|
@@ -89,6 +96,7 @@ function render() {
|
|
|
89
96
|
try { await saveSpeechLanguage(code); if (store.get('speechPopup')) patch({ busy: false, error: '' }); }
|
|
90
97
|
catch (error) { if (store.get('speechPopup')) patch({ busy: false, error: error.message }); }
|
|
91
98
|
});
|
|
99
|
+
enhanceSettingsSelects(popup);
|
|
92
100
|
var focus = Array.from(popup.querySelectorAll('[data-focus]')).find(function (el) { return el.dataset.focus === focusKey && !el.disabled; });
|
|
93
101
|
(form && previousProvider !== state.provider ? form.elements.key : (focus || popup.querySelector('input,button'))).focus();
|
|
94
102
|
}
|
|
@@ -108,14 +116,15 @@ export function initSpeechPicker() {
|
|
|
108
116
|
});
|
|
109
117
|
document.addEventListener('pointerdown', function (event) {
|
|
110
118
|
var state = store.get('speechPopup'); var popup = document.getElementById('speech-picker');
|
|
119
|
+
if(event.target.closest('.admin-modal-overlay, #settings-confirm'))return;
|
|
111
120
|
if (state && popup && !popup.contains(event.target) && !state.anchor.contains(event.target)) closeSpeechPicker();
|
|
112
121
|
});
|
|
113
122
|
document.addEventListener('keydown', function (event) {
|
|
114
|
-
if (!store.get('speechPopup')) return;
|
|
115
|
-
if (event.key === 'Escape') { event.preventDefault(); event.stopPropagation(); closeSpeechPicker(); }
|
|
123
|
+
if (!store.get('speechPopup') || document.querySelector('.admin-modal-overlay, #settings-confirm')) return;
|
|
124
|
+
if (event.key === 'Escape') { if(document.activeElement.closest('.settings-picker') && document.activeElement.closest('.settings-picker').querySelector('[aria-expanded=true]'))return; event.preventDefault(); event.stopPropagation(); closeSpeechPicker(); }
|
|
116
125
|
if (event.key === 'Tab') {
|
|
117
126
|
var popup = document.getElementById('speech-picker');
|
|
118
|
-
var items = popup.querySelectorAll('button:not(:disabled),input:not(:disabled),select:not(:disabled),a[href]');
|
|
127
|
+
var items = Array.from(popup.querySelectorAll('button:not(:disabled),input:not(:disabled),select:not(:disabled),a[href]')).filter(function(el){return el.getClientRects().length && el.tabIndex!==-1;});
|
|
119
128
|
var first = items[0]; var last = items[items.length - 1];
|
|
120
129
|
if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }
|
|
121
130
|
else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
export var TERMINAL_FONT_OPTIONS = [
|
|
2
|
+
{ label: "SF Mono", family: "'SF Mono', Menlo, Monaco, 'Courier New', monospace" },
|
|
3
|
+
{ label: "JetBrains Mono", family: "'JetBrains Mono', 'SF Mono', Menlo, monospace" },
|
|
4
|
+
{ label: "Fira Code", family: "'Fira Code', 'SF Mono', Menlo, monospace" },
|
|
5
|
+
{ label: "Cascadia Code", family: "'Cascadia Code', 'SF Mono', Menlo, monospace" },
|
|
6
|
+
{ label: "IBM Plex Mono", family: "'IBM Plex Mono', 'SF Mono', Menlo, monospace" },
|
|
7
|
+
{ label: "Source Code Pro", family: "'Source Code Pro', 'SF Mono', Menlo, monospace" },
|
|
8
|
+
{ label: "Roboto Mono", family: "'Roboto Mono', 'SF Mono', Menlo, monospace" },
|
|
9
|
+
{ label: "D2 Coding", family: "'D2 coding', 'D2Coding', 'SF Mono', Menlo, monospace" },
|
|
10
|
+
{ label: "System mono", family: "ui-monospace, monospace" },
|
|
11
|
+
];
|
|
12
|
+
|
|
1
13
|
// terminal-prefs.js
|
|
2
14
|
//
|
|
3
15
|
// Single source of truth for terminal font preferences (family + size).
|
|
@@ -98,3 +110,13 @@ export function applyTerminalFont(family, size) {
|
|
|
98
110
|
export function onTerminalFontChange(fn) {
|
|
99
111
|
if (typeof fn === "function") listeners.push(fn);
|
|
100
112
|
}
|
|
113
|
+
|
|
114
|
+
// Publish only acknowledged values, shared by settings and the terminal toolbar.
|
|
115
|
+
export async function saveTerminalFont(family, size) {
|
|
116
|
+
var response = await fetch('/api/user/terminal-font', {
|
|
117
|
+
method: 'PUT', headers: {'Content-Type':'application/json'},
|
|
118
|
+
body: JSON.stringify({family: family, size: size})
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) throw new Error('Could not save terminal typography. Try again.');
|
|
121
|
+
applyTerminalFont(family, size);
|
|
122
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
1
2
|
import { refreshIcons } from './icons.js';
|
|
2
3
|
import { setTerminalTheme } from './terminal.js';
|
|
3
4
|
import { setTuiSessionTheme } from './session-tui-view.js';
|
|
@@ -315,19 +316,7 @@ export function applyTheme(themeId, fromPicker) {
|
|
|
315
316
|
var mermaidVars = computeMermaidVars(theme);
|
|
316
317
|
try { updateMermaidTheme(mermaidVars); } catch (e) {}
|
|
317
318
|
|
|
318
|
-
|
|
319
|
-
localStorage.setItem(STORAGE_KEY, themeId);
|
|
320
|
-
localStorage.setItem(STORAGE_KEY + "-vars", JSON.stringify(vars));
|
|
321
|
-
localStorage.setItem(STORAGE_KEY + "-variant", theme.variant || "dark");
|
|
322
|
-
} catch (e) {}
|
|
323
|
-
|
|
324
|
-
// When picked from skin selector, save as skin preference and sync mode
|
|
325
|
-
if (fromPicker) {
|
|
326
|
-
try {
|
|
327
|
-
localStorage.setItem(SKIN_KEY, themeId);
|
|
328
|
-
localStorage.setItem(MODE_KEY, isLight ? "light" : "dark");
|
|
329
|
-
} catch (e) {}
|
|
330
|
-
}
|
|
319
|
+
if (fromPicker) saveAppearance(themeId, isLight ? 'light' : 'dark');
|
|
331
320
|
|
|
332
321
|
updateToggleIcon();
|
|
333
322
|
|
|
@@ -417,7 +406,7 @@ function getSystemMode() {
|
|
|
417
406
|
// Returns the effective mode: user override or system
|
|
418
407
|
function getEffectiveMode() {
|
|
419
408
|
var saved = null;
|
|
420
|
-
|
|
409
|
+
saved = (store.get('appearanceDraft') || store.get('appearancePreference') || {}).mode;
|
|
421
410
|
if (saved === "light" || saved === "dark") return saved;
|
|
422
411
|
return getSystemMode();
|
|
423
412
|
}
|
|
@@ -426,7 +415,10 @@ function getEffectiveMode() {
|
|
|
426
415
|
// If user has a custom skin selected, find its dark/light counterpart
|
|
427
416
|
function themeIdForMode(mode) {
|
|
428
417
|
var skin = null;
|
|
429
|
-
|
|
418
|
+
var preference = store.get('appearanceDraft') || store.get('appearancePreference') || {};
|
|
419
|
+
var paired = preference[mode + 'Theme'];
|
|
420
|
+
if (paired && getTheme(paired) && getTheme(paired).variant === mode) return paired;
|
|
421
|
+
skin = preference.theme;
|
|
430
422
|
|
|
431
423
|
// Default Clay Studio skin pair.
|
|
432
424
|
if (!skin) {
|
|
@@ -457,24 +449,12 @@ function themeIdForMode(mode) {
|
|
|
457
449
|
|
|
458
450
|
// --- Wide view ---
|
|
459
451
|
|
|
460
|
-
export function isWideView() {
|
|
461
|
-
|
|
462
|
-
var v = localStorage.getItem(WIDE_KEY);
|
|
463
|
-
return v === null ? true : v !== "bubble"; // default: Channel (wide)
|
|
464
|
-
} catch (e) { return true; }
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
export function getChatLayout() {
|
|
468
|
-
try {
|
|
469
|
-
var v = localStorage.getItem(WIDE_KEY);
|
|
470
|
-
return v === "bubble" ? "bubble" : "channel";
|
|
471
|
-
} catch (e) { return "channel"; }
|
|
472
|
-
}
|
|
473
|
-
|
|
452
|
+
export function isWideView() { return getChatLayout() === 'channel'; }
|
|
453
|
+
export function getChatLayout() { return store.get('personalChatLayout') || 'channel'; }
|
|
474
454
|
export function setChatLayout(layout) {
|
|
475
|
-
var val =
|
|
476
|
-
|
|
477
|
-
document.body.classList.toggle(
|
|
455
|
+
var val = layout === 'bubble' ? 'bubble' : 'channel';
|
|
456
|
+
store.set({ personalChatLayout: val });
|
|
457
|
+
document.body.classList.toggle('wide-view', val === 'channel');
|
|
478
458
|
}
|
|
479
459
|
|
|
480
460
|
export function setWideView(enabled) {
|
|
@@ -485,9 +465,10 @@ export function setWideView(enabled) {
|
|
|
485
465
|
export function toggleDarkMode() {
|
|
486
466
|
var current = getEffectiveMode();
|
|
487
467
|
var next = current === "dark" ? "light" : "dark";
|
|
488
|
-
|
|
468
|
+
|
|
489
469
|
var tid = themeIdForMode(next);
|
|
490
470
|
applyTheme(tid);
|
|
471
|
+
saveAppearance(tid, next);
|
|
491
472
|
updateToggleIcon();
|
|
492
473
|
}
|
|
493
474
|
|
|
@@ -633,26 +614,11 @@ function rebuildPicker() {
|
|
|
633
614
|
|
|
634
615
|
// --- Init ---
|
|
635
616
|
export function initTheme() {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
var saved = null;
|
|
639
|
-
var savedSkin = null;
|
|
640
|
-
try { saved = localStorage.getItem(STORAGE_KEY); } catch (e) {}
|
|
641
|
-
try { savedSkin = localStorage.getItem(SKIN_KEY); } catch (e) {}
|
|
642
|
-
|
|
643
|
-
if (saved && savedSkin) {
|
|
644
|
-
currentThemeId = saved;
|
|
645
|
-
} else {
|
|
646
|
-
// No explicit skin — use the Clay Studio pair in the system mode.
|
|
647
|
-
var mode = getSystemMode();
|
|
648
|
-
currentThemeId = mode === "light" ? DEFAULT_LIGHT_THEME_ID : DEFAULT_DARK_THEME_ID;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// Apply wide view state from localStorage
|
|
652
|
-
document.body.classList.toggle("wide-view", isWideView());
|
|
617
|
+
currentThemeId = getSystemMode() === 'light' ? DEFAULT_LIGHT_THEME_ID : DEFAULT_DARK_THEME_ID;
|
|
618
|
+
document.body.classList.toggle('wide-view', isWideView());
|
|
653
619
|
|
|
654
620
|
// Load all themes from server, then apply properly
|
|
655
|
-
loadThemes();
|
|
621
|
+
loadThemes().then(loadAppearance);
|
|
656
622
|
|
|
657
623
|
// The theme picker is mounted in User Settings by user-settings.js.
|
|
658
624
|
|
|
@@ -661,7 +627,7 @@ export function initTheme() {
|
|
|
661
627
|
var mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
662
628
|
var handler = function () {
|
|
663
629
|
var userMode = null;
|
|
664
|
-
|
|
630
|
+
userMode = (store.get('appearanceDraft') || store.get('appearancePreference') || {}).mode;
|
|
665
631
|
if (!userMode) {
|
|
666
632
|
// No manual override — follow system
|
|
667
633
|
var sysMode = getSystemMode();
|
|
@@ -680,3 +646,73 @@ export function initTheme() {
|
|
|
680
646
|
updateToggleIcon();
|
|
681
647
|
|
|
682
648
|
}
|
|
649
|
+
|
|
650
|
+
function appearanceStatus(text, error) {
|
|
651
|
+
store.set({ appearanceSaveStatus: text, appearanceSaveError: !!error });
|
|
652
|
+
var status = document.getElementById('us-appearance-status');
|
|
653
|
+
if (status) { status.textContent = text; status.dataset.state = error ? 'error' : ''; }
|
|
654
|
+
}
|
|
655
|
+
export function getAppearanceChoices() {
|
|
656
|
+
return { mode: (store.get('appearanceDraft') || store.get('appearancePreference') || {}).mode || 'system', lightTheme: themeIdForMode('light'), darkTheme: themeIdForMode('dark') };
|
|
657
|
+
}
|
|
658
|
+
export function retryAppearanceSave() {
|
|
659
|
+
var draft = store.get('appearanceDraft');
|
|
660
|
+
if (draft) saveAppearance(draft.theme, draft.mode, draft);
|
|
661
|
+
}
|
|
662
|
+
export function selectAppearanceMode(mode) {
|
|
663
|
+
var effective = mode === 'system' ? getSystemMode() : mode;
|
|
664
|
+
var id = themeIdForMode(effective);
|
|
665
|
+
saveAppearance(id, mode === 'system' ? null : mode);
|
|
666
|
+
applyTheme(id);
|
|
667
|
+
}
|
|
668
|
+
export function selectAppearanceTheme(mode, id) {
|
|
669
|
+
if (!getTheme(id) || getTheme(id).variant !== mode) return;
|
|
670
|
+
var effective = getEffectiveMode();
|
|
671
|
+
var current = store.get('appearanceDraft') || store.get('appearancePreference') || {};
|
|
672
|
+
var change = {}; change[mode + 'Theme'] = id;
|
|
673
|
+
saveAppearance(mode === effective ? id : currentThemeId, current.mode || null, change);
|
|
674
|
+
if (mode === effective) applyTheme(id);
|
|
675
|
+
}
|
|
676
|
+
function saveAppearance(theme, mode, overrides) {
|
|
677
|
+
var values = Object.assign({}, store.get('appearanceDraft') || store.get('appearancePreference') || {}, {theme:theme, mode:mode}, overrides || {});
|
|
678
|
+
delete values.configured;
|
|
679
|
+
var selected = getTheme(theme);
|
|
680
|
+
if (selected) values[selected.variant + 'Theme'] = theme;
|
|
681
|
+
store.set({appearanceDraft:values});
|
|
682
|
+
var queue = store.get('appearanceSaveQueue') || Promise.resolve();
|
|
683
|
+
var next = queue.catch(function () {}).then(function () {return persistAppearance(values);});
|
|
684
|
+
store.set({appearanceSaveQueue:next});
|
|
685
|
+
return next;
|
|
686
|
+
}
|
|
687
|
+
async function persistAppearance(values) {
|
|
688
|
+
var request = (store.get('appearanceSaveRequest') || 0) + 1;
|
|
689
|
+
store.set({ appearanceSaveRequest: request });
|
|
690
|
+
appearanceStatus('Saving appearance…');
|
|
691
|
+
try {
|
|
692
|
+
var response = await fetch('/api/user/appearance', {method:'PUT',headers:{'Content-Type':'application/json','X-Clay-Settings':'1'},body:JSON.stringify(values)});
|
|
693
|
+
if (!response.ok) throw new Error('Could not save appearance. Your preview remains. Retry to save it.');
|
|
694
|
+
if (store.get('appearanceSaveRequest') !== request) return;
|
|
695
|
+
var saved = await response.json();
|
|
696
|
+
store.set({ appearancePreference: saved, appearanceDraft: store.get('appearanceDraft') === values ? null : store.get('appearanceDraft') });
|
|
697
|
+
[STORAGE_KEY,SKIN_KEY,MODE_KEY,STORAGE_KEY+'-vars',STORAGE_KEY+'-variant'].forEach(function (key) {try {localStorage.removeItem(key);}catch(error){}});
|
|
698
|
+
appearanceStatus('Saved. Applied to this view; other devices use it when they reload.');
|
|
699
|
+
} catch(error) {if(store.get('appearanceSaveRequest') === request) appearanceStatus(error.message,true);}
|
|
700
|
+
}
|
|
701
|
+
async function loadAppearance() {
|
|
702
|
+
try {
|
|
703
|
+
var response = await fetch('/api/user/appearance');
|
|
704
|
+
if (!response.ok) throw new Error('Appearance preferences unavailable.');
|
|
705
|
+
var data = await response.json();
|
|
706
|
+
if(store.get('appearanceSaveRequest'))return;
|
|
707
|
+
if (data.configured) {store.set({appearancePreference:data});applyTheme(themeIdForMode(data.mode || getSystemMode()));}
|
|
708
|
+
else {
|
|
709
|
+
var legacy = null;try {legacy = localStorage.getItem(STORAGE_KEY);}catch(error){}
|
|
710
|
+
if(legacy && getTheme(legacy))store.set({legacyAppearanceAvailable:legacy});
|
|
711
|
+
}
|
|
712
|
+
} catch(error) {appearanceStatus(error.message,true);}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
export function importLegacyAppearance() {
|
|
716
|
+
var legacy=store.get('legacyAppearanceAvailable');
|
|
717
|
+
if(legacy) {applyTheme(legacy,true);store.set({legacyAppearanceAvailable:null});}
|
|
718
|
+
}
|
|
@@ -3,6 +3,7 @@ import { iconHtml, refreshIcons } from './icons.js';
|
|
|
3
3
|
import { store } from './store.js';
|
|
4
4
|
import { addToMessages, keepActiveThinkingAtTail, scrollToBottom } from './chat-render-runtime.js';
|
|
5
5
|
import { prepareThinkingView, bindThinkingView, updateThinkingView, beginThinkingView, setThinkingViewLive, finishThinkingView } from './thinking-view.js';
|
|
6
|
+
import { resolveProjectAssistantIdentity, applyProjectAssistantIdentity } from './project-chat-identity.js';
|
|
6
7
|
|
|
7
8
|
function getThinkingState() {
|
|
8
9
|
return store.get('thinkingState') || null;
|
|
@@ -19,8 +20,7 @@ function updateThinkingState(state, patch) {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
function isMateThinking() {
|
|
22
|
-
|
|
23
|
-
return !!(store.get('dmMode') && target && target.isMate);
|
|
23
|
+
return resolveProjectAssistantIdentity(store.snap(), store.get('currentVendor')).kind === 'mate';
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function maybeScrollToBottom() {
|
|
@@ -30,10 +30,10 @@ function maybeScrollToBottom() {
|
|
|
30
30
|
function createThinkingElement() {
|
|
31
31
|
var el = document.createElement("div");
|
|
32
32
|
el.className = "thinking-item";
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
var mateName =
|
|
36
|
-
var mateAvatar =
|
|
33
|
+
var identity = resolveProjectAssistantIdentity(store.snap(), store.get('currentVendor'));
|
|
34
|
+
if (identity.kind === 'mate') {
|
|
35
|
+
var mateName = identity.name;
|
|
36
|
+
var mateAvatar = identity.avatarUrl;
|
|
37
37
|
el.classList.add("mate-thinking");
|
|
38
38
|
el.innerHTML =
|
|
39
39
|
'<img class="dm-bubble-avatar dm-bubble-avatar-mate" src="' + escapeHtml(mateAvatar) + '" alt="">' +
|
|
@@ -48,6 +48,7 @@ function createThinkingElement() {
|
|
|
48
48
|
'</div>' +
|
|
49
49
|
'<div class="thinking-content"></div>' +
|
|
50
50
|
'</div>';
|
|
51
|
+
applyProjectAssistantIdentity(el, identity);
|
|
51
52
|
} else {
|
|
52
53
|
el.innerHTML =
|
|
53
54
|
'<div class="thinking-header">' +
|