Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
ab56cc0
docs: record upgrade scope and scientific validation plan
CedricHermansBIT Sep 28, 2026
5b76d9e
feat: validate symmetric matrices and support tabular and RDS imports
CedricHermansBIT Sep 28, 2026
93f9783
feat: shared clustering, sampled pairwise comparisons and cluster con…
CedricHermansBIT Sep 28, 2026
48efd93
feat: add scalable raster previews and compact Plotly matrix renderer
CedricHermansBIT Sep 28, 2026
c6f8702
refactor: load shared MAniR modules and declare reproducible runtime …
CedricHermansBIT Sep 28, 2026
34b4068
feat: replace legacy interface with scalable comparison and research …
CedricHermansBIT Sep 28, 2026
9e8793c
feat: lazy matrix views, scoped session analysis, scientific comparis…
CedricHermansBIT Sep 28, 2026
8297711
fix: detect upload formats from original filenames
CedricHermansBIT Sep 28, 2026
fe3c329
fix: combined view dimensions and categorical raster annotations
CedricHermansBIT Sep 28, 2026
024e386
fix: pass original Shiny upload formats to all readers
CedricHermansBIT Sep 28, 2026
356225d
fix: use original filename to detect XLSX, CSV, TSV and RDS
CedricHermansBIT Sep 28, 2026
61d1972
build: declare MAniR runtime and optional accelerated dependencies
CedricHermansBIT Sep 28, 2026
32c5280
test: cover matrix validation, exact values, clustering, Mantel, samp…
CedricHermansBIT Sep 28, 2026
0081051
bench: add repeatable staged scalability and memory benchmark harness
CedricHermansBIT Sep 28, 2026
a82c2f7
fix: read compressed text matrices without optional R.utils
CedricHermansBIT Sep 28, 2026
f2e4736
fix: close PNG device safely after plotting
CedricHermansBIT Sep 28, 2026
9af50c0
ci: add Linux and Windows regression checks and benchmark smoke
CedricHermansBIT Sep 28, 2026
59a7168
feat: add large-matrix region navigation controls
CedricHermansBIT Sep 28, 2026
9b84ff9
perf: render viewport slices without allocating complete large combin…
CedricHermansBIT Sep 28, 2026
d708648
docs: document upgraded MAniR workflow, large-matrix limits and scien…
CedricHermansBIT Sep 28, 2026
aebe178
feat: calculate directional adjusted Wallace cluster agreement
CedricHermansBIT Sep 28, 2026
2ec74c3
feat: report directional Wallace concordance for MALDI and genomic cl…
CedricHermansBIT Sep 28, 2026
26ac9be
test: assert directional Wallace values for equivalent clusterings
CedricHermansBIT Sep 28, 2026
7bd7a73
perf: validate numeric matrices in blocks without multiple full-matri…
CedricHermansBIT Sep 28, 2026
47f0a94
perf: reuse aligned source matrices and validate uploaded data only once
CedricHermansBIT Sep 28, 2026
b201d68
perf: reuse source matrices when matched sample ordering already agrees
CedricHermansBIT Sep 28, 2026
679b940
fix: center difference-map colors on zero without changing data
CedricHermansBIT Sep 28, 2026
e19d4d6
perf: bound combined PNG export and use zero-centered difference maps
CedricHermansBIT Sep 28, 2026
b3b79eb
test: ensure negative and positive differences have symmetric display…
CedricHermansBIT Sep 28, 2026
1ca9814
fix: anchor Plotly cell annotations to axes and escape uploaded sampl…
CedricHermansBIT Sep 28, 2026
e679294
test: compare both shipped XLSX matrices and preserve historical spli…
CedricHermansBIT Sep 28, 2026
0390341
feat: add publication-ready vector PDF/SVG export for moderate matrix…
CedricHermansBIT Sep 28, 2026
26949bb
feat: add compact interactive metadata tracks and consistent export s…
CedricHermansBIT Sep 28, 2026
71f2889
feat: add HTML/PDF/SVG and exact difference exports with metadata-awa…
CedricHermansBIT Sep 28, 2026
b415631
feat: expose standalone HTML, PDF, SVG and difference CSV exports
CedricHermansBIT Sep 28, 2026
ce4e50e
test: validate publication PDF generation
CedricHermansBIT Sep 28, 2026
b720534
fix: match historical complete-linkage clustering by default
CedricHermansBIT Sep 28, 2026
cbba48b
feat: persist selected clustering linkage throughout analysis and exp…
CedricHermansBIT Sep 28, 2026
ca888f4
feat: expose complete, average, single and Ward cluster linkage
CedricHermansBIT Sep 28, 2026
a8939c8
docs: explain historical clustering default and configurable linkage
CedricHermansBIT Sep 28, 2026
c3e9936
fix: preserve sample IDs with leading zeroes in delimited imports
CedricHermansBIT Sep 28, 2026
ae492df
test: guard numeric-looking sample IDs against lossy CSV parsing
CedricHermansBIT Sep 28, 2026
d705414
fix: support zero-diagonal distance matrices in log1p visualization
CedricHermansBIT Sep 28, 2026
175fd07
test: verify zero-safe logarithmic scaling rejects negative values
CedricHermansBIT Sep 28, 2026
60432b5
docs: specify log1p behavior for zero-containing distance matrices
CedricHermansBIT Sep 28, 2026
cb0a672
bench: add reproducible original-versus-upgrade plotting comparison
CedricHermansBIT Sep 28, 2026
a4ee690
feat: make large-data upload size configurable
CedricHermansBIT Sep 28, 2026
6119b90
docs: guide large dataset uploads toward CSV and RDS
CedricHermansBIT Sep 28, 2026
abe374a
docs: cover configurable upload limits and new export and baseline tools
CedricHermansBIT Sep 28, 2026
02f74d3
perf: avoid multiple dense copies of all-numeric fread and Excel tables
CedricHermansBIT Sep 28, 2026
33cd5c7
test: validate numeric data-frame fast path without value changes
CedricHermansBIT Sep 28, 2026
83988d4
test: add headless Shiny upload and reactive state regression
CedricHermansBIT Sep 28, 2026
b686d3c
ci: exercise uploaded CSV data and Shiny reactive state on both platf…
CedricHermansBIT Sep 28, 2026
5c3d95f
docs: record implemented changes and outstanding release validation
CedricHermansBIT Sep 28, 2026
fc76107
chore: ignore generated local benchmark result files
CedricHermansBIT Sep 28, 2026
3410f75
docs: avoid placeholder author contact in prerelease metadata
CedricHermansBIT Sep 28, 2026
4be4671
chore: remove superseded experimental Plotly script
CedricHermansBIT Sep 28, 2026
4664347
fix: initialize click-triggered loading reliably in Shiny and integra…
CedricHermansBIT Sep 28, 2026
f6a4cf2
build: declare HTML widget and safe-label dependencies explicitly
CedricHermansBIT Sep 28, 2026
ad15c10
build: validate direct interactive export dependencies at startup
CedricHermansBIT Sep 28, 2026
f35582b
docs: include HTML dependencies in reproducible installation
CedricHermansBIT Sep 28, 2026
98bed21
fix: use rep() for repeated PDF/SVG vector column indices
CedricHermansBIT Sep 28, 2026
3243e5f
ci: prevent shell expansion from corrupting Shiny startup assertion
CedricHermansBIT Sep 28, 2026
693ab49
fix: correct multiline if/else syntax in benchmark runner
CedricHermansBIT Sep 28, 2026
5ba6ea6
feat: add synthetic teaching example (examples/ani.csv)
CedricHermansBIT Sep 28, 2026
0cb86d8
feat: add synthetic teaching example (examples/maldi.csv)
CedricHermansBIT Sep 28, 2026
040f15c
feat: add synthetic teaching example (examples/metadata.csv)
CedricHermansBIT Sep 28, 2026
7ee5572
feat: add synthetic teaching example (R/example_data.R)
CedricHermansBIT Sep 28, 2026
e4f774b
feat: load bundled synthetic example helpers on app startup
CedricHermansBIT Sep 28, 2026
f3b5f74
feat: add one-click synthetic example and XLSX download to sidebar
CedricHermansBIT Sep 28, 2026
a80e130
feat: load example without uploading and export workbook with metadata
CedricHermansBIT Sep 28, 2026
a268676
test: verify synthetic example matrices, metadata and downloaded XLSX…
CedricHermansBIT Sep 28, 2026
4d8297c
test: check one-click example loading and switching back to uploads
CedricHermansBIT Sep 28, 2026
d6b0678
docs: explain synthetic teaching data and example download
CedricHermansBIT Sep 28, 2026
792d42a
docs: record synthetic example and added validation coverage
CedricHermansBIT Sep 28, 2026
74b83cf
data: vary synthetic between-group MALDI similarities across isolate …
CedricHermansBIT Sep 28, 2026
8b74919
feat: add scale-independent rank-gap exploration for pairwise compari…
CedricHermansBIT Sep 28, 2026
be37a43
fix: put categorical metadata strip above Plotly heatmaps and move le…
CedricHermansBIT Sep 28, 2026
f68b5ea
fix: attach relative color legend to Plotly heatmap trace
CedricHermansBIT Sep 28, 2026
dfa4dec
docs(ui): add contextual interpretation guides for every analysis tab…
CedricHermansBIT Sep 28, 2026
34c41ca
feat: add dynamic plot explanations, rank-gap exploration, cluster ov…
CedricHermansBIT Sep 28, 2026
6e2446c
fix: reset example controls to the measurement types and clustering a…
CedricHermansBIT Sep 28, 2026
6d07691
test: cover interpretation UI, scale-independent rank gaps and nonove…
CedricHermansBIT Sep 28, 2026
b98d0d9
test: block mixed-unit difference and verify compatible uploaded pair…
CedricHermansBIT Sep 28, 2026
4105ba4
docs: provide research-oriented guide to every MAniR visualization an…
CedricHermansBIT Sep 28, 2026
a8376d7
docs: link the in-app interpretation guidance to a complete reference
CedricHermansBIT Sep 28, 2026
899c9b6
test: inspect metadata strip after Plotly constructs the full widget …
CedricHermansBIT Sep 28, 2026
fe2cf0b
fix: omit uninterpretable raw differences from cross-method CSV exports
CedricHermansBIT Sep 28, 2026
230a4e7
feat: distinguish active dataset settings from changed sidebar controls
CedricHermansBIT Sep 28, 2026
48917a9
docs(ui): clarify when type, matching and linkage changes take effect
CedricHermansBIT Sep 28, 2026
0ebef1a
test: ensure mixed-unit pair exports cannot contain raw difference co…
CedricHermansBIT Sep 28, 2026
27c442c
docs: explain why pairwise CSV omits raw differences on mixed-unit data
CedricHermansBIT Sep 28, 2026
c02dd20
style: introduce fixed-height split workspace and accessible persiste…
CedricHermansBIT Sep 28, 2026
565a494
ui: keep results visible with independent scrolling, compact sidebar …
CedricHermansBIT Sep 28, 2026
bf6c50f
ui: size all matrix plots to available result viewport and show loade…
CedricHermansBIT Sep 28, 2026
c32097c
test: confirm workspace controls remain unique and independently scro…
CedricHermansBIT Sep 28, 2026
1a9a0b7
docs: explain persistent display toolbar and independent result scrol…
CedricHermansBIT Sep 28, 2026
11ef141
docs: record desktop and responsive layout improvements
CedricHermansBIT Sep 28, 2026
ee7f37c
test: inspect hoisted Shiny stylesheet in HTML head and report missin…
CedricHermansBIT Sep 28, 2026
159efa7
ui: group five matrix views under Heatmaps and show only controls rel…
CedricHermansBIT Sep 28, 2026
9f7c798
fix: close grouped heatmap tabs without a trailing empty argument
CedricHermansBIT Sep 28, 2026
0c18bf5
style: add compact subnavigation for five heatmaps and context-sensit…
CedricHermansBIT Sep 28, 2026
cc733d0
fix: avoid empty trailing arguments in context-specific Shiny toolbar…
CedricHermansBIT Sep 28, 2026
47867b2
ui: jump directly to the first heatmap after an example or file load
CedricHermansBIT Sep 28, 2026
98e9a57
test: verify nested matrix navigation in responsive workspace
CedricHermansBIT Sep 28, 2026
37a464b
docs: describe grouped heatmap navigation and context-specific controls
CedricHermansBIT Sep 28, 2026
d3f5b67
docs: record grouped results tabs and automatic first plot
CedricHermansBIT Sep 28, 2026
2892278
docs: align example, zoom and pair-limit instructions with compact in…
CedricHermansBIT Sep 28, 2026
a4c4c7e
feat: add exact pair inspection, metadata comparisons and reproducibl…
CedricHermansBIT Sep 29, 2026
e850064
ui: create question-led analysis start screen with five concrete rese…
CedricHermansBIT Sep 29, 2026
38b3dee
build: load task-based research UI and pure research summary modules
CedricHermansBIT Sep 29, 2026
b6f33f2
ui: add researcher question cards, exact pair inspector, group summar…
CedricHermansBIT Sep 29, 2026
333fef7
feat: compute bounded within/between-group values for separate descri…
CedricHermansBIT Sep 29, 2026
7239d66
feat: guide researchers from a dataset readiness screen to question-s…
CedricHermansBIT Sep 29, 2026
77c9651
feat: inspect exact isolate pairs, highlight chosen pairs and compare…
CedricHermansBIT Sep 29, 2026
74b89d5
ui: show group distribution plots and optional cluster calculations i…
CedricHermansBIT Sep 29, 2026
6a3cd89
feat: download editable analysis notes with source values, assumption…
CedricHermansBIT Sep 29, 2026
27b3931
style: make research questions, exact pair drilldown and data readine…
CedricHermansBIT Sep 29, 2026
9c723e0
test: cover research questions, exact pair values, grouped comparison…
CedricHermansBIT Sep 29, 2026
7b81e21
test: exercise question navigation, missing-data guidance and pair/gr…
CedricHermansBIT Sep 29, 2026
c2c32dc
ui: add interpretation cues and direct next-step action to numeric co…
CedricHermansBIT Sep 29, 2026
5dbfc72
feat: give researchers numerical context and one-click follow-up from…
CedricHermansBIT Sep 29, 2026
5dc0b68
docs: provide a question-led MAniR research walkthrough with real nex…
CedricHermansBIT Sep 29, 2026
76adb28
docs: explain researcher-first workflow and editable notes
CedricHermansBIT Sep 29, 2026
be51559
docs: record question-based analysis changes and validation scope
CedricHermansBIT Sep 29, 2026
cd76d2e
fix: clearly uppercase synthetic warning in exported research notes
CedricHermansBIT Sep 29, 2026
9cf1fae
docs: capture real Shiny screenshots with synthetic example using hea…
CedricHermansBIT Sep 29, 2026
5c12535
ci(docs): run real Shiny and capture reproducible example screenshots…
CedricHermansBIT Sep 29, 2026
682973b
docs: add real application screenshot gallery to README
CedricHermansBIT Sep 29, 2026
4b2bf85
docs: illustrate researcher workflows with actual Shiny captures
CedricHermansBIT Sep 29, 2026
f039f5b
docs: pair interpretation guidance with actual app screenshots
CedricHermansBIT Sep 29, 2026
ee22e3f
fix(docs): target first of Plotly's multiple SVG layers in real scree…
CedricHermansBIT Sep 29, 2026
0fdd220
ci(docs): serialize real screenshot captures and rebase image commits…
CedricHermansBIT Sep 29, 2026
229ad90
docs: update real Shiny screenshots from synthetic example
github-actions[bot] 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
106 changes: 106 additions & 0 deletions .github/workflows/docs-screenshots.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: Capture real MAniR documentation screenshots

on:
push:
branches:
- upgrade/scalable-analysis
paths:
- scripts/capture-docs-screenshots.js
- .github/workflows/docs-screenshots.yml
- app.R
- ui.R
- server.R
- R/**
- www/**
- examples/**
workflow_dispatch:

concurrency:
group: manir-real-screenshots-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: write

jobs:
capture-screens:
name: Real Shiny screenshot capture
if: github.ref == 'refs/heads/upgrade/scalable-analysis'
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v4
with:
persist-credentials: true

- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
cache-version: 1

- uses: actions/setup-node@v4
with:
node-version: '22'

- name: Install browser driver
run: |
npm install --no-save --no-package-lock playwright@1.55.1
if command -v google-chrome >/dev/null 2>&1; then
echo "CHROME_BIN=$(command -v google-chrome)" >> "$GITHUB_ENV"
elif command -v chromium >/dev/null 2>&1; then
echo "CHROME_BIN=$(command -v chromium)" >> "$GITHUB_ENV"
else
npx playwright install --with-deps chromium
fi

- name: Start real MAniR Shiny app
run: |
nohup Rscript -e "shiny::runApp('.', host='127.0.0.1', port=3838, launch.browser=FALSE)" \
> "$RUNNER_TEMP/manir-shiny.log" 2>&1 &
echo $! > "$RUNNER_TEMP/manir-shiny.pid"
for attempt in $(seq 1 90); do
if curl --silent --fail --output /dev/null http://127.0.0.1:3838; then
echo "MAniR is running"; break
fi
sleep 2
done
curl --silent --fail http://127.0.0.1:3838 >/dev/null || {
cat "$RUNNER_TEMP/manir-shiny.log"
exit 1
}

- name: Capture actual example-data screens
run: node scripts/capture-docs-screenshots.js
env:
MANIR_SCREENSHOT_URL: http://127.0.0.1:3838

- name: Upload screenshots for review
if: always()
uses: actions/upload-artifact@v4
with:
name: actual-manir-screenshots
path: docs/images/*.png
if-no-files-found: warn

- name: Store actual screenshots on development branch
if: success()
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add docs/images/*.png
if git diff --cached --quiet; then
echo "Documentation screenshots unchanged"
else
git commit -m "docs: update real Shiny screenshots from synthetic example"
# The docs may have changed while this browser job was running.
git fetch origin upgrade/scalable-analysis
git rebase origin/upgrade/scalable-analysis
git push origin HEAD:upgrade/scalable-analysis
fi

- name: Show Shiny server logs on failure
if: failure()
run: cat "$RUNNER_TEMP/manir-shiny.log" || true
53 changes: 53 additions & 0 deletions .github/workflows/r-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: MAniR tests and benchmark smoke

on:
push:
branches:
- main
- upgrade/**
pull_request:

jobs:
r-tests:
name: R regression suite
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v2
with:
cache-version: 1
extra-packages: |
any::data.table
any::fastcluster
- name: Test numerical correctness, files and plot export
run: Rscript tests/run_tests.R
- name: Parse and load the Shiny application
run: Rscript -e "stopifnot(inherits(getElement(source('app.R'), 'value'), 'shiny.appobj'))"
- name: Shiny upload smoke test
run: Rscript tests/test_shiny.R
- name: Benchmark smoke
run: Rscript benchmarks/benchmark.R --smoke
- name: Upload benchmark smoke results
uses: actions/upload-artifact@v4
with:
name: MAniR-benchmark-smoke
path: benchmarks/results/*-smoke.csv

windows-tests:
name: Windows regression suite
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: r-lib/actions/setup-r@v2
- uses: r-lib/actions/setup-r-dependencies@v2
with:
cache-version: 1
- name: Regression tests
run: Rscript tests/run_tests.R
- name: Parse and load application
run: Rscript -e "stopifnot(inherits(getElement(source('app.R'), 'value'), 'shiny.appobj'))"
- name: Shiny upload smoke test
run: Rscript tests/test_shiny.R
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ vignettes/*.pdf
# Shinyapps.io information
rsconnect/
.lintr

# Generated benchmark measurements
benchmarks/results/
22 changes: 22 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Package: MAniR
Type: Project
Title: Interactive Pairwise Matrix Comparison for MALDI-TOF and Genomic Analysis
Version: 3.0.0
Authors@R: person("BiKC", role = "aut")
Description: Shiny application to compare similarity, correlation and distance
matrices using scalable visualization, isolate-level metadata and
pairwise analytical summaries. The application is run directly from app.R.
License: GPL-3
Encoding: UTF-8
Depends:
R (>= 4.2.0)
Imports:
shiny,
plotly,
htmltools,
htmlwidgets,
openxlsx,
RColorBrewer
Suggests:
data.table,
fastcluster
55 changes: 55 additions & 0 deletions R/example_data.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Built-in teaching example. All values are synthetic, not measured isolates.
manir_example_files <- function(root = ".") {
c(ANI = file.path(root, "examples", "ani.csv"),
MALDI = file.path(root, "examples", "maldi.csv"),
metadata = file.path(root, "examples", "metadata.csv"))
}

manir_load_example <- function(root = ".") {
paths <- manir_example_files(root)
missing <- paths[!file.exists(paths)]
if (length(missing)) {
stop("Built-in example files are missing: ",
paste(basename(missing), collapse = ", "))
}
ani <- read_matrix_input(paths[["ANI"]], kind = "similarity")
maldi <- read_matrix_input(paths[["MALDI"]], kind = "similarity")
matched <- match_matrices(ani, maldi)
metadata <- read_metadata_input(paths[["metadata"]])
if (!setequal(rownames(metadata), matched$ids)) {
stop("Built-in example metadata does not match the matrix identifiers.")
}
list(first = ani, second = maldi,
metadata = align_metadata(metadata, matched$ids))
}

manir_write_example_workbook <- function(destination, root = ".") {
example <- manir_load_example(root)
wb <- openxlsx::createWorkbook()
for (name in c("ANI", "MALDI")) {
m <- if (name == "ANI") example$first else example$second
openxlsx::addWorksheet(wb, name)
openxlsx::writeData(wb, name,
data.frame(sample_id = rownames(m), m, check.names = FALSE))
openxlsx::freezePane(wb, name, firstRow = TRUE, firstCol = TRUE)
}
openxlsx::addWorksheet(wb, "metadata")
openxlsx::writeData(wb, "metadata",
data.frame(sample_id = rownames(example$metadata),
example$metadata, check.names = FALSE))
openxlsx::freezePane(wb, "metadata", firstRow = TRUE, firstCol = TRUE)
openxlsx::addWorksheet(wb, "README")
openxlsx::writeData(wb, "README", data.frame(
Notes = c(
"MAniR built-in example. All data are SYNTHETIC and intended for demonstration only.",
"ANI: fictional percentage-like genomic similarity, diagonal = 100.",
"MALDI: fictional spectral similarity on a 0-to-1 scale, diagonal = 1.",
"The two matrices use different scales. Do not enable comparable-units metrics.",
"Metadata includes three fictional groups, specimen source and measurement batch.",
"Two pairs deliberately have discordant MALDI/ANI relationships for exploration.",
"Select ANI as matrix 1, MALDI as matrix 2, and metadata as the annotation sheet."
), check.names = FALSE))
openxlsx::setColWidths(wb, "README", cols = 1, widths = 100)
openxlsx::saveWorkbook(wb, destination, overwrite = TRUE)
invisible(destination)
}
Loading