@wendongfly/myhi 1.3.3 → 1.3.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/dist/attach.js +1 -1
- package/dist/chat.html +5 -5
- package/dist/client-dist/socket.io.esm.min.js +1 -1
- package/dist/client-dist/socket.io.esm.min.js.map +1 -0
- package/dist/client-dist/socket.io.js +1 -1
- package/dist/client-dist/socket.io.js.map +1 -0
- package/dist/client-dist/socket.io.min.js +1 -1
- package/dist/client-dist/socket.io.min.js.map +1 -0
- package/dist/client-dist/socket.io.msgpack.min.js +1 -1
- package/dist/client-dist/socket.io.msgpack.min.js.map +1 -0
- package/dist/index.js +164 -172
- package/dist/index.min.js +376 -0
- package/dist/package.json +3 -1
- package/dist/terminal.html +18 -67
- package/package.json +2 -2
package/dist/package.json
CHANGED
package/dist/terminal.html
CHANGED
|
@@ -241,25 +241,13 @@
|
|
|
241
241
|
transition: right 0.25s ease; display: flex; flex-direction: column;
|
|
242
242
|
}
|
|
243
243
|
#file-panel.open { right: 0; }
|
|
244
|
-
.fp-header {
|
|
245
|
-
display: flex; align-items: center; padding: 0.75rem; border-bottom: 1px solid #30363d;
|
|
246
|
-
gap: 0.5rem; flex-shrink: 0;
|
|
247
|
-
}
|
|
244
|
+
.fp-header { display: flex; align-items: center; padding: 0.75rem; border-bottom: 1px solid #30363d; gap: 0.5rem; flex-shrink: 0; }
|
|
248
245
|
.fp-header h3 { flex: 1; font-size: 0.9rem; margin: 0; color: #e6edf3; }
|
|
249
|
-
.fp-btn {
|
|
250
|
-
background: #21262d; color: #e6edf3; border: 1px solid #30363d; border-radius: 6px;
|
|
251
|
-
padding: 0.3rem 0.6rem; font-size: 0.75rem; cursor: pointer;
|
|
252
|
-
}
|
|
246
|
+
.fp-btn { background: #21262d; color: #e6edf3; border: 1px solid #30363d; border-radius: 6px; padding: 0.3rem 0.6rem; font-size: 0.75rem; cursor: pointer; }
|
|
253
247
|
.fp-btn:active { background: #30363d; }
|
|
254
|
-
.fp-path {
|
|
255
|
-
padding: 0.4rem 0.75rem; font-size: 0.7rem; color: #8b949e;
|
|
256
|
-
border-bottom: 1px solid #21262d; word-break: break-all; flex-shrink: 0;
|
|
257
|
-
}
|
|
248
|
+
.fp-path { padding: 0.4rem 0.75rem; font-size: 0.7rem; color: #8b949e; border-bottom: 1px solid #21262d; word-break: break-all; flex-shrink: 0; }
|
|
258
249
|
.fp-list { flex: 1; overflow-y: auto; padding: 0.25rem 0; }
|
|
259
|
-
.fp-item {
|
|
260
|
-
display: flex; align-items: center; padding: 0.5rem 0.75rem; gap: 0.5rem;
|
|
261
|
-
cursor: pointer; font-size: 0.82rem; color: #e6edf3;
|
|
262
|
-
}
|
|
250
|
+
.fp-item { display: flex; align-items: center; padding: 0.5rem 0.75rem; gap: 0.5rem; cursor: pointer; font-size: 0.82rem; color: #e6edf3; }
|
|
263
251
|
.fp-item:active { background: #21262d; }
|
|
264
252
|
.fp-item .fp-icon { width: 1.2rem; text-align: center; flex-shrink: 0; }
|
|
265
253
|
.fp-item .fp-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
@@ -273,8 +261,6 @@
|
|
|
273
261
|
}
|
|
274
262
|
.fp-drop-zone.dragover { border-color: #58a6ff; background: rgba(88,166,255,0.08); }
|
|
275
263
|
.fp-progress { padding: 0.4rem 0.75rem; font-size: 0.75rem; color: #d29922; flex-shrink: 0; display: none; }
|
|
276
|
-
|
|
277
|
-
/* ── 同步相关 ── */
|
|
278
264
|
.fp-sync-bar { display: flex; gap: 0.4rem; padding: 0.5rem 0.75rem; border-bottom: 1px solid #21262d; flex-shrink: 0; align-items: center; flex-wrap: wrap; }
|
|
279
265
|
.fp-sync-bar .fp-btn { font-size: 0.7rem; padding: 0.25rem 0.5rem; }
|
|
280
266
|
.fp-sync-bar .sync-dir { font-size: 0.68rem; color: #8b949e; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
@@ -793,10 +779,7 @@
|
|
|
793
779
|
}
|
|
794
780
|
}
|
|
795
781
|
|
|
796
|
-
function fpNav(path) {
|
|
797
|
-
fpCurrentPath = path;
|
|
798
|
-
loadFiles();
|
|
799
|
-
}
|
|
782
|
+
function fpNav(path) { fpCurrentPath = path; loadFiles(); }
|
|
800
783
|
|
|
801
784
|
function formatSize(bytes) {
|
|
802
785
|
if (bytes == null) return '';
|
|
@@ -806,9 +789,7 @@
|
|
|
806
789
|
return (bytes / 1024 / 1024 / 1024).toFixed(1) + ' GB';
|
|
807
790
|
}
|
|
808
791
|
|
|
809
|
-
function fpUploadClick() {
|
|
810
|
-
document.getElementById('fp-file-input').click();
|
|
811
|
-
}
|
|
792
|
+
function fpUploadClick() { document.getElementById('fp-file-input').click(); }
|
|
812
793
|
|
|
813
794
|
document.getElementById('fp-file-input').addEventListener('change', (e) => {
|
|
814
795
|
if (e.target.files.length) fpUploadFiles(e.target.files);
|
|
@@ -887,7 +868,6 @@
|
|
|
887
868
|
loadFiles();
|
|
888
869
|
}
|
|
889
870
|
|
|
890
|
-
// 递归遍历本地目录
|
|
891
871
|
async function walkLocalDir(dirHandle, prefix) {
|
|
892
872
|
const files = [];
|
|
893
873
|
for await (const [name, handle] of dirHandle.entries()) {
|
|
@@ -897,20 +877,18 @@
|
|
|
897
877
|
files.push(...await walkLocalDir(handle, rel));
|
|
898
878
|
} else {
|
|
899
879
|
const file = await handle.getFile();
|
|
900
|
-
files.push({ name: rel, size: file.size, handle
|
|
880
|
+
files.push({ name: rel, size: file.size, handle });
|
|
901
881
|
}
|
|
902
882
|
}
|
|
903
883
|
return files;
|
|
904
884
|
}
|
|
905
885
|
|
|
906
|
-
// 计算文件 SHA-256(与服务端一致)
|
|
907
886
|
async function hashFile(file) {
|
|
908
887
|
const buf = await file.arrayBuffer();
|
|
909
888
|
const hash = await crypto.subtle.digest('SHA-256', buf);
|
|
910
889
|
return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
911
890
|
}
|
|
912
891
|
|
|
913
|
-
// 比对差异
|
|
914
892
|
async function fpComputeDiff() {
|
|
915
893
|
if (!fpSessionId || !syncDirHandle) return;
|
|
916
894
|
const list = document.getElementById('fp-list');
|
|
@@ -919,23 +897,20 @@
|
|
|
919
897
|
progress.style.display = 'block';
|
|
920
898
|
|
|
921
899
|
try {
|
|
922
|
-
// 获取服务端清单
|
|
923
900
|
progress.textContent = '获取服务器文件清单...';
|
|
924
901
|
const r = await fetch(`/api/files/${fpSessionId}/manifest?path=${encodeURIComponent(fpCurrentPath)}`);
|
|
925
902
|
const manifest = await r.json();
|
|
926
903
|
if (!manifest.ok) throw new Error(manifest.error);
|
|
927
904
|
|
|
928
|
-
// 遍历本地目录
|
|
929
905
|
progress.textContent = '扫描本地文件...';
|
|
930
906
|
const localFiles = await walkLocalDir(syncDirHandle, '');
|
|
931
907
|
|
|
932
908
|
const serverMap = new Map(manifest.files.map(f => [f.name, f]));
|
|
933
909
|
const localMap = new Map(localFiles.map(f => [f.name, f]));
|
|
934
910
|
|
|
935
|
-
const toDownload = [];
|
|
936
|
-
const toUpload = [];
|
|
911
|
+
const toDownload = [];
|
|
912
|
+
const toUpload = [];
|
|
937
913
|
|
|
938
|
-
// 找需要下载的(服务器→本地)
|
|
939
914
|
let checked = 0;
|
|
940
915
|
for (const [name, sf] of serverMap) {
|
|
941
916
|
checked++;
|
|
@@ -946,16 +921,11 @@
|
|
|
946
921
|
} else if (lf.size !== sf.size) {
|
|
947
922
|
toDownload.push({ name, size: sf.size, reason: 'changed' });
|
|
948
923
|
} else if (sf.hash && sf.size < 10 * 1024 * 1024) {
|
|
949
|
-
// 大小相同但可能内容不同,比对哈希(仅 <10MB)
|
|
950
924
|
const file = await lf.handle.getFile();
|
|
951
925
|
const localHash = await hashFile(file);
|
|
952
|
-
if (localHash !== sf.hash) {
|
|
953
|
-
toDownload.push({ name, size: sf.size, reason: 'changed' });
|
|
954
|
-
}
|
|
926
|
+
if (localHash !== sf.hash) toDownload.push({ name, size: sf.size, reason: 'changed' });
|
|
955
927
|
}
|
|
956
928
|
}
|
|
957
|
-
|
|
958
|
-
// 找需要上传的(本地→服务器)
|
|
959
929
|
for (const [name, lf] of localMap) {
|
|
960
930
|
checked++;
|
|
961
931
|
if (checked % 20 === 0) progress.textContent = `比对中... ${checked}/${serverMap.size + localMap.size}`;
|
|
@@ -967,9 +937,7 @@
|
|
|
967
937
|
} else if (sf.hash && lf.size < 10 * 1024 * 1024) {
|
|
968
938
|
const file = await lf.handle.getFile();
|
|
969
939
|
const localHash = await hashFile(file);
|
|
970
|
-
if (localHash !== sf.hash) {
|
|
971
|
-
toUpload.push({ name, size: lf.size, reason: 'changed', handle: lf.handle });
|
|
972
|
-
}
|
|
940
|
+
if (localHash !== sf.hash) toUpload.push({ name, size: lf.size, reason: 'changed', handle: lf.handle });
|
|
973
941
|
}
|
|
974
942
|
}
|
|
975
943
|
|
|
@@ -1018,13 +986,11 @@
|
|
|
1018
986
|
document.getElementById('fp-diff-actions').style.display = toDownload.length || toUpload.length ? 'flex' : 'none';
|
|
1019
987
|
}
|
|
1020
988
|
|
|
1021
|
-
// 同步到本地:从服务器下载差异文件写入本地目录
|
|
1022
989
|
async function fpSyncToLocal() {
|
|
1023
990
|
const { toDownload } = syncDiff;
|
|
1024
991
|
if (!toDownload.length) return;
|
|
1025
992
|
const progress = document.getElementById('fp-progress');
|
|
1026
993
|
progress.style.display = 'block';
|
|
1027
|
-
|
|
1028
994
|
let done = 0;
|
|
1029
995
|
for (const f of toDownload) {
|
|
1030
996
|
progress.textContent = `下载中 ${++done}/${toDownload.length}: ${f.name}`;
|
|
@@ -1033,7 +999,6 @@
|
|
|
1033
999
|
const r = await fetch(`/api/files/${fpSessionId}/download?path=${encodeURIComponent(relPath)}`);
|
|
1034
1000
|
if (!r.ok) continue;
|
|
1035
1001
|
const blob = await r.blob();
|
|
1036
|
-
// 确保子目录存在并写入文件
|
|
1037
1002
|
const parts = f.name.split('/');
|
|
1038
1003
|
let dir = syncDirHandle;
|
|
1039
1004
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
@@ -1043,24 +1008,18 @@
|
|
|
1043
1008
|
const writable = await fileHandle.createWritable();
|
|
1044
1009
|
await writable.write(blob);
|
|
1045
1010
|
await writable.close();
|
|
1046
|
-
} catch (e) {
|
|
1047
|
-
console.error(`同步下载失败 ${f.name}:`, e);
|
|
1048
|
-
}
|
|
1011
|
+
} catch (e) { console.error(`同步下载失败 ${f.name}:`, e); }
|
|
1049
1012
|
}
|
|
1050
|
-
|
|
1051
1013
|
progress.textContent = `已下载 ${done} 个文件到本地`;
|
|
1052
1014
|
setTimeout(() => { progress.style.display = 'none'; }, 3000);
|
|
1053
|
-
await fpComputeDiff();
|
|
1015
|
+
await fpComputeDiff();
|
|
1054
1016
|
}
|
|
1055
1017
|
|
|
1056
|
-
// 同步到服务器:将本地差异文件上传
|
|
1057
1018
|
async function fpSyncToServer() {
|
|
1058
1019
|
const { toUpload } = syncDiff;
|
|
1059
1020
|
if (!toUpload.length) return;
|
|
1060
1021
|
const progress = document.getElementById('fp-progress');
|
|
1061
1022
|
progress.style.display = 'block';
|
|
1062
|
-
|
|
1063
|
-
// 分批上传,每批最多 20 个文件
|
|
1064
1023
|
const BATCH = 20;
|
|
1065
1024
|
let done = 0;
|
|
1066
1025
|
for (let i = 0; i < toUpload.length; i += BATCH) {
|
|
@@ -1068,30 +1027,22 @@
|
|
|
1068
1027
|
progress.textContent = `上传中 ${done}/${toUpload.length}...`;
|
|
1069
1028
|
const form = new FormData();
|
|
1070
1029
|
for (const f of batch) {
|
|
1071
|
-
try {
|
|
1072
|
-
|
|
1073
|
-
form.append('files', file, f.name);
|
|
1074
|
-
} catch (e) {
|
|
1075
|
-
console.error(`读取本地文件失败 ${f.name}:`, e);
|
|
1076
|
-
}
|
|
1030
|
+
try { form.append('files', await f.handle.getFile(), f.name); }
|
|
1031
|
+
catch (e) { console.error(`读取本地文件失败 ${f.name}:`, e); }
|
|
1077
1032
|
}
|
|
1078
1033
|
try {
|
|
1079
1034
|
await fetch(`/api/files/${fpSessionId}/upload?path=${encodeURIComponent(fpCurrentPath)}`, {
|
|
1080
1035
|
method: 'POST', body: form,
|
|
1081
1036
|
});
|
|
1082
|
-
} catch (e) {
|
|
1083
|
-
console.error('上传批次失败:', e);
|
|
1084
|
-
}
|
|
1037
|
+
} catch (e) { console.error('上传批次失败:', e); }
|
|
1085
1038
|
done += batch.length;
|
|
1086
1039
|
}
|
|
1087
|
-
|
|
1088
1040
|
progress.textContent = `已上传 ${done} 个文件到服务器`;
|
|
1089
1041
|
setTimeout(() => { progress.style.display = 'none'; }, 3000);
|
|
1090
|
-
await fpComputeDiff();
|
|
1042
|
+
await fpComputeDiff();
|
|
1091
1043
|
}
|
|
1092
1044
|
|
|
1093
|
-
//
|
|
1094
|
-
// 使用第一个 tab 的 socket 监听全局事件
|
|
1045
|
+
// 监听文件变更通知
|
|
1095
1046
|
function setupFileChangeListener() {
|
|
1096
1047
|
const tab = tabs[0];
|
|
1097
1048
|
if (!tab) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wendongfly/myhi",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "Web-based terminal sharing with chat UI — control your terminal from phone via LAN/Tailscale",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"node": ">=18.0.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"node-pty": "^1.0.0",
|
|
40
|
+
"node-pty": "^1.0.0",
|
|
41
41
|
"socket.io-client": "^4.8.3"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|