glamour 0.0.1 → 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.
@@ -0,0 +1,300 @@
1
+ /* frozen_string_literal: true */
2
+
3
+ #include <ruby.h>
4
+ #include "libglamour.h"
5
+
6
+ VALUE mGlamour;
7
+ VALUE cRenderer;
8
+
9
+ #define COLOR_PROFILE_AUTO 0
10
+ #define COLOR_PROFILE_TRUE_COLOR 1
11
+ #define COLOR_PROFILE_ANSI256 2
12
+ #define COLOR_PROFILE_ANSI 3
13
+ #define COLOR_PROFILE_ASCII 4
14
+
15
+ /*
16
+ * Render markdown to terminal-styled output.
17
+ *
18
+ * @param markdown [String] The markdown content to render
19
+ * @param style [String] Style name: "auto", "dark", "light", "notty", "dracula"
20
+ * @param width [Integer] Optional word wrap width
21
+ * @param emoji [Boolean] Enable emoji rendering
22
+ * @param preserve_newlines [Boolean] Preserve newlines in output
23
+ * @param base_url [String] Base URL for relative links
24
+ * @param color_profile [Symbol] Color profile: :auto, :true_color, :ansi256, :ansi, :ascii
25
+ * @return [String] Rendered output with ANSI escape codes
26
+ */
27
+ static VALUE glamour_render_rb(int argc, VALUE *argv, VALUE self) {
28
+ VALUE markdown, options;
29
+ rb_scan_args(argc, argv, "1:", &markdown, &options);
30
+
31
+ Check_Type(markdown, T_STRING);
32
+
33
+ const char *style = "auto";
34
+ const char *base_url = NULL;
35
+
36
+ int width = 0;
37
+ int emoji = 0;
38
+ int preserve_newlines = 0;
39
+ int color_profile = COLOR_PROFILE_AUTO;
40
+ int has_advanced_options = 0;
41
+
42
+ if (!NIL_P(options)) {
43
+ VALUE style_value = rb_hash_lookup(options, ID2SYM(rb_intern("style")));
44
+ VALUE width_value = rb_hash_lookup(options, ID2SYM(rb_intern("width")));
45
+ VALUE emoji_value = rb_hash_lookup(options, ID2SYM(rb_intern("emoji")));
46
+ VALUE color_value = rb_hash_lookup(options, ID2SYM(rb_intern("color_profile")));
47
+
48
+ VALUE base_url_value = rb_hash_lookup(options, ID2SYM(rb_intern("base_url")));
49
+ VALUE preserve_value = rb_hash_lookup(options, ID2SYM(rb_intern("preserve_newlines")));
50
+
51
+ if (!NIL_P(style_value)) {
52
+ Check_Type(style_value, T_STRING);
53
+ style = StringValueCStr(style_value);
54
+ }
55
+
56
+ if (!NIL_P(width_value)) {
57
+ width = NUM2INT(width_value);
58
+ }
59
+
60
+ if (RTEST(emoji_value)) {
61
+ emoji = 1;
62
+ has_advanced_options = 1;
63
+ }
64
+
65
+ if (RTEST(preserve_value)) {
66
+ preserve_newlines = 1;
67
+ has_advanced_options = 1;
68
+ }
69
+
70
+ if (!NIL_P(base_url_value)) {
71
+ Check_Type(base_url_value, T_STRING);
72
+ base_url = StringValueCStr(base_url_value);
73
+ has_advanced_options = 1;
74
+ }
75
+
76
+ if (!NIL_P(color_value)) {
77
+ has_advanced_options = 1;
78
+
79
+ if (color_value == ID2SYM(rb_intern("true_color"))) {
80
+ color_profile = COLOR_PROFILE_TRUE_COLOR;
81
+ } else if (color_value == ID2SYM(rb_intern("ansi256"))) {
82
+ color_profile = COLOR_PROFILE_ANSI256;
83
+ } else if (color_value == ID2SYM(rb_intern("ansi"))) {
84
+ color_profile = COLOR_PROFILE_ANSI;
85
+ } else if (color_value == ID2SYM(rb_intern("ascii"))) {
86
+ color_profile = COLOR_PROFILE_ASCII;
87
+ }
88
+ }
89
+ }
90
+
91
+ char *result;
92
+
93
+ if (has_advanced_options || width > 0) {
94
+ result = glamour_render_with_options(
95
+ (char *) StringValueCStr(markdown),
96
+ (char *) style,
97
+ width,
98
+ emoji,
99
+ preserve_newlines,
100
+ (char *) base_url,
101
+ color_profile
102
+ );
103
+ } else {
104
+ result = glamour_render(
105
+ (char *) StringValueCStr(markdown),
106
+ (char *) style
107
+ );
108
+ }
109
+
110
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
111
+ glamour_free(result);
112
+
113
+ return rb_result;
114
+ }
115
+
116
+ /*
117
+ * Render markdown with a custom JSON style.
118
+ *
119
+ * @param markdown [String] The markdown content to render
120
+ * @param json_style [String] JSON style definition
121
+ * @param width [Integer] Optional word wrap width
122
+ * @return [String] Rendered output with ANSI escape codes
123
+ */
124
+ static VALUE glamour_render_with_json_rb(int argc, VALUE *argv, VALUE self) {
125
+ VALUE markdown, json_style, options;
126
+ rb_scan_args(argc, argv, "2:", &markdown, &json_style, &options);
127
+
128
+ Check_Type(markdown, T_STRING);
129
+ Check_Type(json_style, T_STRING);
130
+
131
+ int width = 0;
132
+
133
+ if (!NIL_P(options)) {
134
+ VALUE width_value = rb_hash_lookup(options, ID2SYM(rb_intern("width")));
135
+
136
+ if (!NIL_P(width_value)) {
137
+ width = NUM2INT(width_value);
138
+ }
139
+ }
140
+
141
+ char *result = glamour_render_with_json_style(
142
+ (char *) StringValueCStr(markdown),
143
+ (char *) StringValueCStr(json_style),
144
+ width
145
+ );
146
+
147
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
148
+ glamour_free(result);
149
+
150
+ return rb_result;
151
+ }
152
+
153
+ /*
154
+ * Get the upstream glamour version.
155
+ *
156
+ * @return [String] Upstream glamour version
157
+ */
158
+ static VALUE glamour_upstream_version_rb(VALUE self) {
159
+ char *version = glamour_upstream_version();
160
+ VALUE rb_version = rb_utf8_str_new_cstr(version);
161
+ glamour_free(version);
162
+
163
+ return rb_version;
164
+ }
165
+
166
+ /*
167
+ * Get the glamour version info.
168
+ *
169
+ * @return [String] Version information string
170
+ */
171
+ static VALUE glamour_version_rb(VALUE self) {
172
+ VALUE gem_version = rb_const_get(self, rb_intern("VERSION"));
173
+ VALUE upstream_version = glamour_upstream_version_rb(self);
174
+ VALUE format_string = rb_utf8_str_new_cstr("glamour v%s (upstream v%s) [Go native extension]");
175
+
176
+ return rb_funcall(rb_mKernel, rb_intern("sprintf"), 3, format_string, gem_version, upstream_version);
177
+ }
178
+
179
+ /* Renderer class methods */
180
+
181
+ static VALUE renderer_alloc(VALUE klass) {
182
+ return Data_Wrap_Struct(klass, NULL, NULL, NULL);
183
+ }
184
+
185
+ static VALUE renderer_initialize(int argc, VALUE *argv, VALUE self) {
186
+ VALUE options;
187
+ rb_scan_args(argc, argv, "0:", &options);
188
+
189
+ rb_iv_set(self, "@style", rb_str_new_cstr("auto"));
190
+ rb_iv_set(self, "@width", INT2FIX(0));
191
+ rb_iv_set(self, "@emoji", Qfalse);
192
+ rb_iv_set(self, "@preserve_newlines", Qfalse);
193
+ rb_iv_set(self, "@base_url", Qnil);
194
+ rb_iv_set(self, "@color_profile", ID2SYM(rb_intern("auto")));
195
+ rb_iv_set(self, "@json_style", Qnil);
196
+
197
+ if (!NIL_P(options)) {
198
+ VALUE style = rb_hash_lookup(options, ID2SYM(rb_intern("style")));
199
+ if (!NIL_P(style)) rb_iv_set(self, "@style", style);
200
+
201
+ VALUE width = rb_hash_lookup(options, ID2SYM(rb_intern("width")));
202
+ if (!NIL_P(width)) rb_iv_set(self, "@width", width);
203
+
204
+ VALUE emoji = rb_hash_lookup(options, ID2SYM(rb_intern("emoji")));
205
+ if (RTEST(emoji)) rb_iv_set(self, "@emoji", Qtrue);
206
+
207
+ VALUE preserve = rb_hash_lookup(options, ID2SYM(rb_intern("preserve_newlines")));
208
+ if (RTEST(preserve)) rb_iv_set(self, "@preserve_newlines", Qtrue);
209
+
210
+ VALUE base_url = rb_hash_lookup(options, ID2SYM(rb_intern("base_url")));
211
+ if (!NIL_P(base_url)) rb_iv_set(self, "@base_url", base_url);
212
+
213
+ VALUE color = rb_hash_lookup(options, ID2SYM(rb_intern("color_profile")));
214
+ if (!NIL_P(color)) rb_iv_set(self, "@color_profile", color);
215
+
216
+ VALUE json = rb_hash_lookup(options, ID2SYM(rb_intern("json_style")));
217
+ if (!NIL_P(json)) rb_iv_set(self, "@json_style", json);
218
+ }
219
+
220
+ return self;
221
+ }
222
+
223
+ static VALUE renderer_render(VALUE self, VALUE markdown) {
224
+ Check_Type(markdown, T_STRING);
225
+
226
+ VALUE json_style = rb_iv_get(self, "@json_style");
227
+
228
+ if (!NIL_P(json_style)) {
229
+ int width = NUM2INT(rb_iv_get(self, "@width"));
230
+
231
+ char *result = glamour_render_with_json_style(
232
+ (char *)StringValueCStr(markdown),
233
+ (char *)StringValueCStr(json_style),
234
+ width
235
+ );
236
+
237
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
238
+ glamour_free(result);
239
+ return rb_result;
240
+ }
241
+
242
+ VALUE style_value = rb_iv_get(self, "@style");
243
+ const char *style = NIL_P(style_value) ? "auto" : StringValueCStr(style_value);
244
+ int width = NUM2INT(rb_iv_get(self, "@width"));
245
+ int emoji = RTEST(rb_iv_get(self, "@emoji")) ? 1 : 0;
246
+ int preserve_newlines = RTEST(rb_iv_get(self, "@preserve_newlines")) ? 1 : 0;
247
+
248
+ VALUE base_url_value = rb_iv_get(self, "@base_url");
249
+ const char *base_url = NIL_P(base_url_value) ? NULL : StringValueCStr(base_url_value);
250
+
251
+ VALUE color_value = rb_iv_get(self, "@color_profile");
252
+ int color_profile = COLOR_PROFILE_AUTO;
253
+
254
+ if (color_value == ID2SYM(rb_intern("true_color"))) {
255
+ color_profile = COLOR_PROFILE_TRUE_COLOR;
256
+ } else if (color_value == ID2SYM(rb_intern("ansi256"))) {
257
+ color_profile = COLOR_PROFILE_ANSI256;
258
+ } else if (color_value == ID2SYM(rb_intern("ansi"))) {
259
+ color_profile = COLOR_PROFILE_ANSI;
260
+ } else if (color_value == ID2SYM(rb_intern("ascii"))) {
261
+ color_profile = COLOR_PROFILE_ASCII;
262
+ }
263
+
264
+ char *result = glamour_render_with_options(
265
+ (char *)StringValueCStr(markdown),
266
+ (char *)style,
267
+ width,
268
+ emoji,
269
+ preserve_newlines,
270
+ (char *)base_url,
271
+ color_profile
272
+ );
273
+
274
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
275
+ glamour_free(result);
276
+
277
+ return rb_result;
278
+ }
279
+
280
+ __attribute__((__visibility__("default"))) void Init_glamour(void) {
281
+ mGlamour = rb_define_module("Glamour");
282
+
283
+ rb_define_singleton_method(mGlamour, "render", glamour_render_rb, -1);
284
+ rb_define_singleton_method(mGlamour, "render_with_json", glamour_render_with_json_rb, -1);
285
+ rb_define_singleton_method(mGlamour, "upstream_version", glamour_upstream_version_rb, 0);
286
+ rb_define_singleton_method(mGlamour, "version", glamour_version_rb, 0);
287
+
288
+ cRenderer = rb_define_class_under(mGlamour, "Renderer", rb_cObject);
289
+ rb_define_alloc_func(cRenderer, renderer_alloc);
290
+ rb_define_method(cRenderer, "initialize", renderer_initialize, -1);
291
+ rb_define_method(cRenderer, "render", renderer_render, 1);
292
+
293
+ rb_define_attr(cRenderer, "style", 1, 1);
294
+ rb_define_attr(cRenderer, "width", 1, 1);
295
+ rb_define_attr(cRenderer, "emoji", 1, 1);
296
+ rb_define_attr(cRenderer, "preserve_newlines", 1, 1);
297
+ rb_define_attr(cRenderer, "base_url", 1, 1);
298
+ rb_define_attr(cRenderer, "color_profile", 1, 1);
299
+ rb_define_attr(cRenderer, "json_style", 1, 1);
300
+ }
data/glamour.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/glamour/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "glamour"
7
+ spec.version = Glamour::VERSION
8
+ spec.authors = ["Marco Roth"]
9
+ spec.email = ["marco.roth@intergga.ch"]
10
+
11
+ spec.summary = "Ruby wrapper for Charm's glamour stylesheet-based markdown rendering for Ruby CLI apps."
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/marcoroth/glamour-ruby"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.2.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/marcoroth/glamour-ruby"
19
+ spec.metadata["changelog_uri"] = "https://github.com/marcoroth/glamour-ruby/releases"
20
+ spec.metadata["rubygems_mfa_required"] = "true"
21
+
22
+ spec.files = Dir[
23
+ "glamour.gemspec",
24
+ "LICENSE.txt",
25
+ "README.md",
26
+ "lib/**/*.rb",
27
+ "ext/**/*.{c,h,rb}",
28
+ "go/**/*.{go,mod,sum}",
29
+ "go/build/**/*"
30
+ ]
31
+
32
+ spec.require_paths = ["lib"]
33
+ spec.extensions = ["ext/glamour/extconf.rb"]
34
+ end
data/go/glamour.go ADDED
@@ -0,0 +1,160 @@
1
+ package main
2
+
3
+ /*
4
+ #include <stdlib.h>
5
+ */
6
+ import "C"
7
+
8
+ import (
9
+ "runtime/debug"
10
+ "unsafe"
11
+
12
+ "github.com/charmbracelet/glamour"
13
+ "github.com/muesli/termenv"
14
+ )
15
+
16
+ //export glamour_render
17
+ func glamour_render(markdown *C.char, style *C.char) *C.char {
18
+ result, err := glamour.Render(C.GoString(markdown), C.GoString(style))
19
+
20
+ if err != nil {
21
+ return C.CString("")
22
+ }
23
+
24
+ return C.CString(result)
25
+ }
26
+
27
+ //export glamour_render_with_width
28
+ func glamour_render_with_width(markdown *C.char, style *C.char, width C.int) *C.char {
29
+ renderer, err := glamour.NewTermRenderer(
30
+ glamour.WithStylePath(C.GoString(style)),
31
+ glamour.WithWordWrap(int(width)),
32
+ )
33
+
34
+ if err != nil {
35
+ return C.CString("")
36
+ }
37
+
38
+ result, err := renderer.Render(C.GoString(markdown))
39
+
40
+ if err != nil {
41
+ return C.CString("")
42
+ }
43
+
44
+ return C.CString(result)
45
+ }
46
+
47
+ //export glamour_render_with_options
48
+ func glamour_render_with_options(
49
+ markdown *C.char,
50
+ style *C.char,
51
+ width C.int,
52
+ emoji C.int,
53
+ preserveNewlines C.int,
54
+ baseURL *C.char,
55
+ colorProfile C.int,
56
+ ) *C.char {
57
+ var options []glamour.TermRendererOption
58
+
59
+ styleStr := C.GoString(style)
60
+
61
+ if styleStr != "" {
62
+ options = append(options, glamour.WithStylePath(styleStr))
63
+ } else {
64
+ options = append(options, glamour.WithAutoStyle())
65
+ }
66
+
67
+ if width > 0 {
68
+ options = append(options, glamour.WithWordWrap(int(width)))
69
+ }
70
+
71
+ if emoji != 0 {
72
+ options = append(options, glamour.WithEmoji())
73
+ }
74
+
75
+ if preserveNewlines != 0 {
76
+ options = append(options, glamour.WithPreservedNewLines())
77
+ }
78
+
79
+ if baseURL != nil {
80
+ baseURLStr := C.GoString(baseURL)
81
+
82
+ if baseURLStr != "" {
83
+ options = append(options, glamour.WithBaseURL(baseURLStr))
84
+ }
85
+ }
86
+
87
+ // 0=auto, 1=TrueColor, 2=ANSI256, 3=ANSI, 4=Ascii
88
+ switch colorProfile {
89
+ case 1:
90
+ options = append(options, glamour.WithColorProfile(termenv.TrueColor))
91
+ case 2:
92
+ options = append(options, glamour.WithColorProfile(termenv.ANSI256))
93
+ case 3:
94
+ options = append(options, glamour.WithColorProfile(termenv.ANSI))
95
+ case 4:
96
+ options = append(options, glamour.WithColorProfile(termenv.Ascii))
97
+ }
98
+
99
+ renderer, err := glamour.NewTermRenderer(options...)
100
+
101
+ if err != nil {
102
+ return C.CString("")
103
+ }
104
+
105
+ result, err := renderer.Render(C.GoString(markdown))
106
+
107
+ if err != nil {
108
+ return C.CString("")
109
+ }
110
+
111
+ return C.CString(result)
112
+ }
113
+
114
+ //export glamour_render_with_json_style
115
+ func glamour_render_with_json_style(markdown *C.char, jsonStyle *C.char, width C.int) *C.char {
116
+ var options []glamour.TermRendererOption
117
+
118
+ jsonBytes := []byte(C.GoString(jsonStyle))
119
+ options = append(options, glamour.WithStylesFromJSONBytes(jsonBytes))
120
+
121
+ if width > 0 {
122
+ options = append(options, glamour.WithWordWrap(int(width)))
123
+ }
124
+
125
+ renderer, err := glamour.NewTermRenderer(options...)
126
+
127
+ if err != nil {
128
+ return C.CString("")
129
+ }
130
+
131
+ result, err := renderer.Render(C.GoString(markdown))
132
+ if err != nil {
133
+ return C.CString("")
134
+ }
135
+
136
+ return C.CString(result)
137
+ }
138
+
139
+ //export glamour_free
140
+ func glamour_free(ptr *C.char) {
141
+ C.free(unsafe.Pointer(ptr))
142
+ }
143
+
144
+ //export glamour_upstream_version
145
+ func glamour_upstream_version() *C.char {
146
+ info, ok := debug.ReadBuildInfo()
147
+ if !ok {
148
+ return C.CString("unknown")
149
+ }
150
+
151
+ for _, dep := range info.Deps {
152
+ if dep.Path == "github.com/charmbracelet/glamour" {
153
+ return C.CString(dep.Version)
154
+ }
155
+ }
156
+
157
+ return C.CString("unknown")
158
+ }
159
+
160
+ func main() {}
data/go/go.mod ADDED
@@ -0,0 +1,35 @@
1
+ module github.com/marcoroth/glamour-ruby/go
2
+
3
+ go 1.23.0
4
+
5
+ toolchain go1.24.6
6
+
7
+ require github.com/charmbracelet/glamour v0.10.0
8
+
9
+ require (
10
+ github.com/alecthomas/chroma/v2 v2.14.0 // indirect
11
+ github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
12
+ github.com/aymerick/douceur v0.2.0 // indirect
13
+ github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
14
+ github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect
15
+ github.com/charmbracelet/x/ansi v0.8.0 // indirect
16
+ github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
17
+ github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
18
+ github.com/charmbracelet/x/term v0.2.1 // indirect
19
+ github.com/dlclark/regexp2 v1.11.0 // indirect
20
+ github.com/gorilla/css v1.0.1 // indirect
21
+ github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
22
+ github.com/mattn/go-isatty v0.0.20 // indirect
23
+ github.com/mattn/go-runewidth v0.0.16 // indirect
24
+ github.com/microcosm-cc/bluemonday v1.0.27 // indirect
25
+ github.com/muesli/reflow v0.3.0 // indirect
26
+ github.com/muesli/termenv v0.16.0 // indirect
27
+ github.com/rivo/uniseg v0.4.7 // indirect
28
+ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
29
+ github.com/yuin/goldmark v1.7.8 // indirect
30
+ github.com/yuin/goldmark-emoji v1.0.5 // indirect
31
+ golang.org/x/net v0.33.0 // indirect
32
+ golang.org/x/sys v0.32.0 // indirect
33
+ golang.org/x/term v0.31.0 // indirect
34
+ golang.org/x/text v0.24.0 // indirect
35
+ )
data/go/go.sum ADDED
@@ -0,0 +1,69 @@
1
+ github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
2
+ github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
3
+ github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
4
+ github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
5
+ github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
6
+ github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
7
+ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
8
+ github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
9
+ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
10
+ github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
11
+ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
12
+ github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
13
+ github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
14
+ github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
15
+ github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY=
16
+ github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk=
17
+ github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
18
+ github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
19
+ github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
20
+ github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
21
+ github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
22
+ github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
23
+ github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30=
24
+ github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
25
+ github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
26
+ github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
27
+ github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
28
+ github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
29
+ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
30
+ github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
31
+ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
32
+ github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
33
+ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
34
+ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
35
+ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
36
+ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
37
+ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
38
+ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
39
+ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
40
+ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
41
+ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
42
+ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
43
+ github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
44
+ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
45
+ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
46
+ github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
47
+ github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
48
+ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
49
+ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
50
+ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
51
+ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
52
+ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
53
+ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
54
+ github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
55
+ github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
56
+ github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
57
+ github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
58
+ github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
59
+ golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
60
+ golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
61
+ golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
62
+ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
63
+ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
64
+ golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
65
+ golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
66
+ golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
67
+ golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
68
+ golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
69
+ golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module Glamour
6
+ class Renderer
7
+ # @rbs hash: Hash[Symbol, untyped] -- style definition hash
8
+ # @rbs return: String
9
+ def style_hash=(hash)
10
+ @json_style = JSON.generate(hash)
11
+ end
12
+
13
+ alias original_initialize initialize
14
+
15
+ # @rbs style: String | singleton(Glamour::Style) -- style name or Style subclass
16
+ # @rbs width: Integer -- optional word wrap width
17
+ # @rbs emoji: bool -- whether to render emoji
18
+ # @rbs preserve_newlines: bool -- whether to preserve newlines
19
+ # @rbs base_url: String? -- base URL for relative links
20
+ # @rbs color_profile: Symbol -- color profile to use
21
+ # @rbs json_style: String? -- JSON style definition
22
+ # @rbs style_hash: Hash[Symbol, untyped]? -- style definition hash
23
+ # @rbs return: void
24
+ def initialize(style: "auto", width: 0, emoji: false, preserve_newlines: false, base_url: nil,
25
+ color_profile: :auto, json_style: nil, style_hash: nil)
26
+ actual_style = style
27
+ actual_json_style = json_style
28
+
29
+ if style.is_a?(Class) && style.respond_to?(:glamour_style?) && style.glamour_style?
30
+ actual_style = "auto"
31
+ actual_json_style = style.to_json unless style.to_h.empty?
32
+ end
33
+
34
+ actual_json_style = JSON.generate(style_hash) if style_hash
35
+
36
+ original_initialize(
37
+ style: actual_style,
38
+ width: width,
39
+ emoji: emoji,
40
+ preserve_newlines: preserve_newlines,
41
+ base_url: base_url,
42
+ color_profile: color_profile,
43
+ json_style: actual_json_style
44
+ )
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ require_relative "style_definition"
6
+
7
+ module Glamour
8
+ class Style
9
+ class << self
10
+ # @rbs @styles: Hash[Symbol, Hash[Symbol, untyped]]
11
+
12
+ # @rbs element: Symbol | String -- the element to style
13
+ # @rbs return: Hash[Symbol, untyped] -- the style definition
14
+ def style(element, &)
15
+ styles[element.to_sym] = StyleDefinition.new(&).to_h
16
+ end
17
+
18
+ # @rbs return: Hash[Symbol, Hash[Symbol, untyped]]
19
+ def styles
20
+ @styles ||= {}
21
+ end
22
+
23
+ # @rbs return: Hash[String, Hash[Symbol, untyped]]
24
+ def to_h
25
+ styles.transform_keys(&:to_s)
26
+ end
27
+
28
+ # @rbs return: String
29
+ def to_json(*_args)
30
+ JSON.generate(to_h)
31
+ end
32
+
33
+ # @rbs markdown: String -- the markdown content to render
34
+ # @rbs width: Integer -- optional word wrap width
35
+ # @rbs return: String -- rendered output with ANSI escape codes
36
+ def render(markdown, width: 0, **)
37
+ Glamour.render(markdown, style: self, width: width, **)
38
+ end
39
+
40
+ # @rbs return: true
41
+ def glamour_style?
42
+ true
43
+ end
44
+ end
45
+ end
46
+ end