local-cmd-runner 1.0.4 → 1.0.5
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/public/app.js +49 -6
- package/public/index.html +1 -1
- package/server.js +1 -2
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -206,6 +206,14 @@ function renderTree(items, relPath) {
|
|
|
206
206
|
|
|
207
207
|
if (item.isDirectory) {
|
|
208
208
|
el.onclick = () => loadTree(item.path);
|
|
209
|
+
|
|
210
|
+
const uploadDirBtn = document.createElement('button');
|
|
211
|
+
uploadDirBtn.innerHTML = '上传到此';
|
|
212
|
+
uploadDirBtn.onclick = (e) => {
|
|
213
|
+
e.stopPropagation();
|
|
214
|
+
triggerDirectoryUpload(item.path);
|
|
215
|
+
};
|
|
216
|
+
actions.appendChild(uploadDirBtn);
|
|
209
217
|
} else {
|
|
210
218
|
const previewBtn = document.createElement('button');
|
|
211
219
|
previewBtn.innerHTML = '查看';
|
|
@@ -226,15 +234,21 @@ function renderTree(items, relPath) {
|
|
|
226
234
|
});
|
|
227
235
|
}
|
|
228
236
|
|
|
237
|
+
let pendingUploadPath = '';
|
|
238
|
+
function triggerDirectoryUpload(targetPath) {
|
|
239
|
+
pendingUploadPath = targetPath;
|
|
240
|
+
fmUploadInput.click();
|
|
241
|
+
}
|
|
242
|
+
|
|
229
243
|
if (fmRefreshBtn) {
|
|
230
244
|
fmRefreshBtn.onclick = () => loadTree(currentRelPath);
|
|
231
|
-
fmUploadBtn.onclick = () =>
|
|
245
|
+
fmUploadBtn.onclick = () => triggerDirectoryUpload(currentRelPath);
|
|
232
246
|
fmUploadInput.onchange = async (e) => {
|
|
233
247
|
const file = e.target.files[0];
|
|
234
248
|
if (!file) return;
|
|
235
249
|
const formData = new FormData();
|
|
236
250
|
formData.append('file', file);
|
|
237
|
-
formData.append('path',
|
|
251
|
+
formData.append('path', pendingUploadPath);
|
|
238
252
|
|
|
239
253
|
try {
|
|
240
254
|
fmUploadBtn.textContent = '...';
|
|
@@ -255,14 +269,43 @@ if (fmRefreshBtn) {
|
|
|
255
269
|
|
|
256
270
|
async function previewFile(path) {
|
|
257
271
|
try {
|
|
258
|
-
const res = await fetch(`/cmd/api/preview?path=${encodeURIComponent(path)}`);
|
|
259
|
-
if (!res.ok) throw new Error('获取预览失败');
|
|
260
|
-
const text = await res.text();
|
|
261
272
|
previewTitle.textContent = path;
|
|
262
|
-
previewBody.textContent = text;
|
|
263
273
|
previewModal.classList.remove('hidden');
|
|
274
|
+
previewBody.innerHTML = '<div style="margin: 20px;">加载中...</div>';
|
|
275
|
+
|
|
276
|
+
const ext = path.split('.').pop().toLowerCase();
|
|
277
|
+
const isImage = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico'].includes(ext);
|
|
278
|
+
const isHtml = ['html', 'htm'].includes(ext);
|
|
279
|
+
const isVideo = ['mp4', 'webm', 'ogg'].includes(ext);
|
|
280
|
+
const isAudio = ['mp3', 'wav', 'ogg'].includes(ext);
|
|
281
|
+
|
|
282
|
+
const fileUrl = `/cmd/api/preview?path=${encodeURIComponent(path)}`;
|
|
283
|
+
|
|
284
|
+
if (isImage) {
|
|
285
|
+
previewBody.innerHTML = `<img src="${fileUrl}" style="max-width: 100%; max-height: 70vh; object-fit: contain; border-radius: 4px;" />`;
|
|
286
|
+
} else if (isVideo) {
|
|
287
|
+
previewBody.innerHTML = `<video src="${fileUrl}" controls style="max-width: 100%; max-height: 70vh; border-radius: 4px;"></video>`;
|
|
288
|
+
} else if (isAudio) {
|
|
289
|
+
previewBody.innerHTML = `<audio src="${fileUrl}" controls style="width: 100%; margin-top: 20px;"></audio>`;
|
|
290
|
+
} else if (isHtml) {
|
|
291
|
+
previewBody.innerHTML = `<iframe src="${fileUrl}" style="width: 100%; min-height: 60vh; border: none; background: white; border-radius: 4px;"></iframe>`;
|
|
292
|
+
} else {
|
|
293
|
+
const res = await fetch(fileUrl);
|
|
294
|
+
if (!res.ok) throw new Error('获取预览失败');
|
|
295
|
+
const text = await res.text();
|
|
296
|
+
previewBody.innerHTML = '';
|
|
297
|
+
const pre = document.createElement('pre');
|
|
298
|
+
pre.textContent = text;
|
|
299
|
+
pre.style.whiteSpace = 'pre-wrap';
|
|
300
|
+
pre.style.wordBreak = 'break-all';
|
|
301
|
+
pre.style.margin = '0';
|
|
302
|
+
pre.style.fontFamily = "'Fira Code', monospace";
|
|
303
|
+
pre.style.width = "100%";
|
|
304
|
+
previewBody.appendChild(pre);
|
|
305
|
+
}
|
|
264
306
|
} catch (err) {
|
|
265
307
|
alert('预览失败: ' + err.message);
|
|
308
|
+
previewModal.classList.add('hidden');
|
|
266
309
|
}
|
|
267
310
|
}
|
|
268
311
|
|
package/public/index.html
CHANGED
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
<div id="previewModal" class="modal-overlay hidden">
|
|
69
69
|
<div class="modal-content preview-modal-content">
|
|
70
70
|
<h3 id="previewTitle" style="word-break: break-all;">文件预览</h3>
|
|
71
|
-
<
|
|
71
|
+
<div class="preview-body" id="previewBody" style="display: flex; flex-direction: column; align-items: center; justify-content: center;"></div>
|
|
72
72
|
<div class="modal-actions">
|
|
73
73
|
<button id="previewClose" class="btn-primary">关闭</button>
|
|
74
74
|
</div>
|
package/server.js
CHANGED
|
@@ -64,8 +64,7 @@ app.get('/cmd/api/tree', async (req, res) => {
|
|
|
64
64
|
app.get('/cmd/api/preview', async (req, res) => {
|
|
65
65
|
try {
|
|
66
66
|
const targetPath = getSecurePath(req.query.path);
|
|
67
|
-
|
|
68
|
-
res.send(content);
|
|
67
|
+
res.sendFile(targetPath);
|
|
69
68
|
} catch (err) {
|
|
70
69
|
res.status(500).send(err.message);
|
|
71
70
|
}
|