sexyjekyll-theme 1.0.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.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +82 -0
  3. data/LICENSE +21 -0
  4. data/README.de.md +276 -0
  5. data/README.es.md +276 -0
  6. data/README.fr.md +276 -0
  7. data/README.it.md +219 -0
  8. data/README.md +276 -0
  9. data/_includes/critical-css.html +4 -0
  10. data/_includes/footer.html +81 -0
  11. data/_includes/head.html +88 -0
  12. data/_includes/nav.html +21 -0
  13. data/_includes/related-posts.html +75 -0
  14. data/_includes/social-icon.html +98 -0
  15. data/_includes/structured-data-article.html +55 -0
  16. data/_includes/structured-data-breadcrumb.html +28 -0
  17. data/_includes/structured-data-person.html +82 -0
  18. data/_includes/structured-data-website.html +23 -0
  19. data/_layouts/blog.html +101 -0
  20. data/_layouts/category.html +66 -0
  21. data/_layouts/contact.html +26 -0
  22. data/_layouts/default.html +13 -0
  23. data/_layouts/home.html +30 -0
  24. data/_layouts/llms.txt +34 -0
  25. data/_layouts/post.html +99 -0
  26. data/_plugins/auto_related_posts.rb +153 -0
  27. data/_plugins/category_generator.rb +27 -0
  28. data/_plugins/llms_txt_generator.rb +45 -0
  29. data/_plugins/localized_date.rb +52 -0
  30. data/assets/bg.jpeg +0 -0
  31. data/assets/bg.webp +0 -0
  32. data/assets/debug/blocco.png +0 -0
  33. data/assets/debug/categorie.jpeg +0 -0
  34. data/assets/debug/categorie.png +0 -0
  35. data/assets/debug/codice.png +0 -0
  36. data/assets/debug/contrasto.jpeg +0 -0
  37. data/assets/debug/dipendenze.png +0 -0
  38. data/assets/debug/h1.png +0 -0
  39. data/assets/debug/pagespeed.png +0 -0
  40. data/assets/debug/ricerca.png +0 -0
  41. data/assets/debug/richieste.png +0 -0
  42. data/assets/favicon/android-icon-144x144.png +0 -0
  43. data/assets/favicon/android-icon-192x192.png +0 -0
  44. data/assets/favicon/android-icon-36x36.png +0 -0
  45. data/assets/favicon/android-icon-48x48.png +0 -0
  46. data/assets/favicon/android-icon-72x72.png +0 -0
  47. data/assets/favicon/android-icon-96x96.png +0 -0
  48. data/assets/favicon/apple-icon-114x114.png +0 -0
  49. data/assets/favicon/apple-icon-120x120.png +0 -0
  50. data/assets/favicon/apple-icon-144x144.png +0 -0
  51. data/assets/favicon/apple-icon-152x152.png +0 -0
  52. data/assets/favicon/apple-icon-180x180.png +0 -0
  53. data/assets/favicon/apple-icon-57x57.png +0 -0
  54. data/assets/favicon/apple-icon-60x60.png +0 -0
  55. data/assets/favicon/apple-icon-72x72.png +0 -0
  56. data/assets/favicon/apple-icon-76x76.png +0 -0
  57. data/assets/favicon/apple-icon-precomposed.png +0 -0
  58. data/assets/favicon/apple-icon.png +0 -0
  59. data/assets/favicon/favicon-16x16.png +0 -0
  60. data/assets/favicon/favicon-32x32.png +0 -0
  61. data/assets/favicon/favicon-96x96.png +0 -0
  62. data/assets/favicon/favicon.ico +0 -0
  63. data/assets/favicon/ms-icon-144x144.png +0 -0
  64. data/assets/favicon/ms-icon-150x150.png +0 -0
  65. data/assets/favicon/ms-icon-310x310.png +0 -0
  66. data/assets/favicon/ms-icon-70x70.png +0 -0
  67. data/assets/images/aiact.jpeg +0 -0
  68. data/assets/images/aiethics.jpeg +0 -0
  69. data/assets/images/green.jpeg +0 -0
  70. data/assets/images/jekyll.webp +0 -0
  71. data/assets/images/parenting.jpeg +0 -0
  72. data/assets/images/seo-generativo.jpeg +0 -0
  73. data/assets/images/upskilling-ai.jpeg +0 -0
  74. data/assets/pic.jpeg +0 -0
  75. data/assets/screens/screen.jpeg +0 -0
  76. data/assets/screens/screen2.jpeg +0 -0
  77. data/css/animations.css +404 -0
  78. data/css/style.css +2250 -0
  79. data/css/syntax-dark.css +157 -0
  80. data/css/syntax-light.css +157 -0
  81. data/js/main.js +706 -0
  82. data/js/simple-jekyll-search.min.js +6 -0
  83. metadata +254 -0
@@ -0,0 +1,99 @@
1
+ ---
2
+ layout: default
3
+ ---
4
+ {% assign t = site.data.i18n[site.lang] %}
5
+ <!-- Reading Progress Bar -->
6
+ <div class="reading-progress-container">
7
+ <div class="reading-progress-bar" id="reading-progress-bar"></div>
8
+ </div>
9
+
10
+ {% if page.image %}
11
+ <!-- Hero Section with Cover Image -->
12
+ <div class="post-hero" style="background-image: url('{{ page.image | relative_url }}');">
13
+ <div class="post-hero-overlay"></div>
14
+ <div class="post-hero-content">
15
+ <time class="post-hero-date" datetime="{{ page.date | date_to_xmlschema }}">
16
+ {{ page.date | localized_date }}
17
+ </time>
18
+ {% if page.categories %}
19
+ <div class="post-hero-categories">
20
+ {% for category in page.categories %}
21
+ <a href="{{ '/categories/' | append: category | relative_url }}" class="post-hero-category">{{ category }}</a>
22
+ {% endfor %}
23
+ </div>
24
+ {% endif %}
25
+ <h1 class="post-hero-title">{{ page.title }}</h1>
26
+ {% if page.subtitle %}
27
+ <p class="post-hero-subtitle">{{ page.subtitle }}</p>
28
+ {% endif %}
29
+ {% capture reading_time %}{{ content | reading_time }}{% endcapture %}
30
+ <span class="post-hero-reading-time">{{ reading_time }} {% if reading_time == '1' %}{{ t.post.minute_singular }}{% else %}{{ t.post.minute_plural }}{% endif %} {{ t.post.of_reading }}</span>
31
+ </div>
32
+ </div>
33
+ {% endif %}
34
+
35
+ <main id="main-content">
36
+ <article class="post">
37
+ <div class="post-container">
38
+ {% unless page.image %}
39
+ <header class="post-header">
40
+ <time class="post-date" datetime="{{ page.date | date_to_xmlschema }}">
41
+ {{ page.date | localized_date }}
42
+ </time>
43
+ {% if page.categories %}
44
+ <div class="post-categories">
45
+ {% for category in page.categories %}
46
+ <a href="{{ '/categories/' | append: category | relative_url }}" class="post-category">{{ category }}</a>
47
+ {% endfor %}
48
+ </div>
49
+ {% endif %}
50
+ <h1 class="post-title">{{ page.title }}</h1>
51
+ {% if page.subtitle %}
52
+ <p class="post-subtitle">{{ page.subtitle }}</p>
53
+ {% endif %}
54
+ <div class="post-meta">
55
+ <br />
56
+ {% capture reading_time %}{{ content | reading_time }}{% endcapture %}
57
+ <span class="reading-time">{{ t.post.reading_time }} {{ reading_time }} {% if reading_time == '1' %}{{ t.post.minute_singular }}{% else %}{{ t.post.minute_plural }}{% endif %}</span>
58
+ </div>
59
+ </header>
60
+ {% endunless %}
61
+
62
+ <div class="post-content">
63
+ {{ content }}
64
+ </div>
65
+
66
+ <footer class="post-footer">
67
+ <div class="post-author">
68
+ <div class="author-info">
69
+ <p class="author-name">{{ site.author.name }}</p>
70
+ <p class="author-bio">{{ site.description }}</p>
71
+ </div>
72
+ </div>
73
+
74
+ {% include related-posts.html %}
75
+
76
+ <div class="post-navigation">
77
+ {% if page.previous.url %}
78
+ <a href="{{ page.previous.url | relative_url }}" class="nav-post nav-prev">
79
+ <span class="nav-label">{{ t.post.previous_post }}</span>
80
+ <span class="nav-title">{{ page.previous.title }}</span>
81
+ </a>
82
+ {% endif %}
83
+ {% if page.next.url %}
84
+ <a href="{{ page.next.url | relative_url }}" class="nav-post nav-next">
85
+ <span class="nav-label">{{ t.post.next_post }}</span>
86
+ <span class="nav-title">{{ page.next.title }}</span>
87
+ </a>
88
+ {% endif %}
89
+ </div>
90
+
91
+ <div class="back-to-blog">
92
+ <a href="{{ '/blog' | relative_url }}" class="btn btn-secondary">
93
+ {{ t.post.back_to_blog }}
94
+ </a>
95
+ </div>
96
+ </footer>
97
+ </div>
98
+ </article>
99
+ </main>
@@ -0,0 +1,153 @@
1
+ require 'set'
2
+
3
+ module Jekyll
4
+ class AutoRelatedPostsGenerator < Generator
5
+ safe true
6
+ priority :low
7
+
8
+ # Common words to ignore (Italian and English stop words)
9
+ STOP_WORDS = Set.new(%w[
10
+ il la i le lo gli un una dei delle degli di da in con su per tra fra
11
+ a e o ma se come quando perché dove chi che cosa cui qual quale
12
+ questo questa questi queste quello quella quelli quelle
13
+ essere avere fare dare stare andare venire dire potere dovere volere
14
+ molto poco più meno anche ancora solo sempre mai già
15
+ the be to of and a in that have it for not on with he as you do at
16
+ this but his by from they we say her she or an will my one all would
17
+ there their what so up out if about who get which go me when make can
18
+ like time no just him know take people into year your good some could
19
+ them see other than then now look only come its over think also back
20
+ after use two how our work first well way even new want because any
21
+ these give day most us
22
+ ])
23
+
24
+ # Minimum word length to consider
25
+ MIN_WORD_LENGTH = 4
26
+
27
+ def generate(site)
28
+ return if site.posts.docs.empty?
29
+
30
+ # Extract keywords from all posts
31
+ posts_keywords = {}
32
+ site.posts.docs.each do |post|
33
+ posts_keywords[post] = extract_keywords(post)
34
+ end
35
+
36
+ # Calculate related posts for each post
37
+ site.posts.docs.each do |post|
38
+ related = calculate_related_posts(post, site.posts.docs, posts_keywords)
39
+
40
+ # Store in post data for use in templates
41
+ post.data['auto_related_posts'] = related.take(6).map { |r| r[:post] }
42
+ post.data['auto_related_scores'] = related.take(6).map { |r| r[:score] }
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def extract_keywords(post)
49
+ # Combine title, subtitle, categories, and content
50
+ text = [
51
+ post.data['title'],
52
+ post.data['subtitle'],
53
+ post.data['categories']&.join(' '),
54
+ post.data['tags']&.join(' '),
55
+ post.content
56
+ ].compact.join(' ')
57
+
58
+ # Extract words, clean and filter
59
+ words = text
60
+ .downcase
61
+ .gsub(/<[^>]*>/, ' ') # Remove HTML tags
62
+ .gsub(/[^a-zàèéìòùäöüß\s]/, ' ') # Keep only letters (including Italian/German)
63
+ .split
64
+ .select { |w| w.length >= MIN_WORD_LENGTH && !STOP_WORDS.include?(w) }
65
+
66
+ # Count word frequency
67
+ word_freq = Hash.new(0)
68
+ words.each { |word| word_freq[word] += 1 }
69
+
70
+ # Boost title and category words
71
+ title_words = post.data['title'].to_s.downcase.split
72
+ post.data['categories']&.each do |cat|
73
+ cat.downcase.split(/[-_\s]/).each do |word|
74
+ word_freq[word] *= 3 if word.length >= MIN_WORD_LENGTH
75
+ end
76
+ end
77
+
78
+ title_words.each do |word|
79
+ word_freq[word] *= 2 if word.length >= MIN_WORD_LENGTH
80
+ end
81
+
82
+ # Return top keywords
83
+ word_freq.sort_by { |_, v| -v }.take(20).to_h
84
+ end
85
+
86
+ def calculate_related_posts(current_post, all_posts, posts_keywords)
87
+ current_keywords = posts_keywords[current_post]
88
+ current_categories = Set.new(current_post.data['categories'] || [])
89
+ current_tags = Set.new(current_post.data['tags'] || [])
90
+
91
+ related = []
92
+
93
+ all_posts.each do |other_post|
94
+ next if other_post.url == current_post.url
95
+
96
+ score = 0
97
+
98
+ # Category matching (high weight)
99
+ other_categories = Set.new(other_post.data['categories'] || [])
100
+ shared_categories = current_categories & other_categories
101
+ score += shared_categories.size * 10
102
+
103
+ # Tag matching (medium weight)
104
+ other_tags = Set.new(other_post.data['tags'] || [])
105
+ shared_tags = current_tags & other_tags
106
+ score += shared_tags.size * 5
107
+
108
+ # Keyword matching (calculated by TF-IDF-like similarity)
109
+ other_keywords = posts_keywords[other_post]
110
+ keyword_score = calculate_keyword_similarity(current_keywords, other_keywords)
111
+ score += keyword_score
112
+
113
+ # Time proximity bonus (recent posts get small boost)
114
+ days_diff = (current_post.date - other_post.date).abs / 86400
115
+ if days_diff < 30
116
+ score += 2
117
+ elsif days_diff < 90
118
+ score += 1
119
+ end
120
+
121
+ related << { post: other_post, score: score } if score > 0
122
+ end
123
+
124
+ # Sort by score descending
125
+ related.sort_by { |r| -r[:score] }
126
+ end
127
+
128
+ def calculate_keyword_similarity(keywords1, keywords2)
129
+ return 0 if keywords1.empty? || keywords2.empty?
130
+
131
+ # Calculate overlap score
132
+ shared_keywords = keywords1.keys & keywords2.keys
133
+ return 0 if shared_keywords.empty?
134
+
135
+ # Weight by frequency in both documents
136
+ score = 0
137
+ shared_keywords.each do |keyword|
138
+ # Multiply frequencies and take square root for normalization
139
+ freq_product = keywords1[keyword] * keywords2[keyword]
140
+ score += Math.sqrt(freq_product)
141
+ end
142
+
143
+ # Normalize by document lengths
144
+ magnitude1 = Math.sqrt(keywords1.values.map { |v| v * v }.sum)
145
+ magnitude2 = Math.sqrt(keywords2.values.map { |v| v * v }.sum)
146
+
147
+ return 0 if magnitude1 == 0 || magnitude2 == 0
148
+
149
+ # Cosine similarity, scaled to be comparable with category scores
150
+ (score / (magnitude1 * magnitude2)) * 20
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,27 @@
1
+ module Jekyll
2
+ class CategoryPageGenerator < Generator
3
+ safe true
4
+
5
+ def generate(site)
6
+ if site.layouts.key? 'category'
7
+ site.categories.each_key do |category|
8
+ site.pages << CategoryPage.new(site, site.source, category)
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ class CategoryPage < Page
15
+ def initialize(site, base, category)
16
+ @site = site
17
+ @base = base
18
+ @dir = File.join('categories', category)
19
+ @name = 'index.html'
20
+
21
+ self.process(@name)
22
+ self.read_yaml(File.join(base, '_layouts'), 'category.html')
23
+ self.data['category'] = category
24
+ self.data['title'] = "#{category.gsub('-', ' ').capitalize}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ module Jekyll
2
+ class LLMSTxtGenerator < Generator
3
+ safe true
4
+ priority :low
5
+
6
+ def generate(site)
7
+ # Check if llms.txt layout exists
8
+ return unless site.layouts.key?('llms')
9
+
10
+ # Generate llms.txt for each post
11
+ site.posts.docs.each do |post|
12
+ # Create a new page for the llms.txt file
13
+ site.pages << LLMSTxtPage.new(site, post)
14
+ end
15
+ end
16
+ end
17
+
18
+ class LLMSTxtPage < Page
19
+ def initialize(site, post)
20
+ @site = site
21
+ @base = site.source
22
+
23
+ # Get the post's directory path
24
+ # Posts have permalink like /blog/2025/10/28/post-title/
25
+ # We want to create llms.txt in the same directory
26
+ @dir = post.url.chomp('/')
27
+ @name = 'llms.txt'
28
+
29
+ self.process(@name)
30
+
31
+ # Initialize data hash and copy post data
32
+ self.data = {}
33
+ self.data.merge!(post.data)
34
+
35
+ # Set the content from the post
36
+ self.content = post.content
37
+
38
+ # Use the llms layout
39
+ self.data['layout'] = 'llms'
40
+
41
+ # Ensure the file is treated as a page to be processed
42
+ self.data['permalink'] = File.join(post.url, 'llms.txt')
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,52 @@
1
+ module Jekyll
2
+ module LocalizedDateFilter
3
+ MONTHS = {
4
+ 'it' => %w[
5
+ gennaio febbraio marzo aprile maggio giugno
6
+ luglio agosto settembre ottobre novembre dicembre
7
+ ],
8
+ 'en' => %w[
9
+ January February March April May June
10
+ July August September October November December
11
+ ],
12
+ 'de' => %w[
13
+ Januar Februar März April Mai Juni
14
+ Juli August September Oktober November Dezember
15
+ ],
16
+ 'fr' => %w[
17
+ janvier février mars avril mai juin
18
+ juillet août septembre octobre novembre décembre
19
+ ],
20
+ 'es' => %w[
21
+ enero febrero marzo abril mayo junio
22
+ julio agosto septiembre octubre noviembre diciembre
23
+ ]
24
+ }
25
+
26
+ def localized_date(date)
27
+ return date unless date.is_a?(Time) || date.is_a?(Date)
28
+
29
+ # Get the site language from Jekyll config, default to 'en'
30
+ lang = @context.registers[:site].config['lang'] || 'en'
31
+
32
+ day = date.day
33
+ month = MONTHS[lang][date.month - 1]
34
+ year = date.year
35
+
36
+ "#{day} #{month} #{year}"
37
+ end
38
+
39
+ # Keep italian_date for backward compatibility
40
+ def italian_date(date)
41
+ return date unless date.is_a?(Time) || date.is_a?(Date)
42
+
43
+ day = date.day
44
+ month = MONTHS['it'][date.month - 1]
45
+ year = date.year
46
+
47
+ "#{day} #{month} #{year}"
48
+ end
49
+ end
50
+ end
51
+
52
+ Liquid::Template.register_filter(Jekyll::LocalizedDateFilter)
data/assets/bg.jpeg ADDED
Binary file
data/assets/bg.webp ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/assets/pic.jpeg ADDED
Binary file
Binary file
Binary file