diff --git a/internal/profile-meta.mjs b/internal/profile-meta.mjs index 038b6c69b..3533d195c 100644 --- a/internal/profile-meta.mjs +++ b/internal/profile-meta.mjs @@ -24,14 +24,16 @@ export function readProfileMeta(dir, profileName) { heavy: meta?.heavy === true, widget: CORNERS.includes(corner) ? { corner } : null, label: name ? name.trim() : '', + // Where the plugin runs ("web", "desktop"…); absent = web. A desktop-only plugin is never a gallery tab. + targets: Array.isArray(meta?.targets) ? meta.targets.filter((t) => typeof t === 'string') : null, } - } catch { return { stage: 'experimental', heavy: false, widget: null, label: '' } } + } catch { return { stage: 'experimental', heavy: false, widget: null, label: '', targets: null } } } /** The optional manifest keys for one profile (stage always; heavy / widget only when set). */ export function profileMetaFields(dir, profileName) { const m = readProfileMeta(dir, profileName) - return { stage: m.stage, ...(m.label ? { label: m.label } : {}), ...(m.heavy ? { heavy: true } : {}), ...(m.widget ? { widget: m.widget } : {}) } + return { stage: m.stage, ...(m.label ? { label: m.label } : {}), ...(m.targets ? { targets: m.targets } : {}), ...(m.heavy ? { heavy: true } : {}), ...(m.widget ? { widget: m.widget } : {}) } } /** A bundle is production if ANY of its profiles says so. */ diff --git a/plugin-designs/doxa-maps-spa/doxa-maps-spa.vue b/plugin-designs/doxa-maps-spa/doxa-maps-spa.vue index fe27e16b8..bbc738257 100644 --- a/plugin-designs/doxa-maps-spa/doxa-maps-spa.vue +++ b/plugin-designs/doxa-maps-spa/doxa-maps-spa.vue @@ -599,6 +599,10 @@ function experimentalTabsFromManifest(manifest) { // so this chooses which of its profiles that row opens on. const profiles = Array.isArray(b.profiles) ? b.profiles : [] if (!profiles.length) continue + // Not for the web: a plugin whose every profile is a corner widget or declares targets without "web" + // (Rudder: desktop + the Plugin Studio's corner). As a tab it probed a backend that a static host + // does not have, and every embed of the gallery logged three 404s. + if (profiles.every(p => p.widget || (Array.isArray(p.targets) && !p.targets.includes('web')))) continue const entry = profiles.find(p => p.isMap) || profiles.find(p => p.profile === id) || profiles[0] // A discovered tab nests the same way a curated one does: its ELEMENT, loaded from the bundle's // own scriptUrl. Both are read straight off the manifest, so this path needs no knowledge of the diff --git a/plugin-designs/rudder-voice/rudder-voice.vue b/plugin-designs/rudder-voice/rudder-voice.vue index 69cb5735c..a0cb2087d 100644 --- a/plugin-designs/rudder-voice/rudder-voice.vue +++ b/plugin-designs/rudder-voice/rudder-voice.vue @@ -553,7 +553,17 @@ function onKey(ev) { async function fetchStatus() { try { const r = await fetch(apiBase.value + '/status'); if (r.ok) applyStatus(await r.json()) } catch {} } +// A backend can only exist on the dev server (bun run dev), in the desktop app (Tauri), or where the host +// says so (an explicit apiBase). A built page on a static host has none, so it makes NO network call there: +// it probed /api/rudder anyway and logged three 404s on every page that carried it. +const backendKnown = !!cfg.value.apiBase || import.meta.env.DEV || + (typeof window !== 'undefined' && (!!window.__TAURI__ || !!window.__TAURI_INTERNALS__ || location.protocol === 'tauri:')) onMounted(() => { + if (!backendKnown) { + engine.value = 'offline' + window.addEventListener('keydown', onKey) + return + } fetchStatus() connect() refreshDevices()