diff --git a/packages/core/pluggableElementTypes/renderers/BoxRendererType.ts b/packages/core/pluggableElementTypes/renderers/BoxRendererType.ts index a970827b52..47ceb7468f 100644 --- a/packages/core/pluggableElementTypes/renderers/BoxRendererType.ts +++ b/packages/core/pluggableElementTypes/renderers/BoxRendererType.ts @@ -189,16 +189,16 @@ export default class BoxRendererType extends FeatureRendererType { args, ) as ResultsSerialized - return { - ...serialized, - layout: results.layout.serializeRegion( - this.getExpandedRegion(args.regions[0]!, args), - ), - maxHeightReached: serialized.layout.maxHeightReached, - features: serialized.features.filter( - f => !!serialized.layout.rectangles[f.uniqueId], - ), - } + const region = args.regions[0]! + serialized.layout = results.layout.serializeRegion( + this.getExpandedRegion(region, args), + ) + serialized.features = serialized.features.filter(f => { + return Boolean(serialized.layout.rectangles[f.uniqueId]) + }) + + serialized.maxHeightReached = serialized.layout.maxHeightReached + return serialized } /** diff --git a/packages/core/pluggableElementTypes/renderers/ServerSideRendererType.tsx b/packages/core/pluggableElementTypes/renderers/ServerSideRendererType.tsx index f436d1b9a1..6ce8897b15 100644 --- a/packages/core/pluggableElementTypes/renderers/ServerSideRendererType.tsx +++ b/packages/core/pluggableElementTypes/renderers/ServerSideRendererType.tsx @@ -87,26 +87,34 @@ export default class ServerSideRenderer extends RendererType { * @param results - the results of the render * @param args - the arguments passed to render */ - deserializeResultsInClient(res: ResultsSerialized, args: RenderArgs) { - // if we are rendering svg, we skip hydration and check if rendere support - // SVG (e.g. this.supportsSVG) and then return just the HTML if so - return args.exportSVG - ? { - ...res, - html: this.supportsSVG - ? res.html - : 'SVG export not supported for this track', - } - : { - ...res, - reactElement: ( - - ), - } + deserializeResultsInClient( + res: ResultsSerialized, + args: RenderArgs, + ): ResultsDeserialized { + // if we are rendering svg, we skip hydration + if (args.exportSVG) { + // only return the res if the renderer explicitly has + // this.supportsSVG support to avoid garbage being rendered in SVG + // document + return { + ...res, + html: this.supportsSVG + ? res.html + : 'SVG export not supported for this track', + } + } + + // get res using ServerSideRenderedContent + return { + ...res, + reactElement: ( + + ), + } } /** @@ -115,17 +123,18 @@ export default class ServerSideRenderer extends RendererType { * * @param args - the converted arguments to modify */ - deserializeArgsInWorker(args: RenderArgsSerialized) { - console.log('deserializeArgsInWorker', { args }) - return { - ...args, - config: this.configSchema.create(args.config || {}, { - pluginManager: this.pluginManager, - }), - filters: args.filters - ? new SerializableFilterChain({ filters: args.filters }) - : undefined, - } + deserializeArgsInWorker(args: RenderArgsSerialized): RenderArgsDeserialized { + const deserialized = { ...args } as unknown as RenderArgsDeserialized + deserialized.config = this.configSchema.create(args.config || {}, { + pluginManager: this.pluginManager, + }) + deserialized.filters = args.filters + ? new SerializableFilterChain({ + filters: args.filters, + }) + : undefined + + return deserialized } /** @@ -206,4 +215,4 @@ export default class ServerSideRenderer extends RendererType { } } -export type { RenderResults } from './RendererType' +export { type RenderResults } from './RendererType' diff --git a/plugins/breakpoint-split-view/src/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.tsx b/plugins/breakpoint-split-view/src/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.tsx index e502e5c21c..dee96293b6 100644 --- a/plugins/breakpoint-split-view/src/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.tsx +++ b/plugins/breakpoint-split-view/src/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.tsx @@ -6,15 +6,22 @@ import { } from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail' import { Paper } from '@mui/material' import { observer } from 'mobx-react' +import { SimpleFeatureSerialized } from '@jbrowse/core/util' const BreakpointAlignmentsFeatureDetail = observer(function ({ model, }: { - model: { featureData: Record } + model: { + featureData: { + feature1: SimpleFeatureSerialized + feature2: SimpleFeatureSerialized + } + } }) { - const { feature1, feature2 } = JSON.parse(JSON.stringify(model.featureData)) + const { featureData } = model + const { feature1, feature2 } = structuredClone(featureData) return ( - + diff --git a/plugins/linear-comparative-view/src/SyntenyFeatureDetail/SyntenyFeatureDetail.tsx b/plugins/linear-comparative-view/src/SyntenyFeatureDetail/SyntenyFeatureDetail.tsx index 83b74a0ba7..f05e47a462 100644 --- a/plugins/linear-comparative-view/src/SyntenyFeatureDetail/SyntenyFeatureDetail.tsx +++ b/plugins/linear-comparative-view/src/SyntenyFeatureDetail/SyntenyFeatureDetail.tsx @@ -114,7 +114,7 @@ const SyntenyFeatureDetail = observer(function ({ model: SyntenyFeatureDetailModel }) { return ( - + diff --git a/plugins/variants/src/MultiLinearVariantDisplay/model.ts b/plugins/variants/src/MultiLinearVariantDisplay/model.ts index 21905b76aa..1bf5d02bd9 100644 --- a/plugins/variants/src/MultiLinearVariantDisplay/model.ts +++ b/plugins/variants/src/MultiLinearVariantDisplay/model.ts @@ -22,9 +22,7 @@ import type { Instance } from 'mobx-state-tree' // lazies const Tooltip = lazy(() => import('../shared/Tooltip')) const SetColorDialog = lazy(() => import('../shared/SetColorDialog')) -const HierarchicalClusterDialog = lazy( - () => import('../shared/HierarchicalClusterDialog'), -) +const ClusterDialog = lazy(() => import('../shared/ClusterDialog')) // using a map because it preserves order const rendererTypes = new Map([['multivariant', 'MultiVariantRenderer']]) @@ -263,7 +261,7 @@ export function stateModelFactory( label: 'Cluster by genotype', onClick: () => { getSession(self).queueDialog(handleClose => [ - HierarchicalClusterDialog, + ClusterDialog, { model: self, handleClose, diff --git a/plugins/variants/src/MultiLinearVariantMatrixDisplay/components/VariantDisplayComponent.tsx b/plugins/variants/src/MultiLinearVariantMatrixDisplay/components/VariantDisplayComponent.tsx index 94b11871b6..6d4f95d288 100644 --- a/plugins/variants/src/MultiLinearVariantMatrixDisplay/components/VariantDisplayComponent.tsx +++ b/plugins/variants/src/MultiLinearVariantMatrixDisplay/components/VariantDisplayComponent.tsx @@ -15,6 +15,7 @@ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view' const useStyles = makeStyles()({ cursor: { pointerEvents: 'none', + zIndex: 1000, }, }) diff --git a/plugins/variants/src/MultiLinearVariantMatrixDisplay/model.ts b/plugins/variants/src/MultiLinearVariantMatrixDisplay/model.ts index 5fd03bcc20..187b9fc452 100644 --- a/plugins/variants/src/MultiLinearVariantMatrixDisplay/model.ts +++ b/plugins/variants/src/MultiLinearVariantMatrixDisplay/model.ts @@ -19,9 +19,7 @@ import type { Instance } from 'mobx-state-tree' // lazies const SetColorDialog = lazy(() => import('../shared/SetColorDialog')) const MAFFilterDialog = lazy(() => import('../shared/MAFFilterDialog')) -const HierarchicalClusterDialog = lazy( - () => import('../shared/HierarchicalClusterDialog'), -) +const ClusterDialog = lazy(() => import('../shared/ClusterDialog')) /** * #stateModel LinearVariantMatrixDisplay @@ -176,7 +174,7 @@ export default function stateModelFactory( label: 'Cluster by genotype', onClick: () => { getSession(self).queueDialog(handleClose => [ - HierarchicalClusterDialog, + ClusterDialog, { model: self, handleClose, @@ -252,6 +250,21 @@ export default function stateModelFactory( } } })() + + // eslint-disable-next-line @typescript-eslint/no-floating-promises + ;(async () => { + try { + const { getMultiVariantSourcesAutorun } = await import( + '../getMultiVariantSourcesAutorun' + ) + getMultiVariantSourcesAutorun(self) + } catch (e) { + if (isAlive(self)) { + console.error(e) + getSession(self).notifyError(`${e}`, e) + } + } + })() }, /** diff --git a/plugins/variants/src/MultiLinearVariantMatrixRenderer/components/LinearVariantMatrixRendering.tsx b/plugins/variants/src/MultiLinearVariantMatrixRenderer/components/LinearVariantMatrixRendering.tsx index ee24e51dfa..d29ee1ef00 100644 --- a/plugins/variants/src/MultiLinearVariantMatrixRenderer/components/LinearVariantMatrixRendering.tsx +++ b/plugins/variants/src/MultiLinearVariantMatrixRenderer/components/LinearVariantMatrixRendering.tsx @@ -17,11 +17,11 @@ const LinearVariantMatrixRendering = observer(function (props: { regions: Region[] bpPerPx: number simplifiedFeatures: Feature[] - exportSVG: boolean onMouseMove?: (event: React.MouseEvent, featureId?: string) => void }) { - const { exportSVG, simplifiedFeatures, displayModel, width, height } = props - const [renderLines, setRenderLines] = useState(exportSVG) + const { simplifiedFeatures, displayModel, width, height } = props + console.log('Rendering', props.regions) + const [renderLines, setRenderLines] = useState(false) useEffect(() => { setRenderLines(true) }, []) diff --git a/plugins/variants/src/VariantRPC/MultiVariantGetHierarchicalMatrix.ts b/plugins/variants/src/VariantRPC/MultiVariantGetGenotypeMatrix.ts similarity index 96% rename from plugins/variants/src/VariantRPC/MultiVariantGetHierarchicalMatrix.ts rename to plugins/variants/src/VariantRPC/MultiVariantGetGenotypeMatrix.ts index be0a2a51b1..d0f2ff6e01 100644 --- a/plugins/variants/src/VariantRPC/MultiVariantGetHierarchicalMatrix.ts +++ b/plugins/variants/src/VariantRPC/MultiVariantGetGenotypeMatrix.ts @@ -9,8 +9,8 @@ import type { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAda import type { RenderArgs } from '@jbrowse/core/rpc/coreRpcMethods' import type { Feature, Region } from '@jbrowse/core/util' -export class MultiVariantGetHierarchicalMatrix extends RpcMethodType { - name = 'MultiVariantGetHierarchicalMatrix' +export class MultiVariantGetGenotypeMatrix extends RpcMethodType { + name = 'MultiVariantGetGenotypeMatrix' async deserializeArguments(args: any, rpcDriverClassName: string) { const l = await super.deserializeArguments(args, rpcDriverClassName) diff --git a/plugins/variants/src/VariantRPC/MultiVariantGetSimplifiedFeatures.ts b/plugins/variants/src/VariantRPC/MultiVariantGetSimplifiedFeatures.ts new file mode 100644 index 0000000000..50d43c8c55 --- /dev/null +++ b/plugins/variants/src/VariantRPC/MultiVariantGetSimplifiedFeatures.ts @@ -0,0 +1,76 @@ +import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache' +import RpcMethodType from '@jbrowse/core/pluggableElementTypes/RpcMethodType' +import SerializableFilterChain from '@jbrowse/core/pluggableElementTypes/renderers/util/serializableFilterChain' +import { renameRegionsIfNeeded } from '@jbrowse/core/util' + +import type { AnyConfigurationModel } from '@jbrowse/core/configuration' +import type { RenderArgs } from '@jbrowse/core/rpc/coreRpcMethods' +import type { Region } from '@jbrowse/core/util' +import { toArray } from 'rxjs' + +export class MultiVariantGetSimplifiedFeatures extends RpcMethodType { + name = 'MultiVariantGetSimplifiedFeatures' + + async deserializeArguments(args: any, rpcDriverClassName: string) { + const l = await super.deserializeArguments(args, rpcDriverClassName) + return { + ...l, + filters: args.filters + ? new SerializableFilterChain({ + filters: args.filters, + }) + : undefined, + } + } + + async serializeArguments( + args: RenderArgs & { + stopToken?: string + statusCallback?: (arg: string) => void + }, + rpcDriverClassName: string, + ) { + const pm = this.pluginManager + const assemblyManager = pm.rootModel?.session?.assemblyManager + if (!assemblyManager) { + return args + } + + const renamedArgs = await renameRegionsIfNeeded(assemblyManager, { + ...args, + filters: args.filters?.toJSON().filters, + }) + + return super.serializeArguments(renamedArgs, rpcDriverClassName) + } + + async execute( + args: { + adapterConfig: AnyConfigurationModel + stopToken?: string + sessionId: string + headers?: Record + regions: Region[] + bpPerPx: number + }, + rpcDriverClassName: string, + ) { + const pm = this.pluginManager + const deserializedArgs = await this.deserializeArguments( + args, + rpcDriverClassName, + ) + const { regions, adapterConfig, sessionId } = deserializedArgs + const { dataAdapter } = await getAdapter(pm, sessionId, adapterConfig) + + // @ts-expect-error + const feats = await firstValuFrom( + dataAdapter.getFeatures(regions, deserializedArgs).pipe(toArray()), + ) + return feats.map(f => ({ + start: f.get('start'), + end: f.get('end'), + refName: f.get('refName'), + })) + } +} diff --git a/plugins/variants/src/getMultiVariantSourcesAutorun.ts b/plugins/variants/src/getMultiVariantSourcesAutorun.ts index 65276e67be..7c660a2f1f 100644 --- a/plugins/variants/src/getMultiVariantSourcesAutorun.ts +++ b/plugins/variants/src/getMultiVariantSourcesAutorun.ts @@ -42,7 +42,41 @@ export function getMultiVariantSourcesAutorun(self: { sessionId, 'MultiVariantGetSources', { - regions: view.staticBlocks.contentBlocks, + regions: view.dynamicBlocks.contentBlocks, + sessionId, + adapterConfig, + }, + )) as Source[] + if (isAlive(self)) { + self.setSources(sources) + } + } catch (e) { + if (!isAbortException(e) && isAlive(self)) { + console.error(e) + getSession(self).notifyError(`${e}`, e) + } + } + }), + ) + + addDisposer( + self, + autorun(async () => { + try { + const view = getContainingView(self) as LinearGenomeViewModel + if (!view.initialized) { + return + } + const { rpcManager } = getSession(self) + const { adapterConfig } = self + const token = createStopToken() + self.setSourcesLoading(token) + const sessionId = getRpcSessionId(self) + const sources = (await rpcManager.call( + sessionId, + 'MultiVariantGetSimplifiedFeatures', + { + regions: view.dynamicBlocks.contentBlocks, sessionId, adapterConfig, }, diff --git a/plugins/variants/src/index.ts b/plugins/variants/src/index.ts index e2295dd3cd..b01eecb32f 100644 --- a/plugins/variants/src/index.ts +++ b/plugins/variants/src/index.ts @@ -8,8 +8,9 @@ import LinearVariantMatrixRendererF from './MultiLinearVariantMatrixRenderer' import MultiVariantRendererF from './MultiLinearVariantRenderer' import StructuralVariantChordRendererF from './StructuralVariantChordRenderer' import VariantFeatureWidgetF from './VariantFeatureWidget' -import { MultiVariantGetHierarchicalMatrix } from './VariantRPC/MultiVariantGetHierarchicalMatrix' +import { MultiVariantGetGenotypeMatrix } from './VariantRPC/MultiVariantGetGenotypeMatrix' import { MultiVariantGetSources } from './VariantRPC/MultiVariantGetSources' +import { MultiVariantGetSimplifiedFeatures } from './VariantRPC/MultiVariantGetSimplifiedFeatures' import VariantTrackF from './VariantTrack' import VcfAdapterF from './VcfAdapter' import VcfTabixAdapterF from './VcfTabixAdapter' @@ -36,7 +37,10 @@ export default class VariantsPlugin extends Plugin { pluginManager.addRpcMethod(() => new MultiVariantGetSources(pluginManager)) pluginManager.addRpcMethod( - () => new MultiVariantGetHierarchicalMatrix(pluginManager), + () => new MultiVariantGetGenotypeMatrix(pluginManager), + ) + pluginManager.addRpcMethod( + () => new MultiVariantGetSimplifiedFeatures(pluginManager), ) } } diff --git a/plugins/variants/src/shared/HierarchicalClusterDialog.tsx b/plugins/variants/src/shared/ClusterDialog.tsx similarity index 99% rename from plugins/variants/src/shared/HierarchicalClusterDialog.tsx rename to plugins/variants/src/shared/ClusterDialog.tsx index daf763c046..3c0f4102ec 100644 --- a/plugins/variants/src/shared/HierarchicalClusterDialog.tsx +++ b/plugins/variants/src/shared/ClusterDialog.tsx @@ -68,7 +68,7 @@ export default function HierarchicalCluster({ const sessionId = getRpcSessionId(model) const ret = (await rpcManager.call( sessionId, - 'MultiVariantGetHierarchicalMatrix', + 'MultiVariantGetGenotypeMatrix', { regions: view.dynamicBlocks.contentBlocks, sources, diff --git a/yarn.lock b/yarn.lock index 7f0059b2a0..f90a5798ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -139,9 +139,9 @@ tslib "^2.6.2" "@aws-sdk/client-s3@^3.701.0": - version "3.705.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.705.0.tgz#7a4a4784bd5b3ca3187ff876b771eaf0cbde1c42" - integrity sha512-Fm0Cbc4zr0yG0DnNycz7ywlL5tQFdLSb7xCIPfzrxJb3YQiTXWxH5eu61SSsP/Z6RBNRolmRPvst/iNgX0fWvA== + version "3.703.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.703.0.tgz#5ca20c606e13ca751ef972c82bb8ef27095db083" + integrity sha512-4TSrIamzASTeRPKXrTLcEwo+viPTuOSGcbXh4HC1R0m/rXwK0BHJ4btJ0Q34nZNF+WzvM+FiemXVjNc8qTAxog== dependencies: "@aws-crypto/sha1-browser" "5.2.0" "@aws-crypto/sha256-browser" "5.2.0" @@ -8074,9 +8074,9 @@ dotenv-expand@^11.0.3, dotenv-expand@^11.0.6, dotenv-expand@~11.0.6: dotenv "^16.4.5" dotenv@^16.3.1, dotenv@^16.4.5, dotenv@~16.4.5: - version "16.4.7" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26" - integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ== + version "16.4.6" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.6.tgz#fc88e8a664087abf3e19d61e21f7feee1849bbb1" + integrity sha512-JhcR/+KIjkkjiU8yEpaB/USlzVi3i5whwOjpIRNGi9svKEXZSe+Qp6IWAjFjv+2GViAoDRCUv/QLNziQxsLqDg== duplexer@^0.1.1, duplexer@^0.1.2: version "0.1.2" @@ -16370,9 +16370,9 @@ whatwg-url@^11.0.0: webidl-conversions "^7.0.0" whatwg-url@^14.0.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.1.0.tgz#fffebec86cc8e6c2a657e50dc606207b870f0ab3" - integrity sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w== + version "14.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.0.0.tgz#00baaa7fd198744910c4b1ef68378f2200e4ceb6" + integrity sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw== dependencies: tr46 "^5.0.0" webidl-conversions "^7.0.0"