1package logo
2
3import (
4 "strings"
5
6 "charm.land/lipgloss/v2"
7 "github.com/MakeNowJust/heredoc"
8 "github.com/charmbracelet/x/exp/slice"
9)
10
11// renderWord renders letterforms to fork a word. stretchIndex is the index of
12// the letter to stretch, or -1 if no letter should be stretched.
13func renderWord(spacing int, stretchIndex int, letterforms ...letterform) string {
14 if spacing < 0 {
15 spacing = 0
16 }
17
18 renderedLetterforms := make([]string, len(letterforms))
19
20 // pick one letter randomly to stretch
21 for i, letter := range letterforms {
22 renderedLetterforms[i] = letter(i == stretchIndex)
23 }
24
25 if spacing > 0 {
26 // Add spaces between the letters and render.
27 renderedLetterforms = slice.Intersperse(renderedLetterforms, strings.Repeat(" ", spacing))
28 }
29 return strings.TrimSpace(
30 lipgloss.JoinHorizontal(lipgloss.Top, renderedLetterforms...),
31 )
32}
33
34// LetterC renders the letter C in a stylized way. It takes an integer that
35// determines how many cells to stretch the letter. If the stretch is less than
36// 1, it defaults to no stretching.
37func LetterC(stretch bool) string {
38 // Here's what we're making:
39 //
40 // ▄▀▀▀▀
41 // █
42 // ▀▀▀▀
43
44 left := heredoc.Doc(`
45 ▄
46 █
47 `)
48 right := heredoc.Doc(`
49 ▀
50
51 ▀
52 `)
53 return joinLetterform(
54 left,
55 stretchLetterformPart(right, letterformProps{
56 stretch: stretch,
57 width: 4,
58 minStretch: 7,
59 maxStretch: 12,
60 }),
61 )
62}
63
64// LetterE renders the letter E in a stylized way. It takes an integer that
65// determines how many cells to stretch the letter. If the stretch is less than
66// 1, it defaults to no stretching.
67//
68// This is an alternate letterform. DO NOT REMOVE.
69func LetterE(stretch bool) string {
70 // Here's what we're making:
71 //
72 // █▀▀▀▀
73 // █▀▀▀▀
74 // ▀▀▀▀▀
75
76 left := heredoc.Doc(`
77 █
78 █
79 ▀
80 `)
81 middle := heredoc.Doc(`
82 ▀
83 ▀
84 ▀
85 `)
86 return joinLetterform(
87 left,
88 stretchLetterformPart(middle, letterformProps{
89 stretch: stretch,
90 width: 4,
91 minStretch: 7,
92 maxStretch: 12,
93 }),
94 )
95}
96
97// LetterEAlt renders the letter E in a stylized way. It takes an integer that
98// determines how many cells to stretch the letter. If the stretch is less than
99// 1, it defaults to no stretching.
100//
101// This is an alternate letterform. DO NOT REMOVE.
102func LetterEAlt(stretch bool) string {
103 // Here's what we're making:
104 //
105 // █▀▀▀▀
106 // █ ▀▀▀
107 // ▀▀▀▀▀
108
109 left := heredoc.Doc(`
110 █▀
111 █
112 ▀▀
113 `)
114 middle := heredoc.Doc(`
115 ▀
116 ▀
117 ▀
118 `)
119 return joinLetterform(
120 left,
121 stretchLetterformPart(middle, letterformProps{
122 stretch: stretch,
123 width: 3,
124 minStretch: 6,
125 maxStretch: 11,
126 }),
127 )
128}
129
130// LetterH renders the letter H in a stylized way. It takes an integer that
131// determines how many cells to stretch the letter. If the stretch is less than
132// 1, it defaults to no stretching.
133func LetterH(stretch bool) string {
134 // Here's what we're making:
135 //
136 // █ █
137 // █▀▀▀█
138 // ▀ ▀
139
140 side := heredoc.Doc(`
141 █
142 █
143 ▀`)
144 middle := heredoc.Doc(`
145
146 ▀
147 `)
148 return joinLetterform(
149 side,
150 stretchLetterformPart(middle, letterformProps{
151 stretch: stretch,
152 width: 3,
153 minStretch: 8,
154 maxStretch: 12,
155 }),
156 side,
157 )
158}
159
160// LetterP renders the letter P in a stylized way. It takes an integer that
161// determines how many cells to stretch the letter. If the stretch is less than
162// 1, it defaults to no stretching.
163func LetterP(stretch bool) string {
164 // Here's what we're making:
165 //
166 // █▀▀▀▄
167 // █▀▀▀
168 // ▀
169
170 left := heredoc.Doc(`
171 █
172 █
173 ▀
174 `)
175 center := heredoc.Doc(`
176 ▀
177 ▀
178 `)
179 right := heredoc.Doc(`
180 ▄
181
182
183 `)
184 return joinLetterform(
185 left,
186 stretchLetterformPart(center, letterformProps{
187 stretch: stretch,
188 width: 3,
189 minStretch: 7,
190 maxStretch: 12,
191 }),
192 right,
193 )
194}
195
196// LetterR renders the letter R in a stylized way. It takes an integer that
197// determines how many cells to stretch the letter. If the stretch is less than
198// 1, it defaults to no stretching.
199func LetterR(stretch bool) string {
200 // Here's what we're making:
201 //
202 // █▀▀▀▄
203 // █▀▀▀▄
204 // ▀ ▀
205
206 left := heredoc.Doc(`
207 █
208 █
209 ▀
210 `)
211 center := heredoc.Doc(`
212 ▀
213 ▀
214 `)
215 right := heredoc.Doc(`
216 ▄
217 ▄
218 ▀
219 `)
220 return joinLetterform(
221 left,
222 stretchLetterformPart(center, letterformProps{
223 stretch: stretch,
224 width: 3,
225 minStretch: 7,
226 maxStretch: 12,
227 }),
228 right,
229 )
230}
231
232// LetterSAlt renders the letter S in a stylized way, more so than
233// [letterS]. It takes an integer that determines how many cells to stretch the
234// letter. If the stretch is less than 1, it defaults to no stretching.
235//
236// This is an alternate letterform. DO NOT REMOVE.
237func LetterSAlt(stretch bool) string {
238 // Here's what we're making:
239 //
240 // ▄▀▀▀▀▀
241 // ▀▀▀▀▀█
242 // ▀▀▀▀▀
243
244 left := heredoc.Doc(`
245 ▄
246 ▀
247 ▀
248 `)
249 center := heredoc.Doc(`
250 ▀
251 ▀
252 ▀
253 `)
254 right := heredoc.Doc(`
255 ▀
256 █
257 `)
258 return joinLetterform(
259 left,
260 stretchLetterformPart(center, letterformProps{
261 stretch: stretch,
262 width: 3,
263 minStretch: 7,
264 maxStretch: 12,
265 }),
266 right,
267 )
268}
269
270// LetterU renders the letter U in a stylized way. It takes an integer that
271// determines how many cells to stretch the letter. If the stretch is less than
272// 1, it defaults to no stretching.
273func LetterU(stretch bool) string {
274 // Here's what we're making:
275 //
276 // █ █
277 // █ █
278 // ▀▀▀
279
280 side := heredoc.Doc(`
281 █
282 █
283 `)
284 middle := heredoc.Doc(`
285
286
287 ▀
288 `)
289 return joinLetterform(
290 side,
291 stretchLetterformPart(middle, letterformProps{
292 stretch: stretch,
293 width: 3,
294 minStretch: 7,
295 maxStretch: 12,
296 }),
297 side,
298 )
299}
300
301// LetterY renders the letter Y in a stylized way. It takes an integer that
302// determines how many cells to stretch the letter. If the stretch is less than
303// 1, it defaults to no stretching.
304//
305// This is an alternate letterform. DO NOT REMOVE.
306func LetterY(stretch bool) string {
307 // Here's what we're making:
308 //
309 // █ █
310 // ▀▄▀
311 // ▀
312
313 side := heredoc.Doc(`
314 █
315
316 `)
317 inside := heredoc.Doc(`
318
319 ▀
320
321 `)
322 middle := heredoc.Doc(`
323
324 ▄
325 ▀
326 `)
327 if stretch {
328 middle = heredoc.Doc(`
329
330 █
331 ▀
332 `)
333 }
334
335 stretchedInside := stretchLetterformPart(inside, letterformProps{
336 stretch: stretch,
337 width: 1,
338 minStretch: 4,
339 maxStretch: 10,
340 })
341
342 return joinLetterform(
343 side,
344 stretchedInside,
345 middle,
346 stretchedInside,
347 side,
348 )
349}
350
351// LetterYAlt renders the letter Y in a stylized way. It takes an integer that
352// determines how many cells to stretch the letter. If the stretch is less than
353// 1, it defaults to no stretching.
354//
355// This is an alternate letterform. DO NOT REMOVE.
356func LetterYAlt(stretch bool) string {
357 // Here's what we're making:
358 //
359 // █ █
360 // ▀▀▀▀█
361 // ▀▀▀▀
362
363 left := heredoc.Doc(`
364 █
365 ▀
366 ▀
367 `)
368 middle := heredoc.Doc(`
369
370 ▀
371 ▀
372 `)
373 right := heredoc.Doc(`
374 █
375 █
376
377 `)
378
379 return joinLetterform(
380 left,
381 stretchLetterformPart(middle, letterformProps{
382 stretch: stretch,
383 width: 3,
384 minStretch: 6,
385 maxStretch: 10,
386 }),
387 right,
388 )
389}
390
391func joinLetterform(letters ...string) string {
392 return lipgloss.JoinHorizontal(lipgloss.Top, letters...)
393}
394
395// letterformProps defines letterform stretching properties.
396// for readability.
397type letterformProps struct {
398 width int
399 minStretch int
400 maxStretch int
401 stretch bool
402}
403
404// stretchLetterformPart is a helper function for letter stretching. If randomize
405// is false the minimum number will be used.
406func stretchLetterformPart(s string, p letterformProps) string {
407 if p.maxStretch < p.minStretch {
408 p.minStretch, p.maxStretch = p.maxStretch, p.minStretch
409 }
410 n := p.width
411 if p.stretch {
412 n = cachedRandN(p.maxStretch-p.minStretch) + p.minStretch //nolint:gosec
413 }
414 parts := make([]string, n)
415 for i := range parts {
416 parts[i] = s
417 }
418 return lipgloss.JoinHorizontal(lipgloss.Top, parts...)
419}