vccs
Vue 3용 차트 컴포넌트 모음, Recharts에서 포팅
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Live at https://calcofi.io/explore/.
One web app for looking at the integrated CalCOFI database through six lenses — stations,
hexagons, contours, cruises, regions and sections — for one organism (a taxon, from the net tows
and censuses) or one ocean variable (from the bottle, CTD, carbonate and weather series) at a time.
Everything runs in your browser: the SQL executes in DuckDB-WASM against the frozen release, so there is
no server between you and the data, and the same bytes calcofi4r / calcofi4py read.README 전문 보기 ↗Live at https://calcofi.io/explore/.
One web app for looking at the integrated CalCOFI database through six lenses — stations,
hexagons, contours, cruises, regions and sections — for one organism (a taxon, from the net tows
and censuses) or one ocean variable (from the bottle, CTD, carbonate and weather series) at a time.
Everything runs in your browser: the SQL executes in DuckDB-WASM against the frozen release, so there is
no server between you and the data, and the same bytes calcofi4r / calcofi4py read.
The design history lives in two planning documents in the workflows repo
(.claude/plans/2026-08-28 CalCOFI Explorer … and 2026-08-29 CalCOFI Explorer UI …); this README
says what the app does and how to work on it without needing them.
depth=).calcofi4r::cc_default_stage() / cc_default_denominator().calcofi_ctd-derived dataset publishes: values computed from the CTD profiles rather than
measured. Two are per depth bin (spice, the sensor-pair averaged sigma-theta) and read like any other variable.
The rest are per cast — one number for the whole cast: a mixed-layer depth, the depth and value of the
chlorophyll maximum, the integrated chlorophyll. A per-cast value is not a depth observation, so the release keeps it
in sample_measurement on the cast's sample rather than in obs_env, and the app joins it itself
(sql/slice_cast.sql: sample_measurement ⋈ sample_root) into the same slice every lens runs on. So the title
sentence carries no depth clause — "Mixed-layer depth, … (derived, one value per cast), the mean at each
station, all years · all seasons." — the count is casts, the Depth pill says no depth axis, and the depth band
never filters it. Stations, Contours, Cruises, Regions and Hexagons draw it (Hexagons pool by the cast's
own H3 cell, sample_root.hex7, which the release carries since v2026.10.04; on an older release the lens says why it
is empty); Sections (they cut depth) say why they do not. Hover the word derived, the picker row or how it
is computed for the definition — the registry's own description and derivation, never text written in the app —
and follow the link to the dataset page for its methods. Nothing is keyed on a measurement-type name: what is
per-cast, its label, units, definition and ramp all come from the release's measurement_type registry
(src/castgrain.ts), so a type renamed or redefined in the next release needs no edit here.
docs/cast-grain.md is the design.sql/contour_cast.sql), or the station grid (one point per
grid_key cell; grain=station) — into a surface in a Web Worker (src/contour.worker.ts, plain typed arrays, no library): a thin-plate spline, the
default (mgcv's s(lon, lat) basis, the smoothing picked by GCV — the GAM calcofi4r::pts_to_contours_gam() fits;
station grid only), ordinary kriging (an exponential variogram fitted by weighted least squares; interp=ok) or
IDW (power 1.3, what the superseded Contour Explorer drew with terra::interpIDW; interp=idw). At the site grain kriging and IDW take the 24
nearest points within 180 km per cell (one small solve each, so the error comes free; ≈ 1 s for 12,000 sites);
at the station grid every point is in one system (≈ 0.3 s, the error surface ≈ 3 s more). The surface menu draws the statistic itself, its error (the
kriging standard deviation, or the spline's standard error — IDW has none, it is a weighted average, not a model),
the observation density, the first / last year sampled, the 5th / 95th percentiles or their
spread — each interpolated the same way from the station table (sql/station.sql now carries p05 / p95).
The isolines take a darkened version of the ramp colour at their level, so they stand out of the surface without
fighting it. Contour labels (on by default; labels=off, a checkbox in the options and a row in the Layers card)
write the level along each isoline in white on a dark halo, rotated to follow it, one per ~260 px of line and never
within 70 px of another, so their density follows the zoom. The inputs (the sites or stations the surface was fitted to) are a layer of their own — show the inputs in the
options and a row under Data in the Layers card, inputs=on|off; on by default for the station dots, off for
thousands of sites. The fit line under the method reports the
leave-one-out RMSE, the variogram (nugget · sill · range) or the effective degrees of freedom, and the time it
took. A cell farther than 60 km from any point is blank, the edge fades over the last 15 km, and land is clipped
(Natural Earth 10 m land, bundled as , rasterised onto the grid) — the map never extrapolates, and never over land. , and are in the URL; the map's CSV is the point table the surface
interpolates. and
are the same code by hand, pinned by — written by
the worker itself () and copied byte-for-byte into both packages'
test fixtures — so a surface made in R or Python matches the map cell for cell.lines=on): each CalCOFI line threaded through its official stations in station
order and named at both ends (lineThreads() in src/gridkey.ts, the rule the 3-D curtain draws at the surface),
independent of the Data row. Historical cells (_hist) and a station on a line of its own are not threaded.obs=on, or obs=<hex> for another colour): every cast, tow or site in the
selection at its own position (to 0.01°, sql/contour_cast.sql) as a dot, in any lens and on top of everything.
It is independent of the Data row, so data=off&obs=on draws the sampling over the sea floor and the boundaries
with no summary colour.data=off leaves the sea floor and boundaries
alone), opacity (datao=), the colour ramp (ramp=thermal, _r reverses): cmocean's 22 ramps (Thyng et
al. 2016), viridis and oce's GEBCO ramp, in src/ramps.ts; with no ramp= the variable picks its cmocean
convention (thermal for temperature, haline for salinity, algae for chlorophyll, dense for density, tempo for
nutrients, ice for oxygen; viridis for biology) — and its place among the boundary layers: the Data row under
On the map drags like any boundary, and layers=noaa_onms_sanctuaries,data draws the sanctuaries over the data.
deck.gl runs interleaved inside MapLibre's own layer stack for this (D36), which also puts the data under the
basemap's labels.+proj=calcofi is equidistant
along a line at 7.386 km = 3.99 nmi (4 nautical miles) per station unit, measured constant to 0.02 %
over the 665 km of line 90, so the axis is linear in both at once. It is linear rather than categorical
for that reason: spacing stations evenly by index drew 80 → 90 (74 km) as wide as 30 → 35 (37 km).
ctd-transects draws the same section the same way.line= link to one opens the nearest line that has a section and says so;
clicking such a station in Sections opens its card instead of switching the line.station= key from an earlier release
may name a different polygon or none. The app resolves it through the release's grid_crosswalk (src/gridkey.ts):
a key that is gone opens the new cell holding the largest share of the old one, a key that was redrawn below 90 % of
its old area stays, and a key nobody knows stays selected with an empty card — each with a line on the card saying
which. It never shows a different area without saying so.anom=1) subtracts the release's own climatology table
(calcofi4db::build_climatology()): the mean for that station, the cruise's month (the MM of its
YYYY-MM-NODC key, never the calendar day a station was worked) and its 10 m depth bin over 1993–2013, kept only
where at least 5 cruises contribute — the same table ctd-transects subtracts,
so the two products cannot disagree. The month · season toggle beside it (base=season) subtracts the cruise's
quarter instead (Jul–Sep for a July cruise; the release's season_* columns, calcofi4db 4.24.0+; on an older
release the toggle is disabled and says why). The map lenses subtract each observation's own cell before pooling
(sql/anom_slice.sql): a hexagon or a contour site is a mean of departures, an observation with no baseline drops out
(never 0), and the options say what share had one. A per-cast derived variable (a mixed-layer depth) is matched by its
cast's grid cell, with no depth bin, once the release carries per-cast climatology cells. The year, season and depth
filters do not touch the baseline (that is what makes anomalies comparable across cruises); the datasets in view are
pooled weighted by their observation counts. Red is above normal, blue below (cmocean balance), the scale symmetric
about zero; a cell with no baseline is blank, never zero.q=) and the year range's month edges read the cruise's
designated month, so a July cruise's stations worked on 30 June are in Q3 (explore#17).anom=1, base=season), the contour method and surface (interp=, surface=), the data layer (data=, datao=, ramp=), theme, the sea floor (bathy=, bathyo=), which panels are folded or maximized (hide= · show= · max=), the years strip's mode (strip=),
the map extent (map=lon,lat,zoom) and, in the 3-D section, the camera (cam=lon,lat,zoom,pitch,bearing,
written once you move it) are all in it — so Share → Copy link, a bookmark and a
feedback report all reopen at exactly the same place. ?tour=off suppresses the welcome card and tour
(and opens no modal at all); ?modal=sources opens Data Sources & Attribution, the one modal the URL
carries, so an attribution link is shareable;
?theme=dark|light sets the theme. The sea floor under every lens is GEBCO 2025 (shaded relief +
depth colour + isobaths), drawn from terrain-RGB PMTiles at
storage.calcofi.io/calcofi-db/bathymetry/ (override with VITE_BATHY_URL); the layers button on the
map toggles its parts and opacity, ?bathy=off reproduces the plain basemap, bathy=relief,contours
keeps a subset, bathyo=0.5 sets its opacity. The style is COMPOSED (CARTO ⊕ sea floor, one object,
setStyle(diff)), so a theme flip can never drop the layers — see the 2026-08-31 map-layers plan in
CalCOFI/workflows for how the tiles are built. The coast is a land mask (plan 2026-09-09, D47): CARTO's
water fill sinks to the bottom of the style, the sea floor, the boundaries under the Data row and the data draw
over it, then OpenStreetMap's land polygons (_spatial/osm_land.pmtiles, the coastline CARTO's own water derives
from) in the background colour, a copy of CARTO's water without the ocean (lakes), and every CARTO land layer
where it was — so nothing spills onto land and roads, county / state / country lines and labels stay on top.
land=off restores the old stack (a diagnostic only: the card has no switch for it); basemap=nolabels hides CARTO's own text (place names, road names, points of
interest, water names) for a data-centric view — the isobath labels and the registry's label layers stay. CARTO's
text is always toned down (one mid grey at opacity, 0.6 dark / 0.7 light by default, Regular weight, a
thinner halo, 1 px smaller: its near-white Montserrat Medium out-shouted the data), and gains the
CARTO's tiles carry but its styles never drew ( from z8, from z10); the names layer draws at 12/10/9 px
and its row's opacity (). ride the boundary registry with :
(undersea feature names from the IHO-IOC GEBCO Gazetteer, a layer — symbols by rank,
) and (Esri's World Ocean Reference as a row,
for comparison); the mask itself is never an row, and a label row's opacity slider sits under the row. A link without
shows the registry's default-visible reference layers (the names, above the data, not in the legend);
shows none; any explicit list is exactly that list.reproduce.R /
reproduce.py that run the same query; Copy code gives that SQL, or R or Python; Cite this data
copies the citations; Copy link copies the view, and Copy image / Download PNG the whole view
as a figure with the selection, release and URL stamped in a footer; Register a product, Send
feedback and SQL & timing live there too. Every panel's bar has its own ⬇ (PNG · SVG · CSV), and the
map's ⬇ in its top-right row exports the map with its title (PNG) or the table the lens draws
(CSV) — the map is WebGL, so it has no SVG.shift + arrows rotate and
tilt, + / − zoom (the top-right row has the same moves as buttons, and a compass that resets the framing) ·
? replays the tour · Esc closes a dialog, the welcome or a chip's popover, or restores an
expanded panel · ↑ ↓ Enter in the lists · A–Z strip to jump in the flat list.These are sixteen datasets that people collected, curated and depend on being cited for, and a view usually pools several of them — the statistic is averaged across the datasets that share the chosen life stage and denominator (never across denominators or life stages). Pooling is exactly why each of those datasets has to be named, so the app names them everywhere a number can leave it:
QUESTIONS in src/help.tsx), Start exploring and Take the tour. The citation norm is one sentence
under them — "These data are free to use and are cited when used: every view names its datasets, and
Share → Cite writes the citation for you" — accepted by continuing: every way in stores explore_cite_ack
beside explore_welcome. It is not a gate — Esc and × enter too. ?tour=on shows the card again,
?tour=off never shows it (the brand contract's deterministic screenshot); Help → Start here brings it
back.provider · dataset · licence —
that opens that dataset's citation with a copy button.CalCOFI Explorer · release · the view URL,
and Data: <dataset_key, …> · cite: calcofi.io/explore → Cite this data. PNG (1× and 2×), SVG and the
whole-view capture all share it (src/export.ts stampLines()).dataset_key column. A row that already has one keeps it; a pooled row
gains one holding the datasets it pools, ;-separated. A column, never a leading # comment line — a
comment breaks read.csv, pandas.read_csv and DuckDB's read_csv_auto alike.@misc built from the release's own fields — the app never fetches to cite).?modal=sources, the ❞ button in the header, the ⋯ menu on a phone, and
from About): one row per dataset — never per taxon or per variable, so the phytoplankton dataset is
one row and not 393 — with its citation, licence, DOI, PIs, acknowledgement, contact and links, the rows
in view first, and the integrated database's own citation plus one CalCOFI front door in the footer.All of it reads the release's own dataset rows and catalog.json through src/cite.ts — one set of
builders, so the bundle's CITATION.md, the modal and the clipboard cannot drift. Nothing is fetched.
What degrades, and how. The dataset columns doi, license_url, acknowledgement, contact and
source_accessed, the provider table (for provider_short) and catalog.citation all arrive with the
attribution contract (calcofi4db 3.30.0, workflows WS-A0/A1) in the next release; until then the app runs on
the dev catalog, which has none of them. Each is optional:
| absent | what the app does instead |
|---|---|
dataset.doi | no DOI link; BibTeX omits the doi field |
dataset.license_url | the licence chip is plain text, unless the SPDX id is one cite.ts knows a URL for |
dataset.acknowledgement | the line is omitted (it is not invented from citation_others) |
dataset.contact | no contact link; the modal's front door is the only route |
dataset.source_accessed | no "Accessed:" line in the copied citation or the BibTeX note |
dataset.citation_main | "no citation in this release — the dataset's provider question is open", in italics; the copy button disappears |
dataset.license | no licence chip at all (an absent licence is a fact, not a label) |
a provider table | provider_short falls back to cite.ts's PROVIDER_SHORT map, then to the slug |
catalog.citation | the same wording is built locally from version + release_date (+ catalog.doi when minted), so it matches calcofi4db::release_citation() |
dataset.parquet not yet loaded | the Sources line still names the pooled datasets by key |
npm install
The app needs a release catalog to read. The deployed site reads the real release
(https://storage.googleapis.com/calcofi-db/ + ducklake/releases, the defaults) since v2026.09.04,
the first release to ship the browser-shaped objects. For development you can still use the
catalog-shaped local copy of the v2026.08.25 objects (built once by
~/_big/calcofi/explore-spike/data2/build_dev_catalog.R; the same content is on GCS under
explore-dev, which the site read before the flip):
ln -s ~/_big/calcofi/explore-spike/data2 public/data2 # once
VITE_DATA_URL=data2/ VITE_RELEASE_PREFIX=explore-dev/releases npm run dev
then open http://localhost:5178/. This is Vite's dev server with hot reload: every saved edit shows in the browser without a rebuild, so leave it running while you work. Ctrl-C stops it.
For a production-shaped check — the real bundle, same data — build it and serve the build:
VITE_DATA_URL=data2/ VITE_RELEASE_PREFIX=explore-dev/releases npm run build
npx vite preview --host --port 5179 # http://localhost:5179/ (--host also exposes it to a phone on the same Wi-Fi)
VITE_DATA_URL is the bucket (or folder) root and VITE_RELEASE_PREFIX the releases prefix under it;
unset, they default to the real release on https://storage.googleapis.com/calcofi-db/ +
ducklake/releases, which works too once a release carries the browser-shaped objects.
npm test # vitest: the rules, on synthetic fixtures
npx tsc --noEmit # the type check (npm run build runs it too)
node scripts/smoke_release.mjs https://calcofi.io/explore/ smoke.png # after a release or a deploy: the promoted release, and the per-cast grain
node scripts/verify.mjs http://localhost:5178/ shots/dev --only=<regex> # drive the app through its states
node scripts/verify.mjs http://localhost:5179/ shots/prod --timing # + cold/warm lens timings
node scripts/bundle_check.mjs http://localhost:5178/ shots/bundle # download two bundles and list them
node scripts/card_shots.mjs ~/Github/CalCOFI/CalCOFI.github.io/images # the two themed card screenshots
node scripts/tour_shots.mjs http://localhost:5179/ shots/tour # the docs/deck pictures: a lens each + the UI close-ups
tour_shots.mjs writes the pictures the docs book's Explorer guide
and the update deck use — one per lens (both section shapes, and the 3-D curtain), the two product-card
shots at calcofi.io's own 1200 × 750, and the UI close-ups (the sentence's chips, the three Controls tabs,
the Layers card, a pane's own bar, the Help menu, the Time strip, the Depth profile). Each is named
entirely by URL parameters, so a re-shoot after a UI change is one command; shots/tour/README.md lists
every file with its URL.
verify.mjs opens the installed Chrome (headed, fresh profile) at 1280 × 800 and 390 × 844, walks every
named state, screenshots each, asserts no horizontal overflow and every control in view, and writes
results.json. It is the only reliable way to see the app under automation; --only picks states by
regex, --headless runs without a window (the DOM and layout assertions and the screenshots; the timing runs
stay headed). npm run build also type-checks (tsc --noEmit).
npm test runs the rules on small synthetic fixtures (tests/): the ramp rule, the quality predicate (pinned to
calcofi4r::cc_qual_ok_sql() byte for byte), the per-cast grain's registry rules, and — in duckdb-wasm's node
build, the same engine the browser ships (tests/helpers/duck.ts) — the SQL templates themselves and the SQL that
Copy code hands over. smoke_release.mjs is the check against a real release: it exits 1 when the page does not name
latest.txt's version, or when the per-cast pass fails (the variable the app lists from the registry, opened in the
Stations lens: every value placed and counted, no depth clause, the copied SQL carrying the
sample_measurement ⋈ sample_root join against content-addressed URLs). SMOKE_CAST_VAR=<type> picks the
variable, SMOKE_CAST=off skips the pass for a release older than v2026.10.01.
.github/workflows/pages.yml builds with VITE_DATA_URL + VITE_RELEASE_PREFIX + VITE_BASE=/explore/
(and VITE_FEEDBACK_URL, see below) and publishes dist/ to GitHub Pages on every push to main. The
page reads {prefix}/latest.txt → {prefix}/{version}/catalog.json (plus the coverage.json,
coverage_stations.json, grid.geojson and spatial.geojson sidecars) and every object by its catalog
path — never a hand-built releases/{v}/parquet/ path.
The boundary/reference rows are NOT built in this repo. ~/Github/CalCOFI/workflows/metadata/spatial_layers.csv →
calcofi4db::build_spatial_layers() → the release's spatial_layers.json sidecar. scripts/dev_spatial_layers.R builds
the bundled src/spatial_layers.fallback.ts with the same function, from the spatial and sample_spatial tables of
the release latest.txt names (or the version given as its argument), read over HTTPS by their catalog paths; the
workflows checkout must be on main. Rows backed by the oceanmetrics gazetteer carry three optional fields the app
honours (src/spatial_registry.ts): source_url (absolute PMTiles URL), source_layer (vector layer name) and
popup_fields (JSON list). The CSV has these columns and build_spatial_layers() passes them through (calcofi4db
= 4.23.0, workflows#163). A release built before then lacks them, so
mergeRegistry()overlays the fallback's gazetteer rows on its sidecar.
The dialog posts to a Google Apps Script that calcofi4r::cc_feedback_script() generates. Setup: a
Sheet with a feedback tab (header = calcofi4r::cc_feedback_header()) and a recipients tab (one
email per row — edit a cell to add someone, no redeploy); paste cat(cc_feedback_script()) as the
Sheet's script and deploy it as a web app ("execute as me", "anyone"); add GITHUB_TOKEN (contents +
issues on this repo) as a script property for the public issue; put the /exec URL in the repository
variable VITE_FEEDBACK_URL. Re-paste the script after a calcofi4r change (1.14.1 added the inline
screenshot in the mail). Usage analytics go through the fleet's GA4 snippet in index.html
(calcofi4r::cc_ga_html("public/ga.html", "explore")); an automated browser is never counted.
cc:theme event) · deck.gl MapboxOverlay (ScatterplotLayer carries the station dots between
lenses, H3HexagonLayer, GeoJsonLayer, TripsLayer, BitmapLayer for the contoured surface) · DuckDB-WASM self-hosted in a Web Worker,
no extensions, objects fetched whole and registered as buffers · Plotly for the depth strip, year
strip, section and cruise series · h3-js · the brand from calcofi.io/brand/<VITE_BRAND>/ — v2, the SIO look
(light default, Source Sans 3, the lockup at 28 px, data-cc-scale="app"), since the flip on
2026-09-04; VITE_BRAND=v1 still builds the superseded dark-default look. vite.config.ts injects
brand/<v>.head.html; src/brand.ts carries the same choice into the header and the capture (v2 embeds
the woff2 files so the feedback PNG is set in Source Sans 3). verify.mjs's v2_* states check the
default build and skip on a v1 one.obs_bio, one obs_env partition per variable,
sample_root, sample_spatial, taxon, dataset, measurement_type, cruise — cut by
calcofi4db::build_* at release time, resolved through the catalog by src/release.ts (a port of
calcofi4r::cc_release_sources()). The category tree comes from coverage.json (taxa[] and
variables[].category, calcofi4db ≥ 3.25.0; src/categories.ts keeps a keyword fallback for an
older release); the categories themselves are workflows/metadata/category.csv and the icons
calcofi.io/brand/v2/icons/ (scripts/build_icons.mjs regenerates that sprite from
src/icon-paths.ts, and the app renders the same paths inline).sql/*.sql are the lens queries the browser runs ({{named}} params, the shared
filter in _filters.sql; density.sql is the denominator fixture shared with calcofi4r /
calcofi4py; slice_cast.sql / cast_census.sql / cast_list.sql are the per-cast grain) · src/sqltpl.ts is
the template renderer (pure, so a test can run a template) and runs and times them ·
says which variables are per-cast and derived, from the registry · is
, the quality predicate's twin · resolves the SQL a view ran against the release's
URLs for and the bundle · is the URL selection model
( / ), the stage/denominator defaults and the denominator formulas · the
shell · the layers and the lens-to-lens morph · + the Contours lens's interpolators, isolines and bitmap · the colour ramps · the lens picker · the Plotly panels ·
the organism / variable / cruise picker (tree + flat list) · the
rails, floating cards and phone sheet · per-panel PNG/SVG/CSV and the footer stamp ·
the whole-view figure (one composite; MapLibre runs with
so its canvas can be read) · + the
feedback dialog (and , its second kind) · the download ·
the citation builders every attribution surface shares · the Sources line
and the Data Sources & Attribution modal · the guided tour over
anchors · the welcome (with the agreement) and About dialogs.public/land.geojsoninterp=grain=surface=calcofi4r::cc_interpolate()calcofi4py.interpolate()scripts/parity/contour_fixture.jsonnode scripts/parity/contour_fixture.mjsbasemapo=place_islandplace_island_minorlayers=gebco_gazetteer::0.45role = referencegebco_gazetteerlabellayers=gebco_gazetteer[:colour]esri_ocean_referencerasterlayers=layers=offderived-product. The Apps Script must read label from the payload for the issue to be filed
under it; the zero-backend fallback link already carries labels=derived-product.src/engine.tssrc/castgrain.tssrc/qual.tsqualOkSQL()src/reproduce.tssrc/state.tsfromUrltoUrlsrc/App.tsxsrc/map.tsxsrc/contour.tssrc/contour.worker.tssrc/ramps.tssrc/lenspicker.tsxsrc/charts.tsxsrc/picker.tsxsrc/panels.tsxsrc/export.tssrc/capture.tshtml-to-imagepreserveDrawingBuffersrc/annotate.tsxsrc/feedback.tsxsrc/bundle.tssrc/cite.tssrc/sources.tsxsrc/tour.tsdata-toursrc/help.tsxlocalStorage; folds and
maximize live in the URL.?native=1 swaps the pickers for plain <select>s; SQL & timing (in Export)
opens a card with every timing mark and the last SQL (window.__marks is the same list).