Local files
Open files from disk with your own picker.
A file picker, and a file from another genome
A blob has no URL to append .bai to, so JBrowse cannot find an index beside
it: the picker has to take the pair. A blobId lives in memory, so a local
track is gone after a reload and means nothing in a saved session.
View source — 187 lines
import { useState } from 'react'
import { refNameMismatchMessage } from '@jbrowse/core/assemblyManager/assembly'
import { SessionPaletteProvider } from '@jbrowse/core/ui/PaletteContext'
import {
UNKNOWN,
UNSUPPORTED,
guessAdapter,
guessTrackType,
storeBlobLocation,
} from '@jbrowse/core/util/tracks'
import { DisplayUIProvider } from '@jbrowse/display-ui'
import { TrackStack } from '@jbrowse/display-ui/embed'
import { useCreateViewState } from '@jbrowse/react-linear-genome-view2'
import { observer } from 'mobx-react'
import type { ViewModel } from '@jbrowse/react-linear-genome-view2'
type Session = ViewModel['session']
const notice: React.CSSProperties = {
background: 'color-mix(in srgb, CanvasText 8%, Canvas)',
borderLeft: '3px solid #d97706',
padding: '6px 10px',
fontSize: '0.8rem',
}
function register(file: File) {
const location = storeBlobLocation({ blob: file })
if (!('blobId' in location)) {
throw new Error(`could not register ${file.name}`)
}
return location
}
function openFiles(session: Session, files: File[]) {
const { view } = session
const isIndex = (f: File) => /\.(bai|csi|tbi|crai|fai|gzi)$/i.test(f.name)
for (const data of files.filter(f => !isIndex(f))) {
const index = files.find(f => isIndex(f) && f.name.startsWith(data.name))
const location = register(data)
const adapter = guessAdapter(
location,
index ? register(index) : undefined,
undefined,
view,
)
if (adapter.type === UNKNOWN || adapter.type === UNSUPPORTED) {
throw new Error(`No loaded plugin reads "${data.name}"`)
}
const trackId = `local-${location.blobId}`
session.addSessionTrackConf({
trackId,
type: guessTrackType(adapter.type, view, location),
name: data.name,
assemblyNames: ['hg38'],
adapter,
})
void view.launchTrack(trackId)
}
}
async function fetchDemoBam() {
const base =
'https://jbrowse.org/demos/cgiab/HG008-T_chr10_CUZD1_deletion.bam'
return Promise.all(
[base, `${base}.bai`].map(async url => {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`could not fetch ${url}: ${response.status}`)
}
return new File([await response.arrayBuffer()], url.split('/').pop()!)
}),
)
}
function otherGenomeBed() {
return new File(
['2L\t1000\t5000\tACME1\n2L\t8000\t9000\tACME2\n2R\t2000\t6000\tACME3\n'],
'other-genome.bed',
)
}
const FilePicker = observer(function FilePicker({
session,
}: {
session: Session
}) {
const [error, setError] = useState<unknown>()
const [busy, setBusy] = useState(false)
const open = (files: File[]) => {
setError(undefined)
try {
openFiles(session, files)
} catch (e) {
setError(e)
}
}
const mismatches = session.view.tracks.flatMap(t =>
t.refNameMismatch
? [{ id: t.id, message: refNameMismatchMessage(t.refNameMismatch) }]
: [],
)
return (
<div style={{ display: 'grid', gap: 6, paddingBottom: 8 }}>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
<input
type="file"
multiple
aria-label="Open files"
onChange={event => {
open([...(event.target.files ?? [])])
}}
/>
<button
type="button"
disabled={busy}
onClick={() => {
setBusy(true)
fetchDemoBam()
.then(open)
.catch(setError)
.finally(() => {
setBusy(false)
})
}}
>
{busy ? 'Fetching…' : 'Open a BAM for me'}
</button>
<button
type="button"
onClick={() => {
open([otherGenomeBed()])
}}
>
Open a file from another genome
</button>
</div>
{error ? (
<div role="alert" style={notice}>
{error instanceof Error ? error.message : String(error)}
</div>
) : null}
{mismatches.map(({ id, message }) => (
<div key={id} style={notice}>
{message}
</div>
))}
</div>
)
})
const OpenALocalFile = observer(function OpenALocalFile() {
const state = useCreateViewState({
assembly: {
name: 'hg38',
uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz',
refNameAliases: {
uri: 'https://jbrowse.org/genomes/GRCh38/hg38_aliases.txt',
},
geneticCodes: { chrM: 2 },
},
tracks: [
{
trackId: 'hg38_genes',
name: 'RefSeq curated genes',
uri: 'https://jbrowse.org/ucsc/hg38/ncbiRefSeqCurated.gff.gz',
index: 'https://jbrowse.org/ucsc/hg38/ncbiRefSeqCurated.gff.gz.csi',
displayDefaults: { height: 120 },
},
],
init: {
loc: 'chr10:122,831,700..122,840,800',
tracks: ['hg38_genes'],
},
})
return state ? (
<SessionPaletteProvider session={state.session}>
<DisplayUIProvider>
<FilePicker session={state.session} />
<TrackStack view={state.session.view} />
</DisplayUIProvider>
</SessionPaletteProvider>
) : null
})
export default OpenALocalFile