Module Reference
All 25 modules live under js/. 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 includebiochemistry,hormones,electrolytes,lipids,hematology,differential,thyroid,proteins,vitamins,diabetes,inflammation,fattyAcids,calculatedRatios, and othersUNIT_CONVERSIONS— keyed by"category.markerKey":{ type: 'multiply', factor, unit }for EU→US conversionsOPTIMAL_RANGES— keyed by"category.markerKey":{ optimalMin, optimalMax, optimalMin_f?, optimalMax_f? }PHASE_RANGES— keyed by"category.markerKey":{ menstrual: { min, max }, follicular: {...}, ovulatory: {...}, luteal: {...} }— currently covershormones.estradiolandhormones.progesteroneCHIP_COLORS— status → CSS color stringMODEL_PRICING— AI model pricing metadata, keyed by provider/model
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 personalitiesCHAT_SYSTEM_PROMPT— the base system prompt string injected into all AI chat requestsCOUNTRY_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:js{ 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 insertionhashString(str)— djb2 hash, returns integergetStatus(value, refMin, refMax)—'normal'|'high'|'low'|'missing'. Returns'normal'when refs arenullformatValue(value, unit)— formats a numeric value with appropriate decimal placesshowNotification(message, type)— toast notification ('info'|'success'|'error'|'warning')showConfirmDialog(message)— returnsPromise<boolean>, styled confirm dialoglinearRegression(points)—{ slope, intercept, r2 }from[{ x, y }]arrayhasCardContent(obj)— generic empty-card gate: returnstrueif any field has content (strings non-empty, arrays non-empty,notetrimmed). Used bybuildLabContext()for 7 context card gates
Window exports: showNotification, showConfirmDialog, setDebugMode, setPIIReviewEnabled, hasCardContent
Layer 2 — Core Services
theme.js
Theme management and Chart.js color helpers.
Key exports:
getTheme()/setTheme(theme)/toggleTheme()—'dark'|'light';setThemesetsdata-themeon<html>getChartColors()— reads live CSS custom properties and returns a chart color config objectformatDateLabel(dateStr)— formats ISO date for chart x-axisgetTimeFormat()/setTimeFormat(fmt)—'24h'|'12h', stored inlabcharts-time-formatformatTime(timeStr)— formats a 24h time string for display using the active formatparseTimeInput(input)— accepts both'14:30'and'2:30 PM', always returns 24h format
Window exports: toggleTheme
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 ongetAIProvider()callAnthropicAPI(opts)— Anthropic Messages API with SSE streamingcallOpenRouterAPI(opts)— OpenRouter viacallOpenAICompatibleAPIcallVeniceAPI(opts)— Venice AI viacallOpenAICompatibleAPIcallOllamaChat(opts)— Ollama/api/chatwith newline-delimited JSON streamingcallOpenAICompatibleAPI(endpoint, key, model, providerName, opts, extraHeaders)— shared OpenAI-format helpergetAIProvider()/setAIProvider(provider)—'anthropic'|'openrouter'|'venice'|'ollama'hasAIProvider()— returnstrueif any provider is configured; gates all 7 AI featuresgetAnthropicModel(),getOpenRouterModel(),getVeniceModel(),getOllamaMainModel()fetchAnthropicModels(),fetchOpenRouterModels(),fetchVeniceModels()— dynamic model listsgetModelPricing(modelId)— checks dynamic OpenRouter pricing cache, falls back toMODEL_PRICINGOPENROUTER_CURATED— whitelist of latest-gen medically capable models (prefix-matched)OPENROUTER_EXCLUDE— blocklist filtering codex/audio/image/oss variants
Window exports: none (called via normal imports from feature modules)
Layer 3 — Data & Profile
profile.js
Profile lifecycle and settings persistence.
Key exports:
profileStorageKey(profileId, suffix)— buildslabcharts-{profileId}-{suffix}loadProfiles()/saveProfiles()— profile index CRUDswitchProfile(profileId)— loads importedData from localStorage for the given profiledeleteProfile(profileId)— removes all keys for the profilemigrateProfileData()— upgrades legacy field formats on load (oldsleepCircadian→sleepRest+lightCircadian, oldfieldExperts/fieldLens→interpretiveLens, initializes missing fields withnull)getProfileLocation()/setProfileLocation(country, zip)— country+ZIP storagegetLatitudeFromLocation()— looks up latitude band fromCOUNTRY_LATITUDESrenderProfileDropdown()— 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-clonesMARKER_SCHEMA, merges custom markers, applies sex-specific ranges, populatesvalues[]arrays fromimportedData.entries, computes ratios + PhenoAge + cycle phases, applies unit conversion. Returnsdataobject with{ dates[], dateLabels[], categories, phaseLabels? }saveImportedData()— persistsstate.importedDatato localStorage (or encrypted store), triggers backupbuildMarkerReference()— compact JSON of all known markers for AI system prompts (PDF import)filterDatesByRange(data)— appliesstate.dateRangeFilterto dates + values arrays in-placegetEffectiveRange(marker)— returns{ refMin, refMax }respectingstate.rangeModegetEffectiveRangeForDate(marker, dateIndex)— phase-aware range lookup; falls back togetEffectiveRange()getPhaseRefEnvelope(marker)— widest span across all cycle phases for chart ref bandsregisterRefreshCallback(fn)— registers the refresh function frommain.jsdetectTrendAlerts(data)— sudden-change (25% of ref range, 2+ values) and linear-regression (slope >0.02, R²>0.5 for 4+ points) alertsgetAllFlaggedMarkers(data)— markers >50% of reference range width past their boundarygetFocusCardFingerprint()— djb2 hash of all entries + all 9 context cards + sex + DOB
Window exports: saveImportedData, clearAllData (via export.js), filterDatesByRange
pii.js
Two-path PII obfuscation for PDF text before AI submission.
Key exports:
sanitizeWithOllama(pdfText)— preferred path: sends raw text to local Ollama PII model, returns sanitized textobfuscatePDFText(pdfText)— regex fallback: label-based + pattern-based replacement, returns{ obfuscated, original, replacements }getOllamaPIIModel()/setOllamaPIIModel(model)— separate PII model configshowPIIDiffViewer(original, obfuscated, replacements)— debug diff viewer (requireslabcharts-debugflag)
Window exports: setOllamaPIIModel
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 applieddestroyAllCharts()— destroys all instances instate.chartInstancesto prevent memory leaks- Four Chart.js plugins (registered globally):
refBandPlugin— shaded reference range bandoptimalBandPlugin— green dashed optimal range bandnoteAnnotationPlugin— yellow dot annotations at note dates with hover tooltipssupplementBarPlugin— colored timeline bars for supplementsphaseBandPlugin— 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 ifdateprovidedsaveNote()— saves the current editor content toimportedData.notesdeleteNote(date)— removes a note by date
Window exports: openNoteEditor, saveNote, deleteNote
supplements.js
Supplement and medication timeline.
Key exports:
openSupplementEditor(index?)— opens the supplement editor modalsaveSupplement()— saves supplement toimportedData.supplementsdeleteSupplement(index)— removes by indexrenderSupplementsSection(data)— renders the supplements dashboard section
Window exports: openSupplementEditor, saveSupplement, deleteSupplement
cycle.js
Menstrual cycle tracking, helpers, and dashboard rendering.
Key exports:
getCyclePhase(dateStr, mc)—{ cycleDay, phase, phaseName }for a date against a cycle objectgetNextBestDrawDate(mc)— next early follicular window (days 3–5)getBloodDrawPhases(mc, dates)— maps lab dates to phasescalculateCycleStats(periods)— auto-computes{ cycleLength, periodLength, regularity }from period logdetectPerimenopausePattern(mc, dob)— flags perimenopause pattern (age 35+, 4+ periods, 2+ of 4 indicators)detectCycleIronAlerts(mc, data)— cross-references heavy flow with ferritin/hemoglobin/ironopenMenstrualCycleEditor()— opens the cycle editor modalsaveMenstrualCycle()— saves toimportedData.menstrualCycle, triggers cycle tourrenderMenstrualCycleSection(data)— renders the full cycle dashboard sectionstartCycleTour(auto)— triggers the 8-step cycle spotlight tour
Window exports: openMenstrualCycleEditor, saveMenstrualCycle, addPeriod, deletePeriod, startCycleTour
context-cards.js
All 9 lifestyle context card editors plus AI health dots.
Key exports:
renderProfileContextCards(data)— renders the 3-column context card grid on the dashboardloadContextHealthDots()— 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 - Per-card save functions:
saveDiagnoses,saveDiet,saveExercise,saveSleep,saveLight,saveStress,saveLoveLife,saveEnvironment,saveHealthGoals selectCtxOption(el, group, multi)— shared button-group selection handlergetSelectedOption(group)— reads selected value from a.ctx-btn-groupsummaryFnimplementations — generate the one-line text shown on each collapsed carddebounceContextNotes()— auto-saves the free-form context notes textarea
Window exports: all open/save functions, selectCtxOption, addCondition, deleteCondition, addGoal, deleteGoal, syncDiagnosesNote, openInterpretiveLensEditor, saveInterpretiveLens
Layer 5 — Feature Modules
pdf-import.js
Full PDF-to-lab-data import pipeline.
Key exports:
extractPDFText(file)— pdf.js text extraction with x/y coordinates, returns page-aware formatted textparseLabPDFWithAI(pdfText)— sends text +buildMarkerReference()to AI; maps lab results to marker keyshandleBatchPDFs(files)— sequential multi-file import with per-file confirm/skipshowImportPreview(parsed)— modal with matched (green), new custom (blue), unmatched (yellow) markersconfirmImport(parsed)— merges parsed data intoimportedData.entriesinitDropZone()— wires the drag-and-drop zone for PDF and JSON files
Window exports: confirmImport, skipImport, importNextPDF
export.js
Data export, import, and reset.
Key exports:
exportToJSON()— exports v2 JSON:{ version: 2, exportedAt, entries, notes, diagnoses, diet, exercise, sleepRest, lightCircadian, stress, loveLife, environment, interpretiveLens, healthGoals, contextNotes, menstrualCycle, customMarkers, supplements }importFromJSON(file)— merges entries by date, deduplicates notes, overwrites context fields, merges healthGoals by textexportToPDF()— generates a printable PDF report with all data, charts, and context cardsclearAllData()— confirms and wipes all imported data for the current profile
Window exports: exportToJSON, importFromJSON, exportToPDF, clearAllData
chat.js
AI chat panel, context building, and markdown rendering.
Key exports:
buildLabContext()— serializes all lab entries + all 9 context cards + interpretiveLens + contextNotes + cycle data + notes into a structured AI context stringsendChatMessage()— sends user message with full context to the active AI provider, streams responserenderMarkdown(text)— block-aware parser: headings, lists, code blocks, HR, paragraphs + inline formattingaskAIAboutMarker(markerKey)— per-marker AI explanation, streams into the chat panel- Thread management:
createNewThread(),loadThread(id),deleteThread(id),renameThread(id) setChatPersonality(id)— switches personality, updates system promptgetCustomPersonality()— returns{ name, icon, promptText, evidenceBased }from storagegenerateCustomPersonality()— AI-powered persona generation from a name (2048 tokens, streamed)pickPersonaIcon(name)— djb2 hash into 10-emoji palette
Window exports: sendChatMessage, setChatPersonality, openChatPanel, closeChatPanel, createNewThread, loadThread, deleteThread, renameThread, generateCustomPersonality, saveCustomPersonality, askAIAboutMarker
settings.js
Settings modal with 6 sections.
Key exports:
openSettingsModal()/closeSettingsModal()initSettingsModelFetch()— fetches model lists for all providers on modal opensaveProfileSettings()— saves sex, DOB, location from the Profile sectionsetUnitSystem(system)—'EU'|'US'loadBackupSnapshots()— async; populates the Backup & Restore section with IndexedDB snapshotsrestoreAutoBackup(id)— confirm + restore a snapshot, reloads the page
Window exports: openSettingsModal, closeSettingsModal, saveProfileSettings, setUnitSystem, setAIProvider, restoreAutoBackup, startTour
glossary.js
Searchable marker glossary modal.
Key exports:
openGlossaryModal()/closeGlossaryModal()renderGlossary(data, query)— renders all markers grouped by category, filtered by search query, with latest values and ref ranges
Window exports: openGlossaryModal, closeGlossaryModal
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
nav.js
Sidebar navigation and chart layer controls.
Key exports:
renderSidebar(data)— renders category navigation with marker countsrenderDateRangeFilter()— renders the date range pillsrenderLayersDropdown(data)— renders the Layers dropdown (shown only when profile has notes/supplements/cycle data)setDateRangeFilter(range)— updatesstate.dateRangeFilterand refreshestoggleLayer(layer)— togglesnoteOverlayMode,suppOverlayMode, orphaseOverlayMode
Window exports: setDateRangeFilter, toggleLayer, navigateTo
Layer 6 — Orchestration
views.js
All dashboard and category rendering. The largest module.
Key exports:
navigate(section, params)— main router; calls the appropriate render functionshowDashboard(data?)— renders the full dashboard: drop zone → interpretive lens → focus card → context cards → cycle → supplements → key trends → alerts → data & notesshowCategory(catKey, data?)— renders a single category with chart grid and data tableshowCompare(data?)— renders the date-comparison viewshowCorrelations(data?)— renders the correlation matrixshowDetailModal(markerKey, data?)— opens the marker detail modalshowManualEntry(data?)— renders the manual data entry formshowFocusCard(data?)— renders the AI focus card with cache managementrefreshDashboard()— convenience function: callsgetActiveData()andshowDashboard(data)
Window exports: navigate, showDashboard, showCategory, showDetailModal, showManualEntry, refreshDashboard, openMenstrualCycleEditor (re-exported)
main.js
Entry point. Runs once on DOMContentLoaded.
Responsibilities:
- Imports all feature modules (side-effect imports for window exports)
- Registers
registerRefreshCallback(() => window.refreshDashboard()) - Attaches global event listeners: keyboard shortcuts, modal backdrop clicks, drop zone, profile selector
- Calls initial
navigate('dashboard')
Window exports: none (all exports come from other modules)
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/DonestartTour(auto)— launches the 7-step app tour (auto=truechecks completion flag first)startCycleTour(auto)— launches the 8-step cycle-specific tourendTour()— removes tour DOM elements, stores completion flagCYCLE_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=trueshows all entries,falseshows latest 3closeChangelog()— hides modal, marks current version as seen in localStoragemaybeShowChangelog()— auto-trigger: shows modal iflabcharts-changelog-seen!==APP_VERSION
Window exports: openChangelog, closeChangelog, maybeShowChangelog
crypto.js
Data encryption at rest and IndexedDB auto-backup.
Key exports:
encryptedSetItem(key, value)/encryptedGetItem(key)— AES-256-GCM via PBKDF2 passphrasegetEncryptionEnabled()/setEncryptionEnabled(bool)— encryption togglescheduleAutoBackup()— debounced 60s trigger; saves up to 5 snapshots to IndexedDBbuildBackupSnapshot()— captures all importedData + per-profile preferencesrestoreAutoBackup(id)— writes snapshot to localStorage, reloadsbroadcastDataChanged(profileId)— BroadcastChannel message for multi-tab syncSENSITIVE_PATTERNS— array of localStorage key pattern strings that get encrypted
Window exports: setEncryptionEnabled, changePassphrase, restoreAutoBackup, exportBackup, importBackup