@agent-link/server 0.1.124 → 0.1.126

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.
@@ -1,187 +1,187 @@
1
- // ── File Preview: panel state, content rendering, resize handle ────────
2
-
3
- /**
4
- * Creates the file preview controller.
5
- * @param {object} deps - Reactive state and callbacks
6
- */
7
- export function createFilePreview(deps) {
8
- const {
9
- wsSend,
10
- previewPanelOpen,
11
- previewPanelWidth,
12
- previewFile,
13
- previewLoading,
14
- sidebarView,
15
- sidebarOpen,
16
- isMobile,
17
- } = deps;
18
-
19
- // ── Open / Close ──
20
-
21
- function openPreview(filePath) {
22
- // Skip re-fetch if same file already loaded
23
- if (previewFile.value && previewFile.value.filePath === filePath && !previewFile.value.error) {
24
- if (isMobile.value) {
25
- sidebarView.value = 'preview';
26
- sidebarOpen.value = true;
27
- } else {
28
- previewPanelOpen.value = true;
29
- }
30
- return;
31
- }
32
- if (isMobile.value) {
33
- sidebarView.value = 'preview';
34
- sidebarOpen.value = true;
35
- } else {
36
- previewPanelOpen.value = true;
37
- }
38
- previewLoading.value = true;
39
- previewFile.value = null;
40
- wsSend({ type: 'read_file', filePath });
41
- }
42
-
43
- function closePreview() {
44
- if (isMobile.value) {
45
- sidebarView.value = 'files';
46
- } else {
47
- previewPanelOpen.value = false;
48
- }
49
- }
50
-
51
- // ── Handle file_content response ──
52
-
53
- function handleFileContent(msg) {
54
- previewLoading.value = false;
55
- previewFile.value = {
56
- filePath: msg.filePath,
57
- fileName: msg.fileName,
58
- content: msg.content,
59
- encoding: msg.encoding,
60
- mimeType: msg.mimeType,
61
- truncated: msg.truncated,
62
- totalSize: msg.totalSize,
63
- error: msg.error || null,
64
- };
65
- }
66
-
67
- // ── Workdir changed → close preview ──
68
-
69
- function onWorkdirChanged() {
70
- previewPanelOpen.value = false;
71
- previewFile.value = null;
72
- previewLoading.value = false;
73
- if (sidebarView.value === 'preview') {
74
- sidebarView.value = 'sessions';
75
- }
76
- }
77
-
78
- // ── Syntax highlighting helpers ──
79
-
80
- const LANG_MAP = {
81
- ts: 'typescript', tsx: 'typescript', js: 'javascript', jsx: 'javascript',
82
- mjs: 'javascript', cjs: 'javascript', py: 'python', rb: 'ruby',
83
- rs: 'rust', go: 'go', java: 'java', c: 'c', h: 'c',
84
- cpp: 'cpp', hpp: 'cpp', cs: 'csharp', swift: 'swift', kt: 'kotlin',
85
- lua: 'lua', r: 'r', sql: 'sql', sh: 'bash', bash: 'bash', zsh: 'bash',
86
- fish: 'bash', ps1: 'powershell', bat: 'dos', cmd: 'dos',
87
- json: 'json', json5: 'json', yaml: 'yaml', yml: 'yaml', toml: 'ini',
88
- xml: 'xml', html: 'xml', htm: 'xml', css: 'css', scss: 'scss', less: 'less',
89
- md: 'markdown', txt: 'plaintext', log: 'plaintext', graphql: 'graphql',
90
- proto: 'protobuf', vue: 'xml', svelte: 'xml', ini: 'ini', cfg: 'ini',
91
- conf: 'ini', env: 'bash',
92
- };
93
-
94
- function detectLanguage(fileName) {
95
- const ext = (fileName || '').split('.').pop()?.toLowerCase();
96
- return LANG_MAP[ext] || ext || 'plaintext';
97
- }
98
-
99
- function highlightCode(code, fileName) {
100
- if (!code) return '';
101
- if (!window.hljs) return escapeHtml(code);
102
- const lang = detectLanguage(fileName);
103
- try {
104
- return window.hljs.highlight(code, { language: lang }).value;
105
- } catch {
106
- try {
107
- return window.hljs.highlightAuto(code).value;
108
- } catch {
109
- return escapeHtml(code);
110
- }
111
- }
112
- }
113
-
114
- function escapeHtml(str) {
115
- return str
116
- .replace(/&/g, '&')
117
- .replace(/</g, '&lt;')
118
- .replace(/>/g, '&gt;')
119
- .replace(/"/g, '&quot;');
120
- }
121
-
122
- // ── File size formatting ──
123
-
124
- function formatFileSize(bytes) {
125
- if (bytes == null) return '';
126
- if (bytes < 1024) return bytes + ' B';
127
- if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
128
- return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
129
- }
130
-
131
- // ── Resize handle (mouse + touch) ──
132
-
133
- let _resizing = false;
134
- let _startX = 0;
135
- let _startWidth = 0;
136
- const MIN_WIDTH = 200;
137
- const MAX_WIDTH = 800;
138
-
139
- function onResizeStart(e) {
140
- e.preventDefault();
141
- _resizing = true;
142
- _startX = e.type === 'touchstart' ? e.touches[0].clientX : e.clientX;
143
- _startWidth = previewPanelWidth.value;
144
- document.body.style.cursor = 'col-resize';
145
- document.body.style.userSelect = 'none';
146
- if (e.type === 'touchstart') {
147
- document.addEventListener('touchmove', onResizeMove, { passive: false });
148
- document.addEventListener('touchend', onResizeEnd);
149
- } else {
150
- document.addEventListener('mousemove', onResizeMove);
151
- document.addEventListener('mouseup', onResizeEnd);
152
- }
153
- }
154
-
155
- function onResizeMove(e) {
156
- if (!_resizing) return;
157
- if (e.type === 'touchmove') e.preventDefault();
158
- const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;
159
- // Left edge resize: dragging left = wider, dragging right = narrower
160
- const delta = _startX - clientX;
161
- const newWidth = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, _startWidth + delta));
162
- previewPanelWidth.value = newWidth;
163
- }
164
-
165
- function onResizeEnd() {
166
- if (!_resizing) return;
167
- _resizing = false;
168
- document.body.style.cursor = '';
169
- document.body.style.userSelect = '';
170
- document.removeEventListener('mousemove', onResizeMove);
171
- document.removeEventListener('mouseup', onResizeEnd);
172
- document.removeEventListener('touchmove', onResizeMove);
173
- document.removeEventListener('touchend', onResizeEnd);
174
- localStorage.setItem('agentlink-preview-panel-width', String(previewPanelWidth.value));
175
- }
176
-
177
- return {
178
- openPreview,
179
- closePreview,
180
- handleFileContent,
181
- onWorkdirChanged,
182
- detectLanguage,
183
- highlightCode,
184
- formatFileSize,
185
- onResizeStart,
186
- };
187
- }
1
+ // ── File Preview: panel state, content rendering, resize handle ────────
2
+
3
+ /**
4
+ * Creates the file preview controller.
5
+ * @param {object} deps - Reactive state and callbacks
6
+ */
7
+ export function createFilePreview(deps) {
8
+ const {
9
+ wsSend,
10
+ previewPanelOpen,
11
+ previewPanelWidth,
12
+ previewFile,
13
+ previewLoading,
14
+ sidebarView,
15
+ sidebarOpen,
16
+ isMobile,
17
+ } = deps;
18
+
19
+ // ── Open / Close ──
20
+
21
+ function openPreview(filePath) {
22
+ // Skip re-fetch if same file already loaded
23
+ if (previewFile.value && previewFile.value.filePath === filePath && !previewFile.value.error) {
24
+ if (isMobile.value) {
25
+ sidebarView.value = 'preview';
26
+ sidebarOpen.value = true;
27
+ } else {
28
+ previewPanelOpen.value = true;
29
+ }
30
+ return;
31
+ }
32
+ if (isMobile.value) {
33
+ sidebarView.value = 'preview';
34
+ sidebarOpen.value = true;
35
+ } else {
36
+ previewPanelOpen.value = true;
37
+ }
38
+ previewLoading.value = true;
39
+ previewFile.value = null;
40
+ wsSend({ type: 'read_file', filePath });
41
+ }
42
+
43
+ function closePreview() {
44
+ if (isMobile.value) {
45
+ sidebarView.value = 'files';
46
+ } else {
47
+ previewPanelOpen.value = false;
48
+ }
49
+ }
50
+
51
+ // ── Handle file_content response ──
52
+
53
+ function handleFileContent(msg) {
54
+ previewLoading.value = false;
55
+ previewFile.value = {
56
+ filePath: msg.filePath,
57
+ fileName: msg.fileName,
58
+ content: msg.content,
59
+ encoding: msg.encoding,
60
+ mimeType: msg.mimeType,
61
+ truncated: msg.truncated,
62
+ totalSize: msg.totalSize,
63
+ error: msg.error || null,
64
+ };
65
+ }
66
+
67
+ // ── Workdir changed → close preview ──
68
+
69
+ function onWorkdirChanged() {
70
+ previewPanelOpen.value = false;
71
+ previewFile.value = null;
72
+ previewLoading.value = false;
73
+ if (sidebarView.value === 'preview') {
74
+ sidebarView.value = 'sessions';
75
+ }
76
+ }
77
+
78
+ // ── Syntax highlighting helpers ──
79
+
80
+ const LANG_MAP = {
81
+ ts: 'typescript', tsx: 'typescript', js: 'javascript', jsx: 'javascript',
82
+ mjs: 'javascript', cjs: 'javascript', py: 'python', rb: 'ruby',
83
+ rs: 'rust', go: 'go', java: 'java', c: 'c', h: 'c',
84
+ cpp: 'cpp', hpp: 'cpp', cs: 'csharp', swift: 'swift', kt: 'kotlin',
85
+ lua: 'lua', r: 'r', sql: 'sql', sh: 'bash', bash: 'bash', zsh: 'bash',
86
+ fish: 'bash', ps1: 'powershell', bat: 'dos', cmd: 'dos',
87
+ json: 'json', json5: 'json', yaml: 'yaml', yml: 'yaml', toml: 'ini',
88
+ xml: 'xml', html: 'xml', htm: 'xml', css: 'css', scss: 'scss', less: 'less',
89
+ md: 'markdown', txt: 'plaintext', log: 'plaintext', graphql: 'graphql',
90
+ proto: 'protobuf', vue: 'xml', svelte: 'xml', ini: 'ini', cfg: 'ini',
91
+ conf: 'ini', env: 'bash',
92
+ };
93
+
94
+ function detectLanguage(fileName) {
95
+ const ext = (fileName || '').split('.').pop()?.toLowerCase();
96
+ return LANG_MAP[ext] || ext || 'plaintext';
97
+ }
98
+
99
+ function highlightCode(code, fileName) {
100
+ if (!code) return '';
101
+ if (!window.hljs) return escapeHtml(code);
102
+ const lang = detectLanguage(fileName);
103
+ try {
104
+ return window.hljs.highlight(code, { language: lang }).value;
105
+ } catch {
106
+ try {
107
+ return window.hljs.highlightAuto(code).value;
108
+ } catch {
109
+ return escapeHtml(code);
110
+ }
111
+ }
112
+ }
113
+
114
+ function escapeHtml(str) {
115
+ return str
116
+ .replace(/&/g, '&amp;')
117
+ .replace(/</g, '&lt;')
118
+ .replace(/>/g, '&gt;')
119
+ .replace(/"/g, '&quot;');
120
+ }
121
+
122
+ // ── File size formatting ──
123
+
124
+ function formatFileSize(bytes) {
125
+ if (bytes == null) return '';
126
+ if (bytes < 1024) return bytes + ' B';
127
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
128
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
129
+ }
130
+
131
+ // ── Resize handle (mouse + touch) ──
132
+
133
+ let _resizing = false;
134
+ let _startX = 0;
135
+ let _startWidth = 0;
136
+ const MIN_WIDTH = 200;
137
+ const MAX_WIDTH = 800;
138
+
139
+ function onResizeStart(e) {
140
+ e.preventDefault();
141
+ _resizing = true;
142
+ _startX = e.type === 'touchstart' ? e.touches[0].clientX : e.clientX;
143
+ _startWidth = previewPanelWidth.value;
144
+ document.body.style.cursor = 'col-resize';
145
+ document.body.style.userSelect = 'none';
146
+ if (e.type === 'touchstart') {
147
+ document.addEventListener('touchmove', onResizeMove, { passive: false });
148
+ document.addEventListener('touchend', onResizeEnd);
149
+ } else {
150
+ document.addEventListener('mousemove', onResizeMove);
151
+ document.addEventListener('mouseup', onResizeEnd);
152
+ }
153
+ }
154
+
155
+ function onResizeMove(e) {
156
+ if (!_resizing) return;
157
+ if (e.type === 'touchmove') e.preventDefault();
158
+ const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;
159
+ // Left edge resize: dragging left = wider, dragging right = narrower
160
+ const delta = _startX - clientX;
161
+ const newWidth = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, _startWidth + delta));
162
+ previewPanelWidth.value = newWidth;
163
+ }
164
+
165
+ function onResizeEnd() {
166
+ if (!_resizing) return;
167
+ _resizing = false;
168
+ document.body.style.cursor = '';
169
+ document.body.style.userSelect = '';
170
+ document.removeEventListener('mousemove', onResizeMove);
171
+ document.removeEventListener('mouseup', onResizeEnd);
172
+ document.removeEventListener('touchmove', onResizeMove);
173
+ document.removeEventListener('touchend', onResizeEnd);
174
+ localStorage.setItem('agentlink-preview-panel-width', String(previewPanelWidth.value));
175
+ }
176
+
177
+ return {
178
+ openPreview,
179
+ closePreview,
180
+ handleFileContent,
181
+ onWorkdirChanged,
182
+ detectLanguage,
183
+ highlightCode,
184
+ formatFileSize,
185
+ onResizeStart,
186
+ };
187
+ }