Repository navigation
Conversation
Lists longevity-tools.com interpreters, calculators, bio-age tools and biomarker pages under Analysis tools and opens them with the profile's latest stored values in the URL hash. Exact marker-key mapping, SI values, per-tool input coverage, explicit sharing notice.
[High impact] The new screen can send selected health and profile values to an external site. Fix the height conversion and blood-pressure selection before merging. FindingsSummaryAdds a Longevity Tools screen that opens selected third-party calculators with saved health values.
Acknowledged direction: Diagram%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Sidebar selection] --> B[Load screen and stylesheet]
B --> C[Read active profile values]
C --> D[Show available and missing inputs]
D --> E[User selects a tool]
E --> F[Build URL with selected inputs]
F --> G[Open longevity-tools.com]
Reviews (1) · Last reviewed commit: "Add optional Longevity Tools screen (dee..." · Reviewed by Greptile |
| const { height, unit } = getProfileHeight(state.currentProfile); | ||
| const n = Number(height); | ||
| if (!Number.isFinite(n) || n <= 0) return null; | ||
| return /^in/i.test(String(unit)) ? n * 2.54 : n; |
There was a problem hiding this comment.
For profiles that display inches, getProfileHeight() still returns the saved height in centimetres; unit is only a display preference. Someone who entered 70 inches already has 177.8 cm saved, but profileHeightCm() turns that into 451.6 cm. Height-based calculators then get wrong inputs or reject them. Return the saved centimetre value without multiplying it.
| const { height, unit } = getProfileHeight(state.currentProfile); | |
| const n = Number(height); | |
| if (!Number.isFinite(n) || n <= 0) return null; | |
| return /^in/i.test(String(unit)) ? n * 2.54 : n; | |
| const { height } = getProfileHeight(state.currentProfile); | |
| const n = Number(height); | |
| if (!Number.isFinite(n) || n <= 0) return null; | |
| return n; |
Knowledge Base Used: Body measurement tracking
| if (Number.isFinite(sys) && Number.isFinite(dia) && sys > 0 && dia > 0) candidates.push({ sys, dia, date: String(m?.bp_systolic?.latestDate || m?.bp_diastolic?.latestDate || '') }); | ||
| return candidates.sort((a, b) => b.date.localeCompare(a.date))[0] || null; |
There was a problem hiding this comment.
After a user corrects a migrated blood-pressure reading, latestBloodPressure() can still export the old numbers. Migration retains biometrics.bp, while later edits update the wearable row and summary. Both candidates have the same date, but the legacy copy was inserted first and wins the date-only sort. The risk calculator then receives the uncorrected reading. Prefer the current reading on equal dates and use legacy data only as a fallback.
Summary
Adds an optional Longevity Tools screen under Analysis tools in the sidebar. It lists the interpreters, calculators, biological-age tools and biomarker pages published by longevity-tools.com. Clicking one opens that page in a new window with the profile's latest stored values pre-filled, so the site immediately shows its evaluation (e.g. Levine PhenoAge, Bortz Blood Age, CVD PREVENT, thyroid/liver/iron/kidney interpreters, single-biomarker pages).
A note on direction — please read first
I'm aware this may not be the direction you want for get-based. It sends the user to a third-party service for analyses instead of computing them inside get-based, which runs against the local-first, "everything in one place" spirit of the project. So please treat this PR as a proposal for discussion rather than a finished feature request.
Some options, if the idea is interesting at all:
I'm happy to rework it in whichever direction you prefer, for example by adding the on/off toggle or by reshaping it as a plug-in.
How it works
js/longevity-tools-catalog.ts): 100 entries in 11 sections (11 interpreters, 26 calculators, 2 bio-age tools, 61 biomarker pages). It is taken from the site's publicllms.txtand homepage, and each site parameter ID is mapped to an exact get-based marker key and its stored unit (63 mappings, exact keys only, no fuzzy matching).js/longevity-tools-values.ts): takes the latest stored value per marker fromimportedData.entries. It reads the stored SI values, not the display-converted ones, and keeps the draw date. Age, sex, height, weight, blood pressure and waist come from the profile and body data.#ID=value_unit&…(/→~). The site does its own unit conversion, so values are sent in get-based's stored unit and never pre-converted. The only scale fix is lymphocytes, which are stored as a fraction and sent as %. The legacy Levine PhenoAge page takes bare numbers.js/longevity-tools-view.ts, lazy-loaded viajs/longevity-tools-loader.ts): has search, a Tools/Biomarkers filter and an "only with my data" toggle. Each tool shows which inputs are filled and which are missing, plus the questionnaire items that can only be answered on the site, so the user knows why a calculator may stay blank. Links open throughopenUtilsRuntimeWindow(…, 'noopener,noreferrer').Wiring
longevity-toolswith handlers inviews-router.tsandnavigation-views.ts, a sidebar row innav.ts,sidebar-presentation.ts,SidebarNavigation.svelteandSidebarIcon.svelte, and service-worker assets.js/longevity-tools-*.jsare added to.gitignore.MODULE_MAP.mdandscripts/dom-sink-policy.jsonare regenerated.longevityToolsEntry()intests/helpers/sidebar-lens-entries.ts, inserted after Correlations inexpectedCurrentSidebarPwaReceipt). The grammar SHA-256 digests forSidebarNavigation.svelteandSidebarIcon.svelteare updated.Testing
tests/longevity-tools.test.ts(fictional values) covers: latest value per marker plus draw date, the documented hash format, the fraction→% conversion, profile params, the bare-number Levine page, CVD PREVENT body inputs, and that every biomarker page has a mapped source.tsc --noEmit,svelte-check(0 errors) andquality-guardrails(17/17) all pass. The architecture check passes with 0 cycles. Sidebar suites pass:sidebar-owned,sidebar-presentation,sidebar-pwa-expected-annotations.vitest runcompared against a cleanorigin/mainworktree: the PR introduces no new failures. The failures that remain are the same environment-dependent ones as onmainin my setup.