// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package presentation import ( "fmt" "git.stormux.org/storm/fenrir/internal/i18n" "git.stormux.org/storm/fenrir/internal/screen" "strings" ) // Default_attribute_format is Python Fenrir's default, including its tokens. const Default_attribute_format = "Background fenrirBGColor,Foreground fenrirFGColor,fenrirUnderline,fenrirBold,fenrirBlink, Font fenrirFont,Fontsize fenrirFontSize" func attribute_color(color uint32, background bool) string { if color == screen.ColorDefault { return i18n.T("default") } if color&screen.ColorIndexed == 0 { return fmt.Sprintf("#%06x", color) } index := color &^ screen.ColorIndexed names := []string{"black", "red", "green", "brown/yellow", "blue", "magenta", "cyan", "light gray", "dark gray", "light red", "light green", "light yellow", "light blue", "light magenta", "light cyan", "white"} if index < 16 { if background && index == 7 { return i18n.T("white") } return i18n.T(names[index]) } // xterm's fixed 256-color cube and grayscale, without approximate names. if index < 232 { level := []uint32{0, 95, 135, 175, 215, 255} index -= 16 return fmt.Sprintf("#%02x%02x%02x", level[index/36], level[index/6%6], level[index%6]) } value := 8 + 10*(index-232) return fmt.Sprintf("#%02x%02x%02x", value, value, value) } // Format_attributes substitutes the Python tokens. Font metadata is not // available from either backend; default means unspecified, not a detected font. func Format_attributes(attribute screen.Attribute, format string) string { pairs := []string{"fenrirFGColor", attribute_color(attribute.Foreground, false), "fenrirBGColor", attribute_color(attribute.Background, true), "fenrirFontSize", i18n.T("default"), "fenrirFont", i18n.T("default")} for _, style := range []struct { token, text string mask uint32 }{ {"fenrirBold", "bold", screen.Bold}, {"fenrirItalics", "italic", screen.Italic}, {"fenrirUnderline", "underline", screen.Underline}, {"fenrirStrikethrough", "strikethrough", screen.Strike}, {"fenrirReverse", "reverse", screen.Reverse}, {"fenrirBlink", "blink", screen.Blink}, } { text := "" if attribute.Style&style.mask != 0 { text = i18n.T(style.text) } pairs = append(pairs, style.token, text) } return strings.NewReplacer(pairs...).Replace(format) } func Attribute_message(attribute screen.Attribute, mode, format string) Message { text := Format_attributes(attribute, format) if mode == "beeps" || strings.TrimSpace(text) == "" && !attribute.Default() { text = i18n.T("has attribute") } message := Message{Text: text, Attributes: &Attributes{Speech: mode != "beeps"}} if mode == "beeps" || mode == "both" { message.Cue = HasAttributes } return message }