Ship all three blog themes (Coffee/Harbor/Gruvbox) with palette images, theme-switch guide; make 3D viewer read theme CSS variables
This commit is contained in:
@@ -18,13 +18,27 @@ mind-map notes.
|
|||||||
Everything is driven by Trilium's built-in **Public Share** feature plus a few frontend/backend scripts –
|
Everything is driven by Trilium's built-in **Public Share** feature plus a few frontend/backend scripts –
|
||||||
no external dependencies (except `highlight.js`, vendored in `lib/`).
|
no external dependencies (except `highlight.js`, vendored in `lib/`).
|
||||||
|
|
||||||

|
## Themes
|
||||||
|
|
||||||
*Palette from the [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (Coffee / Coffee Latte) repository, MIT licensed.*
|
The share ships with **three themes**, each with a dark and a light variant (toggled via the header
|
||||||
|
switch / `html.theme-light` + `html.theme-dark`). They live in `theme/` and are set on the blog by
|
||||||
|
pointing the `shareCss` relation at the matching note – see
|
||||||
|
[`docs/switch-blog-theme.md`](docs/switch-blog-theme.md) for the step-by-step guide.
|
||||||
|
|
||||||
|
| Theme | File | Palette (dark / light) | Source |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Coffee** (active by default) | `theme/blog_share_theme_coffee.css` | espresso `#1d150f` / latte-cream `#f0e2d0`, caramel `#e0a15c` · cream `#E8E3C3` / ink `#2D1A1D`, berry `#8e2d53` | [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (MIT) |
|
||||||
|
| **Harbor** | `theme/blog_share_theme_harbor.css` | near-black `#1b1b1b` / cream `#efebdc`, coral `#e75a50` · paper `#dfe4c4` / ink `#1c2d28`, nord-blue `#5e81ac` | [HANCORE-linux/omarchy-harbor-theme](https://github.com/HANCORE-linux/omarchy-harbor-theme) (MIT) |
|
||||||
|
| **Gruvbox** | `theme/blog_share_theme_gruvbox.css` | bg `#282828` / fg `#ebdbb2`, accents red/green/yellow/blue | [morhetz/gruvbox](https://github.com/morhetz/gruvbox) (MIT) |
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Coffee dark / Coffee Latte light theme for the share, with a theme toggle in the header.
|
- Coffee dark / Coffee Latte light theme for the share, with a theme toggle in the header
|
||||||
|
(Gruvbox and Harbor are available as well – see the [Themes](#themes) section).
|
||||||
- Blog index, category pages, sub-categories, tags and tag overview.
|
- Blog index, category pages, sub-categories, tags and tag overview.
|
||||||
- **Menu sorting**: articles in the left menu are sorted by publication date (newest first)
|
- **Menu sorting**: articles in the left menu are sorted by publication date (newest first)
|
||||||
and labelled `Fri 2026-08-28 <shortened title>` automatically. All date displays
|
and labelled `Fri 2026-08-28 <shortened title>` automatically. All date displays
|
||||||
@@ -66,7 +80,9 @@ no external dependencies (except `highlight.js`, vendored in `lib/`).
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `blog_generator.js` | backend script that generates index, feed, sitemap, search data, category & tag pages | code, `application/javascript;env=backend` |
|
| `blog_generator.js` | backend script that generates index, feed, sitemap, search data, category & tag pages | code, `application/javascript;env=backend` |
|
||||||
| `blog_publish_manager.js` | backend script that mirrors notes between `Unpublished` and the blog | code, `application/javascript;env=backend` |
|
| `blog_publish_manager.js` | backend script that mirrors notes between `Unpublished` and the blog | code, `application/javascript;env=backend` |
|
||||||
| `theme/blog_share_theme.css` | the Coffee share theme (Coffee / Coffee Latte) | code, `text/css` |
|
| `theme/blog_share_theme_coffee.css` | Coffee / Coffee Latte share theme (active by default) | code, `text/css` |
|
||||||
|
| `theme/blog_share_theme_harbor.css` | Harbor / Harbordark share theme | code, `text/css` |
|
||||||
|
| `theme/blog_share_theme_gruvbox.css` | Gruvbox light/dark share theme | code, `text/css` |
|
||||||
| `theme/favicon.svg` | default site favicon | code (SVG) |
|
| `theme/favicon.svg` | default site favicon | code (SVG) |
|
||||||
| `theme/cookie.woff2` | "Cookie" font for the Buy me a coffee button | file, `font/woff2` |
|
| `theme/cookie.woff2` | "Cookie" font for the Buy me a coffee button | file, `font/woff2` |
|
||||||
| `lib/highlight.js` | vendored highlight.js | code, `application/javascript` |
|
| `lib/highlight.js` | vendored highlight.js | code, `application/javascript` |
|
||||||
@@ -124,7 +140,9 @@ Under **`.blog`** create one code note per file, using the file name as the note
|
|||||||
|
|
||||||
| Note title | File | mime |
|
| Note title | File | mime |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `blog_share_theme.css` | `theme/blog_share_theme.css` | `text/css` |
|
| `blog_share_theme_coffee.css` | `theme/blog_share_theme_coffee.css` | `text/css` |
|
||||||
|
| `blog_share_theme_harbor.css` | `theme/blog_share_theme_harbor.css` | `text/css` |
|
||||||
|
| `blog_share_theme_gruvbox.css` | `theme/blog_share_theme_gruvbox.css` | `text/css` |
|
||||||
| `favicon` | `theme/favicon.svg` | – (SVG) |
|
| `favicon` | `theme/favicon.svg` | – (SVG) |
|
||||||
| `Cookie` | `theme/cookie.woff2` | `font/woff2` (file note) |
|
| `Cookie` | `theme/cookie.woff2` | `font/woff2` (file note) |
|
||||||
| `highlight.js` | `lib/highlight.js` | `application/javascript` |
|
| `highlight.js` | `lib/highlight.js` | `application/javascript` |
|
||||||
@@ -176,7 +194,7 @@ mime `application/javascript;env=backend`) and `blog_publish_manager` (from
|
|||||||
| `shareOpenGraphURL` | `https://YOUR-INSTANCE.example.com/` | – |
|
| `shareOpenGraphURL` | `https://YOUR-INSTANCE.example.com/` | – |
|
||||||
| `shareOpenGraphColor` | `#83a598` | – |
|
| `shareOpenGraphColor` | `#83a598` | – |
|
||||||
| `iconClass` | `bx bx-edit-alt` | – |
|
| `iconClass` | `bx bx-edit-alt` | – |
|
||||||
| `~shareCss` | → `blog_share_theme.css` | yes |
|
| `~shareCss` | → `blog_share_theme_coffee.css` (the active theme) | yes |
|
||||||
| `~shareFavicon` | → `favicon` | yes |
|
| `~shareFavicon` | → `favicon` | yes |
|
||||||
| `~shareTemplate` | → `share template` | yes |
|
| `~shareTemplate` | → `share template` | yes |
|
||||||
| `~shareOpenGraphImage` | → `og-banner` | – |
|
| `~shareOpenGraphImage` | → `og-banner` | – |
|
||||||
@@ -297,7 +315,10 @@ server {
|
|||||||
|
|
||||||
## Customisation
|
## Customisation
|
||||||
|
|
||||||
- **Theme**: edit `theme/blog_share_theme.css` (Coffee / Coffee Latte via `html.theme-light/dark`).
|
- **Theme**: pick one of `theme/blog_share_theme_{coffee,harbor,gruvbox}.css` and point the blog's
|
||||||
|
`shareCss` relation at it – see [`docs/switch-blog-theme.md`](docs/switch-blog-theme.md). Each theme
|
||||||
|
ships dark + light via `html.theme-light/dark`. The 3D viewer and Mermaid rendering read the theme's
|
||||||
|
CSS variables, so they adapt automatically to whichever theme is active.
|
||||||
- **Buy me a coffee**: edit `scripts/bmc_button.js` (link, text, copyright) – the font is
|
- **Buy me a coffee**: edit `scripts/bmc_button.js` (link, text, copyright) – the font is
|
||||||
referenced via `__COOKIE_FONT_ID__`.
|
referenced via `__COOKIE_FONT_ID__`.
|
||||||
- **Category logos**: copy the `examples/` icon + `ignis8_menu_icon.js` pattern to give a
|
- **Category logos**: copy the `examples/` icon + `ignis8_menu_icon.js` pattern to give a
|
||||||
@@ -309,6 +330,6 @@ This project is licensed under the **GNU General Public License v3.0** – see [
|
|||||||
|
|
||||||
The repository also contains third-party assets that keep their own licenses:
|
The repository also contains third-party assets that keep their own licenses:
|
||||||
`lib/highlight.js` (BSD-3-Clause), `lib/hljs_line_numbers.js` (MIT),
|
`lib/highlight.js` (BSD-3-Clause), `lib/hljs_line_numbers.js` (MIT),
|
||||||
`theme/cookie.woff2` (SIL OFL 1.1), `assets/palette.png` (MIT) and the embedded
|
`theme/cookie.woff2` (SIL OFL 1.1), the three palette images `assets/palette-*.png` (MIT) and the embedded
|
||||||
Trilium logo in `scripts/bmc_button.js` (AGPL-3.0). See
|
Trilium logo in `scripts/bmc_button.js` (AGPL-3.0). See
|
||||||
[`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md) for details.
|
[`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md) for details.
|
||||||
@@ -8,8 +8,12 @@ The following files in this repository are **not** covered by the GPL-3.0 and ke
|
|||||||
| `lib/highlight.js` | [Highlight.js](https://github.com/highlightjs/highlight.js) v11.9.0 | [BSD-3-Clause](#bsd-3-clause) |
|
| `lib/highlight.js` | [Highlight.js](https://github.com/highlightjs/highlight.js) v11.9.0 | [BSD-3-Clause](#bsd-3-clause) |
|
||||||
| `lib/hljs_line_numbers.js` | [highlightjs-line-numbers](https://github.com/yury-dymov/highlightjs-line-numbers) | [MIT](#mit-license) |
|
| `lib/hljs_line_numbers.js` | [highlightjs-line-numbers](https://github.com/yury-dymov/highlightjs-line-numbers) | [MIT](#mit-license) |
|
||||||
| `theme/cookie.woff2` | "Cookie" font, © 2011 [Ania Kruk](https://fonts.google.com/specimen/Cookie) | [SIL Open Font License 1.1](https://openfontlicense.org/) |
|
| `theme/cookie.woff2` | "Cookie" font, © 2011 [Ania Kruk](https://fonts.google.com/specimen/Cookie) | [SIL Open Font License 1.1](https://openfontlicense.org/) |
|
||||||
| `assets/palette.png` | Coffee / Coffee Latte palette image generated from [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (repo: MIT) | [MIT](#mit-license) |
|
| `assets/palette-coffee.png` | Coffee / Coffee Latte palette image generated from [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (repo: MIT) | [MIT](#mit-license) |
|
||||||
| `theme/blog_share_theme.css` | Coffee / Coffee Latte color scheme from [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (repo: MIT) | [MIT](#mit-license) |
|
| `assets/palette-harbor.png` | Harbor / Harbordark palette image generated from [HANCORE-linux/omarchy-harbor-theme](https://github.com/HANCORE-linux/omarchy-harbor-theme) (repo: MIT) | [MIT](#mit-license) |
|
||||||
|
| `assets/palette-gruvbox.png` | Gruvbox palette image generated from [morhetz/gruvbox](https://github.com/morhetz/gruvbox) (repo: MIT) | [MIT](#mit-license) |
|
||||||
|
| `theme/blog_share_theme_coffee.css` | Coffee / Coffee Latte color scheme from [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (repo: MIT) | [MIT](#mit-license) |
|
||||||
|
| `theme/blog_share_theme_harbor.css` | Harbor / Harbordark color scheme from [HANCORE-linux/omarchy-harbor-theme](https://github.com/HANCORE-linux/omarchy-harbor-theme) (repo: MIT) | [MIT](#mit-license) |
|
||||||
|
| `theme/blog_share_theme_gruvbox.css` | Gruvbox color scheme from [morhetz/gruvbox](https://github.com/morhetz/gruvbox) (repo: MIT) | [MIT](#mit-license) |
|
||||||
| `lib/3d/three.min.js`, `lib/3d/OrbitControls.js`, `lib/3d/STLLoader.js` | [three.js](https://github.com/mrdoob/three.js) r128 | [MIT](#mit-license) |
|
| `lib/3d/three.min.js`, `lib/3d/OrbitControls.js`, `lib/3d/STLLoader.js` | [three.js](https://github.com/mrdoob/three.js) r128 | [MIT](#mit-license) |
|
||||||
| `lib/3d/occt-import-js.js`, `lib/3d/occt-import-js.wasm` | [occt-import-js](https://github.com/kovacsv/occt-import-js) (OpenCASCADE, WASM) | [LGPL-2.1](#lgpl-21) |
|
| `lib/3d/occt-import-js.js`, `lib/3d/occt-import-js.wasm` | [occt-import-js](https://github.com/kovacsv/occt-import-js) (OpenCASCADE, WASM) | [LGPL-2.1](#lgpl-21) |
|
||||||
| `lib/3d/occt-wasm-bundle.js`, `lib/3d/occt-wasm.wasm` | [occt-wasm](https://github.com/andymai/occt-wasm) (OpenCASCADE **OCCT V8**, WebAssembly, esbuild-bundled `OcctKernel`) | [MIT OR Apache-2.0](#mit-license) |
|
| `lib/3d/occt-wasm-bundle.js`, `lib/3d/occt-wasm.wasm` | [occt-wasm](https://github.com/andymai/occt-wasm) (OpenCASCADE **OCCT V8**, WebAssembly, esbuild-bundled `OcctKernel`) | [MIT OR Apache-2.0](#mit-license) |
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,65 @@
|
|||||||
|
# Switching the blog theme
|
||||||
|
|
||||||
|
The blog theme is **not** selected by a file name – it is controlled by the **`~shareCss` relation**
|
||||||
|
on the blog root note (`Richard's Blog`) inside Trilium. The relation points at a `text/css` note
|
||||||
|
that holds the theme stylesheet. Everything below uses the notes of this repository's live blog;
|
||||||
|
note IDs are given for reference.
|
||||||
|
|
||||||
|
## Available themes
|
||||||
|
|
||||||
|
| Theme | Repo file | Live note | Dark / light |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Coffee (default) | `theme/blog_share_theme_coffee.css` | `NqZXcFRGi3II` | espresso / latte-cream |
|
||||||
|
| Harbor | `theme/blog_share_theme_harbor.css` | `Ep54EDTwKxup` | harbordark / paper |
|
||||||
|
| Gruvbox | `theme/blog_share_theme_gruvbox.css` | `edhL5cByGwqA` | gruvbox dark / light |
|
||||||
|
|
||||||
|
All three themes include a `html.theme-light` + `html.theme-dark` block, theme-aware **3D viewer**
|
||||||
|
overlays and **Mermaid** rendering. The 3D viewer reads the theme's CSS variables
|
||||||
|
(`--background-primary` for the canvas, `--background-active` for the model material), so it adapts
|
||||||
|
automatically to whichever theme is active – no script changes needed.
|
||||||
|
|
||||||
|
## How the active theme is resolved
|
||||||
|
|
||||||
|
1. The blog root note (`Richard's Blog`, `OKjkpgmXZY1L`) carries `~shareCss` → `blog_share_theme_*.css`.
|
||||||
|
2. The target note is a `code` note with mime `text/css` living under `.blog` (the share system note).
|
||||||
|
3. The public share renders the page and loads that note's CSS.
|
||||||
|
|
||||||
|
## Switching themes
|
||||||
|
|
||||||
|
Pick the target note and re-point the relation. The theme note already exists in Trilium (see the
|
||||||
|
table above), so only the relation needs to change.
|
||||||
|
|
||||||
|
### Option A – via the Trilium UI
|
||||||
|
|
||||||
|
1. Find the target theme note under `.blog` (e.g. `blog_share_theme_gruvbox.css`).
|
||||||
|
2. Open `Richard's Blog` → Attributes → edit the `~shareCss` relation to point at that note.
|
||||||
|
3. Hard-refresh the blog (Ctrl+F5). Done.
|
||||||
|
|
||||||
|
### Option B – via ETAPI (scriptable)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TOKEN="your-etapi-token"
|
||||||
|
SERVER="https://notes.familie-zink.org"
|
||||||
|
|
||||||
|
# 1. Delete the current shareCss relation on the blog root (OKjkpgmXZY1L)
|
||||||
|
curl -X DELETE "$SERVER/etapi/attributes/<attributeId>" -H "Authorization: $TOKEN"
|
||||||
|
|
||||||
|
# 2. Point shareCss at the desired theme note (example: Gruvbox edhL5cByGwqA)
|
||||||
|
curl -X POST "$SERVER/etapi/attributes" \
|
||||||
|
-H "Authorization: $TOKEN" -H "Content-Type: application/json" \
|
||||||
|
-d '{"noteId":"OKjkpgmXZY1L","type":"relation","name":"shareCss","value":"edhL5cByGwqA","isInheritable":true}'
|
||||||
|
```
|
||||||
|
|
||||||
|
The attribute ID of the old `shareCss` relation can be looked up via
|
||||||
|
`GET $SERVER/etapi/notes/OKjkpgmXZY1L/attributes` (or the Trilium attribute list on the note).
|
||||||
|
|
||||||
|
### Option C – create a fresh theme note from this repo
|
||||||
|
|
||||||
|
If you want to add or customise a theme on a new Trilium instance:
|
||||||
|
|
||||||
|
1. Create a `code` note with mime `text/css` under `.blog`, title e.g. `blog_share_theme_coffee.css`.
|
||||||
|
2. Copy the contents of the matching `theme/blog_share_theme_*.css` file into it.
|
||||||
|
3. Point `~shareCss` on the blog root at the new note (Option A or B).
|
||||||
|
|
||||||
|
> **Tip:** After a theme/content change the page may be cached – do a hard reload. If you run
|
||||||
|
> nginx proxy caching, also clear the cache (see `AGENTS.md` / the nginx guide).
|
||||||
@@ -232,8 +232,9 @@
|
|||||||
var initSize = canvasSize();
|
var initSize = canvasSize();
|
||||||
var renderer = new THREE.WebGLRenderer({ antialias: true });
|
var renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||||
renderer.setSize(initSize.w, initSize.h);
|
renderer.setSize(initSize.w, initSize.h);
|
||||||
var isLight = document.documentElement.classList.contains("theme-light");
|
var cs = getComputedStyle(document.documentElement);
|
||||||
renderer.setClearColor(isLight ? 0xE8E3C3 : 0x1d150f, 1);
|
function cssVar(n, fb) { var v = (cs.getPropertyValue(n) || "").trim(); return v || fb; }
|
||||||
|
renderer.setClearColor(cssVar("--background-primary", "#1d150f"), 1);
|
||||||
|
|
||||||
var scene = new THREE.Scene();
|
var scene = new THREE.Scene();
|
||||||
var camera = new THREE.PerspectiveCamera(45, initSize.w / initSize.h, 0.1, 1000000);
|
var camera = new THREE.PerspectiveCamera(45, initSize.w / initSize.h, 0.1, 1000000);
|
||||||
@@ -309,7 +310,7 @@
|
|||||||
geo.setAttribute("position", new THREE.Float32BufferAttribute(e.data.positions, 3));
|
geo.setAttribute("position", new THREE.Float32BufferAttribute(e.data.positions, 3));
|
||||||
if (e.data.indices) geo.setIndex(new THREE.BufferAttribute(e.data.indices, 1));
|
if (e.data.indices) geo.setIndex(new THREE.BufferAttribute(e.data.indices, 1));
|
||||||
fixWinding(geo);
|
fixWinding(geo);
|
||||||
var mat = new THREE.MeshStandardMaterial({ color: isLight ? 0x8e2d53 : 0xe0a15c, metalness: 0.2, roughness: 0.6, side: THREE.DoubleSide, flatShading: false });
|
var mat = new THREE.MeshStandardMaterial({ color: cssVar("--background-active", "#e0a15c"), metalness: 0.2, roughness: 0.6, side: THREE.DoubleSide, flatShading: false });
|
||||||
chunkGeometry(geo, group, mat);
|
chunkGeometry(geo, group, mat);
|
||||||
var tris = (e.data.indices && e.data.indices.length) ? e.data.indices.length / 3 : e.data.positions.length / 9;
|
var tris = (e.data.indices && e.data.indices.length) ? e.data.indices.length / 3 : e.data.positions.length / 9;
|
||||||
fit();
|
fit();
|
||||||
@@ -331,7 +332,7 @@
|
|||||||
} else {
|
} else {
|
||||||
var geo = new THREE.STLLoader().parse(data.buffer);
|
var geo = new THREE.STLLoader().parse(data.buffer);
|
||||||
geo.computeVertexNormals();
|
geo.computeVertexNormals();
|
||||||
var mat = new THREE.MeshStandardMaterial({ color: isLight ? 0x8e2d53 : 0xe0a15c, metalness: 0.25, roughness: 0.55, flatShading: true });
|
var mat = new THREE.MeshStandardMaterial({ color: cssVar("--background-active", "#e0a15c"), metalness: 0.25, roughness: 0.55, flatShading: true });
|
||||||
group.add(new THREE.Mesh(geo, mat));
|
group.add(new THREE.Mesh(geo, mat));
|
||||||
fit();
|
fit();
|
||||||
showCanvas();
|
showCanvas();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user