Click History

add Click History: Page Picker

should help ease overly detailed records.
Even so, it still navigates by default to the last click on that page.
One can modify it to jump on page hierarchy rather than page@pos.

local function getTimes()
  local t = datastore.get({"ClickTimes", "!"}) or {}
  return t.Ctimes or 1
end

local function setTimes(n)
  datastore.set({"ClickTimes", "!"}, { Ctimes = n })
end

local function getBrowse()
  local b = datastore.get({"ClickBrowse", "!"})
  if b then return b end
  local ct = getTimes()
  b = { index = ct, max = ct - 1, active = false }
  datastore.set({"ClickBrowse", "!"}, b)
  return b
end

local function setBrowse(b)
  datastore.set({"ClickBrowse", "!"}, b)
end

local function getRef(idx)
  local rec = datastore.get({"ClickHistory", tostring(idx)}) or {}
  return rec.ref
end

local function getTimeString(idx)
  local rec = datastore.get({"ClickHistory", tostring(idx)}) or {}
  if rec.tstr and rec.tstr ~= "" then
    return rec.tstr
  end
  if rec.ts then
    return os.date("%Y-%m-%d %H:%M:%S", rec.ts)
  end
  return nil
end

local function setRef(idx, ref)
  local now = os.time()
  datastore.set(
    {"ClickHistory", tostring(idx)},
    {
      ref = ref,
      -- ts = now,
      tstr = os.date("%Y-%m-%d %H:%M:%S", now),
    }
  )
end

local function clearAllHistory()
  local Ctimes = getTimes()
  for i = 1, Ctimes do
    datastore.delete({"ClickHistory", tostring(i)})
  end
  setTimes(1)
  setBrowse({ index = 1, max = 0, active = false })
end

local enableTruncateDuringBrowse = false

local function appendHistory(ref)
  local Ctimes = getTimes()
  local lastRef = getRef(Ctimes - 1)
  
  if lastRef and lastRef == ref then
    return
  end

  local browse = getBrowse()

  if enableTruncateDuringBrowse and browse.active and browse.index <= browse.max then
    for i = browse.index + 1, browse.max do
       datastore.delete({"ClickHistory", tostring(i)})
    end
    Ctimes = browse.index + 1
    setTimes(Ctimes)
    browse.index = Ctimes
    browse.max = Ctimes
    setBrowse(browse)
  end

  setRef(Ctimes, ref)
  setTimes(Ctimes + 1)

  local newTimes = Ctimes + 1
  setBrowse({ index = newTimes, max = newTimes - 1, active = false })
end

local function navigateIndex(idx)
  local ref = getRef(idx)
  if not ref then
    return false
  end
  editor.navigate(ref)
  local pos = tonumber(ref:match("@(.*)$"))
  if pos then
      editor.moveCursor(pos, true)
  end
  return true      
end

local function ensureBrowseSession()
  local b = getBrowse()
  if not b.active then
    local Ctimes = getTimes()
    b.max = Ctimes - 1
    b.index = Ctimes
    b.active = true
    setBrowse(b)
  end
  return getBrowse()
end

-- add:Get/Set Record Mode
-- nil/false: `Click` to record (default)
-- true: `Ctrl + Click` to record
local function getRecordMode()
  local ClickHistoryMode = datastore.get({"ClickHistoryMode", "!"}) or {}
  return ClickHistoryMode.currentMode or false
end

event.listen {
  name = "page:click",
  run = function(e)
    local d = e.data or {}
    local pageName = editor.getCurrentPage()
    local pos = d.pos
    if not pageName or not pos then return end

    local ref = string.format("%s@%d", pageName, pos)
    local ctrlRecordMode = getRecordMode()

    if ctrlRecordMode then
      if d.ctrlKey then
        appendHistory(ref)
        editor.moveCursor(pos, true)
        editor.flashNotification("pos @ " .. tostring(pos))
      end
    else
      if d.ctrlKey then
        editor.moveCursor(pos, true)
        editor.flashNotification("pos @ " .. tostring(pos))
      else
        appendHistory(ref)
      end
    end
  end
}

command.define {
  name = "Click History: Toggle Mode",
  key = "Ctrl-Shift-m", 
  priority = 1,
  run = function()
    local currentMode = getRecordMode()
    local newMode = not currentMode
    datastore.set({"ClickHistoryMode", "!"}, {currentMode = newMode})
    
    if newMode then
      editor.flashNotification("Mode switched: [Ctrl + Click] to record history.")
    else
      editor.flashNotification("Mode switched: [Click] to record history.")
    end
  end,
}

command.define {
  name = "Click History: Back",
  run = function()
    local b = ensureBrowseSession()

    if b.max < 1 then
      editor.flashNotification("No history", "warning")
      return
    end

    if b.index > b.max + 1 then
      b.index = b.max + 1
    end
    
    b.index = math.max(b.index - 1, 1)

    setBrowse(b)
    if navigateIndex(b.index) then
      editor.flashNotification(string.format("Back: %d / %d", b.index, b.max))
    end
  end,
  key = "Shift-Alt-ArrowLeft",
  mac = "Shift-Alt-ArrowLeft",
  priority = 1,
}

command.define {
  name = "Click History: Forward",
  run = function()
    local b = ensureBrowseSession()

    if b.max < 1 then
      editor.flashNotification("No history", "warning")
      return
    end

    b.index = math.min(b.index + 1, b.max)
    setBrowse(b)

    if navigateIndex(b.index) then
      editor.flashNotification(string.format("Forward: %d / %d", b.index, b.max))
    end
  end,
  key = "Shift-Alt-ArrowRight",
  mac = "Shift-Alt-ArrowRight",
  priority = 1,
}

command.define {
  name = "Click History: End",
  run = function()
    local Ctimes = getTimes()
    local max = Ctimes - 1
    if max < 1 then
      editor.flashNotification("No history", "warning")
      return
    end
    setBrowse({ index = max, max = max, active = false })
    if navigateIndex(max) then
      editor.flashNotification(string.format("End: %d / %d", max, max))
    end
  end,
  key = "Ctrl-Shift-Alt-ArrowRight",
  mac = "Ctrl-Shift-Alt-ArrowRight",
  priority = 1,
}

command.define {
  name = "Click History: Start",
  run = function()
    local b = ensureBrowseSession()

    if b.max < 1 then
      editor.flashNotification("No history", "warning")
      return
    end

    b.index = 1
    setBrowse(b)
    
    if navigateIndex(1) then
      editor.flashNotification(string.format("Start: 1 / %d", b.max))
    end
  end,
  key = "Ctrl-Shift-Alt-ArrowLeft",
  mac = "Ctrl-Shift-Alt-ArrowLeft",
  priority = 1,
}

command.define {
  name = "Click History: Clear",
  run = function()
    clearAllHistory()
    editor.flashNotification("Click History cleared.", "info")
  end,
  key = "Ctrl-Shift-Alt-Delete", 
  mac = "Ctrl-Shift-Alt-Delete",
  priority = 1,
}

local Ctimes = getTimes()
setBrowse({ index = Ctimes, max = Ctimes - 1, active = false })

------------------------------------------------------------
-- Click History: Cursor Picker Implementation
------------------------------------------------------------

command.define {
  name = "Click History: Cursor Picker",
  run = function()
    local Ctimes = getTimes()
    local max = Ctimes - 1
    
    if max < 1 then
      editor.flashNotification("No click history found.", "warning")
      return
    end

    local historyItems = {}
    
    for i = max, 1, -1 do
      local ref = getRef(i)
      if ref then
        local pageName, pos = ref:match("^(.*)@(%d+)$")
        local displayName = ref
        local tstr = getTimeString(i) or ""
        
        if pageName and tstr then
          displayName = string.format("%d 🖱️ %s", i, pageName)
        else
          displayName = string.format("%d. %s", i, ref)
        end

        table.insert(historyItems, {
          id = i,
          name = displayName,
          description = tstr .. " 📍 " .. pos,
          ref = ref
        })
      end
    end

    local sel = editor.filterBox(
      "Back to",
      historyItems,
      "Select a Click History...",
      "Page @ Pos where you Once Clicked"
    )

    if sel then
      local b = ensureBrowseSession()
      b.index = sel.id
      setBrowse(b)
      if navigateIndex(sel.id) then
        editor.flashNotification(string.format("Jumped to history: %d / %d", sel.id, max))
      end
    end
  end,
  key = "Ctrl-Alt-h",
  priority = 1,
}

------------------------------------------------------------
-- Click History: Page Picker Implementation
------------------------------------------------------------

command.define {
  name = "Click History: Page Picker",
  run = function()
    local Ctimes = getTimes()
    local max = Ctimes - 1
    
    if max < 1 then
      editor.flashNotification("No click history found.", "warning")
      return
    end

    local historyItems, seen = {}, {}
    for i = max, 1, -1 do
      local ref = getRef(i)
      if ref then
        local pageName, pos = ref:match("^(.*)@(%d+)$")

        if not seen[pageName] then
          seen[pageName] = true
          local displayName = ref
          local tstr = getTimeString(i) or ""
          
          if pageName and tstr then
            displayName = string.format("%d 🖱️ %s", i, pageName)
          else
            displayName = string.format("%d. %s", i, ref)
          end
  
          table.insert(historyItems, {
            id = i,
            name = displayName,
            description = tstr .. " 📍 " .. pos,
            ref = ref
          })
        end
      end
    end

    local sel = editor.filterBox(
      "Back to",
      historyItems,
      "Select a Click History...",
      "Page @ Pos where you Once Clicked"
    )

    if sel then
      local b = ensureBrowseSession()
      b.index = sel.id
      setBrowse(b)
      if navigateIndex(sel.id) then
        editor.flashNotification(string.format("Jumped to history: %d / %d", sel.id, max))
      end
    end
  end,
  key = "Shift-Alt-h",
  priority = 1,
}
``