varri-js 1.0.0 → 1.0.1

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/CITATION.bib ADDED
@@ -0,0 +1,13 @@
1
+ @misc{raden_varrii_js_2026,
2
+ title = {vaRRI-js - Visual annotation of RNA-RNA interactions},
3
+ author = {Raden, Martin and Ganter, Fabian},
4
+ year = {2026},
5
+ url = {https://backofenlab.github.io/vaRRI-js/},
6
+ note = {Web application and JavaScript library},
7
+ abstract = {vaRRI-js is a browser-based JavaScript library for visualizing RNA-RNA interactions (RRIs) as 2D diagrams with annotations such as base-pair highlights, probability profiles, subsequence highlights, and point mutation annotations.},
8
+ keywords = {RNA, RNA-RNA interaction, visualization, JavaScript, bioinformatics},
9
+ version = {1.0.0},
10
+ license = {MIT},
11
+ organization = {University of Freiburg, Department of Computer Science, Bioinformatics Lab},
12
+ organization_url = {https://www.bioinformatics.uni-freiburg.de/}
13
+ }
package/CITATION.cff ADDED
@@ -0,0 +1,33 @@
1
+ cff-version: 1.2.0
2
+ title: vaRRI-js - Visual annotation of RNA-RNA interactions
3
+ message: >-
4
+ If you use this software in a publication, please cite it using the metadata
5
+ in this file.
6
+ type: software
7
+ authors:
8
+ - given-names: Martin
9
+ family-names: Raden
10
+ affiliation: University of Freiburg, Germany
11
+ orcid: 'https://orcid.org/0000-0002-7926-5911'
12
+ - given-names: Fabian
13
+ family-names: Ganter
14
+ affiliation: University of Freiburg, Germany
15
+ - name: Bioinformatics Lab, University of Freiburg
16
+ affiliation: Department of Computer Science, University of Freiburg
17
+ website: 'https://www.bioinformatics.uni-freiburg.de/'
18
+ version: 1.0.0
19
+ date-released: '2026-07-31'
20
+ repository-code: 'https://github.com/BackofenLab/vaRRI-js'
21
+ url: 'https://backofenlab.github.io/vaRRI-js/'
22
+ abstract: >-
23
+ vaRRI-js is a browser-based JavaScript library for visualizing RNA-RNA
24
+ interactions (RRIs) as 2D diagrams with annotations such as base-pair
25
+ highlights, probability profiles, subsequence highlights, and point mutation
26
+ annotations.
27
+ keywords:
28
+ - RNA
29
+ - RNA-RNA interaction
30
+ - visualization
31
+ - JavaScript
32
+ - bioinformatics
33
+ license: MIT
package/README.md CHANGED
@@ -132,6 +132,50 @@ Install vaRRI-js in an application with:
132
132
  npm install varri-js
133
133
  ```
134
134
 
135
+ Starting with version 1.0.1, the package includes the complete viewer as well as the library.
136
+
137
+ ### Open `index.html` in Browser
138
+
139
+ After installation via npm, you find the vaRRI-js user interface `index.html` in the following subfolder
140
+
141
+ ```bash
142
+ node_modules/varri-js/index.html
143
+ ```
144
+
145
+ You can open it with any recent browser and start working
146
+
147
+
148
+ ### Local Webserver (optional)
149
+
150
+ Alternatively, serving the installed viewer locally using a local webserver requires e.g. Python 3:
151
+
152
+ ```bash
153
+ python3 -m http.server 8080 --bind 127.0.0.1 --directory node_modules/varri-js
154
+ ```
155
+
156
+ Open `http://localhost:8080/index.html`. Applications can serve or copy the whole package
157
+ directory using their own static-file server, retaining the relative directory layout.
158
+ The viewer includes example inputs, SVG/PNG export controls, help, citation data and local logos.
159
+ Use HTTP rather than `file://` so the help and citation pages can load their packaged data.
160
+
161
+ The JavaScript `main` and root export intentionally remain `src/vaRRI.js`: `require('varri-js')`
162
+ and ESM default imports return the library API. The viewer has a separate public entry:
163
+
164
+ ```javascript
165
+ const viewerPath = require.resolve('varri-js/index.html');
166
+ ```
167
+
168
+ Resolving this path does not start a server; serve its containing directory to make the viewer
169
+ and its assets available. HTML is a browser document, not a JavaScript module.
170
+
171
+ **Network requirements:** the existing viewer uses jsDelivr for `marked` and external
172
+ university logos from `www.bioinf.uni-freiburg.de`. The help page also uses cdnjs for its
173
+ Markdown stylesheet, and the citation page loads Citation.js from jsDelivr. These URLs
174
+ are retained, so this is not a fully offline distribution. Deployments with restricted
175
+ network access must provide those assets locally and update the HTML references/CSP.
176
+ Opening the help or citation page through `file://` can additionally fetch fallback content
177
+ from `raw.githubusercontent.com`; serving the package over HTTP uses the local files.
178
+
135
179
  The package exports the CommonJS-compatible API as `varri-js` and ships the browser assets under
136
180
  `varri-js/fornac/` and `varri-js/dist/`. For a static page served from an npm-based application,
137
181
  load the browser files in this order:
@@ -573,16 +617,26 @@ The `src` directory provides a [detailed vaRRI-js Library API documentation](src
573
617
 
574
618
  ## Release Process
575
619
 
576
- Publishing is automated by [`.github/workflows/publish-npm.yml`](.github/workflows/publish-npm.yml).
577
- For the first publication, repository maintainers can configure a granular npm token with package
578
- write access as the `NPM_TOKEN` GitHub Actions secret. Every published GitHub release runs the
579
- tests, builds and checks the package, derives the npm version from the release tag, and publishes
580
- with provenance.
581
-
582
- After the package exists on npm, the recommended long-term setup is an npm trusted publisher for
583
- the `BackofenLab/vaRRI-js` repository and `publish-npm.yml` workflow, with `npm publish` selected as
584
- an allowed action. The workflow already grants the required OIDC permission; after a successful
585
- trusted-publishing run, the long-lived `NPM_TOKEN` secret can be removed.
620
+ Publishing is automated by [`.github/workflows/publish-npm.yml`](https://github.com/BackofenLab/vaRRI-js/blob/main/.github/workflows/publish-npm.yml).
621
+ The initial `varri-js@1.0.0` publication was manual. Before automated releases, an npm package
622
+ owner must configure a **GitHub Actions trusted publisher** in the package's npm settings:
623
+ organization `BackofenLab`, repository `vaRRI-js`, workflow filename `publish-npm.yml`, no
624
+ environment name, with direct `npm publish` allowed. This one-time account action may request
625
+ 2FA. The workflow uses OIDC and does not require an `NPM_TOKEN` secret or interactive 2FA for
626
+ each release. See [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
627
+
628
+ Merge the workflow changes before creating a release tag on a commit containing them.
629
+ Every published GitHub release runs the tests, derives the npm version from the release tag,
630
+ builds and installs a temporary package to verify its contents, and publishes with provenance.
631
+ Stable releases use the `latest` npm tag; semantic prerelease versions or GitHub releases
632
+ marked as prereleases use `next`. Use `npm install varri-js@next` to try a prerelease.
633
+ Use a new, increasing stable version for each stable release: npm versions are immutable,
634
+ and re-running an already successful publish cannot overwrite that version. Publishing a
635
+ GitHub release is the trigger; pushing a Git tag alone does not publish the npm package.
636
+
637
+ Run `npm run test:ci` and `npm run test:package` locally before proposing a release. The
638
+ package check installs the actual tarball in a temporary consumer and checks module exports,
639
+ viewer asset references, documentation images, and citation data. It also runs in PR CI.
586
640
 
587
641
  Release tags must be valid semantic versions with an optional leading `v`, for example `v1.2.3`
588
642
  or `1.2.3`. The workflow changes `package.json` and `package-lock.json` only inside the temporary
package/citation.html ADDED
@@ -0,0 +1,316 @@
1
+ <!DOCTYPE html>
2
+ <html lang="de">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Citation — vaRRI-js</title>
7
+ <link rel="icon" type="image/png" href="logo/vaRRI.logo.40x40.png">
8
+
9
+ <!-- External Stylesheets (matching index.html) -->
10
+ <link rel="stylesheet" href="fornac/fornac.css" />
11
+ <link rel="stylesheet" href="style.css" />
12
+
13
+ <style>
14
+ /* Tab Buttons & Layout overrides */
15
+ .tab-header {
16
+ display: flex;
17
+ gap: 0.25rem;
18
+ border-bottom: 2px solid #e9ecef;
19
+ margin-bottom: 1rem;
20
+ overflow-x: auto;
21
+ }
22
+
23
+ .tab-btn {
24
+ padding: 0.6rem 1.2rem;
25
+ background: none;
26
+ border: none;
27
+ border-bottom: 3px solid transparent;
28
+ font-weight: 600;
29
+ color: #6c757d;
30
+ cursor: pointer;
31
+ transition: all 0.2s;
32
+ }
33
+
34
+ .tab-btn:hover {
35
+ color: #1d7fa4;
36
+ }
37
+
38
+ .tab-btn.active {
39
+ color: #1d7fa4;
40
+ border-bottom-color: #1d7fa4;
41
+ }
42
+
43
+ /* Content Area */
44
+ .tab-content {
45
+ display: none;
46
+ }
47
+
48
+ .tab-content.active {
49
+ display: block;
50
+ }
51
+
52
+ .action-bar {
53
+ display: flex;
54
+ justify-content: flex-end;
55
+ margin-bottom: 0.5rem;
56
+ }
57
+
58
+ pre.citation-pre {
59
+ background: #f8f9fa;
60
+ padding: 1.25rem;
61
+ border-radius: 6px;
62
+ border: 1px solid #e9ecef;
63
+ overflow-x: auto;
64
+ white-space: pre-wrap;
65
+ word-wrap: break-word;
66
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
67
+ font-size: 0.9rem;
68
+ line-height: 1.45;
69
+ color: #212529;
70
+ }
71
+ </style>
72
+
73
+ <!-- Citation.js Library -->
74
+ <script src="https://cdn.jsdelivr.net/npm/citation-js@0.7.14/build/citation.min.js"></script>
75
+ </head>
76
+ <body>
77
+
78
+ <header>
79
+ <a class="group-logo-link" href="https://www.bioinf.uni-freiburg.de" target="_blank" rel="noopener noreferrer" aria-label="Visit the Bioinformatics Group Freiburg website">
80
+ <img class="group-logo" src="https://www.bioinf.uni-freiburg.de/assets/images/bioinf-fr-logo-blau.png" alt="Bioinformatics Group Freiburg logo" />
81
+ </a>
82
+ <img class="varri-logo" src="logo/vaRRI.logo.200x200.png" alt="vaRRI-js logo">
83
+ <div class="header-title">
84
+ <h1>vaRRI-js</h1>
85
+ <p>Visual Annotation of RNA–RNA Interactions</p>
86
+ </div>
87
+ <div class="uni-logo-wrap">
88
+ <a class="uni-logo-link" href="https://www.bioinf.uni-freiburg.de" target="_blank" rel="noopener noreferrer" aria-label="Open the University Freiburg Bioinformatics website in a new tab">
89
+ <span class="uni-logo-text">Bioinformatics -</span>
90
+ <img src="https://www.bioinf.uni-freiburg.de/assets/images/logo-uni-freiburg.png" alt="University Freiburg logo" class="img-fluid uni-logo" />
91
+ </a>
92
+ </div>
93
+ </header>
94
+
95
+ <main>
96
+ <div class="controls-column" style="grid-column: 1 / -1; max-width: 1000px; margin: 0 auto; width: 100%;">
97
+
98
+ <aside class="panel control-panel">
99
+ <details class="control-group" open>
100
+ <summary>Citation & Export</summary>
101
+ <div class="control-group-body">
102
+ <p style="margin-top:0; color:#6c757d;">
103
+ Choose a format to copy or download the citation data.</p>
104
+
105
+ <!-- Tab Navigationsleiste -->
106
+ <div class="tab-header" id="tab-header">
107
+ <button class="tab-btn active" onclick="switchTab('bibtex')">BibTeX</button>
108
+ <button class="tab-btn" onclick="switchTab('cff')">CFF</button>
109
+ <button class="tab-btn" onclick="switchTab('apa')">APA</button>
110
+ <button class="tab-btn" onclick="switchTab('ieee')">IEEE</button>
111
+ <button class="tab-btn" onclick="switchTab('ris')">RIS (EndNote/Zotero)</button>
112
+ </div>
113
+
114
+ <!-- Inhaltsbereich -->
115
+ <div id="tab-contents"></div>
116
+ </div>
117
+ </details>
118
+ </aside>
119
+
120
+ <!-- Link zurück zur Hauptanwendung -->
121
+ <aside class="panel control-panel">
122
+ <details class="control-group">
123
+ <summary>
124
+ <a href="index.html">
125
+ ← Back to Visualization
126
+ </a>
127
+ </summary>
128
+ </details>
129
+ </aside>
130
+
131
+ </div>
132
+ </main>
133
+
134
+ <footer class="site-footer" aria-label="Service information">
135
+ <div class="footer-content">
136
+ <p>
137
+ Disclaimer: This service is provided "as is" without any warranties or guarantees.
138
+ </p>
139
+ <p>
140
+ For comments or feedback, please contact
141
+ <a href="https://www.bioinf.uni-freiburg.de/" target="_blank" rel="noopener noreferrer">Freiburg Bioinformatics Group of the University of Freiburg</a>.
142
+ </p>
143
+ <p>
144
+ The source code for this service is freely available on
145
+ <a href="https://github.com/BackofenLab/vaRRI-js" target="_blank" rel="noopener noreferrer">GitHub</a>.
146
+ </p>
147
+ </div>
148
+ </footer>
149
+
150
+ <!-- JavaScript Logik -->
151
+ <script>
152
+ const GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/BackofenLab/vaRRI-js/main/';
153
+
154
+ const TABS_CONFIG = {
155
+ bibtex: { label: 'BibTeX', filename: 'CITATION.bib', mime: 'text/plain', source: 'CITATION.bib', type: 'raw' },
156
+ cff: { label: 'CFF', filename: 'CITATION.cff', mime: 'text/yaml', source: 'CITATION.cff', type: 'raw' },
157
+ apa: { label: 'APA', filename: 'citation.txt', mime: 'text/plain', source: 'CITATION.bib', type: 'parsed', style: 'apa' },
158
+ ieee: { label: 'IEEE', filename: 'citation.txt', mime: 'text/plain', source: 'CITATION.bib', type: 'parsed', style: 'ieee' },
159
+ ris: { label: 'RIS', filename: 'citation.ris', mime: 'application/x-research-info-systems', source: 'CITATION.bib', type: 'parsed', style: 'ris' }
160
+ };
161
+
162
+ const dataStore = {};
163
+ let currentTab = 'bibtex';
164
+
165
+ document.addEventListener('DOMContentLoaded', async () => {
166
+ buildTabStructure();
167
+ await Promise.all([
168
+ loadFileWithFallback('CITATION.bib'),
169
+ loadFileWithFallback('CITATION.cff')
170
+ ]);
171
+ renderTabContent(currentTab);
172
+ });
173
+
174
+ async function loadFileWithFallback(filename) {
175
+ try {
176
+ const response = await fetch(filename);
177
+ if (!response.ok) throw new Error(`Status ${response.status}`);
178
+ dataStore[filename] = await response.text();
179
+ return;
180
+ } catch (localErr) {
181
+ console.warn(`Lokaler Aufruf für "${filename}" fehlgeschlagen. Versuche Fallback...`);
182
+ }
183
+
184
+ try {
185
+ const response = await fetch(GITHUB_RAW_BASE + filename);
186
+ if (!response.ok) throw new Error(`GitHub Status ${response.status}`);
187
+ dataStore[filename] = await response.text();
188
+ } catch (githubErr) {
189
+ dataStore[filename] = `[Fehler: Datei "${filename}" konnte nicht geladen werden.]`;
190
+ }
191
+ }
192
+
193
+ function buildTabStructure() {
194
+ const container = document.getElementById('tab-contents');
195
+ container.innerHTML = '';
196
+ Object.keys(TABS_CONFIG).forEach(key => {
197
+ const conf = TABS_CONFIG[key];
198
+ const tabDiv = document.createElement('div');
199
+ tabDiv.id = `content-${key}`;
200
+ tabDiv.className = `tab-content ${key === currentTab ? 'active' : ''}`;
201
+ tabDiv.innerHTML = `
202
+ <div class="action-bar">
203
+ <button class="btn btn-sm" onclick="downloadTabContent('${key}')" style="background:#1d7fa4; color:#fff;">
204
+ 💾 ${conf.label} herunterladen
205
+ </button>
206
+ </div>
207
+ <pre id="pre-${key}" class="citation-pre">Wird geladen...</pre>
208
+ `;
209
+ container.appendChild(tabDiv);
210
+ });
211
+ }
212
+
213
+ function parseBibTeX(bibText) {
214
+ const fields = {};
215
+ const matches = bibText.matchAll(/(\w+)\s*=\s*[{"]([^}"]+)[}"]/g);
216
+ for (const match of matches) {
217
+ fields[match[1].toLowerCase()] = match[2].trim();
218
+ }
219
+ return fields;
220
+ }
221
+
222
+ function formatCitation(bibText, style) {
223
+ const b = parseBibTeX(bibText);
224
+ const authors = b.author || 'Unknown Author';
225
+ const title = b.title || 'Untitled';
226
+ const year = b.year || 'n.d.';
227
+ const journal = b.journal || b.booktitle || '';
228
+ const volume = b.volume ? `, ${b.volume}` : '';
229
+ const number = b.number ? `(${b.number})` : '';
230
+ const pages = b.pages ? `, ${b.pages}` : '';
231
+ const doi = b.doi ? ` https://doi.org/${b.doi}` : '';
232
+ const url = b.url ? ` ${b.url}` : '';
233
+ const link = doi || url;
234
+ const note = b.note ? ` (${b.note})` : '';
235
+ const RIS_type = journal == '' ? 'COMP' : 'JOUR';
236
+
237
+ if (style === 'apa') {
238
+ return `${authors} (${year}). ${title}. ${journal}${volume}${number}${pages}.${note}${link}`;
239
+ }
240
+
241
+ if (style === 'ieee') {
242
+ return `${authors}, "${title}," ${journal}${volume}${number}${pages}, ${year}.${note}${link}`;
243
+ }
244
+
245
+ if (style === 'ris') {
246
+ const risLines = [
247
+ `TY - ${RIS_type}`,
248
+ `AU - ${authors}`,
249
+ `TI - ${title}`,
250
+ `JO - ${journal}`,
251
+ `PY - ${year}`,
252
+ `VL - ${b.volume || ''}`,
253
+ `IS - ${b.number || ''}`,
254
+ `SP - ${b.pages || ''}`,
255
+ `DO - ${b.doi || ''}`,
256
+ `UR - ${b.url || ''}`,
257
+ `AB - ${b.abstract || ''}`,
258
+ `N1 - ${b.note || ''}`
259
+ ];
260
+
261
+ if (b.keywords) {
262
+ b.keywords.split(/[,;]/).forEach(kw => {
263
+ if (kw.trim()) risLines.push(`KW - ${kw.trim()}`);
264
+ });
265
+ }
266
+
267
+ risLines.push('ER -');
268
+ return risLines.filter(line => !line.endsWith(' - ')).join('\n');
269
+ }
270
+
271
+ return bibText;
272
+ }
273
+
274
+ async function renderTabContent(tabKey) {
275
+ const conf = TABS_CONFIG[tabKey];
276
+ const preElem = document.getElementById(`pre-${tabKey}`);
277
+
278
+ if (conf.type === 'raw') {
279
+ preElem.textContent = dataStore[conf.source] || 'Kein Inhalt verfügbar.';
280
+ } else if (conf.type === 'parsed') {
281
+ const rawBib = dataStore[conf.source];
282
+ if (!rawBib || rawBib.startsWith('[Fehler')) {
283
+ preElem.textContent = 'Zitationsdaten konnten nicht geladen werden.';
284
+ return;
285
+ }
286
+ preElem.textContent = formatCitation(rawBib, conf.style);
287
+ }
288
+ }
289
+
290
+ function switchTab(tabKey) {
291
+ currentTab = tabKey;
292
+ document.querySelectorAll('.tab-btn').forEach(btn => {
293
+ btn.classList.toggle('active', btn.getAttribute('onclick').includes(`'${tabKey}'`));
294
+ });
295
+ document.querySelectorAll('.tab-content').forEach(content => {
296
+ content.classList.toggle('active', content.id === `content-${tabKey}`);
297
+ });
298
+ renderTabContent(tabKey);
299
+ }
300
+
301
+ function downloadTabContent(tabKey) {
302
+ const conf = TABS_CONFIG[tabKey];
303
+ const text = document.getElementById(`pre-${tabKey}`).textContent;
304
+ const blob = new Blob([text], { type: `${conf.mime};charset=utf-8` });
305
+ const url = URL.createObjectURL(blob);
306
+ const a = document.createElement('a');
307
+ a.href = url;
308
+ a.download = conf.filename;
309
+ document.body.appendChild(a);
310
+ a.click();
311
+ document.body.removeChild(a);
312
+ URL.revokeObjectURL(url);
313
+ }
314
+ </script>
315
+ </body>
316
+ </html>
Binary file
Binary file
Binary file