table_tennis 0.0.1
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/.github/workflows/test.yml +26 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +58 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +122 -0
- data/LICENSE +21 -0
- data/README.md +154 -0
- data/Rakefile +12 -0
- data/justfile +75 -0
- data/lib/table_tennis/column.rb +41 -0
- data/lib/table_tennis/config.rb +221 -0
- data/lib/table_tennis/row.rb +9 -0
- data/lib/table_tennis/stage/base.rb +19 -0
- data/lib/table_tennis/stage/format.rb +68 -0
- data/lib/table_tennis/stage/layout.rb +76 -0
- data/lib/table_tennis/stage/painter.rb +84 -0
- data/lib/table_tennis/stage/render.rb +146 -0
- data/lib/table_tennis/table.rb +79 -0
- data/lib/table_tennis/table_data.rb +161 -0
- data/lib/table_tennis/theme.rb +92 -0
- data/lib/table_tennis/util/colors.rb +524 -0
- data/lib/table_tennis/util/inspectable.rb +25 -0
- data/lib/table_tennis/util/scale.rb +55 -0
- data/lib/table_tennis/util/strings.rb +62 -0
- data/lib/table_tennis/util/termbg.rb +275 -0
- data/lib/table_tennis/version.rb +3 -0
- data/lib/table_tennis.rb +29 -0
- data/screenshots/dark.png +0 -0
- data/screenshots/droids.png +0 -0
- data/screenshots/hope.png +0 -0
- data/screenshots/light.png +0 -0
- data/screenshots/row_numbers.png +0 -0
- data/screenshots/scales.png +0 -0
- data/screenshots/themes.png +0 -0
- data/table_tennis.gemspec +28 -0
- metadata +145 -0
@@ -0,0 +1,524 @@
|
|
1
|
+
module TableTennis
|
2
|
+
module Util
|
3
|
+
# Named color names and misc color helpers.
|
4
|
+
module Colors
|
5
|
+
module_function
|
6
|
+
|
7
|
+
NAMED = {
|
8
|
+
#
|
9
|
+
# css colors
|
10
|
+
# https://htmlcolorcodes.com/color-names/
|
11
|
+
#
|
12
|
+
|
13
|
+
# red
|
14
|
+
"indianred" => "#cd5c5c",
|
15
|
+
"lightcoral" => "#f08080",
|
16
|
+
"salmon" => "#fa8072",
|
17
|
+
"darksalmon" => "#e9967a",
|
18
|
+
"lightsalmon" => "#ffa07a",
|
19
|
+
"crimson" => "#dc143c",
|
20
|
+
"red" => "#ff0000",
|
21
|
+
"firebrick" => "#b22222",
|
22
|
+
"darkred" => "#8b0000",
|
23
|
+
# pink
|
24
|
+
"pink" => "#ffc0cb",
|
25
|
+
"lightpink" => "#ffb6c1",
|
26
|
+
"hotpink" => "#ff69b4",
|
27
|
+
"deeppink" => "#ff1493",
|
28
|
+
"mediumvioletred" => "#c71585",
|
29
|
+
"palevioletred" => "#db7093",
|
30
|
+
# orange
|
31
|
+
# "lightsalmon" => "#ffa07a",
|
32
|
+
"coral" => "#ff7f50",
|
33
|
+
"tomato" => "#ff6347",
|
34
|
+
"orangered" => "#ff4500",
|
35
|
+
"darkorange" => "#ff8c00",
|
36
|
+
"orange" => "#ffa500",
|
37
|
+
# gold
|
38
|
+
"gold" => "#ffd700",
|
39
|
+
"yellow" => "#ffff00",
|
40
|
+
"lightyellow" => "#ffffe0",
|
41
|
+
"lemonchiffon" => "#fffacd",
|
42
|
+
"lightgoldenrodyellow" => "#fafad2",
|
43
|
+
"papayawhip" => "#ffefd5",
|
44
|
+
"moccasin" => "#ffe4b5",
|
45
|
+
"peachpuff" => "#ffdab9",
|
46
|
+
"palegoldenrod" => "#eee8aa",
|
47
|
+
"khaki" => "#f0e68c",
|
48
|
+
"darkkhaki" => "#bdb76b",
|
49
|
+
# purple
|
50
|
+
"lavender" => "#e6e6fa",
|
51
|
+
"thistle" => "#d8bfd8",
|
52
|
+
"plum" => "#dda0dd",
|
53
|
+
"violet" => "#ee82ee",
|
54
|
+
"orchid" => "#da70d6",
|
55
|
+
"fuchsia" => "#ff00ff",
|
56
|
+
"magenta" => "#ff00ff",
|
57
|
+
"mediumorchid" => "#ba55d3",
|
58
|
+
"mediumpurple" => "#9370db",
|
59
|
+
"rebeccapurple" => "#663399",
|
60
|
+
"blueviolet" => "#8a2be2",
|
61
|
+
"darkviolet" => "#9400d3",
|
62
|
+
"darkorchid" => "#9932cc",
|
63
|
+
"darkmagenta" => "#8b008b",
|
64
|
+
"purple" => "#800080",
|
65
|
+
"indigo" => "#4b0082",
|
66
|
+
"slateblue" => "#6a5acd",
|
67
|
+
"darkslateblue" => "#483d8b",
|
68
|
+
# "mediumslateblue" => "#7b68ee",
|
69
|
+
# green
|
70
|
+
"greenyellow" => "#adff2f",
|
71
|
+
"chartreuse" => "#7fff00",
|
72
|
+
"lawngreen" => "#7cfc00",
|
73
|
+
"lime" => "#00ff00",
|
74
|
+
"limegreen" => "#32cd32",
|
75
|
+
"palegreen" => "#98fb98",
|
76
|
+
"lightgreen" => "#90ee90",
|
77
|
+
"mediumspringgreen" => "#00fa9a",
|
78
|
+
"springgreen" => "#00ff7f",
|
79
|
+
"mediumseagreen" => "#3cb371",
|
80
|
+
"seagreen" => "#2e8b57",
|
81
|
+
"forestgreen" => "#228b22",
|
82
|
+
"green" => "#008000",
|
83
|
+
"darkgreen" => "#006400",
|
84
|
+
"yellowgreen" => "#9acd32",
|
85
|
+
"olivedrab" => "#6b8e23",
|
86
|
+
"olive" => "#808000",
|
87
|
+
"darkolivegreen" => "#556b2f",
|
88
|
+
"mediumaquamarine" => "#66cdaa",
|
89
|
+
"darkseagreen" => "#8fbc8b",
|
90
|
+
"lightseagreen" => "#20b2aa",
|
91
|
+
"darkcyan" => "#008b8b",
|
92
|
+
"teal" => "#008080",
|
93
|
+
# blue
|
94
|
+
"aqua" => "#00ffff",
|
95
|
+
"cyan" => "#00ffff",
|
96
|
+
"lightcyan" => "#e0ffff",
|
97
|
+
"paleturquoise" => "#afeeee",
|
98
|
+
"aquamarine" => "#7fffd4",
|
99
|
+
"turquoise" => "#40e0d0",
|
100
|
+
"mediumturquoise" => "#48d1cc",
|
101
|
+
"darkturquoise" => "#00ced1",
|
102
|
+
"cadetblue" => "#5f9ea0",
|
103
|
+
"steelblue" => "#4682b4",
|
104
|
+
"lightsteelblue" => "#b0c4de",
|
105
|
+
"powderblue" => "#b0e0e6",
|
106
|
+
"lightblue" => "#add8e6",
|
107
|
+
"skyblue" => "#87ceeb",
|
108
|
+
"lightskyblue" => "#87cefa",
|
109
|
+
"deepskyblue" => "#00bfff",
|
110
|
+
"dodgerblue" => "#1e90ff",
|
111
|
+
"cornflowerblue" => "#6495ed",
|
112
|
+
"mediumslateblue" => "#7b68ee",
|
113
|
+
"royalblue" => "#4169e1",
|
114
|
+
"blue" => "#0000ff",
|
115
|
+
"mediumblue" => "#0000cd",
|
116
|
+
"darkblue" => "#00008b",
|
117
|
+
"navy" => "#000080",
|
118
|
+
"midnightblue" => "#191970",
|
119
|
+
# brown
|
120
|
+
"cornsilk" => "#fff8dc",
|
121
|
+
"blanchedalmond" => "#ffebcd",
|
122
|
+
"bisque" => "#ffe4c4",
|
123
|
+
"navajowhite" => "#ffdead",
|
124
|
+
"wheat" => "#f5deb3",
|
125
|
+
"burlywood" => "#deb887",
|
126
|
+
"tan" => "#d2b48c",
|
127
|
+
"rosybrown" => "#bc8f8f",
|
128
|
+
"sandybrown" => "#f4a460",
|
129
|
+
"goldenrod" => "#daa520",
|
130
|
+
"darkgoldenrod" => "#b8860b",
|
131
|
+
"peru" => "#cd853f",
|
132
|
+
"chocolate" => "#d2691e",
|
133
|
+
"saddlebrown" => "#8b4513",
|
134
|
+
"sienna" => "#a0522d",
|
135
|
+
"brown" => "#a52a2a",
|
136
|
+
"maroon" => "#800000",
|
137
|
+
# white
|
138
|
+
"white" => "#ffffff",
|
139
|
+
"snow" => "#fffafa",
|
140
|
+
"honeydew" => "#f0fff0",
|
141
|
+
"mintcream" => "#f5fffa",
|
142
|
+
"azure" => "#f0ffff",
|
143
|
+
"aliceblue" => "#f0f8ff",
|
144
|
+
"ghostwhite" => "#f8f8ff",
|
145
|
+
"whitesmoke" => "#f5f5f5",
|
146
|
+
"seashell" => "#fff5ee",
|
147
|
+
"beige" => "#f5f5dc",
|
148
|
+
"oldlace" => "#fdf5e6",
|
149
|
+
"floralwhite" => "#fffaf0",
|
150
|
+
"ivory" => "#fffff0",
|
151
|
+
"antiquewhite" => "#faebd7",
|
152
|
+
"linen" => "#faf0e6",
|
153
|
+
"lavenderblush" => "#fff0f5",
|
154
|
+
"mistyrose" => "#ffe4e1",
|
155
|
+
# gray
|
156
|
+
"gainsboro" => "#dcdcdc",
|
157
|
+
"lightgray" => "#d3d3d3",
|
158
|
+
"silver" => "#c0c0c0",
|
159
|
+
"darkgray" => "#a9a9a9",
|
160
|
+
"gray" => "#808080",
|
161
|
+
"dimgray" => "#696969",
|
162
|
+
"lightslategray" => "#778899",
|
163
|
+
"slategray" => "#708090",
|
164
|
+
"darkslategray" => "#2f4f4f",
|
165
|
+
"black" => "#000000",
|
166
|
+
|
167
|
+
#
|
168
|
+
# tailwind
|
169
|
+
#
|
170
|
+
|
171
|
+
"slate-50" => "#f8fafc",
|
172
|
+
"slate-100" => "#f1f5f9",
|
173
|
+
"slate-200" => "#e2e8f0",
|
174
|
+
"slate-300" => "#cbd5e1",
|
175
|
+
"slate-400" => "#94a3b8",
|
176
|
+
"slate-500" => "#64748b",
|
177
|
+
"slate-600" => "#475569",
|
178
|
+
"slate-700" => "#334155",
|
179
|
+
"slate-800" => "#1e293b",
|
180
|
+
"slate-900" => "#0f172a",
|
181
|
+
"gray-50" => "#f9fafb",
|
182
|
+
"gray-100" => "#f3f4f6",
|
183
|
+
"gray-200" => "#e5e7eb",
|
184
|
+
"gray-300" => "#d1d5db",
|
185
|
+
"gray-400" => "#9ca3af",
|
186
|
+
"gray-500" => "#6b7280",
|
187
|
+
"gray-600" => "#4b5563",
|
188
|
+
"gray-700" => "#374151",
|
189
|
+
"gray-800" => "#1f2937",
|
190
|
+
"gray-900" => "#111827",
|
191
|
+
"zinc-50" => "#fafafa",
|
192
|
+
"zinc-100" => "#f4f4f5",
|
193
|
+
"zinc-200" => "#e4e4e7",
|
194
|
+
"zinc-300" => "#d4d4d8",
|
195
|
+
"zinc-400" => "#a1a1aa",
|
196
|
+
"zinc-500" => "#71717a",
|
197
|
+
"zinc-600" => "#52525b",
|
198
|
+
"zinc-700" => "#3f3f46",
|
199
|
+
"zinc-800" => "#27272a",
|
200
|
+
"zinc-900" => "#18181b",
|
201
|
+
"neutral-50" => "#fafafa",
|
202
|
+
"neutral-100" => "#f5f5f5",
|
203
|
+
"neutral-200" => "#e5e5e5",
|
204
|
+
"neutral-300" => "#d4d4d4",
|
205
|
+
"neutral-400" => "#a3a3a3",
|
206
|
+
"neutral-500" => "#737373",
|
207
|
+
"neutral-600" => "#525252",
|
208
|
+
"neutral-700" => "#404040",
|
209
|
+
"neutral-800" => "#262626",
|
210
|
+
"neutral-900" => "#171717",
|
211
|
+
"stone-50" => "#fafaf9",
|
212
|
+
"stone-100" => "#f5f5f4",
|
213
|
+
"stone-200" => "#e7e5e4",
|
214
|
+
"stone-300" => "#d6d3d1",
|
215
|
+
"stone-400" => "#a8a29e",
|
216
|
+
"stone-500" => "#78716c",
|
217
|
+
"stone-600" => "#57534e",
|
218
|
+
"stone-700" => "#44403c",
|
219
|
+
"stone-800" => "#292524",
|
220
|
+
"stone-900" => "#1c1917",
|
221
|
+
"red-50" => "#fef2f2",
|
222
|
+
"red-100" => "#fee2e2",
|
223
|
+
"red-200" => "#fecaca",
|
224
|
+
"red-300" => "#fca5a5",
|
225
|
+
"red-400" => "#f87171",
|
226
|
+
"red-500" => "#ef4444",
|
227
|
+
"red-600" => "#dc2626",
|
228
|
+
"red-700" => "#b91c1c",
|
229
|
+
"red-800" => "#991b1b",
|
230
|
+
"red-900" => "#7f1d1d",
|
231
|
+
"orange-50" => "#fff7ed",
|
232
|
+
"orange-100" => "#ffedd5",
|
233
|
+
"orange-200" => "#fed7aa",
|
234
|
+
"orange-300" => "#fdba74",
|
235
|
+
"orange-400" => "#fb923c",
|
236
|
+
"orange-500" => "#f97316",
|
237
|
+
"orange-600" => "#ea580c",
|
238
|
+
"orange-700" => "#c2410c",
|
239
|
+
"orange-800" => "#9a3412",
|
240
|
+
"orange-900" => "#7c2d12",
|
241
|
+
"amber-50" => "#fffbeb",
|
242
|
+
"amber-100" => "#fef3c7",
|
243
|
+
"amber-200" => "#fde68a",
|
244
|
+
"amber-300" => "#fcd34d",
|
245
|
+
"amber-400" => "#fbbf24",
|
246
|
+
"amber-500" => "#f59e0b",
|
247
|
+
"amber-600" => "#d97706",
|
248
|
+
"amber-700" => "#b45309",
|
249
|
+
"amber-800" => "#92400e",
|
250
|
+
"amber-900" => "#78350f",
|
251
|
+
"yellow-50" => "#fefce8",
|
252
|
+
"yellow-100" => "#fef9c3",
|
253
|
+
"yellow-200" => "#fef08a",
|
254
|
+
"yellow-300" => "#fde047",
|
255
|
+
"yellow-400" => "#facc15",
|
256
|
+
"yellow-500" => "#eab308",
|
257
|
+
"yellow-600" => "#ca8a04",
|
258
|
+
"yellow-700" => "#a16207",
|
259
|
+
"yellow-800" => "#854d0e",
|
260
|
+
"yellow-900" => "#713f12",
|
261
|
+
"lime-50" => "#f7fee7",
|
262
|
+
"lime-100" => "#ecfccb",
|
263
|
+
"lime-200" => "#d9f99d",
|
264
|
+
"lime-300" => "#bef264",
|
265
|
+
"lime-400" => "#a3e635",
|
266
|
+
"lime-500" => "#84cc16",
|
267
|
+
"lime-600" => "#65a30d",
|
268
|
+
"lime-700" => "#4d7c0f",
|
269
|
+
"lime-800" => "#3f6212",
|
270
|
+
"lime-900" => "#365314",
|
271
|
+
"green-50" => "#f0fdf4",
|
272
|
+
"green-100" => "#dcfce7",
|
273
|
+
"green-200" => "#bbf7d0",
|
274
|
+
"green-300" => "#86efac",
|
275
|
+
"green-400" => "#4ade80",
|
276
|
+
"green-500" => "#22c55e",
|
277
|
+
"green-600" => "#16a34a",
|
278
|
+
"green-700" => "#15803d",
|
279
|
+
"green-800" => "#166534",
|
280
|
+
"green-900" => "#14532d",
|
281
|
+
"emerald-50" => "#ecfdf5",
|
282
|
+
"emerald-100" => "#d1fae5",
|
283
|
+
"emerald-200" => "#a7f3d0",
|
284
|
+
"emerald-300" => "#6ee7b7",
|
285
|
+
"emerald-400" => "#34d399",
|
286
|
+
"emerald-500" => "#10b981",
|
287
|
+
"emerald-600" => "#059669",
|
288
|
+
"emerald-700" => "#047857",
|
289
|
+
"emerald-800" => "#065f46",
|
290
|
+
"emerald-900" => "#064e3b",
|
291
|
+
"teal-50" => "#f0fdfa",
|
292
|
+
"teal-100" => "#ccfbf1",
|
293
|
+
"teal-200" => "#99f6e4",
|
294
|
+
"teal-300" => "#5eead4",
|
295
|
+
"teal-400" => "#2dd4bf",
|
296
|
+
"teal-500" => "#14b8a6",
|
297
|
+
"teal-600" => "#0d9488",
|
298
|
+
"teal-700" => "#0f766e",
|
299
|
+
"teal-800" => "#115e59",
|
300
|
+
"teal-900" => "#134e4a",
|
301
|
+
"cyan-50" => "#ecfeff",
|
302
|
+
"cyan-100" => "#cffafe",
|
303
|
+
"cyan-200" => "#a5f3fc",
|
304
|
+
"cyan-300" => "#67e8f9",
|
305
|
+
"cyan-400" => "#22d3ee",
|
306
|
+
"cyan-500" => "#06b6d4",
|
307
|
+
"cyan-600" => "#0891b2",
|
308
|
+
"cyan-700" => "#0e7490",
|
309
|
+
"cyan-800" => "#155e75",
|
310
|
+
"cyan-900" => "#164e63",
|
311
|
+
"sky-50" => "#f0f9ff",
|
312
|
+
"sky-100" => "#e0f2fe",
|
313
|
+
"sky-200" => "#bae6fd",
|
314
|
+
"sky-300" => "#7dd3fc",
|
315
|
+
"sky-400" => "#38bdf8",
|
316
|
+
"sky-500" => "#0ea5e9",
|
317
|
+
"sky-600" => "#0284c7",
|
318
|
+
"sky-700" => "#0369a1",
|
319
|
+
"sky-800" => "#075985",
|
320
|
+
"sky-900" => "#0c4a6e",
|
321
|
+
"blue-50" => "#eff6ff",
|
322
|
+
"blue-100" => "#dbeafe",
|
323
|
+
"blue-200" => "#bfdbfe",
|
324
|
+
"blue-300" => "#93c5fd",
|
325
|
+
"blue-400" => "#60a5fa",
|
326
|
+
"blue-500" => "#3b82f6",
|
327
|
+
"blue-600" => "#2563eb",
|
328
|
+
"blue-700" => "#1d4ed8",
|
329
|
+
"blue-800" => "#1e40af",
|
330
|
+
"blue-900" => "#1e3a8a",
|
331
|
+
"indigo-50" => "#eef2ff",
|
332
|
+
"indigo-100" => "#e0e7ff",
|
333
|
+
"indigo-200" => "#c7d2fe",
|
334
|
+
"indigo-300" => "#a5b4fc",
|
335
|
+
"indigo-400" => "#818cf8",
|
336
|
+
"indigo-500" => "#6366f1",
|
337
|
+
"indigo-600" => "#4f46e5",
|
338
|
+
"indigo-700" => "#4338ca",
|
339
|
+
"indigo-800" => "#3730a3",
|
340
|
+
"indigo-900" => "#312e81",
|
341
|
+
"violet-50" => "#f5f3ff",
|
342
|
+
"violet-100" => "#ede9fe",
|
343
|
+
"violet-200" => "#ddd6fe",
|
344
|
+
"violet-300" => "#c4b5fd",
|
345
|
+
"violet-400" => "#a78bfa",
|
346
|
+
"violet-500" => "#8b5cf6",
|
347
|
+
"violet-600" => "#7c3aed",
|
348
|
+
"violet-700" => "#6d28d9",
|
349
|
+
"violet-800" => "#5b21b6",
|
350
|
+
"violet-900" => "#4c1d95",
|
351
|
+
"purple-50" => "#faf5ff",
|
352
|
+
"purple-100" => "#f3e8ff",
|
353
|
+
"purple-200" => "#e9d5ff",
|
354
|
+
"purple-300" => "#d8b4fe",
|
355
|
+
"purple-400" => "#c084fc",
|
356
|
+
"purple-500" => "#a855f7",
|
357
|
+
"purple-600" => "#9333ea",
|
358
|
+
"purple-700" => "#7e22ce",
|
359
|
+
"purple-800" => "#6b21a8",
|
360
|
+
"purple-900" => "#581c87",
|
361
|
+
"fuchsia-50" => "#fdf4ff",
|
362
|
+
"fuchsia-100" => "#fae8ff",
|
363
|
+
"fuchsia-200" => "#f5d0fe",
|
364
|
+
"fuchsia-300" => "#f0abfc",
|
365
|
+
"fuchsia-400" => "#e879f9",
|
366
|
+
"fuchsia-500" => "#d946ef",
|
367
|
+
"fuchsia-600" => "#c026d3",
|
368
|
+
"fuchsia-700" => "#a21caf",
|
369
|
+
"fuchsia-800" => "#86198f",
|
370
|
+
"fuchsia-900" => "#701a75",
|
371
|
+
"pink-50" => "#fdf2f8",
|
372
|
+
"pink-100" => "#fce7f3",
|
373
|
+
"pink-200" => "#fbcfe8",
|
374
|
+
"pink-300" => "#f9a8d4",
|
375
|
+
"pink-400" => "#f472b6",
|
376
|
+
"pink-500" => "#ec4899",
|
377
|
+
"pink-600" => "#db2777",
|
378
|
+
"pink-700" => "#be185d",
|
379
|
+
"pink-800" => "#9d174d",
|
380
|
+
"pink-900" => "#831843",
|
381
|
+
"rose-50" => "#fff1f2",
|
382
|
+
"rose-100" => "#ffe4e6",
|
383
|
+
"rose-200" => "#fecdd3",
|
384
|
+
"rose-300" => "#fda4af",
|
385
|
+
"rose-400" => "#fb7185",
|
386
|
+
"rose-500" => "#f43f5e",
|
387
|
+
"rose-600" => "#e11d48",
|
388
|
+
"rose-700" => "#be123c",
|
389
|
+
"rose-800" => "#9f1239",
|
390
|
+
"rose-900" => "#881337",
|
391
|
+
}
|
392
|
+
|
393
|
+
SYMBOLS = (Paint::ANSI_COLORS.keys + Paint::ANSI_EFFECTS.keys).to_set
|
394
|
+
|
395
|
+
# we support a few different kinds of colors
|
396
|
+
def get(color)
|
397
|
+
case color
|
398
|
+
# nil is ok
|
399
|
+
when nil
|
400
|
+
return nil
|
401
|
+
|
402
|
+
# hex string
|
403
|
+
when /^#?[0-9a-f]{3}([0-9a-f]{3})?$/i
|
404
|
+
return color
|
405
|
+
|
406
|
+
when Symbol
|
407
|
+
# Paint ansi color or ansi effect
|
408
|
+
if !SYMBOLS.include?(color)
|
409
|
+
puts "warning: TableTennis #{color.inspect} unknown ansi color/effect symbol"
|
410
|
+
return
|
411
|
+
end
|
412
|
+
return color
|
413
|
+
|
414
|
+
when String
|
415
|
+
if !(hex = NAMED[color.downcase])
|
416
|
+
puts "warning: TableTennis #{color.inspect} unknown named color string"
|
417
|
+
return
|
418
|
+
end
|
419
|
+
return hex
|
420
|
+
|
421
|
+
else
|
422
|
+
puts "warning: TableTennis #{color.inspect} unknown color"
|
423
|
+
return
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
# sRGB to Y (relative luminance)
|
428
|
+
def luma(hex)
|
429
|
+
rgb = to_rgb(hex)
|
430
|
+
return if !rgb
|
431
|
+
rgb = rgb.map { _1 / 255.0 }
|
432
|
+
coeff = [0.2126, 0.7152, 0.0722]
|
433
|
+
rgb.zip(coeff).sum { (_1**2.2) * _2 }.round(3)
|
434
|
+
end
|
435
|
+
|
436
|
+
DARK_LUMA = 0.36
|
437
|
+
|
438
|
+
# is this color dark?
|
439
|
+
def dark?(color)
|
440
|
+
luma = luma(color)
|
441
|
+
!luma || luma < DARK_LUMA
|
442
|
+
end
|
443
|
+
|
444
|
+
def contrast(bg)
|
445
|
+
dark?(bg) ? "white" : "black"
|
446
|
+
end
|
447
|
+
|
448
|
+
# [r,g,b] => "#rrggbb"
|
449
|
+
def to_hex(rgb)
|
450
|
+
sprintf("#%02x%02x%02x", *rgb)
|
451
|
+
end
|
452
|
+
|
453
|
+
# "#?(rgb|rrggbb|rrrrggggbbbb)" => [r,g,b]
|
454
|
+
def to_rgb(hex)
|
455
|
+
if !hex.match(/\A#?([0-9a-f]+)\z/i)
|
456
|
+
return
|
457
|
+
end
|
458
|
+
hex = $1
|
459
|
+
case hex.length
|
460
|
+
when 3 then hex.scan(/./).map { "#{_1}#{_1}" }
|
461
|
+
when 6 then hex.scan(/../)
|
462
|
+
when 9 then hex.scan(/.../)
|
463
|
+
when 12 then hex.scan(/..../)
|
464
|
+
else; return
|
465
|
+
end.map { _1[0, 2].to_i(16) }
|
466
|
+
end
|
467
|
+
|
468
|
+
# lookup RGB for an ansi fg or bg color
|
469
|
+
def ansi_color_to_hex(num)
|
470
|
+
return if !(0..15).cover?(num)
|
471
|
+
rgb = if num < 8
|
472
|
+
Paint::RGB_COLORS_ANSI.values[num]
|
473
|
+
else
|
474
|
+
Paint::RGB_COLORS_ANSI_BRIGHT.values[num - 8]
|
475
|
+
end
|
476
|
+
to_hex(rgb)
|
477
|
+
end
|
478
|
+
|
479
|
+
# print all colors to $stdout, helps with creating themes
|
480
|
+
def spectrum
|
481
|
+
max = NAMED.keys.map(&:length).max
|
482
|
+
fmt = " %-#{max}s %s %0.3f "
|
483
|
+
NAMED.each do |name, color|
|
484
|
+
str = sprintf(fmt, name, color, luma(color))
|
485
|
+
str1 = Paint[str, color, "white"]
|
486
|
+
str2 = Paint[str, color, "black"]
|
487
|
+
str3 = Paint[" white ", "white", color]
|
488
|
+
str4 = Paint[" black ", "black", color]
|
489
|
+
puts "#{str1}#{str2}#{str3}#{str4}"
|
490
|
+
end
|
491
|
+
|
492
|
+
fmt = " ansi %-6s %-8s "
|
493
|
+
colors = Paint::ANSI_COLORS_FOREGROUND.keys
|
494
|
+
puts
|
495
|
+
colors.each do
|
496
|
+
fg = sprintf(fmt, "normal", _1.inspect)
|
497
|
+
str1 = Paint[fg, _1, :white]
|
498
|
+
str2 = Paint[fg, _1, :black]
|
499
|
+
puts "#{str1}#{str2}"
|
500
|
+
end
|
501
|
+
puts
|
502
|
+
colors.each do
|
503
|
+
str = sprintf(fmt, ":bold", _1.inspect)
|
504
|
+
str1 = Paint[str, _1, :bold, :white]
|
505
|
+
str2 = Paint[str, _1, :bold, :black]
|
506
|
+
puts "#{str1}#{str2}"
|
507
|
+
end
|
508
|
+
puts
|
509
|
+
colors.each do
|
510
|
+
str = sprintf(fmt, ":faint", _1.inspect)
|
511
|
+
str1 = Paint[str, _1, :faint, :white]
|
512
|
+
str2 = Paint[str, _1, :faint, :black]
|
513
|
+
puts "#{str1}#{str2}"
|
514
|
+
end
|
515
|
+
puts
|
516
|
+
colors.each do
|
517
|
+
str1 = Paint[sprintf(" :white on %-8s ", _1.inspect), :white, _1]
|
518
|
+
str2 = Paint[sprintf(" :black on %-8s ", _1.inspect), :black, _1]
|
519
|
+
puts "#{str1}#{str2}"
|
520
|
+
end
|
521
|
+
end
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module TableTennis
|
2
|
+
module Util
|
3
|
+
# A mixin to avoid putting all row data into xxx.inspect. This makes
|
4
|
+
# development much easier.
|
5
|
+
module Inspectable
|
6
|
+
def inspect = ENV["MINITEST"] ? super : inspect_without_rows
|
7
|
+
|
8
|
+
def inspect_without_rows
|
9
|
+
vars = instance_variables.filter_map do
|
10
|
+
value = instance_variable_get(_1)
|
11
|
+
if value.is_a?(Array) && _1.to_s =~ /rows$/
|
12
|
+
value = if value.length == 1
|
13
|
+
"[1 row]"
|
14
|
+
else
|
15
|
+
"[#{value.length} rows]"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
"#{_1}=#{value.inspect}"
|
19
|
+
end.join(" ")
|
20
|
+
|
21
|
+
"<#{self.class.name} #{vars}>"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module TableTennis
|
2
|
+
module Util
|
3
|
+
# Helpers for color scales (adding background colors to a column based on the
|
4
|
+
# value of the float in the cell). Like conditional formattiong with a color
|
5
|
+
# range in google sheets.
|
6
|
+
module Scale
|
7
|
+
prepend MemoWise
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
WHITE, GREEN, RED, YELLOW, BLUE = "#fff", "#57bb8a", "#e67c73", "ffd666", "#6c9eeb"
|
12
|
+
|
13
|
+
SCALES = {
|
14
|
+
# white => color
|
15
|
+
g: [WHITE, GREEN],
|
16
|
+
y: [WHITE, YELLOW],
|
17
|
+
r: [WHITE, RED],
|
18
|
+
b: [WHITE, BLUE],
|
19
|
+
|
20
|
+
# green/yellow/red => white
|
21
|
+
gw: [GREEN, WHITE],
|
22
|
+
yw: [YELLOW, WHITE],
|
23
|
+
rw: [RED, WHITE],
|
24
|
+
bw: [BLUE, WHITE],
|
25
|
+
|
26
|
+
# red <=> green
|
27
|
+
rg: [RED, WHITE, GREEN],
|
28
|
+
gr: [GREEN, WHITE, RED],
|
29
|
+
gyr: [GREEN, YELLOW, RED],
|
30
|
+
}
|
31
|
+
|
32
|
+
def interpolate(name, t)
|
33
|
+
t = t.clamp(0, 1).to_f
|
34
|
+
stops = color_stops(name)
|
35
|
+
rgb = if stops.first.length == 2
|
36
|
+
stops.map { lerp(t, _1, _2) }
|
37
|
+
elsif t < 0.5
|
38
|
+
t *= 2
|
39
|
+
stops.map { lerp(t, _1, _2) }
|
40
|
+
else
|
41
|
+
t = (t - 0.5) * 2
|
42
|
+
stops.map { lerp(t, _2, _3) }
|
43
|
+
end
|
44
|
+
Colors.to_hex(rgb)
|
45
|
+
end
|
46
|
+
|
47
|
+
def color_stops(name)
|
48
|
+
SCALES[name].map { Colors.to_rgb(_1) }.transpose
|
49
|
+
end
|
50
|
+
memo_wise self: :color_stops
|
51
|
+
|
52
|
+
def lerp(t, v0, v1) = t * (v1 - v0) + v0
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module TableTennis
|
2
|
+
module Util
|
3
|
+
# Helpers for measuring and truncating strings.
|
4
|
+
module Strings
|
5
|
+
prepend MemoWise
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
# strip ansi codes
|
10
|
+
def unpaint(str) = str.gsub(/\e\[[0-9;]*m/, "")
|
11
|
+
|
12
|
+
def width(text)
|
13
|
+
simple?(text) ? text.length : Unicode::DisplayWidth.of(text)
|
14
|
+
end
|
15
|
+
|
16
|
+
# truncate a string based on the display width of the grapheme clusters.
|
17
|
+
# Should handle emojis and international characters
|
18
|
+
def truncate(text, stop)
|
19
|
+
if simple?(text)
|
20
|
+
return (text.length > stop) ? "#{text[0, stop - 1]}…" : text
|
21
|
+
end
|
22
|
+
|
23
|
+
# get grapheme clusters, and attach zero width graphemes to the previous grapheme
|
24
|
+
list = [].tap do |accum|
|
25
|
+
text.grapheme_clusters.each do
|
26
|
+
if width(_1) == 0 && !accum.empty?
|
27
|
+
accum[-1] = "#{accum[-1]}#{_1}"
|
28
|
+
else
|
29
|
+
accum << _1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
width = 0
|
35
|
+
list.each_index do
|
36
|
+
w = Unicode::DisplayWidth.of(list[_1])
|
37
|
+
next if (width += w) <= stop
|
38
|
+
|
39
|
+
# we've gone too far. do we need to pop for the ellipsis?
|
40
|
+
text = list[0, _1]
|
41
|
+
text.pop if w == 1
|
42
|
+
return "#{text.join}…"
|
43
|
+
end
|
44
|
+
text
|
45
|
+
end
|
46
|
+
|
47
|
+
SIMPLE = /\A[\x00-\x7F–—…·‘’“”•áéíñóúÓ]*\Z/
|
48
|
+
|
49
|
+
# Is this a "simple" string? (no emojis, etc). Caches results for small
|
50
|
+
# strings for performance reasons.
|
51
|
+
def simple?(str)
|
52
|
+
if str.length <= 8
|
53
|
+
@simple ||= Hash.new { _1[_2] = _2.match?(SIMPLE) }
|
54
|
+
@simple[str]
|
55
|
+
else
|
56
|
+
str.match?(SIMPLE)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
private_class_method :simple?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|