1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 07:39:15 +03:00

font struggling

This commit is contained in:
Anton Afanasyeu
2026-05-30 18:22:59 +02:00
parent a9a9590698
commit 6e28ffa93b
12 changed files with 997 additions and 22 deletions

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<html lang="en" data-theme="dark" data-clock-font="dseg14-classic">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -11,12 +11,13 @@
})();
</script>
<link rel="stylesheet" href="/app/androidcast_project/crashes/assets/css/app.css">
<link rel="stylesheet" href="hub.css?v=20260530i">
<link rel="stylesheet" href="hub.css?v=20260530k">
<link rel="stylesheet" href="hub-logo-layers.css">
<script src="assets/analytics.config.js"></script>
<script src="/app/androidcast_project/crashes/assets/js/analytics.js" defer></script>
</head>
<body class="hub-page">
<div id="lcd-alphabet-host" hidden aria-hidden="true"></div>
<div id="overtime-alphabet-host" hidden aria-hidden="true"></div>
<div id="quartz-alphabet-host" hidden aria-hidden="true"></div>
<div id="hub-logo-stage" class="hub-logo-stage" hidden aria-hidden="true"></div>
@@ -151,14 +152,52 @@
}
var stage = document.getElementById('hub-logo-stage');
if (!stage) return;
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
var supportsSvg = !!(svg && svg.createSVGRect);
var supportsLayout = !!(window.CSS && CSS.supports && CSS.supports('object-fit', 'cover'));
if (!supportsSvg || !supportsLayout) return;
var logoEnabled = !!(stage && supportsSvg && supportsLayout);
var logoBuild = '20260530i';
var logoBuild = '20260530k';
function hubAsset(rel) {
var link = document.querySelector('link[href*="hub.css"]');
if (link) {
var href = link.getAttribute('href') || '';
return href.replace(/[^/]*$/, '') + rel;
}
return rel;
}
var LCD_FONT_CATALOG = {
'dseg14-classic': {
url: 'assets/dseg14-classic-alphabet.svg',
prefix: 'd14c',
css: '"DSEG14 Classic", "Consolas", monospace'
},
'dseg14-modern': {
url: 'assets/dseg14-modern-alphabet.svg',
prefix: 'd14m',
css: '"DSEG14 Modern", "Consolas", monospace'
},
'ds-digital': {
url: 'assets/ds-digital-alphabet.svg',
prefix: 'dsd',
css: '"DS-Digital", "Consolas", monospace'
},
'digital-7-mono-italic': {
url: 'assets/digital-7-mono-italic-alphabet.svg',
prefix: 'd7mi',
css: '"Digital-7 Mono Italic", "Consolas", monospace'
}
};
var clockFontParams = new URLSearchParams(window.location.search);
var clockFontKey = (clockFontParams.get('clockFont') || 'dseg14-classic').toLowerCase();
if (!LCD_FONT_CATALOG[clockFontKey]) clockFontKey = 'dseg14-classic';
var lcdFontReady = false;
var lcdSymbolIds = {};
var lcdFontPrefix = LCD_FONT_CATALOG[clockFontKey].prefix;
var fragments = {
space: 'assets/hub-logo-fragment-space.svg',
globe: 'assets/hub-logo-fragment-globe.svg',
@@ -185,6 +224,7 @@
return base + '?v=' + logoBuild;
}
if (logoEnabled) {
list.forEach(function (name) {
var img = document.createElement('img');
img.className = 'hub-logo-layer hub-logo-layer--' + name;
@@ -208,6 +248,7 @@
stage.hidden = false;
stage.setAttribute('aria-hidden', 'true');
}
/* Nav dock overlaps globe left hemisphere; stack top → bottom. */
var GLOBE_VIEW = { imgW: 1920, imgH: 1080, cx: 720, cy: 560, r: 330 };
@@ -443,7 +484,7 @@
}
function bindCompassOpen() {
if (!compassLayer) return;
if (!logoEnabled || !compassLayer) return;
var open = function () { openDeploymentModel(); };
compassLayer.addEventListener('click', open);
compassLayer.addEventListener('keydown', function (ev) {
@@ -586,6 +627,113 @@
return null;
}
function lcdSymbolId(ch) {
var p = lcdFontPrefix;
if (ch >= '0' && ch <= '9') return p + '-' + ch;
if (ch === ':') return p + '-colon';
if (ch === '+') return p + '-plus';
if (ch === '-') return p + '-minus';
if (ch === '/') return p + '-slash';
if (ch === '.') return p + '-dot';
if (ch === ' ') return p + '-space';
if (ch >= 'A' && ch <= 'Z') return p + '-' + ch;
if (ch >= 'a' && ch <= 'z') return p + '-' + ch.toUpperCase();
return null;
}
function loadLcdAlphabet(done) {
var host = document.getElementById('lcd-alphabet-host');
var spec = LCD_FONT_CATALOG[clockFontKey];
if (!host || !spec) { done(false); return; }
fetch(hubAsset(spec.url) + '?v=' + logoBuild)
.then(function (res) {
if (!res.ok) throw new Error('alphabet fetch failed');
return res.text();
})
.then(function (svgText) {
host.replaceChildren();
var parser = new DOMParser();
var doc = parser.parseFromString(svgText, 'image/svg+xml');
var sprite = doc.documentElement;
if (sprite.querySelector('parsererror')) throw new Error('alphabet parse failed');
sprite.setAttribute('aria-hidden', 'true');
sprite.style.cssText = 'position:absolute;width:0;height:0;overflow:hidden;pointer-events:none';
host.appendChild(sprite);
lcdSymbolIds = {};
sprite.querySelectorAll('symbol').forEach(function (sym) {
if (sym.id) lcdSymbolIds[sym.id] = sym;
});
lcdFontReady = Object.keys(lcdSymbolIds).length > 0;
document.documentElement.setAttribute('data-clock-font', clockFontKey);
var widget = document.getElementById('hub-clock-widget');
if (widget) widget.setAttribute('data-lcd-mode', lcdFontReady ? 'svg' : 'text');
done(lcdFontReady);
})
.catch(function () {
lcdFontReady = false;
var widget = document.getElementById('hub-clock-widget');
if (widget) widget.setAttribute('data-lcd-mode', 'text');
done(false);
});
}
function cloneLcdSymbolContent(svg, sym) {
sym.childNodes.forEach(function (node) {
if (node.nodeType !== 1) return;
svg.appendChild(node.cloneNode(true));
});
}
function renderLcdText(el, text, opts) {
opts = opts || {};
if (!el) return;
el.innerHTML = '';
el.classList.add('cw-lcd-text');
var fontCss = LCD_FONT_CATALOG[clockFontKey].css;
if (!lcdFontReady) {
el.textContent = text;
el.style.fontFamily = fontCss;
return;
}
var renderedSvg = false;
String(text).split('').forEach(function (ch) {
var up = ch.toUpperCase();
var sid = lcdSymbolId(up === ch ? ch : up);
if (!sid || !lcdSymbolIds[sid]) {
var fb = document.createElement('span');
fb.className = 'ot-fallback-char' + (opts.bold ? ' ot-fallback-char--bold' : '');
fb.style.fontFamily = fontCss;
fb.textContent = ch === ' ' ? '\u00A0' : ch;
el.appendChild(fb);
return;
}
var sym = lcdSymbolIds[sid];
if (!sym.childNodes.length) {
var emptyFb = document.createElement('span');
emptyFb.className = 'ot-fallback-char';
emptyFb.style.fontFamily = fontCss;
emptyFb.textContent = ch === ' ' ? '\u00A0' : ch;
el.appendChild(emptyFb);
return;
}
renderedSvg = true;
var wrap = document.createElement('span');
wrap.className = 'ot-glyph-wrap' + (opts.bold ? ' ot-glyph-wrap--bold' : '');
if (opts.blinkColon && ch === ':') wrap.className += ' ot-colon-blink';
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', sym.getAttribute('viewBox') || '0 0 20 32');
svg.setAttribute('class', 'ot-glyph');
svg.setAttribute('aria-hidden', 'true');
cloneLcdSymbolContent(svg, sym);
wrap.appendChild(svg);
el.appendChild(wrap);
});
if (!renderedSvg) {
el.textContent = text;
el.style.fontFamily = fontCss;
}
}
function loadQuartzAlphabet(done) {
var host = document.getElementById('quartz-alphabet-host');
if (!host) { done(false); return; }
@@ -817,19 +965,18 @@
});
function setClockopiaText(el, text) {
if (!el) return;
el.textContent = text;
renderLcdText(el, text, {});
}
function updateClockWidget() {
var now = new Date();
var p = timeParts(now, primaryTz);
setClockopiaText(document.getElementById('cw-hm-lcd'), p.hh + ':' + p.mm);
setClockopiaText(document.getElementById('cw-colon-lcd'), ':');
setClockopiaText(document.getElementById('cw-ss-lcd'), p.ss);
setClockopiaText(document.getElementById('cw-tz-text'), gmtOffsetLabel(now, primaryTz));
setClockopiaText(document.getElementById('cw-region'), regionLabel(primaryTz));
setClockopiaText(document.getElementById('cw-date-line'), formatDateLine(now, primaryTz));
renderLcdText(document.getElementById('cw-hm-lcd'), p.hh + ':' + p.mm, { bold: true });
renderLcdText(document.getElementById('cw-colon-lcd'), ':', { blinkColon: true });
renderLcdText(document.getElementById('cw-ss-lcd'), p.ss, {});
renderLcdText(document.getElementById('cw-tz-text'), gmtOffsetLabel(now, primaryTz), {});
renderLcdText(document.getElementById('cw-region'), regionLabel(primaryTz), {});
renderLcdText(document.getElementById('cw-date-line'), formatDateLine(now, primaryTz), {});
secondary.forEach(function (clock) {
var t = timeParts(now, clock.tz);
@@ -920,8 +1067,10 @@
widget.releasePointerCapture(ev.pointerId);
});
})();
updateClockWidget();
setInterval(updateClockWidget, 1000);
loadLcdAlphabet(function () {
updateClockWidget();
setInterval(updateClockWidget, 1000);
});
})();
</script>
</body>