@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/package.json CHANGED
@@ -1 +1,3 @@
1
- {"type":"module","version":"1.3.2"}
1
+ {
2
+ "type": "module"
3
+ }
@@ -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, dirHandle });
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
- const file = await f.handle.getFile();
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",
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": {