Files
trilium_share_gruvbox/README.md
T

17 KiB
Raw Blame History

Trilium Share Gruvbox Blog System

License Live demo TriliumNext Theme Built with Model

Note: This theme and blog system was created with the help of Opencode and Deepseek V4 Flash.

A complete public-share blog system + Gruvbox theme for TriliumNext. 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

Palette from the morhetz/gruvbox repository.

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.
  • 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 (post cards, garden table, menu) include the weekday (dateText from the generator).
  • Article thumbnails: each post card shows the first image of the article on the left (bounded by the teaser height), summary on the right, lazy-loaded. If an article has a child note named thumb.<ext> or *_thumb.<ext> (e.g. thumb.png, ngc281_thumb.jpg), that image is used instead ideal for serving small pre-made thumbnails. Thumb child notes are auto-hidden from the blog menu (shareHiddenFromTree).
  • Pagination: 10 posts per page on the index and category pages, with prev/next navigation between the pages; article prev/next links stay within the article's category.
  • 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.
  • SEO: per-page canonical + Open Graph (og:url, og:image, article/website type), Twitter cards, JSON-LD structured data (WebSite / BlogPosting / CollectionPage), robots.txt and index,follow meta. Provided by a custom share template (templates/share/page.ejs).
  • Widget rendering for Mermaid, Excalidraw (canvas) and MindElixir (mind map) notes, with a collapsible, highlighted "Source" block and copy button.
  • 3D models: .step / .stp / .stl files (as file notes, as inline attachments, or linked from text notes) render in an interactive three.js viewer (STEP via occt-wasm, OpenCASCADE V8, parsed in a non-blocking module worker). The viewer is full-bleed (~100vw) with a viewport-based height, drag to rotate / scroll to zoom, rounded 8px box, a loading progress bar and a caption showing name · size · triangle count. Large meshes are split into Uint16 chunks so mobile GPUs render them too (≈480k triangles for the demo battery module). See docs/embed-3d-model.md.
  • 3D viewer inside Trilium (app widget): the same viewer runs in the Trilium app itself (independent of the blog) via a note-detail-pane widget see app/.
  • 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/3d/* three.js + OrbitControls + STLLoader + occt-wasm (OCCT V8 bundle + WASM) for the 3D viewer code / file
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
app/3d-model-viewer-app.jsx JSX widget that renders the 3D viewer for model file notes inside the Trilium app (label #widget=3dModelViewer) code, text/jsx
app/3d-worker.js Web Worker (STEP parsing) loaded by the app widget from a code note (CSP-safe) code, text/javascript
templates/blog_article.html article template with promoted labels text
templates/share/*.ejs custom share template + partials (canonical, OG, JSON-LD, robots) code, application/x-ejs
assets/og-banner.png default Open Graph image (1200×630, replace with your own) file
docs/embed-3d-model.md how to embed STEP/STL in a note
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
menu date sort scripts/menu_date_sort.js text/html
3d model viewer scripts/3d_model_viewer.js text/html
three.js lib/3d/three.min.js application/javascript
three orbit controls lib/3d/OrbitControls.js application/javascript
three stl loader lib/3d/STLLoader.js application/javascript
occt-import-js lib/3d/occt-import-js.js application/javascript
occt-import-js.wasm lib/3d/occt-import-js.wasm application/wasm (file note)
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)
robots.txt generated/robots.txt code (static)
share template templates/share/page.ejs application/x-ejs
tree_item, prev_next, toc_item (children of share template) templates/share/tree_item.ejs etc. application/x-ejs
og-banner assets/og-banner.png image note

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
~shareTemplate share template yes
~shareOpenGraphImage og-banner
~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, menu date sort, 3d model viewer 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
robots.txt shareAlias=robots.txt, 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
__THREE_ID__ three.js
__THREE_ORBIT_ID__ three orbit controls
__THREE_STL_ID__ three stl loader
__OCCT_JS_ID__ occt-import-js
__OCCT_WASM_ID__ occt-import-js.wasm
__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/:

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 (first 10 posts) plus page notes (blog-2, blog-3, …) with prev/next navigation between the pages.
  • Writes category pages (sub-category chips + articles, respecting a <!-- blog:articles --> marker so your hand-written intro text is kept), paginated the same way; page notes live in the .blog system note and are tagged with blogPagedFor.
  • Writes tag pages and the tags overview.
  • Regenerates feed.xml (RSS), sitemap.xml, state_map (garden states) and blog_data (search index with title, summary, tags, category and YYYY-MM-DD date used by the header search, the category-scoped prev/next and the date-sorted menu).
  • 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

This project is licensed under the GNU General Public License v3.0 see LICENSE.

The repository also contains third-party assets that keep their own licenses: 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 Trilium logo in scripts/bmc_button.js (AGPL-3.0). See THIRD_PARTY_LICENSES.md for details.