hawktui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.standard.yml +3 -0
- data/LICENSE +21 -0
- data/README.md +5 -0
- data/Rakefile +10 -0
- data/hawktui.jpeg +0 -0
- data/hawktui.txt +93 -0
- data/lib/hawktui/streaming_table/cell.rb +53 -0
- data/lib/hawktui/streaming_table/column.rb +53 -0
- data/lib/hawktui/streaming_table/layout.rb +83 -0
- data/lib/hawktui/streaming_table.rb +261 -0
- data/lib/hawktui/utils/colors.rb +138 -0
- data/lib/hawktui/version.rb +5 -0
- data/lib/hawktui.rb +9 -0
- metadata +74 -0
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
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
data/Rakefile
ADDED
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
|
data/lib/hawktui.rb
ADDED
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: []
|