clay-server 5.2.0-beta.9 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/lib/clay-product-control-tools.js +1 -0
- package/lib/clay-product-control.js +14 -11
- package/lib/clay-vendor-usage.js +44 -0
- package/lib/github-star-count.js +147 -0
- package/lib/notes-checklist.js +43 -1
- package/lib/notes.js +2 -1
- package/lib/project-session-notes.js +33 -8
- package/lib/project-sessions.js +63 -24
- package/lib/project-start-protocol.js +29 -0
- package/lib/project-vendor-login.js +2 -0
- package/lib/project.js +5 -1
- package/lib/public/app.js +27 -18
- package/lib/public/css/contextual-clay.css +1 -1
- package/lib/public/css/filebrowser.css +1 -1
- package/lib/public/css/mate-loading-scene.css +21 -0
- package/lib/public/css/overlays.css +64 -256
- package/lib/public/css/project-mate-creation.css +5 -6
- package/lib/public/css/project-start.css +108 -0
- package/lib/public/css/projects-hub.css +53 -14
- package/lib/public/css/settings-shell.css +103 -0
- package/lib/public/css/sidebar.css +7 -0
- package/lib/public/css/title-bar.css +83 -0
- package/lib/public/index.html +50 -63
- package/lib/public/modules/account-transport-settings.js +8 -2
- package/lib/public/modules/admin-settings-email.js +7 -2
- package/lib/public/modules/admin-settings-invitations.js +2 -2
- package/lib/public/modules/admin-settings-projects.js +2 -2
- package/lib/public/modules/admin-settings-ui.js +20 -4
- package/lib/public/modules/admin-settings-users.js +4 -4
- package/lib/public/modules/app-home-hub.js +2 -1
- package/lib/public/modules/app-messages.js +10 -2
- package/lib/public/modules/app-projects.js +1 -407
- package/lib/public/modules/app-rate-limit.js +12 -1
- package/lib/public/modules/clay-tool-presentation.js +1 -0
- package/lib/public/modules/contextual-clay.js +3 -1
- package/lib/public/modules/github-star.js +66 -0
- package/lib/public/modules/home-surface-boot.js +0 -1
- package/lib/public/modules/mate-loading-scene.js +17 -0
- package/lib/public/modules/pane-appearance-bridge.js +59 -0
- package/lib/public/modules/profile.js +11 -3
- package/lib/public/modules/project-mate-creation-workspace.js +13 -4
- package/lib/public/modules/project-session-visibility-settings.js +19 -16
- package/lib/public/modules/project-settings-environment.js +12 -1
- package/lib/public/modules/project-settings.js +119 -129
- package/lib/public/modules/project-start-dialog.js +130 -0
- package/lib/public/modules/project-start-folder-state.js +78 -0
- package/lib/public/modules/project-start-folder-view.js +190 -0
- package/lib/public/modules/project-start-forms.js +66 -0
- package/lib/public/modules/project-start-path.js +60 -0
- package/lib/public/modules/project-start-state.js +54 -0
- package/lib/public/modules/project-start-submit.js +67 -0
- package/lib/public/modules/projects-hub-view.js +32 -44
- package/lib/public/modules/projects-hub.js +39 -28
- package/lib/public/modules/server-settings.js +36 -91
- package/lib/public/modules/settings-project-target.js +56 -0
- package/lib/public/modules/settings-select.js +1 -1
- package/lib/public/modules/settings-shell-registry.js +88 -0
- package/lib/public/modules/settings-shell-view.js +104 -0
- package/lib/public/modules/settings-shell.js +342 -0
- package/lib/public/modules/sidebar-mobile.js +8 -30
- package/lib/public/modules/sidebar-projects.js +2 -1
- package/lib/public/modules/theme.js +31 -3
- package/lib/public/modules/user-settings-integrations.js +2 -1
- package/lib/public/modules/user-settings-layout.js +12 -34
- package/lib/public/modules/user-settings-profile.js +1 -1
- package/lib/public/modules/user-settings.js +18 -85
- package/lib/public/modules/vendor-usage-format.js +93 -0
- package/lib/public/modules/vendor-usage-list.js +45 -0
- package/lib/public/modules/vendor-usage-model.js +215 -0
- package/lib/public/modules/vendor-usage-overview.js +323 -0
- package/lib/public/style.css +10 -7
- package/lib/public/welcome-art/conversation-desk.png +0 -0
- package/lib/public/welcome-art/mate-clay.png +0 -0
- package/lib/public/welcome-art/project-clay.png +0 -0
- package/lib/sdk-bridge.js +6 -1
- package/lib/sdk-message-processor.js +9 -1
- package/lib/sdk-quota-snapshot.js +20 -2
- package/lib/server-github-stars.js +39 -0
- package/lib/server-global-ws.js +21 -17
- package/lib/server.js +11 -0
- package/lib/session-notes-mcp-server.js +26 -4
- package/lib/session-notes-writes.js +88 -0
- package/lib/session-visibility.js +20 -8
- package/lib/sessions.js +12 -2
- package/lib/vendor-usage-acp-readers.js +97 -0
- package/lib/vendor-usage-capabilities.js +88 -0
- package/lib/vendor-usage-http-readers.js +214 -0
- package/lib/vendor-usage-metrics.js +47 -0
- package/lib/vendor-usage-native-readers.js +106 -0
- package/lib/vendor-usage-overview.js +406 -0
- package/lib/vendor-usage-service.js +281 -0
- package/lib/ws-schema.js +4 -2
- package/lib/yoke/adapters/claude.js +12 -0
- package/lib/yoke/adapters/codex-query-contract.js +1 -0
- package/lib/yoke/adapters/codex.js +45 -12
- package/lib/yoke/claude-hook-installer.js +1 -0
- package/lib/yoke/codex-dynamic-tool-names.js +80 -0
- package/lib/yoke/codex-model-catalog.js +37 -3
- package/lib/yoke/muse-services.js +16 -3
- package/lib/yoke/providers/claude-model-descriptions.js +22 -2
- package/lib/yoke/vendor-registry.js +6 -2
- package/package.json +2 -1
- package/lib/public/modules/server-settings-navigation.js +0 -39
|
@@ -65,7 +65,7 @@ import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundE
|
|
|
65
65
|
import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handleFileHistory, handleGitDiff, handleFileAt, refreshIfOpen, getPendingNavigate, handleFsSearch, presentMarkdownEdit } from './filebrowser.js';
|
|
66
66
|
import { beginMarkdownTurn, finishMarkdownTurn, markdownPathFromToolInput } from './markdown-live-edit.js';
|
|
67
67
|
import { forwardPaneMarkdownPresentation } from './pane-bridge.js';
|
|
68
|
-
import { handleProjectOwnerChanged } from './project-settings.js';
|
|
68
|
+
import { handleProjectOwnerChanged, handleProjectSettingsEditResult } from './project-settings.js';
|
|
69
69
|
import { handleProjectEnv, handleProjectEnvSaved } from './project-settings-environment.js';
|
|
70
70
|
import { handleProjectSessionVisibilityDefault, refreshProjectSessionVisibilitySettings } from './project-session-visibility-settings.js';
|
|
71
71
|
import { updateSettingsStats, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleServerSettingResult } from './server-settings.js';
|
|
@@ -100,13 +100,15 @@ import { markProjectSessionListHydrated, applyProjectSplitGroups, clearProjectSp
|
|
|
100
100
|
import { selectDefaultVendorForBlankSession } from './vendor-selection.js';
|
|
101
101
|
import { getModelInfoUpdate, modelEntryValue, modelEntryMatches, handleModelSelectionResult, requestVendorModels } from './model-picker.js';
|
|
102
102
|
import { getModelEffortLevels, accumulateUsage, updateUsagePanel, accumulateContext, updateContextPanel, renderCtxPopover, updateStatusPanel, markContextUnavailable } from './app-panels.js';
|
|
103
|
-
import { updateProjectList, resetClientState, showUpdateAvailable, handleRemoveProjectCheckResult, handleRemoveProjectResult,
|
|
103
|
+
import { updateProjectList, resetClientState, showUpdateAvailable, handleRemoveProjectCheckResult, handleRemoveProjectResult, finishProjectSessionActivation } from './app-projects.js';
|
|
104
|
+
import { handleBrowseDirResult, handleAddProjectResult, handleCloneProgress } from './project-start-dialog.js';
|
|
104
105
|
import { updateHistorySentinel, prependOlderHistory } from './app-header.js';
|
|
105
106
|
import { handleToolsState, handleToolInstalled, handleToolRemoved, handleToolStorageResult, handleToolLlmResult, handleToolsError, handleToolControlRequest, handleToolServerState, handleToolServerEvent, handleCapsuleGameSession } from './home-tools.js';
|
|
106
107
|
import { handleHomeDockState } from './home-dock.js';
|
|
107
108
|
import { handleToolFrameUrlState } from './home-tool-frame.js';
|
|
108
109
|
import { enterDmMode, exitDmMode, handleMateCreatedInApp, updateMateIconStatus, appendDmMessage, showDmTypingIndicator, buildMateInterviewPrompt } from './app-dm.js';
|
|
109
110
|
import { handleRateLimitEvent, updateRateLimitUsage, handleFastModeState } from './app-rate-limit.js';
|
|
111
|
+
import { handleVendorUsageOverview } from './vendor-usage-overview.js';
|
|
110
112
|
import { handleRemoteCursorMove, handleRemoteCursorLeave, handleRemoteSelection, clearRemoteCursors } from './app-cursors.js';
|
|
111
113
|
import { showLoopBanner, updateLoopBanner, updateLoopInputVisibility, showRalphApprovalBar, updateRalphApprovalStatus, openRalphPreviewModal, showExecModal, updateExecModalStatus } from './app-loop-ui.js';
|
|
112
114
|
import { handleAutonomousRunMessage, syncAutonomousRunForSession } from './autonomous-run.js';
|
|
@@ -1615,6 +1617,10 @@ export function processMessage(msg) {
|
|
|
1615
1617
|
updateRateLimitUsage(msg);
|
|
1616
1618
|
break;
|
|
1617
1619
|
|
|
1620
|
+
case "vendor_usage_overview":
|
|
1621
|
+
handleVendorUsageOverview(msg);
|
|
1622
|
+
break;
|
|
1623
|
+
|
|
1618
1624
|
case "auto_continue_scheduled":
|
|
1619
1625
|
// Scheduler auto-continue, just show info
|
|
1620
1626
|
break;
|
|
@@ -1922,12 +1928,14 @@ export function processMessage(msg) {
|
|
|
1922
1928
|
break;
|
|
1923
1929
|
|
|
1924
1930
|
case "set_project_title_result":
|
|
1931
|
+
handleProjectSettingsEditResult(msg);
|
|
1925
1932
|
if (!msg.ok) {
|
|
1926
1933
|
showToast(msg.error || "Failed to rename project", "error");
|
|
1927
1934
|
}
|
|
1928
1935
|
break;
|
|
1929
1936
|
|
|
1930
1937
|
case "set_project_icon_result":
|
|
1938
|
+
handleProjectSettingsEditResult(msg);
|
|
1931
1939
|
if (!msg.ok) {
|
|
1932
1940
|
showToast(msg.error || "Failed to set icon", "error");
|
|
1933
1941
|
}
|
|
@@ -4,9 +4,8 @@ import { checkComposerDeparture, saveSessionRichDraft } from './session-rich-dra
|
|
|
4
4
|
// Extracted from app.js (PR-29)
|
|
5
5
|
|
|
6
6
|
import { showProjectsHub, hideProjectsHub, completeProjectsOnboarding } from './projects-hub.js';
|
|
7
|
-
import {
|
|
7
|
+
import { showToast } from './utils.js';
|
|
8
8
|
import { refreshIcons } from './icons.js';
|
|
9
|
-
import { parseEmojis } from './markdown.js';
|
|
10
9
|
import { store } from './store.js';
|
|
11
10
|
import { resetModelPickerState } from './model-picker.js';
|
|
12
11
|
import { getWs, setWs } from './ws-ref.js';
|
|
@@ -50,157 +49,6 @@ var cachedRemovedProjects = [];
|
|
|
50
49
|
var pendingRemoveSlug = null;
|
|
51
50
|
var pendingRemoveName = null;
|
|
52
51
|
|
|
53
|
-
// Add-project modal state
|
|
54
|
-
var addProjectModal = null;
|
|
55
|
-
var addProjectInput = null;
|
|
56
|
-
var addProjectCreateInput = null;
|
|
57
|
-
var addProjectCloneInput = null;
|
|
58
|
-
var addProjectCloneProgress = null;
|
|
59
|
-
var addProjectSuggestions = null;
|
|
60
|
-
var addProjectError = null;
|
|
61
|
-
var addProjectOk = null;
|
|
62
|
-
var addProjectCancel = null;
|
|
63
|
-
var addProjectModeBtns = null;
|
|
64
|
-
var addProjectPanels = null;
|
|
65
|
-
var addProjectRemoved = null;
|
|
66
|
-
var addProjectPrefixEl = null;
|
|
67
|
-
var addProjectPrefixValue = "";
|
|
68
|
-
var addProjectDebounce = null;
|
|
69
|
-
var addProjectActiveIdx = -1;
|
|
70
|
-
var addProjectMode = "existing";
|
|
71
|
-
|
|
72
|
-
export function initProjects() {
|
|
73
|
-
// Init add-project modal DOM refs
|
|
74
|
-
addProjectModal = document.getElementById("add-project-modal");
|
|
75
|
-
addProjectInput = document.getElementById("add-project-input");
|
|
76
|
-
addProjectCreateInput = document.getElementById("add-project-create-input");
|
|
77
|
-
addProjectCloneInput = document.getElementById("add-project-clone-input");
|
|
78
|
-
addProjectCloneProgress = document.getElementById("add-project-clone-progress");
|
|
79
|
-
addProjectSuggestions = document.getElementById("add-project-suggestions");
|
|
80
|
-
addProjectError = document.getElementById("add-project-error");
|
|
81
|
-
addProjectOk = document.getElementById("add-project-ok");
|
|
82
|
-
addProjectCancel = document.getElementById("add-project-cancel");
|
|
83
|
-
addProjectModeBtns = addProjectModal.querySelectorAll(".add-project-mode-btn");
|
|
84
|
-
addProjectPanels = addProjectModal.querySelectorAll(".add-project-panel");
|
|
85
|
-
addProjectRemoved = document.getElementById("add-project-removed");
|
|
86
|
-
addProjectPrefixEl = document.getElementById("add-project-prefix");
|
|
87
|
-
|
|
88
|
-
// Mode button click listeners
|
|
89
|
-
for (var mbi = 0; mbi < addProjectModeBtns.length; mbi++) {
|
|
90
|
-
addProjectModeBtns[mbi].addEventListener("click", function () {
|
|
91
|
-
if (this.disabled) return;
|
|
92
|
-
switchAddProjectMode(this.dataset.mode);
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Existing project input listeners
|
|
97
|
-
addProjectInput.addEventListener("focus", function () {
|
|
98
|
-
var val = addProjectInput.value;
|
|
99
|
-
if ((val || addProjectPrefixValue) && addProjectSuggestions.children.length === 0) {
|
|
100
|
-
requestBrowseDir(val);
|
|
101
|
-
} else if (addProjectSuggestions.children.length > 0) {
|
|
102
|
-
addProjectSuggestions.classList.remove("hidden");
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
addProjectModal.querySelector(".confirm-dialog").addEventListener("click", function (e) {
|
|
107
|
-
if (e.target === addProjectInput || addProjectInput.contains(e.target)) return;
|
|
108
|
-
if (e.target === addProjectSuggestions || addProjectSuggestions.contains(e.target)) return;
|
|
109
|
-
addProjectSuggestions.classList.add("hidden");
|
|
110
|
-
addProjectActiveIdx = -1;
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
addProjectInput.addEventListener("input", function () {
|
|
114
|
-
var val = addProjectInput.value;
|
|
115
|
-
addProjectError.classList.add("hidden");
|
|
116
|
-
if (addProjectDebounce) clearTimeout(addProjectDebounce);
|
|
117
|
-
addProjectDebounce = setTimeout(function () {
|
|
118
|
-
requestBrowseDir(val);
|
|
119
|
-
}, 200);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
addProjectInput.addEventListener("keydown", function (e) {
|
|
123
|
-
var items = addProjectSuggestions.querySelectorAll(".add-project-suggestion-item");
|
|
124
|
-
|
|
125
|
-
if (e.key === "ArrowDown") {
|
|
126
|
-
e.preventDefault();
|
|
127
|
-
if (items.length > 0) {
|
|
128
|
-
var next = addProjectActiveIdx < items.length - 1 ? addProjectActiveIdx + 1 : 0;
|
|
129
|
-
setActiveIdx(next);
|
|
130
|
-
}
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (e.key === "ArrowUp") {
|
|
135
|
-
e.preventDefault();
|
|
136
|
-
if (items.length > 0) {
|
|
137
|
-
var prev = addProjectActiveIdx > 0 ? addProjectActiveIdx - 1 : items.length - 1;
|
|
138
|
-
setActiveIdx(prev);
|
|
139
|
-
}
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (e.key === "Tab") {
|
|
144
|
-
e.preventDefault();
|
|
145
|
-
var target = addProjectActiveIdx >= 0 && addProjectActiveIdx < items.length
|
|
146
|
-
? items[addProjectActiveIdx]
|
|
147
|
-
: items.length > 0 ? items[0] : null;
|
|
148
|
-
if (target) {
|
|
149
|
-
var fullP = target.dataset.path + "/";
|
|
150
|
-
addProjectInput.value = stripPrefix(fullP);
|
|
151
|
-
addProjectError.classList.add("hidden");
|
|
152
|
-
requestBrowseDir(addProjectInput.value);
|
|
153
|
-
}
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (e.key === "Enter") {
|
|
158
|
-
e.preventDefault();
|
|
159
|
-
if (addProjectActiveIdx >= 0 && addProjectActiveIdx < items.length) {
|
|
160
|
-
var fullPicked = items[addProjectActiveIdx].dataset.path + "/";
|
|
161
|
-
addProjectInput.value = stripPrefix(fullPicked);
|
|
162
|
-
addProjectError.classList.add("hidden");
|
|
163
|
-
requestBrowseDir(addProjectInput.value);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
submitAddProject();
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (e.key === "Escape") {
|
|
171
|
-
e.preventDefault();
|
|
172
|
-
closeAddProjectModal();
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
// Enter key on create/clone inputs
|
|
178
|
-
addProjectCreateInput.addEventListener("keydown", function (e) {
|
|
179
|
-
if (e.key === "Enter") { e.preventDefault(); submitAddProject(); }
|
|
180
|
-
if (e.key === "Escape") { e.preventDefault(); closeAddProjectModal(); }
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
addProjectCloneInput.addEventListener("keydown", function (e) {
|
|
184
|
-
if (e.key === "Enter") { e.preventDefault(); submitAddProject(); }
|
|
185
|
-
if (e.key === "Escape") { e.preventDefault(); closeAddProjectModal(); }
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
addProjectOk.addEventListener("click", function () { submitAddProject(); });
|
|
189
|
-
addProjectCancel.addEventListener("click", function () { closeAddProjectModal(); });
|
|
190
|
-
|
|
191
|
-
// Close on backdrop click
|
|
192
|
-
addProjectModal.querySelector(".confirm-backdrop").addEventListener("click", function () {
|
|
193
|
-
closeAddProjectModal();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Project list add button
|
|
197
|
-
var projectListAddBtn = document.getElementById("project-list-add");
|
|
198
|
-
if (projectListAddBtn) {
|
|
199
|
-
projectListAddBtn.addEventListener("click", function () {
|
|
200
|
-
openAddProjectModal();
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
52
|
|
|
205
53
|
// --- State accessors ---
|
|
206
54
|
|
|
@@ -643,257 +491,3 @@ export function handleRemoveProjectResult(msg) {
|
|
|
643
491
|
showToast(msg.error || "Failed to remove project", "error");
|
|
644
492
|
}
|
|
645
493
|
}
|
|
646
|
-
|
|
647
|
-
// --- Add project modal ---
|
|
648
|
-
|
|
649
|
-
function switchAddProjectMode(mode) {
|
|
650
|
-
addProjectMode = mode;
|
|
651
|
-
for (var mi = 0; mi < addProjectModeBtns.length; mi++) {
|
|
652
|
-
var btn = addProjectModeBtns[mi];
|
|
653
|
-
if (btn.dataset.mode === mode) {
|
|
654
|
-
btn.classList.add("active");
|
|
655
|
-
} else {
|
|
656
|
-
btn.classList.remove("active");
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
for (var pi = 0; pi < addProjectPanels.length; pi++) {
|
|
660
|
-
var panel = addProjectPanels[pi];
|
|
661
|
-
if (panel.dataset.panel === mode) {
|
|
662
|
-
panel.classList.add("active");
|
|
663
|
-
} else {
|
|
664
|
-
panel.classList.remove("active");
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
addProjectError.classList.add("hidden");
|
|
668
|
-
addProjectCloneProgress.classList.add("hidden");
|
|
669
|
-
if (mode === "existing") {
|
|
670
|
-
addProjectOk.textContent = "Add";
|
|
671
|
-
} else if (mode === "create") {
|
|
672
|
-
addProjectOk.textContent = "Create";
|
|
673
|
-
} else if (mode === "clone") {
|
|
674
|
-
addProjectOk.textContent = "Clone";
|
|
675
|
-
}
|
|
676
|
-
setTimeout(function () {
|
|
677
|
-
if (mode === "existing") {
|
|
678
|
-
addProjectInput.focus();
|
|
679
|
-
} else if (mode === "create") {
|
|
680
|
-
addProjectCreateInput.focus();
|
|
681
|
-
} else if (mode === "clone") {
|
|
682
|
-
addProjectCloneInput.focus();
|
|
683
|
-
}
|
|
684
|
-
}, 50);
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
export function openAddProjectModal(mode) {
|
|
688
|
-
addProjectModal.classList.remove("hidden");
|
|
689
|
-
addProjectCreateInput.value = "";
|
|
690
|
-
addProjectCloneInput.value = "";
|
|
691
|
-
addProjectError.classList.add("hidden");
|
|
692
|
-
addProjectError.textContent = "";
|
|
693
|
-
addProjectCloneProgress.classList.add("hidden");
|
|
694
|
-
addProjectSuggestions.classList.add("hidden");
|
|
695
|
-
addProjectSuggestions.innerHTML = "";
|
|
696
|
-
addProjectActiveIdx = -1;
|
|
697
|
-
addProjectOk.disabled = false;
|
|
698
|
-
var existingBtn = addProjectModal.querySelector('.add-project-mode-btn[data-mode="existing"]');
|
|
699
|
-
var st = store.snap();
|
|
700
|
-
if (st.isOsUsers) {
|
|
701
|
-
existingBtn.disabled = false;
|
|
702
|
-
var myUser = st.cachedAllUsers.find(function (u) { return u.id === st.myUserId; });
|
|
703
|
-
var isAdmin = myUser && myUser.role === "admin";
|
|
704
|
-
if (!isAdmin && myUser && myUser.linuxUser) {
|
|
705
|
-
// Non-admin: lock prefix to home directory
|
|
706
|
-
addProjectPrefixValue = "/home/" + myUser.linuxUser + "/";
|
|
707
|
-
addProjectPrefixEl.textContent = addProjectPrefixValue;
|
|
708
|
-
addProjectPrefixEl.classList.remove("hidden");
|
|
709
|
-
addProjectInput.value = "";
|
|
710
|
-
addProjectInput.placeholder = "subdirectory";
|
|
711
|
-
} else {
|
|
712
|
-
// Admin: no prefix restriction
|
|
713
|
-
addProjectPrefixValue = "";
|
|
714
|
-
addProjectPrefixEl.classList.add("hidden");
|
|
715
|
-
addProjectInput.value = "/";
|
|
716
|
-
addProjectInput.placeholder = "/";
|
|
717
|
-
}
|
|
718
|
-
switchAddProjectMode("existing");
|
|
719
|
-
} else {
|
|
720
|
-
addProjectPrefixValue = "";
|
|
721
|
-
addProjectPrefixEl.classList.add("hidden");
|
|
722
|
-
addProjectInput.value = "/";
|
|
723
|
-
addProjectInput.placeholder = "/";
|
|
724
|
-
existingBtn.disabled = false;
|
|
725
|
-
switchAddProjectMode("existing");
|
|
726
|
-
}
|
|
727
|
-
if (mode === "create" || mode === "clone") switchAddProjectMode(mode);
|
|
728
|
-
renderRemovedProjectsList();
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
function renderRemovedProjectsList() {
|
|
732
|
-
if (!addProjectRemoved) return;
|
|
733
|
-
addProjectRemoved.innerHTML = "";
|
|
734
|
-
if (!cachedRemovedProjects || cachedRemovedProjects.length === 0) {
|
|
735
|
-
addProjectRemoved.classList.add("hidden");
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
addProjectRemoved.classList.remove("hidden");
|
|
739
|
-
for (var ri = 0; ri < cachedRemovedProjects.length; ri++) {
|
|
740
|
-
var rp = cachedRemovedProjects[ri];
|
|
741
|
-
var item = document.createElement("div");
|
|
742
|
-
item.className = "add-project-removed-item";
|
|
743
|
-
item.dataset.path = rp.path;
|
|
744
|
-
item.addEventListener("click", function () {
|
|
745
|
-
var p = this.dataset.path;
|
|
746
|
-
var ws = getWs();
|
|
747
|
-
if (ws && ws.readyState === 1) {
|
|
748
|
-
ws.send(JSON.stringify({ type: "add_project", path: p }));
|
|
749
|
-
}
|
|
750
|
-
closeAddProjectModal();
|
|
751
|
-
});
|
|
752
|
-
var iconEl = document.createElement("span");
|
|
753
|
-
iconEl.className = "add-project-removed-icon";
|
|
754
|
-
iconEl.textContent = rp.icon || "\uD83D\uDCC1";
|
|
755
|
-
item.appendChild(iconEl);
|
|
756
|
-
var info = document.createElement("div");
|
|
757
|
-
info.className = "add-project-removed-info";
|
|
758
|
-
var nameEl = document.createElement("div");
|
|
759
|
-
nameEl.className = "add-project-removed-name";
|
|
760
|
-
nameEl.textContent = rp.title || rp.path.split("/").pop() || rp.path;
|
|
761
|
-
info.appendChild(nameEl);
|
|
762
|
-
var pathEl = document.createElement("div");
|
|
763
|
-
pathEl.className = "add-project-removed-path";
|
|
764
|
-
pathEl.textContent = rp.path;
|
|
765
|
-
info.appendChild(pathEl);
|
|
766
|
-
item.appendChild(info);
|
|
767
|
-
addProjectRemoved.appendChild(item);
|
|
768
|
-
}
|
|
769
|
-
try { parseEmojis(addProjectRemoved); } catch (e) {}
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
export function closeAddProjectModal() {
|
|
773
|
-
addProjectModal.classList.add("hidden");
|
|
774
|
-
addProjectInput.value = "";
|
|
775
|
-
addProjectCreateInput.value = "";
|
|
776
|
-
addProjectCloneInput.value = "";
|
|
777
|
-
addProjectSuggestions.classList.add("hidden");
|
|
778
|
-
addProjectSuggestions.innerHTML = "";
|
|
779
|
-
addProjectError.classList.add("hidden");
|
|
780
|
-
addProjectCloneProgress.classList.add("hidden");
|
|
781
|
-
addProjectActiveIdx = -1;
|
|
782
|
-
addProjectPrefixValue = "";
|
|
783
|
-
addProjectPrefixEl.classList.add("hidden");
|
|
784
|
-
if (addProjectDebounce) { clearTimeout(addProjectDebounce); addProjectDebounce = null; }
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
function getFullPath(inputVal) {
|
|
788
|
-
return addProjectPrefixValue + inputVal;
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
function stripPrefix(fullPath) {
|
|
792
|
-
if (addProjectPrefixValue && fullPath.indexOf(addProjectPrefixValue) === 0) {
|
|
793
|
-
return fullPath.slice(addProjectPrefixValue.length);
|
|
794
|
-
}
|
|
795
|
-
return fullPath;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
function requestBrowseDir(val) {
|
|
799
|
-
var ws = getWs();
|
|
800
|
-
if (!ws || ws.readyState !== 1) return;
|
|
801
|
-
ws.send(JSON.stringify({ type: "browse_dir", path: getFullPath(val) }));
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
export function handleBrowseDirResult(msg) {
|
|
805
|
-
addProjectSuggestions.innerHTML = "";
|
|
806
|
-
addProjectActiveIdx = -1;
|
|
807
|
-
if (msg.error) {
|
|
808
|
-
addProjectSuggestions.classList.add("hidden");
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
var entries = msg.entries || [];
|
|
812
|
-
if (entries.length === 0) {
|
|
813
|
-
addProjectSuggestions.classList.add("hidden");
|
|
814
|
-
return;
|
|
815
|
-
}
|
|
816
|
-
for (var si = 0; si < entries.length; si++) {
|
|
817
|
-
var entry = entries[si];
|
|
818
|
-
var item = document.createElement("div");
|
|
819
|
-
item.className = "add-project-suggestion-item";
|
|
820
|
-
item.dataset.path = entry.path;
|
|
821
|
-
item.innerHTML = '<i data-lucide="folder"></i><span class="add-project-suggestion-name">' +
|
|
822
|
-
escapeHtml(entry.name) + '</span>';
|
|
823
|
-
item.addEventListener("click", function (e) {
|
|
824
|
-
var fullP = this.dataset.path + "/";
|
|
825
|
-
addProjectInput.value = stripPrefix(fullP);
|
|
826
|
-
addProjectInput.focus();
|
|
827
|
-
addProjectError.classList.add("hidden");
|
|
828
|
-
requestBrowseDir(addProjectInput.value);
|
|
829
|
-
});
|
|
830
|
-
addProjectSuggestions.appendChild(item);
|
|
831
|
-
}
|
|
832
|
-
addProjectSuggestions.classList.remove("hidden");
|
|
833
|
-
refreshIcons();
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
export function handleAddProjectResult(msg) {
|
|
837
|
-
addProjectCloneProgress.classList.add("hidden");
|
|
838
|
-
if (msg.ok) {
|
|
839
|
-
closeAddProjectModal();
|
|
840
|
-
if (msg.existing) {
|
|
841
|
-
showToast("Project already registered", "info");
|
|
842
|
-
} else {
|
|
843
|
-
var toastMsg = addProjectMode === "create" ? "Project created" : addProjectMode === "clone" ? "Project cloned" : "Project added";
|
|
844
|
-
showToast(toastMsg, "success");
|
|
845
|
-
}
|
|
846
|
-
if (msg.slug) switchProject(msg.slug);
|
|
847
|
-
} else {
|
|
848
|
-
addProjectError.textContent = msg.error || "Failed to add project";
|
|
849
|
-
addProjectError.classList.remove("hidden");
|
|
850
|
-
addProjectOk.disabled = false;
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
export function handleCloneProgress(msg) {
|
|
855
|
-
if (msg.status === "cloning") {
|
|
856
|
-
addProjectCloneProgress.classList.remove("hidden");
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
function setActiveIdx(idx) {
|
|
861
|
-
var items = addProjectSuggestions.querySelectorAll(".add-project-suggestion-item");
|
|
862
|
-
addProjectActiveIdx = idx;
|
|
863
|
-
for (var ai = 0; ai < items.length; ai++) {
|
|
864
|
-
if (ai === idx) {
|
|
865
|
-
items[ai].classList.add("active");
|
|
866
|
-
items[ai].scrollIntoView({ block: "nearest" });
|
|
867
|
-
} else {
|
|
868
|
-
items[ai].classList.remove("active");
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
function submitAddProject() {
|
|
874
|
-
addProjectError.classList.add("hidden");
|
|
875
|
-
addProjectOk.disabled = true;
|
|
876
|
-
|
|
877
|
-
if (addProjectMode === "existing") {
|
|
878
|
-
var val = getFullPath(addProjectInput.value).replace(/\/+$/, "");
|
|
879
|
-
if (!val) { addProjectOk.disabled = false; return; }
|
|
880
|
-
var ws = getWs();
|
|
881
|
-
if (ws && ws.readyState === 1) {
|
|
882
|
-
ws.send(JSON.stringify({ type: "add_project", path: val }));
|
|
883
|
-
}
|
|
884
|
-
} else if (addProjectMode === "create") {
|
|
885
|
-
var name = addProjectCreateInput.value.trim();
|
|
886
|
-
if (!name) { addProjectOk.disabled = false; return; }
|
|
887
|
-
var ws2 = getWs();
|
|
888
|
-
if (ws2 && ws2.readyState === 1) {
|
|
889
|
-
ws2.send(JSON.stringify({ type: "create_project", name: name }));
|
|
890
|
-
}
|
|
891
|
-
} else if (addProjectMode === "clone") {
|
|
892
|
-
var url = addProjectCloneInput.value.trim();
|
|
893
|
-
if (!url) { addProjectOk.disabled = false; return; }
|
|
894
|
-
var ws3 = getWs();
|
|
895
|
-
if (ws3 && ws3.readyState === 1) {
|
|
896
|
-
ws3.send(JSON.stringify({ type: "clone_project", url: url }));
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
}
|
|
@@ -238,9 +238,16 @@ function tickRateLimitUsage() {
|
|
|
238
238
|
renderRateLimitUsage();
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
// Once the server's vendor overview has answered, every vendor including the
|
|
242
|
+
// displayed one is shown by the overview chips, so this session-scoped link
|
|
243
|
+
// stands down. Rejection, warning and scheduling state is untouched.
|
|
244
|
+
function overviewOwnsUsageDisplay() {
|
|
245
|
+
return !!store.get('vendorUsageOverview');
|
|
246
|
+
}
|
|
247
|
+
|
|
241
248
|
function renderRateLimitUsage() {
|
|
242
249
|
var activeVendor = store.get('currentVendor') || "claude";
|
|
243
|
-
if (!vendorTracksRateLimits(activeVendor)) {
|
|
250
|
+
if (!vendorTracksRateLimits(activeVendor) || overviewOwnsUsageDisplay()) {
|
|
244
251
|
if (rateLimitUsageEl) {
|
|
245
252
|
rateLimitUsageEl.remove();
|
|
246
253
|
rateLimitUsageEl = null;
|
|
@@ -320,6 +327,10 @@ export function initRateLimit() {
|
|
|
320
327
|
|| state.currentSlug !== prev.currentSlug || state.myUserId !== prev.myUserId) {
|
|
321
328
|
resetRateLimitState();
|
|
322
329
|
}
|
|
330
|
+
if (state.vendorUsageOverview !== prev.vendorUsageOverview && state.currentVendor === prev.currentVendor && state.activeSessionId === prev.activeSessionId
|
|
331
|
+
&& state.currentSlug === prev.currentSlug && state.myUserId === prev.myUserId) {
|
|
332
|
+
renderRateLimitUsage();
|
|
333
|
+
}
|
|
323
334
|
if (state.vendorInfo !== prev.vendorInfo && state.currentVendor === prev.currentVendor && state.activeSessionId === prev.activeSessionId
|
|
324
335
|
&& state.currentSlug === prev.currentSlug && state.myUserId === prev.myUserId) {
|
|
325
336
|
renderRateLimitUsage();
|
|
@@ -172,6 +172,7 @@ rows('clay-workspace', [
|
|
|
172
172
|
['inspect_mate_chat_change', 'Inspecting Mate conversation change', 'Mate conversation change inspected', 'Mate conversation'],
|
|
173
173
|
['control_mate_chat', 'Requesting Mate conversation change', 'Mate conversation request returned', 'Mate conversation', 'why', 'mate-chat'],
|
|
174
174
|
['control_schedules', 'Requesting schedule change', 'Schedule change request returned', 'Scheduling', 'why', 'schedule-control'],
|
|
175
|
+
['read_vendor_usage', 'Reading vendor usage', 'Vendor usage read', 'Vendor usage', 'projectSlug'],
|
|
175
176
|
['read_project_records', 'Reading project records', 'Project records read', 'Project records', 'projectSlug'],
|
|
176
177
|
['inspect_project_record_change', 'Inspecting record change', 'Record change inspected', 'Project records', 'projectSlug'],
|
|
177
178
|
['control_project_records', 'Requesting record change', 'Record change request returned', 'Project records', 'why'],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mateLoadingMarkup, updateMateLoadingScene } from './mate-loading-scene.js';
|
|
1
2
|
import { attachContextualSessionCitations } from './contextual-session-citations.js';
|
|
2
3
|
import { attachContextualConversations } from './contextual-mate-sessions.js';
|
|
3
4
|
import { getCachedSessions } from './sidebar-sessions.js';
|
|
@@ -67,6 +68,7 @@ function render() {
|
|
|
67
68
|
var mate = contextualMate(state().mateId);
|
|
68
69
|
if (mate) {
|
|
69
70
|
var name = getHomeMateName(mate);
|
|
71
|
+
updateMateLoadingScene(surface.querySelector('[data-clay-loading]'), name, mateAvatarUrl(mate, 96));
|
|
70
72
|
surface.setAttribute('aria-label', name + ' conversation');
|
|
71
73
|
surface.querySelector('strong').textContent = name;
|
|
72
74
|
var header = surface.querySelector('header');
|
|
@@ -130,7 +132,7 @@ function ensureSurface() {
|
|
|
130
132
|
if (!host()) {
|
|
131
133
|
var surface = document.createElement('section');
|
|
132
134
|
surface.id = 'contextual-clay'; surface.setAttribute('role', 'dialog');
|
|
133
|
-
surface.innerHTML = '<header><img class="contextual-clay-avatar" alt=""><div class="contextual-clay-identity"><strong></strong><div class="contextual-context-line"><span data-clay-context></span><details class="contextual-clay-context"><summary aria-label="What this Mate can see" title="What this Mate can see">' + iconHtml('circle-help') + '</summary><p></p></details></div></div><div class="contextual-clay-actions"><button type="button" data-clay-expand>' + iconHtml('arrow-up-right') + '<span></span></button><button type="button" data-clay-close title="Close">' + iconHtml('x') + '</button></div></header><p class="contextual-capabilities" role="note" aria-label="Conversation capabilities"></p><div class="contextual-clay-body"
|
|
135
|
+
surface.innerHTML = '<header><img class="contextual-clay-avatar" alt=""><div class="contextual-clay-identity"><strong></strong><div class="contextual-context-line"><span data-clay-context></span><details class="contextual-clay-context"><summary aria-label="What this Mate can see" title="What this Mate can see">' + iconHtml('circle-help') + '</summary><p></p></details></div></div><div class="contextual-clay-actions"><button type="button" data-clay-expand>' + iconHtml('arrow-up-right') + '<span></span></button><button type="button" data-clay-close title="Close">' + iconHtml('x') + '</button></div></header><p class="contextual-capabilities" role="note" aria-label="Conversation capabilities"></p><div class="contextual-clay-body">' + mateLoadingMarkup() + '</div>';
|
|
134
136
|
surface.addEventListener('contextual-clay-keydown', function (event) {
|
|
135
137
|
var detail = event.detail; var frame = contextualFrame();
|
|
136
138
|
if (!detail || !frame || detail.source !== frame.contentWindow) return;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// github-star.js - Public star count beside the top-bar GitHub Star link.
|
|
2
|
+
//
|
|
3
|
+
// The link itself is static markup. This module only fills in the count read
|
|
4
|
+
// from the server's cached GET /api/github-stars. An unknown count leaves the
|
|
5
|
+
// count segment hidden; nothing is invented, and the module never writes to
|
|
6
|
+
// GitHub or sends anything about the user.
|
|
7
|
+
|
|
8
|
+
import { store } from './store.js';
|
|
9
|
+
|
|
10
|
+
var REFRESH_MS = 60 * 60 * 1000;
|
|
11
|
+
var MOBILE_QUERY = "(max-width: 768px)";
|
|
12
|
+
|
|
13
|
+
var timer = null;
|
|
14
|
+
var inflight = false;
|
|
15
|
+
|
|
16
|
+
// 999 -> "999", 1234 -> "1.2k", 12500 -> "12.5k", 1200000 -> "1.2m"
|
|
17
|
+
export function formatStarCount(n) {
|
|
18
|
+
if (typeof n !== "number" || !isFinite(n) || n < 0) return "";
|
|
19
|
+
n = Math.floor(n);
|
|
20
|
+
if (n < 1000) return String(n);
|
|
21
|
+
var units = [[1000000, "m"], [1000, "k"]];
|
|
22
|
+
for (var i = 0; i < units.length; i++) {
|
|
23
|
+
if (n >= units[i][0]) {
|
|
24
|
+
var v = Math.floor(n / units[i][0] * 10) / 10;
|
|
25
|
+
return (v % 1 === 0 ? String(v) : v.toFixed(1)) + units[i][1];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return String(n);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function render() {
|
|
32
|
+
var count = store.get("githubStarCount");
|
|
33
|
+
var countEl = document.getElementById("github-star-count");
|
|
34
|
+
var linkEl = document.getElementById("github-star-link");
|
|
35
|
+
if (!countEl || !linkEl) return;
|
|
36
|
+
var text = typeof count === "number" ? formatStarCount(count) : "";
|
|
37
|
+
countEl.textContent = text;
|
|
38
|
+
countEl.classList.toggle("hidden", !text);
|
|
39
|
+
linkEl.setAttribute("aria-label", text ? "Star Clay on GitHub, " + count + " stars" : "Star Clay on GitHub");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isMobile() {
|
|
43
|
+
return !!(window.matchMedia && window.matchMedia(MOBILE_QUERY).matches);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function load() {
|
|
47
|
+
if (inflight || isMobile()) return Promise.resolve();
|
|
48
|
+
inflight = true;
|
|
49
|
+
return fetch("/api/github-stars", { credentials: "same-origin" }).then(function (res) {
|
|
50
|
+
return res.ok ? res.json() : null;
|
|
51
|
+
}).then(function (data) {
|
|
52
|
+
// Keep the last known count when the server has none or the call failed.
|
|
53
|
+
if (data && typeof data.stars === "number") {
|
|
54
|
+
store.set({ githubStarCount: data.stars });
|
|
55
|
+
render();
|
|
56
|
+
}
|
|
57
|
+
}).catch(function () {}).then(function () { inflight = false; });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function initGithubStar() {
|
|
61
|
+
if (!document.getElementById("github-star")) return;
|
|
62
|
+
render();
|
|
63
|
+
load();
|
|
64
|
+
if (timer) clearInterval(timer);
|
|
65
|
+
timer = setInterval(load, REFRESH_MS);
|
|
66
|
+
}
|
|
@@ -11,7 +11,6 @@ function explicitProjectPath(pathname) {
|
|
|
11
11
|
export function resolveHomeBootDestination(options) {
|
|
12
12
|
if (!options) return "wait";
|
|
13
13
|
if (options.paneMode || explicitProjectPath(options.pathname)) return "project";
|
|
14
|
-
if (/^\/projects\/?$/.test(options.pathname || "")) return "projects";
|
|
15
14
|
// The server-selected project embedded in the root shell is authoritative.
|
|
16
15
|
// Home is entered only through an explicit in-app action; reloading `/`
|
|
17
16
|
// returns to the project workspace even if Home was the last open surface.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Decorative opening state; conversation readiness remains with its host.
|
|
2
|
+
export function mateLoadingMarkup() {
|
|
3
|
+
return '<div data-clay-loading class="mate-loading" role="status" aria-live="polite">' +
|
|
4
|
+
'<div class="mate-loading-scene" aria-hidden="true">' +
|
|
5
|
+
'<img class="mate-loading-avatar" alt="">' +
|
|
6
|
+
'<img class="mate-loading-desk" src="/welcome-art/conversation-desk.png" alt="" decoding="async">' +
|
|
7
|
+
'</div><p class="mate-loading-label"></p>' +
|
|
8
|
+
'<div class="mate-loading-dots" aria-hidden="true"><span></span><span></span><span></span></div></div>';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function updateMateLoadingScene(element, name, avatar) {
|
|
12
|
+
var image = element.querySelector('.mate-loading-avatar');
|
|
13
|
+
if (image.getAttribute('src') !== avatar) image.setAttribute('src', avatar);
|
|
14
|
+
var label = element.querySelector('.mate-loading-label');
|
|
15
|
+
var text = 'Getting ready with ' + name;
|
|
16
|
+
if (label.textContent !== text) label.textContent = text;
|
|
17
|
+
}
|