diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e2e704d --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d09b490 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Git LFS tracking +docs/*.gif filter=lfs diff=lfs merge=lfs -text diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..077a514 --- /dev/null +++ b/.github/FUNDING.yml @@ -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] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f1acf3 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..1613acb --- /dev/null +++ b/.luarc.json @@ -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 + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..48bb5d0 --- /dev/null +++ b/AGENTS.md @@ -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 diff --git a/LICENSE b/LICENSE index 7e96918..ccaee7e 100644 --- a/LICENSE +++ b/LICENSE @@ -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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..03d9b8e --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index d159dd2..92d1b70 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,164 @@ -# 🍩 glaze.nvim + + + + glaze.nvim + -> **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.* +

+ + Latest release + + + Last commit + + + License + + + Stars + +

-![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? +glaze.nvim demo -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 `` 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 | -| `` | Toggle details (URL, path, version) | -| `q` / `` | 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 | +| `` | Toggle details | +| `q` / `` | 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) - 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, -- 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_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) diff --git a/doc/glaze.txt b/doc/glaze.txt index f97fa03..451e7d9 100644 --- a/doc/glaze.txt +++ b/doc/glaze.txt @@ -1,7 +1,7 @@ *glaze.txt* 🍩 Centralized Go binary management for Neovim plugins Author: Tai Groot -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* diff --git a/docs/demo.gif b/docs/demo.gif new file mode 100644 index 0000000..9437252 --- /dev/null +++ b/docs/demo.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e33f21d35c9b869528d908207e8d66fd5770d1ff5570ab487ef3f13081348a7 +size 89918 diff --git a/docs/demo.tape b/docs/demo.tape new file mode 100644 index 0000000..1d88911 --- /dev/null +++ b/docs/demo.tape @@ -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 diff --git a/docs/glaze-dark.svg b/docs/glaze-dark.svg new file mode 100644 index 0000000..8916c86 --- /dev/null +++ b/docs/glaze-dark.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + glaze.nvim + + + + + Go binary manager for Neovim + + diff --git a/docs/glaze-light.svg b/docs/glaze-light.svg new file mode 100644 index 0000000..678a2a4 --- /dev/null +++ b/docs/glaze-light.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + glaze.nvim + + + + + Go binary manager for Neovim + + diff --git a/lua/glaze/checker.lua b/lua/glaze/checker.lua index a20806a..90c99e1 100644 --- a/lua/glaze/checker.lua +++ b/lua/glaze/checker.lua @@ -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() diff --git a/lua/glaze/colors.lua b/lua/glaze/colors.lua index c2aa979..751220e 100644 --- a/lua/glaze/colors.lua +++ b/lua/glaze/colors.lua @@ -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) diff --git a/lua/glaze/health.lua b/lua/glaze/health.lua index 8258056..6b489ab 100644 --- a/lua/glaze/health.lua +++ b/lua/glaze/health.lua @@ -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 diff --git a/lua/glaze/init.lua b/lua/glaze/init.lua index 6f1d14b..27ced0f 100644 --- a/lua/glaze/init.lua +++ b/lua/glaze/init.lua @@ -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" }, diff --git a/lua/glaze/view.lua b/lua/glaze/view.lua index 0c6a6f7..3b78866 100644 --- a/lua/glaze/view.lua +++ b/lua/glaze/view.lua @@ -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 - 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") diff --git a/scripts/vhs_init.lua b/scripts/vhs_init.lua new file mode 100644 index 0000000..ebdf71f --- /dev/null +++ b/scripts/vhs_init.lua @@ -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")