name: Library/xczphysics/CONFIG/Widget/BreadCrumbs/Bottom tags: meta/library pageDecoration.prefix: "🥖 "

githubUrl_Original: https://github.com/malys/silverbullet-libraries/blob/main/src/Breadcrumbs.md

Adaptive Bread Crumb: Bottom

warning Warning depend on Library/xczphysics/CONFIG/Add_Fields_for_Obj/Last_Opened-Page#Visitimes 2: Client level|

Ver 5: add Picker widgets + Split into 3

BOTTOM breadcrumb 1

-- priority: 10

-- 主面包屑:按是否有子页面切换 ⇦⇨ / ⬅⮕ 分隔符,并追加 👀访问次数
function Yg.bc(path)
  local thisPage = path or editor.getCurrentPage()
  local mypath = thisPage:match("^(.*)/[^/]*$")
  local arrow_symbol_1 = choose("⇦⇨", "⬅⮕", mypath)
  local arrow_symbol_2 = choose("👶🏻", "👼🏻", mypath)
  local sibling_symbol_3 = choose("🧑‍🤝‍🧑", "👬🏼", mypath)

  -- 构建 .⇦⇨CONFIG⇦⇨Widget... 或 .⬅⮕CONFIG⬅⮕Widget...
  local dom_list = {"[.](.)"}
  local parts = string.split(thisPage, "/")
  local current = ""
  
  -- 抽出来一个辅助函数:给定 current,算出可用的 Children options
  local function collect_children(current_page)
    return query[[
      from index.tag 'page'
      -- where _.name:find("^" .. current_page .. "/")
      where _.name:startsWith(current_page .. "/")
      select { path = _.name,
        name = string.match(_.name, "([^/]+)$") }
    ]]
  end

  for _, part in ipairs(parts) do
    -- 记录当前层级的父路径(用于查询同级页面)
    local parent_path = current

    if current ~= "" then current = current .. "/" end
    current = current .. part

    -- 先预查一次 children
    local options = collect_children(current)

    if #options == 0 then
      -- 没有 children:只渲染一个箭头符号字符串,避免“点了也没用”的按钮
      table.insert(dom_list, arrow_symbol_1)
    else
      -- 有 children:生成按钮,点击时直接用预先算好的 options
      local function pick_child()
        local opt = editor.filterBox("🤏 Pick", options, "Select a Child", "👶🏻 a Child")
        if not opt then return end
        editor.navigate(opt.path)
      end

      local buto = widgets.button(arrow_symbol_2 .. #options, pick_child)
      table.insert(dom_list, buto)
    end
    table.insert(dom_list, "[" .. current .. "](" .. current .. ")")
  end

  -- 访问次数
  local data = datastore.get({"Visitimes", thisPage}) or {}
  local visits = data.value or 0
  -- local visitsSuffix = "[CONFIG/Add Fields for Obj/Last Opened/Visit Times|" .. "👀" .. tostring(visits) .. "](CONFIG/Add Fields for Obj/Last Opened/Visit Times|" .. "👀" .. tostring(visits) .. ")"
  local visiTimes = "[CONFIG/Add_Fields_for_Obj/Last_Opened-Page/Visit_Times|" .. tostring(visits) .. "](CONFIG/Add_Fields_for_Obj/Last_Opened-Page/Visit_Times|" .. tostring(visits) .. ")"

  -- pick siblings
  local options = query[[from index.tag "page" 
         where _.name ~= thisPage and _.name:find(pattern(mypath))
         select { path = _.name,
        name = string.match(_.name, "([^/]+)$") }]]
  -- table.insert(dom_list, " " .. visitsSuffix)
  if #options == 0 then
    table.insert(dom_list, "👀")
  else
    local function pick_sibling()
      local opt = editor.filterBox("🤏 Pick", options, "Select a Sibling", sibling_symbol_3 .. " a Sibling")
      if not opt then return end
      editor.navigate(opt.path)
    end
    local buto = widgets.button(sibling_symbol_3 .. #options, pick_sibling)
    table.insert(dom_list, buto)
  end
  table.insert(dom_list, visiTimes)
  -- table.insert(dom_list, "\n" .. lastMs)
  -- table.insert(dom_list, "\n" .. lastVs)
  
  return dom_list
end

function widgets.breadcrumbs_B1()
  return widget.new {
    -- markdown = Yg.bc()
    html = dom.div(Yg.bc()),
    display = "block",
  }
end
-- priority: 1
event.listen {
  name = "hooks:renderBottomWidgets",
  run = function(e)
    return widgets.breadcrumbs_B1()
  end
}

BOTTOM breadcrumb 2

-- priority: 11
Yg = Yg or {}

-- 模板使用 ${badge},序号徽章在数据阶段注入
function Bc_last()
  return template.new([==[${badge}[${name}](${name})​]==])
end

-- 仅用于 pattern() 的场景选择(保留原逻辑)
function choose(a, b, path)
  if path and #path > 0 then
    return a
  else
    return b
  end
end

-- 找 "parent/folder/to/file" 中的 file name
function pattern(path) -- path 是直接父目录 "parent/folder/to"
  -- return choose("^" .. path .. "/[^/]+$", "^[^/]+$", path)
  local a = path and ("^" .. path .. "/[^/]+$") or nil
  return choose(a, "^[^/]+$", path)
end

local max_num = 5  -- 如需覆盖 1~9,可改为 9

function Yg.lastM(thisPage, mypath)
  local list = query[from index.tag "page" 
         where _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastModified desc
         limit max_num](from index.tag "page" 
         where _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastModified desc
         limit max_num)

  -- 方块风格(沿用 Top 的约定)
  local M_hasFATHER = {"1⃣","2⃣","3⃣","4⃣","5⃣","6⃣","7⃣","8⃣","9⃣"}
  local M_noFATHER  = {"1️⃣","2️⃣","3️⃣","4️⃣","5️⃣","6️⃣","7️⃣","8️⃣","9️⃣"}
  local badges = choose(M_hasFATHER, M_noFATHER, mypath)

  for i, item in ipairs(list) do
    item.badge = badges[i] or ""
  end
  return list
end

function widgets.breadcrumbs_B2()
  local thisPage = path or editor.getCurrentPage()
  local mypath = thisPage:match("^(.*)/[^/]*$")
   -- mypath 是直接父目录 "parent/folder/to"
  local lastMs = template.each(Yg.lastM(thisPage, mypath), Bc_last()) or ""
  
  return widget.new {
    -- markdown = lastMs
    html = dom.div({ lastMs }),
    display = "block",
  }
end
-- priority: 2
event.listen {
  name = "hooks:renderBottomWidgets",
  run = function(e)
    return widgets.breadcrumbs_B2()
  end
}

BOTTOM breadcrumb 3

-- priority: 9
local max_num = 5  -- 如需覆盖 1~9,可改为 9

function Yg.lastV(thisPage, mypath)
  local list = query[from editor.getRecentlyOpenedPages "page"
         where _.lastOpened and _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastOpened desc
         limit max_num](from editor.getRecentlyOpenedPages "page"
         where _.lastOpened and _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastOpened desc
         limit max_num)
  
  -- 圆形风格(沿用 Top 的约定)
  local V_hasFATHER = {"①","②","③","④","⑤","⑥","⑦","⑧","⑨"}
  local V_noFATHER  = {"➊","➋","➌","➍","➎","➏","➐","➑","➒"}
  local badges = choose(V_hasFATHER, V_noFATHER, mypath)

  for i, item in ipairs(list) do
    item.badge = badges[i] or ""
  end
  return list
end

function widgets.breadcrumbs_B3()
  local thisPage = path or editor.getCurrentPage()
  local mypath = thisPage:match("^(.*)/[^/]*$")
  local lastVs = template.each(Yg.lastV(thisPage, mypath), Bc_last()) or ""
  
  return widget.new {
    -- markdown = lastVs
    html = dom.div({ lastVs }),
    display = "block",
  }
end
-- priority: 0
event.listen {
  name = "hooks:renderBottomWidgets",
  run = function(e)
    return widgets.breadcrumbs_B3()
  end
}

Ver 4: Adapt To Library/xczphysics/CONFIG/Add_Fields_for_Obj/Last_Opened-Page#Visitimes 2: Client level and index#Your Last Visit 👀

-- priority: 10
Yg = Yg or {}

-- 仅用于 pattern() 的场景选择(保留原逻辑)
local function choose(a, b, path)
  if path and #path > 0 then
    return a
  else
    return b
  end
end

-- 模板使用 ${badge},序号徽章在数据阶段注入
local function Bc_last()
  return template.new([==[${badge}[${name}](${name})​]==])
end

-- 与原逻辑一致:决定“同父级子页”或“顶层单段”的匹配
local function pattern(path)
  -- return choose("^" .. path .. "/[^/]+$", "^[^/]+$", path)
  local a = path and ("^" .. path .. "/[^/]+$") or nil
  return choose(a, "^[^/]+$", path)
end

local max_num = 5  -- 如需覆盖 1~9,可改为 9

function Yg.lastM(thisPage, mypath)
  local list = query[from index.tag "page" 
         where _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastModified desc
         limit max_num](from index.tag "page" 
         where _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastModified desc
         limit max_num)

  -- 方块风格(沿用 Top 的约定)
  local M_hasFATHER = {"1⃣","2⃣","3⃣","4⃣","5⃣","6⃣","7⃣","8⃣","9⃣"}
  local M_noFATHER  = {"1️⃣","2️⃣","3️⃣","4️⃣","5️⃣","6️⃣","7️⃣","8️⃣","9️⃣"}
  local badges = choose(M_hasFATHER, M_noFATHER, mypath)

  for i, item in ipairs(list) do
    item.badge = badges[i] or ""
  end
  return list
end

function Yg.lastV(thisPage, mypath)
  local list = query[from editor.getRecentlyOpenedPages "page"
         where _.lastOpened and _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastOpened desc
         limit max_num](from editor.getRecentlyOpenedPages "page"
         where _.lastOpened and _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastOpened desc
         limit max_num)
  
  -- 圆形风格(沿用 Top 的约定)
  local V_hasFATHER = {"①","②","③","④","⑤","⑥","⑦","⑧","⑨"}
  local V_noFATHER  = {"➊","➋","➌","➍","➎","➏","➐","➑","➒"}
  local badges = choose(V_hasFATHER, V_noFATHER, mypath)

  for i, item in ipairs(list) do
    item.badge = badges[i] or ""
  end
  return list
end

-- 主面包屑:按是否有子页面切换 ⇦⇨ / ⬅⮕ 分隔符,并追加 👀访问次数
function Yg.bc(path)
  local thisPage = path or editor.getCurrentPage()
  local mypath = thisPage:match("^(.*)/[^/]*$")
  local arrow = choose("⇦⇨", "⬅⮕", mypath)

  -- 构建 .⇦⇨CONFIG⇦⇨Widget... 或 .⬅⮕CONFIG⬅⮕Widget...
  local bc = "[.](.)"
  local parts = string.split(thisPage, "/")
  local current = ""
  for i, part in ipairs(parts) do
    if current ~= "" then current = current .. "/" end
    current = current .. part
    bc = bc .. arrow .. "[" .. current .. "](" .. current .. ")"
  end

  -- 最近修改 / 最近访问(带序号徽章)
  local lastMs = template.each(Yg.lastM(thisPage, mypath), Bc_last()) or ""
  local lastVs = template.each(Yg.lastV(thisPage, mypath), Bc_last()) or ""

  -- 访问次数
  local data = datastore.get({"Visitimes", thisPage}) or {}
  local visits = data.value or 0
  local visitsSuffix = "[CONFIG/Add Fields for Obj/Last Opened/Visit Times|" .. "👀" .. tostring(visits) .. "](CONFIG/Add Fields for Obj/Last Opened/Visit Times|" .. "👀" .. tostring(visits) .. ")"

  return bc .. " " .. visitsSuffix .. " " .. lastMs .. " " .. lastVs
end

function widgets.breadcrumbs_B()
  return widget.new {markdown = Yg.bc()}
end

Ver 3: 👀lastVisit added

-- priority: 10
Yg = Yg or {}

-- 访问次数数据来源:统计表(按 lastVisit 降序维护)
local VISIT_TABLE_PATH = "CONFIG/Add Fields for Obj/Last Opened/Visit Times"
local VISIT_CACHE_TTL = 3 -- 秒级缓存,避免高频 IO
local visitCache = { map = {}, loadedAt = 0 }

-- 行解析辅助(与统计表写入端一致)
local function _isSeparatorLine(line)
  return line:match("^%s*|%s*[%-:]+[%- :|]*$") ~= nil
end

local function _parseRow(line)
  if _isSeparatorLine(line) then return nil end
  local c1, c2, c3 = line:match("^%s*|%s*([^|]-)%s*|%s*([^|]-)%s*|%s*([^|]-)%s*|%s*$")
  if not c1 then return nil end
  return c1, c2, c3
end

local function _extractPageRefFromFirstCell(cellText)
  local cell = (cellText or ""):match("^%s*(.-)%s*$") or ""
  local inner = cell:match("^%[%[%s*(.-)%s*%]%]$")
  if inner then
    local ref = inner:match("^(.-)|") or inner
    return (ref or ""):match("^%s*(.-)%s*$")
  end
  return cell
end

local function _loadVisitMapIfStale(force)
  local now = os.time()
  if not force and (now - (visitCache.loadedAt or 0)) < VISIT_CACHE_TTL then
    return
  end
  local safeRead = (type(space) == "table" and type(space.readPage) == "function")
  local content = safeRead and (space.readPage(VISIT_TABLE_PATH) or "") or ""
  local map = {}

  if content ~= "" then
    local seenHeader, afterSep = false, false
    for line in (content .. "\n"):gmatch("([^\n]*)\n") do
      if not seenHeader then
        if line:match("^%s*|%s*pageRef%s*|%s*lastVisit%s*|%s*visitTimes%s*|%s*$") then
          seenHeader = true
        end
      else
        if not afterSep then
          if _isSeparatorLine(line) then
            afterSep = true
          end
        else
          local c1, _, c3 = _parseRow(line)
          if not c1 then break end -- 数据区结束
          local ref = _extractPageRefFromFirstCell(c1)
          local times = tonumber((c3 or ""):match("^%s*(%d+)%s*$")) or 0
          if ref ~= "" then map[ref] = times end
        end
      end
    end
  end

  visitCache.map = map
  visitCache.loadedAt = now
end

-- 获取某 pageRef 的访问次数:
-- 1) 命中缓存 -> 直接返回
-- 2) 读取统计表,快速路径:检查第一条数据行是否就是当前页,是则 O(1) 取值
-- 3) 否则回退:完整解析构建 map 再取值(并缓存)
local function getVisitTimesFor(pageRef)
  if not pageRef or pageRef == "" then return 0 end

  local now = os.time()
  if (now - (visitCache.loadedAt or 0)) < VISIT_CACHE_TTL then
    return visitCache.map[pageRef] or 0
  end

  local safeRead = (type(space) == "table" and type(space.readPage) == "function")
  local content = safeRead and (space.readPage(VISIT_TABLE_PATH) or "") or ""
  if content == "" then
    visitCache.map, visitCache.loadedAt = {}, now
    return 0
  end

  -- 快速路径:只检查第一条数据行
  local seenHeader, afterSep = false, false
  for line in (content .. "\n"):gmatch("([^\n]*)\n") do
    if not seenHeader then
      if line:match("^%s*|%s*pageRef%s*|%s*lastVisit%s*|%s*visitTimes%s*|%s*$") then
        seenHeader = true
      end
    else
      if not afterSep then
        if _isSeparatorLine(line) then
          afterSep = true
        end
      else
        local c1, _, c3 = _parseRow(line)
        if not c1 then break end -- 数据区结束
        local ref = _extractPageRefFromFirstCell(c1)
        if ref == pageRef then
          local times = tonumber((c3 or ""):match("^%s*(%d+)%s*$")) or 0
          visitCache.map = { [pageRef] = times } -- 轻缓存
          visitCache.loadedAt = now
          return times
        end
        -- 第一行不是当前页 -> 精确回退
        break
      end
    end
  end

  -- 回退:完整解析
  _loadVisitMapIfStale(true)
  return visitCache.map[pageRef] or 0
end

-- 仅用于 pattern() 的场景选择(保留原逻辑)
local function choose(a, b, path)
  if path and #path > 0 then
    return a
  else
    return b
  end
end

-- 模板使用 ${badge},序号徽章在数据阶段注入
local function Bc_last()
  return template.new([==[${badge}[${name}](${name})​]==])
end

-- 与原逻辑一致:决定“同父级子页”或“顶层单段”的匹配
local function pattern(path)
  return choose("^" .. path .. "/[^/]+$", "^[^/]+$", path)
end

local max_num = 5  -- 如需覆盖 1~9,可改为 9

function Yg.lastM(thisPage, mypath)
  local list = query[from index.tag "page" 
         where _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastModified desc
         limit max_num](from index.tag "page" 
         where _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastModified desc
         limit max_num)

  -- 方块风格(沿用 Top 的约定)
  local M_hasFATHER = {"1⃣","2⃣","3⃣","4⃣","5⃣","6⃣","7⃣","8⃣","9⃣"}
  local M_noFATHER  = {"1️⃣","2️⃣","3️⃣","4️⃣","5️⃣","6️⃣","7️⃣","8️⃣","9️⃣"}
  local badges = choose(M_hasFATHER, M_noFATHER, mypath)

  for i, item in ipairs(list) do
    item.badge = badges[i] or ""
  end
  return list
end

function Yg.lastV(thisPage, mypath)
  local list = query[from index.tag "page" 
         where _.lastVisit and _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastVisit desc
         limit max_num](from index.tag "page" 
         where _.lastVisit and _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastVisit desc
         limit max_num)

  -- 圆形风格(沿用 Top 的约定)
  local V_hasFATHER = {"①","②","③","④","⑤","⑥","⑦","⑧","⑨"}
  local V_noFATHER  = {"➊","➋","➌","➍","➎","➏","➐","➑","➒"}
  local badges = choose(V_hasFATHER, V_noFATHER, mypath)

  for i, item in ipairs(list) do
    item.badge = badges[i] or ""
  end
  return list
end

-- 主面包屑:按是否有子页面切换 ⇦⇨ / ⬅⮕ 分隔符,并追加 👀访问次数
function Yg.bc(path)
  local thisPage = path or editor.getCurrentPage()
  local mypath = thisPage:match("^(.*)/[^/]*$")
  local arrow = choose("⇦⇨", "⬅⮕", mypath)

  -- 构建 .⇦⇨CONFIG⇦⇨Widget... 或 .⬅⮕CONFIG⬅⮕Widget...
  local bc = "[.](.)"
  local parts = string.split(thisPage, "/")
  local current = ""
  for i, part in ipairs(parts) do
    if current ~= "" then current = current .. "/" end
    current = current .. part
    bc = bc .. arrow .. "[" .. current .. "](" .. current .. ")"
  end

  -- 最近修改 / 最近访问(带序号徽章)
  local lastMs = template.each(Yg.lastM(thisPage, mypath), Bc_last()) or ""
  local lastVs = template.each(Yg.lastV(thisPage, mypath), Bc_last()) or ""

  -- 访问次数(来自 Visit Times 表,带秒级缓存 + 快速路径)
  local visits = getVisitTimesFor(thisPage)
  local visitsSuffix = "[CONFIG/Add Fields for Obj/Last Opened/Visit Times|" .. "👀" .. tostring(visits) .. "](CONFIG/Add Fields for Obj/Last Opened/Visit Times|" .. "👀" .. tostring(visits) .. ")"

  return bc .. " " .. visitsSuffix .. " " .. lastMs .. " " .. lastVs
end

function widgets.breadcrumbs_B()
  return widget.new {markdown = Yg.bc()}
end

Ver 2: emoji uploaded

➡🢧➩🢥 ⇨🡆🢥⮊

-- priority: 10
Yg = Yg or {}

-- 仅用于 pattern() 的场景选择(保留原逻辑)
local function choose(a, b, path)
  if path and #path > 0 then
    return a
  else
    return b
  end
end

-- 模板使用 ${badge},序号徽章在数据阶段注入
local function Bc_last()
  return template.new([==[${badge}[${name}](${name})​]==])
end

-- 主面包屑:按是否有子页面切换 ⇦⇨ / ⬅⮕ 分隔符
function Yg.bc(path)
  local thisPage = path or editor.getCurrentPage()
  local mypath = thisPage:match("^(.*)/[^/]*$")
  local arrow = choose("⇦⇨", "⬅⮕", mypath)

  -- 构建 .⇦⇨CONFIG⇦⇨Widget... 或 .⬅⮕CONFIG⬅⮕Widget...
  local bc = "[.](.)"
  local parts = string.split(thisPage, "/")
  local current = ""
  for i, part in ipairs(parts) do
    if current ~= "" then current = current .. "/" end
    current = current .. part
    bc = bc .. arrow .. "[" .. current .. "](" .. current .. ")"
  end

  -- 最近修改 / 最近访问(带序号徽章)
  local lastMs = template.each(Yg.lastM(thisPage, mypath), Bc_last()) or ""
  local lastVs = template.each(Yg.lastV(thisPage, mypath), Bc_last()) or ""
  return bc .. " " .. lastMs .. " " .. lastVs
end

-- 与原逻辑一致:决定“同父级子页”或“顶层单段”的匹配
local function pattern(path)
  return choose("^" .. path .. "/[^/]+$", "^[^/]+$", path)
end

local max_num = 5  -- 如需覆盖 1~9,可改为 9

function Yg.lastM(thisPage, mypath)
  local list = query[from index.tag "page" 
         where _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastModified desc
         limit max_num](from index.tag "page" 
         where _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastModified desc
         limit max_num)

  -- 方块风格(沿用 Top 的约定)
  local M_hasFATHER   = {"1⃣","2⃣","3⃣","4⃣","5⃣","6⃣","7⃣","8⃣","9⃣"}
  local M_noFATHER = {"1️⃣","2️⃣","3️⃣","4️⃣","5️⃣","6️⃣","7️⃣","8️⃣","9️⃣"}
  local badges = choose(M_hasFATHER, M_noFATHER, mypath)

  for i, item in ipairs(list) do
    item.badge = badges[i] or ""
  end
  return list
end

function Yg.lastV(thisPage, mypath)
  local list = query[from index.tag "page" 
         where _.lastVisit and _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastVisit desc
         limit max_num](from index.tag "page" 
         where _.lastVisit and _.name ~= thisPage and _.name:find(pattern(mypath))
         order by _.lastVisit desc
         limit max_num)

  -- 圆形风格(沿用 Top 的约定)
  local V_hasFATHER   = {"①","②","③","④","⑤","⑥","⑦","⑧","⑨"}
  local V_noFATHER = {"➊","➋","➌","➍","➎","➏","➐","➑","➒"}
  local badges = choose(V_hasFATHER, V_noFATHER, mypath)

  for i, item in ipairs(list) do
    item.badge = badges[i] or ""
  end
  return list
end

function widgets.breadcrumbs_B()
  return widget.new {markdown = Yg.bc()}
end

Ver 1

  1. modified one https://chatgpt.com/g/g-p-68bb175bf6f48191b504746c0931128f-silverbullet-xue-xi/shared/c/68f9f16d-259c-832e-aae8-699bbb61fd15?owner_user_id=user-h5bPGeyU1zwi7LcI6XCA3cuY
  2. https://community.silverbullet.md/t/abc-adaptive-bread-crumb/3464
-- priority: 10
Yg = Yg or {}
Bc_folder = template.new[==[/[${name}](${name})​]==]

function Yg.breadcrumbs(path)
  -- local mypage = path or editor.getCurrentPage():match("^(.*)/[^/]*$")
  local mypage = path or editor.getCurrentPage()
  -- editor.flashNotification(mypage)
  local parts = string.split(mypage, "/")
  local crumbs = {}
  for i, part in ipairs(parts) do
    local current = table.concat(parts, "/", 1, i)
    table.insert(crumbs, {name = current})
  end
  return crumbs
end

local function choose(a, b, path)
  local mypath = path or editor.getCurrentPage():match("^(.*)/[^/]*$")
  if mypath and #mypath > 0 then
    return a
  else
    return b
  end
end

local function Bc_lastM(path)
  return template.new(choose([==[⇦⇨[${name}](${name})​]==], [==[⬅⮕[${name}](${name})​]==], path))
  -- return template.new(choose([==[⤄[${name}](${name})​]==], [==[⬅⮕[${name}](${name})​]==], path))
  -- https://symbl.cc/cn/search/?q=%E5%90%91%E5%8F%B3%E7%9A%84%E9%BB%91%E7%AE%AD#google_vignette :left_arrow https://symbl.cc/cn/search/?q=%E5%B7%A6%E5%8F%B3%E5%8F%8C%E7%AE%AD%E5%A4%B4#google_vignette
  -- return template.new(choose([==[⬄[${name}](${name})​]==], [==[⬌[${name}](${name})​]==], path))
  -- https://symbl.cc/cn/search/?q=%E5%B7%A6%E5%8F%B3%E7%A9%BA%E5%BF%83%E7%AE%AD%E5%A4%B4
end
local function Bc_lastV(path)
  return template.new(choose([==[◻[${name}](${name})​]==], [==[◼[${name}](${name})​]==], path))
  -- return template.new(choose([==[∴[${name}](${name})​]==], [==[※[${name}](${name})​]==], path))
  -- return template.new(choose([==[☷[${name}](${name})​]==], [==[☰[${name}](${name})​]==], path))
end


function Yg.bc(path)
  local bc = template.each(Yg.breadcrumbs(path), Bc_folder) or ""
  local lastMs = template.each(Yg.lastM(path), Bc_lastM(path)) or ""
  local lastVs = template.each(Yg.lastV(path), Bc_lastV(path)) or ""
  return "[.](.)" .. bc .. " " .. lastMs .. " " .. lastVs
end

local function pattern(path)
  local mypath = path or editor.getCurrentPage():match("^(.*)/[^/]*$")
  return choose("^" .. mypath .. "/[^/]+$", "^[^/]+$", mypath)
end

local max_num = 5

function Yg.lastM(path)
  return query[from index.tag "page" 
         where _.name != editor.getCurrentPage() and _.name:find(pattern(path))
         order by _.lastModified desc
         limit max_num](from index.tag "page" 
         where _.name != editor.getCurrentPage() and _.name:find(pattern(path))
         order by _.lastModified desc
         limit max_num)
end

function Yg.lastV(path)
  return query[from index.tag "page" 
         where _.lastVisit and _.name != editor.getCurrentPage() and _.name:find(pattern(path))
         order by _.lastVisit desc
         limit max_num](from index.tag "page" 
         where _.lastVisit and _.name != editor.getCurrentPage() and _.name:find(pattern(path))
         order by _.lastVisit desc
         limit max_num)
end

function widgets.breadcrumbs_B()
  return widget.new {markdown = Yg.bc()}
end
-- priority: 20
event.listen {
  name = "hooks:renderBottomWidgets",
  run = function(e)
    return widgets.breadcrumbs_B()
  end
}