Skip to content

Create modular javascript for bundling#2815

Draft
oscarlevin wants to merge 12 commits intoPreTeXtBook:masterfrom
oscarlevin:js-cleanup
Draft

Create modular javascript for bundling#2815
oscarlevin wants to merge 12 commits intoPreTeXtBook:masterfrom
oscarlevin:js-cleanup

Conversation

@oscarlevin
Copy link
Copy Markdown
Member

With the help of copilot I have a possible way forward on making the javascript modular and easier to maintain. I'm marking this as draft, since it definitely needs more eyes and testing, but from what I've seen, there are no functional changes to the sample article or sample book.

The idea is that we will maintain a js/src directory with modular files that get bundled by a esbuild function (see script/jsbuilder) This creates js/dist/pretext-core.js and js/dist/pretext-search.js (since we don't always provide the search javascript).

There was an initial attempt to remove jquery (and I think we removed the reliance on jquery from pretext_add_on's replacements), but I think maybe some runestone js requires us to have a copy of jquery? I don't really understand that.

So @rbeezer and @ascholerChemeketa, do you think this is the sort of thing we want? Any suggestions? When we are mostly happy with the new file structure I will remove the old files we no longer need and clean up all the commits.

oscarlevin and others added 12 commits April 2, 2026 08:17
- script/jsbuilder/PLAN.md: full plan for converting JS to ES modules,
  removing jQuery, and building bundles with esbuild (mirroring cssbuilder)
- .github/copilot-instructions.md: Copilot context for this repo

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Corrected line counts and jQuery status per file
- Documented dead/broken code (ptx_search.js, video magnify, knowl_focus_stack)
- Added modular structure: js/src/ with focused modules
- Reorganized phases: modularize first, then convert standalone files
- Added js/src/deprecated/ for flagged dead code

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Create script/jsbuilder/ mirroring the cssbuilder pattern:
- package.json: esbuild dependency, build/watch scripts, Node >=18
- jsbuilder.mjs: two bundle targets (pretext-core, pretext-search),
  CLI with --help, --list-targets, --watch, --output-directory
- README.md: usage docs and build target reference

Entry-point source files (js/src/*-entry.js) will be created in
Phase 4 after modules are extracted.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Break the 1,340-line monolith into 19 focused modules under js/src/:

Core modules:
- permalink.js: copy-to-clipboard for permalinks
- image-magnify.js: click-to-zoom image popup (jQuery removed)
- geogebra.js: GeoGebra calculator integration (jQuery removed)
- keyboard-nav.js: ESC/ENTER key handlers + anchor-knowl nav (jQuery removed)
- theme.js: dark/light mode switching
- embed.js: LMS embed button + embed display mode
- code-copy.js: code block copy button

Print-preview subsystem (9 modules in print-preview/):
- index.js: orchestrator for print preview setup
- pages.js: page creation and adjustment
- workspace.js: workspace height management
- headers-footers.js: editable header/footer management
- page-breaks.js: DP page-break algorithm
- geometry.js: page geometry CSS + unit conversion
- paper-size.js: letter/a4 detection via geolocation
- section-swap.js: printout section loading
- solutions.js: details-to-div rewriting

Deprecated modules (flagged for future removal):
- scrollbar-width.js: only used by GeoGebra
- auto-id.js: suspicious utility with heavy console spam
- video-magnify.js: dead code (selector matches nothing)

Bug fixes in extracted modules:
- keyboard-nav.js: add missing break after case 13 (ENTER),
  preventing fallthrough to ESC handler
- keyboard-nav.js: guard knowl_focus_stack reference with
  typeof check to prevent ReferenceError
- auto-id.js: replace jQuery .prevAll() with vanilla DOM traversal

jQuery removed from: image-magnify, geogebra, keyboard-nav, auto-id
(~35 calls total). Original pretext_add_on.js left intact until Phase 4.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Create ES module versions of the existing standalone scripts in js/src/:

- toc.js: TOC sidebar, scroll-to-active, focused/expandable items
  (from pretext.js)
- knowl.js: xref/born-hidden knowl behavior with slide animation
  (from knowl.js)
- lti-iframe-resizer.js: SPLICE iframe resize message handler
  (from lti_iframe_resizer.js)
- search.js: lunr-based search UI with result ranking and snippets
  (from pretext_search.js)

Each module exports init functions that register event listeners.
Original files are left intact so existing <script> tags continue
to work until Phase 5 updates the XSL.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Entry points:
- js/src/pretext-core-entry.js: imports all core modules (TOC, knowls,
  LTI resizer, permalink, image magnify, GeoGebra, keyboard nav,
  print preview, theme, embed, code-copy, deprecated modules)
- js/src/pretext-search-entry.js: imports search module

Built bundles (committed for users without Node):
- js/dist/pretext-core.js (59 KB) + sourcemap
- js/dist/pretext-search.js (9 KB) + sourcemap

Both bundles are IIFE format, unminified, with sourcemaps.
MathJax is marked as external (loaded separately).

Deleted js/jquery.min.js  no longer needed since all jQuery calls
have been replaced with vanilla DOM in Phase 2.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pretext-js template:
- Replace 3 script tags (jquery.min.js, pretext.js, pretext_add_on.js)
  with single dist/pretext-core.js

knowl template:
- Remove standalone knowl.js script tag (now in core bundle)
- Keep sagecell eval name inline script

lti-iframe-resizer template:
- Remove standalone lti_iframe_resizer.js script tag (now in core bundle)
- Template kept empty so callers don't break

native-search-box-js template:
- Load dist/pretext-search.js instead of pretext_search.js

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update copy_html_js() to exclude the js/src/ directory when copying
JS files to the build output. Only the pre-built bundles in js/dist/
are needed at runtime; the ES module source files are development-only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Expose isDarkMode() and setDarkMode() on window so inline XSL scripts
(mermaid init) can access them from outside the IIFE bundle closure.

Restore jQuery as a local file (upgraded to 3.7.1) for Runestone
compatibility. jQuery is loaded conditionally only when Runestone
JS bundles are present, keeping non-Runestone pages jQuery-free.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Print preview rewrites <details> born-hidden-knowl elements into <div>s
(removing <summary>), but keeps the class. When initKnowls() later
queries for .born-hidden-knowl > summary, it gets null and crashes.
Skip elements that have already been rewritten.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ascholerChemeketa
Copy link
Copy Markdown
Contributor

Thoughts - not trying to start discussion here necessarily, mostly so I remember.

Any appetite for moving raw source to typescript? IMO it goes a long way to reduce some sources of bugs related to type coercion between things like null, undefined, "", false, etc... If there was, seems like a logical time to introduce.

Integration with themes? There isn't tons of theme specific JS. So don't think we need to do anything at this stage. But if a theme needs specific JS for handling UI elements, it would be nice to only include that when necessary (especially if different themes have conflicting requirements). What might that look like? Sidecar files? Monolithic self contained single js file for each theme?

Definitely not a part of this PR, but IMO PTX should not have to inject JQuery for RS. RS should be bundling that itself.

@rbeezer
Copy link
Copy Markdown
Collaborator

rbeezer commented Apr 2, 2026

Yes, I would be in favor of modernization and bundling. pretext_add_on is not a very professional looking file anyway! Not much I can do as far as a review or an opinion (I did read the first commit with the plan, etc.)

I see your CoPilot and raise you by a Claude Code. Next.

@rbeezer
Copy link
Copy Markdown
Collaborator

rbeezer commented Apr 2, 2026

This is a significant infrastructure change — modularizing the JavaScript, removing jQuery from core, and introducing an esbuild-based build system. Some observations:

Architecture. The approach mirrors the existing CSS build system (script/cssbuilder/ -> css/dist/), which is good for consistency. The entry point pattern (pretext-core-entry.js imports focused modules, esbuild bundles to an IIFE) is clean and the jsbuilder tool with watch mode will be useful for development.

jQuery removal. jQuery is removed from core and only loaded conditionally for Runestone builds (pretext-runestone.xsl line 227-229). The jQuery file itself was updated from 3.3.1 to 3.7.1 — presumably for security, but worth noting since it's not mentioned in the PR description. The core bundle uses vanilla DOM throughout.

Old files not removed. The original files (pretext.js, pretext_add_on.js, knowl.js, lti_iframe_resizer.js, pretext_search.js) are still in the repository. The PR description mentions "When we are mostly happy with the new file structure I will remove the old files" — but these should definitely be removed before merge. They're no longer loaded (the XSL now points to dist/pretext-core.js and dist/pretext-search.js) and leaving them creates confusion about which files are authoritative.

Built artifacts in the repo. The js/dist/ directory contains bundled output (pretext-core.js at 1615 lines, pretext-search.js at 263 lines) plus source maps (140KB total). Committing built artifacts is a trade-off — it means users without Node.js can still build PreTeXt, but it creates a risk of the dist files drifting out of sync with the source modules. The CSS build system already sets this precedent, so this is consistent. The source maps might not need to be committed to the repo though, since they're only useful for debugging in a browser dev console.

Copilot instructions file. The PR adds .github/copilot-instructions.md (88 lines). This is GitHub Copilot configuration for the repo, not part of the JS modernization itself. It should be a separate PR if it's wanted — and it's worth a broader discussion about whether the project wants AI tool configuration in the repository.

PLAN.md. script/jsbuilder/PLAN.md (203 lines) documents the modernization phases and dead code analysis. This is useful context during development but probably shouldn't be merged — it's process documentation, not reference documentation. The README.md in the same directory is more appropriate for long-term inclusion.

lti-iframe-resizer template is now empty. The lti-iframe-resizer named template (line 10876) had its <script> tag removed since the code is now bundled, but the empty template shell remains. It's still called at line 11214. This is fine — the template serves as a no-op placeholder — but a comment explaining why it's empty (beyond the existing one-liner) would help future maintainers who might wonder if something was accidentally deleted.

The knowl.js comment reference. Line 13814 still says knowl.js code controls Sage Cells within knowls. This comment should be updated to reference the new location.

The knowl module was substantially rewritten. Comparing the old knowl.js (338 lines, vanilla DOM with manual animation) to the new js/src/knowl.js (337 lines), the new version introduces a SlideRevealer class with a proper state machine. This is a functional change, not just a modularization — it changes how knowl animations work. The PR description says "there are no functional changes" but this should be verified carefully, especially the animation behavior and the edge cases around nested knowls and MathJax re-typesetting.

Dead code is preserved with deprecation flags. The deprecated/ subdirectory contains auto-id.js, scrollbar-width.js, and video-magnify.js. These are still imported and executed in the entry point (lines 80-83 of pretext-core-entry.js). If they're truly dead, they should not be bundled at all — importing dead code adds bundle size and potential for runtime errors. If there's uncertainty about whether they're dead, that's fine to note, but they shouldn't run on every page load.

Responding to @ascholerChemeketa's comment about TypeScript: that would be a separate decision and probably best deferred until the modular structure settles. The current JS modules are a good foundation that TypeScript could be layered onto later.

Claude Opus 4.6, acting as a review assistant for Rob Beezer

@bnmnetp
Copy link
Copy Markdown
Contributor

bnmnetp commented Apr 2, 2026

Removing jQuery will break the interactive components in static html builds.
This would be project on the runestone side as the Sphinx build system always include jQuery in a runestone build.

I'm sure it can be done, but I think I would rather put my efforts into the continued removal of jQuery from the components themselves and once that is complete then you would not need to include it anywhere.

Brad Miller, acting like he knows what he's talking about.

@oscarlevin
Copy link
Copy Markdown
Member Author

Thanks everyone. I am 100% on board with moving toward typescript (that's what I'm using for pretext tools and such).

The various comments about old files and the plan.md and copilot-instructions.md, I totally agree that they will go away before this is merged.

I'll take a close look at the knowl.js rewrite and make sure that is for the best.

So I guess that if that gets resolved, then this is likely good to go. I'll try to get this cleaned up this weekend.

@rbeezer
Copy link
Copy Markdown
Collaborator

rbeezer commented Apr 3, 2026

@oscarlevin - I am battling LaTeX, MathJax 3, and MathJax 4 today. Slow going and subject to big changes at the least surprise. :-( But to your point, I have branch which incorporates #2180 - adding a new *.js and removing mathjaxknowl3.js. But I think it needs to sit and wait for mid-May. Not sure how disruptive that is for you right now?

@oscarlevin
Copy link
Copy Markdown
Member Author

I don't think that change would be difficult to incorporate (this switch leaves mathjaxknowl3.js alone, I think).

While modularizing should not have any effect on users, it will make development a lot easier. So I would recommend we take care of this sooner rather than later. That said, I can redo this work again later if you think it should wait.

@rbeezer
Copy link
Copy Markdown
Collaborator

rbeezer commented Apr 3, 2026

Just a heads-up, I would not, was not, suggesting a pause until mid-May. Unless my change was going to be a headache, otr something. Carry on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants