@platforma-sdk/ui-vue 1.41.17 → 1.42.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.
Files changed (145) hide show
  1. package/.turbo/turbo-build.log +36 -71
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +18 -0
  4. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue2.js +3 -3
  5. package/dist/components/PlMultiSequenceAlignment/Legend.vue.d.ts +2 -2
  6. package/dist/components/PlMultiSequenceAlignment/Legend.vue.d.ts.map +1 -1
  7. package/dist/components/PlMultiSequenceAlignment/Legend.vue2.js +11 -11
  8. package/dist/components/PlMultiSequenceAlignment/Legend.vue3.js +8 -8
  9. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.d.ts +12 -6
  10. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.d.ts.map +1 -1
  11. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js +80 -65
  12. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js.map +1 -1
  13. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js +14 -12
  14. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js.map +1 -1
  15. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.d.ts +36 -1
  16. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.d.ts.map +1 -1
  17. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js +146 -111
  18. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js.map +1 -1
  19. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue.d.ts +2 -0
  20. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue.d.ts.map +1 -1
  21. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js +71 -68
  22. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js.map +1 -1
  23. package/dist/components/PlMultiSequenceAlignment/chemical-properties.d.ts +42 -6
  24. package/dist/components/PlMultiSequenceAlignment/chemical-properties.d.ts.map +1 -1
  25. package/dist/components/PlMultiSequenceAlignment/chemical-properties.js +96 -130
  26. package/dist/components/PlMultiSequenceAlignment/chemical-properties.js.map +1 -1
  27. package/dist/components/PlMultiSequenceAlignment/data.d.ts +3 -9
  28. package/dist/components/PlMultiSequenceAlignment/data.d.ts.map +1 -1
  29. package/dist/components/PlMultiSequenceAlignment/data.js +198 -212
  30. package/dist/components/PlMultiSequenceAlignment/data.js.map +1 -1
  31. package/dist/components/PlMultiSequenceAlignment/markup.d.ts +7 -5
  32. package/dist/components/PlMultiSequenceAlignment/markup.d.ts.map +1 -1
  33. package/dist/components/PlMultiSequenceAlignment/markup.js +47 -26
  34. package/dist/components/PlMultiSequenceAlignment/markup.js.map +1 -1
  35. package/dist/components/PlMultiSequenceAlignment/types.d.ts +1 -1
  36. package/dist/components/PlMultiSequenceAlignment/types.d.ts.map +1 -1
  37. package/dist/lib/ui/uikit/dist/components/DataTable/TableComponent.vue.js +2 -4
  38. package/dist/lib/ui/uikit/dist/components/DataTable/TableComponent.vue.js.map +1 -1
  39. package/dist/lib/ui/uikit/dist/components/DataTable/index.js +2 -2
  40. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/createGridlines.js +6 -8
  41. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/createGridlines.js.map +1 -1
  42. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/createLabels.js.map +1 -1
  43. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/createSvgContainer.js +5 -7
  44. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/createSvgContainer.js.map +1 -1
  45. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/drawBins.js +3 -5
  46. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/drawBins.js.map +1 -1
  47. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/histogram.js +19 -21
  48. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/histogram.js.map +1 -1
  49. package/dist/lib/ui/uikit/dist/components/PlChartHistogram/normalizeBins.js.map +1 -1
  50. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlPureSlideModal.vue.js +2 -4
  51. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlPureSlideModal.vue.js.map +1 -1
  52. package/dist/lib/ui/uikit/dist/index.js +78 -80
  53. package/dist/lib/ui/uikit/dist/index.js.map +1 -1
  54. package/dist/lib/ui/uikit/dist/lib/model/common/dist/index.js.map +1 -1
  55. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/rgb.js +7 -7
  56. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/rgb.js.map +1 -1
  57. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/string.js +9 -9
  58. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/string.js.map +1 -1
  59. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-selection@3.0.0/node_modules/d3-selection/src/selection/index.js +51 -55
  60. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-selection@3.0.0/node_modules/d3-selection/src/selection/index.js.map +1 -1
  61. package/dist/lib/ui/uikit/dist/sdk/model/dist/index.js +1 -1
  62. package/dist/node_modules/.pnpm/@vueuse_core@13.3.0_vue@3.5.13_typescript@5.6.3_/node_modules/@vueuse/core/index.js +111 -165
  63. package/dist/node_modules/.pnpm/@vueuse_core@13.3.0_vue@3.5.13_typescript@5.6.3_/node_modules/@vueuse/core/index.js.map +1 -1
  64. package/dist/node_modules/.pnpm/@vueuse_shared@13.3.0_vue@3.5.13_typescript@5.6.3_/node_modules/@vueuse/shared/index.js +1 -1
  65. package/dist/sdk/model/dist/index.js +1 -1
  66. package/package.json +6 -6
  67. package/src/components/PlMultiSequenceAlignment/Legend.vue +4 -3
  68. package/src/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue +66 -46
  69. package/src/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue +85 -34
  70. package/src/components/PlMultiSequenceAlignment/README.md +10 -8
  71. package/src/components/PlMultiSequenceAlignment/Toolbar.vue +4 -1
  72. package/src/components/PlMultiSequenceAlignment/chemical-properties.ts +154 -161
  73. package/src/components/PlMultiSequenceAlignment/data.ts +65 -85
  74. package/src/components/PlMultiSequenceAlignment/markup.ts +47 -15
  75. package/src/components/PlMultiSequenceAlignment/types.ts +1 -1
  76. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-dispatch@3.0.1/node_modules/d3-dispatch/src/dispatch.js +0 -65
  77. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-dispatch@3.0.1/node_modules/d3-dispatch/src/dispatch.js.map +0 -1
  78. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-ease@3.0.1/node_modules/d3-ease/src/cubic.js +0 -7
  79. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-ease@3.0.1/node_modules/d3-ease/src/cubic.js.map +0 -1
  80. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/transform/decompose.js +0 -24
  81. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/transform/decompose.js.map +0 -1
  82. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/transform/index.js +0 -38
  83. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/transform/index.js.map +0 -1
  84. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/transform/parse.js +0 -14
  85. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-interpolate@3.0.1/node_modules/d3-interpolate/src/transform/parse.js.map +0 -1
  86. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-timer@3.0.1/node_modules/d3-timer/src/timeout.js +0 -11
  87. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-timer@3.0.1/node_modules/d3-timer/src/timeout.js.map +0 -1
  88. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-timer@3.0.1/node_modules/d3-timer/src/timer.js +0 -63
  89. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-timer@3.0.1/node_modules/d3-timer/src/timer.js.map +0 -1
  90. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/interrupt.js +0 -19
  91. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/interrupt.js.map +0 -1
  92. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/selection/index.js +0 -6
  93. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/selection/index.js.map +0 -1
  94. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/selection/interrupt.js +0 -10
  95. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/selection/interrupt.js.map +0 -1
  96. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/selection/transition.js +0 -29
  97. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/selection/transition.js.map +0 -1
  98. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/attr.js +0 -50
  99. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/attr.js.map +0 -1
  100. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/attrTween.js +0 -39
  101. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/attrTween.js.map +0 -1
  102. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/delay.js +0 -19
  103. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/delay.js.map +0 -1
  104. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/duration.js +0 -19
  105. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/duration.js.map +0 -1
  106. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/ease.js +0 -15
  107. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/ease.js.map +0 -1
  108. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/easeVarying.js +0 -16
  109. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/easeVarying.js.map +0 -1
  110. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/end.js +0 -17
  111. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/end.js.map +0 -1
  112. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/filter.js +0 -13
  113. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/filter.js.map +0 -1
  114. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/index.js +0 -66
  115. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/index.js.map +0 -1
  116. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/interpolate.js +0 -12
  117. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/interpolate.js.map +0 -1
  118. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/merge.js +0 -14
  119. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/merge.js.map +0 -1
  120. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/on.js +0 -22
  121. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/on.js.map +0 -1
  122. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/remove.js +0 -14
  123. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/remove.js.map +0 -1
  124. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/schedule.js +0 -86
  125. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/schedule.js.map +0 -1
  126. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/select.js +0 -15
  127. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/select.js.map +0 -1
  128. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/selectAll.js +0 -19
  129. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/selectAll.js.map +0 -1
  130. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/selection.js +0 -9
  131. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/selection.js.map +0 -1
  132. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/style.js +0 -46
  133. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/style.js.map +0 -1
  134. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/styleTween.js +0 -24
  135. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/styleTween.js.map +0 -1
  136. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/text.js +0 -19
  137. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/text.js.map +0 -1
  138. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/textTween.js +0 -24
  139. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/textTween.js.map +0 -1
  140. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/transition.js +0 -20
  141. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/transition.js.map +0 -1
  142. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/tween.js +0 -57
  143. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-transition@3.0.1_d3-selection@3.0.0/node_modules/d3-transition/src/transition/tween.js.map +0 -1
  144. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-zoom@3.0.0/node_modules/d3-zoom/src/transform.js +0 -44
  145. package/dist/lib/ui/uikit/dist/node_modules/.pnpm/d3-zoom@3.0.0/node_modules/d3-zoom/src/transform.js.map +0 -1
@@ -1,6 +1,101 @@
1
- import type { ColorMap, ResidueCounts } from './types';
1
+ import type { HighlightLegend, ResidueCounts } from './types';
2
2
 
3
- export const chemicalPropertiesColorMap: ColorMap = {
3
+ export function highlightByChemicalProperties(
4
+ { sequences, residueCounts }: {
5
+ sequences: string[];
6
+ residueCounts: ResidueCounts;
7
+ },
8
+ ): { blob: Blob; legend: HighlightLegend } {
9
+ const lines: {
10
+ category: ChemicalCategory;
11
+ column: number;
12
+ start: number;
13
+ length: number;
14
+ }[] = [];
15
+ const chemicalProperties = getAlignmentChemicalProperties({
16
+ residueCounts,
17
+ rowCount: sequences.length,
18
+ });
19
+ const width = sequences.at(0)?.length ?? 0;
20
+ const height = sequences.length;
21
+ for (let column = 0; column < width; column += 1) {
22
+ for (let row = 0; row < height; row += 1) {
23
+ const residue = sequences[row][column];
24
+ const category = chemicalProperties
25
+ .at(column)
26
+ ?.find(({ residues }) => residues.includes(residue))
27
+ ?.category;
28
+ if (!category) continue;
29
+ const lastLine = lines.at(-1);
30
+ if (
31
+ lastLine
32
+ && lastLine.category === category
33
+ && lastLine.column === column
34
+ && lastLine.start + lastLine.length === row
35
+ ) {
36
+ lastLine.length += 1;
37
+ } else {
38
+ lines.push({ category, column, start: row, length: 1 });
39
+ }
40
+ }
41
+ }
42
+ const linesByCategory = Map.groupBy(lines, ({ category }) => category);
43
+ const blob = new Blob(
44
+ (function*() {
45
+ const viewBox = `0 0 ${width * 2} ${height}`;
46
+ yield `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}" stroke-width="2" preserveAspectRatio="none">`;
47
+ for (const [category, lines] of linesByCategory) {
48
+ const color = chemicalPropertiesColorScheme[category]?.color;
49
+ if (!color) continue;
50
+ yield `<path stroke="${color}" d="`;
51
+ let x = 0, y = 0;
52
+ for (const { column, start, length } of lines) {
53
+ yield `m${column * 2 + 1 - x},${start - y}v${length}`;
54
+ x = column * 2 + 1;
55
+ y = start + length;
56
+ }
57
+ yield '"/>';
58
+ }
59
+ yield '</svg>';
60
+ })().toArray(),
61
+ { type: 'image/svg+xml' },
62
+ );
63
+ const legend = Object.fromEntries(
64
+ Object.entries(chemicalPropertiesColorScheme)
65
+ .filter(([color]) => linesByCategory.has(color as ChemicalCategory)),
66
+ );
67
+ return { blob, legend };
68
+ }
69
+
70
+ const getAlignmentChemicalProperties = (
71
+ { residueCounts, rowCount }: {
72
+ residueCounts: ResidueCounts;
73
+ rowCount: number;
74
+ },
75
+ ): ColumnChemicalProperties[] =>
76
+ residueCounts.map((column) => {
77
+ const matchedRules = new Set<string>();
78
+ return categoryCriterion.filter(({ residues, rules }) =>
79
+ residues.split('').some((residue) => residue in column)
80
+ && (!rules || rules.split('').map((ruleName) =>
81
+ [ruleName, ruleDefinitions[ruleName as RuleName]] as const,
82
+ ).some(([ruleName, { residues, threshold }]) => {
83
+ if (matchedRules.has(ruleName)) return true;
84
+ const groupCount = residues.split('')
85
+ .reduce((acc, residue) => acc + (column[residue] ?? 0), 0);
86
+ const matches = groupCount >= rowCount * threshold;
87
+ if (matches) matchedRules.add(ruleName);
88
+ return matches;
89
+ })),
90
+ );
91
+ });
92
+
93
+ type ColumnChemicalProperties = {
94
+ residues: string;
95
+ category: ChemicalCategory;
96
+ }[];
97
+
98
+ const chemicalPropertiesColorScheme = {
4
99
  hydrophobic: {
5
100
  label: 'Hydrophobic',
6
101
  color: '#99CCFF',
@@ -33,174 +128,72 @@ export const chemicalPropertiesColorMap: ColorMap = {
33
128
  label: 'Aromatic',
34
129
  color: '#A2F5FA',
35
130
  },
36
- };
131
+ } satisfies HighlightLegend;
37
132
 
38
- export type ChemicalCategory = keyof typeof chemicalPropertiesColorMap;
133
+ export type ChemicalCategory = keyof typeof chemicalPropertiesColorScheme;
39
134
 
40
- export function colorizeSequencesByChemicalProperties(
41
- { sequences, residueCounts, colorMap }: {
42
- sequences: string[];
43
- residueCounts: ResidueCounts;
44
- colorMap: ColorMap;
45
- },
46
- ): Promise<Blob> {
47
- const canvas = new OffscreenCanvas(
48
- sequences[0]?.length ?? 0,
49
- sequences.length,
50
- );
51
- const context = canvas.getContext('2d')!;
52
- const chemicalProperties = getAlignmentChemicalProperties({
53
- residueCounts,
54
- rowCount: sequences.length,
55
- });
56
- for (const [rowIndex, sequence] of sequences.entries()) {
57
- for (const [columnIndex, residue] of sequence.split('').entries()) {
58
- const category = chemicalProperties
59
- .at(columnIndex)
60
- ?.find(({ residues }) => residues.includes(residue))
61
- ?.category;
62
- if (!category) continue;
63
- const color = colorMap[category]?.color;
64
- if (!color) continue;
65
- context.fillStyle = colorMap[category].color;
66
- context.fillRect(columnIndex, rowIndex, 1, 1);
67
- }
68
- }
69
- return canvas.convertToBlob();
70
- }
135
+ /*
136
+ * Below taken mostly from
137
+ * https://www.rbvi.ucsf.edu/chimera/1.2065/docs/ContributedSoftware/multalignviewer/colprot.par}
138
+ */
71
139
 
72
- const getAlignmentChemicalProperties = (
73
- { residueCounts, rowCount }: {
74
- residueCounts: ResidueCounts;
75
- rowCount: number;
76
- },
77
- ): ColumnChemicalProperties[] =>
78
- // for every column in a residue counts table
79
- // (e.g. table = [{ A: 3, R: 5, Q: 1}, { E: 4, T: 2 }, ...])
80
- residueCounts.map((column) => (
81
- // find all matching criterion
82
- categoryCriterion
83
- .filter(({ rules }) =>
84
- // by matching at least one rule
85
- rules.some(({ groups, threshold }) =>
86
- // where at least one residue group
87
- groups.some((group) => {
88
- // combined
89
- const groupCount = group.split('').reduce(
90
- (acc, residue) => acc + (column[residue] ?? 0),
91
- 0,
92
- );
93
- // is above the required threshold
94
- return groupCount > rowCount * threshold;
95
- }),
96
- ),
97
- )
98
- .map(({ residues, category }) => ({ residues, category }))
99
- ));
140
+ const ruleDefinitions = {
141
+ '%': { residues: 'WLVIMAFCYHP', threshold: 0.6 },
142
+ '#': { residues: 'WLVIMAFCYHP', threshold: 0.8 },
143
+ '-': { residues: 'ED', threshold: 0.5 },
144
+ '+': { residues: 'KR', threshold: 0.6 },
145
+ 'g': { residues: 'G', threshold: 0.5 },
146
+ 'n': { residues: 'N', threshold: 0.5 },
147
+ 'q': { residues: 'QE', threshold: 0.5 },
148
+ 'p': { residues: 'P', threshold: 0.5 },
149
+ 't': { residues: 'TS', threshold: 0.5 },
150
+ 'A': { residues: 'A', threshold: 0.85 },
151
+ 'C': { residues: 'C', threshold: 0.85 },
152
+ 'D': { residues: 'D', threshold: 0.85 },
153
+ 'E': { residues: 'E', threshold: 0.85 },
154
+ 'F': { residues: 'F', threshold: 0.85 },
155
+ 'G': { residues: 'G', threshold: 0.85 },
156
+ 'H': { residues: 'H', threshold: 0.85 },
157
+ 'I': { residues: 'I', threshold: 0.85 },
158
+ 'K': { residues: 'K', threshold: 0.85 },
159
+ 'L': { residues: 'L', threshold: 0.85 },
160
+ 'M': { residues: 'M', threshold: 0.85 },
161
+ 'N': { residues: 'N', threshold: 0.85 },
162
+ 'P': { residues: 'P', threshold: 0.85 },
163
+ 'Q': { residues: 'Q', threshold: 0.85 },
164
+ 'R': { residues: 'R', threshold: 0.85 },
165
+ 'S': { residues: 'S', threshold: 0.85 },
166
+ 'T': { residues: 'T', threshold: 0.85 },
167
+ 'V': { residues: 'V', threshold: 0.85 },
168
+ 'W': { residues: 'W', threshold: 0.85 },
169
+ 'Y': { residues: 'Y', threshold: 0.85 },
170
+ };
100
171
 
101
- type ColumnChemicalProperties = {
102
- residues: string;
103
- category: ChemicalCategory;
104
- }[];
172
+ type RuleName = keyof typeof ruleDefinitions;
105
173
 
106
- /** @see {@link https://www.jalview.org/help/html/colourSchemes/clustal.html} */
107
174
  const categoryCriterion: Criteria[] = [
108
- {
109
- residues: 'ACILMFWV',
110
- category: 'hydrophobic',
111
- rules: [
112
- { groups: ['WLVIMAFCYHP'], threshold: 0.6 },
113
- ],
114
- },
115
- {
116
- residues: 'KR',
117
- category: 'positiveCharge',
118
- rules: [
119
- { groups: ['KR'], threshold: 0.6 },
120
- { groups: [...'KRQ'], threshold: 0.8 },
121
- ],
122
- },
123
- {
124
- residues: 'E',
125
- category: 'negativeCharge',
126
- rules: [
127
- { groups: ['KR'], threshold: 0.6 },
128
- { groups: ['QE'], threshold: 0.5 },
129
- { groups: ['ED'], threshold: 0.5 },
130
- { groups: [...'EQD'], threshold: 0.85 },
131
- ],
132
- },
133
- {
134
- residues: 'D',
135
- category: 'negativeCharge',
136
- rules: [
137
- { groups: ['KR'], threshold: 0.6 },
138
- { groups: [...'DEN'], threshold: 0.85 },
139
- { groups: ['ED'], threshold: 0.5 },
140
- ],
141
- },
142
- {
143
- residues: 'N',
144
- category: 'polar',
145
- rules: [
146
- { groups: ['N'], threshold: 0.5 },
147
- { groups: [...'ND'], threshold: 0.85 },
148
- ],
149
- },
150
- {
151
- residues: 'Q',
152
- category: 'polar',
153
- rules: [
154
- { groups: ['KR'], threshold: 0.6 },
155
- { groups: ['QE'], threshold: 0.5 },
156
- { groups: [...'QTKR'], threshold: 0.85 },
157
- ],
158
- },
159
- {
160
- residues: 'ST',
161
- category: 'polar',
162
- rules: [
163
- { groups: ['WLVIMAFCYHP'], threshold: 0.6 },
164
- { groups: ['TS'], threshold: 0.5 },
165
- { groups: [...'ST'], threshold: 0.85 },
166
- ],
167
- },
168
- {
169
- residues: 'C',
170
- category: 'cysteine',
171
- rules: [
172
- { groups: ['C'], threshold: 0.85 },
173
- ],
174
- },
175
- {
176
- residues: 'G',
177
- category: 'glycine',
178
- rules: [
179
- { groups: ['G'], threshold: 0 },
180
- ],
181
- },
182
- {
183
- residues: 'P',
184
- category: 'proline',
185
- rules: [
186
- { groups: ['P'], threshold: 0 },
187
- ],
188
- },
189
- {
190
- residues: 'HY',
191
- category: 'aromatic',
192
- rules: [
193
- { groups: ['WLVIMAFCYHP'], threshold: 0.6 },
194
- { groups: [...'WYACPQFHILMV'], threshold: 0.85 },
195
- ],
196
- },
175
+ { residues: 'G', category: 'glycine', rules: '' },
176
+ { residues: 'P', category: 'proline', rules: '' },
177
+ { residues: 'T', category: 'polar', rules: 'tST%#' },
178
+ { residues: 'S', category: 'polar', rules: 'tST#' },
179
+ { residues: 'N', category: 'polar', rules: 'nND' },
180
+ { residues: 'Q', category: 'polar', rules: 'qQE+KR' },
181
+ // criteria below has to go before the other criteria for C,
182
+ // otherwise it will never match
183
+ { residues: 'C', category: 'cysteine', rules: 'C' },
184
+ { residues: 'WLVIMF', category: 'hydrophobic', rules: '%#ACFHILMVWYPp' },
185
+ // below there was an 's' rule too,
186
+ // but no definition of such rule was provided
187
+ { residues: 'A', category: 'hydrophobic', rules: '%#ACFHILMVWYPpTSG' },
188
+ { residues: 'C', category: 'hydrophobic', rules: '%#AFHILMVWYSPp' },
189
+ { residues: 'HY', category: 'aromatic', rules: '%#ACFHILMVWYPp' },
190
+ { residues: 'E', category: 'negativeCharge', rules: '-DEqQ' },
191
+ { residues: 'D', category: 'negativeCharge', rules: '-DEnN' },
192
+ { residues: 'KR', category: 'positiveCharge', rules: '+KRQ' },
197
193
  ];
198
194
 
199
195
  type Criteria = {
200
196
  residues: string;
201
197
  category: ChemicalCategory;
202
- rules: {
203
- groups: string[];
204
- threshold: number;
205
- }[];
198
+ rules: string;
206
199
  };
@@ -24,20 +24,15 @@ import {
24
24
  pTableValue,
25
25
  } from '@platforma-sdk/model';
26
26
  import { ref, watch } from 'vue';
27
+ import { highlightByChemicalProperties } from './chemical-properties';
27
28
  import {
28
- chemicalPropertiesColorMap,
29
- colorizeSequencesByChemicalProperties,
30
- } from './chemical-properties';
31
- import {
32
- colorizeSequencesByMarkup,
33
- type Markup,
29
+ highlightByMarkup,
34
30
  markupAlignedSequence,
35
- markupColors,
36
31
  parseMarkup,
37
32
  } from './markup';
38
33
  import { multiSequenceAlignment } from './multi-sequence-alignment';
39
34
  import { getResidueCounts } from './residue-counts';
40
- import type { ColorMap, ResidueCounts } from './types';
35
+ import type { HighlightLegend, ResidueCounts } from './types';
41
36
 
42
37
  const getPFrameDriver = () => getRawPlatformaInstance().pFrameDriver;
43
38
 
@@ -70,12 +65,13 @@ async function getSequenceColumnsOptions({
70
65
 
71
66
  const pFrameDriver = getPFrameDriver();
72
67
  const columns = await pFrameDriver.listColumns(pFrame);
73
- const options = columns
68
+ const options = columns.values()
74
69
  .filter((column) => sequenceColumnPredicate(column))
75
70
  .map(({ spec, columnId }) => ({
76
71
  label: spec.annotations?.['pl7.app/label'] ?? 'Unlabelled column',
77
72
  value: columnId,
78
- }));
73
+ }))
74
+ .toArray();
79
75
  const defaults = options.map(({ value }) => value);
80
76
  return { options, defaults };
81
77
  }
@@ -93,14 +89,16 @@ async function getLabelColumnsOptions({
93
89
  const columns = await pFrameDriver.listColumns(pFrame);
94
90
  const optionMap = new Map<CanonicalizedJson<PTableColumnId>, string>();
95
91
 
96
- const sequenceColumnsAxes = new Map(sequenceColumnIds.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
102
- .map((spec) => [canonicalizeJson(getAxisId(spec)), spec]);
103
- }));
92
+ const sequenceColumnsAxes = new Map(
93
+ sequenceColumnIds.values().flatMap((id) => {
94
+ const column = columns.find(({ columnId }) => columnId === id);
95
+ if (!column) {
96
+ throw new Error(`Couldn't find sequence column (ID: \`${id}\`).`);
97
+ }
98
+ return column.spec.axesSpec.values()
99
+ .map((spec) => [canonicalizeJson(getAxisId(spec)), spec]);
100
+ }),
101
+ );
104
102
 
105
103
  for (const [axisIdJson, axisSpec] of sequenceColumnsAxes.entries()) {
106
104
  const axisId = parseJson(axisIdJson);
@@ -119,10 +117,9 @@ async function getLabelColumnsOptions({
119
117
 
120
118
  const { hits: compatibleColumns } = await pFrameDriver.findColumns(pFrame, {
121
119
  columnFilter: {},
122
- compatibleWith: Array.from(
123
- sequenceColumnsAxes.keys(),
124
- (axisIdJson) => parseJson(axisIdJson),
125
- ),
120
+ compatibleWith: sequenceColumnsAxes.keys()
121
+ .map((axisIdJson) => parseJson(axisIdJson))
122
+ .toArray(),
126
123
  strictlyCompatible: false,
127
124
  });
128
125
 
@@ -135,17 +132,18 @@ async function getLabelColumnsOptions({
135
132
  );
136
133
  }
137
134
 
138
- const options = Array.from(optionMap).map(
139
- ([value, label]) => ({ label, value: parseJson(value) }),
140
- );
135
+ const options = optionMap.entries()
136
+ .map(([value, label]) => ({ label, value: parseJson(value) }))
137
+ .toArray();
141
138
 
142
- const defaults = options
139
+ const defaults = options.values()
143
140
  .filter(({ value }) => {
144
141
  if (value.type === 'axis') return true;
145
142
  const column = columns.find(({ columnId }) => columnId === value.id);
146
143
  return column && isLabelColumn(column.spec);
147
144
  })
148
- .map(({ value }) => value);
145
+ .map(({ value }) => value)
146
+ .toArray();
149
147
  return { options, defaults };
150
148
  }
151
149
 
@@ -168,7 +166,7 @@ async function getMarkupColumnsOptions({
168
166
  `Couldn't find sequence column (ID: \`${sequenceColumnIds[0]}\`).`,
169
167
  );
170
168
  }
171
- return columns
169
+ return columns.values()
172
170
  .filter((column) =>
173
171
  column.spec.annotations?.['pl7.app/sequence/isAnnotation'] === 'true'
174
172
  && isJsonEqual(sequenceColumn.spec.axesSpec, column.spec.axesSpec)
@@ -178,14 +176,14 @@ async function getMarkupColumnsOptions({
178
176
  ).map(({ columnId, spec }) => ({
179
177
  value: columnId,
180
178
  label: spec.annotations?.['pl7.app/label'] ?? 'Unlabelled column',
181
- }));
179
+ }))
180
+ .toArray();
182
181
  }
183
182
 
184
183
  async function getMultipleAlignmentData({
185
184
  pframe,
186
185
  sequenceColumnIds,
187
186
  labelColumnIds,
188
- markupColumnId,
189
187
  selection,
190
188
  colorScheme,
191
189
  alignmentParams,
@@ -193,7 +191,6 @@ async function getMultipleAlignmentData({
193
191
  pframe: PFrameHandle | undefined;
194
192
  sequenceColumnIds: PObjectId[] | undefined;
195
193
  labelColumnIds: PTableColumnId[] | undefined;
196
- markupColumnId: PObjectId | undefined;
197
194
  selection: PlSelectionModel | undefined;
198
195
  colorScheme: PlMultiSequenceAlignmentColorSchemeOption;
199
196
  alignmentParams: PlMultiSequenceAlignmentSettings['alignmentParams'];
@@ -249,19 +246,21 @@ async function getMultipleAlignmentData({
249
246
  });
250
247
 
251
248
  // and markup
252
- if (markupColumnId) {
253
- secondaryEntry.push({ type: 'column', column: markupColumnId });
249
+ if (colorScheme.type === 'markup') {
250
+ secondaryEntry.push({ type: 'column', column: colorScheme.columnId });
254
251
  }
255
252
 
256
253
  const sorting: PTableSorting[] = Array.from(
257
- new Set(sequenceColumnIds.flatMap((id) => {
258
- const column = columns.find(({ columnId }) => columnId === id);
259
- if (!column) {
260
- throw new Error(`Couldn't find sequence column (ID: ${id})`);
261
- }
262
- return column.spec.axesSpec
263
- .map((spec) => canonicalizeJson(getAxisId(spec)));
264
- })),
254
+ new Set(
255
+ sequenceColumnIds.values().flatMap((id) => {
256
+ const column = columns.find(({ columnId }) => columnId === id);
257
+ if (!column) {
258
+ throw new Error(`Couldn't find sequence column (ID: ${id})`);
259
+ }
260
+ return column.spec.axesSpec
261
+ .map((spec) => canonicalizeJson(getAxisId(spec)));
262
+ }),
263
+ ),
265
264
  )
266
265
  .sort()
267
266
  .map((id) => ({
@@ -320,9 +319,6 @@ async function getMultipleAlignmentData({
320
319
  return column;
321
320
  });
322
321
 
323
- const markupColumn = markupColumnId
324
- && table.find(({ spec }) => spec.id === markupColumnId);
325
-
326
322
  const alignedSequences = await Promise.all(
327
323
  sequenceColumns.map((column) =>
328
324
  multiSequenceAlignment(
@@ -360,17 +356,26 @@ async function getMultipleAlignmentData({
360
356
  const result: MultipleAlignmentData = {
361
357
  sequences: concatenatedSequences,
362
358
  sequenceNames,
363
- labelsRows: labels,
359
+ labelRows: labels,
364
360
  exceedsLimit,
365
361
  residueCounts,
366
362
  };
367
363
 
368
- if (markupColumn) {
369
- const labels = JSON.parse(
370
- markupColumn.spec.spec.annotations
371
- ?.['pl7.app/sequence/annotation/mapping'] ?? '{}',
364
+ if (colorScheme.type === 'chemical-properties') {
365
+ result.highlightImage = highlightByChemicalProperties({
366
+ sequences: concatenatedSequences,
367
+ residueCounts,
368
+ });
369
+ } else if (colorScheme.type === 'markup') {
370
+ const markupColumn = table.find(({ spec }) =>
371
+ spec.id === colorScheme.columnId,
372
372
  );
373
- const data = Array.from(
373
+ if (!markupColumn) {
374
+ throw new Error(
375
+ `Couldn't find markup column (ID: \`${colorScheme.columnId}\`).`,
376
+ );
377
+ }
378
+ const markupRows = Array.from(
374
379
  { length: rowCount },
375
380
  (_, row) => {
376
381
  const markup = parseMarkup(
@@ -381,36 +386,15 @@ async function getMultipleAlignmentData({
381
386
  return markupAlignedSequence(sequences[row][0], markup);
382
387
  },
383
388
  );
384
- result.markup = { labels, data };
385
- }
386
-
387
- if (colorScheme.type === 'chemical-properties') {
388
- const colorMap = chemicalPropertiesColorMap;
389
- result.highlightImage = {
390
- blob: await colorizeSequencesByChemicalProperties({
391
- sequences: concatenatedSequences,
392
- residueCounts,
393
- colorMap,
394
- }),
395
- colorMap,
396
- };
397
- } else if (colorScheme.type === 'markup') {
398
- const colorMap = Object.fromEntries(
399
- Object.entries(
400
- result.markup?.labels ?? {},
401
- ).map(([id, label], index) => [
402
- id,
403
- { label, color: markupColors[index % markupColors.length] },
404
- ]),
389
+ const labels: Record<string, string> = JSON.parse(
390
+ markupColumn.spec.spec.annotations
391
+ ?.['pl7.app/sequence/annotation/mapping'] ?? '{}',
405
392
  );
406
- result.highlightImage = {
407
- blob: await colorizeSequencesByMarkup({
408
- markupRows: result.markup?.data ?? [],
409
- colorMap,
410
- columnCount: concatenatedSequences.at(0)?.length ?? 0,
411
- }),
412
- colorMap,
413
- };
393
+ result.highlightImage = highlightByMarkup({
394
+ markupRows,
395
+ columnCount: concatenatedSequences.at(0)?.length ?? 0,
396
+ labels,
397
+ });
414
398
  }
415
399
 
416
400
  return result;
@@ -450,15 +434,11 @@ function refreshOnDeepChange<T, P>(cb: (params: P) => Promise<T>) {
450
434
  type MultipleAlignmentData = {
451
435
  sequences: string[];
452
436
  sequenceNames: string[];
453
- labelsRows: string[][];
437
+ labelRows: string[][];
454
438
  residueCounts: ResidueCounts;
455
- markup?: {
456
- labels: Record<string, string>;
457
- data: Markup[];
458
- };
459
439
  highlightImage?: {
460
440
  blob: Blob;
461
- colorMap: ColorMap;
441
+ legend: HighlightLegend;
462
442
  };
463
443
  exceedsLimit: boolean;
464
444
  };