mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 20:02:16 +00:00
stack/render-11087
57 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
c5bc849913 | Blur: keep themed backing and notice actions accessible (#9810) | ||
|
|
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. |
||
|
|
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 ( |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
9b97e06f71 |
Web: open tabs share settings changes, so one tab no longer undoes another's (#8644) (#8832)
* claim: #8644 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Web: open tabs share settings changes, so one tab no longer undoes another's (#8644) Every open tab of the web app saved its whole copy of the settings to the same browser storage, every 30 s and on focus loss, so a tab still holding old settings put them back over a toolbar change made in another tab. A tab now writes its settings in the frame they change, and the other open tabs load them from the browser's storage event, so their own saves keep them. Settings loaded that way aren't written back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
6b523d6839 |
Theme drops: build for the web, and import there too (#8226) (#8534)
DroppedFile::bytes exists only on native; the web build stopped at take_theme_drops. Native keeps reading the file there. On the web, the shell already reads every drop asynchronously before the app sees it: a drop over Preferences' Import tile now arrives as a theme and goes to prefs::import_theme_bytes, instead of being opened as a design. |
||
|
|
d7ef5280c5 |
Ctrl+W closes the design shown, not the browser tab (#5366) (#5466)
Ctrl+W (Cmd+W) closes the active design, asking first when it is unsaved; closing the last one shows the start page. In the web app a capture-phase keydown listener keeps the browser from closing the tab while a design is shown; on the start page the browser closes the tab. Ctrl+W joins the keys the app reserves, and the tab menu's Close shows it. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8be7bb0201 |
Insert design: file names get the dialog's width (#8097) (#8122)
* claim: #8097 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Insert design: file names get the width the size and actions leave (#8097) The web app's "Files in this browser" window listed stored files in an egui Grid sized from cell content: the wide action buttons took most of the fixed window width, the name and size columns were cut, and the rest was empty. Add ui_kit::name_list on the kit's table grid: the name column takes the width the other columns leave, the other columns are as wide as their widest cell (measured as drawn, a changed measurement discards the frame), and a name is cut with an ellipsis and its full text on hover only when it does not fit. The window is a little wider (still capped at the browser width). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
03b8f4138b |
New designs save as .sc; .solidcraft files still open (#7325)
* claim: new designs save as .sc Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * New designs save as .sc; .solidcraft files still open and keep their name on Save DESIGN_EXT is now "sc"; DESIGN_EXTS lists sc, solidcraft and solvecraft and every open path (dialogs, drag-drop, recent files, CLI, MCP, links, the web picker) accepts all three. Save of an opened .solidcraft file writes that file; Save As, autosave, recovery and crash reports use .sc. Windows, macOS and Linux register .sc alongside the older extensions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Update UI tests for the .sc default extension Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7a6c884418 |
Change the app id to io.tangencylabs.solidcraft, with aliases for existing installs (#7681)
The app id moves from ai.storyteller.solidcraft to io.tangencylabs.solidcraft: macOS bundle id (the CLI signs as io.tangencylabs.solidcraft.cli), Linux desktop/metainfo/MIME file names, icon name, Wayland app_id / X11 class, Flatpak, Snap common-id, nfpm/FreeBSD/Nix/AppImage staging, the web app's icon paths and the docs. Existing installs keep working: the Flatpak repository marks both old ids (ai.storyteller.solidcraft and ai.storyteller.solvecraft) end-of-life with a rebase straight to the new id, re-marking the solvecraft ref that pointed at the 0.8 id; the manifest grants read-only access to both old ~/.var/app folders and the first launch copies settings and autosaves from the newest one; AppStream <replaces>/<provides> and X-Flatpak-RenamedFrom list both; the AppImage removes menu entries it wrote under either old id. The data and settings folders (solidcraft / SolidCraft) are unchanged. The MSI UpgradeCode and ProgIDs are unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2346c1ae81 |
Web app: Report a bug saves its screenshot as a download (#7192) (#7197)
* claim: #7192 * Web app: Report a bug saves its screenshot as a download (#7192) Save screenshot… wrote the PNG with image's save, that is std::fs, which the browser doesn't have. The screenshot is now encoded in memory and written through vfs, as every other save is, so the web app hands it out as a download. Natively the same file is written and ui.screenshot replies as before. A failed save says why in plain text instead of a Rust debug value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Web app: a screenshot without a path no longer panics (#7192) `ui.screenshot` without a `path` saved to `std::env::temp_dir()`, which panics on wasm32-unknown-unknown (std has no file system there), and the web app's control requests come through `window.solidcraft.request`. `vfs::temp_dir()` now returns `vfs::BROWSER_TEMP`, a folder of the in-memory files, in the browser, and `save_screenshot` and `vfs::design_folder_refusal` (reached when a request names `allow_paths` while a design is open) use it. Natively nothing changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Web app: keymap and theme exports download (#7192) Both exports wrote with `std::fs::write`, which always fails in the browser. They now write through `vfs::write` like the other saves, so the web app hands the file out as a download. Natively the file is the same; an agent's folder scope now applies to them as to every other write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: say what the folder-scope checks of the saves show (#7192) The screenshot test wraps the write in a folder scope by hand: that proves the write goes through vfs, not that ui.screenshot's write runs inside the request's scope. ui.screenshot checks the agent's folders when the request arrives; the PNG is written in a later frame, outside that scope, as on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9d01dcb358 |
Open from computer: let iOS and iPadOS pick .solidcraft files (#6977) (#7351)
* claim: #6977 * Open from computer: let iOS and iPadOS pick .solidcraft files (#6977) The iOS file picker matches an input's accept list by system file type, not by extension. No system type has the .solidcraft extension, so the designs were greyed out. On an iPhone or iPad the file inputs now offer every file; other devices keep the extension filter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
afd6a5c584 |
Send to Slicer: send the selected bodies to a slicer (#2595) (#3600)
* Add Open in Slicer for the selected bodies (#2595) One command saves the selected bodies as a 3MF in millimetres (an STL if the 3MF can not be written) and opens it in the last slicer used. It is on the File menu, the Mesh tab and the body right-click menu. The first time, or when that slicer is gone, it asks which one with the same list Save as mesh uses. The Save as mesh dialog is unchanged. * Send slicer files to a private per-user folder (#2595) Files sent to a slicer used to go to a shared folder in the temp directory. They now go to the user cache folder (mode 0700 on Unix), under a name that stays the same for the same design and bodies, so sending again replaces the file and the slicer can reload it. The file is written to a temporary name and renamed into place. If the name can not be replaced, as on Windows while the slicer holds the file open, the copy gets a number and the status line says so. Files older than seven days are deleted at startup. The first send tells the user that sending again replaces the open file, and so does the tooltip of Open in Slicer. Send to slicer in Save as mesh uses the same path. * Open files in a running slicer (#2595) PrusaSlicer, SuperSlicer, Bambu Studio and Cura are started with --single-instance, so a slicer that is already open loads the file instead of opening a second window. OrcaSlicer has no such flag and is left alone, and macOS bundles are single-instance already. PrusaSlicer 3.0 ships a launcher entry that already carries --single-instance-on-url; it keeps that one and never gets a second flag. It is a preference, on by default, and scripts can pass single_instance. Preferences also gets a button to forget the slicer Open in Slicer remembers. * List slicers the desktop entries say open 3MF or STL (#2595) On Linux the scan of desktop entries now also lists a program that claims 3MF or STL in MimeType, even when it is not in our table of known slicers. The table still supplies names, ids, icons and flags for the ones it knows. Entries marked NoDisplay or Hidden are skipped, and so are terminal programs and SolidCraft itself. The existing size and count limits stay. The Windows scan now looks inside a Prusa3D folder, where PrusaSlicer installs itself. * Only start slicers the user can see or chose (#2595) file.save_mesh took a slicer parameter that could be any executable path, so a script or an MCP client could run any program. The slicer now has to be one of the detected slicers (by id or name) or a program the user already added under Custom, which the app hands to the engine from its settings. The same rule holds for file.open_in_slicer. The agent chat already asked before any file command; its question now also names the slicer. * Review fixes for Open in Slicer (#2595) The file name hash keeps all 64 bits instead of 32. When Open in Slicer has to fall back to an STL, the status line now says so. The engine test that sets the chosen programs clears them again. The unused "could not make the folder" message is gone from the catalogs, and the docs say plainly that finding slicers by file type is Linux only. * Keep unsaved designs apart and hide Open in Slicer in the browser (#2595) Two unsaved designs with the same name and body names got the same file in the slicer folder. Every document now has a uid that is new for each New, Open and Recover and stays the same for the life of the document, and the file name uses it (with the process id) until the design has a path. At the first Save As the name changes to the path-based one, so the slicer gets one new file; the old one goes with the seven-day sweep. The browser can not start a program, so Open in Slicer is now disabled there: the engine command reports why, the File menu item and the body and component right-click items are greyed, and starting it anyway says so on the status line instead of opening a dialog. The Save as mesh dialog keeps its browser guard for the open flow too. * Put the Session doc comment back on Session (#2595) The new NEXT_UID static had landed between the doc comment and the struct it describes. * List every install of a slicer, and test the first Save As (#2595) Detection kept one entry per known slicer, so a system PrusaSlicer 2.9 and a Flatpak 3.0 beta showed up as one. Every distinct install is listed now. The same program found twice, through a desktop entry and through PATH, is still one. When a slicer has several installs, each gets an id made from where it was found (prusa#comprusa3dprusaslicer) that stays the same on every run, and a name worked out from its command and location without running anything: PrusaSlicer (system), PrusaSlicer (Flatpak, beta), PrusaSlicer (AppImage). A lone install keeps its plain id and name. The plain id or name still picks the first install, so a remembered "prusa" keeps working. Single-instance flags and the other per-slicer behaviour go by the id without the install part. Also a test that the first Save As changes the file name once and later sends keep the name that comes from the saved path. * Start PrusaSlicer 3.0 first and send the file once it is ready (#2595) PrusaSlicer 3.0 starts through slic3r-app-launcher, which ignores a file given at a cold start whatever the flag, and loads it over D-Bus when an instance is already running. A small table in slicers.rs names such slicers by their command; today it holds that launcher, so PrusaSlicer 2.9 and OrcaSlicer are untouched. Ready means a name starting with com.prusa3d.prusaslicer.BroadcastListener. is on the session bus, read from busctl --user list with no new dependency. If busctl is missing or fails, nobody waits and the file is sent as before. When the slicer is not ready, Open in Slicer starts it without the file and looks every 200 ms for up to 10 seconds. The wait runs on another thread, so the window stays live, and the status line says Starting PrusaSlicer. Once the name appears the file is sent with the same arguments as before. If the limit passes, the status line says the slicer did not respond, that the file is already saved, and to send again. A second click during the wait only repeats the Starting line, so there is one launch and one send. Scripts calling file.open_in_slicer wait inside the command by default, or pass wait: return or no. * Always show the slicer picker for Open in Slicer (#2595) File > Open in Slicer, the Mesh tab entry and the right-click entry now always open the picker, with the slicer used last time pre-selected, so the usual case is one click or Enter. Scripts and file.open_in_slicer with a slicer still send without a dialog. The preference button that forgot the remembered slicer is gone: it could no longer make anything ask again, and it did not clear the pre-selection either. The guide says how it works now. * Leave Bambu Studio out of the single-instance list (#2595) Open files in a running slicer now adds --single-instance for PrusaSlicer, SuperSlicer and Cura only. Bambu Studio is started as it is, so it may open a second window. A test pins that, for the detected slicer and for one the user picked as a custom program. The guide and the preference tooltip no longer name Bambu Studio as getting the flag. * Sort the English catalog and leave features.md to the generator (#2595) cargo xtask lists wanted one message moved in en.json after the rebase. docs/features.md is written by the Generated files workflow on main, so the row I had added by hand is gone from this branch; the workflow adds Open in Slicer when it lands. * Drop the unused folder message from the Vietnamese catalog (#2595) Vietnamese arrived on main after I removed ui.save_mesh_temp_failed from the other catalogs, so the check that every translated id exists in English failed on it. Nothing uses the message any more. * Say so when a started slicer can not be found again (#2595) After Open in Slicer had started a slicer on its own, a failure to look it up again to send the file just returned, so the status line stayed on Starting and then nothing. It now says the slicer was started, that it can not be found to send the file, and where the file is saved. A test covers it. * Say why a failed busctl ends the wait (#2595) If busctl fails partway through a wait, the wait stops and the file is sent, the same as when busctl can not be run at all. That is on purpose: a file sent too early is dropped quietly and costs one more send. A comment in wait_for_ready says so. * Keep the readiness wait inside its limit (#2595) wait_until asked the probe before checking the deadline, including right after a sleep, so a yes could be accepted after the limit, and busctl ran with no timeout, so one look could outlast the whole wait. The deadline is checked first now, the probe is told how much time is left, and busctl is given at most two seconds (less near the end of a wait) and is killed after that. A hung busctl counts as not telling, which sends the file as before. This covers the first look on the UI thread too. Tests cover a slow probe, the shrinking time left, and a fake busctl that hangs, answers and fails. * Send the file that was already written once the slicer is ready (#2595) When the wait for a started slicer ended, the UI ran file.open_in_slicer a second time. That exported the design again on the UI thread, from whatever the session looked like by then, and could write a numbered copy while the timeout message still named the first file. The wait now keeps the slicer it found and the answer of the first call, and opens the file that was written. A test changes the design during the wait and shows the same file is sent, is not written again, no command runs, and no second file appears. * Send with the single-instance flag after starting a slicer (#2595) After Open in Slicer has started a launcher that needs a start first, the file has to reach the instance that is now running, so the send that follows a start carries --single-instance whatever the preference says. A normal send, to a slicer that was already up, still follows the preference. A comment in both places says why. The test uses a plain launcher command with the preference off: the normal send has no flag and the send after a start has it. * Find the remembered slicer again after an uninstall (#2595) With two installs the remembered key is prusa#prusaslicer. Remove the other install and the lone one is plain prusa, which matched neither the exact key nor the plain-key fallback, so the picker fell back to the first slicer in the list, maybe a different one. The key is now also matched by the part before the #, so the same slicer is pre-selected. A test covers the lone install, a plain key with two installs, an exact key, and that another slicer is never picked. * Say a second click did not send anything while a slicer starts (#2595) While a started slicer is being waited for, another click showed Starting <the first slicer> even when it had picked another slicer or other bodies. It now says it is still waiting for that slicer and that nothing else was sent. The test checks the wording and that it names the slicer being waited for. * Label macOS installs by their application (#2595) An open -a command resolved its program to /usr/bin/open, so every application on a Mac was labelled system. Labels for open commands now come from the .app path: /Applications is system and a folder under home is shown as ~/Applications. A test with two PrusaSlicer.app bundles and a made-up home checks both labels and that the ids differ. * Pick the same place for a program found twice every time (#2595) When two desktop files, or a desktop file and PATH, pointed at one program, the first one read was kept and its file name went into the id, so the ids could change with the order the folders were read in even though nothing was installed or removed. The place that sorts first now speaks for the program. A test with two aliases and a second install lists them with the folders in both orders and gets the same ids and commands. * Treat flatpak run spelled two ways as one install (#2595) flatpak run ... and /path/to/flatpak run ... with the same arguments had different install keys, so one invocation could be listed twice. The launcher is now compared by its file name and the arguments are kept, so both give the same key, while a different branch is still its own install. The test uses a real launcher file so the full-path entry is not skipped as stale. * Say plainly that a late yes is accepted on purpose (#2595) The comment on wait_until said a slow probe could not accept a yes that came too late. That was not what the code does: no probe is started after the limit, but one already running may finish and its yes is taken, because the file should still go. The comment now says so, with the bound: the limit plus that last probe, which in production is given the time left and so ends by the limit. A test has a probe that runs past the limit and says yes, and checks it is accepted and the wait stays within limit plus the probe time, and that a probe that says no is not followed by another. * Give reading busctl the same deadline as running it (#2595) After busctl exited, the output was read with a join that could wait forever if a process it left behind kept the pipe open. The reader now hands the text back over a channel and the wait uses the time that is left of the same limit, and gives up with nothing otherwise. The polling sleep no longer overshoots the time left either. A test uses a fake busctl that backgrounds a sleep with its output inherited and exits at once: the call returns within its limit. * Tell two different flatpak launchers apart (#2595) Comparing only the file name of the launcher made /opt/tool-a/flatpak run ... and /usr/bin/flatpak run ... with the same arguments one install even when they are different programs. The launcher is now found the way any program is, through PATH and with links followed, and its name stands in only where it can not be found. The test has flatpak on PATH and the same file by its path as one install, a different executable of the same name as a second, a different branch as a third, and the fallback when there is no PATH. * Keep waiting when busctl is slow, send only when it is missing or fails (#2595) Since the busctl timeout, a slow busctl came back as "can not tell", and that was read as send now. One slow look, likely just when a cold PrusaSlicer 3.0 is starting under load, ended the wait and sent the file early, which gets dropped. The look now says why it has nothing: slow means not up yet, so the wait goes on to its limit and then reports that the slicer did not respond; a missing or failing busctl still means it can not be told and the file is sent. The comments name only those two cases. Tests use fake busctl programs: one that hangs keeps the wait going to the limit and times out, a missing one sends at once, one that says not up and then fails sends, and one that names the slicer is ready. * Do not open an empty path when the answer had no file (#2595) If the answer kept for a started slicer had no path, the send ran open with an empty string. The tick now says there is no file to send and starts nothing. The test gives a wait with no path, checks the error line, and that the fake slicer was never run. * Drop the unused folder message from the Danish catalog (#2595) Danish arrived on main with ui.save_mesh_temp_failed, which this branch removed from English because nothing uses it any more, so the check that every translated id exists in English failed. The entry is gone from the Danish catalog too. * Always start the 3.0 launcher with the single-instance flag (#2595) A hand run showed that starting the PrusaSlicer 3.0 launcher with no file while a 3.0 is already running forwards to it and exits when the flag is there, but opens a second full window when it is not. A slow first look at the bus can start the launcher twice, and with the preference off the flag was stripped. A slicer that has to be started first now keeps the flag on that start whatever the preference says, the same reasoning as the send after a start; when nothing runs the flag does no harm. A normal send with the preference off still has none. The test covers the launcher entry, a bare launcher command, a normal send and a slicer that is not on the table. * Swap --single-instance-on-url where it does not hand the file over (#2595) A hand run on the PrusaSlicer 2.9.6 Flatpak showed that its entry's --single-instance-on-url does not forward a file to the running instance: every send opened a new one, three after a cold start and two sends. With plain --single-instance the second process finds the first, forwards, and exits. So when a command carries --single-instance-on-url and the slicer is not on the wait table, that is, not the 3.0 launcher, it is replaced by --single-instance, with the file markers handled as before, when the preference is on, and stripped when it is off. On the 3.0 launcher the flag does forward and stays. The comment on the rule says why. A test uses the real 2.9.6 and 3.0 Flatpak command lines. * Cut long slicer names to fit the picker (#2595) With installs listed on their own, a name like PrusaSlicer (Flatpak, master) is wider than its row. The list painted it with no limit, so it ran out of the popup, and the closed selector wrapped it onto a second line. The list now lays the name out on one line that ends in an ellipsis when it does not fit, the selector truncates, and the full name is in the tooltip when it was cut (for a program the user added, ahead of the delete hint). A test checks that a long name is cut to one line within the width and a short one is left alone. * Keep the 3.0 launcher's flag on a send whatever the preference says (#2595) A hand run with the single-instance preference off showed the 3.0 launcher needs its flag on a send too, not only on a start: a cold start with a file and no flag loaded nothing, and a send to a running 3.0 left a launcher process that never forwarded. A slicer on the wait table now gets the flag on every launch, as it already did when started alone. The doc comment says why. The preference text, in English, Japanese and Chinese, and the guide now say that PrusaSlicer 3.0 is the exception to turning this off. The tests that took the flag away with the preference off now expect it, for the launcher entry, a bare launcher command and the 3.0 Flatpak line; the test of the send in the UI checks the first send carries it. * Keep the install-flag swap to PrusaSlicer, found by what it is (#2595) The swap of --single-instance-on-url for --single-instance ran for any program whose command had the flag, so another program with that flag would have been rewritten. It is now a PrusaSlicer rule: it applies to a slicer recognised as PrusaSlicer by its identity, its id or, for a program the user picked, the name it goes by, never by a version number. The single-instance table uses the same identity check. A test keeps the flag of a program that is not PrusaSlicer, strips it when the preference is off, and swaps it for a picked program named PrusaSlicer. * Do not promise a hard bound in the wait comment (#2595) The comment on wait_until said the production probe ends by the limit. Its clock starts after the process is spawned, and killing and reaping the process and scheduling are not bounded by it, so it is a polling and read budget with process overhead on top. The comment says that. Comment only. * Tell PrusaSlicer 2 from 3 by the installed version (#2595) PrusaSlicer 2 and 3 need different handling, and the rules had to guess from the command line. Each install of PrusaSlicer now carries its major version, read when it is detected from the release at the top of its appstream metadata and never by running the program: a Flatpak's own files (the branch the command names, else the current one), or the metainfo file beside a desktop entry. PrusaSlicer older than 3 is sent the file with --single-instance in place of --single-instance-on-url, which does not hand a file over there, and is not waited for. PrusaSlicer 3 and newer keeps the launcher's flag, is started on its own, waited for, and sent the file. When the version can not be read, the command decides as before: the slic3r-app-launcher launcher counts as 3, and any other entry is left as it is. A program that is not PrusaSlicer is untouched whatever its flags. Tests use metadata fixtures, no Flatpak: 2.9.6, 3.0.0-alpha10, a branch that is not installed, a system package, an unreadable version with and without the launcher, and a non-PrusaSlicer program with the same flag. * Make the busctl wait tests steady under load (#2595) One of them failed once in a full run at its first assertion and passed alone. A script that has only just been written can fail to start (text file busy) while another test forks, and that reads as a busctl that is missing. The fake programs are now started once, patiently, with a flag they answer at once, before a test relies on them; a hanging one uses exec so it is killed cleanly. The time bounds that could trip on a busy machine are several seconds now, and the checks are on outcomes: not ready and keeps waiting, send now, and the number of looks. Thirty runs alone, thirty of the whole slicers filter and thirty of the busctl tests under eight busy loops all pass. * Say what the busctl reader thread leaves behind (#2595) The thread that reads busctl output is left to end when the pipe closes. A busctl that answered but left a process holding its stdout reads as slow, and every poll of such a bus can leave one blocked reader behind until that process lets go. A comment next to the thread says so. Comment only. * Make Save as mesh a plain export (#2595) Save as mesh asked for the bodies and the format, and could also send the file to a slicer with a checkbox and a slicer list. Sending now has its own command, so the checkbox, the list and the Send label are gone from this dialog: bodies, format, a file dialog, Save. The two settings that only the checkbox used, save_mesh_send and save_mesh_slicer, are no longer kept; a settings file that still has them loads, and they are ignored. The strings only they used are gone from the catalogs. The slicer list stays in the dialog of Open in Slicer. file.save_mesh keeps its slicer parameter for scripts. The guide describes the two as separate. * Rename Open in Slicer to Send to Slicer (#2595) The command sends a file to a slicer rather than opening one, and a later Send to App can share the name. File menu, Mesh tab, right-click menus, the dialog button, status lines, the guide and the English, Japanese and Chinese strings now say Send to Slicer, and the command id file.open_in_slicer is file.send_to_slicer. It is new in this change, so there is no alias. The remembered slicer is stored as send_slicer, and the question the agent chat asks says send to. Module and type names stay as they are. * Put the send targets in one table and one send pipeline (#2595) The known slicers, the list of slicers with a single-instance flag and the rule for slicers that must be started and waited for were three separate things. They are one table of targets now: id, label, kind, names and program files, accepted formats and the default, how the file is handed over, the single-instance flag, the version from which PrusaSlicer's on-url flag hands a file over, and the wait-for-ready rule. Programs that are not in the table are slicers with the same defaults. The kind is slicer, the only one so far; its comment says it is where a later Send to App would plug in. The send itself is one function: write the bodies, find the target, start it and wait if its rule says so, hand the file over, report. Detection and resolution take a kind. Nothing changes for what already worked. file.save_mesh keeps its slicer parameter, now through the same function, with its export arguments and result unchanged: a cold PrusaSlicer 3 is started and waited for before the file goes, and it accepts the program path of a detected slicer as file.slicers lists it (file.slicers gives it as program). A path is accepted only when it is the program of exactly one detected target, and that target's own command and arguments run; a launcher several entries share, such as /usr/bin/flatpak, is refused as ambiguous, and any path nobody listed or chose is refused. The parameter and the code say it may be deprecated later in favour of file.send_to_slicer or a Send to App workflow, which is a possibility and not a plan. Tests go through file.save_mesh itself for the two reported failures, a listed AppImage path and a cold PrusaSlicer 3 that is waited for, with the slicers and the busctl made up; and they check that the result and the file are what an export without a slicer gives, and that a path that is not listed, or is shared by two entries, writes nothing. The pipeline has its own tests: a cold launcher waited for, one already up, wait return, and one that never comes up. A test of save_mesh that shared a file name with another is given its own. * Keep an AppImage's id through an upgrade (#2595) When a slicer has several installs, each gets an id made from where it was found. For an AppImage that was its file name, which carries the version, so upgrading it changed the id and the remembered choice missed. The id is now made of the slicer's id, the fact that it is an AppImage and a short hash of its folder, so a new version that replaces the old one in the same folder keeps the id, and the same AppImage in another folder is its own install. Two versions kept side by side in one folder are two installs: while both are there, their file names are added to their ids to tell them apart. A remembered id is found again only when it names exactly one install. An id that was one AppImage and is now two versions, or a plain id that is now several installs, is treated as not remembered, so the picker starts from the first entry as on a first run and does not quietly pick another install; a single install left of the same slicer is still found, as before. Installs that are not AppImages are told apart as before. An id from before this change no longer matches. Tests cover an upgrade, two versions side by side, a second folder, and the picker's side of each case. * Add Open with EasyPrint to Send to Slicer EasyPrint is Printables' slicer in the browser. It's a plain text row in the Send to Slicer dialog, backed by a new file.open_in_easyprint command. The command exports the bodies as a 3MF (or STL), hands them to the host to upload to slicer.printables.com/import, and the app opens the link that comes back. The desktop app uploads with ureq and the browser build with fetch, so this is the one send target that works on the web. The model leaves the machine, so the command refuses without consent: true, the dialog asks once and remembers, and an AI agent's request asks first. A model over 100 MB is refused before anything is sent, and a failed upload shows as an error with the reason. The chat's privacy note said SolidCraft sends nothing anywhere, which isn't true any more, so it now says that about the chat. New strings are in every catalog. * Place Send to Slicer on the Insert Files panel |
||
|
|
66b872a749 |
Web app: open a design from a same-origin path (#6670)
?open= now also accepts an absolute path on the page's own origin, fetched with the page's scheme, so a self-hosted setup that serves SolidCraft and other tools under one origin can link a design on a LAN without a public HTTPS address. Absolute URLs keep the HTTPS-only rule (a URL on the page's own origin is also accepted), redirects must end on HTTPS or the same origin, and protocol-relative, dot-segment, backslash and control-character values are refused. |
||
|
|
03bf992bc1 |
Keep linked components valid through Undo after Save As (#6860) (#6877)
* claim: #6860 * fix: preserve linked component targets across Save As history (#6860) |
||
|
|
0344a41dc4 | Open and insert a DXF outside a sketch (#2052) (#4635) | ||
|
|
4cd9a4f4d4 |
Web app: open a design from a link, and an embed mode (#6252) (#6253)
* claim: #6252 * Open hosted designs in the web app and add an embed layout Fixes #6252. Fetch HTTPS design links with bounded reads and open them as unsaved copies. Keep model panels and joint controls in embeds without browser persistence, application chrome, or unload prompts. Add a full-app link and iframe docs. Cover query parsing, hostile URLs, size limits, and the compact layout. * Translate the linked-design messages into Japanese and Chinese |
||
|
|
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. |
||
|
|
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: |
||
|
|
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 |
||
|
|
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. |
||
|
|
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> |
||
|
|
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> |
||
|
|
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. |
||
|
|
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. |
||
|
|
d3fa22e987 |
3D mouse calibration in Preferences (#2326)
Adds a 3D mouse calibration section to Preferences (#1463). |
||
|
|
a11446fd17 |
Web: a launch behind an expired sign-in shows the sign-in instead of the kept build (#2151)
Thanks @nofuturekid. |
||
|
|
4193ea26ac |
Expose a JavaScript command API for the browser app (#1713)
Thanks @olup. |
||
|
|
7d44359cf6 |
Web: show the SolidCraft mark being cut from a cube while loading (#1908)
Thanks @shuff57. |
||
|
|
c599238653 |
Insert SVG: the file picker opens on SVG files (#1801) (#1827)
* claim: #1801 * Insert SVG: the file picker opens on SVG files (#1801) Every Open went through one host picker whose filters start with designs, CAD files and meshes, so Insert SVG (and Insert DXF) in a sketch opened on STEP, IGES and meshes, and the SVG file had to be found under another filter. The host's pick_open now takes the extensions the command reads and offers them as the picker's first, default filter. Insert SVG and Insert DXF pass their type; every other Open passes none and keeps the list it had. The browser host ignores them (it shows its own file panel). --------- Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> |
||
|
|
d357bd2073 | docs: update public links and custom-domain Pages paths (#1780) | ||
|
|
c5f3515073 |
Viewport: hidden edges no longer show through faces, and thin or coplanar faces stop z-fighting (#1434, #1739) (#1768)
Thanks @CamWight. |
||
|
|
6220d8f8a6 |
Desktop 3D mouse on Windows without a 3DxWare profile: object-mode axes, no emulated wheel (#1380) (#1528)
Thanks @CamWight. |
||
|
|
1a0b33fcf3 |
Headless pictures draw on the GPU, with the viewport's pipelines (CPU rasterizer as last tier) (#1555)
Thanks @jyviko. |
||
|
|
a519f3cf74 |
Web app: 3D mouse navigation through the Gamepad API (#1473)
The web app reads a 3D mouse through the Gamepad API and drives the camera through navigation::device, so it follows each page's navigation capabilities (#1336). Co-authored-by: CamWight <CamWight@users.noreply.github.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
a13124acdd |
Import glTF 2.0 files (.glb, .gltf) as mesh bodies (#1512)
glTF files open, insert and drop like 3MF/STL/OBJ: a component named after the file, the node tree as nested rigid components, repeated meshes as occurrences, metres to mm, Y up turned to Z up by default (up: z keeps axes), materials as PBR appearances. Bounded, hostile-input tested reader; no new persisted data. Closes #1505. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e8c340c59d |
Web: installable as an app, with Install in the File menu (#1233)
The web build ships a manifest so the browser can install it as an app, and offers Install in the File menu while the browser allows it. |
||
|
|
af2f09fac2 |
A new empty design takes Units of new designs (#1283)
The empty design shown at launch, New on a design that is still empty, file.new and the blank design left when the last tab closes all take the Units of new designs preference. Opened files and the sample part keep their own units. Fixes #1205. Co-authored-by: raythurman2386 <raythurman2386@users.noreply.github.com> |
||
|
|
37547f8eba |
Keep Open and Save asynchronous to prevent macOS resize crashes (#1127)
Open and Save use parented AsyncFileDialog sheets; their answers run on the UI thread in a later frame, so resizing the window while a picker is open no longer re-enters winit and aborts. Editors, tab changes, Close/Quit and model-changing control requests wait for the answer; the local library Open and Export in the Appearance dialog use the same flow. Fixes #1088. |
||
|
|
9f76cca8b4 | Collect application resources for #433 (#833) |