'use strict';
// ═══════════════════════════════════════════════════════════════
// openvibe-shared/app-icon — one app icon design for every OpenVibe site.
//
// The icon is the OpenVibe mark (openvibe-shared/mark: the Pair, a ring O and a round V) on the network's deep navy,
// with a soft glow behind it. No text: a home-screen icon is a few millimetres wide.
// svg({ site, maskable }) full-bleed square SVG. maskable keeps the mark inside the 40 % radius
// safe zone, so round, squircle and teardrop masks never clip it.
// favicon({ site }) the heavy form on a rounded tile, tuned for 16–32 px.
// manifest({ site, name, shortName, description, startUrl }) web app manifest with shared colours.
// SITES accent (the glow and the share cards) and the light in the O per site: Live's is the
// red record light, as in ov-mark.js's variants; white means none.
// PNG sets are rendered from these by scripts/build-app-icons.js.
// ═══════════════════════════════════════════════════════════════
const BG = '#0a0f1c', BG_EDGE = '#060912';
const SITES = {
network: { accent: ['#60a5fa', '#2563eb'], dot: '#ffffff' },
live: { accent: ['#60a5fa', '#2563eb'], dot: '#ef4444' },
tools: { accent: ['#38bdf8', '#2563eb'], dot: '#ffffff' },
community: { accent: ['#34d399', '#2563eb'], dot: '#ffffff' },
games: { accent: ['#a78bfa', '#2563eb'], dot: '#facc15' },
media: { accent: ['#fbbf24', '#2563eb'], dot: '#ffffff' },
space: { accent: ['#818cf8', '#4f46e5'], dot: '#ffffff' },
ai: { accent: ['#22d3ee', '#2563eb'], dot: '#ffffff' },
};
const of = (site) => SITES[site] || SITES.network;
const MARK = require('./mark');
// The mark centred on 0,0 (openvibe-shared/mark draws it centred on 50,50 in a 100-unit box), scaled.
function mark(site, scale, form = 'pair') {
const c = of(site), g = MARK.geo(form), { body } = MARK.art({ form, id: 'ov', depth: true });
const light = c.dot && c.dot.toLowerCase() !== '#ffffff' ? `` : '';
return `${body}${light}`;
}
function defs(site, form = 'pair') {
const [a] = of(site).accent;
return `${MARK.art({ form, id: 'ov', depth: true }).defs}
`;
}
/** Full-bleed square. `maskable`: mark radius ≤ 40 % of the half-width; otherwise it fills more of the tile. */
function svg({ site = 'network', maskable = true, size = 512 } = {}) {
// The mark's farthest point (the V's caps) is 50 units from its centre: × 4 is 200 of the 256 half-width, inside
// the maskable safe zone (40 % of the width, 204.8). A rounded tile carries it at three quarters of its width.
const scale = maskable ? 4 : 4.2;
return ``;
}
/** Small sizes: the heavy form, as wide as the tile allows, and the tile rounded because nothing masks a favicon. */
function favicon({ site = 'network' } = {}) {
return ``;
}
function manifest({ site = 'network', name, shortName, description, startUrl = '/', iconBase = '/assets' }) {
const sizes = [72, 192, 512];
return {
name, short_name: shortName || name, description, start_url: startUrl, scope: '/', display: 'standalone',
background_color: BG, theme_color: '#3b82f6',
icons: [...sizes.map(s => ({ src: `${iconBase}/logo-${s}.png`, sizes: `${s}x${s}`, type: 'image/png', purpose: 'any' })),
...[192, 512].map(s => ({ src: `${iconBase}/logo-maskable-${s}.png`, sizes: `${s}x${s}`, type: 'image/png', purpose: 'maskable' })),
{ src: `${iconBase}/logo.svg`, sizes: 'any', type: 'image/svg+xml', purpose: 'any' }],
};
}
// First paint must never depend on another server. These two inline tags give every page the network's
// dark canvas and the visitor's cached theme immediately; theme-loader.js (deferred, from the Network)
// reconciles with the account afterwards. If the Network is slow or restarting, the page is still
// styled and readable instead of blank white.
const CRITICAL = ``
+ ``;
/**
tags every site shares: SVG favicon (inline, no request), theme colour, and the PNG touch icon when the site ships one. */
function headTags({ site = 'network', iconBase = null } = {}) {
const uri = 'data:image/svg+xml,' + encodeURIComponent(favicon({ site })).replace(/'/g, '%27');
return [``, '', '', CRITICAL,
iconBase ? `` : '', iconBase ? `` : ''].filter(Boolean).join('\n');
}
module.exports = { svg, favicon, manifest, headTags, CRITICAL, SITES, BG };