Compare commits

...

10 Commits

Author SHA1 Message Date
Daniel Nitsikopoulos
82cc8db296 Reuire tzinfo 2024-11-12 20:10:53 +11:00
Daniel Nitsikopoulos
0564f0af04 Fix posts rendering 2024-11-07 09:35:11 +11:00
Daniel Nitsikopoulos
8011000ce8 More status link cleanup 2024-10-19 20:00:51 +11:00
Daniel Nitsikopoulos
e3aabfe28b And render links on status pages 2024-10-19 19:39:37 +11:00
Daniel Nitsikopoulos
1bee7ecc68 Fix links in status posts 2024-10-19 19:33:07 +11:00
Daniel Nitsikopoulos
a2e7b33363 Clean up statuses 2024-10-19 18:42:46 +11:00
Daniel Nitsikopoulos
9d9828246e Allow page caching to be toggled 2024-10-13 12:49:59 +11:00
Daniel Nitsikopoulos
27c8470280 Update Hanami to 2.2.0b2 2024-10-09 21:38:54 +11:00
Daniel Nitsikopoulos
437a07f26c Add PatternMate to projects 2024-10-04 17:20:08 +10:00
Daniel Nitsikopoulos
4d4758c6cd Update login UI 2024-09-29 22:27:16 +10:00
15 changed files with 162 additions and 90 deletions

15
Gemfile
View File

@@ -4,13 +4,13 @@ source "https://rubygems.org"
ruby "3.3.0"
gem "hanami", "~> 2.2.0.beta"
gem "hanami-router", "~> 2.2.0.beta"
gem "hanami-controller", "~> 2.2.0.beta"
gem "hanami-validations", "~> 2.2.0.beta"
gem "hanami-assets", "~> 2.2.0.beta"
gem "hanami-view", "~> 2.2.0.beta"
gem "hanami-db", "~> 2.2.0.beta"
gem "hanami", "~> 2.2.0.beta2"
gem "hanami-router", "~> 2.2.0.beta2"
gem "hanami-controller", "~> 2.2.0.beta2"
gem "hanami-validations", "~> 2.2.0.beta2"
gem "hanami-assets", "~> 2.2.0.beta2"
gem "hanami-view", "~> 2.2.0.beta2"
gem "hanami-db", "~> 2.2.0.beta2"
# gem "hanami-view", file: "~/Documents/projects/hanami/view"
gem "csv"
@@ -98,5 +98,4 @@ group :test do
gem "timecop"
end
gem "mini_magick", "~> 4.13"

View File

@@ -36,7 +36,7 @@ GIT
GEM
remote: https://rubygems.org/
specs:
activesupport (7.2.1)
activesupport (7.2.1.1)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -104,7 +104,7 @@ GEM
dry-auto_inject (1.0.1)
dry-core (~> 1.0)
zeitwerk (~> 2.6)
dry-cli (1.1.0)
dry-cli (1.2.0)
dry-configurable (1.2.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
@@ -167,7 +167,7 @@ GEM
zeitwerk (~> 2.6)
ed25519 (1.3.0)
erubi (1.13.0)
faker (3.4.2)
faker (3.5.1)
i18n (>= 1.8.11, < 2)
faraday (1.10.4)
faraday-em_http (~> 1.0)
@@ -294,7 +294,7 @@ GEM
rdoc (>= 4.0.0)
reline (>= 0.4.2)
json (2.7.2)
jwt (2.9.1)
jwt (2.9.3)
base64
language_server-protocol (3.17.0.3)
lastfm (1.27.4)
@@ -313,9 +313,10 @@ GEM
net-smtp
matrix (0.4.2)
method_source (1.1.0)
mime-types (3.5.2)
mime-types (3.6.0)
logger
mime-types-data (~> 3.2015)
mime-types-data (3.2024.0903)
mime-types-data (3.2024.1001)
mini_magick (4.13.2)
mini_mime (1.1.5)
minitest (5.25.1)
@@ -328,7 +329,7 @@ GEM
hansi (~> 0.2.0)
mustermann (= 3.0.3)
nenv (0.3.0)
net-imap (0.4.16)
net-imap (0.5.0)
date
net-protocol
net-pop (0.1.2)
@@ -341,7 +342,7 @@ GEM
net-ssh (>= 5.0.0, < 8.0.0)
net-smtp (0.5.0)
net-protocol
net-ssh (7.2.3)
net-ssh (7.3.0)
netrc (0.11.0)
nio4r (2.7.3)
nokogiri (1.16.7-arm64-darwin)
@@ -382,7 +383,7 @@ GEM
nio4r (~> 2.0)
que (2.4.0)
racc (1.8.1)
rack (2.2.9)
rack (2.2.10)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-contrib (2.5.0)
@@ -452,29 +453,28 @@ GEM
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.1)
rspec-core (3.13.2)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.1)
rspec-mocks (3.13.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.1)
rubocop (1.65.1)
rubocop (1.66.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.4, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-ast (>= 1.32.2, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.32.3)
parser (>= 3.3.1.0)
rubocop-performance (1.21.1)
rubocop-performance (1.22.1)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (1.13.0)
@@ -489,7 +489,7 @@ GEM
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
securerandom (0.3.1)
sequel (5.84.0)
sequel (5.85.0)
bigdecimal
shellany (0.0.1)
simple-rss (1.3.3)
@@ -499,24 +499,24 @@ GEM
snaky_hash (2.0.1)
hashie
version_gem (~> 1.1, >= 1.1.1)
sshkit (1.23.1)
sshkit (1.23.2)
base64
net-scp (>= 1.1.2)
net-sftp (>= 2.1.2)
net-ssh (>= 2.8.0)
ostruct
standard (1.40.0)
standard (1.41.1)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.0)
rubocop (~> 1.65.0)
rubocop (~> 1.66.0)
standard-custom (~> 1.0.0)
standard-performance (~> 1.4)
standard-performance (~> 1.5)
standard-custom (1.0.2)
lint_roller (~> 1.0)
rubocop (~> 1.50)
standard-performance (1.4.0)
standard-performance (1.5.0)
lint_roller (~> 1.1)
rubocop-performance (~> 1.21.0)
rubocop-performance (~> 1.22.0)
steam-api (1.2.0)
faraday (~> 1.0)
stringio (3.1.1)
@@ -530,7 +530,7 @@ GEM
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.6.0)
unicode-emoji (3.5.0)
unicode-emoji (3.7.0)
unicode-version (~> 1.0)
unicode-version (1.4.0)
vernier (1.2.1)
@@ -544,7 +544,7 @@ GEM
date
httparty
json (>= 2.0)
zeitwerk (2.6.18)
zeitwerk (2.7.1)
PLATFORMS
arm64-darwin-23
@@ -576,15 +576,15 @@ DEPENDENCIES
gnuplot
gpx
guard-puma
hanami (~> 2.2.0.beta)
hanami-assets (~> 2.2.0.beta)
hanami-controller (~> 2.2.0.beta)
hanami-db (~> 2.2.0.beta)
hanami (~> 2.2.0.beta2)
hanami-assets (~> 2.2.0.beta2)
hanami-controller (~> 2.2.0.beta2)
hanami-db (~> 2.2.0.beta2)
hanami-reloader (~> 2.2.0.beta)
hanami-router (~> 2.2.0.beta)
hanami-router (~> 2.2.0.beta2)
hanami-rspec (~> 2.1.0.rc)
hanami-validations (~> 2.2.0.beta)
hanami-view (~> 2.2.0.beta)
hanami-validations (~> 2.2.0.beta2)
hanami-view (~> 2.2.0.beta2)
hanami-webconsole (~> 2.2.0.beta)
httparty
image_processing (~> 1.0)

File diff suppressed because one or more lines are too long

View File

@@ -77,7 +77,7 @@ module Adamantium
setting :gist_client_token, default: nil
setting :cache_pages, default: true
setting :cache_pages, default: false
setting :steam_api_key, default: nil
setting :steam_user_id, default: nil
end

View File

@@ -8,7 +8,7 @@ module Adamantium
def call(env)
session = env["rack.session"]
return [403, {"Content-Type" => "text/html"}, ["Unauthorized | <a href=\"/admin/login\">Login</>"]] unless @auth_proc.call(session[:user_id])
return [302, {"Location" => "/admin/login"}, []] unless @auth_proc.call(session[:user_id])
@app.call(env)
end

View File

@@ -7,7 +7,7 @@ module Adamantium
def call(key:, params:, content_proc:, expiry:)
calculated_key = "adamantium:#{key}_#{params.join("_")}"
cached_content = cache_store.read(key: calculated_key)
cached_content = cache_store.read(key: calculated_key) if settings.cache_pages
return cached_content if cached_content

View File

@@ -1,7 +1,11 @@
div class="max-w-prose mx-auto prose dark:prose-invert"
form action="/admin/sessions/create" method="POST"
div class=""
h2 class="m-0" Log in
div class="mb-2"
p Enter your email address and we'll send you a login link
div class="mb-2 flex"
label for="email" class="mr-2" Email
input type="email" id="email" name="email" class="border"
input type="email" id="email" name="email" class="border p-1 rounded w-full"
div class="mb-2"
button Login
button class="rounded bg-blue-100 hover:bg-blue-200 text-blue-600 px-2 hover:cursor-pointer w-full" Log in

View File

@@ -12,6 +12,8 @@ module Main
module Decorators
module Posts
class Decorator < SimpleDelegator
REGEXP = URI::DEFAULT_PARSER.make_regexp
def syndicated?
!syndication_sources.empty?
end
@@ -111,11 +113,18 @@ module Main
photos? ? "<div>#{photos.map { |p| "<img src='#{p["value"]}'/>" }.join("")} #{content}</div>" : content
end
def raw_content
res = Sanitize.fragment(content)
def rendered_content
html_text = wrap_anchors_in_object_tags(replace_urls_with_anchors(content))
res = Sanitize.fragment(html_text,
elements: ["img", "p", "object", "a"],
attributes: {"img" => ["alt", "src", "title"], "a" => ["href", "class"]})
res.gsub(prefix_emoji[0], "") if prefix_emoji
end
def raw_content
rendered_content
end
def excerpt
name ? truncate_html(content, 240, true) : content
end
@@ -174,6 +183,25 @@ module Main
private
def replace_urls_with_anchors(text)
url_regex = %r{(?<!<a href="|img src="|video src=")(https?://[^\s]+)(?![^<>]*(</a>|/>))}
text.gsub(url_regex) do |url|
clean_url = Sanitize.fragment(url).gsub(/\s/, "")
%(<a class="hover:underline decoration-wavy" href="#{clean_url}">#{clean_url}</a>)
end
end
def wrap_anchors_in_object_tags(text)
# Regular expression to match <a> tags
anchor_regex = /(<a\s+[^>]*>.*?<\/a>)/
# Replace the matched anchor tags with <object> wrapped anchor tags
text.gsub(anchor_regex) do |anchor_tag|
%(<object>#{anchor_tag}</object>)
end
end
# e.g. geo:-37.75188,144.90417;u=35
def geo
loc = location.split(":")[1]
@@ -211,8 +239,8 @@ module Main
results << "</#{tag}>"
end
results
rescue REXML::ParseException => e
return "<p>No excerpt</p>"
rescue REXML::ParseException
"<p>No excerpt</p>"
end
def attrs_to_s(attrs)

View File

@@ -11,12 +11,34 @@ module Main
module Statuses
class Decorator < Main::Decorators::Posts::Decorator
def raw_content
res = Sanitize.fragment(content,
elements: ["img", "p"],
attributes: {"img" => ["alt", "src", "title"]})
html_text = wrap_anchors_in_object_tags(replace_urls_with_anchors(content))
res = Sanitize.fragment(html_text,
elements: ["img", "p", "object", "a"],
attributes: {"img" => ["alt", "src", "title"], "a" => ["href", "class"]})
res.gsub(prefix_emoji[0], "") if prefix_emoji
end
private
def replace_urls_with_anchors(text)
url_regex = %r{(?<!<a href="|img src="|video src=")(https?://[^\s]+)(?![^<>]*(</a>|/>))}
text.gsub(url_regex) do |url|
clean_url = Sanitize.fragment(url).gsub(/\s/, "")
%(<a class="hover:underline decoration-wavy" href="#{clean_url}">#{clean_url}</a>)
end
end
def wrap_anchors_in_object_tags(text)
# Regular expression to match <a> tags
anchor_regex = /(<a\s+[^>]*>.*?<\/a>)/
# Replace the matched anchor tags with <object> wrapped anchor tags
text.gsub(anchor_regex) do |anchor_tag|
%(<object>#{anchor_tag}</object>)
end
end
end
end
end

View File

@@ -3,11 +3,31 @@ div class="mb-8 h-entry border border-gray-200 m-2 p-4 bg-gray-50 hover:bg-gray-
div
- if !post.photos? && post.key_image.nil?
div class="flex"
div class="text-8xl px-8"
div class="text-5xl px-8"
= post.prefix_emoji
a href="/post/#{post.slug}"
div class="e-content prose-p:mb-0 prose-img:my-2 prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline p-name text-base prose prose-ul:list-none prose-ul:pl-0 prose-li:pl-0 text-gray-800 dark:text-gray-200 prose-a:dark:text-gray-100 mb-4 prose-img:rounded"
== post.raw_content
div
a href="/post/#{post.slug}"
div class="e-content prose-p:mb-0 prose-img:my-2 prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline p-name text-base prose prose-ul:list-none prose-ul:pl-0 prose-li:pl-0 text-gray-800 dark:text-gray-200 prose-a:dark:text-gray-100 mb-4 prose-img:rounded"
== post.raw_content
div class="mb-8"
p class="text-sm text-blue-400 dark:text-indigo-400"
a class="u-url float-left mr-0 border-dotted border-b-2 border-blue-100 hover:border-blue-200 dark:border-indigo-900 dark:hover:border-indigo-800" href="#{post.permalink}"
time class="dt-published" datetime=post.machine_published_at
= post.display_published_at
- if post.webmentions.count > 0
== " &middot; #{post.webmentions.count} comment(s)"
- post.syndicated_to.each do |loc|
== " &middot;"
a rel="syndication" class="u-syndication inline-block ml-1 float-left" href=loc[:url]
- if loc[:location] != ""
== render "shared/#{loc[:location]}", width: "w-4"
- if post.tags.count > 0
span class="text-sm float-left text-blue-400 dark:text-indigo-400 ml-1"
== " &middot; tagged: "
- post.tags.each do |post_tag|
a class="text-sm p-category float-left mr-1 u-url text-pink-400 hover:text-pink-600 dark:text-pink-400 dark:hover:text-pink-100 text-gray-600 ml-1" href="/tagged/#{post_tag.slug}"
= "##{post_tag.label}"
-else
a href="/post/#{post.slug}"
div class="e-content prose-p:mb-0 prose-img:my-2 prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline p-name text-base prose prose-ul:list-none prose-ul:pl-0 prose-li:pl-0 text-gray-800 dark:text-gray-200 prose-a:dark:text-gray-100 mb-4 prose-img:rounded"
@@ -21,18 +41,22 @@ div class="mb-8 h-entry border border-gray-200 m-2 p-4 bg-gray-50 hover:bg-gray-
- post.videos.each do |video|
video class="rounded w-max" autoplay=false loop=false muted=true controls=true
source type="video/mp4" src="#{video["value"]}"
== render "shared/tags", tags: post.tags
div class="mb-8"
p class="mt-4 text-sm text-blue-400 dark:text-indigo-400"
a class="u-url float-left mr-0 border-dotted border-b-2 border-blue-100 hover:border-blue-200 dark:border-indigo-900 dark:hover:border-indigo-800" href="#{post.permalink}"
time class="dt-published" datetime=post.machine_published_at
= post.display_published_at
- if post.webmentions.count > 0
== " &middot; #{post.webmentions.count} comment(s)"
div class="mb-8"
p class="text-sm text-blue-400 dark:text-indigo-400"
a class="u-url float-left mr-0" href="#{post.permalink}"
time class="dt-published" datetime=post.machine_published_at
= post.display_published_at
- if post.webmentions.count > 0
== " &middot; #{post.webmentions.count} comment(s)"
- post.syndicated_to.each do |loc|
== " &middot;"
a rel="syndication" class="u-syndication inline-block ml-1 float-left" href=loc[:url]
- if loc[:location] != ""
== render "shared/#{loc[:location]}", width: "w-4"
- post.syndicated_to.each do |loc|
== " &middot;"
a rel="syndication" class="u-syndication inline-block ml-1 float-left" href=loc[:url]
- if loc[:location] != ""
== render "shared/#{loc[:location]}", width: "w-4"
- if post.tags.count > 0
span class="text-sm float-left text-blue-400 dark:text-indigo-400 ml-1"
== " &middot; tagged: "
- post.tags.each do |post_tag|
a class="text-sm p-category float-left mr-1 u-url text-pink-400 hover:text-pink-600 dark:text-pink-400 dark:hover:text-pink-100 text-gray-600 ml-1" href="/tagged/#{post_tag.slug}"
= "##{post_tag.label}"

View File

@@ -14,7 +14,7 @@ div class="mb-8 max-w-screen-md mx-auto border-t border-solid border-gray-200 da
- if latest_status
div class="h-entry mb-12 p-2 grid grid-cols-7 gap-4 min-h-16 max-w-prose mx-auto bg-fuchsia-100 dark:bg-fuchsia-800 dark:text-gray-200 rounded"
div class="col-span-7 sm:col-span-6 text-left"
a class="u-url block my-auto hover:underline decoration-wavy" href=latest_status.permalink
a class="u-url block my-auto" href=latest_status.permalink
span class="e-content status-body"
- if latest_status.key_image
img loading="lazy" class="float-start max-w-32 rounded mr-2" src=latest_status.key_image
@@ -25,12 +25,16 @@ div class="mb-8 max-w-screen-md mx-auto border-t border-solid border-gray-200 da
div class="justify-between max-w-prose mx-auto mb-8"
h2 class="text-xl text-emerald-600 dark:text-emerald-100" Projects
div class="flex max-w-prose mx-auto mb-2 gap-4"
div class="flex max-w-prose mx-auto mb-4 gap-4"
a class="p-2 flex flex-col flex-1 rounded bg-emerald-100 hover:bg-emerald-400 dark:bg-emerald-600 hover:dark:bg-emerald-400 block text-emerald-900 dark:text-emerald-200 hover:dark:text-emerald-800" href="https://reprint.computer"
strong 🖥️ reprint.computer
p class="font-semibold" 🖥️ reprint.computer
small Syndicate blog posts around the web
a class="p-2 flex flex-col flex-1 rounded bg-emerald-100 hover:bg-emerald-400 dark:bg-emerald-600 hover:dark:bg-emerald-400 block text-emerald-900 dark:text-emerald-200 hover:dark:text-emerald-800" href="https://apps.apple.com/au/app/patternmate/id6464105499"
p class="font-semibold" 🧵️ PatternMate
small Organise your sewing patterns
div class="flex max-w-prose mx-auto mb-2 gap-4"
a class="p-2 flex flex-col flex-1 rounded bg-emerald-100 hover:bg-emerald-400 dark:bg-emerald-600 hover:dark:bg-emerald-400 block text-emerald-900 dark:text-emerald-200 hover:dark:text-emerald-800" href="https://github.com/dnitza/adamantium"
strong 📝 Adamantium
p class="font-semibold" 📝 Adamantium
small The source code behind this blog
div class="flex justify-between max-w-prose mx-auto mb-2"

View File

@@ -1,3 +1,5 @@
require "tzinfo"
module Main
module Views
module Site

View File

@@ -51,9 +51,9 @@ module Micropub
def display_title
title = name
if prefix_emoji
return "#{prefix_emoji} #{title}"
"#{prefix_emoji} #{title}"
else
return title
title
end
end

View File

@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Adamantium - Micropub</title>
<%= stylesheet_tag "app" %>
</head>
<body>
<%= yield %>
<%= javascript_tag "micropub/app" %>
</body>
</html>

View File

@@ -33,7 +33,9 @@ module.exports = {
"2xl": "1.563rem",
"3xl": "1.953rem",
"4xl": "2.441rem",
"5xl": "3.052rem",
"5xl": "4.052rem",
"6xl": "6.052rem",
"7xl": "7.052rem",
"8xl": "6rem",
},
extend: {