react-msaview 8.0.0 → 8.2.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 +3 -3
- package/dist/calculateBlocks.js +5 -3
- package/dist/calculateBlocks.js.map +1 -1
- package/dist/components/Loading.js +2 -8
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/SequenceTextArea.js +0 -1
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/Track.js +2 -2
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/AboutDialog.js +6 -6
- package/dist/components/dialogs/AboutDialog.js.map +1 -1
- package/dist/components/dialogs/AnnotationFileDialog.js +5 -2
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +1 -1
- package/dist/components/dialogs/FeatureDialog.js +8 -3
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/header/Header.js +1 -1
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/LoadWarnings.js +3 -19
- package/dist/components/header/LoadWarnings.js.map +1 -1
- package/dist/components/header/settingsMenuItems.js +3 -3
- 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/msa/MSACanvas.js +1 -3
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/drawFeatureSpans.d.ts +7 -1
- package/dist/components/msa/drawFeatureSpans.js +58 -23
- package/dist/components/msa/drawFeatureSpans.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +7 -8
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +8 -9
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/tracks/drawTracks.js +8 -5
- package/dist/components/tracks/drawTracks.js.map +1 -1
- package/dist/components/tree/TreeBranchMenu.js +1 -1
- package/dist/components/tree/TreeBranchMenu.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +1 -3
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +2 -2
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeNodeMenu.js +1 -1
- package/dist/components/tree/TreeNodeMenu.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +2 -1
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -1
- package/dist/expandSpec.d.ts +46 -0
- package/dist/expandSpec.js +127 -0
- package/dist/expandSpec.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model.d.ts +52 -33
- package/dist/model.js +232 -142
- package/dist/model.js.map +1 -1
- package/dist/svgTestUtil.d.ts +28 -24
- package/dist/types.d.ts +2 -0
- package/dist/useWheelScroll.js +0 -4
- package/dist/useWheelScroll.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/calculateBlocks.ts +5 -3
- package/src/components/Loading.tsx +2 -30
- package/src/components/SequenceTextArea.tsx +0 -1
- package/src/components/Track.tsx +2 -1
- package/src/components/dialogs/AboutDialog.tsx +35 -6
- package/src/components/dialogs/AnnotationFileDialog.tsx +10 -1
- package/src/components/dialogs/ExportSVGDialog.tsx +1 -1
- package/src/components/dialogs/FeatureDialog.tsx +11 -3
- package/src/components/header/Header.tsx +1 -1
- package/src/components/header/LoadWarnings.tsx +8 -21
- package/src/components/header/settingsMenuItems.ts +3 -3
- package/src/components/import/ImportForm.tsx +3 -5
- package/src/components/msa/MSACanvas.tsx +0 -3
- package/src/components/msa/drawFeatureSpans.ts +80 -25
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +7 -9
- package/src/components/msa/renderMSABlock.ts +9 -8
- package/src/components/tracks/drawTracks.ts +8 -16
- package/src/components/tree/TreeBranchMenu.tsx +1 -2
- package/src/components/tree/TreeCanvas.tsx +0 -3
- package/src/components/tree/TreeCanvasBlock.tsx +2 -2
- package/src/components/tree/TreeNodeMenu.tsx +1 -2
- package/src/components/tree/renderTreeCanvas.ts +2 -1
- package/src/constants.ts +5 -0
- package/src/expandSpec.ts +196 -0
- package/src/index.ts +8 -0
- package/src/model.ts +289 -162
- package/src/types.ts +2 -0
- package/src/useWheelScroll.ts +0 -4
- package/src/version.ts +1 -1
|
@@ -113,19 +113,18 @@ function drawTilesAndText({
|
|
|
113
113
|
columns,
|
|
114
114
|
colWidth,
|
|
115
115
|
rowHeight,
|
|
116
|
+
fontSize,
|
|
116
117
|
relativeTo,
|
|
117
118
|
showMsaLetters,
|
|
118
119
|
subFeatureRows,
|
|
119
120
|
domainUnderline,
|
|
120
121
|
domainBandsByStart,
|
|
122
|
+
featureColors,
|
|
121
123
|
} = model
|
|
122
124
|
|
|
123
125
|
const tiles = drawTiles && bgColor
|
|
124
126
|
const paintTiles = tiles && !rasterTiles
|
|
125
127
|
if (paintTiles || showMsaLetters) {
|
|
126
|
-
// a letter takes its color from the cell it lands on, so the scheme lookup
|
|
127
|
-
// is only waste when nothing draws letters
|
|
128
|
-
const needsColor = paintTiles || showMsaLetters
|
|
129
128
|
const tileColor = tileColorFn(model)
|
|
130
129
|
// Domain fills come from a categorical palette with no fixed lightness, and
|
|
131
130
|
// a dynamic scheme colors a cell from its column, so a readable letter color
|
|
@@ -133,8 +132,10 @@ function drawTilesAndText({
|
|
|
133
132
|
const contrastText = contrastTextFn(theme)
|
|
134
133
|
const offsetXAligned = xStart * colWidth
|
|
135
134
|
const halfColWidth = colWidth / 2
|
|
136
|
-
//
|
|
137
|
-
|
|
135
|
+
// the context is translated by rowHeight/2, so `y` is the row's bottom
|
|
136
|
+
// edge and the baseline lands fontSize/4 below the row center, where the
|
|
137
|
+
// tree puts its tip labels
|
|
138
|
+
const baselineOffset = rowHeight / 2 - fontSize / 4
|
|
138
139
|
// with the tiles coming from the domain overlay instead, letters sit either
|
|
139
140
|
// on a domain box or on the plain background; sub-row layout stacks the boxes
|
|
140
141
|
// above the letters and letter-color mode shrinks them to a bar under the
|
|
@@ -148,7 +149,7 @@ function drawTilesAndText({
|
|
|
148
149
|
const str = columns.get(name)?.slice(xStart, xEnd)
|
|
149
150
|
if (str) {
|
|
150
151
|
const tileY = y - rowHeight
|
|
151
|
-
const textY = y -
|
|
152
|
+
const textY = y - baselineOffset
|
|
152
153
|
const bandAt = domainBandCursor(
|
|
153
154
|
overDomains ? domainBandsByStart.get(name) : undefined,
|
|
154
155
|
)
|
|
@@ -159,7 +160,7 @@ function drawTilesAndText({
|
|
|
159
160
|
const x = j * colWidth + offsetXAligned
|
|
160
161
|
const isMatchingReference =
|
|
161
162
|
referenceSeq && name !== relativeTo && letter === referenceSeq[j]
|
|
162
|
-
const color =
|
|
163
|
+
const color = tileColor(col, letter)
|
|
163
164
|
|
|
164
165
|
if (paintTiles) {
|
|
165
166
|
ctx.fillStyle = isMatchingReference
|
|
@@ -172,7 +173,7 @@ function drawTilesAndText({
|
|
|
172
173
|
const covering = bandAt(col)
|
|
173
174
|
ctx.fillStyle = covering
|
|
174
175
|
? // on top of a domain box: contrast against the box fill
|
|
175
|
-
contrastText(
|
|
176
|
+
contrastText(featureColors.get(covering.annotation)?.fill)
|
|
176
177
|
: isMatchingReference
|
|
177
178
|
? // the dot sits on the faint reference-match wash, which is
|
|
178
179
|
// the theme's background with a little of its text color in it
|
|
@@ -309,18 +309,9 @@ export function drawTrackBlock({
|
|
|
309
309
|
blockSizeXOverride?: number
|
|
310
310
|
highResScaleFactorOverride?: number
|
|
311
311
|
}) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
colWidth,
|
|
316
|
-
colStats,
|
|
317
|
-
colorScheme: modelColorScheme,
|
|
318
|
-
fontSize,
|
|
319
|
-
rowHeight,
|
|
320
|
-
showMsaLetters,
|
|
321
|
-
alphabetMaxBits,
|
|
322
|
-
highResScaleFactor,
|
|
323
|
-
} = model
|
|
312
|
+
// an autorun tracks every model read here, so each kind reads only what it
|
|
313
|
+
// draws and a vertical zoom leaves the bar and arc canvases alone
|
|
314
|
+
const { blockSize, colWidth, highResScaleFactor } = model
|
|
324
315
|
const {
|
|
325
316
|
id,
|
|
326
317
|
kind,
|
|
@@ -367,9 +358,9 @@ export function drawTrackBlock({
|
|
|
367
358
|
case 'logo': {
|
|
368
359
|
drawSequenceLogo({
|
|
369
360
|
ctx,
|
|
370
|
-
colStats,
|
|
371
|
-
colorScheme:
|
|
372
|
-
maxBits: alphabetMaxBits,
|
|
361
|
+
colStats: model.colStats,
|
|
362
|
+
colorScheme: model.colorScheme,
|
|
363
|
+
maxBits: model.alphabetMaxBits,
|
|
373
364
|
textColor,
|
|
374
365
|
colWidth,
|
|
375
366
|
trackHeight,
|
|
@@ -399,13 +390,14 @@ export function drawTrackBlock({
|
|
|
399
390
|
break
|
|
400
391
|
}
|
|
401
392
|
case 'text': {
|
|
393
|
+
const { bgColor, fontSize, rowHeight, showMsaLetters } = model
|
|
402
394
|
ctx.textAlign = 'center'
|
|
403
395
|
ctx.textBaseline = 'middle'
|
|
404
396
|
setFontSize(ctx, fontSize)
|
|
405
397
|
drawTextTrackContent({
|
|
406
398
|
ctx,
|
|
407
399
|
data,
|
|
408
|
-
colorScheme: customColorScheme ??
|
|
400
|
+
colorScheme: customColorScheme ?? model.colorScheme,
|
|
409
401
|
contrastText: contrastTextFn(theme),
|
|
410
402
|
bgColor,
|
|
411
403
|
drawLetters: showMsaLetters,
|
|
@@ -13,12 +13,12 @@ import { useTreeHover } from './useTreeHover.ts'
|
|
|
13
13
|
|
|
14
14
|
import type { MsaViewModel } from '../../model.ts'
|
|
15
15
|
|
|
16
|
-
const useStyles = makeStyles()(
|
|
16
|
+
const useStyles = makeStyles()(theme => ({
|
|
17
17
|
hover: {
|
|
18
18
|
position: 'absolute',
|
|
19
19
|
pointerEvents: 'none',
|
|
20
20
|
zIndex: 100,
|
|
21
|
-
background:
|
|
21
|
+
background: theme.palette.action.hover,
|
|
22
22
|
},
|
|
23
23
|
}))
|
|
24
24
|
|
|
@@ -402,7 +402,8 @@ function renderTreeLabels({
|
|
|
402
402
|
// label when the "name" is just the auto-generated internal-node id
|
|
403
403
|
const isAnonymousCollapsed = collapsedSet.has(id) && name === id
|
|
404
404
|
if (!isAnonymousCollapsed && inYBlock(y, offsetY, by, pad)) {
|
|
405
|
-
//
|
|
405
|
+
// fontSize/4 below the row center, the baseline the alignment letters
|
|
406
|
+
// share
|
|
406
407
|
const yp = y + fontSize / 4
|
|
407
408
|
let xp = 0
|
|
408
409
|
if (!noTree) {
|
package/src/constants.ts
CHANGED
|
@@ -48,6 +48,11 @@ export const defaultScrollZoomAxis: ScrollZoomAxis = 'both'
|
|
|
48
48
|
export const minLetterRowHeight = 8
|
|
49
49
|
export const minLetterColWidth = 5
|
|
50
50
|
|
|
51
|
+
// A feature span draws its label at two pixels under its height, capped at 11,
|
|
52
|
+
// so a span shorter than this holds no readable text: an underline bar, a
|
|
53
|
+
// sub-row band or a features panel over small rows
|
|
54
|
+
export const minFeatureLabelHeight = 9
|
|
55
|
+
|
|
51
56
|
// Tree labels are measured once at this size and scaled to the current font
|
|
52
57
|
// size, so a vertical zoom never re-measures the tree.
|
|
53
58
|
export const labelReferenceFontSize = 16
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { ColumnTrackSpec, Highlight, Region } from './types.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `row: null` opts one entry out of the spec's `query` row, back to alignment
|
|
5
|
+
* columns.
|
|
6
|
+
*/
|
|
7
|
+
type RowChoice = { row?: string | null }
|
|
8
|
+
|
|
9
|
+
export type HighlightShorthand =
|
|
10
|
+
| number
|
|
11
|
+
| string
|
|
12
|
+
| (Omit<Highlight, 'row'> & RowChoice)
|
|
13
|
+
|
|
14
|
+
export type RegionShorthand = string | (Omit<Region, 'row'> & RowChoice)
|
|
15
|
+
|
|
16
|
+
export type ColumnTrackShorthand = Omit<
|
|
17
|
+
ColumnTrackSpec,
|
|
18
|
+
'id' | 'kind' | 'row'
|
|
19
|
+
> &
|
|
20
|
+
RowChoice & {
|
|
21
|
+
id?: string
|
|
22
|
+
kind?: ColumnTrackSpec['kind']
|
|
23
|
+
/** the 1-based position `values` or `data` begins at */
|
|
24
|
+
start?: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The short forms a hand-written or generated link can use. Every long form
|
|
29
|
+
* passes through unchanged, so a snapshot is also a valid spec.
|
|
30
|
+
*/
|
|
31
|
+
export interface MsaSpec {
|
|
32
|
+
/**
|
|
33
|
+
* The row the spec is about: sets `relativeTo`, and is the `row` of every
|
|
34
|
+
* highlight, region and column track that names none
|
|
35
|
+
*/
|
|
36
|
+
query?: string
|
|
37
|
+
/** a url, or the alignment itself when it spans more than one line */
|
|
38
|
+
msa?: string
|
|
39
|
+
/** a url, or a newick string when it starts with "(" */
|
|
40
|
+
tree?: string
|
|
41
|
+
highlights?: HighlightShorthand[]
|
|
42
|
+
region?: RegionShorthand
|
|
43
|
+
columnTracks?: ColumnTrackShorthand[]
|
|
44
|
+
[key: string]: unknown
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The label a single-residue shorthand highlight gets, filled in from the
|
|
49
|
+
* sequence when drawn: `175` on a row reading R there becomes "R175".
|
|
50
|
+
*/
|
|
51
|
+
export const residueLabel = '{residue}{position}'
|
|
52
|
+
|
|
53
|
+
const span = /^(\d+)(?:-(\d+))?(?:\s+(.+))?$/
|
|
54
|
+
|
|
55
|
+
function parseSpan(text: string, what: string) {
|
|
56
|
+
const match = span.exec(text.trim())
|
|
57
|
+
if (!match) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`${what} "${text}" is not "start", "start-end" or either followed by a label`,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
const start = Number(match[1])
|
|
63
|
+
const end = match[2] === undefined ? start : Number(match[2])
|
|
64
|
+
return { start, end, label: match[3] }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function withRow<T extends RowChoice>(entry: T, query?: string) {
|
|
68
|
+
const { row, ...rest } = entry
|
|
69
|
+
const resolved = row === undefined ? query : (row ?? undefined)
|
|
70
|
+
return resolved === undefined ? rest : { ...rest, row: resolved }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function expandHighlight(entry: HighlightShorthand, query?: string) {
|
|
74
|
+
if (typeof entry === 'object') {
|
|
75
|
+
return (
|
|
76
|
+
entry.rows
|
|
77
|
+
? entry
|
|
78
|
+
: withRow(entry, entry.start === undefined ? undefined : query)
|
|
79
|
+
) as Highlight
|
|
80
|
+
}
|
|
81
|
+
const { start, end, label } = parseSpan(String(entry), 'highlight')
|
|
82
|
+
const single = start === end && query !== undefined
|
|
83
|
+
return {
|
|
84
|
+
...(query === undefined ? {} : { row: query }),
|
|
85
|
+
start,
|
|
86
|
+
end,
|
|
87
|
+
...(label ? { label } : single ? { label: residueLabel } : {}),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function expandRegion(region: RegionShorthand, query?: string) {
|
|
92
|
+
if (typeof region === 'string') {
|
|
93
|
+
const { start, end } = parseSpan(region, 'region')
|
|
94
|
+
const parsed: Omit<Region, 'row'> & RowChoice = { start, end }
|
|
95
|
+
return withRow(parsed, query) as Region
|
|
96
|
+
}
|
|
97
|
+
return withRow(region, query) as Region
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function slug(name: string) {
|
|
101
|
+
return name
|
|
102
|
+
.toLowerCase()
|
|
103
|
+
.replaceAll(/[^a-z0-9]+/g, '-')
|
|
104
|
+
.replaceAll(/^-|-$/g, '')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function kindOf(track: ColumnTrackShorthand) {
|
|
108
|
+
if (track.kind) {
|
|
109
|
+
return track.kind
|
|
110
|
+
}
|
|
111
|
+
if (track.values) {
|
|
112
|
+
return 'bar'
|
|
113
|
+
}
|
|
114
|
+
if (track.data !== undefined) {
|
|
115
|
+
return 'text'
|
|
116
|
+
}
|
|
117
|
+
if (track.arcs) {
|
|
118
|
+
return 'arc'
|
|
119
|
+
}
|
|
120
|
+
throw new Error(
|
|
121
|
+
`column track "${track.name}" has no values, data or arcs to take its kind from`,
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function expandTrack(
|
|
126
|
+
track: ColumnTrackShorthand,
|
|
127
|
+
query: string | undefined,
|
|
128
|
+
ids: Set<string>,
|
|
129
|
+
): ColumnTrackSpec {
|
|
130
|
+
const { start, ...rest } = track
|
|
131
|
+
const pad = start && start > 1 ? start - 1 : 0
|
|
132
|
+
let id = track.id ?? (slug(track.name) || 'track')
|
|
133
|
+
for (let n = 2; !track.id && ids.has(id); n++) {
|
|
134
|
+
id = `${slug(track.name) || 'track'}-${n}`
|
|
135
|
+
}
|
|
136
|
+
ids.add(id)
|
|
137
|
+
return {
|
|
138
|
+
...(withRow(rest, query) as Omit<ColumnTrackSpec, 'id' | 'kind'>),
|
|
139
|
+
id,
|
|
140
|
+
kind: kindOf(track),
|
|
141
|
+
...(pad && track.values
|
|
142
|
+
? { values: [...Array<number>(pad).fill(0), ...track.values] }
|
|
143
|
+
: {}),
|
|
144
|
+
...(pad && track.data !== undefined
|
|
145
|
+
? { data: ' '.repeat(pad) + track.data }
|
|
146
|
+
: {}),
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function isInlineMsa(msa: string) {
|
|
151
|
+
return msa.includes('\n')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function isInlineTree(tree: string) {
|
|
155
|
+
return tree.trimStart().startsWith('(')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function uri(location: string) {
|
|
159
|
+
return { uri: location, locationType: 'UriLocation' as const }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Expand the short forms of an MsaView spec into the snapshot the model takes.
|
|
164
|
+
* Idempotent, and a no-op on a spec that uses none of them.
|
|
165
|
+
*/
|
|
166
|
+
export function expandSpec(spec: MsaSpec): Record<string, unknown> {
|
|
167
|
+
const { query, msa, tree, highlights, region, columnTracks, ...rest } = spec
|
|
168
|
+
const data = {
|
|
169
|
+
...(rest.data as Record<string, unknown> | undefined),
|
|
170
|
+
...(msa !== undefined && isInlineMsa(msa) ? { msa } : {}),
|
|
171
|
+
...(tree !== undefined && isInlineTree(tree) ? { tree } : {}),
|
|
172
|
+
}
|
|
173
|
+
const ids = new Set(
|
|
174
|
+
(columnTracks ?? []).map(t => t.id).filter(id => id !== undefined),
|
|
175
|
+
)
|
|
176
|
+
return {
|
|
177
|
+
...rest,
|
|
178
|
+
...(msa !== undefined && !isInlineMsa(msa)
|
|
179
|
+
? { msaFilehandle: uri(msa) }
|
|
180
|
+
: {}),
|
|
181
|
+
...(tree !== undefined && !isInlineTree(tree)
|
|
182
|
+
? { treeFilehandle: uri(tree) }
|
|
183
|
+
: {}),
|
|
184
|
+
...(Object.keys(data).length > 0 ? { data } : {}),
|
|
185
|
+
...(query !== undefined && rest.relativeTo === undefined
|
|
186
|
+
? { relativeTo: query }
|
|
187
|
+
: {}),
|
|
188
|
+
...(highlights
|
|
189
|
+
? { highlights: highlights.map(h => expandHighlight(h, query)) }
|
|
190
|
+
: {}),
|
|
191
|
+
...(region === undefined ? {} : { region: expandRegion(region, query) }),
|
|
192
|
+
...(columnTracks
|
|
193
|
+
? { columnTracks: columnTracks.map(t => expandTrack(t, query, ids)) }
|
|
194
|
+
: {}),
|
|
195
|
+
}
|
|
196
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,14 @@ export {
|
|
|
18
18
|
// the row count above which calculateNeighborJoiningTreeFromMSA throws, for
|
|
19
19
|
// hosts gating their own menu item
|
|
20
20
|
export { maxNeighborJoiningRows } from './constants.ts'
|
|
21
|
+
// the short forms a link or a script can write a view in; see docs/layers.md
|
|
22
|
+
export { expandSpec, residueLabel } from './expandSpec.ts'
|
|
23
|
+
export type {
|
|
24
|
+
ColumnTrackShorthand,
|
|
25
|
+
HighlightShorthand,
|
|
26
|
+
MsaSpec,
|
|
27
|
+
RegionShorthand,
|
|
28
|
+
} from './expandSpec.ts'
|
|
21
29
|
export { default as MSAView } from './components/Loading.tsx'
|
|
22
30
|
export { default as MSAViewer } from './components/MSAViewer.tsx'
|
|
23
31
|
export type { MSAViewerProps } from './components/MSAViewer.tsx'
|