ultimate-jekyll-manager 0.0.117 → 0.0.119
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.
- package/CLAUDE.md +616 -138
- package/README.md +108 -0
- package/TODO.md +1 -1
- package/dist/assets/js/core/auth.js +8 -11
- package/dist/assets/js/core/cookieconsent.js +24 -17
- package/dist/assets/js/core/exit-popup.js +15 -12
- package/dist/assets/js/core/social-sharing.js +8 -4
- package/dist/assets/js/libs/auth/pages.js +14 -13
- package/dist/assets/js/libs/dev.js +192 -129
- package/dist/assets/js/libs/prerendered-icons.js +27 -0
- package/dist/assets/js/pages/account/index.js +4 -3
- package/dist/assets/js/pages/account/sections/api-keys.js +2 -6
- package/dist/assets/js/pages/account/sections/connections.js +101 -59
- package/dist/assets/js/pages/account/sections/delete.js +83 -84
- package/dist/assets/js/pages/account/sections/referrals.js +29 -29
- package/dist/assets/js/pages/account/sections/security.js +51 -71
- package/dist/assets/js/pages/admin/notifications/new/index.js +17 -10
- package/dist/assets/js/pages/blog/index.js +7 -5
- package/dist/assets/js/pages/contact/index.js +6 -33
- package/dist/assets/js/pages/download/index.js +3 -2
- package/dist/assets/js/pages/payment/checkout/index.js +6 -6
- package/dist/assets/js/pages/payment/checkout/modules/processors-main.js +2 -2
- package/dist/assets/js/pages/payment/checkout/modules/session.js +4 -4
- package/dist/assets/js/pages/pricing/index.js +5 -2
- package/dist/assets/themes/classy/css/components/_cards.scss +2 -2
- package/dist/defaults/_.env +6 -0
- package/dist/defaults/_.gitignore +7 -1
- package/dist/defaults/dist/_includes/core/body.html +18 -3
- package/dist/defaults/dist/_includes/core/foot.html +1 -0
- package/dist/defaults/dist/_includes/themes/classy/frontend/sections/nav.html +51 -36
- package/dist/defaults/dist/_layouts/blueprint/admin/notifications/new.html +13 -2
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/about.html +84 -42
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +67 -35
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/index.html +72 -58
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/post.html +46 -29
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html +36 -16
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/download.html +111 -73
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/index.html +111 -56
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html +127 -81
- package/dist/defaults/dist/pages/pricing.md +7 -0
- package/dist/defaults/dist/pages/test/libraries/lazy-loading.html +1 -1
- package/dist/gulp/tasks/defaults.js +210 -1
- package/firebase-debug.log +504 -0
- package/package.json +5 -5
|
@@ -5,8 +5,8 @@ layout: themes/[ site.theme.id ]/frontend/core/base
|
|
|
5
5
|
### PAGE CONFIG ###
|
|
6
6
|
# Hero Section
|
|
7
7
|
hero:
|
|
8
|
-
headline: "The right plans,
|
|
9
|
-
headline_accent: "price"
|
|
8
|
+
headline: "The right plans, "
|
|
9
|
+
headline_accent: "for the right price"
|
|
10
10
|
subheadline: "Simple and affordable pricing. No hidden fees, no surprises."
|
|
11
11
|
|
|
12
12
|
# Pricing Section
|
|
@@ -17,7 +17,7 @@ pricing:
|
|
|
17
17
|
label: "credit" # What to call the unit (e.g., "credit", "user", "GB")
|
|
18
18
|
plans:
|
|
19
19
|
- id: "basic"
|
|
20
|
-
name: Basic
|
|
20
|
+
name: "Basic"
|
|
21
21
|
tagline: "best for getting started"
|
|
22
22
|
url: "/download" # URL for free plan signup
|
|
23
23
|
pricing:
|
|
@@ -26,7 +26,7 @@ pricing:
|
|
|
26
26
|
features:
|
|
27
27
|
- id: "credits"
|
|
28
28
|
name: "Credits"
|
|
29
|
-
value:
|
|
29
|
+
value: 10
|
|
30
30
|
icon: "sparkles"
|
|
31
31
|
- id: "exports"
|
|
32
32
|
name: "Exports"
|
|
@@ -34,7 +34,7 @@ pricing:
|
|
|
34
34
|
icon: "download"
|
|
35
35
|
|
|
36
36
|
- id: "starter"
|
|
37
|
-
name: Starter
|
|
37
|
+
name: "Starter"
|
|
38
38
|
tagline: "best for individuals"
|
|
39
39
|
pricing:
|
|
40
40
|
monthly: 28
|
|
@@ -42,7 +42,7 @@ pricing:
|
|
|
42
42
|
features:
|
|
43
43
|
- id: "credits"
|
|
44
44
|
name: "Credits"
|
|
45
|
-
value:
|
|
45
|
+
value: 100
|
|
46
46
|
icon: "sparkles"
|
|
47
47
|
- id: "exports"
|
|
48
48
|
name: "Exports"
|
|
@@ -58,7 +58,7 @@ pricing:
|
|
|
58
58
|
icon: "headset"
|
|
59
59
|
|
|
60
60
|
- id: "pro"
|
|
61
|
-
name: Pro
|
|
61
|
+
name: "Pro"
|
|
62
62
|
tagline: "best for small businesses"
|
|
63
63
|
popular: true
|
|
64
64
|
pricing:
|
|
@@ -67,7 +67,7 @@ pricing:
|
|
|
67
67
|
features:
|
|
68
68
|
- id: "credits"
|
|
69
69
|
name: "Credits"
|
|
70
|
-
value:
|
|
70
|
+
value: 200
|
|
71
71
|
icon: "sparkles"
|
|
72
72
|
- id: "exports"
|
|
73
73
|
name: "Exports"
|
|
@@ -75,7 +75,7 @@ pricing:
|
|
|
75
75
|
icon: "download"
|
|
76
76
|
|
|
77
77
|
- id: "max"
|
|
78
|
-
name: Max
|
|
78
|
+
name: "Max"
|
|
79
79
|
tagline: "best for growing businesses"
|
|
80
80
|
pricing:
|
|
81
81
|
monthly: 100
|
|
@@ -83,7 +83,7 @@ pricing:
|
|
|
83
83
|
features:
|
|
84
84
|
- id: "credits"
|
|
85
85
|
name: "Credits"
|
|
86
|
-
value:
|
|
86
|
+
value: 500
|
|
87
87
|
icon: "sparkles"
|
|
88
88
|
- id: "exports"
|
|
89
89
|
name: "Exports"
|
|
@@ -100,9 +100,11 @@ pricing:
|
|
|
100
100
|
|
|
101
101
|
# Feature Comparison Section
|
|
102
102
|
feature_comparison:
|
|
103
|
-
superheadline:
|
|
103
|
+
superheadline:
|
|
104
|
+
icon: "sparkles"
|
|
105
|
+
text: "Features"
|
|
104
106
|
headline: "Compare all"
|
|
105
|
-
headline_accent: "
|
|
107
|
+
headline_accent: "plans"
|
|
106
108
|
subheadline: "See exactly what's included in each plan"
|
|
107
109
|
|
|
108
110
|
# Social Proof Section
|
|
@@ -125,19 +127,24 @@ social_proof:
|
|
|
125
127
|
|
|
126
128
|
# CTA Section
|
|
127
129
|
cta:
|
|
128
|
-
superheadline:
|
|
130
|
+
superheadline:
|
|
131
|
+
icon: "comments"
|
|
132
|
+
text: "Support"
|
|
129
133
|
headline: "Need help finding the right plan for your"
|
|
130
134
|
headline_accent: "needs?"
|
|
131
135
|
subheadline: "Talk to our support team 24/7"
|
|
132
136
|
button:
|
|
133
137
|
text: "Talk to Us"
|
|
134
|
-
icon: "
|
|
138
|
+
icon: "headset"
|
|
135
139
|
href: "/contact"
|
|
136
140
|
|
|
137
141
|
# Testimonials Section
|
|
138
142
|
testimonials:
|
|
139
|
-
superheadline:
|
|
140
|
-
|
|
143
|
+
superheadline:
|
|
144
|
+
icon: "megaphone"
|
|
145
|
+
text: "Testimonials"
|
|
146
|
+
headline: "People {% uj_icon \"heart\", \"text-danger\" %} "
|
|
147
|
+
headline_accent: "{{ site.brand.name }}"
|
|
141
148
|
subheadline: "Hear from real people who have transformed their business with us"
|
|
142
149
|
items:
|
|
143
150
|
- quote: "This platform transformed my business. The support is incredible!"
|
|
@@ -158,10 +165,12 @@ testimonials:
|
|
|
158
165
|
|
|
159
166
|
# FAQs Section
|
|
160
167
|
faqs:
|
|
161
|
-
superheadline:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
168
|
+
superheadline:
|
|
169
|
+
icon: "messages-question"
|
|
170
|
+
text: "FAQs"
|
|
171
|
+
headline: "Frequently asked"
|
|
172
|
+
headline_accent: "questions"
|
|
173
|
+
subheadline: "Everything you need to know about {{ site.brand.name }} billing & subscriptions."
|
|
165
174
|
items:
|
|
166
175
|
# - question: "Is {{ site.brand.name }} AI included with my existing {{ site.brand.name }} Studio plan?"
|
|
167
176
|
# answer: "No, {{ site.brand.name }} AI and {{ site.brand.name }} Studio function as separate products with individual pricing plans. If you were an active lifetime deal holder of {{ site.brand.name }} Studio and had purchased it before August 15, 2023, you'll qualify for free {{ site.brand.name }} AI credits. These will be phased in gradually, and you'll be notified via email when they become available. As for other {{ site.brand.name }} Studio subscribers, we're actively exploring ways to offer some benefits to help you get started on {{ site.brand.name }} AI. Stay tuned for more announcements.<br><br>PS: This is available exclusively to users who have purchased their {{ site.brand.name }} accounts from {{ site.brand.name }}."
|
|
@@ -210,7 +219,7 @@ faqs:
|
|
|
210
219
|
{{ page.resolved.hero.headline }} <span class="text-gradient-rainbow">{{ page.resolved.hero.headline_accent }}</span>
|
|
211
220
|
</h1>
|
|
212
221
|
{% iftruthy page.resolved.hero.subheadline %}
|
|
213
|
-
<p class="fs-5 text-muted
|
|
222
|
+
<p class="fs-5 text-muted">
|
|
214
223
|
{{ page.resolved.hero.subheadline }}
|
|
215
224
|
</p>
|
|
216
225
|
{% endiftruthy %}
|
|
@@ -247,6 +256,31 @@ faqs:
|
|
|
247
256
|
|
|
248
257
|
{% assign plan_count = page.resolved.pricing.plans | size %}
|
|
249
258
|
|
|
259
|
+
<!-- Automatically detect common features across ALL plans -->
|
|
260
|
+
{% assign common_feature_ids = "" | split: "," %}
|
|
261
|
+
{% if plan_count > 0 %}
|
|
262
|
+
{% assign first_plan = page.resolved.pricing.plans[0] %}
|
|
263
|
+
{% for first_feature in first_plan.features %}
|
|
264
|
+
{% assign is_common = true %}
|
|
265
|
+
{% for check_plan in page.resolved.pricing.plans %}
|
|
266
|
+
{% assign found_in_plan = false %}
|
|
267
|
+
{% for check_feature in check_plan.features %}
|
|
268
|
+
{% if check_feature.id == first_feature.id %}
|
|
269
|
+
{% assign found_in_plan = true %}
|
|
270
|
+
{% break %}
|
|
271
|
+
{% endif %}
|
|
272
|
+
{% endfor %}
|
|
273
|
+
{% unless found_in_plan %}
|
|
274
|
+
{% assign is_common = false %}
|
|
275
|
+
{% break %}
|
|
276
|
+
{% endunless %}
|
|
277
|
+
{% endfor %}
|
|
278
|
+
{% if is_common %}
|
|
279
|
+
{% assign common_feature_ids = common_feature_ids | push: first_feature.id %}
|
|
280
|
+
{% endif %}
|
|
281
|
+
{% endfor %}
|
|
282
|
+
{% endif %}
|
|
283
|
+
|
|
250
284
|
<!-- Determine column classes based on number of plans -->
|
|
251
285
|
{% if plan_count == 1 %}
|
|
252
286
|
{% assign col_classes = "col-12 col-md-8 col-lg-6 col-xl-5" %}
|
|
@@ -287,10 +321,10 @@ faqs:
|
|
|
287
321
|
|
|
288
322
|
<!-- Price -->
|
|
289
323
|
{% if plan.pricing.monthly == 0 %}
|
|
290
|
-
<p class="display-
|
|
324
|
+
<p class="display-6 fw-bold mb-0">Free</p>
|
|
291
325
|
{% else %}
|
|
292
326
|
<p class="mb-0">
|
|
293
|
-
<span class="display-
|
|
327
|
+
<span class="display-6 fw-bold">
|
|
294
328
|
<span class="fs-3">$</span><span class="amount" data-monthly="{{ plan.pricing.monthly }}" data-annually="{{ plan.pricing.annually | divided_by: 12 | round }}">{{ plan.pricing.annually | divided_by: 12 | round }}</span>
|
|
295
329
|
</span>
|
|
296
330
|
<small class="text-muted">/month</small>
|
|
@@ -343,28 +377,27 @@ faqs:
|
|
|
343
377
|
{% if plan.pricing.monthly == 0 %}
|
|
344
378
|
<span>No credit card required</span>
|
|
345
379
|
{% else %}
|
|
346
|
-
<span class="billing-info" data-monthly="Billed ${{ plan.pricing.monthly }} monthly" data-annually="Billed ${{ plan.pricing.annually }} annually">
|
|
347
|
-
Billed ${{ plan.pricing.annually }} annually
|
|
380
|
+
<span class="billing-info" data-monthly="Billed ${{ plan.pricing.monthly | uj_commaify }} monthly" data-annually="Billed ${{ plan.pricing.annually | uj_commaify }} annually">
|
|
381
|
+
Billed ${{ plan.pricing.annually | uj_commaify }} annually
|
|
348
382
|
</span>
|
|
349
383
|
{% endif %}
|
|
350
384
|
</p>
|
|
351
385
|
|
|
352
|
-
<hr>
|
|
386
|
+
<hr class="border-1">
|
|
353
387
|
|
|
354
388
|
<!-- Features -->
|
|
355
389
|
<div>
|
|
356
390
|
<!-- Common features that vary by plan -->
|
|
357
391
|
<ul class="list-unstyled mb-3">
|
|
358
|
-
{% assign common_features = "credits,exports,video_mins" | split: "," %}
|
|
359
392
|
{% for feature in plan.features %}
|
|
360
|
-
{% if
|
|
393
|
+
{% if common_feature_ids contains feature.id %}
|
|
361
394
|
<li class="d-flex align-items-start mb-2">
|
|
362
395
|
<span class="me-3">{% uj_icon feature.icon, "fa-md" %}</span>
|
|
363
396
|
<span>
|
|
364
397
|
{% if feature.value == "Unlimited" %}
|
|
365
398
|
Unlimited {{ feature.name }}
|
|
366
399
|
{% else %}
|
|
367
|
-
{{ feature.value }} {{ feature.name }}
|
|
400
|
+
{{ feature.value | uj_commaify }} {{ feature.name }}
|
|
368
401
|
{% endif %}
|
|
369
402
|
</span>
|
|
370
403
|
</li>
|
|
@@ -377,7 +410,7 @@ faqs:
|
|
|
377
410
|
<!-- Check if there are additional features -->
|
|
378
411
|
{% assign has_additional = false %}
|
|
379
412
|
{% for feature in plan.features %}
|
|
380
|
-
{% unless
|
|
413
|
+
{% unless common_feature_ids contains feature.id %}
|
|
381
414
|
{% assign has_additional = true %}
|
|
382
415
|
{% break %}
|
|
383
416
|
{% endunless %}
|
|
@@ -401,7 +434,7 @@ faqs:
|
|
|
401
434
|
{% if has_additional %}
|
|
402
435
|
<ul class="list-unstyled mb-0">
|
|
403
436
|
{% for feature in plan.features %}
|
|
404
|
-
{% unless
|
|
437
|
+
{% unless common_feature_ids contains feature.id %}
|
|
405
438
|
<li class="d-flex align-items-start mb-2 {% if forloop.last %}mb-0{% endif %}">
|
|
406
439
|
<span class="me-3">{% uj_icon feature.icon, "fa-md" %}</span>
|
|
407
440
|
<span>
|
|
@@ -410,7 +443,7 @@ faqs:
|
|
|
410
443
|
{% elsif feature.value == "Included" or feature.value == "Available" or feature.value == "Full" %}
|
|
411
444
|
{{ feature.name }}
|
|
412
445
|
{% else %}
|
|
413
|
-
{{ feature.value }} {{ feature.name }}
|
|
446
|
+
{{ feature.value | uj_commaify }} {{ feature.name }}
|
|
414
447
|
{% endif %}
|
|
415
448
|
</span>
|
|
416
449
|
</li>
|
|
@@ -454,8 +487,13 @@ faqs:
|
|
|
454
487
|
<section>
|
|
455
488
|
<div class="container">
|
|
456
489
|
<div class="text-center mb-5" data-lazy="@class animation-slide-up">
|
|
457
|
-
{% iftruthy page.resolved.feature_comparison.superheadline %}
|
|
458
|
-
<span class="badge bg-body-tertiary border-gradient-rainbow border-1 text-body p-2 mb-1 fw-semibold small">
|
|
490
|
+
{% iftruthy page.resolved.feature_comparison.superheadline.text %}
|
|
491
|
+
<span class="badge bg-body-tertiary border-gradient-rainbow border-1 text-body p-2 mb-1 fw-semibold small">
|
|
492
|
+
{% iftruthy page.resolved.feature_comparison.superheadline.icon %}
|
|
493
|
+
{% uj_icon page.resolved.feature_comparison.superheadline.icon, "me-1" %}
|
|
494
|
+
{% endiftruthy %}
|
|
495
|
+
{{ page.resolved.feature_comparison.superheadline.text }}
|
|
496
|
+
</span>
|
|
459
497
|
{% endiftruthy %}
|
|
460
498
|
<h2 class="h2 mb-2">
|
|
461
499
|
{{ page.resolved.feature_comparison.headline }}
|
|
@@ -597,53 +635,46 @@ faqs:
|
|
|
597
635
|
|
|
598
636
|
<!-- CTA Section -->
|
|
599
637
|
<section>
|
|
600
|
-
<div class="container"
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
<div class="
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
638
|
+
<div class="container">
|
|
639
|
+
<div class="card border-0 bg-gradient bg-primary text-white rounded-4 p-4 p-md-5 text-center position-relative overflow-hidden" data-lazy="@class animation-slide-up">
|
|
640
|
+
<!-- Decorative icon - top right -->
|
|
641
|
+
<div class="position-absolute top-0 end-0 opacity-25" style="font-size:10rem;transform:rotate(15deg)translate(30%,-30%)">
|
|
642
|
+
{% uj_icon "messages-question", "text-white" %}
|
|
643
|
+
</div>
|
|
644
|
+
|
|
645
|
+
<!-- Decorative icon - bottom left -->
|
|
646
|
+
<div class="position-absolute bottom-0 start-0 opacity-25" style="font-size:8rem;transform:rotate(-20deg)translate(-30%,30%)">
|
|
647
|
+
{% uj_icon "shield-check", "text-white" %}
|
|
648
|
+
</div>
|
|
649
|
+
|
|
650
|
+
<div class="position-relative">
|
|
651
|
+
{% iftruthy page.resolved.cta.superheadline.text %}
|
|
652
|
+
<span class="badge bg-white bg-opacity-25 text-light p-2 mb-3 fw-semibold small">
|
|
653
|
+
{% iftruthy page.resolved.cta.superheadline.icon %}
|
|
654
|
+
{% uj_icon page.resolved.cta.superheadline.icon, "me-1" %}
|
|
612
655
|
{% endiftruthy %}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
656
|
+
{{ page.resolved.cta.superheadline.text }}
|
|
657
|
+
</span>
|
|
658
|
+
{% endiftruthy %}
|
|
659
|
+
|
|
660
|
+
<h2 class="h3 fw-bold mb-3">
|
|
661
|
+
{{ page.resolved.cta.headline }}
|
|
662
|
+
{% iftruthy page.resolved.cta.headline_accent %}
|
|
663
|
+
<span class="text-accent">{{ page.resolved.cta.headline_accent }}</span>
|
|
616
664
|
{% endiftruthy %}
|
|
617
|
-
|
|
665
|
+
</h2>
|
|
666
|
+
|
|
667
|
+
{% iftruthy page.resolved.cta.subheadline %}
|
|
668
|
+
<p class="lead mb-4">{{ page.resolved.cta.subheadline }}</p>
|
|
669
|
+
{% endiftruthy %}
|
|
670
|
+
|
|
671
|
+
<div class="d-flex flex-column flex-sm-row gap-3 justify-content-center">
|
|
672
|
+
<a href="{{ page.resolved.cta.button.href }}" class="btn btn-light btn-lg px-4">
|
|
618
673
|
{% uj_icon page.resolved.cta.button.icon, "me-2" %}
|
|
619
674
|
{{ page.resolved.cta.button.text }}
|
|
620
675
|
</a>
|
|
621
676
|
</div>
|
|
622
|
-
<div class="col-lg-5 d-none d-lg-block position-relative">
|
|
623
|
-
<div class="text-end">
|
|
624
|
-
<div class="d-inline-block position-relative">
|
|
625
|
-
<!-- <div class="rounded-circle bg-white bg-opacity-25 p-5" style="backdrop-filter: blur(10px);">
|
|
626
|
-
<img src="https://via.placeholder.com/300x300/ffffff00/ffffff?text=👩💻"
|
|
627
|
-
alt="Support"
|
|
628
|
-
class="img-fluid"
|
|
629
|
-
style="filter: drop-shadow(0 10px 30px rgba(0,0,0,0.3));">
|
|
630
|
-
</div> -->
|
|
631
|
-
<!-- <div class="position-absolute bottom-0 end-0 bg-white rounded-circle p-3 shadow-lg">
|
|
632
|
-
</div> -->
|
|
633
|
-
<div class="position-absolute bottom-0 end-0 p-3">
|
|
634
|
-
{% uj_icon "comment-dots", "text-light display-1" %}
|
|
635
|
-
</div>
|
|
636
|
-
</div>
|
|
637
|
-
</div>
|
|
638
|
-
</div>
|
|
639
677
|
</div>
|
|
640
|
-
<!-- Decorative elements -->
|
|
641
|
-
<!-- <div class="position-absolute top-0 end-0 opacity-25">
|
|
642
|
-
<div class="rounded-circle" style="width: 400px; height: 400px; background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%); transform: translate(50%, -50%);"></div>
|
|
643
|
-
</div>
|
|
644
|
-
<div class="position-absolute bottom-0 start-0 opacity-25">
|
|
645
|
-
<div class="rounded-circle" style="width: 300px; height: 300px; background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%); transform: translate(-50%, 50%);"></div>
|
|
646
|
-
</div> -->
|
|
647
678
|
</div>
|
|
648
679
|
</div>
|
|
649
680
|
</section>
|
|
@@ -652,10 +683,20 @@ faqs:
|
|
|
652
683
|
<section>
|
|
653
684
|
<div class="container">
|
|
654
685
|
<div class="text-center mb-5" data-lazy="@class animation-slide-up">
|
|
655
|
-
{% iftruthy page.resolved.testimonials.superheadline %}
|
|
656
|
-
<span class="badge bg-body-tertiary border-gradient-rainbow border-1 text-body p-2 mb-1 fw-semibold small">
|
|
686
|
+
{% iftruthy page.resolved.testimonials.superheadline.text %}
|
|
687
|
+
<span class="badge bg-body-tertiary border-gradient-rainbow border-1 text-body p-2 mb-1 fw-semibold small">
|
|
688
|
+
{% iftruthy page.resolved.testimonials.superheadline.icon %}
|
|
689
|
+
{% uj_icon page.resolved.testimonials.superheadline.icon, "me-1" %}
|
|
690
|
+
{% endiftruthy %}
|
|
691
|
+
{{ page.resolved.testimonials.superheadline.text }}
|
|
692
|
+
</span>
|
|
657
693
|
{% endiftruthy %}
|
|
658
|
-
<h2 class="h2 mb-2">
|
|
694
|
+
<h2 class="h2 mb-2">
|
|
695
|
+
{{ page.resolved.testimonials.headline }}
|
|
696
|
+
{% iftruthy page.resolved.testimonials.headline_accent %}
|
|
697
|
+
<span class="text-accent">{{ page.resolved.testimonials.headline_accent }}</span>
|
|
698
|
+
{% endiftruthy %}
|
|
699
|
+
</h2>
|
|
659
700
|
{% iftruthy page.resolved.testimonials.subheadline %}
|
|
660
701
|
<p class="fs-5 text-muted">{{ page.resolved.testimonials.subheadline }}</p>
|
|
661
702
|
{% endiftruthy %}
|
|
@@ -711,9 +752,14 @@ faqs:
|
|
|
711
752
|
<div class="container">
|
|
712
753
|
<div class="row justify-content-center">
|
|
713
754
|
<div class="col-lg-8">
|
|
714
|
-
<div class="text-center mb-
|
|
715
|
-
{% iftruthy page.resolved.faqs.superheadline %}
|
|
716
|
-
<span class="badge bg-body-tertiary border-gradient-rainbow border-1 text-body p-2 mb-1 fw-semibold small">
|
|
755
|
+
<div class="text-center mb-5" data-lazy="@class animation-slide-up">
|
|
756
|
+
{% iftruthy page.resolved.faqs.superheadline.text %}
|
|
757
|
+
<span class="badge bg-body-tertiary border-gradient-rainbow border-1 text-body p-2 mb-1 fw-semibold small">
|
|
758
|
+
{% iftruthy page.resolved.faqs.superheadline.icon %}
|
|
759
|
+
{% uj_icon page.resolved.faqs.superheadline.icon, "me-1" %}
|
|
760
|
+
{% endiftruthy %}
|
|
761
|
+
{{ page.resolved.faqs.superheadline.text }}
|
|
762
|
+
</span>
|
|
717
763
|
{% endiftruthy %}
|
|
718
764
|
<h2 class="h2 mb-2">
|
|
719
765
|
{{ page.resolved.faqs.headline }}
|
|
@@ -722,7 +768,7 @@ faqs:
|
|
|
722
768
|
{% endiftruthy %}
|
|
723
769
|
</h2>
|
|
724
770
|
{% iftruthy page.resolved.faqs.subheadline %}
|
|
725
|
-
<p class="fs-5 text-muted
|
|
771
|
+
<p class="fs-5 text-muted">{{ page.resolved.faqs.subheadline }}</p>
|
|
726
772
|
{% endiftruthy %}
|
|
727
773
|
</div>
|
|
728
774
|
|
|
@@ -42,7 +42,7 @@ meta:
|
|
|
42
42
|
<!-- Spacer to push content below fold -->
|
|
43
43
|
<div class="d-flex align-items-center justify-content-center text-muted vh-100">
|
|
44
44
|
<div class="text-center">
|
|
45
|
-
|
|
45
|
+
{% uj_icon "arrow-down", "fa-3x mb-3" %}
|
|
46
46
|
<p class="h4">Scroll down to trigger lazy loading...</p>
|
|
47
47
|
</div>
|
|
48
48
|
</div>
|
|
@@ -60,8 +60,14 @@ const FILE_MAP = {
|
|
|
60
60
|
// 'dist/pages/**/*': {
|
|
61
61
|
// path: (file) => file.source.replace('dist/pages', 'dist'),
|
|
62
62
|
// },
|
|
63
|
+
// Files to rename and merge
|
|
63
64
|
'_.gitignore': {
|
|
64
65
|
name: (file) => file.name.replace('_.gitignore', '.gitignore'),
|
|
66
|
+
mergeLines: true, // Merge line-by-line instead of overwriting
|
|
67
|
+
},
|
|
68
|
+
'_.env': {
|
|
69
|
+
name: (file) => file.name.replace('_.env', '.env'),
|
|
70
|
+
mergeLines: true, // Merge line-by-line instead of overwriting
|
|
65
71
|
},
|
|
66
72
|
|
|
67
73
|
// Config file with smart merging
|
|
@@ -110,6 +116,188 @@ const delay = 250;
|
|
|
110
116
|
// Index
|
|
111
117
|
let index = -1;
|
|
112
118
|
|
|
119
|
+
// Helper function to merge line-based files (.gitignore, .env)
|
|
120
|
+
function mergeLineBasedFiles(existingContent, newContent, fileName) {
|
|
121
|
+
// Parse existing content into lines
|
|
122
|
+
const existingLines = existingContent.split('\n');
|
|
123
|
+
const newLines = newContent.split('\n');
|
|
124
|
+
|
|
125
|
+
// For .env files, we track keys; for .gitignore, we track the full line
|
|
126
|
+
const isEnvFile = fileName === '.env';
|
|
127
|
+
|
|
128
|
+
// Markers for separating default values from user custom values
|
|
129
|
+
const DEFAULT_SECTION_MARKER = '# ========== Default Values ==========';
|
|
130
|
+
const CUSTOM_SECTION_MARKER = '# ========== Custom Values ==========';
|
|
131
|
+
|
|
132
|
+
// Parse existing file into default section and custom section
|
|
133
|
+
let defaultSection = [];
|
|
134
|
+
let customSection = [];
|
|
135
|
+
let inCustomSection = false;
|
|
136
|
+
let inDefaultSection = false;
|
|
137
|
+
|
|
138
|
+
const existingDefaultKeys = new Set();
|
|
139
|
+
const existingCustomKeys = new Set();
|
|
140
|
+
|
|
141
|
+
for (const line of existingLines) {
|
|
142
|
+
const trimmed = line.trim();
|
|
143
|
+
|
|
144
|
+
// Check for section markers
|
|
145
|
+
if (trimmed === DEFAULT_SECTION_MARKER) {
|
|
146
|
+
inDefaultSection = true;
|
|
147
|
+
inCustomSection = false;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (trimmed === CUSTOM_SECTION_MARKER) {
|
|
151
|
+
inCustomSection = true;
|
|
152
|
+
inDefaultSection = false;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Add to appropriate section
|
|
157
|
+
if (inCustomSection) {
|
|
158
|
+
customSection.push(line);
|
|
159
|
+
// Track custom keys
|
|
160
|
+
if (isEnvFile && trimmed && !trimmed.startsWith('#')) {
|
|
161
|
+
const key = trimmed.split('=')[0].trim();
|
|
162
|
+
if (key) {
|
|
163
|
+
existingCustomKeys.add(key);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} else if (inDefaultSection) {
|
|
167
|
+
defaultSection.push(line);
|
|
168
|
+
// Track default keys
|
|
169
|
+
if (isEnvFile && trimmed && !trimmed.startsWith('#')) {
|
|
170
|
+
const key = trimmed.split('=')[0].trim();
|
|
171
|
+
if (key) {
|
|
172
|
+
existingDefaultKeys.add(key);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Parse new content to build complete default section in order
|
|
179
|
+
const newDefaultSection = [];
|
|
180
|
+
const newDefaultKeys = new Set();
|
|
181
|
+
|
|
182
|
+
let inNewDefaultSection = false;
|
|
183
|
+
let inNewCustomSection = false;
|
|
184
|
+
|
|
185
|
+
for (const line of newLines) {
|
|
186
|
+
const trimmed = line.trim();
|
|
187
|
+
|
|
188
|
+
// Check for section markers
|
|
189
|
+
if (trimmed === DEFAULT_SECTION_MARKER) {
|
|
190
|
+
inNewDefaultSection = true;
|
|
191
|
+
inNewCustomSection = false;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (trimmed === CUSTOM_SECTION_MARKER) {
|
|
195
|
+
inNewCustomSection = true;
|
|
196
|
+
inNewDefaultSection = false;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Only process default section from new file
|
|
201
|
+
if (inNewDefaultSection) {
|
|
202
|
+
// For env files, check if key exists
|
|
203
|
+
if (isEnvFile && trimmed && !trimmed.startsWith('#')) {
|
|
204
|
+
const key = trimmed.split('=')[0].trim();
|
|
205
|
+
if (key) {
|
|
206
|
+
newDefaultKeys.add(key);
|
|
207
|
+
// If key exists in user's file (either section), skip the default value
|
|
208
|
+
if (!existingDefaultKeys.has(key) && !existingCustomKeys.has(key)) {
|
|
209
|
+
// New key - add it
|
|
210
|
+
newDefaultSection.push(line);
|
|
211
|
+
} else {
|
|
212
|
+
// Key exists - we'll add user's value later in order
|
|
213
|
+
newDefaultSection.push(null); // Placeholder to maintain order
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
newDefaultSection.push(line);
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
// Comments and empty lines
|
|
220
|
+
newDefaultSection.push(line);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Now merge user's existing default values in the correct order
|
|
226
|
+
const mergedDefaultSection = [];
|
|
227
|
+
let defaultSectionIndex = 0;
|
|
228
|
+
|
|
229
|
+
for (const line of newDefaultSection) {
|
|
230
|
+
if (line === null) {
|
|
231
|
+
// Placeholder - insert corresponding user value
|
|
232
|
+
// Find the next user value
|
|
233
|
+
while (defaultSectionIndex < defaultSection.length) {
|
|
234
|
+
const userLine = defaultSection[defaultSectionIndex++];
|
|
235
|
+
const trimmed = userLine.trim();
|
|
236
|
+
if (trimmed && !trimmed.startsWith('#')) {
|
|
237
|
+
mergedDefaultSection.push(userLine);
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
mergedDefaultSection.push(line);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Find any user-added lines in default section that aren't in new defaults
|
|
247
|
+
// These should be moved to custom section
|
|
248
|
+
const userAddedToDefaults = [];
|
|
249
|
+
|
|
250
|
+
for (const line of defaultSection) {
|
|
251
|
+
const trimmed = line.trim();
|
|
252
|
+
|
|
253
|
+
// Skip empty lines and comments
|
|
254
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (isEnvFile) {
|
|
259
|
+
// For .env, check if key exists in new defaults
|
|
260
|
+
const key = trimmed.split('=')[0].trim();
|
|
261
|
+
if (key && !newDefaultKeys.has(key) && !existingCustomKeys.has(key)) {
|
|
262
|
+
// User added this key to defaults section - move to custom
|
|
263
|
+
userAddedToDefaults.push(line);
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
// For .gitignore, check if line exists in new defaults
|
|
267
|
+
// We need to check if this exact line appears in the new default section
|
|
268
|
+
const lineExistsInNewDefaults = newLines.some(newLine => {
|
|
269
|
+
return newLine.trim() === trimmed;
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
if (!lineExistsInNewDefaults) {
|
|
273
|
+
// User added this line to defaults section - move to custom
|
|
274
|
+
userAddedToDefaults.push(line);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Build final result
|
|
280
|
+
const result = [];
|
|
281
|
+
|
|
282
|
+
// Add default section
|
|
283
|
+
result.push(DEFAULT_SECTION_MARKER);
|
|
284
|
+
result.push(...mergedDefaultSection);
|
|
285
|
+
|
|
286
|
+
// Add custom section
|
|
287
|
+
result.push('');
|
|
288
|
+
result.push(CUSTOM_SECTION_MARKER);
|
|
289
|
+
|
|
290
|
+
// First add any user lines that were in default section (moved to custom)
|
|
291
|
+
if (userAddedToDefaults.length > 0) {
|
|
292
|
+
result.push(...userAddedToDefaults);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Then add existing custom section
|
|
296
|
+
result.push(...customSection);
|
|
297
|
+
|
|
298
|
+
return result.join('\n');
|
|
299
|
+
}
|
|
300
|
+
|
|
113
301
|
// Helper function to merge configs intelligently
|
|
114
302
|
function mergeConfigs(existingConfig, newConfig) {
|
|
115
303
|
const merged = { ...newConfig };
|
|
@@ -256,8 +444,27 @@ function customTransform() {
|
|
|
256
444
|
}
|
|
257
445
|
}
|
|
258
446
|
|
|
447
|
+
// Handle line-based merging (.gitignore, .env)
|
|
448
|
+
if (options.mergeLines && exists && !isBinaryFile) {
|
|
449
|
+
try {
|
|
450
|
+
const existingContent = jetpack.read(fullOutputPath);
|
|
451
|
+
const newContent = file.contents.toString();
|
|
452
|
+
|
|
453
|
+
// Merge line-by-line, passing the filename to handle .env differently
|
|
454
|
+
const mergedContent = mergeLineBasedFiles(existingContent, newContent, item.name);
|
|
455
|
+
|
|
456
|
+
// Update file contents
|
|
457
|
+
file.contents = Buffer.from(mergedContent);
|
|
458
|
+
|
|
459
|
+
logger.log(`Merged line-based file: ${relativePath}`);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
logger.error(`Error merging line-based file ${relativePath}:`, error);
|
|
462
|
+
// Fall through to normal processing if merge fails
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
259
466
|
// Skip if instructed
|
|
260
|
-
if (options.skip || (!options.overwrite && exists && !options.merge)) {
|
|
467
|
+
if (options.skip || (!options.overwrite && exists && !options.merge && !options.mergeLines)) {
|
|
261
468
|
logger.log(`Skipping file: ${relativePath}`);
|
|
262
469
|
return callback();
|
|
263
470
|
}
|
|
@@ -325,6 +532,8 @@ function getFileOptions(filePath) {
|
|
|
325
532
|
path: null,
|
|
326
533
|
template: null,
|
|
327
534
|
skip: false,
|
|
535
|
+
merge: false,
|
|
536
|
+
mergeLines: false,
|
|
328
537
|
rule: null,
|
|
329
538
|
};
|
|
330
539
|
|