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
23 changes: 11 additions & 12 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,24 @@ This file provides guidance to AI coding agents (Claude Code, etc.) when working

## What this is

`reactlog` is an R package that visualizes Shiny's reactive dependency graph. A small R side (`R/`) injects Shiny's recorded log into an HTML page; a Flow-typed JavaScript app (`srcjs/`) replays it as a cytoscape graph.
`reactlog` is an R package that visualizes Shiny's reactive dependency graph. A small R side (`R/`) injects Shiny's recorded log into an HTML page; a TypeScript app (`srcts/`) replays it as a cytoscape graph.

The built bundle `inst/reactlog/reactlogAsset/reactlog.js` (and its `.map`) **is committed**. CRAN and `R-CMD-check` never run Node. If you change anything in `srcjs/`, rebuild and commit the bundle with your change (the shared `routine` CI job also runs `npm ci` + `npm run build` and pushes any bundle changes back to same-repo PRs).
The built bundle `inst/reactlog/reactlogAsset/reactlog.js` (and its `.map`) **is committed**. CRAN and `R-CMD-check` never run Node. If you change anything in `srcts/`, rebuild and commit the bundle with your change (the shared `routine` CI job also runs `npm ci` + `npm run build` and pushes any bundle changes back to same-repo PRs).

## Commands

JavaScript (npm; Node 22 works):

```bash
npm ci # install; postinstall also runs the build
npm run build-only # webpack build via grunt -> inst/reactlog/reactlogAsset/reactlog.js
npm run build # eslint --fix + prettier on srcjs, then build
npm run watch # lint, then rebuild on change
npm run lint -- srcjs/foo.js # eslint --fix + prettier on given files
npm run flow-report # flow type coverage (95% threshold, see package.json)
npm run build-only # esbuild -> inst/reactlog/reactlogAsset/reactlog.js (no type check)
npm run typecheck # tsc, no output files
npm run build # eslint --fix + prettier on srcts, typecheck, then build
npm run watch # lint, then rebuild on change (no type check)
npm run lint -- srcts/foo.ts # eslint --fix + prettier on given files
npm run check-shiny-target # fail if tsc/esbuild target differs from rstudio/shiny (needs network)
```

`build-only` and `watch` set `NODE_OPTIONS=--openssl-legacy-provider` because webpack 4 needs it on Node 17+.

R:

```r
Expand All @@ -34,7 +33,7 @@ devtools::document() # regenerate man/ and NAMESPA
devtools::check()
```

`npm ci` installs a git pre-commit hook (`pre-commit` + `lint-staged`, config in `package.json`). On commit it lints staged `R/*.R` with `devtools::lint()` (needs the `lintr` R package) and, for staged `srcjs/**/*.js`, runs lint + build, the flow report, and `git add`s the rebuilt bundle.
`npm ci` installs a git pre-commit hook (`pre-commit` + `lint-staged`, config in `package.json`). On commit it lints staged `R/*.R` with `devtools::lint()` (needs the `lintr` R package) and, for staged `srcts/**/*.ts`, runs lint, typecheck, and build, then `git add`s the rebuilt bundle.

## Memory Bank

Expand All @@ -52,10 +51,10 @@ the `!/.agents/skills/<name>/` lines in `.gitignore` to match.

`memory-bank/architecture.md` explains how the R side, the HTML template, and
the JavaScript app connect, plus how to work on the JS without running Shiny.
Read it before working in `R/` or `srcjs/`.
Read it before working in `R/` or `srcts/`.

## Conventions

- JS files start with `// @flow`. Lint is eslint 5 + prettier 1 (`.eslintrc.js`, `prettier.config.js`).
- Source is TypeScript (`tsconfig.json`, `strict` off for now). Lint is eslint 10 + typescript-eslint + prettier 3 (`eslint.config.mjs`, `prettier.config.js`).
- Add a NEWS.md entry for user-facing changes (CONTRIBUTING.md).
- New top-level files need an entry in `.Rbuildignore`, or R CMD check will warn.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,9 @@ For bug reports, please use the **reactlog** [issue tracker](https://github.com/
## Development

[![node version: 22.x](https://img.shields.io/badge/node-22.x-brightgreen.svg)](https://nodejs.org/en/)
[![JavaScript type: flow](https://img.shields.io/npm/types/flow-typed.svg)](https://github.com/flow-typed/flow-typed)
[![language: TypeScript](https://img.shields.io/badge/language-TypeScript-3178c6.svg)](https://www.typescriptlang.org/)
[![cytoscape](https://img.shields.io/github/package-json/dependency-version/rstudio/reactlog/cytoscape.svg)](https://js.cytoscape.org/)
[![@babel/preset-env](https://img.shields.io/github/package-json/dependency-version/rstudio/reactlog/dev/@babel/preset-env.svg)](https://babeljs.io/)
[![webpack](https://img.shields.io/github/package-json/dependency-version/rstudio/reactlog/dev/webpack.svg)](https://webpack.js.org/)
[![esbuild](https://img.shields.io/github/package-json/dependency-version/rstudio/reactlog/dev/esbuild.svg)](https://esbuild.github.io/)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
[![linter: eslint](https://img.shields.io/badge/linter-eslint-3524ca.svg)](https://eslint.org/)

Expand Down
18 changes: 9 additions & 9 deletions memory-bank/architecture.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: "Architecture"
description: "How reactlog fits together: Shiny's recorded log, the R functions that inject it into an HTML template, and the JavaScript app that replays it as a cytoscape graph. Start here before working in R/ or srcjs/."
description: "How reactlog fits together: Shiny's recorded log, the R functions that inject it into an HTML template, and the JavaScript app that replays it as a cytoscape graph. Start here before working in R/ or srcts/."
---

# Architecture

`reactlog` has two halves:

- **R package** (`R/`): small. It takes the log that Shiny records while `options(shiny.reactlog = TRUE)` is set, turns it into JSON, and injects it into an HTML page.
- **JavaScript app** (`srcjs/`): most of the code. A Flow-typed, webpack-bundled single page app that replays the log step by step and draws the reactive graph with cytoscape + dagre.
- **JavaScript app** (`srcts/`): most of the code. A TypeScript single page app, bundled with esbuild, that replays the log step by step and draws the reactive graph with cytoscape + dagre.

The built bundle `inst/reactlog/reactlogAsset/reactlog.js` (and its `.map`) is committed. CRAN and `R-CMD-check` never run Node; the shared `routine` CI job runs `npm ci` + `npm run build` and commits any bundle changes back to the PR.

Expand All @@ -23,14 +23,14 @@ The built bundle `inst/reactlog/reactlogAsset/reactlog.js` (and its `.map`) is c
3. `reactlog_show()` opens that file in a browser.
4. `reactlog_module_ui()` / `reactlog_module_server()` (`R/shinyModule.R`) embed the viewer as an iframe that points at Shiny's own `reactlog?w=...&s=...` endpoint. The module needs the shiny version listed in DESCRIPTION `Suggests`, which `shiny_version_required()` reads at runtime, so keep that field accurate.

## Browser side (`srcjs/`)
## Browser side (`srcts/`)

Entry point is `srcjs/index.js`, which wires DOM elements from `reactlog.html` to the modules below on page load.
Entry point is `srcts/index.ts`, which wires DOM elements from `reactlog.html` to the modules below on page load.

- `log/initStep.js` runs first on import. It takes `window.__APP_DATA__` (or `window.log` from `defaultLog.js`) and gives each entry `step = index`. `log/logStates.js` lists every log `action` and its Flow types.
- `rlog.js` is a shared mutable singleton (also `window._reactlog`) holding the log, the cytoscape instance, the `GraphAtStep`, and `curTick`. Most modules read and write state through it.
- `graph/Graph.js` is the graph state built by applying log entries in order with `addEntry()`: nodes, edges, ghost edges, statuses, family-tree queries, filtering.
- `graph/GraphAtStep.js` is the time-travel layer. It indexes special steps (idle, output calc, user marks, async) and rebuilds a `Graph` for any step by replaying the log from the start (`rawGraphAtStep`; the cache is commented out). `displayAtStep()` pushes the result into cytoscape.
- `log/initStep.ts` runs first on import. It takes `window.__APP_DATA__` (or `window.log` from `defaultLog.js`) and gives each entry `step = index`. `log/logStates.ts` lists every log `action` and its TypeScript types.
- `rlog.ts` is a shared mutable singleton (also `window._reactlog`) holding the log, the cytoscape instance, the `GraphAtStep`, and `curTick`. Most modules read and write state through it.
- `graph/Graph.ts` is the graph state built by applying log entries in order with `addEntry()`: nodes, edges, ghost edges, statuses, family-tree queries, filtering.
- `graph/GraphAtStep.ts` is the time-travel layer. It indexes special steps (idle, output calc, user marks, async) and rebuilds a `Graph` for any step by replaying the log from the start (`rawGraphAtStep`; the cache is commented out). `displayAtStep()` pushes the result into cytoscape.
- `updateGraph/` holds the navigation actions (next/prev step, idle, output calc, user mark, search, hover/sticky filter). They all end in `atTick()`, which redraws the graph, progress bar and log entry panel.
- `cyto/` has cytoscape setup, styles and event handlers. `layout/` has DOM pieces (keyboard shortcuts, progress bar, log entry panel).

Expand All @@ -50,5 +50,5 @@ The pkgdown site (`pkgdown/`, built by `.github/shiny-workflows/website.R`) copi
| [graphReplay.md](graphReplay.md) | `Graph` / `GraphAtStep`, node and edge status, filtering, the cytoscape diff and layout |
| [viewerUi.md](viewerUi.md) | Page DOM, navigation actions, keyboard shortcuts, timeline, status panel |
| [shinyIntegration.md](shinyIntegration.md) | How shiny calls reactlog, the `/reactlog` endpoint, the module, tests |
| [jsBuild.md](jsBuild.md) | npm / grunt / webpack 4 toolchain, Flow, lint, the pre-commit hook |
| [jsBuild.md](jsBuild.md) | npm / esbuild / TypeScript toolchain, lint, the pre-commit hook |
| [ciAndRelease.md](ciAndRelease.md) | GitHub Actions, the pkgdown site, CRAN release files |
14 changes: 7 additions & 7 deletions memory-bank/ciAndRelease.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ All CI lives in one workflow file that calls three shared jobs from
[rstudio/shiny-workflows](https://github.com/rstudio/shiny-workflows) at tag `@v1`.
This repo only adds one hook script, `.github/shiny-workflows/website.R`.
For how the R and JS halves fit together, see [architecture.md](architecture.md).
For the Node/webpack toolchain, see [jsBuild.md](jsBuild.md).
For the Node/esbuild toolchain, see [jsBuild.md](jsBuild.md).

Key files:

| File | Role |
| --- | --- |
| `.github/workflows/R-CMD-check.yaml` | The only workflow ("Package checks"). Calls `website`, `routine`, `R-CMD-check` from shiny-workflows `@v1` |
| `.github/workflows/R-CMD-check.yaml` | "Package checks". Calls `website`, `routine`, `R-CMD-check` from shiny-workflows `@v1` |
| `.github/workflows/shiny-target.yaml` | "Shiny build target". Runs `npm run check-shiny-target` on build-config PRs and weekly (see [jsBuild.md](jsBuild.md)) |
| `.github/shiny-workflows/website.R` | Hook run by the `website` job before pkgdown builds. Copies the demo and readme gifs into `pkgdown/assets/` |
| `pkgdown/_pkgdown.yml` | pkgdown config: site URL, docsearch, `development: mode: auto` |
| `pkgdown/favicon/` | Site favicons |
Expand Down Expand Up @@ -90,7 +91,7 @@ Steps that may auto-commit, in order:
| `usethis::use_tidy_description()` | Reorders and reformats `DESCRIPTION` |
| `devtools::document()` | `DESCRIPTION`, `NAMESPACE`, `man/` |
| `oxipng` | Lossless PNG shrink on `man/figures/*.png` and `vignettes/*.png`. Git pathspec `*` also matches `/`, so this hits all 22 PNGs under `vignettes/images/` |
| `npm ci` + `npm run build` | Uses Node `current`. `package.json` has a `build` script, so this runs (`npm ci` also runs `postinstall`, which builds). `build` runs `lint-all` (eslint `--fix` + prettier on `srcjs/`) then webpack. Commits changes under `inst/` and `srcjs/` plus `package-lock.json` |
| `npm ci` + `npm run build` | Uses Node `current`. `package.json` has a `build` script, so this runs (`npm ci` also runs `postinstall`, which builds). `build` runs `lint-all` (eslint `--fix` + prettier on `srcts/`), `typecheck` (tsc), then esbuild. Commits changes under `inst/` and `srcts/` plus `package-lock.json` |

There is no `README.Rmd`, `codecov.yml`, or `# @staticimports`, so those steps skip.
After pushing, it runs `devtools::lint()` (because `.lintr` exists) and fails on
Expand Down Expand Up @@ -169,7 +170,7 @@ will otherwise keep rewriting.

## `.Rbuildignore`

Keeps out of the tarball: `srcjs`, `bin`, `node_modules`, JS config files,
Keeps out of the tarball: `srcts`, `bin`, `node_modules`, JS config files,
`package.json`/`package-lock.json`, `*.js.map`, `inst/log-files`, `inst/gantt`,
`readme-images`, `logo`, `pkgdown`, `revdep`, `.github`,
`cran-comments.md`, `AGENTS.md`, `.agents`, `.claude`, `memory-bank`,
Expand All @@ -184,9 +185,8 @@ here.
release tarballs from a clean clone.
- `routine` pushes commits to your PR branch. Pull before pushing again, or you
will get a non-fast-forward error.
- A `srcjs/` lint problem can turn into a `routine` commit (eslint `--fix`), or
fail `npm run build`. Node `current` plus webpack 4 needs
`--openssl-legacy-provider`; keep it in `build-only`.
- A `srcts/` lint problem can turn into a `routine` commit (eslint `--fix`), or
fail `npm run build`. A type error also fails `npm run build`.
- Do not move `website.R` output back to `pkgdown/`. pkgdown 2.x ignores it
and the demo and gifs vanish from the site with no CI error.
- `pkgdown/assets/` is generated and gitignored. Do not commit it.
Expand Down
Loading
Loading