- 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
5.1 KiB
Embedding a 3D model (STEP / STL) in a note
The share can render .step / .stp / .stl models interactively (three.js + occt-import-js,
hosted locally). The viewer fills the available screen width (full-bleed, ~100vw) with a
height based on the viewport (min(70vh, 900px)), so standalone model pages use the maximum
available space instead of the readable content width. Inline viewers embedded in text
articles (Options B and C) are limited to the content width (model-viewer-inline). The caption below the model always shows
<file name> · <size> · <triangle count> (e.g. Wrench.stl · 711 KB · 14.6k triangles).
There are four ways to embed a model, depending on how you store the file.
Option A – Model as its own note (file note)
- Create (or upload) a file note and make sure its title ends in
.step,.stpor.stl, e.g.bracket.steporrear_housing.stp. - Put it under a category in the blog and set
publish=true. - The note's share page then shows the interactive 3D viewer automatically: drag to rotate, scroll to zoom.
Option B – Model embedded inline in a text note (attachment)
- Upload the STEP/STL file as an attachment of the note: drag & drop it onto the note, or use the attachment dialog (paperclip icon).
- Rename the attachment so its title ends in
.step,.stpor.stl(the viewer detects a model by the file name). Example:flange.step. - Insert the attachment link into the note body: right-click the attachment → "Insert link", or drag the attachment into the text.
- On the share, that link is automatically replaced by the embedded 3D viewer.
Option C – Model embedded inline from a file note (recommended for blog posts)
This lets you write blog posts that contain models inline, referencing a model that also exists as its own (published) file note — the model stays in one place.
- Create the model as a file note (title ends in
.step/.stp/.stl), publish it (see Option A). The viewer needs it inblog_data, i.e. it must be a published article reachable in the blog tree. - In your text note, add a link to that file note, e.g.
[Wrench.stl](#root/<noteId>)(markdown) or drag the note reference into the editor. - On the share, the link to a model file note is replaced by the inline viewer. You can embed several models per post this way.
Tip: STL (plain triangle meshes) is rendered with three.js only; STEP (CAD B-rep) is parsed by occt-wasm (OpenCASCADE V8, WASM, MIT/Apache-2.0) in a Web Worker (module worker, non-blocking). The first STEP view downloads the ~22 MB WASM once and then caches it in the browser. STEP tessellation is set to
linearDeflection: 0.4, angularDeflection: 0.2(≈480k triangles for the ignis battery module; tuned for mobile).
Option D – [cad] keyword (editor-safe, no HTML or links)
Plain-text keyword, robust against editor re-saves (unlike raw HTML embeds, which CKEditor strips):
[cad <id|title>] # e.g. [cad base] or [cad __MODEL_FILE_NOTE_ID__]
[cad <id|title>|small] # small | medium | large (viewer height)
[cad <id|title>|Own caption] # free text as caption
The key resolves through the cad index in window.geoData (built by blog_generator.js,
scans the blog tree for .stl / .step / .stp file notes; the extension may live in the
note title or #originalFileName). The model file note does not need its own publish
label — it only has to be inside the blog subtree. See docs/embed-keywords.md for the whole
keyword family (map, gpx, cad, yt, video, audio).
Requirements
- The
3d model viewerscript must be wired:~shareHtml → 3d model viewer(inheritable) on the blog root,shareHtmlLocation=body:end. - The five libraries must exist as notes and be referenced by the script via the
placeholder tokens in
scripts/3d_model_viewer.js:__THREE_ID__,__THREE_ORBIT_ID__,__THREE_STL_ID__,__OCCT_JS_ID__(the occt-wasm bundle),__OCCT_WASM_ID__(occt-wasm.wasm) – see the README installation for the note titles. The STEP worker is a module worker (type: "module"), so the browser must support ES modules in workers (all modern browsers). - Mobile compatibility: the mesh is split into chunks of ≤65000 vertices
(
chunkGeometry,Uint16indices) so it renders on GPUs withoutOES_element_index_uint; the camera near/far is tightened afterfit()for 16-bit depth buffers. - For inline embedding from a file note (Option C) the
blog_datanote must be present and the target model must be a published article (the viewer resolves./<alias>links againstwindow.blogData).
Trilium app widget (independent of the share)
The same viewer is also available inside Trilium itself (independent of the blog):
a note-detail-pane widget (app/3d-model-viewer-app.jsx + app/3d-worker.js) renders
the viewer whenever you open a file note whose title ends in .step / .stp / .stl.
The worker is loaded from a code note (not a Blob) so it works under the app's CSP.
Wire it by giving the JSX note the label #widget=3dModelViewer and reload the app.