Skip to content

Commit 02f7563

Browse files
authored
dev: release v2.2.0 (#17)
This release adds: - 256 Color and True Color support and enabler for Windows Console - `White` and `HiWhite` as inbuilt colors - Terminal detection for Unix and Windows System and also for rounding off True Color into 4 bit or 8 bit as max supported by the terminal. - Deprecates `box.Output` for Windows - Major Code Cleanup - More comments for better understanding of the CodeBase - Doc improvements
1 parent 34c982c commit 02f7563

File tree

10 files changed

+329
-174
lines changed

10 files changed

+329
-174
lines changed

README.md

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Box CLI Maker is a Highly Customized Terminal Box Creator.
1919
## Features
2020

2121
- Make Terminal Box in 8️⃣ inbuilt different styles
22-
- 16 Inbuilt Colors and Custom (24 bit) Colors Support 🎨
22+
- 16 Inbuilt Colors and True Color Support 🎨
2323
- Custom Title Positions
2424
- Make your own Terminal Box style 📦
2525
- Align the text according to the need
@@ -42,8 +42,8 @@ package main
4242
import "github.com/Delta456/box-cli-maker/v2"
4343

4444
func main() {
45-
Box := box.New(box.Config{Px: 2, Py: 5, Type: "Single", Color: "Cyan"})
46-
Box.Print("Box CLI Maker", "Highly Customized Terminal Box Maker")
45+
Box := box.New(box.Config{Px: 2, Py: 5, Type: "Single", Color: "Cyan"})
46+
Box.Print("Box CLI Maker", "Highly Customized Terminal Box Maker")
4747
}
4848
```
4949

@@ -70,7 +70,7 @@ func main() {
7070
- Parameters
7171
- `title` : Title of the Box
7272
- `lines` : Content to be written inside the Box
73-
73+
7474
`Box.String(title, lines string) string` return `string` representation of Box.
7575

7676
- Parameters
@@ -125,20 +125,19 @@ func main() {
125125

126126
<img src="img/bottom.png" alt="bottom" width="400"/>
127127

128-
129128
### Making custom Box
130129

131130
You can make your custom Box by using the inbuilt Box struct provided by the module.
132131

133132
```go
134133
type Box struct {
135-
TopRight string // TopRight corner used for Symbols
136-
TopLeft string // TopLeft corner used for Symbols
137-
Vertical string // Symbols used for Vertical Bars
138-
BottomRight string // BottomRight corner used for Symbols
139-
BottomLeft string // BotromLeft corner used for Symbols
140-
Horizontal string // Symbols used for Horizontal Bars
141-
Config // Configuration for the Box to be made
134+
TopRight string // TopRight corner used for Symbols
135+
TopLeft string // TopLeft corner used for Symbols
136+
Vertical string // Symbols used for Vertical Bars
137+
BottomRight string // BottomRight corner used for Symbols
138+
BottomLeft string // BotromLeft corner used for Symbols
139+
Horizontal string // Symbols used for Horizontal Bars
140+
Config // Configuration for the Box to be made
142141
}
143142
```
144143

@@ -152,7 +151,7 @@ import "github.com/Delta456/box-cli-maker/v2"
152151
func main() {
153152
config := box.Config{Px: 2, Py: 3, Type: "", TitlePos: "Inside"}
154153
boxNew := box.Box{TopRight: "*", TopLeft: "*", BottomRight: "*", BottomLeft: "*", Horizontal: "-", Vertical: "|", Config: config}
155-
boxNew.Print("Box CLI Maker", "Make Highly Customized Terminal Boxes")
154+
boxNew.Println("Box CLI Maker", "Make Highly Customized Terminal Boxes")
156155
}
157156
```
158157

@@ -162,37 +161,45 @@ Output:
162161

163162
### Color Types
164163

165-
It has color support from [fatih/color](https://github.com/fatih/color) module from which this module uses `FgColor` and `FgHiColor`. `Color` is a key for the following maps:
164+
It has color support from [gookit/color](github.com/gookit/color) module from which this module uses `FgColor` and `FgHiColor`. `Color` is a key for the following maps:
166165

167166
```go
168-
var fgColors = map[string]color.Attribute{
169-
"Black": color.FgBlack,
170-
"Blue": color.FgBlue,
171-
"Red": color.FgRed,
172-
"Green": color.FgGreen,
173-
"Yellow": color.FgYellow,
174-
"Cyan": color.FgCyan,
175-
"Magenta": color.FgMagenta,
167+
fgColors map[string]color.Color = {
168+
"Black": color.FgBlack,
169+
"Blue": color.FgBlue,
170+
"Red": color.FgRed,
171+
"Green": color.FgGreen,
172+
"Yellow": color.FgYellow,
173+
"Cyan": color.FgCyan,
174+
"Magenta": color.FgMagenta,
175+
"White": color.FgWhite,
176176
}
177177

178-
var fgHiColors = map[string]color.Attribute{
179-
"HiBlack": color.FgHiBlack,
180-
"HiBlue": color.FgHiBlue,
181-
"HiRed": color.FgHiRed,
182-
"HiGreen": color.FgHiGreen,
183-
"HiYellow": color.FgHiYellow,
184-
"HiCyan": color.FgHiCyan,
185-
"HiMagenta": color.FgHiMagenta,
178+
fgHiColors map[string]color.Color = {
179+
"HiBlack": color.FgDarkGray,
180+
"HiBlue": color.FgLightBlue,
181+
"HiRed": color.FgLightRed,
182+
"HiGreen": color.FgLightGreen,
183+
"HiYellow": color.FgLightYellow,
184+
"HiCyan": color.FgLightCyan,
185+
"HiMagenta": color.FgLightMagenta,
186+
"HiWhite": color.FgLightWhite,
186187
}
187188
```
188189

189-
If you want High Intensity Colors then the Color name should start with `Hi`. If Color option is empty or invalid then Box with default Color is formed.
190+
If you want High Intensity Colors then the Color name must start with `Hi`. If Color option is empty or invalid then Box with default Color is formed.
191+
192+
True Color is possible though you need to provide it as `uint` or `[3]uint` and make sure that the terminals which will be targetted must have it supported.
190193

191-
You can also have more 16 Colors but the terminals must be 24 bit and the `Color` field must be provided either as `[3]uint` or `uint` though the elements of the array must be in a range of `[0x0, 0xFF]` and `uint` must be in a range of `[0x000000, 0xFFFFFF]`.
194+
`[3]uint`'s element all must be in a range of `[0, 0xFF]` and `uint` in range of `[0x000000, 0xFFFFFF]`.
192195

193-
If you want to use the string representation of the `Box` and print them for [`Windows Console`](https://en.wikipedia.org/wiki/Windows_Console) then you would have to use `box.Output` as the passing stream to the respective functions.
196+
As convenience, if the terminal's doesn't support True Color then it will round off according to the terminal's max supported colors which makes it easier for the users not to worry about other terminal for most of the cases.
194197

195-
`Windows Console` is 4 bit (16 colors) so Custom Colors will not work for them but the `Box` will be printed correctly without the Color effect.
198+
Here's a list of 24 bit [supported terminals](https://gist.github.com/XVilka/8346728) and 8 bit [supported terminals](https://fedoraproject.org/wiki/Features/256_Color_Terminals).
199+
200+
This module also enables **True Color** and **256 Colors** support on Windows Console through [Virtual Terminal Processing](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) but you need have at least [Windows 10 Version 1511](https://en.wikipedia.org/wiki/Windows_10_version_history_(version_1511)) for 256 colors or [Windows 10 Version 1607](https://en.wikipedia.org/wiki/Windows_10_version_history_(version_1607)) for True Color Support.
201+
202+
4 bit Color are now supported by every terminal now so there is no list for them unlike the above ones.
196203

197204
### Note
198205

@@ -204,11 +211,25 @@ As different terminals have different font by default so the right vertical alig
204211

205212
It uses [mattn/go-runewidth](https://github.com/mattn/go-runewidth) for Unicode and Emoji support though there are some limitations:
206213

207-
- `Windows Terminal` and `Windows SubSystem Linux` are the only know terminals which can render Unicode and Emojis properly on Windows.
208-
- Marathi Text only works on very few Terminals as less support it.
209-
- It is recommended not to use Online Playgrounds like [`Go Playground`](https://play.golang.org/) and [`Repl.it`](https://repl.it) because they use a font that only has ASCII support and other Character Set is used which becomes problematic for finding the length as the font changes during runtime.
214+
- `Windows Terminal` and `Mintty` are the only know terminal emulators which can render Unicode and Emojis properly on Windows.
215+
- Indic Text only works on very few Terminals as less support it.
216+
- It is recommended not to use this for Online Playgrounds like [`Go Playground`](https://play.golang.org/) and [`Repl.it`](https://repl.it), `CI/CDs` etc. because they use a font that only has ASCII support and other Character Set is used which becomes problematic for finding the length as the font changes during runtime.
210217
- Change your font which supports Unicode and Emojis else the right vertical alignment will break.
211218

219+
#### Terminal Color Detection
220+
221+
It is possible to round off true color provided to 8 bit or 16 bit according to your terminal's maximum capacity.
222+
223+
There is no **standardized way** of detecting the terminal's maximum color capacity so the way of detecting your terminal might not work for you. If this can be fixed for you then you can always make a PR.
224+
225+
If you think that the module can't detect True Color of the terminal then you must set your environment variable `COLORTERM` to `truecolor` or `24bit` for True Color support.
226+
227+
If you are targetting 8 bit color based terminals and if the module couln't detect it then set your environment variable `TERM` to name of the terminal emulator with `256color` as suffix like `xterm-256color`.
228+
229+
There might be no color effect for very old terminals like [`Windows Console (Legacy Mode)`](https://docs.microsoft.com/en-us/windows/console/legacymode) or environment variable `TERM` give `DUMB` so it will output some garbage value or a warning if used.
230+
231+
In `Online Playgrounds`, `CI/CDs`, `Browsers` etc, it is recommended **not** to use this module with color effect as few may have it but this is hard to detect in general. If you think that it's possible then open an issue and address the solution!
232+
212233
### Acknowledgements
213234

214235
I thank the following people and their packages whom I have studied and was able to port to Go accordingly.

box.go

Lines changed: 22 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ package box
22

33
import (
44
"fmt"
5-
"os"
6-
"runtime"
75
"strings"
86

9-
"github.com/fatih/color"
7+
"github.com/gookit/color"
108
"github.com/mattn/go-runewidth"
119
)
1210

@@ -98,47 +96,24 @@ func (b Box) toString(title string, lines []string) string {
9896
Bar := strings.Repeat(b.Horizontal, n-2)
9997
TopBar := b.TopLeft + Bar + b.TopRight
10098
BottomBar := b.BottomLeft + Bar + b.BottomRight
101-
99+
// Check b.TitlePos
102100
if b.TitlePos != "Inside" {
103101
TitleBar := repeatWithString(b.Horizontal, n-2, title)
104102
if b.TitlePos == "Top" {
105103
TopBar = b.TopLeft + TitleBar + b.TopRight
106104
} else if b.TitlePos == "Bottom" {
107105
BottomBar = b.BottomLeft + TitleBar + b.BottomRight
108106
} else {
109-
// Duplicate warning done here if the String() Method is used
107+
// Duplicate warning done here if the String() method is used
110108
// instead of using Print() and Println() methods
111109
errorMsg("[warning]: invalid value provided for TitlePos, using default")
112110
// Using goto here to inorder to exit the current if branch
113111
goto inside
114112
}
115113
}
116114
inside:
117-
if b.Color != nil {
118-
if str, ok := b.Color.(string); ok {
119-
if strings.HasPrefix(str, "Hi") {
120-
if _, ok := fgHiColors[str]; ok {
121-
Style := color.New(fgHiColors[str]).SprintfFunc()
122-
TopBar = Style(TopBar)
123-
BottomBar = Style(BottomBar)
124-
}
125-
} else if _, ok := fgColors[str]; ok {
126-
Style := color.New(fgColors[str]).SprintfFunc()
127-
TopBar = Style(TopBar)
128-
BottomBar = Style(BottomBar)
129-
} else {
130-
errorMsg("[warning]: invalid value provided to Color, using default")
131-
}
132-
} else if hex, ok := b.Color.(uint); ok {
133-
TopBar = rgbHex(hex, TopBar)
134-
BottomBar = rgbHex(hex, BottomBar)
135-
} else if rgb, ok := b.Color.([3]uint); ok {
136-
TopBar = rgbArray(rgb, TopBar)
137-
BottomBar = rgbArray(rgb, BottomBar)
138-
} else {
139-
fmt.Fprintln(os.Stderr, fmt.Sprintf("expected string, [3]uint or uint not %T using default", b.Color))
140-
}
141-
}
115+
// Check type of b.Color then assign the Colors to TopBar and BottomBar accordingly
116+
TopBar, BottomBar = b.checkColorType(TopBar, BottomBar)
142117
if b.TitlePos == "Inside" && runewidth.StringWidth(TopBar) != runewidth.StringWidth(BottomBar) {
143118
panic("cannot create a Box with different sizes of Top and Bottom Bars")
144119
}
@@ -207,23 +182,32 @@ func (b Box) obtainColor() string {
207182
if b.Color == nil { // if nil then just return the string
208183
return b.Vertical
209184
}
185+
// Check if type of b.Color is string
210186
if str, ok := b.Color.(string); ok {
187+
// Hi Intensity Color
211188
if strings.HasPrefix(str, "Hi") {
212189
if _, ok := fgHiColors[str]; ok {
213-
Style := color.New(fgHiColors[str]).SprintfFunc()
214-
return Style(b.Vertical)
190+
return fgHiColors[str].Sprintf(b.Vertical)
215191
}
216192
} else if _, ok := fgColors[str]; ok {
217-
Style := color.New(fgColors[str]).SprintfFunc()
218-
return Style(b.Vertical)
193+
return fgColors[str].Sprintf(b.Vertical)
219194
}
220195
errorMsg("[warning]: invalid value provided to Color, using default")
196+
// Return a warning as Color provided as a string is unknown and
197+
// return without the color effect
221198
return b.Vertical
199+
// Check if type of b.Color is uint
222200
} else if hex, ok := b.Color.(uint); ok {
223-
return rgbHex(hex, b.Vertical)
201+
// Break down the hex into r, g and b respectively
202+
hexArray := [3]uint{hex >> 16, hex >> 8 & 0xff, hex & 0xff}
203+
col := color.RGB(uint8(hexArray[0]), uint8(hexArray[1]), uint8(hexArray[2]))
204+
return b.roundOffColorVertical(col)
205+
// Check if type of b.Color is [3]uint
224206
} else if rgb, ok := b.Color.([3]uint); ok {
225-
return rgbArray(rgb, b.Vertical)
207+
col := color.RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]))
208+
return b.roundOffColorVertical(col)
226209
}
210+
// Panic if b.Color is an unexpected type
227211
panic(fmt.Sprintf("expected string, [3]uint or uint not %T", b.Color))
228212
}
229213

@@ -250,13 +234,7 @@ func (b Box) Print(title, lines string) {
250234
}
251235
}
252236
lines2 = append(lines2, strings.Split(lines, n1)...)
253-
if runtime.GOOS == "windows" {
254-
// Windows Console is 4 bit (16 colors only supported) so if the custom color
255-
// is out of their range then just correctly print the Box without the color effect
256-
fmt.Fprint(Output, b.toString(title, lines2))
257-
} else {
258-
fmt.Print(b.toString(title, lines2))
259-
}
237+
color.Print(b.toString(title, lines2))
260238
}
261239

262240
// Println adds a newline before and after the Box
@@ -282,11 +260,5 @@ func (b Box) Println(title, lines string) {
282260
}
283261
}
284262
lines2 = append(lines2, strings.Split(lines, n1)...)
285-
if runtime.GOOS == "windows" {
286-
// Windows Console is 4 bit (16 colors only supported) so if the custom color
287-
// is out of their range then just correctly print the Box without the color effect
288-
fmt.Fprintf(Output, "\n%s\n", b.toString(title, lines2))
289-
} else {
290-
fmt.Printf("\n%s\n", b.toString(title, lines2))
291-
}
263+
color.Printf("\n%s\n", b.toString(title, lines2))
292264
}

box_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func TestInbuiltStyles(t *testing.T) {
2727

2828
func TestPrintColorBox(t *testing.T) {
2929
StyleCases := []string{"Single", "Double", "Single Double", "Double Single", "Bold", "Round", "Hidden", "Classic"}
30-
ColorTypes := []string{"Black", "Blue", "Red", "Green", "Yellow", "Cyan", "Magenta", "HiBlack", "HiBlue", "HiRed", "HiGreen", "HiYellow", "HiCyan", "HiMagenta"}
30+
ColorTypes := []string{"Black", "Blue", "Red", "Green", "Yellow", "Cyan", "Magenta", "White", "HiBlack", "HiBlue", "HiRed", "HiGreen", "HiYellow", "HiCyan", "HiMagenta", "HiWhite"}
3131

3232
for i := 0; i < len(StyleCases); i++ {
3333
for j := 0; j < len(ColorTypes); j++ {
@@ -103,8 +103,12 @@ func TestUnicodeString(t *testing.T) {
103103
// English, Japanese, Chinese(Traditional), Korean, French, Spanish, Latin, Greek
104104
titles := []string{"Box CLI Maker", "ボックスメーカー", "盒子製造商", "박스 메이커", "Créateur de boîte CLI", "Fabricante de cajas", "Qui fecit me arca CLI", "Κουτί CLI Maker"}
105105
lines := []string{"Make Highly Customized Terminal Boxes", "高度にカスタマイズされた端子ボックスを作成する", "製作高度定制的接線盒", "고도로 맞춤화 된 터미널 박스 만들기", "Créez des boîtes à bornes hautement personnalisées", "Haga cajas de terminales altamente personalizadas", "Fac multum Customized Terminal Pyxidas", "Δημιουργήστε πολύ προσαρμοσμένα τερματικά κουτιά"}
106+
StyleCases := []string{"Single", "Double", "Single Double", "Double Single", "Bold", "Round", "Hidden", "Classic"}
106107
for i := 0; i < len(titles); i++ {
107-
Box := New(Config{Px: 2, Py: 5})
108-
Box.Println(titles[i], lines[i])
108+
for j := 0; j < len(StyleCases); j++ {
109+
Box := New(Config{Px: 2, Py: 5, Type: StyleCases[j]})
110+
Box.Println(titles[i], lines[i])
111+
112+
}
109113
}
110114
}

0 commit comments

Comments
 (0)