clay-server 2.23.1 → 2.24.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/build-user-env.js +6 -0
- package/lib/daemon.js +13 -0
- package/lib/ipv4-only.js +39 -0
- package/lib/project.js +333 -42
- package/lib/public/app.js +119 -69
- package/lib/public/claude-code-avatar.png +0 -0
- package/lib/public/css/debate.css +35 -1
- package/lib/public/css/filebrowser.css +2 -1
- package/lib/public/css/icon-strip.css +23 -0
- package/lib/public/css/input.css +66 -0
- package/lib/public/css/loop.css +0 -2
- package/lib/public/css/mates.css +113 -6
- package/lib/public/css/mention.css +26 -1
- package/lib/public/css/messages.css +97 -0
- package/lib/public/css/overlays.css +0 -4
- package/lib/public/css/server-settings.css +53 -0
- package/lib/public/css/session-search.css +1 -1
- package/lib/public/css/sidebar.css +26 -2
- package/lib/public/index.html +53 -13
- package/lib/public/modules/debate.js +158 -1
- package/lib/public/modules/filebrowser.js +11 -0
- package/lib/public/modules/input.js +20 -2
- package/lib/public/modules/markdown.js +2 -2
- package/lib/public/modules/mention.js +82 -32
- package/lib/public/modules/notifications.js +5 -1
- package/lib/public/modules/session-search.js +5 -5
- package/lib/public/modules/sidebar.js +39 -26
- package/lib/public/modules/theme.js +30 -0
- package/lib/public/modules/user-settings.js +61 -12
- package/lib/sdk-bridge.js +83 -78
- package/lib/sdk-worker.js +83 -3
- package/lib/server.js +93 -3
- package/lib/session-search.js +40 -5
- package/lib/sessions.js +2 -2
- package/lib/users.js +38 -0
- package/package.json +1 -1
|
@@ -1759,4 +1759,101 @@ pre.mermaid-error {
|
|
|
1759
1759
|
}
|
|
1760
1760
|
}
|
|
1761
1761
|
|
|
1762
|
+
/* ==========================================================================
|
|
1763
|
+
Wide View Mode
|
|
1764
|
+
========================================================================== */
|
|
1765
|
+
|
|
1766
|
+
body.wide-view {
|
|
1767
|
+
--content-width: 100%;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
body.wide-view .msg-user,
|
|
1771
|
+
body.wide-view .msg-assistant {
|
|
1772
|
+
display: flex;
|
|
1773
|
+
flex-direction: row;
|
|
1774
|
+
align-items: flex-start;
|
|
1775
|
+
gap: 8px;
|
|
1776
|
+
max-width: 100%;
|
|
1777
|
+
padding: 4px 16px;
|
|
1778
|
+
margin: 0;
|
|
1779
|
+
border-radius: 0;
|
|
1780
|
+
}
|
|
1781
|
+
body.wide-view .msg-user:hover,
|
|
1782
|
+
body.wide-view .msg-assistant:hover {
|
|
1783
|
+
background: var(--bg-alt);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
body.wide-view .msg-user {
|
|
1787
|
+
justify-content: flex-start;
|
|
1788
|
+
}
|
|
1789
|
+
body.wide-view .msg-user .dm-bubble-avatar-me {
|
|
1790
|
+
order: -1;
|
|
1791
|
+
}
|
|
1792
|
+
body.wide-view .msg-user .bubble {
|
|
1793
|
+
background: none;
|
|
1794
|
+
border-radius: 0;
|
|
1795
|
+
padding: 0;
|
|
1796
|
+
max-width: 100%;
|
|
1797
|
+
font-size: 15px;
|
|
1798
|
+
line-height: 1.46;
|
|
1799
|
+
white-space: pre-wrap;
|
|
1800
|
+
word-wrap: break-word;
|
|
1801
|
+
}
|
|
1802
|
+
body.wide-view .msg-user .msg-actions {
|
|
1803
|
+
display: none;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
body.wide-view .msg-assistant .md-content {
|
|
1807
|
+
background: none;
|
|
1808
|
+
border-radius: 0;
|
|
1809
|
+
padding: 0;
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
body.wide-view .thinking-item,
|
|
1813
|
+
body.wide-view .tool-item,
|
|
1814
|
+
body.wide-view .tool-group {
|
|
1815
|
+
max-width: 100%;
|
|
1816
|
+
}
|
|
1817
|
+
body.wide-view .turn-meta {
|
|
1818
|
+
max-width: 100%;
|
|
1819
|
+
}
|
|
1762
1820
|
|
|
1821
|
+
/* Mobile: force bubble layout regardless of user setting */
|
|
1822
|
+
@media (max-width: 768px) {
|
|
1823
|
+
body.wide-view { --content-width: var(--content-width, 720px); }
|
|
1824
|
+
body.wide-view .msg-user,
|
|
1825
|
+
body.wide-view .msg-assistant {
|
|
1826
|
+
display: revert;
|
|
1827
|
+
flex-direction: revert;
|
|
1828
|
+
align-items: revert;
|
|
1829
|
+
gap: revert;
|
|
1830
|
+
max-width: revert;
|
|
1831
|
+
padding: revert;
|
|
1832
|
+
margin: revert;
|
|
1833
|
+
border-radius: revert;
|
|
1834
|
+
}
|
|
1835
|
+
body.wide-view .msg-user:hover,
|
|
1836
|
+
body.wide-view .msg-assistant:hover { background: revert; }
|
|
1837
|
+
body.wide-view .msg-user { justify-content: revert; }
|
|
1838
|
+
body.wide-view .msg-user .dm-bubble-avatar-me { order: revert; }
|
|
1839
|
+
body.wide-view .msg-user .bubble {
|
|
1840
|
+
background: revert;
|
|
1841
|
+
border-radius: revert;
|
|
1842
|
+
padding: revert;
|
|
1843
|
+
max-width: revert;
|
|
1844
|
+
font-size: revert;
|
|
1845
|
+
line-height: revert;
|
|
1846
|
+
white-space: revert;
|
|
1847
|
+
word-wrap: revert;
|
|
1848
|
+
}
|
|
1849
|
+
body.wide-view .msg-user .msg-actions { display: revert; }
|
|
1850
|
+
body.wide-view .msg-assistant .md-content {
|
|
1851
|
+
background: revert;
|
|
1852
|
+
border-radius: revert;
|
|
1853
|
+
padding: revert;
|
|
1854
|
+
}
|
|
1855
|
+
body.wide-view .thinking-item,
|
|
1856
|
+
body.wide-view .tool-item,
|
|
1857
|
+
body.wide-view .tool-group { max-width: revert; }
|
|
1858
|
+
body.wide-view .turn-meta { max-width: revert; }
|
|
1859
|
+
}
|
|
@@ -140,8 +140,6 @@ button.top-bar-pill.pill-accent:hover { background: color-mix(in srgb, var(--acc
|
|
|
140
140
|
position: absolute;
|
|
141
141
|
inset: 0;
|
|
142
142
|
background: rgba(0,0,0,0.55);
|
|
143
|
-
backdrop-filter: blur(8px);
|
|
144
|
-
-webkit-backdrop-filter: blur(8px);
|
|
145
143
|
}
|
|
146
144
|
|
|
147
145
|
.pwa-modal-card {
|
|
@@ -398,8 +396,6 @@ button.top-bar-pill.pill-accent:hover { background: color-mix(in srgb, var(--acc
|
|
|
398
396
|
position: absolute;
|
|
399
397
|
inset: 0;
|
|
400
398
|
background: rgba(var(--shadow-rgb), 0.5);
|
|
401
|
-
backdrop-filter: blur(2px);
|
|
402
|
-
-webkit-backdrop-filter: blur(2px);
|
|
403
399
|
}
|
|
404
400
|
|
|
405
401
|
.confirm-dialog {
|
|
@@ -233,6 +233,9 @@
|
|
|
233
233
|
padding: 4px 0;
|
|
234
234
|
margin-bottom: 16px;
|
|
235
235
|
}
|
|
236
|
+
.settings-card:has(.layout-switcher) {
|
|
237
|
+
padding: 14px 16px;
|
|
238
|
+
}
|
|
236
239
|
|
|
237
240
|
.settings-field {
|
|
238
241
|
padding: 12px 16px;
|
|
@@ -853,3 +856,53 @@
|
|
|
853
856
|
outline: none;
|
|
854
857
|
cursor: pointer;
|
|
855
858
|
}
|
|
859
|
+
|
|
860
|
+
/* --- Layout switcher (Bubble / Channel) --- */
|
|
861
|
+
.layout-switcher {
|
|
862
|
+
display: grid;
|
|
863
|
+
grid-template-columns: 1fr 1fr;
|
|
864
|
+
gap: 12px;
|
|
865
|
+
padding: 4px 0;
|
|
866
|
+
}
|
|
867
|
+
.layout-option {
|
|
868
|
+
display: flex;
|
|
869
|
+
flex-direction: column;
|
|
870
|
+
align-items: center;
|
|
871
|
+
gap: 8px;
|
|
872
|
+
padding: 22px 16px;
|
|
873
|
+
background: var(--bg);
|
|
874
|
+
border: 2px solid var(--border);
|
|
875
|
+
border-radius: 10px;
|
|
876
|
+
cursor: pointer;
|
|
877
|
+
transition: border-color 0.15s, background 0.15s, transform 0.1s;
|
|
878
|
+
color: var(--text);
|
|
879
|
+
font-family: inherit;
|
|
880
|
+
text-align: center;
|
|
881
|
+
}
|
|
882
|
+
.layout-option:hover {
|
|
883
|
+
border-color: var(--text-dimmer);
|
|
884
|
+
background: var(--bg-alt);
|
|
885
|
+
}
|
|
886
|
+
.layout-option:active {
|
|
887
|
+
transform: scale(0.97);
|
|
888
|
+
}
|
|
889
|
+
.layout-option.selected {
|
|
890
|
+
border-color: var(--accent);
|
|
891
|
+
background: var(--accent-8);
|
|
892
|
+
}
|
|
893
|
+
.layout-option-icon {
|
|
894
|
+
font-size: 28px;
|
|
895
|
+
line-height: 1;
|
|
896
|
+
margin-bottom: 2px;
|
|
897
|
+
}
|
|
898
|
+
.layout-option-label {
|
|
899
|
+
font-weight: 700;
|
|
900
|
+
font-size: 14px;
|
|
901
|
+
color: var(--text);
|
|
902
|
+
}
|
|
903
|
+
.layout-option-desc {
|
|
904
|
+
font-size: 11.5px;
|
|
905
|
+
color: var(--text-muted);
|
|
906
|
+
line-height: 1.45;
|
|
907
|
+
margin-top: 2px;
|
|
908
|
+
}
|
|
@@ -1146,8 +1146,6 @@
|
|
|
1146
1146
|
inset: 0;
|
|
1147
1147
|
background: rgba(var(--shadow-rgb), 0.6);
|
|
1148
1148
|
z-index: 99;
|
|
1149
|
-
backdrop-filter: blur(2px);
|
|
1150
|
-
-webkit-backdrop-filter: blur(2px);
|
|
1151
1149
|
}
|
|
1152
1150
|
|
|
1153
1151
|
#sidebar-overlay.visible { display: block; }
|
|
@@ -1350,3 +1348,29 @@
|
|
|
1350
1348
|
}
|
|
1351
1349
|
}
|
|
1352
1350
|
|
|
1351
|
+
/* --- Skeleton loading placeholders --- */
|
|
1352
|
+
.skeleton-session-group {
|
|
1353
|
+
padding: 8px 12px;
|
|
1354
|
+
}
|
|
1355
|
+
.skeleton-session-label {
|
|
1356
|
+
width: 48px;
|
|
1357
|
+
height: 10px;
|
|
1358
|
+
border-radius: 4px;
|
|
1359
|
+
background: var(--text-muted, #888);
|
|
1360
|
+
margin-bottom: 10px;
|
|
1361
|
+
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
|
1362
|
+
}
|
|
1363
|
+
.skeleton-session-item {
|
|
1364
|
+
width: 100%;
|
|
1365
|
+
height: 28px;
|
|
1366
|
+
border-radius: 8px;
|
|
1367
|
+
background: var(--text-muted, #888);
|
|
1368
|
+
margin-bottom: 6px;
|
|
1369
|
+
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
|
1370
|
+
}
|
|
1371
|
+
@keyframes skeleton-shimmer {
|
|
1372
|
+
0% { opacity: 0.1; }
|
|
1373
|
+
50% { opacity: 0.18; }
|
|
1374
|
+
100% { opacity: 0.1; }
|
|
1375
|
+
}
|
|
1376
|
+
|
package/lib/public/index.html
CHANGED
|
@@ -59,9 +59,16 @@
|
|
|
59
59
|
<img class="icon-strip-logo" src="icon-banded-76.png" width="38" height="38" alt="Clay">
|
|
60
60
|
</div>
|
|
61
61
|
<div class="icon-strip-separator"></div>
|
|
62
|
-
<div class="icon-strip-projects" id="icon-strip-projects"
|
|
62
|
+
<div class="icon-strip-projects" id="icon-strip-projects">
|
|
63
|
+
<div class="skeleton-icon-strip-item"></div>
|
|
64
|
+
<div class="skeleton-icon-strip-item"></div>
|
|
65
|
+
</div>
|
|
63
66
|
<button class="icon-strip-add" id="icon-strip-add" title="Add project"><i data-lucide="plus"></i></button>
|
|
64
|
-
<div class="icon-strip-users
|
|
67
|
+
<div class="icon-strip-users" id="icon-strip-users">
|
|
68
|
+
<div class="skeleton-icon-strip-user"></div>
|
|
69
|
+
<div class="skeleton-icon-strip-user"></div>
|
|
70
|
+
<div class="skeleton-icon-strip-user"></div>
|
|
71
|
+
</div>
|
|
65
72
|
<div class="icon-strip-me" id="icon-strip-me"></div>
|
|
66
73
|
</div>
|
|
67
74
|
|
|
@@ -179,7 +186,14 @@
|
|
|
179
186
|
<div id="project-list"></div>
|
|
180
187
|
</div>
|
|
181
188
|
<div id="sidebar-panel-sessions" class="sidebar-panel">
|
|
182
|
-
<div id="session-list"
|
|
189
|
+
<div id="session-list">
|
|
190
|
+
<div class="skeleton-session-group">
|
|
191
|
+
<div class="skeleton-session-label"></div>
|
|
192
|
+
<div class="skeleton-session-item"></div>
|
|
193
|
+
<div class="skeleton-session-item"></div>
|
|
194
|
+
<div class="skeleton-session-item"></div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
183
197
|
</div>
|
|
184
198
|
<div id="sidebar-panel-files" class="sidebar-panel hidden">
|
|
185
199
|
<div id="file-tree"></div>
|
|
@@ -203,7 +217,7 @@
|
|
|
203
217
|
<button id="mate-sticky-notes-btn"><i data-lucide="sticky-note"></i> <span>Sticky Notes</span></button>
|
|
204
218
|
<button id="mate-skills-btn"><i data-lucide="puzzle"></i> <span>Skills</span></button>
|
|
205
219
|
<button id="mate-scheduler-btn"><i data-lucide="calendar-clock"></i> <span>Scheduled Tasks</span></button>
|
|
206
|
-
<button id="mate-debate-btn"><i data-lucide="mic"></i> <span>
|
|
220
|
+
<button id="mate-debate-btn"><i data-lucide="mic"></i> <span>Debate</span></button>
|
|
207
221
|
</div>
|
|
208
222
|
<div id="mate-sidebar-conversations">
|
|
209
223
|
<div class="mate-sidebar-sessions-header">
|
|
@@ -288,6 +302,7 @@
|
|
|
288
302
|
<div id="ralph-sticky" class="hidden"></div>
|
|
289
303
|
<div id="debate-sticky" class="hidden"></div>
|
|
290
304
|
<div class="status">
|
|
305
|
+
<button id="debate-pdf-btn" class="hidden" title="Export debate as PDF"><i data-lucide="download"></i></button>
|
|
291
306
|
<button id="find-in-session-btn" title="Search in session (Ctrl+F)"><i data-lucide="search"></i></button>
|
|
292
307
|
<button id="sticky-notes-toggle-btn" title="Sticky notes"><i data-lucide="sticky-note"></i><span class="sticky-notes-count hidden"></span></button>
|
|
293
308
|
<button id="terminal-toggle-btn" title="Terminal"><i data-lucide="square-terminal"></i><span id="terminal-count" class="hidden"></span></button>
|
|
@@ -399,6 +414,7 @@
|
|
|
399
414
|
<button id="attach-image-btn" type="button" aria-label="Attach image" title="Attach image"><i data-lucide="image"></i></button>
|
|
400
415
|
<button id="stt-btn" type="button" aria-label="Voice input" title="Voice input"><i data-lucide="mic"></i></button>
|
|
401
416
|
<button id="schedule-btn" type="button" aria-label="Schedule message" title="Schedule message"><i data-lucide="clock"></i></button>
|
|
417
|
+
<button id="ask-mate-btn" type="button" aria-label="Ask Mate"><span class="ask-mate-label">@ Ask Mate</span></button>
|
|
402
418
|
</div>
|
|
403
419
|
<div id="input-bottom-right">
|
|
404
420
|
<div id="config-chip-wrap" class="hidden">
|
|
@@ -475,6 +491,7 @@
|
|
|
475
491
|
<button class="file-viewer-btn hidden" id="file-viewer-render" title="Toggle rendered view"><i data-lucide="book-open"></i></button>
|
|
476
492
|
<button class="file-viewer-btn hidden" id="file-viewer-pdf" title="Export PDF"><i data-lucide="file-down"></i></button>
|
|
477
493
|
<button class="file-viewer-btn hidden" id="file-viewer-history" title="Edit history"><i data-lucide="clock"></i></button>
|
|
494
|
+
<button class="file-viewer-btn" id="file-viewer-refresh" title="Refresh"><i data-lucide="refresh-cw"></i></button>
|
|
478
495
|
<button class="file-viewer-btn" id="file-viewer-copy" title="Copy contents"><i data-lucide="copy"></i></button>
|
|
479
496
|
<button class="file-viewer-btn" id="file-viewer-fullscreen" title="Toggle fullscreen"><i data-lucide="maximize-2"></i></button>
|
|
480
497
|
<button class="file-viewer-btn" id="file-viewer-close" title="Close"><i data-lucide="x"></i></button>
|
|
@@ -825,14 +842,34 @@
|
|
|
825
842
|
<div class="us-section" data-section="us-appearance">
|
|
826
843
|
<h2>Appearance</h2>
|
|
827
844
|
<div class="settings-card">
|
|
828
|
-
<
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
<
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
845
|
+
<div class="settings-label" style="margin-bottom:10px;">Theme</div>
|
|
846
|
+
<div class="layout-switcher" id="us-theme-switcher">
|
|
847
|
+
<button class="layout-option" data-theme="light">
|
|
848
|
+
<span class="layout-option-icon">☀️</span>
|
|
849
|
+
<span class="layout-option-label">Light</span>
|
|
850
|
+
<span class="layout-option-desc">For people who open curtains. Bright, clean, productive.</span>
|
|
851
|
+
</button>
|
|
852
|
+
<button class="layout-option" data-theme="dark">
|
|
853
|
+
<span class="layout-option-icon">🌙</span>
|
|
854
|
+
<span class="layout-option-label">Dark</span>
|
|
855
|
+
<span class="layout-option-desc">For those who thrive after sunset. Your screen, your cave.</span>
|
|
856
|
+
</button>
|
|
857
|
+
</div>
|
|
858
|
+
</div>
|
|
859
|
+
<div class="settings-card">
|
|
860
|
+
<div class="settings-label" style="margin-bottom:10px;">Chat layout</div>
|
|
861
|
+
<div class="layout-switcher" id="us-layout-switcher">
|
|
862
|
+
<button class="layout-option" data-layout="bubble">
|
|
863
|
+
<span class="layout-option-icon">💬</span>
|
|
864
|
+
<span class="layout-option-label">Bubble</span>
|
|
865
|
+
<span class="layout-option-desc">Centered and quiet. Just you and the AI, no distractions.</span>
|
|
866
|
+
</button>
|
|
867
|
+
<button class="layout-option" data-layout="channel">
|
|
868
|
+
<span class="layout-option-icon">💻</span>
|
|
869
|
+
<span class="layout-option-label">Channel</span>
|
|
870
|
+
<span class="layout-option-desc">Full-width with faces. Feels like your team is right there.</span>
|
|
871
|
+
</button>
|
|
872
|
+
</div>
|
|
836
873
|
</div>
|
|
837
874
|
</div>
|
|
838
875
|
|
|
@@ -1244,7 +1281,10 @@
|
|
|
1244
1281
|
<div class="confirm-dialog paste-modal-dialog">
|
|
1245
1282
|
<div class="paste-modal-header">
|
|
1246
1283
|
<span class="paste-modal-title">Pasted content</span>
|
|
1247
|
-
<
|
|
1284
|
+
<div class="paste-modal-actions">
|
|
1285
|
+
<button class="paste-modal-copy" title="Copy to clipboard"><i data-lucide="copy" style="width:15px;height:15px"></i></button>
|
|
1286
|
+
<button class="paste-modal-close"><i data-lucide="x" style="width:16px;height:16px"></i></button>
|
|
1287
|
+
</div>
|
|
1248
1288
|
</div>
|
|
1249
1289
|
<pre class="paste-modal-body" id="paste-modal-body"></pre>
|
|
1250
1290
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mateAvatarUrl } from './avatar.js';
|
|
2
|
-
import { renderMarkdown, highlightCodeBlocks } from './markdown.js';
|
|
2
|
+
import { renderMarkdown, highlightCodeBlocks, buildPrintHtml, getPrintCss } from './markdown.js';
|
|
3
3
|
import { escapeHtml } from './utils.js';
|
|
4
4
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
5
5
|
|
|
@@ -21,6 +21,19 @@ var turnDrainTimer = null;
|
|
|
21
21
|
// --- Init ---
|
|
22
22
|
export function initDebate(_ctx) {
|
|
23
23
|
ctx = _ctx;
|
|
24
|
+
|
|
25
|
+
var pdfBtn = document.getElementById("debate-pdf-btn");
|
|
26
|
+
if (pdfBtn) {
|
|
27
|
+
pdfBtn.addEventListener("click", function () {
|
|
28
|
+
pdfBtn.disabled = true;
|
|
29
|
+
exportDebateAsPdf().then(function () {
|
|
30
|
+
pdfBtn.disabled = false;
|
|
31
|
+
}).catch(function (err) {
|
|
32
|
+
pdfBtn.disabled = false;
|
|
33
|
+
console.error("Debate PDF export failed:", err);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
24
37
|
}
|
|
25
38
|
|
|
26
39
|
export function resetDebateState() {
|
|
@@ -31,6 +44,7 @@ export function resetDebateState() {
|
|
|
31
44
|
flushTurnStream();
|
|
32
45
|
currentTurnEl = null;
|
|
33
46
|
currentTurnMateId = null;
|
|
47
|
+
showPdfBtn(false);
|
|
34
48
|
// Remove preparing indicator if present
|
|
35
49
|
if (ctx && ctx.messagesEl) {
|
|
36
50
|
var prep = ctx.messagesEl.querySelector(".debate-preparing-indicator");
|
|
@@ -70,6 +84,14 @@ function showDebateInfoFloat(msg) {
|
|
|
70
84
|
refreshIcons();
|
|
71
85
|
}
|
|
72
86
|
|
|
87
|
+
function showPdfBtn(visible) {
|
|
88
|
+
var btn = document.getElementById("debate-pdf-btn");
|
|
89
|
+
if (btn) {
|
|
90
|
+
if (visible) btn.classList.remove("hidden");
|
|
91
|
+
else btn.classList.add("hidden");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
73
95
|
function hideDebateInfoFloat() {
|
|
74
96
|
var floatEl = document.getElementById("debate-info-float");
|
|
75
97
|
if (floatEl) {
|
|
@@ -88,6 +110,7 @@ export function handleDebateResumed(msg) {
|
|
|
88
110
|
|
|
89
111
|
// Show float info panel again if we have it
|
|
90
112
|
showDebateInfoFloat(msg);
|
|
113
|
+
showPdfBtn(true);
|
|
91
114
|
}
|
|
92
115
|
|
|
93
116
|
export function handleDebatePreparing(msg) {
|
|
@@ -134,6 +157,7 @@ export function handleDebateStarted(msg) {
|
|
|
134
157
|
|
|
135
158
|
// Show float info panel
|
|
136
159
|
showDebateInfoFloat(msg);
|
|
160
|
+
showPdfBtn(true);
|
|
137
161
|
|
|
138
162
|
if (ctx.scrollToBottom) ctx.scrollToBottom();
|
|
139
163
|
}
|
|
@@ -393,6 +417,20 @@ function renderEndedBanner(entry) {
|
|
|
393
417
|
});
|
|
394
418
|
resumeRow.appendChild(resumeBtn);
|
|
395
419
|
|
|
420
|
+
var pdfBtn = document.createElement("button");
|
|
421
|
+
pdfBtn.className = "debate-ended-resume-btn debate-ended-pdf-btn";
|
|
422
|
+
pdfBtn.innerHTML = iconHtml("download") + " PDF";
|
|
423
|
+
pdfBtn.addEventListener("click", function () {
|
|
424
|
+
pdfBtn.disabled = true;
|
|
425
|
+
exportDebateAsPdf().then(function () {
|
|
426
|
+
pdfBtn.disabled = false;
|
|
427
|
+
}).catch(function (err) {
|
|
428
|
+
pdfBtn.disabled = false;
|
|
429
|
+
console.error("Debate PDF export failed:", err);
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
resumeRow.appendChild(pdfBtn);
|
|
433
|
+
|
|
396
434
|
endBanner.appendChild(resumeRow);
|
|
397
435
|
|
|
398
436
|
// Enter in textarea = resume
|
|
@@ -407,6 +445,125 @@ function renderEndedBanner(entry) {
|
|
|
407
445
|
refreshIcons();
|
|
408
446
|
}
|
|
409
447
|
|
|
448
|
+
function exportDebateAsPdf() {
|
|
449
|
+
var popup = window.open("", "_blank", "width=900,height=700");
|
|
450
|
+
if (!popup) {
|
|
451
|
+
return Promise.resolve();
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
popup.document.write("<!DOCTYPE html><html><head><title>Preparing PDF\u2026</title></head><body><p style=\"font-family:sans-serif;padding:32px;color:#555\">Preparing PDF, please wait\u2026</p></body></html>");
|
|
455
|
+
popup.document.close();
|
|
456
|
+
|
|
457
|
+
// Build debate content HTML from DOM
|
|
458
|
+
var contentParts = [];
|
|
459
|
+
var topic = debateTopic || "Debate";
|
|
460
|
+
|
|
461
|
+
// Title
|
|
462
|
+
contentParts.push("<h1>" + escapeHtml(topic) + "</h1>");
|
|
463
|
+
|
|
464
|
+
// Metadata from info float
|
|
465
|
+
var floatEl = document.getElementById("debate-info-float");
|
|
466
|
+
if (floatEl) {
|
|
467
|
+
var modEl = floatEl.querySelector(".debate-info-mod");
|
|
468
|
+
var chips = floatEl.querySelectorAll(".debate-info-chip");
|
|
469
|
+
var metaParts = [];
|
|
470
|
+
if (modEl) metaParts.push("<strong>Moderator:</strong> " + escapeHtml(modEl.textContent.trim()));
|
|
471
|
+
if (chips.length > 0) {
|
|
472
|
+
var panelNames = [];
|
|
473
|
+
for (var c = 0; c < chips.length; c++) {
|
|
474
|
+
panelNames.push(escapeHtml(chips[c].textContent.trim()));
|
|
475
|
+
}
|
|
476
|
+
metaParts.push("<strong>Panel:</strong> " + panelNames.join(", "));
|
|
477
|
+
}
|
|
478
|
+
if (metaParts.length > 0) {
|
|
479
|
+
contentParts.push('<p class="debate-pdf-meta">' + metaParts.join(" | ") + "</p>");
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
contentParts.push("<hr>");
|
|
484
|
+
|
|
485
|
+
// Collect turns and user comments from DOM
|
|
486
|
+
var els = ctx.messagesEl.querySelectorAll(".debate-turn, .debate-user-comment");
|
|
487
|
+
for (var i = 0; i < els.length; i++) {
|
|
488
|
+
var el = els[i];
|
|
489
|
+
if (el.classList.contains("debate-turn")) {
|
|
490
|
+
var nameEl = el.querySelector(".debate-speaker-name");
|
|
491
|
+
var roleEl = el.querySelector(".debate-speaker-role");
|
|
492
|
+
var avatarEl = el.querySelector(".debate-speaker-avatar");
|
|
493
|
+
var contentEl = el.querySelector(".debate-turn-content");
|
|
494
|
+
var speaker = nameEl ? nameEl.textContent.trim() : "Speaker";
|
|
495
|
+
var role = roleEl ? roleEl.textContent.trim() : "";
|
|
496
|
+
var avatarHtml = "";
|
|
497
|
+
if (avatarEl && avatarEl.src) {
|
|
498
|
+
avatarHtml = '<img class="debate-pdf-avatar" src="' + avatarEl.src + '" width="22" height="22" /> ';
|
|
499
|
+
}
|
|
500
|
+
var heading = avatarHtml + escapeHtml(speaker);
|
|
501
|
+
if (role) heading += ' <span class="debate-pdf-role">(' + escapeHtml(role) + ')</span>';
|
|
502
|
+
contentParts.push('<h2 class="debate-pdf-speaker">' + heading + "</h2>");
|
|
503
|
+
if (contentEl) {
|
|
504
|
+
var clone = contentEl.cloneNode(true);
|
|
505
|
+
// Remove copy buttons from code blocks
|
|
506
|
+
var copyBtns = clone.querySelectorAll(".code-copy-btn");
|
|
507
|
+
for (var k = 0; k < copyBtns.length; k++) copyBtns[k].remove();
|
|
508
|
+
contentParts.push(clone.innerHTML);
|
|
509
|
+
}
|
|
510
|
+
} else if (el.classList.contains("debate-user-comment")) {
|
|
511
|
+
var textEl = el.querySelector(".debate-comment-text");
|
|
512
|
+
var commentText = textEl ? escapeHtml(textEl.textContent.trim()) : "";
|
|
513
|
+
if (commentText) {
|
|
514
|
+
contentParts.push('<blockquote><strong>You:</strong> ' + commentText + '</blockquote>');
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
var contentHtml = contentParts.join("\n");
|
|
520
|
+
var baseCss = getPrintCss();
|
|
521
|
+
var debateCss = [
|
|
522
|
+
".debate-pdf-meta { color: #6b6860; font-size: 10pt; margin: 4pt 0 8pt; }",
|
|
523
|
+
".debate-pdf-speaker { display: flex; align-items: center; gap: 6pt; }",
|
|
524
|
+
".debate-pdf-avatar { width: 22pt; height: 22pt; border-radius: 50%; flex-shrink: 0; }",
|
|
525
|
+
".debate-pdf-role { color: #6b6860; font-weight: 400; font-size: 11pt; }",
|
|
526
|
+
].join("\n");
|
|
527
|
+
|
|
528
|
+
var fullHtml = "<!DOCTYPE html>\n" +
|
|
529
|
+
"<html lang=\"ko\"><head>\n" +
|
|
530
|
+
"<meta charset=\"UTF-8\">\n" +
|
|
531
|
+
"<title>" + escapeHtml(topic) + "</title>\n" +
|
|
532
|
+
"<link rel=\"preconnect\" href=\"https://cdn.jsdelivr.net\">\n" +
|
|
533
|
+
"<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css\">\n" +
|
|
534
|
+
"<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n" +
|
|
535
|
+
"<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n" +
|
|
536
|
+
"<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,500;1,400&display=swap\">\n" +
|
|
537
|
+
"<style>\n" + baseCss + "\n" + debateCss + "\n</style>\n" +
|
|
538
|
+
"</head><body>\n" +
|
|
539
|
+
"<div class=\"file-viewer-markdown\">" + contentHtml + "</div>\n" +
|
|
540
|
+
"</body></html>";
|
|
541
|
+
|
|
542
|
+
return new Promise(function (resolve, reject) {
|
|
543
|
+
try {
|
|
544
|
+
popup.document.open();
|
|
545
|
+
popup.document.write(fullHtml);
|
|
546
|
+
popup.document.close();
|
|
547
|
+
|
|
548
|
+
popup.onload = function () {
|
|
549
|
+
popup.document.fonts.ready.then(function () {
|
|
550
|
+
popup.focus();
|
|
551
|
+
popup.print();
|
|
552
|
+
if (typeof popup.onafterprint !== "undefined") {
|
|
553
|
+
popup.onafterprint = function () { popup.close(); };
|
|
554
|
+
} else {
|
|
555
|
+
setTimeout(function () { popup.close(); }, 1000);
|
|
556
|
+
}
|
|
557
|
+
resolve();
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
} catch (err) {
|
|
561
|
+
popup.close();
|
|
562
|
+
reject(err);
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
410
567
|
export function handleDebateError(msg) {
|
|
411
568
|
if (ctx.messagesEl && debateActive) {
|
|
412
569
|
var errEl = document.createElement("div");
|
|
@@ -91,6 +91,17 @@ export function initFileBrowser(_ctx) {
|
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
// File viewer refresh button
|
|
95
|
+
var viewerRefreshBtn = document.getElementById("file-viewer-refresh");
|
|
96
|
+
if (viewerRefreshBtn) {
|
|
97
|
+
viewerRefreshBtn.addEventListener("click", function () {
|
|
98
|
+
if (!currentFilePath) return;
|
|
99
|
+
viewerRefreshBtn.classList.add("spinning");
|
|
100
|
+
setTimeout(function () { viewerRefreshBtn.classList.remove("spinning"); }, 500);
|
|
101
|
+
requestFileContent(currentFilePath);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
94
105
|
// Refresh button
|
|
95
106
|
var refreshBtn = document.getElementById("file-panel-refresh");
|
|
96
107
|
if (refreshBtn) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
2
2
|
import { setRewindMode, isRewindMode } from './rewind.js';
|
|
3
|
-
import { checkForMention, showMentionMenu, hideMentionMenu, isMentionMenuVisible, mentionMenuKeydown, setMentionAtIdx, parseMentionFromInput, clearMentionState, sendMention, renderMentionUser, removeMentionChip } from './mention.js';
|
|
3
|
+
import { checkForMention, showMentionMenu, hideMentionMenu, isMentionMenuVisible, mentionMenuKeydown, setMentionAtIdx, parseMentionFromInput, clearMentionState, stickyReapplyMention, sendMention, renderMentionUser, removeMentionChip } from './mention.js';
|
|
4
4
|
|
|
5
5
|
var ctx;
|
|
6
6
|
|
|
@@ -151,8 +151,8 @@ export function sendMessage() {
|
|
|
151
151
|
// Render user message with mention chip (same as history replay)
|
|
152
152
|
renderMentionUser({ mateName: mention.mateName, text: mentionText, images: mentionImages.length > 0 ? mentionImages : null, pastes: mentionPastes.length > 0 ? mentionPastes : null });
|
|
153
153
|
sendMention(mention.mateId, mentionText, mentionPastes, mentionImages);
|
|
154
|
-
clearMentionState();
|
|
155
154
|
ctx.inputEl.value = "";
|
|
155
|
+
stickyReapplyMention();
|
|
156
156
|
sendInputSync();
|
|
157
157
|
clearPendingImages();
|
|
158
158
|
autoResize();
|
|
@@ -726,6 +726,24 @@ export function initInput(_ctx) {
|
|
|
726
726
|
});
|
|
727
727
|
}
|
|
728
728
|
|
|
729
|
+
// Ask Mate button — insert @ to trigger mention menu
|
|
730
|
+
var askMateBtn = document.getElementById("ask-mate-btn");
|
|
731
|
+
if (askMateBtn) {
|
|
732
|
+
askMateBtn.addEventListener("click", function () {
|
|
733
|
+
var inputEl = document.getElementById("input");
|
|
734
|
+
if (!inputEl) return;
|
|
735
|
+
inputEl.focus();
|
|
736
|
+
// Insert @ at cursor position
|
|
737
|
+
var start = inputEl.selectionStart || 0;
|
|
738
|
+
var end = inputEl.selectionEnd || 0;
|
|
739
|
+
var val = inputEl.value;
|
|
740
|
+
inputEl.value = val.substring(0, start) + "@" + val.substring(end);
|
|
741
|
+
inputEl.selectionStart = inputEl.selectionEnd = start + 1;
|
|
742
|
+
// Trigger the mention detection
|
|
743
|
+
inputEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
|
|
729
747
|
// Paste handler
|
|
730
748
|
document.addEventListener("paste", function (e) {
|
|
731
749
|
// Don't intercept paste when typing in modals or other non-chat inputs
|
|
@@ -293,7 +293,7 @@ export function exportMarkdownAsPdf(markdownEl, filename) {
|
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
function buildPrintHtml(title, contentHtml) {
|
|
296
|
+
export function buildPrintHtml(title, contentHtml) {
|
|
297
297
|
return "<!DOCTYPE html>\n" +
|
|
298
298
|
"<html lang=\"ko\"><head>\n" +
|
|
299
299
|
"<meta charset=\"UTF-8\">\n" +
|
|
@@ -309,7 +309,7 @@ function buildPrintHtml(title, contentHtml) {
|
|
|
309
309
|
"</body></html>";
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
function getPrintCss() {
|
|
312
|
+
export function getPrintCss() {
|
|
313
313
|
return [
|
|
314
314
|
/* MS Word defaults: 2.54cm (1in) margins, 11pt, 115% line-height, 8pt after para */
|
|
315
315
|
"@page { margin: 2.54cm; }",
|