react-msaview 5.10.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/index.js +99 -99
- package/bundle/index.js.map +4 -4
- package/dist/clustalX.js +22 -22
- package/dist/clustalX.js.map +1 -1
- package/dist/columnCounts.d.ts +7 -0
- package/dist/columnCounts.js +18 -0
- package/dist/columnCounts.js.map +1 -1
- package/dist/components/CopyButton.d.ts +9 -0
- package/dist/components/CopyButton.js +23 -0
- package/dist/components/CopyButton.js.map +1 -0
- package/dist/components/DragHandle.d.ts +25 -0
- package/dist/components/DragHandle.js +49 -0
- package/dist/components/DragHandle.js.map +1 -0
- package/dist/components/Loading.js +2 -2
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.js +2 -2
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/ResizeHandles.d.ts +4 -2
- package/dist/components/ResizeHandles.js +35 -54
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/SequenceTextArea.js +3 -10
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/VerticalScrollbar.js +11 -25
- package/dist/components/VerticalScrollbar.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +3 -3
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/dialogs/InterProScanDialog.js +52 -129
- package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
- package/dist/components/dialogs/{UserProvidedDomainsDialog.d.ts → InterProScanFileDialog.d.ts} +2 -2
- package/dist/components/dialogs/{UserProvidedDomainsDialog.js → InterProScanFileDialog.js} +6 -10
- package/dist/components/dialogs/InterProScanFileDialog.js.map +1 -0
- package/dist/components/dialogs/TrackInfoDialog.d.ts +2 -4
- package/dist/components/dialogs/TrackInfoDialog.js +8 -14
- package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
- package/dist/components/header/FileMenu.js +3 -3
- package/dist/components/header/FileMenu.js.map +1 -1
- package/dist/components/header/GappynessSlider.js +1 -1
- package/dist/components/header/GappynessSlider.js.map +1 -1
- package/dist/components/header/{getDomainsMenu.d.ts → getAnnotationsMenu.d.ts} +1 -1
- package/dist/components/header/{getDomainsMenu.js → getAnnotationsMenu.js} +10 -9
- package/dist/components/header/getAnnotationsMenu.js.map +1 -0
- package/dist/components/header/settingsMenuItems.js +15 -0
- package/dist/components/header/settingsMenuItems.js.map +1 -1
- package/dist/components/import/ImportForm.js +1 -1
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/import/ImportFormExamples.js +66 -57
- package/dist/components/import/ImportFormExamples.js.map +1 -1
- package/dist/components/minimap/Minimap.js +40 -23
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.d.ts +2 -1
- package/dist/components/minimap/MinimapSVG.js +3 -4
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/minimap/minimapLayout.d.ts +10 -1
- package/dist/components/minimap/minimapLayout.js +15 -10
- package/dist/components/minimap/minimapLayout.js.map +1 -1
- package/dist/components/msa/{DomainLegend.d.ts → AnnotationLegend.d.ts} +2 -2
- package/dist/components/msa/{DomainLegend.js → AnnotationLegend.js} +4 -4
- package/dist/components/msa/AnnotationLegend.js.map +1 -0
- package/dist/components/msa/MSACanvas.js +9 -3
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +12 -3
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +1 -2
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/MSAPanel.js +2 -2
- package/dist/components/msa/MSAPanel.js.map +1 -1
- package/dist/components/msa/domainBandCursor.d.ts +17 -0
- package/dist/components/msa/domainBandCursor.js +39 -0
- package/dist/components/msa/domainBandCursor.js.map +1 -0
- package/dist/components/msa/msaRaster.d.ts +63 -0
- package/dist/components/msa/msaRaster.js +252 -0
- package/dist/components/msa/msaRaster.js.map +1 -0
- package/dist/components/msa/renderMSABlock.d.ts +2 -1
- package/dist/components/msa/renderMSABlock.js +27 -41
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +1 -2
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/msa/tileColor.d.ts +6 -0
- package/dist/components/msa/tileColor.js +35 -0
- package/dist/components/msa/tileColor.js.map +1 -0
- package/dist/components/renderCtx.d.ts +1 -1
- package/dist/components/tracks/TrackBlocks.d.ts +14 -0
- package/dist/components/tracks/TrackBlocks.js +65 -0
- package/dist/components/tracks/TrackBlocks.js.map +1 -0
- package/dist/components/tracks/{renderTracksSvg.d.ts → drawTracks.d.ts} +22 -10
- package/dist/components/tracks/drawTracks.js +193 -0
- package/dist/components/tracks/drawTracks.js.map +1 -0
- package/dist/components/tree/TreeCanvas.js +8 -2
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +7 -4
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeRuler.js +5 -2
- package/dist/components/tree/TreeRuler.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +5 -2
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -1
- package/dist/ggplotPalettes.d.ts +0 -1
- package/dist/ggplotPalettes.js +0 -3
- package/dist/ggplotPalettes.js.map +1 -1
- package/dist/hierarchy.d.ts +2 -6
- package/dist/hierarchy.js +4 -6
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/launchInterProScan.d.ts +0 -23
- package/dist/launchInterProScan.js +2 -5
- package/dist/launchInterProScan.js.map +1 -1
- package/dist/measureTextCanvas.js +21 -6
- package/dist/measureTextCanvas.js.map +1 -1
- package/dist/model/DataModel.d.ts +1 -4
- package/dist/model/DataModel.js +8 -10
- package/dist/model/DataModel.js.map +1 -1
- package/dist/model.d.ts +86 -107
- package/dist/model.js +238 -162
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.js +34 -18
- package/dist/neighborJoining.js.map +1 -1
- package/dist/renderTestEnv.d.ts +43 -0
- package/dist/renderTestEnv.js +88 -0
- package/dist/renderTestEnv.js.map +1 -0
- package/dist/renderToSvg.js +3 -3
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +13 -17
- package/dist/rowCoordinateCalculations.js +45 -39
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/sequenceLogo.d.ts +28 -0
- package/dist/sequenceLogo.js +47 -0
- package/dist/sequenceLogo.js.map +1 -0
- package/dist/types.d.ts +6 -11
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +5 -5
- package/src/clustalX.ts +22 -22
- package/src/colSpace.test.ts +27 -0
- package/src/columnCounts.test.ts +15 -0
- package/src/columnCounts.ts +19 -0
- package/src/components/CopyButton.tsx +44 -0
- package/src/components/DragHandle.tsx +81 -0
- package/src/components/Loading.tsx +10 -1
- package/src/components/MSAView.tsx +2 -3
- package/src/components/ResizeHandles.tsx +70 -87
- package/src/components/SequenceTextArea.tsx +3 -16
- package/src/components/VerticalScrollbar.tsx +20 -33
- package/src/components/dialogs/FeatureDialog.tsx +3 -4
- package/src/components/dialogs/InterProScanDialog.tsx +54 -131
- package/src/components/dialogs/{UserProvidedDomainsDialog.tsx → InterProScanFileDialog.tsx} +6 -14
- package/src/components/dialogs/TrackInfoDialog.tsx +18 -20
- package/src/components/header/FileMenu.tsx +3 -3
- package/src/components/header/GappynessSlider.tsx +4 -1
- package/src/components/header/{getDomainsMenu.ts → getAnnotationsMenu.ts} +10 -9
- package/src/components/header/settingsMenuItems.ts +18 -0
- package/src/components/import/ImportForm.tsx +1 -1
- package/src/components/import/ImportFormExamples.tsx +82 -133
- package/src/components/minimap/Minimap.tsx +67 -32
- package/src/components/minimap/MinimapSVG.tsx +32 -31
- package/src/components/minimap/minimapLayout.test.ts +60 -0
- package/src/components/minimap/minimapLayout.ts +15 -10
- package/src/components/msa/{DomainLegend.tsx → AnnotationLegend.tsx} +7 -3
- package/src/components/msa/MSACanvas.tsx +24 -11
- package/src/components/msa/MSACanvasBlock.tsx +13 -3
- package/src/components/msa/MSAMouseoverCanvas.tsx +1 -3
- package/src/components/msa/MSAPanel.tsx +2 -2
- package/src/components/msa/domainBandCursor.ts +41 -0
- package/src/components/msa/msaRaster.test.ts +145 -0
- package/src/components/msa/msaRaster.ts +350 -0
- package/src/components/msa/renderMSABlock.ts +34 -43
- package/src/components/msa/renderMSAMouseover.ts +1 -3
- package/src/components/msa/tileColor.ts +37 -0
- package/src/components/renderCtx.ts +2 -0
- package/src/components/tracks/TrackBlocks.tsx +111 -0
- package/src/components/tracks/drawTracks.ts +323 -0
- package/src/components/tree/TreeCanvas.tsx +16 -4
- package/src/components/tree/TreeCanvasBlock.tsx +18 -11
- package/src/components/tree/TreeRuler.tsx +7 -2
- package/src/components/tree/renderTreeCanvas.test.ts +15 -7
- package/src/components/tree/renderTreeCanvas.ts +5 -1
- package/src/constants.ts +10 -0
- package/src/domainBands.test.ts +24 -0
- package/src/domainLetterColor.test.ts +80 -0
- package/src/ggplotPalettes.ts +0 -4
- package/src/hierarchy.test.ts +7 -7
- package/src/hierarchy.ts +6 -9
- package/src/impgRender.test.tsx +2 -57
- package/src/index.ts +4 -1
- package/src/largeTree.test.ts +39 -4
- package/src/launchInterProScan.ts +3 -30
- package/src/loadErrorDisplay.test.tsx +102 -0
- package/src/measureTextCanvas.ts +22 -6
- package/src/model/DataModel.ts +11 -11
- package/src/model.ts +262 -172
- package/src/neighborJoining.ts +36 -18
- package/src/renderDomainsSvg.test.tsx +2 -57
- package/src/renderMinimapSvg.test.tsx +55 -0
- package/src/renderSequenceLogoSvg.test.tsx +176 -0
- package/src/renderTestEnv.ts +96 -0
- package/src/renderToSvg.tsx +3 -3
- package/src/rowCoordinateCalculations.ts +48 -44
- package/src/sequenceLogo.test.ts +88 -0
- package/src/sequenceLogo.ts +63 -0
- package/src/types.ts +17 -16
- package/src/version.ts +1 -1
- package/src/viewChrome.test.tsx +134 -0
- package/dist/components/ConservationTrack.d.ts +0 -8
- package/dist/components/ConservationTrack.js +0 -49
- package/dist/components/ConservationTrack.js.map +0 -1
- package/dist/components/TextTrack.d.ts +0 -8
- package/dist/components/TextTrack.js +0 -58
- package/dist/components/TextTrack.js.map +0 -1
- package/dist/components/dialogs/DomainDialog.d.ts +0 -6
- package/dist/components/dialogs/DomainDialog.js +0 -19
- package/dist/components/dialogs/DomainDialog.js.map +0 -1
- package/dist/components/dialogs/UserProvidedDomainsDialog.js.map +0 -1
- package/dist/components/header/SettingsMenu.d.ts +0 -6
- package/dist/components/header/SettingsMenu.js +0 -40
- package/dist/components/header/SettingsMenu.js.map +0 -1
- package/dist/components/header/getDomainsMenu.js.map +0 -1
- package/dist/components/msa/DomainLegend.js.map +0 -1
- package/dist/components/tracks/renderTracksSvg.js +0 -126
- package/dist/components/tracks/renderTracksSvg.js.map +0 -1
- package/src/components/ConservationTrack.tsx +0 -99
- package/src/components/TextTrack.tsx +0 -103
- package/src/components/dialogs/DomainDialog.tsx +0 -45
- package/src/components/header/SettingsMenu.tsx +0 -57
- package/src/components/tracks/renderTracksSvg.ts +0 -247
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
//
|
|
3
|
+
// What a failed load LOOKS like, which is a different question from what the
|
|
4
|
+
// model recorded. modelFilehandleLoaders.test.ts already pins the model half --
|
|
5
|
+
// a rejected fetch sets `error` and clears `loadingMSA`, under the name "a
|
|
6
|
+
// failed fetch surfaces as an error" -- and it passed throughout the whole time
|
|
7
|
+
// a failed url showed the reader a spinner and nothing else.
|
|
8
|
+
//
|
|
9
|
+
// The error display was never missing. ImportForm renders `model.error`, and
|
|
10
|
+
// the reader could not get to it: a non-abort failure KEEPS its filehandle
|
|
11
|
+
// (only an abort clears it), so `hasPendingFilehandle` stayed true and
|
|
12
|
+
// Loading.tsx took its spinner branch ahead of the import form, permanently.
|
|
13
|
+
// Found on AlphaFold's `files/msa/` prefix answering 403 to every key in August
|
|
14
|
+
// 2026 -- the view opened, said "Downloading file" with a Cancel button, and
|
|
15
|
+
// was still saying it at 105s.
|
|
16
|
+
import React, { act } from 'react'
|
|
17
|
+
|
|
18
|
+
import { createRoot } from 'react-dom/client'
|
|
19
|
+
import { afterEach, beforeEach, expect, test } from 'vitest'
|
|
20
|
+
|
|
21
|
+
import MSAView from './components/Loading.tsx'
|
|
22
|
+
import MSAModelF from './model.ts'
|
|
23
|
+
|
|
24
|
+
import type { MsaViewModel } from './model.ts'
|
|
25
|
+
import type { Root } from 'react-dom/client'
|
|
26
|
+
|
|
27
|
+
Reflect.set(globalThis, 'IS_REACT_ACT_ENVIRONMENT', true)
|
|
28
|
+
|
|
29
|
+
let container: HTMLDivElement
|
|
30
|
+
let root: Root
|
|
31
|
+
let model: MsaViewModel
|
|
32
|
+
|
|
33
|
+
function uri(u: string) {
|
|
34
|
+
return { locationType: 'UriLocation' as const, uri: u }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function render() {
|
|
38
|
+
act(() => {
|
|
39
|
+
root.render(<MSAView model={model} />)
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
container = document.createElement('div')
|
|
45
|
+
document.body.append(container)
|
|
46
|
+
root = createRoot(container)
|
|
47
|
+
model = MSAModelF().create({ type: 'MsaView' })
|
|
48
|
+
model.setWidth(800)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
act(() => {
|
|
53
|
+
root.unmount()
|
|
54
|
+
})
|
|
55
|
+
container.remove()
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('a load that fails shows the error rather than a spinner forever', () => {
|
|
59
|
+
// the state the loader leaves behind on a 403: the filehandle it could not
|
|
60
|
+
// read is still set, nothing parsed, and the error recorded
|
|
61
|
+
model.setMSAFilehandle(uri('https://example.com/blocked.a3m'))
|
|
62
|
+
model.setLoadingMSA(false)
|
|
63
|
+
model.setError(new Error('HTTP 403 fetching blocked.a3m'))
|
|
64
|
+
render()
|
|
65
|
+
|
|
66
|
+
expect(container.textContent).toContain('403')
|
|
67
|
+
expect(container.textContent).not.toContain('Loading')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('a load still in flight is a spinner, not an error', () => {
|
|
71
|
+
model.setMSAFilehandle(uri('https://example.com/slow.a3m'))
|
|
72
|
+
render()
|
|
73
|
+
|
|
74
|
+
expect(model.loadingMSA).toBe(true)
|
|
75
|
+
expect(container.textContent).not.toContain('Return to import form')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// A guard rather than a pin: this one passes without the fix too, because with
|
|
79
|
+
// no filehandle left set the view reaches the ImportForm and that already shows
|
|
80
|
+
// the error. It is here so the new branch cannot quietly swallow the case.
|
|
81
|
+
//
|
|
82
|
+
// It also records the surprise underneath: `dataInitialized` is itself
|
|
83
|
+
// `(msa || tree) && !error`, so ANY error hides a loaded alignment, including a
|
|
84
|
+
// tree or gff that 404s beside a good one -- setError is shared by every
|
|
85
|
+
// filehandle loader. Whether a failed OPTIONAL file should blank a good
|
|
86
|
+
// alignment is a real question and a wider one, since `dataInitialized` is read
|
|
87
|
+
// in several places; narrowing it is a design change rather than this fix.
|
|
88
|
+
test('an error over loaded data says so instead of a bare import form', () => {
|
|
89
|
+
const loaded = MSAModelF().create({
|
|
90
|
+
type: 'MsaView',
|
|
91
|
+
msaFormat: 'fasta',
|
|
92
|
+
height: 200,
|
|
93
|
+
data: { msa: '>seq1\nACDE\n>seq2\nACDF' },
|
|
94
|
+
})
|
|
95
|
+
loaded.setWidth(800)
|
|
96
|
+
loaded.setError(new Error('domains.gff 404'))
|
|
97
|
+
act(() => {
|
|
98
|
+
root.render(<MSAView model={loaded} />)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
expect(container.textContent).toContain('domains.gff 404')
|
|
102
|
+
})
|
package/src/measureTextCanvas.ts
CHANGED
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
import { setFontSize } from './setFontSize.ts'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Cached across calls: labelWidthMap measures every leaf label on every layout,
|
|
4
|
+
// and both createElement and getContext are far more expensive than measureText.
|
|
5
|
+
// `null` records that this environment has no 2d context, so the lookup is not
|
|
6
|
+
// retried on every label.
|
|
7
|
+
let ctxHandle: CanvasRenderingContext2D | null | undefined
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
if (
|
|
7
|
-
|
|
9
|
+
function getCtx() {
|
|
10
|
+
if (ctxHandle === undefined) {
|
|
11
|
+
ctxHandle =
|
|
12
|
+
typeof document === 'undefined'
|
|
13
|
+
? null
|
|
14
|
+
: document.createElement('canvas').getContext('2d')
|
|
8
15
|
}
|
|
16
|
+
return ctxHandle
|
|
17
|
+
}
|
|
9
18
|
|
|
10
|
-
|
|
19
|
+
// Rough average glyph width as a fraction of the font size for the sans-serif
|
|
20
|
+
// stack the tree labels use. Only reached where there is no canvas to measure
|
|
21
|
+
// with (jsdom, SSR); estimating the label gutter beats throwing out of the
|
|
22
|
+
// layout getters that read this on every render.
|
|
23
|
+
const fallbackGlyphWidthRatio = 0.6
|
|
24
|
+
|
|
25
|
+
export function measureTextCanvas(text: string, fontSize: number) {
|
|
26
|
+
const ctx = getCtx()
|
|
11
27
|
if (!ctx) {
|
|
12
|
-
|
|
28
|
+
return text.length * fontSize * fallbackGlyphWidthRatio
|
|
13
29
|
}
|
|
14
30
|
setFontSize(ctx, fontSize)
|
|
15
31
|
return ctx.measureText(text).width
|
package/src/model/DataModel.ts
CHANGED
|
@@ -53,15 +53,15 @@ export function DataModelF() {
|
|
|
53
53
|
self.gff = gff
|
|
54
54
|
},
|
|
55
55
|
}))
|
|
56
|
-
.postProcessSnapshot(snap =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
.postProcessSnapshot(snap =>
|
|
57
|
+
// a large document is dropped from the snapshot rather than inlined
|
|
58
|
+
// into a session or a shared URL, and every field here holds one, so
|
|
59
|
+
// the rule is the same for all of them
|
|
60
|
+
Object.fromEntries(
|
|
61
|
+
Object.entries(snap).map(([key, text]) => [
|
|
62
|
+
key,
|
|
63
|
+
text && text.length > 50_000 ? undefined : text,
|
|
64
|
+
]),
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
67
|
}
|