lipgloss 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9901ec6ebe58427fd807f2b904bc0462afa0c98778ebf7e07bb4e2ab9119a1d3
4
- data.tar.gz: db8ccca392715e11e439378696308b8fa7f6aa50a5883b68593aeab54a6076ff
3
+ metadata.gz: 822cdc4a08be4d36feab181fe8933cf9486c1eba70935eca9d73eb6a573d47f1
4
+ data.tar.gz: 2ac7c9626a365e2747050dd8b2cd067ccad05992b4c063b3dbb3668eff6dbc10
5
5
  SHA512:
6
- metadata.gz: 2f1b4cf84ccca46aadebd0b8c34439460c228f9e221f0282e5f7af7e802dc72e8bfd009b99ee7c577e37942395c400e21edde4f20a8a45a9b537f0089994625f
7
- data.tar.gz: 667e9132355d975ffb0db3db29d13eb92680135cf19eea208e51f881041d4cb9d6871c62d642b11b42260496ac9a391342212691f1113e60cedde1b75156ede4
6
+ metadata.gz: e1ee9fc15a1935945789032584bdac9fb983614e24daf66aca646593ae262dcc9cd395f4e5e74ebbe8589ab2f378245e07c660af66d2baf184451f4c843934ed
7
+ data.tar.gz: f67ac668b982e0f6f06ffa6b1c17031ea491a90df729cc2e9f655e0c0ba78b7217020e39adc940be4c0f9864c7f3418f995dd282ffa29996a4dd9609be4b2389
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  <div align="center">
2
2
  <h1>Lipgloss for Ruby</h1>
3
- <h4>CSS-like Terminal Styling for Ruby CLI Apps</h4>
3
+ <h4>Style Definitions for Nice Terminal Layouts</h4>
4
4
 
5
5
  <p>
6
6
  <a href="https://rubygems.org/gems/lipgloss"><img alt="Gem Version" src="https://img.shields.io/gem/v/lipgloss"></a>
7
7
  <a href="https://github.com/marcoroth/lipgloss-ruby/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/marcoroth/lipgloss-ruby"></a>
8
8
  </p>
9
9
 
10
- <p>Ruby bindings for <a href="https://github.com/charmbracelet/lipgloss">charmbracelet/lipgloss</a>.<br/>Style definitions for nice terminal layouts with a fluent, CSS-like API.</p>
10
+ <p>Ruby bindings for <a href="https://github.com/charmbracelet/lipgloss">charmbracelet/lipgloss</a>.<br/>Style definitions for nice terminal layouts. Built with TUIs in mind.</p>
11
11
  </div>
12
12
 
13
13
  ## Installation
@@ -52,7 +52,7 @@ heading = Lipgloss::Style.new
52
52
 
53
53
  body = Lipgloss::Style.new
54
54
  .width(40)
55
- .align(Lipgloss::CENTER)
55
+ .align(:center)
56
56
 
57
57
  puts heading.render("Welcome")
58
58
  puts body.render("Styled terminal output")
@@ -118,21 +118,23 @@ puts style.render("Rainbow border!")
118
118
  left = box.render("Left")
119
119
  right = box.render("Right")
120
120
 
121
- puts Lipgloss.join_horizontal(Lipgloss::TOP, [left, right])
121
+ puts Lipgloss.join_horizontal(:top, left, right)
122
122
  ```
123
123
 
124
124
  **Join content vertically:**
125
125
 
126
126
  ```ruby
127
- puts Lipgloss.join_vertical(Lipgloss::CENTER, [left, right])
127
+ puts Lipgloss.join_vertical(:center, left, right)
128
128
  ```
129
129
 
130
130
  **Place content in whitespace:**
131
131
 
132
132
  ```ruby
133
- puts Lipgloss.place(40, 10, Lipgloss::CENTER, Lipgloss::CENTER, "Centered")
133
+ puts Lipgloss.place(40, 10, :center, :center, "Centered")
134
134
  ```
135
135
 
136
+ **Position symbols:** `:top`, `:bottom`, `:left`, `:right`, `:center` (or numeric 0.0-1.0)
137
+
136
138
  ### Colors
137
139
 
138
140
  **Simple hex colors:**
@@ -155,18 +157,20 @@ style = Lipgloss::Style.new
155
157
  ```ruby
156
158
  color = Lipgloss::CompleteColor.new(
157
159
  true_color: "#FF6B6B", # 24-bit color
158
- ansi256: "196", # 256-color fallback
159
- ansi: "9" # 16-color fallback
160
+ ansi256: 196, # 256-color fallback (integer)
161
+ ansi: :bright_red # 16-color fallback (symbol)
160
162
  )
161
163
 
162
164
  style = Lipgloss::Style.new.foreground(color)
163
165
  ```
164
166
 
167
+ **ANSI color symbols:** `:black`, `:red`, `:green`, `:yellow`, `:blue`, `:magenta`, `:cyan`, `:white`, and bright variants (`:bright_red`, etc.)
168
+
165
169
  **Complete adaptive colors:**
166
170
 
167
171
  ```ruby
168
- light = Lipgloss::CompleteColor.new(true_color: "#000", ansi256: "0", ansi: "0")
169
- dark = Lipgloss::CompleteColor.new(true_color: "#FFF", ansi256: "15", ansi: "15")
172
+ light = Lipgloss::CompleteColor.new(true_color: "#000", ansi256: :black, ansi: :black)
173
+ dark = Lipgloss::CompleteColor.new(true_color: "#FFF", ansi256: :bright_white, ansi: :bright_white)
170
174
  color = Lipgloss::CompleteAdaptiveColor.new(light: light, dark: dark)
171
175
 
172
176
  style = Lipgloss::Style.new.foreground(color)
@@ -376,7 +380,7 @@ puts tree.render
376
380
  | `align_horizontal(position)` | Horizontal alignment |
377
381
  | `align_vertical(position)` | Vertical alignment |
378
382
 
379
- **Position constants:** `Lipgloss::TOP`, `Lipgloss::BOTTOM`, `Lipgloss::LEFT`, `Lipgloss::RIGHT`, `Lipgloss::CENTER`
383
+ **Position symbols:** `:top`, `:bottom`, `:left`, `:right`, `:center` (or numeric 0.0-1.0)
380
384
 
381
385
  ### Other
382
386
 
@@ -402,17 +406,41 @@ puts tree.render
402
406
 
403
407
  ## Module Methods
404
408
 
405
- | Method | Description |
406
- |---------------------------------------------------------------------------------|--------------------------|
407
- | `Lipgloss.join_horizontal(position, strings)` | Join strings horizontally |
408
- | `Lipgloss.join_vertical(position, strings)` | Join strings vertically |
409
- | `Lipgloss.width(string)` | Get rendered width |
410
- | `Lipgloss.height(string)` | Get rendered height |
411
- | `Lipgloss.size(string)` | Get `[width, height]` |
412
- | `Lipgloss.place(width, height, horizontal_position, vertical_position, string)` | Place in whitespace |
413
- | `Lipgloss.place_horizontal(width, position, string)` | Place horizontally |
414
- | `Lipgloss.place_vertical(height, position, string)` | Place vertically |
415
- | `Lipgloss.has_dark_background?` | Check terminal background |
409
+ | Method | Description |
410
+ |-----------------------------------------------------------|---------------------------|
411
+ | `Lipgloss.join_horizontal(position, *strings)` | Join strings horizontally |
412
+ | `Lipgloss.join_vertical(position, *strings)` | Join strings vertically |
413
+ | `Lipgloss.width(string)` | Get rendered width |
414
+ | `Lipgloss.height(string)` | Get rendered height |
415
+ | `Lipgloss.size(string)` | Get `[width, height]` |
416
+ | `Lipgloss.place(width, height, h_pos, v_pos, string)` | Place in whitespace |
417
+ | `Lipgloss.place_horizontal(width, position, string)` | Place horizontally |
418
+ | `Lipgloss.place_vertical(height, position, string)` | Place vertically |
419
+ | `Lipgloss.has_dark_background?` | Check terminal background |
420
+
421
+ ### Color Blending
422
+
423
+ | Method | Description |
424
+ |----------------------------------------------------------------|--------------------------------|
425
+ | `Lipgloss::ColorBlend.blend(c1, c2, t, mode:)` | Blend two colors (0.0-1.0) |
426
+ | `Lipgloss::ColorBlend.blends(c1, c2, steps, mode:)` | Generate color gradient array |
427
+ | `Lipgloss::ColorBlend.grid(c1, c2, c3, c4, x, y, mode:)` | Generate 2D color grid |
428
+
429
+ **Blend modes:** `:luv` (default, perceptually uniform), `:rgb`, `:hcl`
430
+
431
+ #### Generate a 5-color gradient
432
+
433
+ ```ruby
434
+ colors = Lipgloss::ColorBlend.blends("#FF0000", "#0000FF", 5)
435
+ # => ["#ff0000", "#c1007f", "#7a00c1", "#0000ff", ...]
436
+ ```
437
+
438
+ #### Blend two colors at 50%
439
+
440
+ ```ruby
441
+ mid = Lipgloss::ColorBlend.blend("#FF0000", "#00FF00", 0.5)
442
+ # => "#b5b500"
443
+ ```
416
444
 
417
445
  ## Development
418
446
 
@@ -443,6 +471,7 @@ bundle exec rake test
443
471
  ```bash
444
472
  ./demo/basic
445
473
  ./demo/colors
474
+ ./demo/layout
446
475
  ./demo/table
447
476
  ./demo/list
448
477
  ./demo/tree
@@ -0,0 +1,158 @@
1
+ #include "extension.h"
2
+
3
+ VALUE mColor;
4
+
5
+ #define BLEND_LUV 0
6
+ #define BLEND_RGB 1
7
+ #define BLEND_HCL 2
8
+
9
+ static int blend_mode_from_symbol(VALUE mode) {
10
+ if (NIL_P(mode)) {
11
+ return BLEND_LUV;
12
+ }
13
+
14
+ ID mode_id = SYM2ID(mode);
15
+
16
+ if (mode_id == rb_intern("luv")) {
17
+ return BLEND_LUV;
18
+ } else if (mode_id == rb_intern("rgb")) {
19
+ return BLEND_RGB;
20
+ } else if (mode_id == rb_intern("hcl")) {
21
+ return BLEND_HCL;
22
+ }
23
+
24
+ return BLEND_LUV;
25
+ }
26
+
27
+ static VALUE color_blend(int argc, VALUE *argv, VALUE self) {
28
+ VALUE c1, c2, t, opts;
29
+ rb_scan_args(argc, argv, "3:", &c1, &c2, &t, &opts);
30
+
31
+ Check_Type(c1, T_STRING);
32
+ Check_Type(c2, T_STRING);
33
+
34
+ VALUE mode = Qnil;
35
+ if (!NIL_P(opts)) {
36
+ mode = rb_hash_aref(opts, ID2SYM(rb_intern("mode")));
37
+ }
38
+
39
+ int blend_mode = blend_mode_from_symbol(mode);
40
+ char *result;
41
+
42
+ switch (blend_mode) {
43
+ case BLEND_RGB:
44
+ result = lipgloss_color_blend_rgb(StringValueCStr(c1), StringValueCStr(c2), NUM2DBL(t));
45
+ break;
46
+ case BLEND_HCL:
47
+ result = lipgloss_color_blend_hcl(StringValueCStr(c1), StringValueCStr(c2), NUM2DBL(t));
48
+ break;
49
+ default:
50
+ result = lipgloss_color_blend_luv(StringValueCStr(c1), StringValueCStr(c2), NUM2DBL(t));
51
+ break;
52
+ }
53
+
54
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
55
+ lipgloss_free(result);
56
+
57
+ return rb_result;
58
+ }
59
+
60
+ static VALUE color_blend_luv(VALUE self, VALUE c1, VALUE c2, VALUE t) {
61
+ Check_Type(c1, T_STRING);
62
+ Check_Type(c2, T_STRING);
63
+
64
+ char *result = lipgloss_color_blend_luv(StringValueCStr(c1), StringValueCStr(c2), NUM2DBL(t));
65
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
66
+ lipgloss_free(result);
67
+
68
+ return rb_result;
69
+ }
70
+
71
+ static VALUE color_blend_rgb(VALUE self, VALUE c1, VALUE c2, VALUE t) {
72
+ Check_Type(c1, T_STRING);
73
+ Check_Type(c2, T_STRING);
74
+
75
+ char *result = lipgloss_color_blend_rgb(StringValueCStr(c1), StringValueCStr(c2), NUM2DBL(t));
76
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
77
+ lipgloss_free(result);
78
+
79
+ return rb_result;
80
+ }
81
+
82
+ static VALUE color_blend_hcl(VALUE self, VALUE c1, VALUE c2, VALUE t) {
83
+ Check_Type(c1, T_STRING);
84
+ Check_Type(c2, T_STRING);
85
+
86
+ char *result = lipgloss_color_blend_hcl(StringValueCStr(c1), StringValueCStr(c2), NUM2DBL(t));
87
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
88
+ lipgloss_free(result);
89
+
90
+ return rb_result;
91
+ }
92
+
93
+ static VALUE color_blends(int argc, VALUE *argv, VALUE self) {
94
+ VALUE c1, c2, steps, opts;
95
+ rb_scan_args(argc, argv, "3:", &c1, &c2, &steps, &opts);
96
+
97
+ Check_Type(c1, T_STRING);
98
+ Check_Type(c2, T_STRING);
99
+
100
+ VALUE mode = Qnil;
101
+ if (!NIL_P(opts)) {
102
+ mode = rb_hash_aref(opts, ID2SYM(rb_intern("mode")));
103
+ }
104
+
105
+ int blend_mode = blend_mode_from_symbol(mode);
106
+ char *result = lipgloss_color_blends(StringValueCStr(c1), StringValueCStr(c2), NUM2INT(steps), blend_mode);
107
+ VALUE json_string = rb_utf8_str_new_cstr(result);
108
+ lipgloss_free(result);
109
+
110
+ return rb_funcall(rb_const_get(rb_cObject, rb_intern("JSON")), rb_intern("parse"), 1, json_string);
111
+ }
112
+
113
+ static VALUE color_grid(int argc, VALUE *argv, VALUE self) {
114
+ VALUE x0y0, x1y0, x0y1, x1y1, x_steps, y_steps, opts;
115
+ rb_scan_args(argc, argv, "6:", &x0y0, &x1y0, &x0y1, &x1y1, &x_steps, &y_steps, &opts);
116
+
117
+ Check_Type(x0y0, T_STRING);
118
+ Check_Type(x1y0, T_STRING);
119
+ Check_Type(x0y1, T_STRING);
120
+ Check_Type(x1y1, T_STRING);
121
+
122
+ VALUE mode = Qnil;
123
+ if (!NIL_P(opts)) {
124
+ mode = rb_hash_aref(opts, ID2SYM(rb_intern("mode")));
125
+ }
126
+
127
+ int blend_mode = blend_mode_from_symbol(mode);
128
+
129
+ char *result = lipgloss_color_grid(
130
+ StringValueCStr(x0y0),
131
+ StringValueCStr(x1y0),
132
+ StringValueCStr(x0y1),
133
+ StringValueCStr(x1y1),
134
+ NUM2INT(x_steps),
135
+ NUM2INT(y_steps),
136
+ blend_mode
137
+ );
138
+
139
+ VALUE json_string = rb_utf8_str_new_cstr(result);
140
+ lipgloss_free(result);
141
+
142
+ return rb_funcall(rb_const_get(rb_cObject, rb_intern("JSON")), rb_intern("parse"), 1, json_string);
143
+ }
144
+
145
+ void Init_lipgloss_color(void) {
146
+ VALUE mColorBlend = rb_define_module_under(mLipgloss, "ColorBlend");
147
+
148
+ rb_define_singleton_method(mColorBlend, "blend", color_blend, -1);
149
+ rb_define_singleton_method(mColorBlend, "blend_luv", color_blend_luv, 3);
150
+ rb_define_singleton_method(mColorBlend, "blend_rgb", color_blend_rgb, 3);
151
+ rb_define_singleton_method(mColorBlend, "blend_hcl", color_blend_hcl, 3);
152
+ rb_define_singleton_method(mColorBlend, "blends", color_blends, -1);
153
+ rb_define_singleton_method(mColorBlend, "grid", color_grid, -1);
154
+
155
+ rb_define_const(mColorBlend, "LUV", ID2SYM(rb_intern("luv")));
156
+ rb_define_const(mColorBlend, "RGB", ID2SYM(rb_intern("rgb")));
157
+ rb_define_const(mColorBlend, "HCL", ID2SYM(rb_intern("hcl")));
158
+ }
@@ -56,6 +56,16 @@ when /linux/
56
56
  $LDFLAGS << " -lresolv" if find_library("resolv", "res_query")
57
57
  end
58
58
 
59
- $srcs = ["extension.c", "style.c", "style_spacing.c", "style_border.c", "style_unset.c", "table.c", "list.c", "tree.c"]
59
+ $srcs = [
60
+ "color.c",
61
+ "extension.c",
62
+ "list.c",
63
+ "style_border.c",
64
+ "style_spacing.c",
65
+ "style_unset.c",
66
+ "style.c",
67
+ "table.c",
68
+ "tree.c"
69
+ ]
60
70
 
61
71
  create_makefile("#{extension_name}/#{extension_name}")
@@ -6,11 +6,15 @@ VALUE cTable;
6
6
  VALUE cList;
7
7
  VALUE cTree;
8
8
 
9
+ int is_adaptive_color(VALUE object) {
10
+ return rb_respond_to(object, rb_intern("light")) && rb_respond_to(object, rb_intern("dark"));
11
+ }
12
+
9
13
  static VALUE lipgloss_join_horizontal_rb(VALUE self, VALUE position, VALUE strings) {
10
14
  Check_Type(strings, T_ARRAY);
11
15
 
12
- VALUE json_str = rb_funcall(strings, rb_intern("to_json"), 0);
13
- char *result = lipgloss_join_horizontal(NUM2DBL(position), StringValueCStr(json_str));
16
+ VALUE json_string = rb_funcall(strings, rb_intern("to_json"), 0);
17
+ char *result = lipgloss_join_horizontal(NUM2DBL(position), StringValueCStr(json_string));
14
18
  VALUE rb_result = rb_utf8_str_new_cstr(result);
15
19
 
16
20
  lipgloss_free(result);
@@ -21,8 +25,8 @@ static VALUE lipgloss_join_horizontal_rb(VALUE self, VALUE position, VALUE strin
21
25
  static VALUE lipgloss_join_vertical_rb(VALUE self, VALUE position, VALUE strings) {
22
26
  Check_Type(strings, T_ARRAY);
23
27
 
24
- VALUE json_str = rb_funcall(strings, rb_intern("to_json"), 0);
25
- char *result = lipgloss_join_vertical(NUM2DBL(position), StringValueCStr(json_str));
28
+ VALUE json_string = rb_funcall(strings, rb_intern("to_json"), 0);
29
+ char *result = lipgloss_join_vertical(NUM2DBL(position), StringValueCStr(json_string));
26
30
  VALUE rb_result = rb_utf8_str_new_cstr(result);
27
31
 
28
32
  lipgloss_free(result);
@@ -53,18 +57,55 @@ static VALUE lipgloss_size_rb(VALUE self, VALUE string) {
53
57
  }
54
58
 
55
59
  static VALUE lipgloss_place_rb(int argc, VALUE *argv, VALUE self) {
56
- VALUE width, height, horizontal_position, vertical_position, string;
57
- rb_scan_args(argc, argv, "5", &width, &height, &horizontal_position, &vertical_position, &string);
60
+ VALUE width, height, horizontal_position, vertical_position, string, opts;
61
+ rb_scan_args(argc, argv, "5:", &width, &height, &horizontal_position, &vertical_position, &string, &opts);
58
62
 
59
63
  Check_Type(string, T_STRING);
60
64
 
61
- char *result = lipgloss_place(
62
- NUM2INT(width),
63
- NUM2INT(height),
64
- NUM2DBL(horizontal_position),
65
- NUM2DBL(vertical_position),
66
- StringValueCStr(string)
67
- );
65
+ char *result;
66
+
67
+ if (!NIL_P(opts)) {
68
+ VALUE whitespace_chars = rb_hash_aref(opts, ID2SYM(rb_intern("whitespace_chars")));
69
+ VALUE whitespace_foreground = rb_hash_aref(opts, ID2SYM(rb_intern("whitespace_foreground")));
70
+
71
+ const char *ws_chars = NIL_P(whitespace_chars) ? "" : StringValueCStr(whitespace_chars);
72
+
73
+ if (!NIL_P(whitespace_foreground) && is_adaptive_color(whitespace_foreground)) {
74
+ VALUE light = rb_funcall(whitespace_foreground, rb_intern("light"), 0);
75
+ VALUE dark = rb_funcall(whitespace_foreground, rb_intern("dark"), 0);
76
+
77
+ result = lipgloss_place_with_whitespace_adaptive(
78
+ NUM2INT(width),
79
+ NUM2INT(height),
80
+ NUM2DBL(horizontal_position),
81
+ NUM2DBL(vertical_position),
82
+ StringValueCStr(string),
83
+ ws_chars,
84
+ StringValueCStr(light),
85
+ StringValueCStr(dark)
86
+ );
87
+ } else {
88
+ const char *ws_fg = NIL_P(whitespace_foreground) ? "" : StringValueCStr(whitespace_foreground);
89
+
90
+ result = lipgloss_place_with_whitespace(
91
+ NUM2INT(width),
92
+ NUM2INT(height),
93
+ NUM2DBL(horizontal_position),
94
+ NUM2DBL(vertical_position),
95
+ StringValueCStr(string),
96
+ ws_chars,
97
+ ws_fg
98
+ );
99
+ }
100
+ } else {
101
+ result = lipgloss_place(
102
+ NUM2INT(width),
103
+ NUM2INT(height),
104
+ NUM2DBL(horizontal_position),
105
+ NUM2DBL(vertical_position),
106
+ StringValueCStr(string)
107
+ );
108
+ }
68
109
 
69
110
  VALUE rb_result = rb_utf8_str_new_cstr(result);
70
111
 
@@ -121,7 +162,7 @@ static VALUE lipgloss_upstream_version_rb(VALUE self) {
121
162
  static VALUE lipgloss_version_rb(VALUE self) {
122
163
  VALUE gem_version = rb_const_get(self, rb_intern("VERSION"));
123
164
  VALUE upstream_version = lipgloss_upstream_version_rb(self);
124
- VALUE format_string = rb_utf8_str_new_cstr("lipgloss v%s (upstream v%s) [Go native extension]");
165
+ VALUE format_string = rb_utf8_str_new_cstr("lipgloss v%s (upstream %s) [Go native extension]");
125
166
 
126
167
  return rb_funcall(rb_mKernel, rb_intern("sprintf"), 3, format_string, gem_version, upstream_version);
127
168
  }
@@ -135,15 +176,16 @@ __attribute__((__visibility__("default"))) void Init_lipgloss(void) {
135
176
  Init_lipgloss_table();
136
177
  Init_lipgloss_list();
137
178
  Init_lipgloss_tree();
179
+ Init_lipgloss_color();
138
180
 
139
- rb_define_singleton_method(mLipgloss, "join_horizontal", lipgloss_join_horizontal_rb, 2);
140
- rb_define_singleton_method(mLipgloss, "join_vertical", lipgloss_join_vertical_rb, 2);
181
+ rb_define_singleton_method(mLipgloss, "_join_horizontal", lipgloss_join_horizontal_rb, 2);
182
+ rb_define_singleton_method(mLipgloss, "_join_vertical", lipgloss_join_vertical_rb, 2);
141
183
  rb_define_singleton_method(mLipgloss, "width", lipgloss_width_rb, 1);
142
184
  rb_define_singleton_method(mLipgloss, "height", lipgloss_height_rb, 1);
143
185
  rb_define_singleton_method(mLipgloss, "size", lipgloss_size_rb, 1);
144
- rb_define_singleton_method(mLipgloss, "place", lipgloss_place_rb, -1);
145
- rb_define_singleton_method(mLipgloss, "place_horizontal", lipgloss_place_horizontal_rb, 3);
146
- rb_define_singleton_method(mLipgloss, "place_vertical", lipgloss_place_vertical_rb, 3);
186
+ rb_define_singleton_method(mLipgloss, "_place", lipgloss_place_rb, -1);
187
+ rb_define_singleton_method(mLipgloss, "_place_horizontal", lipgloss_place_horizontal_rb, 3);
188
+ rb_define_singleton_method(mLipgloss, "_place_vertical", lipgloss_place_vertical_rb, 3);
147
189
  rb_define_singleton_method(mLipgloss, "has_dark_background?", lipgloss_has_dark_background_rb, 0);
148
190
  rb_define_singleton_method(mLipgloss, "upstream_version", lipgloss_upstream_version_rb, 0);
149
191
  rb_define_singleton_method(mLipgloss, "version", lipgloss_version_rb, 0);
@@ -68,9 +68,12 @@ void Init_lipgloss_style(void);
68
68
  void Init_lipgloss_table(void);
69
69
  void Init_lipgloss_list(void);
70
70
  void Init_lipgloss_tree(void);
71
+ void Init_lipgloss_color(void);
71
72
 
72
73
  void register_style_spacing_methods(void);
73
74
  void register_style_border_methods(void);
74
75
  void register_style_unset_methods(void);
75
76
 
77
+ int is_adaptive_color(VALUE object);
78
+
76
79
  #endif
data/ext/lipgloss/style.c CHANGED
@@ -103,10 +103,6 @@ static int is_complete_color(VALUE obj) {
103
103
  return rb_respond_to(obj, rb_intern("true_color")) && rb_respond_to(obj, rb_intern("ansi256")) && rb_respond_to(obj, rb_intern("ansi"));
104
104
  }
105
105
 
106
- static int is_adaptive_color(VALUE obj) {
107
- return rb_respond_to(obj, rb_intern("light")) && rb_respond_to(obj, rb_intern("dark"));
108
- }
109
-
110
106
  // Color methods
111
107
 
112
108
  static VALUE style_foreground(VALUE self, VALUE color) {
@@ -348,6 +344,80 @@ static VALUE style_to_s(VALUE self) {
348
344
  return rb_result;
349
345
  }
350
346
 
347
+ // Getter methods
348
+
349
+ static VALUE style_get_bold(VALUE self) {
350
+ GET_STYLE(self, style);
351
+ return lipgloss_style_get_bold(style->handle) ? Qtrue : Qfalse;
352
+ }
353
+
354
+ static VALUE style_get_italic(VALUE self) {
355
+ GET_STYLE(self, style);
356
+ return lipgloss_style_get_italic(style->handle) ? Qtrue : Qfalse;
357
+ }
358
+
359
+ static VALUE style_get_underline(VALUE self) {
360
+ GET_STYLE(self, style);
361
+ return lipgloss_style_get_underline(style->handle) ? Qtrue : Qfalse;
362
+ }
363
+
364
+ static VALUE style_get_strikethrough(VALUE self) {
365
+ GET_STYLE(self, style);
366
+ return lipgloss_style_get_strikethrough(style->handle) ? Qtrue : Qfalse;
367
+ }
368
+
369
+ static VALUE style_get_reverse(VALUE self) {
370
+ GET_STYLE(self, style);
371
+ return lipgloss_style_get_reverse(style->handle) ? Qtrue : Qfalse;
372
+ }
373
+
374
+ static VALUE style_get_blink(VALUE self) {
375
+ GET_STYLE(self, style);
376
+ return lipgloss_style_get_blink(style->handle) ? Qtrue : Qfalse;
377
+ }
378
+
379
+ static VALUE style_get_faint(VALUE self) {
380
+ GET_STYLE(self, style);
381
+ return lipgloss_style_get_faint(style->handle) ? Qtrue : Qfalse;
382
+ }
383
+
384
+ static VALUE style_get_foreground(VALUE self) {
385
+ GET_STYLE(self, style);
386
+ char *result = lipgloss_style_get_foreground(style->handle);
387
+ if (result == NULL || result[0] == '\0') {
388
+ if (result) lipgloss_free(result);
389
+ return Qnil;
390
+ }
391
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
392
+ lipgloss_free(result);
393
+ return rb_result;
394
+ }
395
+
396
+ static VALUE style_get_background(VALUE self) {
397
+ GET_STYLE(self, style);
398
+ char *result = lipgloss_style_get_background(style->handle);
399
+
400
+ if (result == NULL || result[0] == '\0') {
401
+ if (result) lipgloss_free(result);
402
+ return Qnil;
403
+ }
404
+
405
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
406
+ lipgloss_free(result);
407
+
408
+ return rb_result;
409
+ }
410
+
411
+ static VALUE style_get_width(VALUE self) {
412
+ GET_STYLE(self, style);
413
+ return INT2NUM(lipgloss_style_get_width(style->handle));
414
+ }
415
+
416
+ static VALUE style_get_height(VALUE self) {
417
+ GET_STYLE(self, style);
418
+ return INT2NUM(lipgloss_style_get_height(style->handle));
419
+ }
420
+
351
421
  void Init_lipgloss_style(void) {
352
422
  cStyle = rb_define_class_under(mLipgloss, "Style", rb_cObject);
353
423
 
@@ -356,7 +426,6 @@ void Init_lipgloss_style(void) {
356
426
  rb_define_method(cStyle, "initialize", style_initialize, 0);
357
427
  rb_define_method(cStyle, "render", style_render, 1);
358
428
 
359
- // Formatting
360
429
  rb_define_method(cStyle, "bold", style_bold, 1);
361
430
  rb_define_method(cStyle, "italic", style_italic, 1);
362
431
  rb_define_method(cStyle, "underline", style_underline, 1);
@@ -365,23 +434,19 @@ void Init_lipgloss_style(void) {
365
434
  rb_define_method(cStyle, "blink", style_blink, 1);
366
435
  rb_define_method(cStyle, "faint", style_faint, 1);
367
436
 
368
- // Colors
369
437
  rb_define_method(cStyle, "foreground", style_foreground, 1);
370
438
  rb_define_method(cStyle, "background", style_background, 1);
371
439
  rb_define_method(cStyle, "margin_background", style_margin_background, 1);
372
440
 
373
- // Size
374
441
  rb_define_method(cStyle, "width", style_width, 1);
375
442
  rb_define_method(cStyle, "height", style_height, 1);
376
443
  rb_define_method(cStyle, "max_width", style_max_width, 1);
377
444
  rb_define_method(cStyle, "max_height", style_max_height, 1);
378
445
 
379
- // Alignment
380
- rb_define_method(cStyle, "align", style_align, -1);
381
- rb_define_method(cStyle, "align_horizontal", style_align_horizontal, 1);
382
- rb_define_method(cStyle, "align_vertical", style_align_vertical, 1);
446
+ rb_define_method(cStyle, "_align", style_align, -1);
447
+ rb_define_method(cStyle, "_align_horizontal", style_align_horizontal, 1);
448
+ rb_define_method(cStyle, "_align_vertical", style_align_vertical, 1);
383
449
 
384
- // Other
385
450
  rb_define_method(cStyle, "inline", style_inline, 1);
386
451
  rb_define_method(cStyle, "tab_width", style_tab_width, 1);
387
452
  rb_define_method(cStyle, "underline_spaces", style_underline_spaces, 1);
@@ -391,7 +456,18 @@ void Init_lipgloss_style(void) {
391
456
  rb_define_method(cStyle, "inherit", style_inherit, 1);
392
457
  rb_define_method(cStyle, "to_s", style_to_s, 0);
393
458
 
394
- // Register methods from sub-files
459
+ rb_define_method(cStyle, "bold?", style_get_bold, 0);
460
+ rb_define_method(cStyle, "italic?", style_get_italic, 0);
461
+ rb_define_method(cStyle, "underline?", style_get_underline, 0);
462
+ rb_define_method(cStyle, "strikethrough?", style_get_strikethrough, 0);
463
+ rb_define_method(cStyle, "reverse?", style_get_reverse, 0);
464
+ rb_define_method(cStyle, "blink?", style_get_blink, 0);
465
+ rb_define_method(cStyle, "faint?", style_get_faint, 0);
466
+ rb_define_method(cStyle, "get_foreground", style_get_foreground, 0);
467
+ rb_define_method(cStyle, "get_background", style_get_background, 0);
468
+ rb_define_method(cStyle, "get_width", style_get_width, 0);
469
+ rb_define_method(cStyle, "get_height", style_get_height, 0);
470
+
395
471
  register_style_spacing_methods();
396
472
  register_style_border_methods();
397
473
  register_style_unset_methods();
@@ -50,6 +50,20 @@ static VALUE style_border_style(VALUE self, VALUE border_sym) {
50
50
 
51
51
  static VALUE style_border_foreground(VALUE self, VALUE color) {
52
52
  GET_STYLE(self, style);
53
+
54
+ if (is_adaptive_color(color)) {
55
+ VALUE light = rb_funcall(color, rb_intern("light"), 0);
56
+ VALUE dark = rb_funcall(color, rb_intern("dark"), 0);
57
+
58
+ unsigned long long new_handle = lipgloss_style_border_foreground_adaptive(
59
+ style->handle,
60
+ StringValueCStr(light),
61
+ StringValueCStr(dark)
62
+ );
63
+
64
+ return style_wrap(rb_class_of(self), new_handle);
65
+ }
66
+
53
67
  Check_Type(color, T_STRING);
54
68
  unsigned long long new_handle = lipgloss_style_border_foreground(style->handle, StringValueCStr(color));
55
69
  return style_wrap(rb_class_of(self), new_handle);
@@ -57,6 +71,20 @@ static VALUE style_border_foreground(VALUE self, VALUE color) {
57
71
 
58
72
  static VALUE style_border_background(VALUE self, VALUE color) {
59
73
  GET_STYLE(self, style);
74
+
75
+ if (is_adaptive_color(color)) {
76
+ VALUE light = rb_funcall(color, rb_intern("light"), 0);
77
+ VALUE dark = rb_funcall(color, rb_intern("dark"), 0);
78
+
79
+ unsigned long long new_handle = lipgloss_style_border_background_adaptive(
80
+ style->handle,
81
+ StringValueCStr(light),
82
+ StringValueCStr(dark)
83
+ );
84
+
85
+ return style_wrap(rb_class_of(self), new_handle);
86
+ }
87
+
60
88
  Check_Type(color, T_STRING);
61
89
  unsigned long long new_handle = lipgloss_style_border_background(style->handle, StringValueCStr(color));
62
90
  return style_wrap(rb_class_of(self), new_handle);
data/go/color.go ADDED
@@ -0,0 +1,168 @@
1
+ package main
2
+
3
+ import "C"
4
+
5
+ import (
6
+ "encoding/json"
7
+ "github.com/lucasb-eyer/go-colorful"
8
+ )
9
+
10
+ //export lipgloss_color_blend_luv
11
+ func lipgloss_color_blend_luv(c1 *C.char, c2 *C.char, t C.double) *C.char {
12
+ color1, err := colorful.Hex(C.GoString(c1))
13
+ if err != nil {
14
+ return C.CString(C.GoString(c1))
15
+ }
16
+
17
+ color2, err := colorful.Hex(C.GoString(c2))
18
+ if err != nil {
19
+ return C.CString(C.GoString(c1))
20
+ }
21
+
22
+ blended := color1.BlendLuv(color2, float64(t))
23
+ return C.CString(blended.Hex())
24
+ }
25
+
26
+ //export lipgloss_color_blend_rgb
27
+ func lipgloss_color_blend_rgb(c1 *C.char, c2 *C.char, t C.double) *C.char {
28
+ color1, err := colorful.Hex(C.GoString(c1))
29
+ if err != nil {
30
+ return C.CString(C.GoString(c1))
31
+ }
32
+
33
+ color2, err := colorful.Hex(C.GoString(c2))
34
+ if err != nil {
35
+ return C.CString(C.GoString(c1))
36
+ }
37
+
38
+ blended := color1.BlendRgb(color2, float64(t))
39
+ return C.CString(blended.Hex())
40
+ }
41
+
42
+ //export lipgloss_color_blend_hcl
43
+ func lipgloss_color_blend_hcl(c1 *C.char, c2 *C.char, t C.double) *C.char {
44
+ color1, err := colorful.Hex(C.GoString(c1))
45
+ if err != nil {
46
+ return C.CString(C.GoString(c1))
47
+ }
48
+
49
+ color2, err := colorful.Hex(C.GoString(c2))
50
+ if err != nil {
51
+ return C.CString(C.GoString(c1))
52
+ }
53
+
54
+ blended := color1.BlendHcl(color2, float64(t))
55
+ return C.CString(blended.Hex())
56
+ }
57
+
58
+ //export lipgloss_color_blends
59
+ func lipgloss_color_blends(c1 *C.char, c2 *C.char, steps C.int, blendMode C.int) *C.char {
60
+ color1, err := colorful.Hex(C.GoString(c1))
61
+ if err != nil {
62
+ return C.CString("[]")
63
+ }
64
+
65
+ color2, err := colorful.Hex(C.GoString(c2))
66
+ if err != nil {
67
+ return C.CString("[]")
68
+ }
69
+
70
+ n := int(steps)
71
+ colors := make([]string, n)
72
+
73
+ for i := 0; i < n; i++ {
74
+ t := float64(i) / float64(n-1)
75
+ if n == 1 {
76
+ t = 0
77
+ }
78
+
79
+ var blended colorful.Color
80
+ switch int(blendMode) {
81
+ case 0: // LUV
82
+ blended = color1.BlendLuv(color2, t)
83
+ case 1: // RGB
84
+ blended = color1.BlendRgb(color2, t)
85
+ case 2: // HCL
86
+ blended = color1.BlendHcl(color2, t)
87
+ default:
88
+ blended = color1.BlendLuv(color2, t)
89
+ }
90
+ colors[i] = blended.Hex()
91
+ }
92
+
93
+ result, _ := json.Marshal(colors)
94
+ return C.CString(string(result))
95
+ }
96
+
97
+ //export lipgloss_color_grid
98
+ func lipgloss_color_grid(x0y0 *C.char, x1y0 *C.char, x0y1 *C.char, x1y1 *C.char, xSteps C.int, ySteps C.int, blendMode C.int) *C.char {
99
+ c00, err := colorful.Hex(C.GoString(x0y0))
100
+ if err != nil {
101
+ return C.CString("[]")
102
+ }
103
+
104
+ c10, err := colorful.Hex(C.GoString(x1y0))
105
+ if err != nil {
106
+ return C.CString("[]")
107
+ }
108
+
109
+ c01, err := colorful.Hex(C.GoString(x0y1))
110
+ if err != nil {
111
+ return C.CString("[]")
112
+ }
113
+
114
+ c11, err := colorful.Hex(C.GoString(x1y1))
115
+ if err != nil {
116
+ return C.CString("[]")
117
+ }
118
+
119
+ nx := int(xSteps)
120
+ ny := int(ySteps)
121
+ mode := int(blendMode)
122
+
123
+ blendFunc := func(a, b colorful.Color, t float64) colorful.Color {
124
+ switch mode {
125
+ case 0: // LUV
126
+ return a.BlendLuv(b, t)
127
+ case 1: // RGB
128
+ return a.BlendRgb(b, t)
129
+ case 2: // HCL
130
+ return a.BlendHcl(b, t)
131
+ default:
132
+ return a.BlendLuv(b, t)
133
+ }
134
+ }
135
+
136
+ x0 := make([]colorful.Color, ny)
137
+ x1 := make([]colorful.Color, ny)
138
+
139
+ for y := 0; y < ny; y++ {
140
+ t := float64(y) / float64(ny)
141
+
142
+ if ny == 1 {
143
+ t = 0
144
+ }
145
+
146
+ x0[y] = blendFunc(c00, c01, t)
147
+ x1[y] = blendFunc(c10, c11, t)
148
+ }
149
+
150
+ grid := make([][]string, ny)
151
+
152
+ for y := 0; y < ny; y++ {
153
+ grid[y] = make([]string, nx)
154
+
155
+ for x := 0; x < nx; x++ {
156
+ t := float64(x) / float64(nx)
157
+
158
+ if nx == 1 {
159
+ t = 0
160
+ }
161
+
162
+ grid[y][x] = blendFunc(x0[y], x1[y], t).Hex()
163
+ }
164
+ }
165
+
166
+ result, _ := json.Marshal(grid)
167
+ return C.CString(string(result))
168
+ }
data/go/layout.go CHANGED
@@ -49,6 +49,48 @@ func lipgloss_place(width C.int, height C.int, horizontalPosition C.double, vert
49
49
  return C.CString(result)
50
50
  }
51
51
 
52
+ //export lipgloss_place_with_whitespace
53
+ func lipgloss_place_with_whitespace(width C.int, height C.int, horizontalPosition C.double, verticalPosition C.double, text *C.char, whitespaceChars *C.char, whitespaceForeground *C.char) *C.char {
54
+ opts := []lipgloss.WhitespaceOption{}
55
+
56
+ wsChars := C.GoString(whitespaceChars)
57
+ if wsChars != "" {
58
+ opts = append(opts, lipgloss.WithWhitespaceChars(wsChars))
59
+ }
60
+
61
+ wsFg := C.GoString(whitespaceForeground)
62
+ if wsFg != "" {
63
+ opts = append(opts, lipgloss.WithWhitespaceForeground(lipgloss.Color(wsFg)))
64
+ }
65
+
66
+ result := lipgloss.Place(int(width), int(height), lipgloss.Position(horizontalPosition), lipgloss.Position(verticalPosition), C.GoString(text), opts...)
67
+ return C.CString(result)
68
+ }
69
+
70
+ //export lipgloss_place_with_whitespace_adaptive
71
+ func lipgloss_place_with_whitespace_adaptive(width C.int, height C.int, horizontalPosition C.double, verticalPosition C.double, text *C.char, whitespaceChars *C.char, whitespaceForegroundLight *C.char, whitespaceForegroundDark *C.char) *C.char {
72
+ opts := []lipgloss.WhitespaceOption{}
73
+
74
+ wsChars := C.GoString(whitespaceChars)
75
+ if wsChars != "" {
76
+ opts = append(opts, lipgloss.WithWhitespaceChars(wsChars))
77
+ }
78
+
79
+ wsFgLight := C.GoString(whitespaceForegroundLight)
80
+ wsFgDark := C.GoString(whitespaceForegroundDark)
81
+
82
+ if wsFgLight != "" || wsFgDark != "" {
83
+ opts = append(opts, lipgloss.WithWhitespaceForeground(lipgloss.AdaptiveColor{
84
+ Light: wsFgLight,
85
+ Dark: wsFgDark,
86
+ }))
87
+ }
88
+
89
+ result := lipgloss.Place(int(width), int(height), lipgloss.Position(horizontalPosition), lipgloss.Position(verticalPosition), C.GoString(text), opts...)
90
+
91
+ return C.CString(result)
92
+ }
93
+
52
94
  //export lipgloss_place_horizontal
53
95
  func lipgloss_place_horizontal(width C.int, position C.double, text *C.char) *C.char {
54
96
  result := lipgloss.PlaceHorizontal(int(width), lipgloss.Position(position), C.GoString(text))
data/go/style.go CHANGED
@@ -279,3 +279,110 @@ func lipgloss_style_string(id C.ulonglong) *C.char {
279
279
  style := getStyle(uint64(id))
280
280
  return C.CString(style.String())
281
281
  }
282
+
283
+ // Getter methods
284
+
285
+ //export lipgloss_style_get_bold
286
+ func lipgloss_style_get_bold(id C.ulonglong) C.int {
287
+ style := getStyle(uint64(id))
288
+ if style.GetBold() {
289
+ return 1
290
+ }
291
+ return 0
292
+ }
293
+
294
+ //export lipgloss_style_get_italic
295
+ func lipgloss_style_get_italic(id C.ulonglong) C.int {
296
+ style := getStyle(uint64(id))
297
+ if style.GetItalic() {
298
+ return 1
299
+ }
300
+ return 0
301
+ }
302
+
303
+ //export lipgloss_style_get_underline
304
+ func lipgloss_style_get_underline(id C.ulonglong) C.int {
305
+ style := getStyle(uint64(id))
306
+ if style.GetUnderline() {
307
+ return 1
308
+ }
309
+ return 0
310
+ }
311
+
312
+ //export lipgloss_style_get_strikethrough
313
+ func lipgloss_style_get_strikethrough(id C.ulonglong) C.int {
314
+ style := getStyle(uint64(id))
315
+ if style.GetStrikethrough() {
316
+ return 1
317
+ }
318
+ return 0
319
+ }
320
+
321
+ //export lipgloss_style_get_reverse
322
+ func lipgloss_style_get_reverse(id C.ulonglong) C.int {
323
+ style := getStyle(uint64(id))
324
+ if style.GetReverse() {
325
+ return 1
326
+ }
327
+ return 0
328
+ }
329
+
330
+ //export lipgloss_style_get_blink
331
+ func lipgloss_style_get_blink(id C.ulonglong) C.int {
332
+ style := getStyle(uint64(id))
333
+ if style.GetBlink() {
334
+ return 1
335
+ }
336
+ return 0
337
+ }
338
+
339
+ //export lipgloss_style_get_faint
340
+ func lipgloss_style_get_faint(id C.ulonglong) C.int {
341
+ style := getStyle(uint64(id))
342
+ if style.GetFaint() {
343
+ return 1
344
+ }
345
+ return 0
346
+ }
347
+
348
+ // terminalColorToString converts a TerminalColor to its string representation
349
+ func terminalColorToString(tc lipgloss.TerminalColor) string {
350
+ if tc == nil {
351
+ return ""
352
+ }
353
+ switch c := tc.(type) {
354
+ case lipgloss.Color:
355
+ return string(c)
356
+ case lipgloss.NoColor:
357
+ return ""
358
+ default:
359
+ // For adaptive/complete colors, we can't easily extract a single value
360
+ return ""
361
+ }
362
+ }
363
+
364
+ //export lipgloss_style_get_foreground
365
+ func lipgloss_style_get_foreground(id C.ulonglong) *C.char {
366
+ style := getStyle(uint64(id))
367
+ color := terminalColorToString(style.GetForeground())
368
+ return C.CString(color)
369
+ }
370
+
371
+ //export lipgloss_style_get_background
372
+ func lipgloss_style_get_background(id C.ulonglong) *C.char {
373
+ style := getStyle(uint64(id))
374
+ color := terminalColorToString(style.GetBackground())
375
+ return C.CString(color)
376
+ }
377
+
378
+ //export lipgloss_style_get_width
379
+ func lipgloss_style_get_width(id C.ulonglong) C.int {
380
+ style := getStyle(uint64(id))
381
+ return C.int(style.GetWidth())
382
+ }
383
+
384
+ //export lipgloss_style_get_height
385
+ func lipgloss_style_get_height(id C.ulonglong) C.int {
386
+ style := getStyle(uint64(id))
387
+ return C.int(style.GetHeight())
388
+ }
data/go/style_border.go CHANGED
@@ -114,12 +114,32 @@ func lipgloss_style_border_foreground(id C.ulonglong, color *C.char) C.ulonglong
114
114
  return C.ulonglong(allocStyle(style))
115
115
  }
116
116
 
117
+ //export lipgloss_style_border_foreground_adaptive
118
+ func lipgloss_style_border_foreground_adaptive(id C.ulonglong, light *C.char, dark *C.char) C.ulonglong {
119
+ style := getStyle(uint64(id)).BorderForeground(lipgloss.AdaptiveColor{
120
+ Light: C.GoString(light),
121
+ Dark: C.GoString(dark),
122
+ })
123
+
124
+ return C.ulonglong(allocStyle(style))
125
+ }
126
+
117
127
  //export lipgloss_style_border_background
118
128
  func lipgloss_style_border_background(id C.ulonglong, color *C.char) C.ulonglong {
119
129
  style := getStyle(uint64(id)).BorderBackground(lipgloss.Color(C.GoString(color)))
120
130
  return C.ulonglong(allocStyle(style))
121
131
  }
122
132
 
133
+ //export lipgloss_style_border_background_adaptive
134
+ func lipgloss_style_border_background_adaptive(id C.ulonglong, light *C.char, dark *C.char) C.ulonglong {
135
+ style := getStyle(uint64(id)).BorderBackground(lipgloss.AdaptiveColor{
136
+ Light: C.GoString(light),
137
+ Dark: C.GoString(dark),
138
+ })
139
+
140
+ return C.ulonglong(allocStyle(style))
141
+ }
142
+
123
143
  //export lipgloss_style_border_top
124
144
  func lipgloss_style_border_top(id C.ulonglong, value C.int) C.ulonglong {
125
145
  style := getStyle(uint64(id)).BorderTop(value != 0)
@@ -7,6 +7,42 @@ module Lipgloss
7
7
  # type complete_color_hash = { true_color: String, ansi256: String, ansi: String }
8
8
  # type complete_adaptive_color_hash = { light: complete_color_hash, dark: complete_color_hash }
9
9
 
10
+ module ANSIColor
11
+ # @rbs!
12
+ # type ansi_color_symbol = :black | :red | :green | :yellow | :blue | :magenta | :cyan | :white | :bright_black | :bright_red | :bright_green | :bright_yellow | :bright_blue | :bright_magenta | :bright_cyan | :bright_white
13
+ # type ansi_color_value = ansi_color_symbol | Symbol | String | Integer
14
+
15
+ COLORS = {
16
+ black: "0",
17
+ red: "1",
18
+ green: "2",
19
+ yellow: "3",
20
+ blue: "4",
21
+ magenta: "5",
22
+ cyan: "6",
23
+ white: "7",
24
+ bright_black: "8",
25
+ bright_red: "9",
26
+ bright_green: "10",
27
+ bright_yellow: "11",
28
+ bright_blue: "12",
29
+ bright_magenta: "13",
30
+ bright_cyan: "14",
31
+ bright_white: "15"
32
+ }.freeze #: Hash[Symbol, String]
33
+
34
+ # @rbs value: ansi_color_value
35
+ # @rbs return: String
36
+ def self.resolve(value)
37
+ case value
38
+ when Symbol then COLORS.fetch(value) { raise ArgumentError, "Unknown ANSI color: #{value.inspect}" }
39
+ when String then value
40
+ when Integer then value.to_s
41
+ else raise ArgumentError, "ANSI color must be a Symbol, String, or Integer, got #{value.class}"
42
+ end
43
+ end
44
+ end
45
+
10
46
  # Adaptive color that changes based on terminal background
11
47
  #
12
48
  # @example
@@ -38,8 +74,8 @@ module Lipgloss
38
74
  # @example
39
75
  # color = Lipgloss::CompleteColor.new(
40
76
  # true_color: "#0000FF",
41
- # ansi256: "21",
42
- # ansi: "4"
77
+ # ansi256: 21,
78
+ # ansi: :blue
43
79
  # )
44
80
  class CompleteColor
45
81
  # @rbs @true_color: String
@@ -51,13 +87,13 @@ module Lipgloss
51
87
  attr_reader :ansi #: String
52
88
 
53
89
  # @rbs true_color: String -- 24-bit color (e.g., "#0000FF")
54
- # @rbs ansi256: String -- 8-bit ANSI color (e.g., "21")
55
- # @rbs ansi: String -- 4-bit ANSI color (e.g., "4")
90
+ # @rbs ansi256: ANSIColor::ansi_color_value -- 8-bit ANSI color (0-255, or symbol for 0-15)
91
+ # @rbs ansi: ANSIColor::ansi_color_value -- 4-bit ANSI color (:red, :blue, etc., or 0-15)
56
92
  # @rbs return: void
57
93
  def initialize(true_color:, ansi256:, ansi:)
58
94
  @true_color = true_color
59
- @ansi256 = ansi256
60
- @ansi = ansi
95
+ @ansi256 = ANSIColor.resolve(ansi256)
96
+ @ansi = ANSIColor.resolve(ansi)
61
97
  end
62
98
 
63
99
  # @rbs return: complete_color_hash
@@ -71,8 +107,8 @@ module Lipgloss
71
107
  #
72
108
  # @example
73
109
  # color = Lipgloss::CompleteAdaptiveColor.new(
74
- # light: Lipgloss::CompleteColor.new(true_color: "#000", ansi256: "0", ansi: "0"),
75
- # dark: Lipgloss::CompleteColor.new(true_color: "#FFF", ansi256: "15", ansi: "15")
110
+ # light: Lipgloss::CompleteColor.new(true_color: "#000", ansi256: :black, ansi: :black),
111
+ # dark: Lipgloss::CompleteColor.new(true_color: "#FFF", ansi256: :bright_white, ansi: :bright_white)
76
112
  # )
77
113
  class CompleteAdaptiveColor
78
114
  # @rbs @light: CompleteColor
@@ -23,5 +23,28 @@ module Lipgloss
23
23
 
24
24
  # Center alignment (0.5)
25
25
  CENTER = 0.5
26
+
27
+ # @rbs!
28
+ # type position_symbol = :top | :bottom | :left | :right | :center
29
+ # type position_value = position_symbol | Symbol | String | Integer | Float
30
+
31
+ SYMBOLS = {
32
+ top: TOP,
33
+ bottom: BOTTOM,
34
+ left: LEFT,
35
+ right: RIGHT,
36
+ center: CENTER
37
+ }.freeze #: Hash[Symbol, Float]
38
+
39
+ # @rbs value: position_value
40
+ # @rbs return: Float
41
+ def self.resolve(value)
42
+ case value
43
+ when Symbol then SYMBOLS.fetch(value) { raise ArgumentError, "Unknown position: #{value.inspect}" }
44
+ when String then SYMBOLS.fetch(value.to_sym) { raise ArgumentError, "Unknown position: #{value.inspect}" }
45
+ when Numeric then value.to_f
46
+ else raise ArgumentError, "Position must be a Symbol or Numeric, got #{value.class}"
47
+ end
48
+ end
26
49
  end
27
50
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Lipgloss
5
+ class Style
6
+ # @rbs *positions: Position::position_value
7
+ # @rbs return: Style
8
+ def align(*positions)
9
+ _align(*positions.map { |p| Position.resolve(p) })
10
+ end
11
+
12
+ # @rbs position: Position::position_value
13
+ # @rbs return: Style
14
+ def align_horizontal(position)
15
+ _align_horizontal(Position.resolve(position))
16
+ end
17
+
18
+ # @rbs position: Position::position_value
19
+ # @rbs return: Style
20
+ def align_vertical(position)
21
+ _align_vertical(Position.resolve(position))
22
+ end
23
+ end
24
+ end
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Lipgloss
5
- VERSION = "0.1.0" #: String
5
+ VERSION = "0.2.0" #: String
6
6
  end
data/lib/lipgloss.rb CHANGED
@@ -2,10 +2,18 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  require_relative "lipgloss/version"
5
- require_relative "lipgloss/lipgloss"
5
+
6
+ begin
7
+ major, minor, _patch = RUBY_VERSION.split(".") #: [String, String, String]
8
+ require_relative "lipgloss/#{major}.#{minor}/lipgloss"
9
+ rescue LoadError
10
+ require_relative "lipgloss/lipgloss"
11
+ end
12
+
6
13
  require_relative "lipgloss/position"
7
14
  require_relative "lipgloss/border"
8
15
  require_relative "lipgloss/color"
16
+ require_relative "lipgloss/style"
9
17
  require_relative "lipgloss/table"
10
18
 
11
19
  module Lipgloss
@@ -24,4 +32,46 @@ module Lipgloss
24
32
  ASCII_BORDER = Border::ASCII #: Symbol
25
33
 
26
34
  NO_TAB_CONVERSION = -1 #: Integer
35
+
36
+ class << self
37
+ # @rbs position: Position::position_value
38
+ # @rbs *strings: String
39
+ # @rbs return: String
40
+ def join_horizontal(position, *strings)
41
+ _join_horizontal(Position.resolve(position), strings)
42
+ end
43
+
44
+ # @rbs position: Position::position_value
45
+ # @rbs *strings: String
46
+ # @rbs return: String
47
+ def join_vertical(position, *strings)
48
+ _join_vertical(Position.resolve(position), strings)
49
+ end
50
+
51
+ # @rbs width: Integer
52
+ # @rbs height: Integer
53
+ # @rbs horizontal: Position::position_value
54
+ # @rbs vertical: Position::position_value
55
+ # @rbs string: String
56
+ # @rbs return: String
57
+ def place(width, height, horizontal, vertical, string, **opts)
58
+ _place(width, height, Position.resolve(horizontal), Position.resolve(vertical), string, **opts)
59
+ end
60
+
61
+ # @rbs width: Integer
62
+ # @rbs position: Position::position_value
63
+ # @rbs string: String
64
+ # @rbs return: String
65
+ def place_horizontal(width, position, string)
66
+ _place_horizontal(width, Position.resolve(position), string)
67
+ end
68
+
69
+ # @rbs height: Integer
70
+ # @rbs position: Position::position_value
71
+ # @rbs string: String
72
+ # @rbs return: String
73
+ def place_vertical(height, position, string)
74
+ _place_vertical(height, Position.resolve(position), string)
75
+ end
76
+ end
27
77
  end
data/lipgloss.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Marco Roth"]
9
9
  spec.email = ["marco.roth@intergga.ch"]
10
10
 
11
- spec.summary = "Ruby wrapper for Charm's lipgloss CSS-like terminal styling library."
12
- spec.description = spec.summary
11
+ spec.summary = "Ruby wrapper for Charm's lipgloss. CSS-like terminal styling library."
12
+ spec.description = "Style Definitions for Nice Terminal Layouts. Built with TUIs in mind."
13
13
  spec.homepage = "https://github.com/marcoroth/lipgloss-ruby"
14
14
  spec.license = "MIT"
15
15
  spec.required_ruby_version = ">= 3.2.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lipgloss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Roth
@@ -9,7 +9,7 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
- description: Ruby wrapper for Charm's lipgloss CSS-like terminal styling library.
12
+ description: Style Definitions for Nice Terminal Layouts. Built with TUIs in mind.
13
13
  email:
14
14
  - marco.roth@intergga.ch
15
15
  executables: []
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - LICENSE.txt
21
21
  - README.md
22
+ - ext/lipgloss/color.c
22
23
  - ext/lipgloss/extconf.rb
23
24
  - ext/lipgloss/extension.c
24
25
  - ext/lipgloss/extension.h
@@ -29,6 +30,7 @@ files:
29
30
  - ext/lipgloss/style_unset.c
30
31
  - ext/lipgloss/table.c
31
32
  - ext/lipgloss/tree.c
33
+ - go/color.go
32
34
  - go/go.mod
33
35
  - go/go.sum
34
36
  - go/layout.go
@@ -44,6 +46,7 @@ files:
44
46
  - lib/lipgloss/border.rb
45
47
  - lib/lipgloss/color.rb
46
48
  - lib/lipgloss/position.rb
49
+ - lib/lipgloss/style.rb
47
50
  - lib/lipgloss/table.rb
48
51
  - lib/lipgloss/version.rb
49
52
  - lipgloss.gemspec
@@ -69,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
72
  - !ruby/object:Gem::Version
70
73
  version: '0'
71
74
  requirements: []
72
- rubygems_version: 3.6.9
75
+ rubygems_version: 4.0.3
73
76
  specification_version: 4
74
- summary: Ruby wrapper for Charm's lipgloss CSS-like terminal styling library.
77
+ summary: Ruby wrapper for Charm's lipgloss. CSS-like terminal styling library.
75
78
  test_files: []