initial commit

This commit is contained in:
2026-02-18 19:31:33 -05:00
commit 60c2fd8739
8 changed files with 1369 additions and 0 deletions

76
lua/glaze/colors.lua Normal file
View File

@@ -0,0 +1,76 @@
---@brief [[
--- glaze.nvim color definitions
--- Charmbracelet-inspired palette with Lazy.nvim structure
---@brief ]]
local M = {}
-- Charmbracelet-inspired colors (pink/magenta theme)
M.colors = {
-- Headers and accents
H1 = { fg = "#FF6AD5", bold = true }, -- Charm pink
H2 = { fg = "#C4A7E7", bold = true }, -- Soft purple
Title = { fg = "#FF6AD5", bold = true },
-- Status indicators
Done = { fg = "#9FFFCB" }, -- Mint green (success)
Running = { fg = "#FFD866" }, -- Warm yellow (in progress)
Pending = { fg = "#6E6A86" }, -- Muted gray
Error = { fg = "#FF6B6B", bold = true }, -- Soft red
-- Content
Normal = "NormalFloat",
Binary = { fg = "#FF6AD5" }, -- Charm pink for binary names
Url = { fg = "#7DCFFF", italic = true }, -- Bright blue for URLs
Plugin = { fg = "#BB9AF7" }, -- Purple for plugin names
Comment = "Comment",
Dimmed = "Conceal",
-- Progress bar
ProgressDone = { fg = "#FF6AD5" }, -- Charm pink
ProgressTodo = { fg = "#3B3A52" }, -- Dark purple-gray
-- UI elements
Border = { fg = "#FF6AD5" },
Button = "CursorLine",
ButtonActive = { bg = "#FF6AD5", fg = "#1A1B26", bold = true },
Key = { fg = "#FFD866", bold = true }, -- Keybind highlights
-- Version info
Version = { fg = "#9FFFCB" },
Time = { fg = "#6E6A86", italic = true },
-- Icons
Icon = { fg = "#FF6AD5" },
IconDone = { fg = "#9FFFCB" },
IconError = { fg = "#FF6B6B" },
IconRunning = { fg = "#FFD866" },
Bold = { bold = true },
Italic = { italic = true },
}
M.did_setup = false
function M.set_hl()
for name, def in pairs(M.colors) do
local hl = type(def) == "table" and def or { link = def }
hl.default = true
vim.api.nvim_set_hl(0, "Glaze" .. name, hl)
end
end
function M.setup()
if M.did_setup then
return
end
M.did_setup = true
M.set_hl()
vim.api.nvim_create_autocmd("ColorScheme", {
callback = M.set_hl,
})
end
return M

190
lua/glaze/float.lua Normal file
View File

@@ -0,0 +1,190 @@
---@brief [[
--- glaze.nvim floating window (based on lazy.nvim)
--- Creates centered floating window with backdrop
---@brief ]]
local M = {}
---@class GlazeFloatOptions
---@field size? { width: number, height: number }
---@field border? string
---@field title? string
---@field zindex? number
---@class GlazeFloat
---@field buf number
---@field win number
---@field opts GlazeFloatOptions
---@field win_opts table
---@field backdrop_buf? number
---@field backdrop_win? number
local Float = {}
local _id = 0
---@param opts? GlazeFloatOptions
---@return GlazeFloat
function Float.new(opts)
local self = setmetatable({}, { __index = Float })
return self:init(opts)
end
---@param opts? GlazeFloatOptions
function Float:init(opts)
require("glaze.colors").setup()
_id = _id + 1
self.id = _id
local config = require("glaze").config
self.opts = vim.tbl_deep_extend("force", {
size = config.ui.size,
border = config.ui.border,
zindex = 50,
}, opts or {})
self.win_opts = {
relative = "editor",
style = "minimal",
border = self.opts.border,
zindex = self.opts.zindex,
title = self.opts.title,
title_pos = self.opts.title and "center" or nil,
}
self:mount()
return self
end
function Float:layout()
local function size(max, value)
return value > 1 and math.min(value, max) or math.floor(max * value)
end
self.win_opts.width = size(vim.o.columns, self.opts.size.width)
self.win_opts.height = size(vim.o.lines - 4, self.opts.size.height)
self.win_opts.row = math.floor((vim.o.lines - self.win_opts.height) / 2)
self.win_opts.col = math.floor((vim.o.columns - self.win_opts.width) / 2)
if self.opts.border ~= "none" then
self.win_opts.row = self.win_opts.row - 1
self.win_opts.col = self.win_opts.col - 1
end
end
function Float:mount()
self.buf = vim.api.nvim_create_buf(false, true)
-- Create backdrop
local normal = vim.api.nvim_get_hl(0, { name = "Normal" })
if normal.bg and vim.o.termguicolors then
self.backdrop_buf = vim.api.nvim_create_buf(false, true)
self.backdrop_win = vim.api.nvim_open_win(self.backdrop_buf, false, {
relative = "editor",
width = vim.o.columns,
height = vim.o.lines,
row = 0,
col = 0,
style = "minimal",
focusable = false,
zindex = self.opts.zindex - 1,
})
vim.api.nvim_set_hl(0, "GlazeBackdrop", { bg = "#000000", default = true })
vim.wo[self.backdrop_win].winhighlight = "Normal:GlazeBackdrop"
vim.wo[self.backdrop_win].winblend = 60
vim.bo[self.backdrop_buf].buftype = "nofile"
end
self:layout()
self.win = vim.api.nvim_open_win(self.buf, true, self.win_opts)
-- Buffer settings
vim.bo[self.buf].buftype = "nofile"
vim.bo[self.buf].bufhidden = "wipe"
vim.bo[self.buf].filetype = "glaze"
-- Window settings
vim.wo[self.win].conceallevel = 3
vim.wo[self.win].foldenable = false
vim.wo[self.win].spell = false
vim.wo[self.win].wrap = true
vim.wo[self.win].winhighlight = "Normal:GlazeNormal,FloatBorder:GlazeBorder"
vim.wo[self.win].cursorline = true
-- Keymaps
self:map("q", function()
self:close()
end, "Close")
self:map("<Esc>", function()
self:close()
end, "Close")
-- Auto-close on WinClosed
vim.api.nvim_create_autocmd("WinClosed", {
pattern = tostring(self.win),
once = true,
callback = function()
self:close()
end,
})
-- Handle resize
vim.api.nvim_create_autocmd("VimResized", {
callback = function()
if not self:valid() then
return true
end
self:layout()
vim.api.nvim_win_set_config(self.win, {
relative = "editor",
width = self.win_opts.width,
height = self.win_opts.height,
row = self.win_opts.row,
col = self.win_opts.col,
})
if self.backdrop_win and vim.api.nvim_win_is_valid(self.backdrop_win) then
vim.api.nvim_win_set_config(self.backdrop_win, {
width = vim.o.columns,
height = vim.o.lines,
})
end
end,
})
end
---@param key string
---@param fn function
---@param desc string
function Float:map(key, fn, desc)
vim.keymap.set("n", key, fn, { buffer = self.buf, nowait = true, desc = desc })
end
function Float:valid()
return self.win and vim.api.nvim_win_is_valid(self.win)
end
function Float:close()
vim.schedule(function()
if self.backdrop_win and vim.api.nvim_win_is_valid(self.backdrop_win) then
vim.api.nvim_win_close(self.backdrop_win, true)
end
if self.backdrop_buf and vim.api.nvim_buf_is_valid(self.backdrop_buf) then
vim.api.nvim_buf_delete(self.backdrop_buf, { force = true })
end
if self.win and vim.api.nvim_win_is_valid(self.win) then
vim.api.nvim_win_close(self.win, true)
end
if self.buf and vim.api.nvim_buf_is_valid(self.buf) then
vim.api.nvim_buf_delete(self.buf, { force = true })
end
end)
end
---@return number
function Float:width()
return self.win_opts.width
end
M.Float = Float
return M

148
lua/glaze/init.lua Normal file
View File

@@ -0,0 +1,148 @@
---@brief [[
--- glaze.nvim - Centralized Go binary management for Neovim plugins
---
--- A Mason/Lazy-style interface for managing Go binaries across multiple plugins.
--- Register your plugin's binaries once, update them all with a single command.
---
--- Usage:
--- require("glaze").setup({})
--- require("glaze").register("freeze", "github.com/charmbracelet/freeze")
--- require("glaze").register("glow", "github.com/charmbracelet/glow")
---@brief ]]
local M = {}
---@class GlazeBinary
---@field name string Binary name (executable name)
---@field url string Go module URL (without @version)
---@field plugin? string Plugin that registered this binary
---@field callback? fun(success: boolean) Optional callback after install/update
---@class GlazeConfig
---@field ui GlazeUIConfig
---@field concurrency number Max parallel installations
---@field go_cmd string[] Go command (supports goenv)
---@class GlazeUIConfig
---@field border string Border style
---@field size { width: number, height: number }
---@field icons GlazeIcons
---@class GlazeIcons
---@field pending string
---@field running string
---@field done string
---@field error string
---@field binary string
---@type GlazeConfig
M.config = {
ui = {
border = "rounded",
size = { width = 0.7, height = 0.8 },
icons = {
pending = "",
running = "",
done = "",
error = "",
binary = "󰆍",
},
},
concurrency = 4,
go_cmd = { "go" },
}
---@type table<string, GlazeBinary>
M._binaries = {}
---@type number?
M._ns = nil
---@param opts? GlazeConfig
function M.setup(opts)
M.config = vim.tbl_deep_extend("force", M.config, opts or {})
M._ns = vim.api.nvim_create_namespace("glaze")
-- Auto-detect goenv
if vim.fn.executable("goenv") == 1 then
M.config.go_cmd = { "goenv", "exec", "go" }
end
-- Create commands
vim.api.nvim_create_user_command("Glaze", function()
require("glaze.view").open()
end, { desc = "Open Glaze UI" })
vim.api.nvim_create_user_command("GlazeUpdate", function(cmd)
if cmd.args and cmd.args ~= "" then
require("glaze.runner").update({ cmd.args })
else
require("glaze.runner").update_all()
end
end, {
desc = "Update Go binaries",
nargs = "?",
complete = function()
return vim.tbl_keys(M._binaries)
end,
})
vim.api.nvim_create_user_command("GlazeInstall", function(cmd)
if cmd.args and cmd.args ~= "" then
require("glaze.runner").install({ cmd.args })
else
require("glaze.runner").install_missing()
end
end, {
desc = "Install Go binaries",
nargs = "?",
complete = function()
return vim.tbl_keys(M._binaries)
end,
})
end
---Register a binary for management.
---@param name string Binary/executable name
---@param url string Go module URL (e.g., "github.com/charmbracelet/freeze")
---@param opts? { plugin?: string, callback?: fun(success: boolean) }
function M.register(name, url, opts)
opts = opts or {}
M._binaries[name] = {
name = name,
url = url,
plugin = opts.plugin,
callback = opts.callback,
}
end
---Unregister a binary.
---@param name string Binary name to unregister
function M.unregister(name)
M._binaries[name] = nil
end
---Get all registered binaries.
---@return table<string, GlazeBinary>
function M.binaries()
return M._binaries
end
---Check if a binary is installed.
---@param name string Binary name
---@return boolean
function M.is_installed(name)
return vim.fn.executable(name) == 1
end
---Get binary installation status.
---@param name string Binary name
---@return "installed"|"missing"|"unknown"
function M.status(name)
if not M._binaries[name] then
return "unknown"
end
return M.is_installed(name) and "installed" or "missing"
end
return M

242
lua/glaze/runner.lua Normal file
View File

@@ -0,0 +1,242 @@
---@brief [[
--- glaze.nvim task runner
--- Handles parallel Go binary installation/updates
---@brief ]]
local M = {}
---@class GlazeTask
---@field binary GlazeBinary
---@field status "pending"|"running"|"done"|"error"
---@field output string[]
---@field start_time? number
---@field end_time? number
---@field job_id? number
---@type GlazeTask[]
M._tasks = {}
---@type function?
M._on_update = nil
---@type boolean
M._running = false
---Get all current tasks.
---@return GlazeTask[]
function M.tasks()
return M._tasks
end
---Check if runner is active.
---@return boolean
function M.is_running()
return M._running
end
---Set update callback (called when task status changes).
---@param fn function
function M.on_update(fn)
M._on_update = fn
end
---@private
function M._notify()
if M._on_update then
vim.schedule(M._on_update)
end
end
---@param binary GlazeBinary
---@return GlazeTask
local function create_task(binary)
return {
binary = binary,
status = "pending",
output = {},
}
end
---@param task GlazeTask
local function run_task(task)
local glaze = require("glaze")
local cmd = vim.list_extend({}, glaze.config.go_cmd)
table.insert(cmd, "install")
table.insert(cmd, task.binary.url .. "@latest")
task.status = "running"
task.start_time = vim.uv.hrtime()
task.output = {}
M._notify()
task.job_id = vim.fn.jobstart(cmd, {
on_stdout = function(_, data)
if data then
for _, line in ipairs(data) do
if line ~= "" then
table.insert(task.output, line)
end
end
end
end,
on_stderr = function(_, data)
if data then
for _, line in ipairs(data) do
if line ~= "" then
table.insert(task.output, line)
end
end
end
end,
on_exit = function(_, code)
task.end_time = vim.uv.hrtime()
task.status = code == 0 and "done" or "error"
task.job_id = nil
-- Call binary callback if set
if task.binary.callback then
vim.schedule(function()
task.binary.callback(code == 0)
end)
end
M._notify()
M._process_queue()
end,
})
end
---@private
function M._process_queue()
local glaze = require("glaze")
local running = 0
local pending = {}
for _, task in ipairs(M._tasks) do
if task.status == "running" then
running = running + 1
elseif task.status == "pending" then
table.insert(pending, task)
end
end
-- Start pending tasks up to concurrency limit
local to_start = math.min(#pending, glaze.config.concurrency - running)
for i = 1, to_start do
run_task(pending[i])
end
-- Check if all done
if running == 0 and #pending == 0 then
M._running = false
M._notify()
end
end
---Run tasks for specified binaries.
---@param names string[]
---@param mode "install"|"update"
local function run(names, mode)
local glaze = require("glaze")
-- Check for Go
local go_check = glaze.config.go_cmd[1]
if vim.fn.executable(go_check) ~= 1 then
vim.notify("Go is not installed. Please install Go first: https://go.dev/dl/", vim.log.levels.ERROR)
return
end
-- Filter binaries
local binaries = {}
for _, name in ipairs(names) do
local binary = glaze._binaries[name]
if binary then
if mode == "install" and glaze.is_installed(name) then
-- Skip already installed
else
table.insert(binaries, binary)
end
else
vim.notify("Unknown binary: " .. name, vim.log.levels.WARN)
end
end
if #binaries == 0 then
if mode == "install" then
vim.notify("All binaries already installed", vim.log.levels.INFO)
end
return
end
-- Create tasks
M._tasks = {}
for _, binary in ipairs(binaries) do
table.insert(M._tasks, create_task(binary))
end
M._running = true
M._notify()
M._process_queue()
-- Open UI
require("glaze.view").open()
end
---Update specific binaries.
---@param names string[]
function M.update(names)
run(names, "update")
end
---Update all registered binaries.
function M.update_all()
local glaze = require("glaze")
run(vim.tbl_keys(glaze._binaries), "update")
end
---Install specific binaries.
---@param names string[]
function M.install(names)
run(names, "install")
end
---Install all missing binaries.
function M.install_missing()
local glaze = require("glaze")
local missing = {}
for name, _ in pairs(glaze._binaries) do
if not glaze.is_installed(name) then
table.insert(missing, name)
end
end
if #missing > 0 then
run(missing, "install")
else
vim.notify("All binaries already installed", vim.log.levels.INFO)
end
end
---Abort all running tasks.
function M.abort()
for _, task in ipairs(M._tasks) do
if task.job_id then
vim.fn.jobstop(task.job_id)
task.status = "error"
table.insert(task.output, "Aborted by user")
end
end
M._running = false
M._notify()
end
---Get task statistics.
---@return { total: number, done: number, error: number, running: number, pending: number }
function M.stats()
local stats = { total = #M._tasks, done = 0, error = 0, running = 0, pending = 0 }
for _, task in ipairs(M._tasks) do
stats[task.status] = (stats[task.status] or 0) + 1
end
return stats
end
return M

132
lua/glaze/text.lua Normal file
View File

@@ -0,0 +1,132 @@
---@brief [[
--- glaze.nvim text rendering (based on lazy.nvim)
--- Handles buffered text with highlight segments
---@brief ]]
local M = {}
---@class GlazeTextSegment
---@field str string
---@field hl? string|GlazeExtmark
---@class GlazeExtmark
---@field hl_group? string
---@field col? number
---@field end_col? number
---@field virt_text? table
---@field virt_text_win_col? number
---@class GlazeText
---@field _lines GlazeTextSegment[][]
---@field padding number
---@field wrap number
local Text = {}
function Text.new()
local self = setmetatable({}, { __index = Text })
self._lines = {}
self.padding = 2
self.wrap = 80
return self
end
---@param str string
---@param hl? string|GlazeExtmark
---@param opts? { indent?: number, wrap?: boolean }
---@return GlazeText
function Text:append(str, hl, opts)
opts = opts or {}
if #self._lines == 0 then
self:nl()
end
local lines = vim.split(str, "\n")
for i, line in ipairs(lines) do
if opts.indent then
line = string.rep(" ", opts.indent) .. line
end
if i > 1 then
self:nl()
end
-- Handle wrap
if opts.wrap and str ~= "" and self:col() > 0 and self:col() + vim.fn.strwidth(line) + self.padding > self.wrap then
self:nl()
end
table.insert(self._lines[#self._lines], { str = line, hl = hl })
end
return self
end
---@return GlazeText
function Text:nl()
table.insert(self._lines, {})
return self
end
---@return number
function Text:row()
return #self._lines == 0 and 1 or #self._lines
end
---@return number
function Text:col()
if #self._lines == 0 then
return 0
end
local width = 0
for _, segment in ipairs(self._lines[#self._lines]) do
width = width + vim.fn.strwidth(segment.str)
end
return width
end
function Text:trim()
while #self._lines > 0 and #self._lines[#self._lines] == 0 do
table.remove(self._lines)
end
end
---@param buf number
---@param ns number
function Text:render(buf, ns)
local lines = {}
for _, line in ipairs(self._lines) do
local str = string.rep(" ", self.padding)
for _, segment in ipairs(line) do
str = str .. segment.str
end
if str:match("^%s*$") then
str = ""
end
table.insert(lines, str)
end
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
for l, line in ipairs(self._lines) do
if lines[l] ~= "" then
local col = self.padding
for _, segment in ipairs(line) do
local width = vim.fn.strwidth(segment.str)
local extmark = segment.hl
if extmark then
if type(extmark) == "string" then
extmark = { hl_group = extmark, end_col = col + width }
end
---@cast extmark GlazeExtmark
local extmark_col = extmark.col or col
extmark.col = nil
pcall(vim.api.nvim_buf_set_extmark, buf, ns, l - 1, extmark_col, extmark)
end
col = col + width
end
end
end
end
M.Text = Text
return M

264
lua/glaze/view.lua Normal file
View File

@@ -0,0 +1,264 @@
---@brief [[
--- glaze.nvim view/UI
--- Lazy.nvim-style floating window with Charmbracelet aesthetic
---@brief ]]
local M = {}
---@type GlazeFloat?
M._float = nil
---@type number?
M._timer = nil
local SPINNERS = { "", "", "", "" }
local SPINNER_IDX = 1
---Open the Glaze UI.
function M.open()
local Float = require("glaze.float").Float
if M._float and M._float:valid() then
M._float:close()
end
M._float = Float.new({
title = " 󱓞 Glaze ",
})
-- Set up keymaps
M._float:map("u", function()
require("glaze.runner").update_all()
end, "Update all")
M._float:map("i", function()
require("glaze.runner").install_missing()
end, "Install missing")
M._float:map("x", function()
require("glaze.runner").abort()
end, "Abort")
M._float:map("<CR>", function()
M._toggle_details()
end, "Toggle details")
-- Subscribe to runner updates
require("glaze.runner").on_update(function()
M.render()
end)
-- Start update timer for spinner
M._start_timer()
M.render()
end
---@private
function M._start_timer()
if M._timer then
vim.fn.timer_stop(M._timer)
end
M._timer = vim.fn.timer_start(100, function()
if not M._float or not M._float:valid() then
if M._timer then
vim.fn.timer_stop(M._timer)
M._timer = nil
end
return
end
local runner = require("glaze.runner")
if runner.is_running() then
SPINNER_IDX = (SPINNER_IDX % #SPINNERS) + 1
vim.schedule(function()
M.render()
end)
end
end, { ["repeat"] = -1 })
end
---@private
function M._toggle_details()
-- TODO: Implement detail expansion
end
---Render the UI.
function M.render()
if not M._float or not M._float:valid() then
return
end
local glaze = require("glaze")
local runner = require("glaze.runner")
local Text = require("glaze.text").Text
local text = Text.new()
text.wrap = M._float:width() - 4
local icons = glaze.config.ui.icons
-- Header
text:nl()
text:append(" ", "GlazeIcon"):append("Glaze", "GlazeH1"):append(" Go Binary Manager", "GlazeComment"):nl()
text:nl()
-- Stats / Progress
local stats = runner.stats()
if stats.total > 0 then
M._render_progress(text, stats)
text:nl()
end
-- Keybinds
text:append(" ", nil, { indent = 2 })
text:append(" u ", "GlazeButtonActive"):append(" Update ", "GlazeButton")
text:append(" ")
text:append(" i ", "GlazeButtonActive"):append(" Install ", "GlazeButton")
text:append(" ")
text:append(" x ", "GlazeButtonActive"):append(" Abort ", "GlazeButton")
text:append(" ")
text:append(" q ", "GlazeButtonActive"):append(" Close ", "GlazeButton")
text:nl():nl()
-- Tasks section (if running)
if stats.total > 0 then
text:append("Tasks", "GlazeH2"):append(" (" .. stats.done .. "/" .. stats.total .. ")", "GlazeComment"):nl()
text:nl()
for _, task in ipairs(runner.tasks()) do
M._render_task(text, task, icons)
end
text:nl()
end
-- Registered binaries
local binaries = glaze.binaries()
local binary_count = vim.tbl_count(binaries)
text:append("Binaries", "GlazeH2"):append(" (" .. binary_count .. ")", "GlazeComment"):nl()
text:nl()
if binary_count == 0 then
text:append("No binaries registered yet.", "GlazeComment", { indent = 4 }):nl()
text:append("Use ", "GlazeComment", { indent = 4 })
text:append('require("glaze").register(name, url)', "GlazeUrl")
text:append(" to add binaries.", "GlazeComment"):nl()
else
-- Sort by name
local sorted = {}
for name, binary in pairs(binaries) do
table.insert(sorted, { name = name, binary = binary })
end
table.sort(sorted, function(a, b)
return a.name < b.name
end)
for _, item in ipairs(sorted) do
M._render_binary(text, item.binary, icons)
end
end
text:trim()
-- Render to buffer
vim.bo[M._float.buf].modifiable = true
text:render(M._float.buf, glaze._ns)
vim.bo[M._float.buf].modifiable = false
end
---@param text GlazeText
---@param stats table
---@private
function M._render_progress(text, stats)
if not M._float then
return
end
local width = M._float:width() - 6
local done_ratio = stats.total > 0 and (stats.done / stats.total) or 0
local done_width = math.floor(done_ratio * width + 0.5)
if stats.done < stats.total then
text:append("", {
virt_text_win_col = 2,
virt_text = { { string.rep("", done_width), "GlazeProgressDone" } },
})
text:append("", {
virt_text_win_col = 2 + done_width,
virt_text = { { string.rep("", width - done_width), "GlazeProgressTodo" } },
})
end
end
---@param text GlazeText
---@param task GlazeTask
---@param icons GlazeIcons
---@private
function M._render_task(text, task, icons)
local icon, icon_hl
if task.status == "done" then
icon, icon_hl = icons.done, "GlazeIconDone"
elseif task.status == "error" then
icon, icon_hl = icons.error, "GlazeIconError"
elseif task.status == "running" then
icon, icon_hl = SPINNERS[SPINNER_IDX], "GlazeIconRunning"
else
icon, icon_hl = icons.pending, "GlazePending"
end
text:append(" " .. icon .. " ", icon_hl)
text:append(task.binary.name, "GlazeBinary")
-- Time taken
if task.end_time and task.start_time then
local ms = (task.end_time - task.start_time) / 1e6
text:append(string.format(" %.0fms", ms), "GlazeTime")
end
text:nl()
-- Show output for errors or running tasks
if task.status == "error" and #task.output > 0 then
for _, line in ipairs(task.output) do
text:append(line, "GlazeError", { indent = 6 }):nl()
end
end
end
---@param text GlazeText
---@param binary GlazeBinary
---@param icons GlazeIcons
---@private
function M._render_binary(text, binary, icons)
local glaze = require("glaze")
local installed = glaze.is_installed(binary.name)
local icon = installed and icons.done or icons.pending
local icon_hl = installed and "GlazeIconDone" or "GlazePending"
text:append(" " .. icon .. " ", icon_hl)
text:append(binary.name, "GlazeBinary")
if binary.plugin then
text:append(" (" .. binary.plugin .. ")", "GlazePlugin")
end
text:nl()
text:append(binary.url, "GlazeUrl", { indent = 6 }):nl()
end
---Close the UI.
function M.close()
if M._timer then
vim.fn.timer_stop(M._timer)
M._timer = nil
end
if M._float then
M._float:close()
M._float = nil
end
end
return M