Commit Graph
300 Commits
Author SHA1 Message Date
Marc-André Bombeck a53c84bc3f chore(release): v1.28.9 — document chat in the coach dialog
Chatting about a document opens the coach chat dialog scoped to that document,
entered from a neutral icon in the document's top-right (matching insights),
instead of a separate inline panel. The document chat's stricter footing is
unchanged — one document only, no health-record context, no tools, plain text.
2026-07-09 18:05:28 +02:00
Marc-André Bombeck 0c72a5c8c6 chore(release): v1.28.8 — documents automatic when set, cleaner when not
When automatic AI reading is on, the per-document AI action buttons no longer
appear (reading happens on upload); they return when it is off. The
AI-suggestion review drops its heading and grey sub-caption.
2026-07-09 17:32:36 +02:00
Marc-André Bombeck a89e511359 fix(documents): run pdfjs + canvas un-bundled so scanned PDFs actually rasterize
The bundled (Turbopack) copy of pdfjs-dist failed its render path in the
standalone image with a bare Error, so a scanned PDF on a non-Anthropic
provider fell back to 'PDF scanning needs a Claude vision provider' — while the
identical un-bundled module rasterizes the same document fine (verified in the
container end to end on the real document). Externalize pdfjs-dist and
@napi-rs/canvas so the server loads the real modules, and hoist pdfjs-dist to
the top level in the image so the runtime import resolves. Also log the render
error message (not just its name) and drop the redundant 'not saved' note from
the document summary panel.
2026-07-09 16:22:00 +02:00
Marc-André Bombeck 0e7514f7b0 fix(documents): hoist @napi-rs/canvas so pdfjs can load it in the standalone image
PDF rasterization failed at runtime — pdfjs-dist does a bare
require('@napi-rs/canvas') from its own directory, and the standalone image had
no top-level @napi-rs/canvas for Node's upward resolution to find, so pdfjs
could not polyfill DOMMatrix and every scanned-PDF read fell back to
'PDF scanning needs a Claude vision provider'. Hoist the canvas package and its
musl binary to /app/node_modules/@napi-rs via symlink into the pnpm store (the
prior per-package .node copy only fixed canvas's own loader, not pdfjs's
require). Verified in the container: resolves from pdfjs and renders.
2026-07-09 14:40:12 +02:00
Marc-André Bombeck d5f279a248 chore(release): v1.28.5 — read documents with a subscription AI, cleaner surfaces
Document reading no longer treats a signed-in subscription model as unable to
see images, so 'read with AI' works on that path instead of erroring; scanned
PDFs and photos are read the same way. Drops two now-redundant blurbs from the
document panel and, on the recovery insights page, moves each metric's
explanation into its own card instead of repeating the heading above it.
2026-07-09 13:47:20 +02:00
Marc-André Bombeck 783255d833 chore(release): v1.28.4 — medication efficacy
Per-medication effect view: plots the target metric/lab with start, dose
changes, and pauses marked, a before/after-start comparison, and adherence
overlaid — descriptive only, never a verdict or dose advice. Compact summary on
the medication insights area. Target resolved from ATC class or name, with a
per-user override. Migration 0232.
2026-07-09 13:14:07 +02:00
Marc-André Bombeck f1c468f90f chore(release): v1.28.3 — consistent spacing and alignment
A layout-audit-driven consistency pass: one shared left edge for insight cards
(headings and body align, worst on mobile), no scroll past content on settings
and admin pages, Settings/Notifications only in the account menu on mobile, the
dashboard action beside its greeting text, and preventive-care/cycle/onboarding
cards on the shared card anatomy. The spacing and alignment rules are recorded
in the standards. Also stages the document rasterization native binding in the
image so a scanned PDF is read on an image-only AI provider.
2026-07-09 12:17:54 +02:00
Marc-André Bombeck 1a43880ab4 chore(release): v1.28.2 — automatic AI document reading (opt-in)
One opt-in in AI settings reads every uploaded document with the configured
provider — no per-document tap. Off by default; the vault stays local-first
otherwise. Scanned PDFs and photos are read too (a PDF's pages are rendered to
images so an image-only provider can read them). The background indexer now
follows the same opt-in, so nothing leaves the machine automatically unless it
is enabled.
2026-07-09 11:17:07 +02:00
Marc-André Bombeck 951db4947e fix(documents): load the local PDF parser lazily so the vault routes stop crashing
The local content-extraction reader imported pdf-parse at module top level.
pdf-parse pulls in pdfjs, whose module evaluation references the browser-only
DOMMatrix global; on the server that reference throws during chunk
instantiation and takes down every route sharing the chunk — the document
list, upload, and search among them, so old and new documents alike fail to
load. Move the parser to a lazy import() inside the extraction function
(surface type kept as a type-only import). Verified under plain Node that the
lazy path still extracts a real PDF text layer, so local indexing is
unaffected.
2026-07-09 09:29:35 +02:00
Marc-André Bombeck b143576627 chore(release): v1.28.0 — document intelligence
Marks the document line as one coherent milestone: the vault becomes a
place to understand files, not just store them — read, indexed, searched,
shared, and answered — with a local-first / consented AI posture and a
vendor-blind egress notice held at every step. Ships alongside a full
correctness, resilience, and quality sweep down to the low-priority findings.
2026-07-09 08:42:23 +02:00
Marc-André Bombeck 6577bd287e chore(release): v1.27.33 — chat about a document 2026-07-09 04:08:38 +02:00
Marc-André Bombeck 30b745c62f chore(release): v1.27.32 — quality-of-life, accessibility, cold-start and performance polish 2026-07-09 02:14:31 +02:00
Marc-André Bombeck e1a8da5dfb chore(release): v1.27.31 — document AI provider governance 2026-07-09 01:32:14 +02:00
Marc-André Bombeck 6165e7f6b8 chore(release): v1.27.30 — correctness and resilience sweep 2026-07-09 01:05:47 +02:00
Marc-André Bombeck 76277ca31d chore(release): v1.27.29 — automatic document indexing and AI reading 2026-07-08 22:16:12 +02:00
Marc-André Bombeck 3829b80aca chore(release): v1.27.28 — share a document from the document 2026-07-08 20:38:27 +02:00
Marc-André Bombeck 99f3824794 chore(release): v1.27.27 — reorderable hero rings 2026-07-08 09:40:58 +02:00
Marc-André Bombeck 53d8d30bd2 chore(release): v1.27.26 — hero and wellbeing card polish 2026-07-08 00:46:34 +02:00
Marc-André Bombeck 5238fa9923 chore(release): v1.27.25 — documents filter dropdowns 2026-07-08 00:23:23 +02:00
Marc-André Bombeck 97163d1e24 chore(release): v1.27.24 — share documents 2026-07-07 23:08:19 +02:00
Marc-André Bombeck d2048fa171 chore(release): v1.27.23 — dashboard hero polish 2026-07-07 21:59:48 +02:00
Marc-André Bombeck c0676564e6 chore(release): v1.27.22 — document assist and content search 2026-07-07 20:16:08 +02:00
Marc-André Bombeck 4aa3342ba2 chore(release): v1.27.21 — hero spacing and one green 2026-07-07 17:35:50 +02:00
Marc-André Bombeck d560bc4575 chore(release): v1.27.20 — coach and cycle toggle from the modules hub 2026-07-07 17:11:51 +02:00
Marc-André Bombeck a2c6eeeb77 chore(release): v1.27.19 — vault page polish 2026-07-07 16:47:08 +02:00
Marc-André Bombeck 20e052b207 chore(release): v1.27.18 — late doses no longer jump the evening slot 2026-07-07 16:01:24 +02:00
Marc-André Bombeck 1ae5bd8151 chore(release): v1.27.17 — the document vault 2026-07-07 15:19:58 +02:00
Marc-André Bombeck 4dbeafe725 chore(release): v1.27.16 — offline that survives, and a faster first paint 2026-07-07 13:31:12 +02:00
Marc-André Bombeck e24db8a889 perf(i18n): stop shipping two full catalogs on every page load
Two independent every-load costs, one per layer:

1. The static EN import in load-locale.ts put the full en.json catalog
   (334 KB raw / ~106 KB gz) into the client JS of EVERY route, for
   every user in every language. No catalog is statically imported by
   client code anymore: the EN fallback floor of the t() chain loads
   lazily on the first actual key miss (the locale-integrity guard
   keeps every locale key-complete, so the miss path is drift-only),
   and the labs biomarker canonical-name lookup fetches EN on demand.

2. The root layout serialized the ENTIRE active-locale catalog into the
   RSC flight payload of every document (392 KB of a 505 KB dashboard
   HTML, re-downloaded on every hard load, uncacheable). The handoff is
   now a versioned, immutable-cacheable boot script
   (/i18n/<locale>?v=<build>, new route handler) that assigns
   self.__HL_I18N before the hydration scripts run, plus a server-side
   cache seed in load-locale.ts behind a typeof-window branch that
   Turbopack compiles out of the browser bundle (verified: zero catalog
   bytes in any client chunk, zero route manifests referencing one).
   SSR text stays correct and the first client render holds the same
   bundle the server rendered with — no EN flash, no hydration drift.

The boot script is public (proxy allowlist: the login page needs it
pre-auth; the body is the same catalog JSON that ships in the public
repo) and cache-first in the service worker, so an offline relaunch
finds it next to the cached shell.

Measured (client-reference manifests, gzip): route JS baseline
/ 391->287 KB gz, /insights 377->273, /measurements 379->275,
/insights/mood 394->290 — the ~104 KB gz EN catalog left every route.

H2
2026-07-07 12:34:26 +02:00
Marc-André Bombeck 07976121f6 chore(release): v1.27.15 — complete Google Health coverage 2026-07-07 11:27:54 +02:00
Marc-André Bombeck 456a547ada chore(release): v1.27.14 — cross-device dose sync and phone-width polish 2026-07-07 10:41:52 +02:00
Marc-André Bombeck c7b4ccf24a chore(release): v1.27.13 — assessments that interpret 2026-07-07 00:41:30 +02:00
Marc-André Bombeck 8e2fff4e7f chore(release): v1.27.12 — Google Health daily totals 2026-07-06 20:29:17 +02:00
Marc-André Bombeck c49734842b chore(release): v1.27.11 — consistency, colour, and locale fixes 2026-07-05 22:28:39 +02:00
Marc-André Bombeck 0103557566 chore(release): v1.27.10 — two new check-ins: wellbeing and sleep 2026-07-05 20:45:25 +02:00
Marc-André Bombeck 4b3336d1cc chore(release): v1.27.9 — coach plans you can accept 2026-07-05 20:10:57 +02:00
Marc-André Bombeck d7cd29ad56 chore(release): v1.27.8 — dashboard follow-ups and design-system sweeps 2026-07-05 18:50:23 +02:00
Marc-André Bombeck 1d37d12f7a chore(release): v1.27.7 — score rings on the dashboard 2026-07-05 16:06:09 +02:00
Marc-André Bombeck 08d0d4c4cb chore(release): v1.27.6 — a calmer mental-wellbeing check-in 2026-07-05 15:33:42 +02:00
Marc-André Bombeck 5acf922dc8 chore(release): v1.27.5 — insights polish and honest states 2026-07-05 15:15:26 +02:00
Marc-André Bombeck 13ddd371a9 chore(release): v1.27.4 — google health sync fixed against the live api 2026-07-05 14:08:34 +02:00
Marc-André Bombeck c2bffe9c60 chore(release): v1.27.0 — Fitbit and Pixel Watch through Google Health
Add a Google Health connection that reads Fitbit, Pixel Watch, and Fitbit
Air data through the user's Google account, alongside the existing Fitbit
connection, as the path forward before the classic Fitbit Web API sunsets
in September 2026.
2026-07-03 14:43:07 +02:00
Marc-André Bombeck 5d5ec681ee chore(release): v1.25.13 — briefing and medication fixes
Restore the daily briefing when it cites your own vitals (the number-grounding
gate stripped it on any blood-pressure, pulse, weight, mood or sleep figure it
did not recognise), surface a medication's dose as a tag beside its category,
let the custom on-time window switch stay on, and drop the layout-shifting
back-link from the Appearance sub-pages.
2026-07-01 19:29:26 +02:00
Marc-André Bombeck 2d7b23e847 chore(release): v1.25.12 — follow-up fixes 2026-06-30 23:48:12 +02:00
Marc-André Bombeck e4935ce788 chore(release): v1.25.11 — consistent settings and cards 2026-06-30 21:42:46 +02:00
Marc-André Bombeck 9bae4dd2f6 fix(ui): stack the date-time field on narrow viewports
A native date input keeps its intrinsic width on mobile WebKit and won't
shrink, so the `date + fixed-width time` row overran a phone-width sheet —
the time field and its clock icon spilled past the screen edge and every
add form (measurements, mood, medication, …) scrolled sideways. Stack the
two halves on narrow viewports and only place them side-by-side from the
`sm` breakpoint up; each half then takes the full sheet width and nothing
overflows.
2026-06-30 10:33:16 +02:00
Marc-André Bombeck c306d42347 chore(release): v1.25.9 — custom metrics
Renumber the custom-metrics migration to 0223 (the next free slot on the
trunk) and cut the release: version bump, regenerated OpenAPI contract,
service-worker stamp, and CHANGELOG entry.
2026-06-30 08:59:44 +02:00
Marc-André Bombeck bdac5b2c0d feat(admin): resolve the sign-in carrier online + give it its own column
The network operator (carrier) used to resolve only from the optional
offline GeoLite2-ASN database. On a host without it the carrier stayed
empty even though the location resolved fine online.

Mine the operator from the same online provider response the location
lookup already fetches — ip-api's top-level `isp`/`org`/`as`, ipwho.is's
nested `connection.{isp,org,asn}` — so the carrier is populated out of the
box. The offline ASN database stays authoritative when configured.

A single `lookupIpGeo` now returns location + ASN + carrier in one pass;
the audit write and the backfill both go through it. The backfill also
revisits rows that already had a location but no carrier, so the column
fills in across existing history, not just for new sign-ins.

In the admin sign-in overview the carrier moves out of a chip stacked
under the login method into its own column, next to the location.
2026-06-29 23:51:40 +02:00
Marc-André Bombeck b6886c50d2 chore(release): v1.25.7 2026-06-29 22:42:50 +02:00
Marc-André Bombeck e51c6dc2ad feat(geo): allow an opt-in plain-HTTP location provider
A self-hoster can set IP_GEO_ALLOW_INSECURE=true alongside an http:// URL to
use a free HTTP-only provider — the free ip-api.com endpoint is HTTP-only (its
HTTPS form needs a paid key) but its geolocation is often more accurate and it
carries the ISP. The default stays HTTPS-only with ipwho.is; plain HTTP is
refused unless the operator explicitly opts in, since the looked-up IP then
travels unencrypted over the server's own egress. Whitelisted in compose and
documented in the env example.
2026-06-29 20:38:04 +02:00