# Vendored Perspective pf_app runs a **patched build of Perspective**. Upstream's C++ engine has always implemented column-axis expand/collapse — `t_ctx2::set_depth(HEADER_COLUMN, …)` and `open`/`close(HEADER_COLUMN, idx)` are fully written — but nothing above C++ could reach it: `set_column_pivot_depth()` was never called, and `View::expand/collapse` hardcoded `HEADER_ROW`. The patch is wiring, not new engine logic. It buys two things the released packages cannot do at all: - `split_by_depth` in `ViewConfig`, the `split_by` counterpart to `group_by_depth` - `expand_column()` / `collapse_column()`, so one column branch can fold to its subtotal while its siblings stay expanded — the Excel behaviour **Source:** https://github.com/fleetside72/perspective, branch `column-axis-expand-collapse`. See `PROVENANCE.txt` for the exact commit these tarballs were built from. ## Why tarballs and not npm The feature is not released upstream. Until it is, the four packages are built from the fork and committed here as npm tarballs. `npm install` expands them exactly as it expands anything from the registry — no special tooling, and `pf.sh deploy` works unchanged. A deploy machine needs node and nothing else: no emscripten, no cmake, no protoc, no Rust. All four move together, never a subset. Perspective couples loader, package versions, data format and `apache-arrow`; vendoring a partial set reintroduces exactly the drift that causes trouble. ## Changing the engine ./rebuild-perspective.sh # builds the fork, repacks, rewrites PROVENANCE.txt cd .. && npm install git add vendor && git commit Push the fork first — the script warns if the source tree is dirty, because a tarball built from uncommitted code has no recoverable source. The build itself needs cmake >= 3.29.5, protoc >= 22 (its version silently selects which protobuf source tree gets cloned), pnpm, and the Rust nightly the repo pins. Roughly 40 minutes cold. Only ever on a machine changing the engine. ## Getting rid of this This is a fork, with the maintenance that implies. The exit is upstream taking the change — the patch is small and additive, and the engine work is already theirs. When a release ships it, delete this directory and put normal version ranges back in `ui/package.json`. ## Applied: depth fields on a config update `0001-apply-depth-fields-on-config-update.patch` is **in** the vendored tarballs as of the 2026-09-17 build, from branch `apply-depth-on-config-update`. It fixes an upstream bug in `rust/perspective-client/src/rust/config/view_config.rs`: `ViewConfig::apply_update` applies ten fields and **neither `group_by_depth` nor `split_by_depth` is among them**. So a depth can be set when a view is created (`table.view({ group_by_depth: 1 })` — which is what the fork's own `depth_test.mjs` exercises) but never through `restore()`, which is how a viewer changes its own configuration. The field arrives, deserializes, and is discarded before the engine sees it. `group_by_depth` and the omission are both upstream; the fork mirrored `split_by_depth` alongside it faithfully, including the omission. Note the semantics, from `server.cpp`: ```cpp ctx1->set_depth(row_pivot_depth - 1); // one-sided ctx2->set_depth(t_header::HEADER_ROW, row_pivot_depth - 1); // two-sided ``` The config field counts **levels to show**; `view.set_depth()` counts the boundary below them. So `group_by_depth: n` equals `set_depth(n - 1)`, and `Forecast.jsx` sends `d + 1`. To apply: ```bash cd $PSP_DIR # default ~/perspective git apply /path/to/pf_app/ui/vendor/0001-apply-depth-fields-on-config-update.patch ./ui/vendor/rebuild-perspective.sh cd ui && npm install ``` `cargo check` on this patch was clean — the 125 errors it reports without `protoc` installed are unresolved generated protobuf modules, none of them in `view_config.rs`. `applyDepth()` in `Forecast.jsx` reads the config back after restoring it and falls back to the imperative `view.set_depth()` when the value did not stick. With the patched engine installed that fallback should no longer run; it is kept for now so an unpatched engine still works, and can be removed once the declarative path is confirmed. ### Host tools this tree needs The repo pins its Rust toolchain (`nightly-2026-06-01`), Emscripten (4.0.9) and Binaryen (132) exactly, and pins **nothing** for the host tools — no `packageManager`, no `.nvmrc`. So `npm i -g pnpm` and `pip install cmake` both give versions this tree was not written against. The script's preflight now rejects them, but for the record: - **pnpm 10.x.** 11+ rejects a repeated `--if-present`, which `sh_perspective.mjs:189` emits once per package in scope. - **cmake 3.x**, not 4. Both satisfy the `>= 3.29.5` floor; CMake 4 removed support for `cmake_minimum_required < 3.5`, which the C++ dependencies still declare, and fails partway through the Arrow build. - **protoc >= 22** (33.2 known good). - **`pnpm pack`, not `npm pack`.** These packages depend on each other as `workspace:^`, and only pnpm rewrites that to a concrete version. An `npm pack` tarball is rejected at install: `Unsupported URL Type "workspace:"`. Two commits on the build branch are local build concessions rather than fixes, and should be dropped before the real patch goes anywhere upstream: removing the `postinstall:playwright` step (it runs `playwright install --with-deps`, which apt-installs system libraries and so needs root), and filling in the `allowBuilds` block pnpm 12 demanded.