diff --git a/ui/src/app.css b/ui/src/app.css index 5f33900..bc94eda 100644 --- a/ui/src/app.css +++ b/ui/src/app.css @@ -44,6 +44,9 @@ /* Height Telegram's native nav overlays at the top in fullscreen; set from the SDK's content-safe-area inset (Stage 17), 0 elsewhere. */ --tg-content-top: 0px; + /* Telegram device safe-area top (the notch); TG's own nav controls sit between it and + --tg-content-top, so the in-app header aligns to that band (Stage 17), 0 elsewhere. */ + --tg-safe-top: 0px; --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif; --shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 6px 16px rgba(0, 0, 0, 0.06); diff --git a/ui/src/components/Header.svelte b/ui/src/components/Header.svelte index 34b6529..140bbd5 100644 --- a/ui/src/components/Header.svelte +++ b/ui/src/components/Header.svelte @@ -89,17 +89,18 @@ transform: rotate(45deg); margin-left: 3px; } - /* Telegram fullscreen: TG's native nav overlays a band of height --tg-content-top at the top - of the viewport. Pull our title + menu up into the BOTTOM of that band and centre them as a - pair (hamburger right of the title) so they line up with Telegram's own nav controls rather - than floating above them (Stage 17). */ + /* Telegram fullscreen: TG's native nav occupies the band between the device notch + (--tg-safe-top) and --tg-content-top. Our header spans that full band (so the layout below + is unchanged) and centres the title + menu as a pair (hamburger right of the title) within + it, BELOW the notch — lining them up vertically with Telegram's own back/menu controls, + which sit in the band's corners (Stage 17). */ :global(html.tg-fullscreen) .bar { min-height: var(--tg-content-top); box-sizing: border-box; - align-items: flex-end; + align-items: center; justify-content: center; - padding-top: 0; - padding-bottom: 6px; + padding-top: var(--tg-safe-top); + padding-bottom: 0; } :global(html.tg-fullscreen) .spacer { display: none; diff --git a/ui/src/components/Screen.svelte b/ui/src/components/Screen.svelte index 29f7d0c..6ddf877 100644 --- a/ui/src/components/Screen.svelte +++ b/ui/src/components/Screen.svelte @@ -3,7 +3,6 @@ import Header from './Header.svelte'; import AdBanner from './AdBanner.svelte'; import { navigate } from '../lib/router.svelte'; - import { insideTelegram } from '../lib/telegram'; // The app-shell layout (all screens): the nav bar grows; the ad strip, content and // optional tab bar pin to the bottom (ad directly above the content). Pass `scroll` @@ -37,25 +36,28 @@ const SHOW_AD_BANNER = false; // Edge-swipe back (Stage 17): a left-edge rightward drag returns to `back`, the standard - // mobile gesture. Armed only from the very left edge (<=24px) so it never competes with the - // board's own horizontal gestures; touch/pen only. Skipped inside Telegram, whose native - // back button + swipe already cover this (and would otherwise double up). - function onEdgeDown(e: PointerEvent): void { - if (!back || e.pointerType === 'mouse' || insideTelegram() || e.clientX > 24) return; - const x0 = e.clientX; - const y0 = e.clientY; - const onUp = (ev: PointerEvent) => { - window.removeEventListener('pointerup', onUp); - const dx = ev.clientX - x0; - const dy = ev.clientY - y0; - if (back && dx > 64 && Math.abs(dx) > Math.abs(dy) * 1.4) navigate(back); - }; - window.addEventListener('pointerup', onUp); - } + // mobile gesture. Listened at the window in the CAPTURE phase so the board's own pointer + // handlers (which capture/stop the event) can never swallow it; armed only from the very + // left edge (<=24px), touch/pen only, so it never competes with the board's gestures. + $effect(() => { + function onDown(e: PointerEvent) { + if (!back || e.pointerType === 'mouse' || e.clientX > 24) return; + const x0 = e.clientX; + const y0 = e.clientY; + const onUp = (ev: PointerEvent) => { + window.removeEventListener('pointerup', onUp, true); + const dx = ev.clientX - x0; + const dy = ev.clientY - y0; + if (back && dx > 64 && Math.abs(dx) > Math.abs(dy) * 1.4) navigate(back); + }; + window.addEventListener('pointerup', onUp, true); + } + window.addEventListener('pointerdown', onDown, true); + return () => window.removeEventListener('pointerdown', onDown, true); + }); - -