Skip to main content

PDF Viewer

It pulls in the Custom HTML module for you​

The /PDF Viewer component is built around the Custom HTML node (module.inlineHtml), which this entry does not contain. That node comes from the standalone Custom HTML module, and library.json now says so in a field the installer reads:

"dependencies": ["modules/custom-html"]

Installing PDF Viewer installs Custom HTML first, in the same click, without asking you to know that. You will see one consent prompt — Custom HTML ships executable module code, PDF Viewer does not — and, if you already have Custom HTML in the project, the usual import screen for the collision.

On an editor older than this field (anything that predates NodeGX 0.2.2) the dependencies key is ignored and only PDF Viewer installs. The symptom is a red dashed Custom HTML placeholder inside /PDF Viewer and, at runtime, Can't find component model for module.inlineHtml. The fix is to install Custom HTML from the Modules library by hand; order does not matter.

Using it​

Place the /PDF Viewer component and set:

  • PDF URL — the URL of the PDF to display. It is URL-encoded for you and handed to Google's document viewer (docs.google.com/viewer) inside an <iframe>, so the URL must be publicly reachable — the viewer runs on Google's servers, not in your app.
  • Width / Height — the size of the viewer group.

A sample PDF URL is pre-wired as the default.


Why this entry has a dependency instead of a copy (LBR-007)

This is written down so nobody re-derives it. Phase 65 shipped this entry in a state where it could not work: it referenced a node type nothing installed, and the only place the requirement was recorded was prose — this README and the description on the library card. Prose is not an installer. Two shapes of fix were on the table.

Rejected: fold custom-html back into pdf-viewer​

This is what the entry used to do, and it was removed for a reason that has not gone away: a bundled copy and the standalone module both land in noodl_modules/custom-html-module/ in the installing project, and whichever installs second overwrites the first. A user with both entries installed does not have two modules, they have one module of indeterminate version, and no surface anywhere tells them which. The duplication is not the objection — the silent overwrite is.

It also does not generalise. The moment a second entry wants to share something (a user-menu prefab that wants the avatar prefab; anything that wants a shared icon set) the answer is another copy, and the copies drift. The library had one entry with this problem and was about to have a second.

Chosen: a dependencies field with a consumer at both ends​

🔴 A dependency field nothing reads is worse than no field at all — that is the standing rule in this repo, and it is why runtimeVersion is documented as descriptive provenance rather than gated (see packages/noodl-editor/src/editor/src/models/moduleCompatibility.ts). So the field was only worth adding if the whole path existed. It does, and it is a two-stage path:

StageWhereWhat it does
Authorlibrary/<type>/<slug>/library.json"dependencies": ["modules/custom-html"] — type-dir + slug, human-writable, cross-type.
Validatescripts/library/schema.jsonThe field and its "^(prefabs|modules)/…" item pattern. library:check and library:build both compile this schema, so a typo is a build failure, not a runtime surprise.
Resolvescripts/library/build.js → resolveDependencies()Resolves each slug against the other entries on disk, fails the build on an unknown slug, a self-reference or a cycle, flattens the transitive closure into dependency-first order, dedupes, and writes resolved descriptors ({key,label,type,project,version,minEditorVersion}) into index.json.
ConsumeModuleLibraryModel._installWithDependencies() → _installDependency()Walks that flat list in order and installs each descriptor through the same _install a user's click uses, then installs this entry.

Why resolution is a build-time job. Shipping the raw slugs and resolving in the editor would mean deriving a slug back out of library/modules/custom-html-1.0.2.zip, requiring the other tab's index to have been fetched before a module could pull in a prefab, and putting a graph walk in the click path. Resolving at build time makes the editor's half a for loop, and makes an unresolvable dependency a loud failure of npm run library:build — before anything is published — instead of a quiet one in a user's editor.

Why a failed dependency aborts the install. Installing the dependent alone is not a degraded success; it is exactly the red dashed placeholder this field exists to abolish, arrived at more expensively. _installDependency re-throws with a sentence naming both ends.

Why the picker block moved. _install used to take onBeforePopup/onAfterPopup and hold them across its own body, because two modals each taking and releasing the block would unblock the node picker in the gap between them — the moment a second click starts a second install. A dependency chain is that same gap one level up, so the hooks now live on _installWithDependencies and are held across the whole chain, and _install no longer accepts them at all.

Backwards compatibility. The field is additive. An editor that predates it ignores it and installs one entry, which is precisely today's behaviour — which is why this README still documents the manual install order above, and why the card's description still names Custom HTML.

🔴 Do not delete project/noodl_modules/README.md​

That directory is empty of module code on purpose, but its existence is what scripts/library/check.ts reads as providesNodes — the flag that exempts an entry from the FH-023 "no node has lost its type" rule. /PDF Viewer contains a module.inlineHtml node whose type is registered by a different entry, so without that directory npm run library:check fails this entry on a node that is correct. The honest rule would be "provides nodes or declares dependencies", and check.ts should grow it; until then the placeholder directory is load-bearing.