Add Pagefind search, local server, move pages_dir to config
Pagefind indexes episode pages for full-text search. Local
server via rake serve uses Puma directly.

Assisted-by: Claude Opus 4.6 via pi
change kovyzozzowqpwxysluslmusqrwuwtwkk
commit 3bb138b12bf3587659001959849e9560127b4937
author Alpha Chen <alpha@kejadlen.dev>
date
parent qqokuyvy
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 5b2e264..069804a 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -26,6 +26,9 @@ jobs:
         with:
           bundler-cache: true
 
+      - name: Install uv
+        uses: astral-sh/setup-uv@v5
+
       - name: Generate pages
         run: bundle exec rake pages
 
diff --git a/Gemfile b/Gemfile
index 105965a..e385e9f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,4 +1,5 @@
 source "https://rubygems.org"
 
 gem "nokogiri"
+gem "puma"
 gem "rake"
diff --git a/Gemfile.lock b/Gemfile.lock
index 186f700..fec29e6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,6 +1,7 @@
 GEM
   remote: https://rubygems.org/
   specs:
+    nio4r (2.7.5)
     nokogiri (1.19.2-aarch64-linux-gnu)
       racc (~> 1.4)
     nokogiri (1.19.2-aarch64-linux-musl)
@@ -17,6 +18,8 @@ GEM
       racc (~> 1.4)
     nokogiri (1.19.2-x86_64-linux-musl)
       racc (~> 1.4)
+    puma (7.2.0)
+      nio4r (~> 2.0)
     racc (1.8.1)
     rake (13.3.1)
 
@@ -32,7 +35,8 @@ PLATFORMS
 
 DEPENDENCIES
   nokogiri
+  puma
   rake
 
 BUNDLED WITH
-   4.0.9
+  4.0.9
diff --git a/Rakefile b/Rakefile
index 374be99..9ba69b1 100644
--- a/Rakefile
+++ b/Rakefile
@@ -123,8 +123,6 @@ task :retranscribe, [:number] do |_t, args|
   Rake::Task[et.text_path].invoke
 end
 
-PAGES_DIR = Pathname("pages")
-
 desc "Generate HTML transcript pages"
 task :pages do
   require "cgi"
@@ -145,15 +143,39 @@ task :pages do
       }
     end
 
-  PAGES_DIR.mkpath
+  CONFIG.pages_dir.mkpath
 
   transcripts.each do |ep|
     html = episode_template.result(binding)
-    (PAGES_DIR / "#{ep[:slug]}.html").write(html)
+    (CONFIG.pages_dir / "#{ep[:slug]}.html").write(html)
   end
 
   html = index_template.result(binding)
-  (PAGES_DIR / "index.html").write(html)
+  (CONFIG.pages_dir / "index.html").write(html)
 
   puts "Generated #{transcripts.length} episode pages + index."
+
+  sh "uv", "run", "--with", "pagefind[bin]", "python3", "-m", "pagefind", "--site", CONFIG.pages_dir.to_s
+end
+
+desc "Serve the generated pages locally"
+task serve: :pages do
+  require "puma"
+  require "puma/configuration"
+  require "puma/launcher"
+  require "rack/files"
+
+  files = Rack::Files.new(CONFIG.pages_dir.to_s)
+  app = ->(env) do
+    env["PATH_INFO"] = "/index.html" if env["PATH_INFO"] == "/"
+    files.call(env)
+  end
+  config = Puma::Configuration.new do |c|
+    c.port 8000
+    c.app app
+    c.log_requests
+  end
+
+  puts "Serving #{CONFIG.pages_dir} at http://localhost:8000"
+  Puma::Launcher.new(config).run
 end
diff --git a/lib/config.rb b/lib/config.rb
index b9e110e..ed7658d 100644
--- a/lib/config.rb
+++ b/lib/config.rb
@@ -5,6 +5,7 @@ module CookingIssues
     :cache_dir,
     :audio_dir,
     :transcripts_dir,
+    :pages_dir,
     :hrn_feed_path,
     :hrn_feed_url,
     :acast_feed_path,
@@ -22,6 +23,7 @@ module CookingIssues
         cache_dir: cache_dir,
         audio_dir: cache_dir / "audio",
         transcripts_dir: Pathname("transcripts"),
+        pages_dir: Pathname("pages"),
         hrn_feed_path: cache_dir / "hrn_feed.xml",
         hrn_feed_url: "https://rss.art19.com/cooking-issues",
         acast_feed_path: cache_dir / "acast_feed.xml",
diff --git a/lib/pages/index.html.erb b/lib/pages/index.html.erb
index bc0f578..21819f1 100644
--- a/lib/pages/index.html.erb
+++ b/lib/pages/index.html.erb
@@ -4,10 +4,12 @@
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <title>Cooking Issues Transcripts</title>
+<link href="pagefind/pagefind-ui.css" rel="stylesheet">
 <style>
   * { box-sizing: border-box; margin: 0; padding: 0; }
   body { font-family: system-ui, sans-serif; line-height: 1.6; color: #222; max-width: 50rem; margin: 0 auto; padding: 1rem; }
   h1 { margin-bottom: 1rem; }
+  #search { margin-bottom: 1.5rem; }
   ul { list-style: none; }
   li { padding: 0.4rem 0; border-bottom: 1px solid #eee; }
   a { color: #0066cc; text-decoration: none; }
@@ -17,6 +19,9 @@
 <body>
 
 <h1>Cooking Issues Transcripts</h1>
+
+<div id="search"></div>
+
 <p><%= transcripts.length %> episodes</p>
 
 <ul>
@@ -25,5 +30,12 @@
 <% end %>
 </ul>
 
+<script src="/pagefind/pagefind-ui.js"></script>
+<script>
+  window.addEventListener('DOMContentLoaded', (event) => {
+    new PagefindUI({ element: "#search", showSubResults: true });
+  });
+</script>
+
 </body>
 </html>