-
Notifications
You must be signed in to change notification settings - Fork 227
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This introduces a helper type `LightDark` that takes a boolean to determine which `Color(light, dark)` to choose from. The `adaptive` package is a helper package that uses the `lipgloss.LightDark` along with querying the terminal when the module is imported to choose the appropriate light-dark color. Example: ```go var ( light = "#0000ff" dark = "#ff0000" ) colorToUse := adaptive.Color(light, dark) // the terminal is queried before choosing the color fmt.Println(colorToUse) ```
- Loading branch information
1 parent
b89f1a3
commit 3014273
Showing
8 changed files
with
228 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package adaptive | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/charmbracelet/lipgloss" | ||
"github.com/charmbracelet/x/term" | ||
) | ||
|
||
// Stdin, Stdout, and HasDarkBackground are the standard input, output, and | ||
// default background color value to use. They can be overridden by the | ||
// importing program. | ||
var ( | ||
Stdin = os.Stdin | ||
Stdout = os.Stdout | ||
HasDarkBackground = true | ||
) | ||
|
||
// colorFn is the light-dark Lip Gloss color function to use to determine the | ||
// appropriate color based on the terminal's background color. | ||
// When a program imports this package, it will query the terminal's background | ||
// color and use it to determine whether to use the light or dark color. | ||
var colorFn lipgloss.LightDark | ||
|
||
func init() { | ||
Query() | ||
} | ||
|
||
// Query queries the terminal's background color and updates the color function | ||
// accordingly. | ||
func Query() { | ||
colorFn = lipgloss.LightDark(func() bool { | ||
state, err := term.MakeRaw(Stdin.Fd()) | ||
if err != nil { | ||
return HasDarkBackground | ||
} | ||
|
||
defer term.Restore(Stdin.Fd(), state) //nolint:errcheck | ||
|
||
bg, err := queryBackgroundColor(Stdin, Stdout) | ||
if err == nil { | ||
return lipgloss.IsDarkColor(bg) | ||
} | ||
|
||
return HasDarkBackground | ||
}()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package adaptive | ||
|
||
import ( | ||
"image/color" | ||
) | ||
|
||
// Color returns the color that should be used based on the terminal's | ||
// background color. | ||
func Color(light, dark any) color.Color { | ||
return colorFn.Color(light, dark) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package adaptive | ||
|
||
import ( | ||
"image/color" | ||
"io" | ||
"time" | ||
|
||
"github.com/charmbracelet/x/ansi" | ||
"github.com/charmbracelet/x/input" | ||
) | ||
|
||
// queryBackgroundColor queries the terminal for the background color. | ||
// If the terminal does not support querying the background color, nil is | ||
// returned. | ||
// | ||
// Note: you will need to set the input to raw mode before calling this | ||
// function. | ||
// | ||
// state, _ := term.MakeRaw(in.Fd()) | ||
// defer term.Restore(in.Fd(), state) | ||
// | ||
// copied from x/[email protected]. | ||
func queryBackgroundColor(in io.Reader, out io.Writer) (c color.Color, err error) { | ||
// nolint: errcheck | ||
err = queryTerminal(in, out, defaultQueryTimeout, | ||
func(events []input.Event) bool { | ||
for _, e := range events { | ||
switch e := e.(type) { | ||
case input.BackgroundColorEvent: | ||
c = e.Color | ||
continue // we need to consume the next DA1 event | ||
case input.PrimaryDeviceAttributesEvent: | ||
return false | ||
} | ||
} | ||
return true | ||
}, ansi.RequestBackgroundColor+ansi.RequestPrimaryDeviceAttributes) | ||
return | ||
} | ||
|
||
const defaultQueryTimeout = time.Second * 2 | ||
|
||
// queryTerminalFilter is a function that filters input events using a type | ||
// switch. If false is returned, the QueryTerminal function will stop reading | ||
// input. | ||
type queryTerminalFilter func(events []input.Event) bool | ||
|
||
// queryTerminal queries the terminal for support of various features and | ||
// returns a list of response events. | ||
// Most of the time, you will need to set stdin to raw mode before calling this | ||
// function. | ||
// Note: This function will block until the terminal responds or the timeout | ||
// is reached. | ||
// copied from x/[email protected]. | ||
func queryTerminal( | ||
in io.Reader, | ||
out io.Writer, | ||
timeout time.Duration, | ||
filter queryTerminalFilter, | ||
query string, | ||
) error { | ||
rd, err := input.NewDriver(in, "", 0) | ||
if err != nil { | ||
return err | ||
Check failure on line 64 in adaptive/terminal.go GitHub Actions / lint-soft
Check failure on line 64 in adaptive/terminal.go GitHub Actions / lint-soft
|
||
} | ||
|
||
defer rd.Close() // nolint: errcheck | ||
|
||
done := make(chan struct{}, 1) | ||
defer close(done) | ||
go func() { | ||
select { | ||
case <-done: | ||
case <-time.After(timeout): | ||
rd.Cancel() | ||
} | ||
}() | ||
|
||
if _, err := io.WriteString(out, query); err != nil { | ||
return err | ||
Check failure on line 80 in adaptive/terminal.go GitHub Actions / lint-soft
|
||
} | ||
|
||
for { | ||
events, err := rd.ReadEvents() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if !filter(events) { | ||
break | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters