-
-
Notifications
You must be signed in to change notification settings - Fork 28
Deferred compression of frontend objects #494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -26,26 +26,45 @@ Objects = <||> | |||||||||
| Symbols = <||> | ||||||||||
|
|
||||||||||
|
|
||||||||||
| Compressed[string_String, {"ExpressionJSON", "ZLIB"}] := ImportByteArray[ByteArray[Developer`RawUncompress[BaseDecode[string]//Normal]], "ExpressionJSON"] | ||||||||||
| (* ::: Compression for large frontend objects :::*) | ||||||||||
|
|
||||||||||
| Compressed[string_String, {"ExpressionJSON", "ZLIB"}] := ImportByteArray[ByteArray[Developer`RawUncompress[BaseDecode[string]//Normal]], "ExpressionJSON"] // ReleaseHold | ||||||||||
|
|
||||||||||
| compression; | ||||||||||
|
|
||||||||||
| (* [NOTE] This is a deferred compression method, i.e. it is only applied when the object is requested via net / link *) | ||||||||||
| (* Otherwise ExpressionJSON uncontrollably lifts the context from symbols depending where forntend object was created, *) | ||||||||||
| (* this leads to some symbols to be falsly assumed to be in Global`, which will throw errors on the frontend *) | ||||||||||
|
||||||||||
| (* this leads to some symbols to be falsly assumed to be in Global`, which will throw errors on the frontend *) | |
| (* this leads to some symbols to be falsely assumed to be in Global`, which will throw errors on the frontend *) |
Copilot
AI
Apr 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in comment: “deffer” should be “defer”.
| (* The only way to avoid this is to deffer compression and ExpressionJSON convertion. *) | |
| (* The only way to avoid this is to defer compression and ExpressionJSON convertion. *) |
Copilot
AI
Apr 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in comment: “convertion” should be “conversion”.
| (* The only way to avoid this is to deffer compression and ExpressionJSON convertion. *) | |
| (* The only way to avoid this is to deffer compression and ExpressionJSON conversion. *) |
Copilot
AI
Apr 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FrontEndRef now routes stored objects through releaseCompression, which (for deferred wrappers) performs an ExportByteArray[..., "ExpressionJSON"] + compress, then ReleaseHold immediately triggers Compressed[...] and re-imports/decompresses the JSON. This adds a costly JSON round-trip on every FrontEndRef call and can reintroduce the symbol-context issues described in the header comment (since ExpressionJSON conversion happens under the current $Context). Consider unwrapping the deferred wrapper for kernel-side access (e.g., return the original expr/Hold[expr]) and only run ExpressionJSON conversion when actually sending over the link.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,9 +25,13 @@ syncMonitor = ImportComponent[FileNameJoin[{rootDir, "templates", "SyncMonitor.w | |
| EventHandler[NotebookEditorChannel // EventClone, { | ||
| "FetchFrontEndObject" -> Function[data, | ||
| Echo["Sync >> requested from master kernel"]; | ||
| With[{promise = data["Promise"], kernel = GenericKernel`HashMap[ data["Kernel"] ]}, | ||
| With[{result = CoffeeLiqueur`Extensions`FrontendObject`Internal`Objects[data["UId"] ]}, | ||
| GenericKernel`Async[kernel, EventFire[promise, Resolve, result["Public"] ] ]; | ||
| With[{promise = data["Promise"], kernel = GenericKernel`HashMap[ data["Kernel"] ]}, | ||
| (* [FIXME] Include these symbols normally using Needs[] *) | ||
| (* we release any possible deferred compression wrappers *) | ||
| With[{result = CoffeeLiqueur`Extensions`FrontendObject`Internal`Objects[data["UId"] ]["Public"]}, | ||
| With[{c = CoffeeLiqueur`Extensions`FrontendObject`Internal`releaseCompression[result]}, | ||
| GenericKernel`Async[kernel, EventFire[promise, Resolve, c ] ]; | ||
|
Comment on lines
+28
to
+33
|
||
| ]; | ||
| ]; | ||
| ]; | ||
| ] | ||
|
|
||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -5526,7 +5526,9 @@ if (!GUI && PathRendering) { | |||||
| /** | ||||||
| * @type {HTMLElement} | ||||||
| */ | ||||||
| const container = env.element; | ||||||
| const container = document.createElement('div'); | ||||||
| container.classList.add('relative'); | ||||||
| env.element.appendChild(container); | ||||||
|
|
||||||
| /** | ||||||
| * @type {[Number, Number]} | ||||||
|
|
@@ -5694,7 +5696,7 @@ env.local.renderer = renderer; | |||||
|
|
||||||
| //fix for translate-50% layout | ||||||
| const layoutOffset = {x:0, y:0}; | ||||||
| if (container.classList.contains('slide-frontend-object')) { | ||||||
| if (env.element.classList.contains('slide-frontend-object')) { | ||||||
| layoutOffset.x = -1.0; | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -5778,19 +5780,22 @@ if (PathRendering) { | |||||
| } | ||||||
|
|
||||||
| let controlObject = { | ||||||
| init: (camera, dom) => { | ||||||
| controlObject.o = new OrbitControls( camera, domElement ); | ||||||
| controlObject.o.addEventListener('change', wakeFunction); | ||||||
| controlObject.o.target.set( 0, 1, 0 ); | ||||||
| controlObject.o.update(); | ||||||
| }, | ||||||
| init: (camera, dom) => { | ||||||
| controlObject.o = new OrbitControls( camera, domElement ); | ||||||
|
||||||
| controlObject.o = new OrbitControls( camera, domElement ); | |
| controlObject.o = new OrbitControls( camera, dom ); |
Copilot
AI
Apr 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When Controls is explicitly set to a falsy value, you disable pointer events on domElement, but controlObject.init(...) is still called unconditionally later (and controls = controlObject.o assumes init ran). This still constructs OrbitControls and registers listeners even though controls are intended to be disabled. Consider gating the init call (and controls assignment) behind the same enabled/disabled condition, or explicitly set controls.enabled = false / configure OrbitControls’ enableRotate|enablePan|enableZoom depending on what "Controls" is meant to control (the current pointerEvents = 'none' disables all interactions, not just rotation/pan).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in comment: “forntend” should be “frontend”.