Files
trilium_share_gruvbox/AGENTS.md
T

8.6 KiB
Raw Blame History

AGENTS.md Trilium Share Gruvbox Blog System

Public repository for the Gruvbox public-share theme + blog system that runs on a TriliumNext instance (notes.familie-zink.org, live blog: richard.familie-zink.org). This file captures the requirements and design decisions so future work stays consistent.

What this is

A blog built on Trilium's Public Share: a backend blog generator builds index/category/tag pages, an RSS feed, a sitemap, a search index and a garden-state map from notes that carry a publish=true label. Frontend shareHtml scripts add search, prev/next, reading time, widget rendering (Mermaid/Excalidraw/MindMap) and a themed footer. All styling lives in one CSS file (Gruvbox, light + dark, theme toggle without reload).

Repository layout

Path Note in Trilium Purpose
blog_generator.js blog_generator (backend script) builds index, categories, tags, feed, sitemap, blog_data, state_map
blog_publish_manager.js blog_publish_manager (backend script) mirrors notes between Unpublished and the blog on publish label changes
theme/blog_share_theme.css blog_share_theme.css (shareCss) the entire Gruvbox theme
theme/favicon.svg, theme/cookie.woff2 favicon, Cookie site icon + BMC font
lib/highlight.js, lib/hljs_line_numbers.js highlight.js, hljs line numbers plugin code highlighting
lib/3d/* three.js, OrbitControls, STLLoader, occt-import-js (WASM) local libraries for the 3D model viewer
scripts/*.js one code note each (.blog), attached via ~shareHtml frontend snippets (verbatim content incl. <script>)
templates/blog_article.html Blog Article article template with promoted labels
examples/* ignis8 icons/script sample category logo
generated/* feed.xml, sitemap.xml, blog_data, state_map regenerated by the generator (reference only)
assets/palette.png Gruvbox palette (from morhetz/gruvbox, MIT)

How the generator works (keep this intact)

  • Collects notes with publish=true under the blog root (skips blogGarden subtrees).
  • Index: page 1 = blog root, further pages as notes under .blog named by blogPagedFor + blogPagedNumber labels, shareAlias = <alias>-<n> (blog-2, …).
  • Categories: any top-level note under the blog root that is not a system note (Tags, .blog, Impressum, About Me, Search) is a category; blogGarden marks a growth-state garden. Category pages are paginated the same way.
  • Sorting: index, categories, tags and blog_data are all sorted by publication date descending (date label, fallback dateCreated). Never change this.
  • Pagination: POSTS_PER_PAGE = 10. Prev/next on index/category pages link only between that section's pages (server-side .blog-pagination), not globally.
  • Article prev/next (scripts/prev_next.js) is scoped to the article's category (blog_data.articles[].category); falls back to the global list when uncategorized.
  • Menu sorting (scripts/menu_date_sort.js, shareHtmlLocation=body:end): reads blog_data.articles[].date (generator exposes the date label, fallback dateCreated, YYYY-MM-DD), re-sorts each category's menu entries by date descending and prefixes the label with YYYY-MM-DD <shortened title> (icons preserved). Non-article entries are left in place.
  • Thumbnails (thumbSrc()): prefer a child note of the article titled *_thumb.* (e.g. ngc281_thumb.jpg) → api/notes/<id>/download; otherwise use the first <img> from the note content. Layout: image left, summary right (.blog-post-inner), max-height:256px, lazy-loaded. There is no server-side resizing see "Limitations".
  • Regenerates feed.xml, sitemap.xml, state_map (garden states) and blog_data (search index: id/title/url/summary/tags/category). Sets shareDescription from summary.

Wiring / attributes (the ground truth)

  • All inheritable relations live on the blog root: ~shareCss → blog_share_theme.css, ~shareFavicon → favicon, ~shareHtml → <each script>, ~runOnNoteContentChange/… → blog_generator, ~runOnAttributeChange/Creation → blog_publish_manager.
  • Every script note carries shareHtmlLocation (head:end, body:end, content:start, body:start) and shareHiddenFromTree.
  • feed.xmlshareAlias=feed + shareRaw; sitemap.xmlshareAlias=sitemap.xml + shareRaw.
  • Generator + publish manager are application/javascript;env=backend; generator has run=daily.
  • Digital Garden category: blogGarden, label:state definition (promoted,alias=Growth Condition,single,select,options=Seed;Seedling;Sprout;Sprouting;Bud;Evergreen;Draft;Review, inheritable), ~shareHtml → garden h1 green.
  • On import the note IDs differ → replace the __X_ID__ tokens in scripts/generator (see README).

Theme / design rules (keep consistent)

  • Gruvbox palette only: bg #282828/#1d2021, fg #ebdbb2, red #fb4934, green #b8bb26, yellow #fabd2f, blue #83a598, purple #d3869b, aqua #8ec07c, orange #fe8019; light-mode fades from Gruvbox Light.
  • Radius system (user decision, 2026-08-28): 8px for code blocks/code notes, inline code, embedded images, diagrams (mermaid/mindmap/excalidraw) and thumbnails; 10px for blog cards; 6px for chips/tags/buttons/inputs; 4px for tiny menu icons. Embedded images and inline code use the same 8px radius.
  • Inline code: #content code { border-radius:8px; padding:.1em .35em } (dark bg in both themes).
  • Embedded images: #content img:not(.blog-thumb) { border-radius:8px }.
  • Figure captions: #content figcaption is a Gruvbox chip (secondary bg, highlight border, 8px radius, muted italic text, centered).
  • 3D models (scripts/3d_model_viewer.js): .step/.stp/.stl file notes (title ends in the extension) or inline attachment links (link text ends in the extension) get an interactive three.js viewer (drag/zoom), 8px rounded box + caption. STEP is parsed by occt-import-js (OpenCascade WASM). All five libraries are hosted as local notes and referenced via __THREE_ID__/__THREE_ORBIT_ID__/__THREE_STL_ID__/__OCCT_JS_ID__/ __OCCT_WASM_ID__. User guide: docs/embed-3d-model.md.
  • Images in the share are served full-size only (no resize endpoint) → keep the _thumb child-note convention for small thumbnails.
  • Built-in share footer navigation and book children list are hidden (#content-footer .navigation, #index).
  • SEO: a custom share template (templates/share/page.ejs, note share template, application/x-ejs, wired via ~shareTemplate inheritable on the blog root, with tree_item/prev_next/toc_item partials as children) adds per-page canonical, og:url/og:image/og:type, Twitter cards, JSON-LD (WebSite/BlogPosting/CollectionPage) and index,follow. The generator's ensureSeoMeta() sets shareDescription + shareOpenGraphURL on every published note and category. robots.txt (shareAlias=robots.txt, shareRaw) points at the sitemap; ~shareOpenGraphImage on the blog root sets the default OG image.

Limitations / gotchas

  • Backend script module whitelist is hardcoded (dayjs, marked, turndown, cheerio, axios, xml2js, escape-html, sanitize-html, lodash); sharp, fs, child_process, http etc. are blocked. Therefore image resizing inside a backend script is impossible without patching Trilium source. Do not attempt to add sharp-based code to the generator.
  • occt-import-js / OpenCASCADE (used by the 3D viewer) is LGPL-2.1 (see THIRD_PARTY_LICENSES.md); three.js is MIT. Both are hosted locally under lib/3d/.
  • Triggering the generator via an attribute change on the blog root itself is ignored by the self-trigger guard (isGeneratedNoteId(BLOG_ROOT_ID) → true). To force a full regeneration, change an attribute/content on a published article instead.
  • The share never resizes images; the _thumb child-note convention is the intended path for bandwidth-friendly thumbnails.
  • Comments/commits in the repo are English; communication with the owner is German.

Releasing changes

Edit the corresponding Trilium note (live), verify on richard.familie-zink.org, then mirror the anonymized file into this repo (replace richard.familie-zink.orghttps://YOUR-INSTANCE.example.com, Richard's BlogYour Blog, and the note-ID tokens → __…__), update README.md if the feature list changed, commit and push to main. Never commit real note IDs, the ETAPI token, or personal data (name/domain are allowed in the README demo badge only).