clay-server 3.8.1 → 4.0.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/lib/ask-user-mcp-server.js +5 -119
- package/lib/builtin-mates.js +6 -6
- package/lib/claude-hook-installer.js +15 -0
- package/lib/clay-history-mcp-server.js +62 -297
- package/lib/daemon.js +1 -1
- package/lib/debate-model-selection.js +119 -0
- package/lib/home-debate-tool-policy.js +164 -0
- package/lib/home-response-notifications.js +26 -0
- package/lib/mate-creation-mcp-server.js +55 -0
- package/lib/mate-ready-creation.js +95 -0
- package/lib/mates.js +2 -0
- package/lib/notes.js +2 -1
- package/lib/project-ask-user.js +64 -0
- package/lib/project-capsule-catalog.js +101 -0
- package/lib/project-connection.js +4 -0
- package/lib/project-debate-proposal.js +133 -17
- package/lib/project-debate.js +214 -110
- package/lib/project-delegated-follow-up.js +84 -0
- package/lib/project-delegated-session.js +53 -0
- package/lib/project-filesystem.js +7 -9
- package/lib/project-knowledge.js +1 -0
- package/lib/project-mate-creation-proposal.js +120 -0
- package/lib/project-memory.js +14 -7
- package/lib/project-models.js +157 -32
- package/lib/project-session-notes.js +2 -2
- package/lib/project-session-pair.js +8 -8
- package/lib/project-sessions.js +66 -51
- package/lib/project-vendor-login.js +397 -0
- package/lib/project-worker-proposal.js +25 -25
- package/lib/project-workspace-query.js +144 -0
- package/lib/project.js +349 -102
- package/lib/public/app.js +85 -42
- package/lib/public/css/avatar-imprints.css +0 -1
- package/lib/public/css/base.css +4 -0
- package/lib/public/css/capsule-ui.css +389 -0
- package/lib/public/css/command-palette.css +306 -6
- package/lib/public/css/home-capsule-library.css +243 -0
- package/lib/public/css/home-capsule-source.css +192 -0
- package/lib/public/css/home-debate-live.css +146 -0
- package/lib/public/css/home-debate-planning.css +125 -0
- package/lib/public/css/home-debates-archive.css +281 -0
- package/lib/public/css/home-hub.css +896 -405
- package/lib/public/css/home-mate-creation.css +34 -0
- package/lib/public/css/home-mate-model-picker.css +131 -0
- package/lib/public/css/home-mate-settings.css +344 -0
- package/lib/public/css/home-session-actions.css +265 -0
- package/lib/public/css/home-sidebar.css +667 -0
- package/lib/public/css/icon-strip.css +1 -2
- package/lib/public/css/mates.css +7 -34
- package/lib/public/css/pane.css +1 -1
- package/lib/public/css/playbook.css +0 -80
- package/lib/public/css/sticky-notes.css +53 -117
- package/lib/public/css/tui-attention.css +0 -44
- package/lib/public/css/workspace-assignment.css +45 -0
- package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
- package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
- package/lib/public/index.html +93 -74
- package/lib/public/modules/app-connection.js +10 -1
- package/lib/public/modules/app-dm.js +43 -160
- package/lib/public/modules/app-home-hub.js +328 -585
- package/lib/public/modules/app-message-cards.js +242 -0
- package/lib/public/modules/app-message-router.js +84 -0
- package/lib/public/modules/app-messages.js +102 -37
- package/lib/public/modules/app-notifications.js +8 -119
- package/lib/public/modules/app-projects.js +9 -1
- package/lib/public/modules/app-rendering.js +21 -602
- package/lib/public/modules/avatar-imprint.js +11 -8
- package/lib/public/modules/chat-bubble-renderer.js +152 -0
- package/lib/public/modules/chat-render-runtime.js +198 -0
- package/lib/public/modules/command-palette.js +225 -436
- package/lib/public/modules/dm-render.js +82 -0
- package/lib/public/modules/home-capsule-creation-intent.js +31 -0
- package/lib/public/modules/home-capsule-library.js +146 -0
- package/lib/public/modules/home-capsule-source.js +383 -0
- package/lib/public/modules/home-chat-identity.js +35 -0
- package/lib/public/modules/home-chat-scroll.js +77 -0
- package/lib/public/modules/home-chat-stream-state.js +34 -0
- package/lib/public/modules/home-composer-focus.js +25 -0
- package/lib/public/modules/home-conversations-sheet.js +179 -0
- package/lib/public/modules/home-debate-activity.js +48 -0
- package/lib/public/modules/home-debate-controls.js +295 -0
- package/lib/public/modules/home-debate-launch.js +41 -0
- package/lib/public/modules/home-debate-live.js +284 -0
- package/lib/public/modules/home-debate-models.js +73 -0
- package/lib/public/modules/home-debate-planning.js +335 -0
- package/lib/public/modules/home-debates-archive.js +279 -0
- package/lib/public/modules/home-dock-resize.js +74 -0
- package/lib/public/modules/home-dock.js +446 -0
- package/lib/public/modules/home-mate-chat.js +496 -0
- package/lib/public/modules/home-mate-creation.js +109 -0
- package/lib/public/modules/home-mate-model-picker.js +281 -0
- package/lib/public/modules/home-mate-selection.js +38 -0
- package/lib/public/modules/home-mate-settings-menu.js +131 -0
- package/lib/public/modules/home-mate-settings.js +383 -0
- package/lib/public/modules/home-session-actions.js +373 -0
- package/lib/public/modules/home-session-model-confirmation.js +13 -0
- package/lib/public/modules/home-shell.js +9 -0
- package/lib/public/modules/home-sidebar-chat-list.js +63 -0
- package/lib/public/modules/home-sidebar.js +158 -0
- package/lib/public/modules/home-sub-surface.js +19 -0
- package/lib/public/modules/home-surface-boot.js +68 -0
- package/lib/public/modules/home-surface.js +141 -0
- package/lib/public/modules/home-tools.js +271 -0
- package/lib/public/modules/markdown.js +50 -2
- package/lib/public/modules/mate-management.js +86 -0
- package/lib/public/modules/mate-wizard.js +0 -7
- package/lib/public/modules/mention.js +17 -19
- package/lib/public/modules/pane-bridge.js +9 -0
- package/lib/public/modules/profile.js +4 -5
- package/lib/public/modules/project-settings.js +34 -28
- package/lib/public/modules/project-switcher.js +11 -5
- package/lib/public/modules/project-workspace-assignment.js +23 -0
- package/lib/public/modules/search-clay-chat.js +402 -0
- package/lib/public/modules/server-settings.js +14 -12
- package/lib/public/modules/session-actions.js +1 -1
- package/lib/public/modules/sidebar-mates.js +56 -552
- package/lib/public/modules/sidebar-mobile.js +7 -64
- package/lib/public/modules/sidebar-presence.js +37 -0
- package/lib/public/modules/split-pair-ui.js +13 -13
- package/lib/public/modules/split-view.js +7 -0
- package/lib/public/modules/sticky-notes.js +7 -6
- package/lib/public/modules/tool-input-composition.js +48 -0
- package/lib/public/modules/tool-llm-status.js +167 -0
- package/lib/public/modules/tool-renderer-advanced.js +285 -0
- package/lib/public/modules/tool-renderer-chart.js +158 -0
- package/lib/public/modules/tool-renderer-semantics.js +35 -0
- package/lib/public/modules/tool-renderer.js +464 -0
- package/lib/public/modules/tool-runtime.js +309 -0
- package/lib/public/modules/tool-ui-evaluator.js +97 -0
- package/lib/public/modules/tool-ui-tree.js +21 -0
- package/lib/public/modules/tui-attention.js +10 -0
- package/lib/public/modules/user-settings.js +16 -23
- package/lib/public/modules/vendor-login.js +287 -0
- package/lib/public/modules/worker-proposal.js +2 -2
- package/lib/public/modules/workspace-assignment-card.js +149 -0
- package/lib/public/modules/workspace-assignment-routing.js +4 -0
- package/lib/public/style.css +13 -2
- package/lib/runtime-env.js +65 -0
- package/lib/sdk-bridge.js +120 -32
- package/lib/sdk-message-processor.js +10 -6
- package/lib/server-home-capsule-creation.js +36 -0
- package/lib/server-home-chat-events.js +287 -0
- package/lib/server-home-chat.js +518 -0
- package/lib/server-home-clay-entry.js +65 -0
- package/lib/server-home-clay-session-links.js +26 -0
- package/lib/server-home-debate-planning.js +214 -0
- package/lib/server-home-debates.js +137 -0
- package/lib/server-home-mate-creation.js +261 -0
- package/lib/server-home-models.js +241 -0
- package/lib/server-home-preferences.js +61 -0
- package/lib/server-mates.js +37 -0
- package/lib/server-tools.js +377 -0
- package/lib/server.js +111 -59
- package/lib/session-pair-mcp-server.js +6 -6
- package/lib/sessions.js +18 -3
- package/lib/tool-capsule-source.js +142 -0
- package/lib/tool-control-mcp-server.js +126 -0
- package/lib/tool-llm.js +48 -0
- package/lib/tool-storage.js +77 -0
- package/lib/tool-ui-spec-advanced.js +82 -0
- package/lib/tool-ui-spec.js +261 -0
- package/lib/tools-registry.js +286 -0
- package/lib/users-home-dock-preferences.js +77 -0
- package/lib/users-home-surface-preferences.js +99 -0
- package/lib/users-preferences.js +4 -7
- package/lib/users.js +12 -0
- package/lib/whats-new-content.js +3 -3
- package/lib/workspace-assignment-service.js +420 -0
- package/lib/workspace-query-mcp-server.js +154 -0
- package/lib/workspace-query-service.js +492 -0
- package/lib/ws-schema.js +93 -12
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/antigravity.js +2 -1
- package/lib/yoke/adapters/claude-worker.js +11 -4
- package/lib/yoke/adapters/claude.js +73 -43
- package/lib/yoke/adapters/codex.js +40 -10
- package/lib/yoke/adapters/kiro.js +2 -1
- package/lib/yoke/claude-user-input.js +38 -0
- package/lib/yoke/codex-app-server.js +20 -2
- package/lib/yoke/codex-user-input.js +64 -0
- package/lib/yoke/complete-once.js +164 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/interface.js +19 -0
- package/lib/yoke/mcp-bridge-server.js +2 -6
- package/lib/yoke/user-input.js +254 -0
- package/package.json +3 -2
- package/lib/public/css/home-chat.css +0 -380
- package/lib/public/modules/home-chat.js +0 -438
- package/lib/server-clay-home.js +0 -245
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
// home-mate-chat.js - Embedded conversation surface for the selected mate.
|
|
2
|
+
import { store } from './store.js';
|
|
3
|
+
import { getWs } from './ws-ref.js';
|
|
4
|
+
import { renderAssistantBubbleText, finalizeAssistantBubble, disposeChatBubbleTree } from './chat-bubble-renderer.js';
|
|
5
|
+
import { createHomeOrdinaryBubble, createHomeOrdinaryTyping } from './home-chat-identity.js';
|
|
6
|
+
import { openHomeMateSettings, closeHomeMateSettings } from './home-mate-settings.js';
|
|
7
|
+
import { forgetHomeSession, rememberHomeMate, rememberHomeSession } from './home-surface.js';
|
|
8
|
+
import { isOwnedHomeSessionMessage, resolveHomeSessionIdentity, appendHomeStreamText, finalizeHomeAssistant } from './home-chat-stream-state.js';
|
|
9
|
+
import { bindHomeComposerSubmission } from './home-composer-focus.js';
|
|
10
|
+
import { confirmedHomeSessionModel } from './home-session-model-confirmation.js';
|
|
11
|
+
import { createHomeDebateTranscriptCard, createHomeDebateResponder, normalizeHomeTranscript, applyHomeDebateProposal, resolveHomeDebateProposal, applyHomeDebateQuestion, resolveHomeDebateQuestion, failHomeDebateQuestion, hasPendingHomeDebateQuestion, restoreHomeDebateProposalFocus, restoreHomeDebateQuestionFocus, requestHomeDebateComposerFocus, restoreHomeDebateComposerFocus, clearHomeDebatePlanningPending, isHomeDebatePlanningPending, syncHomeDebatePlanningPending, createHomeDebatePlanningPendingRow } from './home-debate-planning.js';
|
|
12
|
+
import { applyHomeDebateEvent, createHomeDebateLiveCard, homeDebatePhase } from './home-debate-live.js';
|
|
13
|
+
import { renderHomeDebateControls } from './home-debate-controls.js';
|
|
14
|
+
import { beginHomeDebateLaunch, resetHomeDebateLaunch, settleHomeDebateLaunch, syncHomeDebateLaunchHistory, isHomeDebateLaunching, createHomeDebateLaunchRow } from './home-debate-launch.js';
|
|
15
|
+
import { setHomeSubSurface } from './home-sub-surface.js';
|
|
16
|
+
import { createWorkspaceAssignmentCard, applyHomeWorkspaceAssignment } from './workspace-assignment-card.js';
|
|
17
|
+
import { markHomeChatActivity, followHomeChatNextRender, captureHomeChatScroll, restoreHomeChatScroll } from './home-chat-scroll.js';
|
|
18
|
+
import { createHomeMateProposalCard, createHomeMateProposalResponder, applyHomeMateProposal, resolveHomeMateProposal, restoreHomeMateProposalFocus } from './home-mate-creation.js';
|
|
19
|
+
import { findHomeMate, getHomeMateName, getHomeMateShortBio } from './home-mate-selection.js';
|
|
20
|
+
var messages = [], streamingText = "", streaming = false, bound = false, animateSwitch = false;
|
|
21
|
+
var chatEl = null, stageEl = null, messagesEl = null, suggestionsEl = null, inputEl = null, sendBtn = null;
|
|
22
|
+
var sessionModelEl = null, sessionModelValueEl = null, sessionModelChooseEl = null, sessionRequestSequence = 0, activeSessionRequestId = null, mateCreationActive = false;
|
|
23
|
+
function sendMessage(message) {
|
|
24
|
+
var ws = getWs();
|
|
25
|
+
if (!ws || ws.readyState !== 1) return false;
|
|
26
|
+
ws.send(JSON.stringify(message));
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
var respondToDebateTranscript = createHomeDebateResponder(sendMessage, function () { return { mateId: store.get('homeChatMateId'), sessionId: store.get('homeChatSessionId'), requestId: activeSessionRequestId }; }, function (message) { renderHomeChat(); if (message && message.role === "question") restoreHomeDebateQuestionFocus(message.toolId); });
|
|
30
|
+
var respondToMateProposal = createHomeMateProposalResponder(sendMessage, function () { return { mateId: store.get('homeChatMateId'), sessionId: store.get('homeChatSessionId'), requestId: activeSessionRequestId }; }, function () { renderHomeChat(); });
|
|
31
|
+
function getMate(mateId) { return findHomeMate(store.get('cachedMatesList') || [], mateId); }
|
|
32
|
+
function resizeInput() {
|
|
33
|
+
if (!inputEl) return;
|
|
34
|
+
inputEl.style.height = "auto";
|
|
35
|
+
inputEl.style.height = Math.min(108, inputEl.scrollHeight) + "px";
|
|
36
|
+
}
|
|
37
|
+
function nextSessionRequestId() {
|
|
38
|
+
sessionRequestSequence++;
|
|
39
|
+
return "home-session-" + Date.now() + "-" + sessionRequestSequence;
|
|
40
|
+
}
|
|
41
|
+
function resetHomeSessionModel(sessionId) {
|
|
42
|
+
activeSessionRequestId = nextSessionRequestId();
|
|
43
|
+
store.set({
|
|
44
|
+
homeChatSessionId: sessionId || null,
|
|
45
|
+
homeChatSessionModel: null,
|
|
46
|
+
homeChatSessionVendor: null,
|
|
47
|
+
homeChatSessionModelLoading: true,
|
|
48
|
+
});
|
|
49
|
+
renderSessionModel();
|
|
50
|
+
}
|
|
51
|
+
function requestHomeSession(message, sessionId) {
|
|
52
|
+
var requestId = nextSessionRequestId();
|
|
53
|
+
message.requestId = requestId;
|
|
54
|
+
if (!sendMessage(message)) return false;
|
|
55
|
+
activeSessionRequestId = requestId;
|
|
56
|
+
store.set({
|
|
57
|
+
homeChatSessionId: sessionId || null,
|
|
58
|
+
homeChatSessionModel: null,
|
|
59
|
+
homeChatSessionVendor: null,
|
|
60
|
+
homeChatSessionModelLoading: true,
|
|
61
|
+
});
|
|
62
|
+
renderSessionModel();
|
|
63
|
+
return requestId;
|
|
64
|
+
}
|
|
65
|
+
function currentSessionModelLabel() {
|
|
66
|
+
if (!store.get('homeChatMateId') || store.get('homeChatSessionModelLoading')) return "Loading model…";
|
|
67
|
+
var model = store.get('homeChatSessionModel');
|
|
68
|
+
if (model) return (store.get('homeChatSessionVendor') || "Vendor") + " · " + model;
|
|
69
|
+
return "Choose model";
|
|
70
|
+
}
|
|
71
|
+
function hasCommittedSessionModel() {
|
|
72
|
+
return !store.get('homeChatSessionModelLoading') && !!store.get('homeChatSessionModel');
|
|
73
|
+
}
|
|
74
|
+
function renderSessionModel() {
|
|
75
|
+
if (!sessionModelEl || !sessionModelValueEl || !sessionModelChooseEl) return;
|
|
76
|
+
var label = currentSessionModelLabel();
|
|
77
|
+
var needsChoice = label === "Choose model";
|
|
78
|
+
var canChangeDraft = !!store.get('homeChatSessionId') && !store.get('homeChatSessionModelLoading') && messages.length === 0 && !streaming;
|
|
79
|
+
sessionModelValueEl.textContent = needsChoice ? "" : label;
|
|
80
|
+
sessionModelValueEl.classList.toggle("hidden", needsChoice);
|
|
81
|
+
sessionModelChooseEl.classList.toggle("hidden", !needsChoice && !canChangeDraft);
|
|
82
|
+
sessionModelChooseEl.setAttribute("aria-label", canChangeDraft ? "Choose a model for this draft conversation and future new conversations." : "Choose a model for the current Mate. Used for new conversations.");
|
|
83
|
+
sessionModelEl.title = "Model for this conversation: " + label;
|
|
84
|
+
sessionModelEl.setAttribute("aria-label", "Model for this conversation: " + label);
|
|
85
|
+
}
|
|
86
|
+
function submitMessage() {
|
|
87
|
+
var mateId = store.get('homeChatMateId');
|
|
88
|
+
var text = inputEl ? inputEl.value.trim() : "";
|
|
89
|
+
if (!mateId || !text || streaming || hasPendingHomeDebateQuestion(messages) || !hasCommittedSessionModel()) return false;
|
|
90
|
+
if (!sendMessage({ type: "home_mate_send", mateId: mateId, sessionId: store.get('homeChatSessionId'), requestId: activeSessionRequestId, text: text })) return false;
|
|
91
|
+
messages.push({ role: "user", text: text, ts: Date.now() });
|
|
92
|
+
streamingText = "";
|
|
93
|
+
streaming = true;
|
|
94
|
+
followHomeChatNextRender();
|
|
95
|
+
inputEl.value = "";
|
|
96
|
+
resizeInput();
|
|
97
|
+
renderHomeChat();
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
function submitSuggestion(text) {
|
|
101
|
+
if (!inputEl || !text) return;
|
|
102
|
+
inputEl.value = text;
|
|
103
|
+
resizeInput();
|
|
104
|
+
submitMessage();
|
|
105
|
+
}
|
|
106
|
+
export function startNewHomeConversation() {
|
|
107
|
+
setHomeSubSurface("chat");
|
|
108
|
+
var mateId = store.get('homeChatMateId');
|
|
109
|
+
if (!mateId || streaming) return;
|
|
110
|
+
resetHomeDebateLaunch();
|
|
111
|
+
mateCreationActive = false;
|
|
112
|
+
if (!requestHomeSession({ type: "home_mate_new_session", mateId: mateId }, null)) return;
|
|
113
|
+
messages = [];
|
|
114
|
+
streamingText = "";
|
|
115
|
+
streaming = false;
|
|
116
|
+
animateSwitch = true;
|
|
117
|
+
renderHomeChat();
|
|
118
|
+
}
|
|
119
|
+
export function startHomeCapsuleCreation(mateId, description) {
|
|
120
|
+
if (!mateId || mateId !== store.get('homeChatMateId') || !description || streaming) return false;
|
|
121
|
+
setHomeSubSurface("chat"); resetHomeDebateLaunch();
|
|
122
|
+
mateCreationActive = false;
|
|
123
|
+
closeHomeMateSettings();
|
|
124
|
+
rememberHomeMate(mateId);
|
|
125
|
+
messages = []; streamingText = ""; streaming = true; animateSwitch = true;
|
|
126
|
+
if (inputEl) inputEl.value = "";
|
|
127
|
+
var requestId = requestHomeSession({
|
|
128
|
+
type: "home_mate_new_session",
|
|
129
|
+
mateId: mateId,
|
|
130
|
+
intent: { type: "capsule_creation", description: description },
|
|
131
|
+
}, null);
|
|
132
|
+
if (!requestId) {
|
|
133
|
+
streaming = false; renderHomeChat();
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
renderHomeChat();
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
function bindComposer() {
|
|
140
|
+
if (bound || !inputEl || !sendBtn) return;
|
|
141
|
+
bound = true;
|
|
142
|
+
inputEl.addEventListener("input", function () {
|
|
143
|
+
resizeInput();
|
|
144
|
+
sendBtn.disabled = streaming || hasPendingHomeDebateQuestion(messages) || !store.get('homeChatMateId') || !hasCommittedSessionModel() || !inputEl.value.trim();
|
|
145
|
+
});
|
|
146
|
+
bindHomeComposerSubmission(inputEl, sendBtn, submitMessage);
|
|
147
|
+
sessionModelChooseEl.addEventListener("click", function () {
|
|
148
|
+
var mate = getMate(store.get('homeChatMateId'));
|
|
149
|
+
if (mate) openHomeMateSettings(mate.id, sessionModelChooseEl, { section: "model", sessionId: store.get('homeChatSessionId') });
|
|
150
|
+
});
|
|
151
|
+
window.addEventListener("clay:home-mate-model-confirmed", function (event) {
|
|
152
|
+
handleHomeMateModelConfirmed(event.detail || {});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function ensureDom() {
|
|
156
|
+
if (!chatEl) chatEl = document.getElementById("home-mate-chat");
|
|
157
|
+
if (!stageEl) stageEl = chatEl ? chatEl.querySelector(".home-mate-chat-stage") : null;
|
|
158
|
+
if (!messagesEl) messagesEl = document.getElementById("home-mate-chat-messages");
|
|
159
|
+
if (!suggestionsEl) suggestionsEl = document.getElementById("home-mate-chat-suggestions");
|
|
160
|
+
if (!inputEl) inputEl = document.getElementById("home-mate-chat-input");
|
|
161
|
+
if (!sendBtn) sendBtn = document.getElementById("home-mate-chat-send");
|
|
162
|
+
if (!sessionModelEl) sessionModelEl = document.getElementById("home-mate-chat-session-model");
|
|
163
|
+
if (!sessionModelValueEl) sessionModelValueEl = document.getElementById("home-mate-chat-session-model-value");
|
|
164
|
+
if (!sessionModelChooseEl) sessionModelChooseEl = document.getElementById("home-mate-chat-session-model-choose");
|
|
165
|
+
bindComposer();
|
|
166
|
+
return !!(chatEl && stageEl && messagesEl && suggestionsEl && inputEl && sendBtn);
|
|
167
|
+
}
|
|
168
|
+
function formatTime(timestamp) { var date = timestamp ? new Date(timestamp) : null; return date ? date.getHours().toString().padStart(2, "0") + ":" + date.getMinutes().toString().padStart(2, "0") : ""; }
|
|
169
|
+
function appendMessage(message, mate, mateName, finalize) {
|
|
170
|
+
if (message.role === "assignment") return createWorkspaceAssignmentCard(message.assignment, { home: true, context: { mateId: store.get('homeChatMateId'), sessionId: store.get('homeChatSessionId'), requestId: activeSessionRequestId } });
|
|
171
|
+
if (message.role === "mate_proposal") return createHomeMateProposalCard(message, respondToMateProposal, function (created) { openHomeChat(created.mateId); });
|
|
172
|
+
if (message.role === "proposal" || message.role === "question") return createHomeDebateTranscriptCard(message, respondToDebateTranscript);
|
|
173
|
+
if (["debate_header", "debate_turn", "debate_user", "debate_tool_decision"].indexOf(message.role) !== -1) return createHomeDebateLiveCard(message, finalize);
|
|
174
|
+
var isUser = message.role === "user";
|
|
175
|
+
var timeText = formatTime(typeof message.ts === "number" ? message.ts : 0);
|
|
176
|
+
var row = createHomeOrdinaryBubble(message, mate, mateName, timeText);
|
|
177
|
+
if (!isUser) {
|
|
178
|
+
if (finalize) finalizeAssistantBubble(row, message.text || "", true);
|
|
179
|
+
else renderAssistantBubbleText(row, message.text || "", false);
|
|
180
|
+
}
|
|
181
|
+
if (timeText) row.title = timeText;
|
|
182
|
+
return row;
|
|
183
|
+
}
|
|
184
|
+
function renderEmptyState(mate, mateName) {
|
|
185
|
+
var empty = document.createElement("div");
|
|
186
|
+
empty.className = "home-mate-chat-empty";
|
|
187
|
+
var brand = document.createElement("div");
|
|
188
|
+
brand.className = "home-mate-chat-brand";
|
|
189
|
+
var symbol = document.createElement("img");
|
|
190
|
+
symbol.src = "/clay-studio-symbol.png";
|
|
191
|
+
symbol.alt = "";
|
|
192
|
+
brand.appendChild(symbol);
|
|
193
|
+
var wordmark = document.createElement("span");
|
|
194
|
+
wordmark.className = "home-sidebar-brand-wordmark home-mate-chat-brand-wordmark";
|
|
195
|
+
wordmark.textContent = "Clay Studio";
|
|
196
|
+
brand.appendChild(wordmark);
|
|
197
|
+
empty.appendChild(brand);
|
|
198
|
+
var greeting = document.createElement("h2");
|
|
199
|
+
greeting.textContent = mate ? "What should we work on, " + mateName + "?" : "Getting Home ready...";
|
|
200
|
+
empty.appendChild(greeting);
|
|
201
|
+
var detail = document.createElement("p");
|
|
202
|
+
detail.textContent = mate ? getHomeMateShortBio(mate) : "Loading your Mate and recent conversation.";
|
|
203
|
+
empty.appendChild(detail);
|
|
204
|
+
messagesEl.appendChild(empty);
|
|
205
|
+
suggestionsEl.innerHTML = "";
|
|
206
|
+
if (!mate) return;
|
|
207
|
+
var suggestions = ["Make me a small tool"];
|
|
208
|
+
for (var i = 0; i < suggestions.length; i++) {
|
|
209
|
+
(function (suggestion) {
|
|
210
|
+
var chip = document.createElement("button");
|
|
211
|
+
chip.type = "button";
|
|
212
|
+
chip.className = "home-mate-chat-suggestion";
|
|
213
|
+
chip.textContent = suggestion;
|
|
214
|
+
chip.addEventListener("click", function () { submitSuggestion(suggestion); });
|
|
215
|
+
suggestionsEl.appendChild(chip);
|
|
216
|
+
})(suggestions[i]);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
export function renderHomeChat() {
|
|
220
|
+
if (!ensureDom()) return;
|
|
221
|
+
var scrollSnapshot = captureHomeChatScroll(animateSwitch);
|
|
222
|
+
var mateId = store.get('homeChatMateId');
|
|
223
|
+
var mate = getMate(mateId);
|
|
224
|
+
var mateName = getHomeMateName(mate);
|
|
225
|
+
var debateLaunching = isHomeDebateLaunching();
|
|
226
|
+
var hasConversation = debateLaunching || messages.length > 0 || streaming || !!streamingText;
|
|
227
|
+
chatEl.classList.toggle("is-empty", !hasConversation);
|
|
228
|
+
chatEl.classList.toggle("has-conversation", hasConversation);
|
|
229
|
+
disposeChatBubbleTree(messagesEl);
|
|
230
|
+
messagesEl.innerHTML = "";
|
|
231
|
+
suggestionsEl.innerHTML = "";
|
|
232
|
+
if (hasConversation) {
|
|
233
|
+
var transcript = document.createElement("div");
|
|
234
|
+
transcript.className = "home-mate-chat-transcript home-chat-bubble-layout";
|
|
235
|
+
for (var i = 0; i < messages.length; i++) transcript.appendChild(appendMessage(messages[i], mate, mateName, !streaming));
|
|
236
|
+
if (isHomeDebatePlanningPending()) transcript.appendChild(createHomeDebatePlanningPendingRow());
|
|
237
|
+
if (debateLaunching) {
|
|
238
|
+
transcript.appendChild(createHomeDebateLaunchRow());
|
|
239
|
+
} else if (streamingText) {
|
|
240
|
+
transcript.appendChild(appendMessage({ role: "assistant", text: streamingText, ts: Date.now() }, mate, mateName, false));
|
|
241
|
+
} else if (streaming && mate) {
|
|
242
|
+
transcript.appendChild(createHomeOrdinaryTyping(mate, mateName));
|
|
243
|
+
}
|
|
244
|
+
messagesEl.appendChild(transcript);
|
|
245
|
+
} else {
|
|
246
|
+
renderEmptyState(mate, mateName);
|
|
247
|
+
}
|
|
248
|
+
var awaitingQuestion = hasPendingHomeDebateQuestion(messages);
|
|
249
|
+
var debatePhase = homeDebatePhase(messages);
|
|
250
|
+
inputEl.disabled = !mateId || awaitingQuestion || !hasCommittedSessionModel(); if (debateLaunching || mateCreationActive || !!debatePhase) inputEl.disabled = true;
|
|
251
|
+
inputEl.placeholder = debateLaunching ? "Preparing your debate…" : (mateCreationActive ? "Use the interview cards above" : (debatePhase ? "Debate session" : (awaitingQuestion ? "Answer the question above to continue" : (mateId ? "Message " + mateName : "Message"))));
|
|
252
|
+
sendBtn.disabled = !mateId || streaming || awaitingQuestion || !hasCommittedSessionModel();
|
|
253
|
+
if (debateLaunching || mateCreationActive || !!debatePhase || !inputEl.value.trim()) sendBtn.disabled = true;
|
|
254
|
+
renderSessionModel();
|
|
255
|
+
renderHomeDebateControls(messages, activeSessionRequestId, function () { store.set({ homeDebateTopicFormOpen: true }); setHomeSubSurface("debates"); });
|
|
256
|
+
if (animateSwitch) {
|
|
257
|
+
messagesEl.classList.remove("is-switching");
|
|
258
|
+
void messagesEl.offsetWidth;
|
|
259
|
+
messagesEl.classList.add("is-switching");
|
|
260
|
+
animateSwitch = false;
|
|
261
|
+
}
|
|
262
|
+
restoreHomeChatScroll(scrollSnapshot);
|
|
263
|
+
}
|
|
264
|
+
export function openHomeChat(mateId) {
|
|
265
|
+
if (!mateId) return;
|
|
266
|
+
setHomeSubSurface("chat");
|
|
267
|
+
resetHomeDebateLaunch();
|
|
268
|
+
mateCreationActive = false;
|
|
269
|
+
if (store.get('homeChatMateId') && store.get('homeChatMateId') !== mateId) closeHomeMateSettings();
|
|
270
|
+
var preferredSession = (store.get('homeActiveSessionByMate') || {})[mateId] || null;
|
|
271
|
+
resetHomeSessionModel(preferredSession);
|
|
272
|
+
store.set({ homeChatMateId: mateId });
|
|
273
|
+
rememberHomeMate(mateId);
|
|
274
|
+
messages = [];
|
|
275
|
+
streamingText = "";
|
|
276
|
+
streaming = false;
|
|
277
|
+
animateSwitch = true;
|
|
278
|
+
if (inputEl) inputEl.value = "";
|
|
279
|
+
renderHomeChat();
|
|
280
|
+
if (store.get('homeSurfaceLoaded')) resumeHomeChat();
|
|
281
|
+
}
|
|
282
|
+
export function openHomeConversation(mateId, sessionId) {
|
|
283
|
+
if (!mateId || !sessionId) return;
|
|
284
|
+
setHomeSubSurface("chat");
|
|
285
|
+
resetHomeDebateLaunch();
|
|
286
|
+
mateCreationActive = false;
|
|
287
|
+
closeHomeMateSettings();
|
|
288
|
+
resetHomeSessionModel(sessionId);
|
|
289
|
+
store.set({ homeChatMateId: mateId });
|
|
290
|
+
rememberHomeMate(mateId);
|
|
291
|
+
rememberHomeSession(mateId, sessionId);
|
|
292
|
+
messages = [];
|
|
293
|
+
streamingText = "";
|
|
294
|
+
streaming = false;
|
|
295
|
+
animateSwitch = true;
|
|
296
|
+
if (inputEl) inputEl.value = "";
|
|
297
|
+
renderHomeChat();
|
|
298
|
+
if (store.get('homeSurfaceLoaded')) resumeHomeChat();
|
|
299
|
+
}
|
|
300
|
+
export function openHomeMateAction(kind, initialTopic) {
|
|
301
|
+
if (kind !== "debate" && kind !== "mate") return false;
|
|
302
|
+
var mates = store.get('cachedMatesList') || [];
|
|
303
|
+
var clay = null;
|
|
304
|
+
for (var i = 0; i < mates.length; i++) if (mates[i] && mates[i].builtinKey === "clay") clay = mates[i];
|
|
305
|
+
if (!clay || streaming) return false;
|
|
306
|
+
requestHomeDebateComposerFocus();
|
|
307
|
+
closeHomeMateSettings();
|
|
308
|
+
store.set({ homeChatMateId: clay.id });
|
|
309
|
+
rememberHomeMate(clay.id);
|
|
310
|
+
messages = []; streamingText = ""; streaming = false; animateSwitch = true;
|
|
311
|
+
resetHomeDebateLaunch();
|
|
312
|
+
var payload = kind === "mate" ? { type: "home_mate_creation_plan", mateId: clay.id } : { type: "home_mate_debate_plan", mateId: clay.id, topic: typeof initialTopic === "string" ? initialTopic.trim().slice(0, 1000) : "" };
|
|
313
|
+
var requestId = requestHomeSession(payload, null);
|
|
314
|
+
if (!requestId) return false;
|
|
315
|
+
setHomeSubSurface("chat");
|
|
316
|
+
if (kind === "debate") beginHomeDebateLaunch(requestId);
|
|
317
|
+
else { streaming = true; mateCreationActive = true; }
|
|
318
|
+
renderHomeChat();
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
export function resumeHomeChat() {
|
|
322
|
+
var mateId = store.get('homeChatMateId');
|
|
323
|
+
if (!mateId) return;
|
|
324
|
+
sendMessage({ type: "home_mate_sessions_list", mateId: mateId });
|
|
325
|
+
var sessionId = store.get('homeChatSessionId') || (store.get('homeActiveSessionByMate') || {})[mateId];
|
|
326
|
+
if (sessionId) requestHomeSession({ type: "home_mate_session_open", mateId: mateId, sessionId: sessionId }, sessionId);
|
|
327
|
+
else requestHomeSession({ type: "home_mate_open", mateId: mateId }, null);
|
|
328
|
+
}
|
|
329
|
+
export function closeHomeChat() {
|
|
330
|
+
resetHomeDebateLaunch();
|
|
331
|
+
sendMessage({ type: "home_mate_close" });
|
|
332
|
+
streaming = false;
|
|
333
|
+
streamingText = "";
|
|
334
|
+
renderHomeChat();
|
|
335
|
+
}
|
|
336
|
+
export function handleHomeMateHistory(msg) {
|
|
337
|
+
if (!isCurrentSessionMessage(msg)) return;
|
|
338
|
+
syncHomeDebateLaunchHistory(msg);
|
|
339
|
+
store.set({
|
|
340
|
+
homeChatSessionId: msg.sessionId || null,
|
|
341
|
+
homeChatSessionModel: typeof msg.model === "string" && msg.model ? msg.model : null,
|
|
342
|
+
homeChatSessionVendor: typeof msg.vendor === "string" && msg.vendor ? msg.vendor : null,
|
|
343
|
+
homeChatSessionModelLoading: false,
|
|
344
|
+
});
|
|
345
|
+
if (msg.sessionId) rememberHomeSession(msg.mateId, msg.sessionId);
|
|
346
|
+
syncHomeDebatePlanningPending(msg);
|
|
347
|
+
mateCreationActive = msg.mateCreation === true;
|
|
348
|
+
messages = normalizeHomeTranscript(msg.messages);
|
|
349
|
+
streamingText = "";
|
|
350
|
+
streaming = msg.isProcessing === true;
|
|
351
|
+
renderHomeChat();
|
|
352
|
+
if (msg.debatePlanning) restoreHomeDebateComposerFocus(inputEl);
|
|
353
|
+
if (msg.mateCreation) for (var i = messages.length - 1; i >= 0; i--) if (messages[i].role === "question" && messages[i].status === "pending") { restoreHomeDebateQuestionFocus(messages[i].toolId); break; }
|
|
354
|
+
}
|
|
355
|
+
export function handleHomeDebateTranscript(msg) {
|
|
356
|
+
if (!isCurrentSessionMessage(msg)) return;
|
|
357
|
+
markHomeChatActivity();
|
|
358
|
+
settleHomeDebateLaunch(msg);
|
|
359
|
+
if (msg.type === "home_debate_proposal" || msg.type === "home_debate_question" || msg.type === "home_debate_event" || msg.type === "home_mate_creation_proposal" || msg.type === "home_mate_creation_question") messages = finalizeHomeAssistant(messages, streamingText, "", Date.now());
|
|
360
|
+
if (msg.type === "home_debate_event") messages = applyHomeDebateEvent(messages, msg);
|
|
361
|
+
if (msg.type === "home_debate_proposal") messages = applyHomeDebateProposal(messages, msg);
|
|
362
|
+
if (msg.type === "home_debate_proposal_resolved") messages = resolveHomeDebateProposal(messages, msg);
|
|
363
|
+
if (msg.type === "home_debate_question") messages = applyHomeDebateQuestion(messages, msg);
|
|
364
|
+
if (msg.type === "home_debate_question_resolved") messages = resolveHomeDebateQuestion(messages, msg);
|
|
365
|
+
if (msg.type === "home_mate_creation_question") messages = applyHomeDebateQuestion(messages, msg);
|
|
366
|
+
if (msg.type === "home_mate_creation_question_resolved") messages = resolveHomeDebateQuestion(messages, msg);
|
|
367
|
+
if (msg.type === "home_mate_creation_proposal") messages = applyHomeMateProposal(messages, msg);
|
|
368
|
+
if (msg.type === "home_mate_creation_proposal_resolved") messages = resolveHomeMateProposal(messages, msg);
|
|
369
|
+
if (msg.type === "home_mate_error" && /^question_/.test(msg.code || "")) messages = failHomeDebateQuestion(messages, msg);
|
|
370
|
+
if (msg.type === "home_debate_proposal" || msg.type === "home_debate_question" || msg.type === "home_debate_event" || msg.type === "home_mate_creation_proposal" || msg.type === "home_mate_creation_question") { clearHomeDebatePlanningPending(); streaming = false; streamingText = ""; }
|
|
371
|
+
if ((msg.type === "home_debate_question_resolved" || msg.type === "home_mate_creation_question_resolved") && msg.status === "expired") clearHomeDebatePlanningPending();
|
|
372
|
+
renderHomeChat();
|
|
373
|
+
if (msg.proposalId) restoreHomeDebateProposalFocus(msg.proposalId);
|
|
374
|
+
if (msg.proposalId && msg.type === "home_mate_creation_proposal_resolved") restoreHomeMateProposalFocus(msg.proposalId);
|
|
375
|
+
if (msg.toolId && msg.type === "home_mate_creation_question") restoreHomeDebateQuestionFocus(msg.toolId);
|
|
376
|
+
if (msg.toolId && msg.type === "home_debate_question_resolved") restoreHomeDebateQuestionFocus(msg.toolId);
|
|
377
|
+
if (msg.toolId && msg.type === "home_mate_error") restoreHomeDebateQuestionFocus(msg.toolId);
|
|
378
|
+
}
|
|
379
|
+
export function handleHomeMateDelta(msg) {
|
|
380
|
+
if (!isCurrentSessionMessage(msg)) return;
|
|
381
|
+
markHomeChatActivity();
|
|
382
|
+
settleHomeDebateLaunch(msg);
|
|
383
|
+
if (msg.sessionId && msg.sessionId !== store.get('homeChatSessionId')) {
|
|
384
|
+
store.set({ homeChatSessionId: msg.sessionId });
|
|
385
|
+
rememberHomeSession(msg.mateId, msg.sessionId);
|
|
386
|
+
}
|
|
387
|
+
updateHomeSessionMetadata(msg);
|
|
388
|
+
streaming = true;
|
|
389
|
+
updateSessionProcessing(msg.mateId, msg.sessionId, true);
|
|
390
|
+
streamingText = appendHomeStreamText(streamingText, msg.text);
|
|
391
|
+
renderHomeChat();
|
|
392
|
+
}
|
|
393
|
+
export function handleHomeWorkspaceAssignment(msg) {
|
|
394
|
+
if (!isCurrentSessionMessage(msg) || !msg.assignment) return;
|
|
395
|
+
markHomeChatActivity();
|
|
396
|
+
messages = applyHomeWorkspaceAssignment(messages, msg);
|
|
397
|
+
renderHomeChat();
|
|
398
|
+
}
|
|
399
|
+
export function handleHomeMateDone(msg) {
|
|
400
|
+
if (!isCurrentSessionMessage(msg)) return;
|
|
401
|
+
markHomeChatActivity();
|
|
402
|
+
settleHomeDebateLaunch(msg);
|
|
403
|
+
if (msg.sessionId && msg.sessionId !== store.get('homeChatSessionId')) {
|
|
404
|
+
store.set({ homeChatSessionId: msg.sessionId });
|
|
405
|
+
rememberHomeSession(msg.mateId, msg.sessionId);
|
|
406
|
+
}
|
|
407
|
+
updateHomeSessionMetadata(msg);
|
|
408
|
+
messages = finalizeHomeAssistant(messages, streamingText, msg.text, Date.now());
|
|
409
|
+
streamingText = "";
|
|
410
|
+
streaming = false;
|
|
411
|
+
updateSessionProcessing(msg.mateId, msg.sessionId, false);
|
|
412
|
+
renderHomeChat();
|
|
413
|
+
}
|
|
414
|
+
export function handleHomeMateSessionIdentity(msg) {
|
|
415
|
+
var nextSessionId = resolveHomeSessionIdentity({
|
|
416
|
+
mateId: store.get('homeChatMateId'),
|
|
417
|
+
requestId: activeSessionRequestId,
|
|
418
|
+
sessionId: store.get('homeChatSessionId'),
|
|
419
|
+
}, msg);
|
|
420
|
+
if (!nextSessionId) return;
|
|
421
|
+
store.set({ homeChatSessionId: nextSessionId });
|
|
422
|
+
rememberHomeSession(msg.mateId, nextSessionId);
|
|
423
|
+
}
|
|
424
|
+
export function handleHomeMateError(msg) {
|
|
425
|
+
if (!isCurrentSessionMessage(msg)) return;
|
|
426
|
+
markHomeChatActivity();
|
|
427
|
+
settleHomeDebateLaunch(msg);
|
|
428
|
+
clearHomeDebatePlanningPending();
|
|
429
|
+
if (msg.code === "session_not_found") {
|
|
430
|
+
forgetHomeSession(msg.mateId);
|
|
431
|
+
requestHomeSession({ type: "home_mate_open", mateId: msg.mateId }, null);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (msg.code === "model_unavailable") {
|
|
435
|
+
store.set({ homeChatSessionModel: null, homeChatSessionModelLoading: false });
|
|
436
|
+
}
|
|
437
|
+
if (streamingText) messages.push({ role: "assistant", text: streamingText, ts: Date.now() });
|
|
438
|
+
streamingText = "";
|
|
439
|
+
streaming = false;
|
|
440
|
+
messages.push({ role: "assistant", text: msg.text || "Chat unavailable.", ts: Date.now() });
|
|
441
|
+
renderHomeChat();
|
|
442
|
+
}
|
|
443
|
+
export function handleHomeMateModelConfirmed(msg) {
|
|
444
|
+
var confirmed = confirmedHomeSessionModel({
|
|
445
|
+
mateId: store.get('homeChatMateId'),
|
|
446
|
+
sessionId: store.get('homeChatSessionId'),
|
|
447
|
+
}, msg);
|
|
448
|
+
if (!confirmed) {
|
|
449
|
+
if (!msg.requestedSessionId && !store.get('homeChatSessionModel')) resumeHomeChat();
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
store.set({
|
|
453
|
+
homeChatSessionId: confirmed.sessionId,
|
|
454
|
+
homeChatSessionVendor: confirmed.vendor,
|
|
455
|
+
homeChatSessionModel: confirmed.model,
|
|
456
|
+
homeChatSessionModelLoading: false,
|
|
457
|
+
});
|
|
458
|
+
rememberHomeSession(msg.mateId, confirmed.sessionId);
|
|
459
|
+
renderHomeChat();
|
|
460
|
+
}
|
|
461
|
+
function isCurrentSessionMessage(msg) {
|
|
462
|
+
return isOwnedHomeSessionMessage({
|
|
463
|
+
mateId: store.get('homeChatMateId'),
|
|
464
|
+
requestId: activeSessionRequestId,
|
|
465
|
+
sessionId: store.get('homeChatSessionId'),
|
|
466
|
+
}, msg);
|
|
467
|
+
}
|
|
468
|
+
function updateHomeSessionMetadata(msg) {
|
|
469
|
+
var update = {};
|
|
470
|
+
if (Object.prototype.hasOwnProperty.call(msg, "model")) {
|
|
471
|
+
update.homeChatSessionModel = typeof msg.model === "string" && msg.model ? msg.model : null;
|
|
472
|
+
update.homeChatSessionModelLoading = false;
|
|
473
|
+
}
|
|
474
|
+
if (Object.prototype.hasOwnProperty.call(msg, "vendor")) {
|
|
475
|
+
update.homeChatSessionVendor = typeof msg.vendor === "string" && msg.vendor ? msg.vendor : null;
|
|
476
|
+
}
|
|
477
|
+
if (Object.keys(update).length) store.set(update);
|
|
478
|
+
}
|
|
479
|
+
export function handleHomeMateSessionsState(msg) {
|
|
480
|
+
if (!msg.mateId) return;
|
|
481
|
+
var sessions = Object.assign({}, store.get('homeMateSessions') || {});
|
|
482
|
+
sessions[msg.mateId] = Array.isArray(msg.sessions) ? msg.sessions : [];
|
|
483
|
+
store.set({ homeMateSessions: sessions });
|
|
484
|
+
}
|
|
485
|
+
function updateSessionProcessing(mateId, sessionId, processing) {
|
|
486
|
+
if (!mateId || !sessionId) return;
|
|
487
|
+
var byMate = Object.assign({}, store.get('homeMateSessions') || {});
|
|
488
|
+
var sessions = Array.isArray(byMate[mateId]) ? byMate[mateId].slice() : [], changed = false;
|
|
489
|
+
for (var i = 0; i < sessions.length; i++) {
|
|
490
|
+
if (!sessions[i] || sessions[i].id !== sessionId || sessions[i].isProcessing === processing) continue;
|
|
491
|
+
sessions[i] = Object.assign({}, sessions[i], { isProcessing: processing });
|
|
492
|
+
changed = true;
|
|
493
|
+
}
|
|
494
|
+
if (!changed) return;
|
|
495
|
+
byMate[mateId] = sessions; store.set({ homeMateSessions: byMate });
|
|
496
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Native proposal card for a Clay-led Mate creation interview.
|
|
2
|
+
|
|
3
|
+
function textElement(tag, className, text) {
|
|
4
|
+
var element = document.createElement(tag);
|
|
5
|
+
element.className = className;
|
|
6
|
+
element.textContent = text || "";
|
|
7
|
+
return element;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createHomeMateProposalCard(message, respond, openMate) {
|
|
11
|
+
var proposal = message.proposal || {};
|
|
12
|
+
var card = document.createElement("section");
|
|
13
|
+
card.className = "home-mate-proposal";
|
|
14
|
+
card.dataset.proposalId = proposal.proposalId || "";
|
|
15
|
+
card.setAttribute("aria-label", "New Mate proposal: " + (proposal.name || "Unnamed Mate"));
|
|
16
|
+
card.appendChild(textElement("div", "home-mate-proposal-eyebrow", "New Mate"));
|
|
17
|
+
card.appendChild(textElement("h3", "home-mate-proposal-name", proposal.name || "Unnamed Mate"));
|
|
18
|
+
if (proposal.bio) card.appendChild(textElement("p", "home-mate-proposal-bio", proposal.bio));
|
|
19
|
+
var facts = document.createElement("dl");
|
|
20
|
+
facts.className = "home-mate-proposal-facts";
|
|
21
|
+
function fact(label, value) {
|
|
22
|
+
if (!value) return;
|
|
23
|
+
facts.appendChild(textElement("dt", "", label));
|
|
24
|
+
facts.appendChild(textElement("dd", "", value));
|
|
25
|
+
}
|
|
26
|
+
fact("Relationship", proposal.relationship);
|
|
27
|
+
fact("Activities", Array.isArray(proposal.activities) ? proposal.activities.join(", ") : "");
|
|
28
|
+
fact("Communication", Array.isArray(proposal.communicationStyle) ? proposal.communicationStyle.join(", ") : "");
|
|
29
|
+
fact("Autonomy", proposal.autonomy);
|
|
30
|
+
card.appendChild(facts);
|
|
31
|
+
if (proposal.identityMarkdown) {
|
|
32
|
+
var details = document.createElement("details");
|
|
33
|
+
details.className = "home-mate-proposal-identity";
|
|
34
|
+
details.appendChild(textElement("summary", "", "Review full identity"));
|
|
35
|
+
details.appendChild(textElement("pre", "", proposal.identityMarkdown));
|
|
36
|
+
card.appendChild(details);
|
|
37
|
+
}
|
|
38
|
+
if (message.error) {
|
|
39
|
+
var error = textElement("p", "home-mate-proposal-error", message.error);
|
|
40
|
+
error.setAttribute("role", "alert");
|
|
41
|
+
card.appendChild(error);
|
|
42
|
+
}
|
|
43
|
+
var actions = document.createElement("div");
|
|
44
|
+
actions.className = "home-mate-proposal-actions";
|
|
45
|
+
if (message.status === "created" || message.status === "cancelled") {
|
|
46
|
+
var resolved = textElement("span", "home-mate-proposal-status", message.status === "created" ? "Mate created" : "Proposal cancelled");
|
|
47
|
+
resolved.setAttribute("role", "status");
|
|
48
|
+
resolved.tabIndex = -1;
|
|
49
|
+
actions.appendChild(resolved);
|
|
50
|
+
if (message.status === "created" && message.mateId && typeof openMate === "function") {
|
|
51
|
+
var mateName = message.mateName || proposal.name || "Mate";
|
|
52
|
+
var open = textElement("button", "home-mate-proposal-open", "Open " + mateName);
|
|
53
|
+
open.type = "button";
|
|
54
|
+
open.setAttribute("aria-label", "Open conversation with " + mateName);
|
|
55
|
+
open.addEventListener("click", function () { openMate(message, open); });
|
|
56
|
+
actions.appendChild(open);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
var create = textElement("button", "home-mate-proposal-create", message.status === "submitting" ? "Creating…" : "Create Mate");
|
|
60
|
+
create.type = "button";
|
|
61
|
+
create.disabled = message.status === "submitting";
|
|
62
|
+
create.addEventListener("click", function () { respond(message, "create", create); });
|
|
63
|
+
var cancel = textElement("button", "home-mate-proposal-cancel", "Cancel");
|
|
64
|
+
cancel.type = "button";
|
|
65
|
+
cancel.disabled = message.status === "submitting";
|
|
66
|
+
cancel.addEventListener("click", function () { respond(message, "cancel", cancel); });
|
|
67
|
+
actions.appendChild(create);
|
|
68
|
+
actions.appendChild(cancel);
|
|
69
|
+
}
|
|
70
|
+
card.appendChild(actions);
|
|
71
|
+
return card;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function applyHomeMateProposal(messages, msg) {
|
|
75
|
+
var id = msg && msg.proposal && msg.proposal.proposalId;
|
|
76
|
+
if (!id) return messages;
|
|
77
|
+
for (var i = 0; i < messages.length; i++) if (messages[i].role === "mate_proposal" && messages[i].proposal && messages[i].proposal.proposalId === id) return messages;
|
|
78
|
+
return messages.concat([{ role: "mate_proposal", proposal: msg.proposal, status: "pending", error: "" }]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function resolveHomeMateProposal(messages, msg) {
|
|
82
|
+
for (var i = 0; i < messages.length; i++) {
|
|
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.mateId || null, mateName: msg.mateName || "", error: msg.error || "" });
|
|
85
|
+
}
|
|
86
|
+
return messages;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function createHomeMateProposalResponder(sendMessage, getContext, onChange) {
|
|
90
|
+
return function (message, action, opener) {
|
|
91
|
+
if (!message || message.status === "submitting") return;
|
|
92
|
+
var payload = { type: "home_mate_creation_proposal_response", proposalId: message.proposal.proposalId, action: action === "create" ? "create" : "cancel" };
|
|
93
|
+
Object.assign(payload, getContext());
|
|
94
|
+
if (!sendMessage(payload)) return;
|
|
95
|
+
message.status = "submitting";
|
|
96
|
+
if (opener) opener.textContent = action === "create" ? "Creating…" : "Cancelling…";
|
|
97
|
+
if (typeof onChange === "function") onChange(message);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function restoreHomeMateProposalFocus(proposalId) {
|
|
102
|
+
var cards = document.querySelectorAll(".home-mate-proposal");
|
|
103
|
+
for (var i = 0; i < cards.length; i++) {
|
|
104
|
+
if (!cards[i].dataset || cards[i].dataset.proposalId !== proposalId) continue;
|
|
105
|
+
var target = cards[i].querySelector('[role="status"]') || cards[i].querySelector('[role="alert"]') || cards[i].querySelector("button");
|
|
106
|
+
if (target) target.focus({ preventScroll: true });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|