Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Keep the third-party parser separate in reviews and preserve upstream bytes.
src/bin/nlohmann/json.hpp linguist-vendored
src/bin/nlohmann/LICENSE.MIT linguist-vendored -whitespace
gui/vendor/plotly-cartesian.min.js linguist-vendored
gui/vendor/plotly.css linguist-vendored
gui/vendor/plotly-LICENSE.txt linguist-vendored -whitespace
3 changes: 3 additions & 0 deletions .github/workflows/build-wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ jobs:
test -s _site/api/index.html
test -s _site/gui/index.html
test -s _site/gui/wasm/intarna.wasm
test -s _site/gui/vendor/plotly-cartesian.min.js
test -s _site/gui/vendor/plotly.css
test -s _site/gui/licenses/Plotly.txt
- uses: actions/upload-artifact@v4
with:
name: intarna-pages-preview
Expand Down
35 changes: 35 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@

## Interface and handling

- refine GUI seed and CSV control widths, filter examples by task, cap result
table height and use dark active tabs for additional output views (PR #303 review)

- compact and left-align GUI parameter rows, split CSV controls into their own
tab, describe every additional output format and plot energy/probability files
with sequence names, source tabs and PNG export (PR #287 review)

- reorganize GUI parameters, add conditional seed/accessibility controls and
grouped CSV defaults, expand FASTA limits and refine result headers (PR #299)

Expand Down Expand Up @@ -289,6 +296,34 @@ energies, restricted partition sums, and trackers.
################################################################################

261026 Alexander Mitrofanov
* gui/app.js, gui/{index.html,styles.css}, scripts/wasm/test-browser.py
* place Explicit seeds beside a half-width Seed constraint selector and halve
the CSV sort selector width; verify desktop/mobile control geometry
* show only examples for the selected task, retain complete CSV downloads while
scrolling result tables above 500 px, and match output tab selection styling
to the parameter tabs (PR #303 maintainer review)
* gui/docs/{json-workflow,step3-frontend-ui,step4-results-and-deployment}.md
* document task-specific examples, compact controls and the bounded result table

261026 Alexander Mitrofanov
* gui/{app,options,sessions,output-visualization}.js,
gui/{index.html,styles.css}
* compact bounded parameter rows while preserving their grouping and align
wrapped labels above left-aligned controls
* move CSV sorting/column selection into a dedicated tab between Output and
Model & Energy; add content/use tooltips to all additional output formats
+ show sequence-aware Plotly profiles, heatmaps and selected-spot bars beside
native file content; add header downloads and verified PNG export
* .gitattributes, gui/vendor/, scripts/wasm/stage.sh,
.github/workflows/build-wasm.yml
+ vendor and checksum Plotly.js Cartesian 4.1.2, stage its MIT license and
verify both assets in the combined Pages artifact
* gui/tests/output-visualization.test.mjs, scripts/wasm/test-browser.py,
README.md, gui/docs/
+ cover signed categorical axes, file/sequence mapping, malformed data,
responsive compact layout, plot rendering and PNG downloads in real
Chromium/Firefox with the existing WASM engine
* address https://github.com/BackofenLab/IntaRNA/pull/287#issuecomment-6416573560
* src/IntaRNA/SeededPartitionFunction.cpp
* Select sparse batch anchors by scanning admitted seeds instead of probing
every possible pair in every source span; retain the grid lookup for dense
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Predict short RNA pairs or evaluate supplied structures locally with the [browse
The browser accepts up to 50 records per strand, 2–5,000 nucleotides each, with longest selected query × longest selected target at most 1,000,000. Prediction defaults to 10 interactions per pair and includes opening and seed energies; evaluation selects all energy columns.
Computed runs stay in separate, foldable sessions under Results in the current tab. Name and select sessions to restore their inputs and compare results; editing or resetting parameters keeps earlier runs.
Select an interaction to view it in vaRRI, or download a session’s ZIP with inputs, compiled versions, reproducible parameters and all outputs. Sessions are temporary and are cleared when the page is reloaded.
Minimum-energy and interaction-probability output files include local Plotly visualizations with PNG export; every additional file retains its original text view and download.
See the [WebAssembly build instructions](gui/docs/step1-wasm-compilation.md) and
[interface/deployment notes](gui/docs/step4-results-and-deployment.md).

Expand Down
28 changes: 20 additions & 8 deletions gui/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,22 @@ function fieldRow(body, fields, suffix = '') {
for (const definition of GROUPS) {
const body = group(definition.key, definition.label);
if (definition.key === 'seed-options') {
const primary = element('div', '', 'seed-primary-row');
const wrapper = element('div', '', 'field seed-mode');
const label = element('label', 'Seed constraint'); label.htmlFor = 'seed-mode';
const select = element('select'); select.id = 'seed-mode';
for (const [value, name] of [['none', 'Disable seed constraint'], ['explicit', 'Explicit seed'], ['identify', 'Identify seeds']]) {
const option = element('option', name); option.value = value; select.append(option);
}
select.value = 'identify'; wrapper.append(label, select); body.append(wrapper);
select.value = 'identify'; wrapper.append(label, select);
primary.append(wrapper, renderField(FIELDS.find((field) => field.key === 'seedTQ')));
body.append(primary);
}
if (definition.rows) {
for (const row of definition.rows) fieldRow(body, row);
for (const row of definition.rows) {
if (definition.key === 'seed-options' && row[0].key === 'seedTQ') continue;
fieldRow(body, row);
}
} else fieldRow(body, definition.fields);
if (definition.strand) {
const p = definition.strand;
Expand All @@ -120,10 +126,10 @@ function checkbox(container, key, labelText, description) {
if (description) label.title = description;
label.append(input, document.createTextNode(labelText)); container.append(label); return input;
}
const outputBody = byId('output-options').querySelector('.group-body');
outputBody.append(element('h3', 'CSV output columns', 'subheading'));
outputBody.append(element('p', 'seq1 is the target; seq2 is the query. Hover over a column name for details. Select columns for the table and CSV download; table visibility can be changed after computation.', 'input-help'));
const selectionTools = element('div', '', 'selection-tools'); outputBody.append(selectionTools);
const csvBody = byId('csv-options').querySelector('.group-body');
csvBody.append(element('h3', 'CSV output columns', 'subheading'));
csvBody.append(element('p', 'seq1 is the target; seq2 is the query. Hover over a column name for details. Select columns for the table and CSV download; table visibility can be changed after computation.', 'input-help'));
const selectionTools = element('div', '', 'selection-tools'); csvBody.append(selectionTools);
const selectColumns = (selection) => {
csvOrder = [...selection]; for (const input of csvInputs) input.checked = selection.includes(input.value); inputsChanged();
};
Expand All @@ -142,16 +148,17 @@ for (const group of CSV_GROUPS) {
inputsChanged();
}); legend.append(button);
}
const picker = element('div', '', 'checkbox-grid csv-columns'); section.append(legend, picker); outputBody.append(section);
const picker = element('div', '', 'checkbox-grid csv-columns'); section.append(legend, picker); csvBody.append(section);
for (const key of group.keys) {
const column = CSV_CATALOG.find((column) => column.key === key);
csvInputs.push(checkbox(picker, key, key, column.description));
}
}
const outputBody = byId('output-options').querySelector('.group-body');
outputBody.append(element('h3', 'Additional output files', 'subheading'));
outputBody.append(element('p', 'Reports are computed with the same settings. ASCII, detailed and ensemble reports each run a fresh engine instance and can take extra time.', 'input-help'));
const outputPicker = element('div', '', 'checkbox-grid'); outputBody.append(outputPicker);
for (const output of EXTRA_OUTPUTS) outputInputs.push(checkbox(outputPicker, output.key, output.label));
for (const output of EXTRA_OUTPUTS) outputInputs.push(checkbox(outputPicker, output.key, output.label, output.description));
const generalBody = group('general-options', 'CLI-only');
generalBody.append(element('p', 'Process-level settings (threads, log paths, parameter-file paths and terminal help) are available in the CLI.'));
const docs = element('a', 'Full CLI parameter documentation'); docs.href = 'https://github.com/BackofenLab/IntaRNA#readme'; generalBody.append(docs);
Expand All @@ -172,6 +179,9 @@ function updateLength(name) {
}
function updateAvailability() {
const evaluation = getTask() === 'evaluation';
for (const button of byId('example-options').querySelectorAll('[data-example-task]')) {
button.hidden = button.dataset.exampleTask !== getTask();
}
byId('evaluation-input').hidden = !evaluation;
byId('task-help').textContent = evaluation ? 'Evaluate supplied structures for one query and one target. Prediction constraints are ignored.' : 'Predict interactions for all query–target sequence pairs.';
byId('compute').textContent = evaluation ? 'Evaluate interactions' : 'Compute interactions';
Expand Down Expand Up @@ -313,6 +323,7 @@ async function loadExamples() {
container.replaceChildren();
for (const example of examples) {
const button = element('button'); button.type = 'button';
button.dataset.exampleTask = example.request.task;
button.append(element('strong', example.name), element('span', example.description));
button.addEventListener('click', () => {
if (byId('inputs').disabled) return;
Expand All @@ -321,6 +332,7 @@ async function loadExamples() {
});
container.append(button);
}
updateAvailability();
} catch (error) { container.replaceChildren(element('p', `${error.message} Reload to try again.`, 'input-help')); }
}
loadExamples();
Expand Down
3 changes: 2 additions & 1 deletion gui/docs/json-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ uses native defaults for omitted parameters. Inactive controls keep their edits
in the form for switching modes, but their values are omitted from executed and
stored settings. Previously computed sessions remain available. Empty FASTA
fields show the short demonstration sequences as placeholders only. The Examples
disclosure menu follows the Input Parameters label.
disclosure menu follows the Input Parameters label and lists only examples for
the selected Prediction or Evaluation task.

Maintainers edit `gui/examples/index.json` (an array of local JSON filenames).
Each listed file contains `name`, `description`, and `configuration`, where the
Expand Down
18 changes: 14 additions & 4 deletions gui/docs/step3-frontend-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ groups, followed by the compute button, results, additional files and versions.
All working content is left aligned; two equally sized strand editors collapse
into one column on mobile. The shared institutional header is the strongest
visual element. The wrapping tab bar identifies parameter groups. Numeric inputs
are 9 rem wide within a responsive grid of compact controls; text/data fields
retain useful width.
are at most 9 rem wide within bounded, left-aligned row grids; select/text inputs
use compact type-specific tracks and text/data fields retain useful width. Rows
keep their scientific grouping without stretching across the complete panel,
use 16 px horizontal gaps and bottom-align labels above their controls.
The Seed tab places the constraint selector (12 rem) beside Explicit seeds on
desktop; the CSV sort selector uses a 19 rem track. Both controls stack at mobile
widths.
The institutional link reads “Bioinformatics - University of Freiburg” and opens
the group website. Footer and table notes use the available width, wrapping only
when the viewport requires it.
Expand All @@ -24,7 +29,7 @@ when the viewport requires it.
Group logo IntaRNA package / RNA–RNA interaction analysis University logo
RNA–RNA interactions Computations stay on your device
Task: Prediction / Evaluation
[ Sequences | Seed | Output | Model & Energy | Query | Target | CLI-only ]
[ Sequences | Seed | Output | CSV | Model & Energy | Query | Target | CLI-only ]
[ Active panel: e.g. query + target, start indices, evaluation structures ]
Compute Input Parameters: Examples / Store / Load / Reset
Results / Show-Hide / name / ZIP download / delete
Expand All @@ -49,7 +54,10 @@ one), and accepts 0–100. Zero enables accessibility-only output.
the worker boundary. Query/target retain their distinct interior-loop defaults
(16 and 10). Meta-options such as `acc`, `accW`, `accL`, `intLenMax` and
`intLoopMax` are represented by per-strand controls to avoid conflicting values.
FASTA headers supply sequence names instead of `qId`/`tId`. `outCsvSort` selects a native download sort column; table sorting is independent.
FASTA headers supply sequence names instead of `qId`/`tId`. The dedicated CSV
tab contains `outCsvSort` and the interaction-table column choices; Output keeps
prediction filters and additional file formats. `outCsvSort` selects a native
download sort column; table sorting is independent.
CSV uses a fixed semicolon delimiter.
Process settings (threads, file/log paths, parameter files, help/version commands)
are replaced by the worker lifecycle, local data inputs, downloads and displayed
Expand All @@ -68,6 +76,8 @@ Function, with per-group selection actions and name-only labels with description
tooltips. Prediction adds ED1, ED2, seedE, seedStart1 and seedStart2 to the native
CSV columns; evaluation selects all energy outputs. JSON records these browser
defaults explicitly so native replay retains the same output.
Every additional output checkbox has a descriptive tooltip covering its content,
format and intended use, including the model restrictions shown in its label.

Prediction accepts plain sequence or up to 50 FASTA records per strand.
Evaluation requires one query and target and 1–100 predefined structures.
Expand Down
50 changes: 45 additions & 5 deletions gui/docs/step4-results-and-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,30 @@ This is relative suboptimality, not a free-energy change or probability.
Every heading is a keyboard-operable sort button with `aria-sort`. Search covers
all returned values. A separate checklist shows/hides table columns after the
run; it does not change selected CSV output. All identifiers, output previews
and diagnostics render with `textContent`. Selected extra files appear below the
table with bounded text previews and complete downloads, including SVG source.
and diagnostics render with `textContent`. The table has a 500 px maximum height
with vertical scrolling for longer results and a sticky heading; CSV downloads
still contain every selected row. Selected extra files appear below the
table with their original filename, covered sequence/sequence-pair names, a
header-level download, and bounded text previews, including SVG source.
Empty output is successful and still permits CSV/accessibility downloads.

## Additional output visualizations

Plot-capable additional CSV files open on a Visualization tab, with the original
File Content in an adjacent tab. Query/target minimum-energy and spot-probability
profiles use line plots; pair minimum-energy and all-spot probability matrices
use heatmaps; explicitly requested spot probabilities use a bar plot. Plot axes
use the literal coordinate strings from IntaRNA rather than numerical scales, so
signed indices cross from -1 to 1 without inventing coordinate zero. Plot titles
and additional-file headers identify the target/query records covered by native
`-t#q#` or `-s#` filename suffixes.

Plotly renders locally from the pinned Cartesian bundle. A separate Export PNG
button produces a 1400 × 850 image for the active plot; the normal file download
and reproducible ZIP retain untouched native data. Plots render lazily when their
file is unfolded, resize with restored sessions, and are purged when a session is
deleted. Non-plot formats keep the direct content view.

## Selected interaction in vaRRI

Each row has a keyboard-accessible View button; clicking a row also selects it.
Expand Down Expand Up @@ -91,15 +111,20 @@ python3 scripts/wasm/test-browser.py http://127.0.0.1:8765 --browser firefox
```

The staging script includes matched WASM assets, only the application source
files needed at runtime, compiled version metadata and dependency notices. Build-time `build-info.json`
files needed at runtime, compiled version metadata and dependency notices. It
also verifies and stages the pinned Plotly.js 4.1.2 Cartesian bundle and MIT
license; no CDN is used. Build-time `build-info.json`
and `build-info.txt` record actual CLI IntaRNA/ViennaRNA/Boost versions, source
commit and tracked modifications, SDK version and asset SHA-256 digests. Staging
checks these digests and preserves the original metadata; it never replaces the
compiled revision with a later staging checkout. The footer displays versions.
It needs the same activated SDK and dependency build directory as the build.
No compiled artifacts are committed. HTTP(S), module workers and WebAssembly
No generated WASM artifacts are committed; the reviewed minified Plotly browser
distribution is the only new runtime artifact. HTTP(S), module workers and WebAssembly
are required; `file://` and legacy browsers are unsupported. No backend,
SharedArrayBuffer, credentials, CDN or cross-origin headers are needed.
SharedArrayBuffer, credentials, CDN or cross-origin headers are needed. CSP
keeps scripts/styles same-origin and allows ephemeral blob images solely for
PNG export.

The existing repository already publishes its user guide and API with Pages.
Publishing a second independent artifact would overwrite them. Therefore
Expand Down Expand Up @@ -156,6 +181,21 @@ GitHub's platform checks independently cover GCC 14 release/debug and Apple
Clang. Deployment itself occurs only after merge; local browser tests do not
claim a live Pages deployment.

### PR #287 visualization review verification, 2026-10-26

- `npm test --prefix gui`: all 25 protocol, results, output-visualization,
archive, upload and worker tests pass. Profile and matrix cases cover signed
categorical coordinates, sequence names, missing values and malformed data.
- Playwright 1.64.0 with Chromium 156 and Firefox 157 passes the full real-WASM
suite, including the compact desktop/mobile parameter layout, separate CSV
tab, output tooltips, native-file headers, Plotly rendering and a PNG whose
signature is checked after download. The staged GUI uses the matching
revision `7378801` engine; scientific code and compiled assets are unchanged.
- Desktop screenshots for every parameter tab and the completed session were
visually reviewed; the 390 px viewport has no document overflow.
- Plotly 4.1.2 is pinned by npm integrity and bundle SHA-256, staged locally with
its MIT license. Shell/Python syntax and `git diff --check` pass.

### PR #287 review verification, 2026-10-21

- Emscripten 4.0.23 rebuild using the pinned official dependency archives passes
Expand Down
Loading
Loading