func()

in pkg/ui/terminal.go [80:224]


func (u *TerminalUI) DocumentChanged(doc *Document, block Block) {
	blockIndex := doc.IndexOf(block)

	if blockIndex != doc.NumBlocks()-1 {
		klog.Warningf("update to blocks other than the last block is not supported in terminal mode")
		return
	}

	if u.currentBlock != block {
		u.currentBlock = block
		if u.currentBlockText != "" {
			fmt.Printf("\n")
		}
		u.currentBlockText = ""
	}

	text := ""
	streaming := false

	var styleOptions []StyleOption
	switch block := block.(type) {
	case *ErrorBlock:
		styleOptions = append(styleOptions, Foreground(ColorRed))
		text = block.Text()
	case *FunctionCallRequestBlock:
		styleOptions = append(styleOptions, Foreground(ColorGreen))
		text = block.Text()
	case *AgentTextBlock:
		styleOptions = append(styleOptions, RenderMarkdown())
		if block.Color != "" {
			styleOptions = append(styleOptions, Foreground(block.Color))
		}
		text = block.Text()
		streaming = block.Streaming()
	case *InputTextBlock:
		fmt.Print("\n>>> ")
		var reader *bufio.Reader
		if u.useTTYForInput {
			// Stdin was used for piped data, open the terminal directly
			tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
			if err != nil {
				block.Observable().Set("", err)
				return
			}
			defer tty.Close()
			reader = bufio.NewReader(tty)
		} else {
			reader = bufio.NewReader(os.Stdin)
		}
		query, err := reader.ReadString('\n')
		if err != nil {
			block.Observable().Set("", err)
		} else {
			block.Observable().Set(query, nil)
		}
		return

	case *InputOptionBlock:
		fmt.Printf("%s\n", block.Prompt)
		var reader *bufio.Reader
		if u.useTTYForInput {
			tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
			if err != nil {
				block.Observable().Set("", err)
				return
			}
			defer tty.Close()
			reader = bufio.NewReader(tty)
		} else {
			reader = bufio.NewReader(os.Stdin)
		}

		for {
			fmt.Print("  Enter your choice (number): ")
			var response string
			response, err := reader.ReadString('\n')
			if err != nil {
				block.Observable().Set("", err)
				break
			}

			choice := strings.TrimSpace(response)

			if slices.Contains(block.Options, choice) {
				block.Observable().Set(choice, nil)
				break
			}

			// If not returned, the choice was invalid
			fmt.Printf("  Invalid choice. Please enter one of: %s\n", strings.Join(block.Options, ", "))
			continue
		}
		return
	}

	computedStyle := &style{}
	for _, opt := range styleOptions {
		opt(computedStyle)
	}

	if streaming && computedStyle.renderMarkdown {
		// Because we can't render markdown incrementally,
		// we "hold back" the text if we are streaming markdown until streaming is done
		text = ""
	}

	printText := text

	if computedStyle.renderMarkdown && printText != "" {
		out, err := u.markdownRenderer.Render(printText)
		if err != nil {
			klog.Errorf("Error rendering markdown: %v", err)
		} else {
			printText = out
		}
	}

	if u.currentBlockText != "" {
		if strings.HasPrefix(text, u.currentBlockText) {
			printText = strings.TrimPrefix(printText, u.currentBlockText)
		} else {
			klog.Warningf("text did not match text already rendered; text %q; currentBlockText %q", text, u.currentBlockText)
		}
	}
	u.currentBlockText = text

	reset := ""
	switch computedStyle.foreground {
	case ColorRed:
		fmt.Printf("\033[31m")
		reset += "\033[0m"
	case ColorGreen:
		fmt.Printf("\033[32m")
		reset += "\033[0m"
	case ColorWhite:
		fmt.Printf("\033[37m")
		reset += "\033[0m"

	case "":
	default:
		klog.Info("foreground color not supported by TerminalUI", "color", computedStyle.foreground)
	}

	fmt.Printf("%s%s", printText, reset)
}