perf: Minify JSON save keys to reduce serialized state size
Replace verbose key names with short identifiers in collectData/applyData (e.g. name→n, zenit→z, heroicSkills→hs). Nested bond, class, and spell objects are mapped at the boundary so in-memory state and rendering code are unchanged. applyData uses ?? fallbacks to remain compatible with data saved under the old key names. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -248,35 +248,54 @@ function addSpell() { spells.push({}); renderSpells(); }
|
||||
function removeSpell(i) { spells.splice(i, 1); renderSpells(); }
|
||||
|
||||
// ── SAVE / LOAD ────────────────────────────────────
|
||||
// Short key legend (serialized form only; in-memory objects use full names):
|
||||
// n=name, pn=pronouns, id=identity, th=theme, og=origin, tr=traits
|
||||
// lv=level, xp=xp, z=zenit
|
||||
// im=initMod, df=defense, md=magDef
|
||||
// dxb=dexBase, dxc=dexCur, inb=insBase, inc=insCur
|
||||
// mgb=migBase, mgc=migCur, wpb=wlpBase, wpc=wlpCur
|
||||
// hx=hpMax, hc=hpCur, mx=mpMax, mc=mpCur, ix=ipMax, ic=ipCur
|
||||
// fp=fp, bp=backpack
|
||||
// acn=accName, acd=accDesc, amn=armName, amd=armDesc
|
||||
// mhn=mhName, mhd=mhDesc, ohn=ohName, ohd=ohDesc
|
||||
// hs=heroicSkills, rn=ritualsNotes
|
||||
// sa=statusesActive, ma=martialChecked, da=disciplinesChecked
|
||||
// bo=bonds, pc=primaryClasses, oc=otherClasses, sp=spells
|
||||
// Nested bonds: n=name, f=feelings
|
||||
// Nested classes: n=name, b=benefits, s=skills
|
||||
// Nested spells: n=name, nt=notes, mp=mp, tg=targets, dr=duration
|
||||
function collectData() {
|
||||
const get = id => { const el = document.getElementById(id); return el ? el.value : ''; };
|
||||
const statusesActive = [...document.querySelectorAll('.status-item.active-status')].map(el => el.dataset.status);
|
||||
const martialChecked = [...document.querySelectorAll('.martial-item.checked')].map(el => el.querySelector('span').textContent);
|
||||
const disciplinesChecked = [...document.querySelectorAll('.disc-item.checked')].map(el => el.querySelector('span').textContent);
|
||||
const sa = [...document.querySelectorAll('.status-item.active-status')].map(el => el.dataset.status);
|
||||
const ma = [...document.querySelectorAll('.martial-item.checked')].map(el => el.querySelector('span').textContent);
|
||||
const da = [...document.querySelectorAll('.disc-item.checked')].map(el => el.querySelector('span').textContent);
|
||||
|
||||
return {
|
||||
name: get('charName'), pronouns: get('charPronouns'),
|
||||
identity: get('charIdentity'), theme: get('charTheme'), origin: get('charOrigin'),
|
||||
traits: get('charTraits'),
|
||||
level, xp: get('xpCurrent'), zenit: get('zenit'),
|
||||
initMod: get('initMod'), defense: get('defense'), magDef: get('magDef'),
|
||||
dexBase: get('dex-base'), dexCur: get('dex-cur'),
|
||||
insBase: get('ins-base'), insCur: get('ins-cur'),
|
||||
migBase: get('mig-base'), migCur: get('mig-cur'),
|
||||
wlpBase: get('wlp-base'), wlpCur: get('wlp-cur'),
|
||||
hpMax: get('hpMax'), hpCur: get('hpCur'),
|
||||
mpMax: get('mpMax'), mpCur: get('mpCur'),
|
||||
ipMax: get('ipMax'), ipCur: get('ipCur'),
|
||||
n: get('charName'), pn: get('charPronouns'),
|
||||
id: get('charIdentity'), th: get('charTheme'), og: get('charOrigin'),
|
||||
tr: get('charTraits'),
|
||||
lv: level, xp: get('xpCurrent'), z: get('zenit'),
|
||||
im: get('initMod'), df: get('defense'), md: get('magDef'),
|
||||
dxb: get('dex-base'), dxc: get('dex-cur'),
|
||||
inb: get('ins-base'), inc: get('ins-cur'),
|
||||
mgb: get('mig-base'), mgc: get('mig-cur'),
|
||||
wpb: get('wlp-base'), wpc: get('wlp-cur'),
|
||||
hx: get('hpMax'), hc: get('hpCur'),
|
||||
mx: get('mpMax'), mc: get('mpCur'),
|
||||
ix: get('ipMax'), ic: get('ipCur'),
|
||||
fp: get('fpCount'),
|
||||
backpack: get('backpack'),
|
||||
accName: get('acc-name'), accDesc: get('acc-desc'),
|
||||
armName: get('arm-name'), armDesc: get('arm-desc'),
|
||||
mhName: get('mh-name'), mhDesc: get('mh-desc'),
|
||||
ohName: get('oh-name'), ohDesc: get('oh-desc'),
|
||||
heroicSkills: get('heroicSkills'),
|
||||
ritualsNotes: get('ritualsNotes'),
|
||||
statusesActive, martialChecked, disciplinesChecked,
|
||||
bonds, primaryClasses, otherClasses, spells
|
||||
bp: get('backpack'),
|
||||
acn: get('acc-name'), acd: get('acc-desc'),
|
||||
amn: get('arm-name'), amd: get('arm-desc'),
|
||||
mhn: get('mh-name'), mhd: get('mh-desc'),
|
||||
ohn: get('oh-name'), ohd: get('oh-desc'),
|
||||
hs: get('heroicSkills'),
|
||||
rn: get('ritualsNotes'),
|
||||
sa, ma, da,
|
||||
bo: bonds.map(b => ({ n: b.name, f: b.feelings })),
|
||||
pc: primaryClasses.map(c => ({ n: c.name, b: c.benefits, s: c.skills })),
|
||||
oc: otherClasses.map(c => ({ n: c.name, b: c.benefits, s: c.skills })),
|
||||
sp: spells.map(s => ({ n: s.name, nt: s.notes, mp: s.mp, tg: s.targets, dr: s.duration })),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -302,54 +321,74 @@ function loadSheet() {
|
||||
|
||||
function applyData(d) {
|
||||
const set = (id, val) => { const el = document.getElementById(id); if (el && val !== undefined) el.value = val; };
|
||||
set('charName', d.name); set('charPronouns', d.pronouns);
|
||||
set('charIdentity', d.identity); set('charTheme', d.theme); set('charOrigin', d.origin);
|
||||
set('charTraits', d.traits);
|
||||
level = d.level || 1;
|
||||
set('charName', d.n ?? d.name); set('charPronouns', d.pn ?? d.pronouns);
|
||||
set('charIdentity',d.id ?? d.identity); set('charTheme', d.th ?? d.theme);
|
||||
set('charOrigin', d.og ?? d.origin); set('charTraits', d.tr ?? d.traits);
|
||||
level = d.lv ?? d.level ?? 1;
|
||||
document.getElementById('levelDisplay').textContent = level;
|
||||
set('xpCurrent', d.xp); set('zenit', d.zenit);
|
||||
set('initMod', d.initMod); set('defense', d.defense); set('magDef', d.magDef);
|
||||
set('dex-base', d.dexBase); set('dex-cur', d.dexCur);
|
||||
set('ins-base', d.insBase); set('ins-cur', d.insCur);
|
||||
set('mig-base', d.migBase); set('mig-cur', d.migCur);
|
||||
set('wlp-base', d.wlpBase); set('wlp-cur', d.wlpCur);
|
||||
set('hpMax', d.hpMax); set('hpCur', d.hpCur);
|
||||
set('mpMax', d.mpMax); set('mpCur', d.mpCur);
|
||||
set('ipMax', d.ipMax); set('ipCur', d.ipCur);
|
||||
set('fpCount', d.fp);
|
||||
set('backpack', d.backpack);
|
||||
set('acc-name', d.accName); set('acc-desc', d.accDesc);
|
||||
set('arm-name', d.armName); set('arm-desc', d.armDesc);
|
||||
set('mh-name', d.mhName); set('mh-desc', d.mhDesc);
|
||||
set('oh-name', d.ohName); set('oh-desc', d.ohDesc);
|
||||
set('heroicSkills', d.heroicSkills);
|
||||
set('ritualsNotes', d.ritualsNotes);
|
||||
set('xpCurrent', d.xp); set('zenit', d.z ?? d.zenit);
|
||||
set('initMod', d.im ?? d.initMod); set('defense', d.df ?? d.defense);
|
||||
set('magDef', d.md ?? d.magDef);
|
||||
set('dex-base', d.dxb ?? d.dexBase); set('dex-cur', d.dxc ?? d.dexCur);
|
||||
set('ins-base', d.inb ?? d.insBase); set('ins-cur', d.inc ?? d.insCur);
|
||||
set('mig-base', d.mgb ?? d.migBase); set('mig-cur', d.mgc ?? d.migCur);
|
||||
set('wlp-base', d.wpb ?? d.wlpBase); set('wlp-cur', d.wpc ?? d.wlpCur);
|
||||
set('hpMax', d.hx ?? d.hpMax); set('hpCur', d.hc ?? d.hpCur);
|
||||
set('mpMax', d.mx ?? d.mpMax); set('mpCur', d.mc ?? d.mpCur);
|
||||
set('ipMax', d.ix ?? d.ipMax); set('ipCur', d.ic ?? d.ipCur);
|
||||
set('fpCount', d.fp);
|
||||
set('backpack', d.bp ?? d.backpack);
|
||||
set('acc-name', d.acn ?? d.accName); set('acc-desc', d.acd ?? d.accDesc);
|
||||
set('arm-name', d.amn ?? d.armName); set('arm-desc', d.amd ?? d.armDesc);
|
||||
set('mh-name', d.mhn ?? d.mhName); set('mh-desc', d.mhd ?? d.mhDesc);
|
||||
set('oh-name', d.ohn ?? d.ohName); set('oh-desc', d.ohd ?? d.ohDesc);
|
||||
set('heroicSkills', d.hs ?? d.heroicSkills);
|
||||
set('ritualsNotes', d.rn ?? d.ritualsNotes);
|
||||
|
||||
// Statuses
|
||||
const sa = d.sa ?? d.statusesActive ?? [];
|
||||
document.querySelectorAll('.status-item').forEach(el => {
|
||||
const active = (d.statusesActive || []).includes(el.dataset.status);
|
||||
const active = sa.includes(el.dataset.status);
|
||||
el.classList.toggle('active-status', active);
|
||||
el.querySelector('.status-check').textContent = active ? '✗' : '';
|
||||
});
|
||||
|
||||
// Martial
|
||||
const ma = d.ma ?? d.martialChecked ?? [];
|
||||
document.querySelectorAll('.martial-item').forEach(el => {
|
||||
const checked = (d.martialChecked || []).includes(el.querySelector('span').textContent);
|
||||
const checked = ma.includes(el.querySelector('span').textContent);
|
||||
el.classList.toggle('checked', checked);
|
||||
el.querySelector('.martial-box').textContent = checked ? '✓' : '';
|
||||
});
|
||||
|
||||
// Disciplines
|
||||
const da = d.da ?? d.disciplinesChecked ?? [];
|
||||
document.querySelectorAll('.disc-item').forEach(el => {
|
||||
const checked = (d.disciplinesChecked || []).includes(el.querySelector('span').textContent);
|
||||
const checked = da.includes(el.querySelector('span').textContent);
|
||||
el.classList.toggle('checked', checked);
|
||||
el.querySelector('.disc-box').textContent = checked ? '✓' : '';
|
||||
});
|
||||
|
||||
if (d.bonds) { bonds = d.bonds; renderBonds(); }
|
||||
if (d.primaryClasses) { primaryClasses = d.primaryClasses; renderPrimaryClasses(); }
|
||||
if (d.otherClasses) { otherClasses = d.otherClasses; renderOtherClasses(); }
|
||||
if (d.spells) { spells = d.spells; renderSpells(); }
|
||||
const rawBonds = d.bo ?? d.bonds;
|
||||
if (rawBonds) {
|
||||
bonds = rawBonds.map(b => ({ name: b.n ?? b.name, feelings: b.f ?? b.feelings ?? [] }));
|
||||
renderBonds();
|
||||
}
|
||||
const rawPrimary = d.pc ?? d.primaryClasses;
|
||||
if (rawPrimary) {
|
||||
primaryClasses = rawPrimary.map(c => ({ name: c.n ?? c.name, benefits: c.b ?? c.benefits, skills: c.s ?? c.skills }));
|
||||
renderPrimaryClasses();
|
||||
}
|
||||
const rawOther = d.oc ?? d.otherClasses;
|
||||
if (rawOther) {
|
||||
otherClasses = rawOther.map(c => ({ name: c.n ?? c.name, benefits: c.b ?? c.benefits, skills: c.s ?? c.skills }));
|
||||
renderOtherClasses();
|
||||
}
|
||||
const rawSpells = d.sp ?? d.spells;
|
||||
if (rawSpells) {
|
||||
spells = rawSpells.map(s => ({ name: s.n ?? s.name, notes: s.nt ?? s.notes, mp: s.mp, targets: s.tg ?? s.targets, duration: s.dr ?? s.duration }));
|
||||
renderSpells();
|
||||
}
|
||||
|
||||
renderFP();
|
||||
updateXPBar();
|
||||
@@ -363,7 +402,7 @@ function exportSheet() {
|
||||
const blob = new Blob([json], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
const charName = (data.name || 'character').replace(/[^a-z0-9_\- ]/gi, '').trim() || 'character';
|
||||
const charName = (data.n || 'character').replace(/[^a-z0-9_\- ]/gi, '').trim() || 'character';
|
||||
a.href = url;
|
||||
a.download = charName + '-fabula-ultima.json';
|
||||
a.click();
|
||||
|
||||
Reference in New Issue
Block a user