openvibers/openvibe.tools Commits
Tools pages run their own pinned shared files (roadmap WS-P task 4, D42: the Tools satellites' static pages). apps/_shared/release.js mounts /shared (openvibe-shared/serve, content-addressed, only the browser files) in every app with the release routes, before any page fallback: until now only the gateway host served it, and the satellites answered /shared/navbar.js with an HTML page. 45 pages in audio, docs, food, gateway, img, maps, text and yt load navbar, footer, theme loader, bell, account switcher, shipped, icons, history, ui and island from /shared/ instead of https://openvibe.network/shared/ (217 references). So each page runs what its app's pin says (openvibe-shared 1.23.0), not what Network happens to pin, and keeps its frame while Network is down. The gateway's own /shared line is folded into the same mount. Tests: release.test serves the hashed navbar (immutable) and refuses a non-browser file; shared-pins.test fails on any page loading a shared file from openvibe.network or naming one its pin does not serve. npm test 53/53.
Changes
@@@ -46,11 +46,20 @@ function toolsRelease(app, appRequire, opts = {}) {},server: { kind: 'server', files: [`${dir}/server`, 'apps/_shared', `${dir}/package-lock.json`] },};-- return createRelease({++ const release = createRelease({service: 'tools', root: ROOT, publicDir: `${dir}/public`, components,...(schema && { schema }),...opts,});++ // D42 (roadmap WS-P task 4): every app serves its OWN pinned copy of the shared browser files at /shared++ // (navbar, footer, theme loader, bell…), content-addressed, so its pages run what its pin says and keep++ // their frame while openvibe.network is down. Mounted with the release routes, before any page fallback.++ const mountRelease = release.mount;++ release.mount = function mount(app, o) {++ app.use('/shared', appRequire('openvibe-shared/serve').handler());++ return mountRelease.call(this, app, o);++ };++ return release;}module.exports = { toolsRelease, PAGE_FILES, ROOT };@@@ -51,6 +51,13 @@ const quiet = { warn() {}, log() {}, error() {} };assert.strictEqual(r.status, 204);const text = await (await fetch(`${base}/metrics`)).text();assert.match(text, /release_client_updates_total\{outcome="applied",reason="style"\} 2/);++ // D42: the app's own pinned shared browser files, content-addressed; nothing else under /shared.++ const nav = req('openvibe-shared/serve').url('navbar.js');++ r = await fetch(base + nav);++ assert.strictEqual(r.status, 200);++ assert.match(r.headers.get('content-type'), /javascript/);++ assert.match(r.headers.get('cache-control'), /immutable/, 'the hashed URL is cached for good');++ assert.strictEqual((await fetch(`${base}/shared/server.js`)).status, 404, 'only the browser files files.js lists');} finally { await new Promise(res => srv.close(res)); inst.stop && inst.stop(); }-- console.log('release: gateway/img/audio/docs serve components (shell, server) valid against their contracts; the others 1.0.0; mount serves /release.json and /release-metrics');++ console.log('release: gateway/img/audio/docs serve components (shell, server) valid against their contracts; the others 1.0.0; mount serves /release.json, /release-metrics and the pinned /shared files');})().catch((err) => { console.error(err); process.exit(1); });nnew file mode 100644@@@ -0,0 +1,27 @@++'use strict';++// D42 (roadmap WS-P task 4): Tools pages load the shared browser files (navbar, footer, theme loader, bell…) from++// their own app's pin at /shared (mounted by apps/_shared/release.js), never from openvibe.network, and only files++// that pin serves. A page that still pointed at openvibe.network would run whatever Network pins, and lose its frame++// while Network is down.++const assert = require('assert');++const fs = require('fs');++const path = require('path');++const { createRequire } = require('module');++++const APPS = path.join(__dirname, '..', '..');++const walk = (dir) => fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => (e.isDirectory() ? (e.name === 'node_modules' ? [] : walk(path.join(dir, e.name))) : [path.join(dir, e.name)]));++let pages = 0; let refs = 0;++for (const app of fs.readdirSync(APPS).filter((a) => !a.startsWith('_') && fs.existsSync(path.join(APPS, a, 'public')))) {++ let files = null;++ try { files = createRequire(path.join(APPS, app, 'server', 'index.js'))('openvibe-shared/files'); } catch { files = require('openvibe-shared/files'); }++ for (const f of walk(path.join(APPS, app, 'public')).filter((x) => /\.(html|js)$/.test(x))) {++ const src = fs.readFileSync(f, 'utf8');++ const rel = path.relative(APPS, f);++ assert.ok(!/https:\/\/openvibe\.network\/shared\/[\w.-]+\.js/.test(src), `${rel} loads a shared file from openvibe.network, not its own /shared`);++ const used = [...src.matchAll(/["'(]\/shared\/([\w.-]+\.js)/g)].map((m) => m[1]);++ if (used.length) pages++;++ for (const name of used) { refs++; assert.ok(files.isBrowserFile(name), `${rel}: /shared/${name} is not a browser file of ${app}'s openvibe-shared`); }++ }++}++assert.ok(pages >= 40 && refs >= 200, `pages ${pages}, references ${refs}`);++console.log(`shared pins: ${pages} pages, ${refs} references, all to their app's own /shared`);@@@ -350,11 +350,11 @@<!-- Footer --><div id="ov-footer"></div>-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/theme-loader.js" defer></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/ov-jobs.js"></script><script src="/js/app.js"></script></body>@@@ -356,7 +356,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {});// ── Static Files ─────────────────────────────────────────────--// (shared client-side libs are loaded absolutely from https://openvibe.network/shared/)++// (the shared browser files come from this app's own pin at /shared: apps/_shared/release.js)// Public assetsapp.use(express.static(path.join(__dirname, '..', 'public'), {@@@ -223,11 +223,11 @@<!-- Footer --><div id="ov-footer"></div>-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/theme-loader.js" defer></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/ov-jobs.js"></script><script src="/js/app.js"></script></body>@@@ -376,7 +376,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {});// ── Static Files ─────────────────────────────────────────────--// (shared client-side libs are loaded absolutely from https://openvibe.network/shared/)++// (the shared browser files come from this app's own pin at /shared: apps/_shared/release.js)// Public assetsapp.use(express.static(path.join(__dirname, '..', 'public'), {@@@ -166,11 +166,11 @@<p class="data-credit"><i class="fa-solid fa-database"></i> Data from USDA, OSM and store pricing research</p><div id="ov-footer"></div>-- <script src="https://openvibe.network/shared/theme-loader.js"></script>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/theme-loader.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/app.js"></script><script>(function () {@@@ -26,7 +26,7 @@</script><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>:root {--bg-primary: #0b0c12;@@@ -135,8 +135,8 @@</div><div id="ov-footer"></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script><script>const AUDIO_TOOLS = {'convert': { name: 'Converter', domain: 'convert.audio.openvibe.tools', icon: 'fa-arrow-right-arrow-left', category: 'Convert' },@@@ -144,9 +144,9 @@ a{color:inherit;text-decoration:none}</div></div><div id="ov-footer"></div><div class="toast" id="toast"></div>--<script src="https://openvibe.network/shared/navbar.js"></script>--<script src="https://openvibe.network/shared/footer.js"></script>--<script src="https://openvibe.network/shared/theme-loader.js" defer></script>++<script src="/shared/navbar.js"></script>++<script src="/shared/footer.js"></script>++<script src="/shared/theme-loader.js" defer></script><script src="/shared/web-runtime.js"></script><script src="/js/dev-engine.js"></script><script>@@@ -26,7 +26,7 @@</script><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>:root {--bg-primary: #0b0c12;@@@ -135,8 +135,8 @@</div><div id="ov-footer"></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script><script>const IMAGE_TOOLS = {'convert': { name: 'Converter', domain: 'convert.openvibe.tools', icon: 'fa-arrow-right-arrow-left', category: 'Convert' },@@@ -113,7 +113,7 @@// Your tools: favourites first (signed in: starred here or on my.openvibe.network), then recent tools,// this person's (every device they sign in to) or this browser's. Filled in after load.-- function mountIcons(el) { if (window.OpenVibeIcons) OpenVibeIcons.mount(el); else if (!document.getElementById('ov-icons-loader')) { var sc = document.createElement('script'); sc.id = 'ov-icons-loader'; sc.src = 'https://openvibe.network/shared/ov-icons.js'; document.head.appendChild(sc); } }++ function mountIcons(el) { if (window.OpenVibeIcons) OpenVibeIcons.mount(el); else if (!document.getElementById('ov-icons-loader')) { var sc = document.createElement('script'); sc.id = 'ov-icons-loader'; sc.src = '/shared/ov-icons.js'; document.head.appendChild(sc); } }favReady = fetch('/api/v1/me/recent-tools', { credentials: 'same-origin' }).then(function (r) { return r.ok ? r.json() : null; }).then(function (d) { favs = (d && d.favorites) || []; if (catalog) syncStars(); return d; }).catch(function () { return null; });if (recentSec) favReady.then(function (d) {@@@ -595,9 +595,9 @@<div class="toast-container" id="toasts"></div><!-- Shared OpenVibe Navbar -->-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/theme-loader.js" defer></script><script>'use strict';@@@ -426,8 +426,8 @@ function sendTool(req, res, file) {// The apex is server-rendered from the registry: index, families, tool pages, search,// sitemap.xml, robots.txt, llms.txt and /api/catalog.json (server/pages/site.js).--// This site's own pinned copy of the OpenVibe Frame's browser files (openvibe-shared/serve).--app.use('/shared', require('openvibe-shared/serve').handler());++// This site's own pinned copy of the OpenVibe Frame's browser files (/shared, openvibe-shared/serve) is++// mounted with the release routes: apps/_shared/release.js.app.use(require('./pages/site').createSiteRouter());// Net tool subdomains → net.html SPA@@@ -167,11 +167,11 @@<!-- Footer --><div id="ov-footer"></div>-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/theme-loader.js" defer></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/ov-jobs.js"></script><script src="/js/app.js"></script></body>@@@ -318,7 +318,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {});// ── Static Files ─────────────────────────────────────────────--// (shared client-side libs are loaded absolutely from https://openvibe.network/shared/)++// (the shared browser files come from this app's own pin at /shared: apps/_shared/release.js)// Public assetsapp.use(express.static(path.join(__dirname, '..', 'public'), {@@@ -392,11 +392,11 @@</div></div>--<script src="https://openvibe.network/shared/theme-loader.js"></script>--<script src="https://openvibe.network/shared/navbar.js"></script>--<script src="https://openvibe.network/shared/footer.js"></script>--<script src="https://openvibe.network/shared/notification-ui.js"></script>--<script src="https://openvibe.network/shared/account-switcher.js"></script>++<script src="/shared/theme-loader.js"></script>++<script src="/shared/navbar.js"></script>++<script src="/shared/footer.js"></script>++<script src="/shared/notification-ui.js"></script>++<script src="/shared/account-switcher.js"></script><script src="/js/app.js"></script><script>(function () {@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -79,10 +79,10 @@<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Zalgo Text|https://zalgo.openvibe.tools;Symbols|https://symbols.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.badge-layout { display: grid; grid-template-columns: 340px 1fr; gap: 2rem; align-items: start; }.badge-controls { display: flex; flex-direction: column; gap: 1rem; }@@@ -107,10 +107,10 @@<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Wordmark|https://wordmark.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/logo-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -53,10 +53,10 @@<div id="ov-footer" data-related="Morse Code|https://morse.openvibe.tools;Escape / Unescape|https://escape.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -49,10 +49,10 @@<div id="ov-footer" data-related="Nickname Generator|https://nickname.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -55,10 +55,10 @@<div id="ov-footer" data-related="Morse Code|https://morse.openvibe.tools;Binary|https://binary.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -44,10 +44,10 @@<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Symbols|https://symbols.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -44,10 +44,10 @@<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Text Cleaner|https://clean.openvibe.tools;Text Counter|https://count.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -63,10 +63,10 @@<div id="ov-footer" data-related="Text Counter|https://count.openvibe.tools;Escape / Unescape|https://escape.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.compare-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }.compare-pane label { display: block; font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 6px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; }@@@ -75,10 +75,10 @@ A brand new line was added.</textarea><div id="ov-footer" data-related="Clean Text|https://clean.openvibe.tools;Sort Lines|https://sort.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/shared.js"></script><script src="/js/format-engine.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -42,10 +42,10 @@<div id="ov-footer" data-related="Text Cleaner|https://clean.openvibe.tools;Sort Lines|https://sort.openvibe.tools;Case Converter|https://case.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.escape-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }.escape-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.5rem; }@@@ -53,10 +53,10 @@<div id="ov-footer" data-related="Unicode Inspector|https://unicode.openvibe.tools;Text Cleaner|https://clean.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -48,10 +48,10 @@<div id="ov-footer" data-related="Zalgo Text|https://zalgo.openvibe.tools;ASCII Art|https://ascii.openvibe.tools;Case Converter|https://case.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.alert-banner {background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);@@@ -470,10 +470,10 @@<div id="ov-footer" data-related="OpenVibePeso|https://peso.openvibe.tools;OpenVibeSpanish|https://spanish.openvibe.tools;OpenVibeSlang|https://slang.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/shared.js"></script><script>OpenVibeUI.initPage();@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="hub-hero">@@@ -228,10 +228,10 @@<div id="ov-footer"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.json-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; min-height: 400px; }.json-pane { display: flex; flex-direction: column; }@@@ -72,10 +72,10 @@<div id="ov-footer" data-related="Escape / Unescape|https://escape.openvibe.tools;Markdown Preview|https://markdown.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/shared.js"></script><script src="/js/format-engine.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap" style="max-width: 1000px;">@@@ -34,10 +34,10 @@<div id="ov-footer" data-related="Symbols|https://symbols.openvibe.tools;Bio Generator|https://bio.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="hub-hero">@@@ -125,10 +125,10 @@<div id="ov-footer"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/logo-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.md-editor { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; min-height: 500px; }.md-pane { display: flex; flex-direction: column; }@@@ -93,10 +93,10 @@ Made with ♥ by the OpenVibe Network</textarea><div id="ov-footer" data-related="Escape / Unescape|https://escape.openvibe.tools;Text Counter|https://count.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/shared.js"></script><script src="/js/format-engine.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -55,10 +55,10 @@<div id="ov-footer" data-related="Binary|https://binary.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -48,10 +48,10 @@<div id="ov-footer" data-related="Bio Generator|https://bio.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.converter-wrap {display: flex;@@@ -183,10 +183,10 @@<div id="ov-footer" data-related="ICE Alerts|https://ice.openvibe.tools;OpenVibeSpanish|https://spanish.openvibe.tools;OpenVibeSlang|https://slang.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/shared.js"></script><script>OpenVibeUI.initPage();@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -44,10 +44,10 @@<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Zalgo Text|https://zalgo.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.search-wrap {position: relative; margin-bottom: 20px;@@@ -133,10 +133,10 @@<div id="ov-footer" data-related="ICE Alerts|https://ice.openvibe.tools;OpenVibePeso|https://peso.openvibe.tools;OpenVibeSpanish|https://spanish.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/shared.js"></script><script>OpenVibeUI.initPage();@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -51,10 +51,10 @@<div id="ov-footer" data-related="Case Converter|https://case.openvibe.tools;Text Cleaner|https://clean.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -61,10 +61,10 @@ elderberry</textarea><div id="ov-footer" data-related="Text Cleaner|https://clean.openvibe.tools;Text Counter|https://count.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.phrase-search {width: 100%;@@@ -119,10 +119,10 @@<div id="ov-footer" data-related="ICE Alerts|https://ice.openvibe.tools;OpenVibePeso|https://peso.openvibe.tools;OpenVibeSlang|https://slang.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/shared.js"></script><script>OpenVibeUI.initPage();@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap" style="max-width: 1000px;">@@@ -44,10 +44,10 @@<div id="ov-footer" data-related="Kaomoji|https://kaomoji.openvibe.tools;Fancy Text|https://fancy.openvibe.tools;Unicode Inspector|https://unicode.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.thumb-layout { display: grid; grid-template-columns: 1fr 320px; gap: 2rem; align-items: start; }.thumb-preview { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }@@@ -123,10 +123,10 @@<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Watermark|https://watermark.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/logo-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.editor-layout { display: grid; grid-template-columns: 1fr 360px; gap: 2rem; align-items: start; }.preview-col { position: sticky; top: 100px; }@@@ -131,10 +131,10 @@<div id="ov-footer" data-related="Wordmark|https://wordmark.openvibe.tools;Badge Maker|https://badge.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/logo-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.tp-layout { max-width: 700px; margin: 0 auto; }.canvas-wrap { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 2rem; text-align: center; margin: 2rem 0; background-image: linear-gradient(45deg, #141414 25%, transparent 25%), linear-gradient(-45deg, #141414 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #141414 75%), linear-gradient(-45deg, transparent 75%, #141414 75%); background-size: 16px 16px; background-position: 0 0, 0 8px, 8px -8px, -8px 0; }@@@ -88,10 +88,10 @@<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Wordmark|https://wordmark.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/logo-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.char-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }.char-table th { text-align: left; padding: 8px 12px; color: var(--text-muted); font-weight: 600; border-bottom: 1px solid var(--border); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; }@@@ -62,10 +62,10 @@<div id="ov-footer" data-related="Text Cleaner|https://clean.openvibe.tools;Binary|https://binary.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.wm-layout { display: grid; grid-template-columns: 1fr 320px; gap: 2rem; align-items: start; }.wm-preview { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.5rem; text-align: center; min-height: 300px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1rem; }@@@ -106,10 +106,10 @@<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Transparent PNG|https://transparent.openvibe.tools;Img.OpenVibe|https://img.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/logo-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script><style>.wordmark-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1.5rem; margin-top: 2rem; }.wm-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; cursor: pointer; transition: all .2s; }@@@ -77,10 +77,10 @@<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Badge Maker|https://badge.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/logo-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -20,7 +20,7 @@<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><link rel="stylesheet" href="/css/shared.css">-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script></head><body><div class="tool-wrap">@@@ -76,10 +76,10 @@<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Text Cleaner|https://clean.openvibe.tools;Symbols|https://symbols.openvibe.tools"></div></div>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script><script src="/js/text-engine.js"></script><script src="/js/shared.js"></script><script>@@@ -107,14 +107,14 @@<!--OV:CONTENT--><div id="ov-footer"></div>-- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>-- <script src="https://openvibe.network/shared/navbar.js"></script>-- <script src="https://openvibe.network/shared/footer.js"></script>-- <script src="https://openvibe.network/shared/notification-ui.js"></script>-- <script src="https://openvibe.network/shared/account-switcher.js"></script>-- <script src="https://openvibe.network/shared/ov-icons.js" defer></script>-- <script src="https://openvibe.network/shared/island.js" defer></script>-- <script src="https://openvibe.network/shared/ui.js" defer></script>++ <script src="/shared/theme-loader.js" defer></script>++ <script src="/shared/navbar.js"></script>++ <script src="/shared/footer.js"></script>++ <script src="/shared/notification-ui.js"></script>++ <script src="/shared/account-switcher.js"></script>++ <script src="/shared/ov-icons.js" defer></script>++ <script src="/shared/island.js" defer></script>++ <script src="/shared/ui.js" defer></script><script src="/js/download-state.js"></script><script src="/js/app.js"></script></body>@@@ -291,7 +291,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {});// ── Static Files ─────────────────────────────────────────────--// (shared client-side libs are loaded absolutely from https://openvibe.network/shared/)++// (the shared browser files come from this app's own pin at /shared: apps/_shared/release.js)app.use(express.static(path.join(__dirname, '..', 'public'), {index: false, // '/' is rendered below with the head for this host (server/seo.js)setHeaders(res, filePath) {
@@ -46,11 +46,20 @@ function toolsRelease(app, appRequire, opts = {}) {
@@ -46,11 +46,20 @@ function toolsRelease(app, appRequire, opts = {}) {
},
},
server: { kind: 'server', files: [`${dir}/server`, 'apps/_shared', `${dir}/package-lock.json`] },
server: { kind: 'server', files: [`${dir}/server`, 'apps/_shared', `${dir}/package-lock.json`] },
};
};
- return createRelease({
+ const release = createRelease({
service: 'tools', root: ROOT, publicDir: `${dir}/public`, components,
service: 'tools', root: ROOT, publicDir: `${dir}/public`, components,
...(schema && { schema }),
...(schema && { schema }),
...opts,
...opts,
});
});
+ // D42 (roadmap WS-P task 4): every app serves its OWN pinned copy of the shared browser files at /shared
+ // (navbar, footer, theme loader, bell…), content-addressed, so its pages run what its pin says and keep
+ // their frame while openvibe.network is down. Mounted with the release routes, before any page fallback.
+ const mountRelease = release.mount;
+ release.mount = function mount(app, o) {
+ app.use('/shared', appRequire('openvibe-shared/serve').handler());
+ return mountRelease.call(this, app, o);
+ };
+ return release;
}
}
module.exports = { toolsRelease, PAGE_FILES, ROOT };
module.exports = { toolsRelease, PAGE_FILES, ROOT };
@@ -51,6 +51,13 @@ const quiet = { warn() {}, log() {}, error() {} };
@@ -51,6 +51,13 @@ const quiet = { warn() {}, log() {}, error() {} };
assert.strictEqual(r.status, 204);
assert.strictEqual(r.status, 204);
const text = await (await fetch(`${base}/metrics`)).text();
const text = await (await fetch(`${base}/metrics`)).text();
assert.match(text, /release_client_updates_total\{outcome="applied",reason="style"\} 2/);
assert.match(text, /release_client_updates_total\{outcome="applied",reason="style"\} 2/);
+ // D42: the app's own pinned shared browser files, content-addressed; nothing else under /shared.
+ const nav = req('openvibe-shared/serve').url('navbar.js');
+ r = await fetch(base + nav);
+ assert.strictEqual(r.status, 200);
+ assert.match(r.headers.get('content-type'), /javascript/);
+ assert.match(r.headers.get('cache-control'), /immutable/, 'the hashed URL is cached for good');
+ assert.strictEqual((await fetch(`${base}/shared/server.js`)).status, 404, 'only the browser files files.js lists');
} finally { await new Promise(res => srv.close(res)); inst.stop && inst.stop(); }
} finally { await new Promise(res => srv.close(res)); inst.stop && inst.stop(); }
- console.log('release: gateway/img/audio/docs serve components (shell, server) valid against their contracts; the others 1.0.0; mount serves /release.json and /release-metrics');
+ console.log('release: gateway/img/audio/docs serve components (shell, server) valid against their contracts; the others 1.0.0; mount serves /release.json, /release-metrics and the pinned /shared files');
})().catch((err) => { console.error(err); process.exit(1); });
})().catch((err) => { console.error(err); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
+'use strict';
+// D42 (roadmap WS-P task 4): Tools pages load the shared browser files (navbar, footer, theme loader, bell…) from
+// their own app's pin at /shared (mounted by apps/_shared/release.js), never from openvibe.network, and only files
+// that pin serves. A page that still pointed at openvibe.network would run whatever Network pins, and lose its frame
+// while Network is down.
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+const { createRequire } = require('module');
+
+const APPS = path.join(__dirname, '..', '..');
+const walk = (dir) => fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => (e.isDirectory() ? (e.name === 'node_modules' ? [] : walk(path.join(dir, e.name))) : [path.join(dir, e.name)]));
+let pages = 0; let refs = 0;
+for (const app of fs.readdirSync(APPS).filter((a) => !a.startsWith('_') && fs.existsSync(path.join(APPS, a, 'public')))) {
+ let files = null;
+ try { files = createRequire(path.join(APPS, app, 'server', 'index.js'))('openvibe-shared/files'); } catch { files = require('openvibe-shared/files'); }
+ for (const f of walk(path.join(APPS, app, 'public')).filter((x) => /\.(html|js)$/.test(x))) {
+ const src = fs.readFileSync(f, 'utf8');
+ const rel = path.relative(APPS, f);
+ assert.ok(!/https:\/\/openvibe\.network\/shared\/[\w.-]+\.js/.test(src), `${rel} loads a shared file from openvibe.network, not its own /shared`);
+ const used = [...src.matchAll(/["'(]\/shared\/([\w.-]+\.js)/g)].map((m) => m[1]);
+ if (used.length) pages++;
+ for (const name of used) { refs++; assert.ok(files.isBrowserFile(name), `${rel}: /shared/${name} is not a browser file of ${app}'s openvibe-shared`); }
+ }
+}
+assert.ok(pages >= 40 && refs >= 200, `pages ${pages}, references ${refs}`);
+console.log(`shared pins: ${pages} pages, ${refs} references, all to their app's own /shared`);
@@ -350,11 +350,11 @@
@@ -350,11 +350,11 @@
<!-- Footer -->
<!-- Footer -->
<div id="ov-footer"></div>
<div id="ov-footer"></div>
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/theme-loader.js" defer></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/ov-jobs.js"></script>
<script src="/js/ov-jobs.js"></script>
<script src="/js/app.js"></script>
<script src="/js/app.js"></script>
</body>
</body>
@@ -356,7 +356,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {
@@ -356,7 +356,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {
});
});
// ── Static Files ─────────────────────────────────────────────
// ── Static Files ─────────────────────────────────────────────
-// (shared client-side libs are loaded absolutely from https://openvibe.network/shared/)
+// (the shared browser files come from this app's own pin at /shared: apps/_shared/release.js)
// Public assets
// Public assets
app.use(express.static(path.join(__dirname, '..', 'public'), {
app.use(express.static(path.join(__dirname, '..', 'public'), {
@@ -223,11 +223,11 @@
@@ -223,11 +223,11 @@
<!-- Footer -->
<!-- Footer -->
<div id="ov-footer"></div>
<div id="ov-footer"></div>
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/theme-loader.js" defer></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/ov-jobs.js"></script>
<script src="/js/ov-jobs.js"></script>
<script src="/js/app.js"></script>
<script src="/js/app.js"></script>
</body>
</body>
@@ -376,7 +376,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {
@@ -376,7 +376,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {
});
});
// ── Static Files ─────────────────────────────────────────────
// ── Static Files ─────────────────────────────────────────────
-// (shared client-side libs are loaded absolutely from https://openvibe.network/shared/)
+// (the shared browser files come from this app's own pin at /shared: apps/_shared/release.js)
// Public assets
// Public assets
app.use(express.static(path.join(__dirname, '..', 'public'), {
app.use(express.static(path.join(__dirname, '..', 'public'), {
@@ -166,11 +166,11 @@
@@ -166,11 +166,11 @@
<p class="data-credit"><i class="fa-solid fa-database"></i> Data from USDA, OSM and store pricing research</p>
<p class="data-credit"><i class="fa-solid fa-database"></i> Data from USDA, OSM and store pricing research</p>
<div id="ov-footer"></div>
<div id="ov-footer"></div>
- <script src="https://openvibe.network/shared/theme-loader.js"></script>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/theme-loader.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/app.js"></script>
<script src="/js/app.js"></script>
<script>
<script>
(function () {
(function () {
@@ -26,7 +26,7 @@
@@ -26,7 +26,7 @@
</script>
</script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
:root {
:root {
--bg-primary: #0b0c12;
--bg-primary: #0b0c12;
@@ -135,8 +135,8 @@
@@ -135,8 +135,8 @@
</div>
</div>
<div id="ov-footer"></div>
<div id="ov-footer"></div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
<script>
<script>
const AUDIO_TOOLS = {
const AUDIO_TOOLS = {
'convert': { name: 'Converter', domain: 'convert.audio.openvibe.tools', icon: 'fa-arrow-right-arrow-left', category: 'Convert' },
'convert': { name: 'Converter', domain: 'convert.audio.openvibe.tools', icon: 'fa-arrow-right-arrow-left', category: 'Convert' },
@@ -144,9 +144,9 @@ a{color:inherit;text-decoration:none}
@@ -144,9 +144,9 @@ a{color:inherit;text-decoration:none}
</div></div>
</div></div>
<div id="ov-footer"></div>
<div id="ov-footer"></div>
<div class="toast" id="toast"></div>
<div class="toast" id="toast"></div>
-<script src="https://openvibe.network/shared/navbar.js"></script>
-<script src="https://openvibe.network/shared/footer.js"></script>
-<script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+<script src="/shared/navbar.js"></script>
+<script src="/shared/footer.js"></script>
+<script src="/shared/theme-loader.js" defer></script>
<script src="/shared/web-runtime.js"></script>
<script src="/shared/web-runtime.js"></script>
<script src="/js/dev-engine.js"></script>
<script src="/js/dev-engine.js"></script>
<script>
<script>
@@ -26,7 +26,7 @@
@@ -26,7 +26,7 @@
</script>
</script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
:root {
:root {
--bg-primary: #0b0c12;
--bg-primary: #0b0c12;
@@ -135,8 +135,8 @@
@@ -135,8 +135,8 @@
</div>
</div>
<div id="ov-footer"></div>
<div id="ov-footer"></div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
<script>
<script>
const IMAGE_TOOLS = {
const IMAGE_TOOLS = {
'convert': { name: 'Converter', domain: 'convert.openvibe.tools', icon: 'fa-arrow-right-arrow-left', category: 'Convert' },
'convert': { name: 'Converter', domain: 'convert.openvibe.tools', icon: 'fa-arrow-right-arrow-left', category: 'Convert' },
@@ -113,7 +113,7 @@
@@ -113,7 +113,7 @@
// Your tools: favourites first (signed in: starred here or on my.openvibe.network), then recent tools,
// Your tools: favourites first (signed in: starred here or on my.openvibe.network), then recent tools,
// this person's (every device they sign in to) or this browser's. Filled in after load.
// this person's (every device they sign in to) or this browser's. Filled in after load.
- function mountIcons(el) { if (window.OpenVibeIcons) OpenVibeIcons.mount(el); else if (!document.getElementById('ov-icons-loader')) { var sc = document.createElement('script'); sc.id = 'ov-icons-loader'; sc.src = 'https://openvibe.network/shared/ov-icons.js'; document.head.appendChild(sc); } }
+ function mountIcons(el) { if (window.OpenVibeIcons) OpenVibeIcons.mount(el); else if (!document.getElementById('ov-icons-loader')) { var sc = document.createElement('script'); sc.id = 'ov-icons-loader'; sc.src = '/shared/ov-icons.js'; document.head.appendChild(sc); } }
favReady = fetch('/api/v1/me/recent-tools', { credentials: 'same-origin' }).then(function (r) { return r.ok ? r.json() : null; })
favReady = fetch('/api/v1/me/recent-tools', { credentials: 'same-origin' }).then(function (r) { return r.ok ? r.json() : null; })
.then(function (d) { favs = (d && d.favorites) || []; if (catalog) syncStars(); return d; }).catch(function () { return null; });
.then(function (d) { favs = (d && d.favorites) || []; if (catalog) syncStars(); return d; }).catch(function () { return null; });
if (recentSec) favReady.then(function (d) {
if (recentSec) favReady.then(function (d) {
@@ -595,9 +595,9 @@
@@ -595,9 +595,9 @@
<div class="toast-container" id="toasts"></div>
<div class="toast-container" id="toasts"></div>
<!-- Shared OpenVibe Navbar -->
<!-- Shared OpenVibe Navbar -->
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/theme-loader.js" defer></script>
<script>
<script>
'use strict';
'use strict';
@@ -426,8 +426,8 @@ function sendTool(req, res, file) {
@@ -426,8 +426,8 @@ function sendTool(req, res, file) {
// The apex is server-rendered from the registry: index, families, tool pages, search,
// The apex is server-rendered from the registry: index, families, tool pages, search,
// sitemap.xml, robots.txt, llms.txt and /api/catalog.json (server/pages/site.js).
// sitemap.xml, robots.txt, llms.txt and /api/catalog.json (server/pages/site.js).
-// This site's own pinned copy of the OpenVibe Frame's browser files (openvibe-shared/serve).
-app.use('/shared', require('openvibe-shared/serve').handler());
+// This site's own pinned copy of the OpenVibe Frame's browser files (/shared, openvibe-shared/serve) is
+// mounted with the release routes: apps/_shared/release.js.
app.use(require('./pages/site').createSiteRouter());
app.use(require('./pages/site').createSiteRouter());
// Net tool subdomains → net.html SPA
// Net tool subdomains → net.html SPA
@@ -167,11 +167,11 @@
@@ -167,11 +167,11 @@
<!-- Footer -->
<!-- Footer -->
<div id="ov-footer"></div>
<div id="ov-footer"></div>
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/theme-loader.js" defer></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/ov-jobs.js"></script>
<script src="/js/ov-jobs.js"></script>
<script src="/js/app.js"></script>
<script src="/js/app.js"></script>
</body>
</body>
@@ -318,7 +318,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {
@@ -318,7 +318,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {
});
});
// ── Static Files ─────────────────────────────────────────────
// ── Static Files ─────────────────────────────────────────────
-// (shared client-side libs are loaded absolutely from https://openvibe.network/shared/)
+// (the shared browser files come from this app's own pin at /shared: apps/_shared/release.js)
// Public assets
// Public assets
app.use(express.static(path.join(__dirname, '..', 'public'), {
app.use(express.static(path.join(__dirname, '..', 'public'), {
@@ -392,11 +392,11 @@
@@ -392,11 +392,11 @@
</div>
</div>
</div>
</div>
-<script src="https://openvibe.network/shared/theme-loader.js"></script>
-<script src="https://openvibe.network/shared/navbar.js"></script>
-<script src="https://openvibe.network/shared/footer.js"></script>
-<script src="https://openvibe.network/shared/notification-ui.js"></script>
-<script src="https://openvibe.network/shared/account-switcher.js"></script>
+<script src="/shared/theme-loader.js"></script>
+<script src="/shared/navbar.js"></script>
+<script src="/shared/footer.js"></script>
+<script src="/shared/notification-ui.js"></script>
+<script src="/shared/account-switcher.js"></script>
<script src="/js/app.js"></script>
<script src="/js/app.js"></script>
<script>
<script>
(function () {
(function () {
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -79,10 +79,10 @@
@@ -79,10 +79,10 @@
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Zalgo Text|https://zalgo.openvibe.tools;Symbols|https://symbols.openvibe.tools"></div>
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Zalgo Text|https://zalgo.openvibe.tools;Symbols|https://symbols.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.badge-layout { display: grid; grid-template-columns: 340px 1fr; gap: 2rem; align-items: start; }
.badge-layout { display: grid; grid-template-columns: 340px 1fr; gap: 2rem; align-items: start; }
.badge-controls { display: flex; flex-direction: column; gap: 1rem; }
.badge-controls { display: flex; flex-direction: column; gap: 1rem; }
@@ -107,10 +107,10 @@
@@ -107,10 +107,10 @@
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Wordmark|https://wordmark.openvibe.tools"></div>
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Wordmark|https://wordmark.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -53,10 +53,10 @@
@@ -53,10 +53,10 @@
<div id="ov-footer" data-related="Morse Code|https://morse.openvibe.tools;Escape / Unescape|https://escape.openvibe.tools"></div>
<div id="ov-footer" data-related="Morse Code|https://morse.openvibe.tools;Escape / Unescape|https://escape.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -49,10 +49,10 @@
@@ -49,10 +49,10 @@
<div id="ov-footer" data-related="Nickname Generator|https://nickname.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div>
<div id="ov-footer" data-related="Nickname Generator|https://nickname.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -55,10 +55,10 @@
@@ -55,10 +55,10 @@
<div id="ov-footer" data-related="Morse Code|https://morse.openvibe.tools;Binary|https://binary.openvibe.tools"></div>
<div id="ov-footer" data-related="Morse Code|https://morse.openvibe.tools;Binary|https://binary.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -44,10 +44,10 @@
@@ -44,10 +44,10 @@
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Symbols|https://symbols.openvibe.tools"></div>
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Symbols|https://symbols.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -44,10 +44,10 @@
@@ -44,10 +44,10 @@
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Text Cleaner|https://clean.openvibe.tools;Text Counter|https://count.openvibe.tools"></div>
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Text Cleaner|https://clean.openvibe.tools;Text Counter|https://count.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -63,10 +63,10 @@
@@ -63,10 +63,10 @@
<div id="ov-footer" data-related="Text Counter|https://count.openvibe.tools;Escape / Unescape|https://escape.openvibe.tools"></div>
<div id="ov-footer" data-related="Text Counter|https://count.openvibe.tools;Escape / Unescape|https://escape.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.compare-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.compare-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.compare-pane label { display: block; font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 6px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; }
.compare-pane label { display: block; font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 6px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; }
@@ -75,10 +75,10 @@ A brand new line was added.</textarea>
@@ -75,10 +75,10 @@ A brand new line was added.</textarea>
<div id="ov-footer" data-related="Clean Text|https://clean.openvibe.tools;Sort Lines|https://sort.openvibe.tools"></div>
<div id="ov-footer" data-related="Clean Text|https://clean.openvibe.tools;Sort Lines|https://sort.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/format-engine.js"></script>
<script src="/js/format-engine.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -42,10 +42,10 @@
@@ -42,10 +42,10 @@
<div id="ov-footer" data-related="Text Cleaner|https://clean.openvibe.tools;Sort Lines|https://sort.openvibe.tools;Case Converter|https://case.openvibe.tools"></div>
<div id="ov-footer" data-related="Text Cleaner|https://clean.openvibe.tools;Sort Lines|https://sort.openvibe.tools;Case Converter|https://case.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.escape-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
.escape-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
.escape-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.5rem; }
.escape-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.5rem; }
@@ -53,10 +53,10 @@
@@ -53,10 +53,10 @@
<div id="ov-footer" data-related="Unicode Inspector|https://unicode.openvibe.tools;Text Cleaner|https://clean.openvibe.tools"></div>
<div id="ov-footer" data-related="Unicode Inspector|https://unicode.openvibe.tools;Text Cleaner|https://clean.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -48,10 +48,10 @@
@@ -48,10 +48,10 @@
<div id="ov-footer" data-related="Zalgo Text|https://zalgo.openvibe.tools;ASCII Art|https://ascii.openvibe.tools;Case Converter|https://case.openvibe.tools"></div>
<div id="ov-footer" data-related="Zalgo Text|https://zalgo.openvibe.tools;ASCII Art|https://ascii.openvibe.tools;Case Converter|https://case.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.alert-banner {
.alert-banner {
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
@@ -470,10 +470,10 @@
@@ -470,10 +470,10 @@
<div id="ov-footer" data-related="OpenVibePeso|https://peso.openvibe.tools;OpenVibeSpanish|https://spanish.openvibe.tools;OpenVibeSlang|https://slang.openvibe.tools"></div>
<div id="ov-footer" data-related="OpenVibePeso|https://peso.openvibe.tools;OpenVibeSpanish|https://spanish.openvibe.tools;OpenVibeSlang|https://slang.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
OpenVibeUI.initPage();
OpenVibeUI.initPage();
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="hub-hero">
<div class="hub-hero">
@@ -228,10 +228,10 @@
@@ -228,10 +228,10 @@
<div id="ov-footer"></div>
<div id="ov-footer"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.json-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; min-height: 400px; }
.json-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; min-height: 400px; }
.json-pane { display: flex; flex-direction: column; }
.json-pane { display: flex; flex-direction: column; }
@@ -72,10 +72,10 @@
@@ -72,10 +72,10 @@
<div id="ov-footer" data-related="Escape / Unescape|https://escape.openvibe.tools;Markdown Preview|https://markdown.openvibe.tools"></div>
<div id="ov-footer" data-related="Escape / Unescape|https://escape.openvibe.tools;Markdown Preview|https://markdown.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/format-engine.js"></script>
<script src="/js/format-engine.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap" style="max-width: 1000px;">
<div class="tool-wrap" style="max-width: 1000px;">
@@ -34,10 +34,10 @@
@@ -34,10 +34,10 @@
<div id="ov-footer" data-related="Symbols|https://symbols.openvibe.tools;Bio Generator|https://bio.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div>
<div id="ov-footer" data-related="Symbols|https://symbols.openvibe.tools;Bio Generator|https://bio.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="hub-hero">
<div class="hub-hero">
@@ -125,10 +125,10 @@
@@ -125,10 +125,10 @@
<div id="ov-footer"></div>
<div id="ov-footer"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.md-editor { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; min-height: 500px; }
.md-editor { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; min-height: 500px; }
.md-pane { display: flex; flex-direction: column; }
.md-pane { display: flex; flex-direction: column; }
@@ -93,10 +93,10 @@ Made with ♥ by the OpenVibe Network</textarea>
@@ -93,10 +93,10 @@ Made with ♥ by the OpenVibe Network</textarea>
<div id="ov-footer" data-related="Escape / Unescape|https://escape.openvibe.tools;Text Counter|https://count.openvibe.tools"></div>
<div id="ov-footer" data-related="Escape / Unescape|https://escape.openvibe.tools;Text Counter|https://count.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/format-engine.js"></script>
<script src="/js/format-engine.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -55,10 +55,10 @@
@@ -55,10 +55,10 @@
<div id="ov-footer" data-related="Binary|https://binary.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div>
<div id="ov-footer" data-related="Binary|https://binary.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -48,10 +48,10 @@
@@ -48,10 +48,10 @@
<div id="ov-footer" data-related="Bio Generator|https://bio.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div>
<div id="ov-footer" data-related="Bio Generator|https://bio.openvibe.tools;Fancy Text|https://fancy.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.converter-wrap {
.converter-wrap {
display: flex;
display: flex;
@@ -183,10 +183,10 @@
@@ -183,10 +183,10 @@
<div id="ov-footer" data-related="ICE Alerts|https://ice.openvibe.tools;OpenVibeSpanish|https://spanish.openvibe.tools;OpenVibeSlang|https://slang.openvibe.tools"></div>
<div id="ov-footer" data-related="ICE Alerts|https://ice.openvibe.tools;OpenVibeSpanish|https://spanish.openvibe.tools;OpenVibeSlang|https://slang.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
OpenVibeUI.initPage();
OpenVibeUI.initPage();
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -44,10 +44,10 @@
@@ -44,10 +44,10 @@
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Zalgo Text|https://zalgo.openvibe.tools"></div>
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Zalgo Text|https://zalgo.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.search-wrap {
.search-wrap {
position: relative; margin-bottom: 20px;
position: relative; margin-bottom: 20px;
@@ -133,10 +133,10 @@
@@ -133,10 +133,10 @@
<div id="ov-footer" data-related="ICE Alerts|https://ice.openvibe.tools;OpenVibePeso|https://peso.openvibe.tools;OpenVibeSpanish|https://spanish.openvibe.tools"></div>
<div id="ov-footer" data-related="ICE Alerts|https://ice.openvibe.tools;OpenVibePeso|https://peso.openvibe.tools;OpenVibeSpanish|https://spanish.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
OpenVibeUI.initPage();
OpenVibeUI.initPage();
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -51,10 +51,10 @@
@@ -51,10 +51,10 @@
<div id="ov-footer" data-related="Case Converter|https://case.openvibe.tools;Text Cleaner|https://clean.openvibe.tools"></div>
<div id="ov-footer" data-related="Case Converter|https://case.openvibe.tools;Text Cleaner|https://clean.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -61,10 +61,10 @@ elderberry</textarea>
@@ -61,10 +61,10 @@ elderberry</textarea>
<div id="ov-footer" data-related="Text Cleaner|https://clean.openvibe.tools;Text Counter|https://count.openvibe.tools"></div>
<div id="ov-footer" data-related="Text Cleaner|https://clean.openvibe.tools;Text Counter|https://count.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.phrase-search {
.phrase-search {
width: 100%;
width: 100%;
@@ -119,10 +119,10 @@
@@ -119,10 +119,10 @@
<div id="ov-footer" data-related="ICE Alerts|https://ice.openvibe.tools;OpenVibePeso|https://peso.openvibe.tools;OpenVibeSlang|https://slang.openvibe.tools"></div>
<div id="ov-footer" data-related="ICE Alerts|https://ice.openvibe.tools;OpenVibePeso|https://peso.openvibe.tools;OpenVibeSlang|https://slang.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
OpenVibeUI.initPage();
OpenVibeUI.initPage();
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap" style="max-width: 1000px;">
<div class="tool-wrap" style="max-width: 1000px;">
@@ -44,10 +44,10 @@
@@ -44,10 +44,10 @@
<div id="ov-footer" data-related="Kaomoji|https://kaomoji.openvibe.tools;Fancy Text|https://fancy.openvibe.tools;Unicode Inspector|https://unicode.openvibe.tools"></div>
<div id="ov-footer" data-related="Kaomoji|https://kaomoji.openvibe.tools;Fancy Text|https://fancy.openvibe.tools;Unicode Inspector|https://unicode.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.thumb-layout { display: grid; grid-template-columns: 1fr 320px; gap: 2rem; align-items: start; }
.thumb-layout { display: grid; grid-template-columns: 1fr 320px; gap: 2rem; align-items: start; }
.thumb-preview { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.thumb-preview { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
@@ -123,10 +123,10 @@
@@ -123,10 +123,10 @@
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Watermark|https://watermark.openvibe.tools"></div>
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Watermark|https://watermark.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.editor-layout { display: grid; grid-template-columns: 1fr 360px; gap: 2rem; align-items: start; }
.editor-layout { display: grid; grid-template-columns: 1fr 360px; gap: 2rem; align-items: start; }
.preview-col { position: sticky; top: 100px; }
.preview-col { position: sticky; top: 100px; }
@@ -131,10 +131,10 @@
@@ -131,10 +131,10 @@
<div id="ov-footer" data-related="Wordmark|https://wordmark.openvibe.tools;Badge Maker|https://badge.openvibe.tools"></div>
<div id="ov-footer" data-related="Wordmark|https://wordmark.openvibe.tools;Badge Maker|https://badge.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.tp-layout { max-width: 700px; margin: 0 auto; }
.tp-layout { max-width: 700px; margin: 0 auto; }
.canvas-wrap { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 2rem; text-align: center; margin: 2rem 0; background-image: linear-gradient(45deg, #141414 25%, transparent 25%), linear-gradient(-45deg, #141414 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #141414 75%), linear-gradient(-45deg, transparent 75%, #141414 75%); background-size: 16px 16px; background-position: 0 0, 0 8px, 8px -8px, -8px 0; }
.canvas-wrap { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 2rem; text-align: center; margin: 2rem 0; background-image: linear-gradient(45deg, #141414 25%, transparent 25%), linear-gradient(-45deg, #141414 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #141414 75%), linear-gradient(-45deg, transparent 75%, #141414 75%); background-size: 16px 16px; background-position: 0 0, 0 8px, 8px -8px, -8px 0; }
@@ -88,10 +88,10 @@
@@ -88,10 +88,10 @@
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Wordmark|https://wordmark.openvibe.tools"></div>
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Wordmark|https://wordmark.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.char-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.char-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.char-table th { text-align: left; padding: 8px 12px; color: var(--text-muted); font-weight: 600; border-bottom: 1px solid var(--border); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; }
.char-table th { text-align: left; padding: 8px 12px; color: var(--text-muted); font-weight: 600; border-bottom: 1px solid var(--border); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; }
@@ -62,10 +62,10 @@
@@ -62,10 +62,10 @@
<div id="ov-footer" data-related="Text Cleaner|https://clean.openvibe.tools;Binary|https://binary.openvibe.tools"></div>
<div id="ov-footer" data-related="Text Cleaner|https://clean.openvibe.tools;Binary|https://binary.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.wm-layout { display: grid; grid-template-columns: 1fr 320px; gap: 2rem; align-items: start; }
.wm-layout { display: grid; grid-template-columns: 1fr 320px; gap: 2rem; align-items: start; }
.wm-preview { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.5rem; text-align: center; min-height: 300px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1rem; }
.wm-preview { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.5rem; text-align: center; min-height: 300px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1rem; }
@@ -106,10 +106,10 @@
@@ -106,10 +106,10 @@
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Transparent PNG|https://transparent.openvibe.tools;Img.OpenVibe|https://img.openvibe.tools"></div>
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Transparent PNG|https://transparent.openvibe.tools;Img.OpenVibe|https://img.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
<style>
<style>
.wordmark-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1.5rem; margin-top: 2rem; }
.wordmark-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1.5rem; margin-top: 2rem; }
.wm-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; cursor: pointer; transition: all .2s; }
.wm-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; cursor: pointer; transition: all .2s; }
@@ -77,10 +77,10 @@
@@ -77,10 +77,10 @@
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Badge Maker|https://badge.openvibe.tools"></div>
<div id="ov-footer" data-related="Title Cards|https://title.openvibe.tools;Badge Maker|https://badge.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/logo-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/css/shared.css">
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
</head>
</head>
<body>
<body>
<div class="tool-wrap">
<div class="tool-wrap">
@@ -76,10 +76,10 @@
@@ -76,10 +76,10 @@
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Text Cleaner|https://clean.openvibe.tools;Symbols|https://symbols.openvibe.tools"></div>
<div id="ov-footer" data-related="Fancy Text|https://fancy.openvibe.tools;Text Cleaner|https://clean.openvibe.tools;Symbols|https://symbols.openvibe.tools"></div>
</div>
</div>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/text-engine.js"></script>
<script src="/js/shared.js"></script>
<script src="/js/shared.js"></script>
<script>
<script>
@@ -107,14 +107,14 @@
@@ -107,14 +107,14 @@
<!--OV:CONTENT-->
<!--OV:CONTENT-->
<div id="ov-footer"></div>
<div id="ov-footer"></div>
- <script src="https://openvibe.network/shared/theme-loader.js" defer></script>
- <script src="https://openvibe.network/shared/navbar.js"></script>
- <script src="https://openvibe.network/shared/footer.js"></script>
- <script src="https://openvibe.network/shared/notification-ui.js"></script>
- <script src="https://openvibe.network/shared/account-switcher.js"></script>
- <script src="https://openvibe.network/shared/ov-icons.js" defer></script>
- <script src="https://openvibe.network/shared/island.js" defer></script>
- <script src="https://openvibe.network/shared/ui.js" defer></script>
+ <script src="/shared/theme-loader.js" defer></script>
+ <script src="/shared/navbar.js"></script>
+ <script src="/shared/footer.js"></script>
+ <script src="/shared/notification-ui.js"></script>
+ <script src="/shared/account-switcher.js"></script>
+ <script src="/shared/ov-icons.js" defer></script>
+ <script src="/shared/island.js" defer></script>
+ <script src="/shared/ui.js" defer></script>
<script src="/js/download-state.js"></script>
<script src="/js/download-state.js"></script>
<script src="/js/app.js"></script>
<script src="/js/app.js"></script>
</body>
</body>
@@ -291,7 +291,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {
@@ -291,7 +291,7 @@ app.get('/api/internal/analytics/bots', (req, res) => {
});
});
// ── Static Files ─────────────────────────────────────────────
// ── Static Files ─────────────────────────────────────────────
-// (shared client-side libs are loaded absolutely from https://openvibe.network/shared/)
+// (the shared browser files come from this app's own pin at /shared: apps/_shared/release.js)
app.use(express.static(path.join(__dirname, '..', 'public'), {
app.use(express.static(path.join(__dirname, '..', 'public'), {
index: false, // '/' is rendered below with the head for this host (server/seo.js)
index: false, // '/' is rendered below with the head for this host (server/seo.js)
setHeaders(res, filePath) {
setHeaders(res, filePath) {