From f6cdcda8fbc5936a72ea34b71425cc57bc254b5d Mon Sep 17 00:00:00 2001 From: Vishakh Date: Wed, 20 May 2026 10:25:33 -0400 Subject: [PATCH 01/10] Switching onboarding flow. --- app/api/sample-results/route.ts | 39 +++++ app/components/ConversionOnboarding.tsx | 1 + app/components/LLMChatInline.tsx | 32 +--- app/components/MenuBar.tsx | 13 +- app/components/MenuDropdowns.tsx | 4 +- app/dna-chat/page.tsx | 204 +++++++++++++++++++++++- app/globals.css | 84 ++++++++++ app/landing-client.tsx | 104 +++++++++--- lib/analytics.ts | 2 +- 9 files changed, 407 insertions(+), 76 deletions(-) create mode 100644 app/api/sample-results/route.ts diff --git a/app/api/sample-results/route.ts b/app/api/sample-results/route.ts new file mode 100644 index 0000000..6cbf207 --- /dev/null +++ b/app/api/sample-results/route.ts @@ -0,0 +1,39 @@ +import { NextResponse } from "next/server"; + +const SAMPLE_RESULTS_URL = "https://monadic-dna-explorer.nyc3.cdn.digitaloceanspaces.com/monadic_dna_explorer_results_2026-05-19.tsv"; + +export async function GET() { + try { + const upstream = await fetch(SAMPLE_RESULTS_URL, { + method: "GET", + cache: "no-store", + }); + + if (!upstream.ok) { + return NextResponse.json( + { error: `Sample results upstream failed with status ${upstream.status}` }, + { status: 502 } + ); + } + + const data = await upstream.arrayBuffer(); + const contentType = upstream.headers.get("content-type") || "text/tab-separated-values; charset=utf-8"; + const contentLength = upstream.headers.get("content-length") || String(data.byteLength); + + return new NextResponse(data, { + status: 200, + headers: { + "Content-Type": contentType, + "Content-Length": contentLength, + "Content-Disposition": "inline; filename=\"monadic_dna_explorer_results_2026-05-19.tsv\"", + "Cache-Control": "no-store", + }, + }); + } catch (error) { + console.error("[sample-results] Failed to fetch sample results:", error); + return NextResponse.json( + { error: error instanceof Error ? error.message : "Failed to fetch sample results" }, + { status: 500 } + ); + } +} diff --git a/app/components/ConversionOnboarding.tsx b/app/components/ConversionOnboarding.tsx index f50f2c3..6d976b2 100644 --- a/app/components/ConversionOnboarding.tsx +++ b/app/components/ConversionOnboarding.tsx @@ -1,3 +1,4 @@ +// MOTHBALLED 2026-05-19: The conversion onboarding flow is preserved for reference, but active entry points now use the lightweight new-user choice modal. "use client"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; diff --git a/app/components/LLMChatInline.tsx b/app/components/LLMChatInline.tsx index 28e5a8c..b08dd1d 100644 --- a/app/components/LLMChatInline.tsx +++ b/app/components/LLMChatInline.tsx @@ -5,14 +5,12 @@ import { SavedResult } from "@/lib/results-manager"; import NilAIConsentModal from "./NilAIConsentModal"; import { useResults } from "./ResultsContext"; import { useCustomization } from "./CustomizationContext"; -import { useAuth } from "./AuthProvider"; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { callLLM, callLLMStream, getLLMDescription, MessageContentPart } from "@/lib/llm-client"; import { getLLMConfig } from "@/lib/llm-config"; import { SparklesIcon } from "./Icons"; import { trackLLMQuestionAsked } from "@/lib/analytics"; -import { hasValidPromoAccess } from "@/lib/promo-access"; type AttachmentType = 'text' | 'pdf' | 'csv' | 'tsv' | 'image'; @@ -68,8 +66,6 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { const resultsContext = useResults(); const { getTopResultsByRelevance } = resultsContext; const { customization, status: customizationStatus } = useCustomization(); - const { hasActiveSubscription } = useAuth(); - const [mounted, setMounted] = useState(false); const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(""); @@ -78,7 +74,6 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { const [error, setError] = useState(null); const [showConsentModal, setShowConsentModal] = useState(false); const [hasConsent, setHasConsent] = useState(false); - const [hasPromoAccess, setHasPromoAccess] = useState(false); const [showPersonalizationPrompt, setShowPersonalizationPrompt] = useState(false); const [expandedMessageIndex, setExpandedMessageIndex] = useState(null); const [attachedFiles, setAttachedFiles] = useState([]); @@ -92,11 +87,6 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { useEffect(() => { setMounted(true); - // Check for promo code access - if (hasValidPromoAccess()) { - setHasPromoAccess(true); - } - // Check consent const consent = localStorage.getItem(CONSENT_STORAGE_KEY); if (consent === 'true') { @@ -355,25 +345,6 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { const query = inputValue.trim(); if (!query) return; - // Check authentication first - if (!hasActiveSubscription && !hasPromoAccess) { - // Check if user is authenticated - const dynamicButton = document.querySelector('[data-dynamic-widget-button]') as HTMLElement; - if (dynamicButton) { - // Try to determine if user is logged in by checking for Dynamic's user indicator - const isLoggedIn = document.querySelector('[data-dynamic-user-profile]'); - if (!isLoggedIn) { - // Not logged in, trigger login - dynamicButton.click(); - return; - } - } - // User is logged in but not subscribed, show payment modal - const event = new CustomEvent('openPaymentModal'); - window.dispatchEvent(event); - return; - } - // Check consent before sending first message if (!hasConsent) { setShowConsentModal(true); @@ -1248,9 +1219,8 @@ Remember: You have plenty of space. Use ALL of it to provide a complete, thoroug className="chat-send-button" onClick={handleSendMessage} disabled={isLoading || !inputValue.trim()} - title={(!hasActiveSubscription && !hasPromoAccess) ? 'Login and subscribe to send messages' : undefined} > - {isLoading ? 'Sending...' : (!hasActiveSubscription && !hasPromoAccess) ? 'Login/Subscribe' : 'Send'} + {isLoading ? 'Sending...' : 'Send'} diff --git a/app/components/MenuBar.tsx b/app/components/MenuBar.tsx index 21a2510..7c0f892 100644 --- a/app/components/MenuBar.tsx +++ b/app/components/MenuBar.tsx @@ -358,9 +358,9 @@ export default function MenuBar() { isOpen={showHelpDropdown} onClose={() => setShowHelpDropdown(false)} onRestartOnboarding={() => { - trackGetStartedClicked("restart_onboarding"); + trackGetStartedClicked("welcome_options"); if (pathname === "/") { - window.dispatchEvent(new CustomEvent("openConversionOnboarding", { detail: { mode: "guided" } })); + window.dispatchEvent(new CustomEvent("openNewUserChoiceModal")); return; } @@ -410,13 +410,10 @@ export default function MenuBar() { - - DNA Chat - Premium - + DNA Chat setShowHelpDropdown(!showHelpDropdown)} - title="Get help and reopen onboarding" + title="Get help and start options" data-tour="help-button" > diff --git a/app/components/MenuDropdowns.tsx b/app/components/MenuDropdowns.tsx index 7adc8d0..70c6140 100644 --- a/app/components/MenuDropdowns.tsx +++ b/app/components/MenuDropdowns.tsx @@ -328,9 +328,9 @@ export function HelpDropdown({ onRestartOnboarding(); onClose(); }} - title="Open the new onboarding flow again" + title="Open start options" > - ↺ Restart Onboarding + Start Options )} (initialSampleLoadState); + const sampleLoadStartedRef = useRef(false); useEffect(() => { trackDNAChatViewed(); @@ -23,17 +50,178 @@ export default function DNAChatPage() { } }, []); + useEffect(() => { + if (typeof window === "undefined" || sampleLoadStartedRef.current) return; + + const url = new URL(window.location.href); + const shouldLoadSample = url.searchParams.get("sample") === "1"; + + if (!shouldLoadSample) return; + + sampleLoadStartedRef.current = true; + url.searchParams.delete("sample"); + window.history.replaceState({}, "", url.toString()); + + const loadSampleResults = async () => { + if (savedResults.length > 0) { + setSampleLoad({ + status: "skipped", + downloadedBytes: 0, + totalBytes: 0, + resultCount: savedResults.length, + error: null, + }); + return; + } + + try { + setSampleLoad({ ...initialSampleLoadState, status: "downloading" }); + + const response = await fetch("/api/sample-results", { method: "GET" }); + if (!response.ok) { + throw new Error(`Sample results download failed with status ${response.status}.`); + } + + const totalBytes = Number(response.headers.get("content-length") || "0"); + const decoder = new TextDecoder(); + let content = ""; + let downloadedBytes = 0; + + if (response.body) { + const reader = response.body.getReader(); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + if (!value) continue; + + downloadedBytes += value.byteLength; + content += decoder.decode(value, { stream: true }); + setSampleLoad({ + status: "downloading", + downloadedBytes, + totalBytes, + resultCount: 0, + error: null, + }); + } + + content += decoder.decode(); + } else { + content = await response.text(); + downloadedBytes = new Blob([content]).size; + } + + setSampleLoad({ + status: "loading", + downloadedBytes, + totalBytes: totalBytes || downloadedBytes, + resultCount: 0, + error: null, + }); + + const session = ResultsManager.parseResultsFile(content, SAMPLE_RESULTS_FILE_NAME); + if (!session.results.length) { + throw new Error("The sample results file did not contain any usable results."); + } + + await clearResults(); + await addResultsBatch(session.results); + localStorage.setItem("dna_chat_sample_results_loaded", "true"); + + setSampleLoad({ + status: "loaded", + downloadedBytes, + totalBytes: totalBytes || downloadedBytes, + resultCount: session.results.length, + error: null, + }); + } catch (error) { + console.error("[DNA Chat] Sample results load failed:", error); + setSampleLoad({ + status: "error", + downloadedBytes: 0, + totalBytes: 0, + resultCount: 0, + error: error instanceof Error ? error.message : "Sample results could not be loaded.", + }); + } + }; + + void loadSampleResults(); + }, [addResultsBatch, clearResults, savedResults.length]); + + const sampleProgress = sampleLoad.totalBytes > 0 + ? Math.min(100, Math.round((sampleLoad.downloadedBytes / sampleLoad.totalBytes) * 100)) + : 0; + return (
- - {null} +
+
+
+ DNA Chat is open to try + Ask private questions about saved genetic results. +
+
+
+ {(sampleLoad.status === "downloading" || sampleLoad.status === "loading") && ( +
+
+ {sampleLoad.status === "downloading" ? "Downloading sample results" : "Loading sample results"} + + {sampleLoad.status === "downloading" + ? sampleLoad.totalBytes > 0 + ? `${sampleProgress}% downloaded, ${formatBytes(sampleLoad.downloadedBytes)} of ${formatBytes(sampleLoad.totalBytes)}` + : `${formatBytes(sampleLoad.downloadedBytes)} downloaded` + : "Preparing the sample results for DNA Chat."} + +
+
+ +
+
+ )} + + {sampleLoad.status === "loaded" && ( +
+
+ Sample results are loaded. + + DNA Chat is using {sampleLoad.resultCount.toLocaleString()} sample results. You can upload your own DNA from My Data, then analyze it in Explore. + +
+ +
+ )} + + {sampleLoad.status === "skipped" && ( +
+
+ Your existing results are loaded. + DNA Chat will use the results already in this browser session. +
+
+ )} + + {sampleLoad.status === "error" && ( +
+
+ Sample results could not be loaded. + {sampleLoad.error} +
+
+ )} + setTourOpen(true)} />
diff --git a/app/globals.css b/app/globals.css index 161f1bc..2f1a048 100644 --- a/app/globals.css +++ b/app/globals.css @@ -11024,3 +11024,87 @@ details[open] .summary-arrow { max-width: calc(100vw - 24px); } } + + +.new-user-choice-slide { + padding: 1rem 0 0.5rem; +} + +.new-user-choice-list { + width: min(560px, 100%); + margin-top: 0.65rem; +} + +.dna-chat-open-header .premium-header-content { + justify-content: space-between; +} + +.dna-chat-sample-notice { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding: 0.85rem 1rem; + border: 1px solid rgba(15, 23, 42, 0.08); + border-left: 4px solid #0f766e; + border-radius: 8px; + background: #ffffff; + color: var(--text-secondary); +} + +.dna-chat-sample-notice > div { + display: flex; + flex-direction: column; + gap: 0.25rem; + min-width: 0; +} + +.dna-chat-sample-notice strong { + color: var(--text-primary); + font-size: 0.95rem; +} + +.dna-chat-sample-notice span { + line-height: 1.45; +} + +.dna-chat-sample-notice .wire-progress-bar { + width: min(220px, 32vw); + flex: 0 0 auto; + margin: 0; +} + +.dna-chat-sample-notice button { + flex: 0 0 auto; + padding: 0.55rem 0.85rem; + border: 1px solid rgba(15, 23, 42, 0.12); + border-radius: 8px; + background: #111111; + color: #ffffff; + font-weight: 750; + cursor: pointer; +} + +.dna-chat-sample-notice button:hover { + background: #2f2f2f; +} + +.dna-chat-sample-notice.error { + border-left-color: #dc2626; + background: rgba(239, 68, 68, 0.06); +} + +@media (max-width: 720px) { + .dna-chat-sample-notice { + align-items: stretch; + flex-direction: column; + } + + .dna-chat-sample-notice .wire-progress-bar { + width: 100%; + } + + .dna-chat-sample-notice button { + width: 100%; + } +} diff --git a/app/landing-client.tsx b/app/landing-client.tsx index fc849b6..d8df21f 100644 --- a/app/landing-client.tsx +++ b/app/landing-client.tsx @@ -2,14 +2,14 @@ import { useCallback, useEffect, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; -import ConversionOnboarding from "./components/ConversionOnboarding"; import { useGenotype } from "./components/UserDataUpload"; import { trackGetStartedClicked } from "@/lib/analytics"; -type FlowMode = "guided" | "instant_preview"; const INSTRUCTIONAL_VIDEO_URL = "https://youtu.be/1mqLYTAOK90"; const SCHEDULE_CALL_URL = "https://calendar.app.google/eVDN4d44GreUjR8p8"; +const NEW_USER_CHOICE_STORAGE_KEY = "new_user_choice_completed"; +const MOTHBALLED_ONBOARDING_STORAGE_KEY = "conversion_onboarding_completed"; const introCopy = [ { @@ -30,27 +30,78 @@ const introCopy = [ }, ]; +function NewUserChoiceModal({ + isOpen, + onClose, + onTryChat, +}: { + isOpen: boolean; + onClose: () => void; + onTryChat: () => void; +}) { + if (!isOpen) return null; + + return ( +
+
+
+ +
Welcome
+
+

Start with the full app overview or try DNA Chat with sample results.

+

+ You can learn how Monadic DNA Explorer works first, or jump straight into + DNA Chat with a prepared result set. +

+
+ + +
+
+
+
+
+ ); +} + export default function LandingClient() { const router = useRouter(); const searchParams = useSearchParams(); - const { isUploaded, error } = useGenotype(); - const [showFlow, setShowFlow] = useState(false); - const [flowMode, setFlowMode] = useState("guided"); + const { error } = useGenotype(); + const [showWelcomeChoice, setShowWelcomeChoice] = useState(false); - const openOnboarding = useCallback((mode: FlowMode = "guided") => { - setFlowMode(mode); - setShowFlow(true); + const completeWelcomeChoice = useCallback(() => { + localStorage.setItem(NEW_USER_CHOICE_STORAGE_KEY, "true"); + localStorage.setItem(MOTHBALLED_ONBOARDING_STORAGE_KEY, "true"); + setShowWelcomeChoice(false); }, []); useEffect(() => { if (typeof window === "undefined") return; - const completed = localStorage.getItem("conversion_onboarding_completed") === "true"; + const completed = localStorage.getItem(NEW_USER_CHOICE_STORAGE_KEY) === "true"; const forceOpen = searchParams.get("onboarding") === "1"; if (forceOpen || !completed) { - setFlowMode(forceOpen ? "guided" : isUploaded ? "instant_preview" : "guided"); - setShowFlow(true); + setShowWelcomeChoice(true); } if (forceOpen) { @@ -58,30 +109,31 @@ export default function LandingClient() { url.searchParams.delete("onboarding"); window.history.replaceState({}, "", url.toString()); } - }, [isUploaded, searchParams]); + }, [searchParams]); useEffect(() => { - const handleOpen = (event: Event) => { - const customEvent = event as CustomEvent<{ mode?: FlowMode }>; - openOnboarding(customEvent.detail?.mode || "guided"); + const handleOpen = () => { + setShowWelcomeChoice(true); }; window.addEventListener("openConversionOnboarding", handleOpen as EventListener); + window.addEventListener("openNewUserChoiceModal", handleOpen as EventListener); return () => { window.removeEventListener("openConversionOnboarding", handleOpen as EventListener); + window.removeEventListener("openNewUserChoiceModal", handleOpen as EventListener); }; - }, [openOnboarding]); + }, []); return ( <> - { - setShowFlow(false); - router.push("/"); + { + completeWelcomeChoice(); + trackGetStartedClicked("try_dna_chat_directly"); + router.push("/dna-chat?sample=1"); }} - onDismiss={() => setShowFlow(false)} />
@@ -107,11 +159,11 @@ export default function LandingClient() { Date: Wed, 20 May 2026 15:44:04 -0400 Subject: [PATCH 02/10] Cleaned up DNA Chat screen --- app/components/Footer.tsx | 19 ++++ app/components/LLMChatInline.tsx | 161 +++++-------------------------- app/dna-chat/page.tsx | 27 +----- app/globals.css | 120 +++++++++++++++++------ app/landing-client.tsx | 2 +- 5 files changed, 142 insertions(+), 187 deletions(-) diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx index 38caebd..f2b32ba 100644 --- a/app/components/Footer.tsx +++ b/app/components/Footer.tsx @@ -69,6 +69,25 @@ export default function Footer() { . All rights reserved.

+

+ + Terms and Conditions + + {" · "} + + Privacy Policy + +

diff --git a/app/components/LLMChatInline.tsx b/app/components/LLMChatInline.tsx index b08dd1d..bf006b5 100644 --- a/app/components/LLMChatInline.tsx +++ b/app/components/LLMChatInline.tsx @@ -8,8 +8,6 @@ import { useCustomization } from "./CustomizationContext"; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { callLLM, callLLMStream, getLLMDescription, MessageContentPart } from "@/lib/llm-client"; -import { getLLMConfig } from "@/lib/llm-config"; -import { SparklesIcon } from "./Icons"; import { trackLLMQuestionAsked } from "@/lib/analytics"; type AttachmentType = 'text' | 'pdf' | 'csv' | 'tsv' | 'image'; @@ -58,11 +56,7 @@ const FOLLOWUP_SUGGESTIONS = [ "How should I adjust my diet and lifestyle?" ]; -type AIChatInlineProps = { - onOpenTour?: () => void; -}; - -export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { +export default function AIChatInline() { const resultsContext = useResults(); const { getTopResultsByRelevance } = resultsContext; const { customization, status: customizationStatus } = useCustomization(); @@ -74,12 +68,10 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { const [error, setError] = useState(null); const [showConsentModal, setShowConsentModal] = useState(false); const [hasConsent, setHasConsent] = useState(false); - const [showPersonalizationPrompt, setShowPersonalizationPrompt] = useState(false); const [expandedMessageIndex, setExpandedMessageIndex] = useState(null); const [attachedFiles, setAttachedFiles] = useState([]); const [attachmentError, setAttachmentError] = useState(null); const [expandedAttachmentIndex, setExpandedAttachmentIndex] = useState(null); - const [showProviderTip, setShowProviderTip] = useState(true); const inputRef = useRef(null); const fileInputRef = useRef(null); @@ -104,14 +96,6 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { } }, []); - useEffect(() => { - // Check if personalization is not set or locked on mount only - if (customizationStatus === 'not-set' || customizationStatus === 'locked') { - setShowPersonalizationPrompt(true); - } else if (customizationStatus === 'unlocked') { - setShowPersonalizationPrompt(false); - } - }, [customizationStatus]); // Removed auto-scroll so user doesn't have to scroll up to read responses // Also removed auto-focus to prevent scrolling to bottom on tab load @@ -121,6 +105,7 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { localStorage.setItem(CONSENT_STORAGE_KEY, "true"); setHasConsent(true); setShowConsentModal(false); + void handleSendMessage(true); } }; @@ -128,9 +113,6 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { setShowConsentModal(false); }; - const handlePersonalizationPromptContinue = () => { - setShowPersonalizationPrompt(false); - }; const handleExampleClick = (question: string) => { setInputValue(question); @@ -155,32 +137,6 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { return `${score.toFixed(2)}x`; }; - const getProviderTip = () => { - if (!mounted) return null; - - const config = getLLMConfig(); - - if (config.provider === 'nilai' || config.provider === 'ollama') { - return { - icon: 'Private', - type: 'privacy', - message: config.provider === 'nilai' - ? 'nilAI is active for privacy-preserving TEE processing.' - : 'Ollama is active for local processing on your device.', - tip: 'Switch models from the LLM button in the menu bar.', - }; - } else if (config.provider === 'huggingface') { - return { - icon: 'Fast', - type: 'performance', - message: 'HuggingFace is active for broader model access.', - tip: 'Switch back to nilAI from the LLM button for stronger privacy.', - }; - } - - return null; - }; - const handleAttachmentClick = () => { setAttachmentError(null); fileInputRef.current?.click(); @@ -341,12 +297,12 @@ export default function AIChatInline({ onOpenTour }: AIChatInlineProps = {}) { return parts; }; - const handleSendMessage = async () => { + const handleSendMessage = async (skipConsentCheck = false) => { const query = inputValue.trim(); if (!query) return; // Check consent before sending first message - if (!hasConsent) { + if (!skipConsentCheck && !hasConsent) { setShowConsentModal(true); return; } @@ -889,34 +845,8 @@ Remember: You have plenty of space. Use ALL of it to provide a complete, thoroug /> )}
-
-
-
- -
-
-

DNA Chat

-

- {getLLMDescription()} - secure processing for your saved genetic results -

- {onOpenTour && ( - - )} -
-
-
- -
-
- {mounted ? resultsContext.savedResults.length.toLocaleString() : '...'} saved genetic results available -
- {messages.length > 0 && ( + {messages.length > 0 && ( +
- )} -
- - {showPersonalizationPrompt && ( -
-
- Personalization improves answers. - - {customizationStatus === 'locked' - ? ' Unlock your saved profile from the Personalize menu when you are ready.' - : ' Add ancestry, history, and demographics from the Personalize menu when you are ready.'} - -
-
)} - {/* Provider tip banner */} - {showProviderTip && (() => { - const tip = getProviderTip(); - if (!tip) return null; - - return ( -
-
- {tip.icon} -
-
{tip.message}
-
{tip.tip}
-
-
- -
- ); - })()} -
{messages.length === 0 && (
-
-

Ask about your DNA results

- Suggested prompts -
- - {mounted && resultsContext.savedResults.length < 1000 && ( -
-

Limited results ({resultsContext.savedResults.length} studies)

-

- For better answers, analyze more studies or load a prior results file. -

-
- )} + {}
    {EXAMPLE_QUESTIONS.map((question) => ( @@ -993,9 +870,6 @@ Remember: You have plenty of space. Use ALL of it to provide a complete, thoroug ))}
-
- Disclaimer: LLMs can report incorrect or fabricated information and are not medical experts. For educational purposes only. Consult a healthcare professional for medical advice. -
)} @@ -1198,7 +1072,22 @@ Remember: You have plenty of space. Use ALL of it to provide a complete, thoroug
{isFirstMessage ? (
- Searches saved traits for context + {mounted && ( + <> + {resultsContext.savedResults.length === 0 ? ( + No results loaded. Use My Data up top to load your data, then Run All to get your results. + ) : resultsContext.savedResults.length < 1000 ? ( + {resultsContext.savedResults.length.toLocaleString()} results loaded. For better answers, run more studies. + ) : ( + {resultsContext.savedResults.length.toLocaleString()} results available for analysis. + )} + {customizationStatus === 'unlocked' ? ( + Personalization on for more relevant results. + ) : customizationStatus !== 'loading' ? ( + Personalization not set. Your demographic, family, and medical history will not be used for interpreting your results. Use Personalize up top for better answers. + ) : null} + + )}
) : (
@@ -1217,7 +1106,7 @@ Remember: You have plenty of space. Use ALL of it to provide a complete, thoroug
- AI-generated content may contain errors. This is not medical advice. + LLMs can report incorrect or fabricated information and are not medical experts. For educational purposes only. Consult a healthcare professional for medical advice.
diff --git a/app/dna-chat/page.tsx b/app/dna-chat/page.tsx index aee55ca..c8b76ff 100644 --- a/app/dna-chat/page.tsx +++ b/app/dna-chat/page.tsx @@ -159,15 +159,6 @@ export default function DNAChatPage() {
-
-
-
- DNA Chat is open to try - Ask private questions about saved genetic results. -
-
-
-
{(sampleLoad.status === "downloading" || sampleLoad.status === "loading") && (
@@ -190,25 +181,17 @@ export default function DNAChatPage() { {sampleLoad.status === "loaded" && (
- Sample results are loaded. - - DNA Chat is using {sampleLoad.resultCount.toLocaleString()} sample results. You can upload your own DNA from My Data, then analyze it in Explore. - + Sample data loaded. + Using {sampleLoad.resultCount.toLocaleString()} sample results. To use your own DNA, click My Data, upload your file, then click Run All.
-
)} {sampleLoad.status === "skipped" && (
- Your existing results are loaded. - DNA Chat will use the results already in this browser session. + Your results are loaded. + DNA Chat will use the results in this browser session.
)} @@ -222,7 +205,7 @@ export default function DNAChatPage() {
)} - setTourOpen(true)} /> +