clay-server 4.1.0-beta.9 → 4.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/context-usage.js +47 -0
- package/lib/daemon-project-access.js +14 -0
- package/lib/daemon.js +7 -0
- package/lib/issues-mcp-server.js +1 -1
- package/lib/issues-service.js +3 -1
- package/lib/loop-registry-occurrence.js +1 -0
- package/lib/notes.js +20 -3
- package/lib/os-users.js +6 -0
- package/lib/project-connection.js +14 -3
- package/lib/project-file-path.js +13 -0
- package/lib/project-file-watch.js +19 -8
- package/lib/project-filesystem.js +10 -16
- package/lib/project-http.js +53 -48
- package/lib/project-issue-launch.js +3 -1
- package/lib/project-logs-mcp-server.js +8 -8
- package/lib/project-logs-schema.js +1 -1
- package/lib/project-logs-service.js +46 -13
- package/lib/project-logs-store.js +17 -15
- package/lib/project-logs.js +70 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-notifications.js +1 -1
- package/lib/project-scheduled-result-store.js +131 -0
- package/lib/project-scheduled-task-execution.js +35 -2
- package/lib/project-session-notes.js +11 -4
- package/lib/project-sessions.js +39 -6
- package/lib/project-user-message.js +47 -0
- package/lib/project.js +57 -3
- package/lib/public/app.js +27 -9
- package/lib/public/css/avatar-imprints.css +1 -1
- package/lib/public/css/command-palette.css +57 -19
- package/lib/public/css/git-panel.css +13 -12
- package/lib/public/css/git-placard.css +4 -4
- package/lib/public/css/issues.css +79 -32
- package/lib/public/css/mobile-nav.css +7 -0
- package/lib/public/css/notifications-center.css +3 -3
- package/lib/public/css/project-logs.css +15 -0
- package/lib/public/css/sidebar.css +7 -0
- package/lib/public/css/sticky-notes.css +29 -0
- package/lib/public/index.html +10 -0
- package/lib/public/modules/app-connection.js +9 -1
- package/lib/public/modules/app-favicon.js +10 -0
- package/lib/public/modules/app-messages.js +50 -12
- package/lib/public/modules/app-notifications.js +2 -0
- package/lib/public/modules/app-panels.js +77 -23
- package/lib/public/modules/app-projects.js +2 -0
- package/lib/public/modules/app-rate-limit.js +148 -61
- package/lib/public/modules/avatar-imprint.js +25 -70
- package/lib/public/modules/avatar.js +12 -4
- package/lib/public/modules/chat-render-runtime.js +20 -0
- package/lib/public/modules/clay-issue-links.js +24 -2
- package/lib/public/modules/clay-log-links.js +12 -1
- package/lib/public/modules/context-view-preference.js +143 -0
- package/lib/public/modules/default-vendor.js +116 -0
- package/lib/public/modules/filebrowser-tabs.js +1 -0
- package/lib/public/modules/filebrowser.js +45 -18
- package/lib/public/modules/issues-render.js +36 -10
- package/lib/public/modules/issues.js +95 -13
- package/lib/public/modules/markdown.js +3 -1
- package/lib/public/modules/pane-bridge.js +14 -8
- package/lib/public/modules/pane-issue-bridge.js +27 -0
- package/lib/public/modules/permission-hints.js +13 -0
- package/lib/public/modules/profile.js +9 -9
- package/lib/public/modules/project-activation.js +49 -0
- package/lib/public/modules/project-logs-render.js +13 -3
- package/lib/public/modules/project-logs.js +45 -15
- package/lib/public/modules/project-session-visibility-settings.js +138 -0
- package/lib/public/modules/project-settings.js +5 -0
- package/lib/public/modules/right-workbench.js +44 -0
- package/lib/public/modules/scheduled-result-client.js +33 -0
- package/lib/public/modules/scheduled-result-display.js +50 -0
- package/lib/public/modules/scheduled-result-notification.js +31 -0
- package/lib/public/modules/scheduled-task-presentation.js +15 -0
- package/lib/public/modules/scheduled-tasks.js +9 -21
- package/lib/public/modules/sidebar-mobile.js +51 -3
- package/lib/public/modules/sidebar-projects.js +14 -3
- package/lib/public/modules/sidebar-sessions.js +65 -5
- package/lib/public/modules/split-session-boundary.js +28 -0
- package/lib/public/modules/split-view.js +42 -7
- package/lib/public/modules/sticky-notes-browser.js +130 -18
- package/lib/public/modules/sticky-notes.js +2 -3
- package/lib/public/modules/terminal.js +4 -4
- package/lib/public/modules/thinking-lifecycle.js +4 -1
- package/lib/public/modules/tools.js +5 -1
- package/lib/scheduled-run-state.js +10 -2
- package/lib/scheduler.js +1 -0
- package/lib/sdk-bridge.js +97 -61
- package/lib/sdk-message-processor.js +44 -13
- package/lib/sdk-skill-discovery.js +6 -28
- package/lib/sdk-turn-queue.js +14 -0
- package/lib/server-context-view.js +53 -0
- package/lib/server-default-vendor.js +60 -0
- package/lib/server.js +31 -7
- package/lib/session-notes-mcp-server.js +3 -5
- package/lib/session-pair-factory.js +2 -2
- package/lib/session-title-generator.js +89 -0
- package/lib/session-title-policy.js +8 -2
- package/lib/session-visibility.js +62 -1
- package/lib/sessions.js +28 -1
- package/lib/user-presence.js +6 -0
- package/lib/users-context-view-preferences.js +65 -0
- package/lib/users-default-vendor-preferences.js +80 -0
- package/lib/users.js +12 -0
- package/lib/ws-schema.js +13 -0
- package/lib/yoke/acp-agent-profiles.js +15 -2
- package/lib/yoke/adapters/acp.js +3 -2
- package/lib/yoke/adapters/antigravity.js +2 -2
- package/lib/yoke/adapters/claude-worker.js +11 -12
- package/lib/yoke/adapters/claude.js +75 -22
- package/lib/yoke/adapters/codex.js +216 -29
- package/lib/yoke/adapters/kiro.js +6 -5
- package/lib/yoke/claude-context-usage.js +57 -0
- package/lib/yoke/claude-message-correlation.js +31 -0
- package/lib/yoke/claude-model-switch.js +15 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/mcp-bridge-server.js +18 -5
- package/lib/yoke/session-skill-catalog.js +45 -0
- package/lib/yoke/skill-discovery.js +60 -20
- package/lib/yoke/vendor-registry.js +1 -1
- package/package.json +5 -7
|
@@ -13,6 +13,7 @@ var rateLimitResetTimer = null;
|
|
|
13
13
|
var rateLimitUsageEl = null;
|
|
14
14
|
var rateLimitResetState = {};
|
|
15
15
|
var rateLimitTickTimer = null;
|
|
16
|
+
var rateLimitPopoverTimers = [];
|
|
16
17
|
var fastModeIndicatorEl = null;
|
|
17
18
|
|
|
18
19
|
// --- Internal helpers ---
|
|
@@ -25,7 +26,7 @@ function getVendorUsageMeta(vendor) {
|
|
|
25
26
|
codex: {
|
|
26
27
|
icon: "/codex-avatar.png",
|
|
27
28
|
alt: "Codex",
|
|
28
|
-
href: "https://chatgpt.com/
|
|
29
|
+
href: "https://chatgpt.com/codex/settings/usage",
|
|
29
30
|
title: "Check usage on ChatGPT",
|
|
30
31
|
},
|
|
31
32
|
claude: {
|
|
@@ -35,7 +36,7 @@ function getVendorUsageMeta(vendor) {
|
|
|
35
36
|
title: "Check usage on claude.ai",
|
|
36
37
|
},
|
|
37
38
|
};
|
|
38
|
-
return fallbacks[vendor] ||
|
|
39
|
+
return fallbacks[vendor] || null;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function vendorTracksRateLimits(vendor) {
|
|
@@ -51,6 +52,37 @@ function vendorSupportsScheduledMessages(vendor) {
|
|
|
51
52
|
return scheduledMessageVendors.indexOf(vendor) !== -1;
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
function rateLimitEventAppliesToPane(msg, state) {
|
|
56
|
+
var activeSessionId = state.activeSessionId;
|
|
57
|
+
var activeVendor = state.currentVendor || "claude";
|
|
58
|
+
if (!msg) return false;
|
|
59
|
+
if (msg.sessionId != null && String(msg.sessionId) !== String(activeSessionId)) return false;
|
|
60
|
+
if (msg.vendor) return msg.vendor === activeVendor;
|
|
61
|
+
// Legacy Claude records predate vendor/session stamps. Never project them
|
|
62
|
+
// into a non-Claude pane, where they would look like Claude usage.
|
|
63
|
+
return activeVendor === "claude";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function rateLimitStateKey(sessionId, vendor) {
|
|
67
|
+
return String(store.get('currentSlug') || "") + "\u0000" +
|
|
68
|
+
String(store.get('myUserId') || "") + "\u0000" +
|
|
69
|
+
String(sessionId == null ? "" : sessionId) + "\u0000" + String(vendor || "claude");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getActiveRateLimitState() {
|
|
73
|
+
var state = store.get('rateLimitState') || {};
|
|
74
|
+
var key = rateLimitStateKey(store.get('activeSessionId'), store.get('currentVendor'));
|
|
75
|
+
return state[key] || {};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function setActiveRateLimitState(next) {
|
|
79
|
+
var state = Object.assign({}, store.get('rateLimitState') || {});
|
|
80
|
+
var key = rateLimitStateKey(store.get('activeSessionId'), store.get('currentVendor'));
|
|
81
|
+
state[key] = next;
|
|
82
|
+
store.set({ rateLimitState: state });
|
|
83
|
+
rateLimitResetState = next;
|
|
84
|
+
}
|
|
85
|
+
|
|
54
86
|
function rateLimitTypeLabel(type) {
|
|
55
87
|
if (!type) return "Usage";
|
|
56
88
|
var labels = {
|
|
@@ -108,13 +140,15 @@ function updateRateLimitIndicator(msg) {
|
|
|
108
140
|
var isRejected = msg.status === "rejected";
|
|
109
141
|
var pillClass = "header-rate-limit" + (isRejected ? " rejected" : " warning");
|
|
110
142
|
var label = isRejected ? "Rate limited" : "Rate warning";
|
|
143
|
+
var meta = getVendorUsageMeta(store.get('currentVendor') || "claude");
|
|
144
|
+
var link = meta
|
|
145
|
+
? '<a href="' + meta.href + '" target="_blank" rel="noopener" class="rate-limit-link" title="' + meta.title + '">' + iconHtml("external-link") + "</a>"
|
|
146
|
+
: '<span class="rate-limit-link unavailable" title="Usage information is unavailable for this vendor">' + iconHtml("external-link") + "</span>";
|
|
111
147
|
rateLimitIndicatorEl.innerHTML =
|
|
112
148
|
'<span class="' + pillClass + '">' +
|
|
113
149
|
iconHtml("alert-triangle") +
|
|
114
150
|
'<span class="header-pill-text">' + label + "</span>" +
|
|
115
|
-
|
|
116
|
-
iconHtml("external-link") +
|
|
117
|
-
"</a>" +
|
|
151
|
+
link +
|
|
118
152
|
"</span>";
|
|
119
153
|
refreshIcons();
|
|
120
154
|
}
|
|
@@ -131,10 +165,12 @@ function showRateLimitPopover(text, isRejected) {
|
|
|
131
165
|
rateLimitIndicatorEl.appendChild(pop);
|
|
132
166
|
|
|
133
167
|
// Auto-dismiss after 5s
|
|
134
|
-
setTimeout(function () {
|
|
168
|
+
var fadeTimer = setTimeout(function () {
|
|
135
169
|
pop.classList.add("fade-out");
|
|
136
|
-
setTimeout(function () { if (pop.parentNode) pop.remove(); }, 300);
|
|
170
|
+
var removeTimer = setTimeout(function () { if (pop.parentNode) pop.remove(); }, 300);
|
|
171
|
+
rateLimitPopoverTimers.push(removeTimer);
|
|
137
172
|
}, 5000);
|
|
173
|
+
rateLimitPopoverTimers.push(fadeTimer);
|
|
138
174
|
}
|
|
139
175
|
|
|
140
176
|
function clearRateLimitIndicator() {
|
|
@@ -168,22 +204,81 @@ function tickRateLimitUsage() {
|
|
|
168
204
|
if (!rateLimitUsageEl) return;
|
|
169
205
|
var parts = [];
|
|
170
206
|
var types = ["five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet"];
|
|
207
|
+
var nextState = Object.assign({}, rateLimitResetState);
|
|
208
|
+
var changed = false;
|
|
171
209
|
for (var i = 0; i < types.length; i++) {
|
|
172
210
|
var entry = rateLimitResetState[types[i]];
|
|
173
211
|
if (!entry || !entry.resetsAt) continue;
|
|
174
212
|
var timeStr = formatResetTime(entry.resetsAt);
|
|
175
|
-
if (!timeStr) { delete
|
|
213
|
+
if (!timeStr) { delete nextState[types[i]]; changed = true; continue; }
|
|
176
214
|
parts.push(rateLimitTypeShortLabel(types[i]) + " resets " + timeStr);
|
|
177
215
|
}
|
|
216
|
+
if (changed) setActiveRateLimitState(nextState);
|
|
178
217
|
if (parts.length === 0) {
|
|
179
|
-
|
|
180
|
-
|
|
218
|
+
renderRateLimitUsage();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
renderRateLimitUsage();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function renderRateLimitUsage() {
|
|
225
|
+
var activeVendor = store.get('currentVendor') || "claude";
|
|
226
|
+
if (!vendorTracksRateLimits(activeVendor)) {
|
|
227
|
+
if (rateLimitUsageEl) {
|
|
228
|
+
rateLimitUsageEl.remove();
|
|
229
|
+
rateLimitUsageEl = null;
|
|
230
|
+
}
|
|
181
231
|
if (rateLimitTickTimer) { clearInterval(rateLimitTickTimer); rateLimitTickTimer = null; }
|
|
182
232
|
return;
|
|
183
233
|
}
|
|
184
|
-
|
|
185
|
-
|
|
234
|
+
|
|
235
|
+
rateLimitResetState = getActiveRateLimitState();
|
|
236
|
+
var topBarActions = document.querySelector("#top-bar .top-bar-actions");
|
|
237
|
+
if (!topBarActions) return;
|
|
238
|
+
if (!rateLimitUsageEl) {
|
|
239
|
+
rateLimitUsageEl = document.createElement("a");
|
|
240
|
+
rateLimitUsageEl.id = "rate-limit-usage-link";
|
|
241
|
+
rateLimitUsageEl.className = "top-bar-pill pill-dim usage-check-link";
|
|
242
|
+
rateLimitUsageEl.target = "_blank";
|
|
243
|
+
rateLimitUsageEl.rel = "noopener";
|
|
244
|
+
var ref = document.getElementById("skip-perms-pill");
|
|
245
|
+
topBarActions.insertBefore(rateLimitUsageEl, ref);
|
|
246
|
+
}
|
|
247
|
+
rateLimitUsageEl.style.display = "";
|
|
248
|
+
|
|
249
|
+
var parts = [];
|
|
250
|
+
var types = ["five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet"];
|
|
251
|
+
for (var i = 0; i < types.length; i++) {
|
|
252
|
+
var entry = rateLimitResetState[types[i]];
|
|
253
|
+
if (!entry || !entry.resetsAt) continue;
|
|
254
|
+
var timeStr = formatResetTime(entry.resetsAt);
|
|
255
|
+
if (!timeStr) continue;
|
|
256
|
+
parts.push(rateLimitTypeShortLabel(types[i]) + " resets " + timeStr);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
var label = parts.length > 0 ? parts.join(" · ") : "Check usage";
|
|
260
|
+
var meta = getVendorUsageMeta(activeVendor);
|
|
261
|
+
if (meta) {
|
|
262
|
+
rateLimitUsageEl.href = meta.href;
|
|
263
|
+
rateLimitUsageEl.title = meta.title;
|
|
264
|
+
rateLimitUsageEl.removeAttribute("aria-disabled");
|
|
265
|
+
rateLimitUsageEl.innerHTML =
|
|
266
|
+
'<img src="' + meta.icon + '" class="usage-check-vendor-icon" alt="' + meta.alt + '">' +
|
|
267
|
+
'<span>' + label + '</span>' + iconHtml("external-link");
|
|
268
|
+
} else {
|
|
269
|
+
rateLimitUsageEl.removeAttribute("href");
|
|
270
|
+
rateLimitUsageEl.setAttribute("aria-disabled", "true");
|
|
271
|
+
rateLimitUsageEl.title = "Usage information is unavailable for this vendor";
|
|
272
|
+
rateLimitUsageEl.innerHTML = '<span>' + (parts.length > 0 ? label : "Usage unavailable") + '</span>';
|
|
273
|
+
}
|
|
186
274
|
refreshIcons();
|
|
275
|
+
|
|
276
|
+
if (parts.length > 0 && !rateLimitTickTimer) {
|
|
277
|
+
rateLimitTickTimer = setInterval(tickRateLimitUsage, 30000);
|
|
278
|
+
} else if (parts.length === 0 && rateLimitTickTimer) {
|
|
279
|
+
clearInterval(rateLimitTickTimer);
|
|
280
|
+
rateLimitTickTimer = null;
|
|
281
|
+
}
|
|
187
282
|
}
|
|
188
283
|
|
|
189
284
|
// --- Exported functions ---
|
|
@@ -193,15 +288,32 @@ export function initRateLimit() {
|
|
|
193
288
|
if (state.currentVendor !== prev.currentVendor && state.currentVendor) {
|
|
194
289
|
if (!vendorSupportsScheduledMessages(state.currentVendor)) clearScheduleDelay();
|
|
195
290
|
}
|
|
196
|
-
if (state.currentVendor !== prev.currentVendor || state.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
291
|
+
if (state.currentVendor !== prev.currentVendor || state.activeSessionId !== prev.activeSessionId
|
|
292
|
+
|| state.currentSlug !== prev.currentSlug || state.myUserId !== prev.myUserId) {
|
|
293
|
+
resetRateLimitState();
|
|
294
|
+
}
|
|
295
|
+
if (state.vendorInfo !== prev.vendorInfo && state.currentVendor === prev.currentVendor && state.activeSessionId === prev.activeSessionId
|
|
296
|
+
&& state.currentSlug === prev.currentSlug && state.myUserId === prev.myUserId) {
|
|
297
|
+
renderRateLimitUsage();
|
|
200
298
|
}
|
|
201
299
|
});
|
|
202
300
|
}
|
|
203
301
|
|
|
204
302
|
export function handleRateLimitEvent(msg) {
|
|
303
|
+
if (!rateLimitEventAppliesToPane(msg, store.snap())) return;
|
|
304
|
+
if (msg.resetsAt && msg.resetsAt <= Date.now()) {
|
|
305
|
+
var expiredState = Object.assign({}, getActiveRateLimitState());
|
|
306
|
+
if (msg.rateLimitType) delete expiredState[msg.rateLimitType];
|
|
307
|
+
setActiveRateLimitState(expiredState);
|
|
308
|
+
clearRateLimitIndicator();
|
|
309
|
+
renderRateLimitUsage();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (msg.rateLimitType && msg.resetsAt) {
|
|
313
|
+
var eventState = Object.assign({}, getActiveRateLimitState());
|
|
314
|
+
eventState[msg.rateLimitType] = { resetsAt: msg.resetsAt, status: msg.status };
|
|
315
|
+
setActiveRateLimitState(eventState);
|
|
316
|
+
}
|
|
205
317
|
var isRejected = msg.status === "rejected";
|
|
206
318
|
var typeLabel = rateLimitTypeLabel(msg.rateLimitType);
|
|
207
319
|
var popoverText = "";
|
|
@@ -226,8 +338,12 @@ export function handleRateLimitEvent(msg) {
|
|
|
226
338
|
rateLimitResetTimer = setTimeout(function () {
|
|
227
339
|
rateLimitResetsAt = null;
|
|
228
340
|
rateLimitResetTimer = null;
|
|
341
|
+
var currentState = Object.assign({}, getActiveRateLimitState());
|
|
342
|
+
delete currentState[msg.rateLimitType];
|
|
343
|
+
setActiveRateLimitState(currentState);
|
|
229
344
|
// Clear schedule mode when rate limit resets
|
|
230
345
|
clearScheduleDelay();
|
|
346
|
+
renderRateLimitUsage();
|
|
231
347
|
}, msg.resetsAt - Date.now() + 1000);
|
|
232
348
|
} else {
|
|
233
349
|
var pct = msg.utilization ? Math.round(msg.utilization * 100) : null;
|
|
@@ -239,58 +355,19 @@ export function handleRateLimitEvent(msg) {
|
|
|
239
355
|
}
|
|
240
356
|
|
|
241
357
|
export function updateRateLimitUsage(msg) {
|
|
358
|
+
if (!rateLimitEventAppliesToPane(msg, store.snap())) return;
|
|
242
359
|
var activeVendor = store.get('currentVendor') || "claude";
|
|
243
360
|
if (!vendorTracksRateLimits(activeVendor)) {
|
|
244
|
-
|
|
361
|
+
renderRateLimitUsage();
|
|
245
362
|
return;
|
|
246
363
|
}
|
|
247
364
|
if (msg.rateLimitType && msg.resetsAt) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
var topBarActions = document.querySelector("#top-bar .top-bar-actions");
|
|
252
|
-
if (!topBarActions) return;
|
|
253
|
-
|
|
254
|
-
if (!rateLimitUsageEl) {
|
|
255
|
-
rateLimitUsageEl = document.createElement("a");
|
|
256
|
-
rateLimitUsageEl.id = "rate-limit-usage-link";
|
|
257
|
-
rateLimitUsageEl.className = "top-bar-pill pill-dim usage-check-link";
|
|
258
|
-
rateLimitUsageEl.target = "_blank";
|
|
259
|
-
rateLimitUsageEl.rel = "noopener";
|
|
260
|
-
var ref = document.getElementById("skip-perms-pill");
|
|
261
|
-
topBarActions.insertBefore(rateLimitUsageEl, ref);
|
|
262
|
-
}
|
|
263
|
-
rateLimitUsageEl.style.display = "";
|
|
264
|
-
|
|
265
|
-
// Build label from available reset times
|
|
266
|
-
var parts = [];
|
|
267
|
-
var types = ["five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet"];
|
|
268
|
-
for (var i = 0; i < types.length; i++) {
|
|
269
|
-
var entry = rateLimitResetState[types[i]];
|
|
270
|
-
if (!entry || !entry.resetsAt) continue;
|
|
271
|
-
var timeStr = formatResetTime(entry.resetsAt);
|
|
272
|
-
if (!timeStr) continue;
|
|
273
|
-
parts.push(rateLimitTypeShortLabel(types[i]) + " resets " + timeStr);
|
|
365
|
+
var nextState = Object.assign({}, getActiveRateLimitState());
|
|
366
|
+
nextState[msg.rateLimitType] = { resetsAt: msg.resetsAt, status: msg.status };
|
|
367
|
+
setActiveRateLimitState(nextState);
|
|
274
368
|
}
|
|
275
369
|
|
|
276
|
-
|
|
277
|
-
var vendor = activeVendor;
|
|
278
|
-
var meta = getVendorUsageMeta(vendor);
|
|
279
|
-
rateLimitUsageEl.href = meta.href;
|
|
280
|
-
rateLimitUsageEl.title = meta.title;
|
|
281
|
-
rateLimitUsageEl.innerHTML =
|
|
282
|
-
'<img src="' + meta.icon + '" class="usage-check-vendor-icon" alt="' + meta.alt + '">' +
|
|
283
|
-
'<span>' + label + '</span>' +
|
|
284
|
-
iconHtml("external-link");
|
|
285
|
-
refreshIcons();
|
|
286
|
-
|
|
287
|
-
// Start or stop live countdown tick
|
|
288
|
-
if (parts.length > 0 && !rateLimitTickTimer) {
|
|
289
|
-
rateLimitTickTimer = setInterval(tickRateLimitUsage, 30000);
|
|
290
|
-
} else if (parts.length === 0 && rateLimitTickTimer) {
|
|
291
|
-
clearInterval(rateLimitTickTimer);
|
|
292
|
-
rateLimitTickTimer = null;
|
|
293
|
-
}
|
|
370
|
+
renderRateLimitUsage();
|
|
294
371
|
}
|
|
295
372
|
|
|
296
373
|
export function handleFastModeState(state) {
|
|
@@ -322,6 +399,16 @@ export function handleFastModeState(state) {
|
|
|
322
399
|
|
|
323
400
|
export function resetRateLimitState() {
|
|
324
401
|
clearRateLimitIndicator();
|
|
402
|
+
rateLimitResetsAt = null;
|
|
403
|
+
if (rateLimitResetTimer) { clearTimeout(rateLimitResetTimer); rateLimitResetTimer = null; }
|
|
404
|
+
clearScheduleDelay();
|
|
405
|
+
for (var i = 0; i < rateLimitPopoverTimers.length; i++) clearTimeout(rateLimitPopoverTimers[i]);
|
|
406
|
+
rateLimitPopoverTimers = [];
|
|
407
|
+
if (rateLimitTickTimer) { clearInterval(rateLimitTickTimer); rateLimitTickTimer = null; }
|
|
325
408
|
if (rateLimitCountdownTimer) { clearInterval(rateLimitCountdownTimer); rateLimitCountdownTimer = null; }
|
|
409
|
+
rateLimitResetState = getActiveRateLimitState();
|
|
410
|
+
renderRateLimitUsage();
|
|
326
411
|
if (fastModeIndicatorEl) { fastModeIndicatorEl.remove(); fastModeIndicatorEl = null; }
|
|
327
412
|
}
|
|
413
|
+
|
|
414
|
+
export { rateLimitEventAppliesToPane, getVendorUsageMeta };
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Local pixel identities: deterministic SVG avatars derived from a stable seed.
|
|
2
2
|
|
|
3
3
|
var PAPER = "#f1efe8";
|
|
4
4
|
var INK = "#1b1b1a";
|
|
5
|
-
var LIGHT_BASE = "#e7e5de";
|
|
6
|
-
var LIGHT_FIELD = "#333330";
|
|
7
5
|
var MUTED_INDIGO = "#72778f";
|
|
6
|
+
var IDENTICON_PALETTE = ["#333330", MUTED_INDIGO, "#6f8382", "#887b91", "#8a806a"];
|
|
8
7
|
var MATE_MARK_PALETTE = [
|
|
9
8
|
{ background: "#6f7489", foreground: PAPER },
|
|
10
9
|
{ background: "#78816c", foreground: PAPER },
|
|
@@ -24,25 +23,6 @@ function hashString(value) {
|
|
|
24
23
|
return hash >>> 0;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
function makeRandom(seed) {
|
|
28
|
-
var state = seed >>> 0;
|
|
29
|
-
return function nextRandom() {
|
|
30
|
-
state += 0x6d2b79f5;
|
|
31
|
-
var value = state;
|
|
32
|
-
value = Math.imul(value ^ (value >>> 15), value | 1);
|
|
33
|
-
value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
|
|
34
|
-
return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function between(random, min, max) {
|
|
39
|
-
return min + random() * (max - min);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function number(value) {
|
|
43
|
-
return Math.round(value * 100) / 100;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
26
|
function xmlText(value) {
|
|
47
27
|
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
48
28
|
}
|
|
@@ -54,62 +34,37 @@ function legacyIdentity(style, seed) {
|
|
|
54
34
|
return "legacy:" + sourceStyle + ":" + sourceSeed;
|
|
55
35
|
}
|
|
56
36
|
|
|
57
|
-
function curvePath(y, bow, endOffset) {
|
|
58
|
-
return "M -28 " + number(y) +
|
|
59
|
-
" C 17 " + number(y + bow) +
|
|
60
|
-
", 71 " + number(y - bow) +
|
|
61
|
-
", 124 " + number(y + endOffset);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function cutPath(random) {
|
|
65
|
-
var start = between(random, 37, 52);
|
|
66
|
-
var first = between(random, 14, 84);
|
|
67
|
-
var second = between(random, 12, 86);
|
|
68
|
-
var end = between(random, 40, 64);
|
|
69
|
-
return "M -22 " + number(start) + " C 21 " + number(first) + ", 73 " + number(second) + ", 118 " + number(end);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
37
|
export function imprintSvg(options) {
|
|
73
38
|
var settings = options || {};
|
|
74
39
|
var size = Math.max(12, Math.min(256, Number(settings.size) || 64));
|
|
75
40
|
var identity = legacyIdentity(settings.style, settings.seed);
|
|
76
41
|
var hash = hashString(identity);
|
|
77
|
-
var
|
|
78
|
-
var
|
|
79
|
-
var
|
|
80
|
-
var
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
var firstBow = between(random, -34, 34);
|
|
89
|
-
var secondBow = between(random, -32, 32);
|
|
90
|
-
var firstPath = curvePath(firstY, firstBow, 6);
|
|
91
|
-
var secondPath = curvePath(secondY, -secondBow, -5);
|
|
92
|
-
var contours = "";
|
|
42
|
+
var cells = identiconCells(hash);
|
|
43
|
+
var foreground = IDENTICON_PALETTE[hash % IDENTICON_PALETTE.length];
|
|
44
|
+
var pixels = "";
|
|
45
|
+
for (var i = 0; i < cells.length; i++) {
|
|
46
|
+
pixels += '<rect x="' + (cells[i][0] + 1) + '" y="' + (cells[i][1] + 1) + '" width="1" height="1"/>';
|
|
47
|
+
}
|
|
48
|
+
return '<svg xmlns="http://www.w3.org/2000/svg" width="' + size + '" height="' + size +
|
|
49
|
+
'" viewBox="0 0 7 7" shape-rendering="crispEdges"><rect width="7" height="7" fill="' + PAPER +
|
|
50
|
+
'"/><g fill="' + foreground + '">' + pixels + '</g><rect x="0.5" y="0.5" width="6" height="6" fill="none" stroke="' +
|
|
51
|
+
INK + '" stroke-opacity="0.14" stroke-width="0.08"/></svg>';
|
|
52
|
+
}
|
|
93
53
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
54
|
+
// Generate the left three columns and mirror them into a compact 5x5 grid.
|
|
55
|
+
export function identiconCells(hash) {
|
|
56
|
+
var value = hash >>> 0;
|
|
57
|
+
var cells = [];
|
|
58
|
+
for (var row = 0; row < 5; row++) {
|
|
59
|
+
for (var column = 0; column < 3; column++) {
|
|
60
|
+
value = Math.imul(value ^ (value >>> 13), 16777619) >>> 0;
|
|
61
|
+
if ((value & 1) || (row === 2 && column === 1)) {
|
|
62
|
+
cells.push([column, row]);
|
|
63
|
+
if (column < 2) cells.push([4 - column, row]);
|
|
64
|
+
}
|
|
99
65
|
}
|
|
100
66
|
}
|
|
101
|
-
|
|
102
|
-
var clipId = "imprint-" + hash.toString(16);
|
|
103
|
-
return '<svg xmlns="http://www.w3.org/2000/svg" width="' + size + '" height="' + size +
|
|
104
|
-
'" viewBox="0 0 96 96"><defs><clipPath id="' + clipId +
|
|
105
|
-
'"><rect width="96" height="96" rx="24"/></clipPath></defs><g clip-path="url(#' + clipId +
|
|
106
|
-
')"><rect width="96" height="96" fill="' + base + '"/><g transform="rotate(' + rotation +
|
|
107
|
-
' 48 48)"><path d="' + firstPath + '" fill="none" stroke="' + field + '" stroke-width="' +
|
|
108
|
-
number(bandWidth) + '" stroke-linecap="square"/><path d="' + secondPath + '" fill="none" stroke="' +
|
|
109
|
-
accent + '" stroke-width="' + number(secondaryWidth) + '" stroke-linecap="square"/><path d="' +
|
|
110
|
-
cutPath(random) + '" fill="none" stroke="' + base + '" stroke-width="' + number(cutWidth) +
|
|
111
|
-
'" stroke-linecap="square"/>' + contours + '</g></g><rect x="0.5" y="0.5" width="95" height="95" rx="23.5" fill="none" stroke="' +
|
|
112
|
-
(darkBase ? "#000000" : INK) + '" stroke-opacity="0.16"/></svg>';
|
|
67
|
+
return cells;
|
|
113
68
|
}
|
|
114
69
|
|
|
115
70
|
export function imprintDataUrl(options) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// Centralized
|
|
1
|
+
// Centralized local avatar URL builder for people and Mates.
|
|
2
2
|
import { imprintDataUrl, mateMarkDataUrl } from './avatar-imprint.js';
|
|
3
3
|
|
|
4
4
|
var imprintCache = new Map();
|
|
5
5
|
var mateMarkCache = new Map();
|
|
6
6
|
|
|
7
7
|
// Legacy style remains part of the identity hash so existing selections map
|
|
8
|
-
// deterministically into the
|
|
8
|
+
// deterministically into the local pixel identity system.
|
|
9
9
|
export function avatarUrl(style, seed, size) {
|
|
10
10
|
var cacheKey = [style || 'imprint', seed || 'anonymous', size || 64].join('|');
|
|
11
11
|
if (imprintCache.has(cacheKey)) return imprintCache.get(cacheKey);
|
|
@@ -15,12 +15,20 @@ export function avatarUrl(style, seed, size) {
|
|
|
15
15
|
return url;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
// Use the same bounded account identity for fresh fallbacks across every surface.
|
|
19
|
+
export function userAvatarSeed(user) {
|
|
20
|
+
var profile = user && user.profile ? user.profile : user;
|
|
21
|
+
var savedSeed = profile && profile.avatarSeed || user && user.avatarSeed;
|
|
22
|
+
if (savedSeed) return String(savedSeed);
|
|
23
|
+
var identity = profile && (profile.username || profile.id || profile.userId) || user && (user.username || user.id || user.userId) || 'anonymous';
|
|
24
|
+
return String(identity).substring(0, 30);
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
// Build avatar URL for a user object, preferring custom avatar if set.
|
|
19
28
|
export function userAvatarUrl(user, size) {
|
|
20
29
|
if (user && user.avatarCustom) return user.avatarCustom;
|
|
21
30
|
var style = (user && user.avatarStyle) || 'imprint';
|
|
22
|
-
|
|
23
|
-
return avatarUrl(style, seed, size);
|
|
31
|
+
return avatarUrl(style, userAvatarSeed(user), size);
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
export function mateMarkUrl(seed, size) {
|
|
@@ -145,6 +145,26 @@ export function addToMessages(element) {
|
|
|
145
145
|
var messagesEl = getMessagesEl();
|
|
146
146
|
if (prependAnchor) messagesEl.insertBefore(element, prependAnchor);
|
|
147
147
|
else messagesEl.appendChild(element);
|
|
148
|
+
keepActiveThinkingAtTail();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Thinking is a single accumulating record for the active turn. Other live
|
|
152
|
+
// transcript entries may arrive between reasoning segments, so keep only the
|
|
153
|
+
// currently active record after the tail activity indicator.
|
|
154
|
+
export function keepActiveThinkingAtTail() {
|
|
155
|
+
if (prependAnchor || store.get('replayingHistory')) return;
|
|
156
|
+
var thinkingState = store.get('thinkingState');
|
|
157
|
+
var thinkingEl = thinkingState && thinkingState.active && thinkingState.el;
|
|
158
|
+
var messagesEl = getMessagesEl();
|
|
159
|
+
if (!thinkingEl || !messagesEl || thinkingEl.parentNode !== messagesEl) return;
|
|
160
|
+
if (activityEl && activityEl.parentNode === messagesEl) {
|
|
161
|
+
if (activityEl.nextSibling !== thinkingEl || messagesEl.lastElementChild !== thinkingEl) {
|
|
162
|
+
messagesEl.appendChild(activityEl);
|
|
163
|
+
messagesEl.appendChild(thinkingEl);
|
|
164
|
+
}
|
|
165
|
+
} else if (messagesEl.lastElementChild !== thinkingEl) {
|
|
166
|
+
messagesEl.appendChild(thinkingEl);
|
|
167
|
+
}
|
|
148
168
|
}
|
|
149
169
|
|
|
150
170
|
export function scrollToBottom() {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { store } from './store.js';
|
|
2
2
|
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { forwardPaneIssueReference } from './pane-bridge.js';
|
|
3
4
|
function accountKey(state) { return state.isMultiUserMode ? 'user:' + String(state.myUserId || '') : 'single'; }
|
|
4
5
|
function requestContext(state) { return { accountKey: accountKey(state), projectSlug: state.currentSlug || null, epoch: state.issuesEpoch || 0 }; }
|
|
5
6
|
function sameContext(request, state) {
|
|
@@ -30,6 +31,7 @@ function createIssueLink(ref, labelText) {
|
|
|
30
31
|
var button = document.createElement("button");
|
|
31
32
|
button.type = "button";
|
|
32
33
|
button.className = "clayos-issue-link";
|
|
34
|
+
button.classList.add("clayos-record-link");
|
|
33
35
|
button.contentEditable = "false";
|
|
34
36
|
button.dataset.issueRef = ref;
|
|
35
37
|
button.setAttribute("aria-label", "Open Project Issue" + (labelText ? " " + labelText : ""));
|
|
@@ -42,12 +44,17 @@ function createIssueLink(ref, labelText) {
|
|
|
42
44
|
button.appendChild(meta);
|
|
43
45
|
}
|
|
44
46
|
var cache = store.get('issuesCache') || {};
|
|
45
|
-
if (cache[ref])
|
|
47
|
+
if (cache[ref]) updateIssueLink(button, cache[ref]);
|
|
46
48
|
setTimeout(function () { resolve(ref); }, 0);
|
|
47
49
|
return button;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
export function enhanceClayIssueLinks(root) {
|
|
53
|
+
root.querySelectorAll('a').forEach(function (anchor) {
|
|
54
|
+
var ref = anchor.getAttribute('data-clay-record-ref') || anchor.getAttribute('href');
|
|
55
|
+
if (anchor.closest('pre, code, button') || !isExactClayIssueReference(ref)) return;
|
|
56
|
+
anchor.replaceWith(createIssueLink(ref.trim(), anchor.textContent.trim()));
|
|
57
|
+
});
|
|
51
58
|
var codeNodes = root.querySelectorAll("code");
|
|
52
59
|
for (var codeIndex = 0; codeIndex < codeNodes.length; codeIndex++) {
|
|
53
60
|
var code = codeNodes[codeIndex];
|
|
@@ -80,6 +87,20 @@ export function enhanceClayIssueLinks(root) {
|
|
|
80
87
|
}
|
|
81
88
|
}
|
|
82
89
|
|
|
90
|
+
export function updateIssueLink(button, target) {
|
|
91
|
+
var status = target.status || 'open';
|
|
92
|
+
var title = target.title || 'Issue';
|
|
93
|
+
button.replaceChildren();
|
|
94
|
+
var label = document.createElement('span');
|
|
95
|
+
label.textContent = 'Issue';
|
|
96
|
+
var meta = document.createElement('small');
|
|
97
|
+
meta.textContent = title;
|
|
98
|
+
button.append(label, meta);
|
|
99
|
+
button.dataset.status = status;
|
|
100
|
+
button.title = title + ' · ' + status.replace(/_/g, ' ');
|
|
101
|
+
button.setAttribute('aria-label', 'Open Project Issue: ' + button.title);
|
|
102
|
+
}
|
|
103
|
+
|
|
83
104
|
function resolve(ref) {
|
|
84
105
|
var ws = getWs();
|
|
85
106
|
var requests = store.get('issueLinkRequests') || {};
|
|
@@ -101,7 +122,7 @@ export function handleIssueReference(msg) {
|
|
|
101
122
|
}
|
|
102
123
|
document.querySelectorAll('[data-issue-ref]').forEach(function (button) {
|
|
103
124
|
if (button.dataset.issueRef !== msg.ref) return;
|
|
104
|
-
if (msg.target)
|
|
125
|
+
if (msg.target) updateIssueLink(button, msg.target);
|
|
105
126
|
else button.title = msg.error || 'Issue unavailable';
|
|
106
127
|
});
|
|
107
128
|
if (store.get('issueLinkOpenRef') === msg.ref) {
|
|
@@ -111,6 +132,7 @@ export function handleIssueReference(msg) {
|
|
|
111
132
|
return true;
|
|
112
133
|
}
|
|
113
134
|
function navigate(target) {
|
|
135
|
+
if (forwardPaneIssueReference(target.ref, target.projectSlug)) return;
|
|
114
136
|
if (target.projectSlug !== store.get('currentSlug')) { window.location.assign('/p/' + encodeURIComponent(target.projectSlug) + '/?issue=' + encodeURIComponent(target.ref)); return; }
|
|
115
137
|
import('./issues.js').then(function (module) { module.openIssues(target.ref); });
|
|
116
138
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import { enhanceClayIssueLinks } from './clay-issue-links.js';
|
|
1
|
+
import { enhanceClayIssueLinks, isExactClayIssueReference } from './clay-issue-links.js';
|
|
2
|
+
|
|
3
|
+
export function renderClayRecordLink(href, text) {
|
|
4
|
+
if (!isExactClayLogReference(href) && !isExactClayIssueReference(href)) return null;
|
|
5
|
+
return '<a href="#" data-clay-record-ref="' + href.trim() + '">' + text + '</a>';
|
|
6
|
+
}
|
|
2
7
|
export function parseClayLogReferences(text) {
|
|
3
8
|
var pattern = /\[clayos\/(log:[A-Za-z0-9_-]{24})(?:\s+[\u2014-]\s+([^\]\n]{1,80}))?\]|(^|[^A-Za-z0-9_/:])(log:[A-Za-z0-9_-]{24})(?![A-Za-z0-9_-])/g;
|
|
4
9
|
var results = [];
|
|
@@ -23,6 +28,7 @@ function createLogLink(ref, labelText) {
|
|
|
23
28
|
var button = document.createElement("button");
|
|
24
29
|
button.type = "button";
|
|
25
30
|
button.className = "clayos-log-link";
|
|
31
|
+
button.classList.add("clayos-record-link");
|
|
26
32
|
button.contentEditable = "false";
|
|
27
33
|
button.dataset.logRef = ref;
|
|
28
34
|
button.setAttribute("aria-label", "Open Project Log" + (labelText ? " " + labelText : ""));
|
|
@@ -39,6 +45,11 @@ function createLogLink(ref, labelText) {
|
|
|
39
45
|
|
|
40
46
|
export function enhanceClayLogLinks(root) {
|
|
41
47
|
enhanceClayIssueLinks(root);
|
|
48
|
+
root.querySelectorAll('a').forEach(function (anchor) {
|
|
49
|
+
var ref = anchor.getAttribute('data-clay-record-ref') || anchor.getAttribute('href');
|
|
50
|
+
if (anchor.closest('pre, code, button') || !isExactClayLogReference(ref)) return;
|
|
51
|
+
anchor.replaceWith(createLogLink(ref.trim(), anchor.textContent.trim()));
|
|
52
|
+
});
|
|
42
53
|
var codeNodes = root.querySelectorAll("code");
|
|
43
54
|
for (var codeIndex = 0; codeIndex < codeNodes.length; codeIndex++) {
|
|
44
55
|
var code = codeNodes[codeIndex];
|