himg 0.0.9-aarch64-linux-musl → 0.0.11-aarch64-linux-musl
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 +4 -4
- data/Appraisals +20 -0
- data/CHANGELOG.md +13 -0
- data/README.md +8 -2
- data/gemfiles/plain_ruby.gemfile.lock +3 -77
- data/gemfiles/rails_6.gemfile +1 -0
- data/gemfiles/rails_6.gemfile.lock +3 -47
- data/gemfiles/rails_7_0.gemfile +1 -0
- data/gemfiles/rails_7_0.gemfile.lock +3 -48
- data/gemfiles/rails_7_1.gemfile.lock +2 -46
- data/gemfiles/rails_7_2.gemfile.lock +2 -47
- data/gemfiles/rails_8.gemfile.lock +2 -46
- data/lib/himg/3.2/himg.so +0 -0
- data/lib/himg/3.3/himg.so +0 -0
- data/lib/himg/3.4/himg.so +0 -0
- data/lib/himg/cli.rb +32 -6
- data/lib/himg/version.rb +1 -1
- data/lib/himg.rb +2 -1
- data/logo.svg +57 -0
- data/readme_opengraph_comparison.png +0 -0
- data/website/himg-opengraph.png +0 -0
- data/website/index.html +694 -0
- metadata +7 -17
data/website/index.html
ADDED
@@ -0,0 +1,694 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>Himg - You give it HTML and it gives back an image!</title>
|
7
|
+
<meta name="description" content="Parses HTML/CSS, fetches nested resources, renders an image. No browser, no fuss. Perfect for OpenGraph previews.">
|
8
|
+
|
9
|
+
<!-- OpenGraph tags -->
|
10
|
+
<meta property="og:title" content="Himg - The Hyper Image Generator">
|
11
|
+
<meta property="og:description" content="HTML to image rendering for Ruby developers. No browser required. Rails ready.">
|
12
|
+
<meta property="og:image" content="https://himg.jamedjo.co.uk/himg-opengraph.png">
|
13
|
+
|
14
|
+
<style>
|
15
|
+
* {
|
16
|
+
margin: 0;
|
17
|
+
padding: 0;
|
18
|
+
box-sizing: border-box;
|
19
|
+
}
|
20
|
+
|
21
|
+
body {
|
22
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
23
|
+
background: #FAFAFA;
|
24
|
+
color: #1a1a1a;
|
25
|
+
line-height: 1.6;
|
26
|
+
}
|
27
|
+
|
28
|
+
/* Hero Section */
|
29
|
+
.hero {
|
30
|
+
background: white;
|
31
|
+
color: #1a1a1a;
|
32
|
+
padding: 30px 20px 20px;
|
33
|
+
text-align: center;
|
34
|
+
border-bottom: 1px solid #E5E7EB;
|
35
|
+
}
|
36
|
+
|
37
|
+
.container {
|
38
|
+
max-width: 1200px;
|
39
|
+
margin: 0 auto;
|
40
|
+
padding: 0 20px;
|
41
|
+
}
|
42
|
+
|
43
|
+
.logo {
|
44
|
+
display: inline-block;
|
45
|
+
margin-bottom: 15px;
|
46
|
+
}
|
47
|
+
|
48
|
+
h1 {
|
49
|
+
font-size: 36px;
|
50
|
+
font-weight: 800;
|
51
|
+
margin-bottom: 10px;
|
52
|
+
letter-spacing: -1px;
|
53
|
+
background: linear-gradient(90deg, #FF006E, #8338EC, #3A86FF, #06FFB4);
|
54
|
+
-webkit-background-clip: text;
|
55
|
+
-webkit-text-fill-color: transparent;
|
56
|
+
}
|
57
|
+
|
58
|
+
.tagline {
|
59
|
+
font-size: 18px;
|
60
|
+
font-weight: 400;
|
61
|
+
margin-bottom: 0;
|
62
|
+
color: #666;
|
63
|
+
}
|
64
|
+
|
65
|
+
.cta-buttons {
|
66
|
+
display: flex;
|
67
|
+
gap: 20px;
|
68
|
+
justify-content: center;
|
69
|
+
flex-wrap: wrap;
|
70
|
+
}
|
71
|
+
|
72
|
+
.btn {
|
73
|
+
display: inline-block;
|
74
|
+
padding: 16px 32px;
|
75
|
+
border-radius: 50px;
|
76
|
+
text-decoration: none;
|
77
|
+
font-weight: 600;
|
78
|
+
transition: all 0.3s;
|
79
|
+
}
|
80
|
+
|
81
|
+
.btn-primary {
|
82
|
+
background: linear-gradient(135deg, #FF006E, #8338EC);
|
83
|
+
color: white;
|
84
|
+
box-shadow: 0 4px 20px rgba(131, 56, 236, 0.3);
|
85
|
+
}
|
86
|
+
|
87
|
+
.btn-primary:hover {
|
88
|
+
transform: translateY(-2px);
|
89
|
+
box-shadow: 0 8px 30px rgba(131, 56, 236, 0.4);
|
90
|
+
}
|
91
|
+
|
92
|
+
/* Code Examples */
|
93
|
+
.examples {
|
94
|
+
padding: 40px 20px 50px;
|
95
|
+
background: #F5F7FA;
|
96
|
+
}
|
97
|
+
|
98
|
+
.code-tabs {
|
99
|
+
display: flex;
|
100
|
+
gap: 20px;
|
101
|
+
justify-content: center;
|
102
|
+
margin-bottom: 20px;
|
103
|
+
flex-wrap: nowrap;
|
104
|
+
}
|
105
|
+
|
106
|
+
.tab {
|
107
|
+
padding: 10px 20px;
|
108
|
+
background: white;
|
109
|
+
border-radius: 8px;
|
110
|
+
cursor: pointer;
|
111
|
+
transition: all 0.3s;
|
112
|
+
border: 2px solid transparent;
|
113
|
+
}
|
114
|
+
|
115
|
+
.tab.active {
|
116
|
+
border-color: #FF006E;
|
117
|
+
color: #FF006E;
|
118
|
+
}
|
119
|
+
|
120
|
+
.code-container {
|
121
|
+
max-width: 800px;
|
122
|
+
margin: 0 auto;
|
123
|
+
}
|
124
|
+
|
125
|
+
.code-block {
|
126
|
+
background: #1a1a1a;
|
127
|
+
color: #fff;
|
128
|
+
padding: 30px;
|
129
|
+
border-radius: 16px;
|
130
|
+
overflow-x: auto;
|
131
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
132
|
+
font-size: 14px;
|
133
|
+
line-height: 1.6;
|
134
|
+
position: relative;
|
135
|
+
}
|
136
|
+
|
137
|
+
.code-block::before {
|
138
|
+
content: '';
|
139
|
+
position: absolute;
|
140
|
+
top: 0;
|
141
|
+
left: 0;
|
142
|
+
right: 0;
|
143
|
+
height: 3px;
|
144
|
+
background: linear-gradient(90deg, #FF006E, #8338EC, #3A86FF);
|
145
|
+
}
|
146
|
+
|
147
|
+
.code-content {
|
148
|
+
display: none;
|
149
|
+
}
|
150
|
+
|
151
|
+
.code-content.active {
|
152
|
+
display: block;
|
153
|
+
}
|
154
|
+
|
155
|
+
/* Why Section */
|
156
|
+
.why-section {
|
157
|
+
padding: 50px 20px;
|
158
|
+
background: #F5F7FA;
|
159
|
+
text-align: center;
|
160
|
+
}
|
161
|
+
|
162
|
+
.why-section h2 {
|
163
|
+
font-size: 32px;
|
164
|
+
margin-bottom: 20px;
|
165
|
+
color: #1a1a1a;
|
166
|
+
}
|
167
|
+
|
168
|
+
.why-section p {
|
169
|
+
font-size: 18px;
|
170
|
+
color: #666;
|
171
|
+
max-width: 800px;
|
172
|
+
margin: 0 auto 40px;
|
173
|
+
line-height: 1.6;
|
174
|
+
}
|
175
|
+
|
176
|
+
/* WhatsApp Mockup */
|
177
|
+
.whatsapp-comparison {
|
178
|
+
display: flex;
|
179
|
+
gap: 40px;
|
180
|
+
max-width: 900px;
|
181
|
+
margin: 0 auto 50px;
|
182
|
+
align-items: center;
|
183
|
+
justify-content: center;
|
184
|
+
}
|
185
|
+
|
186
|
+
@media (max-width: 768px) {
|
187
|
+
.whatsapp-comparison {
|
188
|
+
flex-direction: column;
|
189
|
+
gap: 30px;
|
190
|
+
}
|
191
|
+
}
|
192
|
+
|
193
|
+
.whatsapp-phone {
|
194
|
+
flex: 1;
|
195
|
+
max-width: 380px;
|
196
|
+
}
|
197
|
+
|
198
|
+
.phone-frame {
|
199
|
+
background: #E5DDD5;
|
200
|
+
border-radius: 30px;
|
201
|
+
padding: 60px 0 0;
|
202
|
+
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
|
203
|
+
position: relative;
|
204
|
+
overflow: hidden;
|
205
|
+
}
|
206
|
+
|
207
|
+
|
208
|
+
.chat-header {
|
209
|
+
background: #128C7E;
|
210
|
+
color: white;
|
211
|
+
padding: 15px 20px;
|
212
|
+
margin-top: -60px;
|
213
|
+
border-radius: 30px 30px 0 0;
|
214
|
+
font-weight: 600;
|
215
|
+
display: flex;
|
216
|
+
align-items: center;
|
217
|
+
gap: 10px;
|
218
|
+
}
|
219
|
+
|
220
|
+
.chat-body {
|
221
|
+
background: #E5DDD5;
|
222
|
+
padding: 20px;
|
223
|
+
min-height: 400px;
|
224
|
+
}
|
225
|
+
|
226
|
+
.message {
|
227
|
+
background: #E6FFDB;
|
228
|
+
padding: 10px 15px;
|
229
|
+
border-radius: 10px;
|
230
|
+
margin-bottom: 15px;
|
231
|
+
max-width: 80%;
|
232
|
+
margin-left: auto;
|
233
|
+
font-size: 14px;
|
234
|
+
color: #303030;
|
235
|
+
position: relative;
|
236
|
+
}
|
237
|
+
|
238
|
+
.message::after {
|
239
|
+
content: '';
|
240
|
+
position: absolute;
|
241
|
+
right: -8px;
|
242
|
+
top: 10px;
|
243
|
+
width: 0;
|
244
|
+
height: 0;
|
245
|
+
border-left: 8px solid #E6FFDB;
|
246
|
+
border-top: 5px solid transparent;
|
247
|
+
border-bottom: 5px solid transparent;
|
248
|
+
}
|
249
|
+
|
250
|
+
.link-preview {
|
251
|
+
background: #DDF6D7;
|
252
|
+
border-radius: 3px;
|
253
|
+
overflow: hidden;
|
254
|
+
margin-bottom: 8px;
|
255
|
+
}
|
256
|
+
|
257
|
+
.link-preview img {
|
258
|
+
width: 100%;
|
259
|
+
height: 150px;
|
260
|
+
object-fit: cover;
|
261
|
+
display: block;
|
262
|
+
}
|
263
|
+
|
264
|
+
.link-preview-content {
|
265
|
+
padding: 8px 10px;
|
266
|
+
background: #DDF6D7;
|
267
|
+
}
|
268
|
+
|
269
|
+
.link-preview-title {
|
270
|
+
font-weight: 600;
|
271
|
+
margin-bottom: 3px;
|
272
|
+
color: #000;
|
273
|
+
font-size: 13px;
|
274
|
+
text-align: left;
|
275
|
+
}
|
276
|
+
|
277
|
+
.link-preview-desc {
|
278
|
+
font-size: 12px;
|
279
|
+
color: #666;
|
280
|
+
margin-bottom: 3px;
|
281
|
+
line-height: 1.3;
|
282
|
+
text-align: left;
|
283
|
+
}
|
284
|
+
|
285
|
+
.link-preview-url {
|
286
|
+
font-size: 11px;
|
287
|
+
color: #999;
|
288
|
+
text-transform: uppercase;
|
289
|
+
letter-spacing: 0.3px;
|
290
|
+
text-align: left;
|
291
|
+
}
|
292
|
+
|
293
|
+
.plain-link {
|
294
|
+
color: #0066CC;
|
295
|
+
text-decoration: underline;
|
296
|
+
}
|
297
|
+
|
298
|
+
.comparison-label {
|
299
|
+
font-size: 18px;
|
300
|
+
font-weight: 600;
|
301
|
+
color: #666;
|
302
|
+
margin-bottom: 20px;
|
303
|
+
height: 28px;
|
304
|
+
display: flex;
|
305
|
+
align-items: center;
|
306
|
+
justify-content: center;
|
307
|
+
}
|
308
|
+
|
309
|
+
.comparison-label.bad {
|
310
|
+
color: #E74C3C;
|
311
|
+
}
|
312
|
+
|
313
|
+
.comparison-label.good {
|
314
|
+
color: #27AE60;
|
315
|
+
}
|
316
|
+
|
317
|
+
/* Stats */
|
318
|
+
.stats {
|
319
|
+
display: flex;
|
320
|
+
gap: 40px;
|
321
|
+
justify-content: center;
|
322
|
+
flex-wrap: wrap;
|
323
|
+
margin-top: 40px;
|
324
|
+
}
|
325
|
+
|
326
|
+
.stat {
|
327
|
+
text-align: center;
|
328
|
+
}
|
329
|
+
|
330
|
+
.stat-number {
|
331
|
+
font-size: 48px;
|
332
|
+
font-weight: 800;
|
333
|
+
background: linear-gradient(90deg, #FF006E, #8338EC);
|
334
|
+
-webkit-background-clip: text;
|
335
|
+
-webkit-text-fill-color: transparent;
|
336
|
+
line-height: 1;
|
337
|
+
margin-bottom: 10px;
|
338
|
+
}
|
339
|
+
|
340
|
+
.stat-label {
|
341
|
+
font-size: 16px;
|
342
|
+
color: #666;
|
343
|
+
}
|
344
|
+
|
345
|
+
.stat-label a {
|
346
|
+
color: #8338EC;
|
347
|
+
text-decoration: none;
|
348
|
+
}
|
349
|
+
|
350
|
+
.stat-label a:hover {
|
351
|
+
text-decoration: underline;
|
352
|
+
}
|
353
|
+
|
354
|
+
/* How-to section */
|
355
|
+
.how-to {
|
356
|
+
max-width: 800px;
|
357
|
+
margin: 50px auto 0;
|
358
|
+
}
|
359
|
+
|
360
|
+
.how-to h3 {
|
361
|
+
font-size: 24px;
|
362
|
+
margin-bottom: 20px;
|
363
|
+
text-align: center;
|
364
|
+
color: #1a1a1a;
|
365
|
+
}
|
366
|
+
|
367
|
+
/* Features Section */
|
368
|
+
.features {
|
369
|
+
padding: 50px 20px 60px;
|
370
|
+
background: white;
|
371
|
+
}
|
372
|
+
|
373
|
+
.features h2 {
|
374
|
+
text-align: center;
|
375
|
+
font-size: 36px;
|
376
|
+
margin-bottom: 60px;
|
377
|
+
color: #1a1a1a;
|
378
|
+
}
|
379
|
+
|
380
|
+
.feature-grid {
|
381
|
+
display: grid;
|
382
|
+
grid-template-columns: repeat(3, 1fr);
|
383
|
+
gap: 30px;
|
384
|
+
max-width: 1000px;
|
385
|
+
margin: 0 auto;
|
386
|
+
}
|
387
|
+
|
388
|
+
@media (max-width: 900px) {
|
389
|
+
.feature-grid {
|
390
|
+
grid-template-columns: 1fr;
|
391
|
+
max-width: 400px;
|
392
|
+
}
|
393
|
+
}
|
394
|
+
|
395
|
+
.feature {
|
396
|
+
text-align: center;
|
397
|
+
}
|
398
|
+
|
399
|
+
.feature-icon {
|
400
|
+
width: 80px;
|
401
|
+
height: 80px;
|
402
|
+
margin: 0 auto 20px;
|
403
|
+
background: linear-gradient(135deg, #FF006E, #8338EC);
|
404
|
+
border-radius: 20px;
|
405
|
+
display: flex;
|
406
|
+
align-items: center;
|
407
|
+
justify-content: center;
|
408
|
+
font-size: 36px;
|
409
|
+
}
|
410
|
+
|
411
|
+
.feature h3 {
|
412
|
+
font-size: 24px;
|
413
|
+
margin-bottom: 10px;
|
414
|
+
color: #1a1a1a;
|
415
|
+
}
|
416
|
+
|
417
|
+
.feature p {
|
418
|
+
color: #666;
|
419
|
+
font-size: 16px;
|
420
|
+
}
|
421
|
+
|
422
|
+
/* CTA Section */
|
423
|
+
.cta-section {
|
424
|
+
padding: 60px 20px 70px;
|
425
|
+
text-align: center;
|
426
|
+
background: linear-gradient(135deg, #FFF5F5, #F0F4FF);
|
427
|
+
}
|
428
|
+
|
429
|
+
.cta-section h2 {
|
430
|
+
font-size: 42px;
|
431
|
+
margin-bottom: 20px;
|
432
|
+
background: linear-gradient(90deg, #FF006E, #8338EC, #3A86FF);
|
433
|
+
-webkit-background-clip: text;
|
434
|
+
-webkit-text-fill-color: transparent;
|
435
|
+
}
|
436
|
+
|
437
|
+
.cta-section p {
|
438
|
+
font-size: 20px;
|
439
|
+
color: #666;
|
440
|
+
margin-bottom: 30px;
|
441
|
+
}
|
442
|
+
|
443
|
+
/* Footer */
|
444
|
+
footer {
|
445
|
+
padding: 30px 20px;
|
446
|
+
text-align: center;
|
447
|
+
color: #666;
|
448
|
+
background: white;
|
449
|
+
border-top: 1px solid #E5E7EB;
|
450
|
+
}
|
451
|
+
|
452
|
+
/* Responsive */
|
453
|
+
@media (max-width: 768px) {
|
454
|
+
h1 {
|
455
|
+
font-size: 28px;
|
456
|
+
}
|
457
|
+
|
458
|
+
.tagline {
|
459
|
+
font-size: 16px;
|
460
|
+
}
|
461
|
+
|
462
|
+
}
|
463
|
+
|
464
|
+
/* Syntax Highlighting */
|
465
|
+
.keyword { color: #FF006E; }
|
466
|
+
.string { color: #06FFB4; }
|
467
|
+
.function { color: #8338EC; }
|
468
|
+
.comment { color: #666; }
|
469
|
+
</style>
|
470
|
+
</head>
|
471
|
+
<body>
|
472
|
+
<!-- Hero Section -->
|
473
|
+
<section class="hero">
|
474
|
+
<div class="container">
|
475
|
+
<div class="logo">
|
476
|
+
<img src="../logo.svg" alt="Himg" width="180" height="65">
|
477
|
+
</div>
|
478
|
+
<h1>You give it HTML and it gives back an image!</h1>
|
479
|
+
<p class="tagline">Fast, lightweight rendering - no browser, no fuss</p>
|
480
|
+
<p class="tagline">Stand out with dynamic image previews</p>
|
481
|
+
</div>
|
482
|
+
</section>
|
483
|
+
|
484
|
+
<!-- Code Examples -->
|
485
|
+
<section class="examples" id="examples">
|
486
|
+
<div class="container">
|
487
|
+
<div class="code-tabs">
|
488
|
+
<div class="tab active" data-tab="cli" onclick="showTab('cli')">CLI</div>
|
489
|
+
<div class="tab" data-tab="ruby" onclick="showTab('ruby')">Ruby</div>
|
490
|
+
<div class="tab" data-tab="rails" onclick="showTab('rails')">Rails</div>
|
491
|
+
</div>
|
492
|
+
|
493
|
+
<div class="code-container">
|
494
|
+
<div class="code-block">
|
495
|
+
<div class="code-content active" id="cli">
|
496
|
+
<span class="comment"># Install the gem</span><br>
|
497
|
+
<span class="keyword">gem install</span> <span class="string">himg</span><br><br>
|
498
|
+
|
499
|
+
<span class="comment"># Generate from a file</span><br>
|
500
|
+
<span class="keyword">himg screenshot</span> <span class="string">path/to/your.html screenshot.png</span><br><br>
|
501
|
+
|
502
|
+
<span class="comment"># Or from a URL</span><br>
|
503
|
+
<span class="keyword">himg screenshot</span> <span class="string">https://himg.jamedjo.co.uk himg.png</span> --width=1024
|
504
|
+
</div>
|
505
|
+
|
506
|
+
<div class="code-content" id="ruby">
|
507
|
+
<span class="keyword">require</span> <span class="string">'himg'</span><br><br>
|
508
|
+
|
509
|
+
<span class="comment"># Generate an image from HTML</span><br>
|
510
|
+
png = <span class="keyword">Himg</span>.<span class="function">render</span>(<span class="string">"<h1>Hello Image</h1>"</span>)<br>
|
511
|
+
<span class="keyword">File</span>.<span class="function">open</span>("output.png", "wb") { |f| f.write(png) }<br><br>
|
512
|
+
|
513
|
+
<span class="comment"># With options</span><br>
|
514
|
+
png = <span class="keyword">Himg</span>.<span class="function">render</span>(html, <span class="keyword">width:</span> 720, <span class="keyword">height:</span> 405)
|
515
|
+
</div>
|
516
|
+
|
517
|
+
<div class="code-content" id="rails">
|
518
|
+
<span class="comment"># Create app/views/users/<strong style="color: #999;">show.himg.erb</strong></span><br>
|
519
|
+
<div><%= @user.name %></div><br><br>
|
520
|
+
|
521
|
+
<span class="comment"># Optional: Configure in your controller</span><br>
|
522
|
+
<span class="function">himg_config</span>(<span class="keyword">width:</span> 720, <span class="keyword">height:</span> 405, <span class="keyword">disable_fetch:</span> <span class="keyword">false</span>)<br><br>
|
523
|
+
|
524
|
+
<span class="comment"># That's it! Access at users/123.png</span>
|
525
|
+
</div>
|
526
|
+
</div>
|
527
|
+
</div>
|
528
|
+
|
529
|
+
<div class="cta-buttons" style="margin-top: 30px;">
|
530
|
+
<a href="https://github.com/Jamedjo/himg" class="btn btn-primary">View on GitHub</a>
|
531
|
+
</div>
|
532
|
+
</div>
|
533
|
+
</section>
|
534
|
+
|
535
|
+
<!-- Features Section -->
|
536
|
+
<section class="features">
|
537
|
+
<div class="container">
|
538
|
+
<div class="feature-grid">
|
539
|
+
<div class="feature">
|
540
|
+
<div class="feature-icon">⚡</div>
|
541
|
+
<h3>No Browser Required</h3>
|
542
|
+
<p>Pure HTML/CSS parsing with Rust. No 2GB Chrome process, no Bluetooth APIs, no MIDI keyboards.</p>
|
543
|
+
</div>
|
544
|
+
<div class="feature">
|
545
|
+
<div class="feature-icon">🏗️</div>
|
546
|
+
<h3>Zero Infrastructure</h3>
|
547
|
+
<p>No background jobs, no Redis queues, no external services. Storage and caching optional.</p>
|
548
|
+
</div>
|
549
|
+
<div class="feature">
|
550
|
+
<div class="feature-icon">💎</div>
|
551
|
+
<h3>Rails Ready</h3>
|
552
|
+
<p>Drop in a `.himg.erb` template and you're done. Use template variables to create dynamic images for each user, post, or product.</p>
|
553
|
+
</div>
|
554
|
+
</div>
|
555
|
+
</div>
|
556
|
+
</section>
|
557
|
+
|
558
|
+
<!-- Why Section -->
|
559
|
+
<section class="why-section">
|
560
|
+
<div class="container">
|
561
|
+
<h2 style="background: linear-gradient(90deg, #FF006E, #8338EC, #3A86FF); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">Turn boring links into rich visual previews</h2>
|
562
|
+
|
563
|
+
<div class="whatsapp-comparison">
|
564
|
+
<div class="whatsapp-phone">
|
565
|
+
<div class="comparison-label bad">❌ Without OpenGraph</div>
|
566
|
+
<div class="phone-frame">
|
567
|
+
<div class="chat-header">
|
568
|
+
<span>💬</span> Potential customer
|
569
|
+
</div>
|
570
|
+
<div class="chat-body">
|
571
|
+
<div class="message">
|
572
|
+
<span class="plain-link" style="display: block; text-align: left;">https://store.example.com/products/awesome-widget-2024</span>
|
573
|
+
</div>
|
574
|
+
</div>
|
575
|
+
</div>
|
576
|
+
</div>
|
577
|
+
|
578
|
+
<div class="whatsapp-phone">
|
579
|
+
<div class="comparison-label good">✅ With OpenGraph (using himg)</div>
|
580
|
+
<div class="phone-frame">
|
581
|
+
<div class="chat-header">
|
582
|
+
<span>💬</span> Potential customer
|
583
|
+
</div>
|
584
|
+
<div class="chat-body">
|
585
|
+
<div class="message">
|
586
|
+
<div class="link-preview">
|
587
|
+
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 630'%3E%3Cdefs%3E%3ClinearGradient id='purple' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' style='stop-color:%238338EC'/%3E%3Cstop offset='100%25' style='stop-color:%23FF006E'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='1200' height='630' fill='%23f8f8f8'/%3E%3Crect x='0' y='0' width='600' height='630' fill='url(%23purple)'/%3E%3Ctext x='350' y='350' font-family='Arial' font-size='400' text-anchor='middle' dominant-baseline='middle'%3E%F0%9F%8E%81%3C/text%3E%3Ctext x='620' y='150' font-family='Arial' font-size='56' font-weight='bold' fill='%23333'%3EAwesome Widget%3C/text%3E%3Ctext x='620' y='180' font-family='Arial' font-size='24' fill='%23FFD700'%3E%E2%AD%90%E2%AD%90%E2%AD%90%E2%AD%90%E2%AD%90%3C/text%3E%3Ctext x='620' y='235' font-family='Arial' font-size='28' fill='%23666'%3EThe widget that changes everything%3C/text%3E%3Cg%3E%3Crect x='620' y='280' width='100' height='100' rx='12' fill='url(%23purple)'/%3E%3Crect x='620' y='280' width='100' height='100' rx='12' fill='%2306FFB4' opacity='0.5'/%3E%3Ctext x='670' y='333' font-family='Arial' font-size='54' text-anchor='middle' dominant-baseline='middle'%3E%F0%9F%9A%80%3C/text%3E%3C/g%3E%3Cg%3E%3Crect x='735' y='280' width='100' height='100' rx='12' fill='url(%23purple)'/%3E%3Crect x='735' y='280' width='100' height='100' rx='12' fill='%233A86FF' opacity='0.5'/%3E%3Ctext x='785' y='333' font-family='Arial' font-size='54' text-anchor='middle' dominant-baseline='middle'%3E%E2%9A%A1%3C/text%3E%3C/g%3E%3Cg%3E%3Crect x='850' y='280' width='100' height='100' rx='12' fill='url(%23purple)'/%3E%3Crect x='850' y='280' width='100' height='100' rx='12' fill='%23FF006E' opacity='0.5'/%3E%3Ctext x='900' y='333' font-family='Arial' font-size='54' text-anchor='middle' dominant-baseline='middle'%3E%F0%9F%8E%81%3C/text%3E%3C/g%3E%3Ctext x='620' y='470' font-family='Arial' font-size='72' font-weight='bold' fill='%23FF006E'%3E%C2%A37.99%3C/text%3E%3Ctext x='620' y='500' font-family='Arial' font-size='32' fill='%23999' text-decoration='line-through'%3E%C2%A311.99%3C/text%3E%3Crect x='880' y='530' width='200' height='80' rx='40' fill='%23FF006E'/%3E%3Ctext x='980' y='582' font-family='Arial' font-size='36' font-weight='bold' fill='white' text-anchor='middle'%3E30%25 OFF%3C/text%3E%3C/svg%3E" alt="Product preview">
|
588
|
+
<div class="link-preview-content">
|
589
|
+
<div class="link-preview-title">Awesome Widget Pro - Save 30% Today</div>
|
590
|
+
<div class="link-preview-desc">Premium 5-star widget with free shipping. Limited time offer!</div>
|
591
|
+
<div class="link-preview-url">store.example.com</div>
|
592
|
+
</div>
|
593
|
+
</div>
|
594
|
+
<span style="font-size: 12px; color: #0066CC; text-decoration: underline; display: block; text-align: left;">https://store.example.com/products/awesome-widget-2024</span>
|
595
|
+
</div>
|
596
|
+
</div>
|
597
|
+
</div>
|
598
|
+
</div>
|
599
|
+
</div>
|
600
|
+
|
601
|
+
<p style="margin: 40px auto; max-width: 800px; font-size: 18px; color: #666; line-height: 1.6;">Every time someone shares your link, you have one chance to <strong>make an impression</strong>.</p>
|
602
|
+
|
603
|
+
<p style="margin: 20px auto 40px; max-width: 800px; font-size: 18px; color: #666; line-height: 1.6;">Create dynamic previews tailored to each page. Show actual prices, user profiles, product images - whatever makes your content compelling. Not just another generic placeholder.</p>
|
604
|
+
|
605
|
+
<div class="stats">
|
606
|
+
<div class="stat">
|
607
|
+
<div class="stat-number">2.3x</div>
|
608
|
+
<div class="stat-label">more engagement<br><a href="https://www.buzzsumo.com/blog/how-to-massively-boost-your-blog-traffic-with-these-5-awesome-image-stats/" target="_blank" rel="noopener">on Facebook</a></div>
|
609
|
+
</div>
|
610
|
+
<div class="stat">
|
611
|
+
<div class="stat-number">4x</div>
|
612
|
+
<div class="stat-label">more shares<br><a href="https://www.buzzsumo.com/blog/how-to-massively-boost-your-blog-traffic-with-these-5-awesome-image-stats/" target="_blank" rel="noopener">on Twitter</a></div>
|
613
|
+
</div>
|
614
|
+
<div class="stat">
|
615
|
+
<div class="stat-number">8x</div>
|
616
|
+
<div class="stat-label">more clicks<br><a href="https://simpletexting.com/blog/sms-link-previews/" target="_blank" rel="noopener">on SMS</a></div>
|
617
|
+
</div>
|
618
|
+
</div>
|
619
|
+
|
620
|
+
<div class="how-to">
|
621
|
+
<h3>Add rich previews in 2 simple steps</h3>
|
622
|
+
<div class="code-container">
|
623
|
+
<div class="code-block" style="text-align: left;">
|
624
|
+
<span class="comment"># Step 1: Create app/views/users/<strong style="color: #999;">show.himg.erb</strong></span><br>
|
625
|
+
<div><%= <span class="function">@user</span>.<span class="keyword">name</span> %></div><br><br>
|
626
|
+
|
627
|
+
<span class="comment"># Step 2: Add OpenGraph meta tags to your HTML</span><br>
|
628
|
+
<meta property=<span class="string">"og:title"</span> content="<%= <span class="function">@user</span>.<span class="keyword">username</span> %>" /><br>
|
629
|
+
<meta property=<span class="string">"og:description"</span> content="<%= <span class="function">@user</span>.<span class="keyword">tagline</span> %>" /><br>
|
630
|
+
<meta property=<span class="string">"og:image"</span> content="<%= <span class="keyword">user_url</span>(<span class="function">@user</span>.<span class="keyword">username</span>, <span class="function">format: :png</span>) %>" />
|
631
|
+
</div>
|
632
|
+
</div>
|
633
|
+
</div>
|
634
|
+
</div>
|
635
|
+
</section>
|
636
|
+
|
637
|
+
<!-- CTA Section -->
|
638
|
+
<section class="cta-section">
|
639
|
+
<div class="container">
|
640
|
+
<h2>Ready to Generate Images from HTML?</h2>
|
641
|
+
<p>Check out the full documentation and contribute on GitHub</p>
|
642
|
+
<div class="cta-buttons">
|
643
|
+
<a href="https://github.com/Jamedjo/himg" class="btn btn-primary">Get Started on GitHub</a>
|
644
|
+
</div>
|
645
|
+
</div>
|
646
|
+
</section>
|
647
|
+
|
648
|
+
<!-- Footer -->
|
649
|
+
<footer>
|
650
|
+
<div class="container">
|
651
|
+
<p>Himg - The Hyper Image Generator<br>
|
652
|
+
Built with ❤️ by <a href="https://portfolio.jamedjo.co.uk" style="color: #8338EC; text-decoration: none;">@Jamedjo</a></p>
|
653
|
+
</div>
|
654
|
+
</footer>
|
655
|
+
|
656
|
+
<script>
|
657
|
+
function showTab(tabName, updateHistory = true) {
|
658
|
+
// Hide all tabs and content
|
659
|
+
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
|
660
|
+
document.querySelectorAll('.code-content').forEach(content => content.classList.remove('active'));
|
661
|
+
|
662
|
+
// Show selected tab and content
|
663
|
+
const tab = document.querySelector(`.tab[data-tab="${tabName}"]`);
|
664
|
+
if (tab) {
|
665
|
+
tab.classList.add('active');
|
666
|
+
document.getElementById(tabName).classList.add('active');
|
667
|
+
|
668
|
+
// Update URL hash
|
669
|
+
if (updateHistory) {
|
670
|
+
history.pushState(null, null, '#' + tabName);
|
671
|
+
}
|
672
|
+
}
|
673
|
+
}
|
674
|
+
|
675
|
+
// Handle direct links on page load
|
676
|
+
window.addEventListener('DOMContentLoaded', function() {
|
677
|
+
const hash = window.location.hash.substring(1);
|
678
|
+
if (hash && ['cli', 'ruby', 'rails'].includes(hash)) {
|
679
|
+
showTab(hash, false);
|
680
|
+
}
|
681
|
+
});
|
682
|
+
|
683
|
+
// Handle browser back/forward
|
684
|
+
window.addEventListener('popstate', function() {
|
685
|
+
const hash = window.location.hash.substring(1);
|
686
|
+
if (hash && ['cli', 'ruby', 'rails'].includes(hash)) {
|
687
|
+
showTab(hash, false);
|
688
|
+
} else {
|
689
|
+
showTab('cli', false);
|
690
|
+
}
|
691
|
+
});
|
692
|
+
</script>
|
693
|
+
</body>
|
694
|
+
</html>
|