diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-11 13:57:34 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-11 13:57:34 +0200 |
| commit | 4b74ff3fc54f35fbc41d9c0f7d2b4ec89475154a (patch) | |
| tree | 6a0ab44c40602415015cbacda30e5615e0863d8f /packages/meshbay-hub/src/meshbay_hub/static | |
| parent | cab9ba28cfba9d90d0a6c4c00dbba852825f66c2 (diff) | |
| download | meshbay-4b74ff3fc54f35fbc41d9c0f7d2b4ec89475154a.tar.gz | |
feat(hub): legal information link at the foot of the sidebar
A small link to the hub's legal pages, pinned to the bottom of the
sidebar once signed in. It opens in a new tab: the desktop application
refuses to navigate away from its interface and hands a new window to
the system browser, and in a browser it keeps the session on screen. The
address comes from hubBase(), so it is the legal pages of the hub in use.
The sidebar now sticks under the navigation bar at the window's height;
otherwise, on a long file list, the link would sit at the bottom of the
page. The music bar, pinned to the bottom of the window as well,
publishes its height through useStickyBand as --music-bar-h and the
sidebar stops above it. On a phone the slide-out panel does the same.
test_sidebar_legal_measured.py measures the real stylesheet: the link at
the bottom of the window on a 3000px page, and above the music bar, at
phone and desktop widths. Checked signed in on a local hub in Chrome and
Firefox 155.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D9MCBBWSm9GhBESmqzJxNy
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static')
13 files changed, 47 insertions, 3 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js index dfd0aeb..ba24822 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js @@ -474,10 +474,20 @@ function Sidebar({ groups, presence, indexProgressPct, route, menuOpen, role, ha }) } </div> + <a class="sidebar-legal" href="${legalUrl()}" target="_blank" rel="noopener"> + ${t('sidebar.legal')}</a> </aside> `; } +// The legal pages of the hub in use: the page's own origin in a browser, the +// configured hub in the application. A new tab either way: the application +// refuses to navigate away from its interface and hands a new window to the +// system browser instead, and in a browser it keeps the session on screen. +function legalUrl() { + return `${platform.hubBase().replace(/\/$/, '')}/legal/`; +} + // ── Home Page ──────────────────────────────────────────────────────────────── function NotificationFeed({ notifications, onMarkRead, onPurge }) { diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js index 116bbe9..7eed742 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -23,6 +23,7 @@ export default { // Sidebar 'sidebar.my_groups': 'Meine Gruppen', 'sidebar.no_groups': 'Noch keine Gruppen', + 'sidebar.legal': 'Rechtliche Hinweise', 'sidebar.discover': 'Entdecken', 'sidebar.public_groups': 'Öffentliche Gruppen', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js index 80dd819..143f462 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -26,6 +26,7 @@ export default { // Sidebar 'sidebar.my_groups': 'My Groups', 'sidebar.no_groups': 'No groups yet', + 'sidebar.legal': 'Legal information', 'sidebar.discover': 'Discover', 'sidebar.public_groups': 'Public groups', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js index 9f8b60e..f35d05d 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -22,6 +22,7 @@ export default { // Sidebar 'sidebar.my_groups': 'Mis grupos', 'sidebar.no_groups': 'Aún no hay grupos', + 'sidebar.legal': 'Información legal', 'sidebar.discover': 'Descubrir', 'sidebar.public_groups': 'Grupos públicos', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index d928f52..e8ac577 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -22,6 +22,7 @@ export default { // Sidebar 'sidebar.my_groups': 'Mes groupes', 'sidebar.no_groups': 'Aucun groupe pour le moment', + 'sidebar.legal': 'Informations légales', 'sidebar.discover': 'Découvrir', 'sidebar.public_groups': 'Groupes publics', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js index 58e2389..76caa51 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -23,6 +23,7 @@ export default { // Sidebar 'sidebar.my_groups': 'I miei gruppi', 'sidebar.no_groups': 'Ancora nessun gruppo', + 'sidebar.legal': 'Note legali', 'sidebar.discover': 'Esplora', 'sidebar.public_groups': 'Gruppi pubblici', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js index 234b238..a6c39ee 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -23,6 +23,7 @@ export default { // Sidebar 'sidebar.my_groups': 'マイグループ', 'sidebar.no_groups': 'グループはまだありません', + 'sidebar.legal': '法的情報', 'sidebar.discover': '見つける', 'sidebar.public_groups': '公開グループ', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js index 72511f5..e4a7914 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -23,6 +23,7 @@ export default { // Sidebar 'sidebar.my_groups': 'Mijn groepen', 'sidebar.no_groups': 'Nog geen groepen', + 'sidebar.legal': 'Juridische informatie', 'sidebar.discover': 'Ontdekken', 'sidebar.public_groups': 'Openbare groepen', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js index 12a31aa..d13e3ef 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -26,6 +26,7 @@ export default { // Sidebar 'sidebar.my_groups': 'Moje grupy', 'sidebar.no_groups': 'Nie ma jeszcze żadnych grup', + 'sidebar.legal': 'Informacje prawne', 'sidebar.discover': 'Odkrywaj', 'sidebar.public_groups': 'Grupy publiczne', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js index c5f826b..1b517bb 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js @@ -24,6 +24,7 @@ export default { // Sidebar 'sidebar.my_groups': 'Meus grupos', 'sidebar.no_groups': 'Nenhum grupo ainda', + 'sidebar.legal': 'Informações legais', 'sidebar.discover': 'Descobrir', 'sidebar.public_groups': 'Grupos públicos', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js index e06c5c6..9922a2d 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js @@ -23,6 +23,7 @@ export default { // Sidebar 'sidebar.my_groups': '我的群组', 'sidebar.no_groups': '暂无群组', + 'sidebar.legal': '法律信息', 'sidebar.discover': '发现', 'sidebar.public_groups': '公开群组', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/music-player.js b/packages/meshbay-hub/src/meshbay_hub/static/music-player.js index 2d3c002..a4a8c5c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-player.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-player.js @@ -3,6 +3,7 @@ import { } from './vendor/htm-preact.js'; import { t } from './i18n.js'; import { Icon } from './icon.js'; +import { useStickyBand } from './sticky.js'; import { CHUNK_SIZE, pipelinedDownload } from './file-utils.js'; /** @@ -179,6 +180,9 @@ function QueuePanel({ tracks, order, pos, onSelect, onClose }) { } function MusicPlayerBar({ getConnection, queue, onClose, userPrefs }) { + // Pinned to the bottom of the window, over the bottom of the sidebar; the + // sidebar subtracts this so its last entry is not underneath. + const barBand = useStickyBand('--music-bar-h'); const audioRef = useRef(null); const blobCacheRef = useRef(new Map()); // file id -> { url, order: insertion index } const blobInsertRef = useRef(0); @@ -480,7 +484,7 @@ function MusicPlayerBar({ getConnection, queue, onClose, userPrefs }) { : repeat === 'all' ? t('music.player_repeat_all') : t('music.player_repeat_one'); return html` - <div class="music-player-bar"> + <div class="music-player-bar" ref=${barBand}> <audio ref=${audioRef} onTimeUpdate=${(e) => setCurrentTime(e.target.currentTime)} onDurationChange=${(e) => setDuration(e.target.duration)} diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index ff6fe57..2b35088 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -71,6 +71,8 @@ --nav-h: 52px; --chrome-h: 0px; --toolbar-h: 0px; + /* Published by the music bar while it is on screen (sticky.js). */ + --music-bar-h: 0px; } /* ── Reset ────────────────────────────────────────────────────────────────── */ @@ -387,15 +389,33 @@ a:hover { text-decoration: underline; } /* ── Sidebar ──────────────────────────────────────────────────────────────── */ +/* Pinned under the navigation bar at the window's height, so the legal link at + its foot is at the bottom of the window whatever the page's length — on a + long file list it would otherwise be at the bottom of the page. It stops + above the music bar, which is pinned to the bottom of the window too. */ .sidebar { + position: sticky; + top: var(--nav-h); + align-self: flex-start; + display: flex; + flex-direction: column; width: 240px; + height: calc(100vh - var(--nav-h) - var(--music-bar-h)); background: var(--sidebar-bg); border-right: 1px solid var(--border); - padding: 16px 0; + padding: 16px 0 0; flex-shrink: 0; overflow-y: auto; } +.sidebar-legal { + margin-top: auto; + padding: 10px 20px 12px; + color: var(--text-dim); + font-size: 0.75em; +} +.sidebar-legal:hover { color: var(--accent); } + .sidebar-section { padding: 0 12px; margin-bottom: 24px; } .sidebar-heading { @@ -2465,7 +2485,7 @@ a.transfer-name { position: fixed; left: -240px; top: 52px; - height: calc(100vh - 52px); + height: calc(100vh - 52px - var(--music-bar-h)); z-index: 50; transition: left 0.2s; box-shadow: none; |