summaryrefslogtreecommitdiff
path: root/Omni/Log/Terminal.hs
blob: 1230eb331c7df47b008fb6f00b1651400e388899 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
{-# 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 <> "..."