12 KiB
12 KiB
author, description, name, tags, share.uri, share.hash, share.mode
| author | description | name | tags | share.uri | share.hash | share.mode |
|---|---|---|---|---|---|---|
| malys | List of reusable functions. | Library/Malys/Utilities | meta/library | https://github.com/malys/silverbullet-libraries/blob/main/src/Utilities.md | 004bb007 | pull |
Utilities
mls.getmeetingTitle(): Extracts meeting title from page URL.mls.embedUrl(url, w, h): Embeds a URL using an iframe (width & height optional).mls.debug(message, prefix): Prints debug message (ifLOG_ENABLEis true).mls.getCodeBlock(page, blockId, token, text): Retrieves code from a Markdown code block by ID, with optional token replacement.mls.parseISODate(isoDate): Parses an ISO date string to a timestamp (potential error withxoffset).mls.getStdlibInternal(): Gets a list of internal API keys (cached).mls.positionToLineColumn(text, pos): Converts a text position to line/column numbers.table.appendArray(a, b): Appends arraybto arraya.table.unique(array): Returns unique elements from an array.table.uniqueKVBy(array, keyFn): Returns unique key-value pairs based on a key function.table.mergeKV(t1, t2): Merges two tables (recursive for nested tables).table.map(t, fn): Applies a function to each element of an array.table.toMd(data): Converts a table (or JSON) to a Markdown table.
Debug
How to enable debug mode
- Create space-lua with:
LOG_ENABLE=true
- Reload system.
- Open Chrome Console
- Add filter “[Client] [DEBUG]“
Code
-- priority: 20
mls=mls or {}
-- Convert meeting note title
-- Extracts the meeting title from the current page URL.
-- Assumes the URL format is like "namespace/Meeting_Note Title".
-- Splits the URL by "/", takes the last part, and then splits that by "_"
-- to remove the "Meeting" prefix and joins the remaining parts with a space.
function mls.getmeetingTitle()
local t=string.split(string.split(editor.getCurrentPage(),"/")[#string.split(editor.getCurrentPage(),"/")],"_")
table.remove(t,1)
t=table.concat(t, " ")
return t
end
-- Embed external resources
-- Creates an HTML iframe to embed an external URL within the current page.
-- Allows specifying width and height, defaulting to "100%" and "400px" respectively.
function mls.embedUrl(specOrUrl,w,h)
local width = w or "100%"
local height = h or "400px"
return widget.html(dom.iframe {
src=specOrUrl,
style="width: " .. width .. "; height: " .. height
})
end
---------------------------------------------
---- Debug ---
---------------------------------------------
-- Pretty-print any Lua value (tables included)
-- Recursively prints a Lua value, handling tables with indentation.
-- Prevents infinite recursion by limiting the depth to 5.
local function dump(value, depth)
--print("[DEBUG][TYPE]"..type(value)) -- commented out debug line
depth = depth or 0
if type(value) ~= "table" or (type(value) == "string" and value ~= "[object Object]") then
return value
end
-- Prevent going too deep (avoid infinite recursion)
if depth > 5 then
return "{ ... }"
end
local indent = string.rep(" ", depth)
local next_indent = string.rep(" ", depth + 1)
local parts = {}
table.insert(parts, "{")
for k, v in pairs(value) do
local key = tostring(k)
local val
if type(v) == "table" then
val = dump(v, depth + 1)
else
val = v
end
table.insert(parts, next_indent .. tostring(key) .. " = " .. tostring(val) .. ",")
end
table.insert(parts, indent .. "}")
return table.concat(parts, "\n")
end
-- Debug function
-- Prints a debug message to the console if LOG_ENABLE is true.
-- Uses the dump function to pretty-print the message.
-- Allows specifying a prefix for the debug message.
function mls.debug(message, prefix)
if not LOG_ENABLE then
return message
end
local log_message = dump(message)
local result = "[DEBUG]"
if prefix then
result = result .. "[" .. tostring(prefix) .. "]"
end
result = result .. " " .. tostring(log_message)
print(result)
return result
end
---------------------------------------------
---- Code Block code extraction ---
---------------------------------------------
-- Get child of node
-- Helper function to find a child node of a given type within a parent node.
local getChild = function(node, type)
for _, child in ipairs(node.children) do
if child.type == type then
return child
end
end
end
-- Get text of Node
-- Helper function to recursively extract the text content from a node.
local getText = function(node)
if not node then
return nil
end
if node.text then
return node.text
else
for _, child in ipairs(node.children) do
local text = getText(child)
if text then
return text
end
end
end
end
-- Find codeblock
-- Recursively searches for a code block (FencedCode node) with a specific blockId in the node's children.
local findMyFence = function(node,blockId)
if not node.children then
return nil
end
for _, child in ipairs(node.children) do
--mls.debug(child) -- commented out debug line
if child.type == "FencedCode" then
local info = getText(getChild(child, "CodeInfo"))
--mls.debug(info) -- commented out debug line
if info and info:find(blockId) then
mls.debug(info)
return getChild(child, "CodeText")
end
end
local result= findMyFence(child,blockId)
if result ~=nil then
return result
end
--break -- for loop
end --for
end
-- Get code source in md codeblock
-- Parses a Markdown page, finds a code block with the given blockId, and returns its content.
-- Allows replacing a token within the code block with a new text value.
function mls.getCodeBlock (page,blockId,token,text)
local tree = markdown.parseMarkdown(space.readPage(page))
--mls.debug(tree) -- commented out debug line
if tree then
local fence = findMyFence(tree,blockId)
--mls.debug(fence) -- commented out debug line
if fence then
local result=fence.children[1].text
if token == nil or text==nil then
return result
else
return string.gsub(result, token, text)
end
end
end
return "Error"
end
-- Parse ISO Date
-- Parses an ISO date string into a Unix timestamp.
-- This function appears incomplete and has a potential error in the offset calculation.
-- The `xoffset` variable is not defined.
function mls.parseISODate(isoDate)
local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:%d+:%d+([Z%+%-])(%d?%d?)%:?(%d?%d?)"
local year, month, day, hour, minute,
seconds, offsetsign, offsethour, offsetmin = json_date:match(pattern)
local timestamp = os.time{year = year, month = month,
day = day, hour = hour, min = minute, sec = seconds}
local offset = 0
if offsetsign ~= 'Z' then
offset = tonumber(offsethour) * 60 + tonumber(offsetmin)
if xoffset == "-" then offset = offset * -1 end
end
return timestamp + offset
end
-- Get Stdlib Internal
-- Retrieves a list of internal API keys from a remote file.
-- Caches the results to avoid repeated fetching.
-- The URL points to a file in the silverbulletmd/silverbullet repository.
function mls.getStdlibInternal()
if _G ~=nil then
return table.keys(_G)
end
-- get list of internal api TODO: remove
local KEY="stdlib_internal"
local result=mls.cache.ttl.CacheManager.get(KEY)
if result == nil then
local url = "https://raw.githubusercontent.com/silverbulletmd/silverbullet/refs/heads/main/client/space_lua/stdlib.ts"
local resp = net.proxyFetch(url)
if resp.status ~= 200 then
error("Failed to fetch file: " .. resp.status)
end
local content = resp.body
local results = {}
for line in string.split(content,"\n") do
local key = string.match(string.trim(line), 'env%.set%("(.*)"')
if key then
table.insert(results, key)
end
end
result=table.sort(results)
mls.cache.ttl.CacheManager.set(KEY,result)
end
return table.sort(result)
end
-- Position to Line Column
-- Converts a character position within a text string to a line and column number.
function mls.positionToLineColumn(text, pos)
local line = 1
local column = 0
local lastNewline = -1
for i = 0, pos - 1 do
if string.sub(text, i + 1, i + 1) == "\n" then
line = line + 1
lastNewline = i
column = 0
else
column = column + 1
end
end
return { line = line, column = column }
end
-----------------------------------------
-- TABLE
-----------------------------------------
-- Append Array
-- Appends all elements from one array to another.
function table.appendArray(a, b)
if a~= nil and b ~= nil then
for i = 1, #b do
table.insert(a, b[i])
end
end
return a
end
-- Unique
-- Returns a new array containing only the unique elements from the input array.
function table.unique(array)
local seen = {}
local result = {}
for i = 1, #array do
local v = array[i]
if not seen[v] then
seen[v] = true
result[#result + 1] = v
end
end
return result
end
-- Unique KV By
-- Returns a new array containing only the unique key-value pairs from the input array,
-- based on a provided key function.
function table.uniqueKVBy(array, keyFn)
local seen = {}
local result = {}
for i = 1, #array do
local key = keyFn(array[i])
if not seen[key] then
seen[key] = true
result[#result + 1] = array[i]
end
end
return result
end
-- Merge KV
-- Merges two tables, recursively merging nested tables.
-- If a key exists in both tables and the values are both tables, the nested tables are merged.
-- Otherwise, the value from the second table overwrites the value from the first table.
function table.mergeKV(t1, t2)
for k, v in pairs(t2) do
if type(v) == "table" and type(t1[k]) == "table" then
mergeTablesRecursive(t1[k], v)
else
t1[k] = v
end
end
return t1
end
-- Map
-- Applies a function to each element of an array and returns a new array with the results.
function table.map(t, fn)
local result = {}
for i, v in ipairs(t) do
result[i] = fn(v, i)
end
return result
end
-- To Md
-- Converts a Lua table to a Markdown table string.
-- Handles both arrays of arrays and arrays of objects.
-- Can parse JSON or Lua strings as input.
function table.toMd(data)
local tbl
local input=string.trim(data)
-- If input is a string, try parsing as JSON
if string.startsWith(data,"{") then
-- Lua
tbl=spacelua.evalExpression(spacelua.parseExpression(data))
else
--JSON
tbl=js.tolua(js.window.JSON.parse(js.tojs(data)))
end
if #tbl == 0 then return "" end
local md = {}
-- Helper to convert a row to Markdown
local function rowToMd(row)
local cells = {}
for _, cell in ipairs(row) do
table.insert(cells, tostring(cell))
end
return "| " .. table.concat(cells, " | ") .. " |"
end
-- Handle array of objects
local first = tbl[1]
local headers
if type(first) == "table" and not (#first > 0) then
headers = {}
for k in pairs(first) do table.insert(headers, k) end
local rows = {headers}
for _, obj in ipairs(tbl) do
local row = {}
for _, h in ipairs(headers) do
table.insert(row, obj[h] or "")
end
table.insert(rows, row)
end
tbl = rows
end
-- Header
table.insert(md, rowToMd(tbl[1]))
-- Separator
local sep = {}
for _ = 1, #tbl[1] do table.insert(sep, "---") end
table.insert(md, "| " .. table.concat(sep, " | ") .. " |")
-- Data rows
for i = 2, #tbl do
table.insert(md, rowToMd(tbl[i]))
end
return table.concat(md, "\n")
end
Changelog
- 2026-01-22:
- getStdlibInternal compatible with edge version https://community.silverbullet.md/t/risk-audit/3562/14
- 2026-01-20:
- feat: add table functions
- map
- mergeKV
- uniqueKVBy
- unique
- appendArray
- feat: add
mls.getStdlibInternalfucntion
- feat: add table functions