vimd 0.5.7 → 0.5.8
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/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +17 -75
- package/dist/core/folder-mode/assets/folder-mode.js +35 -5
- package/dist/core/single-file-server.d.ts +87 -0
- package/dist/core/single-file-server.d.ts.map +1 -0
- package/dist/core/single-file-server.js +295 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/templates/single-file.html +259 -0
- package/dist/utils/session-manager.d.ts.map +1 -1
- package/dist/utils/session-manager.js +4 -3
- package/package.json +1 -1
- package/templates/single-file.html +259 -0
- package/dist/core/websocket-server.d.ts +0 -52
- package/dist/core/websocket-server.d.ts.map +0 -1
- package/dist/core/websocket-server.js +0 -221
- package/dist/templates/standalone.html +0 -61
- package/templates/standalone.html +0 -61
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="ja">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<meta name="generator" content="vimd">
|
|
7
|
+
<title>{{title}} - vimd</title>
|
|
8
|
+
<style>
|
|
9
|
+
{{theme_css}}
|
|
10
|
+
</style>
|
|
11
|
+
<style>
|
|
12
|
+
/* Single file mode styles */
|
|
13
|
+
* {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
html, body {
|
|
18
|
+
margin: 0;
|
|
19
|
+
padding: 0;
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.vimd-single-container {
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100vh;
|
|
31
|
+
overflow-y: auto;
|
|
32
|
+
overflow-x: hidden;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.vimd-single-content {
|
|
36
|
+
padding: 32px;
|
|
37
|
+
max-width: 720px;
|
|
38
|
+
margin: 0 auto;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.vimd-single-content pre,
|
|
42
|
+
.vimd-single-content table {
|
|
43
|
+
overflow-x: auto;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Connection status indicator */
|
|
47
|
+
.vimd-connection-status {
|
|
48
|
+
position: fixed;
|
|
49
|
+
bottom: 16px;
|
|
50
|
+
right: 16px;
|
|
51
|
+
padding: 8px 12px;
|
|
52
|
+
background: rgba(0, 0, 0, 0.7);
|
|
53
|
+
color: #fff;
|
|
54
|
+
font-size: 12px;
|
|
55
|
+
border-radius: 4px;
|
|
56
|
+
opacity: 0;
|
|
57
|
+
transition: opacity 0.3s;
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
z-index: 1000;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.vimd-connection-status.visible {
|
|
63
|
+
opacity: 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.vimd-connection-status.error {
|
|
67
|
+
background: rgba(211, 47, 47, 0.9);
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
70
|
+
{{#if math_enabled}}
|
|
71
|
+
<style>
|
|
72
|
+
/* Block math centering */
|
|
73
|
+
.math-block {
|
|
74
|
+
display: block;
|
|
75
|
+
text-align: center;
|
|
76
|
+
margin: 1em 0;
|
|
77
|
+
}
|
|
78
|
+
mjx-container[display="true"] {
|
|
79
|
+
display: block !important;
|
|
80
|
+
text-align: center !important;
|
|
81
|
+
margin: 1em 0 !important;
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
84
|
+
<script>
|
|
85
|
+
MathJax = {
|
|
86
|
+
loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics', '[tex]/boldsymbol']},
|
|
87
|
+
tex: {
|
|
88
|
+
packages: {'[+]': ['bussproofs', 'ams', 'physics', 'boldsymbol']},
|
|
89
|
+
inlineMath: [['$', '$'], ['\\(', '\\)']],
|
|
90
|
+
displayMath: [['$$', '$$'], ['\\[', '\\]']],
|
|
91
|
+
tags: 'ams',
|
|
92
|
+
macros: {
|
|
93
|
+
bm: ['\\boldsymbol{#1}', 1]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
</script>
|
|
98
|
+
<script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
|
99
|
+
{{/if}}
|
|
100
|
+
</head>
|
|
101
|
+
<body>
|
|
102
|
+
<div class="vimd-single-container" id="container">
|
|
103
|
+
<article class="vimd-single-content markdown-body" id="content">
|
|
104
|
+
<!-- Content will be loaded via WebSocket -->
|
|
105
|
+
</article>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div class="vimd-connection-status" id="status"></div>
|
|
109
|
+
|
|
110
|
+
<script>
|
|
111
|
+
(function() {
|
|
112
|
+
'use strict';
|
|
113
|
+
|
|
114
|
+
var container = document.getElementById('container');
|
|
115
|
+
var content = document.getElementById('content');
|
|
116
|
+
var status = document.getElementById('status');
|
|
117
|
+
var ws = null;
|
|
118
|
+
var reconnectAttempts = 0;
|
|
119
|
+
var maxReconnectAttempts = 10;
|
|
120
|
+
var reconnectDelay = 1000;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Show connection status message
|
|
124
|
+
*/
|
|
125
|
+
function showStatus(message, isError) {
|
|
126
|
+
status.textContent = message;
|
|
127
|
+
status.classList.toggle('error', isError);
|
|
128
|
+
status.classList.add('visible');
|
|
129
|
+
|
|
130
|
+
// Hide after 3 seconds for non-error messages
|
|
131
|
+
if (!isError) {
|
|
132
|
+
setTimeout(function() {
|
|
133
|
+
status.classList.remove('visible');
|
|
134
|
+
}, 3000);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Hide connection status
|
|
140
|
+
*/
|
|
141
|
+
function hideStatus() {
|
|
142
|
+
status.classList.remove('visible');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Connect to WebSocket server
|
|
147
|
+
*/
|
|
148
|
+
function connect() {
|
|
149
|
+
var protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
150
|
+
ws = new WebSocket(protocol + '//' + location.host);
|
|
151
|
+
|
|
152
|
+
ws.onopen = function() {
|
|
153
|
+
console.log('[vimd] WebSocket connected');
|
|
154
|
+
reconnectAttempts = 0;
|
|
155
|
+
hideStatus();
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
ws.onmessage = function(event) {
|
|
159
|
+
try {
|
|
160
|
+
var msg = JSON.parse(event.data);
|
|
161
|
+
handleMessage(msg);
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.error('[vimd] Failed to parse message:', e);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
ws.onclose = function() {
|
|
168
|
+
console.log('[vimd] WebSocket disconnected');
|
|
169
|
+
|
|
170
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
171
|
+
reconnectAttempts++;
|
|
172
|
+
showStatus('Reconnecting... (' + reconnectAttempts + '/' + maxReconnectAttempts + ')', false);
|
|
173
|
+
setTimeout(connect, reconnectDelay);
|
|
174
|
+
} else {
|
|
175
|
+
showStatus('Connection lost. Please refresh the page.', true);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
ws.onerror = function(error) {
|
|
180
|
+
console.error('[vimd] WebSocket error:', error);
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Handle incoming WebSocket message
|
|
186
|
+
*/
|
|
187
|
+
function handleMessage(msg) {
|
|
188
|
+
switch (msg.type) {
|
|
189
|
+
case 'content':
|
|
190
|
+
updateContent(msg.data.html);
|
|
191
|
+
if (msg.data.title) {
|
|
192
|
+
document.title = msg.data.title + ' - vimd';
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
|
|
196
|
+
case 'error':
|
|
197
|
+
showError(msg.data.message);
|
|
198
|
+
break;
|
|
199
|
+
|
|
200
|
+
default:
|
|
201
|
+
console.warn('[vimd] Unknown message type:', msg.type);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Update content with scroll position preservation
|
|
207
|
+
*/
|
|
208
|
+
function updateContent(html) {
|
|
209
|
+
// Save scroll position
|
|
210
|
+
var scrollTop = container.scrollTop;
|
|
211
|
+
|
|
212
|
+
// Update content
|
|
213
|
+
content.innerHTML = html;
|
|
214
|
+
|
|
215
|
+
// Re-render MathJax if available
|
|
216
|
+
if (window.MathJax && window.MathJax.typeset) {
|
|
217
|
+
// MathJax.typeset returns a promise in v3
|
|
218
|
+
var promise = window.MathJax.typeset([content]);
|
|
219
|
+
|
|
220
|
+
// Restore scroll position after MathJax completes
|
|
221
|
+
if (promise && promise.then) {
|
|
222
|
+
promise.then(function() {
|
|
223
|
+
container.scrollTop = scrollTop;
|
|
224
|
+
}).catch(function() {
|
|
225
|
+
// Restore scroll even if MathJax fails
|
|
226
|
+
container.scrollTop = scrollTop;
|
|
227
|
+
});
|
|
228
|
+
} else {
|
|
229
|
+
// Fallback: restore scroll immediately
|
|
230
|
+
container.scrollTop = scrollTop;
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
// No MathJax: restore scroll immediately
|
|
234
|
+
container.scrollTop = scrollTop;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Show error message
|
|
240
|
+
*/
|
|
241
|
+
function showError(message) {
|
|
242
|
+
content.innerHTML = '<div class="vimd-error"><h2>Error</h2><p>' + escapeHtml(message) + '</p></div>';
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Escape HTML special characters
|
|
247
|
+
*/
|
|
248
|
+
function escapeHtml(text) {
|
|
249
|
+
var div = document.createElement('div');
|
|
250
|
+
div.textContent = text;
|
|
251
|
+
return div.innerHTML;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Initialize connection
|
|
255
|
+
connect();
|
|
256
|
+
})();
|
|
257
|
+
</script>
|
|
258
|
+
</body>
|
|
259
|
+
</html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/utils/session-manager.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAkC;IACtE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAGnC;IAEF;;OAEG;WACU,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;IAWlD;;OAEG;WACU,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhE;;OAEG;WACU,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAKlE;;OAEG;WACU,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7D;;OAEG;WACU,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvD;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAS3C;;OAEG;WACU,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBvD;;OAEG;WACU,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/utils/session-manager.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAkC;IACtE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAGnC;IAEF;;OAEG;WACU,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;IAWlD;;OAEG;WACU,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhE;;OAEG;WACU,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAKlE;;OAEG;WACU,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7D;;OAEG;WACU,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvD;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAS3C;;OAEG;WACU,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBvD;;OAEG;WACU,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IA2BjD;;OAEG;WACU,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAmCvE;;OAEG;IACH,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYtD;;OAEG;WACU,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAcnE"}
|
|
@@ -87,8 +87,9 @@ export class SessionManager {
|
|
|
87
87
|
for (const [port, session] of Object.entries(sessions)) {
|
|
88
88
|
if (!this.isProcessAlive(session.pid)) {
|
|
89
89
|
// Process is dead, clean up HTML if exists
|
|
90
|
+
// Skip if htmlPath is empty (single file / folder mode)
|
|
90
91
|
try {
|
|
91
|
-
if (await fs.pathExists(session.htmlPath)) {
|
|
92
|
+
if (session.htmlPath && await fs.pathExists(session.htmlPath)) {
|
|
92
93
|
await fs.remove(session.htmlPath);
|
|
93
94
|
}
|
|
94
95
|
}
|
|
@@ -122,9 +123,9 @@ export class SessionManager {
|
|
|
122
123
|
if (this.isProcessAlive(session.pid)) {
|
|
123
124
|
result.killed = await this.killProcess(session.pid);
|
|
124
125
|
}
|
|
125
|
-
// Remove HTML file
|
|
126
|
+
// Remove HTML file (skip if htmlPath is empty - single file / folder mode)
|
|
126
127
|
try {
|
|
127
|
-
if (await fs.pathExists(session.htmlPath)) {
|
|
128
|
+
if (session.htmlPath && await fs.pathExists(session.htmlPath)) {
|
|
128
129
|
await fs.remove(session.htmlPath);
|
|
129
130
|
result.htmlRemoved = true;
|
|
130
131
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="ja">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<meta name="generator" content="vimd">
|
|
7
|
+
<title>{{title}} - vimd</title>
|
|
8
|
+
<style>
|
|
9
|
+
{{theme_css}}
|
|
10
|
+
</style>
|
|
11
|
+
<style>
|
|
12
|
+
/* Single file mode styles */
|
|
13
|
+
* {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
html, body {
|
|
18
|
+
margin: 0;
|
|
19
|
+
padding: 0;
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.vimd-single-container {
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100vh;
|
|
31
|
+
overflow-y: auto;
|
|
32
|
+
overflow-x: hidden;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.vimd-single-content {
|
|
36
|
+
padding: 32px;
|
|
37
|
+
max-width: 720px;
|
|
38
|
+
margin: 0 auto;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.vimd-single-content pre,
|
|
42
|
+
.vimd-single-content table {
|
|
43
|
+
overflow-x: auto;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Connection status indicator */
|
|
47
|
+
.vimd-connection-status {
|
|
48
|
+
position: fixed;
|
|
49
|
+
bottom: 16px;
|
|
50
|
+
right: 16px;
|
|
51
|
+
padding: 8px 12px;
|
|
52
|
+
background: rgba(0, 0, 0, 0.7);
|
|
53
|
+
color: #fff;
|
|
54
|
+
font-size: 12px;
|
|
55
|
+
border-radius: 4px;
|
|
56
|
+
opacity: 0;
|
|
57
|
+
transition: opacity 0.3s;
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
z-index: 1000;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.vimd-connection-status.visible {
|
|
63
|
+
opacity: 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.vimd-connection-status.error {
|
|
67
|
+
background: rgba(211, 47, 47, 0.9);
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
70
|
+
{{#if math_enabled}}
|
|
71
|
+
<style>
|
|
72
|
+
/* Block math centering */
|
|
73
|
+
.math-block {
|
|
74
|
+
display: block;
|
|
75
|
+
text-align: center;
|
|
76
|
+
margin: 1em 0;
|
|
77
|
+
}
|
|
78
|
+
mjx-container[display="true"] {
|
|
79
|
+
display: block !important;
|
|
80
|
+
text-align: center !important;
|
|
81
|
+
margin: 1em 0 !important;
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
84
|
+
<script>
|
|
85
|
+
MathJax = {
|
|
86
|
+
loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics', '[tex]/boldsymbol']},
|
|
87
|
+
tex: {
|
|
88
|
+
packages: {'[+]': ['bussproofs', 'ams', 'physics', 'boldsymbol']},
|
|
89
|
+
inlineMath: [['$', '$'], ['\\(', '\\)']],
|
|
90
|
+
displayMath: [['$$', '$$'], ['\\[', '\\]']],
|
|
91
|
+
tags: 'ams',
|
|
92
|
+
macros: {
|
|
93
|
+
bm: ['\\boldsymbol{#1}', 1]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
</script>
|
|
98
|
+
<script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
|
99
|
+
{{/if}}
|
|
100
|
+
</head>
|
|
101
|
+
<body>
|
|
102
|
+
<div class="vimd-single-container" id="container">
|
|
103
|
+
<article class="vimd-single-content markdown-body" id="content">
|
|
104
|
+
<!-- Content will be loaded via WebSocket -->
|
|
105
|
+
</article>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div class="vimd-connection-status" id="status"></div>
|
|
109
|
+
|
|
110
|
+
<script>
|
|
111
|
+
(function() {
|
|
112
|
+
'use strict';
|
|
113
|
+
|
|
114
|
+
var container = document.getElementById('container');
|
|
115
|
+
var content = document.getElementById('content');
|
|
116
|
+
var status = document.getElementById('status');
|
|
117
|
+
var ws = null;
|
|
118
|
+
var reconnectAttempts = 0;
|
|
119
|
+
var maxReconnectAttempts = 10;
|
|
120
|
+
var reconnectDelay = 1000;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Show connection status message
|
|
124
|
+
*/
|
|
125
|
+
function showStatus(message, isError) {
|
|
126
|
+
status.textContent = message;
|
|
127
|
+
status.classList.toggle('error', isError);
|
|
128
|
+
status.classList.add('visible');
|
|
129
|
+
|
|
130
|
+
// Hide after 3 seconds for non-error messages
|
|
131
|
+
if (!isError) {
|
|
132
|
+
setTimeout(function() {
|
|
133
|
+
status.classList.remove('visible');
|
|
134
|
+
}, 3000);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Hide connection status
|
|
140
|
+
*/
|
|
141
|
+
function hideStatus() {
|
|
142
|
+
status.classList.remove('visible');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Connect to WebSocket server
|
|
147
|
+
*/
|
|
148
|
+
function connect() {
|
|
149
|
+
var protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
150
|
+
ws = new WebSocket(protocol + '//' + location.host);
|
|
151
|
+
|
|
152
|
+
ws.onopen = function() {
|
|
153
|
+
console.log('[vimd] WebSocket connected');
|
|
154
|
+
reconnectAttempts = 0;
|
|
155
|
+
hideStatus();
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
ws.onmessage = function(event) {
|
|
159
|
+
try {
|
|
160
|
+
var msg = JSON.parse(event.data);
|
|
161
|
+
handleMessage(msg);
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.error('[vimd] Failed to parse message:', e);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
ws.onclose = function() {
|
|
168
|
+
console.log('[vimd] WebSocket disconnected');
|
|
169
|
+
|
|
170
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
171
|
+
reconnectAttempts++;
|
|
172
|
+
showStatus('Reconnecting... (' + reconnectAttempts + '/' + maxReconnectAttempts + ')', false);
|
|
173
|
+
setTimeout(connect, reconnectDelay);
|
|
174
|
+
} else {
|
|
175
|
+
showStatus('Connection lost. Please refresh the page.', true);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
ws.onerror = function(error) {
|
|
180
|
+
console.error('[vimd] WebSocket error:', error);
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Handle incoming WebSocket message
|
|
186
|
+
*/
|
|
187
|
+
function handleMessage(msg) {
|
|
188
|
+
switch (msg.type) {
|
|
189
|
+
case 'content':
|
|
190
|
+
updateContent(msg.data.html);
|
|
191
|
+
if (msg.data.title) {
|
|
192
|
+
document.title = msg.data.title + ' - vimd';
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
|
|
196
|
+
case 'error':
|
|
197
|
+
showError(msg.data.message);
|
|
198
|
+
break;
|
|
199
|
+
|
|
200
|
+
default:
|
|
201
|
+
console.warn('[vimd] Unknown message type:', msg.type);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Update content with scroll position preservation
|
|
207
|
+
*/
|
|
208
|
+
function updateContent(html) {
|
|
209
|
+
// Save scroll position
|
|
210
|
+
var scrollTop = container.scrollTop;
|
|
211
|
+
|
|
212
|
+
// Update content
|
|
213
|
+
content.innerHTML = html;
|
|
214
|
+
|
|
215
|
+
// Re-render MathJax if available
|
|
216
|
+
if (window.MathJax && window.MathJax.typeset) {
|
|
217
|
+
// MathJax.typeset returns a promise in v3
|
|
218
|
+
var promise = window.MathJax.typeset([content]);
|
|
219
|
+
|
|
220
|
+
// Restore scroll position after MathJax completes
|
|
221
|
+
if (promise && promise.then) {
|
|
222
|
+
promise.then(function() {
|
|
223
|
+
container.scrollTop = scrollTop;
|
|
224
|
+
}).catch(function() {
|
|
225
|
+
// Restore scroll even if MathJax fails
|
|
226
|
+
container.scrollTop = scrollTop;
|
|
227
|
+
});
|
|
228
|
+
} else {
|
|
229
|
+
// Fallback: restore scroll immediately
|
|
230
|
+
container.scrollTop = scrollTop;
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
// No MathJax: restore scroll immediately
|
|
234
|
+
container.scrollTop = scrollTop;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Show error message
|
|
240
|
+
*/
|
|
241
|
+
function showError(message) {
|
|
242
|
+
content.innerHTML = '<div class="vimd-error"><h2>Error</h2><p>' + escapeHtml(message) + '</p></div>';
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Escape HTML special characters
|
|
247
|
+
*/
|
|
248
|
+
function escapeHtml(text) {
|
|
249
|
+
var div = document.createElement('div');
|
|
250
|
+
div.textContent = text;
|
|
251
|
+
return div.innerHTML;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Initialize connection
|
|
255
|
+
connect();
|
|
256
|
+
})();
|
|
257
|
+
</script>
|
|
258
|
+
</body>
|
|
259
|
+
</html>
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebSocketServer options
|
|
3
|
-
* Note: 'open' property is not included (handled by dev.ts)
|
|
4
|
-
*/
|
|
5
|
-
export interface WebSocketServerOptions {
|
|
6
|
-
port: number;
|
|
7
|
-
root: string;
|
|
8
|
-
host?: string;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Result of server start operation
|
|
12
|
-
* Same interface as previous LiveServer for compatibility
|
|
13
|
-
*/
|
|
14
|
-
export interface ServerStartResult {
|
|
15
|
-
actualPort: number;
|
|
16
|
-
requestedPort: number;
|
|
17
|
-
portChanged: boolean;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* WebSocket server for live reload functionality
|
|
21
|
-
* Replaces live-server with direct WebSocket control
|
|
22
|
-
*/
|
|
23
|
-
export declare class WebSocketServer {
|
|
24
|
-
private httpServer;
|
|
25
|
-
private wsServer;
|
|
26
|
-
private clients;
|
|
27
|
-
private options;
|
|
28
|
-
private _port;
|
|
29
|
-
constructor(options: WebSocketServerOptions);
|
|
30
|
-
/**
|
|
31
|
-
* Get the actual port the server is running on
|
|
32
|
-
*/
|
|
33
|
-
get port(): number;
|
|
34
|
-
/**
|
|
35
|
-
* Start the HTTP and WebSocket servers
|
|
36
|
-
*/
|
|
37
|
-
start(): Promise<ServerStartResult>;
|
|
38
|
-
/**
|
|
39
|
-
* Stop the server
|
|
40
|
-
* Uses force termination to ensure immediate shutdown
|
|
41
|
-
*/
|
|
42
|
-
stop(): Promise<void>;
|
|
43
|
-
/**
|
|
44
|
-
* Broadcast a message to all connected clients
|
|
45
|
-
*/
|
|
46
|
-
broadcast(type: string, data?: unknown): void;
|
|
47
|
-
/**
|
|
48
|
-
* Inject reload script into HTML content
|
|
49
|
-
*/
|
|
50
|
-
private injectReloadScript;
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=websocket-server.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../src/core/websocket-server.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AA0CD;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,EAAE,sBAAsB;IAQ3C;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC;IA4GzC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC3B;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAU7C;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAc3B"}
|