@datagrok/bio 2.26.6 → 2.26.8

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/CLAUDE.md CHANGED
@@ -1,25 +1,11 @@
1
1
  # CLAUDE.md
2
2
 
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
3
  ## Overview
6
4
 
7
5
  **Bio** (`@datagrok/bio`) is the main Datagrok plugin for **bioinformatics** — automatic detection, rendering, editing, analysis, and conversion of macromolecule sequences (peptides, DNA, RNA, HELM, BILN). It provides semantic type detection, custom cell renderers, viewers (WebLogo, VD Regions), analysis tools (sequence space, activity cliffs, MSA, similarity/diversity search), monomer library management, substructure filtering, and atomic-level conversion.
8
6
 
9
7
  Category: **Bioinformatics**. Top menu: `Bio | ...`.
10
8
 
11
- ## Build Commands
12
-
13
- ```bash
14
- npm install
15
- npm run build # grok api && grok check --soft && webpack
16
- npm run test # grok test
17
- npm run lint # eslint src --ext .ts
18
- npm run lint-fix
19
- npm run build-all # Builds chem-meta → js-api → utils → bio-lib → this package
20
- npm run link-all # Links local datagrok-api and @datagrok-libraries/*
21
- ```
22
-
23
9
  ## Key Dependencies
24
10
 
25
11
  - `@datagrok-libraries/bio` — **core shared library**: macromolecule types, HELM types, splitters, palettes, MonomerWorks (seq→molfile), cell renderer engines, monomer hover, seq-helper/seq-handler interfaces
@@ -28,6 +28,15 @@
28
28
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.12);
29
29
  }
30
30
 
31
+ .monomer-collection-card-selected {
32
+ border-color: #ff8c00;
33
+ box-shadow: 0 0 0 1px #ff8c00;
34
+ }
35
+
36
+ .monomer-collection-card-selected:hover {
37
+ box-shadow: 0 0 0 1px #ff8c00, 0 2px 12px rgba(0, 0, 0, 0.12);
38
+ }
39
+
31
40
  .monomer-collection-card-header {
32
41
  padding: 12px 14px 8px 14px;
33
42
  border-bottom: 1px solid var(--grey-2);
@@ -95,7 +104,7 @@
95
104
  }
96
105
 
97
106
  .monomer-collection-card-actions {
98
- padding: 8px 14px;
107
+ padding: 0px 14px;
99
108
  border-top: 1px solid var(--grey-2);
100
109
  display: flex;
101
110
  gap: 8px;
@@ -106,6 +115,7 @@
106
115
  .monomer-collection-card-actions .ui-btn {
107
116
  font-size: 11px;
108
117
  padding: 2px 10px;
118
+ margin: 0;
109
119
  }
110
120
 
111
121
  .monomer-collection-tags {
@@ -122,7 +132,7 @@
122
132
  border: 1px solid var(--grey-2);
123
133
  font-size: 11px;
124
134
  color: var(--grey-5);
125
- cursor: default;
135
+ cursor: pointer;
126
136
  transition: background 0.15s ease;
127
137
  }
128
138
 
package/dist/422.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";(self.webpackChunkbio=self.webpackChunkbio||[]).push([[422],{9422(e,t,i){i.d(t,{showLiabilityScannerDialog:()=>c});var n=i(4328),a=i(7389),o=i(6082),l=i(3785),r=i(4517),d=i(3736);const s=[{id:"deamid-ng",name:"Deamidation (NG)",pattern:/NG/g,length:2,severity:r.Hq.High,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"deamid-ns",name:"Deamidation (NS)",pattern:/NS/g,length:2,severity:r.Hq.Medium,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"deamid-na",name:"Deamidation (NA)",pattern:/NA/g,length:2,severity:r.Hq.Low,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"deamid-nd",name:"Deamidation (ND)",pattern:/ND/g,length:2,severity:r.Hq.Low,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"deamid-nt",name:"Deamidation (NT)",pattern:/NT/g,length:2,severity:r.Hq.Low,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"isom-dg",name:"Isomerization (DG)",pattern:/DG/g,length:2,severity:r.Hq.High,ruleCategory:"isomerization",color:r.D5.liability.isomerization,enabled:!0},{id:"isom-ds",name:"Isomerization (DS)",pattern:/DS/g,length:2,severity:r.Hq.Medium,ruleCategory:"isomerization",color:r.D5.liability.isomerization,enabled:!0},{id:"oxid-m",name:"Oxidation (Met)",pattern:/M/g,length:1,severity:r.Hq.Medium,ruleCategory:"oxidation",color:r.D5.liability.oxidation,enabled:!0},{id:"oxid-w",name:"Oxidation (Trp)",pattern:/W/g,length:1,severity:r.Hq.Low,ruleCategory:"oxidation",color:r.D5.liability.oxidation,enabled:!0},{id:"glyco-nxst",name:"N-glycosylation",pattern:/N[^P][ST]/g,length:3,severity:r.Hq.High,ruleCategory:"glycosylation",color:r.D5.liability.glycosylation,enabled:!0},{id:"free-cys",name:"Free Cysteine",pattern:/C/g,length:1,severity:r.Hq.Info,ruleCategory:"freeCysteine",color:r.D5.liability.freeCysteine,enabled:!1}];function u(e,t){const i=e.getSplitted(t),n=new Array(i.length);for(let e=0;e<i.length;e++)n[e]=i.getOriginal(e);return n.join("")}const g={[r.Hq.High]:"High",[r.Hq.Medium]:"Medium",[r.Hq.Low]:"Low",[r.Hq.Info]:"Info"};function c(){const e=n.shell.tv?.dataFrame;if(!e)return void n.shell.warning("No table open");const t=e.columns.bySemTypeAll(o.SEMTYPE.MACROMOLECULE);if(0===t.length)return void n.shell.warning("No macromolecule columns found");const i=s.map(e=>({...e,pattern:new RegExp(e.pattern.source,"g")})),c=a.input.table("Table",{value:e}),m=a.input.column("Sequence",{table:e,value:t[0],filter:e=>e.semType===o.SEMTYPE.MACROMOLECULE}),y=[],p=a.divV([]);for(const e of i){const t=a.input.bool(e.name,{value:e.enabled,tooltipText:`Severity: ${g[e.severity]??e.severity}`});y.push({rule:e,input:t}),p.append(t.root)}const h=a.input.bool("Highlight in cell renderer",{value:!0}),b=a.input.bool("Create annotation column",{value:!0}),f=a.input.bool("Create summary count column",{value:!1}),v=a.dialog({title:"Scan Sequence Liabilities"}).add(a.inputs([c,m])).add(a.h3("Rules")).add(p).add(a.h3("Output")).add(a.inputs([h,b,f])).onOK(()=>{try{const t=m.value,a=l._package.seqHelper.getSeqHandler(t);for(const{rule:e,input:t}of y)e.enabled=t.value;const o=function(e,t,i){const n=i.filter(e=>e.enabled),a=t.posList,o=new Map,l=new Array(e.length);let d=0;for(let i=0;i<e.length;i++){const e=u(t,i),r=[];for(const t of n){let i;for(t.pattern.lastIndex=0;null!==(i=t.pattern.exec(e));)r.push({annotationId:t.id,positionIndex:i.index,positionName:i.index<a.length?a[i.index]:void 0,matchedMonomers:i[0]}),o.set(t.id,(o.get(t.id)??0)+1),d++}l[i]=r}return{annotations:n.filter(e=>o.has(e.id)).map(e=>({id:e.id,name:e.name,description:`${e.ruleCategory} liability pattern (${o.get(e.id)} hits)`,start:null,end:null,visualType:1===e.length?r.ao.Point:r.ao.Motif,category:r.eI.Liability,color:e.color,severity:e.severity,motifPattern:e.pattern.source,autoGenerated:!0})),rowData:l,totalHits:d}}(t,a,i);(b.value||h.value)&&function(e,t,i){const n=(0,d.Ln)(t).filter(e=>e.category!==r.eI.Liability);(0,d.fh)(t,[...n,...i.annotations]);const a=(0,d.Lz)(e,t);for(let e=0;e<i.rowData.length;e++){const t=(0,d.JG)(a,e)??[];(0,d.z5)(a,e,(0,d.bq)(t,i.rowData[e],!1,!0))}}(e,t,o),f.value&&function(e,t,i){const n=`${t.name}_liability_count`,a=i.rowData.map(e=>e.length),o=e.columns.addNewInt(n);for(let e=0;e<a.length;e++)o.set(e,a[e])}(e,t,o),n.shell.info(`Liability scan: ${o.totalHits} hits found across ${o.annotations.length} rules`),e.fireValuesChanged()}catch(e){n.shell.error(`Liability scan failed: ${e.message??e}`),console.error(e)}});v.show()}}}]);
1
+ "use strict";(self.webpackChunkbio=self.webpackChunkbio||[]).push([[422],{9422(e,t,i){i.d(t,{showLiabilityScannerDialog:()=>c});var n=i(4328),a=i(7389),o=i(6082),l=i(980),r=i(4517),d=i(3736);const s=[{id:"deamid-ng",name:"Deamidation (NG)",pattern:/NG/g,length:2,severity:r.Hq.High,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"deamid-ns",name:"Deamidation (NS)",pattern:/NS/g,length:2,severity:r.Hq.Medium,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"deamid-na",name:"Deamidation (NA)",pattern:/NA/g,length:2,severity:r.Hq.Low,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"deamid-nd",name:"Deamidation (ND)",pattern:/ND/g,length:2,severity:r.Hq.Low,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"deamid-nt",name:"Deamidation (NT)",pattern:/NT/g,length:2,severity:r.Hq.Low,ruleCategory:"deamidation",color:r.D5.liability.deamidation,enabled:!0},{id:"isom-dg",name:"Isomerization (DG)",pattern:/DG/g,length:2,severity:r.Hq.High,ruleCategory:"isomerization",color:r.D5.liability.isomerization,enabled:!0},{id:"isom-ds",name:"Isomerization (DS)",pattern:/DS/g,length:2,severity:r.Hq.Medium,ruleCategory:"isomerization",color:r.D5.liability.isomerization,enabled:!0},{id:"oxid-m",name:"Oxidation (Met)",pattern:/M/g,length:1,severity:r.Hq.Medium,ruleCategory:"oxidation",color:r.D5.liability.oxidation,enabled:!0},{id:"oxid-w",name:"Oxidation (Trp)",pattern:/W/g,length:1,severity:r.Hq.Low,ruleCategory:"oxidation",color:r.D5.liability.oxidation,enabled:!0},{id:"glyco-nxst",name:"N-glycosylation",pattern:/N[^P][ST]/g,length:3,severity:r.Hq.High,ruleCategory:"glycosylation",color:r.D5.liability.glycosylation,enabled:!0},{id:"free-cys",name:"Free Cysteine",pattern:/C/g,length:1,severity:r.Hq.Info,ruleCategory:"freeCysteine",color:r.D5.liability.freeCysteine,enabled:!1}];function u(e,t){const i=e.getSplitted(t),n=new Array(i.length);for(let e=0;e<i.length;e++)n[e]=i.getOriginal(e);return n.join("")}const g={[r.Hq.High]:"High",[r.Hq.Medium]:"Medium",[r.Hq.Low]:"Low",[r.Hq.Info]:"Info"};function c(){const e=n.shell.tv?.dataFrame;if(!e)return void n.shell.warning("No table open");const t=e.columns.bySemTypeAll(o.SEMTYPE.MACROMOLECULE);if(0===t.length)return void n.shell.warning("No macromolecule columns found");const i=s.map(e=>({...e,pattern:new RegExp(e.pattern.source,"g")})),c=a.input.table("Table",{value:e}),m=a.input.column("Sequence",{table:e,value:t[0],filter:e=>e.semType===o.SEMTYPE.MACROMOLECULE}),y=[],p=a.divV([]);for(const e of i){const t=a.input.bool(e.name,{value:e.enabled,tooltipText:`Severity: ${g[e.severity]??e.severity}`});y.push({rule:e,input:t}),p.append(t.root)}const h=a.input.bool("Highlight in cell renderer",{value:!0}),b=a.input.bool("Create annotation column",{value:!0}),f=a.input.bool("Create summary count column",{value:!1}),v=a.dialog({title:"Scan Sequence Liabilities"}).add(a.inputs([c,m])).add(a.h3("Rules")).add(p).add(a.h3("Output")).add(a.inputs([h,b,f])).onOK(()=>{try{const t=m.value,a=l._package.seqHelper.getSeqHandler(t);for(const{rule:e,input:t}of y)e.enabled=t.value;const o=function(e,t,i){const n=i.filter(e=>e.enabled),a=t.posList,o=new Map,l=new Array(e.length);let d=0;for(let i=0;i<e.length;i++){const e=u(t,i),r=[];for(const t of n){let i;for(t.pattern.lastIndex=0;null!==(i=t.pattern.exec(e));)r.push({annotationId:t.id,positionIndex:i.index,positionName:i.index<a.length?a[i.index]:void 0,matchedMonomers:i[0]}),o.set(t.id,(o.get(t.id)??0)+1),d++}l[i]=r}return{annotations:n.filter(e=>o.has(e.id)).map(e=>({id:e.id,name:e.name,description:`${e.ruleCategory} liability pattern (${o.get(e.id)} hits)`,start:null,end:null,visualType:1===e.length?r.ao.Point:r.ao.Motif,category:r.eI.Liability,color:e.color,severity:e.severity,motifPattern:e.pattern.source,autoGenerated:!0})),rowData:l,totalHits:d}}(t,a,i);(b.value||h.value)&&function(e,t,i){const n=(0,d.Ln)(t).filter(e=>e.category!==r.eI.Liability);(0,d.fh)(t,[...n,...i.annotations]);const a=(0,d.Lz)(e,t);for(let e=0;e<i.rowData.length;e++){const t=(0,d.JG)(a,e)??[];(0,d.z5)(a,e,(0,d.bq)(t,i.rowData[e],!1,!0))}}(e,t,o),f.value&&function(e,t,i){const n=`${t.name}_liability_count`,a=i.rowData.map(e=>e.length),o=e.columns.addNewInt(n);for(let e=0;e<a.length;e++)o.set(e,a[e])}(e,t,o),n.shell.info(`Liability scan: ${o.totalHits} hits found across ${o.annotations.length} rules`),e.fireValuesChanged()}catch(e){n.shell.error(`Liability scan failed: ${e.message??e}`),console.error(e)}});v.show()}}}]);
2
2
  //# sourceMappingURL=422.js.map
package/dist/422.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"422.js","mappings":"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","sources":["webpack://bio/./src/utils/annotations/liability-scanner.ts","webpack://bio/./src/utils/annotations/liability-scanner-ui.ts"],"sourcesContent":["/* eslint-disable max-len */\nimport * as DG from 'datagrok-api/dg';\n\nimport {ISeqHandler} from '@datagrok-libraries/bio/src/utils/macromolecule/seq-handler';\nimport {\n SeqAnnotation, SeqAnnotationHit, RowAnnotationData,\n AnnotationVisualType, AnnotationCategory, LiabilitySeverity,\n ANNOTATION_COLORS,\n} from '@datagrok-libraries/bio/src/utils/macromolecule/annotations';\nimport {\n getOrCreateAnnotationColumn, setColumnAnnotations, setRowAnnotations,\n getColumnAnnotations, getRowAnnotations, mergeRowHits,\n} from './annotation-manager';\n\n/** A single liability scanning rule. */\nexport interface LiabilityRule {\n id: string;\n name: string;\n pattern: RegExp;\n length: number;\n severity: LiabilitySeverity;\n /** Sub-category for grouping (e.g. \"deamidation\", \"oxidation\") */\n ruleCategory: string;\n color: string;\n enabled: boolean;\n}\n\n/** Built-in liability rules for antibody engineering. */\nexport const BUILTIN_LIABILITY_RULES: LiabilityRule[] = [\n {id: 'deamid-ng', name: 'Deamidation (NG)', pattern: /NG/g, length: 2, severity: LiabilitySeverity.High, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'deamid-ns', name: 'Deamidation (NS)', pattern: /NS/g, length: 2, severity: LiabilitySeverity.Medium, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'deamid-na', name: 'Deamidation (NA)', pattern: /NA/g, length: 2, severity: LiabilitySeverity.Low, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'deamid-nd', name: 'Deamidation (ND)', pattern: /ND/g, length: 2, severity: LiabilitySeverity.Low, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'deamid-nt', name: 'Deamidation (NT)', pattern: /NT/g, length: 2, severity: LiabilitySeverity.Low, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'isom-dg', name: 'Isomerization (DG)', pattern: /DG/g, length: 2, severity: LiabilitySeverity.High, ruleCategory: 'isomerization', color: ANNOTATION_COLORS.liability.isomerization, enabled: true},\n {id: 'isom-ds', name: 'Isomerization (DS)', pattern: /DS/g, length: 2, severity: LiabilitySeverity.Medium, ruleCategory: 'isomerization', color: ANNOTATION_COLORS.liability.isomerization, enabled: true},\n {id: 'oxid-m', name: 'Oxidation (Met)', pattern: /M/g, length: 1, severity: LiabilitySeverity.Medium, ruleCategory: 'oxidation', color: ANNOTATION_COLORS.liability.oxidation, enabled: true},\n {id: 'oxid-w', name: 'Oxidation (Trp)', pattern: /W/g, length: 1, severity: LiabilitySeverity.Low, ruleCategory: 'oxidation', color: ANNOTATION_COLORS.liability.oxidation, enabled: true},\n {id: 'glyco-nxst', name: 'N-glycosylation', pattern: /N[^P][ST]/g, length: 3, severity: LiabilitySeverity.High, ruleCategory: 'glycosylation', color: ANNOTATION_COLORS.liability.glycosylation, enabled: true},\n {id: 'free-cys', name: 'Free Cysteine', pattern: /C/g, length: 1, severity: LiabilitySeverity.Info, ruleCategory: 'freeCysteine', color: ANNOTATION_COLORS.liability.freeCysteine, enabled: false},\n];\n\n/** Extracts a canonical single-letter string from a sequence handler for a given row. */\nfunction getCanonicalString(sh: ISeqHandler, rowIdx: number): string {\n const splitted = sh.getSplitted(rowIdx);\n const chars: string[] = new Array(splitted.length);\n for (let i = 0; i < splitted.length; i++)\n chars[i] = splitted.getOriginal(i);\n return chars.join('');\n}\n\nexport interface ScanLiabilitiesResult {\n annotations: SeqAnnotation[];\n rowData: RowAnnotationData[];\n totalHits: number;\n}\n\n/** Scans all rows of a macromolecule column for liability motifs.\n * Returns column-level SeqAnnotation entries + per-row SeqAnnotationHit arrays. */\nexport function scanLiabilities(\n col: DG.Column<string>,\n sh: ISeqHandler,\n rules: LiabilityRule[],\n): ScanLiabilitiesResult {\n const enabledRules = rules.filter((r) => r.enabled);\n const posList = sh.posList;\n\n // Track which rules had hits\n const ruleHitCounts = new Map<string, number>();\n\n const rowData: RowAnnotationData[] = new Array(col.length);\n let totalHits = 0;\n\n for (let rowIdx = 0; rowIdx < col.length; rowIdx++) {\n const seq = getCanonicalString(sh, rowIdx);\n const hits: SeqAnnotationHit[] = [];\n\n for (const rule of enabledRules) {\n // Reset regex lastIndex for global patterns\n rule.pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = rule.pattern.exec(seq)) !== null) {\n hits.push({\n annotationId: rule.id,\n positionIndex: match.index,\n positionName: match.index < posList.length ? posList[match.index] : undefined,\n matchedMonomers: match[0],\n });\n ruleHitCounts.set(rule.id, (ruleHitCounts.get(rule.id) ?? 0) + 1);\n totalHits++;\n }\n }\n rowData[rowIdx] = hits;\n }\n\n // Build column-level annotations only for rules that had hits\n const annotations: SeqAnnotation[] = enabledRules\n .filter((r) => ruleHitCounts.has(r.id))\n .map((r) => ({\n id: r.id,\n name: r.name,\n description: `${r.ruleCategory} liability pattern (${ruleHitCounts.get(r.id)} hits)`,\n start: null,\n end: null,\n visualType: r.length === 1 ? AnnotationVisualType.Point : AnnotationVisualType.Motif,\n category: AnnotationCategory.Liability,\n color: r.color,\n severity: r.severity,\n motifPattern: r.pattern.source,\n autoGenerated: true,\n }));\n\n return {annotations, rowData, totalHits};\n}\n\n/** Applies liability scan results to the DataFrame (writes tags + companion column). */\nexport function applyLiabilityScanResults(\n df: DG.DataFrame,\n seqCol: DG.Column<string>,\n result: ScanLiabilitiesResult,\n): void {\n // Merge with existing annotations, removing old liability entries\n const existing = getColumnAnnotations(seqCol)\n .filter((a) => a.category !== AnnotationCategory.Liability);\n setColumnAnnotations(seqCol, [...existing, ...result.annotations]);\n\n // Write per-row data to hidden companion column, preserving region hits from numbering\n const annotCol = getOrCreateAnnotationColumn(df, seqCol);\n for (let i = 0; i < result.rowData.length; i++) {\n const existingHits = getRowAnnotations(annotCol, i) ?? [];\n setRowAnnotations(annotCol, i, mergeRowHits(existingHits, result.rowData[i], false, true));\n }\n}\n\n/** Creates a liability summary count column (total hits per row). */\nexport function createLiabilitySummaryColumn(\n df: DG.DataFrame,\n seqCol: DG.Column<string>,\n result: ScanLiabilitiesResult,\n): DG.Column<number> {\n const colName = `${seqCol.name}_liability_count`;\n const counts = result.rowData.map((hits) => hits.length);\n const col = df.columns.addNewInt(colName);\n for (let i = 0; i < counts.length; i++)\n col.set(i, counts[i]);\n return col;\n}\n","import * as grok from 'datagrok-api/grok';\nimport * as ui from 'datagrok-api/ui';\nimport * as DG from 'datagrok-api/dg';\n\nimport {_package} from '../../package';\nimport {\n BUILTIN_LIABILITY_RULES, LiabilityRule, scanLiabilities,\n applyLiabilityScanResults, createLiabilitySummaryColumn,\n} from './liability-scanner';\nimport {LiabilitySeverity} from '@datagrok-libraries/bio/src/utils/macromolecule/annotations';\n\nconst severityLabels: Record<string, string> = {\n [LiabilitySeverity.High]: 'High',\n [LiabilitySeverity.Medium]: 'Medium',\n [LiabilitySeverity.Low]: 'Low',\n [LiabilitySeverity.Info]: 'Info',\n};\n\nexport function showLiabilityScannerDialog(): void {\n const df = grok.shell.tv?.dataFrame;\n if (!df) {\n grok.shell.warning('No table open');\n return;\n }\n\n const seqCols = df.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);\n if (seqCols.length === 0) {\n grok.shell.warning('No macromolecule columns found');\n return;\n }\n\n const rules = BUILTIN_LIABILITY_RULES.map((r) => ({...r, pattern: new RegExp(r.pattern.source, 'g')}));\n\n const tableInput = ui.input.table('Table', {value: df});\n const seqInput = ui.input.column('Sequence', {\n table: df, value: seqCols[0],\n filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE,\n });\n\n // Rule checkboxes\n const ruleChecks: {rule: LiabilityRule; input: DG.InputBase<boolean>}[] = [];\n const rulesDiv = ui.divV([]);\n for (const rule of rules) {\n const check = ui.input.bool(rule.name, {\n value: rule.enabled,\n tooltipText: `Severity: ${severityLabels[rule.severity] ?? rule.severity}`,\n });\n ruleChecks.push({rule, input: check});\n rulesDiv.append(check.root);\n }\n\n const highlightInput = ui.input.bool('Highlight in cell renderer', {value: true});\n const annotColInput = ui.input.bool('Create annotation column', {value: true});\n const summaryInput = ui.input.bool('Create summary count column', {value: false});\n\n const dialog = ui.dialog({title: 'Scan Sequence Liabilities'})\n .add(ui.inputs([tableInput, seqInput]))\n .add(ui.h3('Rules'))\n .add(rulesDiv)\n .add(ui.h3('Output'))\n .add(ui.inputs([highlightInput, annotColInput, summaryInput]))\n .onOK(() => {\n try {\n const seqCol = seqInput.value!;\n const sh = _package.seqHelper.getSeqHandler(seqCol);\n\n // Apply checkbox state\n for (const {rule, input} of ruleChecks)\n rule.enabled = input.value;\n\n const result = scanLiabilities(seqCol, sh, rules);\n\n if (annotColInput.value || highlightInput.value)\n applyLiabilityScanResults(df, seqCol, result);\n\n if (summaryInput.value)\n createLiabilitySummaryColumn(df, seqCol, result);\n\n grok.shell.info(`Liability scan: ${result.totalHits} hits found across ${result.annotations.length} rules`);\n df.fireValuesChanged();\n } catch (err: any) {\n grok.shell.error(`Liability scan failed: ${err.message ?? err}`);\n console.error(err);\n }\n });\n\n dialog.show();\n}\n"],"names":["BUILTIN_LIABILITY_RULES","id","name","pattern","length","severity","High","ruleCategory","color","liability","deamidation","enabled","Medium","Low","isomerization","oxidation","glycosylation","Info","freeCysteine","getCanonicalString","sh","rowIdx","splitted","getSplitted","chars","Array","i","getOriginal","join","severityLabels","showLiabilityScannerDialog","df","tv","dataFrame","warning","seqCols","columns","bySemTypeAll","MACROMOLECULE","rules","map","r","RegExp","source","tableInput","table","value","seqInput","column","filter","col","semType","ruleChecks","rulesDiv","rule","check","bool","tooltipText","push","input","append","root","highlightInput","annotColInput","summaryInput","dialog","title","add","onOK","seqCol","_package","seqHelper","getSeqHandler","result","enabledRules","posList","ruleHitCounts","Map","rowData","totalHits","seq","hits","match","lastIndex","exec","annotationId","positionIndex","index","positionName","undefined","matchedMonomers","set","get","annotations","has","description","start","end","visualType","Point","Motif","category","Liability","motifPattern","autoGenerated","scanLiabilities","existing","a","annotCol","existingHits","applyLiabilityScanResults","colName","counts","addNewInt","createLiabilitySummaryColumn","info","fireValuesChanged","err","error","message","console","show"],"sourceRoot":""}
1
+ {"version":3,"file":"422.js","mappings":"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","sources":["webpack://bio/./src/utils/annotations/liability-scanner.ts","webpack://bio/./src/utils/annotations/liability-scanner-ui.ts"],"sourcesContent":["/* eslint-disable max-len */\nimport * as DG from 'datagrok-api/dg';\n\nimport {ISeqHandler} from '@datagrok-libraries/bio/src/utils/macromolecule/seq-handler';\nimport {\n SeqAnnotation, SeqAnnotationHit, RowAnnotationData,\n AnnotationVisualType, AnnotationCategory, LiabilitySeverity,\n ANNOTATION_COLORS,\n} from '@datagrok-libraries/bio/src/utils/macromolecule/annotations';\nimport {\n getOrCreateAnnotationColumn, setColumnAnnotations, setRowAnnotations,\n getColumnAnnotations, getRowAnnotations, mergeRowHits,\n} from './annotation-manager';\n\n/** A single liability scanning rule. */\nexport interface LiabilityRule {\n id: string;\n name: string;\n pattern: RegExp;\n length: number;\n severity: LiabilitySeverity;\n /** Sub-category for grouping (e.g. \"deamidation\", \"oxidation\") */\n ruleCategory: string;\n color: string;\n enabled: boolean;\n}\n\n/** Built-in liability rules for antibody engineering. */\nexport const BUILTIN_LIABILITY_RULES: LiabilityRule[] = [\n {id: 'deamid-ng', name: 'Deamidation (NG)', pattern: /NG/g, length: 2, severity: LiabilitySeverity.High, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'deamid-ns', name: 'Deamidation (NS)', pattern: /NS/g, length: 2, severity: LiabilitySeverity.Medium, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'deamid-na', name: 'Deamidation (NA)', pattern: /NA/g, length: 2, severity: LiabilitySeverity.Low, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'deamid-nd', name: 'Deamidation (ND)', pattern: /ND/g, length: 2, severity: LiabilitySeverity.Low, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'deamid-nt', name: 'Deamidation (NT)', pattern: /NT/g, length: 2, severity: LiabilitySeverity.Low, ruleCategory: 'deamidation', color: ANNOTATION_COLORS.liability.deamidation, enabled: true},\n {id: 'isom-dg', name: 'Isomerization (DG)', pattern: /DG/g, length: 2, severity: LiabilitySeverity.High, ruleCategory: 'isomerization', color: ANNOTATION_COLORS.liability.isomerization, enabled: true},\n {id: 'isom-ds', name: 'Isomerization (DS)', pattern: /DS/g, length: 2, severity: LiabilitySeverity.Medium, ruleCategory: 'isomerization', color: ANNOTATION_COLORS.liability.isomerization, enabled: true},\n {id: 'oxid-m', name: 'Oxidation (Met)', pattern: /M/g, length: 1, severity: LiabilitySeverity.Medium, ruleCategory: 'oxidation', color: ANNOTATION_COLORS.liability.oxidation, enabled: true},\n {id: 'oxid-w', name: 'Oxidation (Trp)', pattern: /W/g, length: 1, severity: LiabilitySeverity.Low, ruleCategory: 'oxidation', color: ANNOTATION_COLORS.liability.oxidation, enabled: true},\n {id: 'glyco-nxst', name: 'N-glycosylation', pattern: /N[^P][ST]/g, length: 3, severity: LiabilitySeverity.High, ruleCategory: 'glycosylation', color: ANNOTATION_COLORS.liability.glycosylation, enabled: true},\n {id: 'free-cys', name: 'Free Cysteine', pattern: /C/g, length: 1, severity: LiabilitySeverity.Info, ruleCategory: 'freeCysteine', color: ANNOTATION_COLORS.liability.freeCysteine, enabled: false},\n];\n\n/** Extracts a canonical single-letter string from a sequence handler for a given row. */\nfunction getCanonicalString(sh: ISeqHandler, rowIdx: number): string {\n const splitted = sh.getSplitted(rowIdx);\n const chars: string[] = new Array(splitted.length);\n for (let i = 0; i < splitted.length; i++)\n chars[i] = splitted.getOriginal(i);\n return chars.join('');\n}\n\nexport interface ScanLiabilitiesResult {\n annotations: SeqAnnotation[];\n rowData: RowAnnotationData[];\n totalHits: number;\n}\n\n/** Scans all rows of a macromolecule column for liability motifs.\n * Returns column-level SeqAnnotation entries + per-row SeqAnnotationHit arrays. */\nexport function scanLiabilities(\n col: DG.Column<string>,\n sh: ISeqHandler,\n rules: LiabilityRule[],\n): ScanLiabilitiesResult {\n const enabledRules = rules.filter((r) => r.enabled);\n const posList = sh.posList;\n\n // Track which rules had hits\n const ruleHitCounts = new Map<string, number>();\n\n const rowData: RowAnnotationData[] = new Array(col.length);\n let totalHits = 0;\n\n for (let rowIdx = 0; rowIdx < col.length; rowIdx++) {\n const seq = getCanonicalString(sh, rowIdx);\n const hits: SeqAnnotationHit[] = [];\n\n for (const rule of enabledRules) {\n // Reset regex lastIndex for global patterns\n rule.pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = rule.pattern.exec(seq)) !== null) {\n hits.push({\n annotationId: rule.id,\n positionIndex: match.index,\n positionName: match.index < posList.length ? posList[match.index] : undefined,\n matchedMonomers: match[0],\n });\n ruleHitCounts.set(rule.id, (ruleHitCounts.get(rule.id) ?? 0) + 1);\n totalHits++;\n }\n }\n rowData[rowIdx] = hits;\n }\n\n // Build column-level annotations only for rules that had hits\n const annotations: SeqAnnotation[] = enabledRules\n .filter((r) => ruleHitCounts.has(r.id))\n .map((r) => ({\n id: r.id,\n name: r.name,\n description: `${r.ruleCategory} liability pattern (${ruleHitCounts.get(r.id)} hits)`,\n start: null,\n end: null,\n visualType: r.length === 1 ? AnnotationVisualType.Point : AnnotationVisualType.Motif,\n category: AnnotationCategory.Liability,\n color: r.color,\n severity: r.severity,\n motifPattern: r.pattern.source,\n autoGenerated: true,\n }));\n\n return {annotations, rowData, totalHits};\n}\n\n/** Applies liability scan results to the DataFrame (writes tags + companion column). */\nexport function applyLiabilityScanResults(\n df: DG.DataFrame,\n seqCol: DG.Column<string>,\n result: ScanLiabilitiesResult,\n): void {\n // Merge with existing annotations, removing old liability entries\n const existing = getColumnAnnotations(seqCol)\n .filter((a) => a.category !== AnnotationCategory.Liability);\n setColumnAnnotations(seqCol, [...existing, ...result.annotations]);\n\n // Write per-row data to hidden companion column, preserving region hits from numbering\n const annotCol = getOrCreateAnnotationColumn(df, seqCol);\n for (let i = 0; i < result.rowData.length; i++) {\n const existingHits = getRowAnnotations(annotCol, i) ?? [];\n setRowAnnotations(annotCol, i, mergeRowHits(existingHits, result.rowData[i], false, true));\n }\n}\n\n/** Creates a liability summary count column (total hits per row). */\nexport function createLiabilitySummaryColumn(\n df: DG.DataFrame,\n seqCol: DG.Column<string>,\n result: ScanLiabilitiesResult,\n): DG.Column<number> {\n const colName = `${seqCol.name}_liability_count`;\n const counts = result.rowData.map((hits) => hits.length);\n const col = df.columns.addNewInt(colName);\n for (let i = 0; i < counts.length; i++)\n col.set(i, counts[i]);\n return col;\n}\n","import * as grok from 'datagrok-api/grok';\nimport * as ui from 'datagrok-api/ui';\nimport * as DG from 'datagrok-api/dg';\n\nimport {_package} from '../../package';\nimport {\n BUILTIN_LIABILITY_RULES, LiabilityRule, scanLiabilities,\n applyLiabilityScanResults, createLiabilitySummaryColumn,\n} from './liability-scanner';\nimport {LiabilitySeverity} from '@datagrok-libraries/bio/src/utils/macromolecule/annotations';\n\nconst severityLabels: Record<string, string> = {\n [LiabilitySeverity.High]: 'High',\n [LiabilitySeverity.Medium]: 'Medium',\n [LiabilitySeverity.Low]: 'Low',\n [LiabilitySeverity.Info]: 'Info',\n};\n\nexport function showLiabilityScannerDialog(): void {\n const df = grok.shell.tv?.dataFrame;\n if (!df) {\n grok.shell.warning('No table open');\n return;\n }\n\n const seqCols = df.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);\n if (seqCols.length === 0) {\n grok.shell.warning('No macromolecule columns found');\n return;\n }\n\n const rules = BUILTIN_LIABILITY_RULES.map((r) => ({...r, pattern: new RegExp(r.pattern.source, 'g')}));\n\n const tableInput = ui.input.table('Table', {value: df});\n const seqInput = ui.input.column('Sequence', {\n table: df, value: seqCols[0],\n filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE,\n });\n\n // Rule checkboxes\n const ruleChecks: {rule: LiabilityRule; input: DG.InputBase<boolean>}[] = [];\n const rulesDiv = ui.divV([]);\n for (const rule of rules) {\n const check = ui.input.bool(rule.name, {\n value: rule.enabled,\n tooltipText: `Severity: ${severityLabels[rule.severity] ?? rule.severity}`,\n });\n ruleChecks.push({rule, input: check});\n rulesDiv.append(check.root);\n }\n\n const highlightInput = ui.input.bool('Highlight in cell renderer', {value: true});\n const annotColInput = ui.input.bool('Create annotation column', {value: true});\n const summaryInput = ui.input.bool('Create summary count column', {value: false});\n\n const dialog = ui.dialog({title: 'Scan Sequence Liabilities'})\n .add(ui.inputs([tableInput, seqInput]))\n .add(ui.h3('Rules'))\n .add(rulesDiv)\n .add(ui.h3('Output'))\n .add(ui.inputs([highlightInput, annotColInput, summaryInput]))\n .onOK(() => {\n try {\n const seqCol = seqInput.value!;\n const sh = _package.seqHelper.getSeqHandler(seqCol);\n\n // Apply checkbox state\n for (const {rule, input} of ruleChecks)\n rule.enabled = input.value;\n\n const result = scanLiabilities(seqCol, sh, rules);\n\n if (annotColInput.value || highlightInput.value)\n applyLiabilityScanResults(df, seqCol, result);\n\n if (summaryInput.value)\n createLiabilitySummaryColumn(df, seqCol, result);\n\n grok.shell.info(`Liability scan: ${result.totalHits} hits found across ${result.annotations.length} rules`);\n df.fireValuesChanged();\n } catch (err: any) {\n grok.shell.error(`Liability scan failed: ${err.message ?? err}`);\n console.error(err);\n }\n });\n\n dialog.show();\n}\n"],"names":["BUILTIN_LIABILITY_RULES","id","name","pattern","length","severity","High","ruleCategory","color","liability","deamidation","enabled","Medium","Low","isomerization","oxidation","glycosylation","Info","freeCysteine","getCanonicalString","sh","rowIdx","splitted","getSplitted","chars","Array","i","getOriginal","join","severityLabels","showLiabilityScannerDialog","df","tv","dataFrame","warning","seqCols","columns","bySemTypeAll","MACROMOLECULE","rules","map","r","RegExp","source","tableInput","table","value","seqInput","column","filter","col","semType","ruleChecks","rulesDiv","rule","check","bool","tooltipText","push","input","append","root","highlightInput","annotColInput","summaryInput","dialog","title","add","onOK","seqCol","_package","seqHelper","getSeqHandler","result","enabledRules","posList","ruleHitCounts","Map","rowData","totalHits","seq","hits","match","lastIndex","exec","annotationId","positionIndex","index","positionName","undefined","matchedMonomers","set","get","annotations","has","description","start","end","visualType","Point","Motif","category","Liability","motifPattern","autoGenerated","scanLiabilities","existing","a","annotCol","existingHits","applyLiabilityScanResults","colName","counts","addNewInt","createLiabilitySummaryColumn","info","fireValuesChanged","err","error","message","console","show"],"sourceRoot":""}