openvibers/openvibe.run Commits
Web app manifest: its icons are the shared set this site serves (/shared/icons), not /assets/logo-*.png (all six install icons were 404); pages add the apple-touch-icon (#58)
Changes
@@@ -128,7 +128,12 @@ function createApp(opts = {}) {app.get('/robots.txt', (req, res) => res.type('text/plain').send(['User-agent: *', 'Allow: /$', 'Disallow: /api/', 'Disallow: /internal/', `Sitemap: ${config.baseUrl}/sitemap.xml`, ''].join('\n')));app.get('/sitemap.xml', (req, res) => res.type('application/xml').send(require('openvibe-shared/seo').sitemapXml(discovery.PAGES.map((p) => ({ loc: `${config.baseUrl}${p}` })))));app.get('/favicon.ico', (req, res) => res.type('image/svg+xml').set('Cache-Control', 'public, max-age=86400').send(appIcon.favicon({ site: 'run' })));-- app.get('/manifest.webmanifest', (req, res) => res.type('application/manifest+json').send(JSON.stringify(appIcon.manifest({ site: 'run', name: home.SITE_NAME, shortName: 'Run', description: home.DESCRIPTION, startUrl: '/' }))));++ // The web app manifest. Its icons are the shared set (openvibe-shared/app-icons, under /shared/icons): it named++ // /assets/logo-*.png, which this site never served (every install icon was a 404 until 2026-10-17).++ app.get('/manifest.webmanifest', (req, res) => res.type('application/manifest+json').send(JSON.stringify({++ ...appIcon.manifest({ site: 'run', name: home.SITE_NAME, shortName: 'Run', description: home.DESCRIPTION, startUrl: '/' }),++ icons: [192, 512].flatMap((s) => [false, true].map((maskable) => ({ src: require('openvibe-shared/app-icons').url('run', s, { maskable }), sizes: `${s}x${s}`, type: 'image/png', purpose: maskable ? 'maskable' : 'any' }))),++ })));app.use((req, res) => {if (req.path.startsWith('/api/') || req.path.startsWith('/internal/')) return http.sendProblem(res, 404, 'not_found', { ctx: req.ov });@@@ -57,7 +57,7 @@ function renderHome({ config }) {canonical: `${config.baseUrl}/`, robots: 'index, follow',navbar: nav, footer: FOOTER, home: '/',navLinks: nav.links,-- head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">', `<link rel="stylesheet" href="${ovServe.url(showcase.STYLESHEET)}">`].join('\n'),++ head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">', `<link rel="apple-touch-icon" href="${require('openvibe-shared/app-icons').url('run', 192)}">`, `<link rel="stylesheet" href="${ovServe.url(showcase.STYLESHEET)}">`].join('\n'),body: `<div id="navbar-mount"></div>\n<main id="main">${body()}</main>`,});}@@@ -74,7 +74,7 @@ function renderUpdates({ config }) {canonical: `${config.baseUrl}/updates`, robots: 'index, follow',navbar: { service: 'run', apiBase: NETWORK_URL, links, history: { type: 'page', title: SITE_NAME } },footer: FOOTER, home: '/', navLinks: links,-- head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">'].join('\n'),++ head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">', `<link rel="apple-touch-icon" href="${require('openvibe-shared/app-icons').url('run', 192)}">`].join('\n'),body: `<div id="navbar-mount"></div>\n<main id="main" class="page">\n${frame.updatesBody({ service: 'run', siteName: SITE_NAME })}\n</main>\n<script src="${ovServe.url('shipped.js')}" defer></script>`,});}@@@ -36,6 +36,7 @@ const { boot, check, done } = require('./helpers/app');assert.match((await t.call('GET', '/favicon.ico', { token: null })).headers.get('content-type'), /image\/svg\+xml/);const manifest = JSON.parse((await t.call('GET', '/manifest.webmanifest', { token: null })).text);assert.strictEqual(manifest.name, 'OpenVibe.Run');++ assert.ok(manifest.icons.length === 4 && manifest.icons.every((i) => /^\/shared\/icons\/[a-z0-9-]+-(maskable-)?(192|512)\.png\?v=/.test(i.src)), 'the shared icon set, which the site serves');});await check('/updates: the shared update log in the Frame, one heading, in the sitemap', async () => {const r = await t.call('GET', '/updates', { token: null });
@@ -128,7 +128,12 @@ function createApp(opts = {}) {
@@ -128,7 +128,12 @@ function createApp(opts = {}) {
app.get('/robots.txt', (req, res) => res.type('text/plain').send(['User-agent: *', 'Allow: /$', 'Disallow: /api/', 'Disallow: /internal/', `Sitemap: ${config.baseUrl}/sitemap.xml`, ''].join('\n')));
app.get('/robots.txt', (req, res) => res.type('text/plain').send(['User-agent: *', 'Allow: /$', 'Disallow: /api/', 'Disallow: /internal/', `Sitemap: ${config.baseUrl}/sitemap.xml`, ''].join('\n')));
app.get('/sitemap.xml', (req, res) => res.type('application/xml').send(require('openvibe-shared/seo').sitemapXml(discovery.PAGES.map((p) => ({ loc: `${config.baseUrl}${p}` })))));
app.get('/sitemap.xml', (req, res) => res.type('application/xml').send(require('openvibe-shared/seo').sitemapXml(discovery.PAGES.map((p) => ({ loc: `${config.baseUrl}${p}` })))));
app.get('/favicon.ico', (req, res) => res.type('image/svg+xml').set('Cache-Control', 'public, max-age=86400').send(appIcon.favicon({ site: 'run' })));
app.get('/favicon.ico', (req, res) => res.type('image/svg+xml').set('Cache-Control', 'public, max-age=86400').send(appIcon.favicon({ site: 'run' })));
- app.get('/manifest.webmanifest', (req, res) => res.type('application/manifest+json').send(JSON.stringify(appIcon.manifest({ site: 'run', name: home.SITE_NAME, shortName: 'Run', description: home.DESCRIPTION, startUrl: '/' }))));
+ // The web app manifest. Its icons are the shared set (openvibe-shared/app-icons, under /shared/icons): it named
+ // /assets/logo-*.png, which this site never served (every install icon was a 404 until 2026-10-17).
+ app.get('/manifest.webmanifest', (req, res) => res.type('application/manifest+json').send(JSON.stringify({
+ ...appIcon.manifest({ site: 'run', name: home.SITE_NAME, shortName: 'Run', description: home.DESCRIPTION, startUrl: '/' }),
+ icons: [192, 512].flatMap((s) => [false, true].map((maskable) => ({ src: require('openvibe-shared/app-icons').url('run', s, { maskable }), sizes: `${s}x${s}`, type: 'image/png', purpose: maskable ? 'maskable' : 'any' }))),
+ })));
app.use((req, res) => {
app.use((req, res) => {
if (req.path.startsWith('/api/') || req.path.startsWith('/internal/')) return http.sendProblem(res, 404, 'not_found', { ctx: req.ov });
if (req.path.startsWith('/api/') || req.path.startsWith('/internal/')) return http.sendProblem(res, 404, 'not_found', { ctx: req.ov });
@@ -57,7 +57,7 @@ function renderHome({ config }) {
@@ -57,7 +57,7 @@ function renderHome({ config }) {
canonical: `${config.baseUrl}/`, robots: 'index, follow',
canonical: `${config.baseUrl}/`, robots: 'index, follow',
navbar: nav, footer: FOOTER, home: '/',
navbar: nav, footer: FOOTER, home: '/',
navLinks: nav.links,
navLinks: nav.links,
- head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">', `<link rel="stylesheet" href="${ovServe.url(showcase.STYLESHEET)}">`].join('\n'),
+ head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">', `<link rel="apple-touch-icon" href="${require('openvibe-shared/app-icons').url('run', 192)}">`, `<link rel="stylesheet" href="${ovServe.url(showcase.STYLESHEET)}">`].join('\n'),
body: `<div id="navbar-mount"></div>\n<main id="main">${body()}</main>`,
body: `<div id="navbar-mount"></div>\n<main id="main">${body()}</main>`,
});
});
}
}
@@ -74,7 +74,7 @@ function renderUpdates({ config }) {
@@ -74,7 +74,7 @@ function renderUpdates({ config }) {
canonical: `${config.baseUrl}/updates`, robots: 'index, follow',
canonical: `${config.baseUrl}/updates`, robots: 'index, follow',
navbar: { service: 'run', apiBase: NETWORK_URL, links, history: { type: 'page', title: SITE_NAME } },
navbar: { service: 'run', apiBase: NETWORK_URL, links, history: { type: 'page', title: SITE_NAME } },
footer: FOOTER, home: '/', navLinks: links,
footer: FOOTER, home: '/', navLinks: links,
- head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">'].join('\n'),
+ head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">', `<link rel="apple-touch-icon" href="${require('openvibe-shared/app-icons').url('run', 192)}">`].join('\n'),
body: `<div id="navbar-mount"></div>\n<main id="main" class="page">\n${frame.updatesBody({ service: 'run', siteName: SITE_NAME })}\n</main>\n<script src="${ovServe.url('shipped.js')}" defer></script>`,
body: `<div id="navbar-mount"></div>\n<main id="main" class="page">\n${frame.updatesBody({ service: 'run', siteName: SITE_NAME })}\n</main>\n<script src="${ovServe.url('shipped.js')}" defer></script>`,
});
});
}
}
@@ -36,6 +36,7 @@ const { boot, check, done } = require('./helpers/app');
@@ -36,6 +36,7 @@ const { boot, check, done } = require('./helpers/app');
assert.match((await t.call('GET', '/favicon.ico', { token: null })).headers.get('content-type'), /image\/svg\+xml/);
assert.match((await t.call('GET', '/favicon.ico', { token: null })).headers.get('content-type'), /image\/svg\+xml/);
const manifest = JSON.parse((await t.call('GET', '/manifest.webmanifest', { token: null })).text);
const manifest = JSON.parse((await t.call('GET', '/manifest.webmanifest', { token: null })).text);
assert.strictEqual(manifest.name, 'OpenVibe.Run');
assert.strictEqual(manifest.name, 'OpenVibe.Run');
+ assert.ok(manifest.icons.length === 4 && manifest.icons.every((i) => /^\/shared\/icons\/[a-z0-9-]+-(maskable-)?(192|512)\.png\?v=/.test(i.src)), 'the shared icon set, which the site serves');
});
});
await check('/updates: the shared update log in the Frame, one heading, in the sitemap', async () => {
await check('/updates: the shared update log in the Frame, one heading, in the sitemap', async () => {
const r = await t.call('GET', '/updates', { token: null });
const r = await t.call('GET', '/updates', { token: null });