@agent-link/server 0.1.90 → 0.1.91
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 +27 -16
- package/web/modules/connection.js +4 -1
- package/web/style.css +45 -19
package/package.json
CHANGED
package/web/app.js
CHANGED
|
@@ -47,6 +47,7 @@ const App = {
|
|
|
47
47
|
const inputText = ref('');
|
|
48
48
|
const isProcessing = ref(false);
|
|
49
49
|
const isCompacting = ref(false);
|
|
50
|
+
const queuedMessages = ref([]);
|
|
50
51
|
const inputRef = ref(null);
|
|
51
52
|
|
|
52
53
|
// Sidebar state
|
|
@@ -157,7 +158,7 @@ const App = {
|
|
|
157
158
|
const { connect, wsSend, closeWs, submitPassword, setDequeueNext } = createConnection({
|
|
158
159
|
status, agentName, hostname, workDir, sessionId, error,
|
|
159
160
|
serverVersion, agentVersion,
|
|
160
|
-
messages, isProcessing, isCompacting, visibleLimit,
|
|
161
|
+
messages, isProcessing, isCompacting, visibleLimit, queuedMessages,
|
|
161
162
|
historySessions, currentClaudeSessionId, loadingSessions, loadingHistory,
|
|
162
163
|
folderPickerLoading, folderPickerEntries, folderPickerPath,
|
|
163
164
|
authRequired, authPassword, authError, authAttempts, authLocked,
|
|
@@ -216,9 +217,7 @@ const App = {
|
|
|
216
217
|
};
|
|
217
218
|
|
|
218
219
|
if (isProcessing.value) {
|
|
219
|
-
userMsg.
|
|
220
|
-
userMsg._queuedPayload = payload;
|
|
221
|
-
messages.value.push(userMsg);
|
|
220
|
+
queuedMessages.value.push({ id: streaming.nextId(), content: userMsg.content, attachments: userMsg.attachments, payload });
|
|
222
221
|
} else {
|
|
223
222
|
userMsg.status = 'sent';
|
|
224
223
|
messages.value.push(userMsg);
|
|
@@ -235,19 +234,22 @@ const App = {
|
|
|
235
234
|
}
|
|
236
235
|
|
|
237
236
|
function dequeueNext() {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
|
|
237
|
+
if (queuedMessages.value.length === 0) return;
|
|
238
|
+
const queued = queuedMessages.value.shift();
|
|
239
|
+
const userMsg = {
|
|
240
|
+
id: queued.id, role: 'user', status: 'sent',
|
|
241
|
+
content: queued.content, attachments: queued.attachments,
|
|
242
|
+
timestamp: new Date(),
|
|
243
|
+
};
|
|
244
|
+
messages.value.push(userMsg);
|
|
242
245
|
isProcessing.value = true;
|
|
243
|
-
wsSend(
|
|
244
|
-
delete msg._queuedPayload;
|
|
246
|
+
wsSend(queued.payload);
|
|
245
247
|
scrollToBottom(true);
|
|
246
248
|
}
|
|
247
249
|
|
|
248
250
|
function removeQueuedMessage(msgId) {
|
|
249
|
-
const idx =
|
|
250
|
-
if (idx !== -1)
|
|
251
|
+
const idx = queuedMessages.value.findIndex(m => m.id === msgId);
|
|
252
|
+
if (idx !== -1) queuedMessages.value.splice(idx, 1);
|
|
251
253
|
}
|
|
252
254
|
|
|
253
255
|
function handleKeydown(e) {
|
|
@@ -283,7 +285,7 @@ const App = {
|
|
|
283
285
|
status, agentName, hostname, workDir, sessionId, error,
|
|
284
286
|
serverVersion, agentVersion,
|
|
285
287
|
messages, visibleMessages, hasMoreMessages, loadMoreMessages,
|
|
286
|
-
inputText, isProcessing, isCompacting, canSend, hasInput, inputRef,
|
|
288
|
+
inputText, isProcessing, isCompacting, canSend, hasInput, inputRef, queuedMessages,
|
|
287
289
|
sendMessage, handleKeydown, cancelExecution, removeQueuedMessage, onMessageListScroll,
|
|
288
290
|
getRenderedContent, copyMessage, toggleTool,
|
|
289
291
|
isPrevAssistant: _isPrevAssistant,
|
|
@@ -484,8 +486,7 @@ const App = {
|
|
|
484
486
|
<!-- User message -->
|
|
485
487
|
<template v-if="msg.role === 'user'">
|
|
486
488
|
<div class="message-role-label user-label">You</div>
|
|
487
|
-
<div
|
|
488
|
-
<button v-if="msg.status === 'queued'" class="queue-remove-btn" @click="removeQueuedMessage(msg.id)" title="Remove from queue">×</button>
|
|
489
|
+
<div class="message-bubble user-bubble" :title="formatTimestamp(msg.timestamp)">
|
|
489
490
|
<div class="message-content">{{ msg.content }}</div>
|
|
490
491
|
<div v-if="msg.attachments && msg.attachments.length" class="message-attachments">
|
|
491
492
|
<div v-for="(att, ai) in msg.attachments" :key="ai" class="message-attachment-chip">
|
|
@@ -496,7 +497,6 @@ const App = {
|
|
|
496
497
|
<span>{{ att.name }}</span>
|
|
497
498
|
</div>
|
|
498
499
|
</div>
|
|
499
|
-
<div v-if="msg.status === 'queued'" class="queue-badge">queued</div>
|
|
500
500
|
</div>
|
|
501
501
|
</template>
|
|
502
502
|
|
|
@@ -616,6 +616,17 @@ const App = {
|
|
|
616
616
|
@change="handleFileSelect"
|
|
617
617
|
accept="image/*,text/*,.pdf,.json,.md,.py,.js,.ts,.tsx,.jsx,.css,.html,.xml,.yaml,.yml,.toml,.sh,.sql,.csv"
|
|
618
618
|
/>
|
|
619
|
+
<div v-if="queuedMessages.length > 0" class="queue-bar">
|
|
620
|
+
<div v-for="(qm, qi) in queuedMessages" :key="qm.id" class="queue-item">
|
|
621
|
+
<span class="queue-item-num">{{ qi + 1 }}.</span>
|
|
622
|
+
<span class="queue-item-text">{{ qm.content }}</span>
|
|
623
|
+
<span v-if="qm.attachments && qm.attachments.length" class="queue-item-attach" :title="qm.attachments.map(a => a.name).join(', ')">
|
|
624
|
+
<svg viewBox="0 0 24 24" width="11" height="11"><path fill="currentColor" d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5a2.5 2.5 0 0 1 5 0v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5a2.5 2.5 0 0 0 5 0V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/></svg>
|
|
625
|
+
{{ qm.attachments.length }}
|
|
626
|
+
</span>
|
|
627
|
+
<button class="queue-item-remove" @click="removeQueuedMessage(qm.id)" title="Remove from queue">×</button>
|
|
628
|
+
</div>
|
|
629
|
+
</div>
|
|
619
630
|
<div
|
|
620
631
|
:class="['input-card', { 'drag-over': dragOver }]"
|
|
621
632
|
@dragover="handleDragOver"
|
|
@@ -14,7 +14,7 @@ export function createConnection(deps) {
|
|
|
14
14
|
const {
|
|
15
15
|
status, agentName, hostname, workDir, sessionId, error,
|
|
16
16
|
serverVersion, agentVersion,
|
|
17
|
-
messages, isProcessing, isCompacting, visibleLimit,
|
|
17
|
+
messages, isProcessing, isCompacting, visibleLimit, queuedMessages,
|
|
18
18
|
historySessions, currentClaudeSessionId, loadingSessions, loadingHistory,
|
|
19
19
|
folderPickerLoading, folderPickerEntries, folderPickerPath,
|
|
20
20
|
authRequired, authPassword, authError, authAttempts, authLocked,
|
|
@@ -207,6 +207,7 @@ export function createConnection(deps) {
|
|
|
207
207
|
error.value = 'Agent disconnected. Waiting for reconnect...';
|
|
208
208
|
isProcessing.value = false;
|
|
209
209
|
isCompacting.value = false;
|
|
210
|
+
queuedMessages.value = [];
|
|
210
211
|
} else if (msg.type === 'agent_reconnected') {
|
|
211
212
|
status.value = 'Connected';
|
|
212
213
|
error.value = '';
|
|
@@ -393,6 +394,7 @@ export function createConnection(deps) {
|
|
|
393
394
|
localStorage.setItem(`agentlink-workdir-${sessionId.value}`, msg.workDir);
|
|
394
395
|
sidebar.addToWorkdirHistory(msg.workDir);
|
|
395
396
|
messages.value = [];
|
|
397
|
+
queuedMessages.value = [];
|
|
396
398
|
toolMsgMap.clear();
|
|
397
399
|
visibleLimit.value = 50;
|
|
398
400
|
streaming.setMessageIdCounter(0);
|
|
@@ -414,6 +416,7 @@ export function createConnection(deps) {
|
|
|
414
416
|
const wasConnected = status.value === 'Connected' || status.value === 'Connecting...';
|
|
415
417
|
isProcessing.value = false;
|
|
416
418
|
isCompacting.value = false;
|
|
419
|
+
queuedMessages.value = [];
|
|
417
420
|
|
|
418
421
|
// Don't auto-reconnect if auth-locked or still in auth prompt
|
|
419
422
|
if (authLocked.value || authRequired.value) return;
|
package/web/style.css
CHANGED
|
@@ -787,27 +787,56 @@ body {
|
|
|
787
787
|
color: var(--text-primary);
|
|
788
788
|
}
|
|
789
789
|
|
|
790
|
-
/* ──
|
|
791
|
-
.
|
|
792
|
-
|
|
790
|
+
/* ── Queue bar (pending messages above input) ── */
|
|
791
|
+
.queue-bar {
|
|
792
|
+
max-width: 768px;
|
|
793
|
+
margin: 0 auto 6px;
|
|
794
|
+
display: flex;
|
|
795
|
+
flex-direction: column;
|
|
796
|
+
gap: 3px;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.queue-item {
|
|
800
|
+
display: flex;
|
|
801
|
+
align-items: center;
|
|
802
|
+
gap: 6px;
|
|
803
|
+
background: var(--bg-secondary);
|
|
793
804
|
border: 1px dashed var(--border);
|
|
794
|
-
|
|
805
|
+
border-radius: 8px;
|
|
806
|
+
padding: 4px 8px;
|
|
807
|
+
font-size: 0.8rem;
|
|
808
|
+
line-height: 1.3;
|
|
809
|
+
color: var(--text-secondary);
|
|
810
|
+
opacity: 0.85;
|
|
795
811
|
}
|
|
796
812
|
|
|
797
|
-
.queue-
|
|
798
|
-
|
|
813
|
+
.queue-item-num {
|
|
814
|
+
flex-shrink: 0;
|
|
799
815
|
font-weight: 600;
|
|
800
|
-
text-transform: uppercase;
|
|
801
|
-
letter-spacing: 0.05em;
|
|
802
816
|
color: var(--text-secondary);
|
|
803
|
-
|
|
804
|
-
|
|
817
|
+
font-size: 0.7rem;
|
|
818
|
+
opacity: 0.6;
|
|
805
819
|
}
|
|
806
820
|
|
|
807
|
-
.queue-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
821
|
+
.queue-item-text {
|
|
822
|
+
flex: 1;
|
|
823
|
+
min-width: 0;
|
|
824
|
+
overflow: hidden;
|
|
825
|
+
text-overflow: ellipsis;
|
|
826
|
+
white-space: nowrap;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.queue-item-attach {
|
|
830
|
+
flex-shrink: 0;
|
|
831
|
+
display: flex;
|
|
832
|
+
align-items: center;
|
|
833
|
+
gap: 2px;
|
|
834
|
+
font-size: 0.7rem;
|
|
835
|
+
opacity: 0.6;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
.queue-item-remove {
|
|
839
|
+
flex-shrink: 0;
|
|
811
840
|
background: none;
|
|
812
841
|
border: none;
|
|
813
842
|
color: var(--text-secondary);
|
|
@@ -815,15 +844,12 @@ body {
|
|
|
815
844
|
line-height: 1;
|
|
816
845
|
cursor: pointer;
|
|
817
846
|
padding: 0 2px;
|
|
818
|
-
opacity: 0;
|
|
847
|
+
opacity: 0.5;
|
|
819
848
|
transition: color 0.15s, opacity 0.15s;
|
|
820
849
|
}
|
|
821
850
|
|
|
822
|
-
.
|
|
851
|
+
.queue-item-remove:hover {
|
|
823
852
|
opacity: 1;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
.queue-remove-btn:hover {
|
|
827
853
|
color: var(--error);
|
|
828
854
|
}
|
|
829
855
|
|