@datagrok/sequence-translator 1.10.16 → 1.10.18

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
@@ -2,17 +2,17 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- **Package:** `@datagrok/sequence-translator` (v1.10.8)
5
+ **Package:** `@datagrok/sequence-translator` (v1.10.16)
6
6
  **Category:** Bioinformatics
7
7
  **Author:** Davit Rizhinashvili
8
- **Description:** Translates oligonucleotide sequences between different representations (Axolabs, BioSpring, GCRS, Mermade, HELM, nucleotides, etc.). Also provides pattern design, structure visualization, and advanced polymer conversion (PolyTool).
8
+ **Description:** Translates oligonucleotide sequences between different representations (Axolabs, BioSpring, GCRS, Mermade, HELM, nucleotides, etc.). Provides pattern design, structure visualization, advanced polymer conversion (PolyTool), and a dedicated **OligoNucleotide** semantic type / cell renderer for siRNA / ASO duplex visualization on top of HELM.
9
9
 
10
10
  ## Dependencies
11
11
 
12
12
  | Dependency | Purpose |
13
13
  |------------|---------|
14
14
  | `datagrok-api` | Core Datagrok platform API |
15
- | `@datagrok-libraries/bio` | HELM helper, sequence handler, macromolecule utils, monomer libraries |
15
+ | `@datagrok-libraries/bio` | HELM helper, sequence handler, macromolecule utils, central monomer library |
16
16
  | `@datagrok-libraries/chem-meta` | RDKit JS API, molfile parsing |
17
17
  | `@datagrok-libraries/utils` | Error handling, common utilities |
18
18
  | `@datagrok-libraries/tutorials` | Tutorial creation helpers |
@@ -28,11 +28,13 @@
28
28
 
29
29
  - **MonomersPath** (string): Path to additional monomer libraries. Default: `System:AppData/SequenceTranslator/monomers`. Fallback: `System:AppData/SequenceTranslator/monomers-sample`.
30
30
 
31
+ > The OligoNucleotide subsystem (under `src/oligo-renderer/`) deliberately uses the **central Bio monomer library** (`HELMCoreLibrary.json` + the new `oligo-conjugates.json` shipped in `packages/Bio/files/monomer-libraries/`), not the ST-internal one. ST's monomer-lib stays scoped to translator/pattern/structure apps.
32
+
31
33
  ---
32
34
 
33
35
  ## Architecture
34
36
 
35
- The package is organized into **four main applications** + a **PolyTool subsystem** + shared infrastructure:
37
+ The package is organized into **four main applications**, a **PolyTool subsystem**, the **OligoNucleotide renderer subsystem**, and shared infrastructure:
36
38
 
37
39
  ```
38
40
  src/
@@ -45,26 +47,31 @@ src/
45
47
 
46
48
  ├── apps/
47
49
  │ ├── common/ # Shared model, data loading, validation, UI base
48
- │ │ ├── model/ # Core domain logic
49
- │ │ └── view/ # Shared UI components
50
50
  │ ├── translator/ # Format conversion app
51
- │ │ ├── model/ # Conversion logic
52
- │ │ └── view/ # Translator UI
53
51
  │ ├── pattern/ # Pattern design app
54
- │ │ ├── model/ # Pattern state, persistence, translation
55
- │ │ └── view/ # Pattern editor UI + SVG rendering
56
52
  │ └── structure/ # Molecular structure app
57
- ├── model/ # Molfile generation, V3000 manipulation
58
- │ └── view/ # Structure builder UI
53
+
54
+ ├── oligo-renderer/ # OligoNucleotide semType, cell renderer, panels, converters
55
+ │ ├── types.ts # Constants, parsed-model types, modification dictionary
56
+ │ ├── helm-parser.ts # Lightweight RNA/DNA HELM duplex parser + canonicalizer
57
+ │ ├── canvas-renderer.ts # Pure-fn canvas drawing of duplex, hit-testing
58
+ │ ├── cell-renderer.ts # OligoNucleotideCellRenderer (extends DG.GridCellRenderer)
59
+ │ ├── tooltip.ts # Hover tooltip with cached RDKit structures
60
+ │ ├── legend-panel.ts # Per-cell summary + filtered color legend
61
+ │ ├── structures-panel.ts # Sense/antisense full molecular structures (Bio:toAtomicLevelPanel)
62
+ │ └── converters.ts # convertHelmColumnToOligo / combineSenseAntisenseToOligo / tagAsOligoNucleotide
59
63
 
60
64
  ├── polytool/ # Advanced polymer conversion subsystem
61
65
  │ ├── conversion/ # Core algorithms (chain, rules, reactions)
62
- ├── *.ts # Dialogs, enumeration, handlers
66
+ └── *.ts # Dialogs, enumeration, handlers
63
67
 
64
68
  ├── plugins/ # External plugin integration (MerMade)
65
69
  ├── utils/ # Cyclized/dimerized notation providers, error handling
66
70
  ├── demo/ # Demo entry points
67
71
  └── tests/ # Test suite
72
+
73
+ prototypes/ # Off-tree dev tooling for the OligoNucleotide subsystem
74
+ detectors.js # Autostart bootstrap + context-menu wiring (no semType detector)
68
75
  ```
69
76
 
70
77
  ---
@@ -79,302 +86,247 @@ export const _package: OligoToolkitPackage = new OligoToolkitPackage({debug: tru
79
86
 
80
87
  ### Initialization Flow
81
88
 
82
- 1. `init()` → gets HELM helper from bio library, calls `completeInit()`
89
+ 1. `init()` (decorator `@init`) → gets HELM helper from bio library, calls `completeInit()`
83
90
  2. `completeInit()` → sets up monomer library wrapper
84
91
  3. `initLibData()` → lazy-loads JSON data files (called on app open)
85
92
 
86
93
  ### Registered Functions
87
94
 
88
- | Function | Type | Description |
89
- |----------|------|-------------|
90
- | `oligoToolkitApp()` | app | Combined tabbed app (all 3 sub-apps + plugins) |
91
- | `oligoTranslatorApp()` | app | Standalone translator |
92
- | `oligoPatternApp()` | app | Standalone pattern designer |
93
- | `oligoStructureApp()` | app | Standalone structure viewer |
94
- | `getTranslationHelper()` | func | Returns `ITranslationHelper` for external consumers |
95
- | `validateSequence(seq)` | func | Boolean validation |
96
- | `translateOligonucleotideSequence(seq, src, tgt)` | func | Format-to-format conversion |
97
- | `getMolfileFromGcrsSequence(seq, invert)` | func | GCRS V3000 molfile |
98
- | `linkStrands(strands)` | func | Multi-strand assembly |
99
- | `polyToolConvert2()` | func | Bulk PolyTool conversion |
100
- | `polyToolEnumerateHelmTopMenu()` | dialog | HELM enumeration |
101
- | `polyToolEnumerateChemTopMenu()` | dialog | Chemical enumeration |
102
- | `enumerateSingleHelmSequence()` | func | Single HELM enumeration API |
103
- | `enumerateSingleHelmSequenceWithNaturalAAs()` | func | Enumerate with natural amino acids |
104
- | `getPolyToolCombineDialog()` | dialog | Combine sequences from columns |
105
- | `applyNotationProviderForCyclized()` | func | Register custom notation |
106
- | `createMonomerLibraryForPolyTool()` | func | CSV JSON monomer library |
95
+ The table below lists every function/app/panel registered by `PackageFunctions`. **Method name** is the TypeScript identifier; **registered name** is what the platform sees (defaults to method name when no `name:` decorator option is given).
96
+
97
+ #### Apps
98
+
99
+ | Method | Registered name | browsePath | Description |
100
+ |---|---|---|---|
101
+ | `oligoToolkitApp` | Oligo Toolkit | `Peptides \| Oligo Toolkit` | Combined tabbed app (translator + pattern + structure + plugins) |
102
+ | `oligoTranslatorApp` | Oligo Translator | `Peptides \| Oligo Toolkit` | Standalone translator |
103
+ | `oligoPatternApp` | Oligo Pattern | `Peptides \| Oligo Toolkit` | Standalone pattern designer |
104
+ | `oligoStructureApp` | Oligo Structure | `Peptides \| Oligo Toolkit` | Standalone structure viewer |
105
+ | `ptEnumeratorHelmApp` | HELM Enumerator | `Peptides \| PolyTool` | HELM enumerator app |
106
+ | `ptEnumeratorChemApp` | Chem Enumerator | `Chem \| PolyTool` | Chem enumerator app |
107
+
108
+ #### Top-Menu Functions
109
+
110
+ | Method | Registered name | top-menu | Description |
111
+ |---|---|---|---|
112
+ | `polyToolConvertTopMenu` | polyToolConvert | `Bio \| PolyTool \| Convert...` | Open convert dialog |
113
+ | `polyToolEnumerateHelmTopMenu` | polyToolEnumerateHelm | `Bio \| PolyTool \| Enumerate HELM...` | HELM enumerate dialog |
114
+ | `polyToolEnumerateChemTopMenu` | polyToolEnumerateChem | `Bio \| PolyTool \| Enumerate Chem...` | Chem enumerate dialog |
115
+ | `chemEnumerateReactionsTopMenu` | chemEnumerateReactions | `Chem \| Transform \| Reactions \| Enumerate...` | Alternate menu for chem enumeration |
116
+ | `getPolyToolCombineDialog` | Combine Sequences | `Bio \| PolyTool \| Combine Sequences...` | Cartesian-product combine |
117
+
118
+ #### Cell Renderer
119
+
120
+ | Method | cellType / columnTags | Description |
121
+ |---|---|---|
122
+ | `oligoNucleotideCellRenderer` | `cellType: OligoNucleotide`, `columnTags: quality=OligoNucleotide` | Duplex view for HELM-backed OligoNucleotide columns |
123
+
124
+ #### Panels (`tags: panel, widgets`)
125
+
126
+ | Method | Registered name | Input semType | Description |
127
+ |---|---|---|---|
128
+ | `oligoNucleotidePanel` | Oligo-Nucleotide | OligoNucleotide | Per-cell summary, conjugate counts, color legend filtered to the cell |
129
+ | `oligoNucleotideStructuresPanel` | Oligo Structures | OligoNucleotide | Lazy accordion: sense / antisense full structures via `Bio:toAtomicLevelPanel` |
130
+
131
+ #### Public API & Helpers
132
+
133
+ | Method | Registered name | Notes |
134
+ |---|---|---|
135
+ | `getTranslationHelper` | getTranslationHelper | Returns `ITranslationHelper` (calls `_package.initLibData()` first) |
136
+ | `getCodeToWeightsMap` | getCodeToWeightsMap | Code → MW lookup map |
137
+ | `validateSequence` | validateSequence | Boolean validation against detected format |
138
+ | `getMolfileFromGcrsSequence` | **`validateSequence`** ⚠️ | GCRS → V3000 molfile. **Note:** the `name:` decorator currently mis-registers this as `validateSequence`, colliding with the previous entry. Method name still works for in-package callers. |
139
+ | `linkStrands` | linkStrands | Multi-strand assembly (V3000) |
140
+ | `translateOligonucleotideSequence` | translateOligonucleotideSequence | Format-to-format conversion |
141
+ | `polyToolConvert2` | polyToolConvert2 | Bulk conversion (uses editor `getPolyToolConvertEditor`) |
142
+ | `getPolyToolConvertEditor` | getPolyToolConvertEditor | Function-call editor for `polyToolConvert2` (`@editor` decorator) |
143
+ | `polyToolColumnChoice` | polyToolColumnChoice | Mark macromolecule column + redetect semantic types |
144
+ | `createMonomerLibraryForPolyTool` | createMonomerLibraryForPolyTool | CSV → JSON monomer library |
145
+ | `enumerateSingleHelmSequence` | Enumerate Single HELM Sequence | Programmatic single-HELM enumeration → DataFrame |
146
+ | `enumerateSingleHelmSequenceWithNaturalAAs` | Enumerate Single HELM Sequence with natural amino acids | All-positions natural-AA enumeration |
147
+
148
+ #### Context-Menu Wrappers (invoked from `detectors.js`)
149
+
150
+ | Method | Registered name | Trigger | Description |
151
+ |---|---|---|---|
152
+ | `getPtHelmEnumeratorDialog` | Polytool Helm Enumerator dialog | Right-click Macromolecule cell | HELM enumerator dialog seeded by clicked cell |
153
+ | `getPtChemEnumeratorDialog` | Polytool Chem Enumerator dialog | Right-click Molecule cell | Chem enumerator dialog seeded by clicked cell |
154
+ | `getPtOligoEnumeratorDialog` | Polytool Oligo Enumerator dialog | Right-click OligoNucleotide cell | Wraps oligo HELM in a temp Macromolecule cell, runs HELM enumerator with `outputAsOligo=true` so the result column is tagged OligoNucleotide |
155
+ | `convertHelmToOligoNucleotide` | convertHelmToOligoNucleotide | Submenu `Oligo` on HELM cell | Clones a HELM column and tags it as OligoNucleotide |
156
+ | `combineSenseAntisenseToOligoNucleotide` | combineSenseAntisenseToOligoNucleotide | Submenu `Oligo` on Macromolecule cell | Opens `.prepare(...).edit()` to pick antisense; produces combined Oligo column |
157
+
158
+ #### Notation Provider Glue
159
+
160
+ | Method | Registered name | Notes |
161
+ |---|---|---|
162
+ | `applyNotationProviderForCyclized` | **`applyNotationProviderForHarmonizedSequence`** | Tags column + attaches `CyclizedNotationProvider` |
163
+ | `harmonizedSequenceNotationProviderConstructor` | harmonizedSequenceNotationProviderConstructor | `meta.role: notationProviderConstructor`; returns the provider class |
164
+
165
+ #### Demo Entries
166
+
167
+ | Method | Registered name | demoPath |
168
+ |---|---|---|
169
+ | `demoTranslateSequence` | demoOligoTranslator | `Bioinformatics \| Oligo Toolkit \| Translator` |
170
+ | `demoOligoPattern` | demoOligoPattern | `Bioinformatics \| Oligo Toolkit \| Pattern` |
171
+ | `demoOligoStructure` | demoOligoStructure | `Bioinformatics \| Oligo Toolkit \| Structure` |
107
172
 
108
173
  ---
109
174
 
110
- ## Module Details
111
-
112
- ### apps/common/model/ — Core Domain Model
113
-
114
- **`oligo-toolkit-package.ts`** — `OligoToolkitPackage` class (extends `DG.Package`, implements `ITranslationHelper`)
115
- - Central facade managing initialization, monomer libraries, JSON data
116
- - Factory for `SequenceValidator`, `FormatConverter`, `FormatDetector`
117
- - Lazy init with promise caching
118
-
119
- **`data-loader/json-loader.ts`** — `JsonData` class
120
- - Loads 4 JSON files in parallel from `MonomersPath`:
121
- - `monomer-lib.json` — full monomer library with molfiles
122
- - `formats-to-helm.json` — format code → HELM mappings
123
- - `codes-to-symbols.json` — format code → monomer symbol mappings
124
- - `pattern-app-data.json` — color maps for pattern app
125
- - `linkers.json` — monomers with phosphate groups
126
-
127
- **`monomer-lib/lib-wrapper.ts`** — `MonomerLibWrapper` class
128
- - Adapter between `IMonomerLib` (from bio library) and the application
129
- - Lookups: by symbol, by format, by code
130
- - Molecular weight aggregation, DataFrame generation for viewer
131
-
132
- **`parsing-validation/format-detector.ts`** — `FormatDetector` class
133
- - Infers sequence format from content (checks HELM prefix, then scans for matching codes)
134
- - Validates candidates using `SequenceValidator`
135
-
136
- **`parsing-validation/format-handler.ts`** — `FormatHandler` class
137
- - Bidirectional mapping between source formats and HELM notation
138
- - Regex generation with negative lookaround for accurate code matching
139
- - Phosphate linkage extraction
140
-
141
- **`parsing-validation/sequence-validator.ts`** — `SequenceValidator` class
142
- - Greedy longest-match-first validation
143
- - Returns index of first invalid code or -1 for valid
175
+ ## detectors.js — Bootstrap & Context-Menu Wiring
144
176
 
145
- **`helpers.ts`**Utility functions: `sortByReverseLength()`, `download()`, `tryCatch()`
177
+ `SequenceTranslatorPackageDetectors extends DG.Package`. **No live `semTypeDetector` is registered** only an autostart bootstrap and a `notationRefiner`.
146
178
 
147
- **`const.ts`** `NUCLEOTIDES` array `['A','G','C','U']`, `TECHNOLOGIES`, `DEFAULT_FORMATS` enum
179
+ ### `autostart()` (`tags: autostart, meta.role: autostart`)
180
+ Subscribes once to `grok.events.onContextMenu` and dispatches by `tableColumn.semType`.
148
181
 
149
- ### apps/common/view/ Shared UI
182
+ For `DG.GridCell` items with a `tableColumn`:
150
183
 
151
- **`app-ui-base.ts`** `AppUIBase` abstract class with loading progress indicators
184
+ - **`semType === Macromolecule`**:
185
+ - top-level item **`PolyTool-Enumerate`** → calls `${pkg}:getPtHelmEnumeratorDialog`
186
+ - submenu **`Oligo`**:
187
+ - `Convert HELM to Oligo` (only when `col.meta.units === 'helm'`) → `${pkg}:convertHelmToOligoNucleotide`
188
+ - `Combine sense+antisense to Oligo...` → `DG.Func.find(...).prepare({table, senseCol: col}).edit()` for `combineSenseAntisenseToOligoNucleotide`
189
+ - **`semType === Molecule`**:
190
+ - `PolyTool-Enumerate` → `${pkg}:getPtChemEnumeratorDialog`
191
+ - **`semType === 'OligoNucleotide'`**:
192
+ - `PolyTool-Enumerate` → `${pkg}:getPtOligoEnumeratorDialog`
152
193
 
153
- **`combined-app-ui.ts`** `CombinedAppUI` Multi-tab view (Translator + Pattern + Structure + external plugins)
194
+ Errors are stashed on `window.$sequenceTranslator.contextMenuError` for postmortem.
154
195
 
155
- **`isolated-app-ui.ts`** `IsolatedAppUIBase` Container for standalone app UIs
196
+ ### `refineNotationProviderForHarmonizedSequence` (`meta.role: notationRefiner, tags: notationRefiner`)
197
+ Auto-detects cyclized (`^.+\(\d+\)$`) and dimerized (`^\(#\d\).+$`) sequences from `stats.freq` and applies the cyclized notation provider via `${pkg}:applyNotationProviderForCyclized`.
156
198
 
157
- **`monomer-lib-viewer.ts`** `MonomerLibViewer` Interactive monomer library table with molecule rendering
158
-
159
- **`components/colored-input/`** — `ColoredTextInput` — Textarea with syntax highlighting overlay
160
- - `input-painters.ts` — `highlightInvalidSubsequence()` painter (red for invalid portions)
161
-
162
- **`components/molecule-img.ts`** — `MoleculeImage` — Canvas-based V3000 molfile renderer
163
-
164
- **`components/draw-molecule.ts`** — Molecule drawing utilities
165
-
166
- **`components/router.ts`** — URL routing for combined app tabs
167
-
168
- **`components/app-info-dialog.ts`** — App metadata dialog
199
+ A commented-out `detectHarmonizedSequence` is a placeholder; not active.
169
200
 
170
201
  ---
171
202
 
172
- ### apps/translator/ — Format Conversion App
173
-
174
- **Model:**
175
- - `format-converter.ts` — `FormatConverter` class — Converts between formats using HELM as pivot
176
- - `convertTo(targetFormat)` → helmToFormat / formatToHelm / format→HELM→format
177
- - `conversion-utils.ts` — `getTranslatedSequences()` — Translates to ALL supported formats at once
178
- - `getNucleotidesSequence()` — Extracts nucleotides from HELM
179
- - `convert()` — Legacy single-conversion wrapper
180
- - `const.ts` — `GROUP_TYPE` (NUCLEOSIDE/LINKAGE), `PHOSPHATE_SYMBOL` ('p'), `UNKNOWN_SYMBOL` ('<?>')
181
-
182
- **View:**
183
- - `ui.ts` — `OligoTranslatorUI` + `TranslatorAppLayout`
184
- - **Single mode:** Text input → format detection → all-format output table with copy buttons
185
- - **Bulk mode:** Table/column selection → batch conversion → new column added
186
- - `EventBus` (local) — RxJS BehaviorSubjects for table/column/format state
187
- - Debounced input (300ms), real-time validation via `ColoredTextInput`
188
- - SDF export and SMILES conversion support
203
+ ## Module Details
189
204
 
190
- ---
205
+ ### `src/oligo-renderer/` — OligoNucleotide subsystem
191
206
 
192
- ### apps/pattern/ Pattern Design App
193
-
194
- **Model:**
195
- - `event-bus.ts` — `EventBus` — Central RxJS state manager for entire pattern app
196
- - BehaviorSubjects: pattern name, nucleotide sequences, PTO flags, terminal modifications, table selection, etc.
197
- - Computed observables: `patternStateChanged$`, `strandsUpdated$`, `uniqueNucleotidesChanged$()`
198
- - Change tracking (last-loaded config vs current)
199
- - `data-manager.ts` — `DataManager` (singleton) — Pattern CRUD via Datagrok user storage
200
- - SHA1 hashing for pattern identity
201
- - Current user vs other users pattern management
202
- - Pattern uniqueness validation
203
- - `translator.ts` — `bulkTranslate()` — Applies pattern to table columns
204
- - `applyPatternToRawSequence()` — Single-sequence pattern application
205
- - `router.ts` — `URLRouter` — URL ↔ EventBus synchronization (shareable pattern links)
206
- - `subscription-manager.ts` — `SubscriptionManager` — RxJS subscription cleanup
207
- - `types.ts` — `PatternConfiguration`, `PatternConfigRecord`, error classes
208
- - `const.ts` — Strands (SENSE/ANTISENSE), termini (5'/3'), `MAX_SEQUENCE_LENGTH = 34`, storage name
209
- - `utils.ts` — Nucleotide analysis, strand truncation/extension helpers
210
-
211
- **View:**
212
- - `ui.ts` — `OligoPatternUI` — Main orchestrator (DI wiring, layout composition)
213
- - Left section: Load/Edit controls, table selection for bulk conversion
214
- - Right section: SVG visualization, save/download/share buttons
215
- - `components/left-section.ts` — Load controls, edit controls, bulk convert controls
216
- - `components/edit-block-controls.ts` — Strand length, nucleobase choice, name/comment editing
217
- - `components/load-block-controls.ts` — Author/pattern dropdowns, delete button
218
- - `components/strand-editor/dialog.ts` — Per-position nucleotide + PTO editing
219
- - `components/terminal-modification-editor.ts` — 5'/3' terminal modification editing
220
- - `components/bulk-convert/` — Table + column selection for batch pattern application
221
- - `components/numeric-label-visibility-controls.ts` — Toggle numeric labels per nucleotide
222
- - `components/translation-examples-block.ts` — Live input→output examples
223
-
224
- **SVG Rendering (`view/svg-utils/`):**
225
- - `svg-renderer.ts` — `NucleotidePatternSVGRenderer` — Orchestrates SVG generation
226
- - Composes: `TitleBlock` + `StrandsBlock` + `LegendBlock`
227
- - `strands-block.ts` — Nucleotide circles, PTO stars, terminal mods, strand labels
228
- - `title-block.ts` — Pattern name + strand lengths
229
- - `legend-block.ts` — Color-coded legend with PTO indicator
230
- - `svg-element-factory.ts` — SVG element creation (circles, text, stars, rectangles)
231
- - `svg-display-manager.ts` — Debounced (100ms) SVG updates + PNG export
232
- - `text-dimensions-calculator.ts` — Canvas-based text measurement
233
- - `const.ts` — Radii, fonts, colors, Y-positions, dimension constants
207
+ A self-contained renderer + panels + converters for siRNA/ASO duplex HELM cells. Cell value is HELM under the hood; the `OligoNucleotide` semType + `quality=OligoNucleotide` tag selects the duplex renderer.
234
208
 
235
- ---
209
+ | File | Purpose |
210
+ |---|---|
211
+ | `types.ts` | `OLIGO_SEM_TYPE` / `OLIGO_UNITS` constants, `ParsedNucleotide` / `ParsedConjugate` / `ParsedStrand` / `ParsedDuplex` interfaces, modification dictionary (`SUGAR_MODS`, `PHOSPHATE_MODS`, `CONJUGATE_MODS`), HELMCore-canonical alias maps (`SUGAR_ALIASES`, `PHOSPHATE_ALIASES`), color resolvers, hash-color fallback for unknowns |
212
+ | `helm-parser.ts` | `parseHelmDuplex(helm)` returns `{sense, antisense, raw}`. Parses `RNA1{...}\|RNA2{...}$$$$` shape with bracketed/unbracketed monomers and standalone-conjugate units. Also `canonicalizeHelm()` / `canonicalizeChainBody()` for rewriting aliased symbols (`mR`, `fR`, `LR`, `sP`, …) to HELMCore canonical (`m`, `fl2r`, `lna`, `sp`) before sending HELM to Bio's library-driven pipelines. Custom (not bio's `HelmHelper.parse`) for hot-path performance |
213
+ | `canvas-renderer.ts` | Pure functions: `computeLayout(cellW, cellH, model, opts)`, `drawDuplex()`, `hitTest()`. Variable chip widths (conjugates take their own width), antisense reversal for pair-alignment, leading-conjugate shift on each strand, sugar-mod stripe at chip bottom, PS bar in inter-chip gap (any non-canonical phosphate gets its own colored bar with deterministic color) |
214
+ | `cell-renderer.ts` | `OligoNucleotideCellRenderer extends DG.GridCellRenderer`. Per-value parse cache, per-cell layout cache keyed by `colName@col.version::rowIdx` (column-version invalidates on edits), `onMouseMove` resolves hover via `hitTest` → tooltip |
215
+ | `tooltip.ts` | `showMonomerTooltip(hit, x, y)`. Builds details synchronously, async-loads sugar / base / 3'-linkage RDKit structures from the **central Bio monomer library** (`getMonomerLibHelper().getMonomerLib()`), with alias resolution and structure caching (`Map<kind:canonicalSymbol, HTMLElement>`, reused across hovers) |
216
+ | `legend-panel.ts` | `buildOligoPanel(value)` widget — per-cell sense/antisense lengths, modification counts (collapsed by canonical symbol), conjugates, color legend filtered to mods actually present in the cell |
217
+ | `structures-panel.ts` | `buildOligoStructuresPanel(value)` widget — splits duplex HELM into two single-strand HELMs, runs `canonicalizeHelm` on each, places into a temp Macromolecule DataFrame, wraps each row's cell as `DG.SemanticValue.fromTableCell(cell)` and forwards to `Bio:toAtomicLevelPanel` inside lazy `ui.accordion()` panes ("Sense" / "Antisense") |
218
+ | `converters.ts` | `tagAsOligoNucleotide(col)`, `convertHelmColumnToOligo(table, helmCol)` (creates a tagged clone), `combineSenseAntisenseToOligo(table, senseCol, antiCol)` (renumbers each chain to RNA1{…}, joins with `\|`) |
236
219
 
237
- ### apps/structure/ — Molecular Structure App
238
-
239
- **Model:**
240
- - `sequence-to-molfile.ts` — `SequenceToMolfileConverter` — Parses sequence → retrieves monomer molfiles → links → V3000 molfile
241
- - `monomer-code-parser.ts` — `MonomerSequenceParser` — Greedy longest-match parsing, auto-inserts phosphate linkers
242
- - `mol-transformations.ts` — V3000 molfile manipulation:
243
- - `linkStrandsV3000()` — Links sense/antisense strands with coordinate transformation
244
- - `getNucleotidesMol()` — Combines nucleotide molblocks with linkage
245
- - Rotation, reflection, inversion functions for strand alignment
246
- - Atom/bond renumbering, stereo configuration handling
247
- - `oligo-structure.ts` — High-level API:
248
- - `getMolfileForStrand()` — Single strand molfile
249
- - `getLinkedMolfile()` — Multi-strand assembly
250
- - `saveSdf()` — SDF file download with metadata
251
-
252
- **View:**
253
- - `ui.ts` — `OligoStructureUI` + `StructureAppLayout`
254
- - Three strand inputs (sense, antisense, antisense2) with direction choice (5'→3' / 3'→5')
255
- - Chirality toggle, SDF save, real-time molecule visualization
256
- - Canvas rendering (650x150) with 300ms debounce
220
+ ### `apps/common/model/`Core Domain Model
257
221
 
258
- ---
222
+ **`oligo-toolkit-package.ts`** — `OligoToolkitPackage` (extends `DG.Package`, implements `ITranslationHelper`)
223
+ - Central facade managing initialization, monomer libraries, JSON data
224
+ - Factory for `SequenceValidator`, `FormatConverter`, `FormatDetector`
225
+ - Lazy init with promise caching
259
226
 
260
- ### polytool/ — Advanced Polymer Conversion Subsystem
227
+ **`data-loader/json-loader.ts`**`JsonData` class Loads (in parallel from `MonomersPath`):
228
+ - `monomer-lib.json`, `formats-to-helm.json`, `codes-to-symbols.json`, `pattern-app-data.json`, `linkers.json`
261
229
 
262
- The PolyTool handles complex polymer sequence operations: notation conversion, rules-based transformations, enumeration, and chemical synthesis.
230
+ **`monomer-lib/lib-wrapper.ts`** `MonomerLibWrapper` Adapter between `IMonomerLib` (from bio library) and the application; lookups by symbol/format/code; molecular-weight aggregation.
263
231
 
264
- #### Core Conversion (`conversion/`)
232
+ **`parsing-validation/format-detector.ts`** `FormatDetector` — infers format from content (HELM prefix first, then code-scan).
265
233
 
266
- **`pt-chain.ts`** — `Chain` class Central data model
267
- - `fromSeparator(notation)` — Parse separator notation (e.g., "A-B-{C}-D")
268
- - `fromHelm(helm)` — Parse HELM notation
269
- - `getNotation()` — Output harmonized notation
270
- - `getHelm()` — Output HELM string
271
- - `applyRules(rules)` — Apply link/reaction rules, create position mappings
234
+ **`parsing-validation/format-handler.ts`** — `FormatHandler` — bidirectional format ↔ HELM mapping with regex/negative-lookaround.
272
235
 
273
- **`pt-conversion.ts`** — `doPolyToolConvert()` — Main conversion engine
274
- - Parses separator notation → applies rules → outputs HELM
275
- - Returns `[helms[], isLinear[], positionMaps[]]`
236
+ **`parsing-validation/sequence-validator.ts`** — `SequenceValidator` — greedy longest-match validation.
276
237
 
277
- **`pt-tools-parse.ts`** — Parsing functions
278
- - `parseSeparator()` Handles inline chains with curly-brace fragments
279
- - `parseHelm()` — Extracts peptide definitions and linkages
280
- - `fromObjectsToHelm()` — Serializes monomers + linkages → HELM
281
- - `handleDuplicated()` — Homo/hetero dimer expansion
282
- - `handleLinkRules()` / `handleReactionRules()` — Rule application
238
+ **`helpers.ts`** — `sortByReverseLength()`, `download()`, `tryCatch()`.
239
+ **`const.ts`**`NUCLEOTIDES`, `TECHNOLOGIES`, `DEFAULT_FORMATS` enum.
283
240
 
284
- **`pt-tools-helmmol.ts`** `getHelmMol()` / `helmMolToNotation()` Bidirectional HELM mol ↔ notation
241
+ ### `apps/common/view/`Shared UI
285
242
 
286
- **`pt-atomic.ts`** — `helmToMol()` HELM molfile with linearization and chirality
243
+ `AppUIBase`, `CombinedAppUI`, `IsolatedAppUIBase`, `MonomerLibViewer`, `ColoredTextInput` (textarea with syntax highlighting via `input-painters.ts`), `MoleculeImage` (canvas V3000 renderer), `draw-molecule.ts`, URL `router.ts`, `app-info-dialog.ts`.
287
244
 
288
- **`pt-synthetic.ts`** `getOverriddenLibrary()`RDKit-based reaction execution for synthetic monomers
245
+ ### `apps/translator/`Format Conversion App
289
246
 
290
- **`pt-rules.ts`** — `Rules` class, `RuleInputs` class Rule data model and file I/O
291
- - `RuleLink` type — Linkage rules (monomer pairs, R-groups)
292
- - `RuleReaction` type — Synthesis rules (monomer pairs, SMARTS reactions)
247
+ **Model:** `format-converter.ts` (`FormatConverter` — HELM-pivot conversion), `conversion-utils.ts` (`getTranslatedSequences`, `getNucleotidesSequence`), `const.ts`.
293
248
 
294
- **`pt-rule-cards.ts`** `RuleCards` — Visual rule preview with monomer card gallery
249
+ **View:** `ui.ts` (`OligoTranslatorUI`, `TranslatorAppLayout`)single + bulk modes, debounced input, EventBus state, SDF/SMILES export.
295
250
 
296
- **`rule-manager.ts`** `RulesManager` (singleton) UI-driven rule editing and persistence
251
+ ### `apps/pattern/`Pattern Design App
297
252
 
298
- **`rule-reaction-editor.ts`** Reaction editing dialog with molecule sketchers
253
+ **Model:** `event-bus.ts` (RxJS BehaviorSubjects), `data-manager.ts` (singleton CRUD via user storage, SHA1 identity), `translator.ts` (`bulkTranslate`, `applyPatternToRawSequence`), `router.ts` (URL ↔ EventBus), `subscription-manager.ts`, `types.ts`, `const.ts` (strands, termini, `MAX_SEQUENCE_LENGTH = 34`), `utils.ts`.
299
254
 
300
- **`pt-misc.ts`** `Linkage` type, `getInnerIdx()` / `getOuterIdx()` Index translation for multi-chain
255
+ **View:** `ui.ts` (`OligoPatternUI`), `components/` (left-section, right-section, edit-block, load-block, strand-editor dialog, terminal-modification editor, bulk-convert, numeric-label visibility, translation-examples).
301
256
 
302
- #### Dialogs
257
+ **SVG Rendering** (`view/svg-utils/`): `svg-block-base.ts` (`SVGBlockBase` — base class for composable SVG blocks, see Design Pattern #7), `svg-renderer.ts` (`NucleotidePatternSVGRenderer`), `strands-block.ts`, `title-block.ts`, `legend-block.ts`, `svg-element-factory.ts`, `svg-display-manager.ts` (debounced 100ms + PNG export), `text-dimensions-calculator.ts`, `utils.ts`, `const.ts`.
303
258
 
304
- **`pt-dialog.ts`**Main conversion dialog and chem enumeration dialog
305
- - `getPolyToolConvertDialog()` — Column selector, flags, rule file, history
306
- - `polyToolConvert()` — Execution: validates → applies rules → generates HELM → converts to molfiles
259
+ ### `apps/structure/` Molecular Structure App
307
260
 
308
- **`pt-enumerate-seq-dialog.ts`** HELM enumeration dialog
309
- - `getPolyToolEnumerateDialog()` — HELM editor, placeholder grids, enumeration type selector
310
- - `polyToolEnumerateSeq()` — Execution with single/parallel/matrix/breadth strategies
261
+ **Model:** `sequence-to-molfile.ts` (`SequenceToMolfileConverter`), `monomer-code-parser.ts` (greedy longest-match, auto-inserts phosphate linkers), `mol-transformations.ts` (`linkStrandsV3000`, `getNucleotidesMol`, rotation/reflection helpers), `oligo-structure.ts` (`getMolfileForStrand`, `getLinkedMolfile`, `saveSdf`).
311
262
 
312
- **`pt-enumeration-helm.ts`** `doPolyToolEnumerateHelm()`Core enumeration engine
313
- - Single, Parallel, Matrix, Breadth strategies
263
+ **View:** `ui.ts` (`OligoStructureUI`, `StructureAppLayout`) — three strand inputs, chirality toggle, SDF save, 650×150 canvas with 300ms debounce.
314
264
 
315
- **`pt-enumeration-chem.ts`** `getEnumerationChem()`Chemical library enumeration via RDKit
265
+ ### `polytool/`Advanced Polymer Conversion Subsystem
316
266
 
317
- **`pt-combine-dialog.ts`** — `getPTCombineDialog()` — Cartesian product of sequences from columns
267
+ #### Top-level
318
268
 
319
- **`pt-monomer-selection-dialog.ts`** Autocomplete monomer picker with tag-based display
269
+ `const.ts`, `types.ts` (`PolyToolEnumeratorParams`, `PolyToolEnumeratorTypes`), `utils.ts` (`_setPeptideColumn`, helpers).
320
270
 
321
- **`pt-placeholders-input.ts`** Grid input for point placeholders (position + monomers)
271
+ #### Core Conversion (`conversion/`)
322
272
 
323
- **`pt-placeholders-breadth-input.ts`** Grid input for range placeholders (start/end + monomers)
273
+ `pt-chain.ts` (`Chain`), `pt-conversion.ts` (`doPolyToolConvert`), `pt-tools-parse.ts` (`parseSeparator`, `parseHelm`, `fromObjectsToHelm`, `handleDuplicated`, `handleLinkRules`, `handleReactionRules`), `pt-tools-helmmol.ts` (`getHelmMol`, `helmMolToNotation`), `pt-atomic.ts` (`helmToMol`), `pt-synthetic.ts` (`getOverriddenLibrary`, RDKit reactions), `pt-rules.ts` (`Rules`, `RuleInputs`, `RuleLink`, `RuleReaction`), `pt-rule-cards.ts`, `rule-manager.ts`, `rule-reaction-editor.ts`, `pt-misc.ts` (`Linkage`, index helpers), `style.css`.
324
274
 
325
- **`pt-unrule.ts` / `pt-unrule-dialog.ts`** — HELM → harmonized notation reversal
275
+ #### Dialogs
326
276
 
327
- **`pt-convert-editor.ts`** `PolyToolConvertFuncEditor` Function call editor
277
+ | File | Purpose |
278
+ |---|---|
279
+ | `pt-dialog.ts` | Main convert dialog; `polyToolConvertUI()`, `polyToolConvert()` |
280
+ | `pt-enumerate-seq-dialog.ts` | HELM enumeration dialog; `polyToolEnumerateHelmUI(cell?, outputAsOligo=false)`, `getPolyToolEnumerateDialog`, `polyToolEnumerateSeq`. The `outputAsOligo` flag tags the result column as OligoNucleotide so the duplex renderer picks it up |
281
+ | `pt-chem-enum.ts` | Chem enumeration core (formerly `pt-enumeration-chem.ts` in older docs). Pure logic — RDKit module only, no Datagrok/UI deps. Normalizes 5 R-label spellings (`[N*]`, `[*:N]`, `[*N]`, `[RN]`, `[R:N]`) to `[*:N]`. `Zip` and `Cartesian` modes; `CHEM_ENUM_MAX_RESULTS = 1_000_000` cap. **Two-path SMILES assembly** (`buildJoinedSmiles`): atom-substitution R-groups (no `[*:N]` in their SMILES — single-atom mode) are spliced into the core via plain string replace; labeled R-groups are then joined via shared ring-closure digits across a disconnected SMILES. **`isSingleAtom`** on `ChemEnumRGroup` is set when `makeRGroup` finds 0 R-labels and `trySingleAtomCanonical` (RDKit `get_num_atoms(true) === 1` + `remove_hs_in_place()`) returns a canonical atom token. Pipeline is two-stage: `enumerateRaw` returns parseable-but-uncanonical SMILES with **zero RDKit calls**; `executeEnumeration` then canonicalizes the whole column in one batched `Chem:convertNotation` call, which also fixes aromaticity case after a single-atom splice. `enumerate` / `enumerateSample` are sync-RDKit variants for tests. Helpers: `moveStartRLabelToBranch`, `pickFreeRingDigits`, `formatRingDigit`, `substituteRLabelWithRingDigit`, `substituteRLabelWithAtom` |
282
+ | `pt-chem-enum-dialog.ts` | Chem enumeration dialog UI; `polyToolEnumerateChemUI(cell?)` (dialog; cell preload → first core when it has ≥1 R-label, else `grok.shell.info`) and `polyToolEnumerateChemApp` (`DG.View` entry; both share `buildChemEnumPanel` + `bindActionButton`). Card UI 110×104 with 320×260 hover tooltip, three hover-revealed action icons: edit + duplicate (`var(--blue-3)`) + delete (`var(--red-3)`). **Duplicate** reopens the sketcher pre-loaded with the molecule and pushes a NEW card on OK (original untouched). Cores rendered in a single horizontal `virtualView`; R-groups in one horizontal `virtualView` per R# inside a capped-height vertical scroll. Live preview: up to 12 reservoir-sampled molecules via `enumerateSampleRaw` (uncanonical SMILES, `grok.chem.drawMolecule` parses lazily per visible card). `openCoreSketchDialog` accepts when ≥1 R-label is present; `openRGroupSketchDialog` accepts when **(exactly 1 R-label) OR (single atom + target R# set)**. Import wizard (`openImportWizard`) picks Molecule column from any open table, infers R# from column name via `/r[\s_\-:]*(\d+)/i`, dedup checkbox, single-atom rows labeled `R{n} · atom`. Mode tooltip / status messages explain Zip vs Cartesian and the single-atom shortcut |
283
+ | `pt-enumeration-helm.ts` | `doPolyToolEnumerateHelm` engine — Single / Parallel / Matrix / Breadth strategies |
284
+ | `pt-combine-dialog.ts` | `getPTCombineDialog` — Cartesian combine |
285
+ | `pt-placeholders-input.ts` | Grid input for point placeholders |
286
+ | `pt-placeholders-breadth-input.ts` | Grid input for range placeholders |
287
+ | `pt-unrule.ts` / `pt-unrule-dialog.ts` | HELM → harmonized notation reversal |
288
+ | `pt-convert-editor.ts` | `PolyToolConvertFuncEditor` |
328
289
 
329
290
  #### Handlers
330
291
 
331
- **`monomer-lib-handler.ts`** `PolyToolMonomerLibHandler` — Validates and converts monomer libraries
332
- **`csv-to-json-monomer-lib-converter.ts`** — `PolyToolCsvLibHandler` — CSV → JSON conversion
333
-
334
- ---
292
+ `monomer-lib-handler.ts` (`PolyToolMonomerLibHandler`), `csv-to-json-monomer-lib-converter.ts` (`PolyToolCsvLibHandler`).
335
293
 
336
- ### utils/ — Notation Providers & Error Handling
294
+ ### `utils/` — Notation Providers & Error Handling
337
295
 
338
- **`cyclized.ts`** `CyclizedNotationProvider` (implements `INotationProvider`)
339
- - Custom separator-based notation for cyclized peptides
340
- - Converts to HELM via `Chain.fromSeparator()`
296
+ `cyclized.ts` (`CyclizedNotationProvider` implements `INotationProvider`), `dimerized.ts` (extends cyclized), `cell-renderer-cyclized.ts` (`CyclizedCellRendererBack` for the cyclized notation), `err-info.ts` (`defaultErrorHandler`).
341
297
 
342
- **`dimerized.ts`**`DimerizedNotationProvider` (extends `CyclizedNotationProvider`)
298
+ ### `plugins/` External Plugin Integration
343
299
 
344
- **`cell-renderer-cyclized.ts`** — `CyclizedCellRendererBack` Grid cell rendering for cyclized sequences
300
+ `mermade.ts` — `getExternalAppViewFactories()` loads MerMade synthesis plugin via `grok.functions.call()`.
345
301
 
346
- **`err-info.ts`** `defaultErrorHandler()` — Centralized error logging
302
+ ### `demo/`
347
303
 
348
- ### plugins/ External Plugin Integration
349
-
350
- **`mermade.ts`** — `getExternalAppViewFactories()` — Loads MerMade synthesis plugin dynamically via `grok.functions.call()`
351
-
352
- ### demo/
353
-
354
- **`demo-st-ui.ts`** — Demo functions for all three apps (translator, pattern, structure)
304
+ `demo-st-ui.ts` — demo functions for translator / pattern / structure.
355
305
 
356
306
  ---
357
307
 
358
308
  ## Tests
359
309
 
360
- Test entry: `package-test.ts` → `tests/` directory
310
+ Test entry: `package-test.ts` → imports each test file under `src/tests/`. Test framework: `@datagrok-libraries/test`.
361
311
 
362
- | Test File | Category | What It Tests |
363
- |-----------|----------|---------------|
364
- | `formats-to-helm.ts` | Formats to HELM / HELM to Formats | Bidirectional format↔HELM conversion |
312
+ | File | Category | What it tests |
313
+ |---|---|---|
314
+ | `formats-to-helm.ts` | Formats to HELM / HELM to Formats | Bidirectional format HELM conversion |
365
315
  | `formats-support.ts` | Formats support | All expected formats available |
366
- | `helm-to-nucleotides.ts` | HELM to Nucleotides | HELM → nucleotide string |
316
+ | `helm-to-nucleotides.ts` | HELM to Nucleotides | HELM → bare nucleotide string |
367
317
  | `files-tests.ts` | files | CSV file-based integration tests |
368
318
  | `polytool-chain-from-notation-tests.ts` | PolyTool: Chain | Chain parsing, HELM conversion, rule application |
369
319
  | `polytool-chain-parse-notation-tests.ts` | PolyTool: Chain: parseNotation | Index translation, monomer counts |
370
320
  | `polytool-convert-tests.ts` | PolyTool: Convert | End-to-end conversion pipeline |
371
- | `polytool-detectors-custom-notation-test.ts` | PolyTool: detectors | Semantic type detection |
372
- | `polytool-enumerate-tests.ts` | PolyTool: Enumerate | Single/Parallel/Matrix enumeration |
321
+ | `polytool-detectors-custom-notation-test.ts` | PolyTool: detectors | Semantic-type detection |
322
+ | `polytool-enumerate-tests.ts` | PolyTool: Enumerate | Single / Parallel / Matrix enumeration |
373
323
  | `polytool-enumerate-breadth-tests.ts` | PolyTool: Enumerate | Breadth range enumeration |
324
+ | `polytool-enumerate-chem-tests.ts` | PolyTool: Enumerate | Chem enumeration |
374
325
  | `polytool-unrule-tests.ts` | PolyTool: Unrule | HELM → harmonized notation |
375
326
  | `toAtomicLevel-tests.ts` | toAtomicLevel | Synthetic monomer generation via RDKit |
376
- | `const.ts` | | Test data: formatsToHelm, helmToNucleotides |
377
- | `utils/` | — | Test helpers: detection utils, format conversion |
327
+ | `oligo-renderer-tests.ts` | OligoRenderer: parser / dictionary / layout / hit testing / drawing smoke | HELM parse, alias resolution, layout invariants, hit-test correctness, smoke drawing |
328
+ | `const.ts` | — | Test data: `formatsToHelm`, `helmToNucleotides` |
329
+ | `utils/` | — | Test helpers (`detect-macromolecule-utils.ts`, `index.ts`) |
378
330
 
379
331
  ---
380
332
 
@@ -383,25 +335,45 @@ Test entry: `package-test.ts` → `tests/` directory
383
335
  ```
384
336
  files/
385
337
  ├── monomers-sample/
386
- │ ├── monomer-lib.json # Monomer library (molfiles, SMILES, metadata)
387
- │ ├── codes-to-symbols.json # Format code → monomer symbol mappings
388
- │ ├── formats-to-helm.json # Format code → HELM notation mappings
389
- │ ├── linkers.json # Monomers with phosphate groups
390
- │ ├── pattern-app-data.json # Color maps for pattern app SVG
338
+ │ ├── monomer-lib.json
339
+ │ ├── codes-to-symbols.json
340
+ │ ├── formats-to-helm.json
341
+ │ ├── linkers.json
342
+ │ ├── pattern-app-data.json
391
343
  │ └── README.md
392
344
  ├── polytool-rules/
393
- │ └── rules_example.json # Link/reaction rule definitions
345
+ │ └── rules_example.json
394
346
  ├── samples/
395
- │ ├── HELM.csv # HELM format examples
396
- │ ├── cyclized.csv # Cyclized peptide samples
397
- │ ├── cyclized_MSA.csv # MSA of cyclized variants
398
- └── bulk-translation-axolabs.csv
347
+ │ ├── HELM.csv
348
+ │ ├── cyclized.csv
349
+ │ ├── cyclized_MSA.csv
350
+ ├── bulk-translation-axolabs.csv
351
+ │ └── sirna-demo.csv # 30+ siRNA / ASO duplexes covering 2'-OMe, 2'-F, GalNAc-L3, Chol, LNA/MOE gapmers, fallback cases — generated by prototypes/gen-sirna-demo.mjs
399
352
  └── tests/
400
- ├── axolabs1.csv # Axolabs format test data
401
- ├── polytool-reaction-lib.json # Reaction library for tests
353
+ ├── axolabs1.csv
354
+ ├── polytool-reaction-lib.json
355
+ ├── chem_enum_cores.csv
356
+ ├── chem_enum_rgroups.csv
402
357
  └── README.md
403
358
  ```
404
359
 
360
+ > **Note**: the OligoNucleotide subsystem also relies on `packages/Bio/files/monomer-libraries/oligo-conjugates.json` (GalNAc / L3 / Chol / Bio / Toc / Pal / DBCO from PubChem). That file lives in the **Bio** package because it's a global monomer library merged by `MonomerLibFromFilesProvider` — not an ST-internal asset.
361
+
362
+ ---
363
+
364
+ ## `prototypes/` — Off-Tree Dev Tooling
365
+
366
+ Helper scripts and a standalone HTML prototype for the OligoNucleotide subsystem. These are **not bundled** in the published package and do not register Datagrok functions.
367
+
368
+ | File | Purpose |
369
+ |---|---|
370
+ | `gen-sirna-demo.mjs` | Generates `files/samples/sirna-demo.csv` — 30+ siRNA / ASO duplexes, all conjugate placements (5'-only, 3'-only, both ends, AS-only, dual-strand) |
371
+ | `gen-oligo-conjugates.mjs` | Fetches molfiles + canonical SMILES from PubChem, writes `packages/Bio/files/monomer-libraries/oligo-conjugates.json` (CHEM-type entries: GalNAc, L3, Chol, Bio, Toc, Pal, DBCO) |
372
+ | `validate-oligo-conjugates.mjs` | Validates the generated `oligo-conjugates.json` against Bio's HELM monomer JSON schema using the same Ajv2020 + ajv-errors config as `MonomerLibFileValidator` |
373
+ | `sirna-renderer-prototype.html` | Standalone HTML/CSS/JS prototype of the duplex cell renderer — reference visual against which the production canvas renderer was tuned |
374
+
375
+ ---
376
+
405
377
  ## Key Design Patterns
406
378
 
407
379
  1. **HELM as Pivot Format** — All format conversions go through HELM as intermediate representation
@@ -412,6 +384,12 @@ files/
412
384
  6. **Greedy Longest-Match** — Sequence parsing always tries longest codes first
413
385
  7. **SVG Composable Blocks** — `TitleBlock`, `StrandsBlock`, `LegendBlock` extend `SVGBlockBase`
414
386
  8. **Dialog Singletons** — Strand/terminal editors prevent multiple instances
387
+ 9. **HELMCore-canonical aliases** (oligo-renderer) — input HELM may use vendor / Pistoia / legacy symbols (`mR`, `fR`, `sP`); the renderer resolves through alias maps and serializes canonical (`m`, `fl2r`, `sp`) before forwarding to Bio's library-driven pipelines (so monomer lookups always hit)
388
+ 10. **Column-version-keyed cache** — the OligoNucleotide cell renderer's per-cell layout cache is keyed by `${colName}@${col.version}::${rowIdx}`, so column edits orphan old entries automatically
389
+ 11. **Two-stage chem-enum pipeline** — `enumerateRaw` does pure-string assembly (ring-closure trick + atom splice for single-atom R-groups), produces parseable but uncanonical SMILES with **zero per-row RDKit calls**; `executeEnumeration` then canonicalizes the whole output column in a single batched `Chem:convertNotation` call (`overwrite: true`, `kekulize: false`). This also handles aromaticity case-fixup after a single-atom splice (e.g. uppercase `N` spliced into an aromatic ring). The bulk path is parallelized across Chem workers; per-row sync RDKit (`enumerate` / `enumerateSample`) is reserved for tests
390
+ 12. **Single-atom R-group shortcut** (chem-enum) — drawing a bare atom (`N`, `O`, `Cl`) without any `[*:N]` label is accepted as long as RDKit confirms exactly one heavy atom. Such groups bypass ring-closure joining and are spliced into the core's `[*:N]` slot via plain string replace; mixing single-atom and labeled R-groups in the same enumeration is supported per R-number
391
+
392
+ ---
415
393
 
416
394
  ## Key Data Flow
417
395
 
@@ -424,7 +402,7 @@ User Input (sequence string)
424
402
 
425
403
  PolyTool:
426
404
  Separator notation → parseSeparator() → Chain
427
- → Chain.applyRules() → handleLinkRules/handleReactionRules
405
+ → Chain.applyRules() → handleLinkRules / handleReactionRules
428
406
  → Chain.getHelm() → HELM string
429
407
  → helmToMol() → V3000 molfile (optional)
430
408
 
@@ -432,9 +410,52 @@ Pattern:
432
410
  Raw sequence + PatternConfig
433
411
  → applyPatternToRawSequence() → modified nucleotides
434
412
  → SVGRenderer.renderPattern() → visual output
413
+
414
+ OligoNucleotide cell rendering:
415
+ HELM cell value
416
+ → parseHelmDuplex() → ParsedDuplex {sense, antisense}
417
+ → computeLayout(w, h, model) → ChipPos[] with leading-conjugate shifts
418
+ → drawDuplex() → canvas
419
+ → onMouseMove → hitTest → showMonomerTooltip
420
+ → getBioLib() → findMonomerMolfile (canonical alias)
421
+ → grok.chem.drawMolecule → cached HTMLElement
422
+
423
+ OligoNucleotide structures panel:
424
+ HELM cell value
425
+ → split chains by '|', renumber each to RNA1{…}, canonicalizeHelm
426
+ → temp DataFrame[Macromolecule helm column]
427
+ → DG.SemanticValue.fromTableCell → Bio:toAtomicLevelPanel widget
428
+ → ui.accordion(lazy panes) per strand
435
429
  ```
436
430
 
431
+ ---
432
+
437
433
  ## Scripts
438
434
 
439
- - `scripts/build-monomer-lib.py` — Python script (RDKit) to build monomer library JSON from source files
440
- - `link-bio` — Bash script to build and link `@datagrok-libraries/bio` locally
435
+ - `scripts/build-monomer-lib.py` — Python (RDKit) builds monomer-library JSON from source files
436
+ - `link-bio` — bash script to build and link `@datagrok-libraries/bio` locally
437
+
438
+ ---
439
+
440
+ ## Quick Lookups
441
+
442
+ | Looking for... | Check first |
443
+ |---|---|
444
+ | Function/panel/cell-renderer registration | `src/package.ts` (`PackageFunctions` class) |
445
+ | Context-menu wiring + `Oligo` submenu | `detectors.js` (`autostartContextMenu`) |
446
+ | OligoNucleotide cell renderer | `src/oligo-renderer/cell-renderer.ts` |
447
+ | OligoNucleotide layout / drawing | `src/oligo-renderer/canvas-renderer.ts` |
448
+ | HELM canonicalization (alias → HELMCore) | `src/oligo-renderer/helm-parser.ts` (`canonicalizeHelm`) |
449
+ | Modification colors / aliases | `src/oligo-renderer/types.ts` |
450
+ | Sense/antisense structure panel | `src/oligo-renderer/structures-panel.ts` |
451
+ | Convert HELM → Oligo / combine sense+AS | `src/oligo-renderer/converters.ts` |
452
+ | Translator app | `src/apps/translator/` |
453
+ | Pattern designer app | `src/apps/pattern/` |
454
+ | Structure app | `src/apps/structure/` |
455
+ | PolyTool convert pipeline | `src/polytool/conversion/`, `pt-dialog.ts` |
456
+ | HELM enumeration (with `outputAsOligo`) | `src/polytool/pt-enumerate-seq-dialog.ts` |
457
+ | Chem enumeration (logic) | `src/polytool/pt-chem-enum.ts` (`buildJoinedSmiles` two-path assembly, `trySingleAtomCanonical`, `substituteRLabelWithAtom`) |
458
+ | Chem enumeration (UI) | `src/polytool/pt-chem-enum-dialog.ts` (`buildChemEnumPanel`, sketch dialogs, `openImportWizard`, duplicate/edit/delete card icons) |
459
+ | Demo / sample siRNA data | `files/samples/sirna-demo.csv` |
460
+ | Off-tree dev tooling | `prototypes/` (gen / validate / HTML prototype) |
461
+ | Auto-generated wrappers | `src/package.g.ts`, `src/package-api.ts` |