mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-07 12:31:56 +00:00
main
331 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8c32875160 |
Preferences: save and restore panel layouts by workspace (#11306) (#12587)
* claim: #11306 * Preferences: save and restore panel layouts by workspace (#11306) * Preferences: keep layout keys out of the viewport and fix validation checks * Preferences: save other settings when panel layout conversion is refused * Preferences: keep layout key handling scoped and deduplicate web warnings * fix: retain imported panel column widths * fix: use shared panel preference controls and tokens * test: cover custom-height rows in the UI gallery |
||
|
|
5818c3eb3b |
Kernel tests: export discarded tool operations and handled refusals (#12099)
* claim: preserve complete kernel-case traces Co-Authored-By: Codex <noreply@openai.com> * fix: preserve complete kernel-case execution traces Co-Authored-By: Codex <noreply@openai.com> --------- Co-authored-by: Codex <noreply@openai.com> |
||
|
|
b5a5412d48 |
Command line: run --out exports meshes at a chosen quality with --quality fine or finest (#12270) (#12386)
* claim: #12270 * Test: run --out writes meshes at the --quality asked for (#12270) * Command line: run --quality standard|fine|finest sets the mesh quality of --out exports (#12270) file.export already took quality, but run --out passed the path alone, so a fine STL needed a script. --quality is checked before anything runs and goes to every --out export; the usage text and the getting-started guide say so. * Command line: run refuses --quality given twice, and the docs say the mesh chord never goes under 1 µm (#12270) --------- Co-authored-by: Ahmad Khan <ahmad.khan@shiritai.co> |
||
|
|
e04bc060f6 |
Web app: clippy for wasm passes with -D warnings (#12222) (#12251)
* claim: #12222 * Web app: clippy for wasm passes with -D warnings (#12222) cargo clippy -p solidcraft-web --target wasm32-unknown-unknown stopped at drop(file) in safe_write.rs and showed 12 more warnings only that target has. Each is fixed where it is: - safe_write::create_atomic and control_auth::write_token_file_with close their File by scope (a block; Result::map's closure) instead of drop(), which clippy's drop_non_drop flags where File is a stub. - Native-only imports, the AHEAD constant and the point-cloud worker's Condvar are behind cfg(not(target_arch = "wasm32")); Exported::bodies and Start::Later, read or built only natively, allow dead_code there. - home::ask_present uses ctx in its browser branch; the browser blur stub's begin returns a Scope, as the native one does. - The web app drops three map_err(JsValue::from) on dyn_into's error, which is a JsValue already. Native builds behave the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: thocsmith <193096300+thocsmith@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
283ccd0b21 |
Web: tab title follows a renamed design (#11375) (#11381)
* claim: #11375 Co-authored-by: Cursor <cursoragent@cursor.com> * Web: tab title follows a renamed design (#11375). When an OPFS design is renamed, open tabs keep the new path and a borrowed design name so the tab matches the file list. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
d580c4256c | Evaluation: add --validation to solidcraft-cli eval and skip largest-fit search when validation is off (#12136) | ||
|
|
f1159a5d51 |
Added mesh import units for STL and OBJ (#10406)
Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
7e002f182d |
Opening a design from a newer SolidCraft: it opens with a notice instead of being refused (#12166)
* Open a design from a newer SolidCraft with a warning instead of refusing it A newer format number or an unknown change no longer blocks opening. What this version can read opens; a feature it can't read stays on the timeline with an error, and fields, sections and container parts it doesn't know are named in the reply and written back by Save, under the file's own number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Saving a newer design from the CLI, and editing a feature this version can't read `run --save` writes a design holding a feature only a newer build can build; `--out` still stops, since it would export the part without that feature. Edit Feature on such a feature says why instead of opening the palette. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Translate the newer-design notice for the complete catalogs; clippy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
16f3b939b3 |
Update egui/eframe to 0.37 (#9933)
* Update egui/eframe to 0.37 (WIP) egui, eframe and egui-wgpu 0.36 -> 0.37. SolidCraft's egui-wgpu patch is rebased onto 0.37.0; the vendored accesskit_macos is dropped (0.27.2 has the #7535 fix). Not finished: 36 solidcraft-ui-egui lib tests fail, and the full lib run crashes with SIGSEGV when tests run in parallel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: clear texture deltas that egui 0.37 refuses to drop Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drop non-finite pointer input before egui; keep test time monotonic egui 0.37 debug-asserts on NaN wheel deltas, on time moving backwards in the pointer history, and on add_space inside a Grid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hold a click that follows typing in the same frame until the next frame egui 0.37 drops a field's focus on a click elsewhere when the field first reads its style, before it reads the frame's typing, so text typed in the frame of a click was lost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Name a document tab's close button "Close tab" egui 0.37 names an unnamed widget after its tooltip, so the tab's × became a second control named Close beside a dialog's. The space mouse guard test now clicks where each button was: by name it found nothing behind the guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Turn on egui's default_fonts: 0.37 no longer does The built-in Hack is the UI's symbol fallback (#2955). The app got it through eframe, but solidcraft-ui-egui on its own (its tests) did not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Text menu: act on the selection the menu click took the focus from egui 0.37 drops a field's focus on a click elsewhere before the field draws, and an unfocused field loses its selection, so Copy, Cut and Paste from the right-click menu saw no selection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Placement field: keep its native height under egui 0.37's interact floor Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: pin the theme's 2/3 point radii; measure Japanese ribbon labels with a Japanese face egui 0.37 rounds widgets 4 points by default, which the theme deliberately does not follow. The ribbon test measured Japanese with missing glyphs, whose width changed in 0.37; with BIZ UDPGothic, 駆動 wraps after its particle like 変換 does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: click the render inspector's Perspective checkbox by position egui 0.37 orders the accessibility tree differently, so the name found the stage's Perspective segment first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Translate "Close tab" for the complete catalogs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Point the focus workarounds at emilk/egui#8763 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cargo.lock: update only what egui 0.37 needs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Web: Id::new -> Id::unique for the wasm-only windows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Command search: keep its hint centred under egui 0.37's interact floor Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * egui 0.37 renames in code main added since the branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * egui 0.37 renames in code main added since the last rebase Id::new to Id::unique, ui.id() to ui.scope_id(), WidgetType to Role, the wheel events' source, and the button stroke read from the widget visuals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Review: disclosures report expanded, late screenshots drained, chat width reset on both sides A disclosure's node says whether it is open as expanded, as egui's own collapsing header does. The screenshot channel is drained every frame, so a shot whose request was cancelled is dropped. Resetting the chat panel's width forgets both sides' state, as before 0.37, and the request stays with its window when tabs switch. Tests main added since the last rebase clear their texture deltas. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
733f0b2254 |
MCP: opt-in compact inspect_design and tool output (split from #2901) (#3144)
* MCP: compact inspect_design and tool JSON, off by default (--compact / SOLIDCRAFT_MCP_COMPACT) Split out of #2901 per review: the smaller inspect_design changed the default for everyone, so it is now an MCP server option that is off unless asked for. - solidcraft-cli mcp --compact, or SOLIDCRAFT_MCP_COMPACT=1 (true/yes/on; 0/false/no/off/empty leave it off; anything else is an error at start-up). solidcraft_mcp::Options::resolve is the one place that decides, so an app Preferences setting can feed it later. - With it on, inspect_design summarises sketches (id, name, plane, counts of points, curves, constraints and profiles, dof, status), returns full contents only for the sketches named in `sketches` (names/ids, true, false or "summary"), takes a `bodies` filter and measures only those; unknown names list the existing ones. Tool JSON is compact rather than pretty-printed. - Off, tools/list, argument checks, inspect_design and tool JSON are exactly as before. - document.inspect gains an opt-in `sketch_counts` (plane, points, constraints); without it its reply is unchanged. Tests: both modes in the MCP suite, Options parsing, and the CLI flag and variable end to end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * MCP compact output: a Preferences setting too (AI › Agents through MCP) The desktop app's Preferences › AI gets "Compact output" under a new "Agents through MCP" section, off by default and shown with the chat off too. The app reports it on the control channel as `mcp.options` ({"compact": bool}); the agent chat lets its own MCP server read it. A bridged MCP server (`solidcraft-cli mcp --connect`) asks the app when an agent first lists or calls a tool and keeps the answer for the session, so the tools it listed keep their shape. An app not running yet, or busy past 2 s, is asked again on the next call; an app from before the setting counts as off. `--compact` and SOLIDCRAFT_MCP_COMPACT=1 still turn it on whatever the app says. Tests: the server follows the app's answer (waits for it, keeps it, the flag still wins); the checkbox reaches `mcp.options` and Restore Defaults clears it; the chat gate allows `mcp.options`. docs/mcp.md and docs/control-protocol.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * MCP compact output: review fixes (mcp.options never blocks, headless test, locales, prefs doc) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * MCP compact output: the setting is a section of the AI page; the bridged test answers mcp.options Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * MCP compact output: the preference test sets it through ui.prefs; rustfmt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * MCP compact output: look for a newly started app before asking its options; counts from sketch.inspect for an older app Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a6e7126bf1 |
Closed stderr pipes no longer crash the app, CLI or MCP server (#10111) (#10126)
* Stderr: a closed stderr no longer stops the app, the CLI or the MCP server (#10111) `eprintln!` panics with "failed printing to stderr" when stderr is a pipe its reader has closed. A process another program starts writes into such a pipe: `solidcraft mcp` under an agent, and the app that one starts, which inherits it. #10111 is the app stopping that way while an agent (Codex) connected. `solidcraft_geom::note!` (re-exported as `solidcraft_engine::note!`) writes the same line and ignores a failed write, as the CLI's logger already does; the app, the CLI, the MCP server, the crash hook and the crates they run use it instead of `eprintln!` outside tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Stderr: test that a closed stderr leaves the CLI's exit code as it is (#10111) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Stderr: the old name's alias and the fuzz command don't stop on a closed stderr either (#10111) `solvecraft-cli` writes its deprecation notice before it starts `solidcraft-cli`, so an agent still configured with the old name got the panic before the MCP server started (found in review). It and the fuzz command now write through `note!` too, and the test covers the alias. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1a27c8399d |
Linux 3D mouse: push/lift and tilt/twist axes are no longer swapped (#3523) (#4833)
* claim: #3523 * Linux 3D mouse: decode spacenavd's axes in its documented frame (#3523) spacenavd reports x to the right, y up and z away from the user, with the rotations about those axes. The decoder treated y as away and z as up, so with a stock spacenavd push and lift traded places, as did sideways tilt and twist. Map the daemon's axes to the raw HID frame as translation [x, -z, -y] and rotation [rx, -rz, -ry]. The decode still runs before a device's calibration and the per-axis motion choice, so both apply unchanged on top of the corrected reading. The tests now move each daemon axis on its own, in both directions, so a swap of two axes can no longer pass. |
||
|
|
c4a63a1d43 |
GPU path tracer: don't rerun a setup that crashed in the driver (#9236) (#9666)
* claim: #9236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * GPU path tracer: don't rerun a setup that crashed in the driver (#9236) A driver that takes the process down while it compiles the path tracer (RADV on an RX 7600) is beyond error scopes and unwind guards. A running setup now keeps gpu-path-setup.json (its adapter key) in the settings folder, removed when the setup answers or the app closes normally. A launch that finds it for the same adapter leaves the setup failed with an explanation and a Try again button; CPU ray tracing stays available, and a driver update changes the key and so tries again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4ab1640dc8 |
OCLP detection: run /usr/sbin/nvram by its full path (#10109)
The probe ran `nvram` through PATH, which a Finder launch may not list and which a look-alike earlier in it could change. The doc comment that said exec would need unsafe is replaced by a description of what the code does. Behaviour is otherwise unchanged. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
3aee35fe01 |
Web app: keep unsaved work in the browser instead of the leave-page box (#10220) (#10221)
* claim: #10220 * Web app: keep unsaved work in the browser instead of the leave-page box (#10220) A new preference, off by default, on Preferences > General > Saving (web app only): "Keep unsaved work in this browser instead of asking when the tab closes". With it on, each open design with unsaved changes keeps a copy in the browser's private storage (keep-<page>-<design>.sc/.json), written once the design has been still for 0.8 s (at most 4 s after the first change it lacks) and again on pagehide or when the page is hidden. While every copy is current, beforeunload doesn't ask. A copy goes when its design is saved, closed, or the preference is turned off. The next visit offers the copies newer than their design's last save in SolidCraft's own window, "Restore unsaved design: <name>, changed <time>?", one window for several, with Restore, Open saved version, and Discard. A page holds a Web Lock while it runs, so another open tab's copies are never offered or removed. With the preference off, the single autosave and the browser's prompt work exactly as before. * Web app: the restore window comes from the UI kit's recovery module (#10220) The kit's text and divider helpers are private to the UI crate, so the "Restore unsaved design?" window moves to recovery_ui (browser_copies_window), beside the desktop recovery window, and the web shell only maps its answer. Also keeps the window handle for the page listeners instead of using the one moved into the app. * Web app: restore window buttons sit in one row (#10220) * Web app: keep unsaved work and still ask, unless told not to (#10220) Keeping a copy in the browser no longer replaces the browser's leave-page box by itself. A second option, shown once work is kept and on by default, leaves the box in place; off, the box appears only while a copy isn't current. * Web app: kept-copy review fixes (#10220) A failed write of a kept copy waits 30 s before the next try, a recovery copy is dropped only after the remembered saved file has opened, the preference is documented under its current label, and the ui.prefs list is sorted again. |
||
|
|
0690ee5875 |
Web app: run as a modelling editor inside another page that keeps the design files (#8517) (#11273)
* claim: #8517 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * web: editing embed for host pages (?embed=editor, #8517) A host page that keeps the files itself and frames SolidCraft for modelling needs the modelling UI without the application chrome. `?embed=editor` runs the web app as such an embed: - no application bar (logo, File menu, document tabs, update, help, report a bug) and no start page; the layout is docked; - the ribbon, browser, timeline and properties stay; Ctrl+Z and Ctrl+Y still undo and redo; - Ctrl+N, Ctrl+O, Ctrl+S, Ctrl+W and Ctrl+, are left to the host; - nothing is kept in the browser (no stored designs, autosave, recovery prompt, preferences or leave-page prompt), as with the existing viewer embed. The existing `?embed&open=` viewer embed is unchanged. SolveApp gets one flag, `embed_editor`, for desktop and test hosts too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * control: document.export / document.import / document.saved for a host (#8517) An editing embed's host keeps the design itself. Three control methods (window.solidcraft.request in the browser; the desktop control channel too; not the agent chat's, whose method list is unchanged): - `document.export` -> {name, revision, dirty, format: "json", text}: the active design as a `.json` design file holds it. `revision` is the one `document.revision` answers, so a host that follows the design compares one number. - `document.import {text, name?}`: replaces the active design with that text, timeline and parameters as saved, opened saved with a new `document_id`. It runs as a design opened in a tab of its own, with the old tab closed without asking, so whatever was open in the old design leaves with it. A text that does not read leaves the design as it was. Waits behind a file loading or a design change, as commands do, and evaluates on the window thread. - `document.saved {revision}`: the host saved that revision; refused when the design has changed since. They are control methods, not commands: they act on the app's open design. The browser API test gets a round trip through all three. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: aduijs <7501203+aduijs@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d034633265 |
Desktop: the file chooser says when it is not the desktop's own (#10618)
* test: the file chooser says when it is not the desktop's own (#10159) rfd falls back to zenity in silence when the XDG portal is not there, and the user gets a foreign window with no explanation. The host must be able to say so. RED: the test fails with 'without a D-Bus session bus the file chooser is not the desktop's own: the user has to be told'. * fix: say when the file chooser is not the desktop's own (#10159) rfd opens the file chooser through the XDG desktop portal; without the portal it falls back to zenity, a window that does not belong to the app, and the app never says so. The user gets a foreign window and no explanation. The host now asks the session bus, on a thread of its own, whether the portal is there, and keeps the answer. When a file is opened and the chooser is not the desktop's own, the reason goes to the session log (Help > Messages), which is where a user looks for it. * fix: the file chooser messages read as notices, not assertions (#10159) The maintainer asked for two things on the pull request: the strings of the pending catalogue read like a test assertion ("the user has to be told...") rather than a notice a person would see, and the doc comment above desktop_chooser_fallback was there twice. The three reasons are now plain messages, in every locale of the file: they say what the chooser is and why, and stop there. The doc comment of the function is now the only one, and it says what it did. |
||
|
|
dcbedbd805 |
Don't blindly reopen a design that stopped the last session (#5064) (#10443)
A design that crashes or hangs the app while it is being reopened was reopened blindly every launch, bricking the app each time. A launch that reopens designs now marks them in the recovery folder; the marker is cleared as each design finishes opening. A launch that finds the marker left behind skips those designs once and says which one the last session stopped on, so the app starts usable and the loop breaks. Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com> |
||
|
|
549aef17a5 |
Changing anti-aliasing no longer resets the saved window system (#10116) (#10147)
* claim: #10116 * fix: preserve the saved window-system preference at startup * fix: preserve graphics preferences in safe mode |
||
|
|
c5bc849913 | Blur: keep themed backing and notice actions accessible (#9810) | ||
|
|
fbd30b0bd0 |
Panels: inspect and change layouts through commands (#11304) (#11454)
* claim: #11218 * Panels: add shared layout rules and retained content runtime * claim: #11304 * Panels: inspect and change layouts through shared commands (#11304) * Panels: account for layout commands in geometry coverage |
||
|
|
603c974f37 |
Fix main's build: the CLI imports the B-rep type from its new home (#11634)
The CLI's kernel_cases_export.rs still imported Brep from solidcraft_geom::brep after the layer split moved it to solidcore_record::brep. Changes this one import. |
||
|
|
dc38349cbf |
Layer split stage 1: the B-rep record, native body file and Vec/Plane come from solidcore-record (#11496)
* Kernel: the B-rep record and the native body file come from solidcore-record SolidCraft's portable B-rep record (`solidcraft_geom::brep`: brep.rs, topology, history, envelope, part, curve2) and the native body file (`solidcraft_kernel::native`: the tagged binary codec and the container) move to solidcore's `solidcore-record` crate: geometry as data, with structural validation and no maths (solidcore's docs/layers.md, stage 1). Every caller uses `solidcore_record::brep` and `solidcore_record::native`; the old copies are deleted. What computes from coordinates stays with the kernel, in geom's `brep`: - `brep::check`: the record's geometric checks (orthonormal frames, non-degenerate planes, unit revolution axes, non-singular placements). `check::validate` and `check::validate_built` are the record's validation and these together, and every caller of `Brep::validate` uses them, so what is accepted is unchanged; - `brep::label`: a surface's natural ranges (a cone's apex), `curve_interval`, `edge_curve_interval` and `form_turns`; - `brep::pcurve::eval`: a pcurve's point; - `geometry::check`, `placement::moved` and `split_edges` become functions on the record. The kernel's native module keeps the conversion between a body and its saved record (metadata.rs) and checks the decoded record's geometry. `native/analytic.rs`, which no module declared, is deleted. solidcore-record is pinned to its rev on solidcore's `layer-record` branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT * Geometry: Vec2, Vec3, Plane and the profile types come from solidcore-record The plain data of vectors and points, sketch planes and planar profiles (`Seg2`, `Loop2`, `Region2`) moves to `solidcore-record`, with its constants and component-wise operators only (owner decision, solidcore layer split). Their geometry stays in geom as traits: `Vec2Geom`, `Vec3Geom`, `PlaneGeom`, `Seg2Geom`, `Loop2Geom`, `Region2Geom`, `Seg2ArcFit`, `Loop2ArcFit` and `Region2ArcFit`, in scope with `use solidcraft_geom::prelude::*`. Every caller names the types from `solidcore_record`; geom no longer defines or re-exports them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT * Kernel: pin solidcore-record to solidcore main f5e8e9a29 solidcore-record is merged on solidcore main (tangency-labs/solidcore#2519); its crate there is identical to the rev pinned before but for its clippy configuration. The lock is regenerated against the merged rev. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT --------- Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1b51a0678b |
Merge pull request #11568 from tangency-labs/jyviko/rig-export
Kernel rig: export each case's kernel operations as a corpus for solidcore's rig |
||
|
|
e438a9bbea |
Web app: choose the release, the release candidate or the edge build (#11419)
The web app is now offered at /app/ (latest release), /app-rc/ (newest release candidate) and /app-edge/ (current main), each with its own browser storage. /app/ is built from the latest release's web zip instead of main. |
||
|
|
7b0906b918 |
Kernel rig export: review fixes
- Picks run on a copy of the operand: a pick builds query caches on the body it reads, so the traced body stays as an untraced run leaves it. - An untrim and an extension record their picks with the pickers they run (`face_at`, `nearest_edge`). - A final body matches the newest export of its record that an operation made. - A stopped run is the kernel's when the last failed operation is followed by copies only. - A re-export removes each case's old entry first; fixes.json is written whole. - The manifest names no commit for a copy without git, never an empty one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ |
||
|
|
5fd0ac98a9 |
Kernel rig export: a failure is the kernel's when the last traced operation failed; grades SolidCraft computes are listed apart
An entry's operation is expressible when the kernel accounts for its result; the values only SolidCraft grades (continuity, meshes, files, command answers, merged counts, SolidCraft's wording of a refusal) are listed under `ungraded` instead of making the whole case inexpressible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ |
||
|
|
9c65a30265 |
Kernel rig: export each case's kernel operations as a corpus for solidcore's rig
`cargo xtask kernel-cases [--ladder] --export DIR` runs the rig with the kernel's operations traced (`smlib::trace`) and writes one entry per case: the operation, or chain of operations, that made the bodies the case ends with; each operand that came in as a record, as that record; the parameters, with every pick by points also given as the topology index it picked; the fix set; the case's expected values and the rig's result. A case no kernel operation alone accounts for is written too, marked not expressible with its reasons. DIR/manifest.json counts them per area. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ |
||
|
|
c5ccb9201d |
Wave 43/45: smlib: rustfmt after the C++ kernel's removal (and 1 more)
2 commits, squashed; the originals stay reachable from branch jyviko/merge-main-develop-solidcore (#11360). - |
||
|
|
28e983e191 |
Wave 39/45: diagnostics.kernel takes a typed contract, as main's other diagnostics commands do
1 commits, squashed; the originals stay reachable from branch jyviko/merge-main-develop-solidcore (#11360).
-
|
||
|
|
ea772a0438 |
Merge develop into main: SolidCraft on the SMLib kernel (v1.1.0-alpha) (#11351)
* UI: rim and wall picking tests for a circle of arcs that is one wall A circle drawn as two half arcs now extrudes to one full-turn wall with its seam edge, and each rim is one closed edge (tangency-labs/smlib#67). The tests keep their intent on that body: a rim clicked once is one item, measured and highlighted whole and picked from either side; a wall picked either side of its seam is the same surface, lit whole, and toggles off from the other side; every face is its own patch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fillets: a later round at a corner where earlier rounds meet blends them again with it The owner's ruling of 2026-10-30: a later fillet on an edge at a vertex where earlier rounds already meet in a mitre makes the corner rounding all of them at once makes, one smooth patch, the mitre gone. A body made by constant-radius rounds keeps what they were made on and which edges they rounded at which radius; a later round reaching such a corner blends the earlier rounds' edges (from the earliest of those meeting there on) with the new edges in one SMLib blend of the body before them. The result's history is put over the body the later round was given: an earlier round's face is that face trimmed anew (its name kept), the corner patch and the new rounds are the later fillet's. A picked mitre seam has no round of its own: it is refused unless a new edge of its radius meets it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: a later r3 round at an r2 mitre, through the Fillet feature, makes the all-at-once corner Two box edges rounded r2 in one fillet, then the upright edge at their mitre r3 in a second fillet (tangent chain on, so the seam comes with it): 10 faces, 7928.743041 mm³ as fillet_radii of the box gives, the r2 rounds' names kept, the r3 round and the corner patch the second fillet's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Kernel: a body's record of rounds goes with its history, and can be read back The rounds that made a body are over the body its history is over: a new history (an operation carrying a step's result back to its own operands) leaves the record out, so a later round never reads one history against another body. Body::rounds gives the record and Body::with_rounds_of takes one back, checked (edges of the body before them, a fillet's radii, the body's history over that body), for a saved model to keep it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Model cache: a body made by rounds keeps them, so a later round blends them the same after opening A design gave another body when opened from its model cache: the rounds a body was made of lived only in memory, so a later round at their mitre was built sequentially (or refused, as too large) after opening and blended with them when evaluated anew. The saved model now records, per kernel body made by rounds, the body they were made on (a place in the bodies, stored once though no state holds it) and the edges with their radii; reading it attaches them back down each chain, and a thinned model keeps those bodies with the bodies made from them. Model cache format 7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: a corner patch of rounds 2/3/4 takes no more than the strips leave in its block three_rounds_at_a_corner bounded the patch's extra cut at 3 mm³ beyond the strips, a figure with no derivation; SMLib's all-at-once body at radii 2, 3, 4 takes 3.099 (V 7878.579574 against strips 7881.678). That body is the strips' outside the block its patch lies in: its rounds are the strips' cylinders ending at a's section y = rc, b's x = 20 − rc and c's rails where they meet a's and b's (the patch's four corners, now asserted), so the patch takes at most what the strips leave in that block (11.86 at 2/2/3, 32.69 at 2/3/4). The lower bound is that block; the upper one, at most the strips' volume, stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: the io crate's hand-built saved models list their rounds, as format 7 records do Model cache format 7 ( |
||
|
|
c4808e4715 |
MCP: clean up empty scratch folders on shutdown (#7836) (#7929)
* claim: #7836 * fix(mcp): remove empty scratch directories on command exit |
||
|
|
21fc384e40 |
Control channel: a new connection has five seconds to show its token (#10961)
A client could connect and send nothing, or only blank lines, and hold one of the 16 connection seats for as long as it liked. All reads before the token now share one five-second deadline; the connection is then closed without a reply. An authenticated connection may idle as before. `solidcraft-cli mcp --connect` used to open a connection at start and hold it until the first tool call. It now only checks that the app is there, and the first call connects. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
425417b36a |
Web app: open a large design without freezing the page, with progress and Cancel (#10198) (#10211)
* Engine: open a design a feature at a time (#10198) OpenSteps splits doc.open of a design into steps for a caller that must keep working meanwhile (the web app, which has one thread): start reads the file, each step evaluates the next feature by moving a copy's history marker one feature on (so every result before it is reused), and finish puts the design in the session's place with doc.open's reply. The next feature's imported STEP text is offered first, so a caller can read it in parts. Session::opened and opened_at are split into small helpers so both paths build the session the same way; doc.open is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Web app: open a design a few features a frame, with progress and Cancel (#10198) On the web, a design to open is now a staged file load like a STEP file (#1376): its features are evaluated for about 30 ms a frame through OpenSteps, a feature's imported STEP file is read a solid per frame first and held so the feature finds it read, the progress card counts the design's features, Cancel works between steps, and the meshes are made a few per frame afterwards. The desktop app keeps its worker thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Web app: design links and the files window open designs with progress and Cancel (#10198) Design links (?open=URL, as embedded viewers use), the files window and designs opened from the computer went through the one-shot open_path, which froze the page on a large design. They now use the staged open; a design link becomes an unsaved copy under the link's name once the open's reply arrives (SolveApp::open_path_answered). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
bba2fc1400 |
Report omitted print-face selections in command-script exports (#10230) (#10235)
* claim: #10230 * fix: report omitted print-face assignments in script exports |
||
|
|
538d7fcb43 |
Language: follow the system language by default (#10138) (#10183)
* claim: #10138 * Follow the system language on a first start (#10138) The language preference defaults to a new 'system' value: the OS locale (sys-locale) is matched against the catalog list - exact tag, base language, then English - and a System choice sits at the top of the Preferences language list. A saved language still wins. * Drop the explicit deref clippy flagged; regenerate THIRD-PARTY-LICENSES.txt for sys-locale --------- Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com> |
||
|
|
34c378c063 |
Save As: rename the design to the new file's name (#4907) (#10036)
* claim: #4907 * Save As: rename the design to the new file's name when its name was borrowed (#4907) --------- Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com> |
||
|
|
1bcc1125f0 |
Graphics: apply the chosen card, or say when it could not (#8341) (#9252)
* claim: #8341 Co-authored-by: Cursor <cursoragent@cursor.com> * Graphics: keep a preferred card when surface marks it incompatible (#8341) Prefer the saved adapter by exact then case-insensitive name, including when only listed as surface-incompatible, and explain in Preferences when another card is still in use. Co-authored-by: Cursor <cursoragent@cursor.com> * Format and clippy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2f6ca7e46b |
Linux: optional system title bar (server-side decorations) (#9263)
On Linux the window always had no OS decorations and drew Windows-style caption buttons in the application bar. SOLIDCRAFT_TITLEBAR=system, or "titlebar": "system" in graphics.json, now opens the window with the desktop's own title bar and buttons (server-side decorations where the compositor offers them). The default is unchanged; Windows and macOS are unaffected. Claude-Session: https://claude.ai/code/session_01Gnm1n2PVPqghMuR9kHWXfh Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b8eb8d262c |
Settings: saved to disk as soon as they change, not only when the app closes normally (#8275) (#9711)
* claim: #8275 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Settings: written to disk in the frame they change, not only at a clean exit (#8275) eframe saves app.ron every 30 s and when the window closes normally. A change made since the last save was lost whenever the app didn't close normally: ended from the Task Manager, stopped by a sign-out or shutdown, or crashed or hung in the graphics driver on the way out. The next start then showed the old settings (View navigator back to Axes, Display quality back to Normal). The desktop app now writes its settings and flushes them in the frame they change (frames with input and the one after), as the web app already does (#8644). Unchanged settings are not written again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Settings: write at most once a second, and only after input that can change them (#8275) Review: pointer motion came in every frame while the mouse moved, so the settings were serialized every frame; and some settings change in every frame (a panel's width while its edge is dragged), which would have flushed app.ron on the UI thread each frame. Only clicks, keys, text, paste, wheel and pinch count as input now, and the file is written at most once per second: a change made in between waits, a repaint is asked for when its second is over, and it is written then, so an idle app still writes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
edcf02c0d4 |
Crash reports: a window opened after startup no longer labels the run 'during startup' (#8776) (#9661)
* claim: #8776 * Crash reports: a window opened after startup no longer labels the run "during startup" (#8776) egui-wgpu reports "window surface" and "surface configure" for every native window, also the ones opened after startup, such as the agent chat in its own window. Each one overwrote the journal's "running" phase, so a later unclean exit was reported as "during startup (surface configure, N s after launch)". crash::startup_step records a startup step only until the app runs, and the app's egui-wgpu hook calls it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
fc499441e2 |
Lost GPU: the old process exits instead of hanging in the driver, after the settings are saved (#9044) (#9054)
After a lost device the next start began from on_exit, while eframe was still writing app.ron, and the old process then hung in vkDestroySwapchainKHR on NVIDIA. Start it from Drop for App instead, after eframe has joined the settings save and before it drops the window's GPU surface, and end the process there after a loss. The update restart moves there too. The loss reason is passed to the next start and shown in its note. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
32900556e0 |
Control server: a subscribed connection is told of design changes (#8315) (#8333)
* claim: #8315 * fix(control-server): release the watch seat deterministically (#8315) The seat a subscribed connection holds on the DesignWatch lived in the events thread alone, so it left the watch only when that thread ended: on the first write a closed client refuses, which needs the design to move, or when the thread noticed the end of the stream, which waits for the connection's own thread to come out of its blocking read. A closed connection could therefore keep counting on the watch for a second or more, and the test that asks for the count to be gone could lose its race with the ten seconds it waits. The seat is now shared: it leaves the watch once, whichever thread sees the connection's end first, the connection's own thread at the end of the stream or the events thread at the first refused write. The count of subscribed connections is right as soon as the connection is gone, and the events a subscribed connection is told of do not change. |
||
|
|
6c9afc6f7e |
Start the window on OCLP Macs with SYSTEM_VERSION_COMPAT=1 (#3080) (#8930)
On Macs patched with OpenCore Legacy Patcher the OS reports a newer macOS version than the GPU's Metal compiler supports, so wgpu picks a shader standard the compiler refuses (`invalid value 'metal3.2'`) and the window never draws. The variable has to be set before the process starts, and Rust 2024 makes `set_var` unsafe (this crate denies unsafe code), so main() now re-launches the binary with SYSTEM_VERSION_COMPAT=1 when the OCLP NVRAM key (OCLP-Version) is present, and exits with the child's status. A process that already has the variable set never re-launches, so there is no loop and a user's own value is respected. If the check or the spawn fails, startup continues normally. Tested on an OCLP Mac: one re-launch message, graphics render correctly; with SYSTEM_VERSION_COMPAT=1 set beforehand it does not re-launch. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
aff8106205 |
Help: check for updates and show results in the selected language (#8896)
* Help: add manual update checks and status window * Test manual update checks and localized results * Add update check to the native Help menu * Localize Help update checks in all app languages * Document manual update checks from Help * Document ui.help updates control action |
||
|
|
21340ba019 |
Web: Chinese UI loads a Chinese font (#8894) (#8981)
* claim: #8894 Co-authored-by: Cursor <cursoragent@cursor.com> * Web: load Simplified/Traditional Chinese fonts for zh and zh-TW (#8894) BIZ UDPGothic only covers Japanese, so Chinese UI showed tofu. Ship locale-glyph Craft UI SC/TC subsets and fetch the matching face on demand. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
6e39efd4ac |
Theme: System follows the desktop's dark/light setting on Linux GNOME (#7944) (#8123)
* claim: #7944 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Theme: System follows the desktop's dark/light setting on Linux (#7944) On GNOME (Wayland, or X11 through XWayland) the window toolkit never reports the desktop's dark or light preference, nor its changes, so Theme: System kept its colours. The Linux app now reads org.freedesktop.appearance color-scheme through the XDG Desktop Portal's Settings interface (then GNOME's color-scheme and gtk-theme when it has no preference) on a thread of its own, follows the portal's SettingChanged signal, and falls back to gsettings or a slow poll. The window reads the answer through Services::desktop_dark; the decision from the raw values is prefs::desktop_prefers_dark. Windows and macOS keep the window toolkit's theme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
63923da2ca |
Viewport: an opened design is framed at the home view, in the middle of the view (#9195) (#9204)
* claim: #9195 * Viewport: an opened design is framed at the home view, in the middle of the view (#9195) A fit the viewport cannot frame yet is kept and asked again on the next frame instead of being consumed and lost, so a design opened from the Home screen or by launching on a file no longer comes up at the default camera, wherever its own coordinates put it. Framing then centres the model on the view rather than on the free rectangle between the floating controls: `Layout` carries `middle`, the view without the panels, computed before the compass, the navigation bar and the OK/Cancel corner join the obstacles. The model is still sized to `drawable`, so it stays clear of every control, but only a panel moves it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Viewport: keep the centred fit clear of the controls, and frame --sample (#9195) The fit framed the model into a rectangle the size of `drawable` centred on `middle`, which is not inside `drawable`, so the model could sit under the compass, the navigation bar or the render product frame. The model is now sized to `drawable` as before, and only the room left over in it moves the model towards the middle of the view. `s27_move_triad` turns the Z ring on its far side: the model now sits further right, and the Move dialog that docks on the right afterwards covered the ring's old grab point. A launch with `--sample` builds the design straight into the session and never asked for a fit; it now comes up at the home view, framed, like an opened design. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Viewport tests: the cursor orbit's pivot keeps the pixel it was picked on (#9195) With the model now centred on the view, one of the test's perspective drag points lands within the pick tolerance of the box's top edge, and the pick snaps the pivot onto that edge, a few pixels from the cursor. The orbit keeps that point still, as it should; the test compared it with the cursor instead of with where it started. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
92af4a21c8 |
Control channel: a per-request param_check level, applied and confirmed by the app (#9184) (#9185)
* claim: #9184 * Control channel: a request's own param_check level, applied and confirmed by the app (#9184) `engine.execute`, `engine.batch` and `engine.script` take an optional `param_check: "off"|"warn"|"strict"` in their params. The app runs the request at that level through the same `run_checked` path and batch preflight a headless session uses, puts its own level back, and every reply says the level it ran at as `param_check` beside `ok`. A command that switches designs keeps the app's level; any other word is refused. Readings and exports answered on a copy off the window thread keep the level too. `control.capabilities`, answered at once on the control-server thread, lists what the channel does beyond the methods every version has: `{"param_check": ["off", "warn", "strict"]}`. An older app answers "unknown method". The MCP bridge asks it once per connection. When the app lists the level, every call and batch goes with it and the app checks against its own commands; otherwise the bridge checks before sending, as #9073 does. At strict, a reply that doesn't say the app checked the call is an error, and the next calls are checked by the bridge. Also: `add_param_warnings` takes the reply by `&mut`, and `with_param_check` sits beside `Remote::new`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Control channel: a design-switching command is checked at the request's level Review fixes for #9184. `component.edit_in_place`, `component.finish_edit`, `view.next_tab` and `view.previous_tab` ran at the app's own level and echoed `off`, so a misspelt call went through at strict. They now have their parameters checked at the request's level before they run (and still run at the app's own level, as each design keeps its own), and the reply echoes the requested level. The MCP bridge at strict now requires the echo to match its level, not just to be there. `param_check` is read only by `engine.execute`, `engine.batch` and `engine.script`; other methods ignore it. The off-thread test now shows the design change computing off the window thread before it is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4a781b8cd8 |
MCP: --param-check checks calls to the running app (#9072) (#9073)
* claim: #9072 * MCP: --param-check checks calls to the running app (#9072) `mcp --connect` (and `mcp` once it bridges to an app) took `--param-check` and forwarded every call unchecked, while the app's own session is always off. `Remote` now checks each `engine.execute` and `engine.batch` against this server's command contracts before sending: `strict` refuses a call, or a batch with such a step, without sending anything, and `warn` adds `param_warnings` to the app's reply (each step's result in a batch). A command with no contract here goes to the app unchecked. `Auto` carries the headless session's level over when it bridges to a late app. `batch_preflight::contracts` is the contract half of the existing preflight, for a runner in another process. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |