2021-03-02 02:29:00 +03:00
|
|
|
package lipgloss
|
|
|
|
|
2021-03-04 03:23:52 +03:00
|
|
|
import (
|
2021-04-05 16:39:11 +03:00
|
|
|
"sync"
|
|
|
|
|
2021-03-04 03:23:52 +03:00
|
|
|
"github.com/muesli/termenv"
|
|
|
|
)
|
2021-03-02 02:29:00 +03:00
|
|
|
|
2021-03-04 02:58:45 +03:00
|
|
|
var (
|
2021-05-31 19:27:03 +03:00
|
|
|
colorProfile termenv.Profile
|
|
|
|
getColorProfile sync.Once
|
|
|
|
explicitColorProfile bool
|
2021-04-17 04:18:50 +03:00
|
|
|
|
2022-02-04 06:48:20 +03:00
|
|
|
hasDarkBackground bool
|
|
|
|
getBackgroundColor sync.Once
|
|
|
|
explicitBackgroundColor bool
|
|
|
|
|
2022-02-04 07:07:04 +03:00
|
|
|
colorProfileMtx sync.RWMutex
|
2021-03-04 02:58:45 +03:00
|
|
|
)
|
|
|
|
|
2021-04-05 16:39:11 +03:00
|
|
|
// ColorProfile returns the detected termenv color profile. It will perform the
|
|
|
|
// actual check only once.
|
|
|
|
func ColorProfile() termenv.Profile {
|
2022-02-04 07:07:04 +03:00
|
|
|
colorProfileMtx.RLock()
|
|
|
|
defer colorProfileMtx.RUnlock()
|
|
|
|
|
2021-05-31 19:27:03 +03:00
|
|
|
if !explicitColorProfile {
|
|
|
|
getColorProfile.Do(func() {
|
2022-02-06 02:37:46 +03:00
|
|
|
colorProfile = termenv.EnvColorProfile()
|
2021-05-31 19:27:03 +03:00
|
|
|
})
|
|
|
|
}
|
2021-04-05 16:39:11 +03:00
|
|
|
return colorProfile
|
|
|
|
}
|
|
|
|
|
2021-05-31 19:27:03 +03:00
|
|
|
// SetColorProfile sets the color profile on a package-wide context. This
|
|
|
|
// function exists mostly for testing purposes so that you can assure you're
|
|
|
|
// testing against a specific profile.
|
|
|
|
//
|
|
|
|
// Outside of testing you likely won't want to use this function as
|
|
|
|
// ColorProfile() will detect and cache the terminal's color capabilities
|
|
|
|
// and choose the best available profile.
|
|
|
|
//
|
|
|
|
// Available color profiles are:
|
|
|
|
//
|
|
|
|
// termenv.Ascii (no color, 1-bit)
|
|
|
|
// termenv.ANSI (16 colors, 4-bit)
|
|
|
|
// termenv.ANSI256 (256 colors, 8-bit)
|
|
|
|
// termenv.TrueColor (16,777,216 colors, 24-bit)
|
|
|
|
//
|
|
|
|
// This function is thread-safe.
|
|
|
|
func SetColorProfile(p termenv.Profile) {
|
|
|
|
colorProfileMtx.Lock()
|
|
|
|
defer colorProfileMtx.Unlock()
|
2022-02-04 07:07:04 +03:00
|
|
|
|
2021-05-31 19:27:03 +03:00
|
|
|
colorProfile = p
|
|
|
|
explicitColorProfile = true
|
|
|
|
}
|
|
|
|
|
2022-02-04 06:48:20 +03:00
|
|
|
// HasDarkBackground returns whether or not the terminal has a dark background.
|
2021-04-05 16:39:11 +03:00
|
|
|
func HasDarkBackground() bool {
|
2022-02-04 07:07:04 +03:00
|
|
|
colorProfileMtx.RLock()
|
|
|
|
defer colorProfileMtx.RUnlock()
|
|
|
|
|
2022-02-04 06:48:20 +03:00
|
|
|
if !explicitBackgroundColor {
|
|
|
|
getBackgroundColor.Do(func() {
|
|
|
|
hasDarkBackground = termenv.HasDarkBackground()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-05 16:39:11 +03:00
|
|
|
return hasDarkBackground
|
|
|
|
}
|
|
|
|
|
2022-02-04 06:48:20 +03:00
|
|
|
// SetHasDarkBackground sets the value of the background color detection on a
|
|
|
|
// package-wide context. This function exists mostly for testing purposes so
|
|
|
|
// that you can assure you're testing against a specific background color
|
|
|
|
// setting.
|
|
|
|
//
|
|
|
|
// Outside of testing you likely won't want to use this function as
|
|
|
|
// HasDarkBackground() will detect and cache the terminal's current background
|
|
|
|
// color setting.
|
|
|
|
//
|
|
|
|
// This function is thread-safe.
|
|
|
|
func SetHasDarkBackground(b bool) {
|
|
|
|
colorProfileMtx.Lock()
|
|
|
|
defer colorProfileMtx.Unlock()
|
2022-02-04 07:07:04 +03:00
|
|
|
|
2022-02-04 06:48:20 +03:00
|
|
|
hasDarkBackground = b
|
|
|
|
explicitBackgroundColor = true
|
|
|
|
}
|
|
|
|
|
2021-03-31 05:28:54 +03:00
|
|
|
// TerminalColor is a color intended to be rendered in the terminal. It
|
|
|
|
// satisfies the Go color.Color interface.
|
2021-03-31 05:19:38 +03:00
|
|
|
type TerminalColor interface {
|
2021-03-02 02:29:00 +03:00
|
|
|
value() string
|
2021-03-26 22:57:47 +03:00
|
|
|
color() termenv.Color
|
2021-03-31 05:28:54 +03:00
|
|
|
RGBA() (r, g, b, a uint32)
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// NoColor is used to specify the absence of color styling. When this is active
|
|
|
|
// foreground colors will be rendered with the terminal's default text color,
|
|
|
|
// and background colors will not be drawn at all.
|
|
|
|
//
|
|
|
|
// Example usage:
|
|
|
|
//
|
2022-08-24 19:05:18 +03:00
|
|
|
// var style = someStyle.Copy().Background(lipgloss.NoColor{})
|
2021-03-18 21:34:39 +03:00
|
|
|
type NoColor struct{}
|
2021-03-02 02:29:00 +03:00
|
|
|
|
2021-03-18 21:34:39 +03:00
|
|
|
func (n NoColor) value() string {
|
2021-03-02 02:29:00 +03:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2021-03-26 22:57:47 +03:00
|
|
|
func (n NoColor) color() termenv.Color {
|
2021-04-05 16:39:11 +03:00
|
|
|
return ColorProfile().Color("")
|
2021-03-26 22:57:47 +03:00
|
|
|
}
|
|
|
|
|
2021-03-31 05:28:54 +03:00
|
|
|
// RGBA returns the RGBA value of this color. Because we have to return
|
|
|
|
// something, despite this color being the absence of color, we're returning
|
2022-08-30 23:38:15 +03:00
|
|
|
// black with 100% opacity.
|
2021-04-02 17:05:53 +03:00
|
|
|
//
|
|
|
|
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
2021-03-31 05:28:54 +03:00
|
|
|
func (n NoColor) RGBA() (r, g, b, a uint32) {
|
2021-04-02 17:05:53 +03:00
|
|
|
return 0x0, 0x0, 0x0, 0xFFFF
|
2021-03-31 05:28:54 +03:00
|
|
|
}
|
|
|
|
|
2021-03-18 21:34:39 +03:00
|
|
|
var noColor = NoColor{}
|
|
|
|
|
2021-03-02 02:29:00 +03:00
|
|
|
// Color specifies a color by hex or ANSI value. For example:
|
|
|
|
//
|
2022-08-24 19:05:18 +03:00
|
|
|
// ansiColor := lipgloss.Color("21")
|
|
|
|
// hexColor := lipgloss.Color("#0000ff")
|
2021-03-02 02:29:00 +03:00
|
|
|
type Color string
|
|
|
|
|
|
|
|
func (c Color) value() string {
|
|
|
|
return string(c)
|
|
|
|
}
|
|
|
|
|
2021-03-26 22:57:47 +03:00
|
|
|
func (c Color) color() termenv.Color {
|
2021-04-05 16:39:11 +03:00
|
|
|
return ColorProfile().Color(string(c))
|
2021-03-26 22:57:47 +03:00
|
|
|
}
|
|
|
|
|
2021-04-02 17:05:53 +03:00
|
|
|
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
|
|
|
// interface. Note that on error we return black with 100% opacity, or:
|
|
|
|
//
|
2022-07-06 02:11:11 +03:00
|
|
|
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFF.
|
2021-03-31 05:28:54 +03:00
|
|
|
func (c Color) RGBA() (r, g, b, a uint32) {
|
2022-09-30 05:40:12 +03:00
|
|
|
return termenv.ConvertToRGB(c.color()).RGBA()
|
2021-03-31 05:28:54 +03:00
|
|
|
}
|
|
|
|
|
2021-03-26 17:36:23 +03:00
|
|
|
// AdaptiveColor provides color options for light and dark backgrounds. The
|
2022-02-06 02:35:33 +03:00
|
|
|
// appropriate color will be returned at runtime based on the darkness of the
|
2021-03-31 05:28:54 +03:00
|
|
|
// terminal background color.
|
2021-03-02 02:29:00 +03:00
|
|
|
//
|
|
|
|
// Example usage:
|
|
|
|
//
|
2022-08-24 19:05:18 +03:00
|
|
|
// color := lipgloss.AdaptiveColor{Light: "#0000ff", Dark: "#000099"}
|
2021-03-02 02:29:00 +03:00
|
|
|
type AdaptiveColor struct {
|
|
|
|
Light string
|
|
|
|
Dark string
|
|
|
|
}
|
|
|
|
|
2021-03-31 05:28:54 +03:00
|
|
|
func (ac AdaptiveColor) value() string {
|
2021-04-05 16:39:11 +03:00
|
|
|
if HasDarkBackground() {
|
2021-03-31 05:28:54 +03:00
|
|
|
return ac.Dark
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-31 05:28:54 +03:00
|
|
|
return ac.Light
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ac AdaptiveColor) color() termenv.Color {
|
2021-04-05 16:39:11 +03:00
|
|
|
return ColorProfile().Color(ac.value())
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-26 22:57:47 +03:00
|
|
|
|
2021-04-02 17:05:53 +03:00
|
|
|
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
|
|
|
// interface. Note that on error we return black with 100% opacity, or:
|
|
|
|
//
|
2022-07-06 02:11:11 +03:00
|
|
|
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFF.
|
2021-03-31 05:28:54 +03:00
|
|
|
func (ac AdaptiveColor) RGBA() (r, g, b, a uint32) {
|
2022-09-30 05:40:12 +03:00
|
|
|
return termenv.ConvertToRGB(ac.color()).RGBA()
|
2021-03-26 22:57:47 +03:00
|
|
|
}
|
2022-07-06 02:11:11 +03:00
|
|
|
|
2022-08-30 23:38:15 +03:00
|
|
|
// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
|
|
|
|
// profiles. Automatic color degredation will not be performed.
|
|
|
|
type CompleteColor struct {
|
|
|
|
TrueColor string
|
|
|
|
ANSI256 string
|
|
|
|
ANSI string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c CompleteColor) value() string {
|
|
|
|
switch ColorProfile() {
|
|
|
|
case termenv.TrueColor:
|
|
|
|
return c.TrueColor
|
|
|
|
case termenv.ANSI256:
|
|
|
|
return c.ANSI256
|
|
|
|
case termenv.ANSI:
|
|
|
|
return c.ANSI
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c CompleteColor) color() termenv.Color {
|
|
|
|
return colorProfile.Color(c.value())
|
|
|
|
}
|
|
|
|
|
|
|
|
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
|
|
|
// interface. Note that on error we return black with 100% opacity, or:
|
|
|
|
//
|
|
|
|
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF
|
|
|
|
func (c CompleteColor) RGBA() (r, g, b, a uint32) {
|
2022-09-30 05:40:12 +03:00
|
|
|
return termenv.ConvertToRGB(c.color()).RGBA()
|
2022-08-30 23:38:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
|
|
|
|
// profiles, with separate options for light and dark backgrounds. Automatic
|
|
|
|
// color degredation will not be performed.
|
|
|
|
type CompleteAdaptiveColor struct {
|
|
|
|
Light CompleteColor
|
|
|
|
Dark CompleteColor
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cac CompleteAdaptiveColor) value() string {
|
|
|
|
if HasDarkBackground() {
|
|
|
|
return cac.Dark.value()
|
|
|
|
}
|
|
|
|
return cac.Light.value()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cac CompleteAdaptiveColor) color() termenv.Color {
|
|
|
|
return ColorProfile().Color(cac.value())
|
|
|
|
}
|
|
|
|
|
|
|
|
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
|
|
|
// interface. Note that on error we return black with 100% opacity, or:
|
|
|
|
//
|
|
|
|
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF
|
|
|
|
func (cac CompleteAdaptiveColor) RGBA() (r, g, b, a uint32) {
|
2022-09-30 05:40:12 +03:00
|
|
|
return termenv.ConvertToRGB(cac.color()).RGBA()
|
2022-07-06 02:11:11 +03:00
|
|
|
}
|