Posterverse: a hidden full-page app for every poster plugin, like Deep Research
/posterverse mounts the Posterverse shell (<posterverse-app>) from the site's own /dx-maps tree as a full-page app with no site header, the same way /deep-research mounts the Research Gallery. It is in no menu and not indexed; ?tab=<row id> opens one plugin. The site's language drives the shell's, and the shell's "Back to homepage" leads to this site's home in the reader's language.
This commit is contained in:
parent
072e0d740e
commit
fd771272f7
12 changed files with 152 additions and 0 deletions
141
app/pages/posterverse/index.vue
Normal file
141
app/pages/posterverse/index.vue
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<script setup lang="ts">
|
||||
// Posterverse: every poster plugin (UPG 100 List Posters, Country Posters, Poster Design and the poster
|
||||
// shelf of experimental plugins) in one full-page app, the poster twin of Deep Research. Like Deep
|
||||
// Research, this page is HIDDEN: it is in no menu and links nowhere, and robots are told not to index
|
||||
// it. It is reached by its address, and ?tab=<row id> opens one plugin directly (the shell reads it).
|
||||
//
|
||||
// The shell is a web component served from this site's OWN /dx-maps/ tree, the same copy the Deep
|
||||
// Research page uses (see DX-MAPS-GALLERY.md): one script, one element, <posterverse-app>. A custom
|
||||
// element's name needs a hyphen, which is why the tag is not just <posterverse>.
|
||||
//
|
||||
// The element is created in onMounted rather than written in the template: it is a custom element
|
||||
// that only exists in the browser, so this keeps it out of SSR and out of Vue's component resolver
|
||||
// without any nuxt.config change.
|
||||
|
||||
import { mapboxTokenIfChosen } from '~/utils/mapProvider'
|
||||
|
||||
// A full-page app on every screen: no layout, so no site header and no footer. The way back to the site
|
||||
// is the shell's own "Back to homepage" button, directly above its Collapse button (home-href below),
|
||||
// so the shell fills the whole screen. The page itself never scrolls (the shell's own panes scroll).
|
||||
definePageMeta({ layout: false })
|
||||
|
||||
const { t, locale, locales } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
|
||||
// The dx-maps tree: NUXT_PUBLIC_DX_MAPS_BASE when set (a maps CDN), otherwise this site's own
|
||||
// /dx-maps/ copy. The script and every embed URL come from the same tree.
|
||||
const DX_MAPS_BASE = ((runtimeConfig.public as { dxMapsBase?: string }).dxMapsBase || '').replace(/\/+$/, '')
|
||||
const POSTERVERSE_SCRIPT = `${DX_MAPS_BASE || '/dx-maps'}/posterverse/posterverse.js`
|
||||
|
||||
useHead({
|
||||
title: () => t('Posterverse'),
|
||||
meta: [{ name: 'robots', content: 'noindex' }],
|
||||
script: [{ src: POSTERVERSE_SCRIPT, defer: true, key: 'posterverse' }]
|
||||
})
|
||||
|
||||
// With no layout, this page sets <html lang dir> itself, as the public layout does for every other
|
||||
// page: in Arabic the shell reads right to left.
|
||||
const dir = computed(() => {
|
||||
const loc = locales.value.find(l => typeof l === 'object' && 'code' in l && l.code === locale.value)
|
||||
return (typeof loc === 'object' && loc && 'dir' in loc && loc.dir) ? loc.dir : 'ltr'
|
||||
})
|
||||
useHead(() => ({
|
||||
htmlAttrs: {
|
||||
lang: locale.value,
|
||||
dir: dir.value
|
||||
}
|
||||
}))
|
||||
|
||||
const host = ref<HTMLElement | null>(null)
|
||||
let shell: HTMLElement | null = null
|
||||
|
||||
onMounted(() => {
|
||||
shell = document.createElement('posterverse-app')
|
||||
// Every embed URL is built at RUNTIME: from NUXT_PUBLIC_DX_MAPS_BASE when set, otherwise from the
|
||||
// origin serving this page, so the same build works on any host (laptop, staging, production) with
|
||||
// nothing hardcoded.
|
||||
shell.setAttribute('tree-base', DX_MAPS_BASE || `${window.location.origin}/dx-maps`)
|
||||
// The site's language drives the shell's (and every plugin's); it follows the language switcher.
|
||||
shell.setAttribute('language', locale.value)
|
||||
// The way back to the site: the shell draws it above its Collapse button, in the site's own words.
|
||||
shell.setAttribute('home-href', localePath('/'))
|
||||
shell.setAttribute('home-label', t('Back to homepage'))
|
||||
// MapLibre (keyless) by default; Mapbox only when NUXT_PUBLIC_MAP_PROVIDER=mapbox and a token is set.
|
||||
// The provider is always explicit: a token alone never selects Mapbox in dx-maps.
|
||||
const shellConfig: Record<string, unknown> = {}
|
||||
const token = mapboxTokenIfChosen(runtimeConfig.public)
|
||||
const provider = token ? 'mapbox' : 'maplibre'
|
||||
shellConfig.provider = provider
|
||||
if (token) shellConfig.tk = token
|
||||
shell.setAttribute('provider', provider)
|
||||
shell.setAttribute('profile-config', JSON.stringify(shellConfig))
|
||||
host.value?.appendChild(shell)
|
||||
})
|
||||
|
||||
watch(locale, (lang) => {
|
||||
shell?.setAttribute('language', lang)
|
||||
shell?.setAttribute('home-href', localePath('/'))
|
||||
shell?.setAttribute('home-label', t('Back to homepage'))
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
shell?.remove()
|
||||
shell = null
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="posterverse-page">
|
||||
<h1 class="visually-hidden">
|
||||
{{ t('Posterverse') }}
|
||||
</h1>
|
||||
<div
|
||||
ref="host"
|
||||
class="posterverse-host"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.posterverse-page {
|
||||
/* 100svh: the phone's SMALL viewport, so the browser's own toolbars never cover the shell. */
|
||||
height: 100svh;
|
||||
}
|
||||
/* The shell takes the whole screen, so there is no page scrollbar. */
|
||||
.posterverse-host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
/* 100%, not the element's own 85vh default: here the shell is the whole page. */
|
||||
.posterverse-host :deep(posterverse-app) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0);
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* Not scoped, and keyed to this page's own element, so it applies only while this page is shown and
|
||||
no other page changes. With no layout, the site's stylesheet may not be loaded here: the page sets
|
||||
the two things it needs itself, no body margin and no page scroll. */
|
||||
html:has(.posterverse-page),
|
||||
html:has(.posterverse-page) body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* The site keeps room for a scrollbar on every page (scrollbar-gutter: stable, base/_globals.scss).
|
||||
This page never scrolls, so on a desktop that room would only be an empty strip at the right edge. */
|
||||
html:has(.posterverse-page) {
|
||||
scrollbar-gutter: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -313,6 +313,7 @@
|
|||
"{0} People Groups": "{0} مجموعة شعبية",
|
||||
"{0} unengaged people groups": "{0} مجموعة شعبية غير مَخْدُومَة",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "هل تريد بحثًا أعمق؟",
|
||||
"Deep Research app": "تطبيق Deep Research",
|
||||
"Back to homepage": "العودة إلى الصفحة الرئيسية"
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@
|
|||
"{0} People Groups": "{0} Volksgruppen",
|
||||
"{0} unengaged people groups": "{0} unberührte Volksgruppen",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "Möchten Sie tiefer recherchieren?",
|
||||
"Deep Research app": "Deep-Research-App",
|
||||
"Back to homepage": "Zurück zur Startseite"
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@
|
|||
"{0} People Groups": "{0} People Groups",
|
||||
"{0} unengaged people groups": "{0} unengaged people groups",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "Want Deeper Research?",
|
||||
"Deep Research app": "Deep Research app",
|
||||
"Back to homepage": "Back to homepage"
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@
|
|||
"{0} People Groups": "{0} grupos étnicos",
|
||||
"{0} unengaged people groups": "{0} grupos étnicos no comprometidos",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "¿Quiere investigar más a fondo?",
|
||||
"Deep Research app": "App Deep Research",
|
||||
"Back to homepage": "Volver a la página de inicio"
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@
|
|||
"{0} People Groups": "{0} peuples",
|
||||
"{0} unengaged people groups": "{0} peuples non engagés",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "Vous voulez aller plus loin dans vos recherches ?",
|
||||
"Deep Research app": "Application Deep Research",
|
||||
"Back to homepage": "Retour à l'accueil"
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@
|
|||
"{0} People Groups": "{0} जनसमूह",
|
||||
"{0} unengaged people groups": "{0} अनसंपर्कित जनसमूह",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "क्या आप और गहन शोध चाहते हैं?",
|
||||
"Deep Research app": "Deep Research ऐप",
|
||||
"Back to homepage": "होमपेज पर वापस जाएँ"
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@
|
|||
"{0} People Groups": "{0} gruppi etnici",
|
||||
"{0} unengaged people groups": "{0} gruppi etnici non coinvolti",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "Vuoi approfondire la ricerca?",
|
||||
"Deep Research app": "App Deep Research",
|
||||
"Back to homepage": "Torna alla home page"
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@
|
|||
"{0} People Groups": "{0} Grupos étnicos",
|
||||
"{0} unengaged people groups": "{0} grupos étnicos não engajados",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "Quer uma pesquisa mais aprofundada?",
|
||||
"Deep Research app": "App Deep Research",
|
||||
"Back to homepage": "Voltar à página inicial"
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@
|
|||
"{0} People Groups": "{0} grupuri etnolingvistice",
|
||||
"{0} unengaged people groups": "{0} grupuri etnolingvistice neangajate misionar",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "Vrei o cercetare mai aprofundată?",
|
||||
"Deep Research app": "Aplicația Deep Research",
|
||||
"Back to homepage": "Înapoi la pagina principală"
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@
|
|||
"{0} People Groups": "{0} народов",
|
||||
"{0} unengaged people groups": "{0} невовлечённых народов",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "Хотите исследовать глубже?",
|
||||
"Deep Research app": "Приложение Deep Research",
|
||||
"Back to homepage": "Вернуться на главную"
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@
|
|||
"{0} People Groups": "{0} 个族群",
|
||||
"{0} unengaged people groups": "{0} 个未接触族群",
|
||||
"Deep Research": "Deep Research",
|
||||
"Posterverse": "Posterverse",
|
||||
"Want Deeper Research?": "想进行更深入的研究?",
|
||||
"Deep Research app": "Deep Research 应用",
|
||||
"Back to homepage": "返回首页"
|
||||
|
|
|
|||
Loading…
Reference in a new issue