Add Gruvbox share theme + blog system for Trilium (anonymized)

Blog generator, publish manager, share scripts, theme CSS, highlight.js
bundles, font, favicon, templates and generated-reference outputs, plus an
English installation guide.
This commit is contained in:
trilium-share-gruvbox
2026-08-28 13:36:22 +02:00
commit aa7a2d0ab5
31 changed files with 4387 additions and 0 deletions
+240
View File
@@ -0,0 +1,240 @@
# Trilium Share Gruvbox Blog System
A complete **public-share blog system + Gruvbox theme** for [TriliumNext](https://github.com/TriliumNext/Trilium).
It turns a normal Trilium note tree into a themed, searchable blog with categories, an RSS feed,
a sitemap, a digital garden (growth states) and client-side rendering of Mermaid, Excalidraw and
mind-map notes.
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/`).
![Gruvbox palette](https://raw.githubusercontent.com/morhetz/gruvbox/master/palette.png)
## Features
- Gruvbox light/dark theme for the share, with a theme toggle in the header.
- Blog index, category pages, sub-categories, tags and tag overview.
- Article metadata: date, category, summary, tags, prev/next navigation, reading time.
- **Digital Garden**: `state` labels (Seed → Evergreen, Draft, Review) rendered as badges and
colour-coded growth tables.
- **Search** (client-side over a generated index), **RSS feed** and **sitemap.xml**.
- **Widget rendering** for Mermaid, Excalidraw (canvas) and MindElixir (mind map) notes, with a
collapsible, highlighted "Source" block and copy button.
- Code highlighting (highlight.js + line numbers) and copy buttons on all code boxes.
- Optional "Buy me a coffee" button with locally-hosted Cookie font.
- `blog_generator` (backend) builds index/feed/sitemap/search-data; `blog_publish_manager` moves
notes between an `Unpublished` staging area and the live blog.
## Repository layout
| Path | Content | Trilium note type / mime |
|---|---|---|
| `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` |
| `theme/blog_share_theme.css` | the Gruvbox share theme | code, `text/css` |
| `theme/favicon.svg` | default site favicon | code (SVG) |
| `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/hljs_line_numbers.js` | highlight.js line-numbers plugin | code |
| `scripts/*.js` | frontend share snippets (content is pasted **verbatim** into a code note, including the `<script>`/`<style>` wrapper) | code |
| `templates/blog_article.html` | article template with promoted labels | text |
| `examples/*` | sample category logo + menu icon script (ignis8) | code / SVG |
| `generated/*` | reference output, **regenerated automatically** by `blog_generator.js` | code / xml |
> The file content of a `scripts/*` note already contains the `<script>` / `<style>` tags. When you
> create the code note, paste the **whole file** into the note content.
## Prerequisites
- TriliumNext server with **Public Share** enabled (Options → Public access).
- A public hostname (optional but recommended, e.g. for `sitemap.xml` / OG tags).
- Basic familiarity with Trilium attributes (labels & relations).
---
## Installation
### 1. Create the note structure
```
root
└── Blog (book)
├── Richard's Blog (book) ← blog root, the shared note
│ ├── .blog (book) ← system notes (scripts, css, data)
│ ├── Astrophotography (book) ← example category
│ ├── Electronic (book)
│ ├── Leathercraft (book)
│ ├── Digital Garden (book) ← growth-state garden category
│ ├── Tags (text) ← auto-filled tag overview
│ ├── About Me (text)
│ └── Impressum (text)
├── Unpublished (book) ← staging area
├── blog_generator (code, backend)
└── blog_publish_manager (code, backend)
```
You can use any titles; the generator finds notes by the constants at the top of
`blog_generator.js` (`BLOG_ROOT_NOTE_ID`, `BLOG_SYSTEM_NOTE_ID`, `TAGS_SECTION_TITLE`).
### 2. Create the code notes
Under **`.blog`** create one code note per file, using the file name as the note title
(optional, but matches the generator):
| Note title | File | mime |
|---|---|---|
| `blog_share_theme.css` | `theme/blog_share_theme.css` | `text/css` |
| `favicon` | `theme/favicon.svg` | (SVG) |
| `Cookie` | `theme/cookie.woff2` | `font/woff2` (file note) |
| `highlight.js` | `lib/highlight.js` | `application/javascript` |
| `hljs line numbers plugin` | `lib/hljs_line_numbers.js` | `text/javascript` |
| `hide home title` | `scripts/hide_home_title.js` | `text/html` |
| `garden h1 green` | `scripts/garden_h1_green.js` | `text/html` |
| `table column hover` | `scripts/table_column_hover.js` | `text/html` |
| `blog card click` | `scripts/blog_card_click.js` | `text/html` |
| `garden row click` | `scripts/garden_row_click.js` | `text/html` |
| `code note highlight` | `scripts/code_note_highlight.js` | `text/html` |
| `code copy & lines` | `scripts/code_copy_lines.js` | `text/html` |
| `title state badge` | `scripts/title_state_badge.js` | `text/html` |
| `bmc button` | `scripts/bmc_button.js` | `text/html` |
| `logo share alias` | `scripts/logo_share_alias.js` | `text/html` |
| `prev next` | `scripts/prev_next.js` | `text/html` |
| `reading time` | `scripts/reading_time.js` | `text/html` |
| `search header` | `scripts/search_header.js` | `text/html` |
| `note fallback` | `scripts/note_fallback.js` | `text/html` |
| `feed.xml` | `generated/feed.xml` | code (regenerated) |
| `sitemap.xml` | `generated/sitemap.xml` | code (regenerated) |
| `state_map` | `generated/state_map.js` | `text/html` (regenerated) |
| `blog_data` | `generated/blog_data.js` | `text/html` (regenerated) |
Also create, under the `Blog` book: `blog_generator` (from `blog_generator.js`,
mime `application/javascript;env=backend`) and `blog_publish_manager` (from
`blog_publish_manager.js`, mime `application/javascript;env=backend`).
### 3. Wire the attributes
**On the blog root note** (`Richard's Blog`), add:
| Attribute | Value | Inheritable |
|---|---|---|
| `shareRoot` | `true` | |
| `shareAlias` | `blog` | |
| `shareIndex` | (empty) | |
| `shareDescription` | e.g. `Project documentation & technical blog` | |
| `shareOpenGraphURL` | `https://YOUR-INSTANCE.example.com/` | |
| `shareOpenGraphColor` | `#83a598` | |
| `iconClass` | `bx bx-edit-alt` | |
| `~shareCss` | → `blog_share_theme.css` | yes |
| `~shareFavicon` | → `favicon` | yes |
| `~shareHtml` | → `table column hover`, `blog card click`, `garden row click`, `code note highlight`, `code copy & lines`, `state_map`, `title state badge`, `bmc button`, `logo share alias`, `blog_data`, `prev next`, `reading time`, `search header`, `ignis8 menu icon` (optional), `note fallback` | yes |
| `~shareHtml` | → `hide home title` | **no** (index only) |
| `~runOnNoteContentChange`, `~runOnAttributeChange`, `~runOnNoteCreation`, `~runOnNoteTitleChange`, `~runOnAttributeCreation`, `~runOnBranchDeletion`, `~runOnChildNoteCreation` | → `blog_generator` | yes |
| `~runOnAttributeChange`, `~runOnAttributeCreation` | → `blog_publish_manager` | yes |
**On the script notes** (under `.blog`), add:
| Note | Attribute |
|---|---|
| `hide home title` | `shareHtmlLocation=head:end`, `shareHiddenFromTree` |
| `garden h1 green` | `shareHtmlLocation=head:end`, `shareHiddenFromTree` |
| `state_map` | `shareHtmlLocation=body:start`, `shareHiddenFromTree` |
| `title state badge` | `shareHtmlLocation=content:start`, `shareHiddenFromTree` |
| every other script | `shareHtmlLocation=body:end`, `shareHiddenFromTree` |
| `highlight.js` | `shareHiddenFromTree` |
| `feed.xml` | `shareAlias=feed`, `shareRaw`, `shareHiddenFromTree` |
| `sitemap.xml` | `shareAlias=sitemap.xml`, `shareRaw`, `shareHiddenFromTree` |
| `.blog` (book) | `shareHiddenFromTree`, `viewType=list` |
**On the Digital Garden category note** (and any other garden category), add:
| Attribute | Value | Inheritable |
|---|---|---|
| `blogGarden` | (empty) | |
| `shareAlias` | `digital-garden` | |
| `iconClass` | `bx bx-leaf` | |
| `label:state` | `promoted,alias=Growth Condition,single,select,options=Seed;Seedling;Sprout;Sprouting;Bud;Evergreen;Draft;Review` | yes |
| `~shareHtml` | → `garden h1 green` | |
> Categories: any top-level note under the blog root (that is not `Tags`, `.blog`, `About Me`,
> `Impressum` or `Search`) is treated as a category. `blogGarden` marks it as a growth-state garden.
### 4. Replace the placeholder note IDs
Trilium assigns random note IDs on creation, so the scripts that reference notes by ID contain
placeholder tokens. Replace every `__TOKEN__` with the real note ID of the corresponding note:
| Token | Replace with note |
|---|---|
| `__BLOG_ROOT_ID__` | `Richard's Blog` |
| `__BLOG_SYSTEM_ID__` | `.blog` |
| `__UNPUBLISHED_ROOT_ID__` | `Unpublished` |
| `__BLOG_GENERATOR_ID__` | `blog_generator` |
| `__HIDE_HOME_TITLE_ID__` | `hide home title` |
| `__HLJS_ID__` | `highlight.js` |
| `__HLJS_LN_ID__` | `hljs line numbers plugin` |
| `__COOKIE_FONT_ID__` | `Cookie` |
| `__TITLE_ICON_ID__` | (only needed for the `ignis8` example) |
| `__CATEGORY_NOTE_ID__` | (only needed for the `ignis8` example) |
Then edit the constants at the top of `blog_generator.js` and `blog_publish_manager.js`:
- `BLOG_ROOT_NOTE_ID` / `BLOG_ROOT_ID` → the blog root note ID
- `BLOG_SYSTEM_NOTE_ID` → the `.blog` note ID
- `UNPUBLISHED_ROOT_ID` → the `Unpublished` note ID
- `BLOG_GENERATOR_ID` → the generator note ID
- `SHARE_BASE` → your public base URL, e.g. `https://YOUR-INSTANCE.example.com`
- `SITE_TITLE`, `SITE_DESCRIPTION` → your blog name/tagline
### 5. Enable Public Share and generate
1. On `Richard's Blog` right-click → *Share* → enable sharing.
2. Run the generator once: execute the `blog_generator` script (e.g. press play in the script
editor, or trigger any attribute/content change). It is also wired to run automatically on
every relevant change (`runOn*`), and you can add `run=daily` to it for a nightly run.
3. Open the share (options → Public access → URL) the blog index is generated at the share root.
### 6. Publish an article
1. Create a note from the `templates/blog_article.html` content (or clone the template note).
It promotes the `publish`, `date`, `category`, `summary`, `shareAlias` and `tags` labels.
2. Put it under a category inside `Richard's Blog` and set `publish=true`.
3. To keep it private, put it under `Unpublished` instead `blog_publish_manager` clones it
into the matching category (and back) when you flip `publish`.
### 7. Optional: clean hostname via nginx
If you want a clean domain instead of `/share`, proxy it and rewrite `/` to `/share/`:
```nginx
server {
server_name blog.example.com;
location / {
proxy_pass https://notes.example.com/share/;
proxy_set_header Host notes.example.com;
}
}
```
---
## How the generator works
- Walks the blog root and collects notes with `publish=true`.
- Writes the **index** (article cards), **category pages** (sub-category chips + articles,
respecting a `<!-- blog:articles -->` marker so your hand-written intro text is kept),
**tag pages** and the **tags overview**.
- Regenerates `feed.xml` (RSS), `sitemap.xml`, `state_map` (garden states) and `blog_data`
(search index used by the header search + prev/next).
- Sets `shareDescription` on every published note from its `summary`.
## Customisation
- **Theme**: edit `theme/blog_share_theme.css` (Gruvbox light/dark via `html.theme-light/dark`).
- **Buy me a coffee**: edit `scripts/bmc_button.js` (link, text, copyright) the font is
referenced via `__COOKIE_FONT_ID__`.
- **Category logos**: copy the `examples/` icon + `ignis8_menu_icon.js` pattern to give a
category a custom menu icon and title logo.
## License
The vendored `highlight.js` is licensed under BSD-3-Clause (see its header). All other files in
this repository are provided as-is see the original author's repository for details.