package cmd import ( "context" "fmt" "github.com/eiannone/keyboard" "sync" "time" ) type Log struct { Level string Message string Time string Source string } type LoggerQueue struct { queue chan Log currentInput string } var Logger = &LoggerQueue{ queue: make(chan Log, 100), } func (l *LoggerQueue) log(level string, message string, source string) { l.queue <- Log{ Level: level, Message: message, Time: time.Now().Format("2006/01/02 15:04:05"), Source: source, } } func (l *LoggerQueue) Info(message string) { l.log("INFO", message, "") } func (l *LoggerQueue) Warning(message string) { l.log("WARNING", message, "") } func (l *LoggerQueue) Error(message string) { l.log("ERROR", message, "") } func (l *LoggerQueue) processLogs(ctx context.Context) { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for { select { case logEntry := <-l.queue: displayLog(logEntry) case <-ctx.Done(): fmt.Print("\033[2K\r") return case <-ticker.C: refreshInputDisplay(l.currentInput) } } } func displayLog(logEntry Log) { fmt.Printf("\033[2K\r%s [%s]: %s\n", logEntry.Time, logEntry.Level, logEntry.Message) fmt.Print("> ") } func refreshInputDisplay(input string) { fmt.Print("\033[2K\r> " + input) } func Run(baseCtx context.Context) { ctx, cancel := context.WithCancel(baseCtx) defer cancel() input := "" event, err := keyboard.GetKeys(10) if err != nil { Logger.Error(fmt.Sprintf("Keyboard error: %v", err)) return } var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() Logger.processLogs(ctx) }() loop: for { select { case <-ctx.Done(): cancel() break loop case event := <-event: if event.Rune != 0 { input += string(event.Rune) } switch event.Key { case keyboard.KeyEnter: switch input { case "": // Do nothing case "stop": Logger.Info("Received stop command.") cancel() break loop default: Logger.Info(fmt.Sprintf("Unknown command: %s", input)) } input = "" case keyboard.KeyBackspace | keyboard.KeyBackspace2: if len(input) > 0 { input = input[:len(input)-1] } case keyboard.KeySpace: input += " " case keyboard.KeyCtrlC: Logger.Info("Received stop command.") cancel() break loop } Logger.currentInput = input // Update stored user input } } wg.Wait() }