🧩 Plug-in: “Paste: Smart URL”

Smarter URL is Ready

command.define {
  name = "Paste: Smart URL (via Prompt)",
  key = "Alt-v",
  run = function()
    -- Ask the user to paste the URL into a prompt dialog
    local input = editor.prompt("Enter or paste URL", "")
    if not input then
      editor.flashNotification("Cancelled", "warn")
      return
    end

    -- Trim whitespace
    local clip = input:match("^%s*(.-)%s*$")
    if clip == "" then
      editor.flashNotification("Empty content", "warn")
      return
    end

    -- Basic URL check: http/https, www., or data:image/
    local function isUrl(u)
      return u:match("^https?://")
          or u:match("^www%.")
          or u:match("^data:image/")
    end

    -- Add scheme for bare www.
    local function ensureScheme(u)
      if u:match("^www%.") then return "https://" .. u end
      return u
    end

    -- Image URL check: ignore ? / #; also allow data:image/
    local function isImageUrl(u)
      if u:match("^data:image/") then return true end
      local path = (u:match("^[^%?#]+") or u):lower()
      return path:match("%.png$") or path:match("%.jpe?g$") or
             path:match("%.gif$") or path:match("%.webp$") or
             path:match("%.bmp$") or path:match("%.tiff?$") or
             path:match("%.svg$")
    end

    if not isUrl(clip) then
      editor.flashNotification("Not a URL", "warn")
      return
    end

    -- Helpers for title/tags
    local function urldecode(s)
      s = s:gsub("%+", " ")
      return (s:gsub("%%(%x%x)", function(h)
        local n = tonumber(h, 16)
        return n and string.char(n) or ("%%" .. h)
      end))
    end

    local function trim(s)
      return (s and s:match("^%s*(.-)%s*$")) or s
    end

    local function is_numeric(s) return s:match("^%d+$") ~= nil end

    local TLD_IGNORE = {
      com=true, org=true, net=true, io=true, md=true, app=true, dev=true, edu=true, gov=true,
      cn=true, uk=true, co=true, jp=true, de=true, fr=true, ru=true, nl=true, xyz=true,
      info=true, me=true, tv=true, cc=true, ai=true, us=true, ca=true, au=true, ["in"]=true,
      site=true, top=true, cloud=true, shop=true, blog=true,
      www=true  -- also ignore www label
    }

    local function split(str, pat)
      local t = {}
      str:gsub("([^" .. pat .. "]+)", function(c) t[#t+1] = c end)
      return t
    end

    local function parse_host(u)
      -- extract host from URL
      -- 1) Drop scheme
      local no_scheme = u:gsub("^[a-zA-Z][a-zA-Z0-9+.-]*://", "")
      -- 2) Stop at first / or ? or #
      local host = no_scheme:match("^([^/%?#]+)") or ""
      return host:lower()
    end

    local function build_tags_from_host(host)
      local parts = split(host, "%.")
      local out = {}
      local seen = {}
      for _, p in ipairs(parts) do
        local label = p:lower()
        if not TLD_IGNORE[label] and not seen[label] and label ~= "" then
          out[#out+1] = "#" .. label
          seen[label] = true
        end
      end
      return table.concat(out, " ")
    end

    local function last_non_numeric_segment(path_parts)
      for i = #path_parts, 1, -1 do
        local seg = path_parts[i]
        if seg and seg ~= "" and not is_numeric(seg) then
          return seg
        end
      end
      return nil
    end

    -- title_from_url:
    local function title_from_url(u)
      local path = (u:match("^https?://[^/%?#]+(/[^?#]*)")
                 or u:match("^www%.[^/%?#]+(/[^?#]*)")
                 or "") or ""
      local parts = {}
      for seg in path:gmatch("([^/]+)") do
        parts[#parts+1] = seg
      end
      local slug = last_non_numeric_segment(parts)

      if slug then
        slug = urldecode(slug or "")
        slug = slug:gsub("%-", " ")
        slug = trim((slug:gsub("%s+", " ")))
      else
        slug = ""
      end

      return slug
    end

    local url = ensureScheme(clip)

    -- Case 1: images -> keep original behavior
    if isImageUrl(url) then
      local snippet = string.format("![](%s)", url)

      -- Remember insertion position (selection-aware), insert, then move cursor inside []
      local sel = editor.getSelection and editor.getSelection() or nil
      local startPos = (sel and (sel.from or sel.start)) or editor.getCursor()
      editor.insertAtCursor(snippet, false)

      local targetPos = startPos + 2 -- "![](...)" -> '[' is the 2nd character
      if editor.moveCursor then
        editor.moveCursor(targetPos, false)
      elseif editor.setSelection then
        editor.setSelection(targetPos, targetPos)
      end
      editor.flashNotification("Inserted smart image link")
      return
    end

    -- Case 2: web URL -> build [title](url) + tags (highest priority for non-image)
    local host = parse_host(url)
    local tags = build_tags_from_host(host)       -- e.g. "#community #silverbullet" or "#tex #stackexchange"
    local title = title_from_url(url)

    local suffix = (tags ~= "" and (" " .. tags)) or ""
    local snippet = string.format("[%s](%s)%s", title, url, suffix)

    -- Remember insertion position (selection-aware), insert
    local sel = editor.getSelection and editor.getSelection() or nil
    local startPos = (sel and (sel.from or sel.start)) or editor.getCursor()
    editor.insertAtCursor(snippet, false)

    local function is_nonempty(s) return s and trim(s) ~= "" end
    local targetPos
    if is_nonempty(title) then
      targetPos = startPos + #snippet
    else
      targetPos = startPos + 1 + #title
    end

    if editor.moveCursor then
      editor.moveCursor(targetPos, false)
    elseif editor.setSelection then
      editor.setSelection(targetPos, targetPos)
    end

    editor.flashNotification("Inserted titled link with tags")
  end
}

@ my space/CONFIG/Paste as/Markdown/Image Url

2 Likes