mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 20:02:16 +00:00
f4d937d8a298f9bd930d8a430d82de2ec00e02bb
232 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9ad96fb81b |
Home: keep a removed recent design removed (#4660) (#4718)
Removing a design from the Start page's recent list only changed memory, and on the web sync_recents() rebuilt the list from stored designs on each launch, adding it back. Track removed designs in a persisted 'forgotten' set that sync_recents honours; opening or saving a design, or deleting/renaming its file, clears it. |
||
|
|
05d43d3542 |
Windows: the taskbar button shows the mark when the first frames are slow (#6391)
The taskbar asks a window for its icon once, when the window appears. eframe sets the icon while the window is still hidden and shows it after the first frame; when that frame is slow (safe mode's software renderer) or startup kept the UI thread busy long enough for Windows to mark the window as not responding (building the viewport pipelines), the request isn't answered in time, the taskbar falls back to the window class's icon, and winit's class has none: the button showed Windows' generic icon for as long as the window was open. Give the class the exe's embedded icon (resource 1) before the window is shown. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2ff17c0fcb |
3D mouse: follow the device that moves, not the first one found (#3988) (#4339)
* claim: #3988 (3D mouse: follow the device that moves, not the first one found) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse: follow the device that moves, not the first one found (#3988) On Windows and macOS the reader opened the first 3D-mouse interface it could and read it until it failed. A 3Dconnexion Universal Receiver with nothing paired opens fine, has a valid layout and never fails, so it kept the reader for good. A SpaceMouse on a cable next to it was never read ("Not calibrated (hid 256f:c652)", every axis at zero). The reader now opens every candidate and follows the one in use. The first is followed from the start, as before. Another takes over once it shows use (an axis off zero or a button down) while the followed one has shown none for STALE_AFTER, so two devices moved together don't trade the reading. A candidate that fails is dropped; if it was the followed one the reading resets and the next is read. With one device the reader behaves as before, down to the 100 ms read timeout. Several take 10 ms turns, and reports queue in the system meanwhile. The selection is `follow()`, written against a small `ReportSource` trait, so it is tested with simulated devices on every platform: an idle receiver next to a SpaceMouse, two devices in use at once, and devices failing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse: a press that makes a device the one in use counts as a press Since #3825 a HID device's first button report after it connects is its baseline: a button down in it is held, not pressed. A device taking over from another one was connected at that moment, so the press that took over was swallowed. With an idle Universal Receiver found first, that is the first button press on the SpaceMouse. follow() now keeps the buttons each candidate last reported while another was read, and the reader hands them to the reading as the baseline when that candidate takes over. A device that reported no buttons before it took over keeps the held-not-pressed rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Tyler Chiriboga <18270301+Tyler-Chiriboga@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4cd1a51f48 |
Assistant: a request timeout per endpoint for slow models (#5009) (#5010)
* claim: #5009 * Assistant: a request timeout per endpoint for slow models (#5009) An OpenAI-compatible endpoint now stores how long one model request may take (request_timeout_s, 300 s by default, clamped to 30 s ..= 1 h). It is set in the Add agent dialog and in the endpoint's entry under Agents (a running conversation uses a change from its next request), through ui.agent_chat add_agent, and reported in the chat's state. A request that runs out of time says the limit and where to raise it instead of "timeout: global". The desktop HTTP cap goes from 900 s to an hour. The tool-call timeout stays 330 s: it covers SolidCraft's own tool work and the user's Allow, never the model. * Assistant settings: line up an endpoint's Request timeout with Permissions The label column of an agent's entry is wide enough for "Request timeout", so the label isn't cut and both rows share one column. |
||
|
|
b702ce8c7d |
fix: start the window on Intel Macs without wgpu's indirect-call validation (#3024) (#4808)
Co-authored-by: Nathalie Schönwetter <nathalie@cfw-ag.ch> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
0520910bfd |
Control: name the token file and spot a stale one when a call is unauthorized (#4664) (#4670)
* claim: #4664 * Control: name the token file and spot a stale one when a call is unauthorized (#4664) When the app refuses a token that the MCP client read from a token file, the error now names that file, when it was written and how long ago, without printing the token. The app also writes `<port>.pid` beside the token file (its process id and, on Linux, its start time) and removes both at exit; when that process has exited, the error says the token file is stale. The process check runs only after a refusal, and the token file itself stays one line, so existing clients keep working. Document force-quit leftovers and the MSIX AppData redirection. * Bench assistant: read the token out of the new ClientToken Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: bherbruck <brennen.herbruck@gmail.com> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
4f4b1fc2d2 | Sketch: select edge midpoints for horizontal and vertical constraints (#5159) | ||
|
|
9e6e0b8df1 |
Render workspace: complete experimental ribbon, resources and effects integration (#5493) (#5494)
* wip(render): reconcile completed material and image pipeline source with 0.20 (#5493) Source-only integration reference for staged review, not a merge-ready aggregate. Donor: 0xlillith/solidcraft@aea20b0115. Upstream: |
||
|
|
b7b25f9e2b |
Render workspace: first basic delivery to main (#6050)
* Normalize UI container violation paths across platforms (#5599) Report repository-relative paths with forward slashes so diagnostics and the existing nested-package regression assertions agree on Windows and Unix. Keep the container detection rules unchanged. Validation: official ui-containers gate and all seven focused xtask tests passed; rustfmt and diff checks passed. Only xtask/dependencies were compiled; no application build or heavy tests. (cherry picked from commit |
||
|
|
a633a67e63 |
Preferences: choose the window system on Linux (Wayland or X11) (#3083) (#3084)
* claim: #3083 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * WIP: choose the window system on Linux in Preferences (#3083) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preferences: a window system set over the control channel is saved for the next start again The merge kept main's check in ui.prefs, which only looked at the anti-aliasing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preferences: the window system section drops the restart badge its note already states Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5e0e7a4c6a |
CLI: --help on each subcommand prints its usage and exits 0 (#4538) (#4545)
* claim: #4538 * CLI: --help on each subcommand prints its usage and exits 0 (#4538) Every command now answers `--help` and `-h` with its own usage on stdout and exit 0, the way the top level has since #1828. Before, each subcommand answered differently: `fuzz --help` started a fuzz run with the default seeds and ran until it was stopped, `recipe --help` took the flag for a file name, and `snapshot`, `render`, `oracle`, `run`, `eval`, `script` and `crashes` answered with an error on stderr and exit 1. The help is read before the command runs, so `--help` never starts work: no fuzz run, no MCP server, no file read. `fuzz` also refuses an argument it doesn't take, as `snapshot` already did: a flag it doesn't know and a stray word are errors, not arguments to ignore while a hundred seeds run. The usage lives in one place now: each command's lines are a constant, and the top-level usage is composed from them, so the two cannot drift apart. The top-level usage is unchanged, byte for byte. # Conflicts: # apps/solidcraft-cli/src/main.rs |
||
|
|
612d9d08dd |
cadmpeg 0.6.3: FreeCAD and Rhino import in the browser too (#4482)
cadmpeg 0.6.3 is pure Rust (ruzstd instead of the zstd C library, zip 9), so solidcraft-io depends on it on every target and the wasm build reads .FCStd and .3dm files like the desktop app. The browser's file pickers offer them. 0.6.3 replaces DecodeOptions::container_only with a decode depth; ask for Geometry, what its STEP writer reads. zip 8 leaves the tree, and with it the three cargo-deny advisory exceptions kept for it. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
1f6a2d4daf |
3D mouse: driver choice, guided calibration, button mapping and a picture of the device (#1463) (#3825)
* Add a small test boat to steer around with the 3D mouse A little hull in its own camera, drawn by the UI itself and not the CAD viewport, so a 3D mouse can be tried out without opening a design. It brings the model (generated by assets/models/make_test_boat.py) and the tests for how the camera moves it. Nothing uses it yet; Preferences picks it up with the rest of the 3D mouse work. * UI kit: keep a choice row from flipping with the scrollbar A choice row picks segments or a dropdown by whether its labels fit. A page scrollbar that comes and goes narrows the row by its own width, so the row could flip back and forth between the two on a page that sits right at the edge. It is now judged on the whole row and through a small helper, fits_steadily, that only changes its answer once the miss is wider than the scrollbar. Also tests for that, for typed numbers in a slider's value box, and a test hook to put up the issue-report input barrier by hand. * 3D mouse: device model, guided calibration, buttons and a picture The 3D mouse becomes a device we know something about instead of six raw numbers. The reader picks between spacenavd and the direct hidraw driver, falls back from one to the other, and says so when it can't open the device. Guided calibration asks whether the device is a spring cap or a trackball, adds a touch step, and uses a settle filter so a ball let go of stops reading as motion. Buttons get jobs (keys, held modifiers, commands), saved per device, with defaults for the SpaceMouse Pro. Preferences gets a picture of the device that moves as it does, next to the test boat, and the same picture can sit in the 3D view at Left Viewport, Right Viewport or Left Window, above the floating History card like the legend and readouts. These ride together because the crate's calibration types, the Preferences page, the Linux reader and the button hook all use each other. * 3D mouse: offer to calibrate an uncalibrated device When a 3D mouse shows up that has not been calibrated, a card in the 3D view offers to calibrate it, to swap drivers when the other one would work better, to close, or to never ask again. It is made once per device, goes away after 30 seconds in view, and gives way to a status or computing toast instead of stacking on it. The card is the shared toast, which now takes buttons: they group by purpose, wrap inside the card at narrow widths, and do nothing while an issue report holds CAD input. The picture of the device in the 3D view keeps clear of the card. * 3D mouse: translations and attribution The 3D mouse strings (ui.3d_mouse_*, the calibration steps and the offer card) in every shipped language, the Japanese catalog note, and the credit for the test boat model in ATTRIBUTION.md. This is last because the code before it only looks keys up: it builds and runs without these, falling back to English until they land. * 3D mouse: draw its preferences with the settings kit like the rest of Navigation The 3D mouse section was a folded header with its own layout. It now uses the same kit as the sections above it: a settings_section card, settings_form rows with the shared label column, dividers between the device, axes, picture and buttons groups, Text::Hint for the helper lines, and settings_control_width for the dropdowns. The section is open like every other one (the header still folds it), so the device badge on the folded header is gone; the calibration row already names the device. Every setting, string and key is unchanged. Tests that unfolded the section no longer click it, and the hint lines are looked up as hints instead of notes. * 3D mouse: show each axis's live tilt in Preferences, like main's axis rows Each view axis row is main's axis row again: a meter that follows the tilt of the motion driving it, with the dead zone marked, next to Invert, Speed and the "driven by" menu. The meter reads the calibrated travel when the device has a calibration and the raw reading over full scale when it does not, and asks for a frame whenever the reading changes. The helpers are back as they are on main, and the calibration state is a badge by the enable toggle. * 3D mouse: line its preferences up with the rest of Navigation - The two checkboxes sit side by side in the same two-column grid as Wheel and trackpad. - Row labels stop repeating "3D mouse" and use sentence case: Calibration, Driver, Restore defaults, Reset view, Left viewport, Right viewport, Left window. - Hints line up under the control column instead of the left edge. - The six view axes are named Pan X, Pan Y, Zoom, Orbit X, Orbit Y and Roll so they fit the name column. - Buttons are a divider and rows in a form like the other groups, without the bold sub-heading. - The driver, dead zone, axes, picture and buttons sit behind a collapsed Advanced section, since most people never touch them. * 3D mouse: tidy the Advanced test for clippy |
||
|
|
6c6ffe4802 |
MCP: wait out long requests, honest batch outcomes, batch tool aliases (#2901)
Rebased onto main as one commit (was |
||
|
|
0dffb206b1 |
Assembly: ground the first part; drag free joint chains (#2804) (#2805)
* assembly(feat): ground first parts and drag free joint chains Ground the first top-level part when it is created and let face dragging reposition free occurrences or any member of a floating joint chain in one undo step. Preserve exact repeated-placement frames, joint values, modifier selection and Escape cancellation. Keep the accumulated fixture, viewport layout and documentation integration fixes from the existing PR. Signed-off-by: Karl Von Richter<karl@embeint.com> * assembly(fix): update movable fixtures for first-part grounding Accept the documented grounded override in component creation, explicitly free movement fixtures, and keep linked clipboard pastes movable even in an empty document. Cover first and reused linked pastes, default insertion grounding, and single-step undo in the existing link tests. Signed-off-by: Karl Von Richter<karl@embeint.com> * assembly(test): keep the motion-cost crank free Unground the first component in the document-level joint-face measurement fixture so its motion samples can rotate after first-part grounding. Preserve the rotation and shared-measurement assertions that caught the Linux CI failure. Signed-off-by: Karl Von Richter<karl@embeint.com> * assembly(test): free the frame placement fixture Create the frame-member placement fixture ungrounded before moving it above its root sketch. Keep its world-space member assertions while respecting first-part grounding in the frame tests added on main. Signed-off-by: Karl Von Richter<karl@embeint.com> --------- Signed-off-by: Karl Von Richter<karl@embeint.com> |
||
|
|
98c4f335e8 |
Control channel and MCP on by default, with a setting to turn it off (#5561)
The app opens its localhost control channel on port 27878 (or the next free one) unless Preferences > Assistant turns it off; --control and SOLIDCRAFT_CONTROL_PORT still win. A current.port discovery file lets solidcraft-cli mcp find the running app with no arguments. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
945e6261cc |
3D mouse test: clear the frame's texture changes, so it passes in debug builds (#4382)
each_frame_hands_the_view_its_motion_through_the_drivers_mapping dropped
the output of a frame it ran (`let _ = ctx.run_ui(...)`). epaint panics
in debug builds when a TexturesDelta is dropped with deltas left ("Dropped
TexturesDelta with 1 unapplied deltas"), so the test failed under a plain
`cargo test` on every platform while the ci profile passed. It now clears
them, as the other UI tests do since #60.
Co-authored-by: Tyler Chiriboga <18270301+Tyler-Chiriboga@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
fdde3f9da9 |
Add macOS native menu bar (#2361) (#4531)
* claim: #2361 Co-Authored-By: GitHub Copilot <noreply@github.com> * feat: add native macOS menu bar (#2361) Reuse document actions and unsaved quit handling in native application, File, Edit, Window and Help menus. Replace duplicate header controls only when native installation succeeds, preserving other platforms and the fallback UI. Co-Authored-By: GitHub Copilot <noreply@github.com> --------- Co-authored-by: GitHub Copilot <noreply@github.com> |
||
|
|
be87303683 |
Web: a trackpad swipe pans instead of zooming (#4116) (#4834)
* claim: #4116 * Web: a trackpad swipe pans instead of zooming (#4116) Browsers deliver trackpad swipes as wheel events without scroll phases, so the shared navigation treated every vertical swipe as a mouse-wheel zoom. The web raw-input hook now brackets point-mode swipes (small or horizontal deltas) with gesture start/end phases, ending after 100 ms idle or before line/page wheel or pinch input. Wheel notches and pinch still zoom, and camera input still goes through navigation::handle. |
||
|
|
e33c371e99 |
Model cache: thin the checkpoints of a design too big for its file instead of leaving the cache out (#3220) (#4882)
* claim: #3220 * Model cache: keep fewer states of a design too big for its file instead of none (#3220) A model cache over the container's budget keeps the final state, then the slowest features' states while the bodies they add fit (a body several states hold counts once), and is left out only when not even the final state fits. Opening evaluates just the features whose state the file left out, each from the state before it, so the model is whole afterwards. * Model cache: keep each encoded body apart, copy only what a cache stores Encoding a model too big for its file no longer copies every distinct body into one buffer before thinning: the payload is copied from the encodings once its size fits, so saving holds no more than before. * Model cache tests: canonical runs in the timeline fixture; rustfmt * Model cache: count the records a thinned cache keeps; always try the final state Thinning counted only the bodies, so a model of many copies of one part (records, not bodies, filling the room) shrank its budget to nothing and saved no cache although its final state fitted. The choice now counts the records of what it keeps, always keeps the final state, and says what it counted, so each retry keeps less and the final state alone is always tried. * docs: a thinned model cache counts its records and always tries the final state * Model cache test: the slowest feature's state holds bodies 16 to 23 * Model cache: thinned opens with fingerprints that read the state before a feature Since #3397 a feature's fingerprint reads where the bodies before it lie. After a state the file left out, adopt takes the saved fingerprint and the evaluation that works the state out checks it. A result evaluated ahead is never taken as one the file left out. The thinned round trips include the placed components' design, whose cut reads those placements. --------- Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> |
||
|
|
dad28abecf |
solidcraft-cli bench-assistant: the standard Assistant model test (#4851) (#4930)
* claim: #4851 * solidcraft-cli bench-assistant: the standard Assistant model test Runs the eleven v1 tasks of #4851 against a desktop app that is already running with a control port. Each task starts a new design and a new chat with All permissions, runs only the prescribed setup, sends the prompt once and waits for the turn. The result is checked on the design (document.inspect with measurements): body count, volume within 1% (0.3% for the fillet), bounding-box size in any orientation, the lowest corner, the body name, live feature kinds, and changed or unchanged content; the volume question is checked on the reply. Failures get the issue's categories from the conversation's items. Prints a table, and writes the issue's comment template (--markdown) and the raw results with each conversation (--json). |
||
|
|
b31fddb98e |
OpenGL: the viewport scene renders 1x again, like the window (#5605)
#5187 gave the scene its own offscreen target with the app's sample count,
which skipped the OpenGL clamp to 1x from
|
||
|
|
5962f56f87 |
Viewport: reuse still pictures and draw scenes into a 1x window pass (#4820) (#5187)
* Viewport: show the picture it drew last when nothing changed (#4820) A frame redrew the whole scene whenever egui asked for a frame, even when the camera, scene, highlight and overlays were identical: pointer moves over empty space and UI animations over a still view paid for the full scene. The renderer now hashes everything the picture depends on (scene keys, camera, size, section, images, analysis, accessibility map, occlusion) and blits the last offscreen picture when the hash matches. The first still frame filters a supersampled picture down once and later frames copy it as it is. A frame with the same content costs a blit instead of a scene draw. Co-Authored-By: Claude Code <noreply@anthropic.com> * Viewport: draw the scene offscreen, into a 1x window pass (#4820) The scene keeps the app's depth, stencil and MSAA, but egui's window pass now has one sample and no depth. Whenever the two targets differ, the scene (product previews too) is drawn offscreen and blitted, so the UI pass no longer clears and resolves a multisampled window. Over 8 s of pointer moves on the 450E rotor head, the app's GPU intervals fell from 1325 ms to 1006 ms and egui_render from 892 ms to 509 ms; idle stays at 0%. A GPU test checks the split path's picture matches the direct one. Co-Authored-By: Claude Code <noreply@anthropic.com> * 3D mouse: a repeated report at rest asks for no frame (#4820) A device may repeat its latest report while the cap rests. Publishing it again asked egui for a frame although nothing changed. Only a report that changes the reading now asks for one; while the cap moves, the view asks for the next frame itself. Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: Claude Code <noreply@anthropic.com> |
||
|
|
17f0d6bc68 |
Format registry: one table of formats, extensions and options (#5215) (#5217)
* Add the format registry: one table of formats, extensions and options in crates/io. The hosts' Open-dialog filters and the web accept list derive from it, and registry tests pin it to the path predicates and the command docs. * Fix format-registry tests that compared FormatSpec without Eq. assert_eq on Option<&FormatSpec> does not compile; the none cases only need is_none, matching the other rows that compare ids. |
||
|
|
94279fe059 |
Web: Ctrl+Tab and Ctrl+Shift+Tab switch browser tabs again (#5227) (#5350)
* claim: #5227 * Web: leave Ctrl+Tab and Ctrl+Shift+Tab to the browser (#5227) eframe cancels the default action of every Tab keydown on its canvas (egui moves focus with Tab), so browsers that let a page cancel their shortcuts, such as Vivaldi, could not switch tabs while SolidCraft had the keyboard. A capture listener on the window now stops Ctrl+Tab and Ctrl+Shift+Tab before they reach the canvas, without cancelling them, so the browser acts on them as usual. Plain Tab and Shift+Tab still move between the app's fields. Desktop builds are unchanged. web-api-test checks it in headless Chrome: Ctrl+Tab and Ctrl+Shift+Tab keydowns are not cancelled, Tab still is. --------- Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> |
||
|
|
92d3a2fde5 |
GPU: a lost device stops drawing instead of crashing (#4172) (#4229)
* claim: #4172 * GPU: a lost device stops drawing instead of crashing (#4172) After the driver resets the GPU (Windows TDR), the first queue write to meet the lost device panicked inside wgpu-core (PendingWrites::activate unwrapping DeviceError::Lost). Nothing watched the device for its loss. - egui-wgpu: RenderState::device_lost (DeviceLost) is set by the device's lost callback. The painter draws nothing on a lost device, and before a frame's uploads it submits nothing (DeviceLost::probe), so a loss the driver reports is found by that submission instead of the first write. - Pictures (thumbnails of saves, ui.render) watch the same flag: on a lost device the capturer refuses with GpuError::DeviceLost without touching it, and the picture comes from the CPU rasterizer. Headless capturers watch their own devices. - The app, seeing the loss, closes keeping unsaved changes for recovery (as a forced quit does) and starts again; the new start says what happened. A second loss within ten minutes starts on the software renderer. The loss is noted in the crash journal. |
||
|
|
6f5d0038ab |
3D mouse: find devices on Windows without asking every HID device for its strings (#2614) (#3653)
* claim: #2614 (3D mouse: find devices on Windows without asking every HID device for its strings) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse: find devices on Windows without asking every HID device for its strings (#2614) On Windows the 3D mouse reader rescanned every two seconds while no 3D mouse was plugged in, with hidapi's refresh_devices(). hidapi's Windows enumeration opens every HID interface and reads its serial number, manufacturer and product strings: on USB, three control requests to every keyboard, mouse and touchscreen, forever, on a machine without a 3D mouse. That fits the report of an idle machine whose USB keyboard drops off and returns and whose cursor jumps. Windows now asks Plug and Play which present HID interfaces are multi-axis controllers (hardware ID HID_DEVICE_UP:0001_U:0008), which sends nothing to any device, and opens only those with open_path(). hidapi's own discovery is turned off before the reader makes its one context, so making it touches no device either. macOS keeps hidapi's enumeration (IOKit's registry); Linux is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * AGENTS.md: list the Plug and Play scan among the files allowed unsafe code (#2614) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Tyler Chiriboga <18270301+Tyler-Chiriboga@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
4b23bb3a03 |
Implement compact workspace layout (#2029) (#4533)
* Vertical history strip: left of the browser, compact rows, hollow-circle marker, collapsed rail hover * Vertical history strip: seam handle to expand and collapse names, drag to resize with snap * Vertical history strip: no empty gap below the transport * Show the history seam arrow only on hover (#2028) * claim: #2021 * Redesign the Assistant chat panel with shared UI components * Show Assistant in the shared floating panel shell * Extend floating Assistant to viewport height and shift navigation * Dock Assistant before history and match docking preview bounds * Align Assistant top margin with floating shell cards * Extract a shared panel drag grip * Rebuild responsive history sizing and three-way docking * Share browser and History collapse hover styling * Show the History title as soon as its text fits * Animate stacked browser and History expansion and collapse * Make panel folding responsive and independent of frame delays * Let folding panel contents shrink continuously to header height * Use four explicit History transport actions in every layout * Resize the shared Browser and History column from either card * claim: shared compact-mode containers (#2029) * Centralize layout-aware UI containers and enforce their use * Keep docked Assistant at the outer edge beside tool sheets * Use shared surface policy for Assistant tool space * Let tool sheets drag over shell panels while avoiding Assistant * Update Parameters drag bounds assertion for shell overlap * Keep dragged tool sheets below ribbon and workspace selector * Enforce universal UI containers in mandatory repository checks * Remove legacy report windows and require shared window composition * Remove retired UI styling and require shared controls * claim: compact layout preference (#2029) --------- Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
c54b711345 |
CLI: --help prints usage to stdout and exits 0 (#1818) (#1828)
* claim: #1818 * claim: #1818 * fix(cli): --help, -h and help print usage to stdout and exit 0 (#1818) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
f22a026c27 |
3D mouse: choose which motion drives each axis (#2782)
* 3D mouse calibration: find each axis from a guided recording (#1463) A recording of the cap at rest and moved along each axis both ways gives which raw axis is which, which way it runs, where it rests, how much it wanders at rest and how far it goes each way. Hands aren't exact: on our SpaceMouse Pro, sliding the cap right moved Ry further than X. So each raw axis is picked by the one-to-one assignment that best explains all six motions together, each raw axis scored against its own range so a wider one doesn't win through cross-talk. Each direction is scaled on its own, so a device that goes further one way still reaches full scale both ways; a direction under a twelfth of the furthest axis counts as not having responded. A small state machine walks the user through it and moves on by itself once the cap has been held out for 0.4 s in one go and let go. "Held out" is the least the solver accepts, twice an axis's own rest wander, so whatever the solver would accept can be recorded and a noisy axis doesn't hold up a quiet one. A calibration also carries a dead zone the user sets, a share of full deflection taken off the bottom of the travel, with full deflection still reading full scale. Serde support is behind an optional feature, so the crate still needs nothing by default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse calibration in Preferences, saved per device (#1463) Preferences > Navigation gets a 3D mouse calibration row: whether the device being read is calibrated, Calibrate... and Forget calibration. The window asks for each motion in turn and saves what it found under the device (hid, web, spacenavd or 3dxware, with vendor and product ids), so switching devices or turning spacenavd on doesn't apply the wrong mapping. A calibration is saved for the device it started with, and ends if another one is read instead. navigate_frame puts the reading through the device's calibration before the driver mapping, so the per-axis Invert and speed and everything after them are unchanged. While the window is open the reading goes to it and the view stays put; it closes with Preferences. Restore Defaults on the Navigation page keeps calibrations, since they describe the device. The web app hands over the gamepad's readings as they are, so calibration sees where the cap rests; navigate_frame now leaves a reading inside the dead zone alone after calibration, so a cap resting just off zero still doesn't keep redrawing. Whether the cap is moving is asked of the calibrated reading there, in the web page's idle waker and in both wheel filters. Those run before the frame, so they keep the last frame's calibration with its device and use it only for that device. On the desktop, an axis with nothing fresh (timed out, or not reported since the device connected) reads as the device's calibrated rest, so a device resting off zero doesn't drift. A saved calibration that can't be read or used (a hand edit) is dropped on its own when the preferences load, with a log line. The window shows the device's raw readings live and has a dead zone slider, a share of full travel on top of the measured noise; it's saved with the calibration and adjustable in Preferences afterwards. New strings are in all 14 catalogs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse: choose which motion drives each axis (#1463) After calibrating, which motion of the cap should drive which view axis is partly a matter of taste: rolling and twisting swapped, say, or Y and Z. Each axis row in Preferences > Navigation now has a "driven by" choice next to Invert and speed. Picking a motion another axis uses swaps the two, so each motion always drives exactly one axis, and Reset axes puts them back. The choice applies after the device's calibration and before the driver mapping, so Invert and speed stay with the view axis rather than moving with the motion. It's a preference, not part of a device's calibration, and stays in force when recalibrating; the calibration window says so while one is set. A saved assignment that doesn't drive each axis once loads as the default. New strings are in all 14 catalogs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse: axis choice review follow-ups (#1463) - An axis's speed, like its Invert, stays with the view axis when a motion is reassigned; tested. - Calibrating records the device's own reading, before the axis choice, so a saved swap isn't baked into a new calibration and then applied twice; tested through a whole calibration. - A settings file without the axis choice, or with one of the wrong length, loads with the default and keeps every other setting; tested. - Each axis row says "driven by" in sight before its choice, which otherwise only explained itself on hover (no help on a touchscreen or the web). - "Reset axes" is now "Reset axis assignment", so it doesn't read as forgetting the calibration just below it; the "driven by" tip reads more clearly in English. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse: name axis rows by what they do, and motions without a device in mind (#1463) The axis rows and the "driven by" choices showed the same names, "Push left / right" and so on, which read as one thing mapped to itself, and they described a cap that springs back to centre, which a trackball or a knob that stays put isn't. Each row is now named for what it does to the view: Pan left / right, Zoom, Pan up / down, Orbit up / down, Orbit left / right or Roll, or Off. The name comes from the driver mapping navigation used last frame, so it stays right where the mapping swaps axes (object mode swaps the last two). Before navigation has run there's no mapping to read, and a row is named for its own motion instead of a guessed effect. The choices are the device's calibrated motions: Translation X / Y / Z and Rotation X / Y / Z, under a header row, and the calibration window names a motion that barely moved the same way. This renames #1814's existing rows, which had no translations; every new name is in all 14 catalogs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse: rows never fall back to a motion name (#1463) Without a driver mapping, a row was named for its own calibrated motion, which is also what its dropdown lists. It now takes the default mapping's view axis, so the two sides never share a name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse: grey out Invert and speed on rows the driver turns off (#1463) A row the driver profile turns off says so on hover, and its Invert and speed are greyed out, since they do nothing there. Its motion choice stays live so a motion can still be swapped out. The column header is shorter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse: unmapped rows say Axis 1-6, and the off-row tip explains the swap (#1463) Without a driver mapping, a row is now just "Axis n" rather than the default mapping's view axis, which would be a guess there. The tip on a row the driver turns off says that a motion swapped into it stops moving the view, and a test swaps one in and back out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * 3D mouse calibration: a device that stops short of rest after a release (#2778) Through spacenavd, a relative-axis device like the Space Navigator never reports its return to zero, so the reading stays at its last small report after every release. The wizard never saw the cap let go, and once calibrated, the stuck value crept the view. A reading that stays exactly the same, close to rest, now counts as let go, and where the cap settled after each step goes into the calibration: such a device rests at the middle of where it settled, with a dead zone out to the furthest of them. A device that comes back to rest calibrates as before. * 3D mouse calibration: a steady hold through spacenavd stays a hold (#2778) * 3D mouse calibration: stopping short of rest only counts from spacenavd and 3DxWare (#2778) Recentring on where the cap settled kept the rest stage's wander radius but not its offset, so a rest extreme could fall outside the dead zone; the radius now grows by how far the centre moved. A reading that stops changing close to rest only ends a step from a source that sends nothing at rest, so HID and web devices calibrate exactly as before. * 3D mouse calibration: settling off-centre counts on any device (#2778) A drifting stick settles off its rest after a release just as spacenavd's stuck reading does, but it keeps reporting, noisily. A step now ends once the reading stays steady, each raw axis within twice its rest wander (so exactly still from a silent source), close to rest, on any device. The dead zone takes that wander on top of the furthest settle point. * 3D mouse calibration: steadiness by the run's spread, settled on the step's own axis (#2778) An axis that read flat at rest may dither once off-centre, so steady readings may spread at least 2 units and a 50th of the furthest any step went. Steadiness is the spread of the whole run, not distance from its first reading. Close to rest is judged on the axis that set the step's peak, since a device's axes may have different ranges, and the settle point is the middle of the steady run. * 3D mouse calibration: readings close to rest never count as held (#2778) * 3D mouse calibration: the dead zone reaches every reading of each release (#2778) Saving one reading of each release and padding it by the rest wander could miss a reading seen in the same steady run. Each release now keeps the whole run, and the dead zone is centred on and reaches everything read at rest and after the releases. * 3D mouse calibration: settle for half a second before a step counts as let go (#2778) A hand held still close to rest is less likely to end a step early; a device that comes back to rest never waits for it. * 3D mouse calibration: show the dead zones as a game controller tester would (#2778) Once calibrated, the window, and Preferences under the Dead zone slider, show three pads: slide, press and twist, and tilt. Each has a circle for the full travel, the dead zone shaded inside it with each direction its own size and the user's dead zone on top, and a dot where the cap is now, lit once the view moves. * 3D mouse calibration: settle through cross-talk, per-axis steadiness floor (#2778) The axis that went furthest in a step needn't be the one asked for: cross-talk can move another raw axis further. A step now counts as let go once the furthest axis is at least halfway back and some axis that moved about as far is within a quarter of its own peak; a hand still holding the push keeps the furthest axis out. The steadiness floor follows each axis's own reach, and each stage's steadiness starts with its own readings. * 3D mouse calibration: steady readings may dither ±2 on any axis (#2778) * space-mouse: make the settle check's near band match the steadiness spread, test the 4-unit floor near() measured distance from the settled run's middle against the whole tolerance, so it accepted twice the band the steadiness check does. Use half. Add a short-range test where only the .max(4.0) floor lets a +-2 dither settle, and one where dither the spread check rejects is also not skipped as untouched. * space-mouse: settle on the middle of the steady run so a lopsided dither isnt movement near() allows half the steadiness spread around the settled point, but the point was the run mean. A dither that is mostly one value with an occasional far one (10, 10, 10, 14) is steady, yet the far reading sat 3 from the mean and started a hold in the next step. Use the middle of the run range instead. Adds a lopsided-dither test and a test pinning the documented cross-talk limit ([100,300] then [5,90] stays open). * Judge an untouched cap by the settled run's spread, not a rounded midpoint Rounding the midpoint to i16 could leave one end of an odd-width run more than half the tolerance away, so the cap read as touched while sitting at the end of the run it settled on. Keep the run itself and ask whether one more reading keeps its spread within tolerance. * space-mouse: pin the wider skip band of a narrow settled run and a single-reading settle * space-mouse: keep a step held while a lesser axis stays out as the larger one relaxes Settling needed one axis furthest and returned, and one axis about as far and settled, which a single relaxed cross-talk axis satisfied alone. Every axis that moved at least half as far as the furthest now has to be back by RETURNED_SHARE of its own peak too. * 3D mouse: the calibration tests run in a focused window, as the focus check now requires * 3D mouse: the axis tests run in a focused window, as the focus check now requires * 3D mouse: the calibrated-rest test runs in a focused window, as the focus check now requires * Settle a spacenavd reading that is nearly at rest (#3523) spacenavd sends a record only when the values change and nothing when the cap springs back, so the last small leftover of a release was held forever and the view kept drifting. A held reading now reads as rest once every axis is within 150 raw units of the device rest and no record has arrived for 0.5 s. A firmer push stays as long as it lasts, and so does a small value next to a large one. 150 is the smallest round number above the worst leftover in the issue (129 on one axis). * Choose which 3D mouse driver to read on Linux When both spacenavd and the device itself (hidraw) are there, Preferences now has a "3D mouse driver" choice: Automatic, spacenavd or Direct. Automatic is what it always was, spacenavd when it runs and the device otherwise, so nobody's setup changes. If the chosen one isn't available the other is read and the log says so once. Each driver keeps its own calibration, as before. To switch without a restart both reads now wake up when idle and look at the choice: spacenavd with a 250 ms read timeout, hidraw opened non-blocking and polled every 5 ms when it has nothing. A record that arrives is read as before, and one split across a timeout is kept. The reader also publishes which of the two exist, which is what shows the combo box; with only one it says which one is in use. * Build the 3D mouse row header from existing labels --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
e570c0b4eb |
Windows: a restored window that is out of reach is fitted to the monitor's work area (#4299)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
463cea0615 |
Windows: ask for the foreground once, when the first frame is on screen (#4284)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
0225e4d2a5 |
Startup: the first frame no longer waits for the system CJK/Hangul/Thai font search; SOLIDCRAFT_PROFILE_STARTUP prints startup steps (#4283)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
93a2922c5e |
Display plans and the model cache for large STEP assemblies (#2814) (#4021)
* STEP import: mesh band faces that wrap around a closed surface correctly Fixes two related bugs that caused SolidCraft to fail to mesh a face wrapping all the way around a closed, non-periodic surface (a revolved ridge band, or a tube-shaped B-spline surface): a missing face, a folded/self-overlapping triangulation, or a hole in the rendered body. 1. Ring edges that run slightly off their surface. STEP files write a surface and the curves bounding it to the file's own declared uncertainty, so a ring edge can legitimately sit a few micrometres off the surface it bounds. The code finding where a ring edge crosses the planned split line compared against the edge's raw (off-surface) point using a hardcoded tolerance far stricter than the file's own precision, rejecting a valid cut. Now matches against the point on the surface directly below the ring crossing, respecting the file's declared tolerance. 2. Closed B-spline surfaces, their slits and their slivers. truck only treats the rotation of a revolved surface as periodic; a face on a B-spline surface whose control net closes on itself (a tube) wasn't recognized as wrapping around at all. This produced a folded face, a hole (a boundary loop landing a full period away from where it should cut), and one near-zero-width sliver face costing the entire body its tessellation plan. Adds periodicity detection for closed B-spline surfaces, period- aware hole-loop alignment, dropping of zero-width slit boundaries, and isolating a too-thin-to-triangulate face so it no longer takes the rest of the body down with it. The GPU shader's periodic wrap mirrors the CPU math so the two don't drift apart. Regression tests: band_faces_split_when_ring_edges_run_off_their_surface, band_face_with_a_zero_width_slit_is_still_meshed, a_sliver_face_too_thin_to_triangulate_does_not_cost_the_body_its_plan. Ablation-tested: disabling either mechanism in (2) reproduces its target test's original failure message while leaving the other test unaffected. * STEP import: mesh the band's split faces, not just split them jyviko asked whether a band wall missing from the interactive app's render was a shading issue or a genuinely missing face. It was missing: the ring- edge split fix made the band's topology correct, but plan_face still produced zero triangles for the two new sub-faces. Two causes, both in plan_face (crates/kernel/src/tess/mod.rs): 1. The tolerance for how far an imported boundary may lie off its surface was tied to the requested mesh's chord tolerance instead of to the body's own size. A finer display tolerance could then reject a boundary a coarser one had just accepted, so the face would drop out exactly when zooming in on it. 2. Near a sharp corner of this flat-panelled band, several boundary points that are legitimately a few micrometres apart (within the STEP file's own declared precision) all projected onto the same surface point. The boundary loop then repeated a point in (u, v), which a triangulator can't follow, so the face's plan failed outright. One face without a plan costs the whole body its plan, and the fallback mesher can't handle a closed B-spline surface either, leaving both halves of the split band with no triangles at all: present in the topology, invisible in every renderer, and absent from the body's volume. Fix: scale the off-surface tolerance to the body's own extent (new Rules::extent), and collapse a run of boundary nodes that land on the same surface point to one (drop_collapsed) before triangulating. Regression test additions to band_faces_split_when_ring_edges_run_off_their_surface: the body's volume now matches the analytic pentagon prism (was a third of it - only the top cap - before this fix); display_plan leaves no face without triangles from coarse to fine tolerance; and the actual tolerance and facet angle the viewport draws with gives the split band its full lateral area on both halves. Confirmed on review_band_big.step, the fixture behind the review screenshots: volume's relative error against the analytic pentagon prism drops from 1.4e-4 to 4.1e-6, and the band wall now occludes the floor grid behind it in a snapshot the same way the top and bottom caps always did. Full kernel suite: 739 tests, 0 failed. * STEP import: hold an imported boundary to the file's own tolerance, not the part's size jyviko: the off-surface acceptance added in the last commit (rules.extent * OFF_SURFACE) scaled with the part, so a 2 m part silently accepted a boundary 2 mm off its surface while a 20 mm copy of the same part accepted only 0.02 mm. A STEP file's own declared uncertainty does not grow with the part, so the bound shouldn't either. Body gains boundary_tol: Option<f64>, Some(file's declared tolerance) for a body read from STEP (crate::step_in::Units::tol, already used by the ring-split fix in split.rs), None for anything this kernel built. It's set before the import's own unmeshed-face check runs, and carried over by the two face-split operations that re-cut an imported surface (splitface.rs, outline.rs), since the file's uncertainty still describes those boundaries afterward. plan_face's accept now reads: the file's own tolerance when the body declares one, or the old chord-tolerance/extent bound otherwise. The old bound's tol * 10.0 term had to go too, not just the extent term: at a 2 m part's typical display tolerance that term alone is ~20 mm, nearly ten times the extent bound jyviko objected to, so keeping it would have left the exact 2 m/2 mm example still silently accepted. Two new tests in step_in/tests.rs: - band_faces_split_when_a_scaled_copy_runs_off_its_surface: the same band fixture at a realistic 2 m scale with the same absolute slop and declared uncertainty as the small one, meshes the same way at every chord tolerance from coarse to fine. - off_surface_budget_follows_the_file_not_the_part_size: the actual regression. The same off-surface error is accepted or refused the same way whether the part is 20 mm or 2 m; a 2 mm error against a 1 µm declared uncertainty is refused at both sizes now (accepted at 2 m under the old bound, refused at 20 mm - that asymmetry was the bug). Checked at both a coarse and a fine display tolerance. Known gap, not fixed here: a STEP-imported body saved into a .solidcraft design and reopened loses boundary_tol (native.rs's decoder has no field to read it from) and falls back to the old extent-based bound on reload. Today's design format has no concept of this value at all; persisting it is a real format change, out of scope for this fix. Full kernel suite: 776 tests, 0 failed. clippy -D warnings clean. * Display plans for STEP threads and pointed ends (#3847) Three faces cost a threaded or pointed STEP part its whole display plan, so it was drawn from the reference mesh: cracked, faceted, and never refined up close. - A loop through a pole (a cone's apex on the boundary) seemed to wind around the surface: the jump at the apex, unwrapped to the nearest copy, was arbitrary. The loop is turned to end where it leaves the pole, so the step that closes it is an ordinary one (`seam::close_through_pole`). - Projection onto a spline with hundreds of spans (a helical flank) restarted from a 16 x 16 grid and settled on another turn; it now starts from two points per span, capped. - Sag refinement no longer inserts a point exactly on a boundary side, which split the side and failed the face. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tess: a refused face's edges are divided finer and it is planned again (#2814) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * tess: sliver faces plan empty; a face no method plans is filled flat from its nodes (#2814) A face whose boundary runs out and back along itself (two edges between the same vertices, a rounding apart, as STEP files write slivers) encloses nothing and is planned with no triangles. A face still refused after its edges are divided finer (a cusp, a sliver whose sides sag across each other, a small cap at a pole) is filled flat from its shared boundary nodes, so the rest of the body keeps its display plan instead of falling back to the reference mesh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * display mesh tests: a rim the chord tolerance leaves a line is divided again (#2814) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * portable B-rep: up to 8192 control points along a spline direction (#2814) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * solidcraft-cli: --debug logs what the engine reports to stderr Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * STEP import: a spline written over a tiny parameter range is read over one (#2814) Some writers parameterize splines over a few millionths (a humanoid robot's edges span 5.4e-6). The kernel tells parameters apart only to 1e-6, so over such a range it evaluated the curve's end wrongly, and trimming an edge to its vertices dropped a control point: the bodies could not be stored exactly and the whole model cache was left out of the design. The knots are now stretched to span one; the curve is the same and edges are trimmed by their vertices, so nothing read from the file refers to the old parameters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * model cache: a body keeps its file's declared accuracy (#2814) A body restored from the cache dropped the accuracy the file it was read from declares, so the planner held its boundaries to the kernel's own bound and a reopened design drew differently from the import. It is stored with the body's runtime values, and only when there is one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * tess: a boundary within a hundredth of its body lies on its surface, at every quality (#2814) #1721 held a body read from a file to the accuracy the file declares. A writer declares what it aims for, not a bound its edges keep: a 251-body robot assembly declaring 0.01 µm has boundaries 10 to 30 µm off their surfaces, and 95 of its bodies lost their display plans. The bound is a hundredth of the body again, what Normal quality always allowed, and now the same at every quality, so zooming in still never drops a face (#1721's point). A file declaring more raises it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM * STEP import: keep faces whose edge vertices sit slightly off their curves Some exporters write an edge's vertex beside the end of its curve, further off than the accuracy the file declares (a surface export declares 0.01 mm and puts a vertex 0.19 mm off two spline ends). The reader refused such edges and left the face, and with single-face bodies the body, out. A vertex may now sit up to the file's tolerance or 1/1000 of the model's size (the diagonal around its VERTEX_POINTs), whichever is larger, never more than the existing 1 mm ceiling. Spline and open-conic edges have the end control point moved onto the vertex, so every such edge meets its vertices exactly. The file's declared accuracy still governs closed-curve, seam and circle checks. * tess, io: review clean-ups (#2814) - A sliver face is one whose loops are all slit: drop_slits finds it in the retry after a failed plan, so the pre-check on every face (folded) is gone. - The flat fill uses cdt::triangulate (a lenient fill that leaves out sides crossing one already kept) and its flood-fill inside test, and the plane from geom::Plane. - Refinement only builds its indexes when a face was refused, takes the faces to plan again from edge_faces, and re-samples edges on the pool; Rules::finer for the finer rules. - One PackageWriter::fits for the model cache, the preview and the thumbnail. - The band split's ring tolerance lives in split.rs; comments say what the off-surface bound is now. - The CLI logger prints Level::as_str(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LGeCa5jWWdroMWvXxN3LLM --------- Co-authored-by: David Lackovic <lackovicdavid1234@gmail.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com> Co-authored-by: Ray <raykholo@users.noreply.github.com> |
||
|
|
fdec948abc |
Control channel: control.running lists what the app is running, across connections (#3654) (#3689)
* claim: #3654 * Control channel: control.running lists what the app is running, across connections (#3654) control.pending answers only for the connection that asks, so a second client, the MCP bridge after a reconnect or a script saw {"pending": null} while the app was busy. control.running answers for the whole app, the same on every connection and at once (the connection's own thread answers it, also while it is busy with a timed-out request): every request the app has been sent and not answered yet, oldest first, with its connection, id, method, command id (or a batch's step count), seconds since it arrived and whether it still waits its turn. A timed-out request stays listed until the app answers it, also after its connection closed. Parameters are never shown. The window marks a request started when it takes it (ControlRequest.started), which tells queued from running. The MCP server gets a read-only `running` tool. --------- Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> |
||
|
|
6e22e3709d | Web: reopen designs saved on the computer from the recent list after a reload; forget stale remembered files (#104) (#3834) | ||
|
|
76af4ee855 | Web: Save to computer… for designs kept in the browser; OPFS writes route as before (#104) (#3687) | ||
|
|
b2168435ba |
Web: save to and open from files on your computer (#104) (#3681)
Save As and Open use the browser's file dialogs where the File System Access API exists; Save writes back to the same file, its handle kept in IndexedDB across reloads. File > Download design added. Other browsers keep storing designs in OPFS. |
||
|
|
9e78e86b92 |
perf: renaming a body evaluates nothing (#3044) (#3095)
A body rename changes names only, but committing it went through the general "design changed" path: the whole timeline was evaluated again. On an imported assembly that means reading the STEP file again and meshing every body (about 100 s on the Prusa Core One, and the same again for undo). When the only change from the previous design is one body rename (the features' stored body names and the body-keyed maps the rename updates), the model now renames the body in its results instead: every body keeps its geometry and meshes, and the fingerprints are set to the renamed design's so the next evaluation reuses everything. Undo and redo of the rename take the same path. A rename the model can't follow that way (the body is changed or named by a later feature, sheet metal, threads) still evaluates the design as before. `bench-open --ops script.json` times commands and their undo on an opened design, with the view work each leaves. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e98e065584 |
Open saved designs from the model they were saved with (#3053) (#3102)
* WIP: adopt the saved model on open (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * WIP: tests * WIP: bench-open opens as Open does * docs: opening from the saved model * Tests: an edit after opening evaluates from the feature it changes * clippy * Read SOLIDCRAFT_NO_MODEL_CACHE through the env helper * Open: no picture from the model when the model itself opens; cancel test evaluates * Saved model: bodies passed through found without writing their record; states list runs of bodies * Adopted results go into the evaluation cache: an undone edit, or the marker rolled forward, takes them again * Model cache size guard: the saved model's writer --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b3811168fc |
fix(web): keep the CJK face a fallback for a non-CJK UI (#3535)
BIZ UDPGothic's Cyrillic glyphs are full-width (1.0 em), so promoting the CJK font to the front of Proportional made Ukrainian text look wide with large gaps between letters, broke word wrapping, and truncated UI labels. When CJK text loaded on the web, install_cjk_font hard-coded "ja" and stayed promoted after switching to Ukrainian or English. Pass the live UI language to install_cjk_font_for so the CJK face only leads a CJK UI. Keep fetched CJK font bytes on the web shell and re-apply fonts when the language changes, and add a regression test that a non-CJK UI keeps the CJK face as the last fallback. |
||
|
|
953fd7d5f8 |
Typed parameter contracts and JSON Schemas for solid.box and solid.extrude (#2829) (#2611)
* Spike: typed parameter contracts and JSON Schemas for solid.box and solid.extrude The schema RFC's spike (plan/schema-spike.md). solid.box and solid.extrude get typed contracts with generated JSON Schemas (2020-12), checked in front of the unchanged handlers by a per-session level: off (the default, which changes nothing), warn (the call runs and gains param_warnings) or strict (the call is refused and the design left alone). Also engine.describe and the MCP tool describe_command, `solidcraft-cli mcp --param-check off|warn|strict`, a FeatureResult type for feature replies, and an env-var audit recorder (SOLIDCRAFT_PARAM_AUDIT) used to check the contracts against every test. Local spike branch; not for upstream as is (see the results report). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Parameter contracts: put the audit recorder behind a feature; drop two ignored test keys The audit recorder is now on only in a build with the engine's `param-audit` feature and `SOLIDCRAFT_PARAM_AUDIT` set. Its code is still compiled, linted and tested without the feature. `scripts/param_audit.py` summarises a test run's audit logs, and the schema module's docs give the steps for converting a further command. Two tests passed keys their command ignores, which the audit found: `position` on `solid.box` (`hole_face_tests.rs`) and `symmetric` on `solid.extrude` (`face_tests.rs`). Both are removed, so the tests build what they always did. Comments no longer call the contracts a spike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
d0afc31632 |
Save the evaluated model with every design: exact, stored once, scaled caps (#3053) (#3093)
* Model cache written by default: exact bodies, stored once, scaled caps (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * bench-open: say why bodies were not kept * Exact planes and revolutions; budgets for large bodies (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Native B-rep: rebuild shells as the kernel made them (closed and seam edges) (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the model cache and the exact native B-rep (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * bench-open: time a save without the cache too * Model cache entries are Deflate-compressed (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Model cache: one that does not fit the design file is left out, never a failed save (#3053) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9e7a7b260b |
perf: Save and Save As write on a worker thread (#3044) (#3146)
Save ran on the window's thread: about 130 ms on the Prusa Core One design, and more than a second once saves keep the evaluated model. The toolbar's Save and Save As now run the command on a scratch copy of the session on a worker; the design waits for it as it waits for a file picker (commands are refused meanwhile), and the outcome is taken up as running the command here would have left it: the name and links the save gave the design, its file, its saved state (`Session::adopt_save`), the echo, the error status, the recent files and the home thumbnail. A tab closing or the app quitting after Save waits for the write, as does the app's exit. Scripts, the control channel and the browser build still save in place. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
803ae6d5be |
Script replay keeps a feature name another feature has (#1990) (#1997)
A sketch and the feature built from it may share a name, and the sample and oracle designs do. `timeline.add` refused such a recorded name, so a design's own exported script failed to replay. `Document::insert_feature` now keeps the name, as `add_feature` does; a blank name still gets a fresh one. Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
31b41978fc |
Construction geometry on model vertices follows them when a parameter moves them (#568) (#2409)
Thanks @rlartot. |
||
|
|
26f42ae09b |
Snapshot: draw a see-through appearance see-through, and show a tool body inside its target (#2329) (#2330)
* Snapshot: draw a see-through appearance see-through, and give the CLI --body, --tool and --section A headless picture dropped a body's opacity: the scene carried only a colour, so a design with a translucent appearance was drawn opaque by `solidcraft-cli snapshot`, by MCP screenshots and in a design's thumbnail, although the window has always drawn it as glass. There was then no way to look at the body a cut would remove inside the material it meets: only a volume to measure and reason about, which hides exactly the errors a picture makes obvious (a tool that stops short, or lands in the wrong place). - `SceneMesh` carries the body's opacity; `view::build_scene` takes it from the design (`model_body_opacity`), and a selected body stays solid so the selection colour reads. - The GPU capture routes a see-through body into the scene's glass at its own alpha, as the viewport does; the CPU rasterizer draws the solid bodies first (they own the depth buffer), then the see-through ones blended over them, far sides before near sides and without writing depth, so what lies inside shows and a line between the two is not hidden. - `snapshot --body NAME:opacity=0.3,color=#rrggbb`, `--tool NAME` (that body solid in the tool colour, the rest see-through) and `--section z=27` run the commands the app itself runs (`appearance.assign`, `inspect.section`): the options are shortcuts, not a second renderer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Tests: measure the tool colour as it is drawn through a see-through target The tool-colour filter was derived on paper and demanded a red more than twice either other channel, which the target in front of it tints away (measured: (200, 108, 85) through opacity 0.3); and the colour check painted the tool, which a solid target hides. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fmt * Snapshot options: strip the colour's # rather than slicing it; README Slicing `value[1..]` on an input-derived string is what the house rules ask us not to write, even where a length check has already made it safe. The error a failed assignment reports now names the body rather than claiming it came from --body, since --tool reaches it too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * --tool: assign the bodies around the tool in groups, not one command at a time Each appearance.assign is an undo step with a document snapshot behind it, so one per body made --tool cost grow with the size of the design for no reason: the bodies around the tool all get the same opacity, and the ones that share a colour can be named in one call. A design whose bodies all wear the default grey now costs two commands whatever its size. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Rasterizer: say where its glass and the GPU's differ Splitting each see-through body by facing orders its own two layers; it does not sort across bodies, which the GPU does. Worth stating rather than leaving a reader to find it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
d3fa22e987 |
3D mouse calibration in Preferences (#2326)
Adds a 3D mouse calibration section to Preferences (#1463). |
||
|
|
6f93e7a851 |
fix(examples): cut both mounting holes in the bracket (#1515)
Co-authored-by: bherbruck <brennen.herbruck@gmail.com> |
||
|
|
f3505f0db6 |
World placement on all cores (#3053) (#3123)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |