clay-server 2.31.0 → 2.32.0-beta.10
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/browser-mcp-server.js +32 -44
- package/lib/codex-defaults.js +18 -0
- package/lib/debate-mcp-server.js +14 -31
- package/lib/mcp-local.js +31 -1
- package/lib/project-connection.js +9 -6
- package/lib/project-debate.js +8 -0
- package/lib/project-filesystem.js +47 -1
- package/lib/project-http.js +75 -8
- package/lib/project-mate-interaction.js +102 -16
- package/lib/project-mcp.js +4 -0
- package/lib/project-notifications.js +9 -0
- package/lib/project-sessions.js +94 -51
- package/lib/project-user-message.js +12 -7
- package/lib/project.js +234 -99
- package/lib/public/app.js +135 -454
- package/lib/public/codex-avatar.png +0 -0
- package/lib/public/css/debate.css +3 -2
- package/lib/public/css/filebrowser.css +91 -1
- package/lib/public/css/icon-strip.css +21 -5
- package/lib/public/css/input.css +338 -104
- package/lib/public/css/mates.css +43 -0
- package/lib/public/css/mention.css +48 -4
- package/lib/public/css/menus.css +1 -1
- package/lib/public/css/messages.css +2 -0
- package/lib/public/css/notifications-center.css +26 -0
- package/lib/public/css/tooltip.css +47 -0
- package/lib/public/index.html +78 -26
- package/lib/public/modules/app-connection.js +138 -37
- package/lib/public/modules/app-cursors.js +18 -17
- package/lib/public/modules/app-debate-ui.js +9 -9
- package/lib/public/modules/app-dm.js +175 -131
- package/lib/public/modules/app-favicon.js +28 -26
- package/lib/public/modules/app-header.js +79 -68
- package/lib/public/modules/app-home-hub.js +55 -47
- package/lib/public/modules/app-loop-ui.js +34 -18
- package/lib/public/modules/app-loop-wizard.js +6 -6
- package/lib/public/modules/app-messages.js +199 -153
- package/lib/public/modules/app-misc.js +23 -12
- package/lib/public/modules/app-notifications.js +119 -9
- package/lib/public/modules/app-panels.js +203 -49
- package/lib/public/modules/app-projects.js +161 -150
- package/lib/public/modules/app-rate-limit.js +5 -4
- package/lib/public/modules/app-rendering.js +149 -101
- package/lib/public/modules/app-skills-install.js +4 -4
- package/lib/public/modules/context-sources.js +102 -66
- package/lib/public/modules/dom-refs.js +21 -0
- package/lib/public/modules/filebrowser.js +173 -2
- package/lib/public/modules/input.js +122 -0
- package/lib/public/modules/markdown.js +5 -1
- package/lib/public/modules/mate-sidebar.js +38 -0
- package/lib/public/modules/mention.js +24 -6
- package/lib/public/modules/scheduler.js +1 -1
- package/lib/public/modules/sidebar-mates.js +79 -35
- package/lib/public/modules/sidebar-mobile.js +34 -30
- package/lib/public/modules/sidebar-projects.js +60 -57
- package/lib/public/modules/sidebar-sessions.js +75 -69
- package/lib/public/modules/sidebar.js +12 -20
- package/lib/public/modules/skills.js +8 -9
- package/lib/public/modules/sticky-notes.js +1 -2
- package/lib/public/modules/store.js +9 -2
- package/lib/public/modules/stt.js +4 -1
- package/lib/public/modules/terminal.js +12 -0
- package/lib/public/modules/tools.js +18 -13
- package/lib/public/modules/tooltip.js +32 -5
- package/lib/sdk-bridge.js +562 -1114
- package/lib/sdk-message-processor.js +150 -135
- package/lib/sdk-worker.js +4 -0
- package/lib/server-dm.js +1 -0
- package/lib/server.js +86 -1
- package/lib/sessions.js +81 -37
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +559 -0
- package/lib/yoke/adapters/claude.js +1483 -0
- package/lib/yoke/adapters/codex.js +1121 -0
- package/lib/yoke/adapters/gemini.js +709 -0
- package/lib/yoke/codex-app-server.js +307 -0
- package/lib/yoke/index.js +199 -0
- package/lib/yoke/instructions.js +62 -0
- package/lib/yoke/interface.js +98 -0
- package/lib/yoke/mcp-bridge-server.js +294 -0
- package/lib/yoke/package.json +7 -0
- package/package.json +3 -1
package/lib/public/css/mates.css
CHANGED
|
@@ -616,6 +616,49 @@
|
|
|
616
616
|
font-weight: 600;
|
|
617
617
|
}
|
|
618
618
|
|
|
619
|
+
/* Vendor toggle in mate header (split pill) */
|
|
620
|
+
.mate-vendor-toggle {
|
|
621
|
+
display: flex;
|
|
622
|
+
align-items: center;
|
|
623
|
+
border: 1px solid rgba(255,255,255,0.4);
|
|
624
|
+
border-radius: 6px;
|
|
625
|
+
overflow: hidden;
|
|
626
|
+
height: 24px;
|
|
627
|
+
}
|
|
628
|
+
.mate-vendor-btn {
|
|
629
|
+
display: flex;
|
|
630
|
+
align-items: center;
|
|
631
|
+
gap: 3px;
|
|
632
|
+
padding: 0 6px;
|
|
633
|
+
height: 100%;
|
|
634
|
+
border: none;
|
|
635
|
+
background: transparent;
|
|
636
|
+
cursor: pointer;
|
|
637
|
+
font-size: 11px;
|
|
638
|
+
color: rgba(255,255,255,0.5);
|
|
639
|
+
transition: background 0.15s, color 0.15s;
|
|
640
|
+
white-space: nowrap;
|
|
641
|
+
}
|
|
642
|
+
.mate-vendor-btn:first-child { border-right: 1px solid rgba(255,255,255,0.4); }
|
|
643
|
+
.mate-vendor-btn.active {
|
|
644
|
+
background: rgba(255,255,255,0.25);
|
|
645
|
+
color: #fff;
|
|
646
|
+
font-weight: 600;
|
|
647
|
+
}
|
|
648
|
+
.mate-vendor-btn.disabled {
|
|
649
|
+
opacity: 0.3;
|
|
650
|
+
cursor: default;
|
|
651
|
+
}
|
|
652
|
+
.mate-vendor-icon {
|
|
653
|
+
width: 14px;
|
|
654
|
+
height: 14px;
|
|
655
|
+
border-radius: 3px;
|
|
656
|
+
background: #fff;
|
|
657
|
+
}
|
|
658
|
+
.mate-vendor-label {
|
|
659
|
+
pointer-events: none;
|
|
660
|
+
}
|
|
661
|
+
|
|
619
662
|
/* Collapse button inside mate header: white to match header style */
|
|
620
663
|
.mate-sidebar-header .sidebar-collapse-btn {
|
|
621
664
|
color: rgba(255,255,255,0.7);
|
|
@@ -8,18 +8,44 @@
|
|
|
8
8
|
bottom: 100%;
|
|
9
9
|
left: 0;
|
|
10
10
|
right: 0;
|
|
11
|
-
max-height:
|
|
11
|
+
max-height: 280px;
|
|
12
12
|
overflow-y: auto;
|
|
13
|
-
background: var(--bg
|
|
13
|
+
background: var(--sidebar-bg);
|
|
14
14
|
border: 1px solid var(--border);
|
|
15
|
-
border-radius:
|
|
15
|
+
border-radius: 10px;
|
|
16
16
|
margin-bottom: 8px;
|
|
17
|
-
box-shadow: 0
|
|
17
|
+
box-shadow: 0 4px 12px rgba(var(--shadow-rgb), 0.15);
|
|
18
18
|
z-index: 10;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
#mention-menu.visible { display: block; }
|
|
22
22
|
|
|
23
|
+
.mention-hint {
|
|
24
|
+
position: sticky;
|
|
25
|
+
top: 0;
|
|
26
|
+
padding: 8px 16px;
|
|
27
|
+
font-size: 12px;
|
|
28
|
+
color: var(--text-muted);
|
|
29
|
+
background: var(--sidebar-bg);
|
|
30
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
31
|
+
z-index: 1;
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
justify-content: space-between;
|
|
35
|
+
}
|
|
36
|
+
.mention-close-btn {
|
|
37
|
+
border: none;
|
|
38
|
+
background: none;
|
|
39
|
+
color: var(--text-dimmer);
|
|
40
|
+
font-size: 16px;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
padding: 0 2px;
|
|
43
|
+
line-height: 1;
|
|
44
|
+
}
|
|
45
|
+
.mention-close-btn:hover {
|
|
46
|
+
color: var(--text-secondary);
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
.mention-item {
|
|
24
50
|
display: flex;
|
|
25
51
|
align-items: center;
|
|
@@ -36,12 +62,30 @@
|
|
|
36
62
|
background: rgba(var(--overlay-rgb), 0.05);
|
|
37
63
|
}
|
|
38
64
|
|
|
65
|
+
.mention-item-avatar-wrap {
|
|
66
|
+
position: relative;
|
|
67
|
+
width: 24px;
|
|
68
|
+
height: 24px;
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
}
|
|
39
71
|
.mention-item-avatar {
|
|
40
72
|
width: 24px;
|
|
41
73
|
height: 24px;
|
|
42
74
|
border-radius: 50%;
|
|
43
75
|
flex-shrink: 0;
|
|
44
76
|
}
|
|
77
|
+
.mention-item-vendor-badge {
|
|
78
|
+
position: absolute;
|
|
79
|
+
bottom: -2px;
|
|
80
|
+
right: -2px;
|
|
81
|
+
width: 12px;
|
|
82
|
+
height: 12px;
|
|
83
|
+
border-radius: 50%;
|
|
84
|
+
border: 1.5px solid var(--sidebar-bg);
|
|
85
|
+
background: #fff;
|
|
86
|
+
object-fit: cover;
|
|
87
|
+
object-position: center;
|
|
88
|
+
}
|
|
45
89
|
|
|
46
90
|
.mention-item-info {
|
|
47
91
|
flex: 1;
|
package/lib/public/css/menus.css
CHANGED
|
@@ -1688,6 +1688,7 @@ pre.mermaid-error {
|
|
|
1688
1688
|
/* DM mode: hide project-specific UI elements */
|
|
1689
1689
|
#main-column.dm-mode .msg-group,
|
|
1690
1690
|
#main-column.dm-mode #suggestion-chips,
|
|
1691
|
+
#main-column.dm-mode #ghost-suggestion,
|
|
1691
1692
|
#main-column.dm-mode .tool-group {
|
|
1692
1693
|
display: none;
|
|
1693
1694
|
}
|
|
@@ -1696,6 +1697,7 @@ pre.mermaid-error {
|
|
|
1696
1697
|
#main-column.dm-mode #stt-btn,
|
|
1697
1698
|
#main-column.dm-mode #config-chip-wrap,
|
|
1698
1699
|
#main-column.dm-mode #context-mini,
|
|
1700
|
+
#main-column.dm-mode #context-sources-btn-wrap,
|
|
1699
1701
|
#main-column.dm-mode #slash-menu {
|
|
1700
1702
|
display: none !important;
|
|
1701
1703
|
}
|
|
@@ -81,6 +81,13 @@
|
|
|
81
81
|
}
|
|
82
82
|
.notif-banner-icon .lucide { width: 16px; height: 16px; }
|
|
83
83
|
.notif-banner-emoji { font-size: 18px; line-height: 1; }
|
|
84
|
+
.notif-banner-avatar {
|
|
85
|
+
width: 100%;
|
|
86
|
+
height: 100%;
|
|
87
|
+
object-fit: cover;
|
|
88
|
+
border-radius: 8px;
|
|
89
|
+
display: block;
|
|
90
|
+
}
|
|
84
91
|
|
|
85
92
|
.notif-banner-body {
|
|
86
93
|
flex: 1;
|
|
@@ -180,6 +187,25 @@
|
|
|
180
187
|
}
|
|
181
188
|
.notif-banner-close .lucide { width: 12px; height: 12px; }
|
|
182
189
|
|
|
190
|
+
/* Update available banner */
|
|
191
|
+
.notif-banner-update .notif-banner-icon {
|
|
192
|
+
background: none;
|
|
193
|
+
}
|
|
194
|
+
.notif-banner-update-now {
|
|
195
|
+
padding: 4px 10px;
|
|
196
|
+
border-radius: 6px;
|
|
197
|
+
font-size: 11px;
|
|
198
|
+
font-weight: 600;
|
|
199
|
+
font-family: inherit;
|
|
200
|
+
cursor: pointer;
|
|
201
|
+
border: none;
|
|
202
|
+
background: var(--accent, #6c5ce7);
|
|
203
|
+
color: #fff;
|
|
204
|
+
transition: opacity 0.15s;
|
|
205
|
+
}
|
|
206
|
+
.notif-banner-update-now:hover { opacity: 0.85; }
|
|
207
|
+
.notif-banner-update-now:disabled { opacity: 0.6; cursor: default; }
|
|
208
|
+
|
|
183
209
|
/* ========================================================
|
|
184
210
|
Mobile
|
|
185
211
|
======================================================== */
|
|
@@ -18,3 +18,50 @@
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
.tooltip.visible { opacity: 1; }
|
|
21
|
+
|
|
22
|
+
.tooltip.multi-line {
|
|
23
|
+
white-space: pre-line;
|
|
24
|
+
text-align: left;
|
|
25
|
+
max-width: 320px;
|
|
26
|
+
line-height: 1.5;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Context sources tooltip */
|
|
30
|
+
.ctx-tip-header {
|
|
31
|
+
font-size: 11px;
|
|
32
|
+
font-weight: 600;
|
|
33
|
+
text-transform: uppercase;
|
|
34
|
+
letter-spacing: 0.04em;
|
|
35
|
+
color: var(--text-muted);
|
|
36
|
+
margin-bottom: 6px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.ctx-tip-row {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
gap: 8px;
|
|
43
|
+
padding: 4px 0;
|
|
44
|
+
color: var(--text);
|
|
45
|
+
font-size: 12px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.ctx-tip-row .lucide {
|
|
49
|
+
width: 14px;
|
|
50
|
+
height: 14px;
|
|
51
|
+
flex-shrink: 0;
|
|
52
|
+
color: var(--text-secondary);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.ctx-tip-favicon {
|
|
56
|
+
width: 14px;
|
|
57
|
+
height: 14px;
|
|
58
|
+
flex-shrink: 0;
|
|
59
|
+
border-radius: 2px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.ctx-tip-row span {
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
text-overflow: ellipsis;
|
|
65
|
+
white-space: nowrap;
|
|
66
|
+
max-width: 260px;
|
|
67
|
+
}
|
package/lib/public/index.html
CHANGED
|
@@ -40,10 +40,6 @@
|
|
|
40
40
|
body.wide-view:not(.mate-dm-active) .tool-group{max-width:100%!important}
|
|
41
41
|
body.wide-view:not(.mate-dm-active) .turn-meta{max-width:100%!important}
|
|
42
42
|
}
|
|
43
|
-
#ask-mate-btn::before{content:""!important;position:absolute!important;inset:0!important;background:linear-gradient(135deg,#4ecdc4 0%,#556bf7 33%,#a855f7 55%,#f857a6 78%,#ff6b6b 100%)!important;border-radius:inherit!important;opacity:0;transform:scale(0.3);transition:opacity 0.3s ease,transform 0.35s cubic-bezier(0.34,1.56,0.64,1);z-index:-1}
|
|
44
|
-
#ask-mate-btn:hover::before{opacity:1!important;transform:scale(1)!important}
|
|
45
|
-
#ask-mate-btn:hover{border-color:transparent!important;background:transparent!important;transform:translateY(-1px);box-shadow:0 2px 12px rgba(148,130,247,0.4)}
|
|
46
|
-
#ask-mate-btn:hover .ask-mate-label{background:none!important;-webkit-text-fill-color:#fff!important}
|
|
47
43
|
</style>
|
|
48
44
|
</head>
|
|
49
45
|
<body>
|
|
@@ -219,15 +215,7 @@
|
|
|
219
215
|
<button id="session-search-clear" type="button" aria-label="Clear search"><i data-lucide="x"></i></button>
|
|
220
216
|
</div>
|
|
221
217
|
</div>
|
|
222
|
-
<div id="files-header-content" class="hidden">
|
|
223
|
-
<div class="session-list-header">
|
|
224
|
-
<span>File Browser</span>
|
|
225
|
-
<div class="session-list-header-actions">
|
|
226
|
-
<button id="file-panel-refresh" type="button" title="Refresh file tree"><i data-lucide="refresh-cw"></i></button>
|
|
227
|
-
<button id="file-panel-close" type="button" title="Close file browser"><i data-lucide="x"></i></button>
|
|
228
|
-
</div>
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
218
|
+
<div id="files-header-content" class="hidden"></div>
|
|
231
219
|
</div>
|
|
232
220
|
<div id="ralph-loop-section"></div>
|
|
233
221
|
<div id="sidebar-panel-projects" class="sidebar-panel hidden">
|
|
@@ -244,6 +232,15 @@
|
|
|
244
232
|
</div>
|
|
245
233
|
</div>
|
|
246
234
|
<div id="sidebar-panel-files" class="sidebar-panel hidden">
|
|
235
|
+
<div class="fb-titlebar">
|
|
236
|
+
<button id="file-panel-refresh" type="button" title="Refresh file tree"><i data-lucide="refresh-cw"></i></button>
|
|
237
|
+
<span class="fb-titlebar-title">File Browser</span>
|
|
238
|
+
<button id="file-panel-close" type="button" title="Close file browser"><i data-lucide="x"></i></button>
|
|
239
|
+
</div>
|
|
240
|
+
<div class="fb-search-bar">
|
|
241
|
+
<i data-lucide="search" class="fb-search-icon"></i>
|
|
242
|
+
<input id="fb-search-input" type="text" placeholder="Search files..." autocomplete="off" spellcheck="false" />
|
|
243
|
+
</div>
|
|
247
244
|
<div id="file-tree"></div>
|
|
248
245
|
</div>
|
|
249
246
|
</div>
|
|
@@ -256,6 +253,7 @@
|
|
|
256
253
|
<div class="mate-sidebar-header" id="mate-sidebar-header">
|
|
257
254
|
<img id="mate-sidebar-avatar" class="mate-sidebar-avatar" alt="">
|
|
258
255
|
<span id="mate-sidebar-name" class="mate-sidebar-name"></span>
|
|
256
|
+
<div id="mate-vendor-toggle" class="mate-vendor-toggle"></div>
|
|
259
257
|
<div id="mate-sidebar-seed-tooltip" class="mate-seed-tooltip hidden"></div>
|
|
260
258
|
<button id="mate-sidebar-toggle-btn" class="sidebar-collapse-btn" title="Collapse sidebar"><i data-lucide="panel-left-close"></i></button>
|
|
261
259
|
</div>
|
|
@@ -354,6 +352,7 @@
|
|
|
354
352
|
<div class="status">
|
|
355
353
|
<button id="debate-pdf-btn" class="hidden" title="Export debate as PDF"><i data-lucide="download"></i></button>
|
|
356
354
|
<button id="find-in-session-btn" title="Search in session (Ctrl+F)"><i data-lucide="search"></i></button>
|
|
355
|
+
<button id="terminal-toggle-btn" title="Terminal"><i data-lucide="square-terminal"></i><span id="terminal-count" class="hidden"></span></button>
|
|
357
356
|
</div>
|
|
358
357
|
</div>
|
|
359
358
|
<div id="main-panels">
|
|
@@ -446,15 +445,6 @@
|
|
|
446
445
|
<div id="input-wrapper">
|
|
447
446
|
<div id="mention-menu"></div>
|
|
448
447
|
<div id="slash-menu"></div>
|
|
449
|
-
<div id="context-sources-bar">
|
|
450
|
-
<div id="context-sources-chips"></div>
|
|
451
|
-
<button id="context-sources-add" type="button" title="Add context source"><i data-lucide="plus"></i><span>Context Sources</span></button>
|
|
452
|
-
<div id="context-sources-picker" class="hidden">
|
|
453
|
-
<div class="context-picker-section" id="context-picker-email"></div>
|
|
454
|
-
<div class="context-picker-section" id="context-picker-terminals"></div>
|
|
455
|
-
<div class="context-picker-section" id="context-picker-tabs"></div>
|
|
456
|
-
</div>
|
|
457
|
-
</div>
|
|
458
448
|
<div id="input-row">
|
|
459
449
|
<div id="context-mini" class="hidden">
|
|
460
450
|
<div class="context-mini-bar">
|
|
@@ -464,16 +454,38 @@
|
|
|
464
454
|
</div>
|
|
465
455
|
<div id="image-preview-bar"></div>
|
|
466
456
|
<div id="suggestion-chips" class="hidden"></div>
|
|
467
|
-
<
|
|
457
|
+
<div id="input-textarea-wrap">
|
|
458
|
+
<textarea id="input" rows="1" placeholder="Message Claude Code..." enterkeyhint="send" dir="auto"></textarea>
|
|
459
|
+
<div id="ghost-suggestion" class="hidden" aria-hidden="true"></div>
|
|
460
|
+
</div>
|
|
468
461
|
<div id="input-bottom">
|
|
469
462
|
<div id="attach-wrap">
|
|
470
|
-
<button id="
|
|
471
|
-
<button id="attach-
|
|
463
|
+
<button id="input-more-btn" type="button" aria-label="More options" title="More options" class="mobile-only"><i data-lucide="plus"></i></button>
|
|
464
|
+
<button id="attach-file-btn" type="button" aria-label="Attach file" title="Attach file" class="desktop-only"><i data-lucide="paperclip"></i></button>
|
|
465
|
+
<button id="attach-image-btn" type="button" aria-label="Attach image" title="Attach image" class="desktop-only"><i data-lucide="image"></i></button>
|
|
472
466
|
<button id="stt-btn" type="button" aria-label="Voice input" title="Voice input"><i data-lucide="mic"></i></button>
|
|
473
467
|
<button id="schedule-btn" type="button" aria-label="Schedule message" title="Schedule message"><i data-lucide="clock"></i></button>
|
|
474
|
-
<button id="ask-mate-btn" type="button" aria-label="Ask Mate"><
|
|
468
|
+
<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>
|
|
469
|
+
<div id="context-sources-btn-wrap" class="desktop-only">
|
|
470
|
+
<button id="context-sources-add" type="button" title="Add context sources"><i data-lucide="library"></i><span class="ctx-label">Context</span></button>
|
|
471
|
+
<div id="context-sources-picker" class="hidden">
|
|
472
|
+
<div class="context-picker-section" id="context-picker-email"></div>
|
|
473
|
+
<div class="context-picker-section" id="context-picker-terminals"></div>
|
|
474
|
+
<div class="context-picker-section" id="context-picker-tabs"></div>
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
475
477
|
</div>
|
|
476
478
|
<div id="input-bottom-right">
|
|
479
|
+
<div id="vendor-toggle-wrap">
|
|
480
|
+
<button id="vendor-btn-claude" class="vendor-toggle-btn active" data-vendor="claude">
|
|
481
|
+
<img src="/claude-code-avatar.png" class="vendor-toggle-icon" alt="Claude">
|
|
482
|
+
<span class="vendor-toggle-label">Claude Code</span>
|
|
483
|
+
</button>
|
|
484
|
+
<button id="vendor-btn-codex" class="vendor-toggle-btn" data-vendor="codex">
|
|
485
|
+
<img src="/codex-avatar.png" class="vendor-toggle-icon" alt="Codex">
|
|
486
|
+
<span class="vendor-toggle-label">Codex</span>
|
|
487
|
+
</button>
|
|
488
|
+
</div>
|
|
477
489
|
<div id="config-chip-wrap" class="hidden">
|
|
478
490
|
<button id="config-chip" title="Model, mode, and effort settings">
|
|
479
491
|
<i class="config-chip-icon" data-lucide="sliders-horizontal"></i>
|
|
@@ -511,6 +523,18 @@
|
|
|
511
523
|
</button>
|
|
512
524
|
</div>
|
|
513
525
|
</div>
|
|
526
|
+
<div id="config-approval-section" class="config-section" style="display:none">
|
|
527
|
+
<div class="config-section-label">APPROVAL</div>
|
|
528
|
+
<div id="config-approval-bar" class="config-segmented"></div>
|
|
529
|
+
</div>
|
|
530
|
+
<div id="config-sandbox-section" class="config-section" style="display:none">
|
|
531
|
+
<div class="config-section-label">SANDBOX</div>
|
|
532
|
+
<div id="config-sandbox-bar" class="config-segmented"></div>
|
|
533
|
+
</div>
|
|
534
|
+
<div id="config-websearch-section" class="config-section" style="display:none">
|
|
535
|
+
<div class="config-section-label">WEB SEARCH</div>
|
|
536
|
+
<div id="config-websearch-bar" class="config-segmented"></div>
|
|
537
|
+
</div>
|
|
514
538
|
</div>
|
|
515
539
|
</div>
|
|
516
540
|
<button id="send-btn" disabled aria-label="Send"><i data-lucide="arrow-up"></i></button>
|
|
@@ -520,6 +544,34 @@
|
|
|
520
544
|
</div>
|
|
521
545
|
</div>
|
|
522
546
|
</div>
|
|
547
|
+
<!-- Mobile input-more bottom sheet: attach / image + context sources -->
|
|
548
|
+
<div id="input-more-sheet" class="hidden">
|
|
549
|
+
<div class="input-more-backdrop"></div>
|
|
550
|
+
<div class="input-more-content">
|
|
551
|
+
<div class="input-more-handle"></div>
|
|
552
|
+
<div class="input-more-actions">
|
|
553
|
+
<button class="input-more-action" id="input-more-attach">
|
|
554
|
+
<i data-lucide="paperclip"></i>
|
|
555
|
+
<span>Attach file</span>
|
|
556
|
+
</button>
|
|
557
|
+
<button class="input-more-action" id="input-more-image">
|
|
558
|
+
<i data-lucide="image"></i>
|
|
559
|
+
<span>Image</span>
|
|
560
|
+
</button>
|
|
561
|
+
</div>
|
|
562
|
+
<div class="input-more-divider"></div>
|
|
563
|
+
<div class="input-more-section-label">
|
|
564
|
+
<i data-lucide="library"></i>
|
|
565
|
+
<span>Context sources</span>
|
|
566
|
+
</div>
|
|
567
|
+
<div class="input-more-context-body" id="input-more-context-body">
|
|
568
|
+
<div class="context-picker-section" id="context-picker-email-mobile"></div>
|
|
569
|
+
<div class="context-picker-section" id="context-picker-terminals-mobile"></div>
|
|
570
|
+
<div class="context-picker-section" id="context-picker-tabs-mobile"></div>
|
|
571
|
+
</div>
|
|
572
|
+
</div>
|
|
573
|
+
</div>
|
|
574
|
+
|
|
523
575
|
<!-- Mobile fullscreen sheet overlay (Projects / Sessions) -->
|
|
524
576
|
<div id="mobile-sheet" class="hidden">
|
|
525
577
|
<div class="mobile-sheet-backdrop"></div>
|
|
@@ -1,56 +1,158 @@
|
|
|
1
1
|
// app-connection.js - WebSocket connection, reconnect, status
|
|
2
2
|
// Extracted from app.js (PR-22)
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import { store } from './store.js';
|
|
5
|
+
import { getWs, setWs } from './ws-ref.js';
|
|
6
|
+
import { getStatusDot, getSendBtn } from './dom-refs.js';
|
|
7
|
+
import { setSendBtnMode, blinkIO, setActivity } from './app-favicon.js';
|
|
8
|
+
import { startLogoAnimation, stopLogoAnimation } from './ascii-logo.js';
|
|
9
|
+
import { hasSendableContent } from './input.js';
|
|
10
|
+
import { isNotifAlertEnabled } from './notifications.js';
|
|
11
|
+
import { processMessage } from './app-messages.js';
|
|
12
|
+
import { flushPendingExtMessages } from './app-misc.js';
|
|
13
|
+
import { resetTerminals } from './terminal.js';
|
|
14
|
+
import { closeDmUserPicker } from './sidebar-mates.js';
|
|
15
|
+
import { openDm } from './app-dm.js';
|
|
16
|
+
|
|
5
17
|
var wasConnected = false;
|
|
6
18
|
var reconnectTimer = null;
|
|
7
19
|
var reconnectDelay = 1000;
|
|
8
20
|
var connectTimeoutId = null;
|
|
9
21
|
var disconnectNotifTimer = null;
|
|
10
22
|
var disconnectNotifShown = false;
|
|
23
|
+
var connectOverlay = null;
|
|
24
|
+
|
|
25
|
+
export function initConnection() {
|
|
26
|
+
connectOverlay = document.getElementById("connect-overlay");
|
|
11
27
|
|
|
12
|
-
|
|
13
|
-
|
|
28
|
+
// --- Reactive UI sync for connected/processing state ---
|
|
29
|
+
store.subscribe(function (state, prev) {
|
|
30
|
+
// Status dot (depends on both connected and processing)
|
|
31
|
+
if (state.connected !== prev.connected || state.processing !== prev.processing) {
|
|
32
|
+
var dot = getStatusDot();
|
|
33
|
+
if (dot) {
|
|
34
|
+
dot.className = "icon-strip-status";
|
|
35
|
+
if (state.connected) {
|
|
36
|
+
dot.classList.add("connected");
|
|
37
|
+
if (state.processing) dot.classList.add("processing");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Connected state changed
|
|
43
|
+
if (state.connected !== prev.connected) {
|
|
44
|
+
var sendBtn = getSendBtn();
|
|
45
|
+
if (state.connected) {
|
|
46
|
+
if (sendBtn) sendBtn.disabled = false;
|
|
47
|
+
if (connectOverlay) connectOverlay.classList.add("hidden");
|
|
48
|
+
var updPill = document.getElementById("update-pill-wrap");
|
|
49
|
+
if (updPill) updPill.classList.add("hidden");
|
|
50
|
+
stopLogoAnimation();
|
|
51
|
+
} else {
|
|
52
|
+
if (sendBtn) sendBtn.disabled = true;
|
|
53
|
+
if (connectOverlay) connectOverlay.classList.remove("hidden");
|
|
54
|
+
startLogoAnimation();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Processing state changed
|
|
59
|
+
if (state.processing !== prev.processing) {
|
|
60
|
+
if (state.processing) {
|
|
61
|
+
setSendBtnMode(hasSendableContent() ? "send" : "stop");
|
|
62
|
+
} else if (state.connected) {
|
|
63
|
+
setSendBtnMode("send");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
14
67
|
}
|
|
15
68
|
|
|
69
|
+
// setStatus: now just sets state. UI sync is handled by the subscriber above.
|
|
16
70
|
export function setStatus(status) {
|
|
17
|
-
var dot = _ctx.getStatusDot();
|
|
18
|
-
if (dot) dot.className = "icon-strip-status";
|
|
19
71
|
if (status === "connected") {
|
|
20
|
-
|
|
21
|
-
_ctx.setConnected(true);
|
|
22
|
-
_ctx.setProcessing(false);
|
|
23
|
-
_ctx.sendBtn.disabled = false;
|
|
24
|
-
_ctx.setSendBtnMode("send");
|
|
25
|
-
_ctx.connectOverlay.classList.add("hidden");
|
|
26
|
-
// Hide update banner on reconnect; server will re-send update_available if still needed
|
|
27
|
-
var updPill = document.getElementById("update-pill-wrap");
|
|
28
|
-
if (updPill) updPill.classList.add("hidden");
|
|
29
|
-
_ctx.stopVerbCycle();
|
|
72
|
+
store.set({ connected: true, processing: false });
|
|
30
73
|
} else if (status === "processing") {
|
|
31
|
-
|
|
32
|
-
_ctx.setProcessing(true);
|
|
33
|
-
_ctx.setSendBtnMode(_ctx.hasSendableContent() ? "send" : "stop");
|
|
74
|
+
store.set({ processing: true });
|
|
34
75
|
} else {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
76
|
+
store.set({ connected: false, processing: false });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function onConnected() {
|
|
81
|
+
// Flush any extension messages that arrived before WS was ready
|
|
82
|
+
flushPendingExtMessages();
|
|
83
|
+
|
|
84
|
+
// Reset terminal xterm instances (server will send fresh term_list)
|
|
85
|
+
resetTerminals();
|
|
86
|
+
|
|
87
|
+
// Re-send push subscription on reconnect
|
|
88
|
+
var ws = getWs();
|
|
89
|
+
if (window._pushSubscription) {
|
|
90
|
+
try {
|
|
91
|
+
ws.send(JSON.stringify({
|
|
92
|
+
type: "push_subscribe",
|
|
93
|
+
subscription: window._pushSubscription.toJSON(),
|
|
94
|
+
}));
|
|
95
|
+
} catch(e) {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Request mates list
|
|
99
|
+
try {
|
|
100
|
+
ws.send(JSON.stringify({ type: "mate_list" }));
|
|
101
|
+
} catch(e) {}
|
|
102
|
+
|
|
103
|
+
// If connecting to a mate project, request knowledge list for badge
|
|
104
|
+
if (store.get('mateProjectSlug')) {
|
|
105
|
+
try { ws.send(JSON.stringify({ type: "knowledge_list" })); } catch(e) {}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Session restore is now server-driven (user-presence.json).
|
|
109
|
+
// Mate DM restore is also server-driven via "restore_mate_dm" message.
|
|
110
|
+
// Fallback: if server doesn't restore DM within 2s, try localStorage
|
|
111
|
+
var savedDm = null;
|
|
112
|
+
try { savedDm = localStorage.getItem("clay-active-dm"); } catch (e) {}
|
|
113
|
+
if (savedDm && !store.get('dmMode') && !store.get('mateProjectSlug')) {
|
|
114
|
+
var dmFallbackTimer = setTimeout(function () {
|
|
115
|
+
if (!store.get('dmMode') && savedDm) {
|
|
116
|
+
console.log("[dm-restore] Server did not restore DM, using localStorage fallback:", savedDm);
|
|
117
|
+
openDm(savedDm);
|
|
118
|
+
}
|
|
119
|
+
}, 2000);
|
|
120
|
+
// Cancel fallback if server restores DM first
|
|
121
|
+
var patchedOnce = false;
|
|
122
|
+
var checkRestore = function (evt) {
|
|
123
|
+
try {
|
|
124
|
+
var d = JSON.parse(evt.data);
|
|
125
|
+
if (d.type === "restore_mate_dm" && !patchedOnce) {
|
|
126
|
+
patchedOnce = true;
|
|
127
|
+
clearTimeout(dmFallbackTimer);
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {}
|
|
130
|
+
};
|
|
131
|
+
ws.addEventListener("message", checkRestore);
|
|
132
|
+
setTimeout(function () { ws.removeEventListener("message", checkRestore); }, 3000);
|
|
133
|
+
}
|
|
134
|
+
// Safety: clear returningFromMateDm after initial messages settle
|
|
135
|
+
if (store.get('returningFromMateDm')) {
|
|
136
|
+
setTimeout(function () {
|
|
137
|
+
if (store.get('returningFromMateDm')) {
|
|
138
|
+
store.set({ returningFromMateDm: false });
|
|
139
|
+
}
|
|
140
|
+
}, 2000);
|
|
39
141
|
}
|
|
40
142
|
}
|
|
41
143
|
|
|
42
144
|
export function connect() {
|
|
43
|
-
var ws =
|
|
145
|
+
var ws = getWs();
|
|
44
146
|
if (ws) { ws.onclose = null; ws.close(); }
|
|
45
147
|
if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; }
|
|
46
148
|
|
|
47
149
|
var protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
|
48
|
-
var newWs = new WebSocket(protocol + "//" + location.host +
|
|
49
|
-
|
|
150
|
+
var newWs = new WebSocket(protocol + "//" + location.host + store.get('wsPath'));
|
|
151
|
+
setWs(newWs);
|
|
50
152
|
|
|
51
153
|
// If not connected within 3s, force retry
|
|
52
154
|
connectTimeoutId = setTimeout(function () {
|
|
53
|
-
if (!
|
|
155
|
+
if (!store.get('connected')) {
|
|
54
156
|
newWs.onclose = null;
|
|
55
157
|
newWs.onerror = null;
|
|
56
158
|
newWs.close();
|
|
@@ -68,7 +170,7 @@ export function connect() {
|
|
|
68
170
|
// Only show "restored" notification if "lost" was actually shown
|
|
69
171
|
var isMobileDevice = /Mobi|Android|iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
70
172
|
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
|
|
71
|
-
if (wasConnected && disconnectNotifShown && !isMobileDevice &&
|
|
173
|
+
if (wasConnected && disconnectNotifShown && !isMobileDevice && isNotifAlertEnabled() && !document.hasFocus() && "serviceWorker" in navigator && Notification.permission === "granted") {
|
|
72
174
|
navigator.serviceWorker.ready.then(function (reg) {
|
|
73
175
|
return reg.showNotification("Clay", {
|
|
74
176
|
body: "Server connection restored",
|
|
@@ -83,22 +185,21 @@ export function connect() {
|
|
|
83
185
|
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
|
|
84
186
|
|
|
85
187
|
// Wrap ws.send to blink LED on outgoing traffic
|
|
86
|
-
var currentWs =
|
|
188
|
+
var currentWs = getWs();
|
|
87
189
|
var _origSend = currentWs.send.bind(currentWs);
|
|
88
190
|
currentWs.send = function (data) {
|
|
89
|
-
|
|
191
|
+
blinkIO();
|
|
90
192
|
return _origSend(data);
|
|
91
193
|
};
|
|
92
194
|
|
|
93
|
-
|
|
195
|
+
onConnected();
|
|
94
196
|
};
|
|
95
197
|
|
|
96
198
|
newWs.onclose = function (e) {
|
|
97
199
|
if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; }
|
|
98
|
-
|
|
200
|
+
closeDmUserPicker();
|
|
99
201
|
setStatus("disconnected");
|
|
100
|
-
|
|
101
|
-
_ctx.setActivity(null);
|
|
202
|
+
setActivity(null);
|
|
102
203
|
// Delay "connection lost" notification by 5s to suppress brief disconnects
|
|
103
204
|
if (!disconnectNotifTimer) {
|
|
104
205
|
disconnectNotifTimer = setTimeout(function () {
|
|
@@ -106,7 +207,7 @@ export function connect() {
|
|
|
106
207
|
disconnectNotifShown = true;
|
|
107
208
|
var isMobileDevice = /Mobi|Android|iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
108
209
|
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
|
|
109
|
-
if (!isMobileDevice &&
|
|
210
|
+
if (!isMobileDevice && isNotifAlertEnabled() && !document.hasFocus() && "serviceWorker" in navigator && Notification.permission === "granted") {
|
|
110
211
|
navigator.serviceWorker.ready.then(function (reg) {
|
|
111
212
|
return reg.showNotification("Clay", {
|
|
112
213
|
body: "Server connection lost",
|
|
@@ -123,16 +224,16 @@ export function connect() {
|
|
|
123
224
|
|
|
124
225
|
newWs.onmessage = function (event) {
|
|
125
226
|
// Backup: if we're receiving messages, we're connected
|
|
126
|
-
if (!
|
|
227
|
+
if (!store.get('connected')) {
|
|
127
228
|
setStatus("connected");
|
|
128
229
|
reconnectDelay = 1000;
|
|
129
230
|
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
|
|
130
231
|
}
|
|
131
232
|
|
|
132
|
-
|
|
233
|
+
blinkIO();
|
|
133
234
|
var msg;
|
|
134
235
|
try { msg = JSON.parse(event.data); } catch (e) { return; }
|
|
135
|
-
|
|
236
|
+
processMessage(msg);
|
|
136
237
|
};
|
|
137
238
|
}
|
|
138
239
|
|