From d483b6e8401cea9659b2388f5746b9a0e22b2a15 Mon Sep 17 00:00:00 2001
From: Kevin Leutzinger <6435727+kleutzinger@users.noreply.github.com>
Date: Tue, 30 Sep 2025 18:50:10 -0400
Subject: [PATCH 1/4] add shareHTML relation
---
.../server/src/services/builtin_attributes.ts | 2 ++
packages/share-theme/src/templates/page.ejs | 34 +++++++++++++++++--
2 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/apps/server/src/services/builtin_attributes.ts b/apps/server/src/services/builtin_attributes.ts
index 2d35992aa..ca48f3df1 100644
--- a/apps/server/src/services/builtin_attributes.ts
+++ b/apps/server/src/services/builtin_attributes.ts
@@ -66,6 +66,7 @@ export default [
{ type: "label", name: "shareDisallowRobotIndexing" },
{ type: "label", name: "shareCredentials" },
{ type: "label", name: "shareIndex" },
+ { type: "label", name: "shareHTMLLocation" },
{ type: "label", name: "displayRelations" },
{ type: "label", name: "hideRelations" },
{ type: "label", name: "titleTemplate", isDangerous: true },
@@ -105,6 +106,7 @@ export default [
{ type: "relation", name: "renderNote", isDangerous: true },
{ type: "relation", name: "shareCss" },
{ type: "relation", name: "shareJs" },
+ { type: "relation", name: "shareHTML" },
{ type: "relation", name: "shareTemplate" },
{ type: "relation", name: "shareFavicon" }
];
diff --git a/packages/share-theme/src/templates/page.ejs b/packages/share-theme/src/templates/page.ejs
index e4ac1f603..eaa7dbe3b 100644
--- a/packages/share-theme/src/templates/page.ejs
+++ b/packages/share-theme/src/templates/page.ejs
@@ -1,7 +1,31 @@
- <% const hasTree = subRoot.note.hasVisibleChildren(); %>
+ <%
+ const hasTree = subRoot.note.hasVisibleChildren();
+
+ // Collect HTML snippets by location
+ const htmlSnippetsByLocation = {};
+ for (const htmlRelation of note.getRelations("shareHTML")) {
+ const htmlNote = htmlRelation.targetNote;
+ if (htmlNote) {
+ let location = htmlNote.getLabelValue("shareHTMLLocation") || "content:end";
+ // Default to :end if no position specified
+ if (!location.includes(":")) {
+ location = location + ":end";
+ }
+ if (!htmlSnippetsByLocation[location]) {
+ htmlSnippetsByLocation[location] = [];
+ }
+ htmlSnippetsByLocation[location].push(htmlNote.getContent());
+ }
+ }
+ const renderSnippets = (location) => {
+ const snippets = htmlSnippetsByLocation[location];
+ return snippets ? snippets.join("\n") : "";
+ };
+ %>
+ <%- renderSnippets("head:start") %>
@@ -53,6 +77,7 @@
+ <%- renderSnippets("head:end") %>
<%
const customLogoId = subRoot.note.getRelation("shareLogo")?.value;
@@ -72,6 +97,7 @@ content = content.replaceAll(headingRe, (...match) => {
});
%>
+<%- renderSnippets("body:start") %>
-
ck-content<% } %><% if (isEmpty) { %> no-content<% } %>">
+ <%- renderSnippets("content:start") %>
<%= note.title %>
<% if (isEmpty && (!note.hasVisibleChildren() && note.type !== "book")) { %>
This note has no content.
@@ -132,6 +158,7 @@ content = content.replaceAll(headingRe, (...match) => {
%>
<%- content %>
<% } %>
+ <%- renderSnippets("content:end") %>
<% if (note.hasVisibleChildren() || note.type === "book") { %>
@@ -164,7 +191,7 @@ content = content.replaceAll(headingRe, (...match) => {
<% } %>
- <% if (hasTree) { %>
+ <% if (hasTree) { %>
<%- include("prev_next", { note: note, subRoot: subRoot }) %>
<% } %>
@@ -205,5 +232,6 @@ content = content.replaceAll(headingRe, (...match) => {
<% } %>
+<%- renderSnippets("body:end") %>
From 0a25d4db0d5be2a50ef510549c6f8479f250fb9f Mon Sep 17 00:00:00 2001
From: Kevin Leutzinger <6435727+kleutzinger@users.noreply.github.com>
Date: Wed, 1 Oct 2025 03:30:16 -0400
Subject: [PATCH 2/4] shareHTML to shareHtml
---
apps/server/src/services/builtin_attributes.ts | 4 ++--
packages/share-theme/src/templates/page.ejs | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/apps/server/src/services/builtin_attributes.ts b/apps/server/src/services/builtin_attributes.ts
index ca48f3df1..293bc6b5d 100644
--- a/apps/server/src/services/builtin_attributes.ts
+++ b/apps/server/src/services/builtin_attributes.ts
@@ -66,7 +66,7 @@ export default [
{ type: "label", name: "shareDisallowRobotIndexing" },
{ type: "label", name: "shareCredentials" },
{ type: "label", name: "shareIndex" },
- { type: "label", name: "shareHTMLLocation" },
+ { type: "label", name: "shareHtmlLocation" },
{ type: "label", name: "displayRelations" },
{ type: "label", name: "hideRelations" },
{ type: "label", name: "titleTemplate", isDangerous: true },
@@ -106,7 +106,7 @@ export default [
{ type: "relation", name: "renderNote", isDangerous: true },
{ type: "relation", name: "shareCss" },
{ type: "relation", name: "shareJs" },
- { type: "relation", name: "shareHTML" },
+ { type: "relation", name: "shareHtml" },
{ type: "relation", name: "shareTemplate" },
{ type: "relation", name: "shareFavicon" }
];
diff --git a/packages/share-theme/src/templates/page.ejs b/packages/share-theme/src/templates/page.ejs
index eaa7dbe3b..cc96cc4ca 100644
--- a/packages/share-theme/src/templates/page.ejs
+++ b/packages/share-theme/src/templates/page.ejs
@@ -6,10 +6,10 @@
// Collect HTML snippets by location
const htmlSnippetsByLocation = {};
- for (const htmlRelation of note.getRelations("shareHTML")) {
+ for (const htmlRelation of note.getRelations("shareHtml")) {
const htmlNote = htmlRelation.targetNote;
if (htmlNote) {
- let location = htmlNote.getLabelValue("shareHTMLLocation") || "content:end";
+ let location = htmlNote.getLabelValue("shareHtmlLocation") || "content:end";
// Default to :end if no position specified
if (!location.includes(":")) {
location = location + ":end";
From 888d0d10846d55c44c6b3f438f774303db53cfd1 Mon Sep 17 00:00:00 2001
From: Kevin Leutzinger <6435727+kleutzinger@users.noreply.github.com>
Date: Wed, 1 Oct 2025 03:46:08 -0400
Subject: [PATCH 3/4] add docs about shareHtml and shareHtmlLocation
---
.../Advanced Usage/Attributes/Relations.md | 1 +
.../User Guide/Advanced Usage/Sharing.md | 19 ++++++++++++++++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/docs/User Guide/User Guide/Advanced Usage/Attributes/Relations.md b/docs/User Guide/User Guide/Advanced Usage/Attributes/Relations.md
index 2916640ec..454e7d007 100644
--- a/docs/User Guide/User Guide/Advanced Usage/Attributes/Relations.md
+++ b/docs/User Guide/User Guide/Advanced Usage/Attributes/Relations.md
@@ -50,5 +50,6 @@ These relations are supported and used internally by Trilium.
| `widget_relation` | target of this relation will be executed and rendered as a widget in the sidebar |
| `shareCss` | CSS note which will be injected into the share page. CSS note must be in the shared sub-tree as well. Consider using `share_hidden_from_tree` and `share_omit_default_css` as well. |
| `shareJs` | JavaScript note which will be injected into the share page. JS note must be in the shared sub-tree as well. Consider using `share_hidden_from_tree`. |
+| `shareHtml` | HTML note which will be injected into the share page at locations specified by the `shareHtmlLocation` label. HTML note must be in the shared sub-tree as well. Consider using `share_hidden_from_tree`. |
| `shareTemplate` | Embedded JavaScript note that will be used as the template for displaying the shared note. Falls back to the default template. Consider using `share_hidden_from_tree`. |
| `shareFavicon` | Favicon note to be set in the shared page. Typically you want to set it to share root and make it inheritable. Favicon note must be in the shared sub-tree as well. Consider using `share_hidden_from_tree`. |
\ No newline at end of file
diff --git a/docs/User Guide/User Guide/Advanced Usage/Sharing.md b/docs/User Guide/User Guide/Advanced Usage/Sharing.md
index 37ce5fee8..793389ae2 100644
--- a/docs/User Guide/User Guide/Advanced Usage/Sharing.md
+++ b/docs/User Guide/User Guide/Advanced Usage/Sharing.md
@@ -67,6 +67,23 @@ The default design should be a good starting point, but you can customize it usi
You can inject custom JavaScript into the shared note using the `~shareJs` relation. This allows you to access note attributes or traverse the note tree using the `fetchNote()` API, which retrieves note data based on its ID.
+### Adding custom HTML
+
+You can inject custom HTML snippets into specific locations of the shared page using the `~shareHtml` relation. The HTML note should contain the raw HTML content you want to inject, and you can control where it appears using the `#shareHtmlLocation` label.
+
+The `#shareHtmlLocation` label accepts values in the format `location:position`:
+- **Locations**: `head`, `body`, `content`
+- **Positions**: `start`, `end`
+
+For example:
+- `#shareHtmlLocation=head:start` - Injects HTML at the beginning of the `` section
+- `#shareHtmlLocation=head:end` - Injects HTML at the end of the `` section (default)
+- `#shareHtmlLocation=body:start` - Injects HTML at the beginning of the `` section
+- `#shareHtmlLocation=content:start` - Injects HTML at the beginning of the content area
+- `#shareHtmlLocation=content:end` - Injects HTML at the end of the content area
+
+If no location is specified, the HTML will be injected at `content:end` by default.
+
Example:
```javascript
@@ -106,7 +123,7 @@ To do so, create a shared text note and apply the `shareIndex` label. When viewe
## Attribute reference
-Attribute | Description |
---|
shareHiddenFromTree | this note is hidden from left navigation tree, but still accessible with its URL |
shareExternalLink | note will act as a link to an external website in the share tree |
shareAlias | define an alias using which the note will be available under https://your_trilium_host/share/[your_alias] |
shareOmitDefaultCss | default share page CSS will be omitted. Use when you make extensive styling changes. |
shareRoot | marks note which is served on /share root. |
shareDescription | define text to be added to the HTML meta tag for description |
shareRaw | Note will be served in its raw format, without HTML wrapper. See also Serving directly the content of a note for an alternative method without setting an attribute. |
shareDisallowRobotIndexing | Indicates to web crawlers that the page should not be indexed of this note by: - Setting the
X-Robots-Tag: noindex HTTP header. - Setting the
noindex, follow meta tag.
|
shareCredentials | require credentials to access this shared note. Value is expected to be in format username:password . Don't forget to make this inheritable to apply to child-notes/images. |
shareIndex | Note with this label will list all roots of shared notes. |
+Attribute | Description |
---|
shareHiddenFromTree | this note is hidden from left navigation tree, but still accessible with its URL |
shareExternalLink | note will act as a link to an external website in the share tree |
shareAlias | define an alias using which the note will be available under https://your_trilium_host/share/[your_alias] |
shareOmitDefaultCss | default share page CSS will be omitted. Use when you make extensive styling changes. |
shareRoot | marks note which is served on /share root. |
shareDescription | define text to be added to the HTML meta tag for description |
shareRaw | Note will be served in its raw format, without HTML wrapper. See also Serving directly the content of a note for an alternative method without setting an attribute. |
shareDisallowRobotIndexing | Indicates to web crawlers that the page should not be indexed of this note by: - Setting the
X-Robots-Tag: noindex HTTP header. - Setting the
noindex, follow meta tag.
|
shareCredentials | require credentials to access this shared note. Value is expected to be in format username:password . Don't forget to make this inheritable to apply to child-notes/images. |
shareIndex | Note with this label will list all roots of shared notes. |
shareHtmlLocation | defines where custom HTML injected via ~shareHtml relation should be placed. Format: location:position where location is head , body , or content and position is start or end . Defaults to content:end . |
## Credits
From b8851565ebcba216d5dc025a513edda9242e73ca Mon Sep 17 00:00:00 2001
From: Kevin Leutzinger <6435727+kleutzinger@users.noreply.github.com>
Date: Wed, 1 Oct 2025 03:53:29 -0400
Subject: [PATCH 4/4] docs: clarify shareHtmlLocation goes on snippet note
---
docs/User Guide/User Guide/Advanced Usage/Sharing.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/User Guide/User Guide/Advanced Usage/Sharing.md b/docs/User Guide/User Guide/Advanced Usage/Sharing.md
index 793389ae2..caee9238e 100644
--- a/docs/User Guide/User Guide/Advanced Usage/Sharing.md
+++ b/docs/User Guide/User Guide/Advanced Usage/Sharing.md
@@ -69,7 +69,7 @@ You can inject custom JavaScript into the shared note using the `~shareJs` relat
### Adding custom HTML
-You can inject custom HTML snippets into specific locations of the shared page using the `~shareHtml` relation. The HTML note should contain the raw HTML content you want to inject, and you can control where it appears using the `#shareHtmlLocation` label.
+You can inject custom HTML snippets into specific locations of the shared page using the `~shareHtml` relation. The HTML note should contain the raw HTML content you want to inject, and you can control where it appears by adding the `#shareHtmlLocation` label to the HTML snippet note itself.
The `#shareHtmlLocation` label accepts values in the format `location:position`:
- **Locations**: `head`, `body`, `content`
@@ -123,7 +123,7 @@ To do so, create a shared text note and apply the `shareIndex` label. When viewe
## Attribute reference
-Attribute | Description |
---|
shareHiddenFromTree | this note is hidden from left navigation tree, but still accessible with its URL |
shareExternalLink | note will act as a link to an external website in the share tree |
shareAlias | define an alias using which the note will be available under https://your_trilium_host/share/[your_alias] |
shareOmitDefaultCss | default share page CSS will be omitted. Use when you make extensive styling changes. |
shareRoot | marks note which is served on /share root. |
shareDescription | define text to be added to the HTML meta tag for description |
shareRaw | Note will be served in its raw format, without HTML wrapper. See also Serving directly the content of a note for an alternative method without setting an attribute. |
shareDisallowRobotIndexing | Indicates to web crawlers that the page should not be indexed of this note by: - Setting the
X-Robots-Tag: noindex HTTP header. - Setting the
noindex, follow meta tag.
|
shareCredentials | require credentials to access this shared note. Value is expected to be in format username:password . Don't forget to make this inheritable to apply to child-notes/images. |
shareIndex | Note with this label will list all roots of shared notes. |
shareHtmlLocation | defines where custom HTML injected via ~shareHtml relation should be placed. Format: location:position where location is head , body , or content and position is start or end . Defaults to content:end . |
+Attribute | Description |
---|
shareHiddenFromTree | this note is hidden from left navigation tree, but still accessible with its URL |
shareExternalLink | note will act as a link to an external website in the share tree |
shareAlias | define an alias using which the note will be available under https://your_trilium_host/share/[your_alias] |
shareOmitDefaultCss | default share page CSS will be omitted. Use when you make extensive styling changes. |
shareRoot | marks note which is served on /share root. |
shareDescription | define text to be added to the HTML meta tag for description |
shareRaw | Note will be served in its raw format, without HTML wrapper. See also Serving directly the content of a note for an alternative method without setting an attribute. |
shareDisallowRobotIndexing | Indicates to web crawlers that the page should not be indexed of this note by: - Setting the
X-Robots-Tag: noindex HTTP header. - Setting the
noindex, follow meta tag.
|
shareCredentials | require credentials to access this shared note. Value is expected to be in format username:password . Don't forget to make this inheritable to apply to child-notes/images. |
shareIndex | Note with this label will list all roots of shared notes. |
shareHtmlLocation | defines where custom HTML injected via ~shareHtml relation should be placed. Applied to the HTML snippet note itself. Format: location:position where location is head , body , or content and position is start or end . Defaults to content:end . |
## Credits