summaryrefslogtreecommitdiff
path: root/Omni/Log/Terminal.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Log/Terminal.hs')
-rw-r--r--Omni/Log/Terminal.hs75
1 files changed, 75 insertions, 0 deletions
diff --git a/Omni/Log/Terminal.hs b/Omni/Log/Terminal.hs
new file mode 100644
index 0000000..1a4c717
--- /dev/null
+++ b/Omni/Log/Terminal.hs
@@ -0,0 +1,75 @@
+{-# 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 ANSI support
+ let mode
+ | not supportsANSI = SingleLine -- Fallback to single line for dumb terminals
+ | 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 <> "..."