jackdaw 1.0.3 → 1.0.5

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: 232d8c8859aea9921f207a071ccc7a4ae7e8fa6e41fbe0dbbcaaff4b01372627
4
- data.tar.gz: ef1961fdcbf66820bfcbd503f9b751d8a32397c6e5194ba29511d88c567f271e
3
+ metadata.gz: cafc6529ca363289715f7a561db302b7450bea7fcb84bcad380b1bc2be25933d
4
+ data.tar.gz: fc81ae1d3210cdb40673e7285b0e0ac8049313ae4f4808082c206988d0d0d091
5
5
  SHA512:
6
- metadata.gz: 754345778dd3b3980f736bb2e55ed6c1804cf99bec961ecee2fde42a96735ce87dab596b2470a56d94ef992c5ec8fe8608faaa1ce321003fca7090b3780df680
7
- data.tar.gz: 45e130f32193d153a5cc75e4834b21eebd9d434e6215c075935f043c7661e8922cc71edce930ef46164d8c31a4d0923d471f828469278ca05c05c6ebc02afa68
6
+ metadata.gz: be3c766bc336ffe834cdf91de655643006747054e56c87d5fe1ae7af506b1006e7383865eb83773446b38dc9f645d159fc690604ad5830a0edd958e698e7d22b
7
+ data.tar.gz: e641949dd554beed72fb5049baff34e510350d3026a5b5caa40dce3f79262de9931ab5377628201588641884aacb04068654d112b9de5ec1ada68f570160f15f
data/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ 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.5] - 2026-01-08
9
+
10
+ ### Fixed
11
+ - gemspec links to correct repos and homepage
12
+
13
+ ## [1.0.4] - 2026-01-08
14
+
15
+ ### Fixed
16
+ - Fixed hot reload functionality in development server - browser now properly detects file changes and refreshes automatically
17
+ - Improved live reload middleware to use build counter instead of timestamps for more reliable change detection
18
+ - Fixed partial template changes not triggering rebuilds - changes to partials (e.g., `_nav.html.erb`, `_footer.html.erb`) now properly rebuild all affected pages
19
+ - Fixed build statistics accumulating across multiple rebuilds - stats now reset correctly for each build
20
+ - Added hot reload support for assets (CSS, JS, images) - changes to assets now trigger browser refresh with cache-busting
21
+
8
22
  ## [1.0.0] - 2026-01-06
9
23
 
10
24
  ### Added
data/exe/jackdaw CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding: UTF-8
3
2
  # frozen_string_literal: true
4
3
 
5
4
  # Set default external encoding to UTF-8
@@ -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]
@@ -27,7 +27,12 @@ module Jackdaw
27
27
  end
28
28
 
29
29
  def header(text)
30
- puts "\n#{COLORS[:bold]}#{COLORS[:magenta]}#{text}#{COLORS[:reset]}"
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
 
@@ -17,6 +17,8 @@ module Jackdaw
17
17
  exit 1
18
18
  end
19
19
 
20
+ version_banner
21
+
20
22
  server = Server.new(@project, @options)
21
23
  server.start
22
24
  rescue Interrupt
@@ -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 "\n#{colorize('🚀 Building site...', :magenta)}"
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
- Rack::Handler::Puma.run(rack_app, Port: port, Host: host, Silent: true)
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
- server = self
45
- Rack::Builder.new do
46
- use Rack::CommonLogger
47
- use LiveReloadMiddleware, server if server.livereload?
48
- run StaticFileServer.new(server.project)
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
- # In a real implementation, this would notify WebSocket clients
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
- [200, { 'Content-Type' => content_type, 'Content-Length' => content.bytesize.to_s }, [content]]
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 lastCheck = Date.now();
182
+ let lastBuild = null;
165
183
  setInterval(function() {
166
- fetch('/__rhes_reload_check')
184
+ fetch('/__jackdaw_reload_check')
167
185
  .then(r => r.json())
168
186
  .then(data => {
169
- if (data.lastBuild > lastCheck) {
187
+ if (lastBuild !== null && data.lastBuild > lastBuild) {
170
188
  console.log('Jackdaw: Reloading page...');
171
- location.reload();
189
+ // Force hard reload to bypass cache for CSS/JS changes
190
+ window.location.reload(true);
172
191
  }
173
- lastCheck = Date.now();
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
- @last_build = Time.now
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'] == '/__rhes_reload_check'
212
+ if env['PATH_INFO'] == '/__jackdaw_reload_check'
190
213
  return [
191
214
  200,
192
215
  { 'Content-Type' => 'application/json' },
193
- [JSON.generate({ lastBuild: @last_build.to_f })]
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jackdaw
4
- VERSION = '1.0.3'
4
+ VERSION = '1.0.5'
5
5
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # gem "rails"
6
+
7
+ gem 'jackdaw', path: '..'
@@ -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: #fff;
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: #2c3e50;
42
+ background: var(--gradient);
25
43
  color: white;
26
44
  padding: 1rem 0;
27
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
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: color 0.2s;
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
- color: #3498db;
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: #3498db;
127
+ background: white;
128
+ color: #667eea;
69
129
  padding: 0.5rem 1rem;
70
- border-radius: 4px;
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: #2980b9;
75
- color: white;
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: 800px;
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: #3498db;
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
- text-decoration: underline;
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: #f8f9fa;
128
- padding: 0.2rem 0.4rem;
129
- border-radius: 3px;
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: 1.5rem;
139
- border-radius: 6px;
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: 0.75rem;
160
- border: 1px solid #dee2e6;
308
+ padding: 1rem;
309
+ border: 1px solid #e9ecef;
161
310
  text-align: left;
162
311
  }
163
312
 
164
313
  th {
165
- background: #f8f9fa;
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 #3498db;
186
- padding-left: 1.5rem;
342
+ border-left: 4px solid var(--primary);
343
+ padding: 1rem 1.5rem;
187
344
  margin: 1.5rem 0;
188
- color: #666;
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
- border-top: 2px solid #ecf0f1;
196
- margin: 2rem 0;
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: #2c3e50;
363
+ background: var(--gradient);
202
364
  color: white;
203
- padding: 2rem 0;
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: #3498db;
372
+ color: white;
373
+ text-decoration: underline;
374
+ transition: all 0.3s ease;
210
375
  }
211
376
 
212
377
  .footer a:hover {
213
- color: #5dade2;
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: #95a5a6;
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
  }
@@ -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 style="text-align: center; margin: 2rem 0;">
2
- <img src="/jackdaw.png" alt="Jackdaw" style="max-width: 300px; height: auto;">
3
- </div>
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
- # Jackdaw ⚡️
7
+ </div>
6
8
 
7
- **Lightning-fast static site generator for Ruby**
9
+ <div class="content" markdown="1">
8
10
 
9
- Jackdaw is a minimal, fast static site generator that emphasizes:
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.3
4
+ version: 1.0.5
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
@@ -203,12 +217,12 @@ files:
203
217
  - usejackdaw.com.site/site/templates/blog.html.erb
204
218
  - usejackdaw.com.site/site/templates/layout.html.erb
205
219
  - usejackdaw.com.site/site/templates/page.html.erb
206
- homepage: https://github.com/BilyRuffian/jackdaw
220
+ homepage: https://usejackdaw.com
207
221
  licenses: []
208
222
  metadata:
209
- homepage_uri: https://github.com/BilyRuffian/jackdaw
210
- source_code_uri: https://github.com/yourusername/jackdaw
211
- changelog_uri: https://github.com/yourusername/jackdaw/blob/main/CHANGELOG.md
223
+ homepage_uri: https://usejackdaw.com
224
+ source_code_uri: https://github.com/BillyRuffian/jackdaw
225
+ changelog_uri: https://github.com/BillyRuffian/jackdaw/blob/main/CHANGELOG.md
212
226
  rubygems_mfa_required: 'true'
213
227
  rdoc_options: []
214
228
  require_paths:
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)