Extensions: modules installed from outside the app, and where their numbers come from discussed
I02 · an idea of the seed-place, on its way to the app.
The idea
The option to add extensions, as the TRAE IDE does: a button to search and import them, and a place for each in the left vertical bar. This asks the core to be opened so that extensions connect to it; or extensions could be small cores of their own, written in Rust, that do their calculations and connect to the app to show the results.
Assessment
What exists. The app is, by design, "an extensible umbrella of modules" (app/DESIGN.md, R20–R23, L060): a module is a folder declared once, with its id, name, icon, commands, menu, views, sidebar, inspector, bottom panel, manual entry and Settings pane (app/ui/modules/index.js), and the shell's services are what every module draws with (plot.js, table.js, timeslider.js, canvas3d.js, registerPane, L287). Publish, Explore and Plots are being built by their own streams against that interface, in their own folders: they are extensions in all but packaging. The first half of the idea, the loader, the manifest, the button and the place in the activity bar, is therefore small.
The hard half is Josu's question, and L139 answers it. The app computes nothing: every number it shows comes from the core (L139, a must). An extension cannot be the exception, and it need not be. Two kinds follow.
- A view extension shows the core's results in a new way: a new plot, a table, a 3D scene. It runs in the webview, in JavaScript, talks to the core only through the bridge the modules use, and computes nothing. It is a module in a folder outside the bundle.
- An engine extension computes, and is a program of its own: Josu's "small Rust cores". The app launches it and talks to it over a protocol; every number it returns is shown stamped with the extension's name and version, and written into the results file with that provenance (pdCIF, L099). It need not be Rust: the contract is the protocol and the files. In Rust it depends on the
drscrate and reads the model directly; in Python it reads the control file (.drc, L066), the data (the formats registry) and the results (pdCIF), which are already the core's public surface. JSON over stdio, as the Language Server Protocol does, is enough and needs no bindings; the Python bindings planned for version 2 (P48) make a Python engine faster, not possible.
"Opening the core" means two things that are already partly true: the crate drs with its command line is the library an engine links, and the files are the interface a program in any language reads. What is missing is the promise: a public API surface with a version (semver) that an extension names in its manifest ("core \(\ge 0.50\)"), and the module interface frozen as version 1. That promise is the real cost of the idea: once extensions exist, the module interface and the file formats stop changing freely, and the six streams gain a seventh contract to respect.
Precedents. VS Code runs extensions in a separate Extension Host process, each with a manifest (package.json) and a marketplace; themes and grammars are extensions without code (its documentation). TRAE is a fork of VS Code with its own marketplace (believed, not checked). In scientific software: QGIS's plugins and Blender's add-ons are Python, Fiji's plugins are Java, Mantid's algorithms are Python (their documentation, not re-read today). Among the Rietveld programs this project studied, FullProf has no extension mechanism and GSAS-II has scripting (GSASIIscriptable, L043) but no installable modules; TOPAS has its macro language; Jana2020 was not checked. An extension mechanism with provenance would be the first among them, as far as this project knows.
Risks. An engine extension runs code on the user's Mac: install asks, the manifest says what it reads and writes, a checksum is checked at install; a binary downloaded to Application Support carries macOS's quarantine and may need to be cleared or signed (believed, not checked on the Mac). An extension's numbers are its author's: the provenance stamp is what keeps a paper honest. And the stream model: an extension by a third party asks the core for things through the same REQUESTS.md path, or it forks; the registry says which core version each was tested on.
Proposal
Not before version 1's module interface has been used by Publish, Explore and Plots end to end; then, in steps, each a release Josu tries.
- Step 1. The manifest. Each of the bundled modules gets one (id, name, version, kind, what it needs: core \(\ge\), shell services), and the shell loads them through it, from inside the bundle. No behaviour changes: the step proves the interface is complete, and freezes it as version 1.
- Step 2. A module loaded from a folder in Application Support: Settings ▸ Extensions lists them, enable and disable, and an enabled extension takes its place in the activity bar, in the user's order (C2).
- Step 3. Themes as code-less extensions (I01, step 4): the install path exercised with nothing to run.
- Step 4. The engine protocol, with one engine extension as the reference. The inference crate proposed in L286 (
drs-infer, besidedrs-publish) is the natural first: a separate crate by design, stochastic, kept out of the bit-exact core, whose results the app only draws. Its provenance in the results file is the test. - Step 5. The registry: a public repository with an index (name, version, kind, author, core version tested, URL, checksum); the button at the bottom of the activity bar opens Extensions: search the index, install from it, from a URL or from a folder.
For Josu to decide
- Whether an extension may compute. Proposed: yes, as an engine extension, a program of its own, with its name and version stamped on every number and in the results file (L139 kept: the app still computes nothing).
- The language of engines: any, by the protocol and the files, Rust first since it links the crate; or Rust only.
- Whether this waits for version 1 (the four reference refinements reproduced, L020) or starts with step 1 as soon as Plots and Explore are on the shell's services.
- Whether drs-infer (L286) is the first engine extension, which ties the two decisions together.
The idea's log
- 2026-10-15 · said
Said by Josu in the ideas chat: extensions as TRAE offers them, with a search button and a place in the activity bar; the core opened to them, or small Rust cores connected to the app.
- 2026-10-15 · discussion
Assessed: the module interface and the shell's services are the extension interface in embryo; two kinds (view, engine) follow from L139; the cost is the frozen interface and the versioned core; five steps proposed, drs-infer as the first engine. L288