full suport for models and hotloading Plugins

This commit is contained in:
2026-08-04 22:28:39 -05:00
parent be8c1a9ea8
commit 552f570efb
9 changed files with 236 additions and 7 deletions
+54 -2
View File
@@ -3382,10 +3382,11 @@ a.deletelink {
gap: 0.5rem;
padding: 0.35rem 0.6rem;
border-radius: 6px;
background: var(--md-sys-color-surface-container-low, #f7f7f9);
background: var(--md-sys-color-surface-variant, #45475a);
color: var(--md-sys-color-on-surface, #cdd6f4);
font-size: 0.85rem;
}
.mods-item:hover { background: var(--md-sys-color-surface-container, #efeff3); }
.mods-item:hover { background: var(--md-sys-color-surface2, #585b70); }
.mods-name { font-weight: 600; }
.mods-file { color: var(--md-sys-color-on-surface-variant, #777); font-size: 0.78rem; }
.mods-badge {
@@ -3432,3 +3433,54 @@ a.deletelink {
font-size: 0.72rem;
font-weight: 600;
}
/* Plugins tab */
.plugins-warning {
display: flex;
align-items: flex-start;
gap: 0.5rem;
background: #fff4e0;
border: 1px solid #f0c878;
color: #7a4d00;
padding: 0.7rem 0.9rem;
border-radius: 8px;
margin-bottom: 0.9rem;
font-size: 0.85rem;
}
.plugins-toggle {
display: flex;
align-items: center;
gap: 0.7rem;
margin-bottom: 0.9rem;
font-size: 0.85rem;
}
.plugins-toggle .switch { position: relative; display: inline-block; width: 42px; height: 24px; flex: 0 0 auto; }
.plugins-toggle .switch input { opacity: 0; width: 0; height: 0; }
.plugins-toggle .slider {
position: absolute; cursor: pointer; inset: 0;
background: #cbd5e1; border-radius: 999px; transition: background 0.15s;
}
.plugins-toggle .slider:before {
content: ''; position: absolute; height: 18px; width: 18px; left: 3px; top: 3px;
background: #fff; border-radius: 50%; transition: transform 0.15s;
}
.plugins-toggle .switch input:checked + .slider { background: #059669; }
.plugins-toggle .switch input:checked + .slider:before { transform: translateX(18px); }
.plugins-list {
list-style: none; margin: 0; padding: 0;
display: flex; flex-direction: column; gap: 0.25rem;
}
.plugins-list li {
display: flex; align-items: center; gap: 0.5rem;
padding: 0.35rem 0.6rem; border-radius: 6px;
background: var(--md-sys-color-surface-variant, #45475a);
color: var(--md-sys-color-on-surface, #cdd6f4);
font-size: 0.85rem;
}
.plugins-name { font-weight: 600; color: inherit; }
.plugins-badge.autoload {
margin-left: auto;
border-radius: 999px; background: #e0e7ff; color: #3730a3;
padding: 0.1rem 0.5rem; font-size: 0.68rem; font-weight: 600; text-transform: uppercase;
}
.plugins-size { color: var(--md-sys-color-on-surface-variant, #6b7280); font-size: 0.78rem; }
+11
View File
@@ -177,6 +177,7 @@
const member = btn.dataset.member;
const name = btn.dataset.name || member;
const modelFormat = btn.dataset.modelFormat || '.geo.json';
const slug = btn.dataset.projectSlug || '';
const opts = { baseUrl: btn.dataset.baseUrl, vanillaBaseUrl: btn.dataset.vanillaBaseUrl, defaultTexture: btn.dataset.defaultTexture || null };
if (!member || !opts.baseUrl || busy) return;
busy = true;
@@ -223,6 +224,16 @@
busy = false;
return;
}
// Pack plugins to hotload (autoload.txt), unless the user opted out.
const noHotload = slug && localStorage.getItem('packs:no-hotload:' + slug) === '1';
if (!noHotload && btn.dataset.plugins) {
try {
const members = JSON.parse(btn.dataset.plugins);
msg.plugins = members.map(m => ({ member: m, url: assetUrl(opts.baseUrl, m) }));
} catch (e) {
msg.plugins = [];
}
}
if (EMBEDDED_FORMATS.indexOf(modelFormat) !== -1) {
// Textures baked into the file — hand it to Blockbench as-is.
msg.modelFile = { format: modelFormat, content: await fetchModelText(member, opts.baseUrl) };
@@ -103,9 +103,27 @@
}
}
await new Promise((r) => setTimeout(r, 400));
await hotloadPackPlugins(msg.plugins);
notify('model-open', { name: name });
}
// Hotload plugins bundled with the pack (served via pack_asset). A failing
// plugin is reported but never blocks the model preview.
async function hotloadPackPlugins(plugins) {
for (const p of (plugins || [])) {
if (!p || !p.url || !p.member) continue;
try {
const code = await (await fetch(p.url, { headers: { 'Accept': 'text/javascript' } })).text();
if (!code || code.length < 20) continue;
await new Plugin().loadFromFile({ path: p.member, content: code }, true);
notify('plugin-hotloaded', { name: p.member });
} catch (e) {
console.error('packs pack plugin hotload failed', p.member, e);
notify('plugin-error', { error: 'Failed to load plugin ' + p.member });
}
}
}
window.addEventListener('message', (e) => {
if (!e.data || e.data.type !== 'packs-open-model') return;
openModel(e.data).then(