pax_global_header 0000666 0000000 0000000 00000000064 14064763020 0014514 g ustar 00root root 0000000 0000000 52 comment=1d2849bf91209e37a109120228777b3fa37fd8ff termenv-0.9.0/ 0000775 0000000 0000000 00000000000 14064763020 0013202 5 ustar 00root root 0000000 0000000 termenv-0.9.0/.github/ 0000775 0000000 0000000 00000000000 14064763020 0014542 5 ustar 00root root 0000000 0000000 termenv-0.9.0/.github/FUNDING.yml 0000664 0000000 0000000 00000000017 14064763020 0016355 0 ustar 00root root 0000000 0000000 github: muesli termenv-0.9.0/.github/workflows/ 0000775 0000000 0000000 00000000000 14064763020 0016577 5 ustar 00root root 0000000 0000000 termenv-0.9.0/.github/workflows/build.yml 0000664 0000000 0000000 00000001145 14064763020 0020422 0 ustar 00root root 0000000 0000000 name: build on: [push, pull_request] jobs: build: strategy: matrix: go-version: [~1.11, ^1] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} env: GO111MODULE: "on" steps: - name: Install Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v2 - name: Download Go modules run: go mod download - name: Build run: go build -v ./... - name: Test run: go test ./... if: matrix.platform != 'windows-latest' termenv-0.9.0/.github/workflows/coverage.yml 0000664 0000000 0000000 00000001321 14064763020 0021112 0 ustar 00root root 0000000 0000000 name: coverage on: [push, pull_request] jobs: coverage: strategy: matrix: go-version: [^1] os: [ubuntu-latest] runs-on: ${{ matrix.os }} env: GO111MODULE: "on" steps: - name: Install Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v2 - name: Coverage env: COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | go test -race -covermode atomic -coverprofile=profile.cov ./... GO111MODULE=off go get github.com/mattn/goveralls $(go env GOPATH)/bin/goveralls -coverprofile=profile.cov -service=github termenv-0.9.0/.github/workflows/lint.yml 0000664 0000000 0000000 00000001336 14064763020 0020273 0 ustar 00root root 0000000 0000000 name: lint on: push: pull_request: jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: v1.31 # Optional: golangci-lint command line arguments. args: --issues-exit-code=0 # Optional: working directory, useful for monorepos # working-directory: somedir # Optional: show only new issues if it's a pull request. The default value is `false`. only-new-issues: true termenv-0.9.0/.gitignore 0000664 0000000 0000000 00000000415 14064763020 0015172 0 ustar 00root root 0000000 0000000 # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ termenv-0.9.0/.golangci.yml 0000664 0000000 0000000 00000000535 14064763020 0015571 0 ustar 00root root 0000000 0000000 run: tests: false issues: max-issues-per-linter: 0 max-same-issues: 0 linters: enable: - bodyclose - dupl - exportloopref - goconst - godot - godox - goimports - goprintffuncname - gosec - misspell - prealloc - rowserrcheck - sqlclosecheck - unconvert - unparam - whitespace termenv-0.9.0/LICENSE 0000664 0000000 0000000 00000002067 14064763020 0014214 0 ustar 00root root 0000000 0000000 MIT License Copyright (c) 2019 Christian Muehlhaeuser Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. termenv-0.9.0/README.md 0000664 0000000 0000000 00000016014 14064763020 0014463 0 ustar 00root root 0000000 0000000
`termenv` lets you safely use advanced styling options on the terminal. It gathers information about the terminal environment in terms of its ANSI & color support and offers you convenient methods to colorize and style your output, without you having to deal with all kinds of weird ANSI escape sequences and color conversions.  ## Features - RGB/TrueColor support - Detects the supported color range of your terminal - Automatically converts colors to the best matching, available colors - Terminal theme (light/dark) detection - Chainable syntax - Nested styles ## Installation ```bash go get github.com/muesli/termenv ``` ## Query Terminal Support `termenv` can query the terminal it is running in, so you can safely use advanced features, like RGB colors. `ColorProfile` returns the color profile supported by the terminal: ```go profile := termenv.ColorProfile() ``` This returns one of the supported color profiles: - `termenv.Ascii` - no ANSI support detected, ASCII only - `termenv.ANSI` - 16 color ANSI support - `termenv.ANSI256` - Extended 256 color ANSI support - `termenv.TrueColor` - RGB/TrueColor support You can also query the terminal for its color scheme, so you know whether your app is running in a light- or dark-themed environment: ```go // Returns terminal's foreground color color := termenv.ForegroundColor() // Returns terminal's background color color := termenv.BackgroundColor() // Returns whether terminal uses a dark-ish background darkTheme := termenv.HasDarkBackground() ``` ## Colors `termenv` supports multiple color profiles: ANSI (16 colors), ANSI Extended (256 colors), and TrueColor (24-bit RGB). Colors will automatically be degraded to the best matching available color in the desired profile: `TrueColor` => `ANSI 256 Colors` => `ANSI 16 Colors` => `Ascii` ```go s := termenv.String("Hello World") // Retrieve color profile supported by terminal p := termenv.ColorProfile() // Supports hex values // Will automatically degrade colors on terminals not supporting RGB s.Foreground(p.Color("#abcdef")) // but also supports ANSI colors (0-255) s.Background(p.Color("69")) // ...or the color.Color interface s.Foreground(p.FromColor(color.RGBA{255, 128, 0, 255})) // Combine fore- & background colors s.Foreground(p.Color("#ffffff")).Background(p.Color("#0000ff")) // Supports the fmt.Stringer interface fmt.Println(s) ``` ## Styles You can use a chainable syntax to compose your own styles: ```go s := termenv.String("foobar") // Text styles s.Bold() s.Faint() s.Italic() s.CrossOut() s.Underline() s.Overline() // Reverse swaps current fore- & background colors s.Reverse() // Blinking text s.Blink() // Combine multiple options s.Bold().Underline() ``` ## Template Helpers ```go // load template helpers f := termenv.TemplateFuncs(termenv.ColorProfile()) tpl := template.New("tpl").Funcs(f) // apply bold style in a template bold := `{{ Bold "Hello World" }}` // examples for colorized templates col := `{{ Color "#ff0000" "#0000ff" "Red on Blue" }}` fg := `{{ Foreground "#ff0000" "Red Foreground" }}` bg := `{{ Background "#0000ff" "Blue Background" }}` // wrap styles wrap := `{{ Bold (Underline "Hello World") }}` // parse and render tpl, err = tpl.Parse(bold) var buf bytes.Buffer tpl.Execute(&buf, nil) fmt.Println(&buf) ``` Other available helper functions are: `Faint`, `Italic`, `CrossOut`, `Underline`, `Overline`, `Reverse`, and `Blink`. ## Screen ```go // Reset the terminal to its default style, removing any active styles termenv.Reset() // Switch to the altscreen. The former view can be restored with ExitAltScreen() termenv.AltScreen() // Exit the altscreen and return to the former terminal view termenv.ExitAltScreen() // Clear the visible portion of the terminal termenv.ClearScreen() // Move the cursor to a given position termenv.MoveCursor(row, column) // Hide the cursor termenv.HideCursor() // Show the cursor termenv.ShowCursor() // Save the cursor position termenv.SaveCursorPosition() // Restore a saved cursor position termenv.RestoreCursorPosition() // Move the cursor up a given number of lines termenv.CursorUp(n) // Move the cursor down a given number of lines termenv.CursorDown(n) // Move the cursor up a given number of lines termenv.CursorForward(n) // Move the cursor backwards a given number of cells termenv.CursorBack(n) // Move the cursor down a given number of lines and place it at the beginning // of the line termenv.CursorNextLine(n) // Move the cursor up a given number of lines and place it at the beginning of // the line termenv.CursorPrevLine(n) // Clear the current line termenv.ClearLine() // Clear a given number of lines termenv.ClearLines(n) // Set the scrolling region of the terminal termenv.ChangeScrollingRegion(top, bottom) // Insert the given number of lines at the top of the scrollable region, pushing // lines below down termenv.InsertLines(n) // Delete the given number of lines, pulling any lines in the scrollable region // below up termenv.DeleteLines(n) ``` ## Mouse ```go // Enable X10 mouse mode, only button press events are sent termenv.EnableMousePress() // Disable X10 mouse mode termenv.DisableMousePress() // Enable Mouse Tracking mode termenv.EnableMouse() // Disable Mouse Tracking mode termenv.DisableMouse() // Enable Hilite Mouse Tracking mode termenv.EnableMouseHilite() // Disable Hilite Mouse Tracking mode termenv.DisableMouseHilite() // Enable Cell Motion Mouse Tracking mode termenv.EnableMouseCellMotion() // Disable Cell Motion Mouse Tracking mode termenv.DisableMouseCellMotion() // Enable All Motion Mouse mode termenv.EnableMouseAllMotion() // Disable All Motion Mouse mode termenv.DisableMouseAllMotion() ``` ## Color Chart  You can find the source code used to create this chart in `termenv`'s examples. ## Related Projects - [reflow](https://github.com/muesli/reflow) - ANSI-aware text operations - [Glow](https://github.com/charmbracelet/glow) - a markdown renderer for the command-line, which uses `termenv` ## License [MIT](https://github.com/muesli/termenv/raw/master/LICENSE) termenv-0.9.0/ansicolors.go 0000664 0000000 0000000 00000006513 14064763020 0015712 0 ustar 00root root 0000000 0000000 package termenv const ( ANSIBlack ANSIColor = iota ANSIRed ANSIGreen ANSIYellow ANSIBlue ANSIMagenta ANSICyan ANSIWhite ANSIBrightBlack ANSIBrightRed ANSIBrightGreen ANSIBrightYellow ANSIBrightBlue ANSIBrightMagenta ANSIBrightCyan ANSIBrightWhite ) // RGB values of ANSI colors (0-255). var ansiHex = []string{ "#000000", "#800000", "#008000", "#808000", "#000080", "#800080", "#008080", "#c0c0c0", "#808080", "#ff0000", "#00ff00", "#ffff00", "#0000ff", "#ff00ff", "#00ffff", "#ffffff", "#000000", "#00005f", "#000087", "#0000af", "#0000d7", "#0000ff", "#005f00", "#005f5f", "#005f87", "#005faf", "#005fd7", "#005fff", "#008700", "#00875f", "#008787", "#0087af", "#0087d7", "#0087ff", "#00af00", "#00af5f", "#00af87", "#00afaf", "#00afd7", "#00afff", "#00d700", "#00d75f", "#00d787", "#00d7af", "#00d7d7", "#00d7ff", "#00ff00", "#00ff5f", "#00ff87", "#00ffaf", "#00ffd7", "#00ffff", "#5f0000", "#5f005f", "#5f0087", "#5f00af", "#5f00d7", "#5f00ff", "#5f5f00", "#5f5f5f", "#5f5f87", "#5f5faf", "#5f5fd7", "#5f5fff", "#5f8700", "#5f875f", "#5f8787", "#5f87af", "#5f87d7", "#5f87ff", "#5faf00", "#5faf5f", "#5faf87", "#5fafaf", "#5fafd7", "#5fafff", "#5fd700", "#5fd75f", "#5fd787", "#5fd7af", "#5fd7d7", "#5fd7ff", "#5fff00", "#5fff5f", "#5fff87", "#5fffaf", "#5fffd7", "#5fffff", "#870000", "#87005f", "#870087", "#8700af", "#8700d7", "#8700ff", "#875f00", "#875f5f", "#875f87", "#875faf", "#875fd7", "#875fff", "#878700", "#87875f", "#878787", "#8787af", "#8787d7", "#8787ff", "#87af00", "#87af5f", "#87af87", "#87afaf", "#87afd7", "#87afff", "#87d700", "#87d75f", "#87d787", "#87d7af", "#87d7d7", "#87d7ff", "#87ff00", "#87ff5f", "#87ff87", "#87ffaf", "#87ffd7", "#87ffff", "#af0000", "#af005f", "#af0087", "#af00af", "#af00d7", "#af00ff", "#af5f00", "#af5f5f", "#af5f87", "#af5faf", "#af5fd7", "#af5fff", "#af8700", "#af875f", "#af8787", "#af87af", "#af87d7", "#af87ff", "#afaf00", "#afaf5f", "#afaf87", "#afafaf", "#afafd7", "#afafff", "#afd700", "#afd75f", "#afd787", "#afd7af", "#afd7d7", "#afd7ff", "#afff00", "#afff5f", "#afff87", "#afffaf", "#afffd7", "#afffff", "#d70000", "#d7005f", "#d70087", "#d700af", "#d700d7", "#d700ff", "#d75f00", "#d75f5f", "#d75f87", "#d75faf", "#d75fd7", "#d75fff", "#d78700", "#d7875f", "#d78787", "#d787af", "#d787d7", "#d787ff", "#d7af00", "#d7af5f", "#d7af87", "#d7afaf", "#d7afd7", "#d7afff", "#d7d700", "#d7d75f", "#d7d787", "#d7d7af", "#d7d7d7", "#d7d7ff", "#d7ff00", "#d7ff5f", "#d7ff87", "#d7ffaf", "#d7ffd7", "#d7ffff", "#ff0000", "#ff005f", "#ff0087", "#ff00af", "#ff00d7", "#ff00ff", "#ff5f00", "#ff5f5f", "#ff5f87", "#ff5faf", "#ff5fd7", "#ff5fff", "#ff8700", "#ff875f", "#ff8787", "#ff87af", "#ff87d7", "#ff87ff", "#ffaf00", "#ffaf5f", "#ffaf87", "#ffafaf", "#ffafd7", "#ffafff", "#ffd700", "#ffd75f", "#ffd787", "#ffd7af", "#ffd7d7", "#ffd7ff", "#ffff00", "#ffff5f", "#ffff87", "#ffffaf", "#ffffd7", "#ffffff", "#080808", "#121212", "#1c1c1c", "#262626", "#303030", "#3a3a3a", "#444444", "#4e4e4e", "#585858", "#626262", "#6c6c6c", "#767676", "#808080", "#8a8a8a", "#949494", "#9e9e9e", "#a8a8a8", "#b2b2b2", "#bcbcbc", "#c6c6c6", "#d0d0d0", "#dadada", "#e4e4e4", "#eeeeee", } termenv-0.9.0/color.go 0000664 0000000 0000000 00000010677 14064763020 0014662 0 ustar 00root root 0000000 0000000 package termenv import ( "errors" "fmt" "image/color" "math" "strconv" "strings" "github.com/lucasb-eyer/go-colorful" ) var ( ErrInvalidColor = errors.New("invalid color") ) const ( Foreground = "38" Background = "48" ) type Color interface { Sequence(bg bool) string } type NoColor struct{} // ANSIColor is a color (0-15) as defined by the ANSI Standard. type ANSIColor int // ANSI256Color is a color (16-255) as defined by the ANSI Standard. type ANSI256Color int // RGBColor is a hex-encoded color, e.g. "#abcdef". type RGBColor string func ConvertToRGB(c Color) colorful.Color { var hex string switch v := c.(type) { case RGBColor: hex = string(v) case ANSIColor: hex = ansiHex[v] case ANSI256Color: hex = ansiHex[v] } ch, _ := colorful.Hex(hex) return ch } func (p Profile) Convert(c Color) Color { if p == Ascii { return NoColor{} } switch v := c.(type) { case ANSIColor: return v case ANSI256Color: if p == ANSI { return ansi256ToANSIColor(v) } return v case RGBColor: h, err := colorful.Hex(string(v)) if err != nil { return nil } if p < TrueColor { ac := hexToANSI256Color(h) if p == ANSI { return ansi256ToANSIColor(ac) } return ac } return v } return c } func (p Profile) Color(s string) Color { if len(s) == 0 { return nil } var c Color if strings.HasPrefix(s, "#") { c = RGBColor(s) } else { i, err := strconv.Atoi(s) if err != nil { return nil } if i < 16 { c = ANSIColor(i) } else { c = ANSI256Color(i) } } return p.Convert(c) } func (p Profile) FromColor(c color.Color) Color { col, _ := colorful.MakeColor(c) return p.Color(col.Hex()) } func (c NoColor) Sequence(bg bool) string { return "" } func (c ANSIColor) Sequence(bg bool) string { col := int(c) bgMod := func(c int) int { if bg { return c + 10 } return c } if col < 8 { return fmt.Sprintf("%d", bgMod(col)+30) } return fmt.Sprintf("%d", bgMod(col-8)+90) } func (c ANSI256Color) Sequence(bg bool) string { prefix := Foreground if bg { prefix = Background } return fmt.Sprintf("%s;5;%d", prefix, c) } func (c RGBColor) Sequence(bg bool) string { f, err := colorful.Hex(string(c)) if err != nil { return "" } prefix := Foreground if bg { prefix = Background } return fmt.Sprintf("%s;2;%d;%d;%d", prefix, uint8(f.R*255), uint8(f.G*255), uint8(f.B*255)) } func xTermColor(s string) (RGBColor, error) { if len(s) < 24 || len(s) > 25 { return RGBColor(""), ErrInvalidColor } switch { case strings.HasSuffix(s, "\a"): s = strings.TrimSuffix(s, "\a") case strings.HasSuffix(s, "\033"): s = strings.TrimSuffix(s, "\033") case strings.HasSuffix(s, "\033\\"): s = strings.TrimSuffix(s, "\033\\") default: return RGBColor(""), ErrInvalidColor } s = s[4:] prefix := ";rgb:" if !strings.HasPrefix(s, prefix) { return RGBColor(""), ErrInvalidColor } s = strings.TrimPrefix(s, prefix) h := strings.Split(s, "/") hex := fmt.Sprintf("#%s%s%s", h[0][:2], h[1][:2], h[2][:2]) return RGBColor(hex), nil } func ansi256ToANSIColor(c ANSI256Color) ANSIColor { var r int md := math.MaxFloat64 h, _ := colorful.Hex(ansiHex[c]) for i := 0; i <= 15; i++ { hb, _ := colorful.Hex(ansiHex[i]) d := h.DistanceHSLuv(hb) if d < md { md = d r = i } } return ANSIColor(r) } func hexToANSI256Color(c colorful.Color) ANSI256Color { v2ci := func(v float64) int { if v < 48 { return 0 } if v < 115 { return 1 } return int((v - 35) / 40) } // Calculate the nearest 0-based color index at 16..231 r := v2ci(c.R * 255.0) // 0..5 each g := v2ci(c.G * 255.0) b := v2ci(c.B * 255.0) ci := 36*r + 6*g + b /* 0..215 */ // Calculate the represented colors back from the index i2cv := [6]int{0, 0x5f, 0x87, 0xaf, 0xd7, 0xff} cr := i2cv[r] // r/g/b, 0..255 each cg := i2cv[g] cb := i2cv[b] // Calculate the nearest 0-based gray index at 232..255 var grayIdx int average := (r + g + b) / 3 if average > 238 { grayIdx = 23 } else { grayIdx = (average - 3) / 10 // 0..23 } gv := 8 + 10*grayIdx // same value for r/g/b, 0..255 // Return the one which is nearer to the original input rgb value c2 := colorful.Color{R: float64(cr) / 255.0, G: float64(cg) / 255.0, B: float64(cb) / 255.0} g2 := colorful.Color{R: float64(gv) / 255.0, G: float64(gv) / 255.0, B: float64(gv) / 255.0} colorDist := c.DistanceHSLuv(c2) grayDist := c.DistanceHSLuv(g2) if colorDist <= grayDist { return ANSI256Color(16 + ci) } return ANSI256Color(232 + grayIdx) } termenv-0.9.0/color_test.go 0000664 0000000 0000000 00000002034 14064763020 0015705 0 ustar 00root root 0000000 0000000 package termenv import "testing" func TestXTermColor(t *testing.T) { var tests = []struct { input string color RGBColor valid bool }{ { "\033]11;rgb:fafa/fafa/fafa\033", RGBColor("#fafafa"), true, }, { "\033]11;rgb:fafa/fafa/fafa\033\\", RGBColor("#fafafa"), true, }, { "\033]11;rgb:1212/3434/5656\a", RGBColor("#123456"), true, }, { "\033]11;foo:fafa/fafa/fafaZZ", "", false, }, { "\033]11;rgb:fafa/fafa", "", false, }, { "\033]11;rgb:fafa/fafa/fafaY", "", false, }, { "\033]11;rgb:fafa/fafa/fafaZZ", "", false, }, } for _, test := range tests { t.Run("", func(t *testing.T) { color, err := xTermColor(test.input) if err != nil && test.valid { t.Fatalf("unexpected error for input %q: %v", test.input, err) } if err == nil && !test.valid { t.Fatalf("expected error for input %v not found", test.input) } if color != test.color { t.Fatalf("wrong color returned, want %v, got %v", test.color, color) } }) } } termenv-0.9.0/constants_linux.go 0000664 0000000 0000000 00000000155 14064763020 0016765 0 ustar 00root root 0000000 0000000 package termenv import "golang.org/x/sys/unix" const ( tcgetattr = unix.TCGETS tcsetattr = unix.TCSETS ) termenv-0.9.0/constants_solaris.go 0000664 0000000 0000000 00000000155 14064763020 0017302 0 ustar 00root root 0000000 0000000 package termenv import "golang.org/x/sys/unix" const ( tcgetattr = unix.TCGETS tcsetattr = unix.TCSETS ) termenv-0.9.0/constants_unix.go 0000664 0000000 0000000 00000000312 14064763020 0016604 0 ustar 00root root 0000000 0000000 // +build darwin dragonfly freebsd netbsd openbsd // +build !solaris // +build !illumos package termenv import "golang.org/x/sys/unix" const ( tcgetattr = unix.TIOCGETA tcsetattr = unix.TIOCSETA ) termenv-0.9.0/examples/ 0000775 0000000 0000000 00000000000 14064763020 0015020 5 ustar 00root root 0000000 0000000 termenv-0.9.0/examples/color-chart/ 0000775 0000000 0000000 00000000000 14064763020 0017235 5 ustar 00root root 0000000 0000000 termenv-0.9.0/examples/color-chart/color-chart.png 0000664 0000000 0000000 00001310177 14064763020 0022172 0 ustar 00root root 0000000 0000000 PNG IHDR o gAMA a cHRM z&