jekyll-github-card 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0037bdb90e0651931f5719d6ac712d4e00cd62b88c404a11125a3682ab520f46
4
- data.tar.gz: daaf2b1760e8d90eabd0426a8a972254805c2a19969cb60da5e8933962d7071c
3
+ metadata.gz: 7e9b401cafbd8f56e70fde46581e04862f7b5db93b9145923600cbe9606e5c80
4
+ data.tar.gz: '092cf4dbc24ca5b4b4a1b06e5fa1c17cc1f16f2f3194b851d02984d44bd66f8f'
5
5
  SHA512:
6
- metadata.gz: ab458fb8109ae7b4b9c66b3cc809fdfc15aafafaecef3a5c57807c16e0d387305b15a8b896b13563e30979d11b6beaea9c75699a87a7629bb6d84c37fef8f814
7
- data.tar.gz: 8c13daffa7a93ea45db4956000a11d0e3198accefbc4525c111e5be27b82ab06f643fb78975f69f89b716fe897dda4735f0fe9cb9964aa013153c08f5a823dd9
6
+ metadata.gz: f046ee51c03c30ee68aec16426119fbadf42c045500094199e7c871d049dd309422722b5f194274f35cd53297550db7510df60b12def2ed239986b2888424bf0
7
+ data.tar.gz: 7e5a81ec424d3bea58932054e7a21c8c0ffec7b36772b0db52a5c3be22b383c2043c5df8f4e2ee2d93feb9aa3334ce37fecd7200153fb2d22196ec8827d4647b
data/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2025-12-12
9
+
10
+ ### Added
11
+
12
+ - `{% github user/repo %}` Liquid tag for embedding GitHub repository cards
13
+ - Light and dark theme support
14
+ - Automatic detection via `prefers-color-scheme`
15
+ - Support for Chirpy theme's `data-mode` attribute
16
+ - Support for `data-theme` and class-based theme switching
17
+ - Repository information display:
18
+ - Repository name with link
19
+ - Description
20
+ - Primary programming language with color indicator
21
+ - Star count (with K/M formatting)
22
+ - Fork count (with K/M formatting)
23
+ - Error handling for missing or inaccessible repositories
24
+ - In-memory caching for API responses
25
+ - Support for GitHub token authentication
26
+ - CSS custom properties for easy theming
27
+ - Responsive design
28
+ - XSS protection via HTML escaping
29
+
30
+ ## [0.1.0] - 2025-12-11
31
+
32
+ ### Added
33
+
34
+ - Initial release
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Rodolfo Olivieri
3
+ Copyright (c) 2024 Your Name
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
+
data/README.md CHANGED
@@ -1,61 +1,218 @@
1
1
  # Jekyll GitHub Card
2
2
 
3
- A beautiful Jekyll plugin to display GitHub repository cards with live data from the GitHub API.
3
+ [![Gem Version](https://badge.fury.io/rb/jekyll-github-card.svg)](https://badge.fury.io/rb/jekyll-github-card)
4
+ [![Tests](https://github.com/r0x0d/jekyll-github-card/actions/workflows/test.yml/badge.svg)](https://github.com/r0x0d/jekyll-github-card/actions/workflows/test.yml)
4
5
 
5
- ## Features
6
+ A Jekyll plugin that allows you to embed beautiful GitHub repository cards in your posts and pages using a simple Liquid tag. Supports both light and dark themes automatically.
6
7
 
7
- - 🎨 Beautiful, responsive GitHub repository cards
8
- - 🌙 Dark mode support (works perfectly with Chirpy theme)
9
- - 📊 Live data from GitHub API (stars, forks, language)
10
- - Easy to use with simple Liquid tag
11
- - 🎯 No configuration needed
8
+ ## Preview
9
+
10
+ ### Dark Theme
11
+ ![Dark Theme Preview](docs/dark-preview.png)
12
+
13
+ ### Light Theme
14
+ ![Light Theme Preview](docs/light-preview.png)
12
15
 
13
16
  ## Installation
14
17
 
15
18
  Add this line to your Jekyll site's `Gemfile`:
16
19
 
17
20
  ```ruby
18
- gem 'jekyll-github-card'
21
+ group :jekyll_plugins do
22
+ gem 'jekyll-github-card'
23
+ end
24
+ ```
25
+
26
+ Then execute:
27
+
28
+ ```bash
29
+ bundle install
19
30
  ```
20
31
 
21
- And then add this to your `_config.yml`:
32
+ Or install it yourself:
33
+
34
+ ```bash
35
+ gem install jekyll-github-card
36
+ ```
37
+
38
+ ### Add to your Jekyll configuration
39
+
40
+ Add the plugin to your `_config.yml`:
22
41
 
23
42
  ```yaml
24
43
  plugins:
25
44
  - jekyll-github-card
26
45
  ```
27
46
 
28
- Finally, add these lines to your layout file (e.g., `_layouts/default.html` or `_layouts/post.html`):
47
+ ### Include the CSS
48
+
49
+ Add the stylesheet to your layout (usually in `_includes/head.html` or your main layout file):
29
50
 
30
51
  ```html
31
- <head>
32
- <!-- ... other head content ... -->
33
- <link rel="stylesheet" href="{{ '/assets/css/github-card.css' | relative_url }}">
34
- <script src="{{ '/assets/js/github-card.js' | relative_url }}"></script>
35
- </head>
52
+ <link rel="stylesheet" href="{{ '/assets/css/github-card.css' | relative_url }}">
53
+ ```
54
+
55
+ Or import it in your main SCSS file:
56
+
57
+ ```scss
58
+ @import "github-card";
36
59
  ```
37
60
 
38
61
  ## Usage
39
62
 
40
- In your markdown posts or pages:
63
+ Use the `{% github %}` tag in any post or page:
41
64
 
42
65
  ```liquid
43
66
  {% github facebook/react %}
67
+ ```
68
+
69
+ This will render a card showing:
70
+ - Repository name with link
71
+ - Description
72
+ - Primary programming language with color indicator
73
+ - Star count
74
+ - Fork count
75
+
76
+ ### Examples
77
+
78
+ ```liquid
79
+ {% github microsoft/vscode %}
80
+ {% github rails/rails %}
81
+ {% github jekyll/jekyll %}
82
+ ```
83
+
84
+ ## Theme Support
85
+
86
+ The plugin automatically supports both light and dark themes through CSS.
87
+
88
+ ### Automatic Detection
89
+
90
+ By default, the card respects the user's system preference via `prefers-color-scheme`.
91
+
92
+ ### Manual Theme Control
93
+
94
+ #### For Chirpy Theme
95
+
96
+ Chirpy uses `data-mode="light"` on the HTML element for light mode. This is automatically supported:
97
+
98
+ ```html
99
+ <html data-mode="light"> <!-- Light theme -->
100
+ <html> <!-- Dark theme (default) -->
101
+ ```
102
+
103
+ #### For Other Themes
104
+
105
+ The CSS supports multiple theme conventions:
106
+
107
+ ```html
108
+ <!-- Any of these will trigger light theme -->
109
+ <html data-mode="light">
110
+ <html data-theme="light">
111
+ <html class="light">
112
+ <div class="github-card light">
113
+ ```
114
+
115
+ #### Force a Specific Theme
44
116
 
45
- {% github torvalds/linux %}
117
+ Add the `light` or `dark` class directly to the card:
118
+
119
+ ```html
120
+ <div class="github-card light">...</div>
121
+ <div class="github-card dark">...</div>
122
+ ```
123
+
124
+ ## Configuration
125
+
126
+ ### GitHub API Rate Limits
127
+
128
+ The plugin uses GitHub's public API, which has rate limits:
129
+ - **Unauthenticated**: 60 requests per hour
130
+ - **Authenticated**: 5,000 requests per hour
131
+
132
+ To increase the rate limit, set a GitHub token as an environment variable:
133
+
134
+ ```bash
135
+ export GITHUB_TOKEN=your_github_token
136
+ ```
137
+
138
+ For GitHub Actions or CI/CD:
139
+
140
+ ```yaml
141
+ env:
142
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46
143
  ```
47
144
 
48
- ## Compatibility
145
+ ### Caching
146
+
147
+ The plugin caches API responses in memory during the build process to minimize API calls.
148
+
149
+ ## Customization
49
150
 
50
- - Jekyll 3.7+
51
- - Ruby 2.6+
52
- - Works with Chirpy theme and most Jekyll themes
53
- - Supports both light and dark modes
151
+ ### CSS Variables
54
152
 
55
- ## GitHub API Rate Limiting
153
+ The plugin uses CSS custom properties for easy theming:
56
154
 
57
- The plugin uses the public GitHub API which allows 60 requests per hour without authentication. For higher limits, you can add a personal access token in your JavaScript if needed.
155
+ ```css
156
+ .github-card {
157
+ --github-card-bg: #0d1117;
158
+ --github-card-border: #30363d;
159
+ --github-card-text: #e6edf3;
160
+ --github-card-text-secondary: #8b949e;
161
+ --github-card-link: #58a6ff;
162
+ --github-card-link-hover: #79c0ff;
163
+ }
164
+ ```
165
+
166
+ Override these in your own CSS to match your site's design:
167
+
168
+ ```css
169
+ .github-card {
170
+ --github-card-bg: var(--my-card-background);
171
+ --github-card-border: var(--my-border-color);
172
+ /* ... */
173
+ }
174
+ ```
175
+
176
+ ### Supported Languages
177
+
178
+ The plugin includes color mappings for 25+ popular programming languages. Unknown languages will use a default gray color.
179
+
180
+ ## Development
181
+
182
+ After checking out the repo:
183
+
184
+ ```bash
185
+ # Install dependencies
186
+ bundle install
187
+
188
+ # Run tests
189
+ bundle exec rake test
190
+
191
+ # Build the gem
192
+ gem build jekyll-github-card.gemspec
193
+ ```
194
+
195
+ ### Running Tests
196
+
197
+ ```bash
198
+ bundle exec rake test
199
+ ```
200
+
201
+ ## Contributing
202
+
203
+ Bug reports and pull requests are welcome on GitHub at https://github.com/r0x0d/jekyll-github-card.
204
+
205
+ 1. Fork the repository
206
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
207
+ 3. Commit your changes (`git commit -am 'Add amazing feature'`)
208
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
209
+ 5. Open a Pull Request
58
210
 
59
211
  ## License
60
212
 
61
- MIT License - see LICENSE file for details
213
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
214
+
215
+ ## Changelog
216
+
217
+ See [CHANGELOG.md](CHANGELOG.md) for version history.
218
+
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ desc "Run tests"
13
+ task default: :test
14
+
@@ -1,123 +1,185 @@
1
- /* Base styles (defaults to dark mode for Chirpy) */
1
+ /* Jekyll GitHub Card Styles
2
+ * Supports both light and dark themes
3
+ * For Chirpy theme: uses data-mode="light" for light, default for dark
4
+ */
5
+
2
6
  .github-card {
3
- background: #0d1117;
4
- border: 1px solid #30363d;
5
- border-radius: 6px;
6
- padding: 16px;
7
- max-width: 400px;
8
- transition: box-shadow 0.2s ease, background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
9
- text-decoration: none;
10
- display: block;
11
- color: #e6edf3;
12
- margin: 20px 0;
13
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
7
+ --github-card-bg: #0d1117;
8
+ --github-card-border: #30363d;
9
+ --github-card-text: #e6edf3;
10
+ --github-card-text-secondary: #8b949e;
11
+ --github-card-link: #58a6ff;
12
+ --github-card-link-hover: #79c0ff;
13
+ --github-card-error-bg: #3d1f28;
14
+ --github-card-error-border: #f85149;
15
+
16
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
17
+ background-color: var(--github-card-bg);
18
+ border: 1px solid var(--github-card-border);
19
+ border-radius: 8px;
20
+ padding: 16px;
21
+ margin: 16px 0;
22
+ max-width: 480px;
23
+ box-sizing: border-box;
24
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
14
25
  }
15
26
 
16
27
  .github-card:hover {
17
- box-shadow: 0 3px 12px rgba(0, 0, 0, 0.4);
18
- border-color: #484f58;
28
+ border-color: var(--github-card-link);
29
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
30
+ }
31
+
32
+ /* Light theme support */
33
+ [data-mode="light"] .github-card,
34
+ .light .github-card,
35
+ [data-theme="light"] .github-card,
36
+ .github-card.light {
37
+ --github-card-bg: #ffffff;
38
+ --github-card-border: #d0d7de;
39
+ --github-card-text: #1f2328;
40
+ --github-card-text-secondary: #656d76;
41
+ --github-card-link: #0969da;
42
+ --github-card-link-hover: #0550ae;
43
+ --github-card-error-bg: #ffebe9;
44
+ --github-card-error-border: #cf222e;
45
+ }
46
+
47
+ [data-mode="light"] .github-card:hover,
48
+ .light .github-card:hover,
49
+ [data-theme="light"] .github-card:hover,
50
+ .github-card.light:hover {
51
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
52
+ }
53
+
54
+ /* Prefer color scheme for automatic detection */
55
+ @media (prefers-color-scheme: light) {
56
+ .github-card:not([data-theme]):not(.dark) {
57
+ --github-card-bg: #ffffff;
58
+ --github-card-border: #d0d7de;
59
+ --github-card-text: #1f2328;
60
+ --github-card-text-secondary: #656d76;
61
+ --github-card-link: #0969da;
62
+ --github-card-link-hover: #0550ae;
63
+ --github-card-error-bg: #ffebe9;
64
+ --github-card-error-border: #cf222e;
65
+ }
19
66
  }
20
67
 
21
- .github-card.loading {
22
- min-height: 120px;
23
- display: flex;
24
- align-items: center;
25
- justify-content: center;
26
- color: #8b949e;
68
+ /* Header */
69
+ .github-card-header {
70
+ display: flex;
71
+ align-items: center;
72
+ margin-bottom: 12px;
27
73
  }
28
74
 
29
- .github-card .card-header {
30
- display: flex;
31
- align-items: center;
32
- gap: 8px;
33
- margin-bottom: 8px;
75
+ .github-card-link {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 8px;
79
+ color: var(--github-card-link);
80
+ text-decoration: none;
81
+ font-weight: 600;
82
+ font-size: 16px;
83
+ transition: color 0.2s ease;
34
84
  }
35
85
 
36
- .github-card .github-icon {
37
- width: 20px;
38
- height: 20px;
39
- fill: #8b949e;
40
- flex-shrink: 0;
41
- transition: fill 0.2s ease;
86
+ .github-card-link:hover {
87
+ color: var(--github-card-link-hover);
88
+ text-decoration: underline;
42
89
  }
43
90
 
44
- .github-card .repo-name {
45
- font-size: 14px;
46
- font-weight: 600;
47
- color: #58a6ff;
48
- margin: 0;
49
- transition: color 0.2s ease;
91
+ .github-card-icon {
92
+ flex-shrink: 0;
93
+ color: var(--github-card-text-secondary);
50
94
  }
51
95
 
52
- .github-card .description {
53
- font-size: 12px;
54
- color: #8b949e;
55
- margin: 8px 0 12px 0;
56
- line-height: 1.5;
57
- transition: color 0.2s ease;
96
+ .github-card-repo-name {
97
+ word-break: break-word;
58
98
  }
59
99
 
60
- .github-card .card-footer {
61
- display: flex;
62
- align-items: center;
63
- gap: 16px;
64
- font-size: 12px;
65
- color: #8b949e;
66
- flex-wrap: wrap;
67
- transition: color 0.2s ease;
100
+ /* Body */
101
+ .github-card-body {
102
+ margin-bottom: 12px;
68
103
  }
69
104
 
70
- .github-card .stat {
71
- display: flex;
72
- align-items: center;
73
- gap: 4px;
105
+ .github-card-description {
106
+ color: var(--github-card-text);
107
+ font-size: 14px;
108
+ line-height: 1.5;
109
+ margin: 0;
110
+ word-wrap: break-word;
111
+ overflow-wrap: break-word;
74
112
  }
75
113
 
76
- .github-card .language-dot {
77
- width: 12px;
78
- height: 12px;
79
- border-radius: 50%;
80
- display: inline-block;
114
+ /* Footer */
115
+ .github-card-footer {
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: space-between;
119
+ flex-wrap: wrap;
120
+ gap: 12px;
81
121
  }
82
122
 
83
- .github-card .error-message {
84
- color: #f85149;
85
- font-size: 12px;
86
- transition: color 0.2s ease;
123
+ .github-card-language {
124
+ display: flex;
125
+ align-items: center;
126
+ gap: 4px;
127
+ font-size: 12px;
128
+ color: var(--github-card-text-secondary);
87
129
  }
88
130
 
89
- /* Light mode override for Chirpy theme */
90
- html[data-mode="light"] .github-card {
91
- background: #ffffff;
92
- border-color: #d0d7de;
93
- color: #1f2328;
131
+ .github-card-language-dot {
132
+ width: 12px;
133
+ height: 12px;
134
+ border-radius: 50%;
135
+ flex-shrink: 0;
94
136
  }
95
137
 
96
- html[data-mode="light"] .github-card:hover {
97
- box-shadow: 0 3px 12px rgba(0, 0, 0, 0.1);
98
- border-color: #c0c7ce;
138
+ .github-card-stats {
139
+ display: flex;
140
+ align-items: center;
141
+ gap: 16px;
99
142
  }
100
143
 
101
- html[data-mode="light"] .github-card .github-icon {
102
- fill: #656d76;
144
+ .github-card-stat {
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 4px;
148
+ font-size: 12px;
149
+ color: var(--github-card-text-secondary);
150
+ cursor: default;
103
151
  }
104
152
 
105
- html[data-mode="light"] .github-card .repo-name {
106
- color: #0969da;
153
+ .github-card-stat svg {
154
+ flex-shrink: 0;
155
+ color: var(--github-card-text-secondary);
107
156
  }
108
157
 
109
- html[data-mode="light"] .github-card .description {
110
- color: #656d76;
158
+ /* Error state */
159
+ .github-card-error {
160
+ background-color: var(--github-card-error-bg);
161
+ border-color: var(--github-card-error-border);
111
162
  }
112
163
 
113
- html[data-mode="light"] .github-card .card-footer {
114
- color: #656d76;
164
+ .github-card-error:hover {
165
+ border-color: var(--github-card-error-border);
115
166
  }
116
167
 
117
- html[data-mode="light"] .github-card.loading {
118
- color: #656d76;
168
+ .github-card-error-message {
169
+ color: var(--github-card-error-border);
170
+ font-size: 14px;
171
+ margin: 0;
119
172
  }
120
173
 
121
- html[data-mode="light"] .github-card .error-message {
122
- color: #cf222e;
174
+ /* Responsive */
175
+ @media (max-width: 520px) {
176
+ .github-card {
177
+ max-width: 100%;
178
+ }
179
+
180
+ .github-card-footer {
181
+ flex-direction: column;
182
+ align-items: flex-start;
183
+ }
123
184
  }
185
+
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module GithubCard
5
+ class StyleGenerator < Jekyll::Generator
6
+ safe true
7
+ priority :low
8
+
9
+ def generate(site)
10
+ css_content = File.read(File.join(File.dirname(__FILE__), "..", "..", "assets", "css", "github-card.css"))
11
+
12
+ # Create a static file for the CSS
13
+ site.static_files << StyleFile.new(site, css_content)
14
+ end
15
+ end
16
+
17
+ class StyleFile < Jekyll::StaticFile
18
+ def initialize(site, content)
19
+ @site = site
20
+ @content = content
21
+ @relative_path = "/assets/css/github-card.css"
22
+ @extname = ".css"
23
+ @name = "github-card.css"
24
+ @dir = "/assets/css"
25
+ end
26
+
27
+ def write(dest)
28
+ dest_path = File.join(dest, @relative_path)
29
+ FileUtils.mkdir_p(File.dirname(dest_path))
30
+ File.write(dest_path, @content)
31
+ true
32
+ end
33
+
34
+ def path
35
+ @relative_path
36
+ end
37
+
38
+ def relative_path
39
+ @relative_path
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "uri"
6
+ require "cgi"
7
+
8
+ module Jekyll
9
+ module GithubCard
10
+ class GithubRepoTag < Liquid::Tag
11
+ GITHUB_API_URL = "https://api.github.com/repos"
12
+ CACHE = {}
13
+
14
+ def initialize(tag_name, markup, tokens)
15
+ super
16
+ @repo = markup.strip
17
+ end
18
+
19
+ def render(context)
20
+ return error_card("No repository specified") if @repo.empty?
21
+
22
+ repo_data = fetch_repo_data(@repo)
23
+ return error_card(repo_data[:error]) if repo_data[:error]
24
+
25
+ build_card(repo_data)
26
+ end
27
+
28
+ private
29
+
30
+ def fetch_repo_data(repo)
31
+ return CACHE[repo] if CACHE[repo]
32
+
33
+ uri = URI.parse("#{GITHUB_API_URL}/#{repo}")
34
+ http = Net::HTTP.new(uri.host, uri.port)
35
+ http.use_ssl = true
36
+ http.open_timeout = 5
37
+ http.read_timeout = 5
38
+
39
+ request = Net::HTTP::Get.new(uri.request_uri)
40
+ request["Accept"] = "application/vnd.github.v3+json"
41
+ request["User-Agent"] = "Jekyll-Github-Card"
42
+
43
+ # Use GitHub token if available
44
+ if ENV["GITHUB_TOKEN"]
45
+ request["Authorization"] = "token #{ENV["GITHUB_TOKEN"]}"
46
+ end
47
+
48
+ response = http.request(request)
49
+
50
+ if response.code == "200"
51
+ data = JSON.parse(response.body)
52
+ CACHE[repo] = {
53
+ name: data["name"],
54
+ full_name: data["full_name"],
55
+ description: data["description"],
56
+ html_url: data["html_url"],
57
+ stargazers_count: data["stargazers_count"],
58
+ forks_count: data["forks_count"],
59
+ language: data["language"],
60
+ owner_avatar: data["owner"]["avatar_url"],
61
+ owner_login: data["owner"]["login"],
62
+ open_issues_count: data["open_issues_count"],
63
+ watchers_count: data["watchers_count"],
64
+ default_branch: data["default_branch"]
65
+ }
66
+ else
67
+ { error: "Repository '#{repo}' not found (HTTP #{response.code})" }
68
+ end
69
+ rescue StandardError => e
70
+ { error: "Failed to fetch repository: #{e.message}" }
71
+ end
72
+
73
+ def build_card(data)
74
+ escaped_description = CGI.escapeHTML(data[:description] || "No description available")
75
+ escaped_full_name = CGI.escapeHTML(data[:full_name])
76
+ escaped_language = CGI.escapeHTML(data[:language] || "")
77
+
78
+ <<~HTML
79
+ <div class="github-card" data-repo="#{escaped_full_name}">
80
+ <div class="github-card-header">
81
+ <a href="#{data[:html_url]}" target="_blank" rel="noopener noreferrer" class="github-card-link">
82
+ <svg class="github-card-icon" viewBox="0 0 16 16" width="20" height="20" aria-hidden="true">
83
+ <path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
84
+ </svg>
85
+ <span class="github-card-repo-name">#{escaped_full_name}</span>
86
+ </a>
87
+ </div>
88
+ <div class="github-card-body">
89
+ <p class="github-card-description">#{escaped_description}</p>
90
+ </div>
91
+ <div class="github-card-footer">
92
+ #{language_badge(escaped_language) if data[:language]}
93
+ <div class="github-card-stats">
94
+ <span class="github-card-stat" title="Stars">
95
+ <svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
96
+ <path fill="currentColor" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25z"></path>
97
+ </svg>
98
+ #{format_number(data[:stargazers_count])}
99
+ </span>
100
+ <span class="github-card-stat" title="Forks">
101
+ <svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
102
+ <path fill="currentColor" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path>
103
+ </svg>
104
+ #{format_number(data[:forks_count])}
105
+ </span>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ HTML
110
+ end
111
+
112
+ def language_badge(language)
113
+ return "" if language.nil? || language.empty?
114
+
115
+ color = language_color(language)
116
+ <<~HTML
117
+ <span class="github-card-language">
118
+ <span class="github-card-language-dot" style="background-color: #{color}"></span>
119
+ #{language}
120
+ </span>
121
+ HTML
122
+ end
123
+
124
+ def language_color(language)
125
+ colors = {
126
+ "Ruby" => "#701516",
127
+ "JavaScript" => "#f1e05a",
128
+ "TypeScript" => "#3178c6",
129
+ "Python" => "#3572A5",
130
+ "Java" => "#b07219",
131
+ "Go" => "#00ADD8",
132
+ "Rust" => "#dea584",
133
+ "C" => "#555555",
134
+ "C++" => "#f34b7d",
135
+ "C#" => "#178600",
136
+ "PHP" => "#4F5D95",
137
+ "Swift" => "#F05138",
138
+ "Kotlin" => "#A97BFF",
139
+ "Scala" => "#c22d40",
140
+ "Shell" => "#89e051",
141
+ "HTML" => "#e34c26",
142
+ "CSS" => "#563d7c",
143
+ "Vue" => "#41b883",
144
+ "React" => "#61dafb",
145
+ "Elixir" => "#6e4a7e",
146
+ "Clojure" => "#db5855",
147
+ "Haskell" => "#5e5086",
148
+ "Lua" => "#000080",
149
+ "Perl" => "#0298c3",
150
+ "R" => "#198CE7",
151
+ "Dart" => "#00B4AB",
152
+ "Objective-C" => "#438eff"
153
+ }
154
+ colors[language] || "#586069"
155
+ end
156
+
157
+ def format_number(num)
158
+ return "0" if num.nil?
159
+
160
+ if num >= 1_000_000
161
+ formatted = (num / 1_000_000.0).round(1)
162
+ formatted = formatted.to_i if formatted == formatted.to_i
163
+ "#{formatted}M"
164
+ elsif num >= 1_000
165
+ formatted = (num / 1_000.0).round(1)
166
+ formatted = formatted.to_i if formatted == formatted.to_i
167
+ "#{formatted}k"
168
+ else
169
+ num.to_s
170
+ end
171
+ end
172
+
173
+ def error_card(message)
174
+ <<~HTML
175
+ <div class="github-card github-card-error">
176
+ <div class="github-card-body">
177
+ <p class="github-card-error-message">⚠️ #{CGI.escapeHTML(message)}</p>
178
+ </div>
179
+ </div>
180
+ HTML
181
+ end
182
+ end
183
+ end
184
+ end
185
+
186
+ Liquid::Template.register_tag("github", Jekyll::GithubCard::GithubRepoTag)
187
+
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jekyll
4
- module GitHubCard
5
- VERSION = "0.1.0"
4
+ module GithubCard
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
8
+
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll"
4
+ require_relative "jekyll-github-card/version"
5
+ require_relative "jekyll-github-card/tag"
6
+ require_relative "jekyll-github-card/generator"
7
+
8
+ module Jekyll
9
+ module GithubCard
10
+ end
11
+ end
12
+
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-github-card
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodolfo Olivieri
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2025-12-12 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: jekyll
@@ -29,6 +30,20 @@ dependencies:
29
30
  - - "<"
30
31
  - !ruby/object:Gem::Version
31
32
  version: '5.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: logger
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.5'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.5'
32
47
  - !ruby/object:Gem::Dependency
33
48
  name: bundler
34
49
  requirement: !ruby/object:Gem::Requirement
@@ -43,6 +58,20 @@ dependencies:
43
58
  - - "~>"
44
59
  - !ruby/object:Gem::Version
45
60
  version: '2.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: minitest
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '5.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '5.0'
46
75
  - !ruby/object:Gem::Dependency
47
76
  name: rake
48
77
  requirement: !ruby/object:Gem::Requirement
@@ -57,20 +86,36 @@ dependencies:
57
86
  - - "~>"
58
87
  - !ruby/object:Gem::Version
59
88
  version: '13.0'
60
- description: Add beautiful, responsive GitHub repository cards to your Jekyll blog
61
- with live data from the GitHub API. Supports dark mode and works seamlessly with
62
- Jekyll themes like Chirpy.
89
+ - !ruby/object:Gem::Dependency
90
+ name: webmock
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.18'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.18'
103
+ description: Easily embed beautiful GitHub repository cards in your Jekyll site using
104
+ a simple Liquid tag. Supports light and dark themes.
63
105
  email:
64
106
  - rodolfo.olivieri3@gmail.com
65
107
  executables: []
66
108
  extensions: []
67
109
  extra_rdoc_files: []
68
110
  files:
111
+ - CHANGELOG.md
69
112
  - LICENSE
70
113
  - README.md
114
+ - Rakefile
71
115
  - assets/css/github-card.css
72
- - assets/js/github-card.js
73
- - lib/jekyll-github-card/jekyll-github-card.rb
116
+ - lib/jekyll-github-card.rb
117
+ - lib/jekyll-github-card/generator.rb
118
+ - lib/jekyll-github-card/tag.rb
74
119
  - lib/jekyll-github-card/version.rb
75
120
  homepage: https://github.com/r0x0d/jekyll-github-card
76
121
  licenses:
@@ -79,6 +124,8 @@ metadata:
79
124
  homepage_uri: https://github.com/r0x0d/jekyll-github-card
80
125
  source_code_uri: https://github.com/r0x0d/jekyll-github-card
81
126
  changelog_uri: https://github.com/r0x0d/jekyll-github-card/blob/main/CHANGELOG.md
127
+ rubygems_mfa_required: 'true'
128
+ post_install_message:
82
129
  rdoc_options: []
83
130
  require_paths:
84
131
  - lib
@@ -86,14 +133,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
133
  requirements:
87
134
  - - ">="
88
135
  - !ruby/object:Gem::Version
89
- version: 2.6.0
136
+ version: 2.7.0
90
137
  required_rubygems_version: !ruby/object:Gem::Requirement
91
138
  requirements:
92
139
  - - ">="
93
140
  - !ruby/object:Gem::Version
94
141
  version: '0'
95
142
  requirements: []
96
- rubygems_version: 3.6.9
143
+ rubygems_version: 3.4.19
144
+ signing_key:
97
145
  specification_version: 4
98
- summary: A Jekyll plugin to display beautiful GitHub repository cards
146
+ summary: A Jekyll plugin to display GitHub repository cards in your posts
99
147
  test_files: []
@@ -1,104 +0,0 @@
1
- window.createGitHubCard = async function(elementId, repo) {
2
- const element = document.getElementById(elementId);
3
-
4
- if (!element) {
5
- console.error('GitHub Card: Element with id "' + elementId + '" not found');
6
- return;
7
- }
8
-
9
- element.className = 'github-card loading';
10
- element.innerHTML = 'Loading repository...';
11
-
12
- try {
13
- const response = await fetch('https://api.github.com/repos/' + repo);
14
-
15
- if (!response.ok) {
16
- throw new Error('Repository not found');
17
- }
18
-
19
- const data = await response.json();
20
-
21
- function formatCount(count) {
22
- if (count >= 1000) {
23
- return (count / 1000).toFixed(1) + 'k';
24
- }
25
- return count.toString();
26
- }
27
-
28
- const languageColors = {
29
- 'JavaScript': '#f1e05a',
30
- 'TypeScript': '#3178c6',
31
- 'Python': '#3572A5',
32
- 'Java': '#b07219',
33
- 'Ruby': '#701516',
34
- 'Go': '#00ADD8',
35
- 'Rust': '#dea584',
36
- 'C++': '#f34b7d',
37
- 'C': '#555555',
38
- 'PHP': '#4F5D95',
39
- 'Swift': '#F05138',
40
- 'Kotlin': '#A97BFF',
41
- 'HTML': '#e34c26',
42
- 'CSS': '#563d7c',
43
- 'Shell': '#89e051',
44
- 'Dart': '#00B4AB',
45
- 'Scala': '#c22d40'
46
- };
47
-
48
- const languageColor = languageColors[data.language] || '#858585';
49
-
50
- function escapeHtml(text) {
51
- const div = document.createElement('div');
52
- div.textContent = text;
53
- return div.innerHTML;
54
- }
55
-
56
- element.className = 'github-card';
57
- element.innerHTML =
58
- '<div class="card-header">' +
59
- '<svg class="github-icon" viewBox="0 0 16 16">' +
60
- '<path d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path>' +
61
- '</svg>' +
62
- '<h3 class="repo-name">' + escapeHtml(data.full_name) + '</h3>' +
63
- '</div>' +
64
- '<p class="description">' +
65
- escapeHtml(data.description || 'No description provided') +
66
- '</p>' +
67
- '<div class="card-footer">' +
68
- '<span class="stat">' +
69
- '<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">' +
70
- '<path d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25z"></path>' +
71
- '</svg>' +
72
- formatCount(data.stargazers_count) +
73
- '</span>' +
74
- '<span class="stat">' +
75
- '<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">' +
76
- '<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75v-.878a2.25 2.25 0 111.5 0v.878a2.25 2.25 0 01-2.25 2.25h-1.5v2.128a2.251 2.251 0 11-1.5 0V8.5h-1.5A2.25 2.25 0 013 6.25v-.878a2.25 2.25 0 111.5 0zM5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm6.75.75a.75.75 0 100-1.5.75.75 0 000 1.5zm-3 8.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0z"></path>' +
77
- '</svg>' +
78
- formatCount(data.forks_count) +
79
- '</span>' +
80
- (data.language ?
81
- '<span class="stat">' +
82
- '<span class="language-dot" style="background-color: ' + languageColor + ';"></span>' +
83
- escapeHtml(data.language) +
84
- '</span>'
85
- : '') +
86
- '</div>';
87
-
88
- element.style.cursor = 'pointer';
89
- element.onclick = function() {
90
- window.open(data.html_url, '_blank');
91
- };
92
-
93
- } catch (error) {
94
- element.className = 'github-card';
95
- element.innerHTML = '<p class="error-message">Failed to load repository: ' + escapeHtml(repo) + '</p>';
96
- console.error('GitHub Card Error:', error);
97
- }
98
-
99
- function escapeHtml(text) {
100
- const div = document.createElement('div');
101
- div.textContent = text;
102
- return div.innerHTML;
103
- }
104
- };
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "jekyll-github-card/version"
4
-
5
- module Jekyll
6
- module GitHubCard
7
- class Error < StandardError; end
8
-
9
- # Tag for {% github owner/repo %}
10
- class GitHubCardTag < Liquid::Tag
11
- def initialize(tag_name, text, tokens)
12
- super
13
- @repo = text.strip
14
- end
15
-
16
- def render(context)
17
- # Generate a unique ID from the repo name
18
- id = @repo.gsub('/', '-').gsub(/[^a-zA-Z0-9\-]/, '')
19
-
20
- <<~HTML
21
- <div id="github-card-#{id}" class="github-card loading">Loading repository...</div>
22
- <script>
23
- (function() {
24
- function initCard() {
25
- if (typeof createGitHubCard === 'function') {
26
- createGitHubCard('github-card-#{id}', '#{@repo}');
27
- } else {
28
- setTimeout(initCard, 100);
29
- }
30
- }
31
-
32
- if (document.readyState === 'loading') {
33
- document.addEventListener('DOMContentLoaded', initCard);
34
- } else {
35
- initCard();
36
- }
37
- })();
38
- </script>
39
- HTML
40
- end
41
- end
42
-
43
- # Generator to copy assets to site
44
- class AssetsGenerator < Jekyll::Generator
45
- safe true
46
- priority :low
47
-
48
- def generate(site)
49
- # Get the gem's asset directory
50
- gem_dir = File.expand_path("../../", __dir__)
51
- assets_dir = File.join(gem_dir, "assets")
52
-
53
- return unless File.directory?(assets_dir)
54
-
55
- # Copy CSS
56
- css_source = File.join(assets_dir, "css", "github-card.css")
57
- css_dest = File.join(site.source, "assets", "css", "github-card.css")
58
-
59
- if File.exist?(css_source)
60
- FileUtils.mkdir_p(File.dirname(css_dest))
61
- FileUtils.cp(css_source, css_dest)
62
- site.static_files << Jekyll::StaticFile.new(
63
- site,
64
- site.source,
65
- "assets/css",
66
- "github-card.css"
67
- )
68
- end
69
-
70
- # Copy JS
71
- js_source = File.join(assets_dir, "js", "github-card.js")
72
- js_dest = File.join(site.source, "assets", "js", "github-card.js")
73
-
74
- if File.exist?(js_source)
75
- FileUtils.mkdir_p(File.dirname(js_dest))
76
- FileUtils.cp(js_source, js_dest)
77
- site.static_files << Jekyll::StaticFile.new(
78
- site,
79
- site.source,
80
- "assets/js",
81
- "github-card.js"
82
- )
83
- end
84
- end
85
- end
86
- end
87
- end
88
-
89
- Liquid::Template.register_tag('github', Jekyll::GitHubCard::GitHubCardTag)