forked from 1b-agent/opencadstudio-upstream
feat: in-place save (File System Access), image paste, print wiring, web capture
The last high-severity web gap plus a small integration bundle.
In-place save: with the File System Access API (Chromium/Edge), Save
now works like desktop. Plain Save on a tab with a retained handle
writes straight through — no picker, no download, no "(1)" copies.
First save or format change opens showSaveFilePicker AT the confirm
gesture (the worker serialization that follows can outlast the
transient-activation window; retained handles need no activation to
write). Picker cancel cancels the save like desktop; picker or write
failure falls back to the legacy download with a note; Firefox/Safari
keep the download flow untouched (feature-detected). Handles are
keyed by tab id in a thread-local registry (Message must be Send —
JsValue cannot ride in messages or App state); closing a tab drops
its handle; Save As re-picks. Dirty clears only on successful
persistence, same race guard as before.
Image paste: Ctrl+V of a screenshot inserts it as an image reference
— the clipboard reader falls back to image/* items when neither the
entity clipboard nor text has anything (precedence preserved), the
bytes enter the image source registry as pasted-<timestamp>.<ext>,
and attach reuses the IMAGE command's model-creation path verbatim.
Browser print: beforeprint snapshots the WebGL canvas into a print-
only <img> (the capture_canvas technique), so the browser's own print
menu produces the drawing instead of a blank page.
Automation capture on web: the capture op no longer calls the
synchronous GPU-map screenshot (dead on wasm); main's frame-parked
capture waits for a presented frame and reads via capture_canvas — the
same workaround the save-thumbnail path uses — delivering the PNG as a
download.
(cherry picked from commit 6e89b36d)
Ported onto main (#1717 review): the web capture rides main's existing
frame-parked capture (one ControlCaptureFrame path, ignoring the timer
tick on wasm) instead of a second parked-capture state; closing a tab
drops its file handle inside main's on_tab_close.
This commit is contained in:
1 parent
0459f20129
commit
835e8d469f
33 files changed
+1084
-29
No files matched your search
@@ -38,6 +38,16 @@
|
||||
animation: ocs-spin 0.9s linear infinite;
|
||||
}
|
||||
@keyframes ocs-spin { to { transform: rotate(360deg); } }
|
||||
/* Browser-menu print support: hidden on screen, the only thing shown on
|
||||
paper. beforeprint snapshots the WebGL canvas into it (below), so the
|
||||
printout is the drawing instead of a blank page — WebGL canvases do
|
||||
not composite into the print surface on their own. */
|
||||
#print-snapshot { display: none; }
|
||||
@media print {
|
||||
body > *:not(#print-snapshot) { display: none !important; }
|
||||
html, body { overflow: visible; height: auto; background: #fff; }
|
||||
#print-snapshot { display: block; max-width: 100%; height: auto; }
|
||||
}
|
||||
</style>
|
||||
<link data-trunk rel="rust" data-bin="OpenCADStudio" />
|
||||
<link data-trunk rel="copy-dir" href="web/fonts" />
|
||||
@@ -56,6 +66,8 @@
|
||||
<div class="title">Open <span>CAD</span> Studio</div>
|
||||
<div class="sub">Loading…</div>
|
||||
</div>
|
||||
<!-- Print-only copy of the canvas; filled by beforeprint below. -->
|
||||
<img id="print-snapshot" alt="" />
|
||||
<script src="ocs-control.js" defer></script>
|
||||
<script>
|
||||
// Offline support: register the service worker. It is copied next to
|
||||
@@ -72,6 +84,32 @@
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
// Print support. The drawing lives in a WebGL canvas, which browsers
|
||||
// rasterize as blank on the print surface. beforeprint therefore copies
|
||||
// it onto a 2D canvas (the same drawImage readback the Rust side's
|
||||
// sys::capture_canvas uses — no Rust state involved, the canvas is
|
||||
// located from the DOM) and shows the snapshot in the print-only
|
||||
// <img>; afterprint drops it again to free the PNG.
|
||||
(() => {
|
||||
const snapshot = document.getElementById("print-snapshot");
|
||||
if (!snapshot) return;
|
||||
window.addEventListener("beforeprint", () => {
|
||||
const canvas = document.querySelector("canvas");
|
||||
if (!canvas || !canvas.width || !canvas.height) return;
|
||||
const copy = document.createElement("canvas");
|
||||
copy.width = canvas.width;
|
||||
copy.height = canvas.height;
|
||||
const context = copy.getContext("2d");
|
||||
if (!context) return;
|
||||
context.drawImage(canvas, 0, 0);
|
||||
snapshot.src = copy.toDataURL("image/png");
|
||||
});
|
||||
window.addEventListener("afterprint", () => {
|
||||
snapshot.removeAttribute("src");
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
(() => {
|
||||
const splash = document.getElementById("loading");
|
||||
|
||||
Reference in new issue
Block a user