Skip to main content

Release notes *2.7.1*

ยท 5 min read

๐Ÿ”ฅ Improved Legends Support โ€“ Now with better handling of Legended, PlotLegends. Easily integrate custom legends into your plots.
๐Ÿ“ฆ Export Enhancements โ€“ Cleaner wljs-html-export, better organized formats, and improved Mathematica & HTML notebooks!
๐Ÿ–ฅ๏ธ Dynamic HTML Upgrades โ€“ Smarter virtual machines for tracking symbol mutations, leading to more efficient interactivity.
๐Ÿณ Docker Support โ€“ Deploy effortlessly with a single command, including all the latest libraries and Wolfram Engine support!
๐Ÿ” Autotests โ€“ Now using playwright to catch bugs early with screenshot-based integration testing.
๐Ÿ†• Windows Compatibility โ€“ Full support for ESM & Shell cells on Windows machines!

Try out the latest improvements and experience a smoother workflow! ๐Ÿš€

Download original notebook

const balloonContainer = document.getElementById("balloon-container");

function random(num) {
  return Math.floor(Math.random() * num);
}

function getRandomStyles() {
  var r = random(255);
  var g = random(255);
  var b = random(255);
  var mt = random(200);
  var ml = random(50);
  var dur = random(5) + 5;
  return `
  background-color: rgba(${r},${g},${b},0.7);
  color: rgba(${r},${g},${b},0.7); 
  box-shadow: inset -7px -3px 10px rgba(${r - 10},${g - 10},${b - 10},0.7);
  margin: ${mt}px 0 0 ${ml}px;
  animation: float ${dur}s ease-in infinite
  `;
}

function createBalloons(num) {
  for (var i = num; i > 0; i--) {
    var balloon = document.createElement("div");
    balloon.className = "balloon";
    balloon.style.cssText = getRandomStyles();
    balloonContainer.append(balloon);
  }
}

function removeBalloons() {
  balloonContainer.style.opacity = 0;
  setTimeout(() => {
    balloonContainer.remove()
  }, 500)
}

createBalloons(10);
setTimeout(removeBalloons, 15000);

return '';

Better support for Legendsโ€‹

We have implemented all types of Legeds objects

Plot[{Sin[x], Cos[x]}, {x, 0, 5}, 
 PlotLegends -> SwatchLegend["Expressions"]]
(*VB[*)(Legended[ToExpression[FrontEndRef["e61eab2f-97bc-4480-bdf3-06e0d3ebe318"], InputForm], SwatchLegend[{Directive[Opacity[1.], RGBColor[0.24, 0.6, 0.8], AbsoluteThickness[2]], Directive[Opacity[1.], RGBColor[0.95, 0.627, 0.1425], AbsoluteThickness[2]]}, {HoldForm[Sin[HoldForm[x]]], HoldForm[Cos[HoldForm[x]]]}, LegendMarkers -> Automatic, LabelStyle -> {}, LegendLayout -> "Column"]])(*,*)(*"1:eJylUk1Kw0AUTv23VFAPIAhuA60ptS5KqP3RRUqxKe5nJi92aJqRycQ2B/AQunXnCbr2AC7ceADdiCB6A2cmKEZRF77Fx/C97/3O28Ss588ahhGtSDiiMG4CYRwJxt0FyThwDKHnzyhFXkLLo9KldH5OcesS2pyFohV6rQmQWCAcgLslaaiUAOFt39zdwcQsl6tFE3u+ZRYrUPQswGCVqmniOQm9WIYtqQcgrxsGiWb7PAZ/XmkKEtwxEmTwuSWlcWgk0hGWJTQpByLoKaT9LUroniBCRcINbS92Kta19vcaLGCcTzfOng6nNza3tN3b/OJc2aOdplmTUMcRC2IB/QElwxCiiKoW/lvZ1/Zs86vX2w5efbB5LX93eVK7/rtydgO599QHLPDajI9SRlV0afjdHSlm8ltYg0U/hmX+Td9O+i0dxIfAI72QeizYCAlKvqjVHTkIQ+CKJADfyIyRlRY+EjsoYbHQNykXF4/CNx9Orz8="*)(*]VB*)

This expression can still be copied and reevaluated. Here are some other examples

legend = PointLegend[{Red, Green, Blue}, {"red", "green", "blue"}]
(*VB[*)(PointLegend[{RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1]}, {"red", "green", "blue"}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWlMIB43kAjIz8wr8UlNT81LSWOGKfHJLC6B8DiARJC7k3N+Tn5RJsiITAYYgUUBWI6RkAK4KlQLg0G8otSUYFYgnV6UmpoXDJJKyilNBQBj0SLI"*)(*]VB*)
Plot[{x, (*SpB[*)Power[x(*|*),(*|*)2](*]SpB*), (*SpB[*)Power[x(*|*),(*|*)3](*]SpB*)}, {x,0,3}, PlotStyle->{(*VB[*)(RGBColor[1, 0, 0])(*,*)(*"1:eJxTTMoPSmNiYGAo5gUSYZmp5S6pyflFiSX5RcEsQBHn4PCQNGaQPAeQCHJ3cs7PyS8qYgCDD/ZQBgMDnAEA4iUPRg=="*)(*]VB*), (*VB[*)(RGBColor[0, 1, 0])(*,*)(*"1:eJxTTMoPSmNiYGAo5gUSYZmp5S6pyflFiSX5RcEsQBHn4PCQNGaQPAeQCHJ3cs7PyS8qYoACKOODPVwEANd+D0Y="*)(*]VB*), (*VB[*)(RGBColor[0, 0, 1])(*,*)(*"1:eJxTTMoPSmNiYGAo5gUSYZmp5S6pyflFiSX5RcEsQBHn4PCQNGaQPAeQCHJ3cs7PyS8qYoACdMYHewDM1w9G"*)(*]VB*)}, PlotLegends->legend]
(*VB[*)(Legended[ToExpression[FrontEndRef["8a98ade3-2d63-4f26-a8ae-7dc244b66356"], InputForm], PointLegend[{RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1]}, {"red", "green", "blue"}, LabelStyle -> {}, LegendLayout -> "Column"]])(*,*)(*"1:eJyFUcFOAyEQpVaNqXrxD0y87mV3pdtrdfWySU0xvcMyNCQUEhai+/cO1DbZxMTLY+bNm+ExPAq3VXNCyHCPsNPw9Qq98zw4z66R6WAPVqqLpFggtFJjKenULHEPCG/e2dBa2X5DHwMXBtgT0g1fNVxCVZSSVkWtSlrwhkOxlH1Z14LS6pkeB18ibCO23aQAuNxYM2b200dQKRhuET6ctuHX0fzU2OkhHLPc/b5+ccZ5ndxpcoI/BLk2+09wVk0vZCnzINkVnnsPYFkqCYN2J0/KS+u4AMPCaECRie2p9O68746PLob8AeglHuwPAvJcDg=="*)(*]VB*)

Line legends

Plot[{Sin[x], Cos[x]}, {x, 0, 5}, 
 PlotLegends -> LineLegend["Expressions"]]
(*VB[*)(Legended[ToExpression[FrontEndRef["27faadb4-b494-4f2d-8307-cf1e6bbbeb90"], InputForm], LineLegend[{Directive[Opacity[1.], RGBColor[0.24, 0.6, 0.8], AbsoluteThickness[2]], Directive[Opacity[1.], RGBColor[0.95, 0.627, 0.1425], AbsoluteThickness[2]]}, {HoldForm[Sin[HoldForm[x]]], HoldForm[Cos[HoldForm[x]]]}, LegendMarkers -> None, LabelStyle -> {}, LegendLayout -> "Column"]])(*,*)(*"1:eJylUjtOAzEQ3UD4RSABB0BCoo0UkhUhRbSCfKBIiMhG9PbuGKxs7Mj2QvYAHAJaOk6QmgNQ0HAAaBASghvg9SqIBQEFUzxZb97MG3u8jnmXTFuWJZc0HFE4q4PHBVJcuLOaacExMJ9MxYqchoZPdSrWkUzMrWpoCs5Ug/mNEXihQjgAd0PTxTJByMd2HtsVO2+Top/fLhXKeY9swhbGGHClkDTOauiGumw+PgDyOyyIDNsTIZCZiXmLMvg8UNZwUiUXWNBQpwI8RU8hmW5OQ2eIPKoiYZl4dRKxcdrbrfGACzFeO38+HN86omTiwRGXF3E8OUmbFQ07WPIgVNA7oV6fgZQ0HuG/zsTEiyOu3+7aePnREdXc/dWwevO3c/oFMpPW+zzwm1wMEiZ2dCn7npYxM/qtrMblj2WprZmfk6yljUQfhDSpA87gi9DsEGEIXBUFQKzUDdLSxY+eLRTxUJnPqN8sHLB3riSscA=="*)(*]VB*)

LaTeX can also be used via inset cells

Plot[{Sin[x], Cos[x]}, {x, 0, 5}, 
 PlotLegends -> LineLegend[Automatic, {
   CellView["$\\sin(x)$", "Display"->"markdown"],
   CellView["$\\cos(x)$", "Display"->"markdown"]
 }]]
(*VB[*)(Legended[ToExpression[FrontEndRef["15c987a5-5b18-4040-98d8-b639fdbcef18"], InputForm], LineLegend[{Directive[Opacity[1.], RGBColor[0.24, 0.6, 0.8], AbsoluteThickness[2]], Directive[Opacity[1.], RGBColor[0.95, 0.627, 0.1425], AbsoluteThickness[2]]}, {CellView["$\\sin(x)$", "Display" -> "markdown"], CellView["$\\cos(x)$", "Display" -> "markdown"]}, LegendMarkers -> None, LabelStyle -> {}, LegendLayout -> "Column"]])(*,*)(*"1:eJylUkFOwkAUrYqiRBP1ACYmLHTRBAJoWZBGAd2ARCCu2EzbX50wzJCZqdADeAjduvMErD2ACzceQDfGxOgNnE4DUYwxxr94ad68vv/n/dl0WNOfMwxDrCg4wTCogMs4koy3FhRTg1Ognj8bKVIKqh5WR5HOn4m4dQUHnFFZpV51CG4gkUOglVZ0tuAWrV1UMAtO1jLzmXzGLFqeZTo7uaLvOS74WSs2TihoBuq3xegDkNegJNRsmwfgz4+b1zCFzwMlNCdkfIElBRXMwZX4HOLpkgoafeRiGXJD15sdi3Wnw/0yI4zz0cbFy/HozuY5XY82v7qM6tmObdYU7DmCkUBC+wy7XQpC4GiE/3b2db3a/Ob9vu6sPtm8lHq47pduf+/8NYHZsXUZCInW04oCS3c6AtOt4XZ6Kuiknlj0CQp16D3Eux4b0J99XCb+7DOW6bcVL66uBMCFPjpiFKaEesvIAdKSIQHf+H7HiXR54llDIQukfq4q1aBHPwDgj7dk"*)(*]VB*)

Put anything into Legended!โ€‹

We managed to get a decent coverage of the default Mathematica's Legended features

PieChart[{1, Legended[2, "Bob"], 3, Legended[4, "John"]}]
(*VB[*)(Legended[ToExpression[FrontEndRef["1101bbb8-6474-4e18-97a7-132f94392b5b"], InputForm], SwatchLegend[{Directive[EdgeForm[Directive[GrayLevel[0], Opacity[0.5]]], RGBColor[0.928, 0.5210666666666667, 0.2]], Directive[EdgeForm[Directive[GrayLevel[0], Opacity[0.5]]], RGBColor[0.49920000000000003, 0.5552, 0.8309304]]}, {"Bob", "John"}, LegendMarkers -> {{Graphics[{GrayLevel[0.9], Directive[EdgeForm[Directive[GrayLevel[0], Opacity[0.5]]], RGBColor[0.928, 0.5210666666666667, 0.2]], Rectangle[{0, 0}, {1, 1}]}, {DisplayFunction -> Identity, ImageSize -> 10}], Graphics[{GrayLevel[0.9], Directive[EdgeForm[Directive[GrayLevel[0], Opacity[0.5]]], RGBColor[0.49920000000000003, 0.5552, 0.8309304]], Rectangle[{0, 0}, {1, 1}]}, {DisplayFunction -> Identity, ImageSize -> 10}]}, {None, None, None, None}}, LabelStyle -> {}, LegendLayout -> "Column"]])(*,*)(*"1:eJzVVEFrFDEUHuuqa6kKvXkUeh1wtqvbPQ3U7paW0cKOVDwmM292g7PJ+ibTOv4BQcGTCO0f0IMnf4MXRfEntIIn0YtXL74ksHSqrd6qOXwkX17e+96XzFzhapCd9jyvuECwKWB7BRKFTCuMzxITwRBkms2YiFmCXipoy8Rlpww3T9BHJXVPpr0HkJSa8RziBaKD4GrAOV/yr7c7bb8NwZLf7bCOHyy2sm57sdvi17hL3CAYlHSsaSbA0g2ZV5a9jSVkZ0zMHEG8zXQyOijJxESi0G51nmBFICRabIHT17Sah9BXOD4yyFCryKoItiAXtPAcf45gY8ISoSv07NgPnVlW6OryDZUrxMuNzYv+628hwscn3rNX+yHu7pjxNjxRWXv3Hz/9/H0vxEcv7rxrvfkU4sz8y7s/nn8J697F5uiy4rEh1tVI1i/FPgzn+U2G9wCL31lfXzWd8slIJIVTNt3/tTP88N6Mryfr1t9c4oDqMTnMod6zLeYq1nlT38JRjll/L9leiknOqn4pqSMlrbS1FKQm1YfCjZC1MRtCLB6CmJ1m/w89/9ML/Zc9bxzMbie3lIRjJodSmiQR45DHuqLmvGPEzk0/wIhVqtT2x0wOlmP5E/5XaXY="*)(*]VB*)

Highlight prime numbers

Legended[
 Grid[Partition[
   Table[If[PrimeQ[n], Item[n, Background -> LightBlue], n], {n, 100}],
    10], Frame -> All], SwatchLegend[{LightBlue}, {"prime numbers"}]]
(*VB[*)(Legended[ToExpression[FrontEndRef["16d53b61-0d6d-44a3-9318-25b4a87a5c88"], InputForm], SwatchLegend[{RGBColor[0.87, 0.94, 1]}, {"prime numbers"}]])(*,*)(*"1:eJxTTMoPSmNmYGAo5gUSYZmp5S6pyflFiSX5RcFsQBGf1PTUvJQ0JpAKLiDhmpIJlAKpS2MEiQkBCbei/LwS17wU14rU5NKSxKSc1GAVoLChWYqpcZKZoa5BilmKrolJorGupbGhha6RaZJJooV5ommyhQXEYBYgEVQK1MYBYqQmpvjn5VSCRUOKSlMhaniARHB5YklyBtRJjDCdPpnFJRAvgLW7Oznn5+QXFV1fXGDLdf21fZHIOveHVSLv7DNBOlC1BYM8XVCUmZuqkFeam5RaVAwAF9RFfA=="*)(*]VB*)

Autohide strings double-quotesโ€‹

We added some optimizations on fractions, grids and subscripts. If it sees a raw string, the rendering will be done in a simplified way

RandomWord[5] // TableForm 
(*GB[*){{"localize"}(*||*),(*||*){"pugilistic"}(*||*),(*||*){"publicizing"}(*||*),(*||*){"goulash"}(*||*),(*||*){"premiere"}}(*]GB*)

One this one can be used for labeling

Labeled[Red, (*FB[*)(("\[Alpha]")(*,*)/(*,*)("\[Beta]"))(*]FB*), Left]
(*GB[*){{(*FB[*)(("\[Alpha]")(*,*)/(*,*)("\[Beta]"))(*]FB*)(*VB[*)(**)(*,*)(*"1:eJxTTMoPSmNiYGAo5gUSYZmp5S6pyflFiSX5RcEsQBHPktRciDyIF1Sak1osAGS4pKYlluaUOCUWpwaXVOakBvMABX0Sk1JzUlPAFADFtBeE"*)(*]VB*)(*|*),(*|*)(*BB[*)((*VB[*)(RGBColor[1, 0, 0])(*,*)(*"1:eJxTTMoPSmNiYGAo5gUSYZmp5S6pyflFiSX5RcEsQBHn4PCQNGaQPAeQCHJ3cs7PyS8qYgCDD/ZQBgMDnAEA4iUPRg=="*)(*]VB*))(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWnMIB4vkAjLTC13SU3OL0osyS8KBskHJOalpjHBVAeV5qQWcwIZjjmZ6Xm5qXklCDmfzOKSYjYgwxkonFpUzAFkOiUWp+ZkYpgggCQVkF+cWZKZn4eiHgAIiyhB"*)(*]BB*)(*VB[*)(**)(*,*)(*"1:eJxTTMoPSmNiYGAo5gUSYZmp5S6pyflFiSX5RcEsQBHPktRciDyIF1Sak1osAGS4pKYlluaUOCUWpwaXVOakBrMDBX0Sk1JzUlMAU+0Vnw=="*)(*]VB*)}}(*]GB*)

Note, that those symbols were entered as string. But double quotes are hidden upon rendering.

Exporting formats improvementsโ€‹

Better organized and refactoredโ€‹

We got rid of a mess in the codebase of wljs-html-export, which takes care about various exporting/importing formats. Mathematica notebooks got better support, but still far from perfect. A massive improvement was made in HTML notebooks.

Improvements in Dynamic HTML exportโ€‹

We no longer use JS frontend for inspecting bindings and sampling the data from your sliders. Instead we do everyhting from Wolfram Kernel by injecting a sniffer and listern to symbols mutations and FrontSubmit calls.

There are 3 kinds of virtual machines we use (picked automatically based on the results on analysis) with funny names


  • State Machine - has a state, which is determined by the combination of all input elements. When state changes it dispatches a coresponding symbol mutation.
  • Pavlov Machine - (aka Pavlov Dog) does not have a state. It basically records pairs of event - FrontSubmit calls
  • Animation Machine - detects a series of symbols mutations caused by the same event and records the whole series (typically an animation made using AnimationFrameListener). It does not have a real state, only an abstract frame number

We plan to introduce basic a few kB CNN networks as an option to compress the mutations more efficiently in the future.

Applicationsโ€‹

Symbols mutationsโ€‹

Try to records these Manipulate and ManipulatePlot

Manipulate[Series[Sin[x], {x,0,n}], {n,1,10,1}]
(*GB[*){{(*VB[*)(EventObject[<|"Id" -> "3acbfd8a-0401-4258-83ad-e87bf7879953", "Initial" -> {6}, "View" -> "d9ea10fb-01d8-46d3-bf5b-e10babd2ee53"|>])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKp1imJhoapCXpGhimWOiamKUY6yalmSbpphoaJCUmpRilppoaAwCS5xZ/"*)(*]VB*)}(*||*),(*||*){(*VB[*)(FrontEndRef["2fbe8bf7-0c1e-4ba1-acf1-88f511a751fe"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKG6UlpVokpZnrGiQbpuqaJCUa6iYmpxnqWlikmRoaJpqbGqalAgCWzhZb"*)(*]VB*)}}(*]GB*)
ManipulatePlot[f[w x], {x,-10,10}, {w,0,10,1.0}, {f, {Sinc, Sin}}]
(*GB[*){{(*VB[*)(Graphics[{AbsoluteThickness[2], RGBColor[0.368417, 0.506779, 0.709798], Line[Offload[pts$140885]]}, ImageSize -> {400, 300}, PlotRange -> Automatic, Axes -> True, TransitionType -> "Linear", TransitionDuration -> 50, Epilog -> {}, Prolog -> {}, AxesLabel -> {}, "TrackedExpression" -> Null, PlotRange -> {{-11., 11.}, {-0.2780903607885227, 1.0608614457518346}}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWncIB4HkHAvSizIyEwuTmOGyftkFpdAVAsCCcek4vyc0pLUEKCi7LzU4uJMJqAoRDVIf5C7k3N+Tn5RUcG0SU9Vply3LzKc9nJ6h/kD+6LjLTO8V217Zo+w2iczLxXCYwcS/mlpOfmJKcVcQHZBSbGKoYmBhYVpGhNMdVBpTmoxJ5DhmZuYnhqcWZWKkAM5MnMC0KhMHSCBRU9ATn5JUGJeOoTnWFqSn5tYkpmMphLEcKwA+grECCkqTUWT5wMLJ+YVZ5Zk5ueFVBakBrNB/ZFYhKZWCEWtS2lRIojONGLAcB/ICNeCzJz89DQGlFDHVBZQlE9IGSfUDz6JSak5+FQGC0JcmJydmuJaUVAEisz8PLC0X2lODr5ARA14VF4RAxioHYAxHNDkr920arY+c3F/UUbZx5uePz/YAwAqMZ+h"*)(*]VB*)(*|*),(*|*)(*VB[*)(EventObject[<|"Id" -> "12c9ebfb-489e-419e-8d32-61e984dd9a74", "Initial" -> {5., Sinc}, "View" -> "c8966543-e647-49f5-91d5-35e82af21e90"|>])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKJ1tYmpmZmhjrppqZmOuaWKaZ6loappjqGpumWhglphkZploaAAB1lxT/"*)(*]VB*)}}(*]GB*)

Or custom dynamics

radius = 1.0;
Graphics[{Hue[radius // Offload], Disk[{0,0}, radius // Offload]}, ImageSize->Small]

EventHandler[InputRange[0,1,0.1], (radius = #)&]
(*VB[*)(Graphics[{Hue[Offload[radius]], Disk[{0, 0}, Offload[radius]]}, ImageSize -> Small])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWlMIB4HkHAvSizIyEwuhoiA5H0yi0sgqplBqktTIRx2IOGflpaTn5hSzAZkFyWmZJYiaXPJLM5GNSQTSDOACeJMCCrNSS3mBDI8cxPTU4Mzq1KLWYG84NzEnBwAm6MoCw=="*)(*]VB*)
(*VB[*)(EventObject[<|"Id" -> "14f363aa-79a2-415e-a632-320d3cf23de7", "Initial" -> 0.5, "View" -> "35294751-5497-49ac-9c79-51727b990b44"|>])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKG5saWZqYmxrqmppYmuuaWCYm61omm1vqmhqaG5knWVoaJJmYAABp1BRx"*)(*]VB*)

FrontSubmit callsโ€‹

Here this is done indirectly by EmitSound

EventHandler[InputButton[], (Sound[SoundNote["C5"]] // EmitSound)&]
(*VB[*)(EventObject[<|"Id" -> "72504de6-472e-4867-8ee7-a0093bc4f680", "Initial" -> False, "View" -> "5f9aa11d-3952-4376-8bc5-3c11b423434c"|>])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKm6ZZJiYaGqboGluaGumaGJub6VokJZvqGicbGiaZGBmbGJskAwB88RUJ"*)(*]VB*)

Another example now with Plotly

p = Plotly[{<|
	"values" -> {19, 26, 10}, 
	"labels" -> {"Residential", "Non-Residential", "Utility"}, 
	"type" -> "pie"
|>}]
(*VB[*)(FrontEndRef["4a687ce6-33bb-471d-8db5-e8ce9393f151"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKmySaWZgnp5rpGhsnJemamBum6FqkJJnqplokp1oaWxqnGZoaAgCHcxWy"*)(*]VB*)
EventHandler[InputRange[0,100,10], PlotlyAnimate[p,   
  <|"data" -> {<|"values" -> {19, 26, #}|>},
    "traces" -> {0}
  |>, <||>]&
]
(*VB[*)(EventObject[<|"Id" -> "ee2d0524-c254-4dde-93f2-8b37fb4e045a", "Initial" -> 50, "View" -> "baba10ff-5481-427f-9ce6-1ba59e280380"|>])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKJyUmJRoapKXpmppYGOqaGJmn6Vomp5rpGiYlmlqmGlkYGFsYAACN1hWe"*)(*]VB*)
note

The same works for slides, i.e. SlideEventListener will also be captured by Pavlov Machine if you have one automatically

Docker Imageโ€‹

We refined our docker image with a help of Yan Loose and testing done by @RomchikL. Deploy it in a single line

docker run -it
-v ~/wljs:"/home/wljs/WLJS Notebooks"
-v ~/wljs/Licensing:/home/wljs/.WolframEngine/Licensing
-e PUID=$(id -u)
-e PGID=$(id -g)
-p 8000:3000
--name wljs
ghcr.io/jerryi/wolfram-js-frontend:main

It will prompt you a login and password from Wolfram account and then start a server at

http://127.0.0.1:8000

This docker image includes

  • all latest libraries of WLJS
  • WLJS Notebook
  • Wolfram Engine >=14.2
  • Nginx proxy

See more options here.

Autotestsโ€‹

We integrated playwright into our workflow to capture any bugs before the release. For now we stick to integration tests using screenshots of DOM elements.

Feel free to contribute. All tests are in the main repo.

ESM and Shell cellsโ€‹

Both of them finally got a full support on Windows Machines!

.sh
dir

Ballon animation by Jemima (codepen)