feat(embeds): geo/gpx/3D/YouTube/media keyword system + docs, sanitized mirrors

- new snippet geo_map_render.js: [map] inline geomaps (tree order, tint,
  theme-following), [gpx] stats card + elevation profile with hover cursor
  (km from start, d-height, vertical guide), [yt] privacy click-to-load
  facade, [video]/[audio] inline players fed from the new geoData indexes
- 3d_model_viewer.js: [cad] keyword + title/ID resolution via geoData.cad,
  sizes/captions, real occt-wasm ESM pair (fixes 'STEP worker error'),
  code-span-safe keyword scan (docs can show examples)
- blog_generator.js: buildGeoData() v6 – maps (geolocation + geoShape
  point/line/polygon + child GPX tracks), gpx/cad/media indexes,
  branch-change rebuild for route reordering
- generated/geo_data.js placeholder; docs/embed-keywords.md (new) +
  embed-3d-model Option D; README layout/wiring/notes tables updated
- license: runtime components (MapLibre ISC, VersaTiles MIT/ODbL, YouTube)
- hygiene: removed leaked note IDs and instance domain from theme docs
This commit is contained in:
trilium-share-gruvbox
2026-09-28 14:53:51 +02:00
parent 9e456bbaaa
commit 2818c40f96
10 changed files with 933 additions and 28 deletions
+62
View File
@@ -442,5 +442,67 @@
a.replaceWith(el);
initViewer(el, "api/notes/" + art.id + "/download", mm[0]);
});
// ---- [cad <key>] keyword: inline viewer, key = title or noteId of an .stl/.step/.stp file note.
// Optional pipe suffixes: |small|medium|large for height, free text for a custom caption.
var RE_CAD = /\[cad\s+([^\]]+)\]/i;
var CAD_HEIGHTS = { small: "280px", medium: "440px", large: "620px" };
function scanCadKeywords() {
var content = document.getElementById("content");
if (!content) return;
var list = (window.geoData && window.geoData.cad) || [];
if (!list.length) return;
// Block text WITHOUT inline <code>/<pre> content (keyword examples stay literal).
function textExcludingCode(block) {
var out = "";
var w = document.createTreeWalker(block, NodeFilter.SHOW_TEXT, {
acceptNode: function (n) {
var p = n.parentElement;
while (p && p !== block) {
if (p.tagName === "CODE" || p.tagName === "PRE") return NodeFilter.FILTER_REJECT;
p = p.parentElement;
}
return NodeFilter.FILTER_ACCEPT;
}
});
while (w.nextNode()) out += w.currentNode.nodeValue;
return out;
}
content.querySelectorAll("p, h1, h2, h3, h4, h5, h6, li, blockquote").forEach(function (block) {
var m = textExcludingCode(block).match(RE_CAD);
if (!m) return;
var parts = m[1].split("|");
var key = parts[0].trim();
var size = "", title = "";
for (var i = 1; i < parts.length; i++) {
var p = parts[i].trim(), pl = p.toLowerCase();
if (pl === "small" || pl === "medium" || pl === "large") size = pl;
else if (p && !title) title = p;
}
if (!key) return;
var hit = null;
var kl = key.toLowerCase();
list.forEach(function (c) {
if (c.id === key || c.title === key || c.title.toLowerCase() === kl || (c.title + "." + c.format).toLowerCase() === kl) hit = c;
});
if (!hit) {
block.textContent = 'CAD-Modell \"' + key + '\" nicht gefunden.';
return;
}
var el = makeViewer(title || hit.title);
el.classList.add("model-viewer-inline");
if (size) {
var canvas = el.querySelector(".model-viewer-canvas");
if (canvas) canvas.style.height = CAD_HEIGHTS[size];
}
block.replaceWith(el);
initViewer(el, "api/notes/" + hit.id + "/download", "." + hit.format);
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", scanCadKeywords);
} else {
scanCadKeywords();
}
})();
</script>