# \[FEATURE\] Markdown Table enhancer (formula, chart, insert, renderer)

**URL:** <https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208>\
**Category:** Plugs and Libraries\
**Created:** [September 12, 2025, 10:42am UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208 "2025-09-12T10:42:32Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [September 12, 2025, 10:42am UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/1 "2025-09-12T10:42:32Z")

</div>

My goal was to easily like Excel or google sheet compute data in md table.  
This solution is not perfect, but it's working, and use formulajs for formulas.

code:

```lua
-- ---------------------------
-- SilverBullet Markdown Table + Formulajs evaluator
-- Use: F(formulajs,label)
-- label is optional but necessary if differenciate same formula is different table
-- Example: ${F("SUM(A1:A5)","1")}
-- ---------------------------

LOG_ENABLE = false
function debug_log(message)
  if LOG_ENABLE then
    js.log("[DEBUG] " .. message)
  end
end

-- Import Formulajs
local formulajs = js.import("https://esm.sh/@formulajs/formulajs")

-- ---------------------------
-- Helpers: Column <-> Number
-- ---------------------------
function colToNumber(col)
  local n = 0
  for i = 1, string.len(col) do
    n = n * 26 + (string.byte(col,i) - string.byte('A') + 1)
  end
  return n
end

function numberToColLetters(c)
  local s=""
  while c>0 do
    local r = (c-1)%26
    s = string.char(r+65)..s
    c = math.floor((c-1)/26)
  end
  return s
end

-- ---------------------------
-- Expand ranges (A1:C3) into individual cell references
-- ---------------------------
function expandRange(range, cellMap)
  local colStart, rowStart, colEnd, rowEnd = string.match(range,"([A-Z]+)(%d+):([A-Z]+)(%d+)")
  if not colStart then error("Invalid range: "..range) end
  local sCol = colToNumber(colStart)
  local eCol = colToNumber(colEnd)
  local sRow = tonumber(rowStart)
  local eRow = tonumber(rowEnd)
  local vals = {}
  for r = sRow, eRow do
    for c = sCol, eCol do
      local key = numberToColLetters(c)..r
      table.insert(vals, cellMap[key])
    end
  end
  return vals
end

-- ---------------------------
-- Parse Markdown table into 2D array
-- ---------------------------
function extractTable(rows)
  local data = {}
  for _, row in ipairs(rows) do
    local rowData = {}
    local col = 1
    for k,v in pairs(row) do
      if k ~= "ref" and k ~= "tag" and k ~= "tags" and
         k ~= "itags" and k ~= "page" and k ~= "pos" and
         k ~= "tableref" then
        rowData[col] = v
        col = col + 1
      end
    end
    table.insert(data,rowData)
  end
  return data
end

function extractTables(pageName)
  if pageName==nil then pageName = editor.getCurrentPage() end
  local allRows = query[[from index.tag "table" where page == pageName]]
  local tableGroups = {}
  for _, row in ipairs(allRows) do
    if not tableGroups[row.tableref] then tableGroups[row.tableref] = {} end
    table.insert(tableGroups[row.tableref], row)
  end
  local results = {}
  for tRef, rows in pairs(tableGroups) do
    results[tRef] = extractTable(rows)
  end
  return results
end

-- Convert 2D table to A1-style cell map
function toCellMap(tableData)
  local map={}
  for r,row in ipairs(tableData) do
    for c,val in ipairs(row) do
      local key = numberToColLetters(c)..r
      map[key] = tonumber(val) or val
    end
  end
  return map
end

-- Find table containing a formula (inside ${f("…")})
-- Find table containing the formula based on its position in the page
-- formulaCellValue = e.g. '${F("SUM(A1:A5)")}'
-- pageName = current page
local function findTableOfFormula(pageName, formulaCellValue,label)
  debug_log("findTableOfFormula START: "..formulaCellValue..label)
  local toSearch=formulaCellValue
  if label ~= nil then
    toSearch = '"'..formulaCellValue..'","'..label..'"' 
    debug_log(toSearch)
  end
  if pageName == nil then pageName = editor.getCurrentPage() end
  local allRows = query[[from index.tag "table" where page == pageName]]
  -- Find the first row that contains the formula string in a cell
  for _, row in ipairs(allRows) do
    local formulaColumn = nil
    for k, v in pairs(row) do
      if k ~= "ref" and k ~= "tag" and k ~= "tags" and
         k ~= "itags" and k ~= "page" and k ~= "pos" and
         k ~= "tableref" then
        if type(v) == "string" and string.find(v, toSearch, 1, true) then
          formulaColumn = k
          break
        end
      end
    end

    if formulaColumn then
      -- Once we know the row containing the formula, use its tableref
      debug_log("Formula found in table: "..row.tableref..", column: "..formulaColumn)
      return row.tableref
    end
  end

  debug_log("Formula not found in any table")
  return nil
end

-- ---------------------------
-- Formula evaluator using Formulajs
-- ---------------------------
function F(formulaString,label, pageName)
  if pageName==nil then pageName = editor.getCurrentPage() end
  local tRef = findTableOfFormula(pageName, formulaString,label)
  if not tRef then return "ERROR: formula not in table" end

  local tables = extractTables(pageName)
  local tbl = tables[tRef]
  if not tbl then return "ERROR: table not found" end

  local cellMap = toCellMap(tbl)

  -- Parse function name and arguments
  local funcName = string.match(formulaString,"^(%w+)%(")
  local argsStr = string.match(formulaString,"%((.*)%)")
  if not funcName then return "ERROR: invalid formula" end

  local args={}
  for a in string.gmatch(argsStr,"([^,]+)") do
    a = string.match(a,"^%s*(.-)%s*$")
    -- support ranges like A1:B2
    if string.match(a,"^[A-Z]+%d+:[A-Z]+%d+$") then
      local vals = expandRange(a, cellMap)
      for _,v in ipairs(vals) do table.insert(args,v) end
    elseif string.match(a,"^[A-Z]+%d+$") then
      table.insert(args, cellMap[a])
    else
      table.insert(args, tonumber(a) or a)
    end
  end

  if not formulajs[funcName] then return "ERROR: unknown function "..funcName end

  return formulajs[funcName](args)
end

```

| Header A | Header B |
| --- | --- |
| 1.2 | 2 |
| 3 | 4 |
| 5 | 6 |
| 7 | 8 |
| 9 | 10 |
| ${F("CONCAT(A1,A2)")} | ${F("SUM(A1:B5)")} |

| Header A | Header B | H5 |
| --- | --- | --- |
| 10 | 20 | 50 |
| 30 | 40 | 50 |
| 50 | 60 | 50 |
| 70 | 80 | 50 |
| 90 | 100 | 50 |
| ${F("SUM(A1:B5)","1")} | ${F("SUM(B1:B5)","2")} | ${F("SUM(C1:C5)","2")} |

In action  
 ![table](https://community.silverbullet.md/uploads/default/original/2X/2/2331d5ffb379c3209dc7c0b1065283137f873d72.gif)

For my use case, (SUM, AVERAGE) formulas are enough and they have been tested.  
Feel free to improve it and contribute.

---

<div class="post-metadata">

**Author:** ![zef](https://community.silverbullet.md/user_avatar/community.silverbullet.md/zef/32/7_2.png) [@zef](https://community.silverbullet.md/u/zef)\
**Post date:** [September 12, 2025, 7:03pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/2 "2025-09-12T19:03:41Z")

</div>

Very cool. I need to do some more work I’m currently using spreadsheets for, if I can do it in SB that would be awesome.

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [September 19, 2025, 11:16am UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/3 "2025-09-19T11:16:27Z")

</div>

The next step, in 2026, in theory, hyperformula will release a version compatible with SB. It will be possible to have a full sheet engine in SB.  
For me ,It’s a must to have feature as multi cursor. After that SB will cover all my needs 🥳

---

<div class="post-metadata">

**Author:** ![finestream](https://community.silverbullet.md/user_avatar/community.silverbullet.md/finestream/32/2338_2.png) [@finestream](https://community.silverbullet.md/u/finestream)\
**Post date:** [December 3, 2025, 9:59am UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/4 "2025-12-03T09:59:15Z")

</div>

Very nice feature! Thank you for developing this.

The code works off the shelf (just copied in a space-lua block).

I have noticed a minor bug that can be worked around: empty cells confuse loading of the table. _Workaround:_ avoid empty cells. _Fix:_ I could not find a quick fix in the code, maybe somebody has an idea…

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [December 4, 2025, 12:07pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/5 "2025-12-04T12:07:34Z")

</div>

You can directly install the last version from my library: [GitHub - malys/silverbullet-libraries: Awesome SilverBullet libraries](https://github.com/malys/silverbullet-libraries)  
See libraries manager in SB.  
on GitHub, you could propose a fix or some improvements

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [December 5, 2025, 8:25am UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/6 "2025-12-05T08:25:47Z")

</div>

It could be interesting to couple this plugin with recent ploting plugin conver It full lua @LelouchHe .  
Imagine.  
I define table in formula

I add evvery where in the same page  
${G(“A1:A5”,“B1:B5”,“bar”)}  
Chartjs bar graph will be générateur  
X=A..  
Y=B…  
A big part of lua function are yet available.  
@LelouchHe , how hard is yo create a lua widget whith chartjs capacity?

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [December 6, 2025, 10:23am UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/7 "2025-12-06T10:23:20Z")

</div>

Big Update:

- fix: table detection
- feat: Add G function to insert chart based on md table data and chartjs. (1st and limited version).

![mdtable2](https://community.silverbullet.md/uploads/default/original/2X/a/a79eebba75448fb6f9a1890855b683a4a9b8c282.gif)

Install with SB Library manager:

> <https://github.com/malys/silverbullet-libraries/blob/main/src/MdTableSheet.md>

TODO (contribution accepted)

- pass full chartjs configuration as parameter of G
- improve documentation
- Test and validate chart from other page
- cache chart for performance if it’s necessary
- …

---

<div class="post-metadata">

**Author:** ![bau](https://community.silverbullet.md/user_avatar/community.silverbullet.md/bau/32/2597_2.png) [@bau](https://community.silverbullet.md/u/bau)\
**Post date:** [December 6, 2025, 1:17pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/8 "2025-12-06T13:17:41Z")

</div>

Well done ! Thanks for sharing.  
Note: I found a “slag” in the 1st line of the ChartJS usage example on the github md page : ${G(“MyChart”, “Page 1”, “A1:A5”, “B1:B5”, { type: “line”, w: 400,on: 600, serieLabel: “Series 1” })}. The “:” must be replaced by “=”.

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [December 6, 2025, 4:13pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/9 "2025-12-06T16:13:05Z")

</div>

fixed

---

<div class="post-metadata">

**Author:** ![marcoswur](https://community.silverbullet.md/letter_avatar_proxy/v4/letter/m/a87d85/32.png) [@marcoswur](https://community.silverbullet.md/u/marcoswur)\
**Post date:** [December 6, 2025, 4:40pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/10 "2025-12-06T16:40:42Z")

</div>

To adjust image size…  
include “width=” .. width .. ’ height=’ .. height

* * *

```auto
local html =
    '<div class="sb-chartjs-container">' ..
      '<img src="' .. quickchartUrl .. ' " alt="Chart " '.."width=" .. width .. ' height=' .. height..' />' ..
    '</div>'

```

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [December 6, 2025, 4:46pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/11 "2025-12-06T16:46:05Z")

</div>

Yes, but G function argument in lua is w=100 h=100

---

<div class="post-metadata">

**Author:** ![marcoswur](https://community.silverbullet.md/letter_avatar_proxy/v4/letter/m/a87d85/32.png) [@marcoswur](https://community.silverbullet.md/u/marcoswur)\
**Post date:** [December 6, 2025, 5:07pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/12 "2025-12-06T17:07:02Z")

</div>

Ok, the chart is resized, but the font size used in de chart is not resized…

---

<div class="post-metadata">

**Author:** ![marcoswur](https://community.silverbullet.md/letter_avatar_proxy/v4/letter/m/a87d85/32.png) [@marcoswur](https://community.silverbullet.md/u/marcoswur)\
**Post date:** [December 6, 2025, 5:10pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/13 "2025-12-06T17:10:07Z")

</div>

![image](https://community.silverbullet.md/uploads/default/original/2X/2/2e402f5b1ba6974f42b5acb7e4759bb13ae98a7f.png)

 ![image](https://community.silverbullet.md/uploads/default/original/2X/8/806313ad53f4c20e52f33797f3f25bb5a20ceea0.png)

---

<div class="post-metadata">

**Author:** ![marcoswur](https://community.silverbullet.md/letter_avatar_proxy/v4/letter/m/a87d85/32.png) [@marcoswur](https://community.silverbullet.md/u/marcoswur)\
**Post date:** [December 6, 2025, 5:12pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/14 "2025-12-06T17:12:05Z")

</div>

Ops… I used this table

 ![image](https://community.silverbullet.md/uploads/default/original/2X/f/f49a345a04371c34af27dbe0534d10214a2cfd38.png)

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [December 6, 2025, 5:12pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/15 "2025-12-06T17:12:19Z")

</div>

> [@marcoswur](#):
>
> `quickchart`

check parameters in quickchart or chartjs (dynamic,…). I use the service as this.  
If you find something, I can add it by default.

---

<div class="post-metadata">

**Author:** ![dhendiii](https://community.silverbullet.md/user_avatar/community.silverbullet.md/dhendiii/32/2544_2.png) [@dhendiii](https://community.silverbullet.md/u/dhendiii)\
**Post date:** [January 2, 2026, 4:00am UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/16 "2026-01-02T04:00:47Z")

</div>

Hi @Malys, thanks for the recommenation.  
It really work.

But i have hard time to figure out how SUMIF work.

This is the sample

```auto
|Date-A|Desc-B|Source-C|Person-D|Value-E|
|---|---|---|---|---|
|01|Fuel|Cash|A|70|
|02|Lunch|Credit|B|70|
|03|Snack|Cash|C|21|
|-|-|-|Sum All|${F("SUM(E1:E999)","All")}|
|-|-|-|Sum Cash|${F("SUMIF(C1:C2,'Cash',E1:E2)","Cash")}|

```

The SUMIF return 0. Do i write the formula correctly?

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [January 2, 2026, 11:54am UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/17 "2026-01-02T11:54:23Z")

</div>

fix: function with len(arguments)\>1 was not well managed.

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [January 25, 2026, 8:52am UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/18 "2026-01-25T08:52:10Z")

</div>

![insert](https://community.silverbullet.md/uploads/default/original/2X/e/ec9b8a44f48b1854b94544da609e24fb1c9bf888.gif)

new features! Insert line & column in a table

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [February 1, 2026, 4:55pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/19 "2026-02-01T16:55:14Z")

</div>

Thanks to last update of [Md Table renderers - #22 by malys](https://community.silverbullet.md/t/md-table-renderers/3545/22)  
I'm very proud of this feature

Defining column type ex: #emoji  
alt-/: we will able to add data preformatted  
 ![renderer](https://community.silverbullet.md/uploads/default/original/2X/5/57346b46b8d05cd830ea49153e205d76c2ef471f.gif)

- 2026-02-01:
  - feat: command (alt+/) to insert preformatted data

---

<div class="post-metadata">

**Author:** ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)\
**Post date:** [February 8, 2026, 7:48pm UTC](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208/20 "2026-02-08T19:48:11Z")

</div>

- 2026-02-08:
  - feat: support of multiple series
  - feat: support chartjs options  
 ![image](https://community.silverbullet.md/uploads/default/original/2X/8/8626179a5fcf1b4d42868af294c3a6ec45a321c0.png)

[Next page](https://community.silverbullet.md/t/feature-markdown-table-enhancer-formula-chart-insert-renderer/3208.md?page=2)
