'use strict';
// ── Dashboard Interactive Mini-Widgets ────────────────────────────────────────
// Renders and drives interactive tool tiles pinned to the dashboard.
// Called from connection.js renderPinnedTiles() for cards with type:'widget'.
// Off-screen canvas used by the XAvatar widget for pixel conversion
const _dwXaCanvas = document.createElement('canvas');
let _dwXaFilename = 'avatar';
// ── Render ─────────────────────────────────────────────────────────────────────
// Returns an innerHTML string for a given pinnable card.
function _renderWidgetTile(card) {
switch (card.id) {
case 'psnotify':
return `
📢 Send Notification
`;
case 'payload-mgr':
return `
⇨ Quick Payload Send
Port
`;
case 'xavatar-convert':
return `
👤 XAvatar Quick Send
Drop image or click to browse
`;
case 'dash-savemgr':
return `
💾 Save Manager
Click Reload to fetch saves.
`;
default:
// Fallback: plain nav tile for unknown widget IDs
return `
${escHtml(card.label)}
${escHtml(card.hint)}
`;
}
}
// ── Post-render init ───────────────────────────────────────────────────────────
// Called immediately after the tile is appended to the DOM.
function _initWidgetTile(card, tileEl) {
switch (card.id) {
case 'payload-mgr':
_dwPopulatePayloadSelect(tileEl);
break;
case 'xavatar-convert':
_dwWireXavatarDrop(tileEl);
break;
default:
break;
}
}
// ── Payload select population ──────────────────────────────────────────────────
async function _dwPopulatePayloadSelect(tileEl) {
const sel = tileEl.querySelector('[data-dw-key="payload-file"]');
if (!sel) return;
try {
const files = await window.pork.payloadListLocal();
if (!files.length) {
sel.innerHTML = '';
return;
}
sel.innerHTML = files.map(f =>
``
).join('');
} catch (_) {
sel.innerHTML = '';
}
}
// ── XAvatar drop-zone wiring ───────────────────────────────────────────────────
function _dwWireXavatarDrop(tileEl) {
const drop = tileEl.querySelector('[data-dw-drop="xavatar"]');
if (!drop) return;
drop.addEventListener('click', () => {
const inp = document.createElement('input');
inp.type = 'file';
inp.accept = 'image/*';
inp.onchange = e => {
const file = e.target.files[0];
if (file) _dwLoadXavatarFile(file, tileEl);
};
inp.click();
});
drop.addEventListener('dragover', e => { e.preventDefault(); drop.classList.add('dw-drag-over'); });
drop.addEventListener('dragleave', () => drop.classList.remove('dw-drag-over'));
drop.addEventListener('drop', e => {
e.preventDefault();
drop.classList.remove('dw-drag-over');
const file = e.dataTransfer?.files[0];
if (file && file.type.startsWith('image/')) _dwLoadXavatarFile(file, tileEl);
});
}
async function _dwLoadXavatarFile(file, tileEl) {
const status = tileEl.querySelector('[data-dw-status="xavatar"]');
const sendBtn = tileEl.querySelector('[data-dw-action="xavatar-send"]');
const drop = tileEl.querySelector('[data-dw-drop="xavatar"]');
if (status) status.textContent = 'Loading…';
try {
const bmp = await createImageBitmap(file);
_dwXaCanvas.width = bmp.width;
_dwXaCanvas.height = bmp.height;
_dwXaCanvas.getContext('2d').drawImage(bmp, 0, 0);
_dwXaFilename = file.name.replace(/\.[^.]+$/, '');
if (drop) drop.textContent = file.name;
if (sendBtn) sendBtn.disabled = false;
if (status) status.textContent = 'Ready';
} catch (err) {
if (status) status.textContent = 'Load failed';
}
}
// ── Save Manager ───────────────────────────────────────────────────────────────
async function _dwSavemgrLoad(btn) {
const tile = btn.closest('.dash-pinned-tile');
const listEl = tile?.querySelector('[data-dw-list="savemgr"]');
const status = tile?.querySelector('[data-dw-status="savemgr"]');
if (!listEl) return;
btn.disabled = true;
btn.textContent = 'Loading…';
if (status) status.textContent = '';
try {
const r = await window.pork.savemgrRequest('/api/saves');
const saves = typeof r === 'string' ? JSON.parse(r) : r;
if (!saves || !saves.length) {
listEl.textContent = 'No saves found.';
if (status) status.textContent = '0 saves';
} else {
listEl.innerHTML = saves.slice(0, 8).map((s, i) =>
`