// Command glint is a modeless terminal markdown editor with live styling. package main import ( "flag" "fmt" "os" "glint/internal/app" "glint/internal/config" "glint/internal/configui" "glint/internal/help" "glint/internal/keyprobe" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/muesli/termenv" ) func init() { // Render glint's exact theme hexes regardless of the terminal's detected // profile or OS appearance — never downsample or adapt to the system. lipgloss.SetColorProfile(termenv.TrueColor) } // version is the build version, overridden at release time via // -ldflags "-X main.version=" (the Homebrew formula sets it). var version = "dev" func main() { // Help and version short-circuit before flag parsing (-v means --vault, so // version is long-only). if len(os.Args) > 1 { switch os.Args[1] { case "-h", "--h", "-help", "--help": fmt.Print(help.Text) return case "--version", "-version": fmt.Println("glint", version) return } } cfg, err := config.Load() if err != nil { fmt.Fprintln(os.Stderr, "glint: config:", err) } // Every command is a flag; both -x and --x (letter or word) work. flagNew := boolFlag("n", "new") flagToday := boolFlag("t", "today") flagDaily := boolFlag("d", "daily") flagVault := boolFlag("v", "vault") flagConfig := boolFlag("c", "config") flagInbox := boolFlag("i", "inbox") flagPreview := boolFlag("p", "preview") flagExport := boolFlag("e", "export") flagKeys := flag.Bool("keys", false, "show what the terminal sends for each key") flag.Parse() isNew := *flagNew[0] || *flagNew[1] isToday := *flagToday[0] || *flagToday[1] isDaily := *flagDaily[0] || *flagDaily[1] isVault := *flagVault[0] || *flagVault[1] isConfig := *flagConfig[0] || *flagConfig[1] isInbox := *flagInbox[0] || *flagInbox[1] isPreview := *flagPreview[0] || *flagPreview[1] isExport := *flagExport[0] || *flagExport[1] // Standalone commands (no editor TUI). if isConfig { runOrDie(configui.Run()) return } if *flagKeys { runOrDie(keyprobe.Run()) return } name := "" if args := flag.Args(); len(args) > 0 { name = args[0] } a := app.New(cfg) // Headless export: render a named file to HTML and exit, no TUI. if isExport { if name == "" { fmt.Fprintln(os.Stderr, "glint: -e needs a file to export") os.Exit(1) } out, err := a.ExportFile(name) if err != nil { fmt.Fprintln(os.Stderr, "glint:", err) os.Exit(1) } fmt.Println("glint: exported", out, "— open it, then Print → Save as PDF") return } var startErr error switch { case isPreview: startErr = a.StartPreview(name) // open straight into the read view (glow-style) case isNew: // New note in the current dir, or the inbox/vault when combined. dir := cfg.WorkingDir() if isInbox { dir = cfg.InboxRoot() } if isVault { dir = cfg.Vault() } startErr = a.StartNewIn(dir, name) case isToday: startErr = a.Start("", true) // today's daily note case isVault: startErr = a.StartPickerIn(cfg.Vault()) case isInbox: startErr = a.StartPickerIn(cfg.InboxRoot()) case isDaily: startErr = a.StartPickerIn(cfg.DailyDir()) // browse the daily folder case name != "": startErr = a.Start(name, false) // open a file default: startErr = a.Start("", false) // bare → fuzzy picker over the current dir } if startErr != nil { fmt.Fprintln(os.Stderr, "glint:", startErr) os.Exit(1) } run(a) } // boolFlag registers a short and long name for the same command and returns both // pointers; either being set means the command was given (e.g. -n / --n / -new / // --new). Go's flag package accepts both single- and double-dash for each name. func boolFlag(short, long string) [2]*bool { return [2]*bool{ flag.Bool(short, false, "command: -"+short+" / --"+long), flag.Bool(long, false, ""), } } func runOrDie(err error) { if err != nil { fmt.Fprintln(os.Stderr, "glint:", err) os.Exit(1) } } // run drives the Bubbletea program in the alternate screen. func run(a *app.App) { if _, err := tea.NewProgram(a, tea.WithAltScreen(), tea.WithMouseCellMotion()).Run(); err != nil { fmt.Fprintln(os.Stderr, "glint:", err) os.Exit(1) } }