openvibers/openvibe.games Commits
The OpenVibe Frame on openvibe.games: public/chrome.js is frame.js (__ovgFrame, ovg-frame-hidden, ovg-frame-toggle); the portal shows what shipped (the network's shared shipped views) and the footer's Updates link opens the Games log on openvibe.network
Changes
@@@ -21,14 +21,14 @@<meta property="og:url" content="https://openvibe.games/editor" /><meta property="og:image" content="https://openvibe.games/og-image.png" /><meta name="twitter:card" content="summary_large_image" />-- <!-- Shared network chrome: the user's theme before first paint, then navbar + footer. -->++ <!-- The OpenVibe Frame: the user's theme before first paint, then navbar + footer. --><script src="https://openvibe.network/shared/theme-loader.js"></script><script src="https://openvibe.network/shared/navbar.js" defer></script><script src="https://openvibe.network/shared/footer.js" defer></script><script>-- window.__ovgChrome = { page: 'editor', collapsible: true, toggleFromStart: true }++ window.__ovgFrame = { page: 'editor', collapsible: true, toggleFromStart: true }</script>-- <script src="/chrome.js" defer></script>++ <script src="/frame.js" defer></script></head><body><!--@@@ -8,7 +8,7 @@name="description"content="The games arm of the OpenVibe network. Play Scraplandia — scrappy multiplayer survival-building in Scrap City — in your browser, no download."/>-- <!-- Shared network chrome: the user's theme before first paint, then navbar + footer. -->++ <!-- The OpenVibe Frame: the user's theme before first paint, then navbar + footer. --><script src="https://openvibe.network/shared/theme-loader.js"></script><script src="https://openvibe.network/shared/navbar.js" defer></script><script src="https://openvibe.network/shared/footer.js" defer></script>@@@ -377,7 +377,7 @@color: var(--text-faint);}-- /* ── shared chrome ───────────────────────────────────────────── */++ /* ── the OpenVibe Frame ───────────────────────────────────────────── */#navbar-mount {position: relative;z-index: 5;@@@ -470,13 +470,21 @@</div></section>++ <!-- What shipped on OpenVibe.Games: the network's shared "shipped" views (openvibe-shared++ shipped.js, which the shared footer loads), the same on every OpenVibe site. -->++ <section class="ov-shipped-home" aria-label="What shipped" style="max-width:1100px;margin:0 auto;padding:0 20px">++ <a data-ov-shipped="latest" data-service="games" href="https://openvibe.network/updates?site=games" hidden></a>++ <div data-ov-shipped="list" data-service="games" data-limit="5" data-more="https://openvibe.network/updates?site=games" data-title="Recently shipped on OpenVibe.Games" hidden></div>++ <noscript><p><a href="https://openvibe.network/updates?site=games">What shipped recently</a></p></noscript>++ </section>++<!-- Shared cross-site footer (openvibe.network/shared/footer.js): network links, legal, signed-in row. --><footer id="ov-footer" class="ovf"></footer><script>-- window.__ovgChrome = { page: 'portal' }++ window.__ovgFrame = { page: 'portal' }</script>-- <script src="/chrome.js" defer></script>++ <script src="/frame.js" defer></script><script>// Dev convenience: on localhost the play. vhost doesn't exist, so PLAY// buttons fall back to the same-origin /play route (Vite alias / server).@@@ -8,7 +8,7 @@name="description"content="Play Scraplandia: scrappy multiplayer survival-building in and around Scrap City, in your browser, no download. Part of the OpenVibe network."/>-- <!-- Shared network chrome: the user's theme before first paint, then navbar + footer. -->++ <!-- The OpenVibe Frame: the user's theme before first paint, then navbar + footer. --><script src="https://openvibe.network/shared/theme-loader.js"></script><script src="https://openvibe.network/shared/navbar.js" defer></script><script src="https://openvibe.network/shared/footer.js" defer></script>@@@ -60,15 +60,15 @@</script><link rel="stylesheet" href="/src/ui/styles.css" /><script>-- window.__ovgChrome = { page: 'play', title: 'Scraplandia', collapsible: true }++ window.__ovgFrame = { page: 'play', title: 'Scraplandia', collapsible: true }</script>-- <script src="/chrome.js" defer></script>++ <script src="/frame.js" defer></script></head><body><!--The shared navbar mounts at the top; the game fills the stage below it.-- Once the player is in the world (the `ovg:playing` event) the chrome-- folds away and a small floating button brings it back — see chrome.js.++ Once the player is in the world (the `ovg:playing` event) the Frame++ folds away and a small floating button brings it back — see frame.js.--><div id="navbar-mount"></div><div id="stage">ddeleted file mode 100644@@@ -1,153 +0,0 @@--/**-- * OpenVibe.Games — the shared network chrome (navbar + footer) on every page.-- *-- * The navbar and footer themselves come from openvibe.network/shared/*.js,-- * loaded by each page's <head>; this file only tells them who we are: the-- * site's links, where sign-in/sign-out live, and the session the game keeps-- * in the `ovg_sso` cookie / localStorage entry (set by /auth/callback).-- *-- * Pages configure it with `window.__ovgChrome = { page, title, collapsible }`-- * before this script runs:-- * page 'portal' | 'play' | 'editor'-- * title recorded to the signed-in user's network history (play only)-- * collapsible the chrome folds away once the game starts (the `ovg:playing`-- * event) and a small floating button brings it back.-- * toggleFromStart show that button immediately (the editor), not only after-- * `ovg:playing`.-- */--;(function () {-- 'use strict'-- var NETWORK = 'https://openvibe.network'-- var cfg = window.__ovgChrome || {}-- var local = ['localhost', '127.0.0.1'].indexOf(location.hostname) !== -1-- var onPlayHost = location.hostname.indexOf('play.') === 0-- var PORTAL = local || !onPlayHost ? '/' : 'https://openvibe.games/'-- var PLAY = local ? '/play' : onPlayHost ? '/' : 'https://play.openvibe.games/'-- var EDITOR = local || !onPlayHost ? '/editor' : 'https://openvibe.games/editor'---- function cookie(name) {-- var m = document.cookie.match(new RegExp('(?:^|;\\s*)' + name + '=([^;]*)'))-- try {-- return m ? decodeURIComponent(m[1]) : null-- } catch (e) {-- return m ? m[1] : null-- }-- }-- /** The openvibe.network access token this site holds, if any. */-- function sessionToken() {-- var t = null-- try {-- t = localStorage.getItem('ovg_sso')-- } catch (e) {-- /* storage unavailable */-- }-- return t || cookie('ovg_sso') || undefined-- }---- var here = location.pathname + location.search-- var links = [-- { label: 'Games', href: PORTAL },-- { label: 'Scraplandia', href: PLAY },-- ]-- var footerLinks = [-- {-- heading: 'Games',-- items: [-- { label: 'All games', href: PORTAL },-- { label: 'Play Scraplandia', href: PLAY },-- { label: 'Map editor', href: EDITOR },-- ],-- },-- {-- heading: 'Account',-- items: [-- { label: 'Sign in', href: '/auth/login?next=' + encodeURIComponent(here) },-- { label: 'Sign out', href: '/auth/logout?next=' + encodeURIComponent(location.pathname) },-- { label: 'Themes', href: NETWORK + '/themes' },-- ],-- },-- ]---- function initNavbar() {-- var opts = {-- service: 'games',-- apiBase: NETWORK,-- token: sessionToken(),-- // Same-origin fallback: resolves the ovg_sso cookie server-side when the-- // token is not readable here (or the Network rejects it client-side).-- sessionUrl: '/auth/me',-- loginUrl: '/auth/login?next=' + encodeURIComponent(here),-- onLogout: function () {-- location.href = '/auth/logout?next=' + encodeURIComponent(location.pathname)-- },-- links: links,-- menu: { after: [{ label: 'Map editor', href: EDITOR, icon: 'fa-map' }] },-- // One prompt=none attempt per tab when the browser says it has an OpenVibe account.-- silentLogin: '/auth/login?silent=1&next={url}',-- }-- if (cfg.page === 'play') opts.history = { type: 'game', title: cfg.title || 'Scraplandia' }-- window.OpenVibeNavbar.init(opts)-- }---- function initFooter() {-- if (!document.getElementById('ov-footer')) return-- window.OpenVibeFooter.init({-- service: 'games',-- variant: cfg.page === 'portal' ? 'full' : 'compact',-- links: footerLinks,-- mount: '#ov-footer',-- })-- }---- // ── Collapsible chrome for the game / editor pages ─────────────────────-- // The canvas is sized by CSS below the navbar, so showing or hiding the-- // chrome is a class flip plus a resize event for the engine.-- function setupCollapse() {-- var stage = document.getElementById('stage')-- if (!stage) return-- var btn = document.createElement('button')-- btn.id = 'ovg-chrome-toggle'-- btn.type = 'button'-- btn.setAttribute('aria-label', 'Show or hide the OpenVibe navigation')-- btn.innerHTML = '<span class="ovg-chrome-toggle-mark">☰</span> OpenVibe'-- // The game page reveals it once play starts; the editor has it from the start.-- btn.hidden = !cfg.toggleFromStart-- function apply(hidden) {-- document.body.classList.toggle('ovg-chrome-hidden', hidden)-- btn.classList.toggle('is-hidden', hidden)-- btn.title = hidden ? 'Show the OpenVibe navigation' : 'Hide the OpenVibe navigation'-- window.dispatchEvent(new Event('resize'))-- }-- btn.addEventListener('click', function () {-- apply(!document.body.classList.contains('ovg-chrome-hidden'))-- })-- stage.appendChild(btn)-- window.addEventListener('ovg:playing', function () {-- btn.hidden = false-- apply(true)-- })-- }---- var tries = 0-- function boot() {-- if (!window.OpenVibeNavbar || !window.OpenVibeFooter) {-- // The shared scripts are deferred; if the Network is unreachable the-- // page simply runs without its chrome.-- if (++tries < 60) setTimeout(boot, 100)-- return-- }-- try {-- initNavbar()-- } catch (e) {-- /* navbar optional */-- }-- try {-- initFooter()-- } catch (e) {-- /* footer optional */-- }-- }-- if (cfg.collapsible) setupCollapse()-- if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot)-- else boot()--})()nnew file mode 100644@@@ -0,0 +1,154 @@++/**++ * OpenVibe.Games — the OpenVibe Frame (navbar + footer) on every page.++ *++ * The navbar and footer themselves come from openvibe.network/shared/*.js,++ * loaded by each page's <head>; this file only tells them who we are: the++ * site's links, where sign-in/sign-out live, and the session the game keeps++ * in the `ovg_sso` cookie / localStorage entry (set by /auth/callback).++ *++ * Pages configure it with `window.__ovgFrame = { page, title, collapsible }`++ * before this script runs:++ * page 'portal' | 'play' | 'editor'++ * title recorded to the signed-in user's network history (play only)++ * collapsible the Frame folds away once the game starts (the `ovg:playing`++ * event) and a small floating button brings it back.++ * toggleFromStart show that button immediately (the editor), not only after++ * `ovg:playing`.++ */++;(function () {++ 'use strict'++ var NETWORK = 'https://openvibe.network'++ var cfg = window.__ovgFrame || {}++ var local = ['localhost', '127.0.0.1'].indexOf(location.hostname) !== -1++ var onPlayHost = location.hostname.indexOf('play.') === 0++ var PORTAL = local || !onPlayHost ? '/' : 'https://openvibe.games/'++ var PLAY = local ? '/play' : onPlayHost ? '/' : 'https://play.openvibe.games/'++ var EDITOR = local || !onPlayHost ? '/editor' : 'https://openvibe.games/editor'++++ function cookie(name) {++ var m = document.cookie.match(new RegExp('(?:^|;\\s*)' + name + '=([^;]*)'))++ try {++ return m ? decodeURIComponent(m[1]) : null++ } catch (e) {++ return m ? m[1] : null++ }++ }++ /** The openvibe.network access token this site holds, if any. */++ function sessionToken() {++ var t = null++ try {++ t = localStorage.getItem('ovg_sso')++ } catch (e) {++ /* storage unavailable */++ }++ return t || cookie('ovg_sso') || undefined++ }++++ var here = location.pathname + location.search++ var links = [++ { label: 'Games', href: PORTAL },++ { label: 'Scraplandia', href: PLAY },++ ]++ var footerLinks = [++ {++ heading: 'Games',++ items: [++ { label: 'All games', href: PORTAL },++ { label: 'Play Scraplandia', href: PLAY },++ { label: 'Map editor', href: EDITOR },++ ],++ },++ {++ heading: 'Account',++ items: [++ { label: 'Sign in', href: '/auth/login?next=' + encodeURIComponent(here) },++ { label: 'Sign out', href: '/auth/logout?next=' + encodeURIComponent(location.pathname) },++ { label: 'Themes', href: NETWORK + '/themes' },++ ],++ },++ ]++++ function initNavbar() {++ var opts = {++ service: 'games',++ apiBase: NETWORK,++ token: sessionToken(),++ // Same-origin fallback: resolves the ovg_sso cookie server-side when the++ // token is not readable here (or the Network rejects it client-side).++ sessionUrl: '/auth/me',++ loginUrl: '/auth/login?next=' + encodeURIComponent(here),++ onLogout: function () {++ location.href = '/auth/logout?next=' + encodeURIComponent(location.pathname)++ },++ links: links,++ menu: { after: [{ label: 'Map editor', href: EDITOR, icon: 'fa-map' }] },++ // One prompt=none attempt per tab when the browser says it has an OpenVibe account.++ silentLogin: '/auth/login?silent=1&next={url}',++ }++ if (cfg.page === 'play') opts.history = { type: 'game', title: cfg.title || 'Scraplandia' }++ window.OpenVibeNavbar.init(opts)++ }++++ function initFooter() {++ if (!document.getElementById('ov-footer')) return++ window.OpenVibeFooter.init({++ service: 'games',++ variant: cfg.page === 'portal' ? 'full' : 'compact',++ links: footerLinks,++ mount: '#ov-footer',++ updates: 'https://openvibe.network/updates?site=games',++ })++ }++++ // ── Collapsible Frame for the game / editor pages ─────────────────────++ // The canvas is sized by CSS below the navbar, so showing or hiding the++ // Frame is a class flip plus a resize event for the engine.++ function setupCollapse() {++ var stage = document.getElementById('stage')++ if (!stage) return++ var btn = document.createElement('button')++ btn.id = 'ovg-frame-toggle'++ btn.type = 'button'++ btn.setAttribute('aria-label', 'Show or hide the OpenVibe navigation')++ btn.innerHTML = '<span class="ovg-frame-toggle-mark">☰</span> OpenVibe'++ // The game page reveals it once play starts; the editor has it from the start.++ btn.hidden = !cfg.toggleFromStart++ function apply(hidden) {++ document.body.classList.toggle('ovg-frame-hidden', hidden)++ btn.classList.toggle('is-hidden', hidden)++ btn.title = hidden ? 'Show the OpenVibe navigation' : 'Hide the OpenVibe navigation'++ window.dispatchEvent(new Event('resize'))++ }++ btn.addEventListener('click', function () {++ apply(!document.body.classList.contains('ovg-frame-hidden'))++ })++ stage.appendChild(btn)++ window.addEventListener('ovg:playing', function () {++ btn.hidden = false++ apply(true)++ })++ }++++ var tries = 0++ function boot() {++ if (!window.OpenVibeNavbar || !window.OpenVibeFooter) {++ // The shared scripts are deferred; if the Network is unreachable the++ // page simply runs without its Frame.++ if (++tries < 60) setTimeout(boot, 100)++ return++ }++ try {++ initNavbar()++ } catch (e) {++ /* navbar optional */++ }++ try {++ initFooter()++ } catch (e) {++ /* footer optional */++ }++ }++ if (cfg.collapsible) setupCollapse()++ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot)++ else boot()++})()@@@ -543,11 +543,11 @@ body {padding-top: 6px !important;padding-bottom: 6px !important;}--body.ovg-chrome-hidden #navbar-mount,--body.ovg-chrome-hidden #ov-footer {++body.ovg-frame-hidden #navbar-mount,++body.ovg-frame-hidden #ov-footer {display: none;}--#ovg-chrome-toggle {++#ovg-frame-toggle {position: absolute;right: 10px;bottom: 30px;@@@ -566,11 +566,11 @@ body.ovg-chrome-hidden #ov-footer {cursor: pointer;opacity: 0.6;}--#ovg-chrome-toggle:hover {++#ovg-frame-toggle:hover {opacity: 1;color: var(--ink, #d5dbe2);}--#ovg-chrome-toggle[hidden] {++#ovg-frame-toggle[hidden] {display: none;}@@@ -152,8 +152,8 @@ async function start(): Promise<void> {}const slot = choice.slotsaveName(name)-- // The player is heading into the world: the page folds the network chrome-- // away so the canvas gets the whole viewport (see public/chrome.js).++ // The player is heading into the world: the page folds the OpenVibe Frame++ // away so the canvas gets the whole viewport (see public/frame.js).window.dispatchEvent(new CustomEvent('ovg:playing'))const havok = await havokPromise@@@ -23,8 +23,8 @@ input {/* ── Page frame ────────────────────────────────────────────────────Shared navbar on top, the game stage filling the rest, a one-line-- footer below. Once the player is in the world the chrome folds away-- (body.ovg-chrome-hidden, see public/chrome.js) and the stage takes the++ footer below. Once the player is in the world the Frame folds away++ (body.ovg-frame-hidden, see public/frame.js) and the stage takes thewhole viewport; the engine resizes off the canvas's CSS box. */body {display: flex;@@@ -65,13 +65,13 @@ body {padding-bottom: 8px !important;}--body.ovg-chrome-hidden #navbar-mount,--body.ovg-chrome-hidden #ov-footer {++body.ovg-frame-hidden #navbar-mount,++body.ovg-frame-hidden #ov-footer {display: none;}--/* Floating "bring the chrome back" pill (chrome.js adds it once the game starts). */--#ovg-chrome-toggle {++/* Floating "bring the Frame back" pill (frame.js adds it once the game starts). */++#ovg-frame-toggle {position: absolute;top: 10px;left: 10px;@@@ -91,12 +91,12 @@ body.ovg-chrome-hidden #ov-footer {opacity: 0.55;transition: opacity 0.15s;}--#ovg-chrome-toggle:hover,--#ovg-chrome-toggle:focus-visible {++#ovg-frame-toggle:hover,++#ovg-frame-toggle:focus-visible {opacity: 1;color: var(--text-primary, #e6e6e6);}--#ovg-chrome-toggle[hidden] {++#ovg-frame-toggle[hidden] {display: none;}@@@ -78,7 +78,7 @@ beforeAll(async () => {writeFileSync(join(dist, 'index.html'), '<!doctype html><title>portal page</title>')writeFileSync(join(dist, 'play.html'), '<!doctype html><title>game page</title>')writeFileSync(join(dist, 'editor.html'), '<!doctype html><title>editor page</title>')-- writeFileSync(join(dist, 'chrome.js'), 'void 0')++ writeFileSync(join(dist, 'frame.js'), 'void 0')writeFileSync(join(dist, ASSET.slice(1)), 'export {}')writeFileSync(join(dist, 'assets', 'tex', 'wood_planks.jpg'), 'jpeg')const mapPath = join(dir, 'data', 'map.json')@@@ -141,7 +141,7 @@ describe('real routes on the apex', () => {expect(asset.status).toBe(200)expect(asset.headers['content-type']).toBe('text/javascript')expect(asset.headers['cache-control']).toBe('public, max-age=31536000, immutable')-- expect((await send('/chrome.js')).status).toBe(200)++ expect((await send('/frame.js')).status).toBe(200)expect((await send('/assets/tex/wood_planks.jpg')).status).toBe(200)})
@@ -21,14 +21,14 @@
@@ -21,14 +21,14 @@
<meta property="og:url" content="https://openvibe.games/editor" />
<meta property="og:url" content="https://openvibe.games/editor" />
<meta property="og:image" content="https://openvibe.games/og-image.png" />
<meta property="og:image" content="https://openvibe.games/og-image.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:card" content="summary_large_image" />
- <!-- Shared network chrome: the user's theme before first paint, then navbar + footer. -->
+ <!-- The OpenVibe Frame: the user's theme before first paint, then navbar + footer. -->
<script src="https://openvibe.network/shared/theme-loader.js"></script>
<script src="https://openvibe.network/shared/theme-loader.js"></script>
<script src="https://openvibe.network/shared/navbar.js" defer></script>
<script src="https://openvibe.network/shared/navbar.js" defer></script>
<script src="https://openvibe.network/shared/footer.js" defer></script>
<script src="https://openvibe.network/shared/footer.js" defer></script>
<script>
<script>
- window.__ovgChrome = { page: 'editor', collapsible: true, toggleFromStart: true }
+ window.__ovgFrame = { page: 'editor', collapsible: true, toggleFromStart: true }
</script>
</script>
- <script src="/chrome.js" defer></script>
+ <script src="/frame.js" defer></script>
</head>
</head>
<body>
<body>
<!--
<!--
@@ -8,7 +8,7 @@
@@ -8,7 +8,7 @@
name="description"
name="description"
content="The games arm of the OpenVibe network. Play Scraplandia — scrappy multiplayer survival-building in Scrap City — in your browser, no download."
content="The games arm of the OpenVibe network. Play Scraplandia — scrappy multiplayer survival-building in Scrap City — in your browser, no download."
/>
/>
- <!-- Shared network chrome: the user's theme before first paint, then navbar + footer. -->
+ <!-- The OpenVibe Frame: the user's theme before first paint, then navbar + footer. -->
<script src="https://openvibe.network/shared/theme-loader.js"></script>
<script src="https://openvibe.network/shared/theme-loader.js"></script>
<script src="https://openvibe.network/shared/navbar.js" defer></script>
<script src="https://openvibe.network/shared/navbar.js" defer></script>
<script src="https://openvibe.network/shared/footer.js" defer></script>
<script src="https://openvibe.network/shared/footer.js" defer></script>
@@ -377,7 +377,7 @@
@@ -377,7 +377,7 @@
color: var(--text-faint);
color: var(--text-faint);
}
}
- /* ── shared chrome ───────────────────────────────────────────── */
+ /* ── the OpenVibe Frame ───────────────────────────────────────────── */
#navbar-mount {
#navbar-mount {
position: relative;
position: relative;
z-index: 5;
z-index: 5;
@@ -470,13 +470,21 @@
@@ -470,13 +470,21 @@
</div>
</div>
</section>
</section>
+ <!-- What shipped on OpenVibe.Games: the network's shared "shipped" views (openvibe-shared
+ shipped.js, which the shared footer loads), the same on every OpenVibe site. -->
+ <section class="ov-shipped-home" aria-label="What shipped" style="max-width:1100px;margin:0 auto;padding:0 20px">
+ <a data-ov-shipped="latest" data-service="games" href="https://openvibe.network/updates?site=games" hidden></a>
+ <div data-ov-shipped="list" data-service="games" data-limit="5" data-more="https://openvibe.network/updates?site=games" data-title="Recently shipped on OpenVibe.Games" hidden></div>
+ <noscript><p><a href="https://openvibe.network/updates?site=games">What shipped recently</a></p></noscript>
+ </section>
+
<!-- Shared cross-site footer (openvibe.network/shared/footer.js): network links, legal, signed-in row. -->
<!-- Shared cross-site footer (openvibe.network/shared/footer.js): network links, legal, signed-in row. -->
<footer id="ov-footer" class="ovf"></footer>
<footer id="ov-footer" class="ovf"></footer>
<script>
<script>
- window.__ovgChrome = { page: 'portal' }
+ window.__ovgFrame = { page: 'portal' }
</script>
</script>
- <script src="/chrome.js" defer></script>
+ <script src="/frame.js" defer></script>
<script>
<script>
// Dev convenience: on localhost the play. vhost doesn't exist, so PLAY
// Dev convenience: on localhost the play. vhost doesn't exist, so PLAY
// buttons fall back to the same-origin /play route (Vite alias / server).
// buttons fall back to the same-origin /play route (Vite alias / server).
@@ -8,7 +8,7 @@
@@ -8,7 +8,7 @@
name="description"
name="description"
content="Play Scraplandia: scrappy multiplayer survival-building in and around Scrap City, in your browser, no download. Part of the OpenVibe network."
content="Play Scraplandia: scrappy multiplayer survival-building in and around Scrap City, in your browser, no download. Part of the OpenVibe network."
/>
/>
- <!-- Shared network chrome: the user's theme before first paint, then navbar + footer. -->
+ <!-- The OpenVibe Frame: the user's theme before first paint, then navbar + footer. -->
<script src="https://openvibe.network/shared/theme-loader.js"></script>
<script src="https://openvibe.network/shared/theme-loader.js"></script>
<script src="https://openvibe.network/shared/navbar.js" defer></script>
<script src="https://openvibe.network/shared/navbar.js" defer></script>
<script src="https://openvibe.network/shared/footer.js" defer></script>
<script src="https://openvibe.network/shared/footer.js" defer></script>
@@ -60,15 +60,15 @@
@@ -60,15 +60,15 @@
</script>
</script>
<link rel="stylesheet" href="/src/ui/styles.css" />
<link rel="stylesheet" href="/src/ui/styles.css" />
<script>
<script>
- window.__ovgChrome = { page: 'play', title: 'Scraplandia', collapsible: true }
+ window.__ovgFrame = { page: 'play', title: 'Scraplandia', collapsible: true }
</script>
</script>
- <script src="/chrome.js" defer></script>
+ <script src="/frame.js" defer></script>
</head>
</head>
<body>
<body>
<!--
<!--
The shared navbar mounts at the top; the game fills the stage below it.
The shared navbar mounts at the top; the game fills the stage below it.
- Once the player is in the world (the `ovg:playing` event) the chrome
- folds away and a small floating button brings it back — see chrome.js.
+ Once the player is in the world (the `ovg:playing` event) the Frame
+ folds away and a small floating button brings it back — see frame.js.
-->
-->
<div id="navbar-mount"></div>
<div id="navbar-mount"></div>
<div id="stage">
<div id="stage">
deleted file mode 100644
deleted file mode 100644
@@ -1,153 +0,0 @@
@@ -1,153 +0,0 @@
-/**
- * OpenVibe.Games — the shared network chrome (navbar + footer) on every page.
- *
- * The navbar and footer themselves come from openvibe.network/shared/*.js,
- * loaded by each page's <head>; this file only tells them who we are: the
- * site's links, where sign-in/sign-out live, and the session the game keeps
- * in the `ovg_sso` cookie / localStorage entry (set by /auth/callback).
- *
- * Pages configure it with `window.__ovgChrome = { page, title, collapsible }`
- * before this script runs:
- * page 'portal' | 'play' | 'editor'
- * title recorded to the signed-in user's network history (play only)
- * collapsible the chrome folds away once the game starts (the `ovg:playing`
- * event) and a small floating button brings it back.
- * toggleFromStart show that button immediately (the editor), not only after
- * `ovg:playing`.
- */
-;(function () {
- 'use strict'
- var NETWORK = 'https://openvibe.network'
- var cfg = window.__ovgChrome || {}
- var local = ['localhost', '127.0.0.1'].indexOf(location.hostname) !== -1
- var onPlayHost = location.hostname.indexOf('play.') === 0
- var PORTAL = local || !onPlayHost ? '/' : 'https://openvibe.games/'
- var PLAY = local ? '/play' : onPlayHost ? '/' : 'https://play.openvibe.games/'
- var EDITOR = local || !onPlayHost ? '/editor' : 'https://openvibe.games/editor'
-
- function cookie(name) {
- var m = document.cookie.match(new RegExp('(?:^|;\\s*)' + name + '=([^;]*)'))
- try {
- return m ? decodeURIComponent(m[1]) : null
- } catch (e) {
- return m ? m[1] : null
- }
- }
- /** The openvibe.network access token this site holds, if any. */
- function sessionToken() {
- var t = null
- try {
- t = localStorage.getItem('ovg_sso')
- } catch (e) {
- /* storage unavailable */
- }
- return t || cookie('ovg_sso') || undefined
- }
-
- var here = location.pathname + location.search
- var links = [
- { label: 'Games', href: PORTAL },
- { label: 'Scraplandia', href: PLAY },
- ]
- var footerLinks = [
- {
- heading: 'Games',
- items: [
- { label: 'All games', href: PORTAL },
- { label: 'Play Scraplandia', href: PLAY },
- { label: 'Map editor', href: EDITOR },
- ],
- },
- {
- heading: 'Account',
- items: [
- { label: 'Sign in', href: '/auth/login?next=' + encodeURIComponent(here) },
- { label: 'Sign out', href: '/auth/logout?next=' + encodeURIComponent(location.pathname) },
- { label: 'Themes', href: NETWORK + '/themes' },
- ],
- },
- ]
-
- function initNavbar() {
- var opts = {
- service: 'games',
- apiBase: NETWORK,
- token: sessionToken(),
- // Same-origin fallback: resolves the ovg_sso cookie server-side when the
- // token is not readable here (or the Network rejects it client-side).
- sessionUrl: '/auth/me',
- loginUrl: '/auth/login?next=' + encodeURIComponent(here),
- onLogout: function () {
- location.href = '/auth/logout?next=' + encodeURIComponent(location.pathname)
- },
- links: links,
- menu: { after: [{ label: 'Map editor', href: EDITOR, icon: 'fa-map' }] },
- // One prompt=none attempt per tab when the browser says it has an OpenVibe account.
- silentLogin: '/auth/login?silent=1&next={url}',
- }
- if (cfg.page === 'play') opts.history = { type: 'game', title: cfg.title || 'Scraplandia' }
- window.OpenVibeNavbar.init(opts)
- }
-
- function initFooter() {
- if (!document.getElementById('ov-footer')) return
- window.OpenVibeFooter.init({
- service: 'games',
- variant: cfg.page === 'portal' ? 'full' : 'compact',
- links: footerLinks,
- mount: '#ov-footer',
- })
- }
-
- // ── Collapsible chrome for the game / editor pages ─────────────────────
- // The canvas is sized by CSS below the navbar, so showing or hiding the
- // chrome is a class flip plus a resize event for the engine.
- function setupCollapse() {
- var stage = document.getElementById('stage')
- if (!stage) return
- var btn = document.createElement('button')
- btn.id = 'ovg-chrome-toggle'
- btn.type = 'button'
- btn.setAttribute('aria-label', 'Show or hide the OpenVibe navigation')
- btn.innerHTML = '<span class="ovg-chrome-toggle-mark">☰</span> OpenVibe'
- // The game page reveals it once play starts; the editor has it from the start.
- btn.hidden = !cfg.toggleFromStart
- function apply(hidden) {
- document.body.classList.toggle('ovg-chrome-hidden', hidden)
- btn.classList.toggle('is-hidden', hidden)
- btn.title = hidden ? 'Show the OpenVibe navigation' : 'Hide the OpenVibe navigation'
- window.dispatchEvent(new Event('resize'))
- }
- btn.addEventListener('click', function () {
- apply(!document.body.classList.contains('ovg-chrome-hidden'))
- })
- stage.appendChild(btn)
- window.addEventListener('ovg:playing', function () {
- btn.hidden = false
- apply(true)
- })
- }
-
- var tries = 0
- function boot() {
- if (!window.OpenVibeNavbar || !window.OpenVibeFooter) {
- // The shared scripts are deferred; if the Network is unreachable the
- // page simply runs without its chrome.
- if (++tries < 60) setTimeout(boot, 100)
- return
- }
- try {
- initNavbar()
- } catch (e) {
- /* navbar optional */
- }
- try {
- initFooter()
- } catch (e) {
- /* footer optional */
- }
- }
- if (cfg.collapsible) setupCollapse()
- if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot)
- else boot()
-})()
new file mode 100644
new file mode 100644
@@ -0,0 +1,154 @@
@@ -0,0 +1,154 @@
+/**
+ * OpenVibe.Games — the OpenVibe Frame (navbar + footer) on every page.
+ *
+ * The navbar and footer themselves come from openvibe.network/shared/*.js,
+ * loaded by each page's <head>; this file only tells them who we are: the
+ * site's links, where sign-in/sign-out live, and the session the game keeps
+ * in the `ovg_sso` cookie / localStorage entry (set by /auth/callback).
+ *
+ * Pages configure it with `window.__ovgFrame = { page, title, collapsible }`
+ * before this script runs:
+ * page 'portal' | 'play' | 'editor'
+ * title recorded to the signed-in user's network history (play only)
+ * collapsible the Frame folds away once the game starts (the `ovg:playing`
+ * event) and a small floating button brings it back.
+ * toggleFromStart show that button immediately (the editor), not only after
+ * `ovg:playing`.
+ */
+;(function () {
+ 'use strict'
+ var NETWORK = 'https://openvibe.network'
+ var cfg = window.__ovgFrame || {}
+ var local = ['localhost', '127.0.0.1'].indexOf(location.hostname) !== -1
+ var onPlayHost = location.hostname.indexOf('play.') === 0
+ var PORTAL = local || !onPlayHost ? '/' : 'https://openvibe.games/'
+ var PLAY = local ? '/play' : onPlayHost ? '/' : 'https://play.openvibe.games/'
+ var EDITOR = local || !onPlayHost ? '/editor' : 'https://openvibe.games/editor'
+
+ function cookie(name) {
+ var m = document.cookie.match(new RegExp('(?:^|;\\s*)' + name + '=([^;]*)'))
+ try {
+ return m ? decodeURIComponent(m[1]) : null
+ } catch (e) {
+ return m ? m[1] : null
+ }
+ }
+ /** The openvibe.network access token this site holds, if any. */
+ function sessionToken() {
+ var t = null
+ try {
+ t = localStorage.getItem('ovg_sso')
+ } catch (e) {
+ /* storage unavailable */
+ }
+ return t || cookie('ovg_sso') || undefined
+ }
+
+ var here = location.pathname + location.search
+ var links = [
+ { label: 'Games', href: PORTAL },
+ { label: 'Scraplandia', href: PLAY },
+ ]
+ var footerLinks = [
+ {
+ heading: 'Games',
+ items: [
+ { label: 'All games', href: PORTAL },
+ { label: 'Play Scraplandia', href: PLAY },
+ { label: 'Map editor', href: EDITOR },
+ ],
+ },
+ {
+ heading: 'Account',
+ items: [
+ { label: 'Sign in', href: '/auth/login?next=' + encodeURIComponent(here) },
+ { label: 'Sign out', href: '/auth/logout?next=' + encodeURIComponent(location.pathname) },
+ { label: 'Themes', href: NETWORK + '/themes' },
+ ],
+ },
+ ]
+
+ function initNavbar() {
+ var opts = {
+ service: 'games',
+ apiBase: NETWORK,
+ token: sessionToken(),
+ // Same-origin fallback: resolves the ovg_sso cookie server-side when the
+ // token is not readable here (or the Network rejects it client-side).
+ sessionUrl: '/auth/me',
+ loginUrl: '/auth/login?next=' + encodeURIComponent(here),
+ onLogout: function () {
+ location.href = '/auth/logout?next=' + encodeURIComponent(location.pathname)
+ },
+ links: links,
+ menu: { after: [{ label: 'Map editor', href: EDITOR, icon: 'fa-map' }] },
+ // One prompt=none attempt per tab when the browser says it has an OpenVibe account.
+ silentLogin: '/auth/login?silent=1&next={url}',
+ }
+ if (cfg.page === 'play') opts.history = { type: 'game', title: cfg.title || 'Scraplandia' }
+ window.OpenVibeNavbar.init(opts)
+ }
+
+ function initFooter() {
+ if (!document.getElementById('ov-footer')) return
+ window.OpenVibeFooter.init({
+ service: 'games',
+ variant: cfg.page === 'portal' ? 'full' : 'compact',
+ links: footerLinks,
+ mount: '#ov-footer',
+ updates: 'https://openvibe.network/updates?site=games',
+ })
+ }
+
+ // ── Collapsible Frame for the game / editor pages ─────────────────────
+ // The canvas is sized by CSS below the navbar, so showing or hiding the
+ // Frame is a class flip plus a resize event for the engine.
+ function setupCollapse() {
+ var stage = document.getElementById('stage')
+ if (!stage) return
+ var btn = document.createElement('button')
+ btn.id = 'ovg-frame-toggle'
+ btn.type = 'button'
+ btn.setAttribute('aria-label', 'Show or hide the OpenVibe navigation')
+ btn.innerHTML = '<span class="ovg-frame-toggle-mark">☰</span> OpenVibe'
+ // The game page reveals it once play starts; the editor has it from the start.
+ btn.hidden = !cfg.toggleFromStart
+ function apply(hidden) {
+ document.body.classList.toggle('ovg-frame-hidden', hidden)
+ btn.classList.toggle('is-hidden', hidden)
+ btn.title = hidden ? 'Show the OpenVibe navigation' : 'Hide the OpenVibe navigation'
+ window.dispatchEvent(new Event('resize'))
+ }
+ btn.addEventListener('click', function () {
+ apply(!document.body.classList.contains('ovg-frame-hidden'))
+ })
+ stage.appendChild(btn)
+ window.addEventListener('ovg:playing', function () {
+ btn.hidden = false
+ apply(true)
+ })
+ }
+
+ var tries = 0
+ function boot() {
+ if (!window.OpenVibeNavbar || !window.OpenVibeFooter) {
+ // The shared scripts are deferred; if the Network is unreachable the
+ // page simply runs without its Frame.
+ if (++tries < 60) setTimeout(boot, 100)
+ return
+ }
+ try {
+ initNavbar()
+ } catch (e) {
+ /* navbar optional */
+ }
+ try {
+ initFooter()
+ } catch (e) {
+ /* footer optional */
+ }
+ }
+ if (cfg.collapsible) setupCollapse()
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot)
+ else boot()
+})()
@@ -543,11 +543,11 @@ body {
@@ -543,11 +543,11 @@ body {
padding-top: 6px !important;
padding-top: 6px !important;
padding-bottom: 6px !important;
padding-bottom: 6px !important;
}
}
-body.ovg-chrome-hidden #navbar-mount,
-body.ovg-chrome-hidden #ov-footer {
+body.ovg-frame-hidden #navbar-mount,
+body.ovg-frame-hidden #ov-footer {
display: none;
display: none;
}
}
-#ovg-chrome-toggle {
+#ovg-frame-toggle {
position: absolute;
position: absolute;
right: 10px;
right: 10px;
bottom: 30px;
bottom: 30px;
@@ -566,11 +566,11 @@ body.ovg-chrome-hidden #ov-footer {
@@ -566,11 +566,11 @@ body.ovg-chrome-hidden #ov-footer {
cursor: pointer;
cursor: pointer;
opacity: 0.6;
opacity: 0.6;
}
}
-#ovg-chrome-toggle:hover {
+#ovg-frame-toggle:hover {
opacity: 1;
opacity: 1;
color: var(--ink, #d5dbe2);
color: var(--ink, #d5dbe2);
}
}
-#ovg-chrome-toggle[hidden] {
+#ovg-frame-toggle[hidden] {
display: none;
display: none;
}
}
@@ -152,8 +152,8 @@ async function start(): Promise<void> {
@@ -152,8 +152,8 @@ async function start(): Promise<void> {
}
}
const slot = choice.slot
const slot = choice.slot
saveName(name)
saveName(name)
- // The player is heading into the world: the page folds the network chrome
- // away so the canvas gets the whole viewport (see public/chrome.js).
+ // The player is heading into the world: the page folds the OpenVibe Frame
+ // away so the canvas gets the whole viewport (see public/frame.js).
window.dispatchEvent(new CustomEvent('ovg:playing'))
window.dispatchEvent(new CustomEvent('ovg:playing'))
const havok = await havokPromise
const havok = await havokPromise
@@ -23,8 +23,8 @@ input {
@@ -23,8 +23,8 @@ input {
/* ── Page frame ────────────────────────────────────────────────────
/* ── Page frame ────────────────────────────────────────────────────
Shared navbar on top, the game stage filling the rest, a one-line
Shared navbar on top, the game stage filling the rest, a one-line
- footer below. Once the player is in the world the chrome folds away
- (body.ovg-chrome-hidden, see public/chrome.js) and the stage takes the
+ footer below. Once the player is in the world the Frame folds away
+ (body.ovg-frame-hidden, see public/frame.js) and the stage takes the
whole viewport; the engine resizes off the canvas's CSS box. */
whole viewport; the engine resizes off the canvas's CSS box. */
body {
body {
display: flex;
display: flex;
@@ -65,13 +65,13 @@ body {
@@ -65,13 +65,13 @@ body {
padding-bottom: 8px !important;
padding-bottom: 8px !important;
}
}
-body.ovg-chrome-hidden #navbar-mount,
-body.ovg-chrome-hidden #ov-footer {
+body.ovg-frame-hidden #navbar-mount,
+body.ovg-frame-hidden #ov-footer {
display: none;
display: none;
}
}
-/* Floating "bring the chrome back" pill (chrome.js adds it once the game starts). */
-#ovg-chrome-toggle {
+/* Floating "bring the Frame back" pill (frame.js adds it once the game starts). */
+#ovg-frame-toggle {
position: absolute;
position: absolute;
top: 10px;
top: 10px;
left: 10px;
left: 10px;
@@ -91,12 +91,12 @@ body.ovg-chrome-hidden #ov-footer {
@@ -91,12 +91,12 @@ body.ovg-chrome-hidden #ov-footer {
opacity: 0.55;
opacity: 0.55;
transition: opacity 0.15s;
transition: opacity 0.15s;
}
}
-#ovg-chrome-toggle:hover,
-#ovg-chrome-toggle:focus-visible {
+#ovg-frame-toggle:hover,
+#ovg-frame-toggle:focus-visible {
opacity: 1;
opacity: 1;
color: var(--text-primary, #e6e6e6);
color: var(--text-primary, #e6e6e6);
}
}
-#ovg-chrome-toggle[hidden] {
+#ovg-frame-toggle[hidden] {
display: none;
display: none;
}
}
@@ -78,7 +78,7 @@ beforeAll(async () => {
@@ -78,7 +78,7 @@ beforeAll(async () => {
writeFileSync(join(dist, 'index.html'), '<!doctype html><title>portal page</title>')
writeFileSync(join(dist, 'index.html'), '<!doctype html><title>portal page</title>')
writeFileSync(join(dist, 'play.html'), '<!doctype html><title>game page</title>')
writeFileSync(join(dist, 'play.html'), '<!doctype html><title>game page</title>')
writeFileSync(join(dist, 'editor.html'), '<!doctype html><title>editor page</title>')
writeFileSync(join(dist, 'editor.html'), '<!doctype html><title>editor page</title>')
- writeFileSync(join(dist, 'chrome.js'), 'void 0')
+ writeFileSync(join(dist, 'frame.js'), 'void 0')
writeFileSync(join(dist, ASSET.slice(1)), 'export {}')
writeFileSync(join(dist, ASSET.slice(1)), 'export {}')
writeFileSync(join(dist, 'assets', 'tex', 'wood_planks.jpg'), 'jpeg')
writeFileSync(join(dist, 'assets', 'tex', 'wood_planks.jpg'), 'jpeg')
const mapPath = join(dir, 'data', 'map.json')
const mapPath = join(dir, 'data', 'map.json')
@@ -141,7 +141,7 @@ describe('real routes on the apex', () => {
@@ -141,7 +141,7 @@ describe('real routes on the apex', () => {
expect(asset.status).toBe(200)
expect(asset.status).toBe(200)
expect(asset.headers['content-type']).toBe('text/javascript')
expect(asset.headers['content-type']).toBe('text/javascript')
expect(asset.headers['cache-control']).toBe('public, max-age=31536000, immutable')
expect(asset.headers['cache-control']).toBe('public, max-age=31536000, immutable')
- expect((await send('/chrome.js')).status).toBe(200)
+ expect((await send('/frame.js')).status).toBe(200)
expect((await send('/assets/tex/wood_planks.jpg')).status).toBe(200)
expect((await send('/assets/tex/wood_planks.jpg')).status).toBe(200)
})
})