@platforma-sdk/ui-vue 1.45.35 → 1.45.36

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.
Files changed (117) hide show
  1. package/.turbo/turbo-build.log +203 -234
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +6 -0
  4. package/dist/AgGridVue/useAgGridOptions.js +2 -3
  5. package/dist/AgGridVue/useAgGridOptions.js.map +1 -1
  6. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js +7 -8
  7. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js.map +1 -1
  8. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js +9 -10
  9. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js.map +1 -1
  10. package/dist/components/PlAgRowNumHeader.vue.js +2 -3
  11. package/dist/components/PlAgRowNumHeader.vue.js.map +1 -1
  12. package/dist/index.js +48 -50
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib.d.ts +0 -1
  15. package/package.json +4 -5
  16. package/src/lib.ts +0 -2
  17. package/dist/assets/multi-sequence-alignment.worker-Cm0gZp19.js +0 -6
  18. package/dist/assets/multi-sequence-alignment.worker-Cm0gZp19.js.map +0 -1
  19. package/dist/assets/phylogenetic-tree.worker-4CrExYEo.js +0 -5
  20. package/dist/assets/phylogenetic-tree.worker-4CrExYEo.js.map +0 -1
  21. package/dist/components/PlMultiSequenceAlignment/Consensus.vue.d.ts +0 -9
  22. package/dist/components/PlMultiSequenceAlignment/Consensus.vue.js +0 -10
  23. package/dist/components/PlMultiSequenceAlignment/Consensus.vue.js.map +0 -1
  24. package/dist/components/PlMultiSequenceAlignment/Consensus.vue2.js +0 -122
  25. package/dist/components/PlMultiSequenceAlignment/Consensus.vue2.js.map +0 -1
  26. package/dist/components/PlMultiSequenceAlignment/Consensus.vue3.js +0 -9
  27. package/dist/components/PlMultiSequenceAlignment/Consensus.vue3.js.map +0 -1
  28. package/dist/components/PlMultiSequenceAlignment/Legend.vue.d.ts +0 -6
  29. package/dist/components/PlMultiSequenceAlignment/Legend.vue.js +0 -10
  30. package/dist/components/PlMultiSequenceAlignment/Legend.vue.js.map +0 -1
  31. package/dist/components/PlMultiSequenceAlignment/Legend.vue2.js +0 -28
  32. package/dist/components/PlMultiSequenceAlignment/Legend.vue2.js.map +0 -1
  33. package/dist/components/PlMultiSequenceAlignment/Legend.vue3.js +0 -13
  34. package/dist/components/PlMultiSequenceAlignment/Legend.vue3.js.map +0 -1
  35. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.d.ts +0 -25
  36. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.js +0 -10
  37. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.js.map +0 -1
  38. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js +0 -138
  39. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js.map +0 -1
  40. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js +0 -31
  41. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js.map +0 -1
  42. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue.d.ts +0 -8
  43. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue.js +0 -10
  44. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue.js.map +0 -1
  45. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue2.js +0 -77
  46. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue2.js.map +0 -1
  47. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue3.js +0 -9
  48. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue3.js.map +0 -1
  49. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.d.ts +0 -71
  50. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.js +0 -10
  51. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.js.map +0 -1
  52. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js +0 -224
  53. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js.map +0 -1
  54. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue3.js +0 -9
  55. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue3.js.map +0 -1
  56. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue.d.ts +0 -8
  57. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue.js +0 -10
  58. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue.js.map +0 -1
  59. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue2.js +0 -127
  60. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue2.js.map +0 -1
  61. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue3.js +0 -9
  62. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue3.js.map +0 -1
  63. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue.d.ts +0 -16
  64. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue.js +0 -10
  65. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue.js.map +0 -1
  66. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js +0 -228
  67. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js.map +0 -1
  68. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue3.js +0 -19
  69. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue3.js.map +0 -1
  70. package/dist/components/PlMultiSequenceAlignment/cell-size.d.ts +0 -4
  71. package/dist/components/PlMultiSequenceAlignment/cell-size.js +0 -8
  72. package/dist/components/PlMultiSequenceAlignment/cell-size.js.map +0 -1
  73. package/dist/components/PlMultiSequenceAlignment/chemical-properties.d.ts +0 -44
  74. package/dist/components/PlMultiSequenceAlignment/chemical-properties.js +0 -132
  75. package/dist/components/PlMultiSequenceAlignment/chemical-properties.js.map +0 -1
  76. package/dist/components/PlMultiSequenceAlignment/data.d.ts +0 -61
  77. package/dist/components/PlMultiSequenceAlignment/data.js +0 -370
  78. package/dist/components/PlMultiSequenceAlignment/data.js.map +0 -1
  79. package/dist/components/PlMultiSequenceAlignment/index.d.ts +0 -1
  80. package/dist/components/PlMultiSequenceAlignment/markup.d.ts +0 -16
  81. package/dist/components/PlMultiSequenceAlignment/markup.js +0 -84
  82. package/dist/components/PlMultiSequenceAlignment/markup.js.map +0 -1
  83. package/dist/components/PlMultiSequenceAlignment/migrations.d.ts +0 -3
  84. package/dist/components/PlMultiSequenceAlignment/migrations.js +0 -24
  85. package/dist/components/PlMultiSequenceAlignment/migrations.js.map +0 -1
  86. package/dist/components/PlMultiSequenceAlignment/multi-sequence-alignment.worker.d.ts +0 -6
  87. package/dist/components/PlMultiSequenceAlignment/phylogenetic-tree.worker.d.ts +0 -7
  88. package/dist/components/PlMultiSequenceAlignment/residue-counts.d.ts +0 -2
  89. package/dist/components/PlMultiSequenceAlignment/residue-counts.js +0 -13
  90. package/dist/components/PlMultiSequenceAlignment/residue-counts.js.map +0 -1
  91. package/dist/components/PlMultiSequenceAlignment/settings.d.ts +0 -2
  92. package/dist/components/PlMultiSequenceAlignment/settings.js +0 -9
  93. package/dist/components/PlMultiSequenceAlignment/settings.js.map +0 -1
  94. package/dist/components/PlMultiSequenceAlignment/types.d.ts +0 -5
  95. package/dist/components/PlMultiSequenceAlignment/useMiPlots.d.ts +0 -4
  96. package/dist/components/PlMultiSequenceAlignment/useMiPlots.js +0 -19
  97. package/dist/components/PlMultiSequenceAlignment/useMiPlots.js.map +0 -1
  98. package/src/components/PlMultiSequenceAlignment/Consensus.vue +0 -165
  99. package/src/components/PlMultiSequenceAlignment/Legend.vue +0 -44
  100. package/src/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue +0 -299
  101. package/src/components/PlMultiSequenceAlignment/PhylogeneticTree.vue +0 -110
  102. package/src/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue +0 -314
  103. package/src/components/PlMultiSequenceAlignment/README.md +0 -216
  104. package/src/components/PlMultiSequenceAlignment/SeqLogo.vue +0 -166
  105. package/src/components/PlMultiSequenceAlignment/Toolbar.vue +0 -228
  106. package/src/components/PlMultiSequenceAlignment/cell-size.ts +0 -4
  107. package/src/components/PlMultiSequenceAlignment/chemical-properties.ts +0 -199
  108. package/src/components/PlMultiSequenceAlignment/data.ts +0 -661
  109. package/src/components/PlMultiSequenceAlignment/index.ts +0 -1
  110. package/src/components/PlMultiSequenceAlignment/markup.ts +0 -141
  111. package/src/components/PlMultiSequenceAlignment/migrations.ts +0 -46
  112. package/src/components/PlMultiSequenceAlignment/multi-sequence-alignment.worker.ts +0 -54
  113. package/src/components/PlMultiSequenceAlignment/phylogenetic-tree.worker.ts +0 -89
  114. package/src/components/PlMultiSequenceAlignment/residue-counts.ts +0 -124
  115. package/src/components/PlMultiSequenceAlignment/settings.ts +0 -7
  116. package/src/components/PlMultiSequenceAlignment/types.ts +0 -3
  117. package/src/components/PlMultiSequenceAlignment/useMiPlots.ts +0 -23
@@ -1,661 +0,0 @@
1
- import { isJsonEqual } from '@milaboratories/helpers';
2
- import type { ListOptionNormalized } from '@milaboratories/uikit';
3
- import {
4
- Annotation,
5
- type CalculateTableDataRequest,
6
- type CalculateTableDataResponse,
7
- type CanonicalizedJson,
8
- canonicalizeJson,
9
- createRowSelectionColumn,
10
- ensureError,
11
- getAxisId,
12
- getRawPlatformaInstance,
13
- isLabelColumn,
14
- isLinkerColumn,
15
- type JoinEntry,
16
- matchAxisId,
17
- parseJson,
18
- type PColumnIdAndSpec,
19
- type PFrameHandle,
20
- type PlMultiSequenceAlignmentColorSchemeOption,
21
- type PlMultiSequenceAlignmentSettings,
22
- type PlSelectionModel,
23
- type PObjectId,
24
- type PTableColumnId,
25
- type PTableSorting,
26
- pTableValue,
27
- readAnnotation,
28
- readAnnotationJson,
29
- } from '@platforma-sdk/model';
30
- import { onWatcherCleanup, ref, watch } from 'vue';
31
- import { objectHash } from '../../objectHash';
32
- import { highlightByChemicalProperties } from './chemical-properties';
33
- import type { Markup } from './markup';
34
- import {
35
- highlightByMarkup,
36
- markupAlignedSequence,
37
- parseMarkup,
38
- } from './markup';
39
- import type * as MultiSequenceAlignmentWorker from './multi-sequence-alignment.worker';
40
- import type * as PhylogeneticTreeWorker from './phylogenetic-tree.worker';
41
- import { getResidueCounts } from './residue-counts';
42
- import type { HighlightLegend, ResidueCounts } from './types';
43
-
44
- const getPFrameDriver = () => getRawPlatformaInstance().pFrameDriver;
45
-
46
- export const SEQUENCE_LIMIT = 1000;
47
-
48
- export const useSequenceColumnsOptions = refreshOnDeepChange(
49
- getSequenceColumnsOptions,
50
- );
51
-
52
- export const useLabelColumnsOptions = refreshOnDeepChange(
53
- getLabelColumnsOptions,
54
- );
55
-
56
- export const useMarkupColumnsOptions = refreshOnDeepChange(
57
- getMarkupColumnsOptions,
58
- );
59
-
60
- export const useMultipleAlignmentData = refreshOnDeepChange(
61
- getMultipleAlignmentData,
62
- );
63
-
64
- async function getSequenceColumnsOptions({ pFrame, sequenceColumnPredicate }: {
65
- pFrame: PFrameHandle | undefined;
66
- sequenceColumnPredicate: (column: PColumnIdAndSpec) => boolean;
67
- }): Promise<OptionsWithDefaults<PObjectId> | undefined> {
68
- if (!pFrame) return;
69
-
70
- const pFrameDriver = getPFrameDriver();
71
- const columns = await pFrameDriver.listColumns(pFrame);
72
-
73
- const options = columns.values()
74
- .filter((column) => sequenceColumnPredicate(column))
75
- .map(({ spec, columnId }) => ({
76
- label: readAnnotation(spec, Annotation.Label) ?? 'Unlabeled column',
77
- value: columnId,
78
- }))
79
- .toArray();
80
-
81
- const defaults = options.map(({ value }) => value);
82
-
83
- return { options, defaults };
84
- }
85
-
86
- async function getLabelColumnsOptions({ pFrame, sequenceColumnIds }: {
87
- pFrame: PFrameHandle | undefined;
88
- sequenceColumnIds: PObjectId[] | undefined;
89
- }): Promise<OptionsWithDefaults<PTableColumnId> | undefined> {
90
- if (!pFrame || !sequenceColumnIds) return;
91
-
92
- const pFrameDriver = getPFrameDriver();
93
- const columns = await pFrameDriver.listColumns(pFrame);
94
-
95
- const sequenceColumnsAxes = new Map(
96
- sequenceColumnIds.values().flatMap((id) => {
97
- const column = columns.find(({ columnId }) => columnId === id);
98
- if (!column) {
99
- throw new Error(`Couldn't find sequence column (ID: \`${id}\`).`);
100
- }
101
- return column.spec.axesSpec.values()
102
- .map((spec) => [canonicalizeJson(getAxisId(spec)), spec]);
103
- }),
104
- );
105
-
106
- const optionMap = new Map<CanonicalizedJson<PTableColumnId>, string>();
107
- for (const [axisIdJson, axisSpec] of sequenceColumnsAxes.entries()) {
108
- const axisId = parseJson(axisIdJson);
109
- const labelColumn = columns.find(({ spec }) =>
110
- isLabelColumn(spec) && matchAxisId(axisId, getAxisId(spec.axesSpec[0])),
111
- );
112
- optionMap.set(
113
- labelColumn
114
- ? canonicalizeJson({ type: 'column', id: labelColumn.columnId })
115
- : canonicalizeJson({ type: 'axis', id: axisId }),
116
- readAnnotation(labelColumn?.spec, Annotation.Label)
117
- ?? readAnnotation(axisSpec, Annotation.Label)
118
- ?? 'Unlabeled axis',
119
- );
120
- }
121
-
122
- const { hits: compatibleColumns } = await pFrameDriver.findColumns(pFrame, {
123
- columnFilter: {},
124
- compatibleWith: sequenceColumnsAxes.keys()
125
- .map((axisIdJson) => parseJson(axisIdJson))
126
- .toArray(),
127
- strictlyCompatible: false,
128
- });
129
-
130
- for (const { columnId, spec } of compatibleColumns) {
131
- const columnIdJson = canonicalizeJson<PTableColumnId>({
132
- type: 'column',
133
- id: columnId,
134
- });
135
- if (optionMap.has(columnIdJson)) continue;
136
- optionMap.set(
137
- columnIdJson,
138
- readAnnotation(spec, Annotation.Label) ?? 'Unlabeled column',
139
- );
140
- }
141
-
142
- const options = optionMap.entries()
143
- .map(([value, label]) => ({ label, value: parseJson(value) }))
144
- .toArray();
145
-
146
- const defaults = options.values()
147
- .filter(({ value }) => {
148
- if (value.type === 'axis') return true;
149
- const column = columns.find(({ columnId }) => columnId === value.id);
150
- return column && isLabelColumn(column.spec);
151
- })
152
- .map(({ value }) => value)
153
- .toArray();
154
-
155
- return { options, defaults };
156
- }
157
-
158
- async function getMarkupColumnsOptions({ pFrame, sequenceColumnIds }: {
159
- pFrame: PFrameHandle | undefined;
160
- sequenceColumnIds: PObjectId[] | undefined;
161
- }): Promise<ListOptionNormalized<PObjectId[]>[] | undefined> {
162
- if (!pFrame || !sequenceColumnIds) return;
163
-
164
- const pFrameDriver = getPFrameDriver();
165
- const columns = await pFrameDriver.listColumns(pFrame);
166
-
167
- const sequenceColumns = sequenceColumnIds.map((columnId) => {
168
- const column = columns.find((column) => column.columnId === columnId);
169
- if (!column) {
170
- throw new Error(
171
- `Couldn't find sequence column (ID: \`${sequenceColumnIds[0]}\`).`,
172
- );
173
- }
174
- return column;
175
- });
176
-
177
- const columnPairs = sequenceColumns
178
- .flatMap((sequenceColumn) =>
179
- columns
180
- .filter((column) =>
181
- readAnnotationJson(column.spec, Annotation.Sequence.IsAnnotation)
182
- && isJsonEqual(sequenceColumn.spec.axesSpec, column.spec.axesSpec)
183
- && Object.entries(sequenceColumn.spec.domain ?? {})
184
- .every(([key, value]) => column.spec.domain?.[key] === value),
185
- )
186
- .map((markupColumn) => ({ markupColumn, sequenceColumn })),
187
- );
188
-
189
- const groupedByDomainDiff = Map.groupBy(
190
- columnPairs,
191
- ({ markupColumn, sequenceColumn }) => {
192
- const domainDiff = Object.fromEntries(
193
- Object.entries(markupColumn.spec.domain ?? {})
194
- .filter(([key]) => sequenceColumn.spec.domain?.[key] == undefined),
195
- );
196
- return canonicalizeJson(domainDiff);
197
- },
198
- );
199
-
200
- return groupedByDomainDiff.entries()
201
- .map(([domainDiffJson, columnPairs]) => ({
202
- label: Object.values(parseJson(domainDiffJson)).join(', '),
203
- value: columnPairs.map(({ markupColumn }) => markupColumn.columnId),
204
- }))
205
- .toArray();
206
- }
207
-
208
- async function getMultipleAlignmentData(
209
- {
210
- pFrame,
211
- sequenceColumnIds,
212
- labelColumnIds,
213
- selection,
214
- colorScheme,
215
- alignmentParams,
216
- shouldBuildPhylogeneticTree,
217
- }: {
218
- pFrame: PFrameHandle | undefined;
219
- sequenceColumnIds: PObjectId[] | undefined;
220
- labelColumnIds: PTableColumnId[] | undefined;
221
- selection: PlSelectionModel | undefined;
222
- colorScheme: PlMultiSequenceAlignmentColorSchemeOption;
223
- alignmentParams: PlMultiSequenceAlignmentSettings['alignmentParams'];
224
- shouldBuildPhylogeneticTree: boolean;
225
- },
226
- abortSignal: AbortSignal,
227
- ): Promise<MultipleAlignmentData | undefined> {
228
- if (!pFrame || !sequenceColumnIds?.length || !labelColumnIds) return;
229
-
230
- const table = await getTableData({
231
- pFrame,
232
- sequenceColumnIds,
233
- labelColumnIds,
234
- selection,
235
- colorScheme,
236
- });
237
-
238
- const rowCount = table.at(0)?.data.data.length ?? 0;
239
-
240
- if (rowCount < 2) return;
241
-
242
- const exceedsLimit = rowCount > SEQUENCE_LIMIT;
243
- const rawSequences = extractSequences(sequenceColumnIds, table);
244
- const labels = extractLabels(labelColumnIds, table);
245
- const markups = colorScheme.type === 'markup'
246
- ? extractMarkups(colorScheme.columnIds, table)
247
- : undefined;
248
-
249
- const highlightLegend: HighlightLegend = {};
250
-
251
- const alignedSequences = await Promise.all(
252
- rawSequences.map(async ({ name, rows }) => ({
253
- name,
254
- rows: await alignSequences(
255
- rows,
256
- JSON.parse(JSON.stringify(alignmentParams)),
257
- abortSignal,
258
- ),
259
- })),
260
- );
261
-
262
- let phylogeneticTree: PhylogeneticTreeWorker.TreeNodeData[] | undefined;
263
- if (shouldBuildPhylogeneticTree) {
264
- phylogeneticTree = await buildPhylogeneticTree(
265
- alignedSequences,
266
- abortSignal,
267
- );
268
- const rowOrder = phylogeneticTree.values()
269
- .filter(({ id }) => id >= 0)
270
- .map(({ id }) => id)
271
- .toArray();
272
- for (const sequencesColumn of alignedSequences) {
273
- sequencesColumn.rows = rowOrder.map((i) => sequencesColumn.rows[i]);
274
- }
275
- for (const labelsColumn of labels) {
276
- labelsColumn.rows = rowOrder.map((i) => labelsColumn.rows[i]);
277
- }
278
- for (const markupsColumn of markups ?? []) {
279
- markupsColumn.rows = rowOrder.map((i) => markupsColumn.rows[i]);
280
- }
281
- }
282
-
283
- const sequences = await Promise.all(
284
- alignedSequences.map(async ({ name, rows }, index) => {
285
- const residueCounts = getResidueCounts(rows);
286
- const image = generateHighlightImage({
287
- colorScheme,
288
- sequences: rows,
289
- residueCounts,
290
- markup: markups?.at(index),
291
- });
292
- if (image) {
293
- Object.assign(highlightLegend, image.legend);
294
- }
295
- return {
296
- name,
297
- rows,
298
- residueCounts,
299
- ...image && {
300
- highlightImageUrl: await blobToBase64(image.blob),
301
- },
302
- } satisfies MultipleAlignmentData['sequences'][number];
303
- }),
304
- );
305
-
306
- return {
307
- sequences,
308
- labels,
309
- ...Object.keys(highlightLegend).length && {
310
- highlightLegend,
311
- },
312
- ...phylogeneticTree && {
313
- phylogeneticTree,
314
- },
315
- exceedsLimit,
316
- };
317
- }
318
-
319
- async function getTableData(
320
- { pFrame, sequenceColumnIds, labelColumnIds, selection, colorScheme }: {
321
- pFrame: PFrameHandle;
322
- sequenceColumnIds: PObjectId[];
323
- labelColumnIds: PTableColumnId[];
324
- selection: PlSelectionModel | undefined;
325
- colorScheme: PlMultiSequenceAlignmentColorSchemeOption;
326
- },
327
- ): Promise<CalculateTableDataResponse> {
328
- const pFrameDriver = getPFrameDriver();
329
- const columns = await pFrameDriver.listColumns(pFrame);
330
- const linkerColumns = columns.filter((column) => isLinkerColumn(column.spec));
331
-
332
- const filterColumn = createRowSelectionColumn({ selection });
333
-
334
- // inner join of sequence columns
335
- let primaryEntry: JoinEntry<PObjectId> = {
336
- type: 'inner',
337
- entries: sequenceColumnIds.map((column) => ({
338
- type: 'column',
339
- column,
340
- })),
341
- };
342
-
343
- // if we have linkers, left join them
344
- if (linkerColumns.length > 0) {
345
- primaryEntry = {
346
- type: 'outer',
347
- primary: primaryEntry,
348
- secondary: linkerColumns.map(({ columnId }) => ({
349
- type: 'column',
350
- column: columnId,
351
- })),
352
- };
353
- }
354
-
355
- // inner join with filters
356
- if (filterColumn) {
357
- primaryEntry = {
358
- type: 'inner',
359
- entries: [
360
- primaryEntry,
361
- {
362
- type: 'inlineColumn',
363
- column: filterColumn,
364
- },
365
- ],
366
- };
367
- }
368
-
369
- // left join with labels
370
- const secondaryEntry: JoinEntry<PObjectId>[] = labelColumnIds
371
- .flatMap((column) => {
372
- if (column.type !== 'column') return [];
373
- return { type: 'column', column: column.id };
374
- });
375
-
376
- // and markup
377
- if (colorScheme.type === 'markup') {
378
- for (const column of colorScheme.columnIds) {
379
- secondaryEntry.push({ type: 'column', column });
380
- }
381
- }
382
-
383
- const sorting: PTableSorting[] = Array.from(
384
- new Set(
385
- sequenceColumnIds.values().flatMap((id) => {
386
- const column = columns.find(({ columnId }) => columnId === id);
387
- if (!column) {
388
- throw new Error(`Couldn't find sequence column (ID: ${id})`);
389
- }
390
- return column.spec.axesSpec
391
- .map((spec) => canonicalizeJson(getAxisId(spec)));
392
- }),
393
- ),
394
- )
395
- .sort()
396
- .map((id) => ({
397
- column: { type: 'axis', id: parseJson(id) },
398
- ascending: true,
399
- naAndAbsentAreLeastValues: true,
400
- }));
401
-
402
- const request: CalculateTableDataRequest<PObjectId> = {
403
- src: {
404
- type: 'outer',
405
- primary: primaryEntry,
406
- secondary: secondaryEntry,
407
- },
408
- filters: [],
409
- sorting,
410
- };
411
-
412
- return pFrameDriver.calculateTableData(
413
- pFrame,
414
- JSON.parse(JSON.stringify(request)),
415
- {
416
- offset: 0,
417
- // +1 is a hack to check whether the selection is over the limit
418
- length: SEQUENCE_LIMIT + 1,
419
- },
420
- );
421
- }
422
-
423
- const extractSequences = (
424
- columnIds: PObjectId[],
425
- table: CalculateTableDataResponse,
426
- ): { name: string; rows: string[] }[] =>
427
- columnIds.map((columnId) => {
428
- const column = table.find(({ spec }) => spec.id === columnId);
429
- if (!column) {
430
- throw new Error(`Couldn't find sequence column (ID: \`${columnId}\`).`);
431
- }
432
- const name = readAnnotation(column.spec.spec, Annotation.Label)
433
- ?? 'Unlabeled column';
434
- const rows = column.data.data
435
- .keys()
436
- .take(SEQUENCE_LIMIT)
437
- .map((row) =>
438
- pTableValue(column.data, row, { absent: '', na: '' })?.toString()
439
- ?? '',
440
- )
441
- .toArray();
442
- return { name, rows };
443
- });
444
-
445
- const extractLabels = (
446
- columnIds: PTableColumnId[],
447
- table: CalculateTableDataResponse,
448
- ): { rows: string[] }[] =>
449
- columnIds.map((columnId) => {
450
- const column = table.find(({ spec }) => {
451
- if (columnId.type === 'axis' && spec.type === 'axis') {
452
- return isJsonEqual(columnId.id, spec.id);
453
- }
454
- if (columnId.type === 'column' && spec.type === 'column') {
455
- return columnId.id === spec.id;
456
- }
457
- });
458
- if (!column) {
459
- throw new Error(`Couldn't find label column (ID: \`${columnId}\`).`);
460
- }
461
- const rows = column.data.data
462
- .keys()
463
- .take(SEQUENCE_LIMIT)
464
- .map((row) =>
465
- pTableValue(column.data, row, { absent: '', na: '' })?.toString()
466
- ?? '',
467
- )
468
- .toArray();
469
- return { rows };
470
- });
471
-
472
- const extractMarkups = (
473
- columnIds: PObjectId[],
474
- table: CalculateTableDataResponse,
475
- ): { labels: Record<string, string>; rows: Markup[] }[] =>
476
- columnIds.map((columnId) => {
477
- const column = table.find(({ spec }) => spec.id === columnId);
478
- if (!column) {
479
- throw new Error(`Couldn't find markup column (ID: \`${columnId}\`).`);
480
- }
481
- const labels = readAnnotationJson(
482
- column.spec.spec,
483
- Annotation.Sequence.Annotation.Mapping,
484
- ) ?? {};
485
- const rows = column.data.data
486
- .keys()
487
- .take(SEQUENCE_LIMIT)
488
- .map((row) =>
489
- parseMarkup(
490
- pTableValue(column.data, row, { absent: '', na: '' })?.toString()
491
- ?? '',
492
- ),
493
- )
494
- .toArray();
495
- return { labels, rows };
496
- });
497
-
498
- const alignSequences = (() => {
499
- const cache = new Map<string, string[]>();
500
- return async (
501
- sequences: string[],
502
- alignmentParams: PlMultiSequenceAlignmentSettings['alignmentParams'],
503
- abortSignal: AbortSignal,
504
- ): Promise<string[]> => {
505
- const hash = await objectHash([sequences, alignmentParams]);
506
- let result = cache.get(hash);
507
- if (result) return result;
508
- result = await runInWorker<
509
- MultiSequenceAlignmentWorker.RequestMessage,
510
- MultiSequenceAlignmentWorker.ResponseMessage
511
- >(
512
- new Worker(
513
- new URL('./multi-sequence-alignment.worker.ts', import.meta.url),
514
- { type: 'module' },
515
- ),
516
- { sequences, params: alignmentParams },
517
- abortSignal,
518
- );
519
- cache.set(hash, result);
520
- return result;
521
- };
522
- })();
523
-
524
- function generateHighlightImage(
525
- { colorScheme, sequences, residueCounts, markup }: {
526
- colorScheme: PlMultiSequenceAlignmentColorSchemeOption;
527
- sequences: string[];
528
- residueCounts: ResidueCounts;
529
- markup: { labels: Record<string, string>; rows: Markup[] } | undefined;
530
- },
531
- ): { blob: Blob; legend: HighlightLegend } | undefined {
532
- if (colorScheme.type === 'chemical-properties') {
533
- return highlightByChemicalProperties({ sequences, residueCounts });
534
- }
535
- if (colorScheme.type === 'markup') {
536
- if (!markup) {
537
- throw new Error('Missing markup data.');
538
- }
539
- return highlightByMarkup({
540
- markupRows: sequences.map((sequence, row) => {
541
- const markupRow = markup.rows.at(row);
542
- if (!markupRow) throw new Error(`Missing markup for row ${row}.`);
543
- return markupAlignedSequence(sequence, markupRow);
544
- }),
545
- columnCount: sequences.at(0)?.length ?? 0,
546
- labels: markup.labels,
547
- });
548
- }
549
- }
550
-
551
- const blobToBase64 = (blob: Blob): Promise<string> =>
552
- new Promise<string>((resolve, reject) => {
553
- const reader = new FileReader();
554
- reader.addEventListener('load', () => resolve(reader.result as string));
555
- reader.addEventListener('error', () => reject(reader.error));
556
- reader.readAsDataURL(blob);
557
- });
558
-
559
- const buildPhylogeneticTree = (() => {
560
- const cache = new Map<string, PhylogeneticTreeWorker.TreeNodeData[]>();
561
- return async (data: { rows: string[] }[], abortSignal: AbortSignal) => {
562
- const concatenatedSequences = data.at(0)?.rows
563
- .keys()
564
- .map((row) => data.map((column) => column.rows.at(row) ?? '').join(''))
565
- .toArray() ?? [];
566
- const hash = await objectHash(concatenatedSequences);
567
- let result = cache.get(hash);
568
- if (result) return result;
569
- result = await runInWorker<
570
- PhylogeneticTreeWorker.RequestMessage,
571
- PhylogeneticTreeWorker.ResponseMessage
572
- >(
573
- new Worker(
574
- new URL('./phylogenetic-tree.worker.ts', import.meta.url),
575
- { type: 'module' },
576
- ),
577
- concatenatedSequences,
578
- abortSignal,
579
- );
580
- cache.set(hash, result);
581
- return result;
582
- };
583
- })();
584
-
585
- const runInWorker = <RequestMessage, ResponseMessage>(
586
- worker: Worker,
587
- message: RequestMessage,
588
- abortSignal: AbortSignal,
589
- ) =>
590
- new Promise<ResponseMessage>((resolve, reject) => {
591
- worker.addEventListener('message', ({ data }) => {
592
- resolve(data);
593
- worker.terminate();
594
- });
595
- worker.addEventListener('error', ({ error, message }) => {
596
- reject(error ?? message);
597
- worker.terminate();
598
- });
599
- abortSignal.addEventListener('abort', () => {
600
- reject(abortSignal.reason);
601
- worker.terminate();
602
- });
603
- worker.postMessage(message);
604
- });
605
-
606
- function refreshOnDeepChange<T, P>(
607
- cb: (params: P, abortSignal: AbortSignal) => Promise<T>,
608
- ) {
609
- const data = ref<T>();
610
- const isLoading = ref(true);
611
- const error = ref<Error>();
612
- let requestId: symbol;
613
- return (paramsGetter: () => P) => {
614
- watch(paramsGetter, async (params, prevParams) => {
615
- if (isJsonEqual(params, prevParams)) return;
616
- const abortController = new AbortController();
617
- const currentRequestId = requestId = Symbol();
618
- onWatcherCleanup(() => {
619
- abortController.abort();
620
- });
621
- try {
622
- error.value = undefined;
623
- isLoading.value = true;
624
- const result = await cb(params, abortController.signal);
625
- if (currentRequestId === requestId) {
626
- data.value = result;
627
- }
628
- } catch (err) {
629
- console.error(err);
630
- if (currentRequestId === requestId) {
631
- error.value = ensureError(err);
632
- }
633
- } finally {
634
- if (currentRequestId === requestId) {
635
- isLoading.value = false;
636
- }
637
- }
638
- }, { immediate: true });
639
- return { data, isLoading, error };
640
- };
641
- }
642
-
643
- type MultipleAlignmentData = {
644
- sequences: {
645
- name: string;
646
- rows: string[];
647
- residueCounts: ResidueCounts;
648
- highlightImageUrl?: string;
649
- }[];
650
- labels: {
651
- rows: string[];
652
- }[];
653
- highlightLegend?: HighlightLegend;
654
- phylogeneticTree?: PhylogeneticTreeWorker.TreeNodeData[];
655
- exceedsLimit: boolean;
656
- };
657
-
658
- type OptionsWithDefaults<T> = {
659
- options: ListOptionNormalized<T>[];
660
- defaults: T[];
661
- };
@@ -1 +0,0 @@
1
- export { default as PlMultiSequenceAlignment } from './PlMultiSequenceAlignment.vue';