haskellterminalrgbtuihaskell-brick

RGB Terminal Colors with Haskell and Brick


I know that the Brick and the VTY hackage do not support escape sequences. VTY only supports 240 colors.

Is there any workaround to use true RGB colors and not mess up the layout?

This is an example I made, but I can't get the border right:

module BrickTest where

import Brick                (simpleMain, Widget, str)
import Brick.Widgets.Border (border)
import Text.Printf          (printf)

main :: IO ()
main = simpleMain $ colorWidget (255, 0, 0)

type RGB = (Int, Int, Int)

colorWidget :: RGB -> Widget ()
colorWidget (r, g, b) = border $ str (prefix ++ "a" ++ postfix)
    where
        prefix = printf "\ESC[38;2;%d;%d;%dm" r g b
        postfix = "\ESC[0m"

output:

┌──────────────────┐
│a│
└──────────────────┘

terminal screenshot


Solution

  • I found a workaround. I managed to implement a function zeroWidthStr that can print any string, and Brick handles it as if it has width 0. But I can't really explain how this is working internally, and it might have some other side effects.

    module BrickTest where
    
    import           Brick                       (Widget, raw, simpleMain, str,
                                                  (<+>))
    import           Brick.Widgets.Border        (border)
    import           Data.List                   (intercalate)
    import           Data.Text.Lazy              (pack)
    import           Graphics.Vty                (defAttr)
    import           Graphics.Vty.Image.Internal (Image (HorizText))
    import           Text.Printf                 (printf)
    
    main :: IO ()
    main = simpleMain $ colorWidget (255, 0, 0)
    
    type RGB = (Int, Int, Int)
    
    colorWidget :: RGB -> Widget ()
    colorWidget (r, g, b) = border $ prefix <+> str "a" <+> postfix
        where
            prefix = zeroWidthStr $ printf "\ESC[38;2;%d;%d;%dm" r g b
            postfix = zeroWidthStr $ "\ESC[0m"
    
    zeroWidthStr :: String -> Widget ()
    -- | workaround to print any string in terminal, and hackage Brick (vty) handles it as if it has width 0
    zeroWidthStr str = raw image
        where
            image = HorizText defAttr (pack modStr) 0 0
            modStr = str ++ repeatN "\ESC\ESCa" (length str)
            repeatN :: String -> Int -> String
            repeatN str n = intercalate "" $ take n $ repeat str
    

    output:

    terminal screenshot