Initial curated chimera source from a8ae5b1db07fb49926c94585e2718e5130330974
This commit is contained in:
commit
c737a37f71
531 files changed
+119270
No files matched your search
@@ -0,0 +1,216 @@
|
||||
// ── Payload Manager Settings modal ────────────────────────────────────────────
|
||||
|
||||
$('btn-payload-settings').addEventListener('click', async () => {
|
||||
const s = await window.pork.getSettings();
|
||||
$('pm-local-path').value = s.payloadLocalPath || '';
|
||||
$('pm-remote-path').value = s.payloadRemotePath || '';
|
||||
openAccessibleDialog($('payload-settings-overlay'), $('pm-remote-path'));
|
||||
});
|
||||
|
||||
$('btn-pm-browse-local').addEventListener('click', async () => {
|
||||
const folder = await window.pork.selectFolder();
|
||||
if (folder) $('pm-local-path').value = folder;
|
||||
});
|
||||
|
||||
$('btn-pm-settings-save').addEventListener('click', async () => {
|
||||
await window.pork.setSettings({
|
||||
payloadLocalPath: $('pm-local-path').value.trim(),
|
||||
payloadRemotePath: $('pm-remote-path').value.trim(),
|
||||
});
|
||||
closeAccessibleDialog($('payload-settings-overlay'));
|
||||
});
|
||||
|
||||
['payload-settings-close', 'btn-pm-settings-cancel'].forEach(id =>
|
||||
$(id).addEventListener('click', () => closeAccessibleDialog($('payload-settings-overlay')))
|
||||
);
|
||||
|
||||
$('payload-settings-overlay').addEventListener('click', event => {
|
||||
if (event.target === $('payload-settings-overlay')) closeAccessibleDialog($('payload-settings-overlay'));
|
||||
});
|
||||
$('payload-settings-overlay').addEventListener('keydown', event => {
|
||||
if (event.key === 'Escape') closeAccessibleDialog($('payload-settings-overlay'));
|
||||
});
|
||||
|
||||
// ── Transfer Manager ──────────────────────────────────────────────────────────
|
||||
|
||||
function fmtSpeed(bps) {
|
||||
if (!bps || bps < 0) return '—';
|
||||
if (bps < 1024) return `${Math.round(bps)} B/s`;
|
||||
if (bps < 1048576) return `${(bps / 1024).toFixed(1)} KB/s`;
|
||||
return `${(bps / 1048576).toFixed(1)} MB/s`;
|
||||
}
|
||||
|
||||
let _transferState = { jobs: [], paused: false, maxConcurrent: 1, activeCount: 0, queuedCount: 0, interruptedCount: 0, policy: {} };
|
||||
|
||||
function renderTransferPage(st) {
|
||||
_transferState = st;
|
||||
|
||||
// Stats bar
|
||||
$('tstat-active').textContent = st.activeCount;
|
||||
$('tstat-queued').textContent = st.queuedCount;
|
||||
$('tstat-interrupted').textContent = st.interruptedCount || 0;
|
||||
$('tstat-concurrent').textContent = st.maxConcurrent;
|
||||
|
||||
const slider = $('transfer-concurrent-slider');
|
||||
if (slider && parseInt(slider.value) !== st.maxConcurrent) slider.value = st.maxConcurrent;
|
||||
if ($('transfer-window-start') && document.activeElement !== $('transfer-window-start')) $('transfer-window-start').value = st.policy?.windowStart || '';
|
||||
if ($('transfer-window-end') && document.activeElement !== $('transfer-window-end')) $('transfer-window-end').value = st.policy?.windowEnd || '';
|
||||
if ($('transfer-policy-state')) $('transfer-policy-state').textContent = st.policy?.windowStart && st.policy?.windowEnd
|
||||
? `${st.policy.windowStart}–${st.policy.windowEnd} · ${window.t('transfers.policy.bandwidthUnsupported', 'bandwidth limiting is not supported by the current FTP layer')}`
|
||||
: `${window.t('transfers.policy.noWindow', 'No time window')} · ${window.t('transfers.policy.bandwidthUnsupported', 'bandwidth limiting is not supported by the current FTP layer')}`;
|
||||
|
||||
// Total speed from all active jobs
|
||||
const totalSpeed = st.jobs
|
||||
.filter(j => j.status === 'active')
|
||||
.reduce((sum, j) => sum + (j.progress?.speedBps || 0), 0);
|
||||
$('tstat-speed').textContent = fmtSpeed(totalSpeed);
|
||||
|
||||
// Pause/resume button
|
||||
const pauseBtn = $('btn-transfer-pause');
|
||||
if (pauseBtn) {
|
||||
pauseBtn.textContent = st.paused
|
||||
? `▶ ${window.t('transfers.resumeAll', 'Resume All')}`
|
||||
: `⏸ ${window.t('transfers.pauseAll', 'Pause All')}`;
|
||||
pauseBtn.disabled = !st.paused && !st.jobs.some(job => ['queued', 'active', 'retrying'].includes(job.status));
|
||||
}
|
||||
const clearBtn = $('btn-transfer-clear');
|
||||
if (clearBtn) clearBtn.disabled = !st.jobs.some(job => ['done', 'error', 'cancelled'].includes(job.status));
|
||||
|
||||
// Job list
|
||||
const list = $('transfers-list');
|
||||
const empty = $('transfers-empty');
|
||||
const visibleJobs = st.jobs.filter(j => j.status !== 'cancelled');
|
||||
|
||||
if (!visibleJobs.length) {
|
||||
list.innerHTML = '';
|
||||
empty.hidden = false;
|
||||
return;
|
||||
}
|
||||
empty.hidden = true;
|
||||
|
||||
list.innerHTML = visibleJobs.map(job => {
|
||||
const statusText = {
|
||||
queued: 'Queued',
|
||||
active: 'Transferring…',
|
||||
done: 'Done',
|
||||
error: 'Error',
|
||||
cancelled: 'Cancelled',
|
||||
interrupted: window.t('transfers.status.interrupted', 'Interrupted after restart'),
|
||||
retrying: 'Retrying…',
|
||||
}[job.status] || job.status;
|
||||
|
||||
const typeLabel = { upload: 'UPLOAD', download: 'DOWNLOAD', pork: 'PORK' }[job.type] || (job.type || '').toUpperCase();
|
||||
const typeClass = { upload: 'type-upload', download: 'type-download', pork: 'type-pork' }[job.type] || '';
|
||||
|
||||
const pct = job.progress?.percent || 0;
|
||||
const speed = job.status === 'active' ? fmtSpeed(job.progress?.speedBps) : '';
|
||||
const file = job.progress?.file || '';
|
||||
const filesDone = job.progress?.filesDone || 0;
|
||||
const filesTotal = job.progress?.filesTotal || 0;
|
||||
const transferred = job.progress?.transferred || 0;
|
||||
const total = job.progress?.total || 0;
|
||||
|
||||
const showCancel = job.cancellable !== false && ['queued', 'active', 'retrying'].includes(job.status);
|
||||
const showRetry = ['error', 'interrupted'].includes(job.status) && job.retryable;
|
||||
const showBar = job.status === 'active' || job.status === 'done';
|
||||
|
||||
const currentBytes = job.progress?.currentBytes || 0;
|
||||
const currentFileSize = job.progress?.currentFileSize || 0;
|
||||
|
||||
// File counter or single-file byte progress
|
||||
const sizeStr = filesTotal > 1
|
||||
? `File ${filesDone}/${filesTotal}`
|
||||
: (total > 0 ? `${fmt(transferred)} / ${fmt(total)}` : '');
|
||||
|
||||
// Current-file byte progress for directory uploads (shows activity on large files)
|
||||
const fileByteStr = (filesTotal > 1 && currentFileSize > 0 && job.status === 'active')
|
||||
? `${fmt(currentBytes)} / ${fmt(currentFileSize)}`
|
||||
: '';
|
||||
|
||||
const pctLabel = (showBar && job.status === 'active') ? `${pct}%` : (job.status === 'done' ? '100%' : '');
|
||||
|
||||
return `
|
||||
<div class="transfer-item transfer-item-${job.status}">
|
||||
<div class="transfer-item-header">
|
||||
<span class="transfer-label" title="${escHtml(job.localPath || job.remotePath || '')}">${escHtml(job.label)}</span>
|
||||
<span class="transfer-type-badge ${typeClass}">${typeLabel}</span>
|
||||
<span class="transfer-status-text">${statusText}</span>
|
||||
${showRetry ? `<button class="btn btn-sm btn-retry-transfer" data-job-id="${escHtml(job.id)}">Retry</button>` : ''}
|
||||
${showCancel ? `<button class="btn-cancel-transfer" data-job-id="${job.id}" title="Cancel">×</button>` : ''}
|
||||
</div>
|
||||
${showBar ? `
|
||||
<div class="transfer-progress-wrap">
|
||||
<div class="transfer-progress-bar">
|
||||
<div class="transfer-progress-fill" style="width:${pct}%"></div>
|
||||
</div>
|
||||
${pctLabel ? `<span class="transfer-pct-label">${pctLabel}</span>` : ''}
|
||||
</div>` : ''}
|
||||
<div class="transfer-meta">
|
||||
${speed ? `<span class="transfer-speed">${speed}</span>` : ''}
|
||||
${file ? `<span class="transfer-file">${escHtml(file)}</span>` : ''}
|
||||
${fileByteStr ? `<span class="transfer-sizes">${fileByteStr}</span>` : sizeStr ? `<span class="transfer-sizes">${sizeStr}</span>` : ''}
|
||||
${job.error ? `<span class="transfer-error">${escHtml(job.error)}</span>` : ''}
|
||||
${job.cancellable === false && ['queued', 'active', 'retrying'].includes(job.status) ? '<span class="transfer-error">Upstream offers no safe cancellation; only task status is tracked.</span>' : ''}
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function updateNavTransferBadge(st) {
|
||||
const badge = $('nav-transfer-badge');
|
||||
if (!badge) return;
|
||||
const n = (st.activeCount || 0) + (st.queuedCount || 0);
|
||||
badge.hidden = n === 0;
|
||||
badge.textContent = n;
|
||||
}
|
||||
|
||||
async function loadTransfers() {
|
||||
const st = await window.pork.transferState();
|
||||
renderTransferPage(st);
|
||||
updateNavTransferBadge(st);
|
||||
}
|
||||
|
||||
$('btn-transfer-pause').addEventListener('click', async () => {
|
||||
if (_transferState.paused) await window.pork.transferResume();
|
||||
else await window.pork.transferPause();
|
||||
});
|
||||
|
||||
$('btn-transfer-clear').addEventListener('click', () => window.pork.transferClearDone());
|
||||
|
||||
$('transfer-concurrent-slider').addEventListener('input', async event => {
|
||||
const n = parseInt(event.target.value);
|
||||
$('tstat-concurrent').textContent = n;
|
||||
await window.pork.transferSetConcurrent(n);
|
||||
});
|
||||
|
||||
$('transfers-list').addEventListener('click', event => {
|
||||
const retry = event.target.closest('.btn-retry-transfer');
|
||||
if (retry) {
|
||||
window.pork.transferRetry?.(retry.dataset.jobId).catch(error => showToast(error.message, 'error'));
|
||||
return;
|
||||
}
|
||||
const cancel = event.target.closest('.btn-cancel-transfer');
|
||||
if (cancel?.dataset.jobId) window.pork.transferCancel(cancel.dataset.jobId);
|
||||
});
|
||||
|
||||
$('btn-transfer-policy-save').addEventListener('click', async () => {
|
||||
try {
|
||||
await window.pork.transferSetPolicy?.({
|
||||
windowStart: $('transfer-window-start').value || null,
|
||||
windowEnd: $('transfer-window-end').value || null,
|
||||
});
|
||||
showToast(window.t('transfers.policy.saved', 'Transfer policy saved.'), 'ok');
|
||||
} catch (error) {
|
||||
showToast(error.message, 'error');
|
||||
}
|
||||
});
|
||||
|
||||
window.pork.on('transfer:update', state => {
|
||||
renderTransferPage(state);
|
||||
updateNavTransferBadge(state);
|
||||
});
|
||||
|
||||
window.addEventListener('chimera:language-changed', () => {
|
||||
if (document.querySelector('#page-transfers.active')) renderTransferPage(_transferState);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user