Skip to content

ManualEN

FenTouXunGui edited this page Oct 12, 2026 · 3 revisions

ShinyApps User Manual

English | 中文

For two audiences: (A) App developers — how to bring your Shiny app into ShinyApps; (B) ShinyApps maintainers — how the system runs, what each workflow does, and how to release and troubleshoot.

Compiled from the 2026-09/10 development and acceptance records and verified against the current repositories. When you publish this on the GitHub Wiki, split it into the pages suggested below.


Suggested Wiki page split

Wiki page Content Section(s) below
Home Overview, architecture, navigation 0, 1
User Guide Install, launcher, install/run/update/uninstall, data locations 4
Developer Guide: Add your app Bundle build, catalog registration, icons 2
Maintainer Guide: Repo & workflows Repo layout, each workflow's trigger/output/role 3
Release Process Ship a shell, ship an app, ship icons, rollback 5
FAQ / Troubleshooting Known issues and fixes 6
Known Limits & Roadmap Open issues, suggested additions 7
Contributing How to submit a PR upstream to shinyelectron 8

0. What this is

ShinyApps is an Electron desktop shell/launcher that aggregates several R/Shiny apps into a single launcher page where users can install, run, update, and uninstall each app on demand. The current list of integrated apps — with description, version, supported platforms, license, and availability — is in the live app catalog (https://fentouxungui.github.io/ShinyApps/), regenerated automatically on every publish; the machine-readable manifest is catalog.json.

It is built on a fork of the open-source shinyelectron: fentouxungui/shinyelectron@multi-app-store.

0.1 Three-layer architecture

┌────────────────────────────────────────────────────────────┐
│ Tier 0 · Runtime base (installed once with the shell, stays)│
│   Electron shell + portable R 4.6.1 + minimal deps (shiny)  │
├────────────────────────────────────────────────────────────┤
│ Tier 1 · Shared dependency pool  <userData>/lib            │
│   R packages needed by >= 2 installed apps; ref-counted    │
├────────────────────────────────────────────────────────────┤
│ Tier 2 · App-private deps  <userData>/private/<app-id>     │
│   Packages used by exactly 1 app, or version conflicts     │
└────────────────────────────────────────────────────────────┘
  • Only Tier 0 is hand-authored; Tier 1 / Tier 2 are computed from each bundle's dependency list plus reference counting.
  • The runtime R version is pinned to 4.6.1: the shell runtime and every app bundle must be built against the same R (binary ABI must match).

0.2 Two update channels (do not confuse them)

Channel Target Mechanism
Shell update The Electron shell itself electron-updater + latest.yml / latest-mac.yml on the Release; silent overwrite install on quit
App update A single app The client fetches catalog.json, compares semver, and shows an Update button on the card

0.3 Repositories

Repo Role
fentouxungui/ShinyApps Shell config + catalog + schemas + icons + build script + workflows
fentouxungui/shinyelectron (multi-app-store branch) Electron main process / launcher / store engine / R backend
fentouxungui/SeuratExplorer App: source + standalone installer + app-layer bundle CI
fentouxungui/scConvertShiny App: source + standalone installer + app-layer bundle CI
mianaz/scConvert Third-party R package (a scConvertShiny dependency, built via R CMD build)

Note: a "standalone installer" packages one app into a complete desktop program (shell + runtime + that app baked in). It is a parallel product, independent of joining the ShinyApps store.


1. Quick start (user)

  1. Download shinyapps-Setup-<ver>-x64.exe (Windows) or .dmg (macOS) from ShinyApps Releases and install it.
  2. Open ShinyApps → the launcher appears (a grid of cards).
  3. Each card has state buttons: Install → progress → Run / Uninstall; Update appears when a newer version exists.
  4. After running an app, go back with Apps → Back to Launcher (or Ctrl+L); it first asks "unsaved analysis will be lost".
  5. Only one app runs at a time; returning to the launcher kills that app's whole R process tree.

1.1 Data and log locations

Windows

%APPDATA%\shinyapps\
  ├─ state.json      # installed apps and shared-library reference counts
  ├─ apps\<id>\<ver>\# unpacked app bundle (app/ + rlib/)
  ├─ lib\            # shared R library (Tier 1)
  ├─ private\<id>\   # app-private library (Tier 2)
  ├─ store-cache\    # download cache (.zip)
  └─ logs\           # app-*.log (shop logs are prefixed with [store])

Install dir: %LOCALAPPDATA%\Programs\shinyapps\

Icon caching: store icons come from remote URLs in catalog.json; if loading fails, the card falls back to the app's initial.


2. Developer guide: bring your app into ShinyApps

2.1 Two integration routes

Route Description Best for
A. Store / on-demand install (recommended) Your repo publishes a prebuilt bundle to its own Release; you register it in ShinyApps' catalog/apps.yml. Users install on demand. Long-term apps; add apps without rebuilding the shell
B. Bake-in Put app.R + dependency tarballs into the ShinyApps repo and rebuild the shell. The installer ships the app; no download. Offline, usable right after install, few stable apps

Route A is described first.

2.2 Prerequisites: the contract your app must satisfy

  1. The entry point must return a shinyApp object and must not call runApp() internally. The Electron shell starts the server itself; an app calling runApp() again conflicts. Export a function that only return(shinyApp(ui, server)), like sc_app():

    # scConvertShiny's exported entry
    library(scConvertShiny)
    options(shiny.launch.browser = FALSE, shiny.deprecation.messages = FALSE)
    sc_app()
  2. It must build as a standard R source package (R CMD build). The bundle CI uses it plus Imports/Depends/LinkingTo in DESCRIPTION to compute the dependency closure.

  3. Versions must line up: the Release tag (e.g. v0.1.10) minus v must equal Version in DESCRIPTION. Otherwise the source tarball name won't match and a broken bundle is produced (the CI now has a "source tarball missing -> stop" guard).

  4. R version pinned to 4.6.1 (matching the shell runtime).

2.3 Route A, step by step

Step 1: prepare the entry code

Decide on an entry in your app repo (the CI writes it to app/app.R inside the bundle). SeuratExplorer's example:

library(SeuratExplorer)
options(shiny.launch.browser = FALSE, shiny.deprecation.messages = FALSE)
launchSeuratExplorer(verbose = FALSE,
  ReductionKeyWords = c("umap","tsne","pca"),
  SplitOptionMaxLevel = 12, MaxInputFileSize = 30 * 1024^3)

If your app is an R package whose entry function does not call runApp(), just use library(<pkg>); <entry>().

Step 2: copy the bundle workflow into your repo

Place this workflow in your app repo under .github/workflows/ (the convention is build-app-bundle.yml; the live SeuratExplorer/scConvertShiny files use prefixed names). It will:

  • build on windows-latest / macos-14 with R 4.6.1;
  • run R CMD build;
  • compute the full transitive dependency closure (seed <- unique(c(loc_names, declared))), installing CRAN/Bioc deps as binary and local/source packages from source, all into bundle/rlib/;
  • write bundle/manifest.json and zip app/ + rlib/ + manifest.json;
  • assert the zip contains rlib/ (otherwise fail);
  • compute sha256 and upload <id>-<version>-<platform>.zip as a Release asset.

Edit these env vars at the top of the workflow:

env:
  APP_ID: my-app              # lowercase, [a-z0-9-], must match the id in catalog/apps.yml
  APP_NAME: "My App"
  APP_AUTHOR: "Your Name"
  APP_DESC: "..."
  APP_HOMEPAGE: "https://github.com/you/my-app"
  APP_LICENSE: "MIT"
  R_VERSION: "4.6.1"          # do not change

And put your own entry code into the "Assemble app + manifest.json" step (replace the contents of writeLines(c(...))).

Asset naming contract (mandatory):

<APP_ID>-<VERSION>-<PLATFORM>.zip
e.g. seurat-explorer-0.1.10-win-x64.zip
     scconvert-0.1.10-mac-arm64.zip
PLATFORM in {win-x64, win-arm64, mac-x64, mac-arm64, linux-x64}

publish-catalog.yml relies on exactly this pattern (<id>-<version>-<platform>.zip) to derive url / size / sha256. A wrong name means the artifact never appears in the catalog.

Step 3: publish a Release to produce the bundle

Two trigger options:

  • Publish a Release on GitHub (triggers release: published); or
  • Manually run Build app bundle in Actions and fill in the tag (workflow_dispatch.inputs.tag).

Result: two zips appear as Release assets (win-x64 / mac-arm64), each tens to hundreds of MB (containing rlib/).

Pushing a tag alone does not auto-trigger the bundle workflow (only the standalone build-desktop.yml runs, and a Release created with GITHUB_TOKEN does not cascade). To change behavior, use release: published (as the live setup does) or workflow_run.

Step 4: register the app in ShinyApps

Edit ShinyApps/catalog/apps.yml and append an entry:

apps:
  - id: my-app
    repo: your-name/my-app                 # used by gh api to fetch the latest release
    name: My App
    author: "Your Name"
    description: "..."
    homepage: "https://github.com/your-name/my-app"   # About > Visit Website; also the card name link target
    docs: "https://your-name.github.io/my-app/"        # optional; Help > Documentation
    email: "you@example.org"                           # optional; About > Email
    copyright: "(c) 2026 Your Name"                    # optional; About text
    license: "MIT"
    icon: "https://fentouxungui.github.io/ShinyApps/icons/my-app.png"
    r_version: "4.6.1"
  • id must equal the bundle's APP_ID;
  • repo must let gh api repos/<repo>/releases/latest reach the latest Release;
  • icon is an absolute URL (in store mode the icon comes from the catalog, not from _shinyelectron.yml).
  • homepage / docs / email / author / copyright fill the running app's Help / About; unset values fall back to the shell's own, or the item is omitted.

Step 5: refresh the catalog

Pushing catalog/apps.yml to main triggers Publish catalog; you can also run it manually. It publishes catalog.json to gh-pages:

https://fentouxungui.github.io/ShinyApps/catalog.json

Step 6: verify on the client

Restart the ShinyApps shell → the new card appears → Install → progress → Run.

Recommended: add a repository_dispatch step (types: [bundle-updated]) at the end of your app's bundle workflow to notify ShinyApps to refresh the catalog (publish-catalog.yml already listens for that event). Otherwise you must wait for the 10-minute schedule or run it manually after each new bundle.

2.4 Icons

  • Card icon (per app): put <id>.png (512x512, transparent) into ShinyApps/icons/ and set https://fentouxungui.github.io/ShinyApps/icons/<id>.png in catalog/apps.yml. publish-catalog.yml syncs icons/ to gh-pages so the URL resolves.
  • Shell program icon (.exe/.dmg icon): icons/shinyapps.png (>=1024), icons/ico/shinyapps.ico, icons/mac/shinyapps.icns; build-suite.R selects per platform and passes it via export(icon=).
  • Note: apps[].icon in _shinyelectron.yml has no effect in store mode (icons come from the catalog). Do not rely on it.
  • A failed icon load falls back to the initial, without error.

2.5 Route B: bake-in

To ship an app inside the ShinyApps installer (no store):

  1. Entry: create ShinyApps/apps/<id>/app.R (same entry code as above).
  2. Dependency source packages: put the app and its local deps' *.tar.gz into ShinyApps/dependency/.
  3. Configure ShinyApps/_shinyelectron.yml:
    • list the tarballs in dependencies.r.local_packages (repo-relative, e.g. dependency/SeuratExplorer_0.1.9.tar.gz);
    • list the full dependency closure in dependencies.extra_packages / dependencies.r.packages (renv only scans app.R source and will miss the package's own Imports, so list them explicitly);
    • add an item to the apps: array as { id, name, description, path: ./apps/<id> }.
  4. Rebuild the shell: push a v* tag (or manually run Build ShinyApps desktop with a tag).

Trade-offs: offline-capable with no install step; but a larger installer, and every add/change of an app means re-releasing the shell (updating an app equals updating the whole shell).


3. Maintainer guide: repo layout and workflows

3.1 ShinyApps repo layout (current)

ShinyApps/
├─ _shinyelectron.yml              # shell config (app metadata, runtime strategy, deps, apps[], updates)
├─ build-suite.R                   # build script: auto-set version from tag -> shinyelectron::export()
├─ shared-library-policy.md        # shared-library & compatibility contract (tiers, ref-counting)
├─ apps/
│  ├─ seurat-explorer/app.R        # bake-in entry (Route B)
│  └─ scconvert/app.R
├─ dependency/                     # R source tarballs for bake-in
│  ├─ SeuratExplorer_0.1.9.tar.gz  ├─ presto-1.1.0.tar.gz
│  ├─ scConvertShiny_0.1.1.tar.gz  └─ scConvert-0.2.1.tar.gz
├─ catalog/
│  └─ apps.yml                     # the only hand-maintained app registry
├─ schemas/
│  ├─ catalog.schema.json          # server catalog schema
│  ├─ manifest.schema.json         # bundle manifest.json schema
│  ├─ state.schema.json            # local state.json schema
│  └─ catalog.json                 # example
├─ icons/
│  ├─ seurat-explorer.png  scconvert.png
│  ├─ shinyapps.png  shinyapps-512.png
│  ├─ ico/shinyapps.ico            # Windows program icon
│  └─ mac/shinyapps.icns           # macOS program icon
└─ .github/workflows/
   ├─ build-desktop.yml            # build shell installers + release
   └─ publish-catalog.yml          # generate catalog.json + publish icons to gh-pages

gh-pages branch (publishing output, maintained automatically by workflows):

gh-pages/
├─ catalog.json
└─ icons/<id>.png

3.2 Workflow summary

Workflow Repo Triggers Output
Build ShinyApps desktop (build-desktop.yml) ShinyApps push tag v*; manual (input tag) shinyapps-Setup-<ver>-x64.exe/.dmg + latest*.yml + .blockmap as Release assets
Publish catalog (publish-catalog.yml) ShinyApps cron every 10 min; manual; repository_dispatch: [bundle-updated]; push main (changing catalog/apps.yml or icons/**) catalog.json + icons/ on gh-pages
Build app bundle (*-build-app-bundle.yml) Each app repo release: published; manual (input tag) <id>-<ver>-<platform>.zip (app/+rlib/+manifest.json) as that app's Release asset
standalone installer (build-desktop.yml) Each app repo push tag v* Independent full installer for that app (unrelated to the ShinyApps store)

3.3 Workflow details

3.3.1 build-desktop.yml (ShinyApps)

  • Triggers: push: tags: ['v*']; manual workflow_dispatch (input tag).
  • Jobs:
    • build (matrix: windows-latest / macos-latest):
      1. checkout → r-lib/actions/setup-r (use-public-rspm) → setup-node (node 24);
      2. install fentouxungui/shinyelectron@multi-app-store + rlang/withr/renv via pak;
      3. macOS additionally brew install hdf5 pkg-config and exports PKG_CONFIG_PATH/HDF5_* (for scConvert's configure);
      4. APP_VERSION=${{ inputs.tag || github.ref_name }}, then source("build-suite.R");
      5. upload build/electron-app/dist/*.exe|.dmg|.blockmap|latest*.yml.
    • release: condition release || tag v* || inputs.tag; download artifacts → softprops/action-gh-release (generate_release_notes: true).
  • Version: do not hand-edit _shinyelectron.yml — build-suite.R takes APP_VERSION, strips v, patches only app.version, then export(). Locally without APP_VERSION, it stays as committed.
  • Program icon: build-suite.R picks an icon under icons/ico (Windows) / icons/mac (macOS) and passes it to export(icon=).

3.3.2 publish-catalog.yml (ShinyApps)

  • Triggers: schedule: cron '*/10 * * * *'; workflow_dispatch; repository_dispatch: types: [bundle-updated]; push main for paths catalog/apps.yml or icons/**.
  • Steps:
    1. Read catalog/apps.yml; for each app, gh api repos/<repo>/releases/latest;
    2. scan asset names <id>-<version>-<platform>.zip (version must equal the Release tag minus v) and extract browser_download_url / size / digest(sha256);
    3. generate catalog/catalog.json (schema/updated_at/shell_min_version/apps[]);
    4. push catalog.json and icons/ to gh-pages via a worktree;
    5. skip the commit when nothing changed (ignoring updated_at) to avoid empty commits every 10 minutes.
  • Note: scheduled jobs run only on the default branch and GitHub often delays them (minutes or more).

3.3.3 *-build-app-bundle.yml (each app repo)

  • Triggers: release: published; workflow_dispatch (input tag).
  • matrix: windows-latest -> win-x64, macos-14 -> mac-arm64.
  • Key logic:
    • r-version: 4.6.1;
    • R CMD build --no-build-vignettes --no-manual; scConvertShiny also git clone mianaz/scConvert then R CMD build;
    • derive declared deps from DESCRIPTION (Depends/Imports/LinkingTo), seed <- unique(c(loc_names, declared)), then tools::package_dependencies(..., recursive=TRUE) for the full transitive closure;
    • install CRAN/Bioc deps with type="binary" and local source packages with type="source" (local tarballs cannot be installed as binary);
    • write manifest.json (see 3.4), zip::zipr the bundle and assert rlib/ is present, compute sha256 into meta-<platform>.json;
    • the release job appends the zips to the corresponding Release.

3.4 The three data contracts

catalog.json (gh-pages, fetched by clients) — schemas/catalog.schema.json

{
  "schema": 1,
  "updated_at": "2026-10-08T07:10:25Z",
  "shell_min_version": "0.1.0",
  "apps": [
    {
      "id": "seurat-explorer",
      "name": "SeuratExplorer",
      "version": "0.1.10",
      "author": "...", "description": "...", "homepage": "...", "license": "...",
      "icon": "https://fentouxungui.github.io/ShinyApps/icons/seurat-explorer.png",
      "r_version": "4.6.1",
      "artifacts": {
        "win-x64":   { "url": "https://.../seurat-explorer-0.1.10-win-x64.zip",   "size": 215698175, "sha256": "ac4d81..." },
        "mac-arm64": { "url": "https://.../seurat-explorer-0.1.10-mac-arm64.zip", "size": 199743796, "sha256": "871245..." }
      }
    }
  ]
}

manifest.json (inside a bundle, downloaded with the app) — schemas/manifest.schema.json

{
  "schema": 1,
  "id": "seurat-explorer", "name": "SeuratExplorer", "version": "0.1.10",
  "author": "...", "description": "...", "homepage": "...", "license": "...",
  "runtime": "r-shiny",
  "entry": "app/app.R",
  "platform": "win-x64",
  "r_version": "4.6.1",
  "min_shell_version": "0.1.0",
  "notes": "optional: known platform limitations",
  "packages": [ { "name": "Seurat", "version": "5.x", "path": "rlib/Seurat", "source": "cran" } ]
}

state.json (local %APPDATA%\shinyapps\state.json) — schemas/state.schema.json

{
  "schema": 1,
  "apps":   { "seurat-explorer": { "version": "0.1.10", "platform": "win-x64", "dir": "...", "packages": ["Seurat", "..."] } },
  "shared": { "Seurat": { "version": "5.x", "refs": ["seurat-explorer", "scconvert"] } }
}

3.5 Shared-library policy (shared-library-policy.md)

  • Compatibility baseline: R 4.6.1; packages that must share one version: Seurat, SeuratObject, ggplot2, DT, hdf5r.
  • Placement rules (computed, not authored):
    • a package name needed by >= 2 installed apps at the same version -> shared library <userData>/lib;
    • needed by exactly 1 app, or a version different from the shared pool -> private library <userData>/private/<app-id>;
    • at runtime R_LIBS = <private lib> : <shared lib> (private wins).
  • Reference counting: on install, same version -> refs += app, absent -> copy and refs=[app], version conflict -> private lib; on uninstall, remove the app from refs and delete the shared package only when refs is empty; on update, unpack the new version, add new refs, release dropped refs, and delete the old dir last (rollback-safe).

4. Client runtime

4.1 Startup

The launcher is in store mode: it renders a card grid (name / description / icon; falls back to initial on failure), fetches catalog.json (cached in-process; a restart is the most reliable refresh), compares catalog.version with the installed version in state.json via semver, and shows Install / Run / Update / Stop / Uninstall.

4.2 Install (appstore.js)

  1. Fetch catalog -> choose the platform artifact;
  2. Download: HTTP Range resume, 5 retries, 60s idle timeout, real progress from content-length (downloading N / M MB); cache dir store-cache/;
  3. Verify: downloaded byte count == catalog size, and sha256 matches;
  4. Unzip: built-in zero-dependency ZIP extractor (Node zlib only; stored/deflate + ZIP64 + zip-slip protection) to apps/<id>/<version>/;
  5. Install deps: copy packages from rlib/ missing in the shared lib into <userData>/lib, same version -> refs+1; version conflict -> private lib;
  6. Write state.json.
  7. Feedback: the card shows the current step (downloading % → verifying size → verifying checksum → unpacking → linking packages); on failure it shows which step failed and why — e.g. Install failed at step "verify": size mismatch (catalog N bytes, downloaded M bytes) — and the message persists until the next attempt.

4.3 Run / return / process cleanup

  • At runtime R_LIBS = private lib : shared lib, starting app/app.R with the portable R (reusing native-r.js).
  • Returning to the launcher (menu / Ctrl+L / in-app back) does: confirm unsaved work first -> on confirm stop() the whole R process tree (Windows taskkill /PID <pid> /T /F) -> wait for status.phase === 'app_exit' or a timeout fallback -> load the launcher.
  • Only one app backend runs at a time. On the launcher, the Apps menu is hidden (avoids a meaningless "Back to Launcher").
  • Card name → homepage: hovering an app name shows “click to learn more”; clicking it opens the app's catalog homepage in the default browser. Apps without a homepage show a plain, non-clickable name.
  • Per-app Help / About: while an app runs, the App-menu label, Help > Documentation, and the About dialog use that app's name / version / description / author / copyright / homepage / email / docs; on the launcher they fall back to the shell (ShinyApps).

4.4 Updates

  • Shell update: checked at startup and, on the launcher, via About -> Check for Updates; electron-updater reads the Release latest*.yml, compares versions, downloads (.blockmap delta), and runs quitAndInstall() on quit for a silent overwrite. Caveat: unsigned builds download but fail to install.
  • App update: catalog version > installed version -> the card shows Update; clicking stops the backend -> download -> verify -> unpack to a new version dir -> atomic switch -> recompute ref counts -> delete the old dir. When done, main.js's done event carries op, and launcher.html optimistically clears Update (so a delayed/missed echo no longer leaves a stale button).
    • While a catalog app is running, Help -> About -> Check for Updates targets that app (same catalog version as the card's Update): if newer and confirmed, it stops the app, returns to the launcher, and runs the update (progress/failure shown on the card).

4.5 Uninstall

Stop the backend -> delete the app dir and private lib -> refs-1 on shared packages, delete only when refs reach zero.


5. Release process (maintainer checklist)

5.1 Ship a new app version (store)

  1. App repo: bump Version in DESCRIPTION and commit;
  2. tag (e.g. v0.1.11) and publish a Release (or manually run Build app bundle with the tag);
  3. confirm both platforms' <id>-<ver>-<platform>.zip are uploaded and the size is normal (not a few KB; must contain rlib/);
  4. trigger Publish catalog (push, manual, or repository_dispatch from the app workflow) and confirm the app's version/sha256 updated in catalog.json;
  5. restart the ShinyApps client -> the card shows Update -> verify.

5.2 Ship a new shell version

  1. Edit shell files (_shinyelectron.yml, build-suite.R, icons, ...); no manual version bump needed;
  2. push a vX.Y.Z tag (or manually run Build ShinyApps desktop with the tag);
  3. confirm the Release has .exe/.dmg + latest*.yml + .blockmap;
  4. (To actually enable shell auto-update) configure a code-signing certificate.

5.3 Update icons only

Put the PNG into icons/ -> push (triggers publish-catalog.yml) -> confirm https://fentouxungui.github.io/ShinyApps/icons/<id>.png returns 200 -> restart the shell.


6. Troubleshooting FAQ

Symptom Root cause Fix
Update still shows after updating UI refresh relied on a single async echo, which can be missed during long updates launcher.html now clears Update optimistically via done(op); update the shell
Install progress stuck at 0%, no error Downloads were truncated (case-insensitive Cache/ collision; no progress/timeout/integrity checks) Fixed: HTTP Range resume + 5 retries + size check + 60s idle timeout + store-cache/
Card icons missing No icons/ on gh-pages, or CSP blocks them Use the latest publish-catalog.yml to sync icons/; launcher.html allows img-src https:; falls back to initial
Icon has a blue background / too small .app-icon had a background, small size Fixed: transparent background when an image exists, container 64px
Shell won't open, exits at startup appstore.js wasn't packaged (require('./backends/appstore') fails) Ensure appstore.js is in inst/electron/backends/ and listed in R/build-templates.R's copy list
Bundle zip is only ~2.5 KB Only app/+manifest.json were zipped; rlib/ missing Use zip::zipr and assert rlib/; the CI now has a guard
Bundle CI says not available as a binary A local source tarball can't use type="binary" Local packages use type="source"; CRAN/Bioc closure uses binary
Missing transitive deps (e.g. RANN) Closure expanded only from loc_names, missing declared seed <- unique(c(loc_names, declared))
macOS build fails on scConvert configure can't find HDF5 brew install hdf5 pkg-config and export PKG_CONFIG_PATH/HDF5_*
Tag / DESCRIPTION version mismatch -> broken bundle Workflow can't find Pkg_<ver>.tar.gz Align versions; the CI now has a "source tarball missing -> stop" guard
Shell auto-update downloads but won't install Not signed Buy/configure code signing (signing.sign is currently false)
Windows scConvert conversion reports H5Fclose Known limitation of CRAN hdf5r bundling HDF5 1.12.1 See section 7, deferred
Apps -> Back to Launcher throws ReferenceError leaveToLauncher scoping Ensure it is at module top level (upstream PR prepared)

7. Known limits and suggested additions

7.1 Current known limits

  1. Platforms: the bundled portable-R strategy is Windows / macOS only (Linux needs system); bundle CI currently produces win-x64 and mac-arm64 only.
  2. Windows scConvert conversion: CRAN hdf5r ships HDF5 1.12.1 and the H5Fclose finalizer can fail during GC (uncatchable). Noted in manifest.notes; deferred.
  3. Code signing: unsigned Windows/macOS -> SmartScreen/Gatekeeper warnings; shell auto-update downloads but cannot install.
  4. Disk duplication: after install, apps/<id>/<ver>/rlib/ duplicates the shared lib/; store-cache/ zips are not pruned. Suggest deleting rlib/ after linking and pruning the cache.
  5. State loss on app switch: only one app runs at a time; returning kills the process and unsaved analysis is lost (a confirmation dialog is shown).
  6. Catalog refresh depends on the schedule or a manual run: an app release does not auto-notify ShinyApps (unless repository_dispatch is added).
  7. Licenses: shinyelectron is AGPL-3.0; SeuratExplorer is GPL-3.0-or-later; scConvertShiny is MIT. Respect each obligation when redistributing.

7.2 What I think should be added (by priority)

High priority

  1. README.md (repo home): the repo lacks a newcomer entry point. Include a one-line intro, architecture diagram, quick start, and links to the Wiki pages.
  2. LICENSE and THIRD-PARTY-NOTICES: state ShinyApps' own license and list shinyelectron (AGPL-3.0), each app, and third-party R packages (Seurat, scConvert, ...) with their obligations.
  3. "Add an app" template + validation: a copy-paste build-app-bundle.yml template and apps.yml snippet, plus a CI/script that validates the contract (legal id, id-ver-platform.zip naming, tag == DESCRIPTION version, r_version == 4.6.1, zip contains rlib/).
  4. Auto-refresh the catalog: add repository_dispatch: [bundle-updated] at the end of each app's bundle workflow, turning "manually run Publish catalog" into automatic.
  5. Compatibility matrix: a single table of "shell version x runtime R version x required shared-package baseline" so app developers know what to build against.
  6. Code-signing guide: Windows (CSC_LINK/CSC_KEY_PASSWORD) and macOS (notarization) steps; explain auto-update behavior before signing.

Medium priority

  1. Release checklist / rollback runbook: turn section 5 into a checklist; add "how to roll back a bad app version / bad shell version".
  2. Expanded troubleshooting: add "how to read the [store] lines in %APPDATA%\shinyapps\logs\app-*.log".
  3. Test checklist: menu return, both branches of the unsaved prompt, repeated switching, a real conversion, process cleanup, exit without leftovers, large files, rapid clicking, cold-start time/memory — usable as pre-release acceptance.
  4. Space optimization: implement "delete the bundle rlib/ after install" and "prune store-cache", and document the savings.
  5. Security note: treat catalog and bundles as untrusted input; HTTPS only; verify sha256 (consider adding signatures/verification later).
  6. Screenshots and GIFs: launcher, install progress, update prompt, return-confirmation dialog — greatly improves Wiki readability.
  7. Bilingual coverage: at least Home and Quick Start in both languages.

Low priority / long term

  1. CHANGELOG.md: version history for the shell and apps; the catalog already has a changelog field for the update dialog.
  2. App detail page: expand a card to show author/license/version/changelog (the catalog already carries these fields).
  3. Multi-language / theme / search: launcher UX enhancements.
  4. Contribution and governance: CONTRIBUTING.md, CODE_OF_CONDUCT.md, Issue/PR templates.
  5. CI end-to-end test: a minimal "unzip -> validate manifest -> smoke start" check on the produced bundle in CI.

8. Appendix: contributing upstream (optional)

The multi-app-store branch mixes general shell capabilities with project-specific logic, so do not open it as one PR. Its most general piece — confirm before returning to the launcher + hide the Apps menu on the launcher — has been prepared as a clean patch containing only inst/electron/shared/main.js, generated against upstream main:

git remote add upstream https://github.com/coatless-rpkg/shinyelectron   # skip if it exists
git fetch upstream
git checkout -b confirm-back-to-launcher upstream/main
git apply feature1-unsaved-prompt.patch
git add inst/electron/shared/main.js
git commit -m "Confirm before returning to the launcher; hide Apps menu on launcher"
git push origin confirm-back-to-launcher

Then open a PR against coatless-rpkg:main. The store/installer code should go through a separate issue or later PR.


Appendix A - Key file snippets

A.1 _shinyelectron.yml (shell config highlights)

app: { name: "ShinyApps", version: "0.1.0", slug: "shinyapps" }
build: { type: "r-shiny", runtime_strategy: "bundled" }
installer: { app_id: "com.shinyapps.desktop" }
signing: { sign: false }

dependencies:
  extra_packages: [SeuratExplorer, presto, Seurat, SeuratObject, ggplot2, DT,
                   scConvertShiny, scConvert, hdf5r, crayon, shiny, shinydashboard, callr, zip]
  r:
    version: "4.6.1"
    local_packages:
      - "dependency/SeuratExplorer_0.1.9.tar.gz"
      - "dependency/presto-1.1.0.tar.gz"
      - "dependency/scConvertShiny_0.1.1.tar.gz"
      - "dependency/scConvert-0.2.1.tar.gz"

updates:
  enabled: true
  provider: "github"
  check_on_startup: true
  auto_download: true
  auto_install: true
  github: { owner: "fentouxungui", repo: "ShinyApps" }

apps:
  - { id: seurat-explorer, name: "SeuratExplorer", path: "./apps/seurat-explorer" }
  - { id: scconvert,       name: "scConvertShiny", path: "./apps/scconvert" }

The version is auto-patched by build-suite.R from the tag; do not hand-edit it when releasing.

A.2 catalog/apps.yml fields

schema: 1
shell_min_version: "0.1.0"
apps:
  - id: <lowercase unique id>
    repo: <owner/repo>            # gh api fetches the latest release
    name: <display name>
    author: <author>              # About text
    description: <description>    # About text
    homepage: <URL>               # About > Visit Website; also the card name link target
    docs: <URL>                   # optional; Help > Documentation
    email: <address>              # optional; About > Email
    copyright: <text>             # optional; About text
    license: <SPDX>
    icon: https://fentouxungui.github.io/ShinyApps/icons/<id>.png
    r_version: "4.6.1"

A.3 App entry app.R

library(<YourPkg>)
options(shiny.launch.browser = FALSE, shiny.deprecation.messages = FALSE)
<entry_function>()      # must return a shinyApp object; do not runApp()

Appendix B - Glossary

Term Meaning
shell The Electron desktop program itself (with portable R runtime)
bundle A single app's prebuilt package: app/ + rlib/ + manifest.json
catalog catalog.json hosted on gh-pages: app list + download URLs/checksums
runtime layer Tier 0, the one-time base shipped with the shell
shared library Tier 1, <userData>/lib, reference-counted
private library Tier 2, <userData>/private/<id>
R_LIBS R package search path; run order = private lib : shared lib

Compiled by an Open-Science Agent from the 2026-09/10 development and acceptance session, verified against the current ShinyApps / shinyelectron@multi-app-store / app repos. If the repos change, the files there are authoritative.

🌐 Language / 语言


ShinyApps Wiki

Clone this wiki locally