Conflicted Pages: Widget & Diff Viewer

I’ve put together a little widget that lists any conflicted pages in your SilverBullet instance, with direct links to view the diffs.

When you open a specific diff page, it shows:

  • The actual diff
  • Buttons to delete the conflicted page or apply the diff which will overwrite the original page and delete the conflicted page

:warning: Heads-up

Since this widget deletes and overwrites pages, there’s some risk involved if something doesn’t work as expected. That said, it’s been working smoothly for me so far - no issues yet.

:wrench: Requirements

This script depends on the changes introduced in this PR which was merged about an hour ago. So make sure your SilverBullet instance is up to date.

:package: Grab it here

https://gist.github.com/lochel/22f739cb4f65e586e4df49932bc453b9

Thanks! I was thinking lately to implement a command to embed diff block into current page to solve the long standing issue of comflict resolving in SB. But TBH widget seems to be really a better option. Will try when I got time!

Thank you very much! Works like a charm so far.

In fact, I was also experimenting with the diff command before and could not get it to work. From your PR I learned, why… :slight_smile:

So thanks for BOTH: find and fixing the error and for the great function!

Thanks for sharing!

I never thought about using the pageCreating event to display custom temporary pages.

Hey, this link isn't working for me, any chance to get that code still? Are you still using it? Thank you in advance :slight_smile:

Widget

widgets = widgets or {}

-- Widget to detect and display links to conflicted page versions
function widgets.conflicts(options)
  options = options or {}

  -- Get the current page name, and strip off any `.conflicted.<number>` suffix
  local pageName = editor.getCurrentPage()
  if pageName:match("^.+%.conflicted:[0-9]+$") then
    pageName = pageName:gsub("%.conflicted:[0-9]+", "")
  end

  -- Query all conflicted versions of the current page
  local result = query[[
    from index.tag "page"
    where name:match("^" .. pageName .. "%.conflicted:[0-9]+$")
    select {name=name}
  ]]

  if #result == 0 then
    return -- No conflicts found
  end

  -- Build a markdown widget listing the conflicted versions
  local markdown = "# ⚠️ Other Versions Detected\n"
  markdown = markdown .. "* Page: [[" .. pageName .. "]]\n"

  for _, item in ipairs(result) do
    local baseName = item.name:gsub("^%./", "")
    baseName = baseName:gsub("%.md$", "")
    markdown = markdown .. "* Diff: [[diff:" .. baseName .. "|" .. baseName .. "]]\n"
  end

  return widget.new {
    markdown = markdown
  }
end

-- Register widget to render at the top of the page
event.listen {
  name = "hooks:renderTopWidgets",
  run = function(e)
    return widgets.conflicts()
  end
}

Diff Page

event.listen {
  name = "editor:pageCreating",
  run = function(e)
    if not e.data.name:startsWith("diff:") then
      return
    end

    -- Extract names from the diff page
    local pageName = e.data.name:sub(#"diff:" + 1)
    local originalName = pageName:gsub("%.conflicted:[0-9]+", "")

    -- Compute the diff
    local diff_result = shell.run("diff", {pageName .. ".md", originalName .. ".md"})

    -- Generate interactive markdown content
    local text = "## Diff\n"
    text = text .. "`>`: [[" .. originalName .. "]]\n"
    text = text .. "`<`: [[" .. pageName .. "]]\n\n"
    text = text .. "${widgets.button(\"❌ Delete diff\", function() space.deletePage(\"" .. pageName .. "\"); editor.navigate(\"" .. originalName .. "\") end)}  "
    text = text .. "${widgets.button(\"✅ Use diff\", function() local content = space.readPage(\"" .. pageName .. "\"); space.writePage(\"" .. originalName .. "\", content); space.deletePage(\"" .. pageName .. "\");editor.navigate(\"" .. originalName .. "\") end)}\n\n"

    if diff_result.stdout ~= "" then
      text = text .. "```diff\n" .. diff_result.stdout .. "\n```\n"
    else
      text = text .. "_No differences found._\n"
    end

    return {
      text = text,
      perm = "ro"
    }
  end
}

If you want to reuse diff mechanism of my git plugin. I work fine