SOURCE / PINNED RELEASE
Made of little things.
Black Site
- Release
- 666bc7c74ebd…
- Author-recorded commit
- 05e95af79676…
- License
- LICENSE
- Author’s source reference
- nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-d2225ddc391
Archive hash verified: 1544e072e245c4a8…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import type { ArmorySync, SyncState } from './armory-sync.js';
type SyncActions = Pick<ArmorySync, 'enable' | 'save' | 'restore' | 'importGuest'>;
export function mountArmoryControls(actions: () => SyncActions, customization = false) {
const panels: { panel: HTMLElement; heading: HTMLElement; buttons: HTMLElement; status: HTMLElement; enable: HTMLButtonElement; save: HTMLButtonElement; load: HTMLButtonElement; importGuest: HTMLButtonElement; disclosure: HTMLElement }[] = [];
for (const id of customization ? ['playerAccount'] : ['playerAccount', 'shop']) {
const parent = document.getElementById(id);
if (!parent) continue;
const panel = document.createElement('section');
panel.className = 'armory-sync';
panel.dataset.syncGroup = customization ? 'customization' : 'armory';
panel.setAttribute('aria-label', customization ? 'Player choice sync' : 'Armory sync');
const heading = document.createElement('h3'); heading.textContent = customization ? 'PLAYER CHOICE SYNC' : 'ARMORY SYNC';
const status = document.createElement('p'); status.setAttribute('role', 'status');
const disclosure = document.createElement('p'); disclosure.className = 'sync-disclosure';
disclosure.textContent = customization ? 'Save your character, loadout, equipped armor, difficulty and map to your account. Account choices are public. Each save asks for your approval.' : 'Save permanent unlocks and upgrades to your account for use on another device. Account progress is public. Each save asks for your approval.';
const buttons = document.createElement('div'); buttons.className = 'sync-actions';
const button = (name: string, action: () => void | Promise<void>) => {
const control = document.createElement('button'); control.type = 'button'; control.textContent = name;
control.addEventListener('click', () => { void Promise.resolve().then(action).catch(() => { status.textContent = 'Armory sync failed · Device progress kept'; }); });
buttons.append(control); return control;
};
const enable = button(customization ? 'ENABLE PLAYER CHOICE SYNC' : 'ENABLE ARMORY SYNC', () => actions().enable()); enable.dataset.sync = 'enable';
const save = button(customization ? 'SAVE PLAYER CHOICES' : 'SAVE ARMORY TO ACCOUNT', () => actions().save()); save.dataset.sync = 'save';
const load = button(customization ? 'LOAD ACCOUNT CHOICES' : 'LOAD ACCOUNT ARMORY', () => actions().restore()); load.dataset.sync = 'load';
const importGuest = button(customization ? "USE THIS DEVICE'S CHOICES" : "USE THIS DEVICE'S ARMORY", () => actions().importGuest()); importGuest.dataset.sync = 'import';
panel.append(heading, status, disclosure, buttons);
if (id === 'shop') parent.querySelector('.lbar')?.after(panel); else parent.append(panel);
panels.push({ panel, heading, buttons, status, disclosure, enable, save, load, importGuest });
}
return (state: SyncState) => {
for (const panel of panels) {
panel.panel.hidden = customization && state.unsupported;
panel.panel.classList.toggle('sync-warning', state.unsupported);
panel.heading.hidden = state.unsupported || !state.supported;
panel.buttons.hidden = state.unsupported || !state.supported || (!state.busy && !state.canEnable && !state.canSave && !state.canImport && !state.canLoad);
panel.status.textContent = state.message;
panel.disclosure.hidden = state.unsupported || !state.supported || state.enabled;
panel.enable.hidden = !state.canEnable;
panel.save.hidden = !state.enabled;
panel.save.disabled = !state.canSave;
panel.load.disabled = !state.canLoad;
panel.importGuest.hidden = !state.canImport;
for (const control of [panel.enable, panel.importGuest]) control.disabled = state.busy;
}
};
}
