{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoImplicitPrelude #-} -- | Terminal detection and output mode selection module Omni.Log.Terminal ( TerminalInfo (..), OutputMode (..), detectTerminal, truncateToWidth, ) where import Alpha import qualified Control.Exception as Exception import qualified Data.Text as Text import qualified System.Console.ANSI as ANSI import qualified System.Environment as Env data OutputMode = MultiLine -- Wide terminals (≥80 cols) - reserved lines per namespace | SingleLine -- Narrow terminals (<80 cols) - rotating single line 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" noColor <- Env.lookupEnv "NO_COLOR" -- Check if we support ANSI let supportsANSI = case (term, area, noColor) of (_, _, Just _) -> False -- NO_COLOR set (Just "dumb", _, _) -> False (_, Just "Live", _) -> False -- production logs (Nothing, _, _) -> False _ -> True -- Get terminal size, catching exceptions from stdin issues -- When NO_COLOR is set or ANSI is not supported, skip terminal size detection -- to avoid outputting escape codes mSize <- if supportsANSI then Exception.catch ANSI.getTerminalSize <| \(_ :: Exception.IOException) -> pure Nothing else pure Nothing -- Skip if no ANSI support let (width, height) = case mSize of Just (h, w) -> (w, h) Nothing -> (80, 24) -- sensible default -- Determine mode based on terminal width let mode | not supportsANSI = SingleLine -- Fallback to single line for dumb terminals | width < 80 = SingleLine | otherwise = MultiLine 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 <> "..."