JSComponent import script question

Does JSComponent have a mechanism to import a script and wait for it to be fully loaded
prior to proceeding?

I tried
import MJAX from 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js';
but get console warnings MathJax (not yet) loaded

class Foo(JSComponent):
    _esm = r"""
    import MJAX from 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js';
    import jsxgraph from 'https://cdn.jsdelivr.net/npm/jsxgraph@1.4.6/+esm';

    export function render({model, el}) {
         let boardDiv = document.createElement("div");
          boardDiv.style.width = "500px";
          boardDiv.style.height = "500px";
          el.appendChild(boardDiv);

          // Initialize the JSXGraph board
          let board = JXG.JSXGraph.initBoard(boardDiv, {
            boundingbox: [-5, 5, 5, -5],
            axis: true, showCopyright:  false,
          });
      let p = board.create('point', [1, 1], {name:`\\(\\vec{A}\\)`});
  }
"""
Foo()

What gets me is that I had this working before?!

I think the problem is you need to load the “right” esm scripts.

This works for me

import panel as pn
from panel.custom import JSComponent

pn.extension()

class Foo(JSComponent):
    _esm = r"""
    import MJAX from 'https://esm.sh/mathjax'
    import JXG from 'https://cdn.jsdelivr.net/npm/jsxgraph/distrib/jsxgraphcore.mjs';

    export function render({model, el}) {
         let boardDiv = document.createElement("div");
          boardDiv.style.width = "500px";
          boardDiv.style.height = "500px";
          el.appendChild(boardDiv);

          // Initialize the JXG board
          let board = JXG.JSXGraph.initBoard(boardDiv, {
            boundingbox: [-5, 5, 5, -5],
            axis: true, showCopyright:  false,
          });
      let p = board.create('point', [1, 1], {name:`\\(\\vec{A}\\)`});
  }
"""

Foo().servable()

Hi @Marc, I’ll try it out,
but this whole import business is spooky:
I was using

    _importmap = {
        "imports": {
            "konva": "https://cdn.jsdelivr.net/npm/konva@8/konva.min.js",
        }
    }

all morning, only to suddenly get

4e38bc3d-7c36-4a54-a16e-e7da549f4a76:55 Uncaught (in promise) ReferenceError: Konva is not defined
    at E.render (4e38bc3d-7c36-4a54-a…-e7da549f4a76:55:17)

errors!

class SimplePoint(JSComponent):
    _importmap = {
        "imports": {
            "konva":   "https://cdn.jsdelivr.net/npm/konva@8/konva.min.js",
            "mathjax": "https://esm.sh/mathjax",
        }
    }
    _esm = r"""
    export function render({ model, el }) {
      // Initialize the Konva stage
      const stage = new Konva.Stage({
        container: el, // Attach to the provided container
        width: 400,
        height: 400,
      });

      // Create a layer for the point and label
      const layer = new Konva.Layer();
      stage.add(layer);

      // Center of the canvas
      const centerX = stage.width() / 2;
      const centerY = stage.height() / 2;

      // Add the point
      const point = new Konva.Circle({
        x: centerX,
        y: centerY,
        radius: 5,
        fill: 'blue',
      });
      layer.add(point);

      // Add the label
      const label = new Konva.Text({
        text: 'A',
        x: centerX + 10, // Offset label slightly to the right
        y: centerY - 10, // Offset label slightly above the point
        fontSize: 18,
        fontFamily: 'Arial',
        fill: 'black',
      });
      layer.add(label);

      // Draw the layer
      layer.draw();
    }
    """
p = SimplePoint()
pn.Column(p).servable()

To get it to work,
I’ve resorted to load the library in a script tag in a %%html cell in jupyter and then

   _esm =  r"""
export function render({ model, el }) {
  if (typeof window.Konva === 'undefined') {
    throw new Error('Konva is not loaded correctly!');
  }
  const Konva = window.Konva;
  console.log('Konva is loaded:', Konva);
"""

I keep running into this issue over and over.
Could sombody explain
a) the different types of scripts, and
b) how to load them so as to be able to use them in a JSComponent?

This time around, I am trying to get
https://bloom.js
to work:
<script type="module" src="https://penrose.cs.cmu.edu/bloom.min.js"> is ES6 (whatever that means exaclty.
The example uses

import * as bloom from "https://penrose.cs.cmu.edu/bloom.min.js";
const db = new bloom.DiagramBuilder(bloom.canvas(400, 400), "abcd", 1);
// ...
const diagram = await db.build();

but I can’t get the library to load, no matter what I try?!

Did you include it in your import map?

I tried every which way I could think of, including

    _imports = {
        'bloom': {
            'module': 'https://penrose.cs.cmu.edu/bloom.min.js',
            'name': '*',
            'type': 'module'
        }
    }

I really am missing something about javascript, so I am trying to search the web…

Yeah I agree that it’s confusing how to import things, as I’ve encountered it before too:

Maybe you can submit an issue to request clarification on how to do so! Will help others in the end.

Well yes, but in the meantime I am still stuck with something that should be easy to do
given the right knowledge!

Got it to work, but I’d looooove an explanation!
bloom tutorial example

import panel as pn; pn.extension();
import param

class BloomDiagram(pn.custom.JSComponent):
    width  = param.Integer(default=400, doc="Width of the canvas")
    height = param.Integer(default=400, doc="Height of the canvas")

    _imports = {
        'bloom': {'module': "https://penrose.cs.cmu.edu/bloom.min.js", 'name': '*'}
    }
    _esm = """
    export async function render({ model, el }) {
        const bloom = await import('https://penrose.cs.cmu.edu/bloom.min.js');
        if (!bloom ) { // || !bloom.canvas || !bloom.DiagramBuilder
            console.error('Bloom library not properly loaded');
            return;
        }

        const id = Math.random().toString(36).substr(2, 9);
        const container = document.createElement('div');
        container.id = 'bloom_' + id;
        container.style.width = model.width + 'px';
        container.style.height = model.height + 'px';
        container.style.border = '1px solid black';

        el.appendChild(container);

        // ========================================================= Canvas
        // Canvas: local coordinate system
        const canvas = bloom.canvas(model.width, model.height);
        console.log("CANVAS:", canvas);

        // ========================================================= DiagramBuilder
        // has methods to declare types, substances, shapes, constraints, etc
        const seed="abcd";
        const db = new bloom.DiagramBuilder(canvas, seed, 1);
        const { type, predicate, forall, forallWhere, ensure, circle, line } = db;
        console.log( db);

        // ========================================================= Diagram Code
        // Declare objects and relationships
        const Point = type();
        const Arrow = type();
        const Connects = predicate();
        // ---------------------------------------------------------
        // instantiate objects and relationships
        const p1 = Point();
        const p2 = Point();
        const arrow = Arrow();
        Connects(arrow, p1, p2);
        console.log( "Point", p1);
        // ---------------------------------------------------------
        // style objects and relationships
        const pointRad    = 30;
        const pointMargin = 10;

        forall({ p: Point }, ({ p }) => {  // style for points
          p.icon = circle({ r: pointRad, drag: true, });  // attach "icon" to p
        });

        forallWhere(                       // style for arrows
          { a: Arrow, p: Point, q: Point },
          ({ a, p, q }) => Connects.test(a, p, q),
          ({ a, p, q }) => {
            const pq     = bloom.ops.vsub(q.icon.center, p.icon.center); // vector from p to q
            const pqNorm = bloom.ops.vnormalize(pq); // direction from p to q
            const pStart = bloom.ops.vmul(pointRad + pointMargin, pqNorm); // vector from p to line start
            const start  = bloom.ops.vadd(p.icon.center, pStart); // line start
            const end    = bloom.ops.vsub(q.icon.center, pStart); // line end

            a.icon = line({ start: start, end: end, endArrowhead: "straight", });

            ensure( // constraints: points separated by more than radius_1+radius_2+padding
              bloom.constraints.greaterThan(
                bloom.ops.vdist(p.icon.center, q.icon.center),
                2 * (pointRad + pointMargin) + 20,
              ),
            );
          },
        );
        // ========================================================= Diagram Code
        const diagram = await db.build();
        console.log( diagram);

        const interactiveElement = diagram.getInteractiveElement();
        container.appendChild(interactiveElement);

        return container;
    }
    """

# Usage
bloom_diagram = BloomDiagram()
pn.Column(bloom_diagram).servable()

With pts.js, I managed to get it to load differently?!

%%html
<script type="text/javascript" src="https://unpkg.com/pts@0.12.9/dist/pts.min.js"></script>
class Pts(pn.custom.JSComponent):
    width  = param.Integer(default=300, doc="Width of the canvas")
    height = param.Integer(default=300, doc="Height of the canvas")

    _esm = """
    export async function render({ model, el }) {
        Pts.namespace( window );
        if (!Pts ) {
            console.error('Pts library not properly loaded');
            return;
        }
        const { CanvasSpace, Pt, Line } = Pts; // Destructure Pts to access its classes

        const id = Math.random().toString(36).substr(2, 9);
        const container = document.createElement('canvas');
        container.id = 'pts_' + id;
        container.style.width = model.width + 'px';
        container.style.height = model.height + 'px';
        container.style.border = '1px solid black';

        el.appendChild(container);
        // ============================================================
        var space = new CanvasSpace(container);
        space.setup({ bgcolor: "#a0a0ff" });
        var form = space.getForm();

    space.add(() => {
      let points = Create.distributeRandom(space.innerBound, 100);
      let mouse = space.pointer;

      points.forEach((p) => {
        let dist = p.$subtract(mouse).magnitude();
        let radius = 20 / (dist / 20 + 1);
        form.fill("#123").point(p, radius, "circle");
      });
   });

    space.bindMouse().bindTouch().play();

    return container;
    }
    """

# Usage
pts = Pts()
pn.Column("# Pts.js Demo",
          pts, width=pts.width+5, height=pts.height+65).servable()