From 00b1d4469f0408dfeb4d655ca739dc1e6f7f0f36 Mon Sep 17 00:00:00 2001 From: marc Date: Tue, 25 Aug 2026 11:07:43 +0200 Subject: [PATCH] feat(honeybar): the HoneyOS bar, loaded at runtime A strip along the top that leads to the other apps of the same system. It is served by HoneyOS; InkNote only says where to find it, through INKNOTE_HONEYBAR_ORIGIN. Empty means no bar and no change at all. The origin is served by the API rather than built into the page, because an image is pulled ready-made and a service worker keeps whole responses: anything frozen into index.html would outlive the setting. Layout follows the bar through --honeybar-h. The body has overflow:hidden, so the surplus height would not have scrolled but been cut off at the bottom; and #root padded itself around the notch that the bar now covers, which would have counted that gap twice. Unit 263/263, all fourteen browser suites green. Co-Authored-By: Claude Opus 5 --- .env.example | 9 +++++++++ CHANGELOG.md | 26 ++++++++++++++++++++++++ backend/server.js | 20 ++++++++++++++++++ docker-compose.dev.yml | 5 +++++ docker-compose.yml | 5 +++++ frontend/index.html | 15 +++++++++++++- frontend/src/App.jsx | 4 ++-- frontend/src/honeybar.js | 37 ++++++++++++++++++++++++++++++++++ frontend/src/main.jsx | 4 ++++ frontend/src/ui/AuthScreen.jsx | 2 +- frontend/src/ui/Sidebar.jsx | 2 +- readme.md | 20 ++++++++++++++++++ 12 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 frontend/src/honeybar.js diff --git a/.env.example b/.env.example index 374600a..a12c7b3 100644 --- a/.env.example +++ b/.env.example @@ -24,6 +24,15 @@ INKNOTE_ADMIN_PASSWORD= # ── Optional ── unset keeps the default shown # INKNOTE_MAX_IMAGE_MB=25 # pasted images + +# ── HoneyOS bar ─────────────────────────────────────────────────────────────── +# The strip along the top that ties the apps together. Empty = no bar. +# Wirkt beim Neustart, nicht beim Bauen. +# INKNOTE_HONEYBAR_ORIGIN=https://honeyos.example.com +# Only needed when HoneyOS lives on a different site than InkNote — then the +# browser will not send its session and the bar needs this to fetch the app +# list. Same domain (notes.example.org + example.org): leave empty. +# INKNOTE_HONEYBAR_KEY= # INKNOTE_MAX_PDF_MB=200 # imported PDFs, limited separately # INKNOTE_UID=1000 # must own INKNOTE_DATA_DIR # INKNOTE_GID=1000 diff --git a/CHANGELOG.md b/CHANGELOG.md index d7c5baa..257da0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,32 @@ a rebuild is not bit-identical. --- +## 2026-08-25 — The bar that ties the apps together + +**Images** — web `sha256:7c802a96…`, server `sha256:a156fb92…` + +**Added** +- **A strip along the top that leads to the other apps.** It is served by + HoneyOS, so what it looks like and which apps it lists are settled there — + InkNote only says where to find it. `INKNOTE_HONEYBAR_ORIGIN` in the `.env` + switches it on, empty leaves everything exactly as before. +- **`GET /api/public/config`** hands out that origin, and the key that goes with + it when HoneyOS lives on a different site. Runtime, not build time: an image + is pulled ready-made, so a value baked in at build time could never be changed + from the `.env` — and a service worker keeps whole responses, headers + included, so anything frozen into `index.html` outlives the setting in the + browser. Aether learned that one the hard way first. + +**Changed** +- **The app now measures its height against the bar**, in `#root` and in the + four places that asked for the full window. With `overflow: hidden` on the + body the surplus would not have scrolled — it would have been cut off at the + bottom, silently. And `#root` padded itself around the phone's notch, which + the bar now covers, so that gap would have been counted twice. Without a bar + `--honeybar-h` is `0px` and every one of these is what it was. + +--- + ## 2026-08-22 — A bigger face, and the theme where the settings are **Images** — web `sha256:fe35b0e7…`, server `sha256:a557fb4d…` diff --git a/backend/server.js b/backend/server.js index 914d8cd..600d926 100644 --- a/backend/server.js +++ b/backend/server.js @@ -878,6 +878,26 @@ app.get("/api/auth/methods", (req, res) => { }); }); +/* Where the HoneyOS bar lives, if it is wanted at all. + * + * Served at runtime rather than baked into the build for two reasons. The image + * is pulled ready-made, so a value fixed at build time could never be changed + * from the .env afterwards. And a service worker caches whole responses, + * headers included — anything frozen into index.html stays frozen in the + * browser long after the server has moved on. + * + * The key is only handed out together with an origin: without a bar it has + * nothing to unlock, and it should not sit in the page for no reason. It buys + * the list of apps and nothing else. */ +app.get("/api/public/config", (req, res) => { + const origin = String(process.env.INKNOTE_HONEYBAR_ORIGIN || "").replace(/\/+$/, ""); + res.json({ + honeybar: origin + ? { origin, key: String(process.env.INKNOTE_HONEYBAR_KEY || "") } + : null, + }); +}); + /* The browser is sent to the provider from here rather than from the app, so * the app never has to know the issuer, the scopes or the redirect. */ app.get("/api/auth/oidc/start", async (req, res) => { diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 96c0ff8..a5370bf 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -32,6 +32,11 @@ services: # Optional tuning; unset means the server's own defaults apply. - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-} - INKNOTE_MAX_IMAGE_MB=${INKNOTE_MAX_IMAGE_MB:-} + # HoneyOS bar. Empty origin means no bar at all; the key is only needed + # when HoneyOS sits on a different site, where the browser withholds its + # session. Takes effect on restart — nothing here is baked into the image. + - INKNOTE_HONEYBAR_ORIGIN=${INKNOTE_HONEYBAR_ORIGIN:-} + - INKNOTE_HONEYBAR_KEY=${INKNOTE_HONEYBAR_KEY:-} - INKNOTE_MAX_PDF_MB=${INKNOTE_MAX_PDF_MB:-} - MAX_VERSIONS_PER_NOTE=${MAX_VERSIONS_PER_NOTE:-} - VERSION_RETENTION_DAYS=${VERSION_RETENTION_DAYS:-} diff --git a/docker-compose.yml b/docker-compose.yml index 5e9c18b..e49b0d8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,6 +30,11 @@ services: # Optional tuning; unset means the server's own defaults apply. - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-} - INKNOTE_MAX_IMAGE_MB=${INKNOTE_MAX_IMAGE_MB:-} + # HoneyOS bar. Empty origin means no bar at all; the key is only needed + # when HoneyOS sits on a different site, where the browser withholds its + # session. Takes effect on restart — nothing here is baked into the image. + - INKNOTE_HONEYBAR_ORIGIN=${INKNOTE_HONEYBAR_ORIGIN:-} + - INKNOTE_HONEYBAR_KEY=${INKNOTE_HONEYBAR_KEY:-} - INKNOTE_MAX_PDF_MB=${INKNOTE_MAX_PDF_MB:-} - MAX_VERSIONS_PER_NOTE=${MAX_VERSIONS_PER_NOTE:-} - VERSION_RETENTION_DAYS=${VERSION_RETENTION_DAYS:-} diff --git a/frontend/index.html b/frontend/index.html index e7d8d1e..f47af73 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -38,7 +38,20 @@