Commit Diff


commit - 666a32a200354846aef1c87046dd213ad17868d9
commit + 135c48b2dea5763feabb47606f71ebd713d6567a
blob - c762838b384645e9089fdde04789261559dbb61a
blob + 3b25043d973f7f9d0ad7d185cf90409785702cc8
--- .build.yml
+++ .build.yml
@@ -1,8 +1,8 @@
 image: alpine/latest
 oauth: pages.sr.ht/PAGES:RW
 packages:
-- rsync
-- coreutils
+- ruby
+- ruby-dev
 - go
 - hut
 environment:
@@ -10,16 +10,13 @@ site: jsfree.org
 sources:
 - https://git.sr.ht/~bt/jsfree.org
 tasks:
-- install-smu: |
-    git clone https://git.sr.ht/~bt/smu
-    cd smu
-    sudo make
-    sudo make install
+- install-gems: |
+    sudo gem install bundler 'kramdown:2.4.0' 'rss:0.3.0'
 - build: |
-    cd jsfree.org
-    make build
+    cd wruby
+    sudo make build
 - package: |
     cd jsfree.org/build
     tar -cvz . > ../../site.tar.gz
 - upload: |
-    hut pages publish -d jsfree.org site.tar.gz
\ No newline at end of file
+    hut pages publish -d jsfree.org site.tar.gz
blob - /dev/null
blob + 619b537668489eba5ff985e81afa2c1228281818 (mode 644)
--- /dev/null
+++ .ruby-version
@@ -0,0 +1 @@
+3.3.3
blob - b16a580d4287ec6d562703ab8d5de8619a7ca506
blob + 9d7e5a1fcd3c1f8b86a191e8dd4b0f3ae227ac36
--- Makefile
+++ Makefile
@@ -1,13 +1,8 @@
 build:
-	sh ./barf
-	rsync -r public/ build/public
+	rm -rf build && mkdir build
+	ruby wruby.rb 
 
 clean:
 	rm -rf build/*
 
-watch:
-	while true; do \
-	ls -d .git/* * posts/* pages/* header.html | entr -cd make ;\
-	done
-
-.PHONY: build clean watch
+.PHONY: build clean
blob - 4b065492862b66161543d02d8c914165bbdc3fe8
blob + 26b9e260b25e708206046035f39ae87129ebcc2c
--- README.md
+++ README.md
@@ -5,7 +5,9 @@ The main website and project for jsfree.org
 
 [jsfree.org](https://jsfree.org)
 
-contributing
+Built with [wruby](https://wruby.btxx.org)
+
+Contributing
 ------------
 
-Submit changes, patches or suggestions via email [here](mailto:jsfree@patches.btxx.org)
+Submit changes, patches or suggestions [here](https://lists.sr.ht/~bt/jsfree-devel)
blob - e027fc052c2394db6f8d29a7af7b37e90accf18f (mode 755)
blob + /dev/null
--- barf
+++ /dev/null
@@ -1,158 +0,0 @@
-#!/bin/sh
-
-domain="https://jsfree.org"
-
-# Check the operating system
-os_name=$(uname -s)
-
-if [ "$os_name" = "OpenBSD" ]; then
-    alias sed=gsed
-    alias date=gdate
-    alias rsync=openrsync
-elif [ "$os_name" = "Darwin" ]; then
-    alias sed=gsed
-    alias date=gdate
-fi
-
-set -eu
-MARKDOWN=smu
-IFS='	'
-
-# Create tab separated file with filename, title, creation date, last update
-index_tsv() {
-	for f in "$1"/*.md
-	do
-		title=$(sed -n '/^# /{s/# //p; q}' "$f")
-		printf '%s\t%s\t%s\t%s\n' "$f" "${title:="No Title"}"
-	done
-}
-
-index_html() {
-	# Print header
-	title=$(sed -n '/^# /{s/# //p; q}' index.md)
-	sed "s/{{TITLE}}/$title/" header.html
-
-	# Intro text
-	$MARKDOWN index.md
-
-	echo "<ul>"
-
-	# Posts
-	while read -r f title created; do
-		link=$(echo "$f" | sed -E 's|.*/(.*).md|\1/|')
-		created=$(echo $(head -3 "$f" | tail -1))
-		echo "<li>$created &middot; <a href=\"$link\">$title</a></li>"
-	done < "$1" | sort -r
-
-	echo "</ul>"
-
-	# Print footer after post list
-	cat footer.html
-}
-
-atom_xml() {
-	uri=$(sed -rn '/atom.xml/ s/.*href="([^"]*)".*/\1/ p' header.html)
-	first_commit_date=$(git log --pretty='format:%ai' . | cut -d ' ' -f1 | tail -1)
-
-	cat <<EOF
-<?xml version="1.0" encoding="utf-8"?>
-<feed xmlns="http://www.w3.org/2005/Atom">
-	<title>$(sed -n '/^# /{s/# //p; q}' index.md)</title>
-	<link href="$domain/atom.xml" rel="self" />
-	<updated>$(date +%FT%TZ)</updated>
-	<author>
-		<name>$(git config user.name)</name>
-	</author>
-	<id>$domain,$first_commit_date:default-atom-feed/</id>
-EOF
-
-	while read -r f title created; do
-
-		content=$($MARKDOWN "$f" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g; s/'"'"'/\&#39;/g')
-		post_link=$(echo "$f" | sed -E 's|posts/(.*).md|\1|')
-		basic_date=$(echo $(head -3 "$f" | tail -1))
-		published_date=$(date -d $basic_date -u +%Y-%m-%dT10:%M:%SZ)
-
-		cat <<EOF
-	<entry>
-		<title>$title</title>
-		<content type="html">$content</content>
-		<link href="$domain/$post_link"/>
-		<id>$domain/$post_link</id>
-		<updated>$published_date</updated>
-		<published>$published_date</published>
-	</entry>
-EOF
-	done < "$1"
-
-	echo '</feed>'
-}
-
-rss_xml() {
-	uri=$(sed -rn '/rss.xml/ s/.*href="([^"]*)".*/\1/ p' header.html)
-	first_commit_date=$(git log --pretty='format:%ai' . | cut -d ' ' -f1 | tail -1)
-
-	cat <<EOF
-<?xml version="1.0" encoding="utf-8"?>
-<rss version="2.0">
-	<channel>
-		<title>$(sed -n '/^# /{s/# //p; q}' index.md)</title>
-		<link>$domain/rss.xml</link>
-		<description>Feed description here</description>
-		<lastBuildDate>$(date -u +"%a, %d %b %Y %H:%M:%S %z")</lastBuildDate>
-		<pubDate>$(date -u +"%a, %d %b %Y %H:%M:%S %z")</pubDate>
-		<generator>Custom RSS Generator</generator>
-		<ttl>1800</ttl>
-EOF
-
-	while read -r f title created; do
-		content=$($MARKDOWN "$f" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g; s/'"'"'/\&#39;/g')
-		post_link=$(echo "$f" | sed -E 's|posts/(.*).md|\1|')
-		basic_date=$(echo $(head -3 "$f" | tail -1))
-		published_date=$(date -d "$basic_date" -u +"%a, %d %b %Y %H:%M:%S %z")
-
-		cat <<EOF
-		<item>
-			<title>$title</title>
-			<description>$content</description>
-			<link>$domain/$post_link</link>
-			<guid isPermaLink="false">$domain/$post_link</guid>
-			<pubDate>$published_date</pubDate>
-		</item>
-EOF
-	done < "$1"
-
-	echo '</channel>'
-	echo '</rss>'
-}
-
-write_page() {
-	filename=$1
-	directory=$(echo $(basename "$filename" .md))
-	$(mkdir -p build/$directory)
-	target=$(echo "$filename" | sed -r 's|\w+/(.*).md|build/\1/index.html|')
-	created=$(echo $(head -3 "$filename" | tail -1))
-	title=$2
-
-	$MARKDOWN "$filename" | \
-		cat header.html - |\
-		sed "s|{{TITLE}}|$title|" \
-		> "$target" && cat footer.html >> "$target"
-}
-
-rm -rf build && mkdir build
-
-# Blog posts
-index_tsv posts | sort -rt "	" -k 3 > build/posts.tsv
-index_html build/posts.tsv > build/index.html
-atom_xml build/posts.tsv > build/atom.xml
-rss_xml build/posts.tsv > build/rss.xml
-while read -r f title created; do
-	write_page "$f" "$title" "$created"
-done < build/posts.tsv
-
-# Pages
-index_tsv pages > build/pages.tsv
-while read -r f title created; do
-	write_page "$f" "$title" "$created"
-done < build/pages.tsv
blob - 894338a2baf5b6614095f9feeabc339a8e0e69db
blob + 2c8db8743112ada2016adc67f5e3b8a9c34a3c30
--- footer.html
+++ footer.html
@@ -1,7 +1,7 @@
 <footer role="contentinfo">
     <br>
     <hr>
-    <p>&rarr; <a href="https://git.btxx.org/jsfree">Help edit or improve this project</a>.</p>
+    <p>&rarr; <a href="https://git.sr.ht/~bt/jsfree.org">Help edit or improve this project</a>.</p>
     <hr>
     <h2 id="menu">Menu Navigation</h2>
     <ul>
@@ -10,6 +10,7 @@
         <li><a href="/code-forges">Code Forges</a></li>
         <li><a href="/domains">Domain Registrars</a></li>
         <li><a href="/email">Email</a></li>
+        <li><a href="/extras">Extras</a></li>
         <li><a href="/forums">Forums</a></li>
         <li><a href="/games">Games</a></li>
         <li><a href="/search-engines">Search Engines</a></li>
@@ -19,7 +20,7 @@
         <li><a href="/web-hosting">Web Hosting</a></li>
     </ul>
     <small>
-        Built with <a href="https://git.sr.ht/~bt/barf">barf</a>. <br>
+        Built with <a href="https://git.sr.ht/~bt/wruby">wruby</a>. <br>
         Hosted on <a href="https://sourcehut.org">sourcehut</a>. <br>
         The <a href="https://git.sr.ht/~bt/jsfree.org">code for this site</a> is <a href="https://git.sr.ht/~bt/jsfree.org/tree/master/item/LICENSE">MIT</a>.
     </small>
blob - /dev/null
blob + ea5e802e5f16605e0f573d3f30d20c2c424b3cb3 (mode 644)
--- /dev/null
+++ _config.yml
@@ -0,0 +1,21 @@
+site_url: 'https://jsfree.org'
+site_name: 'jsfree.org'
+author_name: 'Bradley Taunt'
+
+directories:
+  posts: 'posts'
+  pages: 'pages'
+  public: 'public'
+  output: 'build'
+  posts_output: 'build/posts'
+  pages_output: 'build/'
+  
+files:
+  header: 'header.html'
+  footer: 'footer.html'
+  root_index: 'index.md'
+  posts_index: 'pages/posts.md'
+  rss: 'build/index.rss'
+
+misc:
+  post_count: 5
blob - 197f71ed205e1a6318d717941a9023508f5e2a4e
blob + 24e5d50734eae64a3f99415d5a3497ff21b8ebd3
--- header.html
+++ header.html
@@ -3,10 +3,13 @@
 <head>
 	<meta charset="utf-8">
 	<meta name="viewport" content="width=device-width, initial-scale=1">
+	<meta name="color-scheme" content="dark light">
 	<link rel="icon" href="data:,">
 	<title>{{TITLE}}</title>
 	<link href="https://jsfree.org/atom.xml" type="application/atom+xml" rel="alternate" title="Atom feed for blog posts" />
-	<style>*{box-sizing:border-box;}body{font-family:sans-serif;line-height:1.33;margin:0 auto;max-width:650px;padding:1rem;}img{max-width:100%;}pre{overflow:auto;}abbr{background:yellow;color:blue;cursor:help;}nav a.menu{float:right;}.logo{display:block;max-width:60px;}</style>
+	<style>*{box-sizing:border-box;}body{font-family:sans-serif;line-height:1.33;margin:0
+auto;max-width:650px;padding:1rem;}blockquote{border-left:4px
+  solid;padding-left:5px;}img{max-width:100%;}pre{border:1px solid;overflow:auto;padding:5px;}table{text-align:left;width:100%;}.posts,#menu{list-style:none;padding:0;}.posts li{margin-bottom:8px;}.posts li span{display:block;font-size:90%;}#menu li{display:inline-block;margin-right:8px;}.footnotes{font-size:90%;}abbr{background:yellow;color:blue;cursor:help;}nav a.menu{float:right;}.logo{display:block;max-width:60px;}</style>
 </head>
 
 <nav>
blob - f927869c1a4191a939ddb8e4eff340a8b332ba76
blob + 7cd1a5fdccd536278db9c90294d54347f2114b00
--- index.md
+++ index.md
@@ -25,4 +25,4 @@ Things you can do to contribute to the project (in ord
 
 ## News
 
-You can keep up-to-date by following the <a href="/atom.xml">Atom feed</a>.
+You can keep up-to-date by following the <a href="/index.rss">RSS feed</a>.
blob - beed2788d8663c32777bd86b93566c87773e4190
blob + 6002dea13610ab9148fcc7945c2fa41efae83663
--- pages/browsers.md
+++ pages/browsers.md
@@ -24,6 +24,21 @@ This page is a collection of web browsers that have Ja
 </dl>
 
 <dl>
+	<dt><a href="https://github.com/acg/w3m">W3M</a></dt>
+	<dd>A text-based, TUI web browser.</dd>
+</dl>
+
+<dl>
+	<dt><a href="https://lynx.browser.org/">Lynx</a></dt>
+	<dd>A text browser for the World Wide Web.</dd>
+</dl>
+
+<dl>
+	<dt><a href="http://links.twibright.com/">Links</a></dt>
+	<dd>A text and graphic web browser.</dd>
+</dl>
+
+<dl>
 	<dt><a href="https://github.com/ocoufal/mothra">Mothra</a></dt>
 	<dd>A lightweight web browser for Plan 9.</dd>
 </dl>
blob - /dev/null
blob + a382e68e6164a05b40085da707504606c278d32f (mode 644)
--- /dev/null
+++ pages/extras.md
@@ -0,0 +1,13 @@
+# Extras
+
+This page is a collection of software that falls outside the other main categories (that all work without JavaScript)
+
+<dl>
+  <dt><a href="https://plaintextsports.com/">Plain Text Sports</a></dt>
+  <dd>Sports scores rendered in plain text.</dd>
+</dl>
+
+<dl>
+  <dt><a href="https://wttr.in/">wttr.in</a></dt>
+  <dd>Weather information rendered in plain text.</dd>
+</dl>
\ No newline at end of file
blob - /dev/null
blob + ef3567d3ed5508f21303311dd3453fc725c7971c (mode 644)
--- /dev/null
+++ pages/posts.md
@@ -0,0 +1 @@
+# Posts
blob - c329772345c6d142236e8b109db6d8cfd863cc48
blob + 57f4d6bee2b3851e1fbd109754913ed2a5e07e82
--- pages/web-hosting.md
+++ pages/web-hosting.md
@@ -6,3 +6,8 @@ This page is a collection of web hosting providers tha
 	<dt><a href="https://nearlyfreespeech.net">NearlyFreeSpeech.net</a></dt>
 	<dd>The masters of only pay for what you use hosting since 2002.</dd>
 </dl>
+
+<dl>
+	<dt><a href="https://bitfolk.com/">bitfolk</a></dt>
+	<dd>No-nonsense VPS host. Reliable, feature-rich and affordable virtual private servers.</dd>
+</dl>
blob - /dev/null
blob + 446d83fc2da61b0b2bf99c65a6730c09c3946a98 (mode 644)
--- /dev/null
+++ wruby.rb
@@ -0,0 +1,150 @@
+require 'bundler/inline'
+gemfile do
+  gem 'kramdown', '2.4.0'
+  gem 'rss', '0.3.0'
+end
+
+require 'kramdown'
+require 'fileutils'
+require 'date'
+require 'rss'
+require 'find'
+require 'yaml'
+
+# Load configuration
+config = YAML.load_file('_config.yml')
+
+site_url = config['site_url']
+site_name = config['site_name']
+author_name = config['author_name']
+
+posts_dir = config['directories']['posts']
+pages_dir = config['directories']['pages']
+public_dir = config['directories']['public']
+output_dir = config['directories']['output']
+posts_output_dir = config['directories']['posts_output']
+pages_output_dir = config['directories']['pages_output']
+
+header_file = config['files']['header']
+footer_file = config['files']['footer']
+root_index_file = config['files']['root_index']
+posts_index_file = config['files']['posts_index']
+rss_file = config['files']['rss']
+
+post_count = config['misc']['post_count']
+
+# Make sure output directories exist
+[posts_output_dir, pages_output_dir].each { |dir| FileUtils.mkdir_p(dir) }
+
+# Read the footer content
+footer_content = File.read(footer_file)
+
+# Replace the title meta tag in the header.html
+def replace_title_placeholder(header_content, title)
+  header_content.gsub('<title>{{TITLE}}</title>', "<title>#{title}</title>")
+end
+
+# Grab the title from each markdown file
+def extract_title_from_md(lines)
+  first_line = lines.first
+  first_line&.start_with?('# ') ? first_line[2..-1].strip : 'Blog Index'
+end
+
+# Convert markdown files
+def process_markdown_files(input_directory, output_directory, header_content, footer_content)
+  items = []
+
+  Find.find(input_directory) do |path|
+    next unless path =~ /\.md\z/
+
+    md_content = File.read(path)
+    lines = md_content.lines
+
+    title = extract_title_from_md(lines)
+    date = Date.parse(lines[2]&.strip || '') rescue Date.today
+    html_content = Kramdown::Document.new(md_content).to_html
+
+    relative_path = path.sub(input_directory + '/', '').sub('.md', '')
+    item_dir = File.join(output_directory, relative_path)
+    output_file = "#{item_dir}/index.html"
+    FileUtils.mkdir_p(item_dir)
+
+    header = replace_title_placeholder(header_content, title)
+    File.write(output_file, header + html_content + footer_content)
+
+    items << { title: title, date: date, link: relative_path + '/', content: html_content }
+  end
+
+  items
+end
+
+# Create the root index file
+def generate_index(posts, header_content, footer_content, root_index_file, post_count, output_dir, posts_dir)
+  root_index_content = File.read(root_index_file)
+  root_title = extract_title_from_md(root_index_content.lines)
+  root_html = Kramdown::Document.new(root_index_content).to_html
+
+  header = replace_title_placeholder(header_content, root_title)
+
+  index_content = header + root_html + "<ul class=\"posts\">\n"
+  posts.first(post_count).each { |post| index_content << "<li><span>#{post[:date]}</span><a href='/#{posts_dir}/#{post[:link]}'>#{post[:title]}</a></li>\n" }
+  index_content << "</ul>\n<p><a href='/#{posts_dir}'>View all posts &rarr;</a></p>\n" + footer_content
+
+  File.write("#{output_dir}/index.html", index_content)
+end
+
+# Create the full posts list page
+def generate_full_posts_list(posts, header_content, footer_content, posts_index_file, output_dir, posts_dir)
+  posts_index_content = File.read(posts_index_file)
+  posts_title = extract_title_from_md(posts_index_content.lines)
+  posts_html = Kramdown::Document.new(posts_index_content).to_html
+
+  header = replace_title_placeholder(header_content, posts_title)
+
+  list_content = header + posts_html + "<ul class=\"posts\">\n"
+  posts.each { |post| list_content << "<li><span>#{post[:date]}</span><a href='/#{posts_dir}/#{post[:link]}'>#{post[:title]}</a></li>\n" }
+  list_content << "</ul>\n" + footer_content
+
+  File.write("#{output_dir}/posts/index.html", list_content)
+end
+
+# Generate the RSS 2.0 feed
+def generate_rss(posts, rss_file, author_name, site_name, site_url, posts_dir)
+  rss = RSS::Maker.make("2.0") do |maker|
+    maker.channel.author = author_name
+    maker.channel.updated = Time.now.to_s
+    maker.channel.title = "#{site_name} RSS Feed"
+    maker.channel.description = "The official RSS Feed for #{site_url}"
+    maker.channel.link = site_url
+
+    posts.each do |post|
+      date = Date.parse(post[:date].to_s).to_time + 12*60*60 # Force time to midday
+      item_link = "#{site_url}/#{posts_dir}/#{post[:link]}"
+      item_title = post[:title]
+      item_content = post[:content]
+
+      maker.items.new_item do |item|
+        item.link = item_link
+        item.title = item_title
+        item.updated = date.to_s
+        item.pubDate = date.rfc822
+        item.description = item_content
+      end
+    end
+  end
+
+  File.write(rss_file, rss)
+end
+
+# Process header, posts, pages, etc.
+header_content = File.read(header_file)
+
+posts = process_markdown_files(posts_dir, posts_output_dir, header_content, footer_content).sort_by { |post| -post[:date].to_time.to_i }
+pages = process_markdown_files(pages_dir, pages_output_dir, header_content, footer_content)
+
+generate_index(posts, header_content, footer_content, root_index_file, post_count, output_dir, posts_dir)
+generate_full_posts_list(posts, header_content, footer_content, posts_index_file, output_dir, posts_dir)
+FileUtils.cp_r(public_dir, output_dir)
+generate_rss(posts, rss_file, author_name, site_name, site_url, posts_dir)
+
+puts "Blog built successfully in '#{output_dir}' folder. Have a great day!"