Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b498013
update grove yml and .gitignore
mattsolo1 Aug 31, 2025
71daf62
feat: Add gnomAD Assistant powered by CopilotKit
mattsolo1 Aug 31, 2025
89e3499
feat: Replace floating sidebar with resizable split-screen layout for…
mattsolo1 Sep 2, 2025
101d419
feat: Add CopilotKit context awareness and variant display support
mattsolo1 Sep 2, 2025
27d8efd
fix: Update CopilotKit action to match MCP tool name
mattsolo1 Sep 2, 2025
95af816
update model
mattsolo1 Sep 3, 2025
0825aba
Remove get_variant_summary copilot action
mattsolo1 Sep 3, 2025
a88053b
make dataset version copilot readable from document title
mattsolo1 Sep 3, 2025
1f50090
feat: Add context-aware chat suggestions and default open state
mattsolo1 Sep 3, 2025
cab4b22
fix: Remove unused CopilotSidebar component to prevent duplicate acti…
mattsolo1 Sep 3, 2025
457c19c
feat: add mendelian table env
mattsolo1 Sep 3, 2025
3009bfe
feat: Add agent state messages with inline tool execution display
mattsolo1 Sep 3, 2025
7c3ecd4
feat: add variant card display in chat assistant
mattsolo1 Sep 4, 2025
a234693
feat: use 2.5 flash
mattsolo1 Sep 4, 2025
491222e
update pnpm lock
mattsolo1 Nov 18, 2025
11b43f7
update tsv ref
mattsolo1 Nov 19, 2025
f1121b9
feat: add region context support and Juha API query suggestions
mattsolo1 Nov 19, 2025
68d2370
feat: add Juha API presentation components with virtualized tables
mattsolo1 Nov 19, 2025
c4d5b2b
feat: integrate CopilotKit with MCP server and fix production deploym…
mattsolo1 Nov 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ deploy_config.json

browser/build.env

# Environment configuration (contains local paths and secrets)
.env
.env.fish

# Build outputs
browser/dist

Expand All @@ -36,3 +40,10 @@ hail-*.log

# Reads metadata databases
reads/*.db

.grove
.grove-worktrees

# Binary artifacts for Docker builds
bin/
resources/
3 changes: 3 additions & 0 deletions browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
"@gnomad/track-transcripts": "4.0.0",
"@gnomad/track-variants": "3.0.0",
"@gnomad/ui": "^3.0.0",
"@copilotkit/react-core": "^1.10.6",
"@copilotkit/react-ui": "^1.10.6",
"@copilotkit/react-textarea": "^1.10.6",
"@visx/axis": "^3.0.0",
"@visx/group": "^3.0.0",
"@visx/legend": "^3.12.0",
Expand Down
85 changes: 48 additions & 37 deletions browser/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import React, { Suspense, lazy, useEffect, useState } from 'react'
import { BrowserRouter as Router, Route, useLocation } from 'react-router-dom'
import styled from 'styled-components'
import { CopilotKit } from '@copilotkit/react-core'
import '@copilotkit/react-ui/styles.css'

Check failure on line 5 in browser/src/App.tsx

View workflow job for this annotation

GitHub Actions / Checks

Unexpected use of file extension "css" for "@copilotkit/react-ui/styles.css"
import './styles/chatComponents.css'

import Delayed from './Delayed'
import ErrorBoundary from './ErrorBoundary'

import Notifications, { showNotification } from './Notifications'
import StatusMessage from './StatusMessage'
import userPreferences from './userPreferences'
import { GnomadCopilot } from './GnomadCopilot'

const NavBar = lazy(() => import('./NavBar'))
const Routes = lazy(() => import('./Routes'))
Expand Down Expand Up @@ -41,7 +45,7 @@
const location = useLocation()
useEffect(() => {
if ((window as any).gtag) {
;(window as any).gtag('config', (window as any).gaTrackingId, {
; (window as any).gtag('config', (window as any).gaTrackingId, {
page_path: location.pathname,
})
}
Expand Down Expand Up @@ -69,10 +73,12 @@
}
`


const BANNER_CONTENT = null

const App = () => {
const [isLoading, setIsLoading] = useState(true)

useEffect(() => {
userPreferences.loadPreferences().then(
() => {
Expand All @@ -89,44 +95,49 @@
)
}, [])

const copilotKitUrl = '/api/copilotkit'

return (
<Router>
{/* On any navigation, send event to Google Analytics. */}
<Route path="/" component={GoogleAnalytics} />

{/**
* On any navigation, scroll to the anchor specified by location fragment (if any) or to the top of the page.
* If the page's module is already loaded, scrolling is handled by this router's render function. If the page's
* module is loaded by Suspense, scrolling is handled by the useEffect hook in the PageLoading component.
*/}
<Route
path="/"
render={({ location }: any) => {
scrollToAnchorOrStartOfPage(location)
return null
}}
/>

<ErrorBoundary>
{isLoading ? (
<Delayed>
<StatusMessage>Loading</StatusMessage>
</Delayed>
) : (
<Suspense fallback={null}>
<TopBarWrapper>
<NavBar />
{BANNER_CONTENT && <Banner>{BANNER_CONTENT}</Banner>}
</TopBarWrapper>
<Notifications />

<Suspense fallback={<PageLoading />}>
<Routes />
<CopilotKit runtimeUrl={copilotKitUrl}>
<Router>
{/* On any navigation, send event to Google Analytics. */}
<Route path="/" component={GoogleAnalytics} />

{/**
* On any navigation, scroll to the anchor specified by location fragment (if any) or to the top of the page.
* If the page's module is already loaded, scrolling is handled by this router's render function. If the page's
* module is loaded by Suspense, scrolling is handled by the useEffect hook in the PageLoading component.
*/}
<Route
path="/"
render={({ location }: any) => {
scrollToAnchorOrStartOfPage(location)
return null
}}
/>

<ErrorBoundary>
{isLoading ? (
<Delayed>
<StatusMessage>Loading</StatusMessage>
</Delayed>
) : (
<Suspense fallback={null}>
<GnomadCopilot>
<TopBarWrapper>
<NavBar />
{BANNER_CONTENT && <Banner>{BANNER_CONTENT}</Banner>}
</TopBarWrapper>
<Notifications />
<Suspense fallback={<PageLoading />}>
<Routes />
</Suspense>
</GnomadCopilot>
</Suspense>
</Suspense>
)}
</ErrorBoundary>
</Router>
)}
</ErrorBoundary>
</Router>
</CopilotKit>
)
}

Expand Down
50 changes: 49 additions & 1 deletion browser/src/DocumentTitle.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,68 @@
import PropTypes from 'prop-types'
import { useEffect } from 'react'
import { useCopilotReadable } from '@copilotkit/react-core'

const DocumentTitle = ({ title }: any) => {
const DocumentTitle = ({ title, pageContext }: any) => {
useEffect(() => {
const fullTitle = title ? `${title} | gnomAD` : 'gnomAD'
document.title = fullTitle
}, [title])

let contextDescription = 'The current page context'
let contextValue = 'No context is available for the current page.'

if (pageContext) {
if (pageContext.gene_id && pageContext.symbol) {
contextDescription = 'The currently viewed gene'
const geneContext = {
gene_id: pageContext.gene_id,
symbol: pageContext.symbol,
name: pageContext.name,
reference_genome: pageContext.reference_genome,
}
contextValue = JSON.stringify(geneContext, null, 2)
} else if (pageContext.variant_id) {
contextDescription = 'The currently viewed variant'
const datasetId = new URL(window.location.href).searchParams.get('dataset')
const variantContext = {
variant_id: pageContext.variant_id,
dataset: datasetId,
reference_genome: pageContext.reference_genome,
caid: pageContext.caid,
rsids: pageContext.rsids,
}
contextValue = JSON.stringify(variantContext, null, 2)
} else if (pageContext.chrom && pageContext.start && pageContext.stop) {
contextDescription = 'The currently viewed genomic region'
const regionContext = {
chrom: pageContext.chrom,
start: pageContext.start,
stop: pageContext.stop,
reference_genome: pageContext.reference_genome,
}
contextValue = JSON.stringify(regionContext, null, 2)
} else {
// Fallback for other contexts that might be passed
contextValue = JSON.stringify(pageContext, null, 2)
}
}

useCopilotReadable({
description: contextDescription,
value: contextValue,
})

return null
}

DocumentTitle.propTypes = {
title: PropTypes.string,
pageContext: PropTypes.object,

Check failure on line 60 in browser/src/DocumentTitle.ts

View workflow job for this annotation

GitHub Actions / Checks

Prop type "object" is forbidden
}

DocumentTitle.defaultProps = {
title: null,
pageContext: null,
}

export default DocumentTitle
2 changes: 1 addition & 1 deletion browser/src/GenePage/GenePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ const GenePage = ({ datasetId, gene, geneId }: Props) => {
return (
<TrackPage>
<TrackPageSection>
<DocumentTitle title={`${gene.symbol} | ${labelForDataset(datasetId)}`} />
<DocumentTitle title={`${gene.symbol} | ${labelForDataset(datasetId)}`} pageContext={gene} />
<GnomadPageHeading
selectedDataset={datasetId}
datasetOptions={{
Expand Down
Loading
Loading