diff --git a/docs/MODULE-GUIDE.md b/docs/MODULE-GUIDE.md index c49e425..16c7927 100644 --- a/docs/MODULE-GUIDE.md +++ b/docs/MODULE-GUIDE.md @@ -501,7 +501,7 @@ module.exports = { return config.pages.map((page) => ({ id: page.name, // stable id, stored with the tile name: page.name, // picker label AND the tile's title - description: page.url, // optional line under the label + description: page.url, // optional; the picker's tooltip settings: { page: page.name }, // preset instance settings defaultSize: { w: 4, h: 3 }, // optional per-entry size overrides minSize: { w: 2, h: 2 }, @@ -516,6 +516,10 @@ module.exports = { so a dead upstream can't break the picker. - `instanceSchema` / `instanceGroups` on an entry replace the module's for tiles added from it — settings that fit that tile, nothing else. +- `aliases: ['old-id', …]` on an entry names earlier variant ids it stands + for: a tile added under one of them (a layout saved by an older version of + your module) gets this entry's settings too. A tile whose variant matches + no entry and no alias falls back to the module's full `instanceSchema`. - Entry `description`s are tooltips in the + picker now, not visible lines — one short sentence. - `single: true` on an entry allows at most one such tile per dashboard: the diff --git a/modules/propresenter-timers/server.js b/modules/propresenter-timers/server.js index 10dc67f..7ec8414 100644 --- a/modules/propresenter-timers/server.js +++ b/modules/propresenter-timers/server.js @@ -1136,7 +1136,9 @@ module.exports = { const description = src.kind === 'ltc' ? 'The incoming LTC timecode on its own' : src.kind === 'clock' ? 'The time of day on its own' : `This ${src.label} ${KIND_WORD[t.kind] || 'timer'} on its own`; - out.push({ id: `solo:${src.id}:${t.id}`, name: t.label, description, ...oneCard, ...gearFor(src, t) }); + // Layouts from before 1.3.0 hold 'timer:' / 'ltc' — the same tiles. + const aliases = src.id === 'propresenter' ? [`timer:${t.id}`] : src.kind === 'ltc' ? ['ltc'] : []; + out.push({ id: `solo:${src.id}:${t.id}`, name: t.label, description, ...oneCard, ...gearFor(src, t), ...(aliases.length ? { aliases } : {}) }); } } return out; diff --git a/public/shell.js b/public/shell.js index 056f3a6..16ec6a7 100644 --- a/public/shell.js +++ b/public/shell.js @@ -1094,8 +1094,10 @@ function colorHex(value, fallback) { Same for the groups that arrange them. */ function tileSchema(tile, man) { const own = (v) => (v && typeof v === 'object' ? v : null); - const entry = tile?.variant && Array.isArray(man?.tiles) - ? man.tiles.find((e) => String(e.id) === String(tile.variant)) + const variant = String(tile?.variant || ''); + const entry = variant && Array.isArray(man?.tiles) + ? man.tiles.find((e) => String(e.id) === variant) + || man.tiles.find((e) => Array.isArray(e.aliases) && e.aliases.includes(variant)) : null; return { schema: own(entry?.instanceSchema) || own(man?.instanceSchema) || {}, diff --git a/server.js b/server.js index 7344045..9b7de98 100644 --- a/server.js +++ b/server.js @@ -471,6 +471,12 @@ function sanitizeTileEntry(raw) { for (const key of ['instanceSchema', 'instanceGroups']) { if (raw[key] && typeof raw[key] === 'object') out[key] = raw[key]; } + // Earlier variant ids this entry stands for, so a tile added under one of + // them (a layout from an older module version) gets this entry's settings. + if (Array.isArray(raw.aliases)) { + const aliases = raw.aliases.map((a) => String(a)).filter(Boolean); + if (aliases.length) out.aliases = aliases; + } for (const key of ['minSize', 'defaultSize']) { const size = raw[key]; if (size && Number(size.w) > 0 && Number(size.h) > 0) {