@wendongfly/zihi 1.1.3 → 1.1.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/chat.html +166 -2
- package/dist/files.html +2 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/chat.html
CHANGED
|
@@ -203,6 +203,39 @@
|
|
|
203
203
|
.setting-toggle input[type=checkbox]::after { content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; background: #fff; border-radius: 50%; transition: left 0.2s; }
|
|
204
204
|
.setting-toggle input[type=checkbox]:checked::after { left: 18px; }
|
|
205
205
|
|
|
206
|
+
/* ── 文件管理面板 ─────────────────────── */
|
|
207
|
+
#file-panel-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 62; display: none; }
|
|
208
|
+
#file-panel-backdrop.open { display: block; }
|
|
209
|
+
#file-panel {
|
|
210
|
+
position: fixed; top: 0; right: -340px; width: 320px; max-width: 90vw; height: 100%;
|
|
211
|
+
background: #161b22; border-left: 1px solid #30363d; z-index: 63;
|
|
212
|
+
transition: right 0.25s ease; display: flex; flex-direction: column;
|
|
213
|
+
}
|
|
214
|
+
#file-panel.open { right: 0; }
|
|
215
|
+
.fp-header { display: flex; align-items: center; padding: 0.75rem; border-bottom: 1px solid #30363d; gap: 0.5rem; flex-shrink: 0; }
|
|
216
|
+
.fp-header h3 { flex: 1; font-size: 0.9rem; margin: 0; color: #e6edf3; }
|
|
217
|
+
.fp-btn { background: #21262d; color: #e6edf3; border: 1px solid #30363d; border-radius: 6px; padding: 0.3rem 0.6rem; font-size: 0.75rem; cursor: pointer; }
|
|
218
|
+
.fp-btn:active { background: #30363d; }
|
|
219
|
+
.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; }
|
|
220
|
+
.fp-list { flex: 1; overflow-y: auto; padding: 0.25rem 0; }
|
|
221
|
+
.fp-item { display: flex; align-items: center; padding: 0.5rem 0.75rem; gap: 0.5rem; cursor: pointer; font-size: 0.82rem; color: #e6edf3; }
|
|
222
|
+
.fp-item:active { background: #21262d; }
|
|
223
|
+
.fp-item .fp-icon { width: 1.2rem; text-align: center; flex-shrink: 0; }
|
|
224
|
+
.fp-item .fp-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
225
|
+
.fp-item .fp-size { color: #8b949e; font-size: 0.7rem; white-space: nowrap; }
|
|
226
|
+
.fp-item .fp-dl { color: #58a6ff; font-size: 0.7rem; text-decoration: none; padding: 0.2rem 0.4rem; }
|
|
227
|
+
.fp-empty { text-align: center; color: #8b949e; padding: 2rem 0.75rem; font-size: 0.85rem; }
|
|
228
|
+
.fp-drop-zone {
|
|
229
|
+
margin: 0.5rem 0.75rem; padding: 1.5rem; border: 2px dashed #30363d; border-radius: 8px;
|
|
230
|
+
text-align: center; color: #8b949e; font-size: 0.8rem; flex-shrink: 0;
|
|
231
|
+
transition: border-color 0.2s, background 0.2s;
|
|
232
|
+
}
|
|
233
|
+
.fp-drop-zone.dragover { border-color: #58a6ff; background: rgba(88,166,255,0.08); }
|
|
234
|
+
.fp-progress { padding: 0.4rem 0.75rem; font-size: 0.75rem; color: #d29922; flex-shrink: 0; display: none; }
|
|
235
|
+
.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; }
|
|
236
|
+
.fp-sync-bar .fp-btn { font-size: 0.7rem; padding: 0.25rem 0.5rem; }
|
|
237
|
+
.fp-sync-bar .sync-dir { font-size: 0.68rem; color: #8b949e; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
238
|
+
|
|
206
239
|
/* ── 底部选择面板 ─────────────────────── */
|
|
207
240
|
.action-sheet { display: none; position: fixed; inset: 0; z-index: 80; }
|
|
208
241
|
.action-sheet.open { display: flex; align-items: flex-end; justify-content: center; }
|
|
@@ -243,7 +276,7 @@
|
|
|
243
276
|
<div id="session-title">加载中...</div>
|
|
244
277
|
<div id="session-cwd"></div>
|
|
245
278
|
</div>
|
|
246
|
-
<button class="top-btn" onclick="
|
|
279
|
+
<button class="top-btn" onclick="openFilePanel()" title="文件" style="font-size:0.7rem;color:var(--muted)">文件</button>
|
|
247
280
|
<button class="top-btn" onclick="showUsage()" title="用量" style="font-size:0.7rem;color:var(--muted)">用量</button>
|
|
248
281
|
<button class="top-btn" onclick="openSettings()" title="设置">
|
|
249
282
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
|
|
@@ -407,6 +440,27 @@
|
|
|
407
440
|
</div>
|
|
408
441
|
</div>
|
|
409
442
|
|
|
443
|
+
<!-- 文件管理面板 -->
|
|
444
|
+
<div id="file-panel-backdrop" onclick="closeFilePanel()"></div>
|
|
445
|
+
<div id="file-panel">
|
|
446
|
+
<div class="fp-header">
|
|
447
|
+
<h3 id="fp-title">文件管理</h3>
|
|
448
|
+
<button class="fp-btn" onclick="fpUploadClick()">上传</button>
|
|
449
|
+
<button class="fp-btn" onclick="fpStartSync()">同步</button>
|
|
450
|
+
<button class="fp-btn" onclick="closeFilePanel()">关闭</button>
|
|
451
|
+
</div>
|
|
452
|
+
<div class="fp-sync-bar" id="fp-sync-bar" style="display:none">
|
|
453
|
+
<span class="sync-dir" id="fp-sync-dir"></span>
|
|
454
|
+
<button class="fp-btn" onclick="fpPickDir()">换目录</button>
|
|
455
|
+
<button class="fp-btn" onclick="fpExitSync()">退出同步</button>
|
|
456
|
+
</div>
|
|
457
|
+
<div class="fp-path" id="fp-path"></div>
|
|
458
|
+
<div class="fp-list" id="fp-list"></div>
|
|
459
|
+
<div class="fp-progress" id="fp-progress"></div>
|
|
460
|
+
<div class="fp-drop-zone" id="fp-drop">拖拽文件到此处上传,或点击上方按钮</div>
|
|
461
|
+
<input type="file" id="fp-file-input" multiple style="display:none">
|
|
462
|
+
</div>
|
|
463
|
+
|
|
410
464
|
<div id="status-overlay">连接中...</div>
|
|
411
465
|
|
|
412
466
|
<script>
|
|
@@ -1721,7 +1775,117 @@
|
|
|
1721
1775
|
// ── 辅助 ──────────────────────────────────────
|
|
1722
1776
|
function updateViewers(count) { document.getElementById('viewer-count').textContent = count > 1 ? `${count} 人在线` : ''; }
|
|
1723
1777
|
window.goBack = function() { window.location.href = '/'; };
|
|
1724
|
-
|
|
1778
|
+
// ── 文件管理面板 ──────────────────────────────
|
|
1779
|
+
let fpCurrentPath = '';
|
|
1780
|
+
window.openFilePanel = function() {
|
|
1781
|
+
fpCurrentPath = '';
|
|
1782
|
+
fpLoadFiles();
|
|
1783
|
+
document.getElementById('file-panel').classList.add('open');
|
|
1784
|
+
document.getElementById('file-panel-backdrop').classList.add('open');
|
|
1785
|
+
};
|
|
1786
|
+
window.closeFilePanel = function() {
|
|
1787
|
+
document.getElementById('file-panel').classList.remove('open');
|
|
1788
|
+
document.getElementById('file-panel-backdrop').classList.remove('open');
|
|
1789
|
+
};
|
|
1790
|
+
|
|
1791
|
+
async function fpLoadFiles() {
|
|
1792
|
+
const list = document.getElementById('fp-list');
|
|
1793
|
+
list.innerHTML = '<div class="fp-empty">加载中...</div>';
|
|
1794
|
+
try {
|
|
1795
|
+
const r = await fetch('/api/files/list?path=' + encodeURIComponent(fpCurrentPath));
|
|
1796
|
+
const data = await r.json();
|
|
1797
|
+
if (data.error) { list.innerHTML = '<div class="fp-empty">' + escHtml(data.error) + '</div>'; return; }
|
|
1798
|
+
document.getElementById('fp-path').textContent = data.current || '';
|
|
1799
|
+
let html = '';
|
|
1800
|
+
if (data.parent) {
|
|
1801
|
+
html += '<div class="fp-item" onclick="fpNav(\'' + escHtml(data.parent).replace(/'/g, "\\'") + '\')"><span class="fp-icon">📁</span><span class="fp-name">..</span></div>';
|
|
1802
|
+
}
|
|
1803
|
+
for (const e of data.entries) {
|
|
1804
|
+
const full = data.current + '/' + e.name;
|
|
1805
|
+
if (e.type === 'dir') {
|
|
1806
|
+
html += '<div class="fp-item" onclick="fpNav(\'' + escHtml(full).replace(/'/g, "\\'") + '\')"><span class="fp-icon">📁</span><span class="fp-name">' + escHtml(e.name) + '</span></div>';
|
|
1807
|
+
} else {
|
|
1808
|
+
html += '<div class="fp-item"><span class="fp-icon">📄</span><span class="fp-name">' + escHtml(e.name) + '</span><span class="fp-size">' + fpFormatSize(e.size) + '</span><a class="fp-dl" href="/api/files/download?path=' + encodeURIComponent(full) + '" download title="下载">下载</a></div>';
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
list.innerHTML = html || '<div class="fp-empty">空目录</div>';
|
|
1812
|
+
} catch (e) {
|
|
1813
|
+
list.innerHTML = '<div class="fp-empty">加载失败: ' + escHtml(e.message) + '</div>';
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
window.fpNav = function(path) { fpCurrentPath = path; fpLoadFiles(); };
|
|
1818
|
+
|
|
1819
|
+
function fpFormatSize(bytes) {
|
|
1820
|
+
if (bytes == null) return '';
|
|
1821
|
+
if (bytes < 1024) return bytes + ' B';
|
|
1822
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
|
1823
|
+
if (bytes < 1024 * 1024 * 1024) return (bytes / 1024 / 1024).toFixed(1) + ' MB';
|
|
1824
|
+
return (bytes / 1024 / 1024 / 1024).toFixed(1) + ' GB';
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
window.fpUploadClick = function() { document.getElementById('fp-file-input').click(); };
|
|
1828
|
+
|
|
1829
|
+
document.getElementById('fp-file-input').addEventListener('change', function(e) {
|
|
1830
|
+
if (e.target.files.length) fpUploadFiles(e.target.files);
|
|
1831
|
+
e.target.value = '';
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1834
|
+
async function fpUploadFiles(files) {
|
|
1835
|
+
if (!files.length) return;
|
|
1836
|
+
const progress = document.getElementById('fp-progress');
|
|
1837
|
+
progress.style.display = 'block';
|
|
1838
|
+
progress.textContent = '正在上传 ' + files.length + ' 个文件...';
|
|
1839
|
+
try {
|
|
1840
|
+
const form = new FormData();
|
|
1841
|
+
for (const file of files) form.append('files', file, file.webkitRelativePath || file.name);
|
|
1842
|
+
const r = await fetch('/api/files/upload?path=' + encodeURIComponent(fpCurrentPath), { method: 'POST', body: form });
|
|
1843
|
+
const data = await r.json();
|
|
1844
|
+
if (data.ok) {
|
|
1845
|
+
progress.textContent = '已上传 ' + data.files.length + ' 个文件';
|
|
1846
|
+
fpLoadFiles();
|
|
1847
|
+
} else {
|
|
1848
|
+
progress.textContent = '上传失败: ' + (data.error || '未知错误');
|
|
1849
|
+
}
|
|
1850
|
+
} catch (e) {
|
|
1851
|
+
progress.textContent = '上传出错: ' + e.message;
|
|
1852
|
+
}
|
|
1853
|
+
setTimeout(function() { progress.style.display = 'none'; }, 3000);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
// 拖拽上传
|
|
1857
|
+
var fpDropEl = document.getElementById('fp-drop');
|
|
1858
|
+
fpDropEl.addEventListener('dragover', function(e) { e.preventDefault(); fpDropEl.classList.add('dragover'); });
|
|
1859
|
+
fpDropEl.addEventListener('dragleave', function() { fpDropEl.classList.remove('dragover'); });
|
|
1860
|
+
fpDropEl.addEventListener('drop', function(e) {
|
|
1861
|
+
e.preventDefault();
|
|
1862
|
+
fpDropEl.classList.remove('dragover');
|
|
1863
|
+
if (e.dataTransfer.files.length) fpUploadFiles(e.dataTransfer.files);
|
|
1864
|
+
});
|
|
1865
|
+
|
|
1866
|
+
// 文件同步(File System Access API)
|
|
1867
|
+
let syncDirHandle = null;
|
|
1868
|
+
window.fpStartSync = async function() {
|
|
1869
|
+
if (!window.showDirectoryPicker) { alert('当前浏览器不支持文件夹访问,请使用 Chrome 或 Edge'); return; }
|
|
1870
|
+
try {
|
|
1871
|
+
syncDirHandle = await window.showDirectoryPicker({ mode: 'readwrite' });
|
|
1872
|
+
document.getElementById('fp-sync-bar').style.display = 'flex';
|
|
1873
|
+
document.getElementById('fp-sync-dir').textContent = syncDirHandle.name;
|
|
1874
|
+
document.getElementById('fp-drop').style.display = 'none';
|
|
1875
|
+
} catch (e) { if (e.name !== 'AbortError') alert('选择文件夹失败: ' + e.message); }
|
|
1876
|
+
};
|
|
1877
|
+
window.fpPickDir = async function() { await window.fpStartSync(); };
|
|
1878
|
+
window.fpExitSync = function() {
|
|
1879
|
+
syncDirHandle = null;
|
|
1880
|
+
document.getElementById('fp-sync-bar').style.display = 'none';
|
|
1881
|
+
document.getElementById('fp-drop').style.display = '';
|
|
1882
|
+
fpLoadFiles();
|
|
1883
|
+
};
|
|
1884
|
+
|
|
1885
|
+
// 监听文件变更
|
|
1886
|
+
socket.on('files:changed', function() {
|
|
1887
|
+
if (document.getElementById('file-panel').classList.contains('open')) fpLoadFiles();
|
|
1888
|
+
});
|
|
1725
1889
|
function scrollToBottom() { requestAnimationFrame(() => { chatArea.scrollTop = chatArea.scrollHeight; }); }
|
|
1726
1890
|
function trimMessages() {
|
|
1727
1891
|
const msgs = chatArea.querySelectorAll('.msg');
|
package/dist/files.html
CHANGED
|
@@ -576,8 +576,9 @@ function closeUploadSheet() {
|
|
|
576
576
|
|
|
577
577
|
function handleFiles(files) {
|
|
578
578
|
if (!files.length) return;
|
|
579
|
+
const copy = Array.from(files);
|
|
579
580
|
closeUploadSheet();
|
|
580
|
-
uploadFiles(
|
|
581
|
+
uploadFiles(copy);
|
|
581
582
|
}
|
|
582
583
|
|
|
583
584
|
async function uploadFiles(files) {
|
package/dist/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"1.1.
|
|
1
|
+
{"type":"module","version":"1.1.5"}
|