large update

This commit is contained in:
2026-02-18 23:21:33 -05:00
parent 7ab9da9c56
commit ded73a5073
20 changed files with 784 additions and 185 deletions

18
.editorconfig Normal file
View File

@@ -0,0 +1,18 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.lua]
indent_size = 2
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Git LFS tracking
docs/*.gif filter=lfs diff=lfs merge=lfs -text

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: taigrr # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]

21
.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
# Logs
*.log
# OS files
.DS_Store
Thumbs.db
# Editor files
*.swp
*.swo
*~
.idea/
.vscode/
# Neovim
.nvim.lua
.nvimrc
.exrc
# Test artifacts
.tests/

15
.luarc.json Normal file
View File

@@ -0,0 +1,15 @@
{
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
"runtime": {
"version": "LuaJIT"
},
"diagnostics": {
"globals": ["vim"]
},
"workspace": {
"library": [
"${3rd}/luv/library"
],
"checkThirdParty": false
}
}

188
AGENTS.md Normal file
View File

@@ -0,0 +1,188 @@
# AGENTS.md
AI agent guide for working in glaze.nvim.
## Project Overview
**glaze.nvim** is a Neovim plugin that provides centralized management for Go binaries used by other Neovim plugins. Think "Mason for Go binaries" with a lazy.nvim-style UI.
- **Language**: Lua (Neovim plugin)
- **Requirements**: Neovim >= 0.9, Go installed
- **Author**: Tai Groot (taigrr)
## Directory Structure
```
lua/glaze/
├── init.lua # Main module: setup, config, registration API, binary path utilities
├── runner.lua # Task runner: parallel go install with concurrency control
├── checker.lua # Update checker: version comparison, auto-check scheduling
├── view.lua # UI: lazy.nvim-style floating window, keybinds, rendering
├── float.lua # Float window abstraction: backdrop, keymaps, resize handling
├── text.lua # Text rendering: buffered text with highlight segments
├── colors.lua # Highlight definitions: doughnut-inspired pink/magenta theme
├── health.lua # Health check: :checkhealth glaze
doc/
└── glaze.txt # Vim help documentation
```
## Key Concepts
### Binary Registration
Plugins register binaries via `require("glaze").register(name, url, opts)`. Registration stores metadata in `M._binaries` table. Auto-install triggers if enabled and binary is missing.
### Task Runner
`runner.lua` manages parallel `go install` jobs with configurable concurrency. Tasks have states: `pending → running → done|error`. The runner opens the UI automatically when tasks start.
### Update Checking
`checker.lua` compares installed versions (`go version -m`) against latest (`go list -m -json`). State persisted to `vim.fn.stdpath("data") .. "/glaze/state.json"`.
### UI Architecture
- `float.lua`: Generic floating window with backdrop (inspired by lazy.nvim)
- `view.lua`: Glaze-specific rendering, keybinds, spinner animation
- `text.lua`: Buffered text builder with highlight segments and extmarks
## Commands
| Command | Description |
| ---------------------- | ---------------------------------- |
| `:Glaze` | Open the UI |
| `:GlazeUpdate [name]` | Update all or specific binary |
| `:GlazeInstall [name]` | Install missing or specific binary |
| `:GlazeCheck` | Manual update check |
| `:checkhealth glaze` | Run health check |
## Testing
**No automated tests exist.** Manual testing workflow:
```lua
-- In Neovim:
:luafile % -- Reload current file
:Glaze -- Test UI
:checkhealth glaze -- Verify setup
```
Test with actual binaries:
```lua
require("glaze").setup({})
require("glaze").register("glow", "github.com/charmbracelet/glow")
:GlazeInstall glow
```
## Code Conventions
### Module Pattern
All modules return a table `M` with public functions. Private functions prefixed with `_`:
```lua
local M = {}
M._private_state = {}
function M.public_fn() end
function M._private_fn() end
return M
```
### Type Annotations
Uses LuaCATS (`---@class`, `---@param`, `---@return`) for type hints. LSP warnings about `vim` being undefined are expected (Neovim globals).
### Async Patterns
- `vim.fn.jobstart()` for external commands
- `vim.schedule()` to defer to main loop
- `vim.defer_fn()` for delayed execution
### UI Updates
- Runner notifies via `M._on_update` callback
- View subscribes and re-renders on notification
- Timer drives spinner animation (100ms interval)
## Highlight Groups
All highlights prefixed with `Glaze`. Key groups:
- `GlazeH1`, `GlazeH2` - Headers
- `GlazeBinary`, `GlazeUrl`, `GlazePlugin` - Content
- `GlazeDone`, `GlazeError`, `GlazeRunning` - Status
- `GlazeProgressDone`, `GlazeProgressTodo` - Progress bar
Colors follow doughnut aesthetic (pink `#FF6AD5` as primary accent).
## Configuration
Default config in `init.lua:53-78`. Key options:
- `concurrency`: Parallel jobs (default 4)
- `auto_install.enabled`: Install on register
- `auto_check.enabled/frequency`: Update checking
- `auto_update.enabled`: Auto-apply updates
## Common Tasks
### Adding a New Config Option
1. Add to `GlazeConfig` type definition in `init.lua`
2. Add default value in `M.config`
3. Document in `README.md` and `doc/glaze.txt`
### Adding a New Command
1. Create in `M.setup()` via `vim.api.nvim_create_user_command()`
2. Document in README and help file
### Modifying UI
1. Edit `view.lua:render()` for content changes
2. Edit `view.lua:open()` to add keybinds
3. Edit `colors.lua` for new highlight groups
### Adding Health Checks
Add checks in `health.lua:check()` using `vim.health.ok/warn/error/info`.
## Gotchas
1. **vim global**: All `vim.*` calls show LSP warnings - this is normal for Neovim plugins without proper Lua LSP config.
2. **Race conditions**: `runner.lua` rejects new tasks if already running. Check `runner.is_running()` first.
3. **Timer cleanup**: `view.lua._timer` must be stopped on close, or spinner keeps running.
4. **State file**: `checker.lua` persists to data dir. If corrupt, delete `~/.local/share/nvim/glaze/state.json`.
5. **GOBIN detection**: Checks multiple locations: `$GOBIN`, `$GOPATH/bin`, `~/go/bin`. See `init.lua:is_installed()`.
6. **goenv support**: Auto-detected in setup, changes `go_cmd` to `{ "goenv", "exec", "go" }`.
## API Reference
Main module (`require("glaze")`):
- `setup(opts)` - Initialize with config
- `register(name, url, opts)` - Register binary
- `unregister(name)` - Remove binary
- `binaries()` - Get all registered
- `is_installed(name)` - Check if binary exists
- `bin_path(name)` - Get full path
- `status(name)` - Get "installed"/"missing"/"unknown"
Runner (`require("glaze.runner")`):
- `update(names)` / `update_all()` - Update binaries
- `install(names)` / `install_missing()` - Install binaries
- `abort()` - Stop all tasks
- `is_running()` / `tasks()` / `stats()` - Query state
Checker (`require("glaze.checker")`):
- `check(opts)` - Check for updates
- `auto_check()` - Check if interval passed
- `get_update_info()` - Get cached version info

29
LICENSE
View File

@@ -1,21 +1,14 @@
MIT License
BSD Zero Clause License
Copyright (c) 2026 Tai Groot
Copyright (c) 2025 Tai Groot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

11
Makefile Normal file
View File

@@ -0,0 +1,11 @@
.PHONY: demo clean
PLUGIN_PATH := $(shell pwd)
demo:
@echo "Recording demo.gif..."
PLUGIN_PATH=$(PLUGIN_PATH) vhs docs/demo.tape
@echo "Done! Output: docs/demo.gif"
clean:
rm -rf /tmp/vhs-glaze-config /tmp/vhs-glaze-data /tmp/glaze-demo

234
README.md
View File

@@ -1,123 +1,164 @@
# 🍩 glaze.nvim
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/taigrr/glaze.nvim/raw/master/docs/glaze-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/taigrr/glaze.nvim/raw/master/docs/glaze-light.svg">
<img alt="glaze.nvim" src="https://github.com/taigrr/glaze.nvim/raw/master/docs/glaze-dark.svg" width="400">
</picture>
> **Go + Lazy = Glaze** — A Mason/Lazy-style manager for Go binaries in Neovim.
> Charmbracelet-inspired aesthetic. Zero duplication. One source of truth.
>
> *Like a fresh doughnut glaze — smooth, sweet, and holds everything together.*
<p>
<a href="https://github.com/taigrr/glaze.nvim/releases/latest">
<img alt="Latest release" src="https://img.shields.io/github/v/release/taigrr/glaze.nvim?style=for-the-badge&logo=starship&color=FF6AD5&logoColor=D9E0EE&labelColor=302D41&include_prerelease&sort=semver">
</a>
<a href="https://github.com/taigrr/glaze.nvim/pulse">
<img alt="Last commit" src="https://img.shields.io/github/last-commit/taigrr/glaze.nvim?style=for-the-badge&logo=starship&color=8bd5ca&logoColor=D9E0EE&labelColor=302D41">
</a>
<a href="https://github.com/taigrr/glaze.nvim/blob/master/LICENSE">
<img alt="License" src="https://img.shields.io/github/license/taigrr/glaze.nvim?style=for-the-badge&logo=starship&color=ee999f&logoColor=D9E0EE&labelColor=302D41">
</a>
<a href="https://github.com/taigrr/glaze.nvim/stargazers">
<img alt="Stars" src="https://img.shields.io/github/stars/taigrr/glaze.nvim?style=for-the-badge&logo=starship&color=c69ff5&logoColor=D9E0EE&labelColor=302D41">
</a>
</p>
![Lua](https://img.shields.io/badge/Lua-2C2D72?style=flat&logo=lua&logoColor=white)
![Neovim](https://img.shields.io/badge/Neovim%200.9+-57A143?style=flat&logo=neovim&logoColor=white)
![Go Required](https://img.shields.io/badge/Go-required-00ADD8?style=flat&logo=go&logoColor=white)
![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
**Go + Lazy = Glaze** — A centralized manager for Go binaries in Neovim.
## 🤔 Why Glaze?
<img alt="glaze.nvim demo" src="https://github.com/taigrr/glaze.nvim/raw/master/docs/demo.gif" width="700">
Every Go-based Neovim plugin reinvents the wheel: each one ships its own
`go install` wrapper, its own update command, its own version checking.
## The Problem
**Glaze stops the madness.** Register your binaries once, manage them all from
one beautiful UI. Plugin authors get a two-line integration. Users get a single
`:Glaze` command.
Every Go-based Neovim plugin reinvents the wheel:
- **freeze.nvim** needs `freeze` → ships its own installer
- **glow.nvim** needs `glow` → ships its own installer
- **mods.nvim** needs `mods` → ships its own installer
Each plugin implements `go install`, update checking, and version management from scratch.
Users run different update commands for each plugin. Plugin authors duplicate code.
## The Solution
**Glaze** provides a single source of truth for Go binaries:
```lua
-- Plugin authors: two lines
local ok, glaze = pcall(require, "glaze")
if ok then glaze.register("freeze", "github.com/charmbracelet/freeze") end
-- Users: one command
:Glaze
```
## ✨ Features
- **Centralized binary management** — Register binaries from any plugin, update them all at once
- **Lazy.nvim-style UI** — Floating window with progress bars, spinners, and status indicators
- **Cursor-aware keybinds** — `u` updates the binary under your cursor, `U` updates all
- **Parallel installations** — Configurable concurrency for fast updates
- **Auto-update checking** — Daily/weekly checks with non-intrusive notifications
- **GOBIN/GOPATH awareness** — Finds binaries even if not in PATH
- **Detail expansion** — Press `<CR>` to see URL, install path, version info
- **Charmbracelet aesthetic** — Pink/magenta color scheme that matches the Charm toolchain
- **Zero config for dependents** — Just register and go
- **Callback support** — Get notified when your binary is updated
- 📦 **Centralized management** — Register binaries from any plugin, manage from one UI
- 🚀 **Parallel installations** — Configurable concurrency for fast updates
- 🎯 **Cursor-aware keybinds**`u` updates binary under cursor, `U` updates all
- 🔄 **Auto-update checking**Daily/weekly checks with non-intrusive notifications
- 📍 **Smart binary detection**Finds binaries in PATH, GOBIN, and GOPATH
- 🎨 **Sugary-sweet aesthetic**Pink/magenta theme reminding you of doughnuts...
- 🔔 **Callback support**Get notified when your binary is updated
- **Zero config for plugins** — Register and go
## 📦 Installation
Using [lazy.nvim](https://github.com/folke/lazy.nvim):
### [lazy.nvim](https://github.com/folke/lazy.nvim)
```lua
{
"taigrr/glaze.nvim",
config = function()
require("glaze").setup({})
require("glaze").setup()
end,
}
```
### [packer.nvim](https://github.com/wbthomason/packer.nvim)
```lua
use {
"taigrr/glaze.nvim",
config = function()
require("glaze").setup()
end,
}
```
## ⚡ Requirements
- Neovim >= **0.9.0**
- Go >= **1.18** (for `go install` support)
- Optional: [goenv](https://github.com/syndbg/goenv) (auto-detected)
## 🚀 Quick Start
```lua
local glaze = require("glaze")
-- Setup (usually in your plugin config)
glaze.setup({})
-- Setup
glaze.setup()
-- Register binaries
glaze.register("freeze", "github.com/charmbracelet/freeze")
glaze.register("glow", "github.com/charmbracelet/glow")
glaze.register("mods", "github.com/charmbracelet/mods")
glaze.register("gum", "github.com/charmbracelet/gum")
-- Open the UI
vim.cmd("Glaze")
```
## 📖 Usage
### Commands
## 📖 Commands
| Command | Description |
|---------|-------------|
| ---------------------- | ------------------------------------ |
| `:Glaze` | Open the Glaze UI |
| `:GlazeUpdate [name]` | Update all or specific binary |
| `:GlazeInstall [name]` | Install missing or specific binary |
| `:GlazeCheck` | Manually check for available updates |
### Keybinds (in Glaze UI)
## ⌨️ Keybinds
| Key | Action |
|-----|--------|
| `U` | Update ALL binaries |
| ------------- | ---------------------------- |
| `U` | Update all binaries |
| `u` | Update binary under cursor |
| `I` | Install all missing binaries |
| `i` | Install binary under cursor |
| `x` | Abort running tasks |
| `<CR>` | Toggle details (URL, path, version) |
| `<CR>` | Toggle details |
| `q` / `<Esc>` | Close window |
## 🔌 For Plugin Authors
Register your plugin's binaries as a dependency — two lines is all it takes:
Make your plugin a Glaze consumer with two lines:
```lua
-- In your plugin's setup or init:
-- In your plugin's setup:
local ok, glaze = pcall(require, "glaze")
if ok then
glaze.register("mytool", "github.com/me/mytool", {
plugin = "myplugin.nvim",
plugin = "myplugin.nvim", -- Shows in UI
callback = function(success)
if success then
vim.notify("mytool updated!")
end
if success then vim.notify("mytool updated!") end
end,
})
end
```
### Providing Update Commands
### Example Consumers
You can still expose plugin-specific commands that delegate to Glaze:
These plugins use Glaze to manage their Go binaries:
```lua
vim.api.nvim_create_user_command("MyPluginUpdate", function()
require("glaze.runner").update({ "mytool" })
end, {})
```
- [**neocrush.nvim**](https://github.com/taigrr/neocrush.nvim) — AI-powered coding assistant
- [**freeze.nvim**](https://github.com/taigrr/freeze.nvim) — Screenshot code with freeze
- [**blast.nvim**](https://github.com/taigrr/blast.nvim) — Code activity tracking
## ⚙️ Configuration
```lua
require("glaze").setup({
-- UI settings
ui = {
border = "rounded", -- "none", "single", "double", "rounded", "solid", "shadow"
size = { width = 0.7, height = 0.8 }, -- Percentage of screen
size = { width = 0.7, height = 0.8 },
icons = {
pending = "",
running = "",
@@ -125,40 +166,34 @@ require("glaze").setup({
error = "",
binary = "󰆍",
},
use_system_theming = false, -- Use nvim theme instead of doughnut colors
},
concurrency = 4, -- Max parallel installations
go_cmd = { "go" }, -- Auto-detects goenv if available
-- Parallel installations
concurrency = 4,
-- Go command (auto-detects goenv)
go_cmd = { "go" },
-- Auto-install missing binaries on register
auto_install = {
enabled = true, -- Auto-install missing binaries on register
silent = false, -- Suppress install notifications
enabled = true,
silent = false,
},
-- Auto-check for updates
auto_check = {
enabled = true, -- Auto-check for updates (disabling also disables auto_update)
enabled = true,
frequency = "daily", -- "daily", "weekly", or hours as number
},
-- Auto-update when newer versions found
auto_update = {
enabled = false, -- Auto-update binaries when newer versions are found (requires auto_check)
enabled = false, -- Requires auto_check
},
})
```
## 🎨 Highlight Groups
Glaze defines these highlight groups (all prefixed with `Glaze`):
| Group | Description |
|-------|-------------|
| `GlazeH1` | Main title (pink) |
| `GlazeH2` | Section headers |
| `GlazeBinary` | Binary names |
| `GlazeUrl` | Module URLs |
| `GlazePlugin` | Plugin names |
| `GlazeDone` | Success status |
| `GlazeError` | Error status |
| `GlazeRunning` | In-progress status |
| `GlazeProgressDone` | Progress bar (filled) |
| `GlazeProgressTodo` | Progress bar (empty) |
## 📋 API
```lua
@@ -170,36 +205,53 @@ glaze.unregister(name) -- Remove a binary
glaze.binaries() -- Get all registered binaries
-- Status
glaze.is_installed(name) -- Check if binary exists (PATH + GOBIN + GOPATH)
glaze.is_installed(name) -- Check if binary exists
glaze.bin_path(name) -- Get full path to binary
glaze.status(name) -- "installed", "missing", or "unknown"
-- Runner (for programmatic control)
-- Runner
local runner = require("glaze.runner")
runner.update({ "freeze", "glow" }) -- Update specific binaries
runner.update({ "freeze" }) -- Update specific binaries
runner.update_all() -- Update all
runner.install({ "freeze" }) -- Install specific
runner.install_missing() -- Install all missing
runner.abort() -- Stop all tasks
runner.is_running() -- Check if tasks are running
runner.tasks() -- Get current task list
runner.stats() -- Get { total, done, error, running, pending }
-- Update checker
-- Checker
local checker = require("glaze.checker")
checker.check() -- Check for updates (with notifications)
checker.auto_check() -- Check only if enough time has passed
checker.check() -- Check for updates
checker.get_update_info() -- Get cached update info
```
## 🎨 Highlight Groups
| Group | Description |
| ------------------- | --------------------- |
| `GlazeH1` | Main title |
| `GlazeH2` | Section headers |
| `GlazeBinary` | Binary names |
| `GlazeUrl` | Module URLs |
| `GlazePlugin` | Plugin names |
| `GlazeDone` | Success status |
| `GlazeError` | Error status |
| `GlazeRunning` | In-progress status |
| `GlazeProgressDone` | Progress bar (filled) |
| `GlazeProgressTodo` | Progress bar (empty) |
## 🩺 Health Check
```vim
:checkhealth glaze
```
Verifies Go installation, GOBIN configuration, and registered binary status.
## 🤝 Related Projects
- [freeze.nvim](https://github.com/taigrr/freeze.nvim) — Screenshot code with freeze
- [neocrush.nvim](https://github.com/taigrr/neocrush.nvim) — AI-powered coding assistant
- [blast.nvim](https://github.com/taigrr/blast.nvim) — Code activity tracking
- [lazy.nvim](https://github.com/folke/lazy.nvim) — UI inspiration
- [mason.nvim](https://github.com/williamboman/mason.nvim) — Concept inspiration
Glaze is inspired by:
- [lazy.nvim](https://github.com/folke/lazy.nvim) — UI patterns and aesthetics
- [mason.nvim](https://github.com/williamboman/mason.nvim) — Centralized tool management concept
## 📄 License
MIT © [Tai Groot](https://github.com/taigrr)
[0BSD](LICENSE) © [Tai Groot](https://github.com/taigrr)

View File

@@ -1,7 +1,7 @@
*glaze.txt* 🍩 Centralized Go binary management for Neovim plugins
Author: Tai Groot <tai@taigrr.com>
License: MIT
License: 0BSD
Homepage: https://github.com/taigrr/glaze.nvim
==============================================================================
@@ -124,6 +124,7 @@ Runner API (require("glaze.runner")):
runner.is_running() Check if tasks are running
runner.tasks() Get current task list
runner.stats() Get task statistics
runner.on_update({fn}) Register callback for task state changes
*glaze-checker-api*
Checker API (require("glaze.checker")):
@@ -148,6 +149,7 @@ Default configuration:
error = "✗",
binary = "󰆍",
},
use_system_theming = false, -- Use nvim theme instead of doughnut colors
},
concurrency = 4,
go_cmd = { "go" }, -- Auto-detects goenv
@@ -168,20 +170,37 @@ Default configuration:
==============================================================================
7. HIGHLIGHTS *glaze-highlights*
All highlight groups are prefixed with "Glaze":
All highlight groups are prefixed with "Glaze". When `use_system_theming` is
false (default), a doughnut-themed pink/magenta colors are used.
Group Description ~
GlazeH1 Main title
GlazeH2 Section headers
GlazeTitle Window title
GlazeBinary Binary names
GlazeUrl Module URLs
GlazeUrl Module URLs (italic)
GlazePlugin Plugin names
GlazeDone Success status
GlazeError Error status
GlazeRunning In-progress status
GlazePending Pending status
GlazeProgressDone Progress bar (filled)
GlazeProgressTodo Progress bar (empty)
GlazeVersion Version info
GlazeTime Timing info
GlazeTime Timing info (italic)
GlazeNormal Normal text (links to NormalFloat)
GlazeComment Comments (links to Comment)
GlazeDimmed Dimmed text (links to Conceal)
GlazeBorder Window border
GlazeButton Inactive button (links to CursorLine)
GlazeButtonActive Active/selected button
GlazeKey Keybind hints
GlazeIcon Default icon color
GlazeIconDone Done icon
GlazeIconError Error icon
GlazeIconRunning Running/spinner icon
GlazeBold Bold text
GlazeItalic Italic text
==============================================================================
8. AUTO-UPDATE CHECKING *glaze-auto-check*

3
docs/demo.gif Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1e33f21d35c9b869528d908207e8d66fd5770d1ff5570ab487ef3f13081348a7
size 89918

86
docs/demo.tape Normal file
View File

@@ -0,0 +1,86 @@
# glaze.nvim Demo
# Records the main UI and update flow
#
# Requirements:
# - vhs (brew install vhs)
# - nvim >= 0.9
# - go installed
#
# Run: vhs docs/demo.tape
Output docs/demo.gif
Require nvim
Require go
Set Shell bash
Set FontSize 14
Set Width 1000
Set Height 600
Set Theme "Catppuccin Mocha"
Set WindowBar Colorful
Set Padding 15
Set TypingSpeed 30ms
Set CursorBlink false
# Hidden setup - isolate config and set up environment
Hide
Type "export XDG_CONFIG_HOME=/tmp/vhs-glaze-config"
Enter
Type "export XDG_DATA_HOME=/tmp/vhs-glaze-data"
Enter
Type "mkdir -p /tmp/vhs-glaze-config /tmp/vhs-glaze-data /tmp/glaze-demo && cd /tmp/glaze-demo"
Enter
Sleep 200ms
# Create empty file to open
Type "touch demo.lua"
Enter
Sleep 200ms
Type "clear"
Enter
Sleep 200ms
# Start nvim with minimal config (PLUGIN_PATH set by Makefile or manually)
Type `nvim --clean -u $PLUGIN_PATH/scripts/vhs_init.lua --cmd "set rtp+=$PLUGIN_PATH" demo.lua`
Enter
Sleep 2s
Show
# Open Glaze UI
Type ":Glaze"
Enter
Sleep 2s
# Let the UI fully render before navigating
Sleep 500ms
# Navigate down through binaries
Type "j"
Sleep 300ms
Type "j"
Sleep 300ms
# Expand details on current binary
Enter
Sleep 1.2s
# Collapse details
Enter
Sleep 600ms
# Update ALL binaries with U
Type "U"
Sleep 10s
# Show the completed state
Sleep 2s
# Close and quit
Hide
Type "q"
Sleep 300ms
Type ":qa!"
Enter
Sleep 300ms

36
docs/glaze-dark.svg Normal file
View File

@@ -0,0 +1,36 @@
<svg width="400" height="100" viewBox="0 0 400 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="glazeGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#FF6AD5"/>
<stop offset="100%" style="stop-color:#C774E8"/>
</linearGradient>
</defs>
<!-- Donut icon -->
<g transform="translate(20, 15)">
<!-- Outer ring -->
<circle cx="35" cy="35" r="32" fill="url(#glazeGradient)"/>
<!-- Inner hole -->
<circle cx="35" cy="35" r="14" fill="#1E1E2E"/>
<!-- Glaze drips -->
<ellipse cx="20" cy="58" rx="6" ry="10" fill="url(#glazeGradient)"/>
<ellipse cx="35" cy="62" rx="5" ry="8" fill="url(#glazeGradient)"/>
<ellipse cx="50" cy="58" rx="6" ry="10" fill="url(#glazeGradient)"/>
<!-- Sprinkles -->
<rect x="22" y="20" width="8" height="3" rx="1" fill="#A6E3A1" transform="rotate(-30 26 21.5)"/>
<rect x="45" y="18" width="8" height="3" rx="1" fill="#89B4FA" transform="rotate(20 49 19.5)"/>
<rect x="38" y="38" width="8" height="3" rx="1" fill="#F9E2AF" transform="rotate(-10 42 39.5)"/>
<rect x="18" y="40" width="8" height="3" rx="1" fill="#F38BA8" transform="rotate(40 22 41.5)"/>
<rect x="50" y="35" width="8" height="3" rx="1" fill="#94E2D5" transform="rotate(-45 54 36.5)"/>
</g>
<!-- Text -->
<text x="105" y="62" font-family="JetBrains Mono, SF Mono, Consolas, monospace" font-size="48" font-weight="bold" fill="#CDD6F4">
glaze<tspan fill="#6C7086">.nvim</tspan>
</text>
<!-- Tagline -->
<text x="105" y="85" font-family="JetBrains Mono, SF Mono, Consolas, monospace" font-size="14" fill="#6C7086">
Go binary manager for Neovim
</text>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

36
docs/glaze-light.svg Normal file
View File

@@ -0,0 +1,36 @@
<svg width="400" height="100" viewBox="0 0 400 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="glazeGradientLight" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#D946EF"/>
<stop offset="100%" style="stop-color:#A855F7"/>
</linearGradient>
</defs>
<!-- Donut icon -->
<g transform="translate(20, 15)">
<!-- Outer ring -->
<circle cx="35" cy="35" r="32" fill="url(#glazeGradientLight)"/>
<!-- Inner hole -->
<circle cx="35" cy="35" r="14" fill="#FAFAFA"/>
<!-- Glaze drips -->
<ellipse cx="20" cy="58" rx="6" ry="10" fill="url(#glazeGradientLight)"/>
<ellipse cx="35" cy="62" rx="5" ry="8" fill="url(#glazeGradientLight)"/>
<ellipse cx="50" cy="58" rx="6" ry="10" fill="url(#glazeGradientLight)"/>
<!-- Sprinkles -->
<rect x="22" y="20" width="8" height="3" rx="1" fill="#22C55E" transform="rotate(-30 26 21.5)"/>
<rect x="45" y="18" width="8" height="3" rx="1" fill="#3B82F6" transform="rotate(20 49 19.5)"/>
<rect x="38" y="38" width="8" height="3" rx="1" fill="#EAB308" transform="rotate(-10 42 39.5)"/>
<rect x="18" y="40" width="8" height="3" rx="1" fill="#EF4444" transform="rotate(40 22 41.5)"/>
<rect x="50" y="35" width="8" height="3" rx="1" fill="#14B8A6" transform="rotate(-45 54 36.5)"/>
</g>
<!-- Text -->
<text x="105" y="62" font-family="JetBrains Mono, SF Mono, Consolas, monospace" font-size="48" font-weight="bold" fill="#1E293B">
glaze<tspan fill="#64748B">.nvim</tspan>
</text>
<!-- Tagline -->
<text x="105" y="85" font-family="JetBrains Mono, SF Mono, Consolas, monospace" font-size="14" fill="#64748B">
Go binary manager for Neovim
</text>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -152,7 +152,7 @@ function M.check(opts)
local remaining = 0
local updates_found = 0
for name, binary in pairs(binaries) do
for name, _ in pairs(binaries) do
remaining = remaining + 2 -- installed version + latest version
local info = {
@@ -216,7 +216,10 @@ function M.check(opts)
elseif not opts.silent then
if updates_found > 0 then
vim.schedule(function()
vim.notify("Glaze: " .. updates_found .. " update(s) available — run :GlazeUpdate", vim.log.levels.INFO)
vim.notify(
"Glaze: " .. updates_found .. " update(s) available — run :GlazeUpdate",
vim.log.levels.INFO
)
end)
else
vim.schedule(function()

View File

@@ -1,50 +1,110 @@
---@brief [[
--- glaze.nvim color definitions
--- Charmbracelet-inspired palette with Lazy.nvim structure
--- Doughnut-inspired palette with Lazy.nvim structure
--- When use_system_theming is enabled, uses standard nvim highlight groups instead
---@brief ]]
local M = {}
-- Charmbracelet-inspired colors (pink/magenta theme)
-- Doughnut-inspired palette
local palette = {
frosting = "#FF6AD5", -- Primary pink accent
lavender = "#C4A7E7", -- Soft purple
mint = "#9FFFCB", -- Success green
honey = "#FFD866", -- Warning yellow
coral = "#FF6B6B", -- Error red
sky = "#7DCFFF", -- URL blue
grape = "#BB9AF7", -- Plugin purple
ash = "#6E6A86", -- Muted gray
shadow = "#3B3A52", -- Dark purple-gray
ink = "#1A1B26", -- Background dark
}
-- Doughnut-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 },
H1 = { fg = palette.frosting, bold = true },
H2 = { fg = palette.lavender, bold = true },
Title = { fg = palette.frosting, 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
Done = { fg = palette.mint },
Running = { fg = palette.honey },
Pending = { fg = palette.ash },
Error = { fg = palette.coral, bold = true },
-- 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
Binary = { fg = palette.frosting },
Url = { fg = palette.sky, italic = true },
Plugin = { fg = palette.grape },
Comment = "Comment",
Dimmed = "Conceal",
-- Progress bar
ProgressDone = { fg = "#FF6AD5" }, -- Charm pink
ProgressTodo = { fg = "#3B3A52" }, -- Dark purple-gray
ProgressDone = { fg = palette.frosting },
ProgressTodo = { fg = palette.shadow },
-- UI elements
Border = { fg = "#FF6AD5" },
Border = { fg = palette.frosting },
Button = "CursorLine",
ButtonActive = { bg = "#FF6AD5", fg = "#1A1B26", bold = true },
Key = { fg = "#FFD866", bold = true }, -- Keybind highlights
ButtonActive = { bg = palette.frosting, fg = palette.ink, bold = true },
Key = { fg = palette.honey, bold = true },
-- Version info
Version = { fg = "#9FFFCB" },
Time = { fg = "#6E6A86", italic = true },
Version = { fg = palette.mint },
Time = { fg = palette.ash, italic = true },
-- Icons
Icon = { fg = "#FF6AD5" },
IconDone = { fg = "#9FFFCB" },
IconError = { fg = "#FF6B6B" },
IconRunning = { fg = "#FFD866" },
Icon = { fg = palette.frosting },
IconDone = { fg = palette.mint },
IconError = { fg = palette.coral },
IconRunning = { fg = palette.honey },
Bold = { bold = true },
Italic = { italic = true },
}
-- System theme colors (links to standard nvim groups)
M.system_colors = {
-- Headers and accents
H1 = "Title",
H2 = "Title",
Title = "Title",
-- Status indicators
Done = "DiagnosticOk",
Running = "DiagnosticWarn",
Pending = "Comment",
Error = "DiagnosticError",
-- Content
Normal = "NormalFloat",
Binary = "Identifier",
Url = "Underlined",
Plugin = "Special",
Comment = "Comment",
Dimmed = "Conceal",
-- Progress bar
ProgressDone = "DiagnosticOk",
ProgressTodo = "Comment",
-- UI elements
Border = "FloatBorder",
Button = "CursorLine",
ButtonActive = "PmenuSel",
Key = "SpecialKey",
-- Version info
Version = "DiagnosticOk",
Time = "Comment",
-- Icons
Icon = "Special",
IconDone = "DiagnosticOk",
IconError = "DiagnosticError",
IconRunning = "DiagnosticWarn",
Bold = { bold = true },
Italic = { italic = true },
@@ -53,7 +113,10 @@ M.colors = {
M.did_setup = false
function M.set_hl()
for name, def in pairs(M.colors) do
local glaze = require("glaze")
local colors = glaze.config.ui.use_system_theming and M.system_colors or M.colors
for name, def in pairs(colors) do
local hl = type(def) == "table" and def or { link = def }
hl.default = true
vim.api.nvim_set_hl(0, "Glaze" .. name, hl)

View File

@@ -54,7 +54,7 @@ function M.check()
vim.health.ok("GOBIN in PATH: " .. gobin)
else
vim.health.warn("GOBIN exists but is not in PATH: " .. gobin, {
"Add to PATH: export PATH=\"" .. gobin .. ":$PATH\"",
'Add to PATH: export PATH="' .. gobin .. ':$PATH"',
"Binaries installed by Glaze may not be found without this",
})
end

View File

@@ -19,35 +19,36 @@ local M = {}
---@field callback? fun(success: boolean) Optional callback after install/update
---@class GlazeAutoCheckConfig
---@field enabled boolean Whether to auto-check for updates
---@field frequency string|number Frequency: "daily", "weekly", or hours as number
---@field enabled? boolean Whether to auto-check for updates
---@field frequency? string|number Frequency: "daily", "weekly", or hours as number
---@class GlazeAutoInstallConfig
---@field enabled boolean Whether to auto-install missing binaries on register
---@field silent boolean Suppress notifications during auto-install
---@field enabled? boolean Whether to auto-install missing binaries on register
---@field silent? boolean Suppress notifications during auto-install
---@class GlazeAutoUpdateConfig
---@field enabled boolean Whether to auto-update binaries when newer versions are found (requires auto_check)
---@field enabled? boolean Whether to auto-update binaries when newer versions are found (requires auto_check)
---@class GlazeConfig
---@field ui GlazeUIConfig
---@field concurrency number Max parallel installations
---@field go_cmd string[] Go command (supports goenv)
---@field auto_install GlazeAutoInstallConfig
---@field auto_check GlazeAutoCheckConfig
---@field auto_update GlazeAutoUpdateConfig
---@field ui? GlazeUIConfig
---@field concurrency? number Max parallel installations
---@field go_cmd? string[] Go command (supports goenv)
---@field auto_install? GlazeAutoInstallConfig
---@field auto_check? GlazeAutoCheckConfig
---@field auto_update? GlazeAutoUpdateConfig
---@class GlazeUIConfig
---@field border string Border style
---@field size { width: number, height: number }
---@field icons GlazeIcons
---@field border? string Border style
---@field size? { width: number, height: number }
---@field icons? GlazeIcons
---@field use_system_theming? boolean Use nvim highlight groups instead of doughnut colors
---@class GlazeIcons
---@field pending string
---@field running string
---@field done string
---@field error string
---@field binary string
---@field pending? string
---@field running? string
---@field done? string
---@field error? string
---@field binary? string
---@type GlazeConfig
M.config = {
@@ -61,6 +62,7 @@ M.config = {
error = "",
binary = "󰆍",
},
use_system_theming = false,
},
concurrency = 4,
go_cmd = { "go" },

View File

@@ -1,6 +1,6 @@
---@brief [[
--- glaze.nvim view/UI
--- Lazy.nvim-style floating window with Charmbracelet aesthetic
--- Lazy.nvim-style floating window with doughnut aesthetic
---@brief ]]
local M = {}
@@ -32,13 +32,16 @@ function M._get_cursor_binary()
end
---Open the Glaze UI.
---If already open, just re-render without recreating the window.
function M.open()
local Float = require("glaze.float").Float
-- If window is already open and valid, just render and return
if M._float and M._float:valid() then
M._float:close()
M.render()
return
end
local Float = require("glaze.float").Float
M._float = Float.new({
title = " 🍩 Glaze ",
})
@@ -190,17 +193,16 @@ function M.render()
local binaries = glaze.binaries()
local binary_count = vim.tbl_count(binaries)
-- Count updates available
-- Count updates available (only binaries with has_update = true)
local updates_available = 0
local update_info = checker.get_update_info()
for _ in pairs(update_info) do
for _, info in pairs(update_info) do
if info.has_update then
updates_available = updates_available + 1
end
end
local header_suffix = " (" .. binary_count .. ")"
if updates_available > 0 then
header_suffix = header_suffix
end
text:append("Binaries", "GlazeH2"):append(header_suffix, "GlazeComment")
if updates_available > 0 then
text:append(" " .. updates_available .. " update(s) available", "GlazeRunning")

46
scripts/vhs_init.lua Normal file
View File

@@ -0,0 +1,46 @@
-- Minimal init.lua for VHS recordings
-- Loads only glaze.nvim with no colorscheme or other plugins
-- Usage: nvim --clean -u /path/to/vhs_init.lua --cmd "set rtp+=/path/to/glaze.nvim"
-- Basic settings for clean recording
vim.o.number = true
vim.o.relativenumber = false
vim.o.signcolumn = "yes"
vim.o.termguicolors = true
vim.o.showmode = false
vim.o.ruler = false
vim.o.laststatus = 2
vim.o.cmdheight = 1
vim.o.updatetime = 100
vim.o.timeoutlen = 300
vim.o.swapfile = false
vim.o.backup = false
vim.o.writebackup = false
vim.o.autoread = true
-- Disable intro message and other noise
vim.opt.shortmess:append("I")
vim.opt.shortmess:append("c")
-- Simple statusline
vim.o.statusline = " %f %m%=%l:%c "
-- Set leader key
vim.g.mapleader = " "
-- Source the plugin file (--clean doesn't auto-load plugin/ dir)
vim.cmd("runtime! plugin/glaze.lua")
-- Load glaze with some demo binaries pre-registered
local glaze = require("glaze")
glaze.setup({
auto_install = { enabled = false },
auto_check = { enabled = false },
})
-- Register some binaries for the demo
glaze.register("freeze", "github.com/charmbracelet/freeze")
glaze.register("glow", "github.com/charmbracelet/glow")
glaze.register("gum", "github.com/charmbracelet/gum")
glaze.register("mods", "github.com/charmbracelet/mods")
glaze.register("vhs", "github.com/charmbracelet/vhs")