feat(ui): show server URL in Add Display modal + GitHub Releases link on /download/apk (#210)

- Add Device modal now shows the server URL and full Smart TV player URL
- Smart TV note changed from bare /player to full URL (dynamic via JS)
- /download/apk error page now includes a download link to GitHub Releases
- i18n keys added in en + es, old smart_tv_note removed
This commit is contained in:
Fabian Mendoza 2026-07-23 11:25:00 -04:00 committed by GitHub
parent 9e0048eec2
commit e7483dfc24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 21 additions and 6 deletions

View file

@ -184,8 +184,13 @@
<label data-i18n="add_display.display_name">Display Name (optional)</label>
<input type="text" id="deviceNameInput" data-i18n-placeholder="add_display.name_placeholder" placeholder="e.g., Lobby TV" class="input">
</div>
<div style="border-top:1px solid var(--border,#1e293b);margin-top:20px;padding-top:16px">
<p style="font-size:12px;color:var(--text-muted,#64748b);margin-bottom:10px;font-weight:500" data-i18n="add_display.need_player">Need a player app? Install one to get a pairing code:</p>
<div style="background:var(--bg-input,#0f172a);border-radius:6px;padding:10px 12px;margin-top:16px;border:1px solid var(--border,#1e293b)">
<span style="font-size:11px;color:var(--text-muted,#64748b);font-weight:500" data-i18n="add_display.server_url_label">Server URL</span>
<code id="addDeviceServerUrl" style="display:block;font-size:13px;margin:2px 0;color:var(--primary,#3b82f6);word-break:break-all"></code>
<span style="font-size:11px;color:var(--text-muted,#64748b)" data-i18n="add_display.server_url_hint">Use this URL when configuring the Android app</span>
</div>
<div style="border-top:1px solid var(--border,#1e293b);margin-top:20px;padding-top:16px">
<p style="font-size:12px;color:var(--text-muted,#64748b);margin-bottom:10px;font-weight:500" data-i18n="add_display.need_player">Need a player app? Install one to get a pairing code:</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
<a href="/download/apk" class="btn btn-secondary btn-sm" style="text-decoration:none;justify-content:center;font-size:12px">
&#129302; <span data-i18n="add_display.android_apk">Android APK</span>
@ -200,7 +205,7 @@
&#128187; <span data-i18n="add_display.windows">Windows</span>
</a>
</div>
<p style="font-size:11px;color:var(--text-muted,#64748b);margin-top:8px" data-i18n-html="add_display.smart_tv_note">Smart TVs (LG/Samsung): open the built-in browser and navigate to <code style="background:var(--bg-input,#0f172a);padding:1px 4px;border-radius:3px">/player</code></p>
<p style="font-size:11px;color:var(--text-muted,#64748b);margin-top:8px;line-height:1.5"><span data-i18n="add_display.smart_tv_prefix">Smart TVs (LG/Samsung): open the built-in browser and navigate to</span> <code id="addDeviceSmartTvUrl" style="background:var(--bg-input,#0f172a);padding:1px 4px;border-radius:3px;white-space:nowrap">/player</code></p>
</div>
<div style="border-top:1px solid var(--border,#1e293b);margin-top:16px;padding-top:16px">
<p style="font-size:12px;color:var(--text-muted,#64748b);margin-bottom:10px;font-weight:500" data-i18n="add_display.owner_prompt">Provisioning a fresh / factory-reset Android panel? Enroll it as device owner for silent updates, reboot &amp; kiosk:</p>

View file

@ -1530,10 +1530,12 @@ export default {
'add_display.web_player': 'Web Player',
'add_display.raspberry_pi': 'Raspberry Pi',
'add_display.windows': 'Windows',
'add_display.smart_tv_note': 'Smart TVs (LG/Samsung): open the built-in browser and navigate to <code style="background:var(--bg-input,#0f172a);padding:1px 4px;border-radius:3px">/player</code>',
'add_display.owner_prompt': 'Provisioning a fresh / factory-reset Android panel? Enroll it as device owner for silent updates, reboot & kiosk:',
'add_display.owner_qr_btn': 'Show device-owner QR',
'add_display.pair_btn': 'Pair Display',
'add_display.server_url_label': 'Server URL',
'add_display.server_url_hint': 'Use this URL when configuring the Android app',
'add_display.smart_tv_prefix': 'Smart TVs (LG/Samsung): open the built-in browser and navigate to',
// Workspace switcher (Phase 3 MVP). devices_count is the only count exposed
// today; matching pattern for users/playlists/etc. when those land later.

View file

@ -1176,6 +1176,8 @@ export default {
'add_display.web_player': 'Reproductor web',
'add_display.raspberry_pi': 'Raspberry Pi',
'add_display.windows': 'Windows',
'add_display.smart_tv_note': 'Smart TVs (LG/Samsung): abre el navegador integrado y ve a <code style="background:var(--bg-input,#0f172a);padding:1px 4px;border-radius:3px">/player</code>',
'add_display.pair_btn': 'Vincular pantalla',
'add_display.server_url_label': 'URL del servidor',
'add_display.server_url_hint': 'Usa esta URL al configurar la app de Android',
'add_display.smart_tv_prefix': 'Smart TVs (LG/Samsung): abre el navegador integrado y ve a',
};

View file

@ -297,6 +297,12 @@ export function render(container) {
document.getElementById('pairingCodeInput').value = '';
document.getElementById('deviceNameInput').value = '';
document.getElementById('pairingCodeInput').focus();
const serverUrl = `${window.location.protocol}//${window.location.host}`;
const el = document.getElementById('addDeviceServerUrl');
if (el) el.textContent = serverUrl;
const tvEl = document.getElementById('addDeviceSmartTvUrl');
if (tvEl) tvEl.textContent = `${serverUrl}/player`;
});
// #device-owner: provision a fresh/factory-reset Android panel straight from Add Display.

View file

@ -943,7 +943,7 @@ const otaDownloadState = otaDownloadGuard.prodState(); // #146 P3.8: shared si
app.get('/download/apk', (req, res) => {
const apk = apkCache.get();
if (!apk.exists) {
return res.status(404).send(`<!DOCTYPE html><html><head><title>APK Not Found</title><style>body{font-family:-apple-system,system-ui,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0;background:#0f172a;color:#e2e8f0}div{text-align:center;max-width:500px;padding:24px}h1{color:#f87171;font-size:24px}code{background:#1e293b;padding:2px 8px;border-radius:4px;font-size:14px}p{line-height:1.6;color:#94a3b8}</style></head><body><div><h1>APK Not Available</h1><p>The Android APK has not been compiled yet. In Docker, mount a built APK at <code>/data/ScreenTinker.apk</code>, or use the <a href="/player" style="color:#3b82f6">web player</a>.</p></div></body></html>`);
return res.status(404).send(`<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>APK Not Available — ScreenTinker</title><style>body{font-family:-apple-system,system-ui,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0;background:#0f172a;color:#e2e8f0}div{text-align:center;max-width:480px;padding:32px 24px}h1{color:#f87171;font-size:22px;margin:0 0 8px}p{line-height:1.6;color:#94a3b8;font-size:14px;margin:0 0 20px}code{background:#1e293b;padding:2px 6px;border-radius:4px;font-size:13px}a{color:#3b82f6;text-decoration:none}a:hover{text-decoration:underline}.btn{display:inline-block;background:#2563eb;color:#fff;padding:10px 20px;border-radius:6px;font-size:14px;font-weight:500;text-decoration:none;margin-bottom:24px}.btn:hover{background:#1d4ed8;text-decoration:none}.muted{font-size:12px;color:#64748b}</style></head><body><div><h1>APK Not Available</h1><p>The Android APK has not been compiled yet.</p><a class="btn" href="https://github.com/screentinker/screentinker/releases/latest" target="_blank" rel="noopener">&#128230; Download from GitHub Releases</a><p class="muted">Self-hosting? Mount a built APK at <code>/data/ScreenTinker.apk</code> to serve it from this instance. Or use the <a href="/player">web player</a> instead.</p></div></body></html>`);
}
const verdict = otaDownloadGuard.admit(otaDownloadState, getBand());