Compare commits

...
9 Commits
Author SHA1 Message Date
simon ebee3479b1 Load tags for the 'read more' section 2023-10-19 16:32:16 +01:00
simon ca4231f8b0 Add handlebars support for Prism 2023-10-19 16:30:19 +01:00
simon a28cc7cd39 Add comments 2023-10-19 14:51:43 +01:00
simon 9d6cc301e8 Add line before comments 2023-10-19 14:24:04 +01:00
simon 2b68602f86 Update prism 2023-10-18 18:03:54 +01:00
simon 32f457b4cd Update prism 2023-10-18 13:20:29 +01:00
simon 693bc08e61 Add all tags to output 2023-10-18 12:39:54 +01:00
simon be9686d40e Add prism and move Mastodon css to file 2023-10-18 11:24:20 +01:00
simon 5435e0c8c0 Open masto header links externally 2023-10-17 17:04:52 +01:00
12 changed files with 202 additions and 122 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+10 -10
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+89
View File
@@ -0,0 +1,89 @@
.tootlink-tip {
font-size: 0.9em;
padding: 1em;
padding-top: 0.3em;
}
.mastodon-comments-top {
padding-bottom: 1.5em;
}
.mastodon-comment {
background-color: var(--background-color);
border-radius: var(--card-border-radius);
padding: 10px;
margin-bottom: 1rem;
display: flex;
border-top: 1px solid var(--background-secondary);
.mastodon-body {
flex-grow: 1;
}
.mastodon-avatar {
flex-shrink: 1;
}
.mastodon-avatar img {
margin-right: 1rem;
min-width: 60px;
}
.mastodon-meta {
padding-top: 0;
display: flex;
.mastodon-author {
flex-grow: 1;
white-space: nowrap;
.mastodon-author-uid {
font-size: 80%;
color: var(--color-secondary-text);
}
}
.toot-link {
flex-shrink: 1;
text-align: right;
}
}
.mastodon-comment-content {
padding-top: 10px;
}
.disabled {
color: var(--color-secondary-text)
}
}
span.tootlink {
font-size: 0.7em;
font-family: monospace;
padding: 0.2em 0.4em;
cursor: pointer;
border: 1px solid var(--background-secondary)
}
a.date {
font-size: 0.9em;
color: var(--color-secondary-text)
}
span.tootlink::after {
content: "\f0c5";
font-family: "Font Awesome 6 Free";
font-weight: 900;
padding-left: 4px;
}
.tootClick {
animation: tootClickFrames .6s linear;
}
@keyframes tootClickFrames {
0% {
background-color: var(--background-secondary);
}
100% {
background-color: var(--background-color);
}
}
.mastodon-emoji {
display: inline-block;
width: 1em;
height: 1em;
}
.mastodon-comment-content p:first-child {
margin-top: 0;
}
#toot-link-top {
color: var(--ghost-accent-color);
text-decoration: underline;
}
+6
View File
@@ -0,0 +1,6 @@
/* PrismJS 1.29.0
https://prismjs.com/download.html#themes=prism-twilight&languages=markup+css+clike+javascript+bash+go+go-module+handlebars+json+markup-templating+ruby+typescript+yaml&plugins=line-highlight+line-numbers+toolbar+copy-to-clipboard */
code[class*=language-],pre[class*=language-]{color:#fff;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;text-shadow:0 -.1em .2em #000;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}:not(pre)>code[class*=language-],pre[class*=language-]{background:#141414}pre[class*=language-]{border-radius:.5em;border:.3em solid #545454;box-shadow:1px 1px .5em #000 inset;margin:.5em 0;overflow:auto;padding:1em}pre[class*=language-]::-moz-selection{background:#27292a}pre[class*=language-]::selection{background:#27292a}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:hsla(0,0%,93%,.15)}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:hsla(0,0%,93%,.15)}:not(pre)>code[class*=language-]{border-radius:.3em;border:.13em solid #545454;box-shadow:1px 1px .3em -.1em #000 inset;padding:.15em .2em .05em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#777}.token.punctuation{opacity:.7}.token.namespace{opacity:.7}.token.boolean,.token.deleted,.token.number,.token.tag{color:#ce6849}.token.builtin,.token.constant,.token.keyword,.token.property,.token.selector,.token.symbol{color:#f9ed99}.language-css .token.string,.style .token.string,.token.attr-name,.token.attr-value,.token.char,.token.entity,.token.inserted,.token.operator,.token.string,.token.url,.token.variable{color:#909e6a}.token.atrule{color:#7385a5}.token.important,.token.regex{color:#e8c062}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.language-markup .token.attr-name,.language-markup .token.punctuation,.language-markup .token.tag{color:#ac885c}.token{position:relative;z-index:1}.line-highlight.line-highlight{background:hsla(0,0%,33%,.25);background:linear-gradient(to right,hsla(0,0%,33%,.1) 70%,hsla(0,0%,33%,0));border-bottom:1px dashed #545454;border-top:1px dashed #545454;margin-top:.75em;z-index:0}.line-highlight.line-highlight:before,.line-highlight.line-highlight[data-end]:after{background-color:#8693a6;color:#f4f1ef}
pre[data-line]{position:relative;padding:1em 0 1em 3em}.line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:hsla(24,20%,50%,.08);background:linear-gradient(to right,hsla(24,20%,50%,.1) 70%,hsla(24,20%,50%,0));pointer-events:none;line-height:inherit;white-space:pre}@media print{.line-highlight{-webkit-print-color-adjust:exact;color-adjust:exact}}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:hsla(24,20%,50%,.4);color:#f4f1ef;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px #fff}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:after,.line-numbers .line-highlight:before{content:none}pre[id].linkable-line-numbers span.line-numbers-rows{pointer-events:all}pre[id].linkable-line-numbers span.line-numbers-rows>span:before{cursor:pointer}pre[id].linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:rgba(128,128,128,.2)}
pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}
div.code-toolbar{position:relative}div.code-toolbar>.toolbar{position:absolute;z-index:10;top:.3em;right:.2em;transition:opacity .3s ease-in-out;opacity:0}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:0 0;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{color:#bbb;font-size:.8em;padding:0 .5em;background:#f5f2f0;background:rgba(224,224,224,.2);box-shadow:0 2px 0 0 rgba(0,0,0,.2);border-radius:.5em}div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover{color:inherit;text-decoration:none}
+19 -92
View File
@@ -47,6 +47,9 @@ production stylesheet in assets/built/screen.css
*/
@import "mastodon.css";
@import "prism.css";
/* 1. Variables
/* ---------------------------------------------------------- */
@@ -63,9 +66,9 @@ production stylesheet in assets/built/screen.css
--color-border: rgb(0 0 0 / 0.08);
--color-dark-border: rgb(0 0 0 / 0.5);
--font-sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif, "Font Awesome 6 Free", "Font Awesome 6 Brands";
--font-serif: "EB Garamond", Georgia, Times, serif;
--font-serif-alt: Georgia, Times, serif;
--font-mono: "JetBrains Mono", Menlo, Consolas, Monaco, "Liberation Mono", "Lucida Console", monospace;
--font-serif: "EB Garamond", Georgia, Times, serif, "Font Awesome 6 Free", "Font Awesome 6 Brands";
--font-serif-alt: Georgia, Times, serif, "Font Awesome 6 Free", "Font Awesome 6 Brands";
--font-mono: "JetBrains Mono", Menlo, Consolas, Monaco, "Liberation Mono", "Lucida Console", monospace, "Font Awesome 6 Free", "Font Awesome 6 Brands";
--container-width: 1320px;
--container-gap: clamp(24px, 1.7032rem + 1.9355vw, 48px);
--grid-gap: 42px;
@@ -971,12 +974,10 @@ Search LOGO Login Subscribe
}
.gh-card-tag {
display: none;
margin-bottom: 4px;
font-size: 1.2rem;
font-weight: 500;
letter-spacing: 0.01em;
text-transform: uppercase;
}
.gh-card-title {
@@ -1017,6 +1018,10 @@ Search LOGO Login Subscribe
margin-top: 12px;
}
.gh-card-wrapper > .gh-card-meta:not(:empty) ~ .gh-card-meta:not(:empty) {
margin-top: 7px;
}
.gh-card-author + .gh-card-date::before {
content: "—";
margin-right: 4px;
@@ -1954,93 +1959,6 @@ Search LOGO Login Subscribe
.gh-about .gh-button {
margin-top: 32px;
}
.tootlink-tip {
font-size: 0.9em;
padding: 1em;
padding-top: 0.3em;
}
.mastodon-comment {
background-color: var(--background-color);
border-radius: var(--card-border-radius);
padding: 10px;
margin-bottom: 1rem;
display: flex;
border-top: 1px solid var(--background-secondary);
.mastodon-body {
flex-grow: 1;
}
.mastodon-avatar {
flex-shrink: 1;
}
.mastodon-avatar img {
margin-right: 1rem;
min-width: 60px;
}
.mastodon-meta {
padding-top: 0;
display: flex;
.mastodon-author {
flex-grow: 1;
white-space: nowrap;
.mastodon-author-uid {
font-size: 80%;
color: var(--color-secondary-text);
}
}
.toot-link {
flex-shrink: 1;
text-align: right;
}
}
.mastodon-comment-content {
padding-top: 10px;
}
.disabled {
color: var(--color-secondary-text)
}
}
span.tootlink {
font-size: 0.7em;
font-family: monospace;
padding: 0.2em 0.4em;
cursor: pointer;
border: 1px solid var(--background-secondary)
}
a.date {
font-size: 0.9em;
color: var(--color-secondary-text)
}
span.tootlink::after {
content: "\f0c5";
font-family: "Font Awesome 6 Free";
font-weight: 900;
padding-left: 4px;
}
.tootClick {
animation: tootClickFrames .6s linear;
}
@keyframes tootClickFrames {
0% {
background-color: var(--background-secondary);
}
100% {
background-color: var(--background-color);
}
}
.mastodon-emoji {
display: inline-block;
width: 1em;
height: 1em;
}
.mastodon-comment-content p:first-child {
margin-top: 0;
}
#toot-link-top {
color: var(--ghost-accent-color);
text-decoration: underline;
}
/* 14. Post/page
/* ---------------------------------------------------------- */
@@ -2056,15 +1974,24 @@ span.tootlink::after {
.gh-article-header {
margin: clamp(40px, 3.64vw + 25.45px, 72px) 0 40px;
}
.gh-article-tags {
}
.gh-article-tag {
margin-bottom: 12px;
font-size: 1.3rem;
font-weight: 500;
letter-spacing: 0.01em;
text-transform: uppercase;
padding-right: 1em;
color: var(--ghost-accent-color);
}
.gh-article-tag::before {
content: " ";
font-weight: 900;
font-size: 0.8em;
padding-right: 0.3em;
}
.gh-article-title {
font-size: calc(clamp(3.4rem, 1.36vw + 2.85rem, 4.6rem) * var(--factor, 1));
File diff suppressed because one or more lines are too long
+29 -1
View File
@@ -1,6 +1,7 @@
const MASTODON_ACCOUNT_ID = '109285376472065471'
const MASTODON_HOST = 'social.sd.ai'
// Copies the text from an element to the clipboard, and flashes the element to provide visual feedback
async function copyElementTextToClipboard(e)
{
const text = e.textContent
@@ -12,6 +13,7 @@ async function copyElementTextToClipboard(e)
}, 600);
}
// sanitise text content for display
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
@@ -21,16 +23,24 @@ function escapeHtml(unsafe) {
.replace(/'/g, "'");
}
// renders the content - provides an array of toots, the element to add the content to and whether to show a copyable link for replies
function renderMastodonContent(toots, parentElement, showLink) {
// clear the parent element so that we can add the new content
parentElement.innerHTML = ''
// add a simple "no comments" message if there are no toots
if (!Array.isArray(toots) || toots.length === 0) {
document.getElementById('mastodon-comments-list').innerHTML = "<div class='mastodon-comment'>No comments (yet)!</div>"
return
}
// render each toot
for (const toot of toots) {
if (toot.sensitive) {
// don't display toots marked as sensitive
continue
}
// sanitise the toot content for display, including correctly rendering custom emojis
toot.account.display_name = escapeHtml(toot.account.display_name)
toot.account.emojis.forEach(emoji => {
toot.account.display_name = toot.account.display_name.replace(`:${emoji.shortcode}:`,
@@ -40,6 +50,8 @@ function renderMastodonContent(toots, parentElement, showLink) {
toot.content = toot.content.replace(`:${emoji.shortcode}:`,
`<img src="${escapeHtml(emoji.static_url)}" alt="Emoji ${emoji.shortcode}" class="mastodon-emoji" />`);
})
// create a block of HTML content including the toot data
const comment =
`<div class="mastodon-comment">
<div class="mastodon-avatar">
@@ -49,7 +61,7 @@ function renderMastodonContent(toots, parentElement, showLink) {
<div class="mastodon-meta">
<div class="mastodon-author">
<div class="mastodon-author-link">
<a href="${toot.account.url}" rel="nofollow">
<a href="${toot.account.url}" target="_blank" rel="nofollow">
<span>${toot.account.display_name}</span>
</a>
<br/>
@@ -69,30 +81,44 @@ function renderMastodonContent(toots, parentElement, showLink) {
</div>
</div>
</div>`
// Use DOMPurify to create a sanitised element for the toot
const child = DOMPurify.sanitize(comment, {'RETURN_DOM_FRAGMENT': true});
// make all toot links clickable
const links = child.querySelectorAll('.tootlink');
for (const link of links) {
link.onclick = function() { return copyElementTextToClipboard(this); }
}
// insert the toot into the DOM
parentElement.appendChild(child);
}
}
// We set this in the "code injection" footer for any page for which we want to enable comments
let MASTODON_POST_ID
// when the page has finished loading, send a request for the toots
document.addEventListener("DOMContentLoaded", async (event) => {
let url, isComments
// if we're being crawled, don't render comments - may help against spam
const isBot = /bot|google|baidu|bing|msn|teoma|slurp|yandex/i
.test(navigator.userAgent)
// if there is a sidebar, we're expecting to load the toots from the main account
if (document.getElementsByClassName('gh-sidebar').length > 0) {
url = `https://${MASTODON_HOST}/api/v1/accounts/${MASTODON_ACCOUNT_ID}/statuses?exclude_replies=true&exclude_reblogs=true`
}
// if there's a post ID and we're not a bot, we're expecting to load the replies from a specific toot
if (MASTODON_POST_ID && !isBot) {
url = `https://${MASTODON_HOST}/api/v1/statuses/${MASTODON_POST_ID}/context`
isComments = true
}
// find the element to append the content to - if there isn't one, we don't need to query
const element = document.getElementById('mastodon-comments-list')
if (url && element) {
// populate the link to the source toot, if necessary (for replies)
const linkElement = document.getElementById('toot-link-top')
const clipElement = document.getElementById('toot-link-clip')
const tootUrl = `https://${MASTODON_HOST}/@s/${MASTODON_POST_ID}`
@@ -102,11 +128,13 @@ document.addEventListener("DOMContentLoaded", async (event) => {
if (clipElement) {
clipElement.innerText = tootUrl
}
// fetch the data from Mastodon
const response = await fetch(url)
let content = await response.json()
if (isComments) {
content = content.descendants
}
// render the content into the page
const header = document.getElementById('mastodon-comments-header')
if (header) {
header.style.display = ''
+5 -5
View File
@@ -23,7 +23,7 @@
{{#match @custom.header_style "Highlight"}}
{{#if @custom.show_featured_posts}}
{{#match posts.length ">=" 4}}
{{#get "posts" include="authors" limit="16"}}
{{#get "posts" include="authors,tags" limit="16"}}
{{#foreach posts from="5" limit="12"}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
@@ -31,7 +31,7 @@
{{/match}}
{{else}}
{{#match posts.length ">=" 10}}
{{#get "posts" include="authors" limit="22"}}
{{#get "posts" include="authors,tags" limit="22"}}
{{#foreach posts from="11" limit="12"}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
@@ -40,14 +40,14 @@
{{/if}}
{{else match @custom.header_style "Magazine"}}
{{#match posts.length ">=" 7}}
{{#get "posts" include="authors" limit="19"}}
{{#get "posts" include="authors,tags" limit="19"}}
{{#foreach posts from="8" limit="12"}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
{{/get}}
{{/match}}
{{else}}
{{#get "posts" include="authors" limit="12"}}
{{#get "posts" include="authors,tags" limit="12"}}
{{#foreach posts}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
@@ -58,7 +58,7 @@
{{!-- All posts --}}
{{#match feed "index"}}
{{#match pagination.page 2}}
{{#get "posts" include="authors" limit=@config.posts_per_page as |recent|}}
{{#get "posts" include="authors,tags" limit=@config.posts_per_page as |recent|}}
{{#foreach recent}}
{{> "post-card"}}
{{/foreach}}
+9 -3
View File
@@ -17,9 +17,6 @@
</figure>
{{/if}}
<div class="gh-card-wrapper">
{{#if primary_tag}}
<p class="gh-card-tag">{{primary_tag.name}}</p>
{{/if}}
<h3 class="gh-card-title is-title">{{title}}</h3>
{{#if excerpt}}
<p class="gh-card-excerpt is-body">{{excerpt}}</p>
@@ -31,6 +28,15 @@
{{#if @custom.show_publish_date}}
<time class="gh-card-date" datetime="{{date format="YYYY-MM-DD"}}">{{date}}</time>
{{/if}}<!--
--></footer>
<footer class="gh-card-meta"><!--
--><div class="gh-card-tags">
Filed under:
{{#foreach tags}}
{{#if @index}}, {{/if}}
<span class="gh-card-tag">{{name}}</span>
{{/foreach}}
</div><!--
--></footer>
</div>
</a>
+12 -8
View File
@@ -9,13 +9,12 @@
<header class="gh-article-header gh-canvas">
{{#if primary_tag}}
<a class="gh-article-tag" href="{{primary_tag.url}}">{{primary_tag.name}}</a>
{{/if}}
<div class="gh-article-tags">
{{#foreach tags}}
<a class="gh-article-tag" href="{{url}}">{{name}}</a>
{{/foreach}}
</div>
<h1 class="gh-article-title is-title">{{title}}</h1>
{{#if custom_excerpt}}
<p class="gh-article-excerpt is-body">{{custom_excerpt}}</p>
{{/if}}
<div class="gh-article-meta">
<div class="gh-article-author-image">
@@ -50,9 +49,14 @@
</article>
<section class="gh-comments gh-canvas">
<div id="mastodon-comments-header" style="display: none;">
<div class="mastodon-comments-top">
<hr/>
</div>
<h2>Comments</h2>
<p>Comments powered by Mastodon. Reply to <a id="toot-link-top">this status</a> to comment.</p>
<div class="tootlink-tip">
@@ -75,7 +79,7 @@
{{/post}}
{{#get "posts" include="authors" filter="id:-{{post.id}}" limit="4" as |next|}}
{{#get "posts" include="authors,tags" filter="id:-{{post.id}}" limit="4" as |next|}}
{{#if next}}
<section class="gh-container is-grid gh-outer">
<div class="gh-container-inner gh-inner">