Skip to main content

Module Reference

Modules live under js/. At the time of this docs move, the app tree has 332 top-level JavaScript modules. This page is a guided implementation map, not a promise that every small hook/delegate/helper file has its own long section. Grouped by layer — lower layers have no dependencies on higher ones.

Layer 1 — Foundation

schema.js

The single source of truth for all biomarker definitions. No runtime logic — pure data. Key exports:
  • MARKER_SCHEMA — nested object: { categoryKey: { label, icon, markers: { markerKey: { name, unit, refMin, refMax, refMin_f, refMax_f, desc } } } }. Categories include biochemistry, hormones, electrolytes, lipids, hematology, differential, thyroid, proteins, vitamins, diabetes, inflammation, fattyAcids, calculatedRatios, and others
  • UNIT_CONVERSIONS — keyed by "category.markerKey": { type: 'multiply', factor, unit } for EU→US conversions
  • OPTIMAL_RANGES — keyed by "category.markerKey": { optimalMin, optimalMax, optimalMin_f?, optimalMax_f? }
  • PHASE_RANGES — keyed by "category.markerKey": { menstrual: { min, max }, follicular: {...}, ovulatory: {...}, luteal: {...} } — covers hormones.estradiol, hormones.progesterone, hormones.lh, and hormones.fsh
  • SPECIALTY_MARKER_DEFS — re-exported from adapters.js as ADAPTER_MARKERS. Used by migrateProfileData() and buildMarkerReference()
  • CHIP_COLORS — status → CSS color string
  • MODEL_PRICING — AI model pricing metadata, keyed by provider/model
Window exports: none

adapters.js

Parser adapter registry for specialty lab detection and normalization. Single source of truth for all specialty marker definitions (OAT, fatty acids, Metabolomix+, BioStarks). Key exports:
  • ADAPTER_MARKERS — flat object keyed by "category.markerKey": { name, unit, refMin, refMax, categoryLabel, icon, group, singlePoint? } (217 entries). Re-exported from schema.js as SPECIALTY_MARKER_DEFS
  • getAllAdapterMarkers() — returns merged marker map from all registered adapters
  • detectProduct(fileName, pdfText) — runs all adapter detect() functions, returns { adapter, product: { prefix, label } } or null
  • normalizeWithAdapter(adapter, markers, fileName, pdfText, product) — dispatches to adapter.normalize() for post-AI marker key/category rewriting
  • getAdapterByTestType(testType) — looks up adapter by AI-returned test type string
Adapter registry: array of { id, testTypes[], markers, detect?, normalize? }:
  • fattyAcids — 29 markers, detects Spadia/ZinZino/OmegaQuant by filename/text, normalizes to product-prefixed categories under “Fatty Acids” sidebar group
  • metabolomix — no unique markers (reuses OAT + FA), detects Genova Metabolomix+ reports, routes FA add-on markers to metabolomixFA prefix
  • oat — 165 markers, no detect/normalize (AI handles OAT categorization directly)
  • biostarks — 23 markers, detects BioStarks dried blood spot reports by filename/text, normalizes specialty markers (amino acids, serum FA, intracellular minerals, hormones, vitamins) while passing standard blood markers through to schema categories (hybrid import)
Window exports: none

constants.js

Static arrays and string constants used across modules. Key exports:
  • CHAT_PERSONALITIES — array of { id, name, icon, promptText } for the 3 built-in personalities
  • CHAT_SYSTEM_PROMPT — the base system prompt string injected into all AI chat requests
  • COUNTRY_LATITUDES{ countryCode: latitudeBand } (~70 countries, 5 bands: arctic/north/temperate/subtropical/tropical)
  • FAKE_DATA — synthetic name/address/DOB data for PII obfuscation
  • Per-card option arrays: SLEEP_DURATIONS, SLEEP_QUALITIES, SLEEP_SCHEDULES, SLEEP_ISSUES, SLEEP_ENVIRONMENTS, SLEEP_PRACTICES, LIGHT_AM, LIGHT_DAYTIME, LIGHT_UV, LIGHT_EVENING, LIGHT_SCREEN, LIGHT_TECH_ENV, LIGHT_COLD, LIGHT_GROUNDING, LIGHT_MEAL_TIMING, STRESS_LEVELS, STRESS_SOURCES, STRESS_MANAGEMENT, ENV_SETTING, ENV_CLIMATE, ENV_WATER, ENV_WATER_CONCERNS, ENV_EMF, ENV_EMF_MITIGATION, ENV_HOME_LIGHT, ENV_AIR, ENV_TOXINS, ENV_BUILDING, LOVE_STATUS, LOVE_RELATIONSHIP, LOVE_SATISFACTION, LOVE_LIBIDO, LOVE_FREQUENCY, LOVE_ORGASM, LOVE_CONCERNS, PERIOD_SYMPTOMS, DIET_TYPES, DIET_RESTRICTIONS, DIET_PATTERNS, EXERCISE_FREQUENCIES, EXERCISE_TYPES, EXERCISE_INTENSITIES, EXERCISE_DAILY_MOVEMENT
Window exports: none

state.js

Single shared mutable state object. Import state to read or write. No logic. Key exports:
  • state — the mutable singleton:
    {
      chartInstances: {},        // Chart.js instances by element id
      markerRegistry: {},        // runtime marker lookup cache
      importedData: {            // all user data for the active profile
        entries: [],             // lab results: [{ date, markers: { "cat.key": value } }]
        notes: [],               // [{ date, text }]
        supplements: [],         // supplement timeline entries
        healthGoals: [],         // [{ text, severity }]
        diagnoses: null,         // { conditions: [{ name, severity, since? }], note }
        diet: null,              // structured meal object
        exercise: null,          // structured exercise object
        sleepRest: null,         // structured sleep object
        lightCircadian: null,    // structured light/circadian object
        stress: null,            // structured stress object
        loveLife: null,          // structured relationship/sexual health object
        environment: null,       // structured environment object
        interpretiveLens: '',    // freetext string
        contextNotes: '',        // freetext string
        menstrualCycle: null,    // { cycleLength, periodLength, regularity, flow, periods[] }
        customMarkers: {}        // { "cat.key": { name, unit, refMin, refMax, categoryLabel } }
      },
      unitSystem: 'EU',          // 'EU' | 'US'
      currentProfile: 'default', // active profile id
      profiles: null,            // loaded profiles array
      profileSex: null,          // 'male' | 'female' | null
      profileDob: null,          // 'YYYY-MM-DD' | null
      chatHistory: [],           // current thread messages
      chatThreads: [],           // thread index array
      currentThreadId: null,
      currentChatPersonality: 'default',
      dateRangeFilter: 'all',    // 'all' | '6m' | '1y' | '2y'
      rangeMode: 'optimal',      // 'optimal' | 'reference'
      suppOverlayMode: 'off',    // 'off' | 'on'
      noteOverlayMode: 'off',
      phaseOverlayMode: 'off',
      compareDate1: null,
      compareDate2: null,
    }
    
window._labState = state is set for debugging in the browser console. Window exports: none (state is accessed via import)

utils.js

Shared pure utility functions. Key exports:
  • escapeHTML(str) — escapes <>&"' for safe innerHTML insertion
  • hashString(str) — djb2 hash, returns integer
  • getStatus(value, refMin, refMax)'normal' | 'high' | 'low' | 'missing'. Returns 'normal' when refs are null
  • formatValue(value, unit) — formats a numeric value with appropriate decimal places
  • showNotification(message, type) — toast notification ('info' | 'success' | 'error' | 'warning')
  • showConfirmDialog(message) — returns Promise<boolean>, styled confirm dialog
  • linearRegression(points){ slope, intercept, r2 } from [{ x, y }] array
  • hasCardContent(obj) — generic empty-card gate: returns true if any field has content (strings non-empty, arrays non-empty, note trimmed). Used by buildLabContext() for 7 context card gates
  • bindModalSyncRefresh(opts) / bindDetailModalSyncRefresh(kind, refresh) / bindDetachedModalSyncRefresh(opts) — shared labcharts-sync-applied modal refresh bindings with dirty-form guards and scroll restoration
Window exports: showNotification, showConfirmDialog, setDebugMode, setPIIReviewEnabled, hasCardContent

Layer 2 — Core Services

Shared modal backdrop, focus-trap, and body-scroll lock helpers. Owns the cross-module scroll-lock registry used by Light & Sun modals; sun.js re-exports the legacy names for compatibility. Key exports:
  • wireBackdropClose(overlay, closeFn?) / _wireBackdropClose(overlay, closeFn?) — backdrop click close wiring with inside-click guard
  • trapModalFocus(overlay) — locks background scroll, restores focus/overflow on removal, and handles Escape close
Window exports: none directly; sun.js still exposes compatibility globals.

theme.js

Theme management and Chart.js color helpers. Key exports:
  • getTheme() / setTheme(theme) / toggleTheme()'dark' | 'light'; setTheme sets data-theme on <html>
  • getChartColors() — reads live CSS custom properties and returns a chart color config object
  • formatDateLabel(dateStr) — formats ISO date for chart x-axis
  • getTimeFormat() / setTimeFormat(fmt)'24h' | '12h', stored in labcharts-time-format
  • formatTime(timeStr) — formats a 24h time string for display using the active format
  • parseTimeInput(input) — accepts both '14:30' and '2:30 PM', always returns 24h format
Window exports: toggleTheme

hardware.js

GPU detection and model fitness advisor for Local AI settings. Pure functions, no DOM manipulation. Key exports:
  • detectHardware() — async, returns { gpu: { name, vram, unified, renderer, source }, ram: { gb, source }, cpuThreads }. GPU detected via WebGL WEBGL_debug_renderer_info matched against 75-entry GPU_DB (Apple Silicon M1–M4, NVIDIA RTX 30/40/50, AMD RX 6000/7000, Intel Arc, Vega)
  • assessModel(modelObj, hardware) — returns { tier, badge, vramNeeded, label } where tier is 'fits' / 'tight' / 'toobig' / 'unknown'
  • assessFitness(modelName) — rates a model for getbased lab analysis: { tier, note } where tier is 'recommended' / 'capable' / 'underpowered' / 'inadequate'. Benchmarked against Sonnet 4.6
  • getBestModel(modelDetails, hardware) — picks the highest-fitness installed model that fits in VRAM
  • getUpgradeSuggestion(modelDetails, hardware) — returns a pull recommendation if no installed model is “recommended” tier
  • saveHardwareOverride(vram) / getHardwareOverride() — manual VRAM override in localStorage
Window exports: none (imported by settings.js)

api.js

AI provider routing and model management. All AI calls flow through callClaudeAPI. Key exports:
  • callClaudeAPI(opts) — main router: delegates to the active provider based on getAIProvider()
  • callOpenRouterAPI(opts) — OpenRouter via callOpenAICompatibleAPI
  • callRoutstrAPI(opts) — Routstr via callOpenAICompatibleAPI
  • callPPQAPI(opts) — PPQ via callOpenAICompatibleAPI
  • callVeniceAPI(opts) — Venice AI via callOpenAICompatibleAPI
  • callOpenAICompatibleLocalAPI(opts) — Local AI via shared callOpenAICompatibleAPI helper
  • callOpenAICompatibleAPI(endpoint, key, model, providerName, opts, extraHeaders) — shared OpenAI-format helper
  • getAIProvider() / setAIProvider(provider)'openrouter' | 'routstr' | 'ppq' | 'venice' | 'ollama' (internal key for Local)
  • hasAIProvider() — returns true if any provider is configured; gates all 7 AI features
  • getOpenRouterModel(), getRoutstrModel(), getPPQModel(), getVeniceModel(), getOllamaMainModel()
  • fetchOpenRouterModels(), fetchRoutstrModels(), fetchPPQModels(), fetchVeniceModels() — dynamic model lists
  • getModelPricing(modelId) — checks dynamic OpenRouter pricing cache, falls back to MODEL_PRICING
  • OPENROUTER_CURATED — whitelist of latest-gen medically capable models (prefix-matched)
  • OPENROUTER_EXCLUDE — blocklist filtering codex/audio/image/oss variants
Window exports: getRoutstrNodeUrl (used by settings UI for node URL access)

api-transport.js

Shared AI API transport helpers used by api.js. Key exports:
  • fetchWithRetry(url, options, config) — wraps fetch with request timeout, transient network retry, and 429 backoff
  • createProxyFetch(shouldUseProxy) — builds the Custom API proxy fetch wrapper
  • readWithStallTimeout(reader, label) — guards streaming readers from hanging between chunks
  • STREAM_STALL_TIMEOUT_MS, FETCH_REQUEST_TIMEOUT_MS, AI_IMPORT_REQUEST_TIMEOUT_MS — public timeout constants re-exported by api.js
Window exports: none

cashu-wallet.js

In-app Cashu eCash wallet for decentralized AI payments. Proofs stored in IndexedDB, BIP-39 seed encrypted in localStorage. Key exports:
  • getMintUrl() / setMintUrl(url) — configured Cashu mint
  • generateWalletSeed() — creates 12-word BIP-39 mnemonic
  • restoreWalletFromSeed(mnemonic) — restores proofs from mint via batchRestore
  • getWalletBalance() — sum of unspent proofs for current mint
  • createFundingInvoice(amountSats) / checkFundingStatus(quoteId) — Lightning deposit flow
  • receiveToken(tokenString) — deposit a Cashu token
  • depositToNode(nodeUrl, amountSats, existingKey) — swap proofs for a node session key
  • sendAsToken(amountSats) — withdraw as shareable Cashu token
  • createWithdrawQuote(bolt11) / executeWithdraw(quoteId) — Lightning withdrawal
  • withdrawToAddress(address, amountSats) — LNURL-pay withdrawal
  • recoverPendingDeposit() / recoverPendingWithdraw() — failed operation recovery
  • exportWallet() / importWallet(tokenString) — backup/restore
  • getFeePct() — current fee percentage (0 during beta)
Window exports: all functions prefixed with cashu (e.g., cashuGetBalance, cashuDepositToNode)

nostr-discovery.js

Discovers Routstr AI nodes via Nostr relays (Kind 38421 events). Key exports:
  • discoverNodes(forceRefresh) — queries relays in parallel, deduplicates, health-checks, returns sorted node array
  • getSelectedNodeUrl() / setSelectedNodeUrl(url) — persisted node selection
  • clearNodeCache() — force re-discovery on next call
Window exports: nostrDiscoverNodes, nostrGetSelectedNode, nostrSetSelectedNode, nostrClearNodeCache

Layer 3 — Data & Profile

profile.js

Profile lifecycle and settings persistence. Key exports:
  • profileStorageKey(profileId, suffix) — builds labcharts-{profileId}-{suffix}
  • createDefaultProfileData() — returns the empty importedData shape used for new/metadata-only profile sync
  • loadProfiles() / saveProfiles() — profile index CRUD
  • switchProfile(profileId) — loads importedData from localStorage for the given profile
  • deleteProfile(profileId) — removes all keys for the profile
  • migrateProfileData() — upgrades legacy field formats on load (old sleepCircadiansleepRest + lightCircadian, old fieldExperts/fieldLensinterpretiveLens, initializes missing fields with null)
  • getProfileLocation() / setProfileLocation(country, zip) — country+ZIP storage
  • getLatitudeFromLocation() — looks up latitude band from COUNTRY_LATITUDES
  • renderProfileDropdown() — renders the profile selector UI
Window exports: openProfileEditor, switchProfile, deleteProfile, addProfile, saveProfile

data.js

The central data pipeline. Every view gets its data from getActiveData(). Key exports:
  • getActiveData() — deep-clones MARKER_SCHEMA, merges custom markers, applies sex-specific ranges, populates values[] arrays from importedData.entries, computes ratios + PhenoAge + cycle phases, applies unit conversion. Returns data object with { dates[], dateLabels[], categories, phaseLabels? }
  • saveImportedData() — persists state.importedData to localStorage (or encrypted store), triggers backup
  • buildMarkerReference() — compact JSON of all known markers for AI system prompts (PDF import)
  • filterDatesByRange(data) — applies state.dateRangeFilter to dates + values arrays in-place
  • registerRefreshCallback(fn) — registers the refresh function from app-event-listeners.js
  • getFocusCardFingerprint() — djb2 hash of all entries + all 9 context cards + sex + DOB
  • Compatibility re-exports from marker-analysis.js keep existing window.* helper access stable.
Window exports: saveImportedData, clearAllData (via export.js), filterDatesByRange

marker-analysis.js

Read-only marker range, status, and trend helpers. This module owns pure marker analysis so chart, dashboard, AI context, and export code do not need to import the storage/sync-heavy data.js when they only need derived marker calculations. Key exports:
  • getEffectiveRange(marker) — returns { min, max } respecting state.rangeMode
  • getEffectiveRangeForDate(marker, dateIndex) — phase-aware range lookup; falls back to getEffectiveRange()
  • getPhaseRefEnvelope(marker) — widest span across all cycle phases for chart ref bands
  • getLatestValueIndex(values) — index of the latest non-null marker value
  • countFlagged(markers) / getAllFlaggedMarkers(data) — out-of-range marker helpers
  • statusIcon(status) — status-to-symbol mapping used by category cards
  • detectTrendAlerts(data) — sudden-change (25% of ref range, 2+ values) and linear-regression (slope >0.02, R²>0.5 for 4+ points) alerts
  • getKeyTrendMarkers(filteredData) — dashboard key-trend selection
Window exports: none directly; data.js keeps compatibility exports.

pii.js

Two-path PII obfuscation for PDF text before AI submission, with streaming review modal. Key exports:
  • sanitizeWithOllamaStreaming(pdfText, onChunk, signal) — preferred path: SSE streaming via OpenAI-compatible API, calls onChunk(delta) per token, supports AbortSignal
  • sanitizeWithOllama(pdfText) — non-streaming path: same endpoint, used when review is disabled
  • obfuscatePDFText(pdfText) — regex fallback: label-based + pattern-based replacement, returns { obfuscated, original, replacements }
  • reviewPIIBeforeSend(originalText, { obfuscatedText, streamFn }) — review modal: streaming mode (pass streamFn) or static mode (pass obfuscatedText). Returns edited text or 'cancel'
  • checkOllamaPII() — checks PII server availability via /v1/models
  • getOllamaConfig() / saveOllamaConfig(config) — local AI config: { url, model, apiKey }
  • showPIIDiffViewer(original, obfuscated, replacements) — debug diff viewer (requires labcharts-debug flag)
Window exports: setOllamaPIIModel

image-utils.js

Shared image utilities for chat attachments and PDF image fallback. Key exports:
  • resizeImage(file, maxDim?, quality?) — resizes an image to fit within maxDim pixels (default 1024), returns { base64, mediaType, width, height, origWidth, origHeight, quality_warnings }
  • isValidImageType(type) — validates MIME type against image/(jpeg|png|gif|webp)
  • formatImageBlock(base64, mediaType, provider) — returns a provider-specific image content block (type:'image_url' for OpenAI-compatible providers)
  • buildVisionContent(imageBlocks, text, provider) — assembles a content array with image blocks + text block
Internal:
  • analyzeImageQuality(ctx, width, height) — canvas-based pixel sampling (~100k samples): brightness (dark/overexposed) + Laplacian variance (blur detection). Returns string[] warnings
Window exports: resizeImage, isValidImageType, formatImageBlock, buildVisionContent

Layer 4 — Domain Modules

charts.js

Chart.js configuration and all custom plugins. Key exports:
  • createLineChart(canvasId, marker, data, phaseLabels?) — creates a Chart.js line chart with all plugins applied
  • destroyAllCharts() — destroys all instances in state.chartInstances to prevent memory leaks
  • Five Chart.js plugins (registered globally):
    • refBandPlugin — shaded reference range band
    • optimalBandPlugin — green dashed optimal range band
    • noteAnnotationPlugin — yellow dot annotations at note dates with hover tooltips
    • supplementBarPlugin — colored timeline bars for supplements
    • phaseBandPlugin — cycle phase vertical shading (menstrual=red, follicular=blue, ovulatory=purple, luteal=yellow, 8% opacity)
Window exports: none

notes.js

Standalone note management (independent of lab entries). Key exports:
  • openNoteEditor(date?) — opens the note editor modal, pre-filled if date provided
  • saveNote() — saves the current editor content to importedData.notes
  • deleteNote(date) — removes a note by date
Window exports: openNoteEditor, saveNote, deleteNote

supplements.js

Supplement and medication timeline, editor, ingredient tracking, and AI impact analysis. Key exports:
  • openSupplementsEditor(index?) — opens the supplement editor modal
  • saveSupplement(idx) — persists current form state to importedData.supplements
  • deleteSupplement(idx) — removes by index
  • renderSupplementsSection() — dashboard timeline bars
  • renderSupplementImpact(supp, editIdx) — per-supp impact card (shimmer → cached AI summary)
  • computeSupplementImpact(supp, markerKey, ...) — before/after mean comparison for one marker
  • computeAllImpacts(supp, data) — impact vectors across all markers, sorted by |pctChange|
  • parseAmount(str) — extract {value, unit} from “890mg” / “5,4 mg” / “500 IU” (handles comma decimals)
  • effectiveTimesPerDay(ing, supp) — row override wins, else supp-level default
  • ingredientDailyTotal(ing, supp) — computed amount × effectiveTimesPerDay
Ingredient data model: each supp has an optional outer timesPerDay (default multiplier); each ingredient row has {name, amount, timesPerDay?} where timesPerDay is an optional per-row override. Daily total = amount × (row.timesPerDay ?? supp.timesPerDay). Impact cache: per-supp cache in labcharts-{profileId}-suppImpact keyed by supp name with a fingerprint that includes dosage, outer timesPerDay, per-ingredient fields, periods, and lab dates — any edit auto-invalidates only that supp’s cache entry. Concurrent renders are coalesced via a 50ms debounced queue (scheduleAnalyzeflushAnalyses).

cycle.js

Menstrual cycle tracking, helpers, and dashboard rendering. Key exports:
  • getCyclePhase(dateStr, mc){ cycleDay, phase, phaseName } for a date against a cycle object
  • getNextBestDrawDate(mc) — next early follicular window (days 3–5)
  • getBloodDrawPhases(mc, dates) — maps lab dates to phases
  • calculateCycleStats(periods) — auto-computes { cycleLength, periodLength, regularity } from period log
  • detectPerimenopausePattern(mc, dob) — flags perimenopause pattern (age 35+, 4+ periods, 2+ of 4 indicators)
  • detectCycleIronAlerts(mc, data) — cross-references heavy flow with ferritin/hemoglobin/iron
  • openMenstrualCycleEditor() — opens the cycle editor modal
  • saveMenstrualCycle() — saves to importedData.menstrualCycle, triggers cycle tour
  • renderMenstrualCycleSection(data) — renders the full cycle dashboard section
  • startCycleTour(auto) — triggers the 8-step cycle spotlight tour
Window exports: openMenstrualCycleEditor, saveMenstrualCycle, addPeriod, deletePeriod, startCycleTour

context-cards.js

Lifestyle context card rendering plus AI health dots, Interpretive Lens / Knowledge Base dashboard rows, dashboard CTA pills, context change history, and legacy compatibility exports for summary/editor helpers. Key exports:
  • renderProfileContextCards(data) — renders the 3-column context card grid on the dashboard
  • renderInterpretiveLensSection() — renders the lens row (when set) + KB status row (when configured) + AI personalize CTA pill + Data protection CTA pill
  • renderKnowledgeBaseSection() — pure-render helper for the KB status row (returns '' when no library configured)
  • renderDataProtectionCta(stateOverride?) — pure render of the data-protection pill; accepts an explicit state override for testability
  • openPersonalizeAIPicker() — 2-card picker (Lens / Knowledge Base) shown when both are unset
  • openDataProtectionPicker() — 3-card picker (Encryption / Sync / Auto-backup) with configured cards grayed out
  • triggerDNAFilePicker() — programmatic file input trigger that routes through window.handleDNAFile; used by the genetics empty-state stub
  • loadContextHealthDots() — async; fetches AI health ratings for stale cards (per-card fingerprint caching)
  • getCardFingerprint(key) — djb2 hash of lab data + card data + sex + DOB for cache invalidation
  • Per-card editor functions: openDiagnosesEditor, openDietEditor, openExerciseEditor, openSleepEditor, openLightEditor, openStressEditor, openLoveLifeEditor, openEnvironmentEditor, openHealthGoalsEditor; Medical History helpers are compatibility re-exports from context-card-medical-history-editor.js, and lifestyle/simple editors are compatibility re-exports from context-card-lifestyle-editors.js
  • Per-card save functions: saveDiagnoses, saveDiet, saveExercise, saveSleep, saveLight, saveStress, saveLoveLife, saveEnvironment, saveHealthGoals; saveDiagnoses is a compatibility re-export from context-card-medical-history-editor.js, and lifestyle/simple saves are compatibility re-exports from context-card-lifestyle-editors.js
  • selectCtxOption(el, group, multi) — compatibility re-export from context-card-editor-ui.js
  • getSelectedOption(group) — reads selected value from a .ctx-btn-group
  • Summary helpers such as getDietSummary() and getEnvironmentSummary() — compatibility re-exports from context-card-summaries.js
  • debounceContextNotes() — auto-saves the free-form context notes textarea
  • recordChange(field) — snapshots a context field and appends a timestamped entry to importedData.changeHistory (dedup: same-day overwrite, identical skip, 200 cap)
Window exports: all open/save functions, selectCtxOption, addCondition, deleteCondition, addGoal, deleteGoal, syncDiagnosesNote, openInterpretiveLensEditor, saveInterpretiveLens, renderKnowledgeBaseSection, openPersonalizeAIPicker, openDataProtectionPicker, triggerDNAFilePicker

context-card-summaries.js

Context card metadata, filled-state checks, dashboard summary strings, and the EMF assessment launcher/summary. Kept side-effect-light so card rendering, chat/onboarding callers, and tests can share one source of truth without pulling in every editor modal. Key exports: CONTEXT_CARD_KEYS, getContextCardDefs(), isContextFilled(key), getConditionsSummary(), getDietSummary(), getExerciseSummary(), getSleepSummary(), getLightCircadianSummary(), getStressSummary(), getLoveLifeSummary(), getEnvironmentSummary(), getGoalsSummary(), getEMFAssessments(), renderEMFAssessmentLauncher(). Window exports: none directly; legacy globals are assigned by context-cards.js.

context-card-editor-ui.js

Shared context editor modal shell and field-control render/read helpers used by the 9 card editors. Key exports: renderContextEditorModal(), renderSelectField(), selectCtxOption(), getSelectedOption(), renderTagsField(), toggleCtxTag(), getSelectedTags(), renderNoteField(), contextEditorActions(). Window exports: none directly; legacy globals are assigned by context-cards.js.

context-card-medical-history-editor.js

Medical History editor module for personal conditions and family history. Owns the condition autocomplete, relative allowlist, add/delete flows, modal rendering, note sync, and save/clear handlers for importedData.diagnoses. Key exports: configureMedicalHistoryEditor(), openDiagnosesEditor(), renderDiagnosesModal(), filterConditionSuggestions(), selectConditionSuggestion(), closeSuggestionsOnClickOutside(), syncDiagnosesNote(), addCondition(), editCondition(), cancelConditionEdit(), deleteCondition(), addFamilyHistoryEntry(), editFamilyHistoryEntry(), cancelFamilyHistoryEdit(), deleteFamilyHistoryEntry(), filterFamilyConditionSuggestions(), selectFamilyConditionSuggestion(), saveDiagnoses(), closeDiagnoses(), clearDiagnoses(). Window exports: none directly; legacy globals are assigned by context-cards.js.

context-card-lifestyle-editors.js

Lifestyle/simple context editors for Diet & Digestion, Sleep & Rest, Light & Circadian, Exercise, Stress, Love Life, Environment, Health Goals, Interpretive Lens, and the diet contaminant modal. Uses configureLifestyleContextEditors() so context-cards.js remains the owner of recordChange() and saveAndRefresh() while the editor module owns modal rendering and save/clear handlers. Key exports: configureLifestyleContextEditors(), renderDietContaminantsBadge(), openDietEditor(), saveDiet(), clearDiet(), openSleepRestEditor(), saveSleepRest(), clearSleepRest(), openLightCircadianEditor(), saveLightCircadian(), clearLightCircadian(), openExerciseEditor(), saveExercise(), clearExercise(), openStressEditor(), saveStress(), clearStress(), openLoveLifeEditor(), saveLoveLife(), clearLoveLife(), openEnvironmentEditor(), saveEnvironment(), clearEnvironment(), openHealthGoalsEditor(), renderHealthGoalsModal(), addHealthGoal(), deleteHealthGoal(), closeHealthGoals(), clearHealthGoals(), openInterpretiveLensEditor(), saveInterpretiveLens(), clearInterpretiveLens(), showDietContaminantsModal(). Window exports: none directly; legacy globals are assigned by context-cards.js.

Layer 5 — Feature Modules

dna.js

DNA raw data import: client-side parser, storage, dashboard section, AI context assembly. Key exports:
  • detectDNAFile(text) — detects format from file header: 'ancestry' | '23andme' | 'livingdna' | 'csv' | null
  • isDNAFile(file) — checks filename patterns for known DNA providers
  • isDNAFileByContent(file) — async, reads first 500 bytes to detect DNA format by content
  • parseDNAFile(file) — async, runs Web Worker parser, matches against data/snp-health.json (41 SNPs), resolves APOE haplotype, returns enriched matches with effect/note per genotype
  • saveGeneticsData(profileData, result) — stores matched SNPs + APOE in importedData.genetics
  • deleteGeneticsData(profileData) — removes genetics data
  • buildGeneticsContext(genetics, activeMarkerKeys) — serializes genetics for AI context, filtered to SNPs relevant to active markers
  • renderGeneticsSection() — full genetics interpretation section for classic/mobile dashboard contexts; returns an in-context “Add your DNA data” empty-state stub (wired to triggerDNAFilePicker) when no SNPs/mtDNA exist
  • handleDNAFile(file) — full import flow: parse → preview modal → confirm → save
Supports: AncestryDNA (2-column alleles), 23andMe, MyHeritage, FTDNA, Living DNA. Genotype reversal handles strand ambiguity (CT ↔ TC). Window exports: isDNAFile, isDNAFileByContent, handleDNAFile, confirmDNAImport, closeDNAImportPreview, deleteGeneticsData, _buildGeneticsContext, _getRelevantSNPs

pdf-import.js

PDF/image/text-to-lab-data import pipeline and confirm/save merge logic. Key exports:
  • extractPDFText(file) — pdf.js text extraction with x/y coordinates, returns page-aware formatted text
  • parseLabPDFWithAI(pdfText) — sends text + buildMarkerReference() to AI; maps lab results to marker keys
  • handleImageFile(file) — imports lab reports from JPG/PNG/WebP images via AI image pipeline
  • handleBatchPDFs(files) — sequential multi-file import with per-file confirm/skip
  • showImportPreview(parsed) — re-export from pdf-import-review.js
  • confirmImport(parsed) — merges parsed data into importedData.entries
  • setupDropZone() — legacy eager drop-zone binding retained for compatibility; page shells use import-drop-zone.js so the PDF import module stays lazy-loaded
Window exports: confirmImport, skipImport, importNextPDF, syncImportStatusFab, handleImportStatusClick, isImportRunning

pdf-import-preflight.js

Pre-AI import checks for duplicate PDF hashes, previous-model mismatch prompts, and unsupported specialty-test warnings. Key exports:
  • runPreflightChecks(pdfText, fileName) — returns false when the user cancels before token-spending import work starts
  • normalizeImportModelId(id) — shared model ID canonicalization for cross-provider import mismatch checks
Window exports: none directly; pdf-import.js calls this during PDF/batch import.

pdf-import-progress.js

Import progress bar, header import-button status, and batch progress rendering. Key exports:
  • showImportProgress(step, fileName) / hideImportProgress(reason) — render and clear the import progress UI
  • showBatchImportProgress(step, fileName, current, total) — render per-file batch progress
  • updateImportProgressPct(pct) — streaming AI progress update hook
  • syncImportStatusFab() / handleImportStatusClick() / isImportRunning() — status bridge used by pdf-import.js window exports; the legacy function name now syncs the header import button
Window exports: via pdf-import.js

pdf-import-marker-normalization.js

Shared AI marker normalization for PDF text import and image import. Key exports:
  • normalizeParsedImportMarkers(parsed, options) — sanitizes AI marker keys, runs product/test-type adapters, applies specialty guard rules, reconciles marker mappings, and returns { testType, markers }
Window exports: none directly; pdf-import.js calls this after AI JSON parsing.

pdf-import-persistence.js

Durable imported-data persistence helpers for PDF import flows. Key exports:
  • snapshotImportedData() / restoreImportedDataSnapshot(snapshot) — rollback guard used when durable import saves fail
  • refreshImportedDataViews() — rebuilds sidebar/header and restores the current route after imported data changes
  • removeImportedEntry(date) / renameImportedEntryDate(oldDate) — entry delete and date-edit mutations with tombstones, immediate sync save, rollback, and success refresh
Window exports: via pdf-import.js

pdf-import-review.js

Import review modal rendering and interaction state. Key exports:
  • showImportPreview(parsed) — modal with matched, new custom, unmatched, and excluded marker rows
  • applyManualImportDate(date) — updates pending review date and confirm button state
  • mapUnmatchedMarkerInput(input) / toggleImportRow(button) — review-row mapping and exclusion controls
  • getPendingImport() / getExcludedImportIndices() — narrow state access used by confirmImport()
  • showImportPreviewAsync(result, current, total) — batch import preview promise bridge
Window exports: via pdf-import.js

import-drop-zone.js

Lazy drop-zone event binding shared by Dashboard, Labs, and mobile dashboard shells. It imports loadPdfImport() from import-loader.js, classifies dropped files only after interaction, and routes JSON, lab PDFs/images, text imports, and DNA files to the same handlers used by the file input path. Key exports:
  • setupDropZone() — idempotently binds click, drag, and drop handlers to #drop-zone

import-file-input.js

Lazy file-picker import binding for the hidden #pdf-input. It shares loadPdfImport() with the drop-zone path, classifies files on change, clears stale selections on failure, and routes JSON, lab PDFs/images, text imports, and DNA files through the import/DNA handlers exposed on window. Key exports:
  • bindImportFileInput() — idempotently binds #pdf-input change handling and document-level drag/drop prevention
  • handleImportInputChange(e) — routes one file-picker change event through the lazy import classifier

export.js

Data export, import, and reset. Key exports:
  • exportToJSON() — exports v2 JSON for the current profile: { version: 2, exportedAt, entries, notes, diagnoses, diet, exercise, sleepRest, lightCircadian, stress, loveLife, environment, interpretiveLens, healthGoals, contextNotes, menstrualCycle, customMarkers, supplements }
  • buildClientExportObject(profileId, includeChat?) — module-only helper that returns the reusable v2 single-profile export object used by JSON export and encrypted profile sharing. It includes profile metadata, labs, context, supplements, genetics, biometrics, marker notes, wearables summaries/preferences, and Light/Sun data, while excluding OAuth/wearable connection credentials and raw per-device wearable rows
  • exportClientJSON(profileId) — exports a single client’s data (used from Client List ⋮ menu)
  • exportAllDataJSON() — exports a full database bundle with all profiles, chat threads, custom personalities, and settings
  • buildAllDataBundle() — builds the bundle object used by both exportAllDataJSON() and folder backup
  • importFromJSON(file) — merges entries by date, deduplicates notes, overwrites context fields, merges healthGoals by text. Auto-detects database bundles and handles multi-profile merge
  • exportToPDF() — generates a printable PDF report with all data, charts, and context cards
  • clearAllData() — confirms and wipes all imported data for the current profile
Window exports: exportToJSON, exportDataJSON, exportClientJSON, exportAllDataJSON, importFromJSON, exportToPDF, clearAllData

profile-share.js

Encrypted single-profile share links. The module creates a profile export through the module-only buildClientExportObject() helper, encrypts it in the browser with a password, posts only the ciphertext envelope to /api/share, and imports shared profiles from #share/{id} deep links after local password decryption. Shared envelopes require at least 100,000 PBKDF2-SHA256 iterations; the normal generated value is 600,000. Key exports:
  • createProfileShare({ profileId, password, expiresDays }) — builds, encrypts, uploads, and records a managed share link for one profile
  • encryptProfileShareEnvelope(exportObj, secret, options?) / decryptProfileShareEnvelope(envelope, secret) — AES-256-GCM envelope helpers using PBKDF2-SHA256
  • generateProfileSharePassword() — generates a user-copyable random password
  • buildProfileShareUrl(id) / parseProfileShareIdFromLocation(loc?) — link construction and deep-link parsing
  • openProfileShareModal(profileId?) / openSharedProfileImportModal(id) — create/load modal entry points
  • handleProfileShareDeepLink() / initProfileShareLinks() — startup hash handling for shared links
Storage: active links created in the current browser are tracked in localStorage under getbased-profile-shares-v1. Records store share metadata and the management token needed to stop sharing; they do not store the profile password or plaintext profile data. Window exports: openProfileShareModal, closeProfileShareModal, openSharedProfileImportModal, createProfileShare, deleteProfileShareEnvelope, encryptProfileShareEnvelope, decryptProfileShareEnvelope, parseProfileShareIdFromLocation, buildProfileShareUrl

chat.js

Chat public barrel and startup entry point. Importing this module installs chat-window-bindings.js for legacy inline handlers, then re-exports the public chat helpers from the feature modules. Per-marker/correlation prompt builders live in chat-marker-prompts.js; direct send/streaming and image send integration live in chat-send.js; chat transcript rendering lives in chat-render.js; empty/onboarding message states live in chat-empty-state.js; chat-first onboarding handlers and provider quiz helpers live in chat-onboarding.js; multi-persona discussion exports live in chat-discussion.js, with public user-action handlers in chat-discussion-flow.js, callback bridging in chat-discussion-callbacks.js, cleanup/completion helpers in chat-discussion-lifecycle.js, round turn execution helpers in chat-discussion-turns.js, persona/thread state helpers in chat-discussion-state.js, round execution in chat-discussion-round-runner.js, round prompt helpers in chat-discussion-round-prompts.js, round API request helpers in chat-discussion-round-request.js, thread-bound round persistence in chat-discussion-round-state.js, live round message DOM helpers in chat-discussion-round-view.js, persona picker controls in chat-discussion-picker.js, and Discuss button/continue controls in chat-discussion-ui.js; panel chrome lives in chat-panel.js; FAB nudge state lives in chat-nudge.js; personality selection and custom persona editing live in chat-personalities.js; current-thread history persistence lives in chat-history.js; message action bars live in chat-actions.js; chat image attachment state lives in chat-images.js; thread index storage and rail rendering live in chat-threads.js; thread rail message search and match highlighting live in chat-thread-search.js. Key exports:
  • sendChatMessage() — re-exported from chat-send.js for existing callers
  • renderChatMessages() — re-exported from chat-render.js for existing callers
  • askAIAboutMarker(markerKey) — re-exported from chat-marker-prompts.js; opens chat with a marker-specific prompt
  • askAIAboutCorrelations() — re-exported from chat-marker-prompts.js; opens chat with a selected-marker correlation prompt
  • Thread management: createNewThread(), loadThread(id), deleteThread(id), renameThread(id)
Window exports: sendChatMessage, setChatPersonality, openChatPanel, closeChatPanel, createNewThread, loadThread, deleteThread, renameThread, generateCustomPersonality, saveCustomPersonality, askAIAboutMarker, addImageAttachment, toggleHDMode

chat-window-bindings.js

Chat callback wiring and legacy window.* compatibility exports. Owns _resumeAI, configures chat-discussion.js, chat-onboarding.js, and chat-panel.js callbacks, and exposes the chat handlers needed by inline HTML and cross-module window.fn() call sites. Key exports: none; loaded for side effects by chat.js. Window exports: sendChatMessage, renderChatMessages, setChatPersonality, openChatPanel, closeChatPanel, discussion handlers, onboarding handlers, summary handlers, action-bar handlers, and per-marker Ask AI handlers.

chat-marker-prompts.js

Per-marker and selected-correlation chat prompt builders. Owns askAIAboutMarker() and askAIAboutCorrelations(), including phase-aware marker values, effective reference ranges, latest status, and trend text before opening the chat panel with a prefilled prompt. Key exports: askAIAboutMarker, askAIAboutCorrelations Window exports: assigned by chat-window-bindings.js for existing inline Ask AI buttons.

chat-send.js

Direct chat send and streaming state. Owns sendChatMessage(), Enter-key handling, stop-button abort state, send-button icon mode, typewriter trickle, image attachment payload injection, API message assembly, usage footnotes, live EMF/product recommendation injection, and partial-message persistence on abort. It is imported by chat.js, which re-exports its public entry points and passes its typewriter/abort helpers to chat-discussion.js. Key exports: sendChatMessage, handleChatKeydown, isChatStreaming, createTypewriter, getChatAbortController, setChatAbortController, setSendButtonMode, updateSendButtonState Window exports: updateSendButtonState is assigned by this module for chat-images.js; other user-facing handlers are assigned by chat-window-bindings.js.

chat-render.js

Chat transcript rendering. Owns renderChatMessages() for persisted messages, lens-source disclosure rendering, assistant footnotes, image badges, EMF hints, and persisted recommendation sections. It delegates empty chat and onboarding states to chat-empty-state.js and keeps rendering details out of the chat window wiring module. Key exports: renderChatMessages, _getNoDataPrompts, _renderLensSources Window exports: renderChatMessages is assigned by chat-window-bindings.js for existing inline handlers and cross-module callbacks.

chat-empty-state.js

Empty chat and chat-first onboarding message composition. Owns the profile setup bubble, paused-AI prompt, explicit provider quiz entry state, no-lab optional setup cards, no-data prompt selection, and the default empty prompt buttons used before a real thread has messages. Key exports: renderEmptyChatState, _getNoDataPrompts Window exports: none; called by chat-render.js.

chat-panel.js

Chat panel chrome and entry-state helpers. Owns open/close/fullscreen behavior, persisted fullscreen preference, web-search toggle persistence/visibility, and composer disabled state. It imports thread/history/personality helpers directly, delegates FAB nudge dismissal to chat-nudge.js, and receives the active-discussion restore callback through configureChatPanel() to avoid importing chat-discussion.js. Key exports: configureChatPanel, toggleChatPanel, toggleChatFullscreen, openChatPanel, closeChatPanel, updateChatInputState, getChatWebSearchEnabled, setChatWebSearchEnabled, refreshWebSearchToggle; also re-exports setChatNudge and updateChatNudge from chat-nudge.js for compatibility. Window exports: assigned by chat-window-bindings.js for existing inline handlers and cross-module callbacks.

chat-nudge.js

Chat FAB nudge badge state. Owns nudge stage persistence, per-profile dismissal, badge/pulse DOM updates, and the profile/API/data/context card readiness check used by startup, settings, onboarding, DNA import, and panel-open flows. Key exports: setChatNudge, dismissCurrentChatNudge, updateChatNudge Window exports: assigned by chat-window-bindings.js for existing inline handlers and cross-module callbacks.

chat-onboarding.js

Chat-first onboarding helpers. Owns the provider quiz render helper, onboarding progress crumbs, profile/location/cycle/supplement handlers, lab-import CTA behavior, context-card nudge callback, and prompt CTA helper. It receives renderChatMessages, sendChatMessage, closeChatPanel, and nudge callbacks from chat-window-bindings.js through configureChatOnboarding() so it can update the shared chat UI without importing chat UI modules directly. Key exports: configureChatOnboarding, useChatPrompt, requestOnboardingLabImportProvider, startOnboardingLabImport, _renderOnboardCrumbs, _renderProviderQuiz, _countFilledCards, setChatProfileSex, saveChatProfile, saveChatLocation, onboardHeightUnitChanged, saveChatPeriod, addChatSupplement, removeChatSupplement, setProviderQuizBranch, backToProviderQuiz, skipProviderSetup, skipOnboardingExtras, showCycleNoMensesOptions, showCyclePeriodEntry, saveCycleStatus, _updatePeriodBtn, onContextCardSaved Window exports: assigned by chat-window-bindings.js for existing inline handlers and context-cards.js callbacks.

chat-discussion.js

Multi-persona discussion public barrel. Re-exports discussion flow handlers from chat-discussion-flow.js, callback configuration from chat-discussion-callbacks.js, UI compatibility helpers from chat-discussion-ui.js, and persona/thread state helpers from chat-discussion-state.js. Key exports: configureChatDiscussion, getThreadPersonaCount, updateDiscussButton, getCurrentDiscussionState, sendDiscussionUserTurn, restoreDiscussionContinuePrompt, showDiscussContinuePrompt, removeDiscussContinuePrompt, cleanupDiscussionState, startDiscussion, startDiscussionFromPicker, continueDiscussion, endDiscussion Window exports: assigned by chat-window-bindings.js for existing inline handlers and chat-threads.js callbacks.

chat-discussion-flow.js

Multi-persona discussion public user-action handlers. Owns manual-message entry, Continue, Discuss, and picker-start actions. It delegates cleanup/completion to chat-discussion-lifecycle.js, turn execution to chat-discussion-turns.js, and picker/prompt DOM through chat-discussion-ui.js. Key exports: sendDiscussionUserTurn, restoreDiscussionContinuePrompt, showDiscussContinuePrompt, cleanupDiscussionState, startDiscussion, startDiscussionFromPicker, continueDiscussion, endDiscussion Window exports: none directly; chat-discussion.js re-exports the public handlers and chat-window-bindings.js assigns the compatibility handlers.

chat-discussion-lifecycle.js

Discussion cleanup and completion helpers. Owns active-discussion restore, continuation prompt persistence, transient UI cleanup, explicit end lifecycle, original-persona restore, Discuss button/header refresh, and post-round Continue prompt handoff. Key exports: restoreDiscussionContinuePrompt, showDiscussContinuePrompt, cleanupDiscussionState, endDiscussion, finishDiscussionRound Window exports: none directly; chat-discussion-flow.js re-exports the public lifecycle handlers.

chat-discussion-turns.js

Discussion turn execution helpers. Owns continuation round dispatch, first debate turn dispatch, single joined-persona turn dispatch, joined-persona marker insertion, thread-bound discussion state persistence, and round completion handoff. Key exports: runDiscussionContinuation, runSingleDiscussionTurn, runDiscussion Window exports: none.

chat-discussion-callbacks.js

Discussion callback bridge. Owns configureChatDiscussion() and the callback wrappers that let discussion rounds reuse chat-send streaming state, send-button mode, transcript rendering, and typewriter behavior without importing chat-send.js. Key exports: configureChatDiscussion, getChatAbortController, setChatAbortController, renderChatMessages, setSendButtonMode, createDiscussionTypewriter Window exports: none directly; chat-discussion.js re-exports configureChatDiscussion for chat-window-bindings.js.

chat-discussion-round-runner.js

Discussion round execution loop. Owns per-persona iteration, streaming API calls, abort-controller/send-button state, auto-message insertion, typewriter updates, final assistant message persistence, usage footnotes, and modal-aware round error rendering. Key exports: runDiscussionRound Window exports: none.

chat-discussion-round-prompts.js

Discussion round prompt helpers. Owns the default follow-up prompt, initial-analysis prompt, joined-persona prompt, auto-message shape, joined-persona marker shape, and the first-turn versus rebuttal prompt selection logic. Key exports: DEFAULT_DISCUSS_PROMPT, INITIAL_DISCUSS_PROMPT, DISCUSSION_JOIN_PROMPT, hasExistingDiscussionResponses, getDiscussionPromptText, buildDiscussionAutoMessage, buildDiscussionJoinMessage Window exports: none.

chat-discussion-round-request.js

Discussion round API request helpers. Owns lab-context and lens enrichment, active personality prompt assembly, provider/model/web-search snapshots, tagged history messages, assistant message metadata, and usage tracking for multi-persona rounds. Key exports: buildDiscussionRoundRequest, buildDiscussionAssistantMessage, trackDiscussionUsage Window exports: none.

chat-discussion-round-state.js

Thread-bound discussion round persistence helpers. Owns origin-thread activity checks, discussion metadata writes, active-thread repaint handoff, and off-thread history persistence during streaming rounds. Key exports: isRoundThreadActive, persistDiscussionThreadState, renderRoundMessages, saveRoundChatHistory Window exports: none.

chat-discussion-round-view.js

Live discussion round DOM helpers. Owns typing indicators, persona labels, streamed assistant message nodes, final markdown replacement, usage footnotes, and active-thread-only error rendering for multi-persona rounds. Key exports: createDiscussionTypingIndicator, createDiscussionPersonaLabel, appendRoundPersonaLabel, createDiscussionAiMessage, renderFinalDiscussionMessage, appendDiscussionUsageFootnote, renderDiscussionRoundError Window exports: none.

chat-discussion-ui.js

Multi-persona discussion button and continuation prompt controls. Owns the Discuss button visibility/add-persona state and transient state handoff used by continueDiscussion() and endDiscussion(). Re-exports picker helpers from chat-discussion-picker.js so existing discussion flow imports stay stable. Key exports: updateDiscussButton, showDiscussContinuePrompt, removeDiscussContinuePrompt, removeDiscussPersonaPicker, readDiscussPersonaPickerSelection, showDiscussPersonaPicker Window exports: none directly; chat-discussion.js wraps/re-exports public controls and chat-window-bindings.js assigns the compatibility handlers.

chat-discussion-picker.js

Multi-persona discussion picker controls. Owns persona picker markup, active-persona locking, checkbox limiting, and picker selection reads. Key exports: removeDiscussPersonaPicker, readDiscussPersonaPickerSelection, showDiscussPersonaPicker Window exports: none.

chat-discussion-state.js

Multi-persona discussion state helpers. Owns per-thread active discussion detection, history fallback persona collection, current-thread lookup, unique assistant-persona counts, and current-thread discussion lifecycle metadata. Key exports: getThreadPersonaCount, collectDiscussionPersonas, getCurrentThread, clearCurrentDiscussionThreadState, reopenCurrentDiscussionThread, getCurrentDiscussionState Window exports: none directly; chat-discussion.js re-exports the public count/state helpers and chat-window-bindings.js assigns them.

chat-personalities.js

Chat personality storage, personality picker rendering, custom persona editor, AI-powered persona generation, and chat header title/model status. The module imports thread index helpers directly and calls back to window.renderChatMessages?.() for the two places that need to repaint chat content without creating a chat.js import cycle. Key exports: getActivePersonality, getCustomPersonalities, saveCustomPersonalities, getCustomPersonality, getCustomPersonalityText, pickPersonaIcon, setChatPersonality, loadChatPersonality, updateChatHeaderTitle, updateChatHeaderModel, updateSummaryButton, updatePersonalityBar, togglePersonalityBar, generateCustomPersonality, saveCustomPersonality, startNewCustomPersonality, editCustomPersonality, deleteCustomPersonality, autoResizePersonaTextarea, markPersonalityDirty, snapshotPersonalityClean Window exports: assigned by chat-window-bindings.js for existing inline handlers and cross-module callbacks.

chat-history.js

Thread-aware chat message persistence and clearing. The module owns the legacy storage key helper, encrypted/plain thread message load/save, thread metadata updates, saved-summary cleanup, and the clear-history confirmation flow. It uses window.renderChatMessages?.() and window.updateDiscussButton?.() callbacks for repaint/discussion hooks that would otherwise create a chat.js import cycle. Key exports: getChatStorageKey, loadChatHistory, saveChatHistory, clearChatHistory Window exports: assigned by chat-window-bindings.js for existing inline handlers and chat-threads.js callbacks.

chat-thread-search.js

Thread rail message-content search and match highlighting. It owns the debounced per-thread content search, encrypted/plain message reads, result rendering, cache invalidation, jump-to-result behavior, and in-message mark/highlight cleanup while receiving thread key, rail render, and switch callbacks from chat-threads.js. Key exports: configureChatThreadSearch, filterThreadList, invalidateThreadContentCache, jumpToSearchResult Window exports: re-exported and assigned by chat-threads.js for existing inline handlers.

chat-actions.js

Chat message action bar rendering and message-level handlers. The module owns Regenerate, Copy, and context-details toggle behavior while calling back to window.renderChatMessages?.(), window.sendChatMessage?.(), and window.isChatStreaming?.() to avoid importing the streaming/rendering module. Key exports: buildActionBar, regenerateLastMessage, copyMessage, toggleContextDetails Window exports: assigned by the module directly and re-exported by chat.js for existing inline handlers.

chat-icons.js

Shared chat SVG icon strings and icon-button DOM helper extracted from chat.js. Keeps action buttons and streaming send/stop button state changes using the same symbols without keeping SVG construction code in the chat orchestration module. Key exports: CHAT_ICON_COPY, CHAT_ICON_REFRESH, CHAT_ICON_EDIT, CHAT_ICON_X, setIconButtonContent Window exports: none

chat-attestation.js

Venice E2EE attestation display helpers shared by the chat header and assistant-message cost footnotes. Keeps lock glyph, attestation tooltip, and verified/failed mark formatting out of the chat orchestration module. Key exports: attestationTooltip, e2eeLockHTML, e2eeLockFootnote Window exports: none

chat-continuation.js

Response limit detection and automatic continuation wrapper for chat and discussion calls. Centralizes token-headroom constants, incomplete-response heuristics, continuation prompt construction, and usage merging. Key exports: CHAT_RESPONSE_MAX_TOKENS, callChatAPIWithContinuation, isAIResponseTruncated, responseLimitNote Window exports: none

chat-prompt-context.js

Pure prompt/message helpers shared by direct chat sends and multi-persona discussion rounds. Centralizes persona prompt additions, cross-persona API-message tagging, web-search/E2EE system hints, system-prompt assembly, and persisted lens-source serialization. Key exports: buildPersonalityPrompt, buildMultiPersonaInstruction, buildTaggedChatMessages, buildWebSearchHint, buildChatSystemPrompt, serializeLensSources, attachLensSources Window exports: none

chat-summaries.js

Conversation summary generation, saved-summary profile storage, and summary modal actions extracted from chat.js. Key exports: summarizeThread, renderSavedSummaries, viewSavedSummary, deleteSavedSummary, closeSummaryModal, copySummary, downloadSummary, printSummary Window exports: assigned by chat-window-bindings.js for existing inline handlers.

settings.js

Settings modal shell with profile, display, AI, privacy, data, wearables, and Agent Access tabs. Delegates AI provider panels to provider-panels.js, wearables rows to wearables-settings-panel.js, sync controls to settings-sync-panel.js, and Agent Access rendering/actions to settings-agent-access-panel.js. Key exports:
  • openSettingsModal() / closeSettingsModal()
  • initSettingsModelFetch() — fetches model lists for all providers on modal open
  • saveProfileSettings() — saves sex, DOB, location from the Profile section
  • setUnitSystem(system)'EU' | 'US'
Window exports: openSettingsModal, closeSettingsModal, saveProfileSettings, setUnitSystem, setAIProvider, startTour

settings-sync-panel.js

Settings subpanel module for cross-device sync. Owns the Settings → Data sync section, sync setup/restore modal, mnemonic and relay controls, and delegates Settings → Agent Access rendering/actions to settings-agent-access-panel.js. Key exports:
  • renderSyncSection() — renders the Settings → Data sync panel
  • renderMessengerSection() — compatibility export that renders the Settings → Agent Access panel through settings-agent-access-panel.js
  • hydrateSettingsSyncPanel() — loads mnemonic and relay status after the settings modal is painted
  • showSyncSetupModal() — opens the cross-device sync setup wizard directly; used by the dashboard data-protection picker
Window exports: showSyncSetupModal, toggleSync, toggleMnemonicVisibility, copyMnemonic, saveSyncRelay, openRestoreMnemonicDialog, closeRestoreMnemonicDialog, confirmRestoreMnemonic, closeSyncSetup, syncSetupNew, syncSetupRestore, syncSetupBack, syncSetupDoRestore, syncSetupDone, toggleMessenger, toggleMessengerToken, copyMessengerToken, regenerateMessengerToken

settings-agent-access-panel.js

Settings → Agent Access renderer and action handler. It owns the enable/disable state, synced Agent Access metadata, read-only relay token display, local context-key display, wearable daily-series window, and the one-paste setup-command builder for supported MCP clients. Key exports:
  • renderMessengerSection() — renders the Agent Access settings panel, including the target-client chip group
  • buildAgentAccessSetupCommand(client) — builds the private gbsetup_v1_... bootstrap command for hermes, openclaw, claude-code, claude-desktop, cursor, cline, or codex
  • handleMessengerAction(action, el) — delegated action handler for enabling, regenerating, copying secrets, copying setup commands, and series-window changes
  • toggleMessengerToken() / copyMessengerToken() / regenerateMessengerToken() — compatibility actions for legacy window bindings

feedback.js

In-app feedback modal. Key exports:
  • openFeedbackModal() / closeFeedbackModal()
  • submitFeedback() — validates and submits feedback (bug report or feature request)
Window exports: openFeedbackModal, closeFeedbackModal, submitFeedback
Sidebar navigation, profile switcher, and mobile sidebar shell. Key exports:
  • buildSidebar(data) - renders Home, Lenses, Tools, and lab category navigation with marker counts
  • filterSidebar() - filters lab categories and keeps top-level app routes visible during search
  • toggleNavGroup(groupId) - expands/collapses sidebar category groups
  • renderProfileDropdown() / renderProfileButton() - profile switcher UI
  • openRecommendationsFromSidebar() - compatibility helper that routes to the dedicated recommendations page
Window exports: buildSidebar, filterSidebar, toggleNavGroup, renderProfileDropdown, renderProfileButton, toggleMobileSidebar, closeMobileSidebar, openRecommendationsFromSidebar

Layer 6 — Orchestration

views-router.js

Route validation, per-profile last-view persistence, mobile tab sync handoff, and element-anchor scroll preservation. views.js creates the concrete navigator by passing render handlers into createNavigate(). Key exports:
  • createNavigate({ routeHandlers, syncMobileBottomNav }) — builds the app-level navigate() function without importing page renderers
  • getInitialView() — restores the active profile’s last valid route, falling back to dashboard
  • isKnownRoute(route, data?) — validates fixed routes and data-backed lab category routes

lens-pages.js

Dedicated page renderers for Labs, Genome, Body, Insight, and Recommendations. The module receives dashboard, recommendation, and lens shell helper functions from views.js through createLensPageHandlers() so page rendering can move out of the main views module without creating new import cycles. Key exports:
  • createLensPageHandlers(deps) — returns showLabs, showGenomeLens, showBodyLens, showInsightLens, and showRecommendations

dashboard-page-view.js

Dashboard route shell and page-level dashboard orchestration. dashboard-view-composition.js creates it with createDashboardPageView() after wiring dashboard widget renderers, controls, and registry helpers, so the dashboard route can live outside the main compatibility module without importing the widget control layer directly. Key exports:
  • createDashboardPageView(deps) — returns showDashboard(data?); owns the populated dashboard shell, empty welcome/demo state, import drop-zone setup calls, mobile dashboard handoff, focus-card hydration, context-dot hydration, and first-visit tour triggers

dashboard-view-composition.js

Dashboard route, widget, mobile handoff, marker-modal, and lens-shell composition wiring. views.js creates this layer once and keeps compatibility facades, while this module wires dashboard-page-view.js, dashboard-widgets.js, dashboard-widget-controls.js, dashboard-widget-renderers.js, and mobile dashboard dependencies together. Key exports:
  • createDashboardViewComposition(deps) — returns showDashboard, dashboard widget control handlers, recommendation helpers, and dashboard render helpers needed by lens pages and recommendation actions

lens-page-shell.js

Shared lens page chrome and ordering helpers used by views.js and lens-pages.js. The module owns lens headers, reorderable page widget wrappers, per-profile lens widget order persistence, page-section move controls, dashboard add/remove toggles, and the delegated action attributes/listener used by shared lens chrome. Key exports:
  • configureLensPageShell(deps) — injects dashboard widget visibility helpers without importing dashboard modules directly
  • renderLensHeader(title, subtitle, actions?) — renders the shared lens page header
  • renderLensPageWidgets(route, widgets) / renderLensWidget(...) — render reorderable page widgets and their chrome
  • moveLensPageWidget(route, id, direction) — persists per-profile page section order and refreshes the active route
  • lensPageActionAttrs(action, attrs?) — builds escaped data-lens-page-* attributes consumed by the shared delegated click listener

dashboard-widgets.js

Dashboard widget registry and persistence helpers. dashboard-view-composition.js supplies renderer functions from dashboard-widget-renderers.js to createDashboardWidgetRegistry() so widget metadata, defaults, availability gates, per-profile widget preferences, custom marker widget IDs, and visible-entry generation can live outside the main views module. Key exports:
  • createDashboardWidgetRegistry(renderers, opts?) — returns dashboard widget definitions plus preference/order helpers
  • DASHBOARD_WIDGET_SOURCE_ORDER — Lens/source ordering for the widget picker
  • DASHBOARD_WIDGET_DEFAULT_IDS — new-profile dashboard default order
  • DASHBOARD_MANUAL_BIOMETRIC_METRICS and dashboardBiometricSelectionKey() — biometrics widget selection helpers

dashboard-widget-controls.js

Dashboard widget picker, widget chrome, layout actions, and drag/reorder controls. dashboard-view-composition.js creates the control layer with registry, marker, and biometrics dependencies; views.js re-exports the public handlers for existing window.* integrations. Key exports:
  • createDashboardWidgetControls(deps) — returns render helpers and dashboard widget action handlers

dashboard-widget-renderers.js

Dashboard widget body renderers and shared recommendation helpers. dashboard-view-composition.js creates the renderer layer with Light helper and mobile-summary dependencies, then passes the returned renderers to dashboard-widgets.js, dashboard-widget-controls.js, and lens-pages.js. Key exports:
  • createDashboardWidgetRenderers(deps) — returns dashboard body renderers, dashboard context builders, biometrics helpers, custom marker renderer support, and recommendation helper functions

category-view-renderers.js

Category chart card, table, heatmap, and fatty-acid profile renderers. category-page-view.js calls these helpers for category route content, while views.js imports and re-exports them so existing module and window.* integrations stay stable. Key exports:
  • renderChartCard(id, marker, dateLabels) — renders one category/dashboard marker card and registers it for the detail modal
  • renderTableView(cat, dateLabels, categoryKey, dates) / renderHeatmapView(cat, dateLabels, dates, categoryKey) — render category table shells with empty-value add affordances
  • renderFattyAcidsView(cat, categoryKey) / renderFattyAcidsCharts(cat) — render the single-date fatty-acid profile cards and bar chart

category-page-view.js

Category route shell and view-mode orchestration. The module owns showCategory(), switchView(), category card sorting, range-toggle order preservation, chart hydration, and chart-card recommendation loading. It depends on category-view-renderers.js for HTML fragments and keeps views.js as a thin router/compatibility facade. Key exports:
  • showCategory(categoryKey, data?) — renders the category header, view tabs, date/layer controls, chart grid, empty marker affordances, and saved table/heatmap mode
  • switchView(view, categoryKey, btn) — switches between chart, table, and heatmap modes, updates tab ARIA state, destroys stale charts, and rehydrates charts when needed

category-customization.js

Category and marker display override helpers. The module owns category rename, marker rename/revert, the emoji picker, and category icon override persistence while views.js keeps compatibility exports and injects navigation via configureCategoryCustomization(). Key exports:
  • configureCategoryCustomization(deps) — injects the app-level navigate() function without importing views.js
  • renameCategory(categoryKey) / renameMarker(id) / revertMarkerName(id) — persist display-name overrides into importedData.categoryLabels and importedData.markerLabels
  • showEmojiPicker(anchorEl, callback, opts?) — shared category/custom-marker emoji picker used by category views and the marker creation modal
  • changeCategoryIcon(categoryKey) — persists importedData.categoryIcons overrides and mirrors custom marker category icon metadata

views.js

Tool page routing and compatibility exports. Dashboard composition lives in dashboard-view-composition.js; dashboard route shell lives in dashboard-page-view.js; dashboard widget body renderers live in dashboard-widget-renderers.js; recommendation action handlers live in recommendation-actions.js; category route orchestration lives in category-page-view.js; category card/table/heatmap renderers live in category-view-renderers.js; category display overrides live in category-customization.js; the Light page shell lives in light-page-view.js; Light channel pills and drill-down panels live in light-channel-view.js; shared lens page chrome lives in lens-page-shell.js; public navigation and lens page functions remain exported here for compatibility, backed by views-router.js and delegated to route modules where applicable. Key exports:
  • navigate(section, params) — router facade created from views-router.js; calls the appropriate render function
  • showDashboard(data?) - compatibility facade backed by dashboard-view-composition.js and dashboard-page-view.js; renders the customizable widget dashboard whose default widget order starts with Biological Coherence, Current Focus, Current Priority, Quick Markers, Key Trends, Recommended Next Steps, Profile Context, Biometrics Overview, Biological Age, Metabolic Flexibility, and Cycle when available
  • showLabs(data?), showGenomeLens(), showBodyLens(), showInsightLens(data?), showRecommendations(data?) - compatibility facades delegated to lens-pages.js
  • showCategory(categoryKey, data?) - compatibility facade imported from category-page-view.js
  • showLight(data?), renderLightTodayStrip(), renderLightChannelsLive() - compatibility facades imported from light-page-view.js
  • showCompare(data?) / showCorrelations(data?) - focused tool pages
  • Dashboard widget controls: compatibility facades backed by dashboard-widget-controls.js, including openDashboardWidgetPicker(), toggleDashboardOrganizeMode(), resetDashboardWidgets(), clearDashboardWidgets(), addDashboardWidgetFromLens(), and removeDashboardWidgetFromLens()
  • showDetailModal(markerKey, data?) — opens the marker detail modal
Window exports: navigate, showDashboard, showLabs, showGenomeLens, showBodyLens, showInsightLens, showRecommendations, showLight, showCategory, showDetailModal, dashboard widget controls, and recommendation page helpers (openRecommendationDetail, discussRecommendation, saveRecommendation, dismissRecommendation)

recommendation-actions.js

Recommendation modal and action handlers shared by dashboard cards and the Recommendations page. It keeps option-detail rendering, chat prompts, saved/bookmarked state, and dismissed state out of the main route facade. Key exports:
  • createRecommendationActions(deps) — returns openRecommendationDetail(), discussRecommendation(), saveRecommendation(), and dismissRecommendation()

light-page-view.js

Light & Sun page shell, Light Today strip, dashboard Light channel pills, Light session log actions, page widget assembly, and async population of page-only Light workbench sections. It imports shared lens chrome from lens-page-shell.js, condition rendering from light-conditions-now.js, session history from light-sessions-view.js, and channel rows/suggestions from light-channel-view.js. Key exports:
  • showLight(data?) — renders the reorderable Light & Sun page widgets
  • renderLightTodayStrip() — legacy compact Light strip used by embedded/welcome surfaces
  • renderLightChannelsLive() — refreshes the live channel-pills slot after session updates
  • renderDashboardLightChannelPills() / renderLightSessionLogActions() — dashboard widget helper bodies shared through dashboard-widget-renderers.js
  • _expandLightToolsSection() — compatibility handler for expanding the collapsed Light tools placeholder

light-channel-view.js

Light channel pill rows, seven-day sparklines, dashboard-to-Light channel navigation, per-channel drill-down panels, citations, source mix, and channel suggestions. light-page-view.js imports the render helpers; views.js keeps the legacy window._toggleChannelDetail / window._openChannelOnLightPage wiring for inline handlers and dashboard widgets. Key exports:
  • mergeTotals(a, b) — combines sun and device channel totals
  • renderChannelPills(totals7d, totals30d) — renders the interactive Light page channel row and detail slot
  • _toggleChannelDetail(channelKey) / _openChannelOnLightPage(channelKey) — compatibility handlers for expanding channel detail panels
  • renderSuggestion(totals7d) — fallback channel-guidance copy when AI synthesis is unavailable

main.js

Thin module entry point. Imports startup feature side effects, then starts the startup orchestrator. Responsibilities:
  • Imports app-feature-modules.js for startup-loaded feature side effects and window exports
  • Calls startApp() from startup-orchestrator.js
Window exports: none (all exports come from other modules)

app-feature-modules.js

Startup-loaded feature side-effect imports extracted from main.js. This module preserves the previous feature import order while keeping main.js focused on startup orchestration. Foundation/privacy imports are grouped behind app-foundation-modules.js; the Health & Data feature cluster is grouped behind app-health-data-modules.js; the Light & Sun feature cluster is grouped behind app-light-sun-modules.js; the import/export feature cluster is grouped behind app-data-io-modules.js; the AI/chat/settings feature cluster is grouped behind app-ai-interaction-modules.js; the UI shell feature cluster is grouped behind app-ui-shell-modules.js. Key exports: none Window exports: none directly; imported feature modules attach their existing compatibility handlers.

app-foundation-modules.js

Startup-loaded foundation/privacy side-effect imports extracted from app-feature-modules.js. This module preserves the previous schema/constants/utils/pii import order while keeping the top-level feature list as named startup clusters. Key exports: none Window exports: none directly; imported foundation modules attach their existing compatibility handlers.

app-health-data-modules.js

Startup-loaded Health & Data feature side-effect imports extracted from app-feature-modules.js. This module preserves the previous charts/notes/supplements/recommendations/cycle/context/DNA/wearables import order while keeping the top-level feature list easier to scan. Key exports: none Window exports: none directly; imported Health & Data modules attach their existing compatibility handlers.

app-light-sun-modules.js

Startup-loaded Light & Sun feature side-effect imports extracted from app-feature-modules.js. This module preserves the previous Light & Sun import order while keeping the top-level feature list easier to scan. Key exports: none Window exports: none directly; imported Light & Sun modules attach their existing compatibility handlers.

app-data-io-modules.js

Startup-loaded import/export feature side-effect imports extracted from app-feature-modules.js. This module owns the compatibility startup load for data export/import handlers while keeping the top-level feature list as named startup clusters. Key exports: none Window exports: none directly; imported data I/O modules attach their existing compatibility handlers.

app-ai-interaction-modules.js

Startup-loaded AI/chat/settings feature side-effect imports extracted from app-feature-modules.js. This module preserves the previous chat/image/settings/lens/provider helper import order while keeping the top-level feature list easier to scan. Key exports: none Window exports: none directly; imported AI interaction modules attach their existing compatibility handlers.

app-ui-shell-modules.js

Startup-loaded UI shell feature side-effect imports extracted from app-feature-modules.js. This module preserves the previous feedback/tour/touch-tooltip/client-list/views import order while keeping the top-level feature list easier to scan. Key exports: none Window exports: none directly; imported UI shell modules attach their existing compatibility handlers.

startup-orchestrator.js

Startup global wiring and phase ordering. Runs once, installs app-wide startup hooks, and registers the DOMContentLoaded startup sequence. Responsibilities:
  • Sets the _getActiveProfileId startup global
  • Installs lazy EMF window handlers through emf-facade.js
  • Installs app-wide event and refresh wiring through app-event-listeners.js
  • Delegates encryption, backup, and meteo bootstrap to startup-foundation.js
  • Initializes startup profile data
  • Delegates startup service boot and post-profile maintenance to startup-maintenance.js
  • Delegates wearable/OpenRouter callback routing to startup-oauth-callbacks.js
  • Delegates first-render UI bootstrap to startup-ui.js
Key exports:
  • startApp() — idempotently installs startup globals/listeners and registers the startup sequence
Window exports: _getActiveProfileId

emf-facade.js

Lazy window facade for the EMF assessment module extracted from main.js. It installs async window.* handlers for EMF editor and assessment actions, then imports emf.js on first use and replaces the facade handlers with the real module exports. Key exports:
  • EMF_LAZY_WINDOW_FUNCTIONS — list of EMF window handlers covered by the lazy facade
  • installEMFLazyFacade() — installs lazy handlers during app startup
Window exports: EMF handler names from EMF_LAZY_WINDOW_FUNCTIONS

startup-foundation.js

Blocking startup foundation extracted from main.js: encryption unlock, decrypted meteo config cache hydration, cross-tab broadcast setup, and folder backup handle restoration. Key exports:
  • initializeStartupFoundation() — runs the blocking security/storage bootstrap before wearable services or profile data load
Window exports: none

startup-profile.js

Profile startup bootstrap extracted from main.js: legacy v1 profile migration, encrypted profile-cache warmup, active-profile imported-data load, and unit/sex/range/DOB display state. Key exports:
  • initializeProfileData() — creates the default profile for legacy users, migrates old imported data through encrypted storage, warms the profile cache, sets state.currentProfile, and loads active imported data before OAuth callbacks can persist profile-scoped data
  • applyProfileDisplayState() — applies saved units/range mode plus sex/DOB metadata to state and the startup toggle controls
Window exports: none

startup-oauth-callbacks.js

Startup OAuth callback routing extracted from main.js. Wearable callbacks run first after profile load; if they do not consume the URL code, OpenRouter OAuth handles the same ?code=/state pair. Key exports:
  • handleStartupOAuthCallbacks() — delegates wearable OAuth callback handling, exchanges OpenRouter codes with state verification, stores the OpenRouter key, switches the active AI provider, and opens chat after init
Window exports: none

startup-maintenance.js

Non-blocking startup maintenance extracted from main.js: wearable runtime config/scheduler boot, sun session self-heal, light-device preset hydration, and legacy biometrics migration. Key exports:
  • initializeStartupServices() — starts wearable runtime config loading and scheduler setup before profile data loads
  • runPostProfileStartupMaintenance() — schedules post-profile maintenance jobs without blocking OAuth callbacks or first render
Window exports: none

startup-ui.js

First-render UI bootstrap extracted from main.js: profile display state, theme, footer version, sidebar, sync indicator, initial navigation, deferred sync/catalog warmup, changelog, startup nudges, deferred settings/chat opens, header chrome, chat attachment handlers, and file-picker import binding. Key exports:
  • renderStartupUI() — runs the initial UI render and schedules non-blocking startup UI work after profile/OAuth startup completes
Window exports: none

app-event-listeners.js

App-wide browser event wiring that used to live in main.js: modal wheel guards, backdrop click handling, global role-button keyboard activation, Escape/Tab modal behavior, app shortcuts, and the registerRefreshCallback() bridge back into navigate(state.currentView || 'dashboard'). Key exports:
  • installGlobalEventListeners() — idempotently installs document-level modal, keyboard, shortcut, and click handlers
  • registerAppRefreshCallback() — registers the data refresh callback that rebuilds sidebar state and re-renders the active route

tour.js

Generic spotlight tour engine plus the app tour and cycle tour configurations. Key exports:
  • runTour(steps, storageKey, auto) — generic engine: creates #tour-overlay, #tour-spotlight, #tour-tooltip; filters steps with missing targets; navigates with Back/Next/Skip/Done
  • startTour(auto) — launches the 7-step app tour (auto=true checks completion flag first)
  • startCycleTour(auto) — launches the 8-step cycle-specific tour
  • endTour() — removes tour DOM elements, stores completion flag
  • CYCLE_TOUR_STEPS — array of 8 step configs for the cycle tour
Window exports: startTour, startCycleTour, endTour

changelog.js

What’s New modal triggered on version bump so users see what changed after each PWA update. Key exports:
  • APP_VERSION — number matching the SW cache version (e.g., 53)
  • openChangelog(showAll) — renders and shows the modal; showAll=true shows all entries, false shows latest 3
  • closeChangelog() — hides modal, marks current version as seen in localStorage
  • maybeShowChangelog() — auto-trigger: shows modal if labcharts-changelog-seen !== APP_VERSION
Window exports: openChangelog, closeChangelog, maybeShowChangelog

client-list.js

Client List modal for managing multiple profiles. Key exports:
  • openClientListModal() / closeClientListModal() — modal lifecycle
  • renderClientList(query?, sortBy?) — renders searchable, sortable profile list with inline create/edit form
  • Per-profile actions: archive, flag, pin, delete, per-client export
Window exports: openClientListModal, closeClientListModal, createClientInline, saveClientInline, toggleArchiveClient, toggleFlagClient, togglePinClient, deleteClient, exportClientJSON

recommendations.js

Supplement, lifestyle, light-device, and EMF affiliate recommendations driven by a lazy-loaded catalog. Region-aware: products + URLs + coupons are filtered by user country via a single hierarchy chain (CZ → EU → INTL etc.). The global Recommendations page is rendered by lens-pages.js, while dashboard-widgets.js registers the dashboard surface and dashboard-widget-renderers.js owns its dashboard renderer; this module owns catalog loading, slot rendering, disclosure state, product filtering, and DNA/wearable slot helpers. Key exports:
  • loadCatalog() — lazy-loads data/recommendations.json on first call
  • getUserRegion() — derives region code (CZ/SK/DE/AT/EU/US/INTL) from profile country
  • regionLookupChain(region) — returns the lookup chain (most-specific → INTL); used by both product visibility filter and per-region map resolution
  • getProductsForSlot(catalog, slotKey, region) — region-filtered products
  • _resolveCouponForRegion, _resolveHomepageForRegion, _resolveProductUrlForRegion — pick from per-region maps using the chain
  • _addUTMParams(url, content, campaign) — partner-dashboard attribution
  • renderRecommendationSection(slotKey, opts) — renders the “What can help” section in the detail modal
  • renderEMFMeterRecs(catalog, opts) / renderEMFMitigationRecs(catalog, tags, opts) — EMF panel surfaces
Window exports: none (called via imports from views.js, chat.js, context-cards.js)

crypto.js

Data encryption at rest and cross-tab sync. Key exports:
  • encryptedSetItem(key, value) / encryptedGetItem(key) — AES-256-GCM via PBKDF2 passphrase
  • getEncryptionEnabled() / setEncryptionEnabled(bool) — encryption toggle
  • validatePassphrase(p) — checks 4 strength rules (8+ chars, lowercase, uppercase, special), returns { valid, message }
  • broadcastDataChanged(profileId) — BroadcastChannel message for multi-tab sync
  • SENSITIVE_PATTERNS — array of localStorage key pattern strings that get encrypted
Window exports: setEncryptionEnabled, changePassphrase, exportBackup, importBackup

backup.js

IndexedDB auto-backup, folder backup via File System Access API, and backup restore. Extracted from crypto.js. Key exports:
  • scheduleAutoBackup() — debounced 60s trigger; saves up to 5 snapshots to IndexedDB + writes to folder backup if configured
  • buildBackupSnapshot() — captures all importedData + per-profile preferences
  • loadBackupSnapshots() — async; populates the Backup & Restore section with IndexedDB snapshots
  • restoreAutoBackup(id) — writes snapshot to localStorage, reloads
  • saveFolderBackup(snapshot) — writes getbased-backup-latest.json + daily dated snapshot to user-selected folder via File System Access API
Window exports: restoreAutoBackup, pickBackupFolder, showBackupReminder

lab-context.js

Central AI context serializer. Extracted from chat.js. Key exports:
  • buildLabContext() — serializes all lab entries + all 9 context cards + interpretiveLens + contextNotes + cycle data + notes into a structured AI context string
Window exports: buildLabContext

markdown.js

Block-aware markdown parser for chat rendering. Extracted from chat.js. Key exports:
  • renderMarkdown(text) — block-aware parser: headings, lists, code blocks, HR, paragraphs + inline formatting
Window exports: renderMarkdown

provider-panels.js

AI provider settings behavior for the settings modal. Owns provider selection, key validation flows, provider model dropdown refresh, balances, and local-model advisor wiring. Imports provider-panel-renderers.js for provider-specific Settings → AI panel markup. Key exports:
  • renderAIProviderPanel(provider) — returns the active provider configuration panel HTML
  • switchAIProvider(provider) — persists provider choice and refreshes the settings panel
  • Provider key/model helpers for Venice, OpenRouter, Routstr, PPQ, Custom API, and local OpenAI-compatible servers
Window exports: provider panel handlers used by Settings HTML onclick attributes, including imported Routstr wallet handlers from provider-wallet-panels.js

provider-panel-renderers.js

Provider-specific Settings → AI panel markup for OpenRouter, Routstr, Venice, PPQ, Custom, and Local AI. Keeps static panel HTML and cached-model select markup separate from provider validation, balance polling, and local-model advisor behavior. Window exports: none directly. renderAIProviderPanel() is re-exported and attached to window by provider-panels.js.

provider-wallet-panels.js

Routstr/Cashu wallet UI and node funding actions used inside the Routstr provider panel. Extracted from provider-panels.js so wallet deposit, withdrawal, seed backup/restore, mint switching, and node deposit/refund behavior are isolated from general provider key/model rendering. Key exports:
  • configureRoutstrWalletPanels(callbacks) — injects provider-panel callbacks without introducing a circular module import
  • routstrWalletActionButtons(active) / buildRoutstrNodeActions(nodeUrl, hasKey, active) — render the wallet and node action controls used by the Routstr panel
  • doRoutstrWalletFund() / doRoutstrNodeDeposit() / doRoutstrNodeWithdraw() / withdraw and restore helpers — Cashu wallet and Routstr node action handlers
Window exports: exported through provider-panels.js for legacy inline handlers.

provider-qr.js

Shared QR-code loader for provider payment surfaces. Key exports:
  • ensureQRCode() — lazy-loads /vendor/qrcode-generator.js and returns the global qrcode factory

lens.js

Knowledge Base / Interpretive Lens — RAG endpoint config + multi-query rewrite + dedicated modal. Two backends under one UI: 'in-browser' (transformers.js + OPFS via lens-local.js) and 'external-server' (user-configured URL + Bearer key). Key exports:
  • hasLens() — true when a lens is configured AND has indexed content
  • queryLens(hint, opts?) — single-query retrieval; routes to in-browser worker or remote server based on backend
  • queryLensMulti(hint, opts?) — multi-query orchestrator: rewrites the query into N=3 paraphrases via the active LLM, fans out to queryLens for each, fuses results with reciprocal-rank scoring (k=60). Falls back to single-query when no AI provider, when rewrite fails, when query is <3 words, or when the toggle is off
  • getLensConfig() / saveLensConfig(partial) / getLensKey() / saveLensKey(key)
  • getLensSummary() — synchronous status object {configured, backend, displayName, docCount, chunkCount, multiQueryOn, aiAvailable} used by the dashboard KB row
  • openKnowledgeBaseModal() / closeKnowledgeBaseModal() — dedicated modal that wraps renderCustomLensSection(). Replaces the previous Settings → AI inline section
  • buildLensSnippet(chunks, sourceName) — formats retrieved chunks for AI prompt injection
  • testLensConnection() / clearLensCache()
  • renderCustomLensSection() — full settings markup (URL/key inputs, backend toggle, library picker, ingest UI, multi-query toggle); rendered inside the KB modal
  • _resetRewriteCache() / _fuseChunksRRFForTest() / _dedupeQueriesForTest() — test surface
Window exports: hasLens, queryLens, queryLensMulti, buildLensSnippet, testLensConnection, clearLensCache, openKnowledgeBaseModal, closeKnowledgeBaseModal, plus all the in-modal save/toggle/library handlers

wearable-adapters.js

Canonical wearable-metric registry + per-vendor adapter registry. Source of truth for OAuth client IDs, redirect URIs, scopes, and metric → endpoint mappings. Key exports:
  • ADAPTERS — array of {id, displayName, authType, oauth?, apiHost?, metrics, accountInfo?} for the 8 supported sources (Oura / Withings / Ultrahuman / WHOOP / Fitbit / Polar / Apple Health / manual)
  • CANONICAL_METRICS{id: {label, sub, unit, worseWhen}} for every metric the strip can render
  • DEFAULT_METRIC_ORDER — preferred order when multiple sources contribute
  • adapterById(id) / visibleAdapters(connectedIds) / adapterSupportsMetric(adapterId, metricId) / metricsForSources(sourceIds) / canonicalMetric(id)
  • applyOAuthOverrides(map) — merges a {adapterId: clientId} map into the runtime override store; called from loadWearableRuntimeConfig()
  • getOAuthClientId(adapterOrId) — returns the runtime override if set, falling back to the adapter’s hardcoded oauth.clientId. Every consumer reads through this helper so self-host overrides apply uniformly
  • _resetOAuthOverrides() — test surface
Window exports: none

wearables-connect.js

Connect/disconnect/backfill orchestration. OAuth dispatch table, scheduled stale-source sync, runtime config bootstrap. Key exports:
  • OAUTH_DISPATCH{adapterId: {begin, isCallback, complete, withFreshToken, fetchAccountInfo, fetchRange, displayName, postConnect?, commitAfterWrite?}} — generic OAuth orchestration table
  • beginConnectOAuth(adapterId) — kicks off the auth flow; reads clientId via getOAuthClientId(adapter)
  • handleOAuthCallbackOnLoad() — runs via startup-oauth-callbacks.js init; reads pending.clientId from sessionStorage (set at begin time, NOT from runtime config)
  • getConnection(adapterId) / listConnectedSources() / disconnectAdapter(adapterId)
  • syncNow(adapterId, opts?) / forceBackfill(adapterId, days)
  • initWearableScheduler() — visibility-change + 6h interval poll; awaits runtimeConfigReady() before first sync to prevent race against override fetch
  • loadWearableRuntimeConfig() — POSTs {wearable_runtime_config: true} to /api/proxy, applies the returned *_CLIENT_ID overrides via applyOAuthOverrides. Memoized as a promise raced against a 1.5s soft timeout
Window exports: none

Wearables vendor adapters

Each connected source ships as a pair: wearables-<vendor>.js (read API) + wearables-<vendor>-auth.js (OAuth dance). Apple Health and Manual skip the auth half — Apple is file-import only, Manual is fully local. Tokens never leave the device; sync-payload.js strips wearableConnections from the synced payload (and again on pull as defense-in-depth). OAuth2 (server-side secret): wearables-oura.js + …-oura-auth.js, …-withings.js + …-withings-auth.js, …-ultrahuman.js + …-ultrahuman-auth.js, …-polar.js + …-polar-auth.js. Secrets live in .env.local + /api/proxy. OAuth2 PKCE (no secret): wearables-whoop.js + …-whoop-auth.js, wearables-fitbit.js + …-fitbit-auth.js. Code verifier + S256 challenge per the IETF spec. No OAuth: wearables-apple-health.js (file-import — parseAppleHealthXml reads the export.xml from a Health zip), wearables-manual.js (logManualMetric / logManualBP + MANUAL_TAGS whitelist for what users can log by hand). L1 storage + summary derivation: wearables-store.js (per-profile IndexedDB at labcharts-wearables-{profileId}; two-phase upsertDailyBatch), wearables-summary.js (L2 derivation; vendor sources read a 90-day window, while sparse manual rows read all history so older hand-entered BP/pulse/weight entries stay visible; write gate — 5% d7 shift / trend flip / 14d force-refresh / source flip / metric removal triggers). UI surface: wearables.js (dashboard strip, source picker, reorder mode, empty-card manual-log forms, and legacy window facade), wearables-detail-modal.js (metric detail modal, Chart.js series, manual-entry list/add/delete, focus trap, EMF sleep nudge), wearables-formatters.js (shared value/date formatting), wearables-manual-form-ui.js (shared manual chip/note form snippets), wearables-settings-panel.js (Settings → Wearables rows, connect/sync/backfill/disconnect actions, Apple Health file-import UI, manual-source counts/delete action, strip visibility toggle), brand-assets.js (per-vendor logo registry — iconLight/Dark for in-app rows, signInLight/Dark for landing-site Connect buttons, mono SVG fallback while a vendor logo is gated).

Knowledge Base in-browser stack

lens.js is the dispatcher; the 4 sibling lens-local-* modules are the in-browser implementation. The dispatcher routes between in-browser and external-server backends per the user’s selection.
  • lens-local.js — main-thread shell: spawns a module worker, posts ingest/query/list/activate/delete messages, exposes a Promise-based API to lens.js.
  • lens-local-worker.js — module worker. Loads transformers.js (currently from jsdelivr — see update-vendor.sh notes), runs WebGPU embedding when available with WASM fallback, owns OPFS persistence (FileSystemSyncAccessHandle), library CRUD, MMR re-rank (λ named MMR_LAMBDA), per-library model selection from a 4-model catalog.
  • lens-local-utils.js — pure helpers: chunkText, mmrSelect, hashing, vector-pack helpers. Importable from both threads.
  • lens-local-parsers.js — main-thread document parsers (PDF via pdfjs-loader.js, DOCX via mammoth, ZIP via JSZip, plus plain text/markdown/CSV).

Other recently-extracted modules

Not separately documented because their exports are best read from source — kept thin on purpose.
  • chat-images.js — image attachment lifecycle (getPendingAttachments, clearAttachments, etc.). Owns the _pendingAttachments queue. Extracted from chat.js in v1.21.9.
  • chat-threads.js — conversation thread CRUD, list rendering, autoname, onChatSaved debounce trigger. Extracted in v1.21.9.
  • markdown.js — XSS-safe markdown rendering (applyInlineMarkdown for spans, renderMarkdown for full blocks). 34 dedicated XSS test assertions.
  • lab-context.js — lab-data → AI-prompt context assembly; memoized via fingerprint that includes wearable summary, change history, and all 9 context cards.
  • provider-panels.js — Settings → AI provider behavior, validation, balances, dropdown refresh, globals.
  • provider-panel-renderers.js — Settings → AI per-provider panel markup (Venice / OpenRouter / Routstr / PPQ / Local AI / Custom).
  • pdfjs-loader.js — cached dynamic import of vendored pdf.js ESM. Pins isEvalSupported: false defense-in-depth on every getDocument call.
  • sun-body-silhouette.js — anatomical sun-session body-region picker, stock-figure region-map hit testing, and async selection overlay. Re-exported by sun.js for compatibility.
  • sun-active-session.js — quick-log/start modal, live active-session ticker, Simpson live-dose integration, and active-session compatibility handlers. Dependency-injected from sun.js so persistence and hydration stay in the owner module.
  • sun-session-ui.js — saved sun-session row/detail rendering, detailed past-session modal, channel chips, delete, and edit-duration UI. Dependency-injected from sun.js so storage, hydration, and dose math stay in the owning module.
  • light-device-setup-modal.js — add-device preset picker, custom-device form, and URL/photo AI spec extraction. Dependency-injected from light-devices.js so device persistence and channel defaults stay in the owning module.
  • light-device-session-modal.js — log/start light therapy device session modal; dependency-injected from light-devices.js so session persistence and active timer state stay in the owning module.
  • light-env-audits.js — saved Light Environment snapshots, audit comparison renderer, and audit-specific window handlers. Dependency-injected from light-env.js so rooms/screens remain owned by the environment module.
  • light-env-evening.js — canonical room after-sunset exposure helpers; normalizes legacy boolean room rows to numeric eveningHoursAfterSunset while leaving screen eveningUseAfterSunset numeric.
  • light-tool-camera.js / light-tool-camera-modals.js — shared camera lock, row-banding, aiming guides, lux calibration, and the camera-backed Light tool modal flows. Dependency-injected from light-tools.js so measurement persistence stays in the owning module.
  • supplement-warnings.js / food-contaminants.js — keyword scanners that build “harm flag” lists for the AI context.
  • emf.js — Baubiologie SBM-2015 EMF assessment as a sub-module of the Environment context card.
  • sync.js — Public sync entry/barrel; exports sync lifecycle, actions, diagnostics, relay, identity, and cutover APIs while delegating dependency wiring to sync-configure.js.
  • sync-configure.js — Sync subsystem dependency wiring; configures relay health, push/pull/subscription/tombstone/diagnostic/UI/action/recovery/reconcile modules and browser window bindings.
  • sync-push.js — outbound profile push path, in-flight push watchdog, Phase 2 drift auto-revert, and Evolu profileData insert/update.
  • sync-push-deltas.js — push-side per-row delta planning/application; walks DELTA_ARRAYS/MAPS/SCALARS before blob writes, advances snapshots after onComplete, and records delta telemetry.
  • sync-pull.js — inbound pull orchestration and row loop; delegates one-time cleanup, active-profile UI refresh, and rebroadcast scheduling to helper modules.
  • sync-pull-merge.js — pull row recovery/dedupe, importedData blob/per-row merge, wearable-token preservation, metadata-only genetics blob protection, and profile metadata merge helpers.
  • sync-pull-maintenance.js — pull-path one-time migrations such as stale -sync-hash localStorage cleanup.
  • sync-pull-active-refresh.js — active-profile in-memory update, migration, chat reload, sidebar rebuild, current-view refresh, toast, and sync-applied event after a pull.
  • sync-pull-rebroadcast.js — pull-side rebroadcast gating, budget checks, push-pending guard, and delayed active-profile push scheduling.
  • data-merge.js — importedData blob merge helpers, shared record freshness comparison, tombstone state merge, and sync-aware array mutation helpers (appendImportedArrayItem, replaceImportedArrayItem, deleteImportedArrayItem, deleteImportedArrayItems, clearImportedArray) used by feature modules so identity edits/deletes record the right tombstones before the next sync push.
  • sync-cutover.js — readiness-gated Phase 2 lean-sync cutover enable/disable bridge over payload flags and delta readiness.
  • sync-delta.js — per-row CRDT delta facade; config fan-out, apply helpers, and compatibility re-exports for planners, snapshots, merge, telemetry, and readiness.
  • sync-delta-planners.js — push-side planner facade; configures shared planner dependency context and compatibility re-exports for array/map/scalar planners.
  • sync-delta-planner-context.js — shared Evolu/itemRow query dependency access for push-side delta planners.
  • sync-delta-array-planner.js — push-side array planner; diffs array items against snapshots and emits itemRow insert/update/tombstone ops with tombstone-storm protection.
  • sync-delta-map-planner.js — push-side keyed-map planner; diffs keyed objects against snapshots, preserves original raw keys in payloads, and guards genetics SNP empty-map races.
  • sync-delta-scalar-planner.js — push-side scalar planner; handles singleton fields, most-recent canonical row selection, and null-transition tombstones.
  • sync-delta-snapshot.js — delta snapshot keying, reads, plannedAt-gated writes, and snapshot clear helpers.
  • sync-delta-merge.js — pull-side itemRow grouping and merge-shape dispatch into importedData after blob merge.
  • sync-delta-merge-shapes.js — pull-side row overlay facade; compatibility re-exports for array/map/scalar merge helpers.
  • sync-delta-row-codec.js — shared itemRow payload decoder for pull-side delta merge helpers, including gzip envelope handling.
  • sync-delta-array-merge.js — pull-side array row overlay helper; owns item tombstones, nested-path writes, dedupe, composite caps, and pull delta telemetry.
  • sync-delta-map-merge.js — pull-side keyed-map row overlay helper; owns raw-key preservation, synth-id tombstones, proto guards, nested-path writes, and pull delta telemetry.
  • sync-delta-scalar-merge.js — pull-side scalar row overlay helper; owns singleton LWW selection, null tombstones, dotted-path writes, genetics SNP preservation, and pull delta telemetry.
  • sync-delta-registry.js — delta registry facade; compatibility re-exports for surfaces, per-surface config, and identity helpers.
  • sync-delta-surfaces.js — DELTA_ARRAYS/MAPS/SCALARS importedData surface lists for per-row sync.
  • sync-delta-surface-config.js — per-surface itemId/keyId overrides for array/map delta planners and pull overlays.
  • sync-delta-id.js — shared per-row identity helpers, stable hash, allowlist guard, and proto-pollution key rejection.
  • sync-delta-observability.js — delta observability facade; compatibility re-exports for telemetry, pull snapshots, readiness, and injected Evolu query access.
  • sync-delta-observability-context.js — shared Evolu/itemRow query dependency access for delta observability helpers.
  • sync-delta-pull-snapshot.js — in-memory pull-side row-count snapshots for Sync Diagnose delta replication checks.
  • sync-delta-telemetry.js — per-push delta telemetry storage, aggregation, reset, and latest pull-snapshot reporting.
  • sync-delta-readiness.js — Phase 2 cutover readiness checks across array/map/scalar delta surfaces.
  • sync-apply.js — inbound sync apply helpers for AI provider settings and display prefs, with compatibility re-exports for chat apply helpers.
  • sync-chat-apply.js — inbound chat thread/message application, chat delete tombstones, and local freshness locks that protect just-edited local chat from stale remote rows.
  • sync-tombstones.js — remote profile delete propagation helpers, inbound tombstone application, batched-delete quarantine, and apply/reject pending tombstone actions for Settings.
  • sync-messenger.js — Agent Access token/context-key helpers, owner-bound encrypted context writes, revoke helpers, and debounced lab-context push to the Agent Access context gateway.
  • sync-environment.js — relay URL selection, relay connectivity probing, and browser capability checks for Evolu sync.
  • sync-identity.js — BIP-39/QR lazy loaders plus mnemonic read/restore helpers for Evolu owner identity.
  • sync-diagnostics.js — Evolu diagnostics facade; stable exports for configuration, row snapshots, and copy-text formatting.
  • sync-diagnostics-context.js — Injected Evolu/query/subscription state accessors for sync diagnostics.
  • sync-diagnostics-snapshot.js — Evolu row diagnostics snapshot assembly, including live/tombstone rows, active imported counts, delta telemetry, and lean-sync readiness.
  • sync-diagnostics-text.js — Plain-text Sync Diagnose copy formatting for support/debug handoff.
  • sync-diagnose-ui.js — Sync Diagnose modal lifecycle, copy snapshot handling, and compatibility re-exports for Diagnose action handlers.
  • sync-diagnose-render.js — Pure Sync Diagnose panel/table markup for identity, relay health, relay storage, delta telemetry, lean-sync readiness, and local Evolu rows.
  • sync-diagnose-actions.js — Sync Diagnose action facade; keeps the public imports stable while delegating to focused action modules.
  • sync-diagnose-actions-context.js — Shared injected dependencies for Diagnose actions, including sync enable/restore, push, cutover toggles, and modal refresh.
  • sync-diagnose-relay-actions.js — Sync Diagnose relay storage refresh and self-serve compaction handlers.
  • sync-diagnose-identity-actions.js — Sync Diagnose identity rotation modal, BIP-39 mnemonic generation, QR display, copy fallback, and local apply flow.
  • sync-diagnose-cutover-actions.js — Sync Diagnose telemetry reset plus lean-sync enable/backfill/disable handlers.
  • sync-actions.js — user-triggered sync actions, all-profile initial push, and compatibility re-exports for save hooks and storage cleanup.
  • sync-save-hooks.js — save/chat/profile-metadata debounce hooks, AI-setting push scheduling, per-profile timer cleanup, and profile importedData reads for initial push.
  • sync-storage-cleanup.js — emergency sync storage compaction; clears model-list caches, trims oversized changeHistory, saves the active profile, and reports freed bytes.
  • sync-ui.js — header sync badge, popover rendering, status subscription, and activity-log copy helpers.
  • sync-payload-collectors.js — Local AI/chat/display settings collection for sync payloads, including synced setting allowlists and chat thread tombstone keys.
  • sync-payload.js — Evolu wire-payload helpers; outbound envelope assembly, Phase 2 cutover payload shape, gzip envelope handling, inbound payload parsing, and local-only data stripping for wearable credentials and SNP map rows.
  • sync-relay-health.js — Evolu relay helper boundary; client-side quota estimate, signed /self/owner-storage and /self/compact-owner calls, and push-landed health verdict for the silent-reject detector.
  • sync-state.js — in-memory sync status pub-sub, header badge display-state derivation, recent sync activity ring buffer, and per-profile rebroadcast budget guard.