siru 0.3.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 +7 -0
- data/Gemfile +19 -0
- data/README.md +164 -0
- data/bin/siru +89 -0
- data/lib/siru/builder.rb +126 -0
- data/lib/siru/cli.rb +155 -0
- data/lib/siru/config.rb +92 -0
- data/lib/siru/post.rb +92 -0
- data/lib/siru/server.rb +57 -0
- data/lib/siru/site.rb +47 -0
- data/lib/siru/theme.rb +56 -0
- data/lib/siru.rb +23 -0
- data/siru.gemspec +30 -0
- data/themes/paper/layouts/baseof.liquid +39 -0
- data/themes/paper/layouts/index.liquid +17 -0
- data/themes/paper/layouts/post.liquid +13 -0
- data/themes/paper/layouts/single.liquid +18 -0
- data/themes/paper/static/assets/style.css +24 -0
- data/themes/paper+/layouts/baseof.liquid +39 -0
- data/themes/paper+/layouts/index.liquid +17 -0
- data/themes/paper+/layouts/post.liquid +13 -0
- data/themes/paper+/layouts/single.liquid +18 -0
- data/themes/paper+/static/assets/style.css +73 -0
- data/themes/paper+/static/bluesky.svg +1 -0
- data/themes/paper+/static/mastodon.svg +1 -0
- data/themes/paper+/static/nostr.svg +13 -0
- data/themes/paper+/static/twitter.svg +1 -0
- metadata +236 -0
data/lib/siru/post.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
module Siru
|
2
|
+
class Post
|
3
|
+
attr_reader :file_path, :front_matter, :content, :rendered_content
|
4
|
+
|
5
|
+
def initialize(file_path)
|
6
|
+
@file_path = file_path
|
7
|
+
parse_file
|
8
|
+
end
|
9
|
+
|
10
|
+
def title
|
11
|
+
@front_matter['title'] || File.basename(@file_path, '.md').tr('-', ' ').capitalize
|
12
|
+
end
|
13
|
+
|
14
|
+
def date
|
15
|
+
date_str = @front_matter['date']
|
16
|
+
return Date.today unless date_str
|
17
|
+
|
18
|
+
if date_str.is_a?(String)
|
19
|
+
Date.parse(date_str)
|
20
|
+
else
|
21
|
+
date_str
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def draft?
|
26
|
+
@front_matter['draft'] == true
|
27
|
+
end
|
28
|
+
|
29
|
+
def slug
|
30
|
+
@front_matter['slug'] || File.basename(@file_path, '.md')
|
31
|
+
end
|
32
|
+
|
33
|
+
def url
|
34
|
+
"/posts/#{slug}/"
|
35
|
+
end
|
36
|
+
|
37
|
+
def summary
|
38
|
+
@front_matter['summary'] || @content.split("\n\n").first&.strip || ''
|
39
|
+
end
|
40
|
+
|
41
|
+
def tags
|
42
|
+
@front_matter['tags'] || []
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def parse_file
|
48
|
+
raw_content = File.read(@file_path)
|
49
|
+
|
50
|
+
if raw_content.start_with?('+++')
|
51
|
+
# TOML front matter
|
52
|
+
parts = raw_content.split('+++', 3)
|
53
|
+
@front_matter = TOML.load(parts[1]) if parts[1]
|
54
|
+
@content = parts[2]&.strip || ''
|
55
|
+
elsif raw_content.start_with?('---')
|
56
|
+
# YAML front matter
|
57
|
+
parsed = FrontMatterParser.parse(raw_content)
|
58
|
+
@front_matter = parsed.front_matter
|
59
|
+
@content = parsed.content
|
60
|
+
else
|
61
|
+
@front_matter = {}
|
62
|
+
@content = raw_content
|
63
|
+
end
|
64
|
+
|
65
|
+
@front_matter ||= {}
|
66
|
+
@rendered_content = render_markdown(@content)
|
67
|
+
end
|
68
|
+
|
69
|
+
def render_markdown(content)
|
70
|
+
renderer = Redcarpet::Render::HTML.new(
|
71
|
+
filter_html: false,
|
72
|
+
no_intra_emphasis: true,
|
73
|
+
fenced_code_blocks: true,
|
74
|
+
autolink: true,
|
75
|
+
strikethrough: true,
|
76
|
+
superscript: true,
|
77
|
+
tables: true
|
78
|
+
)
|
79
|
+
|
80
|
+
markdown = Redcarpet::Markdown.new(renderer, {
|
81
|
+
autolink: true,
|
82
|
+
tables: true,
|
83
|
+
fenced_code_blocks: true,
|
84
|
+
strikethrough: true,
|
85
|
+
superscript: true,
|
86
|
+
no_intra_emphasis: true
|
87
|
+
})
|
88
|
+
|
89
|
+
markdown.render(content)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/siru/server.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Siru
|
2
|
+
class Server
|
3
|
+
def initialize(site, options = {})
|
4
|
+
@site = site
|
5
|
+
@options = options
|
6
|
+
@port = options[:port] || 3000
|
7
|
+
@watch = options[:watch] || false
|
8
|
+
@output_dir = 'public'
|
9
|
+
end
|
10
|
+
|
11
|
+
def start
|
12
|
+
puts "Starting Siru server on port #{@port}..."
|
13
|
+
|
14
|
+
# Build the site initially
|
15
|
+
builder = Builder.new(@site, @options)
|
16
|
+
builder.build
|
17
|
+
|
18
|
+
# Set up file watching if enabled
|
19
|
+
setup_watcher if @watch
|
20
|
+
|
21
|
+
# Start the web server
|
22
|
+
server = WEBrick::HTTPServer.new(
|
23
|
+
Port: @port,
|
24
|
+
DocumentRoot: @output_dir,
|
25
|
+
Logger: WEBrick::Log.new('/dev/null'),
|
26
|
+
AccessLog: []
|
27
|
+
)
|
28
|
+
|
29
|
+
trap('INT') { server.shutdown }
|
30
|
+
|
31
|
+
puts "Server running at http://localhost:#{@port}/"
|
32
|
+
puts "Press Ctrl+C to stop"
|
33
|
+
|
34
|
+
server.start
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def setup_watcher
|
40
|
+
listener = Listen.to('.', only: /\.(md|toml|yaml|yml|liquid|html|css|js)$/) do |modified, added, removed|
|
41
|
+
puts "Files changed: #{(modified + added + removed).join(', ')}"
|
42
|
+
puts "Rebuilding..."
|
43
|
+
|
44
|
+
# Reload site configuration and rebuild
|
45
|
+
config = Config.load
|
46
|
+
@site = Site.new(config, @options)
|
47
|
+
builder = Builder.new(@site, @options)
|
48
|
+
builder.build
|
49
|
+
|
50
|
+
puts "Rebuild complete!"
|
51
|
+
end
|
52
|
+
|
53
|
+
listener.start
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
data/lib/siru/site.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Siru
|
2
|
+
class Site
|
3
|
+
attr_reader :config, :posts, :theme
|
4
|
+
|
5
|
+
def initialize(config, options = {})
|
6
|
+
@config = config
|
7
|
+
@options = options
|
8
|
+
@theme = Theme.new(config['theme'])
|
9
|
+
@posts = load_posts
|
10
|
+
end
|
11
|
+
|
12
|
+
def title
|
13
|
+
@config['title']
|
14
|
+
end
|
15
|
+
|
16
|
+
def base_url
|
17
|
+
@config['baseURL']
|
18
|
+
end
|
19
|
+
|
20
|
+
def params
|
21
|
+
@config['params'] || {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def language_code
|
25
|
+
@config['languageCode'] || 'en-us'
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def load_posts
|
31
|
+
posts = []
|
32
|
+
content_dir = 'content'
|
33
|
+
|
34
|
+
return posts unless Dir.exist?(content_dir)
|
35
|
+
|
36
|
+
Dir.glob(File.join(content_dir, '**', '*.md')).each do |file|
|
37
|
+
post = Post.new(file)
|
38
|
+
# Include drafts if --draft option is set
|
39
|
+
if @options[:draft] || !post.draft?
|
40
|
+
posts << post
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
posts.sort_by(&:date).reverse
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/siru/theme.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module Siru
|
2
|
+
class Theme
|
3
|
+
attr_reader :name, :path
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
@name = name
|
7
|
+
@path = find_theme_path(name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def layout_path(layout_name)
|
11
|
+
if File.exist?(File.join(@path, 'layouts', "#{layout_name}.liquid"))
|
12
|
+
File.join(@path, 'layouts', "#{layout_name}.liquid")
|
13
|
+
else
|
14
|
+
# Fall back to built-in theme
|
15
|
+
builtin_path = File.join(__dir__, '..', '..', 'themes', @name, 'layouts', "#{layout_name}.liquid")
|
16
|
+
File.exist?(builtin_path) ? builtin_path : nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_layout?(layout_name)
|
21
|
+
!layout_path(layout_name).nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
def render_layout(layout_name, variables = {})
|
25
|
+
path = layout_path(layout_name)
|
26
|
+
return '' unless path
|
27
|
+
|
28
|
+
template = Liquid::Template.parse(File.read(path))
|
29
|
+
template.render(variables)
|
30
|
+
end
|
31
|
+
|
32
|
+
def static_files
|
33
|
+
static_path = File.join(@path, 'static')
|
34
|
+
return [] unless Dir.exist?(static_path)
|
35
|
+
|
36
|
+
Dir.glob(File.join(static_path, '**', '*')).select { |f| File.file?(f) }
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def find_theme_path(name)
|
42
|
+
# Check local themes directory first
|
43
|
+
local_path = File.join('themes', name)
|
44
|
+
return File.expand_path(local_path) if Dir.exist?(local_path)
|
45
|
+
|
46
|
+
# Fall back to built-in theme
|
47
|
+
builtin_path = File.join(__dir__, '..', '..', 'themes', name)
|
48
|
+
return File.expand_path(builtin_path) if Dir.exist?(builtin_path)
|
49
|
+
|
50
|
+
# Create basic theme structure if not found
|
51
|
+
FileUtils.mkdir_p(File.join('themes', name, 'layouts'))
|
52
|
+
FileUtils.mkdir_p(File.join('themes', name, 'static'))
|
53
|
+
File.expand_path(File.join('themes', name))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/siru.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'redcarpet'
|
2
|
+
require 'liquid'
|
3
|
+
require 'toml'
|
4
|
+
require 'parslet'
|
5
|
+
require 'sassc'
|
6
|
+
require 'listen'
|
7
|
+
require 'webrick'
|
8
|
+
require 'front_matter_parser'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'pathname'
|
11
|
+
require 'yaml'
|
12
|
+
|
13
|
+
require_relative 'siru/cli'
|
14
|
+
require_relative 'siru/site'
|
15
|
+
require_relative 'siru/post'
|
16
|
+
require_relative 'siru/theme'
|
17
|
+
require_relative 'siru/server'
|
18
|
+
require_relative 'siru/builder'
|
19
|
+
require_relative 'siru/config'
|
20
|
+
|
21
|
+
module Siru
|
22
|
+
VERSION = "0.3.0"
|
23
|
+
end
|
data/siru.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "siru"
|
3
|
+
spec.version = "0.3.0"
|
4
|
+
spec.authors = ["Tim Apple"]
|
5
|
+
spec.email = ["tim.apple@hey.com"]
|
6
|
+
spec.summary = "A Hugo-inspired static site generator built in Ruby"
|
7
|
+
spec.description = "Siru is a fast, flexible static site generator inspired by Hugo, built in Ruby with support for themes, markdown processing, and live reloading."
|
8
|
+
spec.homepage = "https://github.com/timappledotcom/siru"
|
9
|
+
spec.license = "GPL-3.0"
|
10
|
+
spec.required_ruby_version = ">= 3.0.0"
|
11
|
+
|
12
|
+
spec.files = Dir.glob("{lib,bin,themes,templates}/**/*") + %w[README.md Gemfile siru.gemspec]
|
13
|
+
spec.bindir = "bin"
|
14
|
+
spec.executables = ["siru"]
|
15
|
+
spec.require_paths = ["lib"]
|
16
|
+
|
17
|
+
spec.add_dependency "redcarpet", "~> 3.6"
|
18
|
+
spec.add_dependency "liquid", "~> 5.4"
|
19
|
+
spec.add_dependency "toml", "~> 0.3"
|
20
|
+
spec.add_dependency "sassc", "~> 2.4"
|
21
|
+
spec.add_dependency "listen", "~> 3.8"
|
22
|
+
spec.add_dependency "webrick", "~> 1.8"
|
23
|
+
spec.add_dependency "front_matter_parser", "~> 1.0"
|
24
|
+
spec.add_dependency "base64"
|
25
|
+
spec.add_dependency "logger"
|
26
|
+
spec.add_dependency "parslet"
|
27
|
+
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.12"
|
29
|
+
spec.add_development_dependency "rubocop", "~> 1.56"
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="{{ site.language_code }}" style="--bg: {{ bg_color }}">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>{{ page_title }} - {{ site.title }}</title>
|
7
|
+
<link rel="stylesheet" href="/assets/style.css">
|
8
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
9
|
+
</head>
|
10
|
+
<body class="bg-[--bg] text-black antialiased duration-200 ease-out dark:text-white">
|
11
|
+
<header class="mx-auto max-w-3xl px-8 py-4">
|
12
|
+
<nav class="flex items-center justify-between">
|
13
|
+
<a href="/" class="text-xl font-bold">{{ site.title }}</a>
|
14
|
+
<div class="flex space-x-4">
|
15
|
+
{% if site.params.twitter %}
|
16
|
+
<a href="https://twitter.com/{{ site.params.twitter }}" class="text-blue-500 hover:text-blue-600">Twitter</a>
|
17
|
+
{% endif %}
|
18
|
+
{% if site.params.github %}
|
19
|
+
<a href="https://github.com/{{ site.params.github }}" class="text-gray-700 hover:text-gray-900">GitHub</a>
|
20
|
+
{% endif %}
|
21
|
+
{% if site.params.mastodon %}
|
22
|
+
<a href="{{ site.params.mastodon }}" class="text-purple-600 hover:text-purple-700">Mastodon</a>
|
23
|
+
{% endif %}
|
24
|
+
{% if site.params.bluesky %}
|
25
|
+
<a href="https://bsky.app/profile/{{ site.params.bluesky }}" class="text-sky-600 hover:text-sky-700">Bluesky</a>
|
26
|
+
{% endif %}
|
27
|
+
</div>
|
28
|
+
</nav>
|
29
|
+
</header>
|
30
|
+
|
31
|
+
<main class="prose prose-neutral dark:prose-invert mx-auto min-h-[calc(100vh-9rem)] max-w-3xl px-8 pt-14 pb-16">
|
32
|
+
{{ content }}
|
33
|
+
</main>
|
34
|
+
|
35
|
+
<footer class="mx-auto max-w-3xl px-8 py-4 text-center text-sm text-gray-600 dark:text-gray-400">
|
36
|
+
<p>© {{ "now" | date: "%Y" }} {{ site.title }}. Powered by <a href="https://github.com/timappledotcom/siru" class="text-blue-600 hover:text-blue-700">Siru</a>.</p>
|
37
|
+
</footer>
|
38
|
+
</body>
|
39
|
+
</html>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{% if site.params.bio %}
|
2
|
+
<div class="mb-12 -mt-2 flex items-center">
|
3
|
+
<div>
|
4
|
+
<div class="mt-3 mb-1 text-2xl font-medium text-black dark:text-white">{{ site.title }}</div>
|
5
|
+
<div class="break-words">{{ site.params.bio }}</div>
|
6
|
+
</div>
|
7
|
+
</div>
|
8
|
+
{% endif %}
|
9
|
+
|
10
|
+
{% for post in posts %}
|
11
|
+
<section class="relative my-10 first-of-type:mt-0 last-of-type:mb-0">
|
12
|
+
<h2 class="my-0">{{ post.title }}</h2>
|
13
|
+
<time class="text-xs antialiased opacity-60">{{ post.date | date: "%B %d, %Y" }}</time>
|
14
|
+
<div class="mt-2">{{ post.summary }}</div>
|
15
|
+
<a class="absolute inset-0 text-[0px]" href="{{ post.url }}">{{ post.title }}</a>
|
16
|
+
</section>
|
17
|
+
{% endfor %}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{% layout "default/baseof" %} {% block "contents" %}
|
2
|
+
<main>
|
3
|
+
<article>
|
4
|
+
<header>
|
5
|
+
<h1>{{ post.title }}</h1>
|
6
|
+
<time>{{ post.date | date: "%B %d, %Y" }}</time>
|
7
|
+
</header>
|
8
|
+
<section>{{ post.rendered_content }}</section>
|
9
|
+
<footer>Tags: {% for tag in post.tags %} <a href="/tags/{{ tag }}">{{ tag }}</a> {% unless forloop.last %}, {% endunless %}{% endfor %}
|
10
|
+
</footer>
|
11
|
+
</article>
|
12
|
+
</main>
|
13
|
+
{% endblock %}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<article>
|
2
|
+
<header class="mb-14">
|
3
|
+
<h1 class="my-0! pb-2.5">{{ post.title }}</h1>
|
4
|
+
<div class="text-xs antialiased opacity-60">
|
5
|
+
<time>{{ post.date | date: "%B %d, %Y" }}</time>
|
6
|
+
</div>
|
7
|
+
</header>
|
8
|
+
|
9
|
+
<section>{{ post.rendered_content }}</section>
|
10
|
+
|
11
|
+
{% if post.tags and post.tags.size > 0 %}
|
12
|
+
<footer class="mt-12 flex flex-wrap">
|
13
|
+
{% for tag in post.tags %}
|
14
|
+
<a class="mb-1.5 rounded-lg bg-black/[3%] px-5 py-1 no-underline hover:bg-black/[6%] ltr:mr-1.5 rtl:ml-1.5 dark:bg-white/[8%] dark:hover:bg-white/[12%]" href="/tags/{{ tag | downcase | replace: ' ', '-' }}">{{ tag }}</a>
|
15
|
+
{% endfor %}
|
16
|
+
</footer>
|
17
|
+
{% endif %}
|
18
|
+
</article>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
body {
|
2
|
+
font-family: 'Helvetica Neue', Arial, sans-serif;
|
3
|
+
background-color: #faf8f1;
|
4
|
+
color: #333;
|
5
|
+
}
|
6
|
+
|
7
|
+
article {
|
8
|
+
max-width: 800px;
|
9
|
+
margin: 0 auto;
|
10
|
+
padding: 16px;
|
11
|
+
}
|
12
|
+
|
13
|
+
h1, h2, h3, h4, h5, h6 {
|
14
|
+
color: #333;
|
15
|
+
}
|
16
|
+
|
17
|
+
a {
|
18
|
+
color: #0066cc;
|
19
|
+
}
|
20
|
+
|
21
|
+
a:hover {
|
22
|
+
color: #004999;
|
23
|
+
text-decoration: underline;
|
24
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="{{ site.language_code }}" style="--bg: {{ bg_color }}">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>{{ page_title }} - {{ site.title }}</title>
|
7
|
+
<link rel="stylesheet" href="/assets/style.css">
|
8
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
9
|
+
</head>
|
10
|
+
<body class="bg-[--bg] text-black antialiased duration-200 ease-out dark:text-white">
|
11
|
+
<header class="mx-auto max-w-3xl px-8 py-4">
|
12
|
+
<nav class="flex items-center justify-between">
|
13
|
+
<a href="/" class="text-xl font-bold">{{ site.title }}</a>
|
14
|
+
<div class="flex space-x-4">
|
15
|
+
{% if site.params.social.x %}
|
16
|
+
<a href="{{ site.params.social.x }}" class="text-blue-500 hover:text-blue-600">X</a>
|
17
|
+
{% endif %}
|
18
|
+
{% if site.params.social.mastodon %}
|
19
|
+
<a href="{{ site.params.social.mastodon }}" class="text-purple-600 hover:text-purple-700">Mastodon</a>
|
20
|
+
{% endif %}
|
21
|
+
{% if site.params.social.bluesky %}
|
22
|
+
<a href="{{ site.params.social.bluesky }}" class="text-sky-600 hover:text-sky-700">Bluesky</a>
|
23
|
+
{% endif %}
|
24
|
+
{% if site.params.social.nostr %}
|
25
|
+
<a href="{{ site.params.social.nostr }}" class="text-yellow-600 hover:text-yellow-700">Nostr</a>
|
26
|
+
{% endif %}
|
27
|
+
</div>
|
28
|
+
</nav>
|
29
|
+
</header>
|
30
|
+
|
31
|
+
<main class="prose prose-neutral dark:prose-invert mx-auto min-h-[calc(100vh-9rem)] max-w-3xl px-8 pt-14 pb-16">
|
32
|
+
{{ content }}
|
33
|
+
</main>
|
34
|
+
|
35
|
+
<footer class="mx-auto max-w-3xl px-8 py-4 text-center text-sm text-gray-600 dark:text-gray-400">
|
36
|
+
<p>© {{ "now" | date: "%Y" }} {{ site.title }}. Powered by <a href="https://github.com/timappledotcom/siru" class="text-blue-600 hover:text-blue-700">Siru</a>.</p>
|
37
|
+
</footer>
|
38
|
+
</body>
|
39
|
+
</html>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{% if site.params.bio %}
|
2
|
+
<div class="mb-12 -mt-2 flex items-center">
|
3
|
+
<div>
|
4
|
+
<div class="mt-3 mb-1 text-2xl font-medium text-black dark:text-white">{{ site.title }}</div>
|
5
|
+
<div class="break-words">{{ site.params.bio }}</div>
|
6
|
+
</div>
|
7
|
+
</div>
|
8
|
+
{% endif %}
|
9
|
+
|
10
|
+
{% for post in posts %}
|
11
|
+
<section class="relative my-10 first-of-type:mt-0 last-of-type:mb-0">
|
12
|
+
<h2 class="my-0">{{ post.title }}</h2>
|
13
|
+
<time class="text-xs antialiased opacity-60">{{ post.date | date: "%B %d, %Y" }}</time>
|
14
|
+
<div class="mt-2">{{ post.summary }}</div>
|
15
|
+
<a class="absolute inset-0 text-[0px]" href="{{ post.url }}">{{ post.title }}</a>
|
16
|
+
</section>
|
17
|
+
{% endfor %}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{% layout "default/baseof" %} {% block "contents" %}
|
2
|
+
<main>
|
3
|
+
<article>
|
4
|
+
<header>
|
5
|
+
<h1>{{ post.title }}</h1>
|
6
|
+
<time>{{ post.date | date: "%B %d, %Y" }}</time>
|
7
|
+
</header>
|
8
|
+
<section>{{ post.rendered_content }}</section>
|
9
|
+
<footer>Tags: {% for tag in post.tags %} <a href="/tags/{{ tag }}">{{ tag }}</a> {% unless forloop.last %}, {% endunless %}{% endfor %}
|
10
|
+
</footer>
|
11
|
+
</article>
|
12
|
+
</main>
|
13
|
+
{% endblock %}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<article>
|
2
|
+
<header class="mb-14">
|
3
|
+
<h1 class="my-0! pb-2.5">{{ post.title }}</h1>
|
4
|
+
<div class="text-xs antialiased opacity-60">
|
5
|
+
<time>{{ post.date | date: "%B %d, %Y" }}</time>
|
6
|
+
</div>
|
7
|
+
</header>
|
8
|
+
|
9
|
+
<section>{{ post.rendered_content }}</section>
|
10
|
+
|
11
|
+
{% if post.tags and post.tags.size > 0 %}
|
12
|
+
<footer class="mt-12 flex flex-wrap">
|
13
|
+
{% for tag in post.tags %}
|
14
|
+
<a class="mb-1.5 rounded-lg bg-black/[3%] px-5 py-1 no-underline hover:bg-black/[6%] ltr:mr-1.5 rtl:ml-1.5 dark:bg-white/[8%] dark:hover:bg-white/[12%]" href="/tags/{{ tag | downcase | replace: ' ', '-' }}">{{ tag }}</a>
|
15
|
+
{% endfor %}
|
16
|
+
</footer>
|
17
|
+
{% endif %}
|
18
|
+
</article>
|
@@ -0,0 +1,73 @@
|
|
1
|
+
body {
|
2
|
+
font-family: 'Helvetica Neue', Arial, sans-serif;
|
3
|
+
background-color: #faf8f1;
|
4
|
+
color: #333;
|
5
|
+
}
|
6
|
+
|
7
|
+
article {
|
8
|
+
max-width: 800px;
|
9
|
+
margin: 0 auto;
|
10
|
+
padding: 16px;
|
11
|
+
}
|
12
|
+
|
13
|
+
h1, h2, h3, h4, h5, h6 {
|
14
|
+
color: #333;
|
15
|
+
}
|
16
|
+
|
17
|
+
a {
|
18
|
+
color: #0066cc;
|
19
|
+
}
|
20
|
+
|
21
|
+
a:hover {
|
22
|
+
color: #004999;
|
23
|
+
text-decoration: underline;
|
24
|
+
}
|
25
|
+
|
26
|
+
/* Catppuccin Mocha Dark Theme Overrides */
|
27
|
+
.prose p {
|
28
|
+
margin-top: 1.25em !important;
|
29
|
+
margin-bottom: 1.25em !important;
|
30
|
+
color: #cdd6f4 !important;
|
31
|
+
}
|
32
|
+
|
33
|
+
.prose p:first-child {
|
34
|
+
margin-top: 0 !important;
|
35
|
+
}
|
36
|
+
|
37
|
+
.prose p:last-child {
|
38
|
+
margin-bottom: 0 !important;
|
39
|
+
}
|
40
|
+
|
41
|
+
/* Additional dark theme text styling */
|
42
|
+
.prose {
|
43
|
+
color: #cdd6f4;
|
44
|
+
}
|
45
|
+
|
46
|
+
.prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 {
|
47
|
+
color: #cdd6f4;
|
48
|
+
}
|
49
|
+
|
50
|
+
.prose a {
|
51
|
+
color: #89b4fa;
|
52
|
+
}
|
53
|
+
|
54
|
+
.prose a:hover {
|
55
|
+
color: #b4befe;
|
56
|
+
}
|
57
|
+
|
58
|
+
.prose code {
|
59
|
+
color: #f38ba8;
|
60
|
+
background-color: #313244;
|
61
|
+
padding: 0.2em 0.4em;
|
62
|
+
border-radius: 0.25rem;
|
63
|
+
}
|
64
|
+
|
65
|
+
.prose pre {
|
66
|
+
background-color: #313244;
|
67
|
+
color: #cdd6f4;
|
68
|
+
}
|
69
|
+
|
70
|
+
.prose blockquote {
|
71
|
+
border-left-color: #585b70;
|
72
|
+
color: #a6adc8;
|
73
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1.1 1 21.5 21.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M6.335 5.144c-1.654 -1.199 -4.335 -2.127 -4.335 .826c0 .59 .35 4.953 .556 5.661c.713 2.463 3.13 2.75 5.444 2.369c-4.045 .665 -4.889 3.208 -2.667 5.41c1.03 1.018 1.913 1.59 2.667 1.59c2 0 3.134 -2.769 3.5 -3.5c.333 -.667 .5 -1.167 .5 -1.5c0 .333 .167 .833 .5 1.5c.366 .731 1.5 3.5 3.5 3.5c.754 0 1.637 -.571 2.667 -1.59c2.222 -2.203 1.378 -4.746 -2.667 -5.41c2.314 .38 4.73 .094 5.444 -2.369c.206 -.708 .556 -5.072 .556 -5.661c0 -2.953 -2.68 -2.025 -4.335 -.826c-2.293 1.662 -4.76 5.048 -5.665 6.856c-.905 -1.808 -3.372 -5.194 -5.665 -6.856z"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="3.5 3.5 25 25"><path d="M 15.9375 4.03125 C 12.917 4.0435 9.9179219 4.4269844 8.3574219 5.1464844 C 8.3574219 5.1464844 5 6.6748594 5 11.880859 C 5 18.077859 4.9955 25.860234 10.5625 27.365234 C 12.6945 27.938234 14.527953 28.061562 16.001953 27.976562 C 18.676953 27.825562 20 27.005859 20 27.005859 L 19.910156 25.029297 C 19.910156 25.029297 18.176297 25.640313 16.029297 25.570312 C 13.902297 25.495313 11.6615 25.335688 11.3125 22.679688 C 11.2805 22.432688 11.264625 22.182594 11.265625 21.933594 C 15.772625 23.052594 19.615828 22.420969 20.673828 22.292969 C 23.627828 21.933969 26.199344 20.081672 26.527344 18.388672 C 27.041344 15.720672 26.998047 11.880859 26.998047 11.880859 C 26.998047 6.6748594 23.646484 5.1464844 23.646484 5.1464844 C 22.000984 4.3779844 18.958 4.019 15.9375 4.03125 z M 12.705078 8.0019531 C 13.739953 8.0297031 14.762578 8.4927031 15.392578 9.4707031 L 16.001953 10.505859 L 16.609375 9.4707031 C 17.874375 7.5037031 20.709594 7.6264375 22.058594 9.1484375 C 23.302594 10.596438 23.025391 11.531 23.025391 18 L 23.025391 18.001953 L 20.578125 18.001953 L 20.578125 12.373047 C 20.578125 9.7380469 17.21875 9.6362812 17.21875 12.738281 L 17.21875 16 L 14.787109 16 L 14.787109 12.738281 C 14.787109 9.6362812 11.429688 9.7360938 11.429688 12.371094 L 11.429688 18 L 8.9765625 18 C 8.9765625 11.526 8.7043594 10.585438 9.9433594 9.1484375 C 10.622859 8.3824375 11.670203 7.9742031 12.705078 8.0019531 z"/></svg>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 875 875">
|
3
|
+
<defs>
|
4
|
+
<style>
|
5
|
+
.cls-1 {
|
6
|
+
stroke: #000;
|
7
|
+
stroke-miterlimit: 10;
|
8
|
+
stroke-width: 6px;
|
9
|
+
}
|
10
|
+
</style>
|
11
|
+
</defs>
|
12
|
+
<path class="cls-1" d="m684.72,485.57c.22,12.59-11.93,51.47-38.67,81.3-26.74,29.83-56.02,20.85-58.42,20.16s-3.09-4.46-7.89-3.77-9.6,6.17-18.86,7.2-17.49,1.71-26.06-1.37c-4.46.69-5.14.71-7.2,2.24s-17.83,10.79-21.6,11.47c0,7.2-1.37,44.57,0,55.89s3.77,25.71,7.54,36c3.77,10.29,2.74,10.63,7.54,9.94s13.37.34,15.77,4.11c2.4,3.77,1.37,6.51,5.49,8.23s60.69,17.14,99.43,19.2c26.74.69,42.86,2.74,52.12,19.54,1.37,7.89,7.54,13.03,11.31,14.06s8.23,2.06,12,5.83,1.03,8.23,5.49,11.66c4.46,3.43,14.74,8.57,25.37,13.71,10.63,5.14,15.09,13.37,15.77,16.11s1.71,10.97,1.71,10.97c0,0-8.91,0-10.97-2.06s-2.74-5.83-2.74-5.83c0,0-6.17,1.03-7.54,3.43s.69,2.74-7.89.69-11.66-3.77-18.17-8.57c-6.51-4.8-16.46-17.14-25.03-16.8,4.11,8.23,5.83,8.23,10.63,10.97s8.23,5.83,8.23,5.83l-7.2,4.46s-4.46,2.06-14.74-.69-11.66-4.46-12.69-10.63,0-9.26-2.74-14.4-4.11-15.77-22.29-21.26c-18.17-5.49-66.52-21.26-100.12-24.69s-22.63-2.74-28.11-1.37-15.77,4.46-26.4-1.37c-10.63-5.83-16.8-13.71-17.49-20.23s-1.71-10.97,0-19.2,3.43-19.89,1.71-26.74-14.06-55.89-19.89-64.12c-13.03,1.03-50.74-.69-50.74-.69,0,0-2.4-.69-17.49,5.83s-36.48,13.76-46.77,19.93-14.4,9.7-16.12,13.13c.12,3-1.23,7.72-2.79,9.06s-12.48,2.42-12.48,2.42c0,0-5.85,5.86-8.25,9.97-6.86,9.6-55.2,125.14-66.52,149.83-13.54,32.57-9.77,27.43-37.71,27.43s-8.06.3-8.06.3c0,0-12.34,5.88-16.8,5.88s-18.86-2.4-26.4,0-16.46,9.26-23.31,10.29-4.95-1.34-8.38-3.74c-4-.21-14.27-.12-14.27-.12,0,0,1.74-6.51,7.91-10.88,8.23-5.83,25.37-16.11,34.63-21.26s17.49-7.89,23.31-9.26,18.51-6.17,30.51-9.94,19.54-8.23,29.83-31.54c10.29-23.31,50.4-111.43,51.43-116.23.63-2.96,3.73-6.48,4.8-15.09.66-5.35-2.49-13.04,1.71-22.63,10.97-25.03,21.6-20.23,26.4-20.23s17.14.34,26.4-1.37,15.43-2.74,24.69-7.89,11.31-8.91,11.31-8.91l-19.89-3.43s-18.51.69-25.03-4.46-15.43-15.77-15.43-15.77l-7.54-7.2,1.03,8.57s-5.14-8.91-6.51-10.29-8.57-6.51-11.31-11.31-7.54-25.03-7.54-25.03l-6.17,13.03-1.71-18.86-5.14,7.2-2.74-16.11-4.8,8.23-3.43-14.4-5.83,4.46-2.4-10.29-5.83-3.43s-14.06-9.26-16.46-9.6-4.46,3.43-4.46,3.43l1.37,12-12.2-6.27-7-11.9s2.36,4.01-9.62,7.53c-20.55,0-21.89-2.28-24.93-3.94-1.31-6.56-5.57-10.11-5.57-10.11h-20.57l-.34-6.86-7.89,3.09.69-10.29h-14.06l1.03-11.31h-8.91s3.09-9.26,25.71-22.97,25.03-16.46,46.29-17.14c21.26-.69,32.91,2.74,46.29,8.23s38.74,13.71,43.89,17.49c11.31-9.94,28.46-19.89,34.29-19.89,1.03-2.4,6.19-12.33,17.96-17.6,35.31-15.81,108.13-34,131.53-35.54,31.2-2.06,7.89-1.37,39.09,2.06,31.2,3.43,54.17,7.54,69.6,12.69,12.58,4.19,25.03,9.6,34.29,2.06,4.33-1.81,11.81-1.34,17.83-5.14,30.69-25.09,34.72-32.35,43.63-41.95s20.14-24.91,22.54-45.14,4.46-58.29-10.63-88.12-28.8-45.26-34.63-69.26c-5.83-24-8.23-61.03-6.17-73.03,2.06-12,5.14-22.29,6.86-30.51s9.94-14.74,19.89-16.46c9.94-1.71,17.83,1.37,22.29,4.8,4.46,3.43,11.65,6.28,13.37,10.29.34,1.71-1.37,6.51,8.23,8.23,9.6,1.71,16.05,4.16,16.05,4.16,0,0,15.64,4.29,3.11,7.73-12.69,2.06-20.52-.71-24.29,1.69s-7.21,10.08-9.61,11.1-7.2.34-12,4.11-9.6,6.86-12.69,14.4-5.49,15.77-3.43,26.74,8.57,31.54,14.4,43.2c5.83,11.66,20.23,40.8,24.34,47.66s15.77,29.49,16.8,53.83,1.03,44.23,0,54.86-10.84,51.65-35.53,85.94c-8.16,14.14-23.21,31.9-24.67,35.03-1.45,3.13-3.02,4.88-1.61,7.65,4.62,9.05,12.87,22.13,14.71,29.22,2.29,6.64,6.99,16.13,7.22,28.72Z"/>
|
13
|
+
</svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="4 4 40 40"><path d="M 12.5 6 C 8.9280619 6 6 8.9280619 6 12.5 L 6 35.5 C 6 39.071938 8.9280619 42 12.5 42 L 35.5 42 C 39.071938 42 42 39.071938 42 35.5 L 42 12.5 C 42 8.9280619 39.071938 6 35.5 6 L 12.5 6 z M 12.5 9 L 35.5 9 C 37.450062 9 39 10.549938 39 12.5 L 39 35.5 C 39 37.450062 37.450062 39 35.5 39 L 12.5 39 C 10.549938 39 9 37.450062 9 35.5 L 9 12.5 C 9 10.549938 10.549938 9 12.5 9 z M 13.828125 14 L 21.564453 25.056641 L 13.822266 34 L 15.865234 34 L 22.46875 26.351562 L 27.820312 34 L 34.257812 34 L 26.025391 22.234375 L 33.136719 14 L 31.136719 14 L 25.123047 20.943359 L 20.265625 14 L 13.828125 14 z M 16.935547 15.695312 L 19.498047 15.695312 L 31.150391 32.304688 L 28.587891 32.304688 L 16.935547 15.695312 z"/></svg>
|