adding init program
This commit is contained in:
126
cmd/streamdeck-init/grid.go
Normal file
126
cmd/streamdeck-init/grid.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// grid.go — Renders the 8x4 Stream Deck key grid in the terminal.
|
||||
//
|
||||
// Uses lipgloss for styling: free slots are green with [index], occupied slots
|
||||
// are dimmed with a short label (function name, icon name, or command prefix).
|
||||
//
|
||||
// ## Layout
|
||||
//
|
||||
// The grid mirrors the physical Stream Deck XL layout:
|
||||
//
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// 8 9 10 11 12 13 14 15
|
||||
// 16 17 18 19 20 21 22 23
|
||||
// 24 25 26 27 28 29 30 31
|
||||
//
|
||||
// ## Expanding
|
||||
//
|
||||
// To support non-XL models (e.g. MK.2 with 15 keys in a 5x3 grid), make
|
||||
// gridCols and gridRows parameters instead of constants, and derive them
|
||||
// from the device config's product_id. The device model → key count mapping
|
||||
// is in internal/device/streamdeck.go.
|
||||
//
|
||||
// To add richer labels (e.g. showing params or the icon filename alongside
|
||||
// the function name), modify keyLabel(). Keep labels under 8 chars to fit
|
||||
// the cell width.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/WoodardDigital/streamdeck-go/internal/config"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// Grid dimensions — hardcoded for Stream Deck XL (8 columns × 4 rows = 32 keys).
|
||||
const (
|
||||
gridCols = 8
|
||||
gridRows = 4
|
||||
gridKeys = gridCols * gridRows
|
||||
)
|
||||
|
||||
// Styles for the grid cells. Color numbers are ANSI 256-color palette indices:
|
||||
//
|
||||
// 10 = bright green (free slots — draws the eye)
|
||||
// 8 = dark gray (occupied slots — visually recedes)
|
||||
// 12 = bright blue (title)
|
||||
var (
|
||||
freeStyle = lipgloss.NewStyle().Width(9).Height(2).Align(lipgloss.Center, lipgloss.Center).Bold(true).Foreground(lipgloss.Color("10"))
|
||||
occupiedStyle = lipgloss.NewStyle().Width(9).Height(2).Align(lipgloss.Center, lipgloss.Center).Foreground(lipgloss.Color("8"))
|
||||
borderStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("8"))
|
||||
titleStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("12"))
|
||||
)
|
||||
|
||||
// renderGrid builds a string containing the full 8x4 key grid with a legend.
|
||||
// Free slots show [index] in green; occupied slots show a short label in gray.
|
||||
// The grid is printed to stdout before the key-picking form.
|
||||
func renderGrid(keys map[int]config.KeyConfig) string {
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString(titleStyle.Render(" Stream Deck XL — 8×4 grid"))
|
||||
b.WriteString("\n\n")
|
||||
|
||||
for row := 0; row < gridRows; row++ {
|
||||
cells := make([]string, gridCols)
|
||||
for col := 0; col < gridCols; col++ {
|
||||
idx := row*gridCols + col
|
||||
keyCfg, occupied := keys[idx]
|
||||
|
||||
if occupied {
|
||||
label := keyLabel(keyCfg)
|
||||
cells[col] = borderStyle.Render(occupiedStyle.Render(label))
|
||||
} else {
|
||||
cells[col] = borderStyle.Render(freeStyle.Render(fmt.Sprintf("[%d]", idx)))
|
||||
}
|
||||
}
|
||||
// JoinHorizontal places cells side by side, aligned at the top.
|
||||
b.WriteString(lipgloss.JoinHorizontal(lipgloss.Top, cells...))
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
// Legend explaining the color coding.
|
||||
b.WriteString("\n")
|
||||
b.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Render("[n]"))
|
||||
b.WriteString(" = free ")
|
||||
b.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Render("dim"))
|
||||
b.WriteString(" = occupied\n")
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// keyLabel returns a short display label for an occupied key (max 8 chars).
|
||||
//
|
||||
// Priority order:
|
||||
// 1. Module function name (if module-based key)
|
||||
// 2. Icon filename without extension (if static key)
|
||||
// 3. Command prefix (fallback)
|
||||
// 4. "???" (shouldn't happen with valid config)
|
||||
func keyLabel(k config.KeyConfig) string {
|
||||
if k.Module != "" {
|
||||
label := k.Function
|
||||
if len(label) > 8 {
|
||||
label = label[:8]
|
||||
}
|
||||
return label
|
||||
}
|
||||
if k.Icon != "" {
|
||||
label := k.Icon
|
||||
// Strip file extension for brevity (e.g. "firefox.png" → "firefox").
|
||||
if dot := strings.LastIndex(label, "."); dot > 0 {
|
||||
label = label[:dot]
|
||||
}
|
||||
if len(label) > 8 {
|
||||
label = label[:8]
|
||||
}
|
||||
return label
|
||||
}
|
||||
if k.Command != "" {
|
||||
label := k.Command
|
||||
if len(label) > 8 {
|
||||
label = label[:8]
|
||||
}
|
||||
return label
|
||||
}
|
||||
return "???"
|
||||
}
|
||||
Reference in New Issue
Block a user