{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoImplicitPrelude #-} -- | Terminal detection and output mode selection module Omni.Log.Terminal ( TerminalInfo (..), OutputMode (..), detectTerminal, truncateToWidth, ) where import Alpha import qualified Data.Text as Text import qualified System.Console.ANSI as ANSI import qualified System.Environment as Env data OutputMode = RichMultiLine -- Wide terminals (≥80 cols) | SingleLine -- Narrow terminals (40-79 cols) | SimpleFallback -- Very narrow (<40) or dumb terminals deriving (Eq, Show) data TerminalInfo = TerminalInfo { tiWidth :: Int, tiHeight :: Int, tiMode :: OutputMode, tiSupportsANSI :: Bool } deriving (Eq, Show) detectTerminal :: IO TerminalInfo detectTerminal = do term <- Env.lookupEnv "TERM" area <- Env.lookupEnv "AREA" modeOverride <- Env.lookupEnv "BILD_OUTPUT_MODE" -- Check if we support ANSI let supportsANSI = case (term, area) of (Just "dumb", _) -> False (_, Just "Live") -> False -- production logs (Nothing, _) -> False _ -> True -- Get terminal size mSize <- ANSI.getTerminalSize let (width, height) = case mSize of Just (h, w) -> (w, h) Nothing -> (80, 24) -- sensible default -- Determine mode let autoMode | not supportsANSI = SimpleFallback | width < 40 = SimpleFallback | width < 80 = SingleLine | otherwise = RichMultiLine -- Allow manual override let mode = case modeOverride of Just "simple" -> SimpleFallback Just "single" -> SingleLine Just "rich" -> RichMultiLine _ -> autoMode pure TerminalInfo { tiWidth = width, tiHeight = height, tiMode = mode, tiSupportsANSI = supportsANSI } -- | Truncate text to fit width with ellipsis truncateToWidth :: Int -> Text -> Text truncateToWidth maxWidth text | Text.length text <= maxWidth = text | maxWidth <= 3 = Text.take maxWidth text | otherwise = Text.take (maxWidth - 3) text <> "..."