safe-coloured-text 0.4.0.0 → 0.6.0.0
raw patch · 4 files changed
+124/−64 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Text.Colour: [chunkBackground] :: Chunk -> !Maybe Colour
- Text.Colour: [chunkBlinking] :: Chunk -> !Maybe Blinking
- Text.Colour: [chunkConcealed] :: Chunk -> !Maybe Bool
- Text.Colour: [chunkConsoleIntensity] :: Chunk -> !Maybe ConsoleIntensity
- Text.Colour: [chunkForeground] :: Chunk -> !Maybe Colour
- Text.Colour: [chunkItalic] :: Chunk -> !Maybe Bool
- Text.Colour: [chunkOverlined] :: Chunk -> !Maybe Bool
- Text.Colour: [chunkStrikethrough] :: Chunk -> !Maybe Bool
- Text.Colour: [chunkSwapForegroundBackground] :: Chunk -> !Maybe Bool
- Text.Colour: [chunkUnderlining] :: Chunk -> !Maybe Underlining
- Text.Colour.Chunk: [chunkBackground] :: Chunk -> !Maybe Colour
- Text.Colour.Chunk: [chunkBlinking] :: Chunk -> !Maybe Blinking
- Text.Colour.Chunk: [chunkConcealed] :: Chunk -> !Maybe Bool
- Text.Colour.Chunk: [chunkConsoleIntensity] :: Chunk -> !Maybe ConsoleIntensity
- Text.Colour.Chunk: [chunkForeground] :: Chunk -> !Maybe Colour
- Text.Colour.Chunk: [chunkItalic] :: Chunk -> !Maybe Bool
- Text.Colour.Chunk: [chunkOverlined] :: Chunk -> !Maybe Bool
- Text.Colour.Chunk: [chunkStrikethrough] :: Chunk -> !Maybe Bool
- Text.Colour.Chunk: [chunkSwapForegroundBackground] :: Chunk -> !Maybe Bool
- Text.Colour.Chunk: [chunkUnderlining] :: Chunk -> !Maybe Underlining
- Text.Colour.Chunk: chunkSGR :: TerminalCapabilities -> Chunk -> [SGR]
+ Text.Colour: ChunkStyle :: !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe ConsoleIntensity -> !Maybe Underlining -> !Maybe Blinking -> !Maybe Colour -> !Maybe Colour -> !Maybe Text -> ChunkStyle
+ Text.Colour: [chunkStyleBackground] :: ChunkStyle -> !Maybe Colour
+ Text.Colour: [chunkStyleBlinking] :: ChunkStyle -> !Maybe Blinking
+ Text.Colour: [chunkStyleConcealed] :: ChunkStyle -> !Maybe Bool
+ Text.Colour: [chunkStyleConsoleIntensity] :: ChunkStyle -> !Maybe ConsoleIntensity
+ Text.Colour: [chunkStyleForeground] :: ChunkStyle -> !Maybe Colour
+ Text.Colour: [chunkStyleHyperlink] :: ChunkStyle -> !Maybe Text
+ Text.Colour: [chunkStyleItalic] :: ChunkStyle -> !Maybe Bool
+ Text.Colour: [chunkStyleOverlined] :: ChunkStyle -> !Maybe Bool
+ Text.Colour: [chunkStyleStrikethrough] :: ChunkStyle -> !Maybe Bool
+ Text.Colour: [chunkStyleSwapForegroundBackground] :: ChunkStyle -> !Maybe Bool
+ Text.Colour: [chunkStyleUnderlining] :: ChunkStyle -> !Maybe Underlining
+ Text.Colour: [chunkStyle] :: Chunk -> !ChunkStyle
+ Text.Colour: data ChunkStyle
+ Text.Colour: noStyle :: ChunkStyle
+ Text.Colour.Chunk: ChunkStyle :: !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe ConsoleIntensity -> !Maybe Underlining -> !Maybe Blinking -> !Maybe Colour -> !Maybe Colour -> !Maybe Text -> ChunkStyle
+ Text.Colour.Chunk: [chunkStyleBackground] :: ChunkStyle -> !Maybe Colour
+ Text.Colour.Chunk: [chunkStyleBlinking] :: ChunkStyle -> !Maybe Blinking
+ Text.Colour.Chunk: [chunkStyleConcealed] :: ChunkStyle -> !Maybe Bool
+ Text.Colour.Chunk: [chunkStyleConsoleIntensity] :: ChunkStyle -> !Maybe ConsoleIntensity
+ Text.Colour.Chunk: [chunkStyleForeground] :: ChunkStyle -> !Maybe Colour
+ Text.Colour.Chunk: [chunkStyleHyperlink] :: ChunkStyle -> !Maybe Text
+ Text.Colour.Chunk: [chunkStyleItalic] :: ChunkStyle -> !Maybe Bool
+ Text.Colour.Chunk: [chunkStyleOverlined] :: ChunkStyle -> !Maybe Bool
+ Text.Colour.Chunk: [chunkStyleStrikethrough] :: ChunkStyle -> !Maybe Bool
+ Text.Colour.Chunk: [chunkStyleSwapForegroundBackground] :: ChunkStyle -> !Maybe Bool
+ Text.Colour.Chunk: [chunkStyleUnderlining] :: ChunkStyle -> !Maybe Underlining
+ Text.Colour.Chunk: [chunkStyle] :: Chunk -> !ChunkStyle
+ Text.Colour.Chunk: data ChunkStyle
+ Text.Colour.Chunk: instance Data.Validity.Validity Text.Colour.Chunk.ChunkStyle
+ Text.Colour.Chunk: instance GHC.Classes.Eq Text.Colour.Chunk.ChunkStyle
+ Text.Colour.Chunk: instance GHC.Generics.Generic Text.Colour.Chunk.ChunkStyle
+ Text.Colour.Chunk: instance GHC.Show.Show Text.Colour.Chunk.ChunkStyle
+ Text.Colour.Chunk: noStyle :: ChunkStyle
+ Text.Colour.Chunk: plainStyle :: TerminalCapabilities -> ChunkStyle -> Bool
+ Text.Colour.Chunk: renderOsc8Close :: Builder
+ Text.Colour.Chunk: renderOsc8Open :: Text -> Builder
+ Text.Colour.Chunk: styleSGR :: TerminalCapabilities -> ChunkStyle -> [SGR]
- Text.Colour: Chunk :: !Text -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe ConsoleIntensity -> !Maybe Underlining -> !Maybe Blinking -> !Maybe Colour -> !Maybe Colour -> Chunk
+ Text.Colour: Chunk :: !Text -> !ChunkStyle -> Chunk
- Text.Colour.Chunk: Chunk :: !Text -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe Bool -> !Maybe ConsoleIntensity -> !Maybe Underlining -> !Maybe Blinking -> !Maybe Colour -> !Maybe Colour -> Chunk
+ Text.Colour.Chunk: Chunk :: !Text -> !ChunkStyle -> Chunk
Files
- CHANGELOG.md +18/−0
- safe-coloured-text.cabal +2/−2
- src/Text/Colour.hs +2/−0
- src/Text/Colour/Chunk.hs +102/−62
CHANGELOG.md view
@@ -1,5 +1,23 @@ # Changelog for safe-coloured-text +## [0.6.0.0] - 2026-05-13++### Added++* `chunkStyleHyperlink :: !(Maybe Text)` field in `ChunkStyle` for OSC 8 hyperlink URLs+* `renderOsc8Open` and `renderOsc8Close` for emitting OSC 8 terminal hyperlink sequences+* `renderChunkBuilder` now emits OSC 8 sequences when `chunkStyleHyperlink` is set++## [0.5.0.0] - 2026-04-12++### Changed++* Separated styling from text: `Chunk` now has `chunkText` and `chunkStyle` fields+* New `ChunkStyle` type holds all styling attributes (italic, bold, colours, etc.)+* Styling field names now use `chunkStyle` prefix (e.g. `chunkStyleItalic`)+* Added `noStyle` constructor and `plainStyle`, `styleSGR` functions+* Renamed `chunkSGR` to `styleSGR`+ ## [0.4.0.0] - 2026-04-12 ### Added
safe-coloured-text.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.37.0.+-- This file has been generated from package.yaml by hpack version 0.38.3. -- -- see: https://github.com/sol/hpack name: safe-coloured-text-version: 0.4.0.0+version: 0.6.0.0 synopsis: Safely output coloured text category: User Interfaces homepage: https://github.com/NorfairKing/safe-coloured-text#readme
src/Text/Colour.hs view
@@ -9,6 +9,8 @@ ( -- * Building chunks chunk, Chunk (..),+ ChunkStyle (..),+ noStyle, -- ** Styling
src/Text/Colour/Chunk.hs view
@@ -24,16 +24,7 @@ data Chunk = Chunk { chunkText :: !Text,- chunkItalic :: !(Maybe Bool),- chunkStrikethrough :: !(Maybe Bool),- chunkSwapForegroundBackground :: !(Maybe Bool),- chunkConcealed :: !(Maybe Bool),- chunkOverlined :: !(Maybe Bool),- chunkConsoleIntensity :: !(Maybe ConsoleIntensity),- chunkUnderlining :: !(Maybe Underlining),- chunkBlinking :: !(Maybe Blinking),- chunkForeground :: !(Maybe Colour),- chunkBackground :: !(Maybe Colour)+ chunkStyle :: !ChunkStyle } deriving (Show, Eq, Generic) @@ -42,28 +33,53 @@ instance IsString Chunk where fromString = chunk . fromString +data ChunkStyle = ChunkStyle+ { chunkStyleItalic :: !(Maybe Bool),+ chunkStyleStrikethrough :: !(Maybe Bool),+ chunkStyleSwapForegroundBackground :: !(Maybe Bool),+ chunkStyleConcealed :: !(Maybe Bool),+ chunkStyleOverlined :: !(Maybe Bool),+ chunkStyleConsoleIntensity :: !(Maybe ConsoleIntensity),+ chunkStyleUnderlining :: !(Maybe Underlining),+ chunkStyleBlinking :: !(Maybe Blinking),+ chunkStyleForeground :: !(Maybe Colour),+ chunkStyleBackground :: !(Maybe Colour),+ -- | OSC 8 hyperlink URL, if any.+ -- Rendered as @ESC]8;;\<url\>ESC\\@ before the text and @ESC]8;;ESC\\@ after.+ chunkStyleHyperlink :: !(Maybe Text)+ }+ deriving (Show, Eq, Generic)++instance Validity ChunkStyle+ -- TODO This is not correct because text-width is correct but it's a -- good place to put this so we can fix it later and it'll get fixed -- everywhere. chunkWidth :: Chunk -> Int chunkWidth = T.length . chunkText -plainChunk :: TerminalCapabilities -> Chunk -> Bool-plainChunk tc Chunk {..} =- let Chunk _ _ _ _ _ _ _ _ _ _ _ = undefined+plainStyle :: TerminalCapabilities -> ChunkStyle -> Bool+plainStyle tc ChunkStyle {..} =+ let ChunkStyle _ _ _ _ _ _ _ _ _ _ _ = undefined in and- [ isNothing chunkItalic,- isNothing chunkStrikethrough,- isNothing chunkSwapForegroundBackground,- isNothing chunkConcealed,- isNothing chunkOverlined,- isNothing chunkConsoleIntensity,- isNothing chunkUnderlining,- isNothing chunkBlinking,- maybe True (plainColour tc) chunkForeground,- maybe True (plainColour tc) chunkBackground+ [ isNothing chunkStyleItalic,+ isNothing chunkStyleStrikethrough,+ isNothing chunkStyleSwapForegroundBackground,+ isNothing chunkStyleConcealed,+ isNothing chunkStyleOverlined,+ isNothing chunkStyleConsoleIntensity,+ isNothing chunkStyleUnderlining,+ isNothing chunkStyleBlinking,+ maybe True (plainColour tc) chunkStyleForeground,+ maybe True (plainColour tc) chunkStyleBackground,+ isNothing chunkStyleHyperlink ] +plainChunk :: TerminalCapabilities -> Chunk -> Bool+plainChunk tc Chunk {..} =+ let Chunk _ _ = undefined+ in plainStyle tc chunkStyle+ plainColour :: TerminalCapabilities -> Colour -> Bool plainColour tc = \case Colour8 {} -> tc < With8Colours@@ -113,24 +129,40 @@ then LTB.fromText chunkText else mconcat- [ renderCSI (SGR (chunkSGR tc c)),+ [ maybe mempty renderOsc8Open (chunkStyleHyperlink chunkStyle),+ renderCSI (SGR (styleSGR tc chunkStyle)), LTB.fromText chunkText,- renderCSI (SGR [Reset])+ renderCSI (SGR [Reset]),+ maybe mempty (const renderOsc8Close) (chunkStyleHyperlink chunkStyle) ] -chunkSGR :: TerminalCapabilities -> Chunk -> [SGR]-chunkSGR tc Chunk {..} =+-- | Render an OSC 8 hyperlink open sequence: @ESC]8;;<url>ESC\@.+-- An empty URL renders the close sequence.+renderOsc8Open :: Text -> Text.Builder+renderOsc8Open url =+ mconcat+ [ LTB.fromText (T.pack "\ESC]8;;"),+ LTB.fromText url,+ LTB.fromText (T.pack "\ESC\\")+ ]++-- | Render an OSC 8 hyperlink close sequence: @ESC]8;;ESC\@.+renderOsc8Close :: Text.Builder+renderOsc8Close = LTB.fromText (T.pack "\ESC]8;;\ESC\\")++styleSGR :: TerminalCapabilities -> ChunkStyle -> [SGR]+styleSGR tc ChunkStyle {..} = catMaybes- [ SetItalic <$> chunkItalic,- SetStrikethrough <$> chunkStrikethrough,- SetSwapForegroundBackground <$> chunkSwapForegroundBackground,- SetConcealed <$> chunkConcealed,- SetOverlined <$> chunkOverlined,- SetUnderlining <$> chunkUnderlining,- SetBlinking <$> chunkBlinking,- SetConsoleIntensity <$> chunkConsoleIntensity,- chunkForeground >>= colourSGR tc Foreground,- chunkBackground >>= colourSGR tc Background+ [ SetItalic <$> chunkStyleItalic,+ SetStrikethrough <$> chunkStyleStrikethrough,+ SetSwapForegroundBackground <$> chunkStyleSwapForegroundBackground,+ SetConcealed <$> chunkStyleConcealed,+ SetOverlined <$> chunkStyleOverlined,+ SetUnderlining <$> chunkStyleUnderlining,+ SetBlinking <$> chunkStyleBlinking,+ SetConsoleIntensity <$> chunkStyleConsoleIntensity,+ chunkStyleForeground >>= colourSGR tc Foreground,+ chunkStyleBackground >>= colourSGR tc Background ] -- | Turn a text into a plain chunk, without any styling@@ -138,62 +170,70 @@ chunk t = Chunk { chunkText = t,- chunkItalic = Nothing,- chunkStrikethrough = Nothing,- chunkSwapForegroundBackground = Nothing,- chunkConcealed = Nothing,- chunkOverlined = Nothing,- chunkConsoleIntensity = Nothing,- chunkUnderlining = Nothing,- chunkBlinking = Nothing,- chunkForeground = Nothing,- chunkBackground = Nothing+ chunkStyle = noStyle } +-- | The empty style, without any styling+noStyle :: ChunkStyle+noStyle =+ ChunkStyle+ { chunkStyleItalic = Nothing,+ chunkStyleStrikethrough = Nothing,+ chunkStyleSwapForegroundBackground = Nothing,+ chunkStyleConcealed = Nothing,+ chunkStyleOverlined = Nothing,+ chunkStyleConsoleIntensity = Nothing,+ chunkStyleUnderlining = Nothing,+ chunkStyleBlinking = Nothing,+ chunkStyleForeground = Nothing,+ chunkStyleBackground = Nothing,+ chunkStyleHyperlink = Nothing+ }+ fore :: Colour -> Chunk -> Chunk-fore col chu = chu {chunkForeground = Just col}+fore col chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleForeground = Just col}} back :: Colour -> Chunk -> Chunk-back col chu = chu {chunkBackground = Just col}+back col chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleBackground = Just col}} bold :: Chunk -> Chunk-bold chu = chu {chunkConsoleIntensity = Just BoldIntensity}+bold chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleConsoleIntensity = Just BoldIntensity}} faint :: Chunk -> Chunk-faint chu = chu {chunkConsoleIntensity = Just FaintIntensity}+faint chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleConsoleIntensity = Just FaintIntensity}} italic :: Chunk -> Chunk-italic chu = chu {chunkItalic = Just True}+italic chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleItalic = Just True}} strikethrough :: Chunk -> Chunk-strikethrough chu = chu {chunkStrikethrough = Just True}+strikethrough chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleStrikethrough = Just True}} swapForegroundBackground :: Chunk -> Chunk-swapForegroundBackground chu = chu {chunkSwapForegroundBackground = Just True}+swapForegroundBackground chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleSwapForegroundBackground = Just True}} concealed :: Chunk -> Chunk-concealed chu = chu {chunkConcealed = Just True}+concealed chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleConcealed = Just True}} overlined :: Chunk -> Chunk-overlined chu = chu {chunkOverlined = Just True}+overlined chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleOverlined = Just True}} underline :: Chunk -> Chunk-underline chu = chu {chunkUnderlining = Just SingleUnderline}+underline chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleUnderlining = Just SingleUnderline}} doubleUnderline :: Chunk -> Chunk-doubleUnderline chu = chu {chunkUnderlining = Just DoubleUnderline}+doubleUnderline chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleUnderlining = Just DoubleUnderline}} noUnderline :: Chunk -> Chunk-noUnderline chu = chu {chunkUnderlining = Just NoUnderline}+noUnderline chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleUnderlining = Just NoUnderline}} slowBlinking :: Chunk -> Chunk-slowBlinking chu = chu {chunkBlinking = Just SlowBlinking}+slowBlinking chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleBlinking = Just SlowBlinking}} rapidBlinking :: Chunk -> Chunk-rapidBlinking chu = chu {chunkBlinking = Just RapidBlinking}+rapidBlinking chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleBlinking = Just RapidBlinking}} noBlinking :: Chunk -> Chunk-noBlinking chu = chu {chunkBlinking = Just NoBlinking}+noBlinking chu = chu {chunkStyle = (chunkStyle chu) {chunkStyleBlinking = Just NoBlinking}} -- TODO consider allowing an 8-colour alternative to a given 256-colour data Colour