clay-server 5.0.2-beta.1 → 5.1.0-beta.2
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/lib/daemon-project-access.js +7 -1
- package/lib/daemon.js +2 -0
- 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/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/project-connection.js +36 -2
- 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-file-path.js +2 -3
- package/lib/project-knowledge.js +315 -136
- package/lib/project-mate-creation-proposal.js +1 -0
- package/lib/project-request-access.js +13 -1
- package/lib/project-sessions.js +39 -2
- package/lib/project-user-knowledge.js +60 -0
- package/lib/project.js +59 -18
- package/lib/public/app.js +73 -34
- 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/filebrowser.css +10 -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/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/right-workbench.css +3 -3
- package/lib/public/css/session-folders.css +80 -21
- package/lib/public/css/sidebar.css +183 -107
- package/lib/public/css/title-bar.css +151 -0
- 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 +33 -101
- package/lib/public/modules/account-island.js +230 -0
- package/lib/public/modules/app-cursors.js +19 -43
- package/lib/public/modules/app-debate-ui.js +4 -3
- 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 +34 -9
- package/lib/public/modules/app-notifications.js +4 -7
- package/lib/public/modules/app-projects.js +5 -38
- 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/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 +81 -65
- package/lib/public/modules/filebrowser.js +28 -8
- 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/html-preview.js +19 -0
- 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/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/profile.js +51 -18
- 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-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/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-list-visibility.js +41 -0
- package/lib/public/modules/session-presence-ui.js +4 -1
- package/lib/public/modules/session-title-marquee.js +51 -0
- 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/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/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 +16 -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/server-admin.js +11 -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-mate-instructions.js +44 -0
- package/lib/server-mates.js +12 -3
- package/lib/server-palette.js +1 -1
- package/lib/server-settings.js +65 -1
- package/lib/server-user-knowledge.js +119 -0
- package/lib/server.js +21 -1
- package/lib/session-visibility.js +7 -2
- package/lib/sessions.js +32 -6
- package/lib/user-knowledge-service.js +167 -0
- package/lib/users-permissions.js +7 -0
- package/lib/users-project-mate-filter-preferences.js +39 -0
- package/lib/users.js +6 -0
- package/lib/workspace-query-access.js +4 -1
- package/lib/ws-schema.js +50 -0
- package/package.json +10 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// Mate avatar choices hosted inside the current settings dialog.
|
|
2
|
+
|
|
3
|
+
import { avatarUrl, mateAvatarUrl, MATE_AVATAR_STYLES } from './avatar.js';
|
|
4
|
+
import { showAvatarPositioner } from './profile.js';
|
|
5
|
+
import { getWs } from './ws-ref.js';
|
|
6
|
+
import { store } from './store.js';
|
|
7
|
+
|
|
8
|
+
var activeToken = 0;
|
|
9
|
+
var activeMateId = null;
|
|
10
|
+
var pending = null;
|
|
11
|
+
var statusByMate = Object.create(null);
|
|
12
|
+
var draftByMate = Object.create(null);
|
|
13
|
+
|
|
14
|
+
function mateName(mate) {
|
|
15
|
+
var profile = mate && mate.profile ? mate.profile : {};
|
|
16
|
+
return profile.displayName || (mate && (mate.displayName || mate.name)) || 'Mate';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function mateProfile(mate) {
|
|
20
|
+
return Object.assign({}, mate && mate.profile ? mate.profile : {});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function sendProfile(mate, profile, rerender, message) {
|
|
24
|
+
draftByMate[mate.id] = profile;
|
|
25
|
+
var ws = getWs();
|
|
26
|
+
if (!ws || ws.readyState !== 1) {
|
|
27
|
+
statusByMate[mate.id] = { kind: 'error', text: 'Clay is offline. Your choice was not saved.' };
|
|
28
|
+
rerender();
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
pending = { mateId: mate.id, style: profile.avatarStyle || 'imprint', seed: profile.avatarSeed || '', custom: profile.avatarCustom || '' };
|
|
32
|
+
statusByMate[mate.id] = { kind: 'pending', text: message || 'Saving avatar…' };
|
|
33
|
+
ws.send(JSON.stringify({ type: 'mate_update', mateId: mate.id, updates: { profile: profile } }));
|
|
34
|
+
rerender();
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function appendStatus(body, mate) {
|
|
39
|
+
var state = statusByMate[mate.id];
|
|
40
|
+
var status = document.createElement('div');
|
|
41
|
+
status.className = 'home-mate-avatar-status' + (state ? ' is-' + state.kind : '');
|
|
42
|
+
status.setAttribute('aria-live', 'polite');
|
|
43
|
+
status.textContent = state ? state.text : '';
|
|
44
|
+
body.appendChild(status);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function createChoice(label, src, selected) {
|
|
48
|
+
var button = document.createElement('button');
|
|
49
|
+
button.type = 'button';
|
|
50
|
+
button.className = 'home-mate-avatar-choice';
|
|
51
|
+
button.setAttribute('aria-label', label + ' avatar');
|
|
52
|
+
button.setAttribute('aria-pressed', selected ? 'true' : 'false');
|
|
53
|
+
button.title = label;
|
|
54
|
+
var image = document.createElement('img');
|
|
55
|
+
image.src = src;
|
|
56
|
+
image.alt = '';
|
|
57
|
+
image.draggable = false;
|
|
58
|
+
var text = document.createElement('span');
|
|
59
|
+
text.textContent = label;
|
|
60
|
+
button.appendChild(image);
|
|
61
|
+
button.appendChild(text);
|
|
62
|
+
return button;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function selectStyle(mate, style, seed, rerender) {
|
|
66
|
+
var profile = mateProfile(mate);
|
|
67
|
+
profile.avatarStyle = style;
|
|
68
|
+
profile.avatarSeed = seed;
|
|
69
|
+
profile.avatarCustom = '';
|
|
70
|
+
sendProfile(mate, profile, rerender);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function uploadBlob(mate, blob, token, rerender) {
|
|
74
|
+
pending = null;
|
|
75
|
+
statusByMate[mate.id] = { kind: 'pending', text: 'Uploading image…' };
|
|
76
|
+
rerender();
|
|
77
|
+
blob.arrayBuffer().then(function (buffer) {
|
|
78
|
+
return fetch('/api/mate-avatar/' + encodeURIComponent(mate.id), {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { 'Content-Type': 'application/octet-stream' },
|
|
81
|
+
body: new Uint8Array(buffer),
|
|
82
|
+
});
|
|
83
|
+
}).then(function (response) {
|
|
84
|
+
return response.json().then(function (data) {
|
|
85
|
+
if (!response.ok || !data.ok || !data.avatar) throw new Error(data.error || 'Upload failed');
|
|
86
|
+
return data.avatar;
|
|
87
|
+
});
|
|
88
|
+
}).then(function (avatar) {
|
|
89
|
+
if (token !== activeToken) return;
|
|
90
|
+
var profile = mateProfile(mate);
|
|
91
|
+
profile.avatarCustom = avatar;
|
|
92
|
+
draftByMate[mate.id] = profile;
|
|
93
|
+
statusByMate[mate.id] = { kind: 'saved', text: 'Image saved.' };
|
|
94
|
+
var mates = store.get('cachedMatesList') || [];
|
|
95
|
+
store.set({ cachedMatesList: mates.map(function (item) {
|
|
96
|
+
return item && item.id === mate.id ? Object.assign({}, item, { profile: profile }) : item;
|
|
97
|
+
}) });
|
|
98
|
+
var ws = getWs();
|
|
99
|
+
if (ws && ws.readyState === 1) {
|
|
100
|
+
ws.send(JSON.stringify({ type: 'mate_update', mateId: mate.id, updates: { profile: profile } }));
|
|
101
|
+
}
|
|
102
|
+
rerender();
|
|
103
|
+
}).catch(function (error) {
|
|
104
|
+
if (token !== activeToken) return;
|
|
105
|
+
statusByMate[mate.id] = { kind: 'error', text: error && error.message ? error.message : 'Could not upload this image.' };
|
|
106
|
+
rerender();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function openUpload(mate, input, rerender) {
|
|
111
|
+
var file = input.files && input.files[0];
|
|
112
|
+
if (!file) return;
|
|
113
|
+
var token = ++activeToken;
|
|
114
|
+
var image = new Image();
|
|
115
|
+
var objectUrl = URL.createObjectURL(file);
|
|
116
|
+
image.onload = function () {
|
|
117
|
+
if (token !== activeToken) {
|
|
118
|
+
URL.revokeObjectURL(objectUrl);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
showAvatarPositioner(image, objectUrl, function (blob) {
|
|
122
|
+
URL.revokeObjectURL(objectUrl);
|
|
123
|
+
if (token === activeToken) uploadBlob(mate, blob, token, rerender);
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
image.onerror = function () {
|
|
127
|
+
URL.revokeObjectURL(objectUrl);
|
|
128
|
+
if (token !== activeToken) return;
|
|
129
|
+
statusByMate[mate.id] = { kind: 'error', text: 'Could not read this image.' };
|
|
130
|
+
rerender();
|
|
131
|
+
};
|
|
132
|
+
image.src = objectUrl;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function renderHomeMateAvatarEditor(body, mate, rerender) {
|
|
136
|
+
if (!mate) return;
|
|
137
|
+
if (activeMateId !== mate.id) {
|
|
138
|
+
activeMateId = mate.id;
|
|
139
|
+
activeToken++;
|
|
140
|
+
}
|
|
141
|
+
var profile = draftByMate[mate.id] || mateProfile(mate);
|
|
142
|
+
var name = mateName(mate);
|
|
143
|
+
var style = profile.avatarStyle || 'imprint';
|
|
144
|
+
var seed = profile.avatarSeed || mate.id || name;
|
|
145
|
+
var locked = profile.avatarLocked === true;
|
|
146
|
+
var section = document.createElement('section');
|
|
147
|
+
section.className = 'home-mate-avatar-editor';
|
|
148
|
+
var heading = document.createElement('div');
|
|
149
|
+
heading.className = 'home-mate-avatar-heading';
|
|
150
|
+
var preview = document.createElement('img');
|
|
151
|
+
preview.className = 'home-mate-avatar-preview';
|
|
152
|
+
preview.src = mateAvatarUrl(Object.assign({}, mate, { profile: profile }), 72);
|
|
153
|
+
preview.alt = name + ' avatar';
|
|
154
|
+
var copy = document.createElement('div');
|
|
155
|
+
var title = document.createElement('h3');
|
|
156
|
+
title.textContent = 'Avatar';
|
|
157
|
+
var note = document.createElement('p');
|
|
158
|
+
note.textContent = locked ? 'This built-in avatar is managed by Clay.' : 'Choose a locally generated style or upload your own image.';
|
|
159
|
+
copy.appendChild(title);
|
|
160
|
+
copy.appendChild(note);
|
|
161
|
+
heading.appendChild(preview);
|
|
162
|
+
heading.appendChild(copy);
|
|
163
|
+
section.appendChild(heading);
|
|
164
|
+
if (!locked) {
|
|
165
|
+
var choices = document.createElement('div');
|
|
166
|
+
choices.className = 'home-mate-avatar-choices';
|
|
167
|
+
choices.setAttribute('role', 'group');
|
|
168
|
+
choices.setAttribute('aria-label', 'Avatar for ' + name);
|
|
169
|
+
for (var i = 0; i < MATE_AVATAR_STYLES.length; i++) {
|
|
170
|
+
(function (entry) {
|
|
171
|
+
var choice = createChoice(entry.name, avatarUrl(entry.id, seed, 48), !profile.avatarCustom && style === entry.id);
|
|
172
|
+
choice.addEventListener('click', function () { selectStyle(mate, entry.id, seed, rerender); });
|
|
173
|
+
choices.appendChild(choice);
|
|
174
|
+
})(MATE_AVATAR_STYLES[i]);
|
|
175
|
+
}
|
|
176
|
+
section.appendChild(choices);
|
|
177
|
+
var actions = document.createElement('div');
|
|
178
|
+
actions.className = 'home-mate-avatar-actions';
|
|
179
|
+
var shuffle = document.createElement('button');
|
|
180
|
+
shuffle.type = 'button';
|
|
181
|
+
shuffle.textContent = 'Shuffle selected style';
|
|
182
|
+
shuffle.disabled = style === 'imprint' || style === 'initial' || !!profile.avatarCustom;
|
|
183
|
+
shuffle.addEventListener('click', function () {
|
|
184
|
+
selectStyle(mate, style, Math.random().toString(36).substring(2, 10), rerender);
|
|
185
|
+
});
|
|
186
|
+
var upload = document.createElement('button');
|
|
187
|
+
upload.type = 'button';
|
|
188
|
+
upload.textContent = profile.avatarCustom ? 'Replace image' : 'Upload image';
|
|
189
|
+
var input = document.createElement('input');
|
|
190
|
+
input.type = 'file';
|
|
191
|
+
input.accept = 'image/png,image/jpeg,image/gif,image/webp';
|
|
192
|
+
input.className = 'home-mate-avatar-file-input';
|
|
193
|
+
input.hidden = true;
|
|
194
|
+
input.tabIndex = -1;
|
|
195
|
+
input.setAttribute('aria-hidden', 'true');
|
|
196
|
+
input.addEventListener('change', function () { openUpload(mate, input, rerender); });
|
|
197
|
+
upload.addEventListener('click', function () { input.click(); });
|
|
198
|
+
actions.appendChild(shuffle);
|
|
199
|
+
actions.appendChild(upload);
|
|
200
|
+
actions.appendChild(input);
|
|
201
|
+
section.appendChild(actions);
|
|
202
|
+
}
|
|
203
|
+
appendStatus(section, mate);
|
|
204
|
+
body.appendChild(section);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function confirmHomeMateAvatar(mate) {
|
|
208
|
+
if (!mate || !pending || pending.mateId !== mate.id) return false;
|
|
209
|
+
var profile = mateProfile(mate);
|
|
210
|
+
if ((profile.avatarStyle || 'imprint') !== pending.style || (profile.avatarSeed || '') !== pending.seed || (profile.avatarCustom || '') !== pending.custom) return false;
|
|
211
|
+
pending = null;
|
|
212
|
+
delete draftByMate[mate.id];
|
|
213
|
+
statusByMate[mate.id] = { kind: 'saved', text: 'Avatar saved.' };
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function failHomeMateAvatar(mateId, message) {
|
|
218
|
+
if (!pending || !mateId || pending.mateId !== mateId) return false;
|
|
219
|
+
statusByMate[pending.mateId] = { kind: 'error', text: message || 'Could not save this avatar.' };
|
|
220
|
+
pending = null;
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function clearHomeMateAvatarEditor() {
|
|
225
|
+
activeToken++;
|
|
226
|
+
activeMateId = null;
|
|
227
|
+
pending = null;
|
|
228
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { MATE_WORKSPACE_COLORS, workspaceColor } from './mate-workspace-color.js';
|
|
4
|
+
|
|
5
|
+
var timer = null;
|
|
6
|
+
var sequence = 0;
|
|
7
|
+
function state() { return store.get('mateColorSave'); }
|
|
8
|
+
function put(value) { store.set({ mateColorSave: value }); }
|
|
9
|
+
export function resetMateColor() { clearTimeout(timer); put(null); }
|
|
10
|
+
export function mateColorSaving() { return !!(state() && state().requestId); }
|
|
11
|
+
export function applyMateColorResult(msg) {
|
|
12
|
+
var s = state();
|
|
13
|
+
if (!s || !s.requestId || msg.requestId !== s.requestId || (msg.mate ? msg.mate.id : msg.mateId) !== s.mateId) return false;
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
put(Object.assign({}, s, { requestId: null, error: msg.mate ? '' : (msg.error || 'Could not save the color. Try again.'), saved: !!msg.mate }));
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
export function renderMateColorPicker(body, mate, render) {
|
|
19
|
+
if (!mate || mate.builtinKey === 'clay' || mate.primary) return;
|
|
20
|
+
var color = workspaceColor(mate);
|
|
21
|
+
var s = state();
|
|
22
|
+
if (s && s.mateId !== mate.id) s = null;
|
|
23
|
+
var section = document.createElement('section'); section.className = 'mate-color-picker';
|
|
24
|
+
var title = document.createElement('h4'); title.textContent = 'Workspace color'; section.appendChild(title);
|
|
25
|
+
var help = document.createElement('p'); help.className = 'mate-settings-help';
|
|
26
|
+
help.textContent = 'Give this Mate its own color. Soft in daylight, muted after dark.';
|
|
27
|
+
section.appendChild(help);
|
|
28
|
+
var preview = document.createElement('div'); preview.className = 'mate-color-previews';
|
|
29
|
+
['light', 'dark'].forEach(function (theme) {
|
|
30
|
+
var sample = document.createElement('div'); sample.className = 'mate-color-preview ' + theme;
|
|
31
|
+
sample.style.backgroundColor = color[theme];
|
|
32
|
+
sample.innerHTML = '<span class="mate-color-preview-label">' + (theme === 'light' ? 'Light' : 'Dark') + '</span><div class="mate-color-preview-canvas"><strong>Room to think</strong><span>A little color. A clear mind.</span></div>';
|
|
33
|
+
preview.appendChild(sample);
|
|
34
|
+
});
|
|
35
|
+
section.appendChild(preview);
|
|
36
|
+
var swatches = document.createElement('div'); swatches.className = 'mate-color-swatches';
|
|
37
|
+
swatches.setAttribute('role', 'group'); swatches.setAttribute('aria-label', 'Workspace color');
|
|
38
|
+
MATE_WORKSPACE_COLORS.forEach(function (choice) {
|
|
39
|
+
var button = document.createElement('button'); button.type = 'button'; button.id = 'mate-color-' + choice.id;
|
|
40
|
+
button.className = 'mate-color-swatch'; button.style.setProperty('--swatch', choice.pigment);
|
|
41
|
+
button.title = choice.label; button.setAttribute('aria-label', choice.label);
|
|
42
|
+
button.setAttribute('aria-pressed', String(color.id === choice.id));
|
|
43
|
+
button.setAttribute('aria-disabled', String(!!(s && s.requestId)));
|
|
44
|
+
if (color.id === choice.id) button.textContent = '✓';
|
|
45
|
+
button.addEventListener('click', function () {
|
|
46
|
+
if (mateColorSaving()) return;
|
|
47
|
+
var ws = getWs();
|
|
48
|
+
if (!ws || ws.readyState !== 1) { put({mateId:mate.id,error:'Clay is offline. Reconnect and try again.'}); render(); return; }
|
|
49
|
+
var requestId = 'mate-color-' + Date.now() + '-' + (++sequence);
|
|
50
|
+
put({mateId:mate.id,requestId:requestId,color:choice.id,error:''}); render();
|
|
51
|
+
try { ws.send(JSON.stringify({type:'mate_update',mateId:mate.id,requestId:requestId,updates:{workspaceColor:choice.id}})); }
|
|
52
|
+
catch (error) { put({mateId:mate.id,error:'Could not send the color. Try again.'}); render(); return; }
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
timer = setTimeout(function () {
|
|
55
|
+
if (state() && state().requestId === requestId) {
|
|
56
|
+
put({mateId:mate.id,error:'No response received. Reconnect to check the saved color before retrying.'}); render();
|
|
57
|
+
}
|
|
58
|
+
},15000);
|
|
59
|
+
});
|
|
60
|
+
swatches.appendChild(button);
|
|
61
|
+
});
|
|
62
|
+
section.appendChild(swatches);
|
|
63
|
+
var status = document.createElement('p'); status.className = 'mate-settings-status'; status.setAttribute('role', s && s.error ? 'alert' : 'status');
|
|
64
|
+
status.textContent = (s && s.error) || (s && s.requestId ? 'Saving color…' : color.label + (s && s.saved ? ' · Saved' : ''));
|
|
65
|
+
section.appendChild(status); body.appendChild(section);
|
|
66
|
+
}
|
|
@@ -81,7 +81,7 @@ export function applyHomeMateProposal(messages, msg) {
|
|
|
81
81
|
export function resolveHomeMateProposal(messages, msg) {
|
|
82
82
|
for (var i = 0; i < messages.length; i++) {
|
|
83
83
|
if (messages[i].role !== "mate_proposal" || !messages[i].proposal || messages[i].proposal.proposalId !== msg.proposalId) continue;
|
|
84
|
-
messages[i] = Object.assign({}, messages[i], { status: msg.action === "create" ? "created" : (msg.action === "cancel" ? "cancelled" : "pending"), mateId: msg.
|
|
84
|
+
messages[i] = Object.assign({}, messages[i], { status: msg.action === "create" ? "created" : (msg.action === "cancel" ? "cancelled" : "pending"), mateId: msg.createdMateId || null, mateName: msg.mateName || "", error: msg.error || "" });
|
|
85
85
|
}
|
|
86
86
|
return messages;
|
|
87
87
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
|
|
4
|
+
var sequence = 0;
|
|
5
|
+
var timer = null;
|
|
6
|
+
function state() { return store.get('mateInstructionsDraft'); }
|
|
7
|
+
function put(value) { store.set({mateInstructionsDraft: value}); }
|
|
8
|
+
export function resetMateInstructions(mateId) {
|
|
9
|
+
clearTimeout(timer);
|
|
10
|
+
put(mateId ? {mateId: mateId, content: '', saved: '', revision: '', status: 'idle', error: '', requestId: null} : null);
|
|
11
|
+
}
|
|
12
|
+
export function hasMateInstructionDraft() { var s = state(); return !!s && (s.content !== s.saved || s.status === 'saving'); }
|
|
13
|
+
function request(save, render) {
|
|
14
|
+
var s = state();
|
|
15
|
+
if (!s || s.status === 'saving') return;
|
|
16
|
+
var id = 'mate-instructions-' + Date.now() + '-' + (++sequence);
|
|
17
|
+
var ws = getWs();
|
|
18
|
+
if (!ws || ws.readyState !== 1) { put(Object.assign({}, s, {error: 'Clay is offline. Reconnect and try again.', status: s.revision ? 'ready' : 'error'})); render(); return; }
|
|
19
|
+
put(Object.assign({}, s, {requestId: id, status: save ? 'saving' : 'loading', error: ''}));
|
|
20
|
+
render();
|
|
21
|
+
try { ws.send(JSON.stringify({type: save ? 'mate_instructions_set' : 'mate_instructions_get', mateId: s.mateId, requestId: id, content: save ? s.content : undefined, revision: save ? s.revision : undefined})); }
|
|
22
|
+
catch (error) { put(Object.assign({}, state(), {status: s.revision ? 'ready' : 'error', requestId: null, error: 'Could not send. Your draft is retained.'})); render(); return; }
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
timer = setTimeout(function () {
|
|
25
|
+
var current = state();
|
|
26
|
+
if (!current || current.requestId !== id) return;
|
|
27
|
+
put(Object.assign({}, current, {requestId: null, status: current.revision ? 'ready' : 'error', error: 'No response received. Retry to check the saved prompt.'}));
|
|
28
|
+
render();
|
|
29
|
+
}, 15000);
|
|
30
|
+
}
|
|
31
|
+
export function loadMateInstructions(render) { request(false, render); }
|
|
32
|
+
export function applyMateInstructions(msg) {
|
|
33
|
+
var s = state();
|
|
34
|
+
if (!s || msg.mateId !== s.mateId || msg.requestId !== s.requestId) return false;
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
if (!msg.ok) put(Object.assign({}, s, {status: s.revision ? 'ready' : 'error', requestId: null, conflict: !!msg.conflict, error: msg.error || 'Could not save the prompt.'}));
|
|
37
|
+
else put(Object.assign({}, s, {status: 'ready', requestId: null, content: msg.content, saved: msg.content, revision: msg.revision, conflict: false, error: '', notice: msg.operation === 'save' ? 'Prompt saved. New conversations use these instructions.' : ''}));
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
export function renderMateInstructions(body, render) {
|
|
41
|
+
var s = state();
|
|
42
|
+
if (!s) return;
|
|
43
|
+
var section = document.createElement('section');
|
|
44
|
+
section.className = 'mate-instructions';
|
|
45
|
+
section.innerHTML = '<p class="mate-settings-help">The identity and instructions this Mate uses. Clay manages its system capabilities separately.</p>';
|
|
46
|
+
body.appendChild(section);
|
|
47
|
+
if (s.status === 'idle' || (s.status === 'loading' && !s.revision)) {
|
|
48
|
+
section.insertAdjacentHTML('beforeend', '<p role="status" class="mate-settings-help">Loading prompt…</p>');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (s.revision) {
|
|
52
|
+
var input = document.createElement('textarea');
|
|
53
|
+
input.className = 'mate-instructions-input';
|
|
54
|
+
input.id = 'mate-prompt-input';
|
|
55
|
+
input.setAttribute('aria-label', 'Mate prompt');
|
|
56
|
+
input.spellcheck = false;
|
|
57
|
+
input.value = s.content;
|
|
58
|
+
input.disabled = s.status === 'saving' || s.status === 'loading';
|
|
59
|
+
section.appendChild(input);
|
|
60
|
+
input.addEventListener('input', function () { put(Object.assign({}, state(), {content: input.value, notice: ''})); updateActions(); });
|
|
61
|
+
}
|
|
62
|
+
var status = document.createElement('p');
|
|
63
|
+
status.className = 'mate-settings-status' + (s.error ? ' is-error' : '');
|
|
64
|
+
status.setAttribute('role', s.error ? 'alert' : 'status');
|
|
65
|
+
status.textContent = s.error || s.notice || (s.status === 'saving' ? 'Saving prompt…' : '');
|
|
66
|
+
section.appendChild(status);
|
|
67
|
+
var actions = document.createElement('div');
|
|
68
|
+
actions.className = 'mate-settings-savebar';
|
|
69
|
+
section.appendChild(actions);
|
|
70
|
+
function button(text, action, primary) {
|
|
71
|
+
var el = document.createElement('button'); el.type = 'button'; el.textContent = text; el.className = 'mate-settings-button' + (primary ? ' primary' : ''); el.addEventListener('click', action); actions.appendChild(el); return el;
|
|
72
|
+
}
|
|
73
|
+
function updateActions() {
|
|
74
|
+
var current = state(); actions.replaceChildren();
|
|
75
|
+
if (current.conflict || !current.revision) {
|
|
76
|
+
button(current.conflict ? 'Reload saved prompt' : 'Try again', function () { request(false, render); });
|
|
77
|
+
if (current.conflict) {
|
|
78
|
+
var help = document.createElement('span'); help.textContent = 'Copy your draft before reloading.'; help.className = 'mate-settings-help'; actions.appendChild(help);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
actions.hidden = current.content === current.saved && current.status !== 'saving';
|
|
83
|
+
if (actions.hidden) return;
|
|
84
|
+
var cancel = button('Cancel', function () { put(Object.assign({}, current, {content: current.saved, error: '', notice: ''})); render(); });
|
|
85
|
+
var save = button('Save prompt', function () { request(true, render); }, true);
|
|
86
|
+
cancel.disabled = current.status === 'saving';
|
|
87
|
+
save.disabled = current.status === 'saving';
|
|
88
|
+
}
|
|
89
|
+
updateActions();
|
|
90
|
+
}
|