Renamed blowdit to bludit-blowfish / blowfish

This commit is contained in:
spleenftw committed 2026-06-04 11:10:41 +02:00
1 parent 7906c15fa7
commit 34c1c18f1b
23 files changed
+232 -227

No files matched your search

+55 -50
View File
@@ -1,93 +1,93 @@
# Blowdit # Blowfish
A clean, minimal theme for [Bludit](https://www.bludit.com) inspired by the A clean, minimal theme for [Bludit](https://www.bludit.com) inspired by the
[Blowfish](https://blowfish.page/) Hugo theme. [Blowfish](https://blowfish.page/) Hugo theme.
![Bludit](https://img.shields.io/badge/Bludit-3.9+-informational) ![License](https://img.shields.io/badge/license-MIT-green) ![Version](https://img.shields.io/badge/version-1.6.0-blue) ![Bludit](https://img.shields.io/badge/Bludit-3.22+-informational) ![License](https://img.shields.io/badge/license-MIT-green) ![Version](https://img.shields.io/badge/version-1.6.0-blue)
## Features ## Features
- 🎨 **Five-theme swatch picker** — click the theme button to open a dropdown with coloured swatches for Light, Dark, Nord, Dracula and Catppuccin. Choice persists via `localStorage` + cookie (no flash on next load); falls back to OS preference. - **Five-theme swatch picker** — Light, Dark, Nord, Dracula, Catppuccin. Choice persists via `localStorage` + cookie (no flash on next load); falls back to OS preference.
- 🧱 **Monochrome base design** — restrained grayscale palette with crisp Inter typography. - **Monochrome base design** — restrained grayscale palette with crisp Inter typography (self-hosted, no Google Fonts).
- 👤 **Profile hero** — circular avatar, site title, slogan and social links on the homepage front page. - **Profile hero** — circular avatar, site title, slogan and social links on the homepage.
- 📖 **Sticky Table of Contents** — articles that contain headings (`h2`/`h3`/`h4`) get a left sidebar ToC that sticks while scrolling and highlights the active section. Layout automatically switches to 3-column with a wider container. - **Sticky Table of Contents** — articles with `h2`/`h3`/`h4` headings get a left sidebar ToC that sticks while scrolling and highlights the active section. Layout switches to 3-column with a wider container. A floating button opens a drawer on mobile.
- 📚 **Smart right sidebar** — shows only About / Categories / Hit Counter on the homepage (navigation is redundant there); shows the full plugin set on article pages. - **Smart right sidebar** — shows only About / Categories / Hit Counter on the homepage; full plugin set on article pages.
- 🔀 **Randomized navigation** — the Navigation/Pages plugin list is shuffled on each load. - **Client-side search overlay** — `/` key or navbar button; searches titles, descriptions and tags client-side.
- ⏱️ **Reading time & dates** shown on the post list and single posts. - **Keyboard shortcuts** — `/` (search), `g h` (go home), `t` (back to top), `Esc` (close overlay).
- 🔗 **Social icons** in the navbar and hero (SVG files in `img/`). - **Randomized navigation** — the Navigation/Pages plugin list is shuffled on every load.
- 🏷️ **Tags & categories** rendered as pill badges. - **Reading time & dates** shown on the post list and single posts.
- 🖼️ **Image lightbox** — clicking any article image opens it full-screen. A dark overlay with a magnifier icon appears on hover to hint at the interaction. Closes on click or Esc. - **Social icons** in the navbar and hero (SVG files in `img/`).
- 🎠 **Image carousel** — use a ` ```carousel ` fenced block, one image per line (markdown image syntax or plain URL). - **Tags & categories** rendered as pill badges.
- 📑 **Tabbed code blocks** — use a ` ```tabs ` fenced block with `[Tab Name]` or `@tab Name` headers to show content from multiple servers/environments side by side. - **Image lightbox** — clicking any article image opens it full-screen with a dark overlay and zoom hint.
- 📜 **Code block scrollbar** — always-visible horizontal scrollbar on `<pre>` blocks with a custom slim style that respects the active theme. - **Image carousel** — ` ```carousel ` fenced block, one image per line (markdown syntax or bare URL with optional `| caption`).
- 📱 Fully responsive; no flash of the wrong theme on load. - **Tabbed code blocks** — ` ```tabs ` fenced block with `[Tab Name]` or `@tab Name` headers.
- **Code block header bar** — language label + copy-to-clipboard button on every code block.
- **Callout/admonition boxes** — `> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`, `> [!DANGER]`, `> [!IMPORTANT]`.
- **Series navigation** — posts tagged `series-*` display an ordered series box.
- **SEO** — Open Graph, Twitter Card, JSON-LD (WebSite, Article, BreadcrumbList, sameAs), canonical URL, theme-color meta, cover-image preload.
- **Accessibility** — skip link, ARIA labels, `:focus-visible`, heading anchor links, print stylesheet.
- **PWA** — web app manifest included.
- Fully responsive; no jQuery, no Bootstrap JS, no webfont requests.
## Installation ## Installation
1. Download or clone this repository into your Bludit `bl-themes` directory: 1. Clone or copy this repository into your Bludit `bl-themes/` directory:
``` ```
/usr/share/nginx/html/bl-themes/ bl-themes/bludit-blowfish/
``` ```
2. In the Bludit admin panel go to **Settings → Themes** and activate **Blowdit**. 2. In the Bludit admin panel go to **Settings → Themes** and activate **Blowfish**.
3. Add your assets to `blowdit/img/`: 3. Add your assets to `img/`:
- Your avatar as `spleenftw.jpeg` — used in the homepage hero and the article sidebar compact card. - Your avatar as `spleenftw.jpeg` — used in the homepage hero and the article sidebar card.
To change the filename, update `php/sidebar.php` and `php/home.php`. To change the filename, update `php/sidebar.php` and `php/home.php`.
## Layout ## Layout
``` ```
blowdit/ bludit-blowfish/
├── index.php Main template (layout, all inline JS) ├── index.php Main template (layout, sidebar logic, JS config)
├── metadata.json Theme metadata ├── metadata.json Theme metadata
├── manifest.webmanifest
├── css/ ├── css/
│ └── style.css All styles (CSS custom properties, 5 themes) │ └── style.css All styles (CSS custom properties, 5 themes)
├── js/
│ └── blowfish.js Deferred JS bundle (ToC, lightbox, carousel, search, …)
├── php/ ├── php/
│ ├── head.php <head>, fonts, favicon, anti-FOUC theme script │ ├── head.php <head>, fonts, favicon, anti-FOUC theme script
│ ├── navbar.php Navbar, social SVG icons, theme-picker dropdown │ ├── navbar.php Navbar, social icons, theme-picker dropdown
│ ├── home.php Profile hero + post listing │ ├── home.php Profile hero + post listing
│ ├── page.php Single page / post │ ├── page.php Single page/post
│ ├── sidebar.php Right sidebar plugin container │ ├── sidebar.php Right sidebar plugin container
│ ├── toc.php Left ToC sidebar (article pages with headings) │ ├── toc.php Left ToC sidebar (article pages with headings)
│ └── footer.php Footer │ ├── footer.php Footer + search-index JSON
│ └── icons.php Inline-SVG icon helper + shared PHP functions
├── img/ Social SVG icons + avatar ├── img/ Social SVG icons + avatar
└── languages/ Translations (10 languages) └── languages/ Translations (10 languages)
``` ```
## Themes ## Themes
Each theme is a set of CSS custom properties. Light lives in `:root`; the others Each colour theme is a block of CSS custom properties. Light lives in `:root`; the others override it under `[data-theme="dark"]`, `[data-theme="nord"]`, `[data-theme="dracula"]` and `[data-theme="catppuccin"]`.
override it under `[data-theme="dark"]`, `[data-theme="nord"]`,
`[data-theme="dracula"]` and `[data-theme="catppuccin"]`.
The attribute is set on `<html>` before the first paint (in `php/head.php`) to The `data-theme` attribute is set on `<html>` before the first paint (in `php/head.php`) to avoid a flash. The navbar swatch picker updates it at runtime and persists the choice to `localStorage` and a cookie.
avoid a flash. The navbar swatch picker updates it at runtime and persists the
choice to both `localStorage` and a cookie.
To add a new theme, add a variable block in `css/style.css` and add the theme To add a new theme: add a variable block in `css/style.css`, register the theme name + background colour in `index.php` (`$blowfishThemeBg`), and add a swatch entry in `php/navbar.php`.
name + swatch colours to the picker in `php/navbar.php` and the `THEMES` array
in `index.php`.
## Table of Contents ## Table of Contents
When an article's rendered HTML contains any `h2`, `h3` or `h4` headings, the When an article's rendered HTML contains any `h2`, `h3` or `h4` headings, the layout automatically switches from 2-column to 3-column:
layout automatically switches from 2-column to 3-column:
``` ```
[ Left ToC (col-md-3) ] [ Article (col-md-6) ] [ Right sidebar (col-md-3) ] [ Left ToC (col-md-3) ] [ Article (col-md-6) ] [ Right sidebar (col-md-3) ]
``` ```
The ToC is hidden on mobile. The left column has no `align-self-start` so it The sidebar ToC is hidden on mobile; a floating button opens a slide-up drawer instead. The container switches to `.container-wide` (1400 px max-width) and the navbar widens to match via `body.has-toc .navbar .container`.
stretches to the full article height, which is what `position: sticky` needs to
function. The container switches to `.container-wide` (1400 px max-width) and
the navbar inner container widens to match via `body.has-toc .navbar .container`.
## Tabbed code blocks ## Tabbed code blocks
Use a ` ```tabs ` fenced block. Delimit tabs with `[Tab Name]` (brackets) or Use a ` ```tabs ` fenced block. Delimit tabs with `[Tab Name]` (brackets) or `@tab Tab Name` (@ prefix):
`@tab Tab Name` (@ prefix):
```` ````
```tabs ```tabs
@@ -101,8 +101,7 @@ root@pgs02:~# psql -c "SELECT …"
## Image carousel ## Image carousel
Use a ` ```carousel ` fenced block. Each non-empty line is one slide. Accepts Use a ` ```carousel ` fenced block. Each non-empty line is one slide. Accepts standard markdown image syntax or a bare URL with an optional `| caption`:
standard markdown image syntax or a bare URL with an optional `| caption`:
```` ````
```carousel ```carousel
@@ -111,13 +110,19 @@ standard markdown image syntax or a bare URL with an optional `| caption`:
``` ```
```` ````
Use `![]()` (empty alt) to show no caption. Supports swipe on touch devices. Supports swipe on touch devices.
## Randomized navigation ## Callout boxes
If the **Navigation** (or **Pages**) plugin is in the sidebar, its list is ```markdown
shuffled on every page load. The plugin output is wrapped in `.js-random-nav`; > [!NOTE]
adjust the detection in `index.php` if your plugin uses a different class. > Useful information.
> [!WARNING]
> Something to watch out for.
```
Supported types: `NOTE`, `TIP`, `WARNING`, `DANGER`, `IMPORTANT`.
## License ## License
+2 -2
View File
@@ -1,5 +1,5 @@
/* ======================================== /* ========================================
BLOWDIT — a Bludit theme inspired by Blowfish BLOWFISH — a Bludit theme inspired by Blowfish
Monochrome palette with light & dark modes Monochrome palette with light & dark modes
======================================== */ ======================================== */
+25 -25
View File
@@ -1,54 +1,54 @@
<?php <?php
// Inline-SVG icon helper (replaces the Bootstrap Icons webfont). Included // Inline-SVG icon helper (replaces the Bootstrap Icons webfont). Included
// first so blowdit_icon() is available to head.php and every body template. // first so blowfish_icon() is available to head.php and every body template.
include(THEME_DIR_PHP.'icons.php'); include(THEME_DIR_PHP.'icons.php');
// ---- Theme contact / social config ---- // ---- Theme contact / social config ----
// The mailto icon uses Bludit's "Sender email" (Settings -> Advanced -> // The mailto icon uses Bludit's "Sender email" (Settings -> Advanced ->
// Email account settings). Leave it to auto-detect, or hard-set $blowditEmail // Email account settings). Leave it to auto-detect, or hard-set $blowfishEmail
// below to override. Empty -> the icon is hidden. // below to override. Empty -> the icon is hidden.
$blowditEmail = ''; $blowfishEmail = '';
if (isset($site)) { if (isset($site)) {
// Try Bludit's site settings (accessor is getValue(); getField() as backup). // Try Bludit's site settings (accessor is getValue(); getField() as backup).
$bdGet = method_exists($site, 'getValue') ? 'getValue' $bdGet = method_exists($site, 'getValue') ? 'getValue'
: (method_exists($site, 'getField') ? 'getField' : null); : (method_exists($site, 'getField') ? 'getField' : null);
if ($bdGet) { if ($bdGet) {
$blowditEmail = $site->$bdGet('emailFromAddress'); // Bludit's "Sender email" $blowfishEmail = $site->$bdGet('emailFromAddress'); // Bludit's "Sender email"
if (empty($blowditEmail)) { $blowditEmail = $site->$bdGet('email'); } if (empty($blowfishEmail)) { $blowfishEmail = $site->$bdGet('email'); }
} }
} }
// Bludit's sender email isn't always exposed to themes (it's an SMTP/plugin // Bludit's sender email isn't always exposed to themes (it's an SMTP/plugin
// setting), so fall back to a fixed address. Set to '' to hide the icon. // setting), so fall back to a fixed address. Set to '' to hide the icon.
if (empty($blowditEmail)) { $blowditEmail = 'contact@interlope.xyz'; } if (empty($blowfishEmail)) { $blowfishEmail = 'contact@interlope.xyz'; }
$blowditTwitterHandle = '@spleenftw'; // twitter:site / twitter:creator; '' to omit $blowfishTwitterHandle = '@spleenftw'; // twitter:site / twitter:creator; '' to omit
// Read the theme from a cookie so the root element can be rendered // Read the theme from a cookie so the root element can be rendered
// pre-coloured. This paints the page in the theme colour from the first // pre-coloured. This paints the page in the theme colour from the first
// byte of every response, eliminating the white flash between navigations. // byte of every response, eliminating the white flash between navigations.
$blowditThemeBg = array( $blowfishThemeBg = array(
'light' => '#ffffff', 'light' => '#ffffff',
'dark' => '#171717', 'dark' => '#171717',
'nord' => '#2e3440', 'nord' => '#2e3440',
'dracula' => '#282a36', 'dracula' => '#282a36',
'catppuccin' => '#1e1e2e', 'catppuccin' => '#1e1e2e',
); );
$blowditTheme = (isset($_COOKIE['blowdit-theme']) && isset($blowditThemeBg[$_COOKIE['blowdit-theme']])) $blowfishTheme = (isset($_COOKIE['blowfish-theme']) && isset($blowfishThemeBg[$_COOKIE['blowfish-theme']]))
? $_COOKIE['blowdit-theme'] ? $_COOKIE['blowfish-theme']
: 'light'; : 'light';
$blowditBg = $blowditThemeBg[$blowditTheme]; $blowfishBg = $blowfishThemeBg[$blowfishTheme];
$blowditScheme = ($blowditTheme === 'light') ? 'light' : 'dark'; $blowfishScheme = ($blowfishTheme === 'light') ? 'light' : 'dark';
// Pre-compute before <body> so we can stamp the has-toc class on it, // Pre-compute before <body> so we can stamp the has-toc class on it,
// which lets CSS align the navbar container with the wider article container. // which lets CSS align the navbar container with the wider article container.
$blowditIsArticle = ($WHERE_AM_I === 'page' && isset($page) && !$page->isStatic() && !$url->notFound()); $blowfishIsArticle = ($WHERE_AM_I === 'page' && isset($page) && !$page->isStatic() && !$url->notFound());
$blowditHasHeadings = $blowditIsArticle && (bool) preg_match('/<h[234]/i', $page->content()); $blowfishHasHeadings = $blowfishIsArticle && (bool) preg_match('/<h[234]/i', $page->content());
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="<?php echo Theme::lang() ?>" data-theme="<?php echo $blowditTheme ?>" style="background-color: <?php echo $blowditBg ?>; color-scheme: <?php echo $blowditScheme ?>;"> <html lang="<?php echo Theme::lang() ?>" data-theme="<?php echo $blowfishTheme ?>" style="background-color: <?php echo $blowfishBg ?>; color-scheme: <?php echo $blowfishScheme ?>;">
<head> <head>
<?php include(THEME_DIR_PHP.'head.php'); ?> <?php include(THEME_DIR_PHP.'head.php'); ?>
</head> </head>
<body<?php echo $blowditHasHeadings ? ' class="has-toc"' : ''; ?>> <body<?php echo $blowfishHasHeadings ? ' class="has-toc"' : ''; ?>>
<!-- Skip to content (keyboard / screen-reader navigation) --> <!-- Skip to content (keyboard / screen-reader navigation) -->
<a class="skip-link" href="#main-content"><?php echo $L->get('Skip to content'); ?></a> <a class="skip-link" href="#main-content"><?php echo $L->get('Skip to content'); ?></a>
@@ -126,8 +126,8 @@
// The hero (and the relocated search) appear on every paginated home page. // The hero (and the relocated search) appear on every paginated home page.
$heroVisible = ($WHERE_AM_I === 'home'); $heroVisible = ($WHERE_AM_I === 'home');
// Reuse the values pre-computed before <body> (avoids calling $page->content() twice). // Reuse the values pre-computed before <body> (avoids calling $page->content() twice).
$isArticlePage = $blowditIsArticle; $isArticlePage = $blowfishIsArticle;
$hasHeadings = $blowditHasHeadings; $hasHeadings = $blowfishHasHeadings;
?> ?>
<!-- Content --> <!-- Content -->
@@ -179,7 +179,7 @@
// jQuery and Bootstrap's JS bundle are intentionally NOT loaded: every // jQuery and Bootstrap's JS bundle are intentionally NOT loaded: every
// interactive piece in this theme is vanilla JS (theme picker, tabs, // interactive piece in this theme is vanilla JS (theme picker, tabs,
// carousel, lightbox), and the only Bootstrap-JS feature used — the // carousel, lightbox), and the only Bootstrap-JS feature used — the
// mobile navbar collapse — is reimplemented in blowdit.js. This drops // mobile navbar collapse — is reimplemented in blowfish.js. This drops
// ~115 KB. Re-add Theme::jquery() / Theme::jsBootstrap() here if you // ~115 KB. Re-add Theme::jquery() / Theme::jsBootstrap() here if you
// install a plugin that depends on them. // install a plugin that depends on them.
?> ?>
@@ -190,11 +190,11 @@
<script> <script>
// Theme directory URL, so the deferred bundle can load self-hosted assets // Theme directory URL, so the deferred bundle can load self-hosted assets
// (highlight.js + its theme CSS) relative to the theme. // (highlight.js + its theme CSS) relative to the theme.
window.BLOWDIT_THEME_URL = <?php echo json_encode(DOMAIN_THEME); ?>; window.BLOWFISH_THEME_URL = <?php echo json_encode(DOMAIN_THEME); ?>;
window.BLOWDIT_HOME = <?php echo json_encode(Theme::siteUrl()); ?>; window.BLOWFISH_HOME = <?php echo json_encode(Theme::siteUrl()); ?>;
// Localised UI strings for the deferred bundle. $L->get() falls back to // Localised UI strings for the deferred bundle. $L->get() falls back to
// the key itself when a translation is missing, so this is safe in any language. // the key itself when a translation is missing, so this is safe in any language.
window.BLOWDIT_I18N = { window.BLOWFISH_I18N = {
copy: <?php echo json_encode($L->get('Copy')); ?>, copy: <?php echo json_encode($L->get('Copy')); ?>,
copied: <?php echo json_encode($L->get('Copied')); ?>, copied: <?php echo json_encode($L->get('Copied')); ?>,
copyCode: <?php echo json_encode($L->get('Copy code')); ?>, copyCode: <?php echo json_encode($L->get('Copy code')); ?>,
@@ -213,8 +213,8 @@
</script> </script>
<?php <?php
// Cache-busted by file mtime (mirrors the style.css approach in head.php). // Cache-busted by file mtime (mirrors the style.css approach in head.php).
$jsVersion = @filemtime(THEME_DIR_JS . 'blowdit.js'); $jsVersion = @filemtime(THEME_DIR_JS . 'blowfish.js');
$jsSrc = DOMAIN_THEME . 'js/blowdit.js' . ($jsVersion ? '?v=' . $jsVersion : ''); $jsSrc = DOMAIN_THEME . 'js/blowfish.js' . ($jsVersion ? '?v=' . $jsVersion : '');
?> ?>
<script defer src="<?php echo $jsSrc; ?>"></script> <script defer src="<?php echo $jsSrc; ?>"></script>
+12 -12
View File
@@ -1,5 +1,5 @@
/* ============================================================ /* ============================================================
BLOWDIT — theme behaviour BLOWFISH — theme behaviour
Deferred, cacheable bundle of all client-side enhancements. Deferred, cacheable bundle of all client-side enhancements.
Loaded with <script defer> from index.php, so the DOM is fully Loaded with <script defer> from index.php, so the DOM is fully
parsed before any of these IIFEs run (no DOMContentLoaded needed). parsed before any of these IIFEs run (no DOMContentLoaded needed).
@@ -10,8 +10,8 @@
(function () { (function () {
'use strict'; 'use strict';
// Localised UI strings (overridable via window.BLOWDIT_I18N, set in index.php). // Localised UI strings (overridable via window.BLOWFISH_I18N, set in index.php).
var I18N = window.BLOWDIT_I18N || {}; var I18N = window.BLOWFISH_I18N || {};
var T = { var T = {
copy: I18N.copy || 'Copy', copy: I18N.copy || 'Copy',
copied: I18N.copied || 'Copied', copied: I18N.copied || 'Copied',
@@ -80,7 +80,7 @@
Theme picker Theme picker
-------------------------------------------------------- */ -------------------------------------------------------- */
(function () { (function () {
var STORAGE_KEY = 'blowdit-theme'; var STORAGE_KEY = 'blowfish-theme';
var THEMES = ['light', 'dark', 'nord', 'dracula', 'catppuccin']; var THEMES = ['light', 'dark', 'nord', 'dracula', 'catppuccin'];
// Inline-SVG icons — mirror of php/icons.php (keep the two in sync). // Inline-SVG icons — mirror of php/icons.php (keep the two in sync).
function svg(inner) { function svg(inner) {
@@ -115,8 +115,8 @@
function apply(theme) { function apply(theme) {
root.setAttribute('data-theme', theme); root.setAttribute('data-theme', theme);
try { localStorage.setItem(STORAGE_KEY, theme); } catch (e) {} try { localStorage.setItem(STORAGE_KEY, theme); } catch (e) {}
document.cookie = 'blowdit-theme=' + theme + '; path=/; max-age=31536000; SameSite=Lax'; document.cookie = 'blowfish-theme=' + theme + '; path=/; max-age=31536000; SameSite=Lax';
var bg = window.BLOWDIT_THEME_BG || {}; var bg = window.BLOWFISH_THEME_BG || {};
if (bg[theme]) { root.style.backgroundColor = bg[theme]; } if (bg[theme]) { root.style.backgroundColor = bg[theme]; }
root.style.colorScheme = (theme === 'light') ? 'light' : 'dark'; root.style.colorScheme = (theme === 'light') ? 'light' : 'dark';
syncIcon(theme); syncIcon(theme);
@@ -764,7 +764,7 @@
// loaded lazily (only the themes actually viewed) and toggled via // loaded lazily (only the themes actually viewed) and toggled via
// link.disabled. style.css forces the code background to stay --code-bg, // link.disabled. style.css forces the code background to stay --code-bg,
// so only the palette's token colours are used. // so only the palette's token colours are used.
var BASE = window.BLOWDIT_THEME_URL || ''; var BASE = window.BLOWFISH_THEME_URL || '';
var THEME_CSS = { var THEME_CSS = {
light: 'github', light: 'github',
dark: 'github-dark', dark: 'github-dark',
@@ -1181,7 +1181,7 @@
-------------------------------------------------------- */ -------------------------------------------------------- */
(function () { (function () {
var data = []; var data = [];
var el = document.getElementById('blowdit-search-index'); var el = document.getElementById('blowfish-search-index');
if (el) { try { data = JSON.parse(el.textContent) || []; } catch (e) { data = []; } } if (el) { try { data = JSON.parse(el.textContent) || []; } catch (e) { data = []; } }
var ICON = '<svg class="search-icon" viewBox="0 0 24 24" width="18" height="18" fill="none" ' + var ICON = '<svg class="search-icon" viewBox="0 0 24 24" width="18" height="18" fill="none" ' +
@@ -1291,7 +1291,7 @@
} }
// Expose for the keyboard-shortcuts handler. // Expose for the keyboard-shortcuts handler.
window.BLOWDIT_SEARCH = { open: open, close: close, isOpen: isOpen }; window.BLOWFISH_SEARCH = { open: open, close: close, isOpen: isOpen };
})(); })();
/* -------------------------------------------------------- /* --------------------------------------------------------
@@ -1303,7 +1303,7 @@
return t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable); return t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable);
} }
document.addEventListener('keydown', function (e) { document.addEventListener('keydown', function (e) {
var s = window.BLOWDIT_SEARCH; var s = window.BLOWFISH_SEARCH;
if (e.key === 'Escape' || e.key === 'Esc') { if (e.key === 'Escape' || e.key === 'Esc') {
if (s && s.isOpen()) s.close(); if (s && s.isOpen()) s.close();
return; return;
@@ -1319,7 +1319,7 @@
// "g" then "h" -> home // "g" then "h" -> home
if (e.key === 'g' || e.key === 'G') { lastG = Date.now(); return; } if (e.key === 'g' || e.key === 'G') { lastG = Date.now(); return; }
if ((e.key === 'h' || e.key === 'H') && Date.now() - lastG < 600) { if ((e.key === 'h' || e.key === 'H') && Date.now() - lastG < 600) {
window.location.href = (window.BLOWDIT_HOME || '/'); window.location.href = (window.BLOWFISH_HOME || '/');
} }
}); });
})(); })();
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "Ein Bludit-Theme, inspiriert vom Blowfish-Hugo-Theme. Rechte Seitenleiste, Lesezeit, Umschalter für hellen/dunklen Modus und eine Navigationsleiste mit Social-Media-Icons und statischen Seiten." "description": "Ein Bludit-Theme, inspiriert vom Blowfish-Hugo-Theme. Rechte Seitenleiste, Lesezeit, Umschalter für hellen/dunklen Modus und eine Navigationsleiste mit Social-Media-Icons und statischen Seiten."
}, },
"Toggle theme": "Design wechseln", "Toggle theme": "Design wechseln",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "Ein Bludit-Theme, inspiriert vom Blowfish-Hugo-Theme. Rechte Seitenleiste, Lesezeit, Umschalter für hellen/dunklen Modus und eine Navigationsleiste mit Social-Media-Icons und statischen Seiten." "description": "Ein Bludit-Theme, inspiriert vom Blowfish-Hugo-Theme. Rechte Seitenleiste, Lesezeit, Umschalter für hellen/dunklen Modus und eine Navigationsleiste mit Social-Media-Icons und statischen Seiten."
}, },
"Toggle theme": "Design wechseln", "Toggle theme": "Design wechseln",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "A Bludit theme inspired by the Blowfish Hugo theme. Right sidebar, reading time, a light/dark mode toggle and a navbar with social icons and static pages." "description": "A Bludit theme inspired by the Blowfish Hugo theme. Right sidebar, reading time, a light/dark mode toggle and a navbar with social icons and static pages."
}, },
"Toggle theme": "Toggle theme", "Toggle theme": "Toggle theme",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "Un tema para Bludit inspirado en el tema Blowfish de Hugo. Barra lateral derecha, tiempo de lectura, conmutador de modo claro/oscuro y barra de navegación con iconos sociales y páginas estáticas." "description": "Un tema para Bludit inspirado en el tema Blowfish de Hugo. Barra lateral derecha, tiempo de lectura, conmutador de modo claro/oscuro y barra de navegación con iconos sociales y páginas estáticas."
}, },
"Toggle theme": "Cambiar tema", "Toggle theme": "Cambiar tema",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "یک قالب بلودیت با الهام از قالب Blowfish هوگو. نوار کناری راست، زمان مطالعه، کلید تغییر حالت روشن/تاریک و نوار ناوبری به همراه آیکون‌های شبکه‌های اجتماعی و صفحات ایستا." "description": "یک قالب بلودیت با الهام از قالب Blowfish هوگو. نوار کناری راست، زمان مطالعه، کلید تغییر حالت روشن/تاریک و نوار ناوبری به همراه آیکون‌های شبکه‌های اجتماعی و صفحات ایستا."
}, },
"Toggle theme": "تغییر پوسته", "Toggle theme": "تغییر پوسته",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "Un tema per Bludit ispirato al tema Blowfish di Hugo. Barra laterale a destra, tempo di lettura, interruttore per la modalità chiara/scura e barra di navigazione con icone social e pagine statiche." "description": "Un tema per Bludit ispirato al tema Blowfish di Hugo. Barra laterale a destra, tempo di lettura, interruttore per la modalità chiara/scura e barra di navigazione con icone social e pagine statiche."
}, },
"Toggle theme": "Cambia tema", "Toggle theme": "Cambia tema",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "Hugo の Blowfish テーマにインスパイアされた Bludit テーマ。右サイドバー、読了時間、ライト/ダークモードの切り替え、ソーシャルアイコンと固定ページを備えたナビゲーションバーを搭載しています。" "description": "Hugo の Blowfish テーマにインスパイアされた Bludit テーマ。右サイドバー、読了時間、ライト/ダークモードの切り替え、ソーシャルアイコンと固定ページを備えたナビゲーションバーを搭載しています。"
}, },
"Toggle theme": "テーマを切り替え", "Toggle theme": "テーマを切り替え",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "Een Bludit-thema geïnspireerd op het Blowfish-thema voor Hugo. Zijbalk rechts, leestijd, schakelaar voor lichte/donkere modus en een navigatiebalk met sociale iconen en statische pagina's." "description": "Een Bludit-thema geïnspireerd op het Blowfish-thema voor Hugo. Zijbalk rechts, leestijd, schakelaar voor lichte/donkere modus en een navigatiebalk met sociale iconen en statische pagina's."
}, },
"Toggle theme": "Thema wisselen", "Toggle theme": "Thema wisselen",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "Тема для Bludit, вдохновлённая темой Blowfish для Hugo. Боковая панель справа, время чтения, переключатель светлой/тёмной темы и панель навигации со значками соцсетей и статическими страницами." "description": "Тема для Bludit, вдохновлённая темой Blowfish для Hugo. Боковая панель справа, время чтения, переключатель светлой/тёмной темы и панель навигации со значками соцсетей и статическими страницами."
}, },
"Toggle theme": "Переключить тему", "Toggle theme": "Переключить тему",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"theme-data": "theme-data":
{ {
"name": "Blowdit", "name": "Blowfish",
"description": "Hugo'nun Blowfish temasından esinlenen bir Bludit teması. Sağ kenar çubuğu, okuma süresi, açık/koyu mod düğmesi ve sosyal simgeler ile sabit sayfalar içeren bir gezinme çubuğu." "description": "Hugo'nun Blowfish temasından esinlenen bir Bludit teması. Sağ kenar çubuğu, okuma süresi, açık/koyu mod düğmesi ve sosyal simgeler ile sabit sayfalar içeren bir gezinme çubuğu."
}, },
"Toggle theme": "Temayı değiştir", "Toggle theme": "Temayı değiştir",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "Blog", "name": "Blog",
"short_name": "Blog", "short_name": "Blog",
"description": "A Bludit blog using the Blowdit theme.", "description": "A Bludit blog using the Blowfish theme.",
"start_url": "/", "start_url": "/",
"scope": "/", "scope": "/",
"display": "standalone", "display": "standalone",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"author": "spleenftw", "author": "spleenftw",
"email": "", "email": "",
"website": "https://github.com/Spleenftw/blowdit", "website": "https://github.com/Spleenftw/bludit-blowfish",
"version": "1.6.0", "version": "1.6.0",
"releaseDate": "2026-06-03", "releaseDate": "2026-06-03",
"license": "MIT", "license": "MIT",
+6 -6
View File
@@ -1,4 +1,4 @@
<footer class="footer footer-modern"> <footer class="footer footer-modern">
<div class="container"> <div class="container">
<div class="row align-items-center"> <div class="row align-items-center">
<?php if (defined('BLUDIT_PRO')): ?> <?php if (defined('BLUDIT_PRO')): ?>
@@ -30,15 +30,15 @@
$bdSp = new Page($bdSk); $bdSp = new Page($bdSk);
if ($bdSp->isStatic()) { continue; } // posts only if ($bdSp->isStatic()) { continue; } // posts only
$bdSearchIndex[] = array( $bdSearchIndex[] = array(
't' => blowdit_plain($bdSp->title()), 't' => blowfish_plain($bdSp->title()),
'u' => $bdSp->permalink(), 'u' => $bdSp->permalink(),
'd' => blowdit_plain($bdSp->description()), 'd' => blowfish_plain($bdSp->description()),
'c' => blowdit_plain($bdSp->category()), 'c' => blowfish_plain($bdSp->category()),
'g' => array_values(array_map('blowdit_plain', $bdSp->tags(true))), 'g' => array_values(array_map('blowfish_plain', $bdSp->tags(true))),
); );
} }
} }
echo '<script type="application/json" id="blowdit-search-index">' echo '<script type="application/json" id="blowfish-search-index">'
. json_encode($bdSearchIndex, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . json_encode($bdSearchIndex, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
. '</script>'; . '</script>';
} catch (\Throwable $e) {} } catch (\Throwable $e) {}
+45 -45
View File
@@ -1,27 +1,27 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="generator" content="Bludit"> <meta name="generator" content="Bludit">
<!-- Tint the mobile browser chrome to match the navbar (--chrome-bg per theme). <!-- Tint the mobile browser chrome to match the navbar (--chrome-bg per theme).
Set server-side from the theme cookie so it's correct on the first paint. --> Set server-side from the theme cookie so it's correct on the first paint. -->
<?php <?php
$blowditChromeColors = array( $blowfishChromeColors = array(
'light' => '#1f1f1f', 'light' => '#1f1f1f',
'dark' => '#1f1f1f', 'dark' => '#1f1f1f',
'nord' => '#3b4252', 'nord' => '#3b4252',
'dracula' => '#343746', 'dracula' => '#343746',
'catppuccin' => '#313244', 'catppuccin' => '#313244',
); );
$blowditChrome = isset($blowditChromeColors[$blowditTheme]) ? $blowditChromeColors[$blowditTheme] : '#1f1f1f'; $blowfishChrome = isset($blowfishChromeColors[$blowfishTheme]) ? $blowfishChromeColors[$blowfishTheme] : '#1f1f1f';
?> ?>
<meta name="theme-color" content="<?php echo $blowditChrome; ?>"> <meta name="theme-color" content="<?php echo $blowfishChrome; ?>">
<?php if (!empty($blowditIsArticle) && $page->coverImage()): ?> <?php if (!empty($blowfishIsArticle) && $page->coverImage()): ?>
<!-- Preload the article cover image (typically the LCP element) --> <!-- Preload the article cover image (typically the LCP element) -->
<link rel="preload" as="image" href="<?php echo htmlspecialchars($page->coverImage(true), ENT_QUOTES, 'UTF-8'); ?>" fetchpriority="high"> <link rel="preload" as="image" href="<?php echo htmlspecialchars($page->coverImage(true), ENT_QUOTES, 'UTF-8'); ?>" fetchpriority="high">
<?php endif ?> <?php endif ?>
<?php if (!empty($blowditIsArticle)): ?> <?php if (!empty($blowfishIsArticle)): ?>
<!-- Warm up connections to article third-parties (comments + tweet embeds) --> <!-- Warm up connections to article third-parties (comments + tweet embeds) -->
<link rel="preconnect" href="https://utteranc.es" crossorigin> <link rel="preconnect" href="https://utteranc.es" crossorigin>
<link rel="dns-prefetch" href="https://platform.twitter.com"> <link rel="dns-prefetch" href="https://platform.twitter.com">
@@ -32,7 +32,7 @@
Sets data-theme AND paints the html background/color-scheme inline, so the Sets data-theme AND paints the html background/color-scheme inline, so the
very first frame (before style.css loads) already uses the theme colour. --> very first frame (before style.css loads) already uses the theme colour. -->
<script data-cfasync="false"> <script data-cfasync="false">
window.BLOWDIT_THEME_BG = { window.BLOWFISH_THEME_BG = {
light: '#ffffff', light: '#ffffff',
dark: '#171717', dark: '#171717',
nord: '#2e3440', nord: '#2e3440',
@@ -41,8 +41,8 @@
}; };
(function () { (function () {
try { try {
var bg = window.BLOWDIT_THEME_BG; var bg = window.BLOWFISH_THEME_BG;
var stored = localStorage.getItem('blowdit-theme'); var stored = localStorage.getItem('blowfish-theme');
if (!bg[stored]) { if (!bg[stored]) {
stored = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; stored = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
} }
@@ -51,7 +51,7 @@
root.style.colorScheme = (stored === 'light') ? 'light' : 'dark'; root.style.colorScheme = (stored === 'light') ? 'light' : 'dark';
root.style.backgroundColor = bg[stored]; root.style.backgroundColor = bg[stored];
// Persist to a cookie so PHP can pre-colour the next page server-side. // Persist to a cookie so PHP can pre-colour the next page server-side.
document.cookie = 'blowdit-theme=' + stored + '; path=/; max-age=31536000; SameSite=Lax'; document.cookie = 'blowfish-theme=' + stored + '; path=/; max-age=31536000; SameSite=Lax';
} catch (e) {} } catch (e) {}
})(); })();
</script> </script>
@@ -64,13 +64,13 @@
<?php <?php
// ---- Social sharing metadata (Open Graph + Twitter Card) ---- // ---- Social sharing metadata (Open Graph + Twitter Card) ----
// $blowditIsArticle is pre-computed in index.php before this file is included. // $blowfishIsArticle is pre-computed in index.php before this file is included.
$ogIsArticle = !empty($blowditIsArticle); $ogIsArticle = !empty($blowfishIsArticle);
// ISO 8601 publish date — formatted explicitly so it's valid regardless of // ISO 8601 publish date — formatted explicitly so it's valid regardless of
// the admin's configured date format. Falls back to dateRaw() if Date is unavailable. // the admin's configured date format. Falls back to dateRaw() if Date is unavailable.
if (!function_exists('blowdit_iso_date')) { if (!function_exists('blowfish_iso_date')) {
function blowdit_iso_date($page, $field) { function blowfish_iso_date($page, $field) {
$raw = $page->getValue($field); $raw = $page->getValue($field);
if (!$raw) { return ''; } if (!$raw) { return ''; }
if (class_exists('Date')) { if (class_exists('Date')) {
@@ -86,7 +86,7 @@
$ogDesc = $page->description(); $ogDesc = $page->description();
$ogUrl = $page->permalink(true); $ogUrl = $page->permalink(true);
$ogImage = $page->coverImage(true); $ogImage = $page->coverImage(true);
$ogPublishedIso = blowdit_iso_date($page, 'date'); $ogPublishedIso = blowfish_iso_date($page, 'date');
} else { } else {
$ogTitle = $site->title(); $ogTitle = $site->title();
$ogDesc = ''; $ogDesc = '';
@@ -105,52 +105,52 @@
<!-- Open Graph --> <!-- Open Graph -->
<meta property="og:type" content="<?php echo htmlspecialchars($ogType, ENT_QUOTES, 'UTF-8'); ?>"> <meta property="og:type" content="<?php echo htmlspecialchars($ogType, ENT_QUOTES, 'UTF-8'); ?>">
<meta property="og:site_name" content="<?php echo blowdit_text($ogSiteName); ?>"> <meta property="og:site_name" content="<?php echo blowfish_text($ogSiteName); ?>">
<meta property="og:title" content="<?php echo blowdit_text($ogTitle); ?>"> <meta property="og:title" content="<?php echo blowfish_text($ogTitle); ?>">
<meta property="og:description" content="<?php echo blowdit_text($ogDesc); ?>"> <meta property="og:description" content="<?php echo blowfish_text($ogDesc); ?>">
<meta property="og:url" content="<?php echo htmlspecialchars($ogUrl, ENT_QUOTES, 'UTF-8'); ?>"> <meta property="og:url" content="<?php echo htmlspecialchars($ogUrl, ENT_QUOTES, 'UTF-8'); ?>">
<meta property="og:image" content="<?php echo htmlspecialchars($ogImage, ENT_QUOTES, 'UTF-8'); ?>"> <meta property="og:image" content="<?php echo htmlspecialchars($ogImage, ENT_QUOTES, 'UTF-8'); ?>">
<meta property="og:image:alt" content="<?php echo blowdit_text($ogTitle); ?>"> <meta property="og:image:alt" content="<?php echo blowfish_text($ogTitle); ?>">
<meta property="og:locale" content="<?php echo htmlspecialchars(str_replace('-', '_', Theme::lang()), ENT_QUOTES, 'UTF-8'); ?>"> <meta property="og:locale" content="<?php echo htmlspecialchars(str_replace('-', '_', Theme::lang()), ENT_QUOTES, 'UTF-8'); ?>">
<?php if ($ogIsArticle): ?> <?php if ($ogIsArticle): ?>
<meta property="article:published_time" content="<?php echo htmlspecialchars($ogPublishedIso, ENT_QUOTES, 'UTF-8'); ?>"> <meta property="article:published_time" content="<?php echo htmlspecialchars($ogPublishedIso, ENT_QUOTES, 'UTF-8'); ?>">
<meta property="article:author" content="<?php echo blowdit_text($page->username()); ?>"> <meta property="article:author" content="<?php echo blowfish_text($page->username()); ?>">
<?php endif ?> <?php endif ?>
<!-- Twitter Card --> <!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image"> <meta name="twitter:card" content="summary_large_image">
<?php if (!empty($blowditTwitterHandle)): ?> <?php if (!empty($blowfishTwitterHandle)): ?>
<meta name="twitter:site" content="<?php echo htmlspecialchars($blowditTwitterHandle, ENT_QUOTES, 'UTF-8'); ?>"> <meta name="twitter:site" content="<?php echo htmlspecialchars($blowfishTwitterHandle, ENT_QUOTES, 'UTF-8'); ?>">
<meta name="twitter:creator" content="<?php echo htmlspecialchars($blowditTwitterHandle, ENT_QUOTES, 'UTF-8'); ?>"> <meta name="twitter:creator" content="<?php echo htmlspecialchars($blowfishTwitterHandle, ENT_QUOTES, 'UTF-8'); ?>">
<?php endif ?> <?php endif ?>
<meta name="twitter:title" content="<?php echo blowdit_text($ogTitle); ?>"> <meta name="twitter:title" content="<?php echo blowfish_text($ogTitle); ?>">
<meta name="twitter:description" content="<?php echo blowdit_text($ogDesc); ?>"> <meta name="twitter:description" content="<?php echo blowfish_text($ogDesc); ?>">
<meta name="twitter:image" content="<?php echo htmlspecialchars($ogImage, ENT_QUOTES, 'UTF-8'); ?>"> <meta name="twitter:image" content="<?php echo htmlspecialchars($ogImage, ENT_QUOTES, 'UTF-8'); ?>">
<meta name="twitter:image:alt" content="<?php echo blowdit_text($ogTitle); ?>"> <meta name="twitter:image:alt" content="<?php echo blowfish_text($ogTitle); ?>">
<?php <?php
// ---- JSON-LD structured data ---- // ---- JSON-LD structured data ----
$blowditEmitLd = function ($doc) { $blowfishEmitLd = function ($doc) {
echo '<script type="application/ld+json">' echo '<script type="application/ld+json">'
. json_encode($doc, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . json_encode($doc, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
. '</script>' . "\n"; . '</script>' . "\n";
}; };
// Social profile URLs, used as schema.org "sameAs". // Social profile URLs, used as schema.org "sameAs".
$blowditSameAs = array(); $blowfishSameAs = array();
foreach (Theme::socialNetworks() as $snKey => $snLabel) { foreach (Theme::socialNetworks() as $snKey => $snLabel) {
$snUrl = $site->{$snKey}(); $snUrl = $site->{$snKey}();
if (!empty($snUrl)) { $blowditSameAs[] = $snUrl; } if (!empty($snUrl)) { $blowfishSameAs[] = $snUrl; }
} }
// WebSite (every page) — declares the site + a sitelinks search box. // WebSite (every page) — declares the site + a sitelinks search box.
$siteLd = array( $siteLd = array(
'@context' => 'https://schema.org', '@context' => 'https://schema.org',
'@type' => 'WebSite', '@type' => 'WebSite',
'name' => blowdit_plain($site->title()), 'name' => blowfish_plain($site->title()),
'url' => Theme::siteUrl(), 'url' => Theme::siteUrl(),
); );
$siteDesc = $site->description() ?: $site->slogan(); $siteDesc = $site->description() ?: $site->slogan();
if ($siteDesc) { $siteLd['description'] = blowdit_plain($siteDesc); } if ($siteDesc) { $siteLd['description'] = blowfish_plain($siteDesc); }
$siteLd['potentialAction'] = array( $siteLd['potentialAction'] = array(
'@type' => 'SearchAction', '@type' => 'SearchAction',
'target' => array( 'target' => array(
@@ -159,7 +159,7 @@
), ),
'query-input' => 'required name=search_term_string', 'query-input' => 'required name=search_term_string',
); );
$blowditEmitLd($siteLd); $blowfishEmitLd($siteLd);
// BlogPosting + BreadcrumbList — articles only. // BlogPosting + BreadcrumbList — articles only.
if ($ogIsArticle) { if ($ogIsArticle) {
@@ -168,7 +168,7 @@
$ldPublished = $ogPublishedIso; $ldPublished = $ogPublishedIso;
// dateModified: fall back to published date when not tracked. // dateModified: fall back to published date when not tracked.
$ldModified = blowdit_iso_date($page, 'dateModified'); $ldModified = blowfish_iso_date($page, 'dateModified');
if (!$ldModified) { $ldModified = $ldPublished; } if (!$ldModified) { $ldModified = $ldPublished; }
// timeRequired as an ISO 8601 duration (e.g. "PT8M") parsed from readingTime(). // timeRequired as an ISO 8601 duration (e.g. "PT8M") parsed from readingTime().
@@ -179,23 +179,23 @@
$author = array( $author = array(
'@type' => 'Person', '@type' => 'Person',
'name' => blowdit_plain($page->username()), 'name' => blowfish_plain($page->username()),
'url' => Theme::siteUrl(), 'url' => Theme::siteUrl(),
); );
if (!empty($blowditSameAs)) { $author['sameAs'] = $blowditSameAs; } if (!empty($blowfishSameAs)) { $author['sameAs'] = $blowfishSameAs; }
$jsonLd = array( $jsonLd = array(
'@context' => 'https://schema.org', '@context' => 'https://schema.org',
'@type' => 'BlogPosting', '@type' => 'BlogPosting',
'headline' => blowdit_plain($page->title()), 'headline' => blowfish_plain($page->title()),
'description' => blowdit_plain($ldDescription), 'description' => blowfish_plain($ldDescription),
'image' => $ldImage, 'image' => $ldImage,
'datePublished' => $ldPublished, 'datePublished' => $ldPublished,
'dateModified' => $ldModified, 'dateModified' => $ldModified,
'author' => $author, 'author' => $author,
'publisher' => array( 'publisher' => array(
'@type' => 'Organization', '@type' => 'Organization',
'name' => blowdit_plain($site->title()), 'name' => blowfish_plain($site->title()),
'logo' => array( 'logo' => array(
'@type' => 'ImageObject', '@type' => 'ImageObject',
'url' => DOMAIN_THEME . 'img/favicon.png', 'url' => DOMAIN_THEME . 'img/favicon.png',
@@ -208,25 +208,25 @@
'wordCount' => str_word_count(strip_tags($page->content())), 'wordCount' => str_word_count(strip_tags($page->content())),
); );
if ($ldTimeRequired) { $jsonLd['timeRequired'] = $ldTimeRequired; } if ($ldTimeRequired) { $jsonLd['timeRequired'] = $ldTimeRequired; }
$blowditEmitLd($jsonLd); $blowfishEmitLd($jsonLd);
// Breadcrumb: Home > [Category] > Article // Breadcrumb: Home > [Category] > Article
$crumbs = array(array( $crumbs = array(array(
'@type' => 'ListItem', 'position' => 1, '@type' => 'ListItem', 'position' => 1,
'name' => blowdit_plain($site->title()), 'item' => Theme::siteUrl(), 'name' => blowfish_plain($site->title()), 'item' => Theme::siteUrl(),
)); ));
$pos = 2; $pos = 2;
if ($page->categoryKey()) { if ($page->categoryKey()) {
$crumbs[] = array( $crumbs[] = array(
'@type' => 'ListItem', 'position' => $pos++, '@type' => 'ListItem', 'position' => $pos++,
'name' => blowdit_plain($page->category()), 'item' => $page->categoryPermalink(), 'name' => blowfish_plain($page->category()), 'item' => $page->categoryPermalink(),
); );
} }
$crumbs[] = array( $crumbs[] = array(
'@type' => 'ListItem', 'position' => $pos, '@type' => 'ListItem', 'position' => $pos,
'name' => blowdit_plain($page->title()), 'item' => $page->permalink(true), 'name' => blowfish_plain($page->title()), 'item' => $page->permalink(true),
); );
$blowditEmitLd(array( $blowfishEmitLd(array(
'@context' => 'https://schema.org', '@context' => 'https://schema.org',
'@type' => 'BreadcrumbList', '@type' => 'BreadcrumbList',
'itemListElement' => $crumbs, 'itemListElement' => $crumbs,
@@ -235,7 +235,7 @@
?> ?>
<!-- RSS feed auto-discovery (RSS plugin serves /rss.xml) --> <!-- RSS feed auto-discovery (RSS plugin serves /rss.xml) -->
<link rel="alternate" type="application/rss+xml" title="<?php echo blowdit_text($site->title()); ?>" href="<?php echo rtrim(Theme::siteUrl(), '/') . '/rss.xml'; ?>"> <link rel="alternate" type="application/rss+xml" title="<?php echo blowfish_text($site->title()); ?>" href="<?php echo rtrim(Theme::siteUrl(), '/') . '/rss.xml'; ?>">
<!-- Include Favicon --> <!-- Include Favicon -->
<?php echo Theme::favicon('img/favicon.png'); ?> <?php echo Theme::favicon('img/favicon.png'); ?>
@@ -252,7 +252,7 @@
<?php echo Theme::cssBootstrap(); ?> <?php echo Theme::cssBootstrap(); ?>
<!-- Bootstrap Icons webfont removed: the few icons used are inline SVGs <!-- Bootstrap Icons webfont removed: the few icons used are inline SVGs
(see php/icons.php + blowdit.js), saving ~120 KB font + ~80 KB CSS. --> (see php/icons.php + blowfish.js), saving ~120 KB font + ~80 KB CSS. -->
<!-- Include CSS Styles from this theme (cache-busted by file mtime) --> <!-- Include CSS Styles from this theme (cache-busted by file mtime) -->
<?php <?php
+18 -18
View File
@@ -1,4 +1,4 @@
<?php <?php
// ---- Profile hero (Blowfish-style) ------------------------------------- // ---- Profile hero (Blowfish-style) -------------------------------------
// Shown only on the blog front page (first paginated page). // Shown only on the blog front page (first paginated page).
// Drop your avatar at img/spleenftw.jpeg (change the filename below to reuse). // Drop your avatar at img/spleenftw.jpeg (change the filename below to reuse).
@@ -7,29 +7,29 @@
?> ?>
<?php if ($showProfile) : ?> <?php if ($showProfile) : ?>
<header class="profile text-center"> <header class="profile text-center">
<img class="profile-avatar" src="<?php echo $profileImage; ?>" alt="<?php echo blowdit_text($site->title()); ?>" /> <img class="profile-avatar" src="<?php echo $profileImage; ?>" alt="<?php echo blowfish_text($site->title()); ?>" />
<h1 class="profile-name"><?php echo blowdit_text($site->title()); ?></h1> <h1 class="profile-name"><?php echo blowfish_text($site->title()); ?></h1>
<?php if ($site->slogan()) : ?> <?php if ($site->slogan()) : ?>
<p class="profile-bio"><?php echo blowdit_text($site->slogan()); ?></p> <p class="profile-bio"><?php echo blowfish_text($site->slogan()); ?></p>
<?php elseif ($site->description()) : ?> <?php elseif ($site->description()) : ?>
<p class="profile-bio"><?php echo blowdit_text($site->description()); ?></p> <p class="profile-bio"><?php echo blowfish_text($site->description()); ?></p>
<?php endif ?> <?php endif ?>
<?php $networks = Theme::socialNetworks(); ?> <?php $networks = Theme::socialNetworks(); ?>
<div class="profile-social"> <div class="profile-social">
<?php foreach ($networks as $key => $label) : ?> <?php foreach ($networks as $key => $label) : ?>
<a href="<?php echo $site->{$key}(); ?>" target="_blank" rel="noopener" title="<?php echo blowdit_text($label) ?>"> <a href="<?php echo $site->{$key}(); ?>" target="_blank" rel="noopener" title="<?php echo blowfish_text($label) ?>">
<img class="profile-social-icon" src="<?php echo blowdit_asset('img/' . $key . '.svg') ?>" alt="<?php echo blowdit_text($label) ?>" /> <img class="profile-social-icon" src="<?php echo blowfish_asset('img/' . $key . '.svg') ?>" alt="<?php echo blowfish_text($label) ?>" />
</a> </a>
<?php endforeach ?> <?php endforeach ?>
<!-- RSS feed (served by the RSS plugin at /rss.xml) --> <!-- RSS feed (served by the RSS plugin at /rss.xml) -->
<a href="<?php echo rtrim(Theme::siteUrl(), '/') . '/rss.xml'; ?>" target="_blank" rel="noopener" title="<?php echo $L->get('RSS'); ?>"> <a href="<?php echo rtrim(Theme::siteUrl(), '/') . '/rss.xml'; ?>" target="_blank" rel="noopener" title="<?php echo $L->get('RSS'); ?>">
<img class="profile-social-icon" src="<?php echo blowdit_asset('img/rss.svg') ?>" alt="<?php echo $L->get('RSS'); ?>" /> <img class="profile-social-icon" src="<?php echo blowfish_asset('img/rss.svg') ?>" alt="<?php echo $L->get('RSS'); ?>" />
</a> </a>
<!-- Email (mailto) --> <!-- Email (mailto) -->
<?php if (!empty($blowditEmail)) : ?> <?php if (!empty($blowfishEmail)) : ?>
<a href="mailto:<?php echo htmlspecialchars($blowditEmail, ENT_QUOTES, 'UTF-8'); ?>" title="<?php echo $L->get('Email'); ?>"> <a href="mailto:<?php echo htmlspecialchars($blowfishEmail, ENT_QUOTES, 'UTF-8'); ?>" title="<?php echo $L->get('Email'); ?>">
<img class="profile-social-icon" src="<?php echo blowdit_asset('img/mail.svg') ?>" alt="<?php echo $L->get('Email'); ?>" /> <img class="profile-social-icon" src="<?php echo blowfish_asset('img/mail.svg') ?>" alt="<?php echo $L->get('Email'); ?>" />
</a> </a>
<?php endif ?> <?php endif ?>
</div> </div>
@@ -59,12 +59,12 @@
<div class="post-list-head"> <div class="post-list-head">
<a href="<?php echo $page->permalink(); ?>"> <a href="<?php echo $page->permalink(); ?>">
<h2 class="post-list-title"><?php echo blowdit_text($page->title()); ?></h2> <h2 class="post-list-title"><?php echo blowfish_text($page->title()); ?></h2>
</a> </a>
<div class="post-list-meta"> <div class="post-list-meta">
<span><?php echo blowdit_icon('calendar'); ?><?php echo $page->date(); ?></span> <span><?php echo blowfish_icon('calendar'); ?><?php echo $page->date(); ?></span>
<?php if (!$page->isStatic()) : ?> <?php if (!$page->isStatic()) : ?>
<span><?php echo blowdit_icon('clock'); ?><?php echo $page->readingTime(); ?></span> <span><?php echo blowfish_icon('clock'); ?><?php echo $page->readingTime(); ?></span>
<?php endif ?> <?php endif ?>
</div> </div>
</div> </div>
@@ -75,11 +75,11 @@
<div class="post-taxonomy"> <div class="post-taxonomy">
<?php if ($categoryKey) : ?> <?php if ($categoryKey) : ?>
<a class="taxonomy-badge" href="<?php echo $page->categoryPermalink(); ?>"> <a class="taxonomy-badge" href="<?php echo $page->categoryPermalink(); ?>">
<?php echo blowdit_icon('folder'); ?><?php echo blowdit_text($page->category()); ?> <?php echo blowfish_icon('folder'); ?><?php echo blowfish_text($page->category()); ?>
</a> </a>
<?php endif ?> <?php endif ?>
<?php foreach ($tagsList as $tagKey => $tagName) : ?> <?php foreach ($tagsList as $tagKey => $tagName) : ?>
<a class="taxonomy-badge" href="<?php echo DOMAIN_TAGS . $tagKey; ?>"><?php echo blowdit_icon('tag'); ?><?php echo blowdit_text($tagName); ?></a> <a class="taxonomy-badge" href="<?php echo DOMAIN_TAGS . $tagKey; ?>"><?php echo blowfish_icon('tag'); ?><?php echo blowfish_text($tagName); ?></a>
<?php endforeach ?> <?php endforeach ?>
</div> </div>
<?php endif ?> <?php endif ?>
@@ -120,7 +120,7 @@
<?php if (Paginator::showPrev()) : ?> <?php if (Paginator::showPrev()) : ?>
<li class="page-item"> <li class="page-item">
<a class="page-link is-arrow" href="<?php echo Paginator::previousPageUrl() ?>" aria-label="<?php echo $L->get('Previous'); ?>"> <a class="page-link is-arrow" href="<?php echo Paginator::previousPageUrl() ?>" aria-label="<?php echo $L->get('Previous'); ?>">
<?php echo blowdit_icon('chevron-left'); ?> <?php echo blowfish_icon('chevron-left'); ?>
</a> </a>
</li> </li>
<?php endif; ?> <?php endif; ?>
@@ -140,7 +140,7 @@
<?php if (Paginator::showNext()) : ?> <?php if (Paginator::showNext()) : ?>
<li class="page-item"> <li class="page-item">
<a class="page-link is-arrow" href="<?php echo Paginator::nextPageUrl() ?>" aria-label="<?php echo $L->get('Next'); ?>"> <a class="page-link is-arrow" href="<?php echo Paginator::nextPageUrl() ?>" aria-label="<?php echo $L->get('Next'); ?>">
<?php echo blowdit_icon('chevron-right'); ?> <?php echo blowfish_icon('chevron-right'); ?>
</a> </a>
</li> </li>
<?php endif; ?> <?php endif; ?>
+12 -12
View File
@@ -1,4 +1,4 @@
<?php <?php
/** /**
* Inline SVG icons — replaces the Bootstrap Icons webfont (~120 KB font + * Inline SVG icons — replaces the Bootstrap Icons webfont (~120 KB font +
* ~80 KB CSS) that was loaded just to render a handful of glyphs. * ~80 KB CSS) that was loaded just to render a handful of glyphs.
@@ -7,10 +7,10 @@
* font-size rules (.navbar .nav-link .bd-icon, .metadata .bd-icon, …) scale it. * font-size rules (.navbar .nav-link .bd-icon, .metadata .bd-icon, …) scale it.
* *
* Theme-picker icons (sun/moon/snow/droplet/cup/half) are duplicated in * Theme-picker icons (sun/moon/snow/droplet/cup/half) are duplicated in
* blowdit.js so the toggle can swap them client-side; keep the two in sync. * blowfish.js so the toggle can swap them client-side; keep the two in sync.
*/ */
if (!function_exists('blowdit_icon')) { if (!function_exists('blowfish_icon')) {
function blowdit_icon($name) { function blowfish_icon($name) {
static $paths = array( static $paths = array(
// --- UI --- // --- UI ---
'calendar' => '<rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/>', 'calendar' => '<rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/>',
@@ -23,7 +23,7 @@ if (!function_exists('blowdit_icon')) {
'link' => '<path d="M9 17H7A5 5 0 0 1 7 7h2"/><path d="M15 7h2a5 5 0 0 1 0 10h-2"/><path d="M8 12h8"/>', 'link' => '<path d="M9 17H7A5 5 0 0 1 7 7h2"/><path d="M15 7h2a5 5 0 0 1 0 10h-2"/><path d="M8 12h8"/>',
'search' => '<circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/>', 'search' => '<circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/>',
'share' => '<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="M8.6 13.5l6.8 4M15.4 6.5l-6.8 4"/>', 'share' => '<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="M8.6 13.5l6.8 4M15.4 6.5l-6.8 4"/>',
// --- Theme picker (mirror in blowdit.js) --- // --- Theme picker (mirror in blowfish.js) ---
'sun' => '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>', 'sun' => '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>',
'moon' => '<path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z"/>', 'moon' => '<path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z"/>',
'snow' => '<path d="M12 2v20M2 12h20M5 5l14 14M19 5 5 19"/>', 'snow' => '<path d="M12 2v20M2 12h20M5 5l14 14M19 5 5 19"/>',
@@ -39,24 +39,24 @@ if (!function_exists('blowdit_icon')) {
// Bludit already HTML-escapes titles/names on save (Sanitize::html), so a // Bludit already HTML-escapes titles/names on save (Sanitize::html), so a
// plain htmlspecialchars() in the theme double-encodes ("&" -> "&amp;amp;", // plain htmlspecialchars() in the theme double-encodes ("&" -> "&amp;amp;",
// shown as "&amp;"). blowdit_text() decodes any existing entities first, then // shown as "&amp;"). blowfish_text() decodes any existing entities first, then
// encodes exactly once — correct whether the value is pre-encoded or raw, and // encodes exactly once — correct whether the value is pre-encoded or raw, and
// always output-safe. blowdit_plain() returns decoded text (for JSON-LD etc.). // always output-safe. blowfish_plain() returns decoded text (for JSON-LD etc.).
function blowdit_plain($s) { function blowfish_plain($s) {
return html_entity_decode((string) $s, ENT_QUOTES, 'UTF-8'); return html_entity_decode((string) $s, ENT_QUOTES, 'UTF-8');
} }
function blowdit_text($s) { function blowfish_text($s) {
return htmlspecialchars(blowdit_plain($s), ENT_QUOTES, 'UTF-8'); return htmlspecialchars(blowfish_plain($s), ENT_QUOTES, 'UTF-8');
} }
// Theme asset URL with an mtime cache-buster (so edited images/SVGs refetch). // Theme asset URL with an mtime cache-buster (so edited images/SVGs refetch).
function blowdit_asset($rel) { function blowfish_asset($rel) {
$v = @filemtime(THEME_DIR . $rel); $v = @filemtime(THEME_DIR . $rel);
return DOMAIN_THEME . $rel . ($v ? '?v=' . $v : ''); return DOMAIN_THEME . $rel . ($v ? '?v=' . $v : '');
} }
// Map a theme key to its toggle icon name. // Map a theme key to its toggle icon name.
function blowdit_theme_icon($theme) { function blowfish_theme_icon($theme) {
$map = array( $map = array(
'light' => 'sun', 'light' => 'sun',
'dark' => 'moon', 'dark' => 'moon',
+13 -13
View File
@@ -1,7 +1,7 @@
<nav class="navbar navbar-expand-md navbar-dark fixed-top navbar-modern"> <nav class="navbar navbar-expand-md navbar-dark fixed-top navbar-modern">
<div class="container"> <div class="container">
<a class="navbar-brand" href="<?php echo Theme::siteUrl() ?>"> <a class="navbar-brand" href="<?php echo Theme::siteUrl() ?>">
<span class="text-white"><?php echo blowdit_text($site->title()) ?></span> <span class="text-white"><?php echo blowfish_text($site->title()) ?></span>
</a> </a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
@@ -19,7 +19,7 @@
<!-- Static pages --> <!-- Static pages -->
<?php foreach ($staticContent as $staticPage) : ?> <?php foreach ($staticContent as $staticPage) : ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link<?php echo ($url->slug() == $staticPage->slug()) ? ' active' : '' ?>" href="<?php echo $staticPage->permalink() ?>"><?php echo blowdit_text($staticPage->title()) ?></a> <a class="nav-link<?php echo ($url->slug() == $staticPage->slug()) ? ' active' : '' ?>" href="<?php echo $staticPage->permalink() ?>"><?php echo blowfish_text($staticPage->title()) ?></a>
</li> </li>
<?php endforeach ?> <?php endforeach ?>
@@ -33,9 +33,9 @@
<!-- Social Networks (SVG icons live in img/<network>.svg) --> <!-- Social Networks (SVG icons live in img/<network>.svg) -->
<?php foreach (Theme::socialNetworks() as $key => $label) : ?> <?php foreach (Theme::socialNetworks() as $key => $label) : ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="<?php echo $site->{$key}(); ?>" target="_blank" rel="noopener" title="<?php echo blowdit_text($label) ?>"> <a class="nav-link" href="<?php echo $site->{$key}(); ?>" target="_blank" rel="noopener" title="<?php echo blowfish_text($label) ?>">
<img class="d-none d-md-block nav-svg-icon" src="<?php echo blowdit_asset('img/' . $key . '.svg') ?>" alt="<?php echo blowdit_text($label) ?>" /> <img class="d-none d-md-block nav-svg-icon" src="<?php echo blowfish_asset('img/' . $key . '.svg') ?>" alt="<?php echo blowfish_text($label) ?>" />
<span class="d-inline d-md-none"><?php echo blowdit_text($label); ?></span> <span class="d-inline d-md-none"><?php echo blowfish_text($label); ?></span>
</a> </a>
</li> </li>
<?php endforeach; ?> <?php endforeach; ?>
@@ -43,16 +43,16 @@
<!-- RSS feed (served by the RSS plugin at /rss.xml) --> <!-- RSS feed (served by the RSS plugin at /rss.xml) -->
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="<?php echo rtrim(Theme::siteUrl(), '/') . '/rss.xml'; ?>" target="_blank" rel="noopener" title="<?php echo $L->get('RSS'); ?>"> <a class="nav-link" href="<?php echo rtrim(Theme::siteUrl(), '/') . '/rss.xml'; ?>" target="_blank" rel="noopener" title="<?php echo $L->get('RSS'); ?>">
<img class="d-none d-md-block nav-svg-icon" src="<?php echo blowdit_asset('img/rss.svg') ?>" alt="<?php echo $L->get('RSS'); ?>" /> <img class="d-none d-md-block nav-svg-icon" src="<?php echo blowfish_asset('img/rss.svg') ?>" alt="<?php echo $L->get('RSS'); ?>" />
<span class="d-inline d-md-none"><?php echo $L->get('RSS'); ?></span> <span class="d-inline d-md-none"><?php echo $L->get('RSS'); ?></span>
</a> </a>
</li> </li>
<!-- Email (mailto) --> <!-- Email (mailto) -->
<?php if (!empty($blowditEmail)) : ?> <?php if (!empty($blowfishEmail)) : ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="mailto:<?php echo htmlspecialchars($blowditEmail, ENT_QUOTES, 'UTF-8'); ?>" title="<?php echo $L->get('Email'); ?>"> <a class="nav-link" href="mailto:<?php echo htmlspecialchars($blowfishEmail, ENT_QUOTES, 'UTF-8'); ?>" title="<?php echo $L->get('Email'); ?>">
<img class="d-none d-md-block nav-svg-icon" src="<?php echo blowdit_asset('img/mail.svg') ?>" alt="<?php echo $L->get('Email'); ?>" /> <img class="d-none d-md-block nav-svg-icon" src="<?php echo blowfish_asset('img/mail.svg') ?>" alt="<?php echo $L->get('Email'); ?>" />
<span class="d-inline d-md-none"><?php echo $L->get('Email'); ?></span> <span class="d-inline d-md-none"><?php echo $L->get('Email'); ?></span>
</a> </a>
</li> </li>
@@ -60,17 +60,17 @@
<?php endif; ?> <?php endif; ?>
<!-- Search (opens the overlay; also bound to the "/" key). Shown by <!-- Search (opens the overlay; also bound to the "/" key). Shown by
blowdit.js only when no on-page search box is visible. --> blowfish.js only when no on-page search box is visible. -->
<li class="nav-item nav-search ml-md-2 mt-2 mt-md-0"> <li class="nav-item nav-search ml-md-2 mt-2 mt-md-0">
<button type="button" class="theme-toggle js-search-open" aria-label="<?php echo $L->get('Search'); ?>" title="<?php echo $L->get('Search'); ?> (/)"> <button type="button" class="theme-toggle js-search-open" aria-label="<?php echo $L->get('Search'); ?>" title="<?php echo $L->get('Search'); ?> (/)">
<?php echo blowdit_icon('search'); ?> <?php echo blowfish_icon('search'); ?>
</button> </button>
</li> </li>
<!-- Theme picker --> <!-- Theme picker -->
<li class="nav-item ml-md-2 mt-2 mt-md-0 theme-picker-wrap"> <li class="nav-item ml-md-2 mt-2 mt-md-0 theme-picker-wrap">
<button type="button" id="theme-toggle" class="theme-toggle" aria-label="<?php echo $L->get('Toggle theme') ?>" title="<?php echo $L->get('Toggle theme') ?>" aria-haspopup="true" aria-expanded="false"> <button type="button" id="theme-toggle" class="theme-toggle" aria-label="<?php echo $L->get('Toggle theme') ?>" title="<?php echo $L->get('Toggle theme') ?>" aria-haspopup="true" aria-expanded="false">
<?php echo blowdit_icon(blowdit_theme_icon($blowditTheme)); ?> <?php echo blowfish_icon(blowfish_theme_icon($blowfishTheme)); ?>
</button> </button>
<div id="theme-picker" class="theme-picker" role="menu"> <div id="theme-picker" class="theme-picker" role="menu">
<button class="swatch" data-pick="light" title="Light" style="--sb:#ffffff;--sa:#1f1f1f"><span class="swatch-dot"></span><span class="swatch-label">Light</span></button> <button class="swatch" data-pick="light" title="Light" style="--sb:#ffffff;--sa:#1f1f1f"><span class="swatch-dot"></span><span class="swatch-label">Light</span></button>
+17 -17
View File
@@ -1,4 +1,4 @@
<?php if ($url->notFound()) : ?> <?php if ($url->notFound()) : ?>
<!-- Custom 404 --> <!-- Custom 404 -->
<article class="card card-modern my-5 page-404"> <article class="card card-modern my-5 page-404">
@@ -9,7 +9,7 @@
<?php if (!empty($sidebarSearchHtml)) : ?> <?php if (!empty($sidebarSearchHtml)) : ?>
<div class="error-search"><?php echo $sidebarSearchHtml; ?></div> <div class="error-search"><?php echo $sidebarSearchHtml; ?></div>
<?php endif ?> <?php endif ?>
<a class="btn-primary-gradient error-home" href="<?php echo Theme::siteUrl(); ?>"><?php echo blowdit_icon('house'); ?> <?php echo $L->get('Back to home'); ?></a> <a class="btn-primary-gradient error-home" href="<?php echo Theme::siteUrl(); ?>"><?php echo blowfish_icon('house'); ?> <?php echo $L->get('Back to home'); ?></a>
</div> </div>
</article> </article>
@@ -24,7 +24,7 @@
<!-- Cover image --> <!-- Cover image -->
<?php if ($page->coverImage()): ?> <?php if ($page->coverImage()): ?>
<div class="cover-image-wrapper"> <div class="cover-image-wrapper">
<img class="card-img-top" alt="<?php echo blowdit_text($page->title()); ?>" src="<?php echo $page->coverImage(); ?>" fetchpriority="high" decoding="async"/> <img class="card-img-top" alt="<?php echo blowfish_text($page->title()); ?>" src="<?php echo $page->coverImage(); ?>" fetchpriority="high" decoding="async"/>
</div> </div>
<?php endif ?> <?php endif ?>
@@ -36,21 +36,21 @@
<a href="<?php echo Theme::siteUrl(); ?>"><?php echo $L->get('Home'); ?></a> <a href="<?php echo Theme::siteUrl(); ?>"><?php echo $L->get('Home'); ?></a>
<?php if ($page->categoryKey()) : ?> <?php if ($page->categoryKey()) : ?>
<span class="breadcrumb-sep" aria-hidden="true">›</span> <span class="breadcrumb-sep" aria-hidden="true">›</span>
<a href="<?php echo $page->categoryPermalink(); ?>"><?php echo blowdit_text($page->category()); ?></a> <a href="<?php echo $page->categoryPermalink(); ?>"><?php echo blowfish_text($page->category()); ?></a>
<?php endif ?> <?php endif ?>
<span class="breadcrumb-sep" aria-hidden="true">›</span> <span class="breadcrumb-sep" aria-hidden="true">›</span>
<span class="breadcrumb-current" aria-current="page"><?php echo blowdit_text($page->title()); ?></span> <span class="breadcrumb-current" aria-current="page"><?php echo blowfish_text($page->title()); ?></span>
</nav> </nav>
<?php endif ?> <?php endif ?>
<!-- Title --> <!-- Title -->
<h1 class="title"><?php echo blowdit_text($page->title()); ?></h1> <h1 class="title"><?php echo blowfish_text($page->title()); ?></h1>
<?php if (!$page->isStatic() && !$url->notFound()): ?> <?php if (!$page->isStatic() && !$url->notFound()): ?>
<!-- Creation date and reading time --> <!-- Creation date and reading time -->
<div class="metadata mb-4"> <div class="metadata mb-4">
<span><?php echo blowdit_icon('calendar'); ?><?php echo $page->date(); ?></span> <span><?php echo blowfish_icon('calendar'); ?><?php echo $page->date(); ?></span>
<span><?php echo blowdit_icon('clock'); ?><?php echo $L->get('Reading time') . ': ' . $page->readingTime() ?></span> <span><?php echo blowfish_icon('clock'); ?><?php echo $L->get('Reading time') . ': ' . $page->readingTime() ?></span>
</div> </div>
<?php endif ?> <?php endif ?>
@@ -74,16 +74,16 @@
} }
} catch (\Throwable $e) { $bdSeriesPosts = array(); } } catch (\Throwable $e) { $bdSeriesPosts = array(); }
} }
$bdSeriesLabel = trim(preg_replace('/^series\s*[:\-_]?\s*/i', '', blowdit_plain($bdSeriesName))); $bdSeriesLabel = trim(preg_replace('/^series\s*[:\-_]?\s*/i', '', blowfish_plain($bdSeriesName)));
?> ?>
<?php if (count($bdSeriesPosts) > 1) : ?> <?php if (count($bdSeriesPosts) > 1) : ?>
<aside class="series-box"> <aside class="series-box">
<div class="series-box-title"><?php echo blowdit_icon('folder'); ?><span><?php echo $L->get('Part of a series'); ?><?php echo $bdSeriesLabel !== '' ? ': ' . htmlspecialchars($bdSeriesLabel, ENT_QUOTES, 'UTF-8') : ''; ?></span></div> <div class="series-box-title"><?php echo blowfish_icon('folder'); ?><span><?php echo $L->get('Part of a series'); ?><?php echo $bdSeriesLabel !== '' ? ': ' . htmlspecialchars($bdSeriesLabel, ENT_QUOTES, 'UTF-8') : ''; ?></span></div>
<ol class="series-list"> <ol class="series-list">
<?php foreach ($bdSeriesPosts as $bdSp) : $bdIsCur = ($bdSp->key() === $page->key()); ?> <?php foreach ($bdSeriesPosts as $bdSp) : $bdIsCur = ($bdSp->key() === $page->key()); ?>
<li class="series-item<?php echo $bdIsCur ? ' is-current' : ''; ?>"> <li class="series-item<?php echo $bdIsCur ? ' is-current' : ''; ?>">
<?php if ($bdIsCur) : ?><span><?php echo blowdit_text($bdSp->title()); ?></span> <?php if ($bdIsCur) : ?><span><?php echo blowfish_text($bdSp->title()); ?></span>
<?php else : ?><a href="<?php echo $bdSp->permalink(); ?>"><?php echo blowdit_text($bdSp->title()); ?></a><?php endif ?> <?php else : ?><a href="<?php echo $bdSp->permalink(); ?>"><?php echo blowfish_text($bdSp->title()); ?></a><?php endif ?>
</li> </li>
<?php endforeach ?> <?php endforeach ?>
</ol> </ol>
@@ -111,24 +111,24 @@
<div class="post-taxonomy mt-4"> <div class="post-taxonomy mt-4">
<?php if ($categoryKey) : ?> <?php if ($categoryKey) : ?>
<a class="taxonomy-badge" href="<?php echo $page->categoryPermalink(); ?>"> <a class="taxonomy-badge" href="<?php echo $page->categoryPermalink(); ?>">
<?php echo blowdit_icon('folder'); ?><?php echo blowdit_text($page->category()); ?> <?php echo blowfish_icon('folder'); ?><?php echo blowfish_text($page->category()); ?>
</a> </a>
<?php endif ?> <?php endif ?>
<?php foreach ($tagsList as $tagKey => $tagName) : ?> <?php foreach ($tagsList as $tagKey => $tagName) : ?>
<a class="taxonomy-badge" href="<?php echo DOMAIN_TAGS . $tagKey; ?>"><?php echo blowdit_icon('tag'); ?><?php echo blowdit_text($tagName); ?></a> <a class="taxonomy-badge" href="<?php echo DOMAIN_TAGS . $tagKey; ?>"><?php echo blowfish_icon('tag'); ?><?php echo blowfish_text($tagName); ?></a>
<?php endforeach ?> <?php endforeach ?>
</div> </div>
<?php endif ?> <?php endif ?>
<!-- Share --> <!-- Share -->
<?php if (!$page->isStatic() && !$url->notFound()) : ?> <?php if (!$page->isStatic() && !$url->notFound()) : ?>
<?php $bdShareUrl = $page->permalink(true); $bdShareTitle = blowdit_plain($page->title()); ?> <?php $bdShareUrl = $page->permalink(true); $bdShareTitle = blowfish_plain($page->title()); ?>
<div class="post-share mt-4"> <div class="post-share mt-4">
<span class="post-share-label"><?php echo $L->get('Share'); ?></span> <span class="post-share-label"><?php echo $L->get('Share'); ?></span>
<a class="post-share-btn" href="https://twitter.com/intent/tweet?url=<?php echo rawurlencode($bdShareUrl); ?>&amp;text=<?php echo rawurlencode($bdShareTitle); ?>" target="_blank" rel="noopener" aria-label="<?php echo $L->get('Share on X (Twitter)'); ?>">X (Twitter)</a> <a class="post-share-btn" href="https://twitter.com/intent/tweet?url=<?php echo rawurlencode($bdShareUrl); ?>&amp;text=<?php echo rawurlencode($bdShareTitle); ?>" target="_blank" rel="noopener" aria-label="<?php echo $L->get('Share on X (Twitter)'); ?>">X (Twitter)</a>
<a class="post-share-btn" href="https://www.reddit.com/submit?url=<?php echo rawurlencode($bdShareUrl); ?>&amp;title=<?php echo rawurlencode($bdShareTitle); ?>" target="_blank" rel="noopener" aria-label="<?php echo $L->get('Share on Reddit'); ?>">Reddit</a> <a class="post-share-btn" href="https://www.reddit.com/submit?url=<?php echo rawurlencode($bdShareUrl); ?>&amp;title=<?php echo rawurlencode($bdShareTitle); ?>" target="_blank" rel="noopener" aria-label="<?php echo $L->get('Share on Reddit'); ?>">Reddit</a>
<a class="post-share-btn" href="https://www.linkedin.com/sharing/share-offsite/?url=<?php echo rawurlencode($bdShareUrl); ?>" target="_blank" rel="noopener" aria-label="<?php echo $L->get('Share on LinkedIn'); ?>">LinkedIn</a> <a class="post-share-btn" href="https://www.linkedin.com/sharing/share-offsite/?url=<?php echo rawurlencode($bdShareUrl); ?>" target="_blank" rel="noopener" aria-label="<?php echo $L->get('Share on LinkedIn'); ?>">LinkedIn</a>
<button type="button" class="post-share-btn js-copy-link" data-url="<?php echo htmlspecialchars($bdShareUrl, ENT_QUOTES, 'UTF-8'); ?>" aria-label="<?php echo $L->get('Copy link'); ?>"><?php echo blowdit_icon('link'); ?><span class="post-share-copy-label"><?php echo $L->get('Copy link'); ?></span></button> <button type="button" class="post-share-btn js-copy-link" data-url="<?php echo htmlspecialchars($bdShareUrl, ENT_QUOTES, 'UTF-8'); ?>" aria-label="<?php echo $L->get('Copy link'); ?>"><?php echo blowfish_icon('link'); ?><span class="post-share-copy-label"><?php echo $L->get('Copy link'); ?></span></button>
</div> </div>
<?php endif ?> <?php endif ?>
@@ -179,7 +179,7 @@
<?php if ($bdRp->coverImage()) : ?> <?php if ($bdRp->coverImage()) : ?>
<span class="related-cover" style="background-image:url('<?php echo htmlspecialchars($bdRp->coverImage(), ENT_QUOTES, 'UTF-8'); ?>')"></span> <span class="related-cover" style="background-image:url('<?php echo htmlspecialchars($bdRp->coverImage(), ENT_QUOTES, 'UTF-8'); ?>')"></span>
<?php endif ?> <?php endif ?>
<span class="related-card-title"><?php echo blowdit_text($bdRp->title()); ?></span> <span class="related-card-title"><?php echo blowfish_text($bdRp->title()); ?></span>
</a> </a>
<?php endforeach ?> <?php endforeach ?>
</div> </div>
+4 -4
View File
@@ -1,4 +1,4 @@
<aside class="sidebar-container"> <aside class="sidebar-container">
<?php <?php
// Compact profile card — shown only while reading an article // Compact profile card — shown only while reading an article
@@ -10,16 +10,16 @@
<span class="sidebar-author-label">Author</span> <span class="sidebar-author-label">Author</span>
<div class="sidebar-author-identity"> <div class="sidebar-author-identity">
<a href="<?php echo Theme::siteUrl() ?>"> <a href="<?php echo Theme::siteUrl() ?>">
<img class="sidebar-author-avatar" src="<?php echo DOMAIN_THEME . 'img/spleenftw.jpeg' ?>" alt="<?php echo blowdit_text($page->username()) ?>" /> <img class="sidebar-author-avatar" src="<?php echo DOMAIN_THEME . 'img/spleenftw.jpeg' ?>" alt="<?php echo blowfish_text($page->username()) ?>" />
</a> </a>
<div class="sidebar-author-name"><?php echo blowdit_text($page->username()) ?></div> <div class="sidebar-author-name"><?php echo blowfish_text($page->username()) ?></div>
<?php <?php
// Bio: set in Bludit Admin → Settings → General → Description // Bio: set in Bludit Admin → Settings → General → Description
// Falls back to Slogan if Description is empty. // Falls back to Slogan if Description is empty.
$_authorBio = $site->description() ?: $site->slogan(); $_authorBio = $site->description() ?: $site->slogan();
?> ?>
<?php if ($_authorBio) : ?> <?php if ($_authorBio) : ?>
<p class="sidebar-author-bio"><?php echo blowdit_text($_authorBio) ?></p> <p class="sidebar-author-bio"><?php echo blowfish_text($_authorBio) ?></p>
<?php endif ?> <?php endif ?>
</div> </div>
</div> </div>