Repository navigation
Conversation
SqModule.getImports called linker.resolve without catching, so a linker whose resolve throws made SqProject.setHead (via ProjectState.gc) throw outside the project's error handling: browsers never finished the run and the CLI exception escaped. getImports now returns a result; each resolve failure becomes an SqImportError at the import statement, reported like a failed loadModule. All callers handle the failed case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
squiggle-lang now reports resolve failures as import errors, so the invalid: sentinel ids are no longer needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Import errors without a location of their own (unresolvable or unloadable imports) point to the import statement in --json output. Line-based hints only use the error's own location, so they don't fire on the import line. - run --json and check --json report unexpected exceptions as a JSON object with an "internal" error, exit code 1, instead of plain text on stderr. Same for reruns in run --json --watch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- resolveSafePath rejects hidden segments (.env, .ssh/..., .git/...), and
only .squiggle/.json/.md files can be read (as for writes).
- A non-loopback --host needs --allow-network (allowNetwork: true), which
prints a warning and disables POST /api/root and GET /api/browse. In that
mode IP-literal Host headers are accepted.
- Root guard: file read/write/rename and Hub import requests may carry
x-squiggle-studio-root (STUDIO_ROOT_HEADER, URI-encoded); a mismatch with
the open folder answers 409 { code: "root-changed", root }.
- PUT /api/file { create: true } fails with 409 "file-exists" if the file
exists; POST /api/file/rename { from, to } renames/moves a file.
- Client api: setExpectedRoot, createFile, renameFile; ApiError has code
and root.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
open() switched the server's root before the reload's beforeunload prompt, so "Cancel" left unsaved buffers pointing at the new folder. Now it asks the page (window.squiggleStudio.hasUnsavedChanges()) first and shows a native Discard/Cancel dialog; on Discard it calls discardChanges() so the reload doesn't prompt again. Pages that can't answer count as clean. Aborted loadURL navigations are no longer unhandled errors, and failures to open a folder from the menu show an error box. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lets Squiggle Studio show a single variable per card without the viewer's toolbar, and put a code editor next to its own views. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Output view shows every variable as a card with its chart (Cards/List toggle), keeps the last good run visible while the code is broken, and shows functions compactly. - Code pane next to Output and Relative values shares the workspace's simulation, so files run once; the playground view is now 'Playground'. - View switcher and save state live in the tab bar; one header row. - Agent-watching: files changed on disk are highlighted and listed under 'Changed on disk'; 'Follow changes' opens them; live connection status. - New file (+) and rename (double-click) in the file tree. - Folder switches ask before discarding unsaved edits; a switch from another window shows a banner instead of dropping them; saves send the expected root so they can't land in the wrong folder. - ?file= applies only on first load; deleted files aren't restored. - Per-file view choice; relative-values files open on their dashboard. - Resizable sidebar and code pane; richer empty state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Watchers were closed at the start of each rerun and recreated after it, and events that arrived while running were dropped, so edits made during a run were lost. Watching now continues during runs; a change that arrives mid-run schedules one more run after the current one (still debounced by 100ms), and the watched set is updated to the new imports after each run. Files are watched through their directories, so editors that save by renaming a temp file over the original keep working without recreating watchers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
makeEnvironment's `sampleCount ?? default` let `-n 0` or SAMPLE_COUNT=0 produce a 0-sample environment. run's --sample-count (and its SAMPLE_COUNT env var) and check's --sample-count now parse with myParsePositiveInt, which fails with a clear error. runCLI in tests now applies exitOverride and the output capture to subcommands too, so option parsing errors don't call process.exit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ector Dropdown, MouseTooltip and Modal already wrap their portal content in a div with TailwindProvider's selector, so that scoped Tailwind styles apply to them. The two hover tooltips didn't, so they lost their styles inside scoped embeds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rv CLI - ModelEvaluator.computePairs / computeAll: asynchronous, time-sliced versions of compareAll that yield to the event loop, with AbortSignal support and progress callbacks. isCached() checks whether pairs are already computed. - Cache-only evaluators: ModelEvaluator.fromCache / fromBundle never run Squiggle code (canCompute is false) and report missing pairs as errors. - RelativeValuesBundle: a versioned, zod-validated JSON format with the definition, all n^2 pairs and metadata (generatedAt, seed, sampleCount, sourceHash, variableName). Non-finite numbers are stored as strings. createRelativeValuesBundle, parseRelativeValuesBundle, stringifyRelativeValuesBundle (one pair per line, for readable diffs). - squiggle-rv bundle <file.squiggle>: evaluates a model with relative file imports and writes a bundle. - Clearer error message for invalid relative values (e.g. NaN). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@quri/squiggle-relative-values/styles.css is safe to load in any page:
- every selector is prefixed with .squiggle-rv (a small PostCSS plugin,
postcss-scope.cjs, runs after Tailwind), including Tailwind's preflight,
so nothing outside the dashboard is restyled; keyframes are renamed to
squiggle-rv-*;
- the wrapper doesn't inherit text styles from the page, and the page's
element rules (button {}, div {}, ...) are reverted inside it;
- it includes @quri/squiggle-components' common styles.
full.css is unchanged. Package: squiggle-rv bin, ./styles.css export,
@quri/squiggle-lang moves to peerDependencies (a single copy is needed to
pass Squiggle values between the app and this package).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- createRelativeValuesBundle({ precision }) and squiggle-rv bundle
--precision round numbers to N significant digits (recorded in
meta.precision): a 150-item bundle goes from 3.7MB to 2.3MB at 4 digits.
- yieldToEventLoop uses MessageChannel instead of scheduler.yield():
yield() continuations are prioritized over other tasks, which starved
the page's own timers for the whole computation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preflight rules without classes (button, h1, *, ...) become .squiggle-rv :where(button): one class of specificity is enough to beat the page's element rules, and the page's class rules (e.g. in custom tabs or renderItemName output) still win when loaded after styles.css. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion
Dashboards (RelativeValuesDashboard, RelativeValuesSpecDashboard and the
new RelativeValuesBundleDashboard) now:
- render a .squiggle-rv wrapper (RelativeValuesRoot) for styles.css, and
set @quri/ui's TailwindContext so that dropdowns and tooltips in portals
are scoped too;
- accept tabs (built-in ids and custom { id, label, icon?, render(ctx) }
tabs, in any order; ctx has the definition, evaluator, filtered and
sorted items, and clusters), onItemClick, renderItemName, className,
height (fixed or "100%" with internal scrolling) and clusterOverrides;
existing props keep working, and tab ids are typed (generic components);
- compute the pairs a view needs asynchronously with a progress bar (list:
per-cluster pairs; other views: all n^2), then the rest in the background
(useRelativeValuesComputation). While a new evaluator computes, the last
computed one stays on screen. precompute={false} restores synchronous
rendering. Progress elements are marked with data-rv-computing, which
squiggle-studio render now waits for.
RelativeValuesBundleDashboard takes a bundle (validated) and doesn't run
Squiggle; the list sidebar shows summary numbers without charts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
packages/relative-values/examples/host-app is a private workspace package that embeds dashboards in a mock corporate portal with its own fonts and loud global element rules (body, h1, button, input, a, td): - #/live: a model imported with ?raw and evaluated in the browser, with onItemClick; - #/bundle: a bundle generated by `pnpm bundle` (squiggle-rv, with a relative import), with a custom Ranking tab, renderItemName links, clusterOverrides and a fixed height; #/initiative/:id uses the bundle through ModelEvaluator.fromBundle without a dashboard; - #/large: 120 items (14,400 pairs), to check responsiveness. `pnpm screenshots` takes headless Chrome screenshots of the production build and checks style isolation against the dev server (computed styles of the host page with and without styles.css, and of the dashboard with and without the host CSS), interactions and responsiveness. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With NoInfer on tab / initialTab / onTabChange, initialTab="nope" is a type error instead of widening the tab id type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ngeset README: install, the scoped stylesheet and its limits, live vs bundle mode, the bundle format and CLI, customization props, custom tabs, the async evaluator API and the example app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 'core-fixes' of ../core-fixes: CLI: reject non-positive sample counts CLI: don't miss changes made while --watch is running CLI: keep the --json contract for import and unexpected errors Studio: let the linker's resolve throw again squiggle-lang: report linker resolve failures as import errors
* 'rv-lib' of ../rv-lib:
Relative values: document embedding ("Use in your app") and add a changeset
Relative values: infer custom tab ids only from `tabs`
Relative values: example host app (Vite + React)
Relative values: embeddable dashboard customization and async computation
Relative values: scope element selectors with :where() in styles.css
Relative values: bundle precision option; yield with MessageChannel
Relative values: scoped stylesheet for embedding (styles.css)
Relative values: async computation, precomputed bundles and squiggle-rv CLI
UI: wrap Tooltip and TextTooltip portals with the TailwindContext selector
State (view, unit, filters, search, sort, limit, columns, selection) as flat, serializable JSON with resolve/normalize helpers, a reducer, the selectRelativeValues pipeline (scope -> filter -> rank -> limit -> sort), pair requirements, URL search param and localStorage helpers, ModelEvaluator.peek and a memoized bundle loader. Exported from /core. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
serve (the default command, defined on the program itself), render, and stubs for run (WP8) and rv (WP6) that register nothing yet, so the CLI's behavior and help output are unchanged. findChrome and loadPuppeteer move from render.ts to browser.ts so the e2e harness can share them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gle views Rebuilds the dashboards on one provider that loads a source (definition + evaluator, bundle or spec), owns flat controllable state (state, defaultState, onStateChange, locked, apiRef), runs the selectRelativeValues pipeline and computes the pairs that mounted consumers register (ranking pairs first, then visible pairs, then everything in the background), staying on the previous snapshot while a new evaluator computes. Adds RelativeValuesSingleView, useRelativeValues (headless data with requires and local overrides), placeable primitives (search box, sort menu, unit picker, grid mode, cluster diagram, pair details, progress bar), presentation props (controls, components slots with Default, labels, format, display, onPairClick, themeClassName, compact density), CSS variables and data-rv-part hooks. Views read state instead of local component state; ForcePlot starts its d3 simulation in an effect so server rendering leaves no timers. Existing props, exports and legacy hooks keep working. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… SSR, types) Unit tests for state resolution and normalization, the reducer and legacy actions, URL and storage helpers, the pipeline (scope, filter, sorts, limit, limitBy, units, columns, cached compute) and pair requirements; jsdom tests for controlled/uncontrolled state, locking, aliases, apiRef and the headless hook; server rendering without timers; compile-time type checks. Adds jest-environment-jsdom and @testing-library/react (already in the lockfile). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
values/ (formats, every kind of dist, collections, functions incl. multi-parameter and @doc, plots, @hide/String/Date, all-hidden, result-only, empty), errors/ (parse, runtime with stack, missing import, import chain), imports/ (lib and ../../ imports), hub/ with a hub-stub/ for the local Hub stub, rv/ (values and fn forms, two specs, invalid spec, error cells, imports, zero ratio, unit change), text/ and tree/ (long name, deep nesting, same-name files). gen.ts writes rv/large-120.squiggle and the hidden and node_modules fixtures (node_modules can't be committed) into each fixture root. expectations.json lists views, default view, error and card counts and RV specs per file; every file was checked against the UI at b9591b3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The committed harness of spec 6.6 (packages/studio/e2e): - lib/harness.ts: the frozen Harness and Scenario types and startHarness(): an in-process Studio server on a temporary copy of the fixtures (isolated recent/state files, trackRecent off, a local Hub stub), and a page in a fresh context of a shared headless Chrome at DPR 1 with reduced motion, ?test=1 and recorded console, page, request and dialog errors. whenIdle() uses window.squiggleStudio.whenIdle() when present, else waits for the ready/error markers, a quiet DOM, fonts and two frames. - lib/invariants.ts: global invariants after every scenario (page errors, idle within 15 s, accessible button names via Chrome's AX tree, contrast >= 4.5:1, hidden/ignored files never in the tree, one command per keypress); contrast fails only with --strict. - lib/contrast.ts, lib/a11y.ts, lib/hubStub.ts, lib/contactSheet.ts, lib/ports.ts (STUDIO_E2E_PORTS), lib/scenarios.ts (discovery and needs:WPn detection), lib/expect.ts (expect that also works outside vitest, for e2e:shoot). - studio.e2e.test.ts runs every scenario plus harness self-tests (the contrast and accessible-name checks, and e2e:shoot --strict exiting 1 on a throwing page); run.ts passes --skip-needs, --strict, --only and --merged to it, since vitest rejects unknown options. - shoot.ts writes shots, a11y text, manifest.json and contact-sheet.png; serve.ts starts a fixture server for manual or agent driving. - scenarios/smoke.ts: smoke/start, smoke/open-file, smoke/url-initial-state (#17), shell/toast-position (#33), smoke/embed. The #17 and #33 scenarios need WP0 and fail at b9591b3, as they should. package.json gains the e2e, e2e:shoot, e2e:serve and typecheck:e2e scripts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the agent hooks h.whenIdle() now also waits for the shown view's [data-studio-ready] markers after window.squiggleStudio.whenIdle() resolves, and records a warning when the API answered early (runs report busy only from WP5 on). The keypress invariant finds new __studioCommandLog entries by their last id and time, since the log is capped. A self-test drives whenIdle, command, state and key against injected stand-ins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One page per scenario (#/s1 ... #/s10, plus a #/scenarios index): single views without chrome, scope and top-N, fixed/controlled unit and sort with a custom sort, slots and primitives in host chrome, URL state with Back/Forward, localStorage preferences, linked dashboards and apiRef, grid and plot options, headless custom views in bundle and live mode, French labels / number format / compact density / theme variables, and several dashboards sharing a bundle or a live evaluator plus server markup. The custom Ranking tab now uses ctx.rows and ctx.unit. screenshots.mjs captures every example as s<n>-<variant>.png, checks what each one claims, waits for [data-rv-ready], and runs the style isolation checks on every scenario page; results go to screenshots/checks.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ar on errors Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d add a changeset README section with the three kinds of input, a quick reference, one example per scenario (S1-S10) and a reference: state keys and defaults, the pipeline, controlled/locked rules, stale ids, computation, the URL format and the behavior changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
document.execCommand can't edit CodeMirror (Chrome edits it through EditContext), so menu Cut did nothing and menu Paste showed the "browser blocks pasting" toast even with clipboard access. The components' editor bridge gains editorClipboardText, deleteEditorClipboardRanges and pasteIntoEditor; Studio moves the text through navigator.clipboard and keeps execCommand for inputs and the text-file textarea. The blocked toast shows only when the browser refuses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The max history index lives in session storage, which outlives the document; a fresh navigation (no Studio state on the entry) starts a new entry with nothing forward of it, so the stored index is reset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fter the reconnect After a resync, every dirty working-set file that autosave may write and that has no pending timer or save in flight is flushed, so an edit made while the server was down reaches the disk without another keystroke (a file the agent changed meanwhile is a conflict by then and isn't written). The failure toast says the server isn't reachable and that Studio saves when the connection is back, instead of "Failed to fetch". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A .json buffer that doesn't parse is never autosaved (§2.5), but the status bar only said "Unsaved", and an agent reading the file meanwhile silently got the old content. It now has its own save state, "Not saved: invalid JSON · ⌘S", with a tooltip saying ⌘S writes it anyway. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Recent folders show their name with the parent as a dim, start-truncated hint and the full path as a tooltip, like recent files, so folders with a long common prefix no longer look identical. A "More…" item opens the Open Recent pick and shows its key (⌃R, Ctrl+R in the desktop app), in the browser menu and the native menu. The menus scenario now also checks that F10 focuses File without opening it and ↓ opens it at its first item (§3.3); the README says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The selector that hid "simulation #N in N ms" matched a colour class the contrast fix changed. The indicator now carries data-simulation-indicator and data-running; Output's List layout hides it by that, and the Playground's ready check reads data-running. New scenarios check the List layout and render's embed list have no visible "simulation #". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Show plot" was offered for every one-parameter function, so a function of a distribution (scaleDist(d) = SampleSet.fromDist(d) * 2) opened a "Function Display Failed" box. The card now makes the viewer's own first two calls (each end of the plotted range) and offers the plot only when one returns a number or a distribution. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rv-grid-cell test id sat on a display: contents wrapper, so agents could neither click a cell nor read its box. The wrapper is now the grid item itself. The grid's Row Settings / Column Settings / Cluster Diagram buttons keep their labels on one line and wrap as a row, so they no longer spill out of 32 px buttons in a 400 px view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At 720 px with the Explorer open, opening the code pane left a 100 px editor (the view kept its 360 px). An editor area narrower than a 240 px editor plus a 360 px view now puts the code on top and the view below, both full width, with a horizontal resize handle; side by side, the editor keeps at least 240 px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tooltips fade in through framer-motion's inline opacity, which the test-mode "no transitions or animations" rule can't stop (and reduced motion keeps), so e2e:shoot's contrast check sometimes caught one mid-fade (2.19:1). Under html.studio-test they're opaque from the first frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a window on the app folder In development targetsFromArgv dropped argv[0..1], so with `electron --inspect=5456 <app> <file>` the app path itself became a folder to open. The app is now the first positional argument after any switches, and an argument that resolves to app.getAppPath() is never a target. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tacked code pane Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"2x2" dimmed its x with opacity-60 on slate-600 (2.88:1); the Playground's collapsed previews showed it, and the strict contrast check failed the new focus scenario on a Playground with a table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rv-lib2's host-control architecture (core provider, flat state, pipeline, slots, presentation, primitives) is the base of every conflicted file; the Studio v2 relative-values changes are re-applied onto it: - Pair charts (#27): the slot-based DefaultPairDetails runs the standalone chart program from the evaluator's comparison function (PairCharts, SquiggleViewer) instead of re-running the model; the Playground code keeps the value_<id> fix; linker/sourceId are documented as unused. - Stale picks (#28): the core forgets uncontrolled unit, clusterUnits and selectedPair picks whose item is gone from the shown definition, so the list follows recommendedUnit and the sidebar stays closed when the item comes back. Controlled state and other keys keep raw ids (README). - Drawer (#29): under 900 px the list sidebar is an overlay drawer (Esc, close button focused, max height of the scroll viewport); "side" only. - n pairs (#15): list colors and the list requirement (listPairs) use each table's items x its unit, not items squared. - Test ids and hooks: rv-list-row, rv-grid-cell (on the grid item, min-w-0), rv-pair-sidebar [data-mode], rv-plot, data-rv-list, data-rv-sidebar-mode, data-rv-error-cell (with rv-lib2's title). - Grid toolbar wraps; contrast colors (ValueCell tiers 3-5 at 800, muted fallbacks at slate-500 / gray-600, red-700 errors); search box label "Filter items"; close label "Close pair details"; drawer "Pair details". - core.ts and ModelEvaluator keep both sides' exports and methods (CSV, pair charts, comparePairValues; loadRelativeValuesBundle, state, peek). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…size Beyond the viewport, Chrome painted the sticky sidebar from a stale raster (the charts' loading spinner) although the page showed the charts; the live-sidebar shot now resizes the viewport to the page instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the rv-lib2 merge - A failed comparison's sidebar has its close button (focused in the drawer) and shows the comparison's error. Clicks below a short drawer panel reach the list. - The QA #28 forgetting only drops what the user picked in the built-in UI (unit, clusterUnits entries, selectedPair). Values from defaultState, apiRef/actions, controlled state and locked keys keep their raw ids again, as the README says. - The drawer opens for pairs picked in the list or set by the host. A grid cell click only highlights the cell in a narrow list; a wide list still shows that pair in its column. A row's cell opens its own pair instead of toggling a hidden one. - Focus moves to the drawer's close button only after a click in the list, not when the host opens it (on mount, from a URL or apiRef). - A locked selectedPair is never a drawer: in a narrow list it goes below the tables, without a close button. - The drawer covers the tables, not the toolbar or the host's Toolbar slot. The list is a stacking context (isolation: isolate), so the drawer stays below a host's sticky header; --squiggle-rv-sticky-top sets where it sticks. - Tests: 12 list view cases in relative-values, and the Studio e2e scenarios rv/error-pair-sidebar and rv/grid-pick-then-list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in main's follow-ups to #4173-#4176: Studio server error hiding, "use client" markers, Hub imports against this Hub instance, the relative-values pair chart fix for numeric values, linear-time CLI hints and lint depending on ^build for Studio and Electron. Conflicts: - packages/studio/src/server/server.ts: main's rule (only HttpError, PathError and HubError messages reach the client; anything else is logged and answered "Internal server error") now lives in studio-v2's sendApiError, so it covers every API route, and in the static and top-level handlers. Abandoned requests (#49) aren't logged. New unit test with an unreadable file (raw EACCES). - relative-values ListView/sidebar.tsx: keeps studio-v2's standalone pair charts (relativeValuesPairCharts, which already handle numbers). The "Open in Playground" link uses main's lib/pairSquiggleCode.ts, which takes studio-v2's fuller fix (the scatter plot uses the Dist-wrapped values too). Semantic clashes: - components: studio-v2's new index exports (CodeEditor, editorCommands, cursorEventExtension) get "use client" like main's. Without it the website's docs page (a server component) fails to build (useImperativeHandle). - Hub: drop the linker and sourceId props main passed to RelativeValuesProvider; studio-v2 deprecated them as unused (the pair charts don't re-run the model). Imports still resolve against this Hub through ModelEvaluator.create's linker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getCodeWarnings removed each closed /* */ on a line by rebuilding the rest of the line, so a line with many comments took quadratic time (`x = 1 ` + "/* */".repeat(100000): 3.3 s in getCodeWarnings, 4.5 s for `squiggle check`). It now copies the code between comments once. The output is the same as before (checked against the old loop on every string of "/", "*", "a" and " " up to length 9, plus random ones). The pathological-input test covers 200k comments on one line (10.7 s before, 10 ms now), and a new test covers back-to-back comments and one left open across lines. This applies to main as well (631309b). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
serveStatic answered every read error with 500 "Squiggle Studio client is not built (looked in <absolute path>)", so an unreadable asset or index.html (EACCES, EISDIR, EMFILE) was misreported, leaked the install path and was never logged. Now only ENOENT/ENOTDIR on index.html gets that message; any other error goes to server.ts's catch, which logs it and answers 500 "Internal server error" (main's rule). New tests: an unreadable asset and an unreadable index.html get the generic answer and are logged; a missing build still says how to build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since the merge of main's error hiding, a save into a read-only folder
toasted only "Couldn't save x.squiggle: Internal server error". writeFile
and renameFile now turn EACCES/EPERM and EROFS into a 403, and
ENOSPC/EDQUOT into a 507, PathError that names only the root-relative
path ("Studio can't write values/misc.squiggle: permission denied",
"Studio can't move a.squiggle to ro/a.squiggle: permission denied",
"... the disk is read-only", "... the disk is full"). This also covers
Hub import, which writes through writeFile. Other errors are still
logged and answered "Internal server error". README documents both.
New tests: PUT into a chmod-555 folder (overwrite and create), rename
into and out of it, and ENOSPC/EROFS from the temp-file write all give
the reason, write nothing and log nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: db07936 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
| hostKind: Host, | ||
| settingsPath: string = SETTINGS_PATH | ||
| ): string { | ||
| const cell = (s: string) => s.replace(/\|/g, "\\|"); |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR makes Squiggle Studio work like a real text editor and makes the relative-values library controllable by host apps. It builds on #4173–#4176 and is up to date with
main.Studio is the local authoring tool, often used alongside a coding agent that edits the same files. The relative-values library is what a separate app (for example a corporate site) embeds to show dashboards without running Squiggle Hub.
Squiggle Studio v2 (
packages/studio,packages/studio-electron)pageshow, Studio re-checks the folder, the file tree and every open buffer. Settings and UI state live in~/.squiggle-studio/, shared by every tab and window.--hostrequires--allow-network, the server checks the Host header, and unexpected errors are logged without being sent to the client.Agents can see outputs and review Studio
squiggle-studio run <file> --jsonprints values with Studio's import rules.squiggle-studio rv <file> --format json|csvprints the relative-values pair matrix.squiggle-studio render <file> [--view relative-values --tab grid] --out x.png --jsonrenders any view to a PNG and reports errors.data-testids and ARIA roles,<html data-studio-*>state attributes,window.squiggleStudio.{whenIdle, getState, commands, getRunJson, getRvMatrix}, URLs that encode the current view, and Run ▸ Copy for Agent.packages/studio/e2e(puppeteer-core + vitest) with fixture models;pnpm e2e:shootscreenshots every view and state and writes a contact sheet.packages/studio/CLAUDE.mdand the plugin skills explain how an agent uses it.Relative values: host control (
packages/relative-values)state/defaultStateand getonStateChange, can lock keys, and can drive the dashboard throughapiRef. URL and storage helpers are included.controlshides any built-in piece,componentsreplaces the toolbar, cells, pair details, empty and error states, andlabels,format, a compact density and CSS-variable theming are available.RelativeValuesSingleViewshows one view with no chrome. Small controls (search, sort menu, unit picker, progress) can be placed in the host's own layout.useRelativeValuesreturns filtered, sorted rows with their values, in live mode and in precomputed-bundle mode.examples/host-apphas one page per scenario (S1–S10).pnpm screenshotsruns 107 checks, including that host and dashboard styles don't leak into each other.Fixes
resolveerrors become import errors (from CLI: commands and output for scripts and coding agents #4173).CodeEditor, the editor-command bridge and the cursor extension are exported with"use client"(the websitenext buildneeds it). Toasts get roles and test ids, and several low-contrast texts are darker.Try it
After
pnpm installandturbo run build --filter=@quri/squiggle-studio...at the repo root:For agents, from
packages/studio:node bin/squiggle-studio.js run examples/main.squiggle --jsonandnode bin/squiggle-studio.js render examples/relative-values/quri.squiggle --view relative-values --tab grid --out grid.png. The library's scenario pages:cd packages/relative-values/examples/host-app && pnpm dev, then open#/scenarios.Testing
All of these pass locally on the final commit:
--strict;e2e:shoot --strict(180 states, no contrast, page-error or unnamed-button findings); lint andtsc -b.tsc --noEmitandpnpm lint.next build(72 pages).On top of the tests, independent agents reproduced each QA finding before fixing it, adversarially verified each merge (Studio packages, the relative-values host-control branch, then
main), and checked the main flows by hand in a browser.Not verified
Notes for reviewers
The diff is large: much of it is e2e fixtures and screenshot baselines (
packages/studio/e2e/fixtures/__golden__), tests, and the host-app example. Suggested order:packages/studio/src/shared/commands(manifest),src/client/commands(registry, dispatcher, focus),src/client/lib(workspace model and sync) andsrc/server, then the shell components, thenpackages/relative-values/src/stateandsrc/components/host(host-control state and pipeline). Changesets are included for each published package.🤖 Generated with Claude Code