Plotting plug

Plug to plot things

page1
---
date: 2025-01-01
squirrels: 2
cats: 4
---

page1
---
date: 2025-01-03
squirrels: 4
cats: 4
---

Replicate a subset of the functionality from some plotting library. Perhaps the api could look something like

```plotplug
type: scatter
data: query(from tags.page select {x: date, y: [squirrels, cats]})

This could be achieved using Chartist and some SpaceLua.

I have tried Chart.js with below space-lua:

local chartjs = js.import("https://cdn.jsdelivr.net/npm/[email protected]/auto/+esm")

function chart(config)
  local canvas = dom.canvas {
    id = "chart-" .. os.time() .. "-" .. math.random()
  }
  local rendered = false

  local function timeout()
    local target = js.window.document.getElementById(canvas.id)
    if not target then
      js.window.setTimeout(timeout, 100)
      return
    end

    if rendered then
      return
    end

    rendered = true
    js.new(chartjs.Chart, target, config)
  end

  js.window.setTimeout(timeout, 100)
  return widget.html(canvas)
end

And it should be used as inline expression, with similar config of its JS version:

${chart {
    type = "bar",
    data = {
      labels = {"A", "B", "C"},
      datasets = {{data = {10, 20, 30}}}
    }
}}

It then turns into something like:

But it has some limits:

  1. current js interop doesn’t import static member of an exported class. one of the static member is Chart.register, which is used to enable plugins. so plugins can’t be used
  2. Chart.js uses canvas to render chart, and it loses all drawn content once it’s passed to main frame. so I have to use a timeout to monitor when it’s mounted. it’s better to have a native “mount” event for widget
3 Likes

Nice!
This can be adapted into a simple inline expression and a YAML block with configuration and datapoints.

#plot
type: bar
labels: ['eat', 'sleep', 'code']
name: hrs
data: [2,8,1]
#plot
type: scatter
name: func1
data:
- [0,0]
- [2,2]
- [3,9]

or simply:

#plot
data: [3,2,4]

${plt.plot()} for first local #plot data block or
${plt.plot('objname', i)} for i-th local #objname data block

space-lua:


plt = plt or {}
plt.queryLocalObjs = function (object, idx)
  local pg = editor.getCurrentPage()
  local ret = query[[from index.tag(object) where page == pg order by pos]]
  if idx then
    ret = ret[idx]
  end
  return ret
end

local chartjs = js.import("https://cdn.jsdelivr.net/npm/[email protected]/auto/+esm")

plt.plot = function (obj, idx)
  local obj = obj or 'plot'
  local idx = idx or 1
  local plotcfg = plt.queryLocalObjs(obj, idx)
  local config = {
    type = plotcfg.type or 'bar',
    data = {
      labels = plotcfg.labels or plotcfg.data,
      datasets = {
        {
          label = plotcfg.name or {'trace1'},
          showLine = 'true',
          data = plotcfg.data
        }
      }
    }
  }
  
  local canvas = dom.canvas {
    id = "chart-" .. os.time() .. "-" .. math.random()
  }
  local rendered = false

  local function timeout()
    local target = js.window.document.getElementById(canvas.id)
    if not target then
      js.window.setTimeout(timeout, 100)
      return
    end

    if rendered then
      return
    end

    rendered = true
    js.new(chartjs.Chart, target, config)
  end

  js.window.setTimeout(timeout, 100)
  return widget.html(canvas)
end
1 Like

Is there any way right now to get Silverbullet to distribute JS libraries locally? @zef

It’ll be useful here, since this won’t be available offline.

1 Like

I think we can do that with the new Libraries in the edge version. But it still requires online when you first install that library.

Let me try it later.

1 Like

I have created a plug to provide offline chart plot here: GitHub - LelouchHe/silverbullet-chart

You can install it from Library Manager with URI ghr:LelouchHe/silverbullet-chart/PLUG.md

For now, it bundles Chart.js (4.5.1) and chartjs-plugin-annotation (3.1.0)

Feel free to try and create a pull request to add new plugins.

2 Likes

Fantastic. The next step is to publish It as SB library

1 Like

Work in progress.