# CLAUDE.md Tips for future agents working in this repo. Patterned after the sibling `surfacefun-interactive` project (same numbl + uihtml two-way bridge), but the figure is a 2D canvas instead of a three.js surface. ## Architecture - `app/` — React single-file widget (`npm run build` → `app/dist/index.html`) - `*.m` at root — scripts users open and run in [numbl](https://numbl.org) - `helpers/` — the region/sampling algorithm (`make_region`, `hit_and_run`, `hit_and_run_general`), put on the path by the driver via `addpath('helpers')` - `numbl-project.json` — project metadata; `entry` is the landing page. Its `figures` array declares the editor-less figure view the deploy exposes at `#figure/sampler` (runs `hitandrun_demo.m`, shows just the interactive figure); the org profile's "live" link points there. ### Multi-file layout / addpath `hitandrun_demo.m` is the **driver**; its first statement is `addpath('helpers')` so `make_region` / `hit_and_run` resolve from the subdir. Two constraints make that placement mandatory: - `addpath()` must be a **leading** statement in the driver, before any other non-comment statement (numbl's static path resolution requires it; the runtime/interpreter path is fine with it too). It also cannot live inside a function file — only the driver. So `hitandrun_sampler.m` can't add the path itself; run `hitandrun_demo.m`, not the sampler directly. - Plain subdirectories are otherwise invisible to function resolution — only `addpath`'d dirs (plus root, `+pkg`, `@Class`, `private/`) are searched. This works the same in the browser deploy (the worker runs the interpreter, which honors the runtime `addpath`). ## Data flow **MATLAB → figure:** set `uihtml(..., 'Data', struct)` or call `sendEventToHTMLSource(src, 'eventName', data)` **Figure → MATLAB:** call `sendToMATLAB('eventName', value)` (see `bridge.ts`); receive via `HTMLEventReceivedFcn` ## The demo, built in stages (all done) 1. **(done)** Generate a convex region + `N` hit-and-run samples in the script; show region + samples in the figure. The samples are drawn server-side and sent via `Data`. 2. **(done)** Add controls to the figure panel: a **Samples** slider, a **Resample** button, and a **New region** button. 3. **(done)** Wire those controls to the sampler (two-way bridge). 4. **(done)** Non-convex regions. A **non-convex region** checkbox switches to a star polygon (`make_region(false)`) sampled by `hit_and_run_general`; the movie draws the multiple in-region segments a line makes through it. The figure → script protocol (each request carries `convex`): - **Samples** slider (on release) / **Resample** → `sendToMATLAB('resample', { n, x, y, convex })`. `x, y` are the *current region's* vertices — the script is **stateless**, so it samples the region it is given and replies with `sendEventToHTMLSource(src, 'samples', { x, y, n })`. - **New region** / **non-convex** toggle → `sendToMATLAB('newRegion', { n, convex })`. The script builds a new region of that type and replies with a full `sendEventToHTMLSource(src, 'data', )` (same shape as the initial `Data`, plus `convex`). `hitandrun_sampler.m`'s `sample_region(vx, vy, n, convex)` dispatches to `hit_and_run` (convex, JIT) or `hit_and_run_general` (non-convex, interpreter). The non-convex path can't JIT (sort + point-in-polygon), so `App.tsx` caps its `N` lower via `SAMPLE_CHOICES_NONCONVEX`. Ideas for the next iteration: show the chain path / burn-in, animate the walk, or a "uniform vs. non-uniform target" toggle. ## Client-side vs. server-side interactivity - **Low-latency / view-only** (e.g. dot size, zoom): update React state only — instant, no MATLAB round-trip. - **New samples** (depends on the RNG and the algorithm): `sendToMATLAB` → script re-runs → `sendEventToHTMLSource` → React re-renders. ## Key files | File | Purpose | |------|---------| | `app/src/App.tsx` | Main React component; reads data, hosts the panel, non-convex toggle + movie `regionSegments` | | `app/src/render/RegionView.tsx` | 2D canvas renderer (region outline + sample dots + movie `segments`) | | `app/src/bridge.ts` | `onData` / `onHostEvent` / `sendToMATLAB` helpers (generic; identical across these projects) | | `hitandrun_sampler.m` | Opens the figure, sends data, handles `resample`/`newRegion` (figure plumbing: `on_event`, `sample_region`, `pack_data`, `pack_samples`) | | `helpers/make_region.m` | Random region — convex hull of disk points, or `make_region(false)` for a star polygon | | `helpers/hit_and_run.m` | Convex hit-and-run sampler (half-plane chord intersection; JIT) | | `helpers/hit_and_run_general.m` | Non-convex hit-and-run sampler (all in-region segments; interpreter) | | `hitandrun_demo.m` | User-facing driver: `addpath('helpers')` + seed + call the sampler | ## Local iteration Always rebuild the figure app first — both workflows read the prebuilt `app/dist/index.html`: ``` cd app && npm install && npm run build # produces app/dist/index.html cd .. ``` `NUMBL` below is a local clone of https://github.com/flatironinstitute/numbl (e.g. `~/src/numbl`). Run its CLI with `npx tsx $NUMBL/src/cli.ts …` — no global install. ### A. Quick run via the CLI (fastest inner loop) ``` npx tsx $NUMBL/src/cli.ts run hitandrun_demo.m --plot ``` `--plot` opens the figure in a browser and keeps the script's session alive so figure → script callbacks work (Ctrl+C to stop). Good for iterating on the `.m` + app together. ### B. Preview the actual GitHub Pages site locally This reproduces what the [deploy workflow](.github/workflows/deploy.yml) ships (it calls `flatironinstitute/numbl/.github/actions/build-site@main`, which is just `cli.ts build-site` after building the site-viewer). The numbl in the deployed site runs the `.m` in a **web worker in the browser**, and it keeps a live uihtml session there — so figure → script callbacks (`resample`) round-trip in the deployed site exactly like in the CLI `--plot` path. (That two-way bridge is why the workflow builds numbl from `main` rather than the npm release.) ``` # one-time, in the numbl clone: build the browser site-viewer ( cd $NUMBL && npm ci && npm run build:site-viewer ) # from this project: bundle project + viewer into _site/ npx tsx $NUMBL/src/cli.ts build-site . --out _site --base / # serve it (localhost is a secure context, so coi-serviceworker.js can set the # COOP/COEP headers numbl's WASM needs) ( cd _site && python3 -m http.server 8080 ) # → open http://localhost:8080/ ``` Notes: - `--base /` is for serving at localhost root. GitHub Pages serves this project at `//`, so the workflow auto-detects `--base /hitandrun-interactive/`. Content is identical; only asset paths differ. - `_site/` and `dist/` are git-ignored (see `.gitignore`). - The real target is GitHub Pages, where the same single-file app runs against numbl compiled in the browser.