lazygit.go

  1// Package lazygit provides a dialog component for embedding lazygit in the TUI.
  2package lazygit
  3
  4import (
  5	"context"
  6	"fmt"
  7	"image/color"
  8	"os"
  9
 10	tea "charm.land/bubbletea/v2"
 11
 12	"git.secluded.site/crush/internal/terminal"
 13	"git.secluded.site/crush/internal/tui/components/dialogs"
 14	"git.secluded.site/crush/internal/tui/components/dialogs/termdialog"
 15	"git.secluded.site/crush/internal/tui/styles"
 16)
 17
 18// DialogID is the unique identifier for the lazygit dialog.
 19const DialogID dialogs.DialogID = "lazygit"
 20
 21// NewDialog creates a new lazygit dialog. The context controls the lifetime
 22// of the lazygit process - when cancelled, the process will be killed.
 23func NewDialog(ctx context.Context, workingDir string) *termdialog.Dialog {
 24	configFile := createThemedConfig()
 25
 26	cmd := terminal.PrepareCmd(
 27		ctx,
 28		"lazygit",
 29		nil,
 30		workingDir,
 31		[]string{"LG_CONFIG_FILE=" + configFile},
 32	)
 33
 34	return termdialog.New(termdialog.Config{
 35		ID:         DialogID,
 36		Title:      "Lazygit",
 37		LoadingMsg: "Starting lazygit...",
 38		Term:       terminal.New(terminal.Config{Context: ctx, Cmd: cmd}),
 39		OnClose: func() tea.Cmd {
 40			if configFile != "" {
 41				_ = os.Remove(configFile)
 42			}
 43			return nil
 44		},
 45	})
 46}
 47
 48// colorToHex converts a color.Color to a hex string.
 49func colorToHex(c color.Color) string {
 50	r, g, b, _ := c.RGBA()
 51	return fmt.Sprintf("#%02x%02x%02x", r>>8, g>>8, b>>8)
 52}
 53
 54// createThemedConfig creates a temporary lazygit config file with Crush theme.
 55// Theme mappings align with Crush's UX patterns:
 56// - Borders: BorderFocus (purple) for active, Border (gray) for inactive
 57// - Selection: Primary (purple) background matches app's TextSelected style
 58// - Status: Success (green), Error (red), Info (blue), Warning (orange)
 59func createThemedConfig() string {
 60	t := styles.CurrentTheme()
 61
 62	config := fmt.Sprintf(`gui:
 63  border: rounded
 64  showFileTree: true
 65  showRandomTip: false
 66  showCommandLog: false
 67  showBottomLine: true
 68  showPanelJumps: false
 69  nerdFontsVersion: ""
 70  showFileIcons: false
 71  theme:
 72    activeBorderColor:
 73      - "%s"
 74      - bold
 75    inactiveBorderColor:
 76      - "%s"
 77    searchingActiveBorderColor:
 78      - "%s"
 79      - bold
 80    optionsTextColor:
 81      - "%s"
 82    selectedLineBgColor:
 83      - "%s"
 84    inactiveViewSelectedLineBgColor:
 85      - "%s"
 86    cherryPickedCommitFgColor:
 87      - "%s"
 88    cherryPickedCommitBgColor:
 89      - "%s"
 90    markedBaseCommitFgColor:
 91      - "%s"
 92    markedBaseCommitBgColor:
 93      - "%s"
 94    unstagedChangesColor:
 95      - "%s"
 96    defaultFgColor:
 97      - default
 98`,
 99		colorToHex(t.BorderFocus),
100		colorToHex(t.FgMuted),
101		colorToHex(t.Info),
102		colorToHex(t.FgMuted),
103		colorToHex(t.Primary),
104		colorToHex(t.BgSubtle),
105		colorToHex(t.Success),
106		colorToHex(t.BgSubtle),
107		colorToHex(t.Info),
108		colorToHex(t.BgSubtle),
109		colorToHex(t.Error),
110	)
111
112	f, err := os.CreateTemp("", "crush-lazygit-*.yml")
113	if err != nil {
114		return ""
115	}
116	defer f.Close()
117
118	_, _ = f.WriteString(config)
119	return f.Name()
120}