From 2af845bfc6dd01fd56388b445e4e5a148a1735b9 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Sat, 2 May 2026 15:56:21 +0200 Subject: [PATCH] feat(offline): redesign sync UI and PWA polish Split the single OfflineSyncButton into two surfaces with distinct intents: - OfflineSyncBanner: dismissable promo on the recipe index that encourages first-time download (only when standalone + not yet synced). - OfflineSyncIndicator: small status pip overlaid on the nav logo when offline data is available, opening a popover with sync / clear actions. Also fold the sync / clear actions into the UserHeader options menu so the avatar dropdown is the canonical place to manage offline data. Header.svelte gains a `logo_overlay` snippet slot to host the indicator pip. Other: - manifest.json: prefer the theme-aware SVG as the primary install icon and drop the redundant 512px raster (kept maskable 192px). - scripts/deploy.sh: build locally and rsync artifacts to the server, avoiding any pnpm/git work on the production host. Bump 1.57.8 -> 1.58.0. --- package.json | 2 +- scripts/deploy.sh | 86 +++++ src/lib/components/Header.svelte | 13 +- src/lib/components/OfflineSyncBanner.svelte | 245 ++++++++++++++ src/lib/components/OfflineSyncButton.svelte | 257 --------------- .../components/OfflineSyncIndicator.svelte | 300 ++++++++++++++++++ src/lib/components/UserHeader.svelte | 115 ++++++- src/lib/i18n/common/de.ts | 5 + src/lib/i18n/common/en.ts | 5 + .../[recipeLang=recipeLang]/+layout.svelte | 19 +- .../[recipeLang=recipeLang]/+page.svelte | 20 ++ static/manifest.json | 12 +- 12 files changed, 796 insertions(+), 283 deletions(-) create mode 100755 scripts/deploy.sh create mode 100644 src/lib/components/OfflineSyncBanner.svelte delete mode 100644 src/lib/components/OfflineSyncButton.svelte create mode 100644 src/lib/components/OfflineSyncIndicator.svelte diff --git a/package.json b/package.json index ee9e13d5..9253644b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homepage", - "version": "1.57.8", + "version": "1.58.0", "private": true, "type": "module", "scripts": { diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 00000000..bbf0c9b4 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# Build locally and rsync artifacts to the production server. +# Avoids running pnpm / npm / any git-hosted prepare step on the server. +# +# Assumes: +# - Local machine matches the server's arch + libc (linux-x64-glibc). +# - Local Node major version matches the server's. +# - Root SSH to $REMOTE works (key-based). +# +# Usage: scripts/deploy.sh [--dry-run] + +set -euo pipefail + +REMOTE="${REMOTE:-root@bocken.org}" +REMOTE_DIR="${REMOTE_DIR:-/usr/share/webapps/homepage}" +REMOTE_USER_GROUP="${REMOTE_USER_GROUP:-homepage:homepage}" +SERVICE="${SERVICE:-homepage.service}" + +DRY="" +if [[ "${1:-}" == "--dry-run" ]]; then + DRY="--dry-run" + echo ":: DRY RUN — no files will be transferred" +fi + +cd "$(dirname "$0")/.." + +echo ":: Sanity-checking local/remote toolchain parity" +local_node=$(node --version) +remote_node=$(ssh "$REMOTE" 'node --version') +if [[ "${local_node%%.*}" != "${remote_node%%.*}" ]]; then + echo "!! Node major mismatch: local $local_node vs remote $remote_node" + echo " Native modules (sharp, onnxruntime, bson) may break. Aborting." + exit 1 +fi +echo " node $local_node (match)" + +echo ":: Installing deps (frozen lockfile)" +pnpm install --frozen-lockfile + +echo ":: Building" +pnpm build + +if [[ ! -d build ]]; then + echo "!! build/ not produced — aborting" + exit 1 +fi + +# The server's systemd unit runs from $REMOTE_DIR/dist, so map build → dist. +echo ":: Syncing build/ → $REMOTE:$REMOTE_DIR/dist/" +rsync -az --delete $DRY --info=progress2 \ + build/ "$REMOTE:$REMOTE_DIR/dist/" + +echo ":: Syncing node_modules/ → $REMOTE:$REMOTE_DIR/node_modules/" +rsync -az --delete $DRY --info=progress2 \ + node_modules/ "$REMOTE:$REMOTE_DIR/node_modules/" + +echo ":: Syncing static/ → $REMOTE:$REMOTE_DIR/static/" +rsync -az --delete $DRY --info=progress2 \ + static/ "$REMOTE:$REMOTE_DIR/static/" + +echo ":: Syncing package.json + pnpm-lock.yaml" +rsync -az $DRY \ + package.json pnpm-lock.yaml "$REMOTE:$REMOTE_DIR/" + +if [[ -n "$DRY" ]]; then + echo ":: Dry run complete — no service restart" + exit 0 +fi + +echo ":: Fixing ownership on server" +ssh "$REMOTE" "chown -R $REMOTE_USER_GROUP $REMOTE_DIR/dist $REMOTE_DIR/node_modules $REMOTE_DIR/static $REMOTE_DIR/package.json $REMOTE_DIR/pnpm-lock.yaml" + +echo ":: Restarting $SERVICE" +ssh "$REMOTE" "systemctl restart $SERVICE" + +echo ":: Verifying service is active" +sleep 2 +if ssh "$REMOTE" "systemctl is-active --quiet $SERVICE"; then + echo " $SERVICE is running" +else + echo "!! $SERVICE failed to start — check logs:" + ssh "$REMOTE" "journalctl -u $SERVICE -n 30 --no-pager" + exit 1 +fi + +echo ":: Deploy complete" diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte index 8f5724d7..1dc6ca47 100644 --- a/src/lib/components/Header.svelte +++ b/src/lib/components/Header.svelte @@ -12,6 +12,7 @@ let { language_selector_mobile, language_selector_desktop, right_side, + logo_overlay, children, fullSymbol = false }: { @@ -19,6 +20,7 @@ let { language_selector_mobile?: Snippet; language_selector_desktop?: Snippet; right_side?: Snippet; + logo_overlay?: Snippet; children?: Snippet; fullSymbol?: boolean; } = $props(); @@ -118,6 +120,12 @@ nav { /* ═══════════════════════════════════════════ LOGO ═══════════════════════════════════════════ */ +.logo-slot { + position: relative; + display: inline-flex; + align-items: center; + flex-shrink: 0; +} .home-link { view-transition-name: nav-logo; display: flex; @@ -330,7 +338,10 @@ nav {