hawktui 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 07fe25870ab381af56a2690f6b7befd22933dea333cb63f2d4d9e96b6801af5e
4
+ data.tar.gz: 8d7e1ce5dc90cfcc1d07e7adcb2b9f0c99949f612616c5ab64b7a3e34529f7c3
5
+ SHA512:
6
+ metadata.gz: 25ead068f0039099acabceba170d120aaa8c674cae0b607df496a7de60fdc9a722c827376bf2bb6929522d5405861e515e4e8b3713d9b4bcce964bd855d51061
7
+ data.tar.gz: 5c6b1112d3519712730b7c84dccd153d0ae6062afd5c325beea6ad70cd13f6da8d6bed61f5f94ce9b0c20a0d03325117416326413b76301994cdf3698becb297
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/standardrb/standard
3
+ ruby_version: 3.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jonathan Hoyt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Hawktui
2
+
3
+ Hawktui is a simple and easy to use TUI (Terminal User Interface) library for Ruby. It is built on the [curses](https://github.com/ruby/curses) library.
4
+
5
+ ![hawktui](https://github.com/jonmagic/hawktui/blob/main/hawktui.jpeg)
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "standard/rake"
9
+
10
+ task default: %i[test standard]
data/hawktui.jpeg ADDED
Binary file
data/hawktui.txt ADDED
@@ -0,0 +1,93 @@
1
+
2
+
3
+
4
+ .-. .-.
5
+ .-%%#-. .+@@%-. .*#-. :##- ...
6
+ .%@@@@*.. .+@@@@%.. ... .#%%+. :#%#= .++.
7
+ :#@@@@@%=. .:%@@@@@* .+#= .+%%%*. .*%%#+ +#*:
8
+ .*@@@@@@@%=. :%@@@@@@= .*###. :#%%%#. =%%%#= .-##*:
9
+ .::... .:%@@@@@@@@#:. :%@@@@@@@-. .=###%-. .+%%%%#: .#%%##- :###*.
10
+ :#%%%%#-. .-%@@@@@@@@@#: .*@@@@@@@%: .#%%%%+. .#%%%%#:.+%%%##:.+###=.
11
+ .+%%%%%@@#+:. -#@@@@@@@@@@#=. .-@@@@@@@@#-. .*#+.. :#%%%%#- =%%%%##.-#####=.+###*:
12
+ .=%%%%%%@@@@*-... .#@@@@@@@@@@@%-.. .+@@@@@@@@%:. =###*-. -#%%%%##.*%%###+-####*+.*###*=
13
+ .:*%%%%@%%%@@@%*-. .-%@@@@@@@@@@@%*-. .+@@@@@@@@%=. =#####*: .######**#####***#***=-*##**=.
14
+ .-#%%%@%@@@@@@@@%*=:. ..=%@@@@@@@@@@@@%+:. =%@@@@%%@@+.. .##%%%%#*=*%%##%*#####*****++=+***++:.
15
+ .:#%@@@@%%%@@@@@@@@%+-... .+%@@@@@@%%@@%@@@*:..:#@@@@@%%@*.. ..#%@:. .-==-:....#%%%%%%#*#######**#++**-+++*+++=:.
16
+ ..=%%%%%%%@@@%%%@@@@@@%#+:. ..=%@%%%%%%%###%%@%#*%%%##*%##%=. -%@%@@+. :########*+++#%%%%#%#**##+***+**====*+++:.
17
+ :*%%%#+=:.. .:=*%%%%%%%%%%%%%@@@@@%%%%*=-*%%###%#*++#%*+#%**###**##*#*:. =@@%%%@@#. .-#%%%%%%%#%##**######+##**#*+*********+=:
18
+ :*%%%%%%@@%*=:.... .-*#%%@@@@%%%%@@#**@@*+#@#*%*+*#%%++#%*==#%*++**+*#**+*=..#@%%%%@@@%.. .......=%%%%%%%%%%%%#%*%##*##*********+++=-:.
19
+ .=%%%%%%%%%@@@@@%%%#*+-:.. ..:-#@@%#*+*%@#+++%%+=*@*=%%+##+=*#+==#*=**###*%%#%##%@@%%%%@@@@%: -#%%%%%%%##%%%####%#*#%%#**+===---+*++=-:.. ....
20
+ .-+%%%%%%%%%%%%@@@@@@@@@@@%%%%###*****%%%%#*++#@#==*@+=+%*-+%*=**=**=+%####%*%###+*%#*##%%%%%%@@*. .:*%%%%%%%%%%%#%%#*#%%%#**++++==--::. ...:-=++**########-.
21
+ ..=##%%%%%%%%%%%%%%@%#*#@@#**@%**@%++#@+=*@#+*@#==%#==#+-+#=+#--#%+%#**=+++=+**%*+*#@@%%%@@%#-#%%%%%%%%%%%%%%%%%%%%#**+++==+===-:.. ......:-+*%%%#**++++++++++++++++++:
22
+ ..:=*#%%%%%%%#**#%@#**#@*+*@%==#@*=+@#==%#-=%+-*%+-*%+-*+=*##%#%*===+++*#**+#%###%%%%%%*-*####%%%%%%%%%%%##*+====***+++==-:. ..:-=*##***+++++===+++++++++++++++++++++++++*-
23
+ .:--=+#%####%@#++%@*+#%%+=#%+-+@*-+%=-##--%+-=#--%#####=-===++****+*#####%#%%%#=+%%%%%%%%%%%%%##*+++==+**++++++=-.. ...:-=+++******+=================+++++++++++++++***####*++++*=
24
+ ...=****#%#=+#%%++#%*==#%==%#-+%=-##--%=-*+*%*##=====+*%#**########%#%#*-:=*#%@@%%%%%%%**++****#****+++==: ....... ......::=*###*+===============================++++++**#%%@@@@@@@@@@@@@*+++*#-
25
+ ..... ........::--==+++*###*##%%#*#%%**#@%==%%+=*@*=+%*=+%=-*#--*%-=#-=%=*%#%%%*=+++**+######%%#%%#%%#*--::=#%%%##%###*###%**##***+=-=-====+++====--:. ..:=+***+++++==============+@#===============+**#%%@@@@@@@@@@@@@@@@@@@@@@@@@@@%++++*%:
26
+ .=*%%%%%%%%%%%%%%%%%%%@%*#@@%+*@@*+#@#++%@+=+@#==#%==+%#-=@*-*#-=%--%*-=%=-#+*#+*#**+*+***#%###%%%%%%###*+--::::+#%%@%%%%%#%%#***+***###%%##%%%###+=----:. .:-==++++*++===============================+**##%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@++++*#-.
27
+ ..-+###%%%%%%%%%%%%%*#%#+*@%#*#@%*+*@%=+%@*==@%--#%--#%-=%+-=%=-%+:+#+=#*+**#%*+#*%######@%%%%%%%%%%%##*=-::-:..-#%@@%*+**##**+++#@@%@@@@@@%%%%#*=-::-:. .:*-=========================++++*#%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#=+++#+.
28
+ ...::-==+***#%%*+#@@#++#@%++#@#=+#@*==%%+-=@#-=%*:=%+-*#--%*-+%==+#******%%%%%##*##%%*#%%#%%%%%%%%%**+--:----:.::-==+==+*+*%@@@@@@@@@@%#%@@%*-::--. :+-============++*#%%@@@@@@@@@@@@@@@@@@@@@@@@@#=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%++++##:
29
+ ..:::-+*##**%###*+*@%--@@=-#%=-*%+-+%+=+#-=%=*#-##=++*##@@@@%*****%%%##*###%%%######%####*==--::::--=+#%++**##@@@@@@@@##*+*#@@@*-:--. .=-+*##%%@@@@@@@@@@@@@@@@@@@@@@=*@@@@:%@@@@@*@%+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*++++%=.
30
+ ....:=*###@@#*%@*=+@#=*@+-*@#++#%+-=#%*--#%*+=%--##=+++*++##%%@@%#%**####%#+#=#+%#%%%%#%######%#***#*++=++*@@*%%*#@@@@@@@@@@%#*+*#@@@@+=-. .-=%@@@@@@@@@**@@@@@@@@@@@@@@%**-@*-#:=#==@@++@:#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#++++#*.
31
+ .-=*##%%%%%%%@**%@*+%%++%#*%%+=#%#==#%#*#==#%===%#-+%#=-*#**++%#**@@@@%%%#**#@@%*****+++%#%%#%%#########**#%#%%@@@@@%###@@@@@@@@@@@@@%#*%@%@@#+: :=*@@@@@@@@@@--:#@=**:@.#*:%#+@--+@@#-@%-@@#=@=*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%++++##:
32
+ .=#%%%%%%%%%%%%%%##%@%*+#@%++#%*+*%%*+*@%=-*%#==%#-=%#*-=%#=+*+*#+=*%#@@@@@%#***##%%@#%+=#++++*+%##%####****##%%%@@@@@%**++###%@@%%%@@@@@@@@###@@*:. .==@@#-=#@@@@-#@+%%++:#@.*#=++@-#+=@%:@@.#@+.@+-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*+++*#+.
33
+ .:-****#######%%%*=*%#*+%%%%@%**%@#==@#=*%+==%%#=-#%=***#*#=++#*+%@@@@@%%#%####*#*###%*#++**#***+*+*%+*###%@@@@@@%%#++=*%%***%#%%*#+**+#%###%#+=.. .=-%@@@@%=+@@+*@%+=*@*=@*.#@.%@=+@@:@#-%@**@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%=+++##.
34
+ ....... .-#%%@%%%%#**%*=*%*++%%#==%%+=#####*+=###-*%**@@@@@%##%####****+**#####%+#+***#*##%%@@@@@@@@@@#++==+=*#++*++++=-......-#@%#%+. -=#@@-+%@@@@@*@@#@%#%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+=++*#-.
35
+ .+#%@@%%%%%%%%%#*#@%%%%**%%+-*%#+*#==*%%*-=#%=*%#+%@@@@@@%%%%###***#*%%%%####%#%%#%@@@@@@@@@@@@%#%%*===+-++=++=-. .-@@@*. :==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*++++#*.
36
+ :#%%%%%%%%%%%###%@@@%%%#%#+#%%%%#*#*%+*#=-##+-%#*+%%%%#%%%%%@%@%%%####%%#@%#@#@@#%@@@@@@@@@@@@%@%@*+===--+---=-=. .*+. .+-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%++++*%:
37
+ .-+##%%##*+-:.:#@@@%%%%%%%#%@%%#%%#==%%##+#*#*###%#%=#%%@@@%@%@%@###%%%%%%@%@@%@@@@@@@@@@@@@@@@@%#**%%+=--------.. .+=#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+++++%-.
38
+ :#%%%%%%%%%%@@@%%%%%%%%@%#-=%#+=#%#++%*%%+#*#%%%%%%%%@%@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%#+------:-::. -=+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#++++#*.
39
+ ..*%@%%%%#*+@@@%%%%%%%%%@@%%%%%%%%@*=#%#%#-*%%+##%#%%%%@%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%*+#==+=--::::. .==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@++++#%-.
40
+ .........-%%@%%%%%%%%@@@%%%@%%%%@@%%##+*+#@#+*%##*%##%%%@%@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%@@+%*=%-::::. .*-%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*++++%+.
41
+ .*%%%%%%%*=+%@@%%%@%%%%@@@%%%%%%%%#*#@%++%%*%%%%@@@@@@@@@@@@@#+-@@@@@@@@@@@@@@@@%@@+=#+%=-++: ..::. .--*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%++++#*.
42
+ .+***=: .*@@@%%%%%%%@@@%%%%%%%%@@%@@#%@@#%@@%@@@@@@@@@@@@%- .*@@@@@@@@@@@@@@@@@#**%#=++**:... .*%= .+=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@++++*#=
43
+ :#%%%@%%%*-%@@@%%%%%%%@@%@@@@@@@@@@@@@@@@@@@%*#+:. *@@@@@@@@@@@@@@@%###+==+-#**-::... =*=-*=. .+=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#++++##.
44
+ .=#%%%#=:.=%@@@@@@%%%@@@%@@@@@@@@@@@@@@@@@@@%: .#@@@@@@@@@@@@@@@@@@#+++=+=+*=*++-:... .*%##=:. .==*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@++++*%:.
45
+ .. -%@@@@@%%#@@@@@@@@@@@@@@@@@@@@@@@@*. .+@@@@@@@@@@@@@@@@@@@%==::..+++####*=::.. .#%#= :==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#++++%+.
46
+ -%%@%#-.:%@@@@@#@@@@@@@@@@@@@@@%%:. -%@@@@@@@@@@@@@@@@@@@@@%*=-:..*#######*-:.. .#%@%- .*=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%++++##.
47
+ .::. .*%@@*-.#@@@%+%@@@++@@#... :#@@@@@@@@@@@@@@@@@@@@@@@@%*+-::-*#######*=::.... ..=%@#- .+=#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*+++*%=.
48
+ :-. .#%#:.:#%=. .=. .+%@@@@@@@@@@@@@@@@@@@@@@@@@@%*=::.+#*#######*+--:::....:=*%@@@%- :++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#++++#*.
49
+ .. .. .=#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#+-:.:****#########**+*#%%%*=::#@-. .*=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@++++*#-
50
+ :#@@@@@@@@@@@%%%%%%%%%@%@@@@@@@@@%*=::..-+**###########+:. :%-. .+=%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#++++#+.
51
+ .-%%@@@@@@@%%%%%######%%%%%%@@@@@@@%*+--.. :=+**#######*+:. :%*. -+*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%*+++*#.
52
+ .*%@@@@@@@#%%%%############*+%@@@@@@%#+=-:. .:-+***#***+=:. -@@= :++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*++++%-.
53
+ .+%@@@@@@%##%%%####**#*****+-..*@@@@@@@%*=-:.. .:-=++++=--:. :%-.: :++%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#++++#*.
54
+ .:*##@@@%%########****++++=-. .*%@@@@@%%#*=-.. ..:.::... .=+*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%*+++#%-.
55
+ -#@%%@@%%######******++==-=+. .+%@@@@@@%**+-:.. =++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#*++*%=.
56
+ .+%%%%@@%%####******++======-=*- :=#%@@@%%%#*+=:.. .-++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%*++*%*.
57
+ .-#%%%%@%%%*##*****+++===++=-=++++- .:*%@@@@%%%####+-...:+.. ..=##- .++#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*****#-.
58
+ .-#%%%@%@@%%*#******+++==+==+:===+*#*. ..=*%%%%%%%%#+=*%@%%@%**%#. :#*+%#=. .+++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%****##..
59
+ .-*%%%#%%%@%%**#*#*+++++++=+*===..-+##+:. .:+*######+-. .+@@@@@+...:+####+. .-. .=++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*****%:.
60
+ .:#%%#*#@%%@#%#****#*=**++++===+=+= .. ..::-+*=*=.. *@@@%@@@%@@@@%@@%+: :++%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#****%=.
61
+ .+##%**+%@@%@%%%#+#***==**+==+=*=-=++- ...:.:.. .+-..*@@%@*-..:.. .*+*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%****##:
62
+ .:=##+***++%%#%%%###=+*+**-+++++*+=-=+++**. .%#:. -=: .++*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%#****#-.
63
+ ..:*#*+::#+*-*@**%%#*#*+:##**=:*#++=+**+:=*##*. .=@%: -**%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%****#*.
64
+ .++=-:.=*+*--#+#=#%%=***:+#***:-+*+++====.:*##-. .%@#-. .**#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%****##-
65
+ .:=+++=-*#*+-*%#==%#-.+*+*+.=#++--+**#: ... :%:. .*+*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#****#+.
66
+ ...=*-=*=::#**=.*%#-:##+.:#*+*:.=**+-.*%%#. :#+. =**@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%****#%:.
67
+ .=+=:..*#*+..###:.*##:.-****..=*++. +%#=. :*+#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#****%=.
68
+ .-#*-:.:##*: -%#=..+#*#-..-*##. ::. .*++##%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%****%*.
69
+ ..=++. .#%#. .***. .***%. -@%= .++++++++++*+**#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%#####%-.
70
+ ... .*%#:..=%%=. :%@%+. -@+. -**********************##%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%#####+..
71
+ .=#=. .*%*. .%%*: :. .......::::::---:::::::=++*##*************************##%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%######:.
72
+ .*=. .*+. .............:::-----===--=+===---:::-+**#*-:--=-:::::----==+++**######***********************#%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#####%=.
73
+ .. ...:::::----*=-------=#%*-------==+++=--=+++**==--==++++=-==+=--==**+=-=*+---------==+++++*#########*******************###%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%####%#..
74
+ ....::::::------==============-----------+*%*=---====*****+=====-=+==-==--=========-=+==------=-=--=++*#+==+=--------====++****##############*******######*##%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%######:.
75
+ .=++=================================------------*%#+==-====-=*##=-:::-=*#**+=----:+*=-====-==---====--------*%%%#+---=-------====++++**################################%@@@@@@@@@@@@@@@@@@@@@@@@#####%+.
76
+ .-*+++===============================-----------=+*+=*%*=:---::---+#+-------=#+=-----=+*+-=----:::-====+=--=-----===+*+*#*+------=======++++**#################################%@@@@@@@@@@@@@@%######:
77
+ .-*#*++==========++++*******+++++++++======--------=+++++##+-:::::::-+**+==---=+++=-----=+=++===-:--::---=+===========+***##*==-=+=========++++++*###%%%%%###################%%%%%#%%%%@@@@%%%%#%%=.
78
+ .*@%**++==++++======++++====================-------:-**--*%#=:::::::::+*+==-----=###*+===-----*#*++--::::--============++++*##*+*#*===+++++++++++++++*#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*.
79
+ .-@@%**++=++===+++++++=====++++++==========--------::--=*+-+#%+::::::::-=**-:::--=++*#+==-:::::-=***=------=**======++++*********###*++++*++++++++++++++***#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#-.
80
+ .-*@%%#%@#+==++++++++++++++++++++++=======-----------:::=++==+##+=+++++++*+**=-==----=***++=======+***+=-----=+***++++++++*#****+***##%#*++*#*+++++++**+++++**##%%%%%%%%%%%%%%%%%%%%%%+.
81
+ .*@@@@@%%*=+++++++++++++++++++++++======------------:::::=-::=%%+::::::::-+%%#+-::::::--=#+---------+%#*+======-+##*++++++#******##**###%#***##*++++++++***+=====*%%%%%%%%%%%%%%%%:.
82
+ ..-%@@@@@#+++++++++++++++++++*****+++========-------::::::::::-=+@%=::::::::-+%%@%*-:::::--=*#=::------=##+-====--==+##+++*+*************#*##%%#***+****+++++**********#%%%%%%%-.
83
+ -*@@@%##**++*****************++++++=======---------:::::::::::-+##*+++#*=:::-+#%%*+-::::::-+**+-::::::-+***+=------=***#*++++*###*#####%%%########****++++++++++===*@@@%*:.
84
+ .+@@@@%#*********************+++++======-----------:::::::::::::=#%%*==+=-------+#@#=::::::::=%%+-::::----=#%**#%%@%#****++++++++++++====+++++++=*%%@@@%@*++++==:::..
85
+ .-%@@@@%#*******************+++++=====-------------::::::::::::::-+%#+--*%=::::::-+#%%++**##%%=##**++====-------=====++++++%%%%%@@+=*%%%%%@*==+@@@@@@%======-.
86
+ .:*@@@@%#########***********++++====--------------:::::::::::::::::-==-=*#####*++-:----------======++++++++++++========#@@@@%%#==+#########%@@@@@@@@@%#*=-:..
87
+ .+@@@@@%#######**********++++====-----------------::::::::::::::::::::-----==++***++++++++++++++++++++++++++++**#%%@@@@@@@@@@@@%%%%#*+=-:....
88
+ ..:%@@@@@%####*********++++=====------------------::::-==+++++++++++++++++++++++++++++**###%%@@@@@@@@@@@@@@@@%#*+=-:::.....
89
+ :*@@@@@@%###*******++++====----====++++++**+++++++++++++++++****####%%%@@@@@@@@@@@@@%%#**++=-::...
90
+ .+%@@@@@%%#************+*++++++++++++++++++**#%%@@@@@@@@@@@@@@@@@%%#*+=-:.
91
+ .-#@@@@@@%***++++++++**##%%%@@@@@@@@@@@@@@@@@%#*=--::.....
92
+ .:*@@@@@@@@@@@@@@@@@@@@%%#*++=--::..
93
+ .+%%%#*+=:..
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hawktui
4
+ class StreamingTable
5
+ # Public: Represents a single cell in a table. Holds the cell’s value and
6
+ # optional color, and can return both in a consistent format.
7
+ #
8
+ # Examples
9
+ #
10
+ # cell = Tui::Cell.new("Hello")
11
+ # cell.value # => "Hello"
12
+ # cell.color # => nil
13
+ #
14
+ # colored_cell = Tui::Cell.new(value: "Error", color: :red)
15
+ # colored_cell.value # => "Error"
16
+ # colored_cell.color # => :red
17
+ #
18
+ class Cell
19
+ attr_reader :value, :color
20
+
21
+ # Public: Create a new Cell object from either a raw value or a Hash with
22
+ # `:value` and `:color` keys.
23
+ #
24
+ # value_or_hash - A raw value (e.g., String, Integer) or a Hash of the form:
25
+ # { value: <String/Integer>, color: <Symbol/Integer> }.
26
+ #
27
+ # Examples
28
+ #
29
+ # Cell.new("Hello")
30
+ # Cell.new(value: "Hello", color: :blue)
31
+ #
32
+ # Returns a new Cell instance.
33
+ def initialize(value_or_hash)
34
+ @value, @color = extract_value_and_color(value_or_hash)
35
+ end
36
+
37
+ # Internal: Extract the cell's value and color from the given parameter.
38
+ # If value_or_hash is a Hash, expects :value and :color keys.
39
+ #
40
+ # value_or_hash - A raw value or a Hash with :value and :color.
41
+ #
42
+ # Returns an Array [value, color].
43
+ def extract_value_and_color(value_or_hash)
44
+ case value_or_hash
45
+ when Hash
46
+ [value_or_hash[:value], value_or_hash[:color]]
47
+ else
48
+ [value_or_hash, nil]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hawktui
4
+ class StreamingTable
5
+ # Public: Represents a single column in a table. Knows how to truncate or
6
+ # pad a cell’s text to fit within its width.
7
+ #
8
+ # Examples
9
+ #
10
+ # column = Tui::Column.new(name: :message, width: 50)
11
+ #
12
+ class Column
13
+ attr_reader :name, :width
14
+
15
+ # Public: Create a new Column with a name and width.
16
+ #
17
+ # name - A Symbol or String representing the column's name.
18
+ # width - An Integer specifying how wide the column is.
19
+ #
20
+ # Examples
21
+ #
22
+ # column = Tui::Column.new(name: :message, width: 50)
23
+ #
24
+ # Returns a new Column instance.
25
+ def initialize(name:, width:)
26
+ @name = name
27
+ @width = width
28
+ end
29
+
30
+ # Public: Format the cell’s textual value to fit within the column width.
31
+ # If the text is longer than `width`, it will be truncated with an ellipsis ("…").
32
+ # Otherwise, it’s left-padded with spaces to fill the width.
33
+ #
34
+ # cell - A Tui::Cell containing the raw value and color.
35
+ #
36
+ # Examples
37
+ #
38
+ # cell = Tui::Cell.new("Some message")
39
+ # column.format_cell(cell)
40
+ # # => ["Some message ", nil] # => example if width is bigger
41
+ #
42
+ # Returns an Array [formatted_string, color].
43
+ def format_cell(cell)
44
+ str_value = cell.value.to_s
45
+ if str_value.size > width
46
+ [str_value[0...width - 1] + "…", cell.color]
47
+ else
48
+ [str_value.ljust(width), cell.color]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hawktui
4
+ class StreamingTable
5
+ # Public: A class to layout tables with columns and rows. Knows how to convert
6
+ # a hash of row data into an array of Cell objects in the correct column order,
7
+ # and can also build a header row for the table.
8
+ #
9
+ # Examples
10
+ #
11
+ # columns = [
12
+ # { name: :timestamp, width: 20 },
13
+ # { name: :message, width: 50 },
14
+ # ]
15
+ # layout = Tui::TableLayout.new(columns: columns, header_color: :white)
16
+ #
17
+ # header_cells = layout.build_header_row # => [Cell(...), Cell(...)]
18
+ #
19
+ class Layout
20
+ attr_reader :columns
21
+
22
+ # Public: Create a new TableLayout.
23
+ #
24
+ # columns - An Array of Hashes or Tui::Column objects. Each element must
25
+ # have a `:name` and `:width`.
26
+ # header_color - A Symbol representing the color used in the header (defaults to :white).
27
+ #
28
+ # Examples
29
+ #
30
+ # layout = Tui::TableLayout.new(
31
+ # columns: [
32
+ # { name: :time, width: 10 },
33
+ # { name: :level, width: 5 },
34
+ # ],
35
+ # header_color: :yellow,
36
+ # )
37
+ #
38
+ # Returns a new TableLayout instance.
39
+ def initialize(columns:, header_color: :white)
40
+ @columns = columns.map do |col|
41
+ # Accept either raw Hashes or Column objects
42
+ col.is_a?(Column) ? col : Column.new(**col)
43
+ end
44
+ @header_color = header_color
45
+ end
46
+
47
+ # Public: Return an Array of Cell objects for the header row.
48
+ # Uses a header-specific color if desired.
49
+ #
50
+ # Examples
51
+ #
52
+ # layout.build_header_row
53
+ # # => [Cell(value="time", color=:white), Cell(value="level", color=:white)]
54
+ #
55
+ # Returns an Array of Tui::Cell objects.
56
+ def build_header_row
57
+ columns.map do |col|
58
+ # Use the stored @header_color for all headers
59
+ Cell.new(value: col.name, color: @header_color)
60
+ end
61
+ end
62
+
63
+ # Public: Convert a single row of data (a Hash) into an array of Cells, in column order.
64
+ #
65
+ # row_hash - A Hash whose keys are the column names and whose values may be raw
66
+ # or a Hash with :value and :color.
67
+ #
68
+ # Examples
69
+ #
70
+ # row_hash = { time: "12:00", level: { value: "INFO", color: :green } }
71
+ # layout.build_cells_for_row(row_hash)
72
+ # # => [Cell(value="12:00", color=nil), Cell(value="INFO", color=:green)]
73
+ #
74
+ # Returns an Array of Tui::Cell objects.
75
+ def build_cells_for_row(row_hash)
76
+ columns.map do |col|
77
+ value_or_hash = row_hash[col.name] || ""
78
+ Cell.new(value_or_hash)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,261 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "curses"
4
+ require "hawktui/utils/colors"
5
+ require "hawktui/streaming_table/cell"
6
+ require "hawktui/streaming_table/column"
7
+ require "hawktui/streaming_table/layout"
8
+
9
+ module Hawktui
10
+ # Public: Streams a dynamic table to the terminal, handling user input such as
11
+ # pausing/unpausing and quitting. Continually appends new rows at the top of the
12
+ # table and enforces a maximum row limit.
13
+ #
14
+ # Examples
15
+ #
16
+ # columns = [
17
+ # { name: :timestamp, width: 20 },
18
+ # { name: :message, width: 50 },
19
+ # ]
20
+ # table = Tui::StreamingTable.new(columns: columns, max_rows: 1000)
21
+ # table.start
22
+ #
23
+ # # In a separate thread or async process:
24
+ # table.add_row(timestamp: Time.now.to_s, message: "Hello, world!")
25
+ #
26
+ # WARNING: The StreamingTable must run in the main thread and table.add_row should
27
+ # be called from a separate thread or async process. This is because the table
28
+ # uses curses, which is not thread-safe and does not respond to user input in
29
+ # a separate thread.
30
+
31
+ class StreamingTable
32
+ # Public: Create a new StreamingTable.
33
+ #
34
+ # columns - An Array of Hashes or Tui::Column objects that define the table’s
35
+ # columns. Each element should at least contain `:name` and `:width`.
36
+ # max_rows - The maximum number of rows to keep in the table. Defaults to 100000.
37
+ #
38
+ # Examples
39
+ #
40
+ # table = Tui::StreamingTable.new(columns: [{ name: :time, width: 10 }], max_rows: 500)
41
+ #
42
+ # Returns a new StreamingTable instance.
43
+ def initialize(columns:, max_rows: 100_000)
44
+ @layout = Layout.new(columns: columns)
45
+ @max_rows = max_rows
46
+ @rows = [] # Store rows newest-first
47
+ @paused = false
48
+ @input_thread = nil
49
+ @should_exit = false
50
+ end
51
+
52
+ # Public accessors
53
+ attr_reader :layout, :max_rows, :rows, :paused, :should_exit
54
+ attr_accessor :win
55
+
56
+ # Public: Start the table UI. Initializes curses, sets up input handling,
57
+ # and draws the initial screen.
58
+ #
59
+ # Examples
60
+ #
61
+ # table.start
62
+ #
63
+ # Returns nothing.
64
+ def start
65
+ setup
66
+ start_input_handling
67
+ draw
68
+ end
69
+
70
+ # Public: Stop the table UI. Stops the input thread, closes the curses screen,
71
+ # and exits the process.
72
+ #
73
+ # Examples
74
+ #
75
+ # table.stop
76
+ #
77
+ # Returns nothing. Exits the process.
78
+ def stop
79
+ @input_thread&.exit
80
+ Curses.close_screen if win
81
+ self.win = nil
82
+ Process.exit(0)
83
+ end
84
+
85
+ # Public: Add a new row of data to the top of the table. If the table has
86
+ # reached its maximum row limit, the oldest row is dropped.
87
+ #
88
+ # row_data - A Hash of row data where keys match column names. Values can be
89
+ # raw (String, Integer, etc.) or a Hash with :value and :color.
90
+ #
91
+ # Examples
92
+ #
93
+ # table.add_row(timestamp: "2025-01-01 12:00", message: { value: "New Year!", color: :red })
94
+ #
95
+ # Returns nothing.
96
+ def add_row(row_data)
97
+ return unless win
98
+
99
+ rows.unshift(row_data)
100
+ rows.pop if rows.size > max_rows
101
+ draw unless paused
102
+ end
103
+
104
+ # Internal: Set up curses, initialize colors, etc. Called by #start.
105
+ #
106
+ # Returns nothing.
107
+ def setup
108
+ Curses.init_screen
109
+ Utils::Colors.setup_colors
110
+ Curses.start_color
111
+ Curses.noecho
112
+ Curses.curs_set(0)
113
+ Curses.stdscr.keypad(true)
114
+ Curses.timeout = 0
115
+
116
+ self.win = Curses.stdscr
117
+ win.clear
118
+ end
119
+
120
+ # Internal: Start a separate thread to handle user input (non-blocking).
121
+ #
122
+ # Returns nothing.
123
+ def start_input_handling
124
+ @input_thread = Thread.new do
125
+ loop do
126
+ handle_input
127
+ sleep 0.1
128
+ break if should_exit
129
+ end
130
+ rescue => e
131
+ win.setpos(0, 0)
132
+ win.addstr("Error in input thread: #{e.message}")
133
+ win.refresh
134
+ end
135
+ end
136
+
137
+ # Internal: Handle a single character of user input, toggling pause or stopping
138
+ # the table as appropriate.
139
+ #
140
+ # Returns nothing.
141
+ def handle_input
142
+ case Curses.getch
143
+ when "p"
144
+ toggle_pause
145
+ when "q"
146
+ @should_exit = true
147
+ stop
148
+ end
149
+ end
150
+
151
+ # Internal: Toggle whether the table is paused. When paused, new rows
152
+ # are still collected but not rendered until unpaused.
153
+ #
154
+ # Returns nothing.
155
+ def toggle_pause
156
+ @paused = !paused
157
+ draw_footer
158
+ end
159
+
160
+ # Internal: Draw the entire table (header, rows, status line).
161
+ #
162
+ # Returns nothing.
163
+ def draw
164
+ return unless win
165
+
166
+ win.clear
167
+ draw_header
168
+ draw_body
169
+ draw_footer
170
+ win.refresh
171
+ end
172
+
173
+ # Internal: Draw the header row of the table.
174
+ #
175
+ # Returns nothing.
176
+ def draw_header
177
+ header_cells = layout.build_header_row
178
+ formatted_header_cells = header_cells.zip(layout.columns).map do |cell, column|
179
+ column.format_cell(cell)
180
+ end
181
+ draw_row(0, formatted_header_cells)
182
+ end
183
+
184
+ # Internal: Draw the body of the table (rows of data).
185
+ #
186
+ # Returns nothing.
187
+ def draw_body
188
+ max_display_rows = Curses.lines - 2
189
+ display_rows = rows.first(max_display_rows)
190
+
191
+ display_rows.each_with_index do |row_data, idx|
192
+ cells = layout.build_cells_for_row(row_data)
193
+ formatted_cells = cells.zip(layout.columns).map do |cell, column|
194
+ column.format_cell(cell)
195
+ end
196
+ draw_row(idx + 1, formatted_cells)
197
+ end
198
+ end
199
+
200
+ # Internal: Draw the status line at the bottom of the screen.
201
+ #
202
+ # Returns nothing.
203
+ def draw_footer
204
+ return unless win
205
+
206
+ win.setpos(Curses.lines - 1, 0)
207
+ status = paused ? "PAUSED" : "RUNNING"
208
+ help_text = " | Press 'p' to pause/unpause, 'q' to quit"
209
+ win.addstr("Status: #{status}#{help_text}".ljust(Curses.cols))
210
+ win.refresh
211
+ end
212
+
213
+ # Internal: Draw a single row of the table, given already-formatted cells.
214
+ #
215
+ # y_pos - The Integer row position (0-based) on the screen to draw.
216
+ # formatted_cells - An Array of [string_value, color], as returned by column formatting.
217
+ #
218
+ # Returns nothing.
219
+ def draw_row(y_pos, formatted_cells)
220
+ x_pos = 0
221
+ formatted_cells.each do |str_value, color|
222
+ win.setpos(y_pos, x_pos)
223
+
224
+ if y_pos == 0
225
+ # Bold the header row
226
+ win.attron(Curses::A_BOLD) { draw_with_color(str_value, color) }
227
+ else
228
+ draw_with_color(str_value, color)
229
+ end
230
+
231
+ x_pos += str_value.length + 1
232
+ end
233
+ end
234
+
235
+ # Internal: Add a string to the screen, optionally with a color attribute.
236
+ #
237
+ # str_value - The String text to draw.
238
+ # color - An Integer color index or Symbol referencing a base color in Colors.
239
+ #
240
+ # Returns nothing.
241
+ def draw_with_color(str_value, color)
242
+ if color
243
+ color_index =
244
+ case color
245
+ when Integer
246
+ color
247
+ when Symbol, String
248
+ Utils::Colors::BASE_COLORS[color.to_sym]&.first
249
+ end
250
+
251
+ if color_index
252
+ win.attron(Curses.color_pair(color_index + 1)) { win.addstr(str_value) }
253
+ else
254
+ win.addstr(str_value)
255
+ end
256
+ else
257
+ win.addstr(str_value)
258
+ end
259
+ end
260
+ end
261
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hawktui
4
+ module Utils
5
+ module Colors
6
+ # Define base color ranges
7
+ STANDARD_COLORS = (0..15).to_a
8
+ COLOR_CUBE = (16..231).to_a
9
+ GRAYSCALE = (232..255).to_a
10
+ BASE_COLORS = {
11
+ black: [0, 8], # Standard and bright black
12
+ red: [1, 9], # Standard and bright red
13
+ green: [2, 10], # Standard and bright green
14
+ yellow: [3, 11], # Standard and bright yellow
15
+ blue: [4, 12], # Standard and bright blue
16
+ magenta: [5, 13], # Standard and bright magenta
17
+ cyan: [6, 14], # Standard and bright cyan
18
+ white: [7, 15] # Standard and bright white
19
+ }.freeze
20
+
21
+ # Human-readable mapping for colors
22
+ COLOR_MAP = BASE_COLORS.keys.map(&:to_s).sort
23
+
24
+ # Helper for composite colors (yellow, cyan, magenta)
25
+ COMPOSITE_COLORS = {
26
+ yellow: ->(r, g, b) { r == g && r > b },
27
+ cyan: ->(r, g, b) { g == b && g > r },
28
+ magenta: ->(r, g, b) { r == b && r > g }
29
+ }.freeze
30
+
31
+ # Public: Find all shades for a base or composite color in the color cube.
32
+ # Returns an array of color indexes that represent different intensities
33
+ # of the requested color within the 216-color cube (indexes 16-231).
34
+ #
35
+ # Examples
36
+ #
37
+ # Colors.color_cube_shades(:red)
38
+ # # => [196, 197, 198, 199, 200, 201]
39
+ #
40
+ # Colors.color_cube_shades(:yellow)
41
+ # # => [226, 227, 228, 229, 230]
42
+ #
43
+ # base_color - A Symbol or String representing the color to find shades for.
44
+ # Must be one of: red, green, blue, yellow, cyan, or magenta.
45
+ #
46
+ # Returns an Array of Integers representing color indexes in the color cube.
47
+ def self.color_cube_shades(base_color)
48
+ base_color = base_color.to_sym
49
+ color_index = {red: 0, green: 1, blue: 2}[base_color]
50
+
51
+ if color_index
52
+ # Handle primary colors (red, green, blue)
53
+ COLOR_CUBE.select do |color|
54
+ r, g, b = color_cube_rgb(color)
55
+ [r, g, b].each_with_index.max[1] == color_index
56
+ end
57
+ elsif COMPOSITE_COLORS[base_color]
58
+ # Handle composite colors (yellow, cyan, magenta)
59
+ COLOR_CUBE.select do |color|
60
+ r, g, b = color_cube_rgb(color)
61
+ COMPOSITE_COLORS[base_color].call(r, g, b)
62
+ end
63
+ else
64
+ [] # Return empty array for unrecognized colors
65
+ end
66
+ end
67
+
68
+ # Public: Decompose a color cube index into its RGB components.
69
+ # Converts a color index (16-231) into RGB values on a scale of 0-5.
70
+ #
71
+ # Examples
72
+ #
73
+ # Colors.color_cube_rgb(16)
74
+ # # => [0, 0, 0]
75
+ #
76
+ # Colors.color_cube_rgb(231)
77
+ # # => [5, 5, 5]
78
+ #
79
+ # color - Integer representing the color index in the color cube (16-231)
80
+ #
81
+ # Returns an Array of three Integers representing RGB values (0-5)
82
+ def self.color_cube_rgb(color)
83
+ red_intensity = (color - 16) / 36
84
+ green_intensity = ((color - 16) % 36) / 6
85
+ blue_intensity = (color - 16) % 6
86
+ [red_intensity, green_intensity, blue_intensity]
87
+ end
88
+
89
+ # Public: Get all available shades for a given color.
90
+ # Combines both the base color values (standard + bright)
91
+ # and any matching shades from the color cube.
92
+ #
93
+ # Examples
94
+ #
95
+ # Colors.shades_for(:red)
96
+ # # => [1, 9, 196, 197, 198, 199, 200, 201]
97
+ #
98
+ # Colors.shades_for(:white)
99
+ # # => [7, 15]
100
+ #
101
+ # color_name - A Symbol or String representing the color
102
+ #
103
+ # Returns an Array of Integers representing color indexes
104
+ def self.shades_for(color_name)
105
+ (BASE_COLORS[color_name.to_sym] || []) + color_cube_shades(color_name)
106
+ end
107
+
108
+ # Public: Initialize all possible color pairs for use with Curses.
109
+ # Sets up 256 color pairs, each with a unique foreground color
110
+ # and black background.
111
+ #
112
+ # Examples
113
+ #
114
+ # Colors.setup_colors
115
+ # # => Sets up color pairs 1-256
116
+ #
117
+ # Returns nothing.
118
+ def self.setup_colors
119
+ Curses.start_color
120
+ (0..255).each do |color|
121
+ Curses.init_pair(color + 1, color, Curses::COLOR_BLACK)
122
+ end
123
+ end
124
+
125
+ # Public: Get a comma-separated list of all available base colors.
126
+ #
127
+ # Examples
128
+ #
129
+ # Colors.available_colors
130
+ # # => "black, blue, cyan, green, magenta, red, white, yellow"
131
+ #
132
+ # Returns a String of color names separated by commas
133
+ def self.available_colors
134
+ COLOR_MAP.join(", ")
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hawktui
4
+ VERSION = "0.1.0"
5
+ end
data/lib/hawktui.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "hawktui/version"
4
+
5
+ module Hawktui
6
+ class Error < StandardError; end
7
+
8
+ # Your code goes here...
9
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hawktui
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Hoyt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-01-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: curses
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.7
27
+ description: Use this gem to build TUIs in Ruby.
28
+ email:
29
+ - jonmagic@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".standard.yml"
35
+ - LICENSE
36
+ - README.md
37
+ - Rakefile
38
+ - hawktui.jpeg
39
+ - hawktui.txt
40
+ - lib/hawktui.rb
41
+ - lib/hawktui/streaming_table.rb
42
+ - lib/hawktui/streaming_table/cell.rb
43
+ - lib/hawktui/streaming_table/column.rb
44
+ - lib/hawktui/streaming_table/layout.rb
45
+ - lib/hawktui/utils/colors.rb
46
+ - lib/hawktui/version.rb
47
+ homepage: https://github.com/jonmagic/hawktui
48
+ licenses:
49
+ - MIT
50
+ metadata:
51
+ allowed_push_host: https://rubygems.org
52
+ homepage_uri: https://github.com/jonmagic/hawktui
53
+ changelog_uri: https://github.com/jonmagic/hawktui/blob/main/CHANGELOG.md
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.0.0
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubygems_version: 3.5.16
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: HawkTui is a simple and easy to use TUI (Terminal User Interface) library
73
+ for Ruby.
74
+ test_files: []