@agent-link/server 0.1.27 → 0.1.28
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/package.json +1 -1
- package/web/app.js +29 -9
- package/web/style.css +18 -1
package/package.json
CHANGED
package/web/app.js
CHANGED
|
@@ -356,7 +356,16 @@ const App = {
|
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
let _scrollTimer = null;
|
|
359
|
-
|
|
359
|
+
let _userScrolledUp = false;
|
|
360
|
+
|
|
361
|
+
function onMessageListScroll(e) {
|
|
362
|
+
const el = e.target;
|
|
363
|
+
// Consider "at bottom" if within 80px of the bottom
|
|
364
|
+
_userScrolledUp = (el.scrollHeight - el.scrollTop - el.clientHeight) > 80;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function scrollToBottom(force) {
|
|
368
|
+
if (_userScrolledUp && !force) return;
|
|
360
369
|
if (_scrollTimer) return;
|
|
361
370
|
_scrollTimer = setTimeout(() => {
|
|
362
371
|
_scrollTimer = null;
|
|
@@ -394,7 +403,7 @@ const App = {
|
|
|
394
403
|
timestamp: new Date(),
|
|
395
404
|
});
|
|
396
405
|
isProcessing.value = true;
|
|
397
|
-
scrollToBottom();
|
|
406
|
+
scrollToBottom(true);
|
|
398
407
|
|
|
399
408
|
// Build payload
|
|
400
409
|
const payload = { type: 'chat', prompt: text || '(see attached files)' };
|
|
@@ -428,6 +437,12 @@ const App = {
|
|
|
428
437
|
}
|
|
429
438
|
|
|
430
439
|
// ── Rendered markdown for assistant messages ──
|
|
440
|
+
function formatTimestamp(ts) {
|
|
441
|
+
if (!ts) return '';
|
|
442
|
+
const d = ts instanceof Date ? ts : new Date(ts);
|
|
443
|
+
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) + ' · ' + d.toLocaleDateString();
|
|
444
|
+
}
|
|
445
|
+
|
|
431
446
|
function getRenderedContent(msg) {
|
|
432
447
|
if (msg.role !== 'assistant' && !msg.isCommandOutput) return msg.content;
|
|
433
448
|
return renderMarkdown(msg.content);
|
|
@@ -1144,12 +1159,17 @@ const App = {
|
|
|
1144
1159
|
onMounted(() => { connect(); });
|
|
1145
1160
|
onUnmounted(() => { if (reconnectTimer) clearTimeout(reconnectTimer); if (ws) ws.close(); });
|
|
1146
1161
|
|
|
1162
|
+
// Dynamic page title
|
|
1163
|
+
watch(agentName, (name) => {
|
|
1164
|
+
document.title = name ? `${name} — AgentLink` : 'AgentLink';
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1147
1167
|
return {
|
|
1148
1168
|
status, agentName, hostname, workDir, sessionId, error,
|
|
1149
1169
|
messages, visibleMessages, hasMoreMessages, loadMoreMessages,
|
|
1150
1170
|
inputText, isProcessing, isCompacting, canSend, inputRef,
|
|
1151
|
-
sendMessage, handleKeydown, cancelExecution,
|
|
1152
|
-
getRenderedContent, copyMessage, toggleTool, isPrevAssistant, toggleContextSummary,
|
|
1171
|
+
sendMessage, handleKeydown, cancelExecution, onMessageListScroll,
|
|
1172
|
+
getRenderedContent, copyMessage, toggleTool, isPrevAssistant, toggleContextSummary, formatTimestamp,
|
|
1153
1173
|
getToolIcon, getToolSummary, isEditTool, getEditDiffHtml, getFormattedToolInput, autoResize,
|
|
1154
1174
|
// AskUserQuestion
|
|
1155
1175
|
selectQuestionOption, submitQuestionAnswer, hasQuestionAnswer, getQuestionResponseSummary,
|
|
@@ -1261,7 +1281,7 @@ const App = {
|
|
|
1261
1281
|
|
|
1262
1282
|
<!-- Chat area -->
|
|
1263
1283
|
<div class="chat-area">
|
|
1264
|
-
<div class="message-list">
|
|
1284
|
+
<div class="message-list" @scroll="onMessageListScroll">
|
|
1265
1285
|
<div class="message-list-inner">
|
|
1266
1286
|
<div v-if="messages.length === 0 && status === 'Connected' && !loadingHistory" class="empty-state">
|
|
1267
1287
|
<div class="empty-state-icon">
|
|
@@ -1286,7 +1306,7 @@ const App = {
|
|
|
1286
1306
|
<!-- User message -->
|
|
1287
1307
|
<template v-if="msg.role === 'user'">
|
|
1288
1308
|
<div class="message-role-label user-label">You</div>
|
|
1289
|
-
<div class="message-bubble user-bubble">
|
|
1309
|
+
<div class="message-bubble user-bubble" :title="formatTimestamp(msg.timestamp)">
|
|
1290
1310
|
<div v-if="msg.isCommandOutput" class="message-content markdown-body" v-html="getRenderedContent(msg)"></div>
|
|
1291
1311
|
<div v-else class="message-content">{{ msg.content }}</div>
|
|
1292
1312
|
<div v-if="msg.attachments && msg.attachments.length" class="message-attachments">
|
|
@@ -1304,7 +1324,7 @@ const App = {
|
|
|
1304
1324
|
<!-- Assistant message (markdown) -->
|
|
1305
1325
|
<template v-else-if="msg.role === 'assistant'">
|
|
1306
1326
|
<div v-if="!isPrevAssistant(msgIdx)" class="message-role-label assistant-label">Claude</div>
|
|
1307
|
-
<div :class="['message-bubble', 'assistant-bubble', { streaming: msg.isStreaming }]">
|
|
1327
|
+
<div :class="['message-bubble', 'assistant-bubble', { streaming: msg.isStreaming }]" :title="formatTimestamp(msg.timestamp)">
|
|
1308
1328
|
<div class="message-actions">
|
|
1309
1329
|
<button class="icon-btn" @click="copyMessage(msg)" :title="msg.copied ? 'Copied!' : 'Copy'">
|
|
1310
1330
|
<svg v-if="!msg.copied" viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
|
|
@@ -1327,7 +1347,7 @@ const App = {
|
|
|
1327
1347
|
</span>
|
|
1328
1348
|
<span class="tool-toggle">{{ msg.expanded ? '\u{25B2}' : '\u{25BC}' }}</span>
|
|
1329
1349
|
</div>
|
|
1330
|
-
<div v-
|
|
1350
|
+
<div v-show="msg.expanded" class="tool-expand">
|
|
1331
1351
|
<div v-if="isEditTool(msg) && getEditDiffHtml(msg)" class="tool-diff" v-html="getEditDiffHtml(msg)"></div>
|
|
1332
1352
|
<div v-else-if="getFormattedToolInput(msg)" class="tool-input-formatted" v-html="getFormattedToolInput(msg)"></div>
|
|
1333
1353
|
<pre v-else-if="msg.toolInput" class="tool-block">{{ msg.toolInput }}</pre>
|
|
@@ -1428,7 +1448,7 @@ const App = {
|
|
|
1428
1448
|
@input="autoResize"
|
|
1429
1449
|
@paste="handlePaste"
|
|
1430
1450
|
:disabled="status !== 'Connected' || isCompacting"
|
|
1431
|
-
:placeholder="isCompacting ? 'Context compacting in progress...' : 'Send a message
|
|
1451
|
+
:placeholder="isCompacting ? 'Context compacting in progress...' : 'Send a message · Enter to send'"
|
|
1432
1452
|
rows="1"
|
|
1433
1453
|
></textarea>
|
|
1434
1454
|
<div v-if="attachments.length > 0" class="attachment-bar">
|
package/web/style.css
CHANGED
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
padding: 0;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/* Keyboard focus outlines (visible only for keyboard navigation) */
|
|
10
|
+
:focus-visible {
|
|
11
|
+
outline: 2px solid var(--accent);
|
|
12
|
+
outline-offset: 2px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
:focus:not(:focus-visible) {
|
|
16
|
+
outline: none;
|
|
17
|
+
}
|
|
18
|
+
|
|
9
19
|
:root {
|
|
10
20
|
--bg-primary: #0f172a;
|
|
11
21
|
--bg-secondary: #1e293b;
|
|
@@ -29,7 +39,7 @@
|
|
|
29
39
|
--bg-secondary: #f8f9fa;
|
|
30
40
|
--bg-tertiary: #e9ecef;
|
|
31
41
|
--text-primary: #1a1a1a;
|
|
32
|
-
--text-secondary: #
|
|
42
|
+
--text-secondary: #4b5563;
|
|
33
43
|
--accent: #2563eb;
|
|
34
44
|
--accent-hover: #1d4ed8;
|
|
35
45
|
--success: #16a34a;
|
|
@@ -753,6 +763,13 @@ body {
|
|
|
753
763
|
margin-left: 20px;
|
|
754
764
|
border-left: 1px solid var(--border);
|
|
755
765
|
padding-left: 8px;
|
|
766
|
+
overflow: hidden;
|
|
767
|
+
animation: toolExpand 0.15s ease-out;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
@keyframes toolExpand {
|
|
771
|
+
from { opacity: 0; max-height: 0; }
|
|
772
|
+
to { opacity: 1; max-height: 500px; }
|
|
756
773
|
}
|
|
757
774
|
|
|
758
775
|
.tool-block {
|