clay-server 5.2.0-beta.6 → 5.2.0-beta.8
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/clay-control-presentation-binding.js +96 -0
- package/lib/clay-control-presentation.js +128 -0
- package/lib/clay-control-target-projection.js +26 -0
- package/lib/clay-mate-chat.js +1 -0
- package/lib/clay-mate-knowledge.js +1 -1
- package/lib/clay-product-control-tools.js +1 -1
- package/lib/clay-product-control.js +22 -12
- package/lib/clay-project-records.js +2 -1
- package/lib/clay-scheduling.js +1 -1
- package/lib/clay-screen-controls.js +13 -5
- package/lib/clay-workspace-controls.js +2 -1
- package/lib/contextual-screen-snapshot.js +38 -0
- package/lib/mate-context-service.js +138 -0
- package/lib/mate-context-tools.js +21 -0
- package/lib/project-connection.js +8 -5
- package/lib/project-control-git-view.js +12 -0
- package/lib/project-filesystem.js +5 -1
- package/lib/project-http.js +3 -0
- package/lib/project-notifications.js +5 -4
- package/lib/project-product-control.js +3 -1
- package/lib/project-scheduled-messages.js +4 -4
- package/lib/project-scheduled-task-control.js +1 -1
- package/lib/project-session-document.js +20 -5
- package/lib/project-shared-browser.js +22 -5
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-message.js +36 -0
- package/lib/project-workspace-query.js +11 -1
- package/lib/project.js +24 -7
- package/lib/public/app.js +24 -0
- package/lib/public/appearance-bootstrap.js +2 -0
- package/lib/public/css/contextual-clay.css +240 -0
- package/lib/public/modules/app-favicon.js +7 -7
- package/lib/public/modules/app-message-router.js +5 -1
- package/lib/public/modules/app-messages.js +14 -1
- package/lib/public/modules/app-notifications.js +10 -3
- package/lib/public/modules/app-projects.js +3 -0
- package/lib/public/modules/clay-control-activity.js +214 -0
- package/lib/public/modules/clay-control-copy.js +36 -0
- package/lib/public/modules/clay-control-detail-targets.js +44 -0
- package/lib/public/modules/clay-control-object-targets.js +52 -0
- package/lib/public/modules/clay-control-target-visibility.js +12 -0
- package/lib/public/modules/clay-control-targets.js +44 -0
- package/lib/public/modules/clay-navigation-render.js +52 -0
- package/lib/public/modules/clay-owner-navigation.js +11 -2
- package/lib/public/modules/clay-session-links.js +31 -0
- package/lib/public/modules/clay-studio-bridge.js +20 -0
- package/lib/public/modules/clay-studio-document.js +67 -0
- package/lib/public/modules/clay-studio-render.js +144 -0
- package/lib/public/modules/clay-tool-presentation.js +4 -0
- package/lib/public/modules/command-palette.js +26 -31
- package/lib/public/modules/contextual-ask-session.js +26 -0
- package/lib/public/modules/contextual-clay-geometry.js +155 -0
- package/lib/public/modules/contextual-clay-session.js +130 -0
- package/lib/public/modules/contextual-clay-workspace.js +157 -0
- package/lib/public/modules/contextual-clay.js +237 -0
- package/lib/public/modules/contextual-composer.js +91 -0
- package/lib/public/modules/contextual-mate-context.js +115 -0
- package/lib/public/modules/contextual-mate-hint.js +40 -0
- package/lib/public/modules/contextual-mate-identity.js +23 -0
- package/lib/public/modules/contextual-mate-sessions.js +134 -0
- package/lib/public/modules/contextual-search.js +137 -0
- package/lib/public/modules/contextual-session-citations.js +58 -0
- package/lib/public/modules/filebrowser.js +20 -1
- package/lib/public/modules/generation-stop.js +8 -5
- package/lib/public/modules/git-panel.js +3 -1
- package/lib/public/modules/icon-strip-tooltip.js +2 -1
- package/lib/public/modules/input.js +35 -1
- package/lib/public/modules/issues-render.js +3 -2
- package/lib/public/modules/knowledge-db-workbench.js +4 -4
- package/lib/public/modules/markdown.js +2 -34
- package/lib/public/modules/mate-knowledge-workbench.js +1 -1
- package/lib/public/modules/message-delivery.js +10 -0
- package/lib/public/modules/pane-bridge.js +2 -2
- package/lib/public/modules/pending-message-queue.js +1 -0
- package/lib/public/modules/product-review.js +26 -0
- package/lib/public/modules/project-chat-identity.js +13 -4
- package/lib/public/modules/project-logs-render.js +3 -0
- package/lib/public/modules/project-mate-navigation.js +58 -21
- package/lib/public/modules/scheduled-tasks.js +2 -0
- package/lib/public/modules/search-clay-chat.js +5 -454
- package/lib/public/modules/shared-browser.js +2 -0
- package/lib/public/modules/sidebar-mobile.js +4 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-sessions.js +1 -0
- package/lib/public/modules/split-view.js +7 -16
- package/lib/public/modules/stt.js +7 -2
- package/lib/public/modules/terminal.js +9 -0
- package/lib/public/modules/tools.js +4 -1
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/style.css +2 -0
- package/lib/sdk-bridge.js +4 -0
- package/lib/server-contextual-clay-settings.js +40 -0
- package/lib/server-contextual-mate-sessions.js +70 -0
- package/lib/server-home-chat.js +7 -1
- package/lib/server-home-clay-entry.js +23 -4
- package/lib/server-home-clay-session-links.js +12 -0
- package/lib/server-owner-ui.js +49 -8
- package/lib/server-settings.js +2 -0
- package/lib/server.js +17 -0
- package/lib/sessions.js +10 -3
- package/lib/terminal-manager.js +18 -11
- package/lib/users-contextual-clay-preferences.js +31 -0
- package/lib/users.js +3 -0
- package/lib/workspace-assignment-service.js +25 -0
- package/lib/workspace-query-mcp-server.js +1 -1
- package/lib/ws-request.js +1 -0
- package/lib/ws-schema.js +10 -0
- package/lib/yoke/claude-hook-installer.js +4 -0
- package/package.json +1 -1
|
@@ -125,7 +125,7 @@ export function handleKnowledgeDbClick(event, panel) {
|
|
|
125
125
|
if (event.target.closest('[data-db-create-submit]')) { submitCreate(panel); return true; }
|
|
126
126
|
if (event.target.closest('[data-db-create-cancel]')) { patchState(workspaceKey(), { createDraft: null }); patchMain(workspaceKey(), { fullscreen: false }); if (renderOwner) renderOwner({ forceDocument: true }); return true; }
|
|
127
127
|
var reference = event.target.closest('[data-db-reference]'); if (reference) { var referenceKey = workspaceKey(); if (reference.dataset.dbReferenceType === 'text') { if (openOwnerDocument) openOwnerDocument('local:' + reference.dataset.dbReference); } else { var databaseIdentity = identityOwner ? identityOwner({ id: reference.dataset.dbReferenceDatabase, itemType: 'db', common: false }) : 'local:' + reference.dataset.dbReferenceDatabase; if (openOwnerDocument) openOwnerDocument(databaseIdentity); openRecord(reference.dataset.dbReferenceDatabase, reference.dataset.dbReference, referenceKey); } return true; }
|
|
128
|
-
var row = event.target.closest('[data-db-record]'); if (row) { openRecord(row.dataset.dbDatabase, row.dataset.dbRecord); return true; }
|
|
128
|
+
var row = event.target.closest('tr[data-db-record]'); if (row) { openRecord(row.dataset.dbDatabase, row.dataset.dbRecord); return true; }
|
|
129
129
|
var edit = event.target.closest('[data-db-edit-record]'); if (edit) { beginRecordEdit(edit.dataset.dbEditRecord, false); return true; }
|
|
130
130
|
var add = event.target.closest('[data-db-add-record]'); if (add) { beginRecordEdit(add.dataset.dbAddRecord, true); return true; }
|
|
131
131
|
var save = event.target.closest('[data-db-save-record]'); if (save) { submitRecord(panel, save.dataset.dbSaveRecord); return true; }
|
|
@@ -155,7 +155,7 @@ export function handleKnowledgeDbClick(event, panel) {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
export function handleKnowledgeDbKeydown(event, panel) {
|
|
158
|
-
var keyboardRow = event.target.closest && event.target.closest('[data-db-record]'); if (keyboardRow && (event.key === 'Enter' || event.key === ' ')) { event.preventDefault(); openRecord(keyboardRow.dataset.dbDatabase, keyboardRow.dataset.dbRecord); return true; }
|
|
158
|
+
var keyboardRow = event.target.closest && event.target.closest('tr[data-db-record]'); if (keyboardRow && (event.key === 'Enter' || event.key === ' ')) { event.preventDefault(); openRecord(keyboardRow.dataset.dbDatabase, keyboardRow.dataset.dbRecord); return true; }
|
|
159
159
|
var dialog = panel && panel.querySelector('.knowledge-db-dialog'); if (!dialog) return false;
|
|
160
160
|
if (event.key === 'Escape') { event.preventDefault(); closeArchiveDialog(panel); return true; }
|
|
161
161
|
if (event.key !== 'Tab') return false; var controls = dialog.querySelectorAll('button:not(:disabled)'); if (!controls.length) return false; var first = controls[0]; var last = controls[controls.length - 1];
|
|
@@ -175,10 +175,10 @@ function renderDatabaseRecovery(body, toolbar, draft) { toolbar.innerHTML = '<sp
|
|
|
175
175
|
function renderTable(body, dbState) {
|
|
176
176
|
var database = dbState.database; var view = database.views.filter(function (item) { return item.id === dbState.selectedViewId; })[0] || database.views[0] || { columns: database.schema.filter(function (field) { return !field.archived; }).map(function (field) { return field.id; }) }; var fields = view.columns.map(function (id) { return fieldById(database, id); }).filter(Boolean);
|
|
177
177
|
var html = '<div class="knowledge-db-table-wrap"><table class="knowledge-db-table"><thead><tr>' + fields.map(function (field) { return '<th scope="col">' + escapeText(field.name) + '</th>'; }).join('') + '</tr></thead><tbody>';
|
|
178
|
-
html += dbState.records.map(function (record) { return '<tr tabindex="0" data-db-record="' + escapeText(record.id) + '" data-db-database="' + escapeText(database.id) + '">' + fields.map(function (field) { return '<td>' + displayValue(field, record.values[field.id], record.references) + '</td>'; }).join('') + '</tr>'; }).join('');
|
|
178
|
+
html += dbState.records.map(function (record) { return '<tr data-control-runtime="' + escapeText(store.get('controlRuntimeId')) + '" data-control-revision="' + escapeText(record.revision) + '" tabindex="0" data-db-record="' + escapeText(record.id) + '" data-db-database="' + escapeText(database.id) + '">' + fields.map(function (field) { return '<td>' + displayValue(field, record.values[field.id], record.references) + '</td>'; }).join('') + '</tr>'; }).join('');
|
|
179
179
|
html += '</tbody></table>' + (!dbState.records.length && !dbState.loading ? '<p class="knowledge-empty">No records in this view.</p>' : '') + (dbState.nextCursor ? '<button type="button" class="knowledge-search-more" data-db-more="' + escapeText(database.id) + '">Load more</button>' : '') + '</div>'; body.innerHTML = html;
|
|
180
180
|
}
|
|
181
|
-
function renderRecord(body, dbState) { var detail = selectedRecordState(workspaceKey(), dbState.database.id); if (!detail) { body.innerHTML = '<p class="knowledge-empty">Loading record…</p>'; return; } var history = (detail.history || []).map(function (entry) { return '<li><b>' + escapeText(entry.action || 'update') + '</b><span>Revision ' + escapeText(entry.revision) + ' · ' + new Date(entry.at).toLocaleString() + '</span></li>'; }).join(''); body.innerHTML = '<article class="knowledge-db-record"><button type="button" data-db-record-back="' + escapeText(dbState.database.id) + '">' + iconHtml('arrow-left') + 'Table</button><h2>' + escapeText(detail.title) + '</h2><dl>' + dbState.database.schema.filter(function (field) { return !field.archived; }).map(function (field) { return '<dt>' + escapeText(field.name) + '</dt><dd>' + displayValue(field, detail.record.values[field.id], detail.record.references) + '</dd>'; }).join('') + '</dl><p class="knowledge-db-attribution">Revision ' + detail.record.revision + ' · updated ' + new Date(detail.record.updatedAt).toLocaleString() + '</p>' + (history ? '<h3>Recent history</h3><ol class="knowledge-db-history">' + history + '</ol>' : '') + '</article>'; }
|
|
181
|
+
function renderRecord(body, dbState) { var detail = selectedRecordState(workspaceKey(), dbState.database.id); if (!detail) { body.innerHTML = '<p class="knowledge-empty">Loading record…</p>'; return; } var history = (detail.history || []).map(function (entry) { return '<li><b>' + escapeText(entry.action || 'update') + '</b><span>Revision ' + escapeText(entry.revision) + ' · ' + new Date(entry.at).toLocaleString() + '</span></li>'; }).join(''); body.innerHTML = '<article class="knowledge-db-record" data-control-runtime="' + escapeText(store.get('controlRuntimeId')) + '" data-control-revision="' + escapeText(detail.record.revision) + '" data-db-record="' + escapeText(detail.record.id) + '" data-db-database="' + escapeText(dbState.database.id) + '"><button type="button" data-db-record-back="' + escapeText(dbState.database.id) + '">' + iconHtml('arrow-left') + 'Table</button><h2>' + escapeText(detail.title) + '</h2><dl>' + dbState.database.schema.filter(function (field) { return !field.archived; }).map(function (field) { return '<dt>' + escapeText(field.name) + '</dt><dd>' + displayValue(field, detail.record.values[field.id], detail.record.references) + '</dd>'; }).join('') + '</dl><p class="knowledge-db-attribution">Revision ' + detail.record.revision + ' · updated ' + new Date(detail.record.updatedAt).toLocaleString() + '</p>' + (history ? '<h3>Recent history</h3><ol class="knowledge-db-history">' + history + '</ol>' : '') + '</article>'; }
|
|
182
182
|
function renderRecordForm(body, toolbar, dbState) { var edit = dbState.edit; var values = edit.values || {}; toolbar.innerHTML = '<span class="knowledge-preview-name">' + (edit.creating ? 'New record' : 'Edit record') + '</span>' + (!edit.creating ? '<button type="button" class="danger" data-db-archive-record="' + escapeText(dbState.database.id) + '">Archive</button>' : '') + '<button type="button" data-db-cancel-edit>Cancel</button><button type="button" class="primary" data-db-save-record="' + escapeText(dbState.database.id) + '">' + (edit.saving ? 'Retry save' : 'Save') + '</button>'; body.innerHTML = '<form class="knowledge-db-form" onsubmit="return false"><h2>' + (edit.creating ? 'New record' : 'Edit record') + '</h2>' + dbState.database.schema.filter(function (field) { return !field.archived; }).map(function (field) { return '<label>' + escapeText(field.name) + fieldInput(field, values[field.id]) + '</label>'; }).join('') + (edit.error ? '<p class="knowledge-inline-error">' + escapeText(edit.error) + '</p>' : '') + '</form>'; }
|
|
183
183
|
function renderSettings(body, toolbar, dbState) { var edit = dbState.edit; toolbar.innerHTML = '<span class="knowledge-preview-name">Database settings</span><button type="button" data-db-cancel-edit>Cancel</button><button type="button" class="primary" data-db-save-settings="' + escapeText(dbState.database.id) + '">' + (edit.saving ? 'Retry rename' : 'Save name') + '</button>'; body.innerHTML = '<form class="knowledge-db-form" onsubmit="return false"><h2>Database settings</h2><label>Name<input data-db-settings-name value="' + escapeText(edit.name) + '" maxlength="120"></label>' + (edit.error ? '<p class="knowledge-inline-error">' + escapeText(edit.error) + '</p>' : '') + '<section class="knowledge-danger-zone"><h3>Archive database</h3><p>Records, schema, views, and history remain recoverable.</p><button type="button" class="danger" data-db-archive-database="' + escapeText(dbState.database.id) + '">Archive database</button></section></form>'; }
|
|
184
184
|
function renderRecover(body, toolbar, dbState) { var edit = dbState.edit; toolbar.innerHTML = '<span class="knowledge-preview-name">Recover record</span><button type="button" data-db-cancel-edit>Cancel</button><button type="button" class="primary" data-db-confirm-recover-record="' + escapeText(dbState.database.id) + '">' + (edit.saving ? 'Retry recovery' : 'Recover') + '</button>'; body.innerHTML = '<div class="knowledge-db-form"><h2>Recover archived record?</h2><p>This restores the exact record at revision ' + escapeText(edit.baseVersion) + '.</p>' + (edit.error ? '<p class="knowledge-inline-error">' + escapeText(edit.error) + '</p>' : '') + '</div>'; }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { enhanceSessionCitations } from './clay-session-links.js';
|
|
1
2
|
import { renderBrowserLink, enhanceBrowserLinks } from './clay-browser-links.js';
|
|
2
3
|
import { renderWireframeBlocks } from './wireframe-chat.js';
|
|
3
4
|
import { copyToClipboard, escapeHtml } from './utils.js';
|
|
@@ -70,40 +71,7 @@ export function renderMarkdown(text) {
|
|
|
70
71
|
|
|
71
72
|
export function enhanceClaySessionLinks(root) {
|
|
72
73
|
if (!root || typeof document === "undefined") return;
|
|
73
|
-
|
|
74
|
-
var nodes = [];
|
|
75
|
-
var node;
|
|
76
|
-
while ((node = walker.nextNode())) {
|
|
77
|
-
if (!node.parentElement || node.parentElement.closest("code, pre, a, button")) continue;
|
|
78
|
-
if (/\[clayos\/session:[A-Za-z0-9_-]{24}(?:\s+[\u2014-]\s+[^\]\n]{1,80})?\]/.test(node.nodeValue || "")) nodes.push(node);
|
|
79
|
-
}
|
|
80
|
-
for (var i = 0; i < nodes.length; i++) {
|
|
81
|
-
var text = nodes[i].nodeValue || "";
|
|
82
|
-
var pattern = /\[clayos\/(session:[A-Za-z0-9_-]{24})(?:\s+[\u2014-]\s+([^\]\n]{1,80}))?\]/g;
|
|
83
|
-
var fragment = document.createDocumentFragment();
|
|
84
|
-
var offset = 0;
|
|
85
|
-
var match;
|
|
86
|
-
while ((match = pattern.exec(text))) {
|
|
87
|
-
fragment.appendChild(document.createTextNode(text.slice(offset, match.index)));
|
|
88
|
-
var button = document.createElement("button");
|
|
89
|
-
button.type = "button";
|
|
90
|
-
button.className = "clayos-session-link clayos-record-link";
|
|
91
|
-
button.dataset.sessionRef = match[1];
|
|
92
|
-
button.setAttribute("aria-label", "Open session" + (match[2] ? " from " + match[2].trim() : ""));
|
|
93
|
-
var label = document.createElement("span");
|
|
94
|
-
label.textContent = "Session";
|
|
95
|
-
button.appendChild(label);
|
|
96
|
-
if (match[2]) {
|
|
97
|
-
var meta = document.createElement("small");
|
|
98
|
-
meta.textContent = match[2].trim();
|
|
99
|
-
button.appendChild(meta);
|
|
100
|
-
}
|
|
101
|
-
fragment.appendChild(button);
|
|
102
|
-
offset = pattern.lastIndex;
|
|
103
|
-
}
|
|
104
|
-
fragment.appendChild(document.createTextNode(text.slice(offset)));
|
|
105
|
-
nodes[i].parentNode.replaceChild(fragment, nodes[i]);
|
|
106
|
-
}
|
|
74
|
+
enhanceSessionCitations(root);
|
|
107
75
|
enhanceClayLogLinks(root);
|
|
108
76
|
enhanceClayFileLinks(root);
|
|
109
77
|
enhanceBrowserLinks(root);
|
|
@@ -350,7 +350,7 @@ function renderExplorer() {
|
|
|
350
350
|
files.forEach(function (file) {
|
|
351
351
|
var parts = file.name.split('/'); var folder = parts.length > 1 ? parts.slice(0, -1).join('/') : '';
|
|
352
352
|
if (!state.query.trim() && folder !== lastFolder) { var folderRow = document.createElement('div'); folderRow.className = 'knowledge-folder-row'; folderRow.innerHTML = iconHtml('folder') + '<span>' + escapeText(folder || 'Top level') + '</span>'; list.appendChild(folderRow); lastFolder = folder; }
|
|
353
|
-
var id = documentIdentity(file); var button = document.createElement('button'); button.type = 'button'; button.className = 'knowledge-file-row'; button.dataset.openDocument = id; button.style.setProperty('--knowledge-depth', String(Math.min(parts.length - 1, 6))); if (id === state.selected) button.setAttribute('aria-current', 'true');
|
|
353
|
+
var id = documentIdentity(file); var button = document.createElement('button'); button.type = 'button'; button.className = 'knowledge-file-row'; button.dataset.openDocument = id; button.dataset.controlRevision = String(file.revision || ''); button.dataset.controlRuntime = store.get('controlRuntimeId') || ''; button.style.setProperty('--knowledge-depth', String(Math.min(parts.length - 1, 6))); if (id === state.selected) button.setAttribute('aria-current', 'true');
|
|
354
354
|
button.innerHTML = iconHtml(file.itemType === 'db' || file.name.endsWith('.jsonl') ? 'database' : 'file-text') + '<span>' + escapeText(parts[parts.length - 1].replace(/\.(md|jsonl)$/i, '')) + (file.snippet ? '<em>' + escapeText(file.snippet) + '</em>' : '') + '</span>' + (file.common ? '<small>shared</small>' : ''); list.appendChild(button);
|
|
355
355
|
});
|
|
356
356
|
if (state.query.trim() && state.searchFiles.length < state.searchTotal) { var more = document.createElement('button'); more.type = 'button'; more.className = 'knowledge-search-more'; more.dataset.searchMore = ''; more.disabled = state.searching; more.textContent = state.searching ? 'Loading more…' : 'Load more results'; list.appendChild(more); }
|
|
@@ -179,6 +179,7 @@ export function sendAcknowledgedMessage(payload) {
|
|
|
179
179
|
nextPayload.cliSessionId = entry.cliSessionId;
|
|
180
180
|
nextPayload.sessionOriginId = entry.sessionOriginId;
|
|
181
181
|
entry.payload = nextPayload;
|
|
182
|
+
if (nextPayload.contextualSnapshot) store.set({ contextualSnapshotSubmissions: (store.get('contextualSnapshotSubmissions') || []).concat([{ clientMessageId: nextPayload.clientMessageId, accountId: entry.accountId, projectSlug: entry.projectSlug, sessionId: entry.sessionId, sessionOriginId: entry.sessionOriginId, snapshot: nextPayload.contextualSnapshot }]).slice(-64) });
|
|
182
183
|
replacePending(pendingMessages().concat([entry]));
|
|
183
184
|
if (!transmit(entry, false)) {
|
|
184
185
|
if (!getWs() || getWs().readyState !== 1) markUnconfirmed(entry);
|
|
@@ -204,6 +205,15 @@ export function retryPendingMessage(clientMessageId) {
|
|
|
204
205
|
return false;
|
|
205
206
|
}
|
|
206
207
|
|
|
208
|
+
// Stop retains the uncertain receipt but prevents automatic retransmission.
|
|
209
|
+
export function pausePendingMessage(clientMessageId) {
|
|
210
|
+
var entry = pendingEntry(clientMessageId);
|
|
211
|
+
if (!entry || !matchesCurrentContext(entry)) return false;
|
|
212
|
+
clearAckTimer(clientMessageId);
|
|
213
|
+
markUnconfirmed(entry);
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
207
217
|
export function acknowledgeMessage(clientMessageId) {
|
|
208
218
|
if (typeof clientMessageId !== "string") return null;
|
|
209
219
|
var pending = pendingMessages();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { forwardStudioPresentation } from './clay-studio-render.js';
|
|
1
2
|
// Pane-side postMessage bridge for the split-view shell. A chrome-less pane
|
|
2
3
|
// iframe reports its context usage up to the parent (which renders a chip in
|
|
3
4
|
// the pane header) and honors panel-toggle requests coming back down.
|
|
@@ -32,8 +33,7 @@ export function forwardPaneAuthRequired(message) {
|
|
|
32
33
|
|
|
33
34
|
export function forwardPaneMarkdownPresentation(message) {
|
|
34
35
|
if (!store.get('paneMode') || window.parent === window) return false;
|
|
35
|
-
|
|
36
|
-
return true;
|
|
36
|
+
return forwardStudioPresentation(message, "clay-pane-present-markdown");
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export function forwardPaneIssueReference(ref, projectSlug) {
|
|
@@ -330,6 +330,7 @@ function renderItem(item, index) {
|
|
|
330
330
|
var disabled = controlsDisabled ? ' disabled aria-disabled="true"' : '';
|
|
331
331
|
var text = (item.message && item.message.text || "").trim() || "Attachment-only message";
|
|
332
332
|
var html = '<div class="pending-message-chip' + (item.state === "claimed" ? ' is-claimed' : '') + (scheduled ? ' is-scheduled' : '') + (futureScheduled ? ' is-future' : '') + '" data-pending-id="' + escapeHtml(item.id) + '">';
|
|
333
|
+
if (item.schedule && item.schedule.logicalScheduleId) html = html.replace('data-pending-id=', 'data-control-schedule-id="' + escapeHtml(item.schedule.logicalScheduleId) + '" data-control-revision="' + escapeHtml(item.schedule.logicalRevision || '') + '" data-control-runtime="' + escapeHtml(store.get('controlRuntimeId') || '') + '" data-pending-id=');
|
|
333
334
|
html += '<span class="pending-message-number" aria-hidden="true">' + (index + 1) + '</span>';
|
|
334
335
|
if (own && item.state === "pending") html += '<button class="pending-message-drag" type="button" data-action="drag" aria-label="Reorder queued message ' + (index + 1) + '" title="Drag, or use arrow keys to reorder"' + disabled + '>' + '<i data-lucide="grip-vertical"></i></button>';
|
|
335
336
|
else html += '<span class="pending-message-drag is-static"><i data-lucide="' + (item.state === "claimed" ? "loader-circle" : "lock-keyhole") + '"></i></span>';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Display-only metadata from a bound product service. The exact question and
|
|
2
|
+
// authenticated response remain unchanged; this renderer grants no authority.
|
|
3
|
+
export function renderProductReview(element, question) {
|
|
4
|
+
var review = question.review;
|
|
5
|
+
if (!review || review.kind !== 'product-control' || typeof review.action !== 'string' || typeof review.target !== 'string' || typeof review.context !== 'string' || typeof review.reason !== 'string') return false;
|
|
6
|
+
element.classList.add('product-review');
|
|
7
|
+
element.textContent = '';
|
|
8
|
+
function text(tag, className, value) {
|
|
9
|
+
var node = document.createElement(tag);
|
|
10
|
+
node.className = className;
|
|
11
|
+
node.textContent = value;
|
|
12
|
+
element.appendChild(node);
|
|
13
|
+
}
|
|
14
|
+
text('strong', 'product-review-action', review.action + ': ' + review.target);
|
|
15
|
+
text('p', 'product-review-context', review.context);
|
|
16
|
+
text('p', 'product-review-reason', review.reason);
|
|
17
|
+
var details = document.createElement('details');
|
|
18
|
+
var summary = document.createElement('summary');
|
|
19
|
+
summary.textContent = 'Exact request details';
|
|
20
|
+
var payload = document.createElement('pre');
|
|
21
|
+
payload.textContent = question.question;
|
|
22
|
+
details.appendChild(summary);
|
|
23
|
+
details.appendChild(payload);
|
|
24
|
+
element.appendChild(details);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
@@ -4,6 +4,9 @@ import { store } from './store.js';
|
|
|
4
4
|
import { mateAvatarUrl } from './avatar.js';
|
|
5
5
|
import { VENDOR_AVATARS, VENDOR_NAMES } from './chat-render-runtime.js';
|
|
6
6
|
|
|
7
|
+
// A missing or failed profile image must never borrow another speaker's identity.
|
|
8
|
+
var NEUTRAL_AVATAR = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="8" fill="#888" fill-opacity=".14"/><circle cx="16" cy="12" r="5" fill="#888"/><path d="M6 29a10 10 0 0 1 20 0" fill="#888"/></svg>');
|
|
9
|
+
|
|
7
10
|
function mateById(state, mateId) {
|
|
8
11
|
var mates = state.cachedMatesList || [];
|
|
9
12
|
for (var i = 0; i < mates.length; i++) {
|
|
@@ -24,12 +27,11 @@ export function resolveProjectAssistantIdentity(state, vendor) {
|
|
|
24
27
|
if (mateId) {
|
|
25
28
|
var mate = mateById(state, mateId) || dmTarget;
|
|
26
29
|
var fallbackName = dmTarget && dmTarget.displayName || state.activeSessionMateName || 'Mate';
|
|
27
|
-
var avatarMate = mate || { id: mateId, name: fallbackName, profile: { displayName: fallbackName } };
|
|
28
30
|
return {
|
|
29
31
|
kind: 'mate',
|
|
30
32
|
mateId: mateId,
|
|
31
33
|
name: mateName(mate, fallbackName),
|
|
32
|
-
avatarUrl: mateAvatarUrl(
|
|
34
|
+
avatarUrl: mate ? mateAvatarUrl(mate, 34) : NEUTRAL_AVATAR,
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
37
|
var vendorId = vendor || state.currentVendor || 'claude';
|
|
@@ -47,9 +49,16 @@ export function getProjectAssistantPlaceholder(state, vendor) {
|
|
|
47
49
|
|
|
48
50
|
export function applyProjectAssistantIdentity(element, identity) {
|
|
49
51
|
if (!element || !identity) return element;
|
|
50
|
-
var avatar = element.querySelector('.dm-bubble-avatar');
|
|
52
|
+
var avatar = element.querySelector('.dm-bubble-avatar') || element.querySelector('.activity-inline-avatar');
|
|
51
53
|
var name = element.querySelector('.dm-bubble-name');
|
|
52
|
-
if (avatar)
|
|
54
|
+
if (avatar) {
|
|
55
|
+
avatar.onerror = identity.kind === 'mate' ? function () { avatar.onerror = null; avatar.src = NEUTRAL_AVATAR; } : null;
|
|
56
|
+
avatar.src = identity.avatarUrl;
|
|
57
|
+
}
|
|
58
|
+
if (element.className.indexOf('activity-inline') >= 0) {
|
|
59
|
+
var activityLabel = element.querySelector('.activity-state-label');
|
|
60
|
+
element.setAttribute('aria-label', identity.name + ' · ' + (activityLabel ? activityLabel.textContent : 'Responding'));
|
|
61
|
+
}
|
|
53
62
|
if (name) name.textContent = identity.name;
|
|
54
63
|
if (identity.kind === 'mate') {
|
|
55
64
|
element.dataset.sessionMateId = identity.mateId;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
1
2
|
// Project Logs rendering — ledger rows, entry detail, and discussion.
|
|
2
3
|
//
|
|
3
4
|
// Split from project-logs.js so both stay well inside the module size limit.
|
|
@@ -142,6 +143,8 @@ function renderRow(entry, onOpen) {
|
|
|
142
143
|
row.type = "button";
|
|
143
144
|
row.className = "project-log-row";
|
|
144
145
|
row.dataset.ref = entry.ref;
|
|
146
|
+
row.dataset.controlRuntime = store.get("controlRuntimeId") || "";
|
|
147
|
+
row.dataset.controlRevision = String(entry.revisions);
|
|
145
148
|
row.setAttribute("role", "listitem");
|
|
146
149
|
|
|
147
150
|
var head = document.createElement("div");
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { contextualMateStamp, contextualMate } from './contextual-mate-identity.js';
|
|
2
|
+
import { finishContextualWorkspace } from './contextual-clay-workspace.js';
|
|
3
|
+
import { initContextualClay, cycleContextualClay, contextualClayMode } from './contextual-clay.js';
|
|
1
4
|
// Existing Mates share the project rail while retaining their conversations.
|
|
2
5
|
import { store } from './store.js';
|
|
3
6
|
import { applyWorkspaceColor } from './mate-workspace-color.js';
|
|
@@ -103,7 +106,7 @@ function renderMateHeaderDefault(state) {
|
|
|
103
106
|
|
|
104
107
|
function ordinaryProjects(state) {
|
|
105
108
|
return (state.projectsHubList || []).filter(function (project) {
|
|
106
|
-
return project && project.slug && project.isMate !== true;
|
|
109
|
+
return project && project.slug && project.isMate !== true && project.contentAccess !== false;
|
|
107
110
|
});
|
|
108
111
|
}
|
|
109
112
|
|
|
@@ -124,7 +127,7 @@ export function clayReturnTargetForState(state) {
|
|
|
124
127
|
};
|
|
125
128
|
}
|
|
126
129
|
|
|
127
|
-
function rememberClayReturnTarget(state) {
|
|
130
|
+
export function rememberClayReturnTarget(state) {
|
|
128
131
|
if (!state || state.homeShellVisible || state.dmMode || isMateWorkspace(state) || !state.currentSlug ||
|
|
129
132
|
state.activeProjectSlug !== state.currentSlug || state.sessionActivatedProjectSlug !== state.currentSlug) return;
|
|
130
133
|
var projects = ordinaryProjects(state);
|
|
@@ -135,15 +138,18 @@ function rememberClayReturnTarget(state) {
|
|
|
135
138
|
return target;
|
|
136
139
|
}
|
|
137
140
|
|
|
138
|
-
function returnFromClay() {
|
|
141
|
+
export function returnFromClay() {
|
|
142
|
+
var handoff = store.get('contextualWorkspace');
|
|
143
|
+
var originalMate = handoff && handoff.returnSlug && handoff.returnSlug.indexOf('mate-') === 0 && contextualMate(handoff.returnSlug.slice(5));
|
|
144
|
+
if (finishContextualWorkspace() === false) return false;
|
|
139
145
|
var state = store.snap();
|
|
140
|
-
var target = clayReturnTargetForState(state);
|
|
146
|
+
var target = originalMate ? { projectSlug: handoff.returnSlug, sessionId: handoff.returnSessionId } : clayReturnTargetForState(state);
|
|
141
147
|
if (!target) {
|
|
142
148
|
store.set({ pendingProjectSessionNavigation: null, clayReturnPendingProjectSlug: null, clayReturnTarget: null });
|
|
143
149
|
showProjectsHub();
|
|
144
150
|
return false;
|
|
145
151
|
}
|
|
146
|
-
store.set({ clayReturnTarget: null, clayReturnPendingProjectSlug: target.sessionId ? target.projectSlug : null });
|
|
152
|
+
store.set({ clayReturnTarget: originalMate ? state.clayReturnTarget : null, clayReturnPendingProjectSlug: target.sessionId ? target.projectSlug : null });
|
|
147
153
|
if (target.sessionId) openProjectSession(target.projectSlug, target.sessionId);
|
|
148
154
|
else {
|
|
149
155
|
store.set({ pendingProjectSessionNavigation: null });
|
|
@@ -156,7 +162,8 @@ function updateClayBrand(state) {
|
|
|
156
162
|
var entry = document.querySelector('.icon-strip-brand');
|
|
157
163
|
if (!entry) return;
|
|
158
164
|
var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
|
|
159
|
-
var
|
|
165
|
+
var ownPopover = (state.contextualClay || {}).mateId === (clay && clay.id) && contextualClayMode() !== 'closed';
|
|
166
|
+
var active = clayIsActive(state, clay) || ownPopover;
|
|
160
167
|
var speaking = !!(state.youActivity && state.youActivity.active);
|
|
161
168
|
entry.dataset.clayMode = active ? 'mate' : 'project';
|
|
162
169
|
entry.classList.toggle('clay-speaking', speaking);
|
|
@@ -166,20 +173,40 @@ function updateClayBrand(state) {
|
|
|
166
173
|
if (logo) logo.src = 'clay-studio-symbol.png';
|
|
167
174
|
entry.disabled = !clay;
|
|
168
175
|
entry.classList.toggle('active', active);
|
|
169
|
-
entry.setAttribute('aria-label', clay ? (active ? 'Return to project' : 'Open Clay') : 'Clay is unavailable');
|
|
170
|
-
|
|
176
|
+
entry.setAttribute('aria-label', clay ? (ownPopover && contextualClayMode() === 'popover' ? 'Open Clay workspace' : active ? 'Return to project' : 'Open Clay') : 'Clay is unavailable');
|
|
177
|
+
entry.setAttribute('aria-expanded', String(ownPopover));
|
|
178
|
+
entry.setAttribute('aria-controls', 'contextual-clay');
|
|
179
|
+
if (clayIsActive(state, clay)) entry.setAttribute('aria-current', 'page');
|
|
171
180
|
else entry.removeAttribute('aria-current');
|
|
172
181
|
}
|
|
173
182
|
|
|
174
|
-
export function openMateWorkspace(mateId) {
|
|
183
|
+
export function openMateWorkspace(mateId, sessionId) {
|
|
175
184
|
var mate = navigationMates(store.get('cachedMatesList')).find(function (item) { return item.id === mateId; });
|
|
176
|
-
if (!mate) return;
|
|
185
|
+
if (!mate || !contextualMate(mateId)) return;
|
|
177
186
|
if (mate.builtinKey === 'clay' && store.snap) rememberClayReturnTarget(store.snap());
|
|
178
187
|
if (store.get('currentSlug') !== 'mate-' + mateId) {
|
|
179
188
|
closeTerminal();
|
|
180
189
|
closeFileViewer();
|
|
181
190
|
}
|
|
182
|
-
|
|
191
|
+
if (Number.isSafeInteger(sessionId) && sessionId > 0) openProjectSession('mate-' + mateId, sessionId);
|
|
192
|
+
else switchProject('mate-' + mateId);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function activateContextualMate(mateId) {
|
|
196
|
+
var mate = contextualMate(mateId);
|
|
197
|
+
if (!mate) return;
|
|
198
|
+
var handoff = store.get('contextualWorkspace');
|
|
199
|
+
if (handoff && handoff.stage !== 'returning') {
|
|
200
|
+
if (handoff.stage !== 'workspace') return;
|
|
201
|
+
var popup = store.get('contextualClay') || {};
|
|
202
|
+
var otherSession = popup.mode === 'popover' && popup.mateId === mateId && popup.sessionId && popup.sessionId !== handoff.sessionId;
|
|
203
|
+
if (handoff.slug === 'mate-' + mateId && !otherSession && !(popup.mode === 'popover' && popup.mateId === mateId)) return returnFromClay();
|
|
204
|
+
cycleContextualClay(mate);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (handoff) { store.set({ contextualMatePending: mateId }); return; }
|
|
208
|
+
rememberClayReturnTarget(store.snap());
|
|
209
|
+
cycleContextualClay(mate);
|
|
183
210
|
}
|
|
184
211
|
|
|
185
212
|
function createMateIcon(mateId) {
|
|
@@ -190,9 +217,10 @@ function createMateIcon(mateId) {
|
|
|
190
217
|
button.innerHTML = '<img class="icon-strip-mate-avatar" alt="" draggable="false"><span class="icon-strip-pill"></span><span class="icon-strip-status"></span><span class="icon-strip-project-badge"></span>';
|
|
191
218
|
button.addEventListener('click', function () {
|
|
192
219
|
hideIconTooltip();
|
|
193
|
-
|
|
220
|
+
activateContextualMate(mateId);
|
|
194
221
|
});
|
|
195
222
|
bindIconTooltip(button, function () {
|
|
223
|
+
if ((store.get('contextualClay') || {}).mode === 'popover' && (store.get('contextualClay') || {}).mateId === mateId) return { text: '' };
|
|
196
224
|
var mate = navigationMates(store.get('cachedMatesList')).find(function (item) { return item.id === mateId; });
|
|
197
225
|
return {
|
|
198
226
|
text: mate ? getHomeMateName(mate) : 'Mate',
|
|
@@ -225,21 +253,25 @@ export function renderProjectMates() {
|
|
|
225
253
|
applyWorkspaceColor(mateForWorkspace(state));
|
|
226
254
|
renderMateHeaderDefault(state);
|
|
227
255
|
var mates = navigationMates(state.cachedMatesList).filter(function (mate) {
|
|
228
|
-
return mate.builtinKey !== 'clay';
|
|
256
|
+
return mate.builtinKey !== 'clay' && contextualMate(mate.id);
|
|
229
257
|
});
|
|
230
258
|
var mentionActive = getActiveMentionMateIds();
|
|
231
259
|
var projects = state.projectsHubList || [];
|
|
232
260
|
var unread = state.dmUnread || {};
|
|
233
261
|
var icons = {};
|
|
234
|
-
|
|
262
|
+
document.querySelectorAll('.icon-strip-project-mate[data-mate-id]').forEach(function (button) { icons[button.dataset.mateId] = button; });
|
|
235
263
|
mates.forEach(function (mate) {
|
|
236
264
|
var button = icons[mate.id] || createMateIcon(mate.id);
|
|
237
265
|
delete icons[mate.id];
|
|
266
|
+
button.dataset.controlMateStamp = contextualMateStamp(mate);
|
|
238
267
|
var active = !state.homeShellVisible && !state.dmMode && state.currentSlug === 'mate-' + mate.id;
|
|
239
268
|
var project = projects.find(function (item) { return item.slug === 'mate-' + mate.id; });
|
|
240
269
|
var busy = !!(project && project.isProcessing) || !!mentionActive[mate.id];
|
|
241
270
|
var count = active ? 0 : (unread[mate.id] || 0);
|
|
242
|
-
|
|
271
|
+
var popover = (state.contextualClay || {}).mateId === mate.id && contextualClayMode() === 'popover';
|
|
272
|
+
button.classList.toggle('active', !!active || popover);
|
|
273
|
+
button.setAttribute('aria-expanded', String(popover));
|
|
274
|
+
button.setAttribute('aria-controls', 'contextual-clay');
|
|
243
275
|
button.setAttribute('aria-label', getHomeMateName(mate) + ', Mate' + (busy ? ', working' : '') + (count ? ', ' + count + ' unread' : ''));
|
|
244
276
|
if (active) button.setAttribute('aria-current', 'page');
|
|
245
277
|
else button.removeAttribute('aria-current');
|
|
@@ -257,6 +289,7 @@ export function renderProjectMates() {
|
|
|
257
289
|
Object.keys(icons).forEach(function (id) { icons[id].remove(); });
|
|
258
290
|
var ordered = Array.from(rail.querySelectorAll('[data-mate-id]'));
|
|
259
291
|
mates.forEach(function (mate, index) {
|
|
292
|
+
if (document.querySelector('.contextual-mate-mobile-trigger[data-mate-id="' + CSS.escape(mate.id) + '"]')) return;
|
|
260
293
|
var button = ordered.find(function (item) { return item.dataset.mateId === mate.id; });
|
|
261
294
|
if (ordered[index] === button) return;
|
|
262
295
|
rail.insertBefore(button, ordered[index] || null);
|
|
@@ -284,21 +317,25 @@ export function renderProjectMates() {
|
|
|
284
317
|
}
|
|
285
318
|
|
|
286
319
|
export function initProjectMateNavigation() {
|
|
320
|
+
initContextualClay();
|
|
287
321
|
var clayEntry = document.querySelector('.icon-strip-brand');
|
|
288
322
|
if (clayEntry) {
|
|
289
|
-
clayEntry.addEventListener('click', function () {
|
|
323
|
+
clayEntry.addEventListener('click', function (event) {
|
|
290
324
|
var state = store.snap();
|
|
291
325
|
var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
|
|
292
326
|
hideIconTooltip();
|
|
293
327
|
if (!clay) return;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
328
|
+
var popup = state.contextualClay || {};
|
|
329
|
+
if (clayIsActive(state, clay) && !(popup.mode === 'popover' && popup.mateId === clay.id)) returnFromClay();
|
|
330
|
+
else {
|
|
331
|
+
activateContextualMate(clay.id);
|
|
332
|
+
}
|
|
297
333
|
});
|
|
298
334
|
bindIconTooltip(clayEntry, function () {
|
|
299
335
|
var state = store.snap();
|
|
300
336
|
var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
|
|
301
|
-
|
|
337
|
+
if ((state.contextualClay || {}).mateId === (clay && clay.id) && contextualClayMode() === 'popover') return { text: '' };
|
|
338
|
+
return { text: (state.contextualClay || {}).mateId === (clay && clay.id) && contextualClayMode() === 'popover' ? 'Open Clay workspace' : clayIsActive(state, clay) ? 'Return to project' : 'Open Clay', kind: 'mate', avatarUrl: 'clay-studio-symbol.png' };
|
|
302
339
|
});
|
|
303
340
|
}
|
|
304
341
|
var knob = document.getElementById('icon-strip-filter-knob');
|
|
@@ -313,7 +350,7 @@ export function initProjectMateNavigation() {
|
|
|
313
350
|
if (state.cachedMatesList !== previous.cachedMatesList || state.homeChatMateId !== previous.homeChatMateId ||
|
|
314
351
|
state.homeShellVisible !== previous.homeShellVisible || state.projectsHubList !== previous.projectsHubList ||
|
|
315
352
|
state.dmUnread !== previous.dmUnread || state.currentSlug !== previous.currentSlug ||
|
|
316
|
-
state.dmMode !== previous.dmMode || state.connected !== previous.connected || state.activeSessionId !== previous.activeSessionId || state.youActivity !== previous.youActivity) renderProjectMates();
|
|
353
|
+
state.contextualClay !== previous.contextualClay || state.dmMode !== previous.dmMode || state.connected !== previous.connected || state.activeSessionId !== previous.activeSessionId || state.youActivity !== previous.youActivity) renderProjectMates();
|
|
317
354
|
if (state.projectMateFilterMode !== previous.projectMateFilterMode ||
|
|
318
355
|
state.projectMateFilterTurns !== previous.projectMateFilterTurns) applyProjectMateFilter();
|
|
319
356
|
});
|
|
@@ -170,6 +170,8 @@ function buildRow(record) {
|
|
|
170
170
|
var row = document.createElement("article");
|
|
171
171
|
row.className = "scheduled-task-row" + (record.enabled === false ? " paused" : "");
|
|
172
172
|
row.dataset.taskId = record.id;
|
|
173
|
+
row.dataset.controlRuntime = store.get("controlRuntimeId") || "";
|
|
174
|
+
row.dataset.controlRevision = String(record.updatedAt);
|
|
173
175
|
row.setAttribute("role", "listitem");
|
|
174
176
|
var stateLabel = scheduledTaskExecutionLabel(record);
|
|
175
177
|
row.innerHTML =
|