Genie Discord forum

Author Avatargrendel
1/12/2024, 9:52:55 PM

I'm building an application centered around a Cesium viewer. The Cesium viewer runs on the frontend, along with all of my TypeScript code that adds camera transforms, entities and graphics, etc.

In a previous iteration of this application, I started with a SolidJS template, which made it very easy to add Cesium as a dependency and start building the frontend.

  1. What's the best way to incorporate JavaScript libraries such as Cesium into a Genie app? Essentially, how do I do the equivalent of pnpm add cesium such that I can call its API from app.jl?
  2. I know I can add javascript files to the public folder which will be automatically imported, but how do I actually call them? Does this work with TypeScript files?

I suspect that I'll end up just using the Genie backend as an API and figure out how to fit it into my SolidJS template, but I wanted to know if there was a way that I could make use of the Stipple.jl frontend instead.

Author AvatarPere
1/16/2024, 11:47:33 AM

You might want to use a layout so that you can edit the page headers and include anything you need. You can use the Stipple.ReactiveTools.DEFAULT_LAYOUT as a base, like I did here

https://github.com/BuiltWithGenie/ComponentGallery/blob/main/layout.html

then you include it with @page("/", "ui.jl", layout = "layout.html")

There's probably other ways, perhaps @hhaensel can provide some

Author Avatarhhaensel
1/16/2024, 11:41:04 PM

Using Cesium is possible. Following the installation instruction from https://cesium.com/learn/cesiumjs-learn/cesiumjs-quickstart/ I composed this little app:

using GenieFramework

@genietools

cesium_token = "your token"

cesium_module() = [
  script(type = "module", "
    Cesium.Ion.defaultAccessToken = '$cesium_token';

    // Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.
    const viewer = new Cesium.Viewer('cesiumContainer', {
      terrain: Cesium.Terrain.fromWorldTerrain(),
    });    

    // Fly the camera to San Francisco at the given longitude, latitude, and height.
    viewer.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(-122.4175, 37.655, 400),
      orientation: {
        heading: Cesium.Math.toRadians(0.0),
        pitch: Cesium.Math.toRadians(-15.0),
      }
    });

    // Add Cesium OSM Buildings, a global 3D buildings layer.
    const buildingTileset = await Cesium.createOsmBuildingsAsync();
    viewer.scene.primitives.add(buildingTileset);
  ")
]

cesium_head = [
  script(src = "https://cesium.com/downloads/cesiumjs/releases/1.113/Build/Cesium/Cesium.js", ),
  link(href = "https://cesium.com/downloads/cesiumjs/releases/1.113/Build/Cesium/Widgets/widgets.css", rel = "stylesheet", )
]

@app begin
  @in mybutton = false

  @onbutton mybutton begin
        println("button clicked")
  end
end

@deps cesium_module

const UI = Ref(ParsedHTMLString[])

UI[] = [
  row(cell(class = "st-module",
    h1("My first Cesium App")
  ))

  row(cell(class = "st-module", [
    h3("Demo")
    htmldiv(id="cesiumContainer")
  ]))
]

ui() = UI[]

route("/") do
  page(@init, ui, head_content = join(cesium_head))
end

up(open_browser = true)

# verify page content at the command line
page(@init, ui, head_content = join(cesium_head)) |> println
Author Avatarhhaensel
1/16/2024, 11:44:07 PM

Note that here I used the explicit page() function rather than the @page macro, because the latter supports the head_content kwarg other than the macro.

Author Avatarhhaensel
1/16/2024, 11:44:56 PM

You could also design your own layout as @Pere mentioned above, but this version was faster for me.

Author AvatarPere
1/17/2024, 6:09:09 PM

Thanks for the great example @hhaensel , I've added a doc page on this

https://learn.genieframework.com/docs/reference/reactive-ui/adding-js-libraries

Author Avatarhhaensel
1/17/2024, 10:36:41 PM

I found a way of using a modified DEFAULT_LAYOUT together with the page macro. The point is that v-if:'isready' hides the element when the init code runs and hence the initialisation of cesium fails. A workaround is to delay script execution to when the app is ready. So the new way of defining cesium_module() would be:

cesium_module_text = """
    // Your access token can be found at: https://ion.cesium.com/tokens.
    // Replace `your_access_token` with your Cesium ion access token.

    Cesium.Ion.defaultAccessToken = '$cesium_token';

    // Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.
    const viewer = new Cesium.Viewer('cesiumContainer', {
      terrain: Cesium.Terrain.fromWorldTerrain(),
    });    

    // Fly the camera to San Francisco at the given longitude, latitude, and height.
    viewer.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(-122.4175, 37.655, 400),
      orientation: {
        heading: Cesium.Math.toRadians(0.0),
        pitch: Cesium.Math.toRadians(-15.0),
      }
    });

    // Add Cesium OSM Buildings, a global 3D buildings layer.
    const buildingTileset = await Cesium.createOsmBuildingsAsync();
    viewer.scene.primitives.add(buildingTileset);
"""

cesium_module() = [script(Stipple.js_initscript("""
  newscript = document.createElement('script')
  newscript.text = $(json(cesium_module_text))
  newscript.type = 'module'
  document.head.appendChild(newscript)
"""))]
Author Avatarhhaensel
1/17/2024, 10:56:27 PM

@Pere let's wait for the PR to be accepted and then modify your docs accordingly

Author Avatargrendel
1/18/2024, 12:18:58 AM

Holy moly! This is so much more helpful than I could have anticipated. Thank you both so much!

Author Avatargrendel
1/18/2024, 12:43:49 AM

Changed the channel name: SOLVED Using TypeScript and managing dependencies

Author AvatarPere
1/18/2024, 11:40:38 AM

@hhaensel there's some caveats, like if you want to call the JS code from a Vue component you need to add it with @methods as I had to do with this tailwind example https://discord.com/channels/774897545717219328/1197340350075916328 . I'll need to do some thorough documentation about all this...

Author Avatarhhaensel
1/18/2024, 11:43:44 AM

Agree, but as a starting point for people who are familiar with js it's probably already helpful ...

Author Avatargrendel
1/18/2024, 9:23:08 PM

As a stopgap, you could probably just have a FAQ or "how do I..." page that contains the various examples we've come up with here along with the current Genie version number (so people know when/if it's outdated). I would hate for you to spend time on thorough documentation for this especially if you change how it works in future releases (e.g. if you provided a macro that toggled TailwindCSS instead of having the user copy-paste all that stuff).

Author AvatarPere
1/19/2024, 9:37:05 AM

We use the code examples for that, I was planning to add the Tailwind example there ๐Ÿ™‚. The versioning is a good idea though, although right now we just try to make sure they work on the latest versions https://learn.genieframework.com/docs/examples/reactive-ui/mapbox