jackdaw 1.0.3 → 1.0.4
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/CHANGELOG.md +9 -0
- data/exe/jackdaw +0 -1
- data/lib/jackdaw/builder.rb +13 -0
- data/lib/jackdaw/cli_helpers.rb +6 -1
- data/lib/jackdaw/commands/build.rb +1 -1
- data/lib/jackdaw/commands/serve.rb +2 -0
- data/lib/jackdaw/server.rb +42 -20
- data/lib/jackdaw/version.rb +1 -1
- data/usejackdaw.com.site/Gemfile +7 -0
- data/usejackdaw.com.site/Gemfile.lock +93 -0
- data/usejackdaw.com.site/site/assets/css/style.css +255 -33
- data/usejackdaw.com.site/site/assets/jackdaw.png +0 -0
- data/usejackdaw.com.site/site/src/content.page.md +0 -14
- data/usejackdaw.com.site/site/src/getting-started.page.md +0 -4
- data/usejackdaw.com.site/site/src/index.page.md +30 -6
- data/usejackdaw.com.site/site/templates/layout.html.erb +20 -0
- metadata +17 -3
- data/.DS_Store +0 -0
- data/TODO.md +0 -167
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2ca507791f3ec7ecffc5ada817cdf5d2db4f5d7ac4b21e25142931285ceef0dc
|
|
4
|
+
data.tar.gz: d3ffd9be8b6c37b337a554725e662db4a77fafa78163f283aa948e3d6fd8ec8c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3598cb14c96e229d9961270a6c10a495093082e7da614b87a92e2daf2da56c0a70f7cd57aeeb36477153a024af7b469eb7e2f7344b8bbb5d99efbc47f7c05055
|
|
7
|
+
data.tar.gz: a790cb63130eb2f3a575855e0b4a3b947840d6185af9c63825c892ed8ae47140c554dd627a0277e96aec6d2ca5a13f21f8f1c5eb7e1777243e0317a63e1c903f
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ All notable changes to Jackdaw will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.4] - 2026-01-08
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Fixed hot reload functionality in development server - browser now properly detects file changes and refreshes automatically
|
|
12
|
+
- Improved live reload middleware to use build counter instead of timestamps for more reliable change detection
|
|
13
|
+
- Fixed partial template changes not triggering rebuilds - changes to partials (e.g., `_nav.html.erb`, `_footer.html.erb`) now properly rebuild all affected pages
|
|
14
|
+
- Fixed build statistics accumulating across multiple rebuilds - stats now reset correctly for each build
|
|
15
|
+
- Added hot reload support for assets (CSS, JS, images) - changes to assets now trigger browser refresh with cache-busting
|
|
16
|
+
|
|
8
17
|
## [1.0.0] - 2026-01-06
|
|
9
18
|
|
|
10
19
|
### Added
|
data/exe/jackdaw
CHANGED
data/lib/jackdaw/builder.rb
CHANGED
|
@@ -15,6 +15,8 @@ module Jackdaw
|
|
|
15
15
|
|
|
16
16
|
# Run full build
|
|
17
17
|
def build
|
|
18
|
+
# Reset stats for this build
|
|
19
|
+
@stats = BuildStats.new
|
|
18
20
|
start_time = Time.now
|
|
19
21
|
|
|
20
22
|
# Clean if requested
|
|
@@ -137,9 +139,20 @@ module Jackdaw
|
|
|
137
139
|
layout_file = File.join(project.templates_dir, 'layout.html.erb')
|
|
138
140
|
return true if File.exist?(layout_file) && File.mtime(layout_file) > output_mtime
|
|
139
141
|
|
|
142
|
+
# Check if any partials are newer
|
|
143
|
+
return true if any_partial_newer?(output_mtime)
|
|
144
|
+
|
|
140
145
|
false
|
|
141
146
|
end
|
|
142
147
|
|
|
148
|
+
def any_partial_newer?(output_mtime)
|
|
149
|
+
# Get all partial files (starting with _)
|
|
150
|
+
partial_pattern = File.join(project.templates_dir, '_*.erb')
|
|
151
|
+
Dir.glob(partial_pattern).any? do |partial_file|
|
|
152
|
+
File.mtime(partial_file) > output_mtime
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
143
156
|
def needs_asset_copy?(asset_file)
|
|
144
157
|
# Always copy if clean build
|
|
145
158
|
return true if @options[:clean]
|
data/lib/jackdaw/cli_helpers.rb
CHANGED
|
@@ -27,7 +27,12 @@ module Jackdaw
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def header(text)
|
|
30
|
-
puts "
|
|
30
|
+
puts "#{COLORS[:bold]}#{COLORS[:magenta]}#{text}#{COLORS[:reset]}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def version_banner
|
|
34
|
+
puts "🐦 #{colorize('Jackdaw', :bold)} #{colorize("v#{Jackdaw::VERSION}", :cyan)}"
|
|
35
|
+
puts ''
|
|
31
36
|
end
|
|
32
37
|
end
|
|
33
38
|
end
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
module Jackdaw
|
|
@@ -15,6 +14,7 @@ module Jackdaw
|
|
|
15
14
|
def execute
|
|
16
15
|
return handle_missing_project unless @project.exists?
|
|
17
16
|
|
|
17
|
+
version_banner
|
|
18
18
|
header('🚀 Building your site...')
|
|
19
19
|
info('Cleaning output directory...') if @options[:clean]
|
|
20
20
|
|
data/lib/jackdaw/server.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
module Jackdaw
|
|
@@ -19,7 +18,7 @@ module Jackdaw
|
|
|
19
18
|
# Start the server
|
|
20
19
|
def start
|
|
21
20
|
# Initial build
|
|
22
|
-
puts "
|
|
21
|
+
puts "#{colorize('🚀 Building site...', :magenta)}"
|
|
23
22
|
stats = builder.build
|
|
24
23
|
show_build_stats(stats)
|
|
25
24
|
|
|
@@ -31,7 +30,13 @@ module Jackdaw
|
|
|
31
30
|
puts colorize('Press Ctrl+C to stop', :magenta)
|
|
32
31
|
puts ''
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
# Build and start the Rack app
|
|
34
|
+
app = rack_app
|
|
35
|
+
|
|
36
|
+
# Notify that initial build is complete (sets counter to 1)
|
|
37
|
+
notify_reload if @livereload
|
|
38
|
+
|
|
39
|
+
Rack::Handler::Puma.run(app, Port: port, Host: host, Silent: true)
|
|
35
40
|
end
|
|
36
41
|
|
|
37
42
|
def livereload?
|
|
@@ -41,11 +46,16 @@ module Jackdaw
|
|
|
41
46
|
private
|
|
42
47
|
|
|
43
48
|
def rack_app
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
static_server = StaticFileServer.new(project)
|
|
50
|
+
|
|
51
|
+
# Create the middleware instance if livereload is enabled
|
|
52
|
+
if @livereload
|
|
53
|
+
@livereload_middleware = LiveReloadMiddleware.new(static_server, self)
|
|
54
|
+
# Return an app that chains CommonLogger -> LiveReload -> StaticServer
|
|
55
|
+
Rack::CommonLogger.new(@livereload_middleware)
|
|
56
|
+
else
|
|
57
|
+
# No livereload, just CommonLogger -> StaticServer
|
|
58
|
+
Rack::CommonLogger.new(static_server)
|
|
49
59
|
end
|
|
50
60
|
end
|
|
51
61
|
|
|
@@ -89,8 +99,7 @@ module Jackdaw
|
|
|
89
99
|
end
|
|
90
100
|
|
|
91
101
|
def notify_reload
|
|
92
|
-
|
|
93
|
-
# For now, the LiveReloadMiddleware handles it with polling
|
|
102
|
+
@livereload_middleware&.update_build_time
|
|
94
103
|
end
|
|
95
104
|
|
|
96
105
|
def colorize(text, color)
|
|
@@ -138,7 +147,16 @@ module Jackdaw
|
|
|
138
147
|
content = File.read(path)
|
|
139
148
|
content_type = mime_type(path)
|
|
140
149
|
|
|
141
|
-
|
|
150
|
+
headers = {
|
|
151
|
+
'Content-Type' => content_type,
|
|
152
|
+
'Content-Length' => content.bytesize.to_s,
|
|
153
|
+
# Disable caching in development for assets
|
|
154
|
+
'Cache-Control' => 'no-cache, no-store, must-revalidate',
|
|
155
|
+
'Pragma' => 'no-cache',
|
|
156
|
+
'Expires' => '0'
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
[200, headers, [content]]
|
|
142
160
|
end
|
|
143
161
|
|
|
144
162
|
def mime_type(path)
|
|
@@ -161,16 +179,17 @@ module Jackdaw
|
|
|
161
179
|
RELOAD_SCRIPT = <<~JS
|
|
162
180
|
<script>
|
|
163
181
|
(function() {
|
|
164
|
-
let
|
|
182
|
+
let lastBuild = null;
|
|
165
183
|
setInterval(function() {
|
|
166
|
-
fetch('/
|
|
184
|
+
fetch('/__jackdaw_reload_check')
|
|
167
185
|
.then(r => r.json())
|
|
168
186
|
.then(data => {
|
|
169
|
-
if (data.lastBuild >
|
|
187
|
+
if (lastBuild !== null && data.lastBuild > lastBuild) {
|
|
170
188
|
console.log('Jackdaw: Reloading page...');
|
|
171
|
-
|
|
189
|
+
// Force hard reload to bypass cache for CSS/JS changes
|
|
190
|
+
window.location.reload(true);
|
|
172
191
|
}
|
|
173
|
-
|
|
192
|
+
lastBuild = data.lastBuild;
|
|
174
193
|
})
|
|
175
194
|
.catch(() => {});
|
|
176
195
|
}, 1000);
|
|
@@ -181,16 +200,20 @@ module Jackdaw
|
|
|
181
200
|
def initialize(app, server)
|
|
182
201
|
@app = app
|
|
183
202
|
@server = server
|
|
184
|
-
@
|
|
203
|
+
@build_count = 0
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def update_build_time
|
|
207
|
+
@build_count += 1
|
|
185
208
|
end
|
|
186
209
|
|
|
187
210
|
def call(env)
|
|
188
211
|
# Handle reload check endpoint
|
|
189
|
-
if env['PATH_INFO'] == '/
|
|
212
|
+
if env['PATH_INFO'] == '/__jackdaw_reload_check'
|
|
190
213
|
return [
|
|
191
214
|
200,
|
|
192
215
|
{ 'Content-Type' => 'application/json' },
|
|
193
|
-
[JSON.generate({ lastBuild: @
|
|
216
|
+
[JSON.generate({ lastBuild: @build_count })]
|
|
194
217
|
]
|
|
195
218
|
end
|
|
196
219
|
|
|
@@ -203,7 +226,6 @@ module Jackdaw
|
|
|
203
226
|
|
|
204
227
|
if body.include?('</body>')
|
|
205
228
|
body = body.sub('</body>', "#{RELOAD_SCRIPT}</body>")
|
|
206
|
-
@last_build = Time.now
|
|
207
229
|
headers['Content-Length'] = body.bytesize.to_s
|
|
208
230
|
response = [body]
|
|
209
231
|
end
|
data/lib/jackdaw/version.rb
CHANGED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: ..
|
|
3
|
+
specs:
|
|
4
|
+
jackdaw (1.0.4)
|
|
5
|
+
kramdown (~> 2.4)
|
|
6
|
+
kramdown-parser-gfm (~> 1.1)
|
|
7
|
+
listen (~> 3.9)
|
|
8
|
+
logger (~> 1.7)
|
|
9
|
+
parallel (~> 1.24)
|
|
10
|
+
puma (~> 6.4)
|
|
11
|
+
rack (~> 3.0)
|
|
12
|
+
rouge (~> 4.2)
|
|
13
|
+
thor (~> 1.3)
|
|
14
|
+
|
|
15
|
+
GEM
|
|
16
|
+
remote: https://rubygems.org/
|
|
17
|
+
specs:
|
|
18
|
+
ffi (1.17.3)
|
|
19
|
+
ffi (1.17.3-aarch64-linux-gnu)
|
|
20
|
+
ffi (1.17.3-aarch64-linux-musl)
|
|
21
|
+
ffi (1.17.3-arm-linux-gnu)
|
|
22
|
+
ffi (1.17.3-arm-linux-musl)
|
|
23
|
+
ffi (1.17.3-arm64-darwin)
|
|
24
|
+
ffi (1.17.3-x86-linux-gnu)
|
|
25
|
+
ffi (1.17.3-x86-linux-musl)
|
|
26
|
+
ffi (1.17.3-x86_64-darwin)
|
|
27
|
+
ffi (1.17.3-x86_64-linux-gnu)
|
|
28
|
+
ffi (1.17.3-x86_64-linux-musl)
|
|
29
|
+
kramdown (2.5.1)
|
|
30
|
+
rexml (>= 3.3.9)
|
|
31
|
+
kramdown-parser-gfm (1.1.0)
|
|
32
|
+
kramdown (~> 2.0)
|
|
33
|
+
listen (3.9.0)
|
|
34
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
35
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
|
36
|
+
logger (1.7.0)
|
|
37
|
+
nio4r (2.7.5)
|
|
38
|
+
parallel (1.27.0)
|
|
39
|
+
puma (6.6.1)
|
|
40
|
+
nio4r (~> 2.0)
|
|
41
|
+
rack (3.2.4)
|
|
42
|
+
rb-fsevent (0.11.2)
|
|
43
|
+
rb-inotify (0.11.1)
|
|
44
|
+
ffi (~> 1.0)
|
|
45
|
+
rexml (3.4.4)
|
|
46
|
+
rouge (4.7.0)
|
|
47
|
+
thor (1.5.0)
|
|
48
|
+
|
|
49
|
+
PLATFORMS
|
|
50
|
+
aarch64-linux-gnu
|
|
51
|
+
aarch64-linux-musl
|
|
52
|
+
arm-linux-gnu
|
|
53
|
+
arm-linux-musl
|
|
54
|
+
arm64-darwin
|
|
55
|
+
ruby
|
|
56
|
+
x86-linux-gnu
|
|
57
|
+
x86-linux-musl
|
|
58
|
+
x86_64-darwin
|
|
59
|
+
x86_64-linux-gnu
|
|
60
|
+
x86_64-linux-musl
|
|
61
|
+
|
|
62
|
+
DEPENDENCIES
|
|
63
|
+
jackdaw!
|
|
64
|
+
|
|
65
|
+
CHECKSUMS
|
|
66
|
+
ffi (1.17.3) sha256=0e9f39f7bb3934f77ad6feab49662be77e87eedcdeb2a3f5c0234c2938563d4c
|
|
67
|
+
ffi (1.17.3-aarch64-linux-gnu) sha256=28ad573df26560f0aedd8a90c3371279a0b2bd0b4e834b16a2baa10bd7a97068
|
|
68
|
+
ffi (1.17.3-aarch64-linux-musl) sha256=020b33b76775b1abacc3b7d86b287cef3251f66d747092deec592c7f5df764b2
|
|
69
|
+
ffi (1.17.3-arm-linux-gnu) sha256=5bd4cea83b68b5ec0037f99c57d5ce2dd5aa438f35decc5ef68a7d085c785668
|
|
70
|
+
ffi (1.17.3-arm-linux-musl) sha256=0d7626bb96265f9af78afa33e267d71cfef9d9a8eb8f5525344f8da6c7d76053
|
|
71
|
+
ffi (1.17.3-arm64-darwin) sha256=0c690555d4cee17a7f07c04d59df39b2fba74ec440b19da1f685c6579bb0717f
|
|
72
|
+
ffi (1.17.3-x86-linux-gnu) sha256=868a88fcaf5186c3a46b7c7c2b2c34550e1e61a405670ab23f5b6c9971529089
|
|
73
|
+
ffi (1.17.3-x86-linux-musl) sha256=f0286aa6ef40605cf586e61406c446de34397b85dbb08cc99fdaddaef8343945
|
|
74
|
+
ffi (1.17.3-x86_64-darwin) sha256=1f211811eb5cfaa25998322cdd92ab104bfbd26d1c4c08471599c511f2c00bb5
|
|
75
|
+
ffi (1.17.3-x86_64-linux-gnu) sha256=3746b01f677aae7b16dc1acb7cb3cc17b3e35bdae7676a3f568153fb0e2c887f
|
|
76
|
+
ffi (1.17.3-x86_64-linux-musl) sha256=086b221c3a68320b7564066f46fed23449a44f7a1935f1fe5a245bd89d9aea56
|
|
77
|
+
jackdaw (1.0.4)
|
|
78
|
+
kramdown (2.5.1) sha256=87bbb6abd9d3cebe4fc1f33e367c392b4500e6f8fa19dd61c0972cf4afe7368c
|
|
79
|
+
kramdown-parser-gfm (1.1.0) sha256=fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729
|
|
80
|
+
listen (3.9.0) sha256=db9e4424e0e5834480385197c139cb6b0ae0ef28cc13310cfd1ca78377d59c67
|
|
81
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
82
|
+
nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
|
|
83
|
+
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
|
|
84
|
+
puma (6.6.1) sha256=b9b56e4a4ea75d1bfa6d9e1972ee2c9f43d0883f011826d914e8e37b3694ea1e
|
|
85
|
+
rack (3.2.4) sha256=5d74b6f75082a643f43c1e76b419c40f0e5527fcfee1e669ac1e6b73c0ccb6f6
|
|
86
|
+
rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe
|
|
87
|
+
rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e
|
|
88
|
+
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
|
89
|
+
rouge (4.7.0) sha256=dba5896715c0325c362e895460a6d350803dbf6427454f49a47500f3193ea739
|
|
90
|
+
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
|
|
91
|
+
|
|
92
|
+
BUNDLED WITH
|
|
93
|
+
4.0.3
|
|
@@ -5,11 +5,29 @@
|
|
|
5
5
|
box-sizing: border-box;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
:root {
|
|
9
|
+
--primary: #3498db;
|
|
10
|
+
--primary-dark: #2980b9;
|
|
11
|
+
--secondary: #9b59b6;
|
|
12
|
+
--dark: #2c3e50;
|
|
13
|
+
--darker: #1a252f;
|
|
14
|
+
--light: #ecf0f1;
|
|
15
|
+
--accent: #e74c3c;
|
|
16
|
+
--success: #2ecc71;
|
|
17
|
+
--gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
18
|
+
--gradient-alt: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
19
|
+
--shadow-sm: 0 2px 4px rgba(0,0,0,0.1);
|
|
20
|
+
--shadow-md: 0 4px 6px rgba(0,0,0,0.1);
|
|
21
|
+
--shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
|
|
22
|
+
--shadow-xl: 0 20px 25px rgba(0,0,0,0.15);
|
|
23
|
+
}
|
|
24
|
+
|
|
8
25
|
body {
|
|
9
26
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
10
27
|
line-height: 1.6;
|
|
11
28
|
color: #333;
|
|
12
|
-
background: #
|
|
29
|
+
background: linear-gradient(to bottom, #ffffff 0%, #f8f9fa 100%);
|
|
30
|
+
min-height: 100vh;
|
|
13
31
|
}
|
|
14
32
|
|
|
15
33
|
/* Container */
|
|
@@ -21,10 +39,14 @@ body {
|
|
|
21
39
|
|
|
22
40
|
/* Navigation */
|
|
23
41
|
.navbar {
|
|
24
|
-
background:
|
|
42
|
+
background: var(--gradient);
|
|
25
43
|
color: white;
|
|
26
44
|
padding: 1rem 0;
|
|
27
|
-
box-shadow:
|
|
45
|
+
box-shadow: var(--shadow-lg);
|
|
46
|
+
position: sticky;
|
|
47
|
+
top: 0;
|
|
48
|
+
z-index: 100;
|
|
49
|
+
backdrop-filter: blur(10px);
|
|
28
50
|
}
|
|
29
51
|
|
|
30
52
|
.navbar .container {
|
|
@@ -41,38 +63,85 @@ body {
|
|
|
41
63
|
display: flex;
|
|
42
64
|
align-items: center;
|
|
43
65
|
gap: 0.75rem;
|
|
66
|
+
transition: transform 0.3s ease;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.nav-brand a:hover {
|
|
70
|
+
transform: scale(1.05);
|
|
44
71
|
}
|
|
45
72
|
|
|
46
73
|
.nav-brand .logo {
|
|
47
74
|
height: 40px;
|
|
48
75
|
width: auto;
|
|
76
|
+
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2));
|
|
77
|
+
transition: transform 0.3s ease;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.nav-brand a:hover .logo {
|
|
81
|
+
transform: rotate(-10deg);
|
|
49
82
|
}
|
|
50
83
|
|
|
51
84
|
.nav-menu {
|
|
52
85
|
display: flex;
|
|
53
86
|
list-style: none;
|
|
54
87
|
gap: 2rem;
|
|
88
|
+
align-items: center;
|
|
89
|
+
margin: 0;
|
|
55
90
|
}
|
|
56
91
|
|
|
57
92
|
.nav-menu a {
|
|
58
93
|
color: white;
|
|
59
94
|
text-decoration: none;
|
|
60
|
-
transition:
|
|
95
|
+
transition: all 0.3s ease;
|
|
96
|
+
padding: 0.5rem 1rem;
|
|
97
|
+
border-radius: 6px;
|
|
98
|
+
position: relative;
|
|
99
|
+
font-weight: 500;
|
|
100
|
+
display: inline-block;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.nav-menu a::after {
|
|
104
|
+
content: '';
|
|
105
|
+
position: absolute;
|
|
106
|
+
bottom: 0;
|
|
107
|
+
left: 50%;
|
|
108
|
+
width: 0;
|
|
109
|
+
height: 2px;
|
|
110
|
+
background: #667eea;
|
|
111
|
+
transition: all 0.3s ease;
|
|
112
|
+
transform: translateX(-50%);
|
|
61
113
|
}
|
|
62
114
|
|
|
63
115
|
.nav-menu a:hover {
|
|
64
|
-
|
|
116
|
+
background: white;
|
|
117
|
+
color: #667eea;
|
|
118
|
+
transform: translateY(-2px);
|
|
119
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.nav-menu a:hover::after {
|
|
123
|
+
width: 80%;
|
|
65
124
|
}
|
|
66
125
|
|
|
67
126
|
.nav-menu .github {
|
|
68
|
-
background:
|
|
127
|
+
background: white;
|
|
128
|
+
color: #667eea;
|
|
69
129
|
padding: 0.5rem 1rem;
|
|
70
|
-
border-radius:
|
|
130
|
+
border-radius: 6px;
|
|
131
|
+
font-weight: 600;
|
|
132
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.nav-menu .github::after {
|
|
136
|
+
display: none;
|
|
71
137
|
}
|
|
72
138
|
|
|
73
139
|
.nav-menu .github:hover {
|
|
74
|
-
background:
|
|
75
|
-
color:
|
|
140
|
+
background: rgba(255,255,255,0.95);
|
|
141
|
+
color: #764ba2;
|
|
142
|
+
transform: translateY(-2px);
|
|
143
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
|
144
|
+
text-shadow: none;
|
|
76
145
|
}
|
|
77
146
|
|
|
78
147
|
/* Main Content */
|
|
@@ -82,7 +151,26 @@ main {
|
|
|
82
151
|
}
|
|
83
152
|
|
|
84
153
|
.content {
|
|
85
|
-
max-width:
|
|
154
|
+
max-width: 1000px;
|
|
155
|
+
background: white;
|
|
156
|
+
padding: 2rem;
|
|
157
|
+
border-radius: 12px;
|
|
158
|
+
box-shadow: var(--shadow-md);
|
|
159
|
+
margin: 0 auto 2rem;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Hero Section */
|
|
163
|
+
.hero {
|
|
164
|
+
text-align: center;
|
|
165
|
+
padding: 4rem 0;
|
|
166
|
+
margin-bottom: 3rem;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.hero img {
|
|
170
|
+
max-width: 300px;
|
|
171
|
+
height: auto;
|
|
172
|
+
margin: 0 auto 2rem;
|
|
173
|
+
filter: drop-shadow(0 10px 20px rgba(0,0,0,0.1));
|
|
86
174
|
}
|
|
87
175
|
|
|
88
176
|
/* Typography */
|
|
@@ -91,18 +179,41 @@ h1, h2, h3, h4, h5, h6 {
|
|
|
91
179
|
margin-bottom: 1rem;
|
|
92
180
|
line-height: 1.2;
|
|
93
181
|
color: #2c3e50;
|
|
182
|
+
font-weight: 800;
|
|
94
183
|
}
|
|
95
184
|
|
|
96
185
|
h1 {
|
|
97
186
|
font-size: 2.5rem;
|
|
98
|
-
border-bottom: 3px solid #3498db;
|
|
99
187
|
padding-bottom: 0.5rem;
|
|
188
|
+
position: relative;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
h1::after {
|
|
192
|
+
content: '';
|
|
193
|
+
position: absolute;
|
|
194
|
+
bottom: 0;
|
|
195
|
+
left: 0;
|
|
196
|
+
width: 100px;
|
|
197
|
+
height: 4px;
|
|
198
|
+
background: var(--gradient);
|
|
199
|
+
border-radius: 2px;
|
|
100
200
|
}
|
|
101
201
|
|
|
102
202
|
h2 {
|
|
103
203
|
font-size: 2rem;
|
|
104
|
-
border-bottom: 2px solid #ecf0f1;
|
|
105
204
|
padding-bottom: 0.5rem;
|
|
205
|
+
position: relative;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
h2::after {
|
|
209
|
+
content: '';
|
|
210
|
+
position: absolute;
|
|
211
|
+
bottom: 0;
|
|
212
|
+
left: 0;
|
|
213
|
+
width: 60px;
|
|
214
|
+
height: 3px;
|
|
215
|
+
background: var(--gradient);
|
|
216
|
+
border-radius: 2px;
|
|
106
217
|
}
|
|
107
218
|
|
|
108
219
|
h3 {
|
|
@@ -111,34 +222,66 @@ h3 {
|
|
|
111
222
|
|
|
112
223
|
p {
|
|
113
224
|
margin-bottom: 1rem;
|
|
225
|
+
line-height: 1.8;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
strong {
|
|
229
|
+
color: var(--dark);
|
|
230
|
+
font-weight: 600;
|
|
114
231
|
}
|
|
115
232
|
|
|
116
233
|
a {
|
|
117
|
-
color:
|
|
234
|
+
color: var(--primary);
|
|
118
235
|
text-decoration: none;
|
|
236
|
+
position: relative;
|
|
237
|
+
transition: color 0.3s ease;
|
|
119
238
|
}
|
|
120
239
|
|
|
121
240
|
a:hover {
|
|
122
|
-
|
|
241
|
+
color: var(--primary-dark);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Feature Lists */
|
|
245
|
+
ul li strong {
|
|
246
|
+
background: var(--gradient);
|
|
247
|
+
-webkit-background-clip: text;
|
|
248
|
+
-webkit-text-fill-color: transparent;
|
|
249
|
+
background-clip: text;
|
|
123
250
|
}
|
|
124
251
|
|
|
125
252
|
/* Code */
|
|
126
253
|
code {
|
|
127
|
-
background: #
|
|
128
|
-
padding: 0.2rem 0.
|
|
129
|
-
border-radius:
|
|
254
|
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
255
|
+
padding: 0.2rem 0.6rem;
|
|
256
|
+
border-radius: 6px;
|
|
130
257
|
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
|
131
258
|
font-size: 0.9em;
|
|
132
259
|
color: #e83e8c;
|
|
260
|
+
font-weight: 600;
|
|
261
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
133
262
|
}
|
|
134
263
|
|
|
135
264
|
pre {
|
|
136
265
|
background: #2c3e50;
|
|
137
266
|
color: #ecf0f1;
|
|
138
|
-
padding:
|
|
139
|
-
border-radius:
|
|
267
|
+
padding: 2rem;
|
|
268
|
+
border-radius: 12px;
|
|
140
269
|
overflow-x: auto;
|
|
141
270
|
margin: 1.5rem 0;
|
|
271
|
+
box-shadow: var(--shadow-lg);
|
|
272
|
+
position: relative;
|
|
273
|
+
border: 1px solid rgba(102, 126, 234, 0.3);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
pre::before {
|
|
277
|
+
content: '';
|
|
278
|
+
position: absolute;
|
|
279
|
+
top: 0;
|
|
280
|
+
left: 0;
|
|
281
|
+
right: 0;
|
|
282
|
+
height: 4px;
|
|
283
|
+
background: var(--gradient);
|
|
284
|
+
border-radius: 12px 12px 0 0;
|
|
142
285
|
}
|
|
143
286
|
|
|
144
287
|
pre code {
|
|
@@ -146,6 +289,9 @@ pre code {
|
|
|
146
289
|
color: inherit;
|
|
147
290
|
padding: 0;
|
|
148
291
|
font-size: 0.9rem;
|
|
292
|
+
box-shadow: none;
|
|
293
|
+
position: relative;
|
|
294
|
+
z-index: 1;
|
|
149
295
|
}
|
|
150
296
|
|
|
151
297
|
/* Tables */
|
|
@@ -153,21 +299,26 @@ table {
|
|
|
153
299
|
width: 100%;
|
|
154
300
|
border-collapse: collapse;
|
|
155
301
|
margin: 1.5rem 0;
|
|
302
|
+
box-shadow: var(--shadow-md);
|
|
303
|
+
border-radius: 8px;
|
|
304
|
+
overflow: hidden;
|
|
156
305
|
}
|
|
157
306
|
|
|
158
307
|
th, td {
|
|
159
|
-
padding:
|
|
160
|
-
border: 1px solid #
|
|
308
|
+
padding: 1rem;
|
|
309
|
+
border: 1px solid #e9ecef;
|
|
161
310
|
text-align: left;
|
|
162
311
|
}
|
|
163
312
|
|
|
164
313
|
th {
|
|
165
|
-
background:
|
|
314
|
+
background: var(--gradient);
|
|
315
|
+
color: white;
|
|
166
316
|
font-weight: 600;
|
|
167
317
|
}
|
|
168
318
|
|
|
169
319
|
tr:hover {
|
|
170
|
-
background: #f8f9fa;
|
|
320
|
+
background: linear-gradient(to right, #f8f9fa 0%, #e9ecef 100%);
|
|
321
|
+
transition: background 0.3s ease;
|
|
171
322
|
}
|
|
172
323
|
|
|
173
324
|
/* Lists */
|
|
@@ -178,47 +329,90 @@ ul, ol {
|
|
|
178
329
|
|
|
179
330
|
li {
|
|
180
331
|
margin-bottom: 0.5rem;
|
|
332
|
+
padding-left: 0.5rem;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
ul li::marker {
|
|
336
|
+
color: var(--primary);
|
|
337
|
+
font-weight: bold;
|
|
181
338
|
}
|
|
182
339
|
|
|
183
340
|
/* Blockquotes */
|
|
184
341
|
blockquote {
|
|
185
|
-
border-left: 4px solid
|
|
186
|
-
padding
|
|
342
|
+
border-left: 4px solid var(--primary);
|
|
343
|
+
padding: 1rem 1.5rem;
|
|
187
344
|
margin: 1.5rem 0;
|
|
188
|
-
|
|
345
|
+
background: linear-gradient(to right, #f8f9fa 0%, #ffffff 100%);
|
|
346
|
+
border-radius: 0 8px 8px 0;
|
|
347
|
+
box-shadow: var(--shadow-sm);
|
|
189
348
|
font-style: italic;
|
|
349
|
+
color: #555;
|
|
190
350
|
}
|
|
191
351
|
|
|
192
352
|
/* Horizontal Rule */
|
|
193
353
|
hr {
|
|
194
354
|
border: none;
|
|
195
|
-
|
|
196
|
-
|
|
355
|
+
height: 3px;
|
|
356
|
+
background: var(--gradient);
|
|
357
|
+
margin: 3rem 0;
|
|
358
|
+
border-radius: 2px;
|
|
197
359
|
}
|
|
198
360
|
|
|
199
361
|
/* Footer */
|
|
200
362
|
.footer {
|
|
201
|
-
background:
|
|
363
|
+
background: var(--gradient);
|
|
202
364
|
color: white;
|
|
203
|
-
padding:
|
|
365
|
+
padding: 3rem 0;
|
|
204
366
|
margin-top: 4rem;
|
|
205
367
|
text-align: center;
|
|
368
|
+
box-shadow: 0 -4px 6px rgba(0,0,0,0.1);
|
|
206
369
|
}
|
|
207
370
|
|
|
208
371
|
.footer a {
|
|
209
|
-
color:
|
|
372
|
+
color: white;
|
|
373
|
+
text-decoration: underline;
|
|
374
|
+
transition: all 0.3s ease;
|
|
210
375
|
}
|
|
211
376
|
|
|
212
377
|
.footer a:hover {
|
|
213
|
-
|
|
378
|
+
transform: translateY(-2px);
|
|
379
|
+
text-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
214
380
|
}
|
|
215
381
|
|
|
216
382
|
.copyright {
|
|
217
383
|
margin-top: 1rem;
|
|
218
|
-
color:
|
|
384
|
+
color: rgba(255,255,255,0.8);
|
|
219
385
|
font-size: 0.9rem;
|
|
220
386
|
}
|
|
221
387
|
|
|
388
|
+
/* Badges & Tags */
|
|
389
|
+
.badge {
|
|
390
|
+
display: inline-block;
|
|
391
|
+
padding: 0.25rem 0.75rem;
|
|
392
|
+
background: var(--gradient);
|
|
393
|
+
color: white;
|
|
394
|
+
border-radius: 20px;
|
|
395
|
+
font-size: 0.85rem;
|
|
396
|
+
font-weight: 600;
|
|
397
|
+
box-shadow: var(--shadow-sm);
|
|
398
|
+
margin: 0.25rem;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/* Cards */
|
|
402
|
+
.card {
|
|
403
|
+
background: white;
|
|
404
|
+
border-radius: 12px;
|
|
405
|
+
padding: 2rem;
|
|
406
|
+
box-shadow: var(--shadow-md);
|
|
407
|
+
transition: all 0.3s ease;
|
|
408
|
+
margin-bottom: 2rem;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.card:hover {
|
|
412
|
+
transform: translateY(-4px);
|
|
413
|
+
box-shadow: var(--shadow-xl);
|
|
414
|
+
}
|
|
415
|
+
|
|
222
416
|
/* Responsive */
|
|
223
417
|
@media (max-width: 768px) {
|
|
224
418
|
.navbar .container {
|
|
@@ -239,4 +433,32 @@ hr {
|
|
|
239
433
|
h2 {
|
|
240
434
|
font-size: 1.5rem;
|
|
241
435
|
}
|
|
436
|
+
|
|
437
|
+
.content {
|
|
438
|
+
padding: 1.5rem;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.hero {
|
|
442
|
+
padding: 2rem 0;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.hero img {
|
|
446
|
+
max-width: 200px;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Animations */
|
|
451
|
+
@keyframes fadeIn {
|
|
452
|
+
from {
|
|
453
|
+
opacity: 0;
|
|
454
|
+
transform: translateY(20px);
|
|
455
|
+
}
|
|
456
|
+
to {
|
|
457
|
+
opacity: 1;
|
|
458
|
+
transform: translateY(0);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
main {
|
|
463
|
+
animation: fadeIn 0.6s ease-out;
|
|
242
464
|
}
|
|
Binary file
|
|
@@ -143,26 +143,12 @@ You can include HTML:
|
|
|
143
143
|
The first paragraph becomes the excerpt:
|
|
144
144
|
|
|
145
145
|
```markdown
|
|
146
|
-
---
|
|
147
|
-
title: My Post
|
|
148
|
-
---
|
|
149
146
|
|
|
150
147
|
This paragraph is automatically used as the excerpt.
|
|
151
148
|
|
|
152
149
|
This is regular content.
|
|
153
150
|
```
|
|
154
151
|
|
|
155
|
-
Or set it manually:
|
|
156
|
-
|
|
157
|
-
```markdown
|
|
158
|
-
---
|
|
159
|
-
title: My Post
|
|
160
|
-
excerpt: Custom excerpt here
|
|
161
|
-
---
|
|
162
|
-
|
|
163
|
-
Content...
|
|
164
|
-
```
|
|
165
|
-
|
|
166
152
|
## Nested Content
|
|
167
153
|
|
|
168
154
|
Organize with directories:
|
|
@@ -150,10 +150,6 @@ jackdaw create blog "Building Fast Sites"
|
|
|
150
150
|
3. **Edit the content** (`site/src/2026-01-06-building-fast-sites.blog.md`):
|
|
151
151
|
|
|
152
152
|
```markdown
|
|
153
|
-
---
|
|
154
|
-
title: Building Fast Sites
|
|
155
|
-
excerpt: Learn how to build lightning-fast static sites
|
|
156
|
-
---
|
|
157
153
|
|
|
158
154
|
# Building Fast Sites
|
|
159
155
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
<div
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<div class="hero" markdown="1">
|
|
2
|
+
|
|
3
|
+
<img src="/jackdaw.png" alt="Jackdaw">
|
|
4
|
+
|
|
5
|
+
<p style="font-size: 1.3rem; color: #666; margin: 1rem 0;"><strong>Lightning-fast static site generator for Ruby</strong></p>
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
</div>
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
<div class="content" markdown="1">
|
|
8
10
|
|
|
9
|
-
Jackdaw is a minimal, fast static site generator that
|
|
11
|
+
Jackdaw is a minimal, fast static site generator that emphasises:
|
|
10
12
|
|
|
11
13
|
- **Speed** - Build 600 files in under 1 second with parallel processing
|
|
12
14
|
- **Convention over configuration** - Zero config required to get started
|
|
@@ -27,25 +29,33 @@ cd my-blog.site
|
|
|
27
29
|
jackdaw serve
|
|
28
30
|
```
|
|
29
31
|
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div class="content" markdown="1">
|
|
35
|
+
|
|
30
36
|
## Why Jackdaw?
|
|
31
37
|
|
|
32
38
|
### ⚡️ Blazing Fast
|
|
39
|
+
|
|
33
40
|
- Parallel processing for maximum speed
|
|
34
41
|
- Incremental builds - only rebuild what changed
|
|
35
42
|
- 693 files/second full build, 16,280 files/second incremental
|
|
36
43
|
|
|
37
44
|
### 🎯 Convention Over Configuration
|
|
45
|
+
|
|
38
46
|
- No configuration files needed
|
|
39
47
|
- Intuitive project structure
|
|
40
48
|
- Smart defaults that just work
|
|
41
49
|
|
|
42
50
|
### 🛠 Great Developer Experience
|
|
51
|
+
|
|
43
52
|
- Live reload development server
|
|
44
53
|
- Helpful CLI commands
|
|
45
54
|
- Clear error messages
|
|
46
55
|
- Ruby 4.0 ready
|
|
47
56
|
|
|
48
57
|
### 📦 Everything Included
|
|
58
|
+
|
|
49
59
|
- Markdown with GitHub-flavored syntax
|
|
50
60
|
- Syntax highlighting with Rouge
|
|
51
61
|
- Partials and layouts
|
|
@@ -53,6 +63,10 @@ jackdaw serve
|
|
|
53
63
|
- Sitemap generation
|
|
54
64
|
- SEO helpers
|
|
55
65
|
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="content" markdown="1">
|
|
69
|
+
|
|
56
70
|
## Key Features
|
|
57
71
|
|
|
58
72
|
- **Markdown** - Write content in Markdown
|
|
@@ -65,6 +79,10 @@ jackdaw serve
|
|
|
65
79
|
- **Sitemap** - SEO-friendly sitemap.xml
|
|
66
80
|
- **Zero Config** - Convention over configuration
|
|
67
81
|
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="content" markdown="1">
|
|
85
|
+
|
|
68
86
|
## Learn More
|
|
69
87
|
|
|
70
88
|
- [Installation](/installation.html) - Get Jackdaw up and running
|
|
@@ -73,6 +91,10 @@ jackdaw serve
|
|
|
73
91
|
- [Templates](/templates.html) - Master templating
|
|
74
92
|
- [Content](/content.html) - Write and organize content
|
|
75
93
|
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="content" markdown="1">
|
|
97
|
+
|
|
76
98
|
## Performance Benchmarks
|
|
77
99
|
|
|
78
100
|
| Operation | Time | Files/Second |
|
|
@@ -81,6 +103,8 @@ jackdaw serve
|
|
|
81
103
|
| Incremental build | 0.04s | 16,280 |
|
|
82
104
|
| Cold start | 0.69s | 870 |
|
|
83
105
|
|
|
106
|
+
</div>
|
|
107
|
+
|
|
84
108
|
## Open Source
|
|
85
109
|
|
|
86
110
|
Jackdaw is MIT licensed and available on [GitHub](https://github.com/yourusername/jackdaw).
|
|
@@ -7,6 +7,26 @@
|
|
|
7
7
|
<% if defined?(description) && description %>
|
|
8
8
|
<meta name="description" content="<%= description %>">
|
|
9
9
|
<% end %>
|
|
10
|
+
|
|
11
|
+
<!-- Favicons -->
|
|
12
|
+
<link rel="apple-touch-icon" sizes="57x57" href="/favicon/apple-icon-57x57.png">
|
|
13
|
+
<link rel="apple-touch-icon" sizes="60x60" href="/favicon/apple-icon-60x60.png">
|
|
14
|
+
<link rel="apple-touch-icon" sizes="72x72" href="/favicon/apple-icon-72x72.png">
|
|
15
|
+
<link rel="apple-touch-icon" sizes="76x76" href="/favicon/apple-icon-76x76.png">
|
|
16
|
+
<link rel="apple-touch-icon" sizes="114x114" href="/favicon/apple-icon-114x114.png">
|
|
17
|
+
<link rel="apple-touch-icon" sizes="120x120" href="/favicon/apple-icon-120x120.png">
|
|
18
|
+
<link rel="apple-touch-icon" sizes="144x144" href="/favicon/apple-icon-144x144.png">
|
|
19
|
+
<link rel="apple-touch-icon" sizes="152x152" href="/favicon/apple-icon-152x152.png">
|
|
20
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-icon-180x180.png">
|
|
21
|
+
<link rel="icon" type="image/png" sizes="192x192" href="/favicon/android-icon-192x192.png">
|
|
22
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
|
|
23
|
+
<link rel="icon" type="image/png" sizes="96x96" href="/favicon/favicon-96x96.png">
|
|
24
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
|
|
25
|
+
<link rel="manifest" href="/favicon/manifest.json">
|
|
26
|
+
<meta name="msapplication-TileColor" content="#667eea">
|
|
27
|
+
<meta name="msapplication-TileImage" content="/favicon/ms-icon-144x144.png">
|
|
28
|
+
<meta name="theme-color" content="#667eea">
|
|
29
|
+
|
|
10
30
|
<link rel="stylesheet" href="/css/style.css">
|
|
11
31
|
<link rel="alternate" type="application/rss+xml" title="Jackdaw RSS" href="/feed.xml">
|
|
12
32
|
</head>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jackdaw
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nigel Brookes-Thomas
|
|
@@ -51,6 +51,20 @@ dependencies:
|
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '3.9'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: logger
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.7'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.7'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
69
|
name: parallel
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -131,12 +145,10 @@ executables:
|
|
|
131
145
|
extensions: []
|
|
132
146
|
extra_rdoc_files: []
|
|
133
147
|
files:
|
|
134
|
-
- ".DS_Store"
|
|
135
148
|
- ".rubocop.yml"
|
|
136
149
|
- CHANGELOG.md
|
|
137
150
|
- README.md
|
|
138
151
|
- Rakefile
|
|
139
|
-
- TODO.md
|
|
140
152
|
- benchmark.rb
|
|
141
153
|
- exe/jackdaw
|
|
142
154
|
- lib/jackdaw.rb
|
|
@@ -160,6 +172,8 @@ files:
|
|
|
160
172
|
- lib/jackdaw/watcher.rb
|
|
161
173
|
- sig/jackdaw.rbs
|
|
162
174
|
- usejackdaw.com.site/.gitignore
|
|
175
|
+
- usejackdaw.com.site/Gemfile
|
|
176
|
+
- usejackdaw.com.site/Gemfile.lock
|
|
163
177
|
- usejackdaw.com.site/site/assets/css/style.css
|
|
164
178
|
- usejackdaw.com.site/site/assets/favicon/android-icon-144x144.png
|
|
165
179
|
- usejackdaw.com.site/site/assets/favicon/android-icon-192x192.png
|
data/.DS_Store
DELETED
|
Binary file
|
data/TODO.md
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# Jackdaw - Static Site Generator TODO
|
|
2
|
-
|
|
3
|
-
## Project Overview
|
|
4
|
-
A fast, incremental static site generator with folder-based organization using Thor for CLI.
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Phase 1: Project Setup
|
|
9
|
-
- [x] Add Thor gem dependency to gemspec
|
|
10
|
-
- [x] Add development dependencies (rspec, rubocop, etc.)
|
|
11
|
-
- [x] Create basic CLI structure in exe/jackdaw
|
|
12
|
-
- [x] Set up Thor CLI class structure
|
|
13
|
-
- [x] Add any additional dependencies (for markdown parsing, file watching, etc.)
|
|
14
|
-
|
|
15
|
-
## Phase 2: Core File System Structure
|
|
16
|
-
- [x] Define standard project structure (site/ directory)
|
|
17
|
-
- [x] Create file watcher/tracker for incremental builds
|
|
18
|
-
- [x] Implement directory scanner
|
|
19
|
-
- [x] Create file type registry (.page, .post, etc.)
|
|
20
|
-
- [x] Build metadata extractor from file structure (path, name, dates)
|
|
21
|
-
- [x] Extract metadata from first heading as title (H1)
|
|
22
|
-
- [x] Implement content type handlers
|
|
23
|
-
- [x] Page handler
|
|
24
|
-
- [x] Post handler
|
|
25
|
-
- [x] Asset handler
|
|
26
|
-
|
|
27
|
-
## Phase 3: Template System
|
|
28
|
-
- [x] Design template engine integration (ERB)
|
|
29
|
-
- [x] Implement template loader from templates/ directory
|
|
30
|
-
- [x] Create template context builder
|
|
31
|
-
- [x] Add layout/partial support
|
|
32
|
-
- [x] Implement template caching for performance
|
|
33
|
-
|
|
34
|
-
## Phase 4: Content Processing
|
|
35
|
-
- [x] Implement markdown parser integration
|
|
36
|
-
- [x] Create content preprocessor pipeline
|
|
37
|
-
- [x] Extract metadata from filename (dates, slugs)
|
|
38
|
-
- [x] Extract title from first H1 in content
|
|
39
|
-
- [x] Derive metadata from folder structure and hierarchy
|
|
40
|
-
- [x] Implement content transformation pipeline
|
|
41
|
-
- [x] Add syntax highlighting support (Rouge)
|
|
42
|
-
|
|
43
|
-
## Phase 5: Build System
|
|
44
|
-
- [x] Design dependency graph for incremental builds
|
|
45
|
-
- [x] Implement file change detection
|
|
46
|
-
- [x] Create build cache system
|
|
47
|
-
- [x] Build output directory management (public/)
|
|
48
|
-
- [x] Implement parallel processing for speed
|
|
49
|
-
- [x] Add build statistics/reporting
|
|
50
|
-
- [x] Create clean build option
|
|
51
|
-
|
|
52
|
-
## Phase 6: CLI Commands - Build
|
|
53
|
-
- [x] Implement `jackdaw build` command
|
|
54
|
-
- [x] Full build option
|
|
55
|
-
- [x] Incremental build (default)
|
|
56
|
-
- [x] Watch mode option (deferred to serve command)
|
|
57
|
-
- [x] Verbose/debug output
|
|
58
|
-
- [x] Clean option
|
|
59
|
-
- [x] Add build configuration loading (skipped - convention over configuration)
|
|
60
|
-
- [x] Implement error handling and reporting
|
|
61
|
-
|
|
62
|
-
## Phase 7: CLI Commands - Serve
|
|
63
|
-
- [x] Implement `jackdaw serve` command
|
|
64
|
-
- [x] Choose and integrate web server (Puma with Rack)
|
|
65
|
-
- [x] Add live reload support (polling-based with JavaScript)
|
|
66
|
-
- [x] Implement file watching for auto-rebuild (Listen gem)
|
|
67
|
-
- [x] Configure port and host options
|
|
68
|
-
- [x] Add middleware for development (StaticFileServer + LiveReloadMiddleware)
|
|
69
|
-
- [x] Beautiful CLI output with rebuild stats
|
|
70
|
-
- [x] Graceful shutdown handling (Ctrl+C)
|
|
71
|
-
|
|
72
|
-
## Phase 8: CLI Commands - Create
|
|
73
|
-
- [x] Implement `jackdaw create <template> <name>` command
|
|
74
|
-
- [x] Create template scaffolding system
|
|
75
|
-
- [x] Add default built-in templates:
|
|
76
|
-
- [x] `page` template (basic page.html.erb)
|
|
77
|
-
- [x] `blog` template (blog post with date handling)
|
|
78
|
-
- [x] Support arbitrary user-defined templates in templates/ directory
|
|
79
|
-
- [x] Discover available templates from templates/ folder
|
|
80
|
-
- [x] Add date/timestamp handling for posts (auto-prefix for blog/post/article/news)
|
|
81
|
-
- [x] Allow users to add custom templates by creating template files
|
|
82
|
-
- [x] Implement `jackdaw template list` command
|
|
83
|
-
- [x] Add --dated and --no-date flags for override control
|
|
84
|
-
- [x] Support nested paths (e.g., `jackdaw create page company/about`)
|
|
85
|
-
- [x] Validate template existence before creating files
|
|
86
|
-
- [x] Add Builder warnings for missing templates
|
|
87
|
-
|
|
88
|
-
## Phase 9: Configuration System
|
|
89
|
-
- [x] Skipped - Maintaining config-less, convention-over-configuration approach
|
|
90
|
-
|
|
91
|
-
## Phase 10: Asset Handling
|
|
92
|
-
- [x] Implement asset copying
|
|
93
|
-
- [x] Keeping minimal - assets copied as-is for speed and simplicity
|
|
94
|
-
|
|
95
|
-
## Phase 11: Performance Optimization
|
|
96
|
-
- [x] Profile build performance
|
|
97
|
-
- [x] Implement multi-threading for file processing (Parallel gem)
|
|
98
|
-
- [x] Optimize file I/O operations (incremental builds with mtime checks)
|
|
99
|
-
- [ ] Add memory usage optimization
|
|
100
|
-
- [x] Benchmark against common static site generators
|
|
101
|
-
- Small (30 files): 164 files/sec clean, 5821 files/sec incremental
|
|
102
|
-
- Medium (150 files): 428 files/sec clean, 12343 files/sec incremental
|
|
103
|
-
- Large (600 files): 693 files/sec clean, 16280 files/sec incremental
|
|
104
|
-
- Incremental builds: 6-18x faster than clean builds
|
|
105
|
-
- [x] Cache template compilation (ERB caching in Renderer)
|
|
106
|
-
- [x] Optimize dependency graph traversal (mtime-based change detection)
|
|
107
|
-
|
|
108
|
-
## Phase 12: Documentation
|
|
109
|
-
- [x] Write comprehensive README with quickstart guide
|
|
110
|
-
- [x] Document CLI commands and options (in README)
|
|
111
|
-
- [x] Create project structure guide (in README)
|
|
112
|
-
- [x] Write template documentation (in README)
|
|
113
|
-
- [x] Add configuration reference (N/A - convention-based, no config needed)
|
|
114
|
-
- [x] Add troubleshooting guide (in README)
|
|
115
|
-
- [x] Consolidate all require statements to jackdaw.rb
|
|
116
|
-
- [x] Add comprehensive code comments to jackdaw.rb and project.rb
|
|
117
|
-
- [ ] Add code comments to remaining core files
|
|
118
|
-
- [ ] Create example site (basic example already created by `jackdaw new`)
|
|
119
|
-
|
|
120
|
-
## Phase 13: Testing
|
|
121
|
-
- [x] Unit tests for core components (Project, FileTypes, Scanner)
|
|
122
|
-
- [x] Unit tests for Renderer (21 tests)
|
|
123
|
-
- [x] Integration tests for Builder (18 tests)
|
|
124
|
-
- [x] End-to-end build tests (10 comprehensive workflow tests)
|
|
125
|
-
- [x] Test helper infrastructure with fixtures
|
|
126
|
-
- [x] Performance benchmarks (completed in Phase 11)
|
|
127
|
-
- **Total: 99 passing tests, 0 failures**
|
|
128
|
-
|
|
129
|
-
## Phase 14: Polish & Release
|
|
130
|
-
- [x] Code cleanup and refactoring (reduced complexity in Build command)
|
|
131
|
-
- [x] RuboCop compliance (all files passing, 0 offenses)
|
|
132
|
-
- [x] Error message improvements (beautiful CLI output with colors)
|
|
133
|
-
- [x] Add progress indicators (build stats, rebuild notifications)
|
|
134
|
-
- [x] Version 1.0.0 release preparation
|
|
135
|
-
- [x] Updated version to 1.0.0
|
|
136
|
-
- [x] Created comprehensive CHANGELOG.md
|
|
137
|
-
- [x] Updated gemspec with proper metadata
|
|
138
|
-
- [x] All 99 tests passing
|
|
139
|
-
- [ ] Publish to RubyGems (deferred)
|
|
140
|
-
|
|
141
|
-
## Phase 15: Essential Web Features
|
|
142
|
-
- [ ] RSS/Atom feed generation (for blog posts)
|
|
143
|
-
- [ ] Sitemap.xml generation (for SEO)
|
|
144
|
-
- [ ] SEO meta tag helpers (Open Graph, Twitter Cards)
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## Nice-to-Have Features (Future)
|
|
149
|
-
- [ ] Search index generation (JSON for client-side search)
|
|
150
|
-
- [ ] Git integration (simple deploy command wrapper)
|
|
151
|
-
|
|
152
|
-
## Won't Implement (Against Project Philosophy)
|
|
153
|
-
- ~~Multiple template engine support~~ - ERB is sufficient, adds complexity
|
|
154
|
-
- ~~Image lazy loading helpers~~ - Can be done in templates/CSS
|
|
155
|
-
- ~~i18n/l10n support~~ - Too complex, users can handle with folder structure
|
|
156
|
-
- ~~CloudFlare/Netlify deployment helpers~~ - Too specific, manual deploy is simple enough
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## Technical Decisions to Make
|
|
161
|
-
- [x] Template engine: ERB (built-in, fast, zero dependencies)
|
|
162
|
-
- [x] Markdown parser: Kramdown (pure Ruby, no compilation issues)
|
|
163
|
-
- [x] Server: Puma (fast, industry standard)
|
|
164
|
-
- [x] File watching: Listen gem (reliable, cross-platform)
|
|
165
|
-
- [ ] Config format: YAML vs TOML vs Ruby DSL? (may skip - convention over configuration)
|
|
166
|
-
- [x] Incremental build strategy: Timestamp-based (mtime checks)
|
|
167
|
-
- [x] Metadata approach: Convention-based (NO frontmatter, derive from structure/content)
|