@agent-link/server 0.1.126 → 0.1.127
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 +40 -6
- package/web/modules/connection.js +4 -0
- package/web/modules/filePreview.js +16 -0
- package/web/modules/sidebar.js +11 -1
- package/web/style.css +70 -0
package/package.json
CHANGED
package/web/app.js
CHANGED
|
@@ -80,6 +80,9 @@ const App = {
|
|
|
80
80
|
// Working directory history
|
|
81
81
|
const workdirHistory = ref([]);
|
|
82
82
|
|
|
83
|
+
// Working directory switching loading state
|
|
84
|
+
const workdirSwitching = ref(false);
|
|
85
|
+
|
|
83
86
|
// Authentication state
|
|
84
87
|
const authRequired = ref(false);
|
|
85
88
|
const authPassword = ref('');
|
|
@@ -112,6 +115,7 @@ const App = {
|
|
|
112
115
|
const previewPanelWidth = ref(parseInt(localStorage.getItem('agentlink-preview-panel-width'), 10) || 400);
|
|
113
116
|
const previewFile = ref(null);
|
|
114
117
|
const previewLoading = ref(false);
|
|
118
|
+
const previewMarkdownRendered = ref(false);
|
|
115
119
|
|
|
116
120
|
// ── switchConversation: save current → load target ──
|
|
117
121
|
// Defined here and used by sidebar.newConversation, sidebar.resumeSession, workdir_changed
|
|
@@ -249,7 +253,7 @@ const App = {
|
|
|
249
253
|
folderPickerLoading, folderPickerSelected, streaming,
|
|
250
254
|
deleteConfirmOpen, deleteConfirmTitle,
|
|
251
255
|
renamingSessionId, renameText,
|
|
252
|
-
hostname, workdirHistory,
|
|
256
|
+
hostname, workdirHistory, workdirSwitching,
|
|
253
257
|
// Multi-session parallel
|
|
254
258
|
currentConversationId, conversationCache, processingConversations,
|
|
255
259
|
switchConversation,
|
|
@@ -263,6 +267,7 @@ const App = {
|
|
|
263
267
|
folderPickerLoading, folderPickerEntries, folderPickerPath,
|
|
264
268
|
authRequired, authPassword, authError, authAttempts, authLocked,
|
|
265
269
|
streaming, sidebar, scrollToBottom,
|
|
270
|
+
workdirSwitching,
|
|
266
271
|
// Multi-session parallel
|
|
267
272
|
currentConversationId, processingConversations, conversationCache,
|
|
268
273
|
switchConversation,
|
|
@@ -287,7 +292,7 @@ const App = {
|
|
|
287
292
|
// File preview module
|
|
288
293
|
const filePreview = createFilePreview({
|
|
289
294
|
wsSend, previewPanelOpen, previewPanelWidth, previewFile, previewLoading,
|
|
290
|
-
sidebarView, sidebarOpen, isMobile,
|
|
295
|
+
previewMarkdownRendered, sidebarView, sidebarOpen, isMobile, renderMarkdown,
|
|
291
296
|
});
|
|
292
297
|
setFilePreview(filePreview);
|
|
293
298
|
|
|
@@ -498,6 +503,7 @@ const App = {
|
|
|
498
503
|
filteredWorkdirHistory: sidebar.filteredWorkdirHistory,
|
|
499
504
|
switchToWorkdir: sidebar.switchToWorkdir,
|
|
500
505
|
removeFromWorkdirHistory: sidebar.removeFromWorkdirHistory,
|
|
506
|
+
workdirSwitching,
|
|
501
507
|
// Authentication
|
|
502
508
|
authRequired, authPassword, authError, authAttempts, authLocked,
|
|
503
509
|
submitPassword,
|
|
@@ -516,7 +522,7 @@ const App = {
|
|
|
516
522
|
sidebarView, isMobile, fileBrowser,
|
|
517
523
|
flattenedTree: fileBrowser.flattenedTree,
|
|
518
524
|
// File preview
|
|
519
|
-
previewPanelOpen, previewPanelWidth, previewFile, previewLoading, filePreview,
|
|
525
|
+
previewPanelOpen, previewPanelWidth, previewFile, previewLoading, previewMarkdownRendered, filePreview,
|
|
520
526
|
workdirMenuOpen,
|
|
521
527
|
toggleWorkdirMenu() { workdirMenuOpen.value = !workdirMenuOpen.value; },
|
|
522
528
|
workdirMenuBrowse() {
|
|
@@ -617,9 +623,17 @@ const App = {
|
|
|
617
623
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
|
618
624
|
Files
|
|
619
625
|
</button>
|
|
620
|
-
<
|
|
621
|
-
|
|
622
|
-
|
|
626
|
+
<div class="preview-header-actions">
|
|
627
|
+
<button v-if="previewFile?.content && filePreview.isMarkdownFile(previewFile.fileName)"
|
|
628
|
+
class="preview-md-toggle" :class="{ active: previewMarkdownRendered }"
|
|
629
|
+
@click="previewMarkdownRendered = !previewMarkdownRendered"
|
|
630
|
+
:title="previewMarkdownRendered ? 'Show source' : 'Render markdown'">
|
|
631
|
+
<svg viewBox="0 0 16 16" width="14" height="14"><path fill="currentColor" d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"/></svg>
|
|
632
|
+
</button>
|
|
633
|
+
<span v-if="previewFile" class="file-preview-mobile-size">
|
|
634
|
+
{{ filePreview.formatFileSize(previewFile.totalSize) }}
|
|
635
|
+
</span>
|
|
636
|
+
</div>
|
|
623
637
|
</div>
|
|
624
638
|
<div class="file-preview-mobile-filename" :title="previewFile?.filePath">
|
|
625
639
|
{{ previewFile?.fileName || 'Preview' }}
|
|
@@ -634,6 +648,9 @@ const App = {
|
|
|
634
648
|
<img :src="'data:' + previewFile.mimeType + ';base64,' + previewFile.content"
|
|
635
649
|
:alt="previewFile.fileName" class="preview-image" />
|
|
636
650
|
</div>
|
|
651
|
+
<div v-else-if="previewFile?.content && previewMarkdownRendered && filePreview.isMarkdownFile(previewFile.fileName)"
|
|
652
|
+
class="preview-markdown-rendered markdown-body" v-html="filePreview.renderedMarkdownHtml(previewFile.content)">
|
|
653
|
+
</div>
|
|
637
654
|
<div v-else-if="previewFile?.content" class="preview-text-container">
|
|
638
655
|
<pre class="preview-code"><code v-html="filePreview.highlightCode(previewFile.content, previewFile.fileName)"></code></pre>
|
|
639
656
|
<div v-if="previewFile.truncated" class="preview-truncated-notice">
|
|
@@ -1036,6 +1053,12 @@ const App = {
|
|
|
1036
1053
|
<span class="preview-panel-filename" :title="previewFile?.filePath">
|
|
1037
1054
|
{{ previewFile?.fileName || 'Preview' }}
|
|
1038
1055
|
</span>
|
|
1056
|
+
<button v-if="previewFile?.content && filePreview.isMarkdownFile(previewFile.fileName)"
|
|
1057
|
+
class="preview-md-toggle" :class="{ active: previewMarkdownRendered }"
|
|
1058
|
+
@click="previewMarkdownRendered = !previewMarkdownRendered"
|
|
1059
|
+
:title="previewMarkdownRendered ? 'Show source' : 'Render markdown'">
|
|
1060
|
+
<svg viewBox="0 0 16 16" width="14" height="14"><path fill="currentColor" d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"/></svg>
|
|
1061
|
+
</button>
|
|
1039
1062
|
<span v-if="previewFile" class="preview-panel-size">
|
|
1040
1063
|
{{ filePreview.formatFileSize(previewFile.totalSize) }}
|
|
1041
1064
|
</span>
|
|
@@ -1051,6 +1074,9 @@ const App = {
|
|
|
1051
1074
|
<img :src="'data:' + previewFile.mimeType + ';base64,' + previewFile.content"
|
|
1052
1075
|
:alt="previewFile.fileName" class="preview-image" />
|
|
1053
1076
|
</div>
|
|
1077
|
+
<div v-else-if="previewFile?.content && previewMarkdownRendered && filePreview.isMarkdownFile(previewFile.fileName)"
|
|
1078
|
+
class="preview-markdown-rendered markdown-body" v-html="filePreview.renderedMarkdownHtml(previewFile.content)">
|
|
1079
|
+
</div>
|
|
1054
1080
|
<div v-else-if="previewFile?.content" class="preview-text-container">
|
|
1055
1081
|
<pre class="preview-code"><code v-html="filePreview.highlightCode(previewFile.content, previewFile.fileName)"></code></pre>
|
|
1056
1082
|
<div v-if="previewFile.truncated" class="preview-truncated-notice">
|
|
@@ -1165,6 +1191,14 @@ const App = {
|
|
|
1165
1191
|
</div>
|
|
1166
1192
|
</div>
|
|
1167
1193
|
|
|
1194
|
+
<!-- Workdir switching overlay -->
|
|
1195
|
+
<Transition name="fade">
|
|
1196
|
+
<div v-if="workdirSwitching" class="workdir-switching-overlay">
|
|
1197
|
+
<div class="workdir-switching-spinner"></div>
|
|
1198
|
+
<div class="workdir-switching-text">Switching directory...</div>
|
|
1199
|
+
</div>
|
|
1200
|
+
</Transition>
|
|
1201
|
+
|
|
1168
1202
|
<!-- File context menu -->
|
|
1169
1203
|
<div
|
|
1170
1204
|
v-if="fileContextMenu"
|
|
@@ -20,6 +20,7 @@ export function createConnection(deps) {
|
|
|
20
20
|
authRequired, authPassword, authError, authAttempts, authLocked,
|
|
21
21
|
streaming, sidebar,
|
|
22
22
|
scrollToBottom,
|
|
23
|
+
workdirSwitching,
|
|
23
24
|
// Multi-session parallel
|
|
24
25
|
currentConversationId, processingConversations, conversationCache,
|
|
25
26
|
switchConversation,
|
|
@@ -504,6 +505,8 @@ export function createConnection(deps) {
|
|
|
504
505
|
sidebar.addToWorkdirHistory(msg.agent.workDir);
|
|
505
506
|
const savedDir = localStorage.getItem(`agentlink-workdir-${sessionId.value}`);
|
|
506
507
|
if (savedDir && savedDir !== msg.agent.workDir) {
|
|
508
|
+
workdirSwitching.value = true;
|
|
509
|
+
setTimeout(() => { workdirSwitching.value = false; }, 10000);
|
|
507
510
|
wsSend({ type: 'change_workdir', workDir: savedDir });
|
|
508
511
|
}
|
|
509
512
|
sidebar.requestSessionList();
|
|
@@ -797,6 +800,7 @@ export function createConnection(deps) {
|
|
|
797
800
|
} else if (msg.type === 'file_content') {
|
|
798
801
|
if (filePreview) filePreview.handleFileContent(msg);
|
|
799
802
|
} else if (msg.type === 'workdir_changed') {
|
|
803
|
+
workdirSwitching.value = false;
|
|
800
804
|
workDir.value = msg.workDir;
|
|
801
805
|
localStorage.setItem(`agentlink-workdir-${sessionId.value}`, msg.workDir);
|
|
802
806
|
sidebar.addToWorkdirHistory(msg.workDir);
|
|
@@ -11,9 +11,11 @@ export function createFilePreview(deps) {
|
|
|
11
11
|
previewPanelWidth,
|
|
12
12
|
previewFile,
|
|
13
13
|
previewLoading,
|
|
14
|
+
previewMarkdownRendered,
|
|
14
15
|
sidebarView,
|
|
15
16
|
sidebarOpen,
|
|
16
17
|
isMobile,
|
|
18
|
+
renderMarkdown,
|
|
17
19
|
} = deps;
|
|
18
20
|
|
|
19
21
|
// ── Open / Close ──
|
|
@@ -52,6 +54,7 @@ export function createFilePreview(deps) {
|
|
|
52
54
|
|
|
53
55
|
function handleFileContent(msg) {
|
|
54
56
|
previewLoading.value = false;
|
|
57
|
+
previewMarkdownRendered.value = false;
|
|
55
58
|
previewFile.value = {
|
|
56
59
|
filePath: msg.filePath,
|
|
57
60
|
fileName: msg.fileName,
|
|
@@ -174,6 +177,17 @@ export function createFilePreview(deps) {
|
|
|
174
177
|
localStorage.setItem('agentlink-preview-panel-width', String(previewPanelWidth.value));
|
|
175
178
|
}
|
|
176
179
|
|
|
180
|
+
// ── Markdown preview ──
|
|
181
|
+
|
|
182
|
+
function isMarkdownFile(fileName) {
|
|
183
|
+
const ext = (fileName || '').split('.').pop()?.toLowerCase();
|
|
184
|
+
return ext === 'md' || ext === 'mdx';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function renderedMarkdownHtml(content) {
|
|
188
|
+
return renderMarkdown(content || '');
|
|
189
|
+
}
|
|
190
|
+
|
|
177
191
|
return {
|
|
178
192
|
openPreview,
|
|
179
193
|
closePreview,
|
|
@@ -183,5 +197,7 @@ export function createFilePreview(deps) {
|
|
|
183
197
|
highlightCode,
|
|
184
198
|
formatFileSize,
|
|
185
199
|
onResizeStart,
|
|
200
|
+
isMarkdownFile,
|
|
201
|
+
renderedMarkdownHtml,
|
|
186
202
|
};
|
|
187
203
|
}
|
package/web/modules/sidebar.js
CHANGED
|
@@ -31,12 +31,20 @@ export function createSidebar(deps) {
|
|
|
31
31
|
loadingSessions, loadingHistory, workDir, visibleLimit,
|
|
32
32
|
folderPickerOpen, folderPickerPath, folderPickerEntries,
|
|
33
33
|
folderPickerLoading, folderPickerSelected, streaming,
|
|
34
|
-
hostname, workdirHistory,
|
|
34
|
+
hostname, workdirHistory, workdirSwitching,
|
|
35
35
|
// Multi-session parallel
|
|
36
36
|
currentConversationId, conversationCache, processingConversations,
|
|
37
37
|
switchConversation,
|
|
38
38
|
} = deps;
|
|
39
39
|
|
|
40
|
+
// ── Workdir switching timeout ──
|
|
41
|
+
let _workdirSwitchTimer = null;
|
|
42
|
+
function setWorkdirSwitching() {
|
|
43
|
+
workdirSwitching.value = true;
|
|
44
|
+
clearTimeout(_workdirSwitchTimer);
|
|
45
|
+
_workdirSwitchTimer = setTimeout(() => { workdirSwitching.value = false; }, 10000);
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
// ── Session management ──
|
|
41
49
|
|
|
42
50
|
let _sessionListTimer = null;
|
|
@@ -278,6 +286,7 @@ export function createSidebar(deps) {
|
|
|
278
286
|
path = path.replace(/[/\\]$/, '') + sep + folderPickerSelected.value;
|
|
279
287
|
}
|
|
280
288
|
folderPickerOpen.value = false;
|
|
289
|
+
setWorkdirSwitching();
|
|
281
290
|
wsSend({ type: 'change_workdir', workDir: path });
|
|
282
291
|
}
|
|
283
292
|
|
|
@@ -316,6 +325,7 @@ export function createSidebar(deps) {
|
|
|
316
325
|
}
|
|
317
326
|
|
|
318
327
|
function switchToWorkdir(path) {
|
|
328
|
+
setWorkdirSwitching();
|
|
319
329
|
wsSend({ type: 'change_workdir', workDir: path });
|
|
320
330
|
}
|
|
321
331
|
|
package/web/style.css
CHANGED
|
@@ -1939,6 +1939,42 @@ body {
|
|
|
1939
1939
|
color: var(--text-secondary);
|
|
1940
1940
|
}
|
|
1941
1941
|
|
|
1942
|
+
/* ── Workdir switching overlay ── */
|
|
1943
|
+
.workdir-switching-overlay {
|
|
1944
|
+
position: fixed;
|
|
1945
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
1946
|
+
background: rgba(0, 0, 0, 0.45);
|
|
1947
|
+
z-index: 1100;
|
|
1948
|
+
display: flex;
|
|
1949
|
+
flex-direction: column;
|
|
1950
|
+
align-items: center;
|
|
1951
|
+
justify-content: center;
|
|
1952
|
+
gap: 16px;
|
|
1953
|
+
backdrop-filter: blur(2px);
|
|
1954
|
+
}
|
|
1955
|
+
.workdir-switching-spinner {
|
|
1956
|
+
width: 36px;
|
|
1957
|
+
height: 36px;
|
|
1958
|
+
border: 3px solid rgba(255, 255, 255, 0.2);
|
|
1959
|
+
border-top-color: rgba(255, 255, 255, 0.8);
|
|
1960
|
+
border-radius: 50%;
|
|
1961
|
+
animation: workdir-spin 0.7s linear infinite;
|
|
1962
|
+
}
|
|
1963
|
+
@keyframes workdir-spin {
|
|
1964
|
+
to { transform: rotate(360deg); }
|
|
1965
|
+
}
|
|
1966
|
+
.workdir-switching-text {
|
|
1967
|
+
color: rgba(255, 255, 255, 0.9);
|
|
1968
|
+
font-size: 0.9rem;
|
|
1969
|
+
font-weight: 500;
|
|
1970
|
+
}
|
|
1971
|
+
.fade-enter-active, .fade-leave-active {
|
|
1972
|
+
transition: opacity 0.2s ease;
|
|
1973
|
+
}
|
|
1974
|
+
.fade-enter-from, .fade-leave-to {
|
|
1975
|
+
opacity: 0;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1942
1978
|
/* ── Folder Picker Modal ── */
|
|
1943
1979
|
.folder-picker-overlay {
|
|
1944
1980
|
position: fixed;
|
|
@@ -2618,6 +2654,40 @@ body {
|
|
|
2618
2654
|
color: var(--text);
|
|
2619
2655
|
}
|
|
2620
2656
|
|
|
2657
|
+
.preview-md-toggle {
|
|
2658
|
+
background: none;
|
|
2659
|
+
border: 1px solid var(--border);
|
|
2660
|
+
border-radius: 4px;
|
|
2661
|
+
cursor: pointer;
|
|
2662
|
+
color: var(--text-secondary);
|
|
2663
|
+
padding: 2px 6px;
|
|
2664
|
+
line-height: 1;
|
|
2665
|
+
display: flex;
|
|
2666
|
+
align-items: center;
|
|
2667
|
+
flex-shrink: 0;
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
.preview-md-toggle:hover {
|
|
2671
|
+
color: var(--text);
|
|
2672
|
+
border-color: var(--text-secondary);
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
.preview-md-toggle.active {
|
|
2676
|
+
color: var(--accent, #6366f1);
|
|
2677
|
+
border-color: var(--accent, #6366f1);
|
|
2678
|
+
background: color-mix(in srgb, var(--accent, #6366f1) 10%, transparent);
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
.preview-header-actions {
|
|
2682
|
+
display: flex;
|
|
2683
|
+
align-items: center;
|
|
2684
|
+
gap: 0.5rem;
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
.preview-markdown-rendered {
|
|
2688
|
+
padding: 1rem;
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2621
2691
|
.preview-panel-body {
|
|
2622
2692
|
flex: 1;
|
|
2623
2693
|
overflow: auto;
|