Skip to content

Commit f8781ef

Browse files
authored
Deferred compression of frontend objects (#494)
* Deferred compression to avoid contexts missmatch when using ExpressionJSON * fix container layout offset & added Controls disable * updated release notes and readme
1 parent 8f82bed commit f8781ef

7 files changed

Lines changed: 175 additions & 103 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ Typeset math input, syntactic sugar, multiple languages, and a fast, granular ev
5353

5454
See how at [wljs.io](https://wljs.io/frontend/setup)
5555

56+
## Star History
57+
58+
<a href="https://www.star-history.com/?repos=WLJSTeam%2Fwljs-notebook&type=date&legend=top-left">
59+
<picture>
60+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=WLJSTeam/wljs-notebook&type=date&theme=dark&legend=top-left" />
61+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=WLJSTeam/wljs-notebook&type=date&legend=top-left" />
62+
<img alt="Star History Chart" src="https://api.star-history.com/image?repos=WLJSTeam/wljs-notebook&type=date&legend=top-left" />
63+
</picture>
64+
</a>
65+
5666
## Highlights
5767

5868
### Feels like Mathematica and Jupyter

modules/wljs-demos-archive/Demos/Release notes/3.0.3.wln

Lines changed: 82 additions & 63 deletions
Large diffs are not rendered by default.

modules/wljs-editor/src/FrontendObject.wl

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,45 @@ Objects = <||>
2626
Symbols = <||>
2727

2828

29-
Compressed[string_String, {"ExpressionJSON", "ZLIB"}] := ImportByteArray[ByteArray[Developer`RawUncompress[BaseDecode[string]//Normal]], "ExpressionJSON"]
29+
(* ::: Compression for large frontend objects :::*)
30+
31+
Compressed[string_String, {"ExpressionJSON", "ZLIB"}] := ImportByteArray[ByteArray[Developer`RawUncompress[BaseDecode[string]//Normal]], "ExpressionJSON"] // ReleaseHold
32+
33+
compression;
34+
35+
(* [NOTE] This is a deferred compression method, i.e. it is only applied when the object is requested via net / link *)
36+
(* Otherwise ExpressionJSON uncontrollably lifts the context from symbols depending where forntend object was created, *)
37+
(* this leads to some symbols to be falsly assumed to be in Global`, which will throw errors on the frontend *)
38+
(* The only way to avoid this is to deffer compression and ExpressionJSON convertion. *)
39+
(* [FIXME] for the future: switch to Compress and WXF formats instead of JSON !!! *)
40+
41+
(* apply only on large objects*)
42+
compression[expr_, {"ExpressionJSON", "ZLIB", "Defer"}] := Hold[expr] /; (ByteCount[expr] < 0.1 * 1024 * 1024);
43+
44+
SetAttributes[releaseCompression, HoldAll]
45+
46+
releaseCompression[compression[expr_, {"ExpressionJSON", "ZLIB", "Defer"}]] := With[{arr = Normal[ExportByteArray[expr, "ExpressionJSON"] ]},
47+
With[{data = BaseEncode[ByteArray[Developer`RawCompress[arr] ] ]},
48+
Compressed[data, {"ExpressionJSON", "ZLIB"}] // Hold
49+
]
50+
]
51+
52+
releaseCompression[expr_] := expr
53+
54+
3055

31-
compress[expr_, f: {"ExpressionJSON", "ZLIB"}] := Hold[expr]
32-
compress[expr_, f: {"ExpressionJSON", "ZLIB"}] := With[{arr = Normal[ExportByteArray[expr, "ExpressionJSON"] ]},
33-
With[{data = BaseEncode[ByteArray[Developer`RawCompress[arr] ] ]},
34-
Compressed[data, f] // Hold
35-
]
36-
] /; (ByteCount[expr] > 0.1 * 1024 * 1024);
3756

3857
CreateFrontEndObject[expr_, uid_String, OptionsPattern[] ] := With[{},
3958
With[{
4059
data = Switch[OptionValue["Store"]
4160
, "Kernel"
42-
, <|"Private" -> compress[expr, {"ExpressionJSON", "ZLIB"}]|>
61+
, <|"Private" -> compression[expr, {"ExpressionJSON", "ZLIB", "Defer"}]|>
4362

4463
, "Frontend"
45-
, <|"Public" -> compress[expr, {"ExpressionJSON", "ZLIB"}]|>
64+
, <|"Public" -> compression[expr, {"ExpressionJSON", "ZLIB", "Defer"}]|>
4665

4766
,_
48-
, <|"Private" -> compress[expr, {"ExpressionJSON", "ZLIB"}], "Public" :> Objects[uid, "Private"]|>
67+
, <|"Private" -> compression[expr, {"ExpressionJSON", "ZLIB", "Defer"}], "Public" :> Objects[uid, "Private"]|>
4968
]
5069
},
5170
If[!AssociationQ[Objects],
@@ -69,7 +88,9 @@ CreateFrontEndObject[expr_, opts: OptionsPattern[] ] := CreateFrontEndObject[exp
6988
Options[CreateFrontEndObject] = {"Store" -> All}
7089

7190
FrontEndRef[uid_String] := If[KeyExistsQ[Objects, uid],
72-
Objects[uid, "Private"] // ReleaseHold
91+
With[{o = Objects[uid, "Private"]},
92+
releaseCompression[o] (*Ehhhh Okay... we need to release it anyway *)
93+
] // ReleaseHold
7394
,
7495
$MissingHandler[uid, "Private"] // ReleaseHold
7596
]
@@ -79,7 +100,9 @@ FrontEndExecutable /: MakeBoxes[FrontEndExecutable[uid_String], StandardForm] :=
79100
GetObject[uid_String] := With[{},
80101
(*Echo["Getting object >> "<>uid];*)
81102
If[KeyExistsQ[Objects, uid],
82-
Objects[uid, "Public"]
103+
With[{ c = Objects[uid, "Public"] },
104+
releaseCompression[c]
105+
]
83106
,
84107
$Failed
85108
]

modules/wljs-editor/src/FrontendObjectSync.wl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ syncMonitor = ImportComponent[FileNameJoin[{rootDir, "templates", "SyncMonitor.w
2525
EventHandler[NotebookEditorChannel // EventClone, {
2626
"FetchFrontEndObject" -> Function[data,
2727
Echo["Sync >> requested from master kernel"];
28-
With[{promise = data["Promise"], kernel = GenericKernel`HashMap[ data["Kernel"] ]},
29-
With[{result = CoffeeLiqueur`Extensions`FrontendObject`Internal`Objects[data["UId"] ]},
30-
GenericKernel`Async[kernel, EventFire[promise, Resolve, result["Public"] ] ];
28+
With[{promise = data["Promise"], kernel = GenericKernel`HashMap[ data["Kernel"] ]},
29+
(* [FIXME] Include these symbols normally using Needs[] *)
30+
(* we release any possible deferred compression wrappers *)
31+
With[{result = CoffeeLiqueur`Extensions`FrontendObject`Internal`Objects[data["UId"] ]["Public"]},
32+
With[{c = CoffeeLiqueur`Extensions`FrontendObject`Internal`releaseCompression[result]},
33+
GenericKernel`Async[kernel, EventFire[promise, Resolve, c ] ];
34+
];
3135
];
3236
];
3337
]

modules/wljs-graphics3d-threejs/dist/kernel.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5303,7 +5303,9 @@ if (!GUI && PathRendering) {
53035303
/**
53045304
* @type {HTMLElement}
53055305
*/
5306-
const container = env.element;
5306+
const container = document.createElement('div');
5307+
container.classList.add('relative');
5308+
env.element.appendChild(container);
53075309

53085310
/**
53095311
* @type {[Number, Number]}
@@ -5469,7 +5471,7 @@ env.local.renderer = renderer;
54695471

54705472
//fix for translate-50% layout
54715473
const layoutOffset = {x:0, y:0};
5472-
if (container.classList.contains('slide-frontend-object')) {
5474+
if (env.element.classList.contains('slide-frontend-object')) {
54735475
layoutOffset.x = -1.0;
54745476
}
54755477

@@ -5553,19 +5555,22 @@ if (PathRendering) {
55535555
}
55545556

55555557
let controlObject = {
5556-
init: (camera, dom) => {
5557-
controlObject.o = new OrbitControls( camera, domElement );
5558-
controlObject.o.addEventListener('change', wakeFunction);
5559-
controlObject.o.target.set( 0, 1, 0 );
5560-
controlObject.o.update();
5561-
},
5558+
init: (camera, dom) => {
5559+
controlObject.o = new OrbitControls( camera, domElement );
5560+
controlObject.o.addEventListener('change', wakeFunction);
5561+
controlObject.o.target.set( 0, 1, 0 );
5562+
controlObject.o.update();
5563+
},
55625564

5563-
dispose: () => {
5564-
5565-
}
5566-
};
5565+
dispose: () => {
55675566

5567+
}
5568+
};
55685569

5570+
if ('Controls' in options && !(await interpretate(options.Controls))) {
5571+
controlObject.disabled = true;
5572+
domElement.style.pointerEvents = 'none';
5573+
}
55695574

55705575
if (options.Controls) {
55715576

@@ -5755,6 +5760,8 @@ if (options.Controls) {
57555760
}
57565761
}
57575762

5763+
5764+
57585765
env.local.controlObject = controlObject;
57595766

57605767

@@ -6981,6 +6988,7 @@ core.Graphics3D.destroy = (args, env) => {
69816988
if (env.local.labelContainer) env.local.labelContainer.remove();
69826989
if (env.local.guiContainer) env.local.guiContainer.remove();
69836990
env.local.rendererContainer.remove();
6991+
env.local.element.remove();
69846992
};
69856993

69866994
core.Graphics3D.virtual = true;

modules/wljs-graphics3d-threejs/dist/kernel.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/wljs-graphics3d-threejs/src/kernel.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5526,7 +5526,9 @@ if (!GUI && PathRendering) {
55265526
/**
55275527
* @type {HTMLElement}
55285528
*/
5529-
const container = env.element;
5529+
const container = document.createElement('div');
5530+
container.classList.add('relative');
5531+
env.element.appendChild(container);
55305532

55315533
/**
55325534
* @type {[Number, Number]}
@@ -5694,7 +5696,7 @@ env.local.renderer = renderer;
56945696

56955697
//fix for translate-50% layout
56965698
const layoutOffset = {x:0, y:0};
5697-
if (container.classList.contains('slide-frontend-object')) {
5699+
if (env.element.classList.contains('slide-frontend-object')) {
56985700
layoutOffset.x = -1.0;
56995701
}
57005702

@@ -5778,19 +5780,22 @@ if (PathRendering) {
57785780
}
57795781

57805782
let controlObject = {
5781-
init: (camera, dom) => {
5782-
controlObject.o = new OrbitControls( camera, domElement );
5783-
controlObject.o.addEventListener('change', wakeFunction);
5784-
controlObject.o.target.set( 0, 1, 0 );
5785-
controlObject.o.update();
5786-
},
5783+
init: (camera, dom) => {
5784+
controlObject.o = new OrbitControls( camera, domElement );
5785+
controlObject.o.addEventListener('change', wakeFunction);
5786+
controlObject.o.target.set( 0, 1, 0 );
5787+
controlObject.o.update();
5788+
},
57875789

5788-
dispose: () => {
5789-
5790-
}
5791-
};
5790+
dispose: () => {
57925791

5792+
}
5793+
}
57935794

5795+
if ('Controls' in options && !(await interpretate(options.Controls))) {
5796+
controlObject.disabled = true;
5797+
domElement.style.pointerEvents = 'none';
5798+
}
57945799

57955800
if (options.Controls) {
57965801

@@ -5980,6 +5985,8 @@ if (options.Controls) {
59805985
}
59815986
}
59825987

5988+
5989+
59835990
env.local.controlObject = controlObject;
59845991

59855992

@@ -7209,6 +7216,7 @@ core.Graphics3D.destroy = (args, env) => {
72097216
if (env.local.labelContainer) env.local.labelContainer.remove();
72107217
if (env.local.guiContainer) env.local.guiContainer.remove();
72117218
env.local.rendererContainer.remove();
7219+
env.local.element.remove();
72127220
}
72137221

72147222
core.Graphics3D.virtual = true

0 commit comments

Comments
 (0)