@datagrok/sequence-translator 1.10.13 → 1.10.15
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/CHANGELOG.md +6 -0
- package/detectors.js +30 -2
- package/dist/455.js +1 -1
- package/dist/455.js.map +1 -1
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/files/samples/sirna-demo.csv +38 -0
- package/files/tests/chem_enum_cores.csv +5 -0
- package/files/tests/chem_enum_rgroups.csv +5 -0
- package/package.json +2 -2
- package/src/apps/structure/view/ui.ts +1 -1
- package/src/apps/translator/view/ui.ts +1 -1
- package/src/oligo-renderer/canvas-renderer.ts +500 -0
- package/src/oligo-renderer/cell-renderer.ts +105 -0
- package/src/oligo-renderer/converters.ts +77 -0
- package/src/oligo-renderer/helm-parser.ts +154 -0
- package/src/oligo-renderer/legend-panel.ts +154 -0
- package/src/oligo-renderer/structures-panel.ts +96 -0
- package/src/oligo-renderer/tooltip.ts +223 -0
- package/src/oligo-renderer/types.ts +221 -0
- package/src/package-api.ts +43 -1
- package/src/package-test.ts +2 -0
- package/src/package.g.ts +56 -3
- package/src/package.ts +92 -5
- package/src/polytool/const.ts +1 -1
- package/src/polytool/pt-chem-enum-dialog.ts +940 -0
- package/src/polytool/pt-chem-enum.ts +553 -0
- package/src/polytool/pt-dialog.ts +4 -125
- package/src/polytool/pt-enumerate-seq-dialog.ts +3 -3
- package/src/tests/oligo-renderer-tests.ts +299 -0
- package/src/tests/polytool-enumerate-chem-tests.ts +408 -0
- package/test-console-output-1.log +303 -97
- package/test-record-1.mp4 +0 -0
- package/src/polytool/pt-enumeration-chem.ts +0 -100
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
id,target,status,length_s,length_as,sense_helm,antisense_helm,oligo_helm,tm_c,ic50_nm,kd_pct,off_target_count
|
|
2
|
+
siR-0001,KRAS,lead,19,19,RNA1{m(G)[sp].m(A)[sp].m(C)p.m(U)p.m(G)p.m(A)p.m(A)p.m(U)p.m(A)p.m(U)p.m(A)p.m(A)p.m(A)p.m(C)p.m(U)p.m(U)p.m(G)[sp].m(U)[sp].m(G).[L3]}$$$$,RNA1{m(C)[sp].m(A)[sp].m(C)p.m(A)p.m(A)p.m(G)p.m(U)p.m(U)p.m(U)p.m(A)p.m(U)p.m(A)p.m(U)p.m(U)p.m(C)p.m(A)p.m(G)[sp].m(U)[sp].m(C)}$$$$,RNA1{m(G)[sp].m(A)[sp].m(C)p.m(U)p.m(G)p.m(A)p.m(A)p.m(U)p.m(A)p.m(U)p.m(A)p.m(A)p.m(A)p.m(C)p.m(U)p.m(U)p.m(G)[sp].m(U)[sp].m(G).[L3]}|RNA2{m(C)[sp].m(A)[sp].m(C)p.m(A)p.m(A)p.m(G)p.m(U)p.m(U)p.m(U)p.m(A)p.m(U)p.m(A)p.m(U)p.m(U)p.m(C)p.m(A)p.m(G)[sp].m(U)[sp].m(C)}$$$$,76.9,0.29,99,7
|
|
3
|
+
siR-0002,KRAS,clinical,19,19,RNA1{[Chol].m(G)[sp].[fl2r](A)[sp].m(C)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](U)p.m(A)p.[fl2r](U)p.m(A)p.[fl2r](A)p.m(A)p.[fl2r](C)p.m(U)p.[fl2r](U)p.m(G)[sp].[fl2r](U)[sp].m(G).[L3]}$$$$,RNA1{m(C)[sp].[fl2r](A)[sp].m(C)p.[fl2r](A)p.m(A)p.[fl2r](G)p.m(U)p.[fl2r](U)p.m(U)p.[fl2r](A)p.m(U)p.[fl2r](A)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(G)[sp].[fl2r](U)[sp].m(C)}$$$$,RNA1{[Chol].m(G)[sp].[fl2r](A)[sp].m(C)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](U)p.m(A)p.[fl2r](U)p.m(A)p.[fl2r](A)p.m(A)p.[fl2r](C)p.m(U)p.[fl2r](U)p.m(G)[sp].[fl2r](U)[sp].m(G).[L3]}|RNA2{m(C)[sp].[fl2r](A)[sp].m(C)p.[fl2r](A)p.m(A)p.[fl2r](G)p.m(U)p.[fl2r](U)p.m(U)p.[fl2r](A)p.m(U)p.[fl2r](A)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(G)[sp].[fl2r](U)[sp].m(C)}$$$$,83.8,0.28,99,7
|
|
4
|
+
siR-0003,KRAS,parent,19,19,RNA1{r(G)p.r(A)p.r(C)p.r(U)p.r(G)p.r(A)p.r(A)p.r(U)p.r(A)p.r(U)p.r(A)p.r(A)p.r(A)p.r(C)p.r(U)p.r(U)p.r(G)p.r(U)p.r(G)}$$$$,RNA1{r(C)p.r(A)p.r(C)p.r(A)p.r(A)p.r(G)p.r(U)p.r(U)p.r(U)p.r(A)p.r(U)p.r(A)p.r(U)p.r(U)p.r(C)p.r(A)p.r(G)p.r(U)p.r(C)}$$$$,RNA1{r(G)p.r(A)p.r(C)p.r(U)p.r(G)p.r(A)p.r(A)p.r(U)p.r(A)p.r(U)p.r(A)p.r(A)p.r(A)p.r(C)p.r(U)p.r(U)p.r(G)p.r(U)p.r(G)}|RNA2{r(C)p.r(A)p.r(C)p.r(A)p.r(A)p.r(G)p.r(U)p.r(U)p.r(U)p.r(A)p.r(U)p.r(A)p.r(U)p.r(U)p.r(C)p.r(A)p.r(G)p.r(U)p.r(C)}$$$$,59.6,0.17,65,4
|
|
5
|
+
siR-0004,PCSK9,lead,19,19,RNA1{m(U)[sp].m(C)[sp].m(A)p.m(G)p.m(G)p.m(A)p.m(C)p.m(A)p.m(U)p.m(G)p.m(G)p.m(A)p.m(A)p.m(G)p.m(C)p.m(U)p.m(G)[sp].m(A)[sp].m(A).[L3]}$$$$,RNA1{m(U)[sp].m(U)[sp].m(C)p.m(A)p.m(G)p.m(C)p.m(U)p.m(U)p.m(C)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(C)p.m(C)p.m(U)[sp].m(G)[sp].m(A)}$$$$,RNA1{m(U)[sp].m(C)[sp].m(A)p.m(G)p.m(G)p.m(A)p.m(C)p.m(A)p.m(U)p.m(G)p.m(G)p.m(A)p.m(A)p.m(G)p.m(C)p.m(U)p.m(G)[sp].m(A)[sp].m(A).[L3]}|RNA2{m(U)[sp].m(U)[sp].m(C)p.m(A)p.m(G)p.m(C)p.m(U)p.m(U)p.m(C)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(C)p.m(C)p.m(U)[sp].m(G)[sp].m(A)}$$$$,79,0.38,99,10
|
|
6
|
+
siR-0005,PCSK9,clinical,19,19,RNA1{[Chol].m(U)[sp].[fl2r](C)[sp].m(A)p.[fl2r](G)p.m(G)p.[fl2r](A)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](G)p.m(C)p.[fl2r](U)p.m(G)[sp].[fl2r](A)[sp].m(A).[L3]}$$$$,RNA1{m(U)[sp].[fl2r](U)[sp].m(C)p.[fl2r](A)p.m(G)p.[fl2r](C)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](U)p.m(C)p.[fl2r](C)p.m(U)[sp].[fl2r](G)[sp].m(A)}$$$$,RNA1{[Chol].m(U)[sp].[fl2r](C)[sp].m(A)p.[fl2r](G)p.m(G)p.[fl2r](A)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](G)p.m(C)p.[fl2r](U)p.m(G)[sp].[fl2r](A)[sp].m(A).[L3]}|RNA2{m(U)[sp].[fl2r](U)[sp].m(C)p.[fl2r](A)p.m(G)p.[fl2r](C)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](U)p.m(C)p.[fl2r](C)p.m(U)[sp].[fl2r](G)[sp].m(A)}$$$$,82.1,0.23,99,5
|
|
7
|
+
siR-0006,PCSK9,parent,19,19,RNA1{r(U)p.r(C)p.r(A)p.r(G)p.r(G)p.r(A)p.r(C)p.r(A)p.r(U)p.r(G)p.r(G)p.r(A)p.r(A)p.r(G)p.r(C)p.r(U)p.r(G)p.r(A)p.r(A)}$$$$,RNA1{r(U)p.r(U)p.r(C)p.r(A)p.r(G)p.r(C)p.r(U)p.r(U)p.r(C)p.r(C)p.r(A)p.r(U)p.r(G)p.r(U)p.r(C)p.r(C)p.r(U)p.r(G)p.r(A)}$$$$,RNA1{r(U)p.r(C)p.r(A)p.r(G)p.r(G)p.r(A)p.r(C)p.r(A)p.r(U)p.r(G)p.r(G)p.r(A)p.r(A)p.r(G)p.r(C)p.r(U)p.r(G)p.r(A)p.r(A)}|RNA2{r(U)p.r(U)p.r(C)p.r(A)p.r(G)p.r(C)p.r(U)p.r(U)p.r(C)p.r(C)p.r(A)p.r(U)p.r(G)p.r(U)p.r(C)p.r(C)p.r(U)p.r(G)p.r(A)}$$$$,64.8,0.56,81,15
|
|
8
|
+
siR-0007,TTR,lead,19,19,RNA1{m(A)[sp].m(U)[sp].m(U)p.m(G)p.m(G)p.m(G)p.m(A)p.m(U)p.m(U)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(A)[sp].m(A)[sp].m(U).[L3]}$$$$,RNA1{m(A)[sp].m(U)[sp].m(U)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(A)p.m(A)p.m(U)p.m(C)p.m(C)p.m(C)p.m(A)[sp].m(A)[sp].m(U)}$$$$,RNA1{m(A)[sp].m(U)[sp].m(U)p.m(G)p.m(G)p.m(G)p.m(A)p.m(U)p.m(U)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(A)[sp].m(A)[sp].m(U).[L3]}|RNA2{m(A)[sp].m(U)[sp].m(U)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(A)p.m(A)p.m(U)p.m(C)p.m(C)p.m(C)p.m(A)[sp].m(A)[sp].m(U)}$$$$,78.1,0.34,99,9
|
|
9
|
+
siR-0008,TTR,clinical,19,19,RNA1{[Chol].m(A)[sp].[fl2r](U)[sp].m(U)p.[fl2r](G)p.m(G)p.[fl2r](G)p.m(A)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(A)[sp].[fl2r](A)[sp].m(U).[L3]}$$$$,RNA1{m(A)[sp].[fl2r](U)[sp].m(U)p.[fl2r](C)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(U)p.[fl2r](C)p.m(C)p.[fl2r](C)p.m(A)[sp].[fl2r](A)[sp].m(U)}$$$$,RNA1{[Chol].m(A)[sp].[fl2r](U)[sp].m(U)p.[fl2r](G)p.m(G)p.[fl2r](G)p.m(A)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(A)[sp].[fl2r](A)[sp].m(U).[L3]}|RNA2{m(A)[sp].[fl2r](U)[sp].m(U)p.[fl2r](C)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(U)p.[fl2r](C)p.m(C)p.[fl2r](C)p.m(A)[sp].[fl2r](A)[sp].m(U)}$$$$,80.7,0.19,99,4
|
|
10
|
+
siR-0009,TTR,parent,19,19,RNA1{r(A)p.r(U)p.r(U)p.r(G)p.r(G)p.r(G)p.r(A)p.r(U)p.r(U)p.r(U)p.r(C)p.r(A)p.r(U)p.r(G)p.r(A)p.r(G)p.r(A)p.r(A)p.r(U)}$$$$,RNA1{r(A)p.r(U)p.r(U)p.r(C)p.r(U)p.r(C)p.r(A)p.r(U)p.r(G)p.r(A)p.r(A)p.r(A)p.r(U)p.r(C)p.r(C)p.r(C)p.r(A)p.r(A)p.r(U)}$$$$,RNA1{r(A)p.r(U)p.r(U)p.r(G)p.r(G)p.r(G)p.r(A)p.r(U)p.r(U)p.r(U)p.r(C)p.r(A)p.r(U)p.r(G)p.r(A)p.r(G)p.r(A)p.r(A)p.r(U)}|RNA2{r(A)p.r(U)p.r(U)p.r(C)p.r(U)p.r(C)p.r(A)p.r(U)p.r(G)p.r(A)p.r(A)p.r(A)p.r(U)p.r(C)p.r(C)p.r(C)p.r(A)p.r(A)p.r(U)}$$$$,59.5,0.16,65,3
|
|
11
|
+
siR-0010,APOC3,lead,19,19,RNA1{m(A)[sp].m(U)[sp].m(C)p.m(A)p.m(G)p.m(U)p.m(A)p.m(C)p.m(G)p.m(U)p.m(U)p.m(G)p.m(U)p.m(C)p.m(A)p.m(A)p.m(C)[sp].m(G)[sp].m(G).[L3]}$$$$,RNA1{m(C)[sp].m(C)[sp].m(G)p.m(U)p.m(U)p.m(G)p.m(A)p.m(C)p.m(A)p.m(A)p.m(C)p.m(G)p.m(U)p.m(A)p.m(C)p.m(U)p.m(G)[sp].m(A)[sp].m(U)}$$$$,RNA1{m(A)[sp].m(U)[sp].m(C)p.m(A)p.m(G)p.m(U)p.m(A)p.m(C)p.m(G)p.m(U)p.m(U)p.m(G)p.m(U)p.m(C)p.m(A)p.m(A)p.m(C)[sp].m(G)[sp].m(G).[L3]}|RNA2{m(C)[sp].m(C)[sp].m(G)p.m(U)p.m(U)p.m(G)p.m(A)p.m(C)p.m(A)p.m(A)p.m(C)p.m(G)p.m(U)p.m(A)p.m(C)p.m(U)p.m(G)[sp].m(A)[sp].m(U)}$$$$,77.5,0.32,99,8
|
|
12
|
+
siR-0011,APOC3,clinical,19,19,RNA1{[Chol].m(A)[sp].[fl2r](U)[sp].m(C)p.[fl2r](A)p.m(G)p.[fl2r](U)p.m(A)p.[fl2r](C)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)[sp].[fl2r](G)[sp].m(G).[L3]}$$$$,RNA1{m(C)[sp].[fl2r](C)[sp].m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](G)p.m(U)p.[fl2r](A)p.m(C)p.[fl2r](U)p.m(G)[sp].[fl2r](A)[sp].m(U)}$$$$,RNA1{[Chol].m(A)[sp].[fl2r](U)[sp].m(C)p.[fl2r](A)p.m(G)p.[fl2r](U)p.m(A)p.[fl2r](C)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)[sp].[fl2r](G)[sp].m(G).[L3]}|RNA2{m(C)[sp].[fl2r](C)[sp].m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](G)p.m(U)p.[fl2r](A)p.m(C)p.[fl2r](U)p.m(G)[sp].[fl2r](A)[sp].m(U)}$$$$,76.8,0.07,92,1
|
|
13
|
+
siR-0012,APOC3,parent,19,19,RNA1{r(A)p.r(U)p.r(C)p.r(A)p.r(G)p.r(U)p.r(A)p.r(C)p.r(G)p.r(U)p.r(U)p.r(G)p.r(U)p.r(C)p.r(A)p.r(A)p.r(C)p.r(G)p.r(G)}$$$$,RNA1{r(C)p.r(C)p.r(G)p.r(U)p.r(U)p.r(G)p.r(A)p.r(C)p.r(A)p.r(A)p.r(C)p.r(G)p.r(U)p.r(A)p.r(C)p.r(U)p.r(G)p.r(A)p.r(U)}$$$$,RNA1{r(A)p.r(U)p.r(C)p.r(A)p.r(G)p.r(U)p.r(A)p.r(C)p.r(G)p.r(U)p.r(U)p.r(G)p.r(U)p.r(C)p.r(A)p.r(A)p.r(C)p.r(G)p.r(G)}|RNA2{r(C)p.r(C)p.r(G)p.r(U)p.r(U)p.r(G)p.r(A)p.r(C)p.r(A)p.r(A)p.r(C)p.r(G)p.r(U)p.r(A)p.r(C)p.r(U)p.r(G)p.r(A)p.r(U)}$$$$,60.2,0.21,67,5
|
|
14
|
+
siR-0013,ANGPTL3,lead,19,19,RNA1{m(G)[sp].m(A)[sp].m(A)p.m(C)p.m(U)p.m(C)p.m(U)p.m(G)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(G)p.m(C)p.m(A)p.m(U)[sp].m(U)[sp].m(C).[L3]}$$$$,RNA1{m(G)[sp].m(A)[sp].m(A)p.m(U)p.m(G)p.m(C)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(C)p.m(A)p.m(G)p.m(A)p.m(G)p.m(U)[sp].m(U)[sp].m(C)}$$$$,RNA1{m(G)[sp].m(A)[sp].m(A)p.m(C)p.m(U)p.m(C)p.m(U)p.m(G)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(G)p.m(C)p.m(A)p.m(U)[sp].m(U)[sp].m(C).[L3]}|RNA2{m(G)[sp].m(A)[sp].m(A)p.m(U)p.m(G)p.m(C)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(C)p.m(A)p.m(G)p.m(A)p.m(G)p.m(U)[sp].m(U)[sp].m(C)}$$$$,76.6,0.28,99,7
|
|
15
|
+
siR-0014,ANGPTL3,clinical,19,19,RNA1{[Chol].m(G)[sp].[fl2r](A)[sp].m(A)p.[fl2r](C)p.m(U)p.[fl2r](C)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(G)p.[fl2r](G)p.m(C)p.[fl2r](A)p.m(U)[sp].[fl2r](U)[sp].m(C).[L3]}$$$$,RNA1{m(G)[sp].[fl2r](A)[sp].m(A)p.[fl2r](U)p.m(G)p.[fl2r](C)p.m(C)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(U)[sp].[fl2r](U)[sp].m(C)}$$$$,RNA1{[Chol].m(G)[sp].[fl2r](A)[sp].m(A)p.[fl2r](C)p.m(U)p.[fl2r](C)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(G)p.[fl2r](G)p.m(C)p.[fl2r](A)p.m(U)[sp].[fl2r](U)[sp].m(C).[L3]}|RNA2{m(G)[sp].[fl2r](A)[sp].m(A)p.[fl2r](U)p.m(G)p.[fl2r](C)p.m(C)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(U)[sp].[fl2r](U)[sp].m(C)}$$$$,76.6,0.07,92,1
|
|
16
|
+
siR-0015,ANGPTL3,parent,19,19,RNA1{r(G)p.r(A)p.r(A)p.r(C)p.r(U)p.r(C)p.r(U)p.r(G)p.r(A)p.r(U)p.r(G)p.r(A)p.r(G)p.r(G)p.r(C)p.r(A)p.r(U)p.r(U)p.r(C)}$$$$,RNA1{r(G)p.r(A)p.r(A)p.r(U)p.r(G)p.r(C)p.r(C)p.r(U)p.r(C)p.r(A)p.r(U)p.r(C)p.r(A)p.r(G)p.r(A)p.r(G)p.r(U)p.r(U)p.r(C)}$$$$,RNA1{r(G)p.r(A)p.r(A)p.r(C)p.r(U)p.r(C)p.r(U)p.r(G)p.r(A)p.r(U)p.r(G)p.r(A)p.r(G)p.r(G)p.r(C)p.r(A)p.r(U)p.r(U)p.r(C)}|RNA2{r(G)p.r(A)p.r(A)p.r(U)p.r(G)p.r(C)p.r(C)p.r(U)p.r(C)p.r(A)p.r(U)p.r(C)p.r(A)p.r(G)p.r(A)p.r(G)p.r(U)p.r(U)p.r(C)}$$$$,65,0.58,82,16
|
|
17
|
+
siR-0016,HBV,lead,19,19,RNA1{m(G)[sp].m(G)[sp].m(A)p.m(A)p.m(C)p.m(U)p.m(C)p.m(U)p.m(U)p.m(U)p.m(A)p.m(C)p.m(A)p.m(U)p.m(G)p.m(C)p.m(U)[sp].m(G)[sp].m(U).[L3]}$$$$,RNA1{m(A)[sp].m(C)[sp].m(A)p.m(G)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(A)p.m(A)p.m(A)p.m(G)p.m(A)p.m(G)p.m(U)p.m(U)[sp].m(C)[sp].m(C)}$$$$,RNA1{m(G)[sp].m(G)[sp].m(A)p.m(A)p.m(C)p.m(U)p.m(C)p.m(U)p.m(U)p.m(U)p.m(A)p.m(C)p.m(A)p.m(U)p.m(G)p.m(C)p.m(U)[sp].m(G)[sp].m(U).[L3]}|RNA2{m(A)[sp].m(C)[sp].m(A)p.m(G)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(A)p.m(A)p.m(A)p.m(G)p.m(A)p.m(G)p.m(U)p.m(U)[sp].m(C)[sp].m(C)}$$$$,78.9,0.37,99,10
|
|
18
|
+
siR-0017,HBV,clinical,19,19,RNA1{[Chol].m(G)[sp].[fl2r](G)[sp].m(A)p.[fl2r](A)p.m(C)p.[fl2r](U)p.m(C)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(A)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](C)p.m(U)[sp].[fl2r](G)[sp].m(U).[L3]}$$$$,RNA1{m(A)[sp].[fl2r](C)[sp].m(A)p.[fl2r](G)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(U)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(G)p.[fl2r](A)p.m(G)p.[fl2r](U)p.m(U)[sp].[fl2r](C)[sp].m(C)}$$$$,RNA1{[Chol].m(G)[sp].[fl2r](G)[sp].m(A)p.[fl2r](A)p.m(C)p.[fl2r](U)p.m(C)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(A)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](C)p.m(U)[sp].[fl2r](G)[sp].m(U).[L3]}|RNA2{m(A)[sp].[fl2r](C)[sp].m(A)p.[fl2r](G)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(U)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(G)p.[fl2r](A)p.m(G)p.[fl2r](U)p.m(U)[sp].[fl2r](C)[sp].m(C)}$$$$,76.2,0.06,91,0
|
|
19
|
+
siR-0018,HBV,parent,19,19,RNA1{r(G)p.r(G)p.r(A)p.r(A)p.r(C)p.r(U)p.r(C)p.r(U)p.r(U)p.r(U)p.r(A)p.r(C)p.r(A)p.r(U)p.r(G)p.r(C)p.r(U)p.r(G)p.r(U)}$$$$,RNA1{r(A)p.r(C)p.r(A)p.r(G)p.r(C)p.r(A)p.r(U)p.r(G)p.r(U)p.r(A)p.r(A)p.r(A)p.r(G)p.r(A)p.r(G)p.r(U)p.r(U)p.r(C)p.r(C)}$$$$,RNA1{r(G)p.r(G)p.r(A)p.r(A)p.r(C)p.r(U)p.r(C)p.r(U)p.r(U)p.r(U)p.r(A)p.r(C)p.r(A)p.r(U)p.r(G)p.r(C)p.r(U)p.r(G)p.r(U)}|RNA2{r(A)p.r(C)p.r(A)p.r(G)p.r(C)p.r(A)p.r(U)p.r(G)p.r(U)p.r(A)p.r(A)p.r(A)p.r(G)p.r(A)p.r(G)p.r(U)p.r(U)p.r(C)p.r(C)}$$$$,59,0.13,63,2
|
|
20
|
+
siR-0019,HSD17B13,lead,19,19,RNA1{m(C)[sp].m(A)[sp].m(U)p.m(G)p.m(G)p.m(U)p.m(U)p.m(G)p.m(A)p.m(A)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(C)[sp].m(A)[sp].m(A).[L3]}$$$$,RNA1{m(U)[sp].m(U)[sp].m(G)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(U)p.m(C)p.m(A)p.m(A)p.m(C)p.m(C)p.m(A)[sp].m(U)[sp].m(G)}$$$$,RNA1{m(C)[sp].m(A)[sp].m(U)p.m(G)p.m(G)p.m(U)p.m(U)p.m(G)p.m(A)p.m(A)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(C)[sp].m(A)[sp].m(A).[L3]}|RNA2{m(U)[sp].m(U)[sp].m(G)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(U)p.m(C)p.m(A)p.m(A)p.m(C)p.m(C)p.m(A)[sp].m(U)[sp].m(G)}$$$$,77.4,0.31,99,8
|
|
21
|
+
siR-0020,HSD17B13,clinical,19,19,RNA1{[Chol].m(C)[sp].[fl2r](A)[sp].m(U)p.[fl2r](G)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(C)[sp].[fl2r](A)[sp].m(A).[L3]}$$$$,RNA1{m(U)[sp].[fl2r](U)[sp].m(G)p.[fl2r](C)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](C)p.m(A)[sp].[fl2r](U)[sp].m(G)}$$$$,RNA1{[Chol].m(C)[sp].[fl2r](A)[sp].m(U)p.[fl2r](G)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(C)[sp].[fl2r](A)[sp].m(A).[L3]}|RNA2{m(U)[sp].[fl2r](U)[sp].m(G)p.[fl2r](C)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](C)p.m(A)[sp].[fl2r](U)[sp].m(G)}$$$$,79.6,0.16,99,3
|
|
22
|
+
siR-0021,HSD17B13,parent,19,19,RNA1{r(C)p.r(A)p.r(U)p.r(G)p.r(G)p.r(U)p.r(U)p.r(G)p.r(A)p.r(A)p.r(C)p.r(A)p.r(U)p.r(G)p.r(A)p.r(G)p.r(C)p.r(A)p.r(A)}$$$$,RNA1{r(U)p.r(U)p.r(G)p.r(C)p.r(U)p.r(C)p.r(A)p.r(U)p.r(G)p.r(U)p.r(U)p.r(C)p.r(A)p.r(A)p.r(C)p.r(C)p.r(A)p.r(U)p.r(G)}$$$$,RNA1{r(C)p.r(A)p.r(U)p.r(G)p.r(G)p.r(U)p.r(U)p.r(G)p.r(A)p.r(A)p.r(C)p.r(A)p.r(U)p.r(G)p.r(A)p.r(G)p.r(C)p.r(A)p.r(A)}|RNA2{r(U)p.r(U)p.r(G)p.r(C)p.r(U)p.r(C)p.r(A)p.r(U)p.r(G)p.r(U)p.r(U)p.r(C)p.r(A)p.r(A)p.r(C)p.r(C)p.r(A)p.r(U)p.r(G)}$$$$,62.5,0.38,74,10
|
|
23
|
+
siR-0022,AGT,lead,19,19,RNA1{m(A)[sp].m(A)[sp].m(A)p.m(C)p.m(U)p.m(A)p.m(C)p.m(G)p.m(U)p.m(G)p.m(G)p.m(U)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)[sp].m(G)[sp].m(C).[L3]}$$$$,RNA1{m(G)[sp].m(C)[sp].m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(A)p.m(C)p.m(C)p.m(A)p.m(C)p.m(G)p.m(U)p.m(A)p.m(G)p.m(U)[sp].m(U)[sp].m(U)}$$$$,RNA1{m(A)[sp].m(A)[sp].m(A)p.m(C)p.m(U)p.m(A)p.m(C)p.m(G)p.m(U)p.m(G)p.m(G)p.m(U)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)[sp].m(G)[sp].m(C).[L3]}|RNA2{m(G)[sp].m(C)[sp].m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(A)p.m(C)p.m(C)p.m(A)p.m(C)p.m(G)p.m(U)p.m(A)p.m(G)p.m(U)[sp].m(U)[sp].m(U)}$$$$,75.1,0.21,94,5
|
|
24
|
+
siR-0023,AGT,clinical,19,19,RNA1{[Chol].m(A)[sp].[fl2r](A)[sp].m(A)p.[fl2r](C)p.m(U)p.[fl2r](A)p.m(C)p.[fl2r](G)p.m(U)p.[fl2r](G)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)[sp].[fl2r](G)[sp].m(C).[L3]}$$$$,RNA1{m(G)[sp].[fl2r](C)[sp].m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](C)p.m(A)p.[fl2r](C)p.m(G)p.[fl2r](U)p.m(A)p.[fl2r](G)p.m(U)[sp].[fl2r](U)[sp].m(U)}$$$$,RNA1{[Chol].m(A)[sp].[fl2r](A)[sp].m(A)p.[fl2r](C)p.m(U)p.[fl2r](A)p.m(C)p.[fl2r](G)p.m(U)p.[fl2r](G)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)[sp].[fl2r](G)[sp].m(C).[L3]}|RNA2{m(G)[sp].[fl2r](C)[sp].m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](C)p.m(A)p.[fl2r](C)p.m(G)p.[fl2r](U)p.m(A)p.[fl2r](G)p.m(U)[sp].[fl2r](U)[sp].m(U)}$$$$,82.4,0.24,99,6
|
|
25
|
+
siR-0024,AGT,parent,19,19,RNA1{r(A)p.r(A)p.r(A)p.r(C)p.r(U)p.r(A)p.r(C)p.r(G)p.r(U)p.r(G)p.r(G)p.r(U)p.r(U)p.r(C)p.r(A)p.r(U)p.r(G)p.r(G)p.r(C)}$$$$,RNA1{r(G)p.r(C)p.r(C)p.r(A)p.r(U)p.r(G)p.r(A)p.r(A)p.r(C)p.r(C)p.r(A)p.r(C)p.r(G)p.r(U)p.r(A)p.r(G)p.r(U)p.r(U)p.r(U)}$$$$,RNA1{r(A)p.r(A)p.r(A)p.r(C)p.r(U)p.r(A)p.r(C)p.r(G)p.r(U)p.r(G)p.r(G)p.r(U)p.r(U)p.r(C)p.r(A)p.r(U)p.r(G)p.r(G)p.r(C)}|RNA2{r(G)p.r(C)p.r(C)p.r(A)p.r(U)p.r(G)p.r(A)p.r(A)p.r(C)p.r(C)p.r(A)p.r(C)p.r(G)p.r(U)p.r(A)p.r(G)p.r(U)p.r(U)p.r(U)}$$$$,59.7,0.18,65,4
|
|
26
|
+
siR-0025,KRAS,backup,19,19,RNA1{[Chol].m(G)[sp].[fl2r](A)[sp].m(C)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](U)p.m(A)p.[fl2r](U)p.m(A)p.[fl2r](A)p.m(A)p.[fl2r](C)p.m(U)p.[fl2r](U)p.m(G)[sp].[fl2r](U)[sp].m(G)}$$$$,RNA1{m(C)[sp].[fl2r](A)[sp].m(C)p.[fl2r](A)p.m(A)p.[fl2r](G)p.m(U)p.[fl2r](U)p.m(U)p.[fl2r](A)p.m(U)p.[fl2r](A)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(G)[sp].[fl2r](U)[sp].m(C)}$$$$,RNA1{[Chol].m(G)[sp].[fl2r](A)[sp].m(C)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](U)p.m(A)p.[fl2r](U)p.m(A)p.[fl2r](A)p.m(A)p.[fl2r](C)p.m(U)p.[fl2r](U)p.m(G)[sp].[fl2r](U)[sp].m(G)}|RNA2{m(C)[sp].[fl2r](A)[sp].m(C)p.[fl2r](A)p.m(A)p.[fl2r](G)p.m(U)p.[fl2r](U)p.m(U)p.[fl2r](A)p.m(U)p.[fl2r](A)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(G)[sp].[fl2r](U)[sp].m(C)}$$$$,72.7,0.24,90,6
|
|
27
|
+
siR-0026,PCSK9,backup,19,19,RNA1{[Chol].m(U)[sp].m(C)[sp].m(A)p.m(G)p.m(G)p.m(A)p.m(C)p.m(A)p.m(U)p.m(G)p.m(G)p.m(A)p.m(A)p.m(G)p.m(C)p.m(U)p.m(G)[sp].m(A)[sp].m(A)}$$$$,RNA1{m(U)[sp].m(U)[sp].m(C)p.m(A)p.m(G)p.m(C)p.m(U)p.m(U)p.m(C)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(C)p.m(C)p.m(U)[sp].m(G)[sp].m(A).[Toc]}$$$$,RNA1{[Chol].m(U)[sp].m(C)[sp].m(A)p.m(G)p.m(G)p.m(A)p.m(C)p.m(A)p.m(U)p.m(G)p.m(G)p.m(A)p.m(A)p.m(G)p.m(C)p.m(U)p.m(G)[sp].m(A)[sp].m(A)}|RNA2{m(U)[sp].m(U)[sp].m(C)p.m(A)p.m(G)p.m(C)p.m(U)p.m(U)p.m(C)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(C)p.m(C)p.m(U)[sp].m(G)[sp].m(A).[Toc]}$$$$,78.3,0.22,99,5
|
|
28
|
+
siR-0027,TTR,lead,19,19,RNA1{m(A)[sp].[fl2r](U)[sp].m(U)p.[fl2r](G)p.m(G)p.[fl2r](G)p.m(A)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(A)[sp].[fl2r](A)[sp].m(U).[L3]}$$$$,RNA1{m(A)[sp].[fl2r](U)[sp].m(U)p.[fl2r](C)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(U)p.[fl2r](C)p.m(C)p.[fl2r](C)p.m(A)[sp].[fl2r](A)[sp].m(U).[Bio]}$$$$,RNA1{m(A)[sp].[fl2r](U)[sp].m(U)p.[fl2r](G)p.m(G)p.[fl2r](G)p.m(A)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(A)[sp].[fl2r](A)[sp].m(U).[L3]}|RNA2{m(A)[sp].[fl2r](U)[sp].m(U)p.[fl2r](C)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](A)p.m(A)p.[fl2r](A)p.m(U)p.[fl2r](C)p.m(C)p.[fl2r](C)p.m(A)[sp].[fl2r](A)[sp].m(U).[Bio]}$$$$,78.5,0.29,99,7
|
|
29
|
+
siR-0028,APOC3,backup,19,19,RNA1{m(A)[sp].[fl2r](U)[sp].m(C)p.[fl2r](A)p.m(G)p.[fl2r](U)p.m(A)p.[fl2r](C)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)[sp].[fl2r](G)[sp].m(G)}$$$$,RNA1{[Bio].m(C)[sp].[fl2r](C)[sp].m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](G)p.m(U)p.[fl2r](A)p.m(C)p.[fl2r](U)p.m(G)[sp].[fl2r](A)[sp].m(U)}$$$$,RNA1{m(A)[sp].[fl2r](U)[sp].m(C)p.[fl2r](A)p.m(G)p.[fl2r](U)p.m(A)p.[fl2r](C)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(U)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)[sp].[fl2r](G)[sp].m(G)}|RNA2{[Bio].m(C)[sp].[fl2r](C)[sp].m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](C)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](G)p.m(U)p.[fl2r](A)p.m(C)p.[fl2r](U)p.m(G)[sp].[fl2r](A)[sp].m(U)}$$$$,70.5,0.07,81,1
|
|
30
|
+
siR-0029,ANGPTL3,deprio,19,19,RNA1{[Chol].m(G)[sp].m(A)[sp].m(A)p.m(C)p.m(U)p.m(C)p.m(U)p.m(G)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(G)p.m(C)p.m(A)p.m(U)[sp].m(U)[sp].m(C).[L3]}$$$$,RNA1{[Pal].m(G)[sp].m(A)[sp].m(A)p.m(U)p.m(G)p.m(C)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(C)p.m(A)p.m(G)p.m(A)p.m(G)p.m(U)[sp].m(U)[sp].m(C).[Bio]}$$$$,RNA1{[Chol].m(G)[sp].m(A)[sp].m(A)p.m(C)p.m(U)p.m(C)p.m(U)p.m(G)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(G)p.m(C)p.m(A)p.m(U)[sp].m(U)[sp].m(C).[L3]}|RNA2{[Pal].m(G)[sp].m(A)[sp].m(A)p.m(U)p.m(G)p.m(C)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(C)p.m(A)p.m(G)p.m(A)p.m(G)p.m(U)[sp].m(U)[sp].m(C).[Bio]}$$$$,80.1,0.22,99,5
|
|
31
|
+
siR-0030,HBV,lead,19,19,RNA1{[Pal].m(G)[sp].[fl2r](G)[sp].m(A)p.[fl2r](A)p.m(C)p.[fl2r](U)p.m(C)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(A)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](C)p.m(U)[sp].[fl2r](G)[sp].m(U)}$$$$,RNA1{m(A)[sp].m(C)[sp].m(A)p.m(G)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(A)p.m(A)p.m(A)p.m(G)p.m(A)p.m(G)p.m(U)p.m(U)[sp].m(C)[sp].m(C).[L3]}$$$$,RNA1{[Pal].m(G)[sp].[fl2r](G)[sp].m(A)p.[fl2r](A)p.m(C)p.[fl2r](U)p.m(C)p.[fl2r](U)p.m(U)p.[fl2r](U)p.m(A)p.[fl2r](C)p.m(A)p.[fl2r](U)p.m(G)p.[fl2r](C)p.m(U)[sp].[fl2r](G)[sp].m(U)}|RNA2{m(A)[sp].m(C)[sp].m(A)p.m(G)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(A)p.m(A)p.m(A)p.m(G)p.m(A)p.m(G)p.m(U)p.m(U)[sp].m(C)[sp].m(C).[L3]}$$$$,76.7,0.28,99,7
|
|
32
|
+
siR-0031,HSD17B13,backup,19,19,RNA1{[DBCO].m(C)[sp].[fl2r](A)[sp].m(U)p.[fl2r](G)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(C)[sp].[fl2r](A)[sp].m(A)}$$$$,RNA1{m(U)[sp].m(U)[sp].m(G)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(U)p.m(C)p.m(A)p.m(A)p.m(C)p.m(C)p.m(A)[sp].m(U)[sp].m(G)}$$$$,RNA1{[DBCO].m(C)[sp].[fl2r](A)[sp].m(U)p.[fl2r](G)p.m(G)p.[fl2r](U)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](A)p.m(C)p.[fl2r](A)p.m(U)p.[fl2r](G)p.m(A)p.[fl2r](G)p.m(C)[sp].[fl2r](A)[sp].m(A)}|RNA2{m(U)[sp].m(U)[sp].m(G)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(U)p.m(U)p.m(C)p.m(A)p.m(A)p.m(C)p.m(C)p.m(A)[sp].m(U)[sp].m(G)}$$$$,72.6,0.3,92,8
|
|
33
|
+
siR-0032,TTR,backup,19,19,RNA1{m(A)[sp].m(U)[sp].[fl2r](U)p.[fl2r](G)p.[fl2r](G)p.[fl2r](G)p.[fl2r](A)p.[fl2r](U)p.m(U)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(A)[sp].m(A)[sp].m(U).[L3]}$$$$,RNA1{m(A)[sp].m(U)[sp].m(U)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(A)p.m(A)p.m(U)p.m(C)p.m(C)p.m(C)p.m(A)[sp].m(A)[sp].m(U)}$$$$,RNA1{m(A)[sp].m(U)[sp].[fl2r](U)p.[fl2r](G)p.[fl2r](G)p.[fl2r](G)p.[fl2r](A)p.[fl2r](U)p.m(U)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(G)p.m(A)[sp].m(A)[sp].m(U).[L3]}|RNA2{m(A)[sp].m(U)[sp].m(U)p.m(C)p.m(U)p.m(C)p.m(A)p.m(U)p.m(G)p.m(A)p.m(A)p.m(A)p.m(U)p.m(C)p.m(C)p.m(C)p.m(A)[sp].m(A)[sp].m(U)}$$$$,69.7,0.16,83,3
|
|
34
|
+
siR-0033,KRAS,deprio,19,19,RNA1{[r](G)p.m(A)p.m(C)p.m(U)p.m(G)p.[r](A)p.m(A)p.m(U)p.m(A)p.m(U)p.m(A)p.m(A)p.m(A)p.m(C)p.m(U)p.m(U)p.m(G)p.m(U)p.m(G)}$$$$,RNA1{m(C)[sp].m(A)[sp].m(C)p.m(A)p.m(A)p.m(G)p.m(U)p.m(U)p.m(U)p.m(A)p.m(U)p.m(A)p.m(U)p.m(U)p.m(C)p.m(A)p.m(G)[sp].m(U)[sp].m(C)}$$$$,RNA1{[r](G)p.m(A)p.m(C)p.m(U)p.m(G)p.[r](A)p.m(A)p.m(U)p.m(A)p.m(U)p.m(A)p.m(A)p.m(A)p.m(C)p.m(U)p.m(U)p.m(G)p.m(U)p.m(G)}|RNA2{m(C)[sp].m(A)[sp].m(C)p.m(A)p.m(A)p.m(G)p.m(U)p.m(U)p.m(U)p.m(A)p.m(U)p.m(A)p.m(U)p.m(U)p.m(C)p.m(A)p.m(G)[sp].m(U)[sp].m(C)}$$$$,63.7,0.11,71,2
|
|
35
|
+
aso-0034,TTR_ASO,clinical,19,0,RNA1{[lna](C)[sp].[lna](A)[sp].[lna](G)[sp].d(T)[sp].d(G)[sp].d(T)[sp].d(T)[sp].d(C)[sp].d(T)[sp].d(T)[sp].d(G)[sp].d(C)[sp].d(T)[sp].d(C)[sp].d(T)[sp].d(A)[sp].[lna](T)[sp].[lna](A)[sp].[lna](A)}$$$$,,RNA1{[lna](C)[sp].[lna](A)[sp].[lna](G)[sp].d(T)[sp].d(G)[sp].d(T)[sp].d(T)[sp].d(C)[sp].d(T)[sp].d(T)[sp].d(G)[sp].d(C)[sp].d(T)[sp].d(C)[sp].d(T)[sp].d(A)[sp].[lna](T)[sp].[lna](A)[sp].[lna](A)}$$$$,79.6,0.3,99,7
|
|
36
|
+
aso-0035,PCSK9_ASO,lead,19,0,RNA1{[moe](C)[sp].[moe](C)[sp].[moe](A)[sp].[moe](G)[sp].[moe](G)[sp].d(A)[sp].d(C)[sp].d(T)[sp].d(G)[sp].d(A)[sp].d(G)[sp].d(G)[sp].d(C)[sp].d(T)[sp].[moe](T)[sp].[moe](A)[sp].[moe](C)[sp].[moe](G)[sp].[moe](T)}$$$$,,RNA1{[moe](C)[sp].[moe](C)[sp].[moe](A)[sp].[moe](G)[sp].[moe](G)[sp].d(A)[sp].d(C)[sp].d(T)[sp].d(G)[sp].d(A)[sp].d(G)[sp].d(G)[sp].d(C)[sp].d(T)[sp].[moe](T)[sp].[moe](A)[sp].[moe](C)[sp].[moe](G)[sp].[moe](T)}$$$$,74.4,0.1,89,2
|
|
37
|
+
aso-0036,AGT_ASO,lead,19,0,RNA1{[Chol].[lna](C)[sp].[lna](A)[sp].[lna](C)[sp].d(A)[sp].d(G)[sp].d(T)[sp].d(A)[sp].d(C)[sp].d(T)[sp].d(G)[sp].d(G)[sp].d(A)[sp].d(G)[sp].d(C)[sp].d(A)[sp].d(T)[sp].[lna](G)[sp].[lna](G)[sp].[lna](T).[GalNAc]}$$$$,,RNA1{[Chol].[lna](C)[sp].[lna](A)[sp].[lna](C)[sp].d(A)[sp].d(G)[sp].d(T)[sp].d(A)[sp].d(C)[sp].d(T)[sp].d(G)[sp].d(G)[sp].d(A)[sp].d(G)[sp].d(C)[sp].d(A)[sp].d(T)[sp].[lna](G)[sp].[lna](G)[sp].[lna](T).[GalNAc]}$$$$,74.7,0.11,90,2
|
|
38
|
+
aso-0037,TTR_ASO,backup,19,0,RNA1{[Bio].[moe](C)[sp].[moe](A)[sp].[moe](G)[sp].[moe](T)[sp].[moe](G)[sp].d(T)[sp].d(T)[sp].d(C)[sp].d(T)[sp].d(T)[sp].d(G)[sp].d(C)[sp].d(T)[sp].d(C)[sp].[moe](T)[sp].[moe](A)[sp].[moe](T)[sp].[moe](A)[sp].[moe](A)}$$$$,,RNA1{[Bio].[moe](C)[sp].[moe](A)[sp].[moe](G)[sp].[moe](T)[sp].[moe](G)[sp].d(T)[sp].d(T)[sp].d(C)[sp].d(T)[sp].d(T)[sp].d(G)[sp].d(C)[sp].d(T)[sp].d(C)[sp].[moe](T)[sp].[moe](A)[sp].[moe](T)[sp].[moe](A)[sp].[moe](A)}$$$$,77.9,0.24,99,6
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/sequence-translator",
|
|
3
3
|
"friendlyName": "Sequence Translator",
|
|
4
|
-
"version": "1.10.
|
|
4
|
+
"version": "1.10.15",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Davit Rizhinashvili",
|
|
7
7
|
"email": "drizhinashvili@datagrok.ai"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
}
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@datagrok-libraries/bio": "^5.
|
|
25
|
+
"@datagrok-libraries/bio": "^5.65.0",
|
|
26
26
|
"@datagrok-libraries/chem-meta": "^1.2.8",
|
|
27
27
|
"@datagrok-libraries/tutorials": "^1.6.1",
|
|
28
28
|
"@datagrok-libraries/utils": "^4.6.5",
|
|
@@ -140,7 +140,7 @@ class StructureAppLayout {
|
|
|
140
140
|
const clearBlock = Object.fromEntries(
|
|
141
141
|
STRANDS.map(
|
|
142
142
|
(key) => {
|
|
143
|
-
const clearIcon = ui.icons.delete(() => { coloredInput[key].inputBase.value = ''; });
|
|
143
|
+
const clearIcon = ui.icons.delete(() => { coloredInput[key].inputBase.value = ''; }, 'Delete');
|
|
144
144
|
const clearButton = ui.button(clearIcon, () => {});
|
|
145
145
|
ui.tooltip.bind(clearButton, `Clear ${key.toUpperCase()}`);
|
|
146
146
|
return [key, clearIcon];
|
|
@@ -202,7 +202,7 @@ class TranslatorAppLayout {
|
|
|
202
202
|
const formatChoiceInput = ui.div([this.formatChoiceInput]);
|
|
203
203
|
|
|
204
204
|
const clearButton = ui.button(
|
|
205
|
-
ui.icons.delete(() => { sequenceColoredInput.inputBase.value = ''; }),
|
|
205
|
+
ui.icons.delete(() => { sequenceColoredInput.inputBase.value = ''; }, 'Delete'),
|
|
206
206
|
() => {}
|
|
207
207
|
);
|
|
208
208
|
ui.tooltip.bind(clearButton, 'Clear input');
|
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas drawing for the OligoNucleotide cell renderer.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions — no DG / DOM dependencies — so the renderer is easy to
|
|
5
|
+
* unit-test and to drive from the HTML prototype as well.
|
|
6
|
+
*
|
|
7
|
+
* Visual model:
|
|
8
|
+
* - Chip body uses base-canonical color (A green / C blue / G tan / U pink).
|
|
9
|
+
* - Sugar modifications are shown as a colored stripe at the bottom of the
|
|
10
|
+
* chip (so the chip itself stays readable for the base sequence, and the
|
|
11
|
+
* modification track scans easily horizontally).
|
|
12
|
+
* - Phosphate linkage modifications (PS) are shown as a saturated bar in
|
|
13
|
+
* the gap *between* chips — this is the actual chemistry: the linkage
|
|
14
|
+
* belongs to the bond, not to either nucleotide.
|
|
15
|
+
* - Wide gaps between chips give the modification markers room to breathe.
|
|
16
|
+
* - Antisense is rendered 3'→5' (reversed) so position N of sense visually
|
|
17
|
+
* pairs with position N of antisense (anti-parallel base-pair register).
|
|
18
|
+
* - Conjugates are rendered as wider pills at the chain ends; their actual
|
|
19
|
+
* width is propagated through layout so adjacent chips don't overlap.
|
|
20
|
+
*
|
|
21
|
+
* Sizing is fully adaptive: chip dimensions scale to fit the cell, preserving
|
|
22
|
+
* aspect ratio, down to a minimum below which the cell falls back to a text
|
|
23
|
+
* summary.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
BASE_COLORS, FALLBACK_COLOR,
|
|
28
|
+
canonicalSugarSymbol,
|
|
29
|
+
ParsedDuplex, ParsedMonomer, ParsedNucleotide, ParsedStrand,
|
|
30
|
+
resolveConjugate, resolvePhosphate, resolveSugar,
|
|
31
|
+
} from './types';
|
|
32
|
+
|
|
33
|
+
export interface RenderOpts {
|
|
34
|
+
/** Show base letter inside chip. False at very small sizes. */
|
|
35
|
+
showLetters: boolean;
|
|
36
|
+
/** Draw antisense 3'→5' for base-pair alignment with sense. Default true. */
|
|
37
|
+
pairAlign: boolean;
|
|
38
|
+
/** Optional column-level scheme name (currently ignored — palette is fixed). */
|
|
39
|
+
scheme?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const DEFAULT_OPTS: RenderOpts = {showLetters: true, pairAlign: true};
|
|
43
|
+
|
|
44
|
+
/* Visual tuning. */
|
|
45
|
+
const ASPECT_H_OVER_W = 1.25;
|
|
46
|
+
const STRAND_GAP_RATIO = 0.5;
|
|
47
|
+
const CHIP_GAP_RATIO = 0.32; // wider — gives PS bar room to breathe
|
|
48
|
+
const MIN_CHIP_W = 5;
|
|
49
|
+
const MAX_CHIP_W = 17;
|
|
50
|
+
const PAD = 4;
|
|
51
|
+
const LABEL_W = 30;
|
|
52
|
+
const CHIP_FILL_ALPHA = 0.85; // chip body, base-canonical pale color
|
|
53
|
+
const CHIP_BORDER_W = 0.5;
|
|
54
|
+
const SUGAR_STRIPE_RATIO = 0.22; // height of bottom sugar-mod stripe
|
|
55
|
+
const PS_BAR_RATIO = 0.55; // PS bar width as fraction of chip gap
|
|
56
|
+
|
|
57
|
+
/** Cached layout for one rendered cell. */
|
|
58
|
+
export interface DuplexLayout {
|
|
59
|
+
chipW: number;
|
|
60
|
+
chipH: number;
|
|
61
|
+
chipGap: number;
|
|
62
|
+
strandGap: number;
|
|
63
|
+
fontSize: number;
|
|
64
|
+
labelW: number;
|
|
65
|
+
padding: number;
|
|
66
|
+
senseY: number;
|
|
67
|
+
antiY: number; // -1 if no antisense
|
|
68
|
+
seqX: number;
|
|
69
|
+
textOnlyFallback: boolean;
|
|
70
|
+
senseChips: ChipPos[];
|
|
71
|
+
antiChips: ChipPos[];
|
|
72
|
+
senseLinks: LinkagePos[];
|
|
73
|
+
antiLinks: LinkagePos[];
|
|
74
|
+
/** Whether antisense was rendered in reversed (3'→5') order. */
|
|
75
|
+
antiReversed: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ChipPos {
|
|
79
|
+
x: number;
|
|
80
|
+
w: number;
|
|
81
|
+
monomer: ParsedMonomer;
|
|
82
|
+
/** Original 0-based index in the data strand (regardless of display order). */
|
|
83
|
+
origIdx: number;
|
|
84
|
+
strand: StrandSide;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface LinkagePos {
|
|
88
|
+
x: number; w: number; y: number; h: number;
|
|
89
|
+
phosphateSymbol: string;
|
|
90
|
+
/** Original index of the nucleotide that owns the 3' phosphate. */
|
|
91
|
+
ownerOrigIdx: number;
|
|
92
|
+
strand: StrandSide;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type StrandSide = 'sense' | 'antisense';
|
|
96
|
+
|
|
97
|
+
/* ---------------------------------------------------------------- *
|
|
98
|
+
* Layout
|
|
99
|
+
* ---------------------------------------------------------------- */
|
|
100
|
+
|
|
101
|
+
export function computeLayout(
|
|
102
|
+
cellW: number, cellH: number, model: ParsedDuplex, opts: Partial<RenderOpts> = {},
|
|
103
|
+
): DuplexLayout {
|
|
104
|
+
const o: RenderOpts = {...DEFAULT_OPTS, ...opts};
|
|
105
|
+
const senseLen = model.sense.monomers.length;
|
|
106
|
+
const antiLen = model.antisense ? model.antisense.monomers.length : 0;
|
|
107
|
+
const hasAnti = antiLen > 0;
|
|
108
|
+
const strandsCount = hasAnti ? 2 : 1;
|
|
109
|
+
const maxLen = Math.max(1, senseLen, antiLen);
|
|
110
|
+
|
|
111
|
+
const availW = Math.max(0, cellW - LABEL_W - 2 * PAD);
|
|
112
|
+
const wChipW = availW / (maxLen + Math.max(0, maxLen - 1) * CHIP_GAP_RATIO);
|
|
113
|
+
|
|
114
|
+
const heightFactor = strandsCount + Math.max(0, strandsCount - 1) * STRAND_GAP_RATIO;
|
|
115
|
+
const hChipH = (cellH - 2 * PAD) / heightFactor;
|
|
116
|
+
const hChipW = hChipH / ASPECT_H_OVER_W;
|
|
117
|
+
|
|
118
|
+
let chipW = Math.min(MAX_CHIP_W, wChipW, hChipW);
|
|
119
|
+
const textOnlyFallback = chipW < MIN_CHIP_W;
|
|
120
|
+
if (chipW < MIN_CHIP_W) chipW = MIN_CHIP_W;
|
|
121
|
+
|
|
122
|
+
const chipH = chipW * ASPECT_H_OVER_W;
|
|
123
|
+
const chipGap = Math.max(2, chipW * CHIP_GAP_RATIO);
|
|
124
|
+
const strandGap = chipH * STRAND_GAP_RATIO;
|
|
125
|
+
const fontSize = Math.max(7, Math.min(13, chipW * 0.62));
|
|
126
|
+
|
|
127
|
+
const blockH = strandsCount * chipH + (strandsCount - 1) * strandGap;
|
|
128
|
+
const blockTop = Math.max(PAD, (cellH - blockH) / 2);
|
|
129
|
+
const senseY = blockTop;
|
|
130
|
+
const antiY = hasAnti ? blockTop + chipH + strandGap : -1;
|
|
131
|
+
const seqX = PAD + LABEL_W;
|
|
132
|
+
const seqEndX = cellW - PAD;
|
|
133
|
+
|
|
134
|
+
const layoutBase: Omit<DuplexLayout, 'senseChips' | 'antiChips' | 'senseLinks' | 'antiLinks' | 'antiReversed'> = {
|
|
135
|
+
chipW, chipH, chipGap, strandGap, fontSize,
|
|
136
|
+
labelW: LABEL_W, padding: PAD,
|
|
137
|
+
senseY, antiY, seqX, textOnlyFallback,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if (textOnlyFallback) {
|
|
141
|
+
return {
|
|
142
|
+
...layoutBase,
|
|
143
|
+
senseChips: [], antiChips: [], senseLinks: [], antiLinks: [],
|
|
144
|
+
antiReversed: false,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Conjugates on either strand's leftmost end push back the start of
|
|
149
|
+
// *that* strand's nucleotide region. To keep base pairs aligned across
|
|
150
|
+
// strands, we measure the leading-conjugate width of each strand (in
|
|
151
|
+
// its display order — antisense is reversed when pair-aligned) and
|
|
152
|
+
// shift each strand right by the difference, so the first NUCLEOTIDE
|
|
153
|
+
// of each strand sits at the same X coordinate.
|
|
154
|
+
const antiReversed = hasAnti && o.pairAlign;
|
|
155
|
+
const senseLeadW = leadingConjugateWidth(model.sense.monomers, false, chipW, fontSize, chipGap);
|
|
156
|
+
const antiLeadW = hasAnti ?
|
|
157
|
+
leadingConjugateWidth(model.antisense!.monomers, antiReversed, chipW, fontSize, chipGap) : 0;
|
|
158
|
+
const alignAt = Math.max(senseLeadW, antiLeadW);
|
|
159
|
+
const senseStartX = seqX + (alignAt - senseLeadW);
|
|
160
|
+
const antiStartX = seqX + (alignAt - antiLeadW);
|
|
161
|
+
|
|
162
|
+
const senseRes = placeStrand(model.sense, false, senseY, senseStartX, seqEndX, layoutBase, 'sense');
|
|
163
|
+
const antiRes = hasAnti ?
|
|
164
|
+
placeStrand(model.antisense!, antiReversed, antiY, antiStartX, seqEndX, layoutBase, 'antisense') :
|
|
165
|
+
{chips: [], links: []};
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
...layoutBase,
|
|
169
|
+
senseChips: senseRes.chips,
|
|
170
|
+
antiChips: antiRes.chips,
|
|
171
|
+
senseLinks: senseRes.links,
|
|
172
|
+
antiLinks: antiRes.links,
|
|
173
|
+
antiReversed,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Place chips for one strand, optionally reversed. Returns chip and linkage positions. */
|
|
178
|
+
function placeStrand(
|
|
179
|
+
strand: ParsedStrand, reverse: boolean, y: number, startX: number, endX: number,
|
|
180
|
+
layout: Omit<DuplexLayout, 'senseChips' | 'antiChips' | 'senseLinks' | 'antiLinks' | 'antiReversed'>,
|
|
181
|
+
side: StrandSide,
|
|
182
|
+
): { chips: ChipPos[]; links: LinkagePos[] } {
|
|
183
|
+
const monomers = reverse ? strand.monomers.slice().reverse() : strand.monomers;
|
|
184
|
+
const chips: ChipPos[] = [];
|
|
185
|
+
const links: LinkagePos[] = [];
|
|
186
|
+
let x = startX;
|
|
187
|
+
|
|
188
|
+
for (let i = 0; i < monomers.length; i++) {
|
|
189
|
+
const m = monomers[i];
|
|
190
|
+
const w = m.kind === 'conjugate' ?
|
|
191
|
+
estimateConjugateWidth(m.symbol, layout.chipW, layout.fontSize) :
|
|
192
|
+
layout.chipW;
|
|
193
|
+
|
|
194
|
+
if (x + w > endX) break; // truncate at cell edge
|
|
195
|
+
|
|
196
|
+
chips.push({x, w, monomer: m, origIdx: m.position, strand: side});
|
|
197
|
+
|
|
198
|
+
// Linkage from this monomer's 3'-phosphate goes in the gap to the right.
|
|
199
|
+
// When the strand is reversed (anti-parallel), the linkage that was
|
|
200
|
+
// "owned by monomer N's 3' end" connects monomer N to monomer N+1 in the
|
|
201
|
+
// data; in display these are still adjacent, so the gap-to-right is still
|
|
202
|
+
// the right place to draw it. We just need to look up the correct owner.
|
|
203
|
+
if (m.kind === 'nucleotide' && i < monomers.length - 1) {
|
|
204
|
+
const nt = m as ParsedNucleotide;
|
|
205
|
+
if (nt.phosphate) {
|
|
206
|
+
links.push({
|
|
207
|
+
x: x + w, w: layout.chipGap, y, h: layout.chipH,
|
|
208
|
+
phosphateSymbol: nt.phosphate,
|
|
209
|
+
ownerOrigIdx: nt.position,
|
|
210
|
+
strand: side,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
x += w + layout.chipGap;
|
|
215
|
+
}
|
|
216
|
+
return {chips, links};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Width estimate for a conjugate pill, using only chip metrics (no canvas measure). */
|
|
220
|
+
function estimateConjugateWidth(symbol: string, chipW: number, fontSize: number): number {
|
|
221
|
+
const meta = resolveConjugate(symbol).meta;
|
|
222
|
+
const charW = fontSize * 0.55;
|
|
223
|
+
const textW = meta.short.length * charW;
|
|
224
|
+
const padding = chipW * 0.6;
|
|
225
|
+
return Math.max(chipW, Math.min(chipW * 4, textW + padding));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Sum of widths of leading conjugates in display order (after reversal if any),
|
|
229
|
+
* including their trailing chipGap, so antisense and sense can be shifted
|
|
230
|
+
* independently to align their first nucleotides at the same X coordinate. */
|
|
231
|
+
function leadingConjugateWidth(
|
|
232
|
+
monomers: ParsedMonomer[], reversed: boolean,
|
|
233
|
+
chipW: number, fontSize: number, chipGap: number,
|
|
234
|
+
): number {
|
|
235
|
+
const seq = reversed ? monomers.slice().reverse() : monomers;
|
|
236
|
+
let w = 0;
|
|
237
|
+
for (const m of seq) {
|
|
238
|
+
if (m.kind !== 'conjugate') break;
|
|
239
|
+
w += estimateConjugateWidth(m.symbol, chipW, fontSize) + chipGap;
|
|
240
|
+
}
|
|
241
|
+
return w;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* ---------------------------------------------------------------- *
|
|
245
|
+
* Drawing
|
|
246
|
+
* ---------------------------------------------------------------- */
|
|
247
|
+
|
|
248
|
+
export function drawDuplex(
|
|
249
|
+
g: CanvasRenderingContext2D, cellX: number, cellY: number,
|
|
250
|
+
cellW: number, cellH: number, model: ParsedDuplex,
|
|
251
|
+
opts: Partial<RenderOpts> = {},
|
|
252
|
+
): DuplexLayout {
|
|
253
|
+
const o: RenderOpts = {...DEFAULT_OPTS, ...opts};
|
|
254
|
+
const layout = computeLayout(cellW, cellH, model, o);
|
|
255
|
+
|
|
256
|
+
g.save();
|
|
257
|
+
g.beginPath();
|
|
258
|
+
g.rect(cellX, cellY, cellW, cellH);
|
|
259
|
+
g.clip();
|
|
260
|
+
g.translate(cellX, cellY);
|
|
261
|
+
|
|
262
|
+
if (layout.textOnlyFallback) {
|
|
263
|
+
drawFallbackText(g, cellW, cellH, model, layout);
|
|
264
|
+
g.restore();
|
|
265
|
+
return layout;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Strand label "S 5'" left of sense
|
|
269
|
+
drawStrandLabel(g, 'S', '5\'', layout.padding, layout.senseY + layout.chipH / 2, layout);
|
|
270
|
+
|
|
271
|
+
// Linkages first (so chips paint over their rounded edges cleanly)
|
|
272
|
+
for (const link of layout.senseLinks) drawLinkage(g, link);
|
|
273
|
+
drawChips(g, layout.senseChips, layout, o);
|
|
274
|
+
drawTruncationMarker(g, layout.senseChips, model.sense.monomers.length, layout);
|
|
275
|
+
|
|
276
|
+
if (layout.antiY >= 0 && model.antisense) {
|
|
277
|
+
// When reversed, the leftmost chip in display is the 3' end of antisense.
|
|
278
|
+
const leftLabel = layout.antiReversed ? '3\'' : '5\'';
|
|
279
|
+
drawStrandLabel(g, 'AS', leftLabel, layout.padding, layout.antiY + layout.chipH / 2, layout);
|
|
280
|
+
for (const link of layout.antiLinks) drawLinkage(g, link);
|
|
281
|
+
drawChips(g, layout.antiChips, layout, o);
|
|
282
|
+
drawTruncationMarker(g, layout.antiChips, model.antisense.monomers.length, layout);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
g.restore();
|
|
286
|
+
return layout;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function drawStrandLabel(
|
|
290
|
+
g: CanvasRenderingContext2D, strand: string, terminus: string, x: number, y: number, layout: DuplexLayout,
|
|
291
|
+
): void {
|
|
292
|
+
g.fillStyle = '#8b949e';
|
|
293
|
+
g.font = `${Math.max(8, layout.fontSize - 1)}px ui-monospace, Menlo, monospace`;
|
|
294
|
+
g.textBaseline = 'middle';
|
|
295
|
+
g.textAlign = 'left';
|
|
296
|
+
g.fillText(`${strand} ${terminus}`, x, y);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function drawTruncationMarker(
|
|
300
|
+
g: CanvasRenderingContext2D, chips: ChipPos[], totalCount: number, layout: DuplexLayout,
|
|
301
|
+
): void {
|
|
302
|
+
if (chips.length >= totalCount) return;
|
|
303
|
+
const last = chips[chips.length - 1];
|
|
304
|
+
if (!last) return;
|
|
305
|
+
g.fillStyle = '#6e7681';
|
|
306
|
+
g.font = `${Math.max(7, layout.fontSize - 1)}px ui-monospace, Menlo, monospace`;
|
|
307
|
+
g.textBaseline = 'middle';
|
|
308
|
+
g.textAlign = 'left';
|
|
309
|
+
g.fillText('…', last.x + last.w + 1, layout.senseY + layout.chipH / 2);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function drawChips(g: CanvasRenderingContext2D, chips: ChipPos[], layout: DuplexLayout, opts: RenderOpts): void {
|
|
313
|
+
const y = chips[0]?.strand === 'sense' ? layout.senseY : layout.antiY;
|
|
314
|
+
for (const cp of chips) {
|
|
315
|
+
if (cp.monomer.kind === 'conjugate')
|
|
316
|
+
drawConjugate(g, cp.monomer.symbol, cp.x, y, cp.w, layout.chipH, layout.fontSize);
|
|
317
|
+
else
|
|
318
|
+
drawChip(g, cp.monomer as ParsedNucleotide, cp.x, y, cp.w, layout.chipH, layout.fontSize, opts);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function drawChip(
|
|
323
|
+
g: CanvasRenderingContext2D, m: ParsedNucleotide, x: number, y: number,
|
|
324
|
+
w: number, h: number, fontSize: number, opts: RenderOpts,
|
|
325
|
+
): void {
|
|
326
|
+
const sugarRes = resolveSugar(m.sugar, m.base);
|
|
327
|
+
const baseColor = BASE_COLORS[m.base ?? ''] ?? FALLBACK_COLOR;
|
|
328
|
+
const isModSugar = isModifiedSugar(m.sugar);
|
|
329
|
+
const r = Math.min(2.5, w / 4);
|
|
330
|
+
|
|
331
|
+
// Chip body — base-canonical pale color
|
|
332
|
+
drawRoundRect(g, x, y, w, h, r);
|
|
333
|
+
g.fillStyle = withAlpha(baseColor, CHIP_FILL_ALPHA);
|
|
334
|
+
g.fill();
|
|
335
|
+
g.lineWidth = CHIP_BORDER_W;
|
|
336
|
+
g.strokeStyle = 'rgba(0,0,0,0.22)';
|
|
337
|
+
g.stroke();
|
|
338
|
+
|
|
339
|
+
// Sugar modification stripe at chip bottom — clipped to rounded shape
|
|
340
|
+
if (isModSugar) {
|
|
341
|
+
const stripeH = Math.max(2, h * SUGAR_STRIPE_RATIO);
|
|
342
|
+
g.save();
|
|
343
|
+
drawRoundRect(g, x, y, w, h, r);
|
|
344
|
+
g.clip();
|
|
345
|
+
g.fillStyle = sugarRes.color;
|
|
346
|
+
g.fillRect(x, y + h - stripeH, w, stripeH);
|
|
347
|
+
g.restore();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Base letter — biased upward to leave room for stripe
|
|
351
|
+
if (opts.showLetters && m.base && fontSize >= 8) {
|
|
352
|
+
const stripeH = isModSugar ? Math.max(2, h * SUGAR_STRIPE_RATIO) : 0;
|
|
353
|
+
g.fillStyle = '#1a1a1a';
|
|
354
|
+
g.font = `600 ${fontSize}px system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif`;
|
|
355
|
+
g.textBaseline = 'middle';
|
|
356
|
+
g.textAlign = 'center';
|
|
357
|
+
g.fillText(m.base, x + w / 2, y + (h - stripeH) / 2 + 0.5);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function isModifiedSugar(sugar: string): boolean {
|
|
362
|
+
const c = canonicalSugarSymbol(sugar);
|
|
363
|
+
return c !== 'r' && c !== 'd';
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function drawLinkage(g: CanvasRenderingContext2D, link: LinkagePos): void {
|
|
367
|
+
const ps = resolvePhosphate(link.phosphateSymbol);
|
|
368
|
+
if (ps.meta.short !== 'PS' && ps.meta.short !== 'PS₂' && ps.meta.short !== 'MeP')
|
|
369
|
+
return; // only draw markers for non-canonical linkages
|
|
370
|
+
const barW = Math.max(2.5, link.w * PS_BAR_RATIO);
|
|
371
|
+
const barX = link.x + (link.w - barW) / 2;
|
|
372
|
+
g.fillStyle = ps.color;
|
|
373
|
+
g.fillRect(barX, link.y + 1, barW, link.h - 2);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function drawConjugate(
|
|
377
|
+
g: CanvasRenderingContext2D, symbol: string, x: number, y: number,
|
|
378
|
+
w: number, chipH: number, fontSize: number,
|
|
379
|
+
): void {
|
|
380
|
+
const conj = resolveConjugate(symbol);
|
|
381
|
+
const r = chipH / 2;
|
|
382
|
+
drawRoundRect(g, x, y, w, chipH, r);
|
|
383
|
+
g.fillStyle = conj.color;
|
|
384
|
+
g.fill();
|
|
385
|
+
g.lineWidth = 0.5;
|
|
386
|
+
g.strokeStyle = 'rgba(0,0,0,0.2)';
|
|
387
|
+
g.stroke();
|
|
388
|
+
|
|
389
|
+
if (fontSize >= 8) {
|
|
390
|
+
g.fillStyle = '#ffffff';
|
|
391
|
+
g.font = `600 ${Math.max(8, fontSize - 1)}px system-ui, sans-serif`;
|
|
392
|
+
g.textBaseline = 'middle';
|
|
393
|
+
g.textAlign = 'center';
|
|
394
|
+
g.fillText(conj.meta.short, x + w / 2, y + chipH / 2 + 0.5);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function drawRoundRect(
|
|
399
|
+
g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number,
|
|
400
|
+
): void {
|
|
401
|
+
g.beginPath();
|
|
402
|
+
g.moveTo(x + r, y);
|
|
403
|
+
g.lineTo(x + w - r, y);
|
|
404
|
+
g.quadraticCurveTo(x + w, y, x + w, y + r);
|
|
405
|
+
g.lineTo(x + w, y + h - r);
|
|
406
|
+
g.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
|
|
407
|
+
g.lineTo(x + r, y + h);
|
|
408
|
+
g.quadraticCurveTo(x, y + h, x, y + h - r);
|
|
409
|
+
g.lineTo(x, y + r);
|
|
410
|
+
g.quadraticCurveTo(x, y, x + r, y);
|
|
411
|
+
g.closePath();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function drawFallbackText(
|
|
415
|
+
g: CanvasRenderingContext2D, _w: number, h: number, model: ParsedDuplex, layout: DuplexLayout,
|
|
416
|
+
): void {
|
|
417
|
+
const sLen = model.sense.monomers.length;
|
|
418
|
+
const aLen = model.antisense ? model.antisense.monomers.length : 0;
|
|
419
|
+
const summary = model.antisense ? `${sLen}+${aLen} nt duplex` : `${sLen} nt`;
|
|
420
|
+
g.fillStyle = '#8b949e';
|
|
421
|
+
g.font = `${Math.max(8, layout.fontSize)}px ui-monospace, Menlo, monospace`;
|
|
422
|
+
g.textBaseline = 'middle';
|
|
423
|
+
g.textAlign = 'left';
|
|
424
|
+
g.fillText(summary, 4, h / 2);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Apply alpha to any CSS color string, returning rgba(...). Memoized. */
|
|
428
|
+
const _alphaCache = new Map<string, string>();
|
|
429
|
+
function withAlpha(color: string, alpha: number): string {
|
|
430
|
+
const key = `${color}|${alpha}`;
|
|
431
|
+
const cached = _alphaCache.get(key);
|
|
432
|
+
if (cached) return cached;
|
|
433
|
+
const probe = document.createElement('span');
|
|
434
|
+
probe.style.color = color;
|
|
435
|
+
document.body.appendChild(probe);
|
|
436
|
+
const rgb = getComputedStyle(probe).color;
|
|
437
|
+
document.body.removeChild(probe);
|
|
438
|
+
const m = rgb.match(/\d+/g);
|
|
439
|
+
const out = m ? `rgba(${m[0]},${m[1]},${m[2]},${alpha})` : color;
|
|
440
|
+
if (_alphaCache.size > 256) _alphaCache.clear();
|
|
441
|
+
_alphaCache.set(key, out);
|
|
442
|
+
return out;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* ---------------------------------------------------------------- *
|
|
446
|
+
* Hit testing — uses the cached chip / linkage positions so it works
|
|
447
|
+
* correctly with variable widths (conjugate pills) and reversed antisense.
|
|
448
|
+
* ---------------------------------------------------------------- */
|
|
449
|
+
|
|
450
|
+
export interface HitResult {
|
|
451
|
+
strand: StrandSide;
|
|
452
|
+
/** Original 0-based index in the data strand. */
|
|
453
|
+
position: number;
|
|
454
|
+
monomer: ParsedMonomer;
|
|
455
|
+
/** Set if the hit is on an inter-chip linkage marker (PS, etc.). */
|
|
456
|
+
linkage?: { phosphateSymbol: string };
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export function hitTest(
|
|
460
|
+
localX: number, localY: number, model: ParsedDuplex, layout: DuplexLayout,
|
|
461
|
+
): HitResult | null {
|
|
462
|
+
if (layout.textOnlyFallback) return null;
|
|
463
|
+
|
|
464
|
+
// Sense chip?
|
|
465
|
+
if (localY >= layout.senseY && localY <= layout.senseY + layout.chipH) {
|
|
466
|
+
const cp = findChip(localX, layout.senseChips);
|
|
467
|
+
if (cp) return {strand: 'sense', position: cp.origIdx, monomer: cp.monomer};
|
|
468
|
+
const link = findLink(localX, localY, layout.senseLinks);
|
|
469
|
+
if (link) return resolveLinkHit(link, model.sense, 'sense');
|
|
470
|
+
}
|
|
471
|
+
// Antisense chip?
|
|
472
|
+
if (layout.antiY >= 0 && localY >= layout.antiY && localY <= layout.antiY + layout.chipH) {
|
|
473
|
+
const cp = findChip(localX, layout.antiChips);
|
|
474
|
+
if (cp) return {strand: 'antisense', position: cp.origIdx, monomer: cp.monomer};
|
|
475
|
+
const link = findLink(localX, localY, layout.antiLinks);
|
|
476
|
+
if (link && model.antisense) return resolveLinkHit(link, model.antisense, 'antisense');
|
|
477
|
+
}
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function findChip(x: number, chips: ChipPos[]): ChipPos | null {
|
|
482
|
+
for (const cp of chips) if (x >= cp.x && x < cp.x + cp.w) return cp;
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function findLink(x: number, y: number, links: LinkagePos[]): LinkagePos | null {
|
|
487
|
+
for (const l of links)
|
|
488
|
+
if (x >= l.x && x < l.x + l.w && y >= l.y && y < l.y + l.h) return l;
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function resolveLinkHit(link: LinkagePos, strand: ParsedStrand, side: StrandSide): HitResult {
|
|
493
|
+
const owner = strand.monomers.find((m) => m.position === link.ownerOrigIdx)!;
|
|
494
|
+
return {
|
|
495
|
+
strand: side,
|
|
496
|
+
position: link.ownerOrigIdx,
|
|
497
|
+
monomer: owner,
|
|
498
|
+
linkage: {phosphateSymbol: link.phosphateSymbol},
|
|
499
|
+
};
|
|
500
|
+
}
|