clay-server 4.0.0-beta.8 → 4.0.0
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/capsule-display-floor.js +51 -0
- package/lib/capsule-frame-server.js +255 -0
- package/lib/capsule-mate-turn.js +105 -0
- package/lib/capsule-pig-logic.js +300 -0
- package/lib/capsule-server-runtimes.js +39 -0
- package/lib/capsules/pig/display.js +198 -0
- package/lib/capsules/pig/manifest.json +9 -0
- package/lib/capsules/pig/ui.json +84 -0
- package/lib/config.js +25 -1
- package/lib/daemon-projects.js +13 -8
- package/lib/daemon.js +127 -14
- package/lib/git-cli.js +95 -1
- package/lib/knowledge-import.js +436 -0
- package/lib/knowledge-record-store.js +188 -0
- package/lib/knowledge-search.js +240 -0
- package/lib/mate-knowledge-mcp-server.js +151 -0
- package/lib/mate-knowledge-migration.js +283 -0
- package/lib/mate-knowledge-service.js +488 -0
- package/lib/mate-knowledge-sync.js +482 -0
- package/lib/migrate-single-user.js +2 -0
- package/lib/notes-lifecycle.js +149 -0
- package/lib/notes.js +60 -3
- package/lib/os-users.js +3 -1
- package/lib/project-capsule-catalog.js +9 -1
- package/lib/project-capsule-turn.js +96 -0
- package/lib/project-connection.js +15 -1
- package/lib/project-file-history.js +223 -0
- package/lib/project-file-watch.js +77 -49
- package/lib/project-filesystem.js +31 -220
- package/lib/project-http.js +15 -1
- package/lib/project-knowledge.js +22 -0
- package/lib/project-log-context.js +172 -0
- package/lib/project-log-feedback-delivery.js +118 -0
- package/lib/project-logs-comments.js +137 -0
- package/lib/project-logs-context-state.js +42 -0
- package/lib/project-logs-mcp-server.js +328 -0
- package/lib/project-logs-query.js +185 -0
- package/lib/project-logs-root.js +75 -0
- package/lib/project-logs-schema.js +259 -0
- package/lib/project-logs-service.js +463 -0
- package/lib/project-logs-snapshot.js +205 -0
- package/lib/project-logs-store.js +495 -0
- package/lib/project-logs-versioning.js +132 -0
- package/lib/project-logs.js +400 -0
- package/lib/project-mate-interaction.js +14 -0
- package/lib/project-mate-knowledge.js +110 -0
- package/lib/project-memory.js +22 -0
- package/lib/project-message-delivery.js +70 -0
- package/lib/project-pair-lifecycle.js +460 -0
- package/lib/project-request-access.js +66 -0
- package/lib/project-session-handoff.js +1 -1
- package/lib/project-session-notes.js +64 -13
- package/lib/project-session-pair.js +159 -146
- package/lib/project-session-spawn.js +1 -1
- package/lib/project-sessions.js +58 -13
- package/lib/project-user-message.js +53 -4
- package/lib/project-worker-permission.js +430 -0
- package/lib/project-worker-proposal.js +208 -62
- package/lib/project.js +185 -6
- package/lib/public/app.js +63 -35
- package/lib/public/css/admin.css +2 -2
- package/lib/public/css/capsule-ui.css +38 -0
- package/lib/public/css/command-palette.css +19 -9
- package/lib/public/css/git-placard.css +154 -0
- package/lib/public/css/home-hub.css +4 -0
- package/lib/public/css/home-session-actions.css +66 -0
- package/lib/public/css/home-sidebar.css +66 -0
- package/lib/public/css/input.css +84 -3
- package/lib/public/css/messages.css +40 -1
- package/lib/public/css/mobile-nav.css +80 -0
- package/lib/public/css/notifications-center.css +67 -0
- package/lib/public/css/pane.css +97 -10
- package/lib/public/css/project-logs.css +198 -0
- package/lib/public/css/scheduler.css +1 -1
- package/lib/public/css/session-actions.css +16 -101
- package/lib/public/css/sidebar.css +103 -0
- package/lib/public/css/sticky-notes.css +237 -229
- package/lib/public/css/worker-proposal.css +36 -3
- package/lib/public/index.html +43 -11
- package/lib/public/modules/admin.js +8 -8
- package/lib/public/modules/app-connection.js +124 -6
- package/lib/public/modules/app-home-hub.js +12 -8
- package/lib/public/modules/app-message-cards.js +2 -1
- package/lib/public/modules/app-messages.js +92 -11
- package/lib/public/modules/app-misc.js +2 -14
- package/lib/public/modules/app-notifications.js +11 -7
- package/lib/public/modules/app-panels.js +3 -2
- package/lib/public/modules/app-projects.js +56 -18
- package/lib/public/modules/app-rendering.js +1 -0
- package/lib/public/modules/capsule-preference.js +44 -0
- package/lib/public/modules/chat-bubble-renderer.js +1 -1
- package/lib/public/modules/clay-log-links.js +70 -0
- package/lib/public/modules/command-palette.js +52 -15
- package/lib/public/modules/context-sources.js +0 -3
- package/lib/public/modules/git-agent-sessions.js +85 -0
- package/lib/public/modules/git-panel.js +111 -84
- package/lib/public/modules/git-placard.js +161 -0
- package/lib/public/modules/home-chat-empty-state.js +39 -0
- package/lib/public/modules/home-chat-identity.js +9 -0
- package/lib/public/modules/home-conversations-sheet.js +102 -48
- package/lib/public/modules/home-mate-chat.js +15 -35
- package/lib/public/modules/home-session-actions.js +6 -0
- package/lib/public/modules/home-sidebar-chat-list.js +101 -41
- package/lib/public/modules/home-sidebar.js +7 -0
- package/lib/public/modules/home-surface-boot.js +7 -3
- package/lib/public/modules/home-tool-frame.js +182 -0
- package/lib/public/modules/home-tools.js +138 -1
- package/lib/public/modules/input.js +38 -7
- package/lib/public/modules/markdown.js +2 -0
- package/lib/public/modules/mate-sidebar.js +0 -8
- package/lib/public/modules/message-delivery.js +97 -0
- package/lib/public/modules/paste-modal.js +84 -0
- package/lib/public/modules/project-activation.js +45 -0
- package/lib/public/modules/project-logs-render.js +405 -0
- package/lib/public/modules/project-logs.js +461 -0
- package/lib/public/modules/project-removal-target.js +26 -0
- package/lib/public/modules/scheduler.js +17 -3
- package/lib/public/modules/session-actions.js +29 -105
- package/lib/public/modules/session-hierarchy.js +54 -0
- package/lib/public/modules/sidebar-mobile.js +40 -15
- package/lib/public/modules/sidebar-session-hierarchy.js +214 -0
- package/lib/public/modules/sidebar-sessions.js +77 -11
- package/lib/public/modules/sidebar.js +17 -5
- package/lib/public/modules/split-group-helpers.js +8 -0
- package/lib/public/modules/split-pair-ui.js +15 -4
- package/lib/public/modules/split-view.js +5 -2
- package/lib/public/modules/split-worker-runtime.js +24 -0
- package/lib/public/modules/sticky-note-markdown.js +5 -0
- package/lib/public/modules/sticky-notes-browser.js +326 -0
- package/lib/public/modules/sticky-notes-card.js +360 -0
- package/lib/public/modules/sticky-notes-editor.js +291 -0
- package/lib/public/modules/sticky-notes-shared.js +90 -0
- package/lib/public/modules/sticky-notes.js +170 -946
- package/lib/public/modules/thinking-lifecycle.js +175 -0
- package/lib/public/modules/thinking-summary.js +28 -0
- package/lib/public/modules/thinking-view.js +75 -0
- package/lib/public/modules/tool-palette-order.js +143 -0
- package/lib/public/modules/tool-palette-overlays.js +159 -0
- package/lib/public/modules/tool-palette.js +21 -188
- package/lib/public/modules/tools.js +31 -123
- package/lib/public/modules/update-snooze.js +169 -0
- package/lib/public/modules/user-settings.js +4 -0
- package/lib/public/modules/worker-pane-lock.js +176 -0
- package/lib/public/modules/worker-proposal-state.js +16 -0
- package/lib/public/modules/worker-proposal.js +49 -20
- package/lib/public/style.css +3 -1
- package/lib/sdk-bridge.js +135 -46
- package/lib/sdk-message-processor.js +36 -19
- package/lib/server-experimental-settings.js +49 -0
- package/lib/server-global-ws.js +11 -0
- package/lib/server-home-chat-events.js +31 -1
- package/lib/server-home-chat.js +27 -4
- package/lib/server-home-clay-entry.js +1 -1
- package/lib/server-home-clay-session-links.js +34 -5
- package/lib/server-settings.js +5 -0
- package/lib/server-tools.js +127 -9
- package/lib/server.js +137 -22
- package/lib/session-driver-eligibility.js +38 -0
- package/lib/session-driver-orchestration.js +31 -0
- package/lib/session-hygiene.js +3 -0
- package/lib/session-notes-mcp-server.js +25 -5
- package/lib/session-pair-factory.js +247 -0
- package/lib/session-pair-mcp-server.js +46 -8
- package/lib/session-pair-prompts.js +79 -0
- package/lib/session-pair-turn-control.js +153 -0
- package/lib/session-provenance.js +119 -0
- package/lib/session-spawn-mcp-server.js +1 -1
- package/lib/session-split-groups.js +4 -1
- package/lib/session-title-policy.js +60 -0
- package/lib/session-visibility.js +41 -0
- package/lib/sessions.js +57 -10
- package/lib/tools-registry.js +89 -10
- package/lib/update-snooze.js +437 -0
- package/lib/users-experimental-preferences.js +30 -0
- package/lib/users-permissions.js +6 -6
- package/lib/users.js +22 -16
- package/lib/workspace-query-access.js +102 -0
- package/lib/workspace-query-service.js +24 -17
- package/lib/worktree.js +25 -38
- package/lib/ws-schema.js +38 -7
- package/lib/yoke/adapters/claude-worker.js +16 -6
- package/lib/yoke/adapters/claude.js +12 -0
- package/lib/yoke/adapters/codex.js +24 -14
- package/lib/yoke/vendor-registry.js +1 -1
- package/package.json +2 -2
package/lib/public/index.html
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
(function(){try{var k="clay-theme-vars",v=localStorage.getItem(k),r=document.documentElement;if(v){var o=JSON.parse(v),p;for(p in o)r.style.setProperty(p,o[p]);var vt=localStorage.getItem(k.replace("-vars","-variant"));if(vt==="light"){r.classList.add("light-theme");r.classList.remove("dark-theme")}else{r.classList.add("dark-theme");r.classList.remove("light-theme")}var m=document.querySelector('meta[name="theme-color"]');if(m&&o["--bg"])m.setAttribute("content",o["--bg"])}else{var sl=window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches;if(sl){r.classList.add("light-theme");r.classList.remove("dark-theme")}}}catch(e){}})();
|
|
32
32
|
</script>
|
|
33
33
|
<script>if(window.navigator.standalone||window.matchMedia("(display-mode:standalone)").matches){document.documentElement.classList.add("pwa-standalone")}</script>
|
|
34
|
-
<link rel="stylesheet" href="style.css?v=
|
|
34
|
+
<link rel="stylesheet" href="style.css?v=20260908-log-links2">
|
|
35
35
|
<style>
|
|
36
36
|
@media(max-width:768px){
|
|
37
37
|
/* User messages: vertical stack, avatar on top, right-aligned */
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
}
|
|
51
51
|
</style>
|
|
52
52
|
</head>
|
|
53
|
-
<body>
|
|
53
|
+
<body class="capsules-disabled">
|
|
54
54
|
<div id="layout">
|
|
55
55
|
<!-- === Top Bar (full width, forms reverse-ㄱ with icon strip) === -->
|
|
56
56
|
<div id="top-bar">
|
|
@@ -137,6 +137,7 @@
|
|
|
137
137
|
<nav class="home-sidebar-primary-actions" aria-label="Home actions">
|
|
138
138
|
<button id="home-sidebar-new" class="home-sidebar-action home-sidebar-new" type="button" title="New Chat" aria-label="Start a new chat with the current Mate" disabled><span class="home-sidebar-action-icon" aria-hidden="true"><i data-lucide="message-square-plus"></i></span><span>New Chat</span></button>
|
|
139
139
|
<button id="home-sidebar-debate" class="home-sidebar-action" type="button" title="Browse debates" aria-label="Browse debates" aria-pressed="false"><span class="home-sidebar-action-icon" aria-hidden="true"><i data-lucide="messages-square"></i></span><span>Debates</span></button>
|
|
140
|
+
<button id="home-scheduler-btn" class="home-sidebar-action" type="button" title="Scheduled Tasks" aria-label="Scheduled Tasks"><span class="home-sidebar-action-icon" aria-hidden="true"><i data-lucide="calendar-clock"></i></span><span>Scheduled Tasks</span></button>
|
|
140
141
|
<button id="home-tools-btn" class="home-sidebar-action" type="button" title="Capsules" aria-label="Capsules" aria-expanded="false" aria-controls="home-tool-workbench"><span class="home-sidebar-action-icon" aria-hidden="true"><i data-lucide="box"></i></span><span id="home-tools-label">Capsules</span><span id="home-tools-activity" class="home-tools-activity" aria-hidden="true"></span></button>
|
|
141
142
|
</nav>
|
|
142
143
|
<section class="home-sidebar-section home-sidebar-mate-section" aria-labelledby="home-sidebar-mate-label">
|
|
@@ -246,6 +247,15 @@
|
|
|
246
247
|
</div>
|
|
247
248
|
<div id="session-actions"></div>
|
|
248
249
|
<div id="session-actions-hidden" class="tool-palette-hidden-section hidden"></div>
|
|
250
|
+
<!-- Repository placard. Hidden until a real Git repository is
|
|
251
|
+
confirmed, so non-Git projects and pending detection show
|
|
252
|
+
nothing rather than an empty block. -->
|
|
253
|
+
<div id="git-placard" class="git-placard hidden" role="group">
|
|
254
|
+
<div id="git-placard-body" class="git-placard-body" aria-hidden="true"></div>
|
|
255
|
+
<button id="git-placard-more" class="git-placard-more" type="button" aria-label="Open Git panel for more repository detail" title="More Git detail">
|
|
256
|
+
<span>More</span><i data-lucide="chevron-right"></i>
|
|
257
|
+
</button>
|
|
258
|
+
</div>
|
|
249
259
|
</div>
|
|
250
260
|
<div id="sidebar-sessions-header">
|
|
251
261
|
<div id="sessions-header-content">
|
|
@@ -412,9 +422,6 @@
|
|
|
412
422
|
<div id="ralph-sticky" class="hidden"></div>
|
|
413
423
|
<div id="debate-sticky" class="hidden"></div>
|
|
414
424
|
<div class="status">
|
|
415
|
-
<button id="header-session-actions-btn" type="button" aria-label="Session actions" aria-haspopup="menu" aria-expanded="false">
|
|
416
|
-
<i data-lucide="bot"></i><span>Add Split Worker</span><i class="session-actions-trigger-chevron" data-lucide="chevron-down"></i>
|
|
417
|
-
</button>
|
|
418
425
|
<button type="button" id="header-tui-font-btn" class="header-tui-font-btn hidden" title="Terminal font" aria-label="Terminal font">
|
|
419
426
|
<i data-lucide="type"></i>
|
|
420
427
|
</button>
|
|
@@ -541,13 +548,15 @@
|
|
|
541
548
|
<button id="schedule-btn" type="button" aria-label="Schedule message" title="Schedule message"><i data-lucide="clock"></i></button>
|
|
542
549
|
<button id="ask-mate-btn" type="button" aria-label="Ask Mate" title="Ask a Mate for advice on this session"><i data-lucide="at-sign"></i></button>
|
|
543
550
|
<div id="context-sources-btn-wrap" class="desktop-only">
|
|
544
|
-
<button id="context-sources-add" type="button" title="Add context sources"><i data-lucide="library"></i
|
|
551
|
+
<button id="context-sources-add" type="button" aria-label="Add context" title="Add context sources"><i data-lucide="library"></i></button>
|
|
545
552
|
<div id="context-sources-picker" class="hidden">
|
|
546
553
|
<div class="context-picker-section" id="context-picker-email"></div>
|
|
547
554
|
<div class="context-picker-section" id="context-picker-terminals"></div>
|
|
548
555
|
<div class="context-picker-section" id="context-picker-tabs"></div>
|
|
549
556
|
</div>
|
|
550
557
|
</div>
|
|
558
|
+
<button id="composer-add-worker-btn" type="button" class="composer-session-action desktop-only" aria-label="Add Split Worker" title="Add Split Worker"><i data-lucide="bot"></i></button>
|
|
559
|
+
<button id="composer-handoff-btn" type="button" class="composer-session-action desktop-only" aria-label="Continue in another agent" title="Continue in another agent"><i data-lucide="send"></i></button>
|
|
551
560
|
</div>
|
|
552
561
|
<div id="input-bottom-right">
|
|
553
562
|
<div id="active-vendor-indicator" class="hidden" title="Session vendor">
|
|
@@ -998,6 +1007,7 @@
|
|
|
998
1007
|
<option value="us-appearance">Appearance</option>
|
|
999
1008
|
<option value="us-chat">Chat</option>
|
|
1000
1009
|
<option value="us-mates">Mates</option>
|
|
1010
|
+
<option value="us-experimental">Experimental</option>
|
|
1001
1011
|
<option value="us-email">Email</option>
|
|
1002
1012
|
</select>
|
|
1003
1013
|
<div class="us-modal-nav-items">
|
|
@@ -1005,6 +1015,7 @@
|
|
|
1005
1015
|
<button class="us-nav-item" data-section="us-appearance"><span>Appearance</span></button>
|
|
1006
1016
|
<button class="us-nav-item" data-section="us-chat"><span>Chat</span></button>
|
|
1007
1017
|
<button class="us-nav-item" data-section="us-mates"><span>Mates</span></button>
|
|
1018
|
+
<button class="us-nav-item" data-section="us-experimental"><span>Experimental</span></button>
|
|
1008
1019
|
<button class="us-nav-item" data-section="us-email"><span>Email</span></button>
|
|
1009
1020
|
<div class="us-nav-separator"></div>
|
|
1010
1021
|
<button class="us-nav-item us-nav-danger" data-section="us-logout"><span>Log Out</span></button>
|
|
@@ -1160,6 +1171,22 @@
|
|
|
1160
1171
|
</div>
|
|
1161
1172
|
</div>
|
|
1162
1173
|
|
|
1174
|
+
<!-- Experimental section -->
|
|
1175
|
+
<div class="us-section" data-section="us-experimental">
|
|
1176
|
+
<h2>Experimental</h2>
|
|
1177
|
+
<p class="settings-hint">Preview features may change or behave unexpectedly.</p>
|
|
1178
|
+
<div class="settings-card">
|
|
1179
|
+
<label class="settings-toggle-row">
|
|
1180
|
+
<div>
|
|
1181
|
+
<span class="settings-label">Capsules</span>
|
|
1182
|
+
<div class="settings-hint">Enable persistent mini-apps that you and your Mates can use.</div>
|
|
1183
|
+
</div>
|
|
1184
|
+
<input type="checkbox" id="us-capsules-enabled">
|
|
1185
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
1186
|
+
</label>
|
|
1187
|
+
</div>
|
|
1188
|
+
</div>
|
|
1189
|
+
|
|
1163
1190
|
<!-- Email section -->
|
|
1164
1191
|
<div class="us-section" data-section="us-email">
|
|
1165
1192
|
<h2>Email Accounts</h2>
|
|
@@ -1516,17 +1543,22 @@
|
|
|
1516
1543
|
</div>
|
|
1517
1544
|
</div>
|
|
1518
1545
|
|
|
1519
|
-
<div id="paste-modal" class="hidden">
|
|
1546
|
+
<div id="paste-modal" class="hidden" role="dialog" aria-modal="true" aria-labelledby="paste-modal-title">
|
|
1520
1547
|
<div class="confirm-backdrop"></div>
|
|
1521
1548
|
<div class="confirm-dialog paste-modal-dialog">
|
|
1522
1549
|
<div class="paste-modal-header">
|
|
1523
|
-
<span class="paste-modal-title">Pasted content</span>
|
|
1550
|
+
<span class="paste-modal-title" id="paste-modal-title">Pasted content</span>
|
|
1524
1551
|
<div class="paste-modal-actions">
|
|
1525
|
-
<button class="paste-modal-copy" title="Copy to clipboard"><i data-lucide="copy" style="width:15px;height:15px"></i></button>
|
|
1526
|
-
<button class="paste-modal-close"><i data-lucide="x" style="width:16px;height:16px"></i></button>
|
|
1552
|
+
<button class="paste-modal-copy" type="button" title="Copy to clipboard" aria-label="Copy pasted content"><i data-lucide="copy" style="width:15px;height:15px"></i></button>
|
|
1553
|
+
<button class="paste-modal-close" type="button" aria-label="Close pasted content"><i data-lucide="x" style="width:16px;height:16px"></i></button>
|
|
1527
1554
|
</div>
|
|
1528
1555
|
</div>
|
|
1529
1556
|
<pre class="paste-modal-body" id="paste-modal-body"></pre>
|
|
1557
|
+
<textarea class="paste-modal-editor" id="paste-modal-editor" aria-label="Pasted content"></textarea>
|
|
1558
|
+
<div class="paste-modal-footer" id="paste-modal-footer">
|
|
1559
|
+
<button class="paste-modal-cancel" type="button">Cancel</button>
|
|
1560
|
+
<button class="paste-modal-save" id="paste-modal-save" type="button">Save changes</button>
|
|
1561
|
+
</div>
|
|
1530
1562
|
</div>
|
|
1531
1563
|
</div>
|
|
1532
1564
|
|
|
@@ -2305,7 +2337,7 @@
|
|
|
2305
2337
|
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0/lib/addon-fit.min.js"></script>
|
|
2306
2338
|
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-web-links@0/lib/addon-web-links.min.js"></script>
|
|
2307
2339
|
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0/lib/addon-webgl.min.js"></script>
|
|
2308
|
-
<script type="module" src="app.js?v=
|
|
2340
|
+
<script type="module" src="app.js?v=20260908-log-links2"></script>
|
|
2309
2341
|
<div id="pwa-install-modal" class="pwa-modal hidden">
|
|
2310
2342
|
<div class="pwa-modal-backdrop"></div>
|
|
2311
2343
|
<div class="pwa-modal-card">
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
3
3
|
import { showToast, copyToClipboard, escapeHtml } from './utils.js';
|
|
4
4
|
|
|
5
|
-
function showConfirmDialog(message, onConfirm) {
|
|
5
|
+
function showConfirmDialog(message, actionLabel, onConfirm) {
|
|
6
6
|
var modal = document.createElement("div");
|
|
7
7
|
modal.className = "admin-modal-overlay";
|
|
8
|
-
var html = '<div class="admin-modal">' +
|
|
8
|
+
var html = '<div class="admin-modal" role="dialog" aria-modal="true" aria-label="Confirm action">' +
|
|
9
9
|
'<div class="admin-modal-body" style="padding:20px 16px 16px">' +
|
|
10
10
|
'<p class="admin-modal-desc" style="margin:0;font-size:14px;color:var(--text)">' + escapeHtml(message) + '</p>' +
|
|
11
11
|
'</div>' +
|
|
12
12
|
'<div class="admin-modal-footer">' +
|
|
13
|
-
'<button class="admin-modal-save admin-modal-confirm-danger">
|
|
13
|
+
'<button class="admin-modal-save admin-modal-confirm-danger">' + escapeHtml(actionLabel) + '</button>' +
|
|
14
14
|
'<button class="admin-modal-cancel">Cancel</button>' +
|
|
15
15
|
'</div></div>';
|
|
16
16
|
modal.innerHTML = html;
|
|
@@ -198,9 +198,9 @@ function renderUsersTab(body) {
|
|
|
198
198
|
var userId = this.dataset.userId;
|
|
199
199
|
var user = cachedUsers.find(function (u) { return u.id === userId; });
|
|
200
200
|
var name = user ? (user.displayName || user.username) : "this user";
|
|
201
|
-
|
|
201
|
+
showConfirmDialog("Remove " + name + "? This cannot be undone.", "Remove user", function () {
|
|
202
202
|
removeUser(userId, body);
|
|
203
|
-
}
|
|
203
|
+
});
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
206
|
}
|
|
@@ -509,7 +509,7 @@ function renderInvitesTab(body) {
|
|
|
509
509
|
revokeBtns[k].addEventListener("click", function () {
|
|
510
510
|
var code = this.dataset.code;
|
|
511
511
|
var btn = this;
|
|
512
|
-
showConfirmDialog("Revoke this invite? The link will no longer work.", function () {
|
|
512
|
+
showConfirmDialog("Revoke this invite? The link will no longer work.", "Revoke", function () {
|
|
513
513
|
btn.disabled = true;
|
|
514
514
|
fetch("/api/admin/invites/" + encodeURIComponent(code), { method: "DELETE" })
|
|
515
515
|
.then(function (r) { return r.json(); })
|
|
@@ -751,12 +751,12 @@ function renderSmtpTab(body, cfg) {
|
|
|
751
751
|
var removeBtn = body.querySelector("#smtp-remove");
|
|
752
752
|
if (removeBtn) {
|
|
753
753
|
removeBtn.addEventListener("click", function () {
|
|
754
|
-
|
|
754
|
+
showConfirmDialog("Remove SMTP configuration? Users will need to use PIN login.", "Remove SMTP", function () {
|
|
755
755
|
apiPost("/api/admin/smtp", { host: "", user: "", pass: "", from: "" }).then(function () {
|
|
756
756
|
showToast("SMTP configuration removed");
|
|
757
757
|
loadSmtpTab(body);
|
|
758
758
|
});
|
|
759
|
-
}
|
|
759
|
+
});
|
|
760
760
|
});
|
|
761
761
|
}
|
|
762
762
|
}
|
|
@@ -23,8 +23,80 @@ var connectTimeoutId = null;
|
|
|
23
23
|
var connectOverlay = null;
|
|
24
24
|
var hasConnectedOnce = false;
|
|
25
25
|
|
|
26
|
+
// The connect overlay is a full-viewport opaque panel. Revealing it for a
|
|
27
|
+
// reconnect that resolves in under a second reads as the whole screen
|
|
28
|
+
// blinking, so after the first successful connection it is only revealed once
|
|
29
|
+
// a disconnect has actually persisted. The status dot carries the state
|
|
30
|
+
// immediately and without covering anything.
|
|
31
|
+
var OVERLAY_GRACE_MS = 800;
|
|
32
|
+
var overlayGraceTimer = null;
|
|
33
|
+
|
|
34
|
+
// Keeps the socket from looking idle to an intermediary proxy. A proxy resets
|
|
35
|
+
// its upstream read timeout on data flowing back from the server, so this only
|
|
36
|
+
// protects the connection because the server answers with a pong: the project
|
|
37
|
+
// socket via project-connection.js and the slug-less socket via
|
|
38
|
+
// server-global-ws.js. Well under the common 60s proxy read timeout, and one
|
|
39
|
+
// timer per live socket.
|
|
40
|
+
var HEARTBEAT_MS = 25000;
|
|
41
|
+
var heartbeatTimer = null;
|
|
42
|
+
var heartbeatDeadlineTimer = null;
|
|
43
|
+
var disconnectedAt = 0;
|
|
44
|
+
|
|
45
|
+
function clearOverlayGrace() {
|
|
46
|
+
if (overlayGraceTimer) {
|
|
47
|
+
clearTimeout(overlayGraceTimer);
|
|
48
|
+
overlayGraceTimer = null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function showOverlayNow() {
|
|
53
|
+
if (!connectOverlay) return;
|
|
54
|
+
if (hasConnectedOnce) {
|
|
55
|
+
var overlayMessage = document.getElementById("connect-overlay-msg");
|
|
56
|
+
if (overlayMessage) overlayMessage.textContent = "Reconnecting to server…";
|
|
57
|
+
}
|
|
58
|
+
connectOverlay.classList.remove("hidden");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function stopHeartbeat() {
|
|
62
|
+
if (heartbeatTimer) {
|
|
63
|
+
clearInterval(heartbeatTimer);
|
|
64
|
+
heartbeatTimer = null;
|
|
65
|
+
}
|
|
66
|
+
if (heartbeatDeadlineTimer) {
|
|
67
|
+
clearTimeout(heartbeatDeadlineTimer);
|
|
68
|
+
heartbeatDeadlineTimer = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Started once per live socket. The socket it was started for is captured, so
|
|
73
|
+
// a timer that outlives its socket stops itself instead of writing to a stale
|
|
74
|
+
// connection. The pong the server sends back keeps a proxy's upstream read
|
|
75
|
+
// timeout from expiring. A missing pong also identifies a half-open socket,
|
|
76
|
+
// which browsers can otherwise leave OPEN while silently losing sends.
|
|
77
|
+
export function startHeartbeat(socket) {
|
|
78
|
+
stopHeartbeat();
|
|
79
|
+
if (!socket) return;
|
|
80
|
+
heartbeatTimer = setInterval(function () {
|
|
81
|
+
if (!socket || socket.readyState !== 1 || getWs() !== socket) {
|
|
82
|
+
stopHeartbeat();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
try { socket.send(JSON.stringify({ type: "ping" })); } catch (e) {}
|
|
86
|
+
if (heartbeatDeadlineTimer) clearTimeout(heartbeatDeadlineTimer);
|
|
87
|
+
heartbeatDeadlineTimer = setTimeout(function () {
|
|
88
|
+
heartbeatDeadlineTimer = null;
|
|
89
|
+
if (getWs() === socket && socket.readyState === 1) connect();
|
|
90
|
+
}, 10000);
|
|
91
|
+
}, HEARTBEAT_MS);
|
|
92
|
+
}
|
|
93
|
+
|
|
26
94
|
export function initConnection() {
|
|
27
95
|
connectOverlay = document.getElementById("connect-overlay");
|
|
96
|
+
window.addEventListener("clay-message-delivery-timeout", function (event) {
|
|
97
|
+
var socket = event.detail && event.detail.socket;
|
|
98
|
+
if (!socket || getWs() === socket) connect();
|
|
99
|
+
});
|
|
28
100
|
|
|
29
101
|
// --- Reactive UI sync for connected/processing state ---
|
|
30
102
|
store.subscribe(function (state, prev) {
|
|
@@ -44,18 +116,30 @@ export function initConnection() {
|
|
|
44
116
|
if (state.connected !== prev.connected) {
|
|
45
117
|
var sendBtn = getSendBtn();
|
|
46
118
|
if (state.connected) {
|
|
119
|
+
// Cancel a pending reveal and hide immediately: a reconnect that beat
|
|
120
|
+
// the grace delay must never flash the overlay at all.
|
|
121
|
+
clearOverlayGrace();
|
|
47
122
|
hasConnectedOnce = true;
|
|
48
123
|
if (sendBtn) sendBtn.disabled = false;
|
|
49
124
|
if (connectOverlay) connectOverlay.classList.add("hidden");
|
|
50
125
|
var updPill = document.getElementById("update-pill-wrap");
|
|
51
126
|
if (updPill) updPill.classList.add("hidden");
|
|
52
127
|
} else {
|
|
128
|
+
// The composer stays disabled while the socket is down because there
|
|
129
|
+
// is no outbound queue: every sender drops on readyState !== 1, so
|
|
130
|
+
// enabling it here would silently discard the message.
|
|
53
131
|
if (sendBtn) sendBtn.disabled = true;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
132
|
+
clearOverlayGrace();
|
|
133
|
+
if (!hasConnectedOnce) {
|
|
134
|
+
// First connection of the page: the startup overlay is the intended
|
|
135
|
+
// presentation and appears immediately.
|
|
136
|
+
showOverlayNow();
|
|
137
|
+
} else {
|
|
138
|
+
overlayGraceTimer = setTimeout(function () {
|
|
139
|
+
overlayGraceTimer = null;
|
|
140
|
+
if (!store.get('connected')) showOverlayNow();
|
|
141
|
+
}, OVERLAY_GRACE_MS);
|
|
57
142
|
}
|
|
58
|
-
if (connectOverlay) connectOverlay.classList.remove("hidden");
|
|
59
143
|
}
|
|
60
144
|
}
|
|
61
145
|
|
|
@@ -133,15 +217,28 @@ function onConnected() {
|
|
|
133
217
|
|
|
134
218
|
export function connect() {
|
|
135
219
|
var ws = getWs();
|
|
136
|
-
|
|
220
|
+
// Tear down the previous socket's heartbeat before a new socket exists, so
|
|
221
|
+
// two sockets can never both be pinging.
|
|
222
|
+
stopHeartbeat();
|
|
223
|
+
if (ws && ws.readyState === 1) setStatus("disconnected");
|
|
224
|
+
if (ws) {
|
|
225
|
+
ws.onopen = null;
|
|
226
|
+
ws.onmessage = null;
|
|
227
|
+
ws.onerror = null;
|
|
228
|
+
ws.onclose = null;
|
|
229
|
+
ws.close();
|
|
230
|
+
}
|
|
137
231
|
if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; }
|
|
138
232
|
|
|
139
233
|
var protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
|
140
|
-
var
|
|
234
|
+
var socketPath = store.get('wsPath');
|
|
235
|
+
store.set({ socketPath: socketPath, activeProjectSlug: null, sessionActivatedProjectSlug: null });
|
|
236
|
+
var newWs = new WebSocket(protocol + "//" + location.host + socketPath);
|
|
141
237
|
setWs(newWs);
|
|
142
238
|
|
|
143
239
|
// If not connected within 3s, force retry
|
|
144
240
|
connectTimeoutId = setTimeout(function () {
|
|
241
|
+
if (getWs() !== newWs) return;
|
|
145
242
|
if (!store.get('connected')) {
|
|
146
243
|
newWs.onclose = null;
|
|
147
244
|
newWs.onerror = null;
|
|
@@ -151,8 +248,14 @@ export function connect() {
|
|
|
151
248
|
}, 3000);
|
|
152
249
|
|
|
153
250
|
newWs.onopen = function () {
|
|
251
|
+
if (getWs() !== newWs) return;
|
|
154
252
|
if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; }
|
|
253
|
+
if (hasConnectedOnce && disconnectedAt) {
|
|
254
|
+
console.log("[clay] WebSocket reconnected after " + (Date.now() - disconnectedAt) + "ms");
|
|
255
|
+
}
|
|
256
|
+
disconnectedAt = 0;
|
|
155
257
|
setStatus("connected");
|
|
258
|
+
startHeartbeat(newWs);
|
|
156
259
|
reconnectDelay = 1000;
|
|
157
260
|
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
|
|
158
261
|
|
|
@@ -174,8 +277,18 @@ export function connect() {
|
|
|
174
277
|
};
|
|
175
278
|
|
|
176
279
|
newWs.onclose = function (e) {
|
|
280
|
+
if (getWs() !== newWs) return;
|
|
177
281
|
if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; }
|
|
282
|
+
stopHeartbeat();
|
|
283
|
+
disconnectedAt = Date.now();
|
|
284
|
+
// Abnormal closes are the ones worth reporting: a clean close is either a
|
|
285
|
+
// deliberate navigation or a server shutdown the user already knows about.
|
|
286
|
+
if (e && !e.wasClean) {
|
|
287
|
+
console.warn("[clay] WebSocket closed abnormally: code=" + e.code +
|
|
288
|
+
" reason=" + (e.reason || "(none)") + " wasClean=false");
|
|
289
|
+
}
|
|
178
290
|
closeDmUserPicker();
|
|
291
|
+
store.set({ activeProjectSlug: null, sessionActivatedProjectSlug: null });
|
|
179
292
|
setStatus("disconnected");
|
|
180
293
|
setActivity(null);
|
|
181
294
|
scheduleReconnect();
|
|
@@ -184,6 +297,7 @@ export function connect() {
|
|
|
184
297
|
newWs.onerror = function () {};
|
|
185
298
|
|
|
186
299
|
newWs.onmessage = function (event) {
|
|
300
|
+
if (getWs() !== newWs) return;
|
|
187
301
|
// Backup: if we're receiving messages, we're connected
|
|
188
302
|
if (!store.get('connected')) {
|
|
189
303
|
setStatus("connected");
|
|
@@ -194,6 +308,10 @@ export function connect() {
|
|
|
194
308
|
blinkIO();
|
|
195
309
|
var msg;
|
|
196
310
|
try { msg = JSON.parse(event.data); } catch (e) { return; }
|
|
311
|
+
if (msg.type === "pong" && heartbeatDeadlineTimer) {
|
|
312
|
+
clearTimeout(heartbeatDeadlineTimer);
|
|
313
|
+
heartbeatDeadlineTimer = null;
|
|
314
|
+
}
|
|
197
315
|
processMessage(msg);
|
|
198
316
|
};
|
|
199
317
|
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { store } from './store.js';
|
|
4
4
|
import { getWs } from './ws-ref.js';
|
|
5
|
-
import { getCachedProjects } from './app-projects.js';
|
|
5
|
+
import { getCachedProjects, switchProject } from './app-projects.js';
|
|
6
|
+
import { chooseProjectActivationTarget } from './project-activation.js';
|
|
6
7
|
import { mateAvatarUrl } from './avatar.js';
|
|
7
8
|
import { exitDmMode } from './app-dm.js';
|
|
8
9
|
import { closeHomeDock, initHomeDock, renderDock, requestHomeDockPreference } from './home-dock.js';
|
|
@@ -41,7 +42,14 @@ function syncHomePresentation() {
|
|
|
41
42
|
function syncHomeCloseControl() {
|
|
42
43
|
var close = document.getElementById("home-close-control");
|
|
43
44
|
if (!close) return;
|
|
44
|
-
close.classList.toggle("hidden", !
|
|
45
|
+
close.classList.toggle("hidden", !getHomeReturnSlug());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getHomeReturnSlug() {
|
|
49
|
+
return chooseProjectActivationTarget(
|
|
50
|
+
getCachedProjects(),
|
|
51
|
+
[store.get('currentSlug'), store.get('homeSurfaceProjectSlug')]
|
|
52
|
+
);
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
function getVisibleMates() {
|
|
@@ -390,13 +398,9 @@ export function hideHomeHub() {
|
|
|
390
398
|
}
|
|
391
399
|
|
|
392
400
|
export function minimizeHomeHub() {
|
|
393
|
-
var slug =
|
|
401
|
+
var slug = getHomeReturnSlug();
|
|
394
402
|
if (!slug || !homeHubVisible) return;
|
|
395
|
-
|
|
396
|
-
hideHomeHub();
|
|
397
|
-
var route = "/p/" + slug + "/";
|
|
398
|
-
if (document.documentElement.classList.contains("pwa-standalone")) history.replaceState(null, "", route);
|
|
399
|
-
else history.pushState(null, "", route);
|
|
403
|
+
switchProject(slug);
|
|
400
404
|
var projectInput = document.getElementById("input");
|
|
401
405
|
if (projectInput && !projectInput.disabled) projectInput.focus({ preventScroll: true });
|
|
402
406
|
}
|
|
@@ -6,7 +6,8 @@ import { escapeHtml, copyToClipboard } from './utils.js';
|
|
|
6
6
|
import { renderMarkdown, highlightCodeBlocks } from './markdown.js';
|
|
7
7
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
8
8
|
import { userAvatarUrl } from './avatar.js';
|
|
9
|
-
import { showImageModal
|
|
9
|
+
import { showImageModal } from './app-misc.js';
|
|
10
|
+
import { showPasteModal } from './paste-modal.js';
|
|
10
11
|
import { addToMessages, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, setTurnCounter, VENDOR_AVATARS } from './chat-render-runtime.js';
|
|
11
12
|
|
|
12
13
|
export function addUserMessage(text, images, pastes, fromUserId, fromUserName, delegatedMeta) {
|