@datagrok/sequence-translator 0.0.6 → 0.0.12

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.
@@ -0,0 +1,432 @@
1
+ import * as OCL from 'openchemlib/full.js';
2
+
3
+ const PHOSHATE = `
4
+ Datagrok monomer library Nucleotides
5
+
6
+ 0 0 0 0 0 0 0 V3000
7
+ M V30 BEGIN CTAB
8
+ M V30 COUNTS 5 4 0 0 0
9
+ M V30 BEGIN ATOM
10
+ M V30 1 O -1.5 0 0 0
11
+ M V30 2 P 0 0 0 0
12
+ M V30 3 O 0 1 0 0
13
+ M V30 4 O 0 -1 0 0
14
+ M V30 5 O 1.5 0 0 0
15
+ M V30 END ATOM
16
+ M V30 BEGIN BOND
17
+ M V30 1 1 1 2
18
+ M V30 2 2 2 3
19
+ M V30 3 1 2 4
20
+ M V30 4 1 2 5
21
+ M V30 END BOND
22
+ M V30 END CTAB
23
+ M V30 BEGIN COLLECTION
24
+ M V30 END COLLECTION
25
+ M END`;
26
+
27
+ const THIOPHOSHATE = `
28
+ Datagrok monomer library Nucleotides
29
+
30
+ 0 0 0 0 0 0 0 V3000
31
+ M V30 BEGIN CTAB
32
+ M V30 COUNTS 5 4 0 0 0
33
+ M V30 BEGIN ATOM
34
+ M V30 1 O -1.5 0 0 0
35
+ M V30 2 P 0 0 0 0
36
+ M V30 3 O 0 1 0 0
37
+ M V30 4 S 0 -1 0 0
38
+ M V30 5 O 1.5 0 0 0
39
+ M V30 END ATOM
40
+ M V30 BEGIN BOND
41
+ M V30 1 1 1 2
42
+ M V30 2 2 2 3
43
+ M V30 3 1 2 4
44
+ M V30 4 1 2 5
45
+ M V30 END BOND
46
+ M V30 END CTAB
47
+ M V30 BEGIN COLLECTION
48
+ M V30 END COLLECTION
49
+ M END`;
50
+
51
+ const INVABASIC = `
52
+ Datagrok monomer library Nucleotides
53
+
54
+ 0 0 0 0 0 0 0 V3000
55
+ M V30 BEGIN CTAB
56
+ M V30 COUNTS 8 8 0 0 0
57
+ M V30 BEGIN ATOM
58
+ M V30 1 O 1.0934 -2.1636 0 0
59
+ M V30 2 C 1.8365 -1.4945 0 0 CFG=2
60
+ M V30 3 C 2.8147 -1.7024 0 0
61
+ M V30 4 C 3.3147 -0.8364 0 0 VAL=3
62
+ M V30 5 O 2.6455 -0.0932 0 0
63
+ M V30 6 C 1.732 -0.5 0 0 CFG=1
64
+ M V30 7 C 0.866 0 0 0
65
+ M V30 8 O 0.866 1 0 0
66
+ M V30 END ATOM
67
+ M V30 BEGIN BOND
68
+ M V30 1 1 2 1 CFG=1
69
+ M V30 2 1 2 3
70
+ M V30 3 1 3 4
71
+ M V30 4 1 4 5
72
+ M V30 5 1 6 5
73
+ M V30 6 1 2 6
74
+ M V30 7 1 6 7 CFG=3
75
+ M V30 8 1 7 8
76
+ M V30 END BOND
77
+ M V30 BEGIN COLLECTION
78
+ M V30 MDLV30/STEABS ATOMS=(2 2 6)
79
+ M V30 END COLLECTION
80
+ M V30 END CTAB
81
+ M END`;
82
+
83
+ export function getNucleotidesMol(smilesCodes: string[], oclRender: boolean = false) {
84
+ const molBlocks: string[] = [];
85
+
86
+ for (let i = 0; i < smilesCodes.length - 1; i++) {
87
+ smilesCodes[i] == 'OP(=O)(O)O' ? molBlocks.push(PHOSHATE) :
88
+ smilesCodes[i] == 'OP(=O)(S)O' ? molBlocks.push(THIOPHOSHATE) :
89
+ smilesCodes[i] == 'O[C@@H]1C[C@@H]O[C@H]1CO' ? molBlocks.push(rotateNucleotidesV3000(INVABASIC)) :
90
+ molBlocks.push(rotateNucleotidesV3000(smilesCodes[i]));
91
+ }
92
+
93
+ return linkV3000(molBlocks, false, oclRender);
94
+ }
95
+
96
+ export function linkV3000(molBlocks: string[], twoMolecules: boolean = false, oclRender: boolean = false) {
97
+ let macroMolBlock = '\nDatagrok macromolecule handler\n\n';
98
+ macroMolBlock += ' 0 0 0 0 0 0 999 V3000\n';
99
+ macroMolBlock += 'M V30 BEGIN CTAB\n';
100
+ let atomBlock = '';
101
+ let bondBlock = '';
102
+ let collectionBlock = '';
103
+ const collection: number [] = [];
104
+ let natom = 0;
105
+ let nbond = 0;
106
+ let sequenceShift = 0;
107
+ let xShift = 0;
108
+
109
+ if (twoMolecules && molBlocks.length > 1)
110
+ molBlocks[1] = invertNucleotidesV3000(molBlocks[1]);
111
+
112
+ for (let i = 0; i < molBlocks.length; i++) {
113
+ molBlocks[i] = molBlocks[i].replaceAll('(-\nM V30 ', '(')
114
+ .replaceAll('-\nM V30 ', '').replaceAll(' )', ')');
115
+ const numbers = extractAtomsBondsNumbersV3000(molBlocks[i]);
116
+ const coordinates = extractAtomDataV3000(molBlocks[i]);
117
+ let indexAtoms = molBlocks[i].indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
118
+ indexAtoms = molBlocks[i].indexOf('\n', indexAtoms);
119
+ let index = indexAtoms;
120
+ let indexEnd = indexAtoms;
121
+
122
+ for (let j = 0; j < numbers.natom; j++) {
123
+ if (coordinates.atomIndex[j] != 1 || i == 0 || twoMolecules) {
124
+ //rewrite atom number
125
+ index = molBlocks[i].indexOf('V30', index) + 4;
126
+ indexEnd = molBlocks[i].indexOf(' ', index);
127
+ const atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
128
+ molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
129
+
130
+ //rewrite coordinates
131
+ index = molBlocks[i].indexOf(' ', index) + 1;
132
+ index = molBlocks[i].indexOf(' ', index) + 1;
133
+ indexEnd = molBlocks[i].indexOf(' ', index);
134
+
135
+ const totalShift = xShift - coordinates.x[0];
136
+ let coordinate = Math.round(10000*(parseFloat(molBlocks[i].substring(index, indexEnd)) + totalShift))/10000;
137
+ molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
138
+
139
+ index = molBlocks[i].indexOf(' ', index) + 1;
140
+ indexEnd = molBlocks[i].indexOf(' ', index);
141
+ coordinate = Math.round(10000*(parseFloat(molBlocks[i].substring(index, indexEnd)) + sequenceShift))/10000;
142
+ molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
143
+
144
+ index = molBlocks[i].indexOf('\n', index) + 1;
145
+ } else {
146
+ index = molBlocks[i].indexOf('M V30', index) - 1;
147
+ indexEnd = molBlocks[i].indexOf('\n', index + 1);
148
+ molBlocks[i] = molBlocks[i].slice(0, index) + molBlocks[i].slice(indexEnd);
149
+ }
150
+ }
151
+
152
+ const indexAtomsEnd = molBlocks[i].indexOf('M V30 END ATOM');
153
+ atomBlock += molBlocks[i].substring(indexAtoms + 1, indexAtomsEnd);
154
+
155
+ let indexBonds = molBlocks[i].indexOf('M V30 BEGIN BOND'); // V3000 index for bonds
156
+ indexBonds = molBlocks[i].indexOf('\n', indexBonds);
157
+ index = indexBonds;
158
+ indexEnd = indexBonds;
159
+
160
+ for (let j = 0; j < numbers.nbond; j++) {
161
+ //rewrite bond number
162
+ index = molBlocks[i].indexOf('V30', index) + 4;
163
+ indexEnd = molBlocks[i].indexOf(' ', index);
164
+ const bondNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + nbond;
165
+ molBlocks[i] = molBlocks[i].slice(0, index) + bondNumber + molBlocks[i].slice(indexEnd);
166
+
167
+ //rewrite atom pair in bond
168
+ index = molBlocks[i].indexOf(' ', index) + 1;
169
+ index = molBlocks[i].indexOf(' ', index) + 1;
170
+ indexEnd = molBlocks[i].indexOf(' ', index);
171
+ let atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
172
+ molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
173
+ index = molBlocks[i].indexOf(' ', index) + 1;
174
+ indexEnd = Math.min(molBlocks[i].indexOf('\n', index), molBlocks[i].indexOf(' ', index));
175
+ atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
176
+ molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
177
+
178
+ index = molBlocks[i].indexOf('\n', index) + 1;
179
+ }
180
+
181
+ const indexBondEnd = molBlocks[i].indexOf('M V30 END BOND');
182
+ bondBlock += molBlocks[i].substring(indexBonds + 1, indexBondEnd);
183
+
184
+ let indexCollection = molBlocks[i].indexOf('M V30 MDLV30/STEABS ATOMS=('); // V3000 index for collections
185
+
186
+ while (indexCollection != -1) {
187
+ indexCollection += 28;
188
+ const collectionEnd = molBlocks[i].indexOf(')', indexCollection);
189
+ const collectionEntries = molBlocks[i].substring(indexCollection, collectionEnd).split(' ').slice(1);
190
+ collectionEntries.forEach((e) => {
191
+ collection.push(parseInt(e) + natom);
192
+ });
193
+ indexCollection = collectionEnd;
194
+ indexCollection = molBlocks[i].indexOf('M V30 MDLV30/STEABS ATOMS=(', indexCollection);
195
+ }
196
+
197
+ natom += twoMolecules ? numbers.natom : numbers.natom - 1;
198
+ nbond += numbers.nbond;
199
+ xShift += twoMolecules ? 0 : coordinates.x[numbers.natom - 1] - coordinates.x[0];
200
+ sequenceShift += twoMolecules ? -7 : 0;
201
+ }
202
+
203
+ const entries = 4;
204
+ const collNumber = Math.ceil(collection.length / entries);
205
+
206
+ if (oclRender) {
207
+ collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length;
208
+
209
+ for (let j = 0; j < collection.length; j++)
210
+ collectionBlock += ' ' + collection[j];
211
+
212
+ collectionBlock += ')\n';
213
+ } else {
214
+ collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length + ' -\n';
215
+ for (let i = 0; i < collNumber; i++) {
216
+ collectionBlock += 'M V30 ';
217
+ const entriesCurrent = i + 1 == collNumber ? collection.length - (collNumber - 1)*entries : entries;
218
+ for (let j = 0; j < entriesCurrent; j++) {
219
+ collectionBlock += (j + 1 == entriesCurrent) ?
220
+ (i == collNumber - 1 ? collection[entries*i + j] + ')\n' : collection[entries*i + j] + ' -\n') :
221
+ collection[entries*i + j] + ' ';
222
+ }
223
+ }
224
+ }
225
+
226
+ //generate file
227
+ twoMolecules? natom : natom++;
228
+ macroMolBlock += 'M V30 COUNTS ' + natom + ' ' + nbond + ' 0 0 0\n';
229
+ macroMolBlock += 'M V30 BEGIN ATOM\n';
230
+ macroMolBlock += atomBlock;
231
+ macroMolBlock += 'M V30 END ATOM\n';
232
+ macroMolBlock += 'M V30 BEGIN BOND\n';
233
+ macroMolBlock += bondBlock;
234
+ macroMolBlock += 'M V30 END BOND\n';
235
+ macroMolBlock += 'M V30 BEGIN COLLECTION\n';
236
+ macroMolBlock += collectionBlock;
237
+ macroMolBlock += 'M V30 END COLLECTION\n';
238
+ macroMolBlock += 'M V30 END CTAB\n';
239
+ macroMolBlock += 'M END\n';
240
+
241
+ return macroMolBlock;
242
+ }
243
+
244
+ function rotateNucleotidesV3000(molecule: string) {
245
+ let molBlock = molecule.includes('M END') ? molecule : OCL.Molecule.fromSmiles(molecule).toMolfileV3();
246
+ const coordinates = extractAtomDataV3000(molBlock);
247
+ const natom = coordinates.atomIndex.length;
248
+
249
+ const indexFivePrime = coordinates.atomIndex.indexOf(1);
250
+ const indexThreePrime = coordinates.atomIndex.indexOf(natom);
251
+
252
+ //fix 5 prime if inadequate
253
+ if (natom > 8)
254
+ fix5Prime(coordinates, indexFivePrime, indexThreePrime);
255
+
256
+ const xCenter = (coordinates.x[indexThreePrime] + coordinates.x[indexFivePrime])/2;
257
+ const yCenter = (coordinates.y[indexThreePrime] + coordinates.y[indexFivePrime])/2;
258
+
259
+ //place to center
260
+ for (let i = 0; i < natom; i++) {
261
+ coordinates.x[i] -= xCenter;
262
+ coordinates.y[i] -= yCenter;
263
+ }
264
+
265
+ let angle = 0;
266
+ if (coordinates.x[indexFivePrime] == 0)
267
+ angle = coordinates.y[indexFivePrime] > coordinates.y[indexThreePrime] ? Math.PI/2 : 3*Math.PI/2;
268
+ else if (coordinates.y[indexFivePrime] == 0)
269
+ angle = coordinates.x[indexFivePrime] > coordinates.x[indexThreePrime] ? Math.PI : 0;
270
+ else {
271
+ const derivative = coordinates.y[indexFivePrime]/coordinates.x[indexFivePrime];
272
+ angle = derivative > 0 ? Math.PI - Math.atan(derivative) : Math.atan(derivative);
273
+ }
274
+
275
+ const cos = Math.cos(angle);
276
+ const sin = Math.sin(angle);
277
+
278
+ for (let i = 0; i < natom; i++) {
279
+ const xAdd = coordinates.x[i];
280
+ coordinates.x[i] = xAdd*cos - coordinates.y[i]*sin;
281
+ coordinates.y[i] = xAdd*sin + coordinates.y[i]*cos;
282
+ }
283
+
284
+ //place to right
285
+ const xShift = coordinates.x[indexFivePrime];
286
+ for (let i = 0; i < natom; i++)
287
+ coordinates.x[i] -= xShift;
288
+
289
+ //rewrite molBlock
290
+ let index = molBlock.indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
291
+ index = molBlock.indexOf('\n', index);
292
+ let indexEnd = index;
293
+ for (let i = 0; i < natom; i++) {
294
+ index = molBlock.indexOf('V30', index) + 4;
295
+ index = molBlock.indexOf(' ', index) + 1;
296
+ index = molBlock.indexOf(' ', index) + 1;
297
+ indexEnd = molBlock.indexOf(' ', index) + 1;
298
+ indexEnd = molBlock.indexOf(' ', indexEnd);
299
+
300
+ molBlock = molBlock.slice(0, index) +
301
+ coordinates.x[i] + ' ' + coordinates.y[i] +
302
+ molBlock.slice(indexEnd);
303
+
304
+ index = molBlock.indexOf('\n', index) + 1;
305
+ }
306
+
307
+ return molBlock;
308
+ }
309
+
310
+ function invertNucleotidesV3000(molecule: string) {
311
+ let molBlock = molecule.includes('M END') ? molecule : OCL.Molecule.fromSmiles(molecule).toMolfileV3();
312
+ const coordinates = extractAtomDataV3000(molBlock);
313
+ const natom = coordinates.atomIndex.length;
314
+
315
+ const xCenter = (Math.max(...coordinates.x) + Math.min(...coordinates.x))/2;
316
+ const yCenter = (Math.max(...coordinates.y) + Math.min(...coordinates.y))/2;
317
+
318
+ //place to center
319
+ for (let i = 0; i < natom; i++) {
320
+ coordinates.x[i] -= xCenter;
321
+ coordinates.y[i] -= yCenter;
322
+ }
323
+
324
+ const angle = Math.PI;
325
+
326
+ const cos = Math.cos(angle);
327
+ const sin = Math.sin(angle);
328
+
329
+ for (let i = 0; i < natom; i++) {
330
+ const xAdd = coordinates.x[i];
331
+ coordinates.x[i] = xAdd*cos - coordinates.y[i]*sin;
332
+ coordinates.y[i] = xAdd*sin + coordinates.y[i]*cos;
333
+ }
334
+
335
+ //place back
336
+ const yShift = Math.max(...coordinates.y);
337
+ for (let i = 0; i < natom; i++) {
338
+ coordinates.x[i] += xCenter;
339
+ coordinates.y[i] -= yShift;
340
+ }
341
+
342
+ //rewrite molBlock
343
+ let index = molBlock.indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
344
+ index = molBlock.indexOf('\n', index);
345
+ let indexEnd = index;
346
+ for (let i = 0; i < natom; i++) {
347
+ index = molBlock.indexOf('V30', index) + 4;
348
+ index = molBlock.indexOf(' ', index) + 1;
349
+ index = molBlock.indexOf(' ', index) + 1;
350
+ indexEnd = molBlock.indexOf(' ', index) + 1;
351
+ indexEnd = molBlock.indexOf(' ', indexEnd);
352
+
353
+ molBlock = molBlock.slice(0, index) +
354
+ coordinates.x[i] + ' ' + coordinates.y[i] +
355
+ molBlock.slice(indexEnd);
356
+
357
+ index = molBlock.indexOf('\n', index) + 1;
358
+ }
359
+
360
+ return molBlock;
361
+ }
362
+
363
+ function fix5Prime(coordinates: {atomIndex: number[], atomType: string[], x: number[], y: number[]},
364
+ indexFivePrime: number, indexThreePrime: number) {
365
+ const indexFivePrimeNeighbour = indexFivePrime + 1;
366
+ const xShift = coordinates.x[indexFivePrimeNeighbour];
367
+ const yShift = coordinates.y[indexFivePrimeNeighbour];
368
+ const base3PrimeX = coordinates.x[indexThreePrime] - xShift;
369
+ const base3PrimeY = coordinates.y[indexThreePrime] - yShift;
370
+ const base5PrimeX = coordinates.x[indexFivePrime] - xShift;
371
+ const base5PrimeY = coordinates.y[indexFivePrime] - yShift;
372
+
373
+ const rotated5PrimeX = base5PrimeX*Math.cos(Math.PI*2/3) - base5PrimeY*Math.sin(Math.PI*2/3);
374
+ const rotated5PrimeY = base5PrimeX*Math.sin(Math.PI*2/3) + base5PrimeY*Math.cos(Math.PI*2/3);
375
+
376
+ const dx = base5PrimeX - base3PrimeX;
377
+ const dy = base5PrimeY - base3PrimeY;
378
+ const dxRotated = rotated5PrimeX - base3PrimeX;
379
+ const dyRotated = rotated5PrimeY - base3PrimeY;
380
+
381
+ if (Math.sqrt(dyRotated*dyRotated + dxRotated*dxRotated) >= Math.sqrt(dy*dy + dx*dx)) {
382
+ coordinates.x[indexFivePrime] = rotated5PrimeX + xShift;
383
+ coordinates.y[indexFivePrime] = rotated5PrimeY + yShift;
384
+ }
385
+ }
386
+
387
+ function extractAtomsBondsNumbersV3000(molBlock: string): {natom: number, nbond: number} {
388
+ molBlock = molBlock.replaceAll('\r', ''); //equalize old and new sdf standards
389
+ let index = molBlock.indexOf('COUNTS') + 7; // V3000 index for atoms and bonds number
390
+ let indexEnd = molBlock.indexOf(' ', index);
391
+
392
+ const atomsNumber = parseInt(molBlock.substring(index, indexEnd));
393
+ index = indexEnd + 1;
394
+ indexEnd = molBlock.indexOf(' ', index);
395
+ const bondsNumber = parseInt(molBlock.substring(index, indexEnd));
396
+
397
+ return {natom: atomsNumber, nbond: bondsNumber};
398
+ }
399
+
400
+ function extractAtomDataV3000(molBlock: string) {
401
+ const numbers = extractAtomsBondsNumbersV3000(molBlock);
402
+ let index = molBlock.indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
403
+ index = molBlock.indexOf('\n', index);
404
+ let indexEnd = index;
405
+
406
+ const indexes: number[] = Array(numbers.natom);
407
+ const types: string[] = Array(numbers.natom);
408
+ const x: number[] = Array(numbers.natom);
409
+ const y: number[] = Array(numbers.natom);
410
+
411
+ for (let i = 0; i < numbers.natom; i++) {
412
+ index = molBlock.indexOf('V30', index) + 4;
413
+ indexEnd = molBlock.indexOf(' ', index);
414
+ indexes[i] = parseInt(molBlock.substring(index, indexEnd));
415
+
416
+ index = indexEnd + 1;
417
+ indexEnd = molBlock.indexOf(' ', index);
418
+ types[i] = molBlock.substring(index, indexEnd);
419
+
420
+ index = indexEnd + 1;
421
+ indexEnd = molBlock.indexOf(' ', index);
422
+ x[i] = parseFloat(molBlock.substring(index, indexEnd));
423
+
424
+ index = indexEnd + 1;
425
+ indexEnd = molBlock.indexOf(' ', index);
426
+ y[i] = parseFloat(molBlock.substring(index, indexEnd));
427
+
428
+ index = molBlock.indexOf('\n', index) + 1;
429
+ }
430
+
431
+ return {atomIndex: indexes, atomType: types, x: x, y: y};
432
+ }
@@ -0,0 +1,51 @@
1
+ import * as ui from 'datagrok-api/ui';
2
+ import {sequenceToMolV3000} from '../structures-works/from-monomers';
3
+ import {linkV3000} from '../structures-works/mol-transformations';
4
+
5
+ export function saveSdf(as: string, ss: string, oneEntity: boolean, fit3dx: boolean) {
6
+ const molSS = sequenceToMolV3000(ss);
7
+ const molAS = sequenceToMolV3000(as, true);
8
+ let result: string;
9
+ if (oneEntity)
10
+ result = linkV3000([molSS, molAS], true, !fit3dx) + '\n\n$$$$\n';
11
+ else {
12
+ result =
13
+ molSS + '\n' +
14
+ `> <Sequence>\nSense Strand\n\n$$$$\n` +
15
+ molAS + '\n' +
16
+ `> <Sequence>\nAnti Sense\n\n$$$$\n`;
17
+ }
18
+
19
+ const element = document.createElement('a');
20
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
21
+ element.setAttribute('download', ss.replace(/\s/g, '') + '.sdf');
22
+ element.click();
23
+ }
24
+
25
+ export function saveSenseAntiSense() {
26
+ const moleculeSvgDiv = ui.block([]);
27
+ const ssInput = ui.textInput('Sense Strand 5\' ->3\'', '');
28
+ const asInput = ui.textInput('Anti Sense 3\' ->5\'', '');
29
+ const saveOption = ui.switchInput('Save as one entity', true);
30
+ const save3dx = ui.switchInput('Save 3dx', true);
31
+ const saveBtn = ui.button('Save SDF', () =>
32
+ saveSdf(asInput.value, ssInput.value, saveOption.value, save3dx.value));
33
+
34
+ const saveSection = ui.panel([
35
+ ui.div([
36
+ ui.div([
37
+ ui.divH([ui.h1('Inputs')]),
38
+ ui.divV([
39
+ ui.div([ssInput.root]),
40
+ ui.div([asInput.root]),
41
+ saveOption,
42
+ save3dx,
43
+ ui.buttonsInput([saveBtn]),
44
+ ], 'ui-form'),
45
+ ], 'ui-form'),
46
+ ], 'ui-form'),
47
+ moleculeSvgDiv,
48
+ ]);
49
+
50
+ return saveSection;
51
+ }