@datagrok/helm 2.1.18 → 2.1.21

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,5 @@
1
+ Helm,Protein,RNA,DNA
2
+ PEPTIDE1{I.H.A.N.T.Thr_PO3H2.Aca.D-Tyr_Et}$$$$,IHANTK,ACGU,ACGT
3
+ RNA1{r(A)p.r(C)p.r(G)p.r(U)p}$$$$,KHAQTRT,CGUG,CGGT
4
+ RNA1{d(A)p.d(C)p.d(G)p.d(U)p}$$$$,IHANTKT,CCGU,ACCT
5
+ RNA1{d(A)p.d(C)p.d(G)p.d(U)p}|PEPTIDE1{I.H.A.N.T.Thr_PO3H2}$$$$,PQRHLF,UAAA,CTAA
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/helm",
3
3
  "friendlyName": "Helm",
4
- "version": "2.1.18",
4
+ "version": "2.1.21",
5
5
  "author": {
6
6
  "name": "Oleksandra Serhiienko",
7
7
  "email": "oserhiienko@datagrok.ai"
@@ -15,15 +15,16 @@
15
15
  "helm/JSDraw/Pistoia.HELM-uncompressed.js"
16
16
  ],
17
17
  "dependencies": {
18
- "@datagrok-libraries/bio": "^5.38.0",
18
+ "@datagrok-libraries/bio": "^5.39.6",
19
19
  "@datagrok-libraries/utils": "^4.0.17",
20
20
  "cash-dom": "^8.1.1",
21
21
  "datagrok-api": "^1.10.2",
22
22
  "dayjs": "^1.10.6",
23
- "rxjs": "^6.5.5"
23
+ "rxjs": "^6.5.5",
24
+ "wu": "latest"
24
25
  },
25
26
  "devDependencies": {
26
- "@datagrok/bio": "^2.1.12",
27
+ "@types/wu": "latest",
27
28
  "path": "^0.12.7",
28
29
  "source-map-loader": "^4.0.1",
29
30
  "ts-loader": "^9.2.6",
@@ -33,10 +34,11 @@
33
34
  "@typescript-eslint/eslint-plugin": "^5.11.0",
34
35
  "@typescript-eslint/parser": "^5.11.0",
35
36
  "eslint": "^8.18.0",
36
- "eslint-config-google": "^0.14.0"
37
+ "eslint-config-google": "^0.14.0",
38
+ "@datagrok/bio": "^2.11.8"
37
39
  },
38
40
  "scripts": {
39
- "link-all": "npm link datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio",
41
+ "link-all": "npm link datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio",
40
42
  "debug-helm": "webpack && grok publish",
41
43
  "release-helm": "webpack && grok publish --release",
42
44
  "build-helm": "webpack",
@@ -51,7 +53,8 @@
51
53
  "lint-fix": "eslint \"./src/**/*.ts\" --fix",
52
54
  "test": "grok test",
53
55
  "test-dev": "grok test --host dev",
54
- "test-local": "grok test --host localhost"
56
+ "test-local": "grok test --host localhost",
57
+ "build-all": "npm --prefix ./../../js-api run build && npm --prefix ./../../libraries/utils run build && npm --prefix ./../../libraries/bio run build && npm run build"
55
58
  },
56
59
  "canEdit": [
57
60
  "Developers"
@@ -8,7 +8,10 @@ import {printLeftOrCentered} from '@datagrok-libraries/bio/src/utils/cell-render
8
8
  import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
9
9
 
10
10
  import {findMonomers, parseHelm} from './utils';
11
- import {HelmMonomerPlacer} from './helm-monomer-placer';
11
+ import {IEditor, HelmMonomerPlacer, IEditorMolAtom, ISeqMonomer} from './helm-monomer-placer';
12
+ import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
13
+
14
+ // import {_package} from './package'; // NullError: method not found: '_package' on null
12
15
 
13
16
  const enum tempTAGS {
14
17
  helmSumMaxLengthWords = 'helm-sum-maxLengthWords',
@@ -22,7 +25,154 @@ const helmGapStartRe = /\{(\*\.)+/g;
22
25
  const helmGapIntRe = /\.(\*\.)+/g;
23
26
  const helmGapEndRe = /(\.\*)+\}/g;
24
27
 
25
- type TempType = { [tagName: string]: any };
28
+ function getSeqMonomerFromHelm(
29
+ helmPrefix: string, symbol: string, monomerLib: IMonomerLib
30
+ ): ISeqMonomer {
31
+ let resSeqMonomer: ISeqMonomer | undefined = undefined;
32
+ for (const polymerType of monomerLib.getPolymerTypes()) {
33
+ if (helmPrefix.startsWith(polymerType))
34
+ resSeqMonomer = {symbol: symbol, polymerType: polymerType};
35
+ }
36
+ if (!resSeqMonomer)
37
+ resSeqMonomer = {symbol: symbol};
38
+ return resSeqMonomer;
39
+ }
40
+
41
+ function getSeqMonomerFromHelmAtom(atom: IEditorMolAtom): ISeqMonomer {
42
+ let polymerType: string | undefined = undefined;
43
+ // @ts-ignore
44
+ switch (atom.bio.type) {
45
+ case 'HELM_BASE':
46
+ case 'HELM_SUGAR': // r - ribose, d - deoxyribose
47
+ case 'HELM_LINKER': // p - phosphate
48
+ polymerType = 'RNA';
49
+ break;
50
+ case 'HELM_AA':
51
+ polymerType = 'PEPTIDE';
52
+ break;
53
+ default:
54
+ polymerType = 'PEPTIDE';
55
+ }
56
+ return {symbol: atom.elem, polymerType: polymerType};
57
+ }
58
+
59
+ function getHoveredMonomerFromEditor(
60
+ argsX: number, argsY: number, gridCell: DG.GridCell, editor: IEditor
61
+ ): ISeqMonomer | null {
62
+ let hoveredSeqMonomer: ISeqMonomer | null = null;
63
+
64
+ /** @return {[number, number]} [atom, distance] */
65
+ function getNearest(excluded: (number | undefined)[]): [number | undefined, number | undefined] {
66
+ let atom: number | undefined = undefined;
67
+ let distance: number | undefined = undefined;
68
+ for (let atomI = 0; atomI < editor.m.atoms.length; ++atomI) {
69
+ if (!excluded.includes(atomI)) {
70
+ const aX: number = editor.m.atoms[atomI].p.x;
71
+ const aY: number = editor.m.atoms[atomI].p.y;
72
+ const distanceToAtomI: number = Math.sqrt((argsX - aX) ** 2 + (argsY - aY) ** 2);
73
+ if (distance === undefined || distance > distanceToAtomI) {
74
+ atom = atomI;
75
+ distance = distanceToAtomI;
76
+ }
77
+ }
78
+ }
79
+ return [atom, distance];
80
+ }
81
+
82
+ const [firstAtomI, firstDistance] = getNearest([]);
83
+ const [secondAtomI, secondDistance] = getNearest([firstAtomI]);
84
+
85
+ // for (let atomI = 0; atomI < editor.m.atoms.length; ++atomI) {
86
+ // const aX: number = editor.m.atoms[atomI].p.x;
87
+ // const aY: number = editor.m.atoms[atomI].p.y;
88
+ // const distanceToAtomI: number = Math.sqrt((argsX - aX) ** 2 + (argsY - aY) ** 2);
89
+ // if (firstDistance === undefined || firstDistance > distanceToAtomI) {
90
+ // secondAtomI = firstAtomI;
91
+ // firstAtomI = atomI;
92
+ //
93
+ // secondDistance = firstDistance;
94
+ // firstDistance = distanceToAtomI;
95
+ // }
96
+ // }
97
+
98
+ if (firstAtomI !== undefined && firstDistance !== undefined) {
99
+ const firstAtom = editor.m.atoms[firstAtomI];
100
+ const firstSeqMonomer = getSeqMonomerFromHelmAtom(firstAtom);
101
+ if (secondAtomI !== undefined && secondDistance !== undefined) {
102
+ if (firstDistance < secondDistance * 0.45)
103
+ hoveredSeqMonomer = firstSeqMonomer;
104
+ } else {
105
+ if (firstDistance < 0.35 * gridCell.bounds.height)
106
+ hoveredSeqMonomer = firstSeqMonomer;
107
+ }
108
+ }
109
+ return hoveredSeqMonomer;
110
+ }
111
+
112
+ function getHoveredMonomerFallback(
113
+ argsX: number, _argsY: number, gridCell: DG.GridCell, helmPlacer: HelmMonomerPlacer
114
+ ): ISeqMonomer | null {
115
+ let hoveredSeqMonomer: ISeqMonomer | null = null;
116
+ const [allParts, lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRowIndex!);
117
+ const maxIndex = Object.values(lengths).length - 1;
118
+ let left = 0;
119
+ let right = maxIndex;
120
+ let found = false;
121
+ let iterCount: number = 0;
122
+
123
+ let mid = 0;
124
+ if (argsX > sumLengths[0]) {
125
+ while (!found && iterCount < sumLengths.length) {
126
+ mid = Math.floor((right + left) / 2);
127
+ if (argsX >= sumLengths[mid] && argsX <= sumLengths[mid + 1]) {
128
+ left = mid;
129
+ found = true;
130
+ } else if (argsX < sumLengths[mid])
131
+ right = mid - 1;
132
+ else if (argsX > sumLengths[mid + 1])
133
+ left = mid + 1;
134
+
135
+ if (left == right)
136
+ found = true;
137
+
138
+ iterCount++;
139
+ }
140
+ }
141
+ left = (argsX >= sumLengths[left]) ? left : left - 1; // correct left to between sumLengths
142
+ if (left >= 0)
143
+ hoveredSeqMonomer = getSeqMonomerFromHelm(allParts[0], allParts[left], helmPlacer.monomerLib);
144
+ return hoveredSeqMonomer;
145
+ }
146
+
147
+ // patch window.dojox.gfx.svg.Text.prototype.getTextWidth hangs
148
+ /** get the text width in pixels */
149
+ // @ts-ignore
150
+ window.dojox.gfx.svg.Text.prototype.getTextWidth = function() {
151
+ const rawNode = this.rawNode;
152
+ const oldParent = rawNode.parentNode;
153
+ const _measurementNode = rawNode.cloneNode(true);
154
+ _measurementNode.style.visibility = 'hidden';
155
+
156
+ // solution to the "orphan issue" in FF
157
+ let _width = 0;
158
+ const _text = _measurementNode.firstChild.nodeValue;
159
+ oldParent.appendChild(_measurementNode);
160
+
161
+ // solution to the "orphan issue" in Opera
162
+ // (nodeValue == "" hangs firefox)
163
+ if (_text != '') {
164
+ let watchdogCounter = 100;
165
+ while (!_width && --watchdogCounter > 0) { // <-- hangs
166
+ //Yang: work around svgweb bug 417 -- http://code.google.com/p/svgweb/issues/detail?id=417
167
+ if (_measurementNode.getBBox)
168
+ _width = parseInt(_measurementNode.getBBox().width);
169
+ else
170
+ _width = 68;
171
+ }
172
+ }
173
+ oldParent.removeChild(_measurementNode);
174
+ return _width;
175
+ };
26
176
 
27
177
  /** Helm cell renderer in case of no missed monomer draws with JSDraw2.Editor (webeditor),
28
178
  * in case of missed monomers presented, draws linear sequences aligned in width per monomer.
@@ -43,68 +193,69 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
43
193
  try { tableCol = gridCell.tableColumn; } catch { }
44
194
  if (!tableCol) return;
45
195
 
46
- const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
47
- const [allParts, lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRowIndex!);
48
-
49
- const maxIndex = Object.values(lengths).length - 1;
50
196
  const argsX = e.offsetX - gridCell.bounds.x;
51
- let left = 0;
52
- let right = maxIndex;
53
- let found = false;
54
- let iterCount: number = 0;
55
-
56
- let mid = 0;
57
- if (argsX > sumLengths[0]) {
58
- while (!found && iterCount < sumLengths.length) {
59
- mid = Math.floor((right + left) / 2);
60
- if (argsX >= sumLengths[mid] && argsX <= sumLengths[mid + 1]) {
61
- left = mid;
62
- found = true;
63
- } else if (argsX < sumLengths[mid])
64
- right = mid - 1;
65
- else if (argsX > sumLengths[mid + 1])
66
- left = mid + 1;
67
-
68
- if (left == right)
69
- found = true;
70
-
71
- iterCount++;
72
- }
197
+ const argsY = e.offsetY - gridCell.bounds.y;
198
+
199
+ const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
200
+ const editor: IEditor | null = helmPlacer.getEditor(gridCell.tableRowIndex!);
201
+ const seqMonomer: ISeqMonomer | null = editor ? getHoveredMonomerFromEditor(argsX, argsY, gridCell, editor) :
202
+ getHoveredMonomerFallback(argsX, argsY, gridCell, helmPlacer);
203
+ if (!seqMonomer) {
204
+ ui.tooltip.hide();
205
+ return;
73
206
  }
74
- left = (argsX >= sumLengths[left]) ? left : left - 1; // correct left to between sumLengths
75
207
 
76
208
  const seq: string = !gridCell.cell.value ? '' : gridCell.cell.value
77
209
  .replaceAll(helmGapStartRe, '{').replaceAll(helmGapIntRe, '.').replaceAll(helmGapEndRe, '}')
78
210
  .replace('{*}', '{}');
79
211
  const monomerList = parseHelm(seq);
80
- const monomers = new Set<string>(monomerList);
81
212
  const missedMonomers = findMonomers(monomerList);
82
213
 
83
- const tooltipMessage: HTMLElement[] = [];
84
- for (const [part, partI] of wu.enumerate(allParts)) {
85
- if (missedMonomers.has(part)) {
86
- tooltipMessage[partI] = ui.divV([
87
- ui.divText(`Monomer ${allParts[partI]} not found.`),
88
- ui.divText('Open the Context Panel, then expand Manage Libraries')
89
- ]);
90
- } else if (monomers.has(part)) {
91
- const elList = [ui.div(part)];
92
- const monomer = helmPlacer.getMonomer(part);
93
- if (monomer) {
94
- const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
95
- const monomerSvg = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
96
- elList.push(monomerSvg);
97
- }
98
- tooltipMessage[partI] = ui.divV(elList);
214
+ if (missedMonomers.has(seqMonomer.symbol)) {
215
+ ui.tooltip.show(ui.divV([
216
+ ui.divText(`Monomer ${seqMonomer.symbol} not found.`),
217
+ ui.divText('Open the Context Panel, then expand Manage Libraries'),
218
+ ]), e.x + 16, e.y + 16);
219
+ } else {
220
+ const monomer = helmPlacer.getMonomer(seqMonomer);
221
+ if (monomer) {
222
+ const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
223
+ const monomerSvg = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
224
+ ui.tooltip.show(ui.divV([
225
+ ui.divText(seqMonomer.symbol),
226
+ monomerSvg,
227
+ ]), e.x + 16, e.y + 16);
99
228
  }
100
229
  }
101
230
 
102
- (((tooltipMessage[left]?.childNodes.length ?? 0) > 0)) ?
103
- ui.tooltip.show(tooltipMessage[left], e.x + 16, e.y + 16) :
104
- ui.tooltip.hide();
231
+ // const tooltipMessage: HTMLElement[] = [];
232
+ // for (const [part, partI] of wu.enumerate(allParts)) {
233
+ // if (missedMonomers.has(part)) {
234
+ // tooltipMessage[partI] = ui.divV([
235
+ // ui.divText(`Monomer ${allParts[partI]} not found.`),
236
+ // ui.divText('Open the Context Panel, then expand Manage Libraries')
237
+ // ]);
238
+ // } else if (monomers.has(part)) {
239
+ // const elList = [ui.div(part)];
240
+ // const monomer = helmPlacer.getMonomer(part);
241
+ // if (monomer) {
242
+ // const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
243
+ // const monomerSvg = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
244
+ // elList.push(monomerSvg);
245
+ // }
246
+ // tooltipMessage[partI] = ui.divV(elList);
247
+ // }
248
+ // }
249
+ //
250
+ // (((tooltipMessage[left]?.childNodes.length ?? 0) > 0)) ?
251
+ // ui.tooltip.show(tooltipMessage[left], e.x + 16, e.y + 16) :
252
+ // ui.tooltip.hide();
105
253
  } catch (err: any) {
106
254
  const errMsg: string = errorToConsole(err);
107
255
  console.error('Helm: HelmCellRenderer.onMouseMove() error:\n' + errMsg);
256
+ } finally {
257
+ e.preventDefault();
258
+ e.stopPropagation();
108
259
  }
109
260
  }
110
261
 
@@ -132,13 +283,17 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
132
283
  const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
133
284
 
134
285
  if (missedMonomers.size == 0) {
135
- helmPlacer.skipCell(gridCell.tableRowIndex!);
136
- const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
286
+ // Recreate host to avoid hanging in window.dojox.gfx.svg.Text.prototype.getTextWidth
287
+ const host = gridCell.element = ui.div([],
288
+ {style: {width: `${w - 2}px`, height: `${h - 2}px`, margin: `${1}px`, backgroundColor: '#FFE0E0'}});
137
289
  host.setAttribute('dataformat', 'helm');
138
290
  host.setAttribute('data', seq /* gaps skipped */);
139
- gridCell.element = host;
140
- //@ts-ignore
141
- const canvas = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true});
291
+
292
+ // Recreate editor to avoid hanging in window.dojox.gfx.svg.Text.prototype.getTextWidth
293
+ const editor = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true}) as IEditor;
294
+ helmPlacer.setEditor(gridCell.tableRowIndex!, editor);
295
+
296
+ helmPlacer.skipCell(gridCell.tableRowIndex!);
142
297
  return;
143
298
  }
144
299
 
@@ -13,11 +13,39 @@ export const enum Temps {
13
13
  helmMonomerPlacer = 'bio-helmMonomerPlacer',
14
14
  }
15
15
 
16
+ export interface IEditor {
17
+ get m(): IEditorMol;
18
+
19
+ redraw(): void;
20
+ }
21
+
22
+ export interface IEditorMol {
23
+ get atoms(): IEditorMolAtom[];
24
+ }
25
+
26
+ export interface IEditorMolAtom {
27
+ get p(): IEditorPoint;
28
+
29
+ get elem(): string;
30
+ }
31
+
32
+ export interface IEditorPoint {
33
+ get x(): number;
34
+
35
+ get y(): number;
36
+ }
37
+
38
+ export interface ISeqMonomer {
39
+ symbol: string,
40
+ polymerType?: string
41
+ }
42
+
16
43
  export class HelmMonomerPlacer {
17
- private _allPartsList: (string[] | null)[] | null = null;
18
- private _lengthsList: (number[] | null) [] | null = null;
44
+ private readonly _allPartsList: (string[] | null)[];
45
+ private readonly _lengthsList: (number[] | null)[];
46
+ private readonly _editorList: (IEditor | null)[];
19
47
 
20
- private monomerLib: IMonomerLib;
48
+ public readonly monomerLib: IMonomerLib;
21
49
 
22
50
  public monomerCharWidth: number = 7;
23
51
  public leftPadding: number = 5;
@@ -26,25 +54,20 @@ export class HelmMonomerPlacer {
26
54
  this.col.dataFrame.onDataChanged.subscribe();
27
55
  this.monomerLib = getMonomerLib();
28
56
  this.monomerLib.onChanged.subscribe(this.monomerLibOnChanged.bind(this));
57
+
58
+ this._allPartsList = new Array<string[] | null>(this.col.length).fill(null);
59
+ this._lengthsList = new Array<number[] | null>(this.col.length).fill(null);
60
+ this._editorList = new Array<IEditor | null>(this.col.length).fill(null);
29
61
  }
30
62
 
63
+ /** Skips cell for the fallback rendering */
31
64
  public skipCell(rowIdx: number): void {
32
- if (this._allPartsList === null)
33
- this._allPartsList = new Array<string[] | null>(this.col.length).fill(null);
34
- if (this._lengthsList === null)
35
- this._lengthsList = new Array<number[] | null>(this.col.length).fill(null);
36
-
37
65
  this._allPartsList[rowIdx] = [];
38
66
  this._lengthsList[rowIdx] = [];
39
67
  }
40
68
 
41
69
  /** @param rowIdx Row index of the table {@link DG.DataFrame}, HelmMonomerPlacer is {@link DG.Column} based */
42
70
  public getCellAllPartsLengths(rowIdx: number): [string[], number[], number[]] {
43
- if (this._allPartsList === null)
44
- this._allPartsList = new Array<string[] | null>(this.col.length).fill(null);
45
- if (this._lengthsList === null)
46
- this._lengthsList = new Array<number[] | null>(this.col.length).fill(null);
47
-
48
71
  const [allParts, lengths] = this.getCellMonomerLengthsForSeq(rowIdx);
49
72
 
50
73
  const sumLengths: number[] = new Array<number>(lengths.length + 1);
@@ -77,11 +100,15 @@ export class HelmMonomerPlacer {
77
100
  return seq ? getParts(monomerList, seq) : [];
78
101
  }
79
102
 
80
- getMonomer(monomerSymbol: any): Monomer | null {
103
+ getMonomer(monomer: ISeqMonomer): Monomer | null {
81
104
  let res: Monomer | null = null;
82
- for (const polymerType of this.monomerLib.getPolymerTypes()) {
83
- res = this.monomerLib.getMonomer(polymerType, monomerSymbol);
84
- if (res) break;
105
+ if (monomer.polymerType)
106
+ res = this.monomerLib.getMonomer(monomer.polymerType, monomer.symbol);
107
+ else {
108
+ for (const polymerType of this.monomerLib.getPolymerTypes()) {
109
+ res = this.monomerLib.getMonomer(polymerType, monomer.symbol);
110
+ if (res) break;
111
+ }
85
112
  }
86
113
  return res;
87
114
  }
@@ -89,8 +116,9 @@ export class HelmMonomerPlacer {
89
116
  // -- Handle events --
90
117
 
91
118
  private monomerLibOnChanged(_value: any): void {
92
- this._lengthsList = null;
93
- this._allPartsList = null;
119
+ this._lengthsList.fill(null);
120
+ this._allPartsList.fill(null);
121
+ this._editorList.fill(null);
94
122
  // TODO: Invalidate all grids of this.col.dataFrame
95
123
  }
96
124
 
@@ -98,4 +126,12 @@ export class HelmMonomerPlacer {
98
126
  if (!(Temps.helmMonomerPlacer in col.temp)) col.temp[Temps.helmMonomerPlacer] = new HelmMonomerPlacer(col);
99
127
  return col.temp[Temps.helmMonomerPlacer];
100
128
  }
129
+
130
+ public setEditor(tableRowIndex: number, editor: IEditor) {
131
+ this._editorList![tableRowIndex] = editor;
132
+ }
133
+
134
+ public getEditor(tableRowIndex: number): IEditor | null {
135
+ return this._editorList![tableRowIndex];
136
+ }
101
137
  }
package/src/package.ts CHANGED
@@ -3,17 +3,19 @@ import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
 
6
- import {WebEditorMonomer, RGROUP_CAP_GROUP_NAME, RGROUP_LABEL, SMILES} from './constants';
7
- import {HelmWebEditor} from './helm-web-editor';
8
- import {HelmCellRenderer} from './cell-renderer';
9
- import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
10
- import {GapSymbols, UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
11
- import {findMonomers, parseHelm} from './utils';
12
6
  import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
13
7
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
8
+ import {GapSymbols, UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
9
+ import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
10
+
11
+ import {findMonomers, parseHelm} from './utils';
12
+ import {HelmWebEditor} from './helm-web-editor';
13
+ import {HelmCellRenderer} from './cell-renderer';
14
14
 
15
15
  let monomerLib: IMonomerLib | null = null;
16
16
 
17
+ import {WebEditorMonomer, RGROUP_CAP_GROUP_NAME, RGROUP_LABEL, SMILES} from './constants';
18
+
17
19
  export const _package = new DG.Package();
18
20
 
19
21
  //tags: init
@@ -110,7 +112,7 @@ function rewriteLibraries() {
110
112
  //name: helmCellRenderer
111
113
  //tags: cellRenderer
112
114
  //meta.cellType: helm
113
- //meta.columnTags: units=helm
115
+ //meta.columnTags: quality=Macromolecule, units=helm
114
116
  //output: grid_cell_renderer result
115
117
  export function helmCellRenderer(): HelmCellRenderer {
116
118
  return new HelmCellRenderer();
@@ -281,7 +283,7 @@ export function getMolfiles(col: DG.Column): DG.Column {
281
283
  }
282
284
 
283
285
  //name: helmWebEditor
284
- //output: object
286
+ //output: object result
285
287
  export function helmWebEditor(): HelmWebEditor {
286
288
  return new HelmWebEditor();
287
289
  }