List of Page Navigation Functions

  1. https://community.silverbullet.md/t/page-navigation-functions/3484

page.parents(path)

${page.parents()}

-- Returns a table with the list of parent pages.
page = page or {}
function page.parents(path)
  local path = path or editor.getCurrentPage()
  local livelli = string.split(path, "/")
  local genitori = {}
  local pathParziale = ""

  for i = 1, #livelli - 1 do
    pathParziale = (i == 1) and livelli[i] or (pathParziale .. "/" .. livelli[i])
    table.insert(genitori, pathParziale)
  end
  return genitori
end

page.sisters()

${page.sisters()}

-- Collection of โ€œsisterโ€ pages at the same level.
page = page or {}
function page.sisters()
  local pa = query[from index.tag "page"
  where string.startsWith(name, page.up() .. "/") 
  and not string.match(name, page.up() .. "/.+/")
  ](from index.tag "page"
  where string.startsWith(name, page.up() .. "/") 
  and not string.match(name, page.up() .. "/.+/")
  )
  return pa
end

page.prec()

${page.prec()}

-- Returns the previous page at the same level.  
-- Returns an empty string if no page is found.
page = page or {}
function page.prec()
  local filtro1 = page.up() .. "/"
  local filtro2 = page.up() .. "/.+/"
  local paa = editor.getCurrentPage()
  local pa = query[from index.tag "page"
              where string.startsWith(name, filtro1) 
              and not string.match(name, filtro2)
              and name < paa
              order by name desc
              limit 1](from index.tag "page"
              where string.startsWith(name, filtro1) 
              and not string.match(name, filtro2)
              and name < paa
              order by name desc
              limit 1)
  -- Se pa รจ nil o vuota, restituisce una stringa vuota
  if not pa or #pa == 0 then
    return ""
  end 

  return pa[1].name
end

page.succ()

${page.succ()}

-- Returns the next page at the same level.  
-- Returns an empty string if no page is found.
page = page or {}
function page.succ()
  local filtro1 = page.up() .. "/"
  local filtro2 = page.up() .. "/.+/"
  local paa = editor.getCurrentPage()
  local pa = query[from index.tag "page"
              where string.startsWith(name, filtro1) 
              and not string.match(name, filtro2)
              and name > paa
              order by name 
              limit 1](from index.tag "page"
              where string.startsWith(name, filtro1) 
              and not string.match(name, filtro2)
              and name > paa
              order by name 
              limit 1)
    if not pa or #pa == 0 then
      return ""
    end
    return pa[1].name
end

page.up()

${page.up()}

-- Returns the parent page.
page = page or {}
function page.up(path)
  local path = path or editor.getCurrentPage()
  local parts = {}
  
  -- Suddivide il percorso in segmenti
  for part in string.split(path, "/") do
    table.insert(parts, part)
  end
  for i = 1, #parts - 1 do
    if i == 1 then
      currentPath = parts[i]
    else
      currentPath = currentPath .. "/" .. parts[i]
    end
  end
  return currentPath
end

page.child(path)

${page.child()}

page = page or {}
function page.child(path)
  local path = path or editor.getCurrentPage()
  local parts = {}
  local pa = query[from index.tag "page"
    where string.startsWith(name, path .. "/") 
    and not string.match(name, path .. "/.+/")
    ](from index.tag "page"
    where string.startsWith(name, path .. "/") 
    and not string.match(name, path .. "/.+/")
    )
  
  if not pa or #pa == 0 then
    -- "๐Ÿ˜ฎ Nessuna pagina."
    -- Returns `nil` if no pages are found. (โ€œ๐Ÿ˜ฎ No pages.โ€)
    return nil 
  end 

  return pa
end

page.lev()

${page.lev()}

-- Calculates the number of levels in the path.
page = page or {}
function page.lev(path)
  local path = path or editor.getCurrentPage()
  return #string.split(path, "/")
end

page.meta(where)

${page.meta()}

-- Returns metadata of the specified page (or current page if omitted).  
page = page or {}
function page.meta(where)
  local where = where or editor.getCurrentPage()
  local meta = query[
    from index.tag "page" 
    where name == where
    ](
    from index.tag "page" 
    where name == where
    )
  return meta or Null
end

page.nome()

${page.nome("CONFIG/API/Page Navigation")}

page = page or {}
-- Extracts the final part of the path as the page name.  
-- If the last part contains an ISO date, returns only the date.  

function page.nome(path)
  -- Extracts the last part after the final โ€œ/โ€.    

  local lastPart = path:match(".*/(.*)$") or path
  -- Searches for an ISO-formatted date (YYYY-MM-DD).  

  local dateISO = lastPart:match("(%d%d%d%d%-%d%d%-%d%d)")
  if dateISO then return dateISO end

  local cleaned = lastPart:gsub("_", " ")
  -- Replaces underscores with spaces.  
  -- Decodes URL-encoded characters (e.g. %27 โ†’ ').
  cleaned = cleaned:gsub("%%(%x%x)", function(hex)
    return string.char(tonumber(hex, 16))
  end)

  return cleaned
end

page.title(โ€œpathโ€)

${page.title()}

-- Returns the first H1 header as the page title; 
-- if none exists, uses `page.nome()`.

page = page or {} -- function page.title(pagina)
function page.title(pagina)
  pagina = pagina or editor.getCurrentPage()
  -- Searches for a level-1 header.
  local titolo = query[
      from index.tag "header" where
        page == pagina and
        level == 1
        order by pos
        limit 1
    ](
      from index.tag "header" where
        page == pagina and
        level == 1
        order by pos
        limit 1
    )
  if titolo then
    return tostring(titolo[1].name)
  else
    return page.nome(pagina)
  end
end

Widget

page.childlink()

CONFIG/API

-- function page.childlink(div)
-- Widget that lists and links all subpages.  
-- Returns โ€œNo subpages.โ€ if none exist.
-- div is the delimiter of the page list
page = page or {}

function page.childlink(div)
  local div = div or "\n* "
  local subpage = page.child()
  local subpagenum = #subpage
  local result = {}

  if subpagenum == 0 then
    return "Non ci sono pagine sottese."
  end

  table.insert(result, subpagenum .. " pagine sottese:\n")
  for i = 1, subpagenum do
    local s = subpage[i]
    local label = page.title(s.name) or s.displayName or s.name 
    table.insert(result, string.format("[%s|%s](%s|%s)", s.name, label))
  end

  return table.concat(result, div)
end

page.navp()

${page.navp()}

-- Returns the previous page within the same section.  
-- Returns an empty string if no page is found.
page = page or {}
function page.navp()
  local paa = editor.getCurrentPage()
  local p = string.split(paa, "/")
  local pa = query[[from index.tag "page"
      where string.startsWith(name, tostring(p[1]))
      and name < paa
      order by name desc
      limit 1]]
  -- Returns an empty string if no page is found.  
  if not pa or #pa == 0 then
    return ""
  end 

  return pa[1].name
end

page.navs()

${page.navs()}

-- Returns the next page within the same section.  
-- Returns an empty string if no page is found.  
page = page or {}
function page.navs()
  local paa = editor.getCurrentPage()
  local p = string.split(paa, "/")
  local pa = query[[from index.tag "page"
              where string.startsWith(name, tostring(p[1]))
              and name > paa
              order by name 
              limit 1]]
    -- Returns an empty string if no page is found.  
    if not pa or #pa == 0 then
      return ""
    end
    return pa[1].name
end

Marco breadcrumb (Top Widget)

-- Creates breadcrumb navigation links up to the parent page,  
-- optionally showing previous/next arrows (๐Ÿ‘ˆ ๐Ÿ‘‰).  
-- Checks `navp` and `navs` for nil or empty values.  
-- Uses `displayName` when available.

function Marco_breadcrumb()
  local path = editor.getCurrentPage()
  local child = page.child()
  local parts = string.split(path, "/")
  local breadcrumbs = {}
  local currentPath = ""

  local navp_raw = page.navp()
  local navs_raw = page.navs()

  local navp = (navp_raw and navp_raw ~= "") and ("[" .. navp_raw .. "|๐Ÿ‘ˆ](" .. navp_raw .. "|๐Ÿ‘ˆ)") or nil
  local navs = (navs_raw and navs_raw ~= "") and ("[" .. navs_raw .. "|๐Ÿ‘‰](" .. navs_raw .. "|๐Ÿ‘‰)") or nil

  if parts[1] == "Diario" then
    return
  elseif parts[1] == "index" then
    return
  end

  local function getDisplayName(pagePath)
    local meta = query[
      from index.tag "page"
      where name == pagePath
      select { displayName = displayName }
    ](
      from index.tag "page"
      where name == pagePath
      select { displayName = displayName }
    )
    if meta[1] and meta[1].displayName then
      return meta[1].displayName
    else
      return string.match(pagePath, "([^/]+)$") or pagePath
    end
  end

  for i, part in ipairs(parts) do
    if i == 1 then
      currentPath = part
    else
      currentPath = currentPath .. "/" .. part
    end
    local label = page.title(currentPath)
    if i == #parts then
      if navp then table.insert(breadcrumbs, navp) end
      table.insert(breadcrumbs, "**" .. label .. "**")
    else
      table.insert(breadcrumbs, string.format("[%s|%s](%s|%s)", currentPath, label))
    end
  end

  if navs then table.insert(breadcrumbs, navs) end

  return table.concat(breadcrumbs, "|")
end
event.listen {
  name = "hooks:renderTopWidgets",
  run = function(e)
    if editor.getCurrentPage().startsWith("Diario/") then
      return
    elseif editor.getCurrentPage().startsWith("index") then
      return
    end
    return widget.new {markdown = "\n\n".. Marco_breadcrumb().."\n\n"}
  end
}