Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
72f2434
Add optional checksum-verified reference loading for Shinylive
CedricHermansBIT Sep 29, 2026
134118b
Fetch immutable reference data on first use when absent
CedricHermansBIT Sep 29, 2026
1aa8443
Allow deferred grids in cached reference profiles
CedricHermansBIT Sep 29, 2026
93fde36
Load deferred references and preserve all background choices
CedricHermansBIT Sep 29, 2026
cdaab72
Add thin Shinylive export with external reference assets and manifests
CedricHermansBIT Sep 29, 2026
e13d82b
Use visible manifest filenames for Shinylive export
CedricHermansBIT Sep 29, 2026
06fcf98
Include visible index in thin export
CedricHermansBIT Sep 29, 2026
65635ad
Retain standalone scientific test compatibility
CedricHermansBIT Sep 29, 2026
1e1bd72
Document reference-data export, XML dependency and deployment checks
CedricHermansBIT Sep 29, 2026
8ca3ed2
Add local and thin-manifest reference loader regression tests
CedricHermansBIT Sep 29, 2026
56131bc
Make reference loader smoke test runnable at top level
CedricHermansBIT Sep 29, 2026
0d5dee1
Add RamplotR phi/psi SVG favicon
CedricHermansBIT Sep 29, 2026
8013075
Load Plotly only after a structure needs plotting
CedricHermansBIT Sep 29, 2026
0471aa9
Use new favicon and defer Plotly until a plot is requested
CedricHermansBIT Sep 29, 2026
49677d8
Queue plot rendering during lazy Plotly download and preserve WebGL c…
CedricHermansBIT Sep 29, 2026
d9117c2
Load Plotly on demand when a confidence heatmap is opened
CedricHermansBIT Sep 29, 2026
fcf18a2
Publish favicon and stage thin Shinylive references without redundant…
CedricHermansBIT Sep 29, 2026
2396029
Prefetch Plotly on Analyse clicks while R processes the structure
CedricHermansBIT Sep 29, 2026
c0d1a2b
Test deferred Plotly loading, shared downloads and failure retries
CedricHermansBIT Sep 29, 2026
5d79082
Run deferred Plotly and reference loader regression tests in CI
CedricHermansBIT Sep 29, 2026
f9666b8
Verify deferred library and favicon in UI contract
CedricHermansBIT Sep 29, 2026
f56ca3a
Assert form loads before Plotly in real browser tests
CedricHermansBIT Sep 29, 2026
6f4c85d
Document cold-start measurement and static asset caching
CedricHermansBIT Sep 29, 2026
3de6b05
Add scoped one.com compression and safe caching for RamplotR
CedricHermansBIT Sep 29, 2026
ed93a94
Add scoped one.com compression and caching for shared Shinylive assets
CedricHermansBIT Sep 29, 2026
3e6304b
Correct Apache FilesMatch regex escaping
CedricHermansBIT Sep 29, 2026
eb44a46
Correct Apache FilesMatch regex escaping
CedricHermansBIT Sep 29, 2026
74d7359
Include scoped one.com .htaccess files in static export
CedricHermansBIT Sep 29, 2026
0fe029e
Document public Shinylive app, optimized export and one.com hosting i…
CedricHermansBIT Sep 29, 2026
c11ca92
Revalidate unchanged RDS reference files across deployments
CedricHermansBIT Sep 29, 2026
458e718
Add reference-data cache revalidation rules to generated export
CedricHermansBIT Sep 29, 2026
80f224d
Document one.com compression, cache templates and optimized Shinylive…
CedricHermansBIT Sep 29, 2026
813d7ae
Validate optimized export syntax and one.com templates in CI
CedricHermansBIT Sep 29, 2026
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
15 changes: 14 additions & 1 deletion .github/workflows/scientific-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ jobs:
with:
use-public-rspm: true
- name: Check source syntax
run: Rscript -e 'for (p in c("shinyRam/app.R", list.files("shinyRam/R", full.names=TRUE))) parse(file=p)'
run: Rscript -e 'for (p in c("shinyRam/app.R", "scripts/export-shinylive.R", list.files("shinyRam/R", full.names=TRUE))) parse(file=p)'
shell: bash
- name: Check scoped one.com deployment templates
run: |
test -s config/onecom/ramplotr.htaccess
test -s config/onecom/shinylive.htaccess
test -s config/onecom/reference-data.htaccess
grep -q 'shinylive.htaccess' scripts/export-shinylive.R
grep -q 'reference-data.htaccess' scripts/export-shinylive.R
shell: bash
- name: Check the responsive interface and preserved Shiny IDs
run: Rscript tests/ui-contract.R
Expand All @@ -33,6 +41,8 @@ jobs:
node --check shinyRam/www/custom.js
node --check shinyRam/www/prediction.js
node --check shinyRam/www/density.js
node --check shinyRam/www/plotly-loader.js
node tests/plotly-loader.test.cjs
node tests/density-ui.test.cjs
node tests/ui.test.cjs
node tests/prediction-ui.test.cjs
Expand Down Expand Up @@ -65,6 +75,9 @@ jobs:
- name: Verify cached reference profiles
run: Rscript tests/profile.R
shell: bash
- name: Verify local and thin-manifest reference access
run: Rscript tests/reference-loader.R
shell: bash
- name: Verify structure input and reference-specific classifications
run: |
Rscript tests/input.R
Expand Down
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ RamplotR is an open-source R Shiny application that brings backbone geometry, a

*RamplotR's default publication palette. Example: PDB [1CRN](https://www.rcsb.org/structure/1CRN). [More screenshots](#screenshots).*

[**Get started**](#get-started) · [**Explore the features**](#what-you-can-do) · [**Scientific interpretation**](#scientific-interpretation) · [**Documentation**](#documentation)
[**Try the browser app**](https://bikc.be/RamplotR/) · [**Get started**](#get-started) · [**Explore the features**](#what-you-can-do) · [**Scientific interpretation**](#scientific-interpretation) · [**Documentation**](#documentation)

## What you can do

Expand Down Expand Up @@ -57,6 +57,24 @@ Enter a four-character **PDB ID** (for example, `1CRN`), **upload** your own PDB

The project has also been hosted at [bioit.shinyapps.io/RamplotR](https://bioit.shinyapps.io/RamplotR/), but that deployment may not reflect the latest GitHub version. Running locally is the most reliable way to use the current implementation. Public-accession retrieval requires an internet connection; uploaded coordinates and local map files can be inspected without an external folding service.

### Use the browser version

[**Open RamplotR at bikc.be/RamplotR**](https://bikc.be/RamplotR/). The public version runs through **Shinylive** on static one.com hosting. R runs in your browser through webR, so no R installation is required. A first visit downloads and starts webR and its R packages; subsequent visits can reuse cached assets. Loading large structures still depends on the visitor's device.

The browser build uses the same five reference datasets and original RDS distributions as the desktop/server app. To avoid including all 110 distributions in the initial `app.json`, reference files are hosted separately and downloaded on first use. Every file is checked against the export's MD5 manifest, and loaded references are cached within the session. The full Plotly library starts downloading when you click **Analyse** instead of delaying the initial form. A small φ/ψ favicon matches the app's teal colour scheme.

For a reproducible deployment, run this from the repository root with the [shinylive R package](https://posit-dev.github.io/r-shinylive/) installed:

```bash
Rscript scripts/export-shinylive.R bikc.be https://bikc.be/RamplotR/reference-data
```

Upload the **contents** of the generated `bikc.be/` directory to the site's document root on one.com, including `RamplotR/reference-data/` and the shared `shinylive/` assets. The export also includes optional, directory-scoped `.htaccess` files for gzip/Brotli (when available) and cautious browser caching; these do not alter the website's root configuration. one.com restricts some Apache features, so check the HTTP response headers after deployment rather than assuming that compression is active.

Ordinary PDB/mmCIF analysis does not require `xml2`. Importing official wwPDB validation XML does require it; compatibility of that optional feature should be checked in the specific exported webR build. The local/server Shiny app and the batch command remain available when a browser package is unsupported.

See the [Shinylive export and one.com deployment guide](docs/shinylive-deployment.md) for hosting checks, caching rules and troubleshooting.

### Analyse many structures

From the repository root, process a structure or a directory containing supported structure files:
Expand Down Expand Up @@ -86,6 +104,7 @@ The default RamplotR teal contour palette provides consistent, recognisable publ
- [Geometry, official wwPDB evidence, cryo-EM overlays, ensembles and batch mode](docs/structural-verification.md)
- [Independent wwPDB validation protocol and benchmark results](docs/wwpdb-validation.md) · [Results](docs/validation-results.md)
- [Performance and large-structure benchmarks](docs/benchmark-results.md) · [Scaling results](docs/scaling-results.md)
- [Shinylive browser deployment and one.com caching](docs/shinylive-deployment.md)

Developers can run the pure-R scientific regression suite from the repository root with `Rscript tests/scientific.R`. Additional tests cover structure parsing, validation imports, confidence formats, geometry, ensembles and the batch CLI. GitHub Actions also exercises the application in a real browser and runs the scientific tests on Ubuntu and Windows.

Expand Down
24 changes: 24 additions & 0 deletions config/onecom/ramplotr.htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Optional one.com/Apache settings for /RamplotR/ only.
# Keep other bikc.be applications and any root .htaccess unchanged.
# One.com may restrict modules. If a server error appears, remove this file.
#
# Compress text assets when the corresponding Apache module is available.
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/html text/css text/plain text/xml image/svg+xml application/javascript text/javascript application/json
</IfModule>
<IfModule !mod_brotli.c>
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml image/svg+xml application/javascript text/javascript application/json
</IfModule>
</IfModule>

<IfModule mod_headers.c>
# App and manifest changes must be revalidated after a deployment.
<FilesMatch "^(index\.html|app\.json)$">
Header set Cache-Control "no-cache"
</FilesMatch>
# Other local assets can be reused briefly but may change on deployment.
<FilesMatch "\.(css|js|svg|png|woff2?)$">
Header set Cache-Control "public, max-age=86400"
</FilesMatch>
</IfModule>
5 changes: 5 additions & 0 deletions config/onecom/reference-data.htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Original reference RDS distributions are already compressed on disk.
# The MD5 manifest and the RDS payload must always come from the same export.
<IfModule mod_headers.c>
Header set Cache-Control "no-cache"
</IfModule>
19 changes: 19 additions & 0 deletions config/onecom/shinylive.htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Optional one.com/Apache settings for the shared /shinylive/ runtime.
# Do not replace an existing .htaccess: merge these rules if needed.
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/html text/css text/plain text/xml image/svg+xml application/javascript text/javascript application/json application/wasm
</IfModule>
<IfModule !mod_brotli.c>
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml image/svg+xml application/javascript text/javascript application/json application/wasm
</IfModule>
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|wasm|woff2?|svg)$">
Header set Cache-Control "public, max-age=86400"
</FilesMatch>
# Package metadata may change after rebuilding and must be revalidated.
<FilesMatch "\.(json|rds)$">
Header set Cache-Control "no-cache"
</FilesMatch>
</IfModule>
150 changes: 150 additions & 0 deletions docs/shinylive-deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Thin Shinylive deployment

The normal app and batch CLI continue using all five local RDS reference
directories. A thin browser deployment keeps the same reference options,
but moves the original, unchanged RDS files out of the initial app.json.

## Build

From the repository root, install `shinylive` and run the optimized export:

```bash
Rscript scripts/export-shinylive.R bikc.be https://bikc.be/RamplotR/reference-data
```

On Windows, use `Rscript.exe` from your R installation if it is not on
`PATH`. The second argument must be the public URL of the deployed
reference-data directory. The ordinary `shinylive::export()` command
still creates a full package containing every bundled RDS file, so use the
repository script for the smaller browser deployment.

Upload the **contents** of the generated `bikc.be` directory to the site document root. This
includes `RamplotR/reference-data`, `RamplotR/app.json`, the assets in
`shinylive`, and the static HTML entry point. The data must be available
at exactly the URL passed during export. Same-origin hosting is preferred.
If serving data from another origin, configure that server's CORS policy.

`reference-index.tsv` inside each staged reference directory lists
every available group and the MD5 of its original RDS file. The browser
downloads only the files it requests and checks the downloaded bytes before
loading them. A mismatch fails closed: rebuild and redeploy the HTML app
and reference-data together. Keep the URL and deployed files in sync.

This is a **first-load** optimization, not a reduction in the number of
scientific reference datasets: users who select many datasets will download
them during their session. Ordinary Shiny and offline batch analysis still
work directly with the original checked-in local files.

## Check the export

1. Build into an empty destination or use the script to replace its app
directory.
2. Verify `RamplotR/app.json` is substantially smaller than the original
export and that five directories exist under `RamplotR/reference-data`.
3. Serve the output locally with `httpuv::runStaticServer("bikc.be")`;
for a local test, export with the correct local absolute URL, not the
production URL.
4. Check browser Network and Console tabs. Load a structure, change between
all five reference datasets, select a per-amino-acid background, and
exercise residue-aware and legacy classification.
5. Compare representative reference-grid MD5s and classification outputs
between the local app and browser app. Exported source files retain
identical bytes; independent execution in webR should still be tested.
6. Verify ordinary PNG/SVG/HTML exports and that production hosting allows
same-origin requests to `reference-data`.

## xml2

`xml2` is not needed for routine PDB/mmCIF Ramachandran analysis.
It is needed to import official wwPDB validation XML files. A local
`shinylive::export()` warning about a missing local `xml2` is not
proof that it is absent from or supported by the selected webR package
repository. Install it locally to eliminate the local missing-package
warning. Test the XML attachment feature specifically in the exported
browser app. If no compatible webR binary exists, keep that feature in
the regular Shiny version and explain its browser limitation rather than
silently disabling it.

## First-load performance

Shinylive runs webR and Shiny entirely in the browser. A cold visitor must
download and initialize webR and the required R WebAssembly packages before
interacting with the app. The reference-data split reduces `app.json`,
but does not remove the webR boot cost.

The app no longer downloads and parses the full Plotly JavaScript bundle
before displaying the input form. Selecting **Analyse** begins fetching
Plotly while R parses the selected structure. Plot rendering waits for this
download when necessary; a prediction-only PAE heatmap can also request it.
The full bundle remains intentional: RamplotR's comparison view uses
`scattergl` for large structures, and Plotly's smaller Cartesian partial
bundle does not include this trace type.

The generated Shinylive index page uses the same small SVG favicon as the
Shiny app's embedded page.

### Measure your deployment

1. Open browser Developer Tools, Network, disable cache, and reload the
published RamplotR page. Record the transferred size and time for
`app.json`, `webr` assets and the downloaded `*.wasm` / R package files.
2. Without loading a structure, verify Plotly is absent in Network. Click
**Analyse** and check that the Plotly request begins at the click.
3. Repeat with the browser cache enabled. A faster second visit points to
downloadable webR/assets and HTTP caching as the cold-start cost. Compare
with a locally hosted normal Shiny app if CPU startup remains slow.

On the static web server, enable Brotli or gzip for HTML, JSON, JavaScript
and other text assets, and provide sensible caching for the webR runtime,
WASM packages and unchanged reference files. Prefer `Cache-Control:
no-cache` for the generated `index.html` so deployments refresh.
Do not give mutable `app.json` or `reference-data` long immutable caching
unless the deployment uses a versioned URL, since a stale manifest paired
with new RDS files will correctly fail checksum verification.

## one.com hosting

one.com supports Apache `.htaccess` files but restricts some directives.
The export includes three optional, scoped configurations. They **do not**
change the website root `.htaccess` or the setup of other apps:

| Generated file | Effect |
| --- | --- |
| `RamplotR/.htaccess` | Enables Brotli or gzip for the browser app's HTML, JSON, JS and CSS if the matching Apache module is available. Revalidates `index.html` and `app.json`; caches local assets for one day. |
| `RamplotR/reference-data/.htaccess` | Revalidates the RDS files between deployments. They are already gzip-compressed R objects and should not be recompressed. |
| `shinylive/.htaccess` | Optionally compresses shared webR and WASM assets and caches static assets for one day, but revalidates metadata. An existing shared `.htaccess` is **not overwritten**. If one exists, review and merge the template under `config/onecom/` manually. |

The files are in `config/onecom/` if you need to inspect or adjust them.
One.com may not allow every `mod_brotli`, `mod_deflate` or `mod_headers`
directive. Conditional module blocks mean missing modules are skipped, but
they do not bypass one.com's hosting restrictions. If you get a 500 error
after uploading, remove the generated `.htaccess` files and ask one.com
support whether those directives are permitted on your hosting plan.
Your Shiny app does not require these rules to function.

Do not enable the WordPress-specific Performance Cache plugin as a
requirement for this static app. It is separate from ordinary HTTP caching.

### Confirm HTTP compression and caching

In a terminal, inspect response headers (use `curl.exe` on Windows if
PowerShell's `curl` alias is active):

```bash
curl -I -H "Accept-Encoding: br,gzip" https://bikc.be/RamplotR/app.json
curl -I -H "Accept-Encoding: br,gzip" https://bikc.be/RamplotR/favicon.svg
```

The `app.json` request should show `Cache-Control: no-cache` when `mod_headers`
is enabled. For text resources, `Content-Encoding: br` or `gzip` indicates
compression is active. No such header means the host is not compressing
that response or the requested file is not found; check with the browser
Network panel using the correct asset URL from the generated page.
For WASM and other webR assets, inspect the actual paths reported in the
Network panel rather than assuming their locations.

To assess a first visit, disable browser cache and record network transfers
separately from webR startup and R-package initialization. Then reload
with caching enabled. A long download points to hosting and network costs;
slow initialization after all downloads points to webR/package startup
or device CPU. This is useful before making further application changes.
Loading
Loading