mirror of
https://github.com/taigrr/glaze.nvim.git
synced 2026-04-02 03:09:10 -07:00
Compare commits
9 Commits
v0.0.1
...
cd/bugfixe
| Author | SHA1 | Date | |
|---|---|---|---|
| 67255b346c | |||
| 93acbc55c5 | |||
| d94fee916b | |||
| ea73ed51b1 | |||
| 5867abd592 | |||
| ded73a5073 | |||
| 7ab9da9c56 | |||
| c905edc162 | |||
| 6927b0b914 |
18
.editorconfig
Normal file
18
.editorconfig
Normal 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
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Git LFS tracking
|
||||
docs/*.gif filter=lfs diff=lfs merge=lfs -text
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal 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
21
.gitignore
vendored
Normal 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
15
.luarc.json
Normal 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
188
AGENTS.md
Normal 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
|
||||
27
LICENSE
27
LICENSE
@@ -1,21 +1,14 @@
|
||||
MIT License
|
||||
BSD Zero Clause License
|
||||
|
||||
Copyright (c) 2026 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
11
Makefile
Normal 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
|
||||
267
README.md
267
README.md
@@ -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&sort=semver&include_prerelease">
|
||||
</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&labelColor=302D41&logoColor=D9E0EE">
|
||||
</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&labelColor=302D41&logoColor=D9E0EE">
|
||||
</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&labelColor=302D41&logoColor=D9E0EE">
|
||||
</a>
|
||||
</p>
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
**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 |
|
||||
|
||||
| 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
|
||||
|
||||
### Keybinds (in Glaze UI)
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `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) |
|
||||
| `q` / `<Esc>` | Close window |
|
||||
| Key | Action |
|
||||
| ------------- | ---------------------------- |
|
||||
| `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 |
|
||||
| `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,33 +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,
|
||||
silent = false,
|
||||
},
|
||||
|
||||
-- Auto-check for updates
|
||||
auto_check = {
|
||||
enabled = true, -- Auto-check for updates
|
||||
frequency = "daily", -- "daily", "weekly", or hours as number
|
||||
enabled = true,
|
||||
frequency = "daily", -- "daily", "weekly", or hours as number
|
||||
},
|
||||
|
||||
-- Auto-update when newer versions found
|
||||
auto_update = {
|
||||
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
|
||||
@@ -163,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_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 }
|
||||
runner.update({ "freeze" }) -- Update specific binaries
|
||||
runner.update_all() -- Update all
|
||||
runner.install_missing() -- Install all missing
|
||||
runner.abort() -- Stop all tasks
|
||||
|
||||
-- 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.get_update_info() -- Get cached update info
|
||||
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)
|
||||
|
||||
@@ -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,33 +149,58 @@ Default configuration:
|
||||
error = "✗",
|
||||
binary = "",
|
||||
},
|
||||
use_system_theming = false, -- Use nvim theme instead of doughnut colors
|
||||
},
|
||||
concurrency = 4,
|
||||
go_cmd = { "go" }, -- Auto-detects goenv
|
||||
auto_install = {
|
||||
enabled = true, -- Auto-install missing binaries on register
|
||||
silent = false, -- Suppress install notifications
|
||||
},
|
||||
auto_check = {
|
||||
enabled = true, -- Auto-check for updates on setup
|
||||
enabled = true, -- Auto-check for updates (disabling also disables auto_update)
|
||||
frequency = "daily", -- "daily", "weekly", or hours (number)
|
||||
},
|
||||
auto_update = {
|
||||
enabled = false, -- Auto-update when newer versions found (requires auto_check)
|
||||
},
|
||||
})
|
||||
<
|
||||
|
||||
==============================================================================
|
||||
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
3
docs/demo.gif
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1e33f21d35c9b869528d908207e8d66fd5770d1ff5570ab487ef3f13081348a7
|
||||
size 89918
|
||||
86
docs/demo.tape
Normal file
86
docs/demo.tape
Normal 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
36
docs/glaze-dark.svg
Normal 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
36
docs/glaze-light.svg
Normal 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 |
@@ -127,6 +127,60 @@ function M.get_update_info()
|
||||
return M._update_info
|
||||
end
|
||||
|
||||
---Refresh version info for a single binary (called after install/update).
|
||||
---@param name string Binary name
|
||||
---@param callback? fun() Optional callback when done
|
||||
function M.refresh_version(name, callback)
|
||||
local glaze = require("glaze")
|
||||
local binary = glaze._binaries[name]
|
||||
if not binary then
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
get_installed_version(name, function(installed)
|
||||
local info = M._update_info[name] or {
|
||||
name = name,
|
||||
installed_version = nil,
|
||||
latest_version = nil,
|
||||
has_update = false,
|
||||
}
|
||||
info.installed_version = installed
|
||||
|
||||
-- If we have latest version cached, check if still needs update
|
||||
if info.latest_version and installed then
|
||||
info.has_update = installed ~= info.latest_version
|
||||
else
|
||||
info.has_update = false
|
||||
end
|
||||
|
||||
M._update_info[name] = info
|
||||
|
||||
-- Persist to state
|
||||
local state = read_state()
|
||||
state.update_info = state.update_info or {}
|
||||
state.update_info[name] = {
|
||||
installed_version = info.installed_version,
|
||||
latest_version = info.latest_version,
|
||||
has_update = info.has_update,
|
||||
}
|
||||
write_state(state)
|
||||
|
||||
-- Refresh UI if open
|
||||
vim.schedule(function()
|
||||
local ok, view = pcall(require, "glaze.view")
|
||||
if ok and view._float and view._float:valid() then
|
||||
view.render()
|
||||
end
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
---Check for updates on all registered binaries.
|
||||
---@param opts? { silent?: boolean }
|
||||
function M.check(opts)
|
||||
@@ -149,12 +203,9 @@ function M.check(opts)
|
||||
end
|
||||
|
||||
M._checking = true
|
||||
local remaining = 0
|
||||
local updates_found = 0
|
||||
|
||||
local remaining = vim.tbl_count(binaries)
|
||||
for name, binary in pairs(binaries) do
|
||||
remaining = remaining + 2 -- installed version + latest version
|
||||
|
||||
local info = {
|
||||
name = name,
|
||||
installed_version = nil,
|
||||
@@ -163,16 +214,6 @@ function M.check(opts)
|
||||
}
|
||||
M._update_info[name] = info
|
||||
|
||||
get_installed_version(name, function()
|
||||
-- callback receives version from the jobstart; re-check via closure
|
||||
end)
|
||||
end
|
||||
|
||||
-- Simplified: check each binary sequentially-ish
|
||||
remaining = vim.tbl_count(binaries)
|
||||
for name, binary in pairs(binaries) do
|
||||
local info = M._update_info[name]
|
||||
|
||||
get_installed_version(name, function(installed)
|
||||
info.installed_version = installed
|
||||
|
||||
@@ -201,10 +242,25 @@ function M.check(opts)
|
||||
end
|
||||
write_state(state)
|
||||
|
||||
if not opts.silent then
|
||||
-- Auto-update if enabled (requires auto_check to be enabled)
|
||||
if updates_found > 0 and glaze.config.auto_update.enabled and glaze.config.auto_check.enabled then
|
||||
vim.schedule(function()
|
||||
local to_update = {}
|
||||
for n, i in pairs(M._update_info) do
|
||||
if i.has_update then
|
||||
table.insert(to_update, n)
|
||||
end
|
||||
end
|
||||
vim.notify("Glaze: auto-updating " .. #to_update .. " binary(ies)…", vim.log.levels.INFO)
|
||||
require("glaze.runner").update(to_update)
|
||||
end)
|
||||
elseif not opts.silent then
|
||||
if updates_found > 0 then
|
||||
vim.schedule(function()
|
||||
vim.notify("Glaze: " .. updates_found .. " update(s) available", vim.log.levels.INFO)
|
||||
vim.notify(
|
||||
"Glaze: " .. updates_found .. " update(s) available — run :GlazeUpdate",
|
||||
vim.log.levels.INFO
|
||||
)
|
||||
end)
|
||||
else
|
||||
vim.schedule(function()
|
||||
@@ -213,7 +269,7 @@ function M.check(opts)
|
||||
end
|
||||
elseif updates_found > 0 then
|
||||
vim.schedule(function()
|
||||
vim.notify("Glaze: " .. updates_found .. " update(s) available", vim.log.levels.INFO)
|
||||
vim.notify("Glaze: " .. updates_found .. " update(s) available — run :GlazeUpdate", vim.log.levels.INFO)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
90
lua/glaze/health.lua
Normal file
90
lua/glaze/health.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
---@brief [[
|
||||
--- glaze.nvim health check
|
||||
--- Run with :checkhealth glaze
|
||||
---@brief ]]
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.check()
|
||||
vim.health.start("glaze.nvim")
|
||||
|
||||
-- Check Neovim version
|
||||
if vim.fn.has("nvim-0.9") == 1 then
|
||||
vim.health.ok("Neovim >= 0.9")
|
||||
else
|
||||
vim.health.error("Neovim >= 0.9 required", { "Upgrade Neovim to 0.9 or later" })
|
||||
end
|
||||
|
||||
-- Check Go (required)
|
||||
if vim.fn.executable("go") == 1 then
|
||||
local go_version = vim.fn.system("go version"):gsub("%s+$", "")
|
||||
vim.health.ok(go_version)
|
||||
elseif vim.fn.executable("goenv") == 1 then
|
||||
local go_version = vim.fn.system("goenv exec go version 2>/dev/null"):gsub("%s+$", "")
|
||||
if go_version ~= "" and not go_version:match("not found") then
|
||||
vim.health.ok(go_version .. " (via goenv)")
|
||||
else
|
||||
vim.health.error("goenv found but no Go version installed", {
|
||||
"Run: goenv install <version>",
|
||||
"Or install Go directly: https://go.dev/dl/",
|
||||
})
|
||||
end
|
||||
else
|
||||
vim.health.error("Go not found", {
|
||||
"Go is required for installing and updating binaries",
|
||||
"Install from: https://go.dev/dl/",
|
||||
})
|
||||
end
|
||||
|
||||
-- Check GOBIN / GOPATH
|
||||
local gobin = vim.env.GOBIN
|
||||
if not gobin or gobin == "" then
|
||||
local gopath = vim.env.GOPATH
|
||||
if gopath and gopath ~= "" then
|
||||
gobin = gopath .. "/bin"
|
||||
else
|
||||
gobin = vim.env.HOME .. "/go/bin"
|
||||
end
|
||||
end
|
||||
|
||||
if vim.fn.isdirectory(gobin) == 1 then
|
||||
-- Check if GOBIN is in PATH
|
||||
local path = vim.env.PATH or ""
|
||||
if path:find(gobin, 1, true) then
|
||||
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"',
|
||||
"Binaries installed by Glaze may not be found without this",
|
||||
})
|
||||
end
|
||||
else
|
||||
vim.health.info("GOBIN directory does not exist yet: " .. gobin)
|
||||
end
|
||||
|
||||
-- Check registered binaries
|
||||
local glaze = require("glaze")
|
||||
local binaries = glaze.binaries()
|
||||
local count = vim.tbl_count(binaries)
|
||||
|
||||
if count == 0 then
|
||||
vim.health.info("No binaries registered (plugins will register on setup)")
|
||||
else
|
||||
vim.health.ok(count .. " binary(ies) registered")
|
||||
for name, binary in pairs(binaries) do
|
||||
local pi = (binary.plugins and #binary.plugins > 0)
|
||||
and (" (" .. table.concat(binary.plugins, ", ") .. ")")
|
||||
or ""
|
||||
if glaze.is_installed(name) then
|
||||
vim.health.ok(" " .. name .. " — installed" .. pi)
|
||||
else
|
||||
vim.health.warn(" " .. name .. " — missing" .. pi, {
|
||||
"Run :GlazeInstall " .. name,
|
||||
"Or: go install " .. binary.url .. "@latest",
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -15,30 +15,40 @@ 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
|
||||
---@field plugins string[] Plugins that registered this binary
|
||||
---@field callbacks table<string, fun(success: boolean)> Callbacks keyed by plugin name
|
||||
|
||||
---@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
|
||||
|
||||
---@class GlazeAutoUpdateConfig
|
||||
---@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_check GlazeAutoCheckConfig
|
||||
---@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 = {
|
||||
@@ -52,13 +62,21 @@ M.config = {
|
||||
error = "✗",
|
||||
binary = "",
|
||||
},
|
||||
use_system_theming = false,
|
||||
},
|
||||
concurrency = 4,
|
||||
go_cmd = { "go" },
|
||||
auto_install = {
|
||||
enabled = true,
|
||||
silent = false,
|
||||
},
|
||||
auto_check = {
|
||||
enabled = true,
|
||||
frequency = "daily",
|
||||
},
|
||||
auto_update = {
|
||||
enabled = false,
|
||||
},
|
||||
}
|
||||
|
||||
---@type table<string, GlazeBinary>
|
||||
@@ -123,17 +141,60 @@ function M.setup(opts)
|
||||
end
|
||||
|
||||
---Register a binary for management.
|
||||
---If auto_install is enabled and the binary is missing, it will be installed automatically.
|
||||
---@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 {}
|
||||
local plugin = opts.plugin or "unknown"
|
||||
|
||||
-- Check if this URL is already registered under a different name
|
||||
for existing_name, binary in pairs(M._binaries) do
|
||||
if binary.url == url and existing_name ~= name then
|
||||
-- Same URL, different name - merge into existing entry
|
||||
if not vim.tbl_contains(binary.plugins, plugin) then
|
||||
table.insert(binary.plugins, plugin)
|
||||
end
|
||||
if opts.callback then
|
||||
binary.callbacks[plugin] = opts.callback
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if this name is already registered
|
||||
local existing = M._binaries[name]
|
||||
if existing then
|
||||
-- Merge plugin into existing entry
|
||||
if not vim.tbl_contains(existing.plugins, plugin) then
|
||||
table.insert(existing.plugins, plugin)
|
||||
end
|
||||
if opts.callback then
|
||||
existing.callbacks[plugin] = opts.callback
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- New binary registration
|
||||
M._binaries[name] = {
|
||||
name = name,
|
||||
url = url,
|
||||
plugin = opts.plugin,
|
||||
callback = opts.callback,
|
||||
plugins = opts.plugin and { opts.plugin } or {},
|
||||
callbacks = opts.callback and { [plugin] = opts.callback } or {},
|
||||
}
|
||||
|
||||
-- Auto-install if enabled and binary is missing
|
||||
if M.config.auto_install.enabled and not M.is_installed(name) then
|
||||
vim.defer_fn(function()
|
||||
if not M.is_installed(name) then
|
||||
if not M.config.auto_install.silent then
|
||||
vim.notify("[glaze] Auto-installing " .. name .. "…", vim.log.levels.INFO)
|
||||
end
|
||||
require("glaze.runner").install({ name })
|
||||
end
|
||||
end, 100)
|
||||
end
|
||||
end
|
||||
|
||||
---Unregister a binary.
|
||||
|
||||
@@ -93,10 +93,17 @@ local function run_task(task)
|
||||
task.status = code == 0 and "done" or "error"
|
||||
task.job_id = nil
|
||||
|
||||
-- Call binary callback if set
|
||||
if task.binary.callback then
|
||||
-- Refresh version info on success
|
||||
if code == 0 then
|
||||
require("glaze.checker").refresh_version(task.binary.name)
|
||||
end
|
||||
|
||||
-- Call all registered callbacks
|
||||
if task.binary.callbacks then
|
||||
vim.schedule(function()
|
||||
task.binary.callback(code == 0)
|
||||
for _, cb in pairs(task.binary.callbacks) do
|
||||
cb(code == 0)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -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 ",
|
||||
})
|
||||
@@ -161,16 +164,16 @@ function M.render()
|
||||
text:append(" ", nil, { indent = 2 })
|
||||
text:append(" U ", "GlazeButtonActive"):append(" Update All ", "GlazeButton")
|
||||
text:append(" ")
|
||||
text:append(" u ", "GlazeButtonActive"):append(" Update ", "GlazeButton")
|
||||
text:append(" u ", "GlazeButtonActive"):append(" Update Selected ", "GlazeButton")
|
||||
text:append(" ")
|
||||
text:append(" I ", "GlazeButtonActive"):append(" Install All ", "GlazeButton")
|
||||
text:append(" ")
|
||||
text:append(" i ", "GlazeButtonActive"):append(" Install ", "GlazeButton")
|
||||
text:append(" i ", "GlazeButtonActive"):append(" Install Selected ", "GlazeButton")
|
||||
text:nl()
|
||||
text:append(" ", nil, { indent = 2 })
|
||||
text:append(" x ", "GlazeButtonActive"):append(" Abort ", "GlazeButton")
|
||||
text:append(" ")
|
||||
text:append(" ⏎ ", "GlazeButtonActive"):append(" Details ", "GlazeButton")
|
||||
text:append(" <enter> ", "GlazeButtonActive"):append(" Details ", "GlazeButton")
|
||||
text:append(" ")
|
||||
text:append(" q ", "GlazeButtonActive"):append(" Close ", "GlazeButton")
|
||||
text:nl():nl()
|
||||
@@ -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
|
||||
updates_available = updates_available + 1
|
||||
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")
|
||||
@@ -321,8 +323,8 @@ function M._render_binary(text, binary, icons, update_info)
|
||||
text:append(" " .. icon .. " ", icon_hl)
|
||||
text:append(binary.name, "GlazeBinary")
|
||||
|
||||
if binary.plugin then
|
||||
text:append(" (" .. binary.plugin .. ")", "GlazePlugin")
|
||||
if binary.plugins and #binary.plugins > 0 then
|
||||
text:append(" (" .. table.concat(binary.plugins, ", ") .. ")", "GlazePlugin")
|
||||
end
|
||||
|
||||
-- Show update available indicator
|
||||
@@ -351,9 +353,9 @@ function M._render_binary(text, binary, icons, update_info)
|
||||
text:append(bin_path, "GlazeUrl"):nl()
|
||||
end
|
||||
|
||||
if binary.plugin then
|
||||
text:append("Plugin: ", "GlazeComment", { indent = 6 })
|
||||
text:append(binary.plugin, "GlazePlugin"):nl()
|
||||
if binary.plugins and #binary.plugins > 0 then
|
||||
text:append("Plugins: ", "GlazeComment", { indent = 6 })
|
||||
text:append(table.concat(binary.plugins, ", "), "GlazePlugin"):nl()
|
||||
end
|
||||
|
||||
-- Show last error output from tasks
|
||||
|
||||
46
scripts/vhs_init.lua
Normal file
46
scripts/vhs_init.lua
Normal 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")
|
||||
Reference in New Issue
Block a user