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,268 @@
|
||||
// ── PS Notify page ────────────────────────────────────────────────────────────
|
||||
|
||||
let _psnotifyConfigured = false;
|
||||
|
||||
function applyPsNotifyAvailability(configured, detail = '') {
|
||||
_psnotifyConfigured = configured;
|
||||
['btn-psn-send', 'btn-psn-test', 'btn-psn-push-payload'].forEach(id => {
|
||||
const button = $(id);
|
||||
if (!button) return;
|
||||
button.disabled = !configured;
|
||||
button.title = configured ? '' : 'Configure a PS5 host in Settings before using this action.';
|
||||
});
|
||||
document.querySelectorAll('#page-psnotify [id^="btn-psn-exp-"]').forEach(button => {
|
||||
button.disabled = !configured;
|
||||
button.title = configured ? '' : 'Configure a PS5 host in Settings before using this action.';
|
||||
});
|
||||
document.querySelectorAll('.psnotify-compose-card, .psnotify-payload-card').forEach(card => {
|
||||
card.classList.toggle('is-unavailable', !configured);
|
||||
});
|
||||
const status = $('psn-status');
|
||||
if (!configured && status && !status.textContent) {
|
||||
status.textContent = detail || 'Configure a PS5 host in Settings to enable console actions.';
|
||||
}
|
||||
}
|
||||
|
||||
function enhancePsNotifyLayout() {
|
||||
const page = $('page-psnotify');
|
||||
if (!page || page.dataset.enhanced) return;
|
||||
page.dataset.enhanced = 'true';
|
||||
const header = page.querySelector(':scope > .page-header');
|
||||
header?.insertAdjacentHTML('beforeend', '<p class="page-subtitle psnotify-page-subtitle">Compose, test and review PS5 notifications from one focused workspace.</p><span id="psn-page-readiness" class="operations-status operations-status-unknown">Not checked</span>');
|
||||
const cards = [...page.querySelectorAll(':scope > .card')];
|
||||
const [compose, settings, payload, lab, history] = cards;
|
||||
if (!compose || !settings || !payload || !lab || !history) return;
|
||||
compose.classList.add('psnotify-compose-card');
|
||||
settings.classList.add('psnotify-settings-card');
|
||||
payload.classList.add('psnotify-payload-card');
|
||||
lab.classList.add('psnotify-lab', 'collapsed');
|
||||
history.classList.add('psnotify-history-card');
|
||||
compose.querySelector('.card-header h3').textContent = 'Compose notification';
|
||||
compose.querySelector('.hint').textContent = 'Sent to the PS5 host from your active console profile.';
|
||||
compose.querySelector('.form-grid')?.insertAdjacentHTML('beforebegin', `
|
||||
<div class="psnotify-templates" aria-label="Notification templates">
|
||||
<button class="btn btn-xs" data-psn-template="complete">Build complete</button>
|
||||
<button class="btn btn-xs" data-psn-template="backup">Backup complete</button>
|
||||
<button class="btn btn-xs" data-psn-template="update">Update ready</button>
|
||||
<button class="btn btn-xs" data-psn-template="warning">Attention needed</button>
|
||||
</div>`);
|
||||
compose.querySelector('.form-grid')?.insertAdjacentHTML('afterend', `
|
||||
<div class="psnotify-preview" aria-live="polite">
|
||||
<span>PS5 preview</span>
|
||||
<strong id="psn-preview-message">Your notification appears here</strong>
|
||||
<small id="psn-preview-submessage">Optional supporting detail</small>
|
||||
</div>`);
|
||||
$('psn-message')?.insertAdjacentHTML('afterend', '<span id="psn-message-count" class="psnotify-counter">0/120</span>');
|
||||
$('psn-submessage')?.insertAdjacentHTML('afterend', '<span id="psn-submessage-count" class="psnotify-counter">0/120</span>');
|
||||
settings.querySelector('.card-header h3').textContent = 'Service connection';
|
||||
payload.querySelector('.card-header h3').textContent = 'Notification payload';
|
||||
history.querySelector('.card-header h3').textContent = 'Notification history';
|
||||
lab.querySelector('.card-header')?.insertAdjacentHTML('beforeend', '<button id="psn-lab-toggle" class="btn btn-sm" type="button" aria-expanded="false">Show advanced lab</button>');
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'psnotify-grid';
|
||||
compose.insertAdjacentElement('beforebegin', grid);
|
||||
grid.append(compose);
|
||||
const side = document.createElement('div');
|
||||
side.className = 'psnotify-side';
|
||||
side.append(settings, payload);
|
||||
grid.append(side);
|
||||
history.insertAdjacentElement('afterend', lab);
|
||||
$('psn-lab-toggle')?.addEventListener('click', event => {
|
||||
const expanded = event.currentTarget.getAttribute('aria-expanded') === 'true';
|
||||
event.currentTarget.setAttribute('aria-expanded', String(!expanded));
|
||||
event.currentTarget.textContent = expanded ? 'Show advanced lab' : 'Hide advanced lab';
|
||||
lab.classList.toggle('collapsed', expanded);
|
||||
});
|
||||
const templates = {
|
||||
complete: ['Build complete', 'The latest Chimera task finished successfully.'],
|
||||
backup: ['Backup complete', 'Your PS5 data has been verified and stored.'],
|
||||
update: ['Update ready', 'A reviewed update is ready in Chimera.'],
|
||||
warning: ['Attention needed', 'Open Chimera for details and the recommended next step.'],
|
||||
};
|
||||
compose.querySelector('.psnotify-templates')?.addEventListener('click', event => {
|
||||
const button = event.target.closest('[data-psn-template]');
|
||||
if (!button) return;
|
||||
[$('psn-message').value, $('psn-submessage').value] = templates[button.dataset.psnTemplate];
|
||||
updatePsNotifyPreview();
|
||||
$('psn-message').focus();
|
||||
});
|
||||
}
|
||||
|
||||
function updatePsNotifyPreview() {
|
||||
const message = $('psn-message')?.value || '';
|
||||
const submessage = $('psn-submessage')?.value || '';
|
||||
if ($('psn-message-count')) $('psn-message-count').textContent = `${message.length}/120`;
|
||||
if ($('psn-submessage-count')) $('psn-submessage-count').textContent = `${submessage.length}/120`;
|
||||
if ($('psn-preview-message')) $('psn-preview-message').textContent = message || 'Your notification appears here';
|
||||
if ($('psn-preview-submessage')) $('psn-preview-submessage').textContent = submessage || 'Optional supporting detail';
|
||||
}
|
||||
|
||||
async function loadPsNotify() {
|
||||
enhancePsNotifyLayout();
|
||||
// Populate settings fields from store
|
||||
try {
|
||||
const s = await window.pork.getSettings();
|
||||
$('psn-enabled').checked = s.psnotifyEnabled !== false;
|
||||
$('psn-port').value = s.psnotifyPort || 6969;
|
||||
const readiness = $('psn-page-readiness');
|
||||
if (readiness) {
|
||||
readiness.textContent = s.ftpHost ? `Configured · ${s.ftpHost}:${s.psnotifyPort || 6969}` : 'PS5 host not configured';
|
||||
readiness.className = `operations-status ${s.ftpHost ? 'operations-status-compatible' : 'operations-status-warning'}`;
|
||||
}
|
||||
applyPsNotifyAvailability(Boolean(s.ftpHost));
|
||||
} catch (_) {
|
||||
applyPsNotifyAvailability(false, 'Console configuration could not be loaded.');
|
||||
}
|
||||
updatePsNotifyPreview();
|
||||
await renderPsnHistory();
|
||||
}
|
||||
|
||||
$('psn-message').addEventListener('input', updatePsNotifyPreview);
|
||||
$('psn-submessage').addEventListener('input', updatePsNotifyPreview);
|
||||
$('psn-message').addEventListener('keydown', event => {
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') $('btn-psn-send').click();
|
||||
});
|
||||
|
||||
async function renderPsnHistory() {
|
||||
const hist = await window.pork.psnotifyHistory();
|
||||
const list = $('psn-history');
|
||||
const empty = $('psn-history-empty');
|
||||
if (!hist.length) {
|
||||
list.innerHTML = '';
|
||||
empty.hidden = false;
|
||||
return;
|
||||
}
|
||||
empty.hidden = true;
|
||||
list.innerHTML = hist.slice().reverse().map(e => {
|
||||
const cls = e.ok !== false ? 'psn-ok' : 'psn-fail';
|
||||
const ts = new Date(e.ts).toLocaleTimeString();
|
||||
const sub = e.subMessage ? `<span class="psn-entry-sub">\u2014 ${escHtml(e.subMessage)}</span>` : '';
|
||||
return `<div class="psn-entry ${cls}">
|
||||
<span class="psn-entry-ts">${ts}</span>
|
||||
<span class="psn-entry-msg">${escHtml(e.message)}</span>${sub}
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
$('btn-psn-send').addEventListener('click', async () => {
|
||||
if (!_psnotifyConfigured) return;
|
||||
const msg = $('psn-message').value.trim();
|
||||
const sub = $('psn-submessage').value.trim();
|
||||
if (!msg) { $('psn-status').textContent = 'Message is required.'; return; }
|
||||
const btn = $('btn-psn-send');
|
||||
btn.disabled = true;
|
||||
$('psn-status').textContent = 'Sending\u2026';
|
||||
try {
|
||||
await window.pork.psnotifySend(msg, sub);
|
||||
$('psn-status').textContent = 'Sent \u2713';
|
||||
$('psn-message').value = '';
|
||||
$('psn-submessage').value = '';
|
||||
updatePsNotifyPreview();
|
||||
await renderPsnHistory();
|
||||
} catch (e) {
|
||||
$('psn-status').textContent = 'Error: ' + e.message;
|
||||
}
|
||||
btn.disabled = !_psnotifyConfigured;
|
||||
});
|
||||
|
||||
$('btn-psn-test').addEventListener('click', async () => {
|
||||
if (!_psnotifyConfigured) return;
|
||||
const btn = $('btn-psn-test');
|
||||
btn.disabled = true;
|
||||
$('psn-status').textContent = 'Sending test\u2026';
|
||||
try {
|
||||
await window.pork.psnotifyTest();
|
||||
$('psn-status').textContent = 'Test sent \u2713';
|
||||
if ($('psn-page-readiness')) {
|
||||
$('psn-page-readiness').textContent = 'Online';
|
||||
$('psn-page-readiness').className = 'operations-status operations-status-compatible';
|
||||
}
|
||||
await renderPsnHistory();
|
||||
} catch (e) {
|
||||
$('psn-status').textContent = 'Error: ' + e.message;
|
||||
if ($('psn-page-readiness')) {
|
||||
$('psn-page-readiness').textContent = 'Offline';
|
||||
$('psn-page-readiness').className = 'operations-status operations-status-warning';
|
||||
}
|
||||
}
|
||||
btn.disabled = !_psnotifyConfigured;
|
||||
});
|
||||
|
||||
$('btn-psn-save-settings').addEventListener('click', async () => {
|
||||
const port = parseInt($('psn-port').value) || 6969;
|
||||
await window.pork.setSettings({
|
||||
psnotifyEnabled: $('psn-enabled').checked,
|
||||
psnotifyPort: port,
|
||||
});
|
||||
$('psn-port').value = port;
|
||||
$('psn-status').textContent = 'Settings saved \u2713';
|
||||
});
|
||||
|
||||
$('btn-psn-refresh-history').addEventListener('click', renderPsnHistory);
|
||||
|
||||
// ── PS Notify colour/encoding experiments ────────────────────────────────────────
|
||||
|
||||
async function _sendExp(msg, sub = '') {
|
||||
const el = $('psn-exp-status');
|
||||
if (!_psnotifyConfigured) {
|
||||
el.textContent = 'Configure a PS5 host in Settings first.';
|
||||
return;
|
||||
}
|
||||
el.textContent = 'Sending…';
|
||||
try {
|
||||
await window.pork.psnotifySend(msg, sub);
|
||||
el.textContent = 'Sent \u2713 — check your PS5';
|
||||
await renderPsnHistory();
|
||||
} catch (e) {
|
||||
el.textContent = 'Error: ' + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
$('btn-psn-push-payload').addEventListener('click', async () => {
|
||||
if (!_psnotifyConfigured) return;
|
||||
const btn = $('btn-psn-push-payload');
|
||||
const st = $('psn-push-status');
|
||||
btn.disabled = true;
|
||||
st.textContent = 'Queuing upload\u2026';
|
||||
try {
|
||||
const res = await window.pork.psnotifyPushPayload();
|
||||
st.textContent = `Queued \u2713 \u2014 sending to ${res.remotePath}. Reload the payload on your PS5 then emoji will work.`;
|
||||
} catch (e) {
|
||||
st.textContent = 'Error: ' + e.message;
|
||||
}
|
||||
btn.disabled = !_psnotifyConfigured;
|
||||
});
|
||||
|
||||
// HTML markup
|
||||
$('btn-psn-exp-html-red').addEventListener('click', () =>
|
||||
_sendExp('<font color="red">This is red text</font>', 'HTML <font color> test'));
|
||||
|
||||
$('btn-psn-exp-html-multi').addEventListener('click', () =>
|
||||
_sendExp('<font color="red">Red</font> <font color="green">Green</font> <font color="blue">Blue</font>', 'HTML multi-colour test'));
|
||||
|
||||
$('btn-psn-exp-sony-tag').addEventListener('click', () =>
|
||||
_sendExp('$[color=ff0000]Red$[/color] $[color=00ff00]Green$[/color]', 'Sony $[color] tag test'));
|
||||
|
||||
// Emoji colour squares (4-byte UTF-8 — need TextDecoder patch in customPSNotify.js to arrive intact)
|
||||
$('btn-psn-exp-sq-red').addEventListener('click', () =>
|
||||
_sendExp('\uD83D\uDFE5 Red notification', 'Emoji \uD83D\uDFE5 (U+1F7E5, 4-byte UTF-8)'));
|
||||
|
||||
$('btn-psn-exp-sq-green').addEventListener('click', () =>
|
||||
_sendExp('\uD83D\uDFE9 Green notification', 'Emoji \uD83D\uDFE9 (U+1F7E9, 4-byte UTF-8)'));
|
||||
|
||||
$('btn-psn-exp-sq-blue').addEventListener('click', () =>
|
||||
_sendExp('\uD83D\uDFE6 Blue notification', 'Emoji \uD83D\uDFE6 (U+1F7E6, 4-byte UTF-8)'));
|
||||
|
||||
$('btn-psn-exp-sq-yellow').addEventListener('click', () =>
|
||||
_sendExp('\uD83D\uDFE8 Yellow notification', 'Emoji \uD83D\uDFE8 (U+1F7E8, 4-byte UTF-8)'));
|
||||
|
||||
$('btn-psn-exp-sq-all').addEventListener('click', () =>
|
||||
_sendExp('\uD83D\uDFE5\uD83D\uDFE7\uD83D\uDFE8\uD83D\uDFE9\uD83D\uDFE6\uD83D\uDFEA Colour squares test', 'All 6 colour squares'));
|
||||
|
||||
// ANSI escape codes
|
||||
$('btn-psn-exp-ansi-red').addEventListener('click', () =>
|
||||
_sendExp('\x1b[31mRed ANSI text\x1b[0m', 'ANSI \\x1b[31m colour test'));
|
||||
|
||||
$('btn-psn-exp-ansi-bold').addEventListener('click', () =>
|
||||
_sendExp('\x1b[1mBold ANSI text\x1b[0m', 'ANSI \\x1b[1m bold test'));
|
||||
|
||||
Reference in new issue
Block a user