clay-server 5.2.0-beta.10 → 5.2.0-beta.11
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 +3 -0
- package/lib/public/app.js +25 -17
- 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 +79 -0
- package/lib/public/index.html +50 -63
- 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/mate-loading-scene.js +17 -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.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 +33 -14
- package/lib/public/modules/projects-hub.js +10 -6
- 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 +10 -2
- package/lib/public/modules/user-settings-layout.js +8 -33
- 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 +312 -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 +4 -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 +2 -0
- 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
|
@@ -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
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -135,9 +135,12 @@ export function uploadAvatarBlob(blob) {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
// --- Avatar position picker ---
|
|
138
|
-
|
|
138
|
+
// With a host element the editor mounts inline as a settings subview instead
|
|
139
|
+
// of a page-level overlay.
|
|
140
|
+
export function showAvatarPositioner(img, objectUrl, onDone, host) {
|
|
139
141
|
var outputSize = 256;
|
|
140
142
|
var viewSize = 220;
|
|
143
|
+
var priorFocus = document.activeElement;
|
|
141
144
|
|
|
142
145
|
// State
|
|
143
146
|
var scale = 1;
|
|
@@ -169,7 +172,7 @@ export function showAvatarPositioner(img, objectUrl, onDone) {
|
|
|
169
172
|
|
|
170
173
|
// Build overlay
|
|
171
174
|
var overlay = document.createElement('div');
|
|
172
|
-
overlay.className = 'avatar-positioner-overlay';
|
|
175
|
+
overlay.className = host ? 'avatar-positioner-inline settings-subview' : 'avatar-positioner-overlay';
|
|
173
176
|
|
|
174
177
|
var container = document.createElement('div');
|
|
175
178
|
container.className = 'avatar-positioner-container';
|
|
@@ -225,8 +228,10 @@ export function showAvatarPositioner(img, objectUrl, onDone) {
|
|
|
225
228
|
container.appendChild(btnRow);
|
|
226
229
|
|
|
227
230
|
overlay.appendChild(container);
|
|
228
|
-
|
|
231
|
+
if (host) { host.classList.add('has-subview'); host.appendChild(overlay); overlay.setAttribute('role', 'region'); overlay.setAttribute('aria-label', 'Position your avatar'); }
|
|
232
|
+
else document.body.appendChild(overlay);
|
|
229
233
|
updatePreview();
|
|
234
|
+
if (host) doneFocus();
|
|
230
235
|
|
|
231
236
|
// Drag
|
|
232
237
|
var dragging = false;
|
|
@@ -288,9 +293,12 @@ export function showAvatarPositioner(img, objectUrl, onDone) {
|
|
|
288
293
|
updatePreview();
|
|
289
294
|
}, { passive: false });
|
|
290
295
|
|
|
296
|
+
function doneFocus() { closeBtn.focus(); }
|
|
297
|
+
|
|
291
298
|
function cleanup(revoke) {
|
|
292
299
|
document.removeEventListener('keydown', onOverlayKeydown);
|
|
293
300
|
overlay.remove();
|
|
301
|
+
if (host) { host.classList.remove('has-subview'); if (priorFocus && priorFocus.isConnected) priorFocus.focus(); }
|
|
294
302
|
if (revoke) URL.revokeObjectURL(objectUrl);
|
|
295
303
|
}
|
|
296
304
|
|
|
@@ -81,6 +81,9 @@ function startMateCreation() {
|
|
|
81
81
|
maybeStartPendingCreation();
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// Entry for the zero-project welcome: the same Clay-guided flow the add button's Mate choice runs.
|
|
85
|
+
export function startClayMateCreation() { startMateCreation(); }
|
|
86
|
+
|
|
84
87
|
function maybeStartPendingCreation() {
|
|
85
88
|
var state = store.snap();
|
|
86
89
|
if (!state.projectMateCreationPendingStart || !state.connected || state.activeProjectSlug !== 'mate-' + state.projectMateCreationMateId) return;
|
|
@@ -89,11 +92,17 @@ function maybeStartPendingCreation() {
|
|
|
89
92
|
store.set({ projectMateCreationPendingStart: false, projectMateCreationAwaitingHistory: true, projectMateCreationRequestId: requestId });
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
function choiceButton(
|
|
95
|
+
function choiceButton(artwork, title, description) {
|
|
93
96
|
var button = document.createElement('button');
|
|
94
97
|
button.type = 'button';
|
|
95
98
|
button.className = 'project-create-choice-option';
|
|
96
|
-
|
|
99
|
+
var image = document.createElement('img');
|
|
100
|
+
image.src = '/welcome-art/' + artwork + '.png';
|
|
101
|
+
image.alt = '';
|
|
102
|
+
image.width = 1536;
|
|
103
|
+
image.height = 1024;
|
|
104
|
+
image.decoding = 'async';
|
|
105
|
+
button.appendChild(image);
|
|
97
106
|
var copy = document.createElement('span');
|
|
98
107
|
var heading = document.createElement('strong');
|
|
99
108
|
heading.textContent = title;
|
|
@@ -121,9 +130,9 @@ export function openProjectCreateChoice(opener, openProject) {
|
|
|
121
130
|
var title = document.createElement('h2');
|
|
122
131
|
title.id = 'project-create-choice-title';
|
|
123
132
|
title.textContent = 'What would you like to add?';
|
|
124
|
-
var mate = choiceButton('
|
|
133
|
+
var mate = choiceButton('mate-clay', 'Create a Mate', 'A specialist to work alongside you.');
|
|
125
134
|
mate.addEventListener('click', startMateCreation);
|
|
126
|
-
var project = choiceButton('
|
|
135
|
+
var project = choiceButton('project-clay', 'Start a project', 'Work on code or files together.');
|
|
127
136
|
project.addEventListener('click', function () {
|
|
128
137
|
closeChoice(false);
|
|
129
138
|
openProject();
|