Extensible Bible translation software
Platform.Bible is extensible Bible translation software. Its functionality is provided almost completely by extensions in order to be very powerful and flexible, giving developers the freedom to create and to share their desired Bible translation experience.
This repository contains the core Platform.Bible software (Electron client, extension host including the Platform API (PAPI), and .NET library) and the extensions that come bundled with it. There are many other repositories containing additional extensions.
This repository does not distribute a built application. The application released to users is Paratext 10, which is built from this source in paranext/paratext-10-studio; see LICENSING.md for how the source and the released binary are licensed. To try Platform.Bible itself, build it from source with the developer install instructions below.
To install a locally created snap package, run the following commands:
sudo snap install <path to snap file> --dangerous
sudo snap connect platform-bible:dot-platform-bibleSome users may find that not everything works properly in Linux without some additional setup. Please see How to set up Platform.Bible on Linux for more information.
If you download and run the ARM release of Platform.Bible from a computer running Apple Silicon, you will likely encounter a warning from Apple's Gatekeeper stating that "Platform.Bible is an app downloaded from the Internet. Are you sure you want to open it?":
Don't be alarmed! This is a typical warning, seen when downloading most apps from the Internet. It also says "Apple checked it for malicious software and none was detected." If you trust Platform.Bible and would like to run it select "Open."
Once the app is opened, you may see a dialog that says "Platform.Bible wants to use your confidential information stored in 'Platform.Bible Safe Storage' in your keychain."
We use encryption to keep your data safe, allowing access to keychain provides encryption services with papi.dataProtection so extensions can store user data securely. If you approve of this, please allow keychain access.
We strongly recommend you use Volta with this repo to automatically use the right version of tools such as node, npm, and pnpm for each relevant repo.
If you don't use Volta, you need to look at the volta property in the package.json of this repository and each development package repository to see the right tool versions to install in your preferred way.
Set up pre-requisites, build, and run:
-
Install the version of
Node.jsthat matches the version specified inpackage.jsonatvolta.nodeand any other necessary package managers. We strongly recommend using Volta to detect and install all necessary JavaScript package managers automatically. If you would like to install them manually, see the JavaScript Tool Manager section for more information on how to determine which JavaScript package managers are required and at which versions. -
Install .NET 8 SDK.
To check if
dotnetis installed run:dotnet --version # 8.0.412 [/usr/local/share/dotnet/sdk] (or some other directory) dotnet --list-sdks # 8.0.412 (or similar 8.* version)
-
Ruby >= 3.2 and Bundler - only needed to regenerate
THIRD-PARTY-NOTICES.md, which is done on Linux (see Third-party notices). Not needed to build or run the application. On Ubuntu 22.04:sudo snap install ruby --classic --channel=3.4/stable. On 24.04+:sudo apt install ruby-full. Then:gem install --user-install bundler bundle install # installs licensee, pinned in Gemfile.lock -
Install
gitleaks. The pre-commit hook runsgitleakson staged files to block accidental secret commits — without it, everygit commitfails.- macOS:
brew install gitleaks - Windows:
winget install Gitleaks.Gitleaks - Linux: download a prebuilt binary from the releases page and place it on your
PATH(e.g.,~/.local/bin/gitleaks), orsudo apt install gitleakson Ubuntu 24.04+.
To verify:
gitleaks version
- macOS:
-
Prerequisites for macOS or Linux (below).
-
Clone, install, build, and run (below).
-
Add the system libraries needed for Electron, Build Instructions (Linux). While those are the official instructions they aren't as clear as they used to be - adding these libraries work (this list taken from stagePackages):
# Ubuntu-22/Debian sudo apt-get install libappindicator3-1 libasound2 libdrm2 libdrm-nouveau2 libgbm1 libgl1 libgtk-3-0 libnspr4 libnss3 libsecret-1-0 libtinfo5 libxss1 -
If you want to build the app package locally (i.e. by running
npm run package) also run these commands.
If you are developing on Ubuntu 24.04 with AppArmor, you will need to modify package.json locally to add the --no-sandbox flag to electronmon. For example:
{
"scripts": {
"start:main": "concurrently -k -P \"cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --watch --config ./.erb/configs/webpack.config.main.dev.ts\" \"electronmon --no-sandbox . {@}\" --"
}
}This should be a temporary workaround until we update to newer versions of electron and electron-builder.
If you want your development Platform.Bible to handle the opening of platform-bible:// URIs, such as for completing authentication from a web browser, do the following to install a development Platform.Bible .desktop launcher.
- From the root of the repository, use these commands to copy the launcher into your local
applications launchers list, replacing REPO_PATH with the path to your source repository, and
update:
perl -p -e "s#REPO_PATH#$(pwd)#g" platform.bible-dev.desktop > \ ~/.local/share/applications/platform.bible-dev.desktop update-desktop-database ~/.local/share/applications/
- Set the newly installed .desktop file as the handler for platform-bible:
xdg-mime default platform.bible-dev.desktop x-scheme-handler/platform-bible
- Optionally check that the handler is set:
xdg-mime query default x-scheme-handler/platform-bible
- After starting Platform.Bible, you can manually check how it handles the links by running:
xdg-open platform-bible://test
This allows platform-bible:// handling by an existing Platform.Bible process. More work may be needed to successfully handle platform-bible:// if Platform.Bible is not already launched.
macOS doesn't come preinstalled with all the icu4c libraries. They must be installed separately to provide Unicode support to our .NET code. Platform.Bible is configured to expect those libraries to be installed using MacPorts. The icu package on MacPorts has the icu4c libraries needed for icu.net to run properly.
The build processes are configured to automatically download and package icu4c libraries with the application, but for development this has to be done manually.
The .NET data provider is configured to automatically copy the icu4c dylibs into
its build output directory. If for some reason you need to disable that, you will
need to set an environment variable for the OS to find them. For example:
export DYLD_FALLBACK_LIBRARY_PATH="$HOME/lib:/usr/local/lib:/usr/lib:/opt/local/lib"If you need to set environment variables like the above, consider adding them to
your .zprofile so you don't have to remember to do it manually.
Clone the repo:
git clone https://github.com/paranext/paranext-core.git
cd paranext-coreIf you want to develop an extension for Platform.Bible, check out the Git reference (usually a tag) corresponding to the version for which you want to develop your extension. We recommend you develop for the latest released version, not main. Consult the version table for more information. For example, if you want to develop your extension for version 0.3.0, run the following:
git checkout v0.3.0If you are developing paranext-core itself or developing extensions based on a development version of paranext-core like main (as opposed to developing based on a specific tag), you must also have some local development packages set up. npm install in paranext-core should do this automatically for you; you can skip this section if you do not have particular requirements for these local development packages. We develop paranext-core based on specific branches of some dependent repositories, so npm install makes them available for paranext-core to find if they are not already in the expected locations on your computer. If you are developing extensions based on a specific release of Platform.Bible, no further action is necessary; you can skip to Install and Build.
paranext-core looks for local development packages first as sub-directories of the dev-packages folder (e.g. paranext-core/dev-packages/scripture-editors). If it cannot find the appropriate directories there, it looks in the repo's parent folder (e.g. paranext-core/../scripture-editors).
npm install will clone and install the following repositories in dev-packages at the revision listed in dev-packages.json if they are not in one of the expected directories as mentioned above:
Running npm install in paranext-core builds development versions of the npm packages @eten-tech-foundation/platform-editor and @eten-tech-foundation/scripture-utilities from scripture-editors and stages them into dev-packages/staging/. This repo's package.json files depend on those staged folders with file: specifiers, so npm install reads each staged package's own manifest and installs its dependencies into this repo's tree. The staged copies are symlinked into node_modules, so a rebuild is visible immediately without reinstalling.
scripture-editors commits its built dist/, so this is a copy rather than a build: you do not need its toolchain — no pnpm, no nx — to run Platform.Bible. A build only happens if you are editing the editor yourself (npm run build:editor) or your checkout predates the committed build.
This means scripture-editors is free to add, bump, or drop its own dependencies without this repo restating them, and nothing here resolves the editor from the npm registry.
Fresh clones just work: on the very first npm install, the staged packages are created
during the install itself — after npm has already resolved the dependency tree — so the install
automatically runs a second pass to pick up their dependencies. You will see a message about this;
no action is needed. (npm ci doesn't need the second pass at all: it installs the staged
packages' dependencies from package-lock.json.) Repeat installs skip the editor build entirely
when the staged copy is already current, so this cost is paid only when scripture-editors
actually changed.
When scripture-editors' own dependencies change (its package.json, not ours), this repo's
package-lock.json must be refreshed to match: run npm install here and commit the lockfile
change. Until that lands, npm ci and CI builds fail with a message pointing at exactly this
paragraph's fix — loudly, not with stale packages. The check on scripture-editors'
platform-yalc branch watches for this so the person updating that branch is reminded to open the
core PR.
Note: npm install will check out the revision of scripture-editors specified in dev-packages.json (it will throw and ask you to do something with your working changes if you have any so your changes don't get messed up). However, the general expectation is that this revision for the scripture-editors repository is the branch named platform-yalc when this repo is on main and release-prep when this repo is on release-prep. platform-yalc exists so a breaking change can land on scripture-editors' main before build servers pick it up; move it forward only once this repo is ready for it.
To make your local development and build servers use changes from scripture-editors/main, use that repo's own script rather than moving the branch by hand:
# from a clone of scripture-editors, with platform-yalc checked out and a clean tree
npm run move-platform-yalcIt resets your local branch to origin's state, rebases it onto origin/main, checks that this repo's package-lock.json still matches the editor's dependencies, and force-pushes only if that passes — so a move that would break every core build stops before the push rather than after it. -- --dry-run stops short of pushing; -- --skip-verify is the emergency hatch. Give it a token first (export GITHUB_TOKEN=$(gh auth token)) — the check reads this repo through the GitHub API and the unauthenticated budget is 60 requests an hour.
Moving the branch by hand skips all of that — most importantly the lockfile check, which is the one failure that breaks every build here rather than only the mover's.
To manually set up scripture-editors to be staged locally (this should all be done automatically during npm install):
-
Clone
scripture-editorsas a sibling to this repo (alternatively, clone it inparanext-core/dev-packages):# from paranext-core cd .. git clone https://github.com/paranext/scripture-editors.git scripture-editors # optionally checkout the platform-yalc branch to match the build servers cd scripture-editors git checkout platform-yalc # go back to paranext-core cd ../paranext-core
-
Run
npm installin this repo. The preinstall script stages each package fromscripture-editorsintodev-packages/staging/, building it only when the pinned revision has no committeddist/to copy.Only that build needs
scripture-editors' own dependencies, so install them only if you are editing the editor (if you are using Volta, you must set the environment variableVOLTA_FEATURE_PNPMto1):# from paranext-core cd ../scripture-editors pnpm install cd ../paranext-core
A sibling checkout is used and moved, not just read. When ../scripture-editors exists, npm install here stages from it rather than from its own clone under dev-packages/ — and staging brings that checkout to the pinned revision, fetching and switching branches in it. It refuses to touch a checkout with uncommitted changes, and leaves one alone with a warning when it is on a branch of your own or parked on a detached commit; a clean checkout on main or on the pinned branch is moved. If you would rather keep your own clone out of this entirely, move it somewhere that is not a sibling of this repo and let npm install manage its own under dev-packages/scripture-editors.
Pick by what you need the change to show up in:
| You want to | Run, in this repo |
|---|---|
| see it in the running dev app | npm run build:editor |
| run tests, typecheck, or a production build | npm run stage-dev-packages -- --local |
| pick up a change to the editor's own dependencies | commit in scripture-editors, then npm install |
--local is the whole difference between the first two and a normal install: it stages your working tree, uncommitted changes included, instead of the revision dev-packages.json pins. npm run build:editor is that plus a webpack DLL rebuild, and the dev app needs both halves — the dev renderer serves @eten-tech-foundation/platform-editor out of the DLL, so staging alone leaves the app running the old editor while your tests and lint pass against the new one. Restart the dev server afterwards.
The dependency case is the odd one out because npm, not this repo, has to install whatever the editor now declares, and that only happens on a full npm install — which stages a committed revision and refuses to run against a checkout with uncommitted changes. So commit in scripture-editors first; committing on a branch of your own is enough. (A lockfile built from uncommitted editor work would record a dependency closure nobody else can reproduce, which the pre-commit hook blocks you from committing anyway.)
npm install leaves your editor checkout alone unless it is somewhere nothing is being kept — a clean checkout on main, or on the pinned branch itself, which is force-pushed by design. On a branch of your own, or parked on a detached commit, it warns and stages what it finds rather than moving you. So developing both repos at once works the way you would expect: keep your editor branch checked out and this repo builds against it.
Where it does move your checkout, it never discards commits. Anything on the branch that the remote does not have is parked on a refs/stage-rescue/… ref first, and the install prints how to get it back — at the end of its own output as well as when it happens, so it is not lost in the middle of a build. Nothing deletes those refs, so the notice repeats on every install until you deal with the commits and git update-ref -d the ref.
npm run verify:dev-packagespackage-lock.json records each staged package's version and dependencies, but the packages themselves come from the branch dev-packages.json pins, which moves independently of this repo's commits. A checkout of this repo can therefore stage an editor its own lockfile does not describe.
A plain npm install here makes this check on its own and repairs what it can. This command is the check by itself, for the case that cannot: a consumer repo. Every repo that builds against this one installs it with npm ci --ignore-scripts — required, because this repo's postinstall builds an Electron DLL they have no use for — and that skips the check along with everything else. Run it in this repo's directory right after that install:
# in the paranext-core checkout, after: npm ci --ignore-scripts
npm run verify:dev-packagesIt reads the tree and reports; it never installs or repairs anything. It needs Node 22.18 or later, like stage-dev-packages — below that, invoke it the same way you invoke that one (node --experimental-strip-types .erb/scripts/postinstall.ts --check).
Install dependencies:
npm installTo build, run the following:
npm run buildStart the app in the dev environment:
npm startAfter you run npm start (or, in VSCode, launch Debug Platform), you can edit the code, and the relevant processes will hot reload.
Dev builds are cached under node_modules/.cache. If you ever suspect a stale bundle, npm run clean:build-cache clears every cache in that directory (Storybook's and the extensions' included) and the next build repopulates them.
npm start runs the .NET data provider under dotnet watch, which restores and builds the project before the provider's Main() runs — 15-24 seconds of dev startup, depending on how warm the MSBuild and Roslyn servers are (measured on one machine; reproduce with the Startup performance timing tooling below). If you are not editing C#, you can skip it:
npm run start:no-dotnet-watchThis runs the already-built assembly instead, which takes roughly 3 seconds off the time to a fully loaded app and about 15 seconds off how long the data provider takes to become ready.
The script is a thin wrapper that sets PT_DOTNET_NO_WATCH=true. The value must be exactly true; any other value (including other truthy-looking strings) is ignored and the watcher runs as normal. If you start the app some other way, set the variable directly — this is the only handle you have when the npm script is out of reach:
PT_DOTNET_NO_WATCH=true npm startIn VSCode, use the Debug Platform (no .NET watcher) compound instead of Debug Platform — it is the same configuration with PT_DOTNET_NO_WATCH already set. (Debug Platform Backend runs npm run start with a fixed env block, so it cannot pick up the npm script, which is why this ships as its own entry rather than something you edit.)
The trade-off is that C# changes are no longer picked up. After editing C# you have to run npm run build:data yourself and then restart the app — the provider is started once and never re-spawned in place, so a rebuild alone will not reach it. Otherwise the app keeps running the previous build: it starts and behaves normally, just against older C# code, which is why the startup log says explicitly when this mode is active. You also need to have built the provider at least once before the script will work at all.
Platform.Bible core extensions are found in the extensions folder. Please follow the instructions in
extensions/README.md to develop core extensions.
Please see the Extension Template wiki for guides on developing additional extensions that are not part of the Platform.Bible core. Once you have packaged an extension, it can be distributed for Platform.Bible users to install. See Running your extension in an app for installation information.
Platform.Bible can emit startup timing marks across all four processes (main, renderer, extension host, and the .NET data provider) so you can profile where cold-start time goes. Marks are off by default - when disabled, each mark call is a single boolean check, so calls are safe to leave in the code.
To capture a run, set the PT_STARTUP_MARKS=true environment variable when launching. The main process forwards it to the renderer automatically, and the .NET provider reads the same variable:
# Dev (quick, but see the caveat below - dotnet watch distorts the .NET timings)
PT_STARTUP_MARKS=true npm start
# Packaged build (representative numbers - use these for comparisons)
PT_STARTUP_MARKS=true ./release/build/linux-unpacked/platform-bible \
--no-sandbox --remote-debugging-port=9223Every process writes its marks to the Electron main.log. Render them as an ordered waterfall from the repo root:
npm run startup-waterfall # reads the default main.log for your platform
npm run startup-waterfall -- --log=/path/to/main.log # or point at a specific capture
npm run startup-waterfall -- --help # usageNotes:
- Use a packaged build for numbers you intend to compare. In dev, the .NET provider runs under
dotnet watch, which inflates the C# portion of the timeline and is not representative. main.logaccumulates across launches; the tool shows only the latest run and warns when it drops older ones. For a clean capture, delete (or copy aside) the log between runs.
Platform.Bible's analytics abstraction (src/extension-host/services/analytics.service.ts) normally decides whether events target the "test" or "production" analytics audience by checking whether the build is packaged and which Send/Receive server it's configured against. Developers and testers who need to force "test" targeting regardless of build/server configuration — so their activity never lands in production analytics — can set the PT_ANALYTICS_TEST_OVERRIDE=true environment variable when launching:
PT_ANALYTICS_TEST_OVERRIDE=true npm startThe value must be exactly true; any other value (including other truthy-looking strings) is ignored and normal resolution applies.
Platform.Bible API Documentation
- Explore the declarations of types available on the PAPI.
Platform.Bible React Components and Hooks Documentation
- Check out the React components and hooks available to use.
Platform.Bible Utilities Documentation
- Check out the utility functions, types, and classes available to use.
Platform.Bible and Paratext 10 Studio Wiki
- Explore links to other resources relevant to Platform.Bible and Paratext 10 Studio.
To package apps for the local platform:
npm run packageThese steps will walk you through releasing a version on GitHub and bumping the version to a new version so future changes apply to the new in-progress version.
-
Prepare each repository in your
dev-packages.jsondepending on what kind of release you are publishing:- Release candidate, alpha, etc.: rebase
release-preponmainif it has not been rebased already for this release cycle. - Full release: create a release of that repository, then set its
revisionindev-packages.jsonto that release's tag. These packages are not published to npm — this repo stages them from a checkout of that revision — so a release is pinned by naming its tag here.
- Release candidate, alpha, etc.: rebase
-
Make sure the versions in this repo are on the version number you want to release. If they are not, manually dispatch the Bump Versions workflow or run the
bump-versionsnpm script to set the versions to what you want to release on the branch you want to release from. -
Manually dispatch the Publish workflow in GitHub Actions targeting the branch you want to release from. This workflow creates a new pre-release for the version you intend to release and creates a new
bump-versions-<next_version>branch to bump the version after the release so future changes apply to a new in-progress version instead of to the already released version. This workflow has the following inputs:version: enter the version you intend to publish (e.g. 0.2.0). This is simply for verification to make sure you release the code that you intend to release. It is compared to the version in the code, and the workflow will fail if they do not match.newVersionAfterPublishing: enter the version you want to bump to after releasing (e.g. 0.3.0-alpha.0). Future changes will apply to this new version instead of to the version that was already released. Leave blank if you don't want to bumpbumpRef: enter the Git ref you want to create the bump versions branch from, e.g.main. Leave blank if you want to use the branch selected for the workflow run. For example, if you release from a stable branch namedrelease-prep, you may want to bump the version onmainso future development work happens on the new version, then you can rebaserelease-prepontomainwhen you are ready to start preparing the next stable release.uploadReleaseAssets: whether to upload the Windows and macOS installers to Amazon S3 for internal sharing. This is the only place the built installers go; the GitHub release itself carries no assets.
-
In GitHub, adjust the new draft release's body and other metadata as desired, then publish the release. The release carries no installers - it is the version tag and the generated notes, which is what a downstream build constructs its own release from.
-
Open a PR and merge the newly created
bump-versions-<next_version>branch. -
Update the Software Version Info page with information about this release.
Automatically uploading release assets to Amazon S3 when running the Publish workflow requires that some GitHub repository secrets and repository variables are configured properly. If uploading to S3 isn't working, make sure the GitHub secrets and variables are set up properly by checking the following:
-
You need an AWS access key for a user with the following permissions:
s3:*on the S3 bucket where the release assets should be uploaded and the files in the directory in which to put the release assets (see below for more information). There are likely narrower permissions you can set and still successfully upload the release assets.
-
The following repository secrets and variables need to be correct
- Repository secrets:
AWS_S3_RELEASE_ACCESS_KEY_ID: The access key ID for authenticating with AWSAWS_S3_RELEASE_SECRET_ACCESS_KEY: The secret access key for authenticating with AWS
- Repository variables:
AWS_S3_RELEASE_BUCKET_NAME: The name of the S3 bucket where the release assets should be uploadedAWS_S3_RELEASE_DIRECTORY: The directory in which to put the release build directories. The builds themselves will be in$AWS_S3_RELEASE_DIRECTORY/$RELEASE_VERSION/$RUNNER_OS/
- Repository secrets:
Sometimes, it may be useful to change the version without publishing a release.
To bump versions without publishing a release, manually dispatch the Bump Versions workflow in GitHub Actions targeting the branch on which you want to change versions. Alternatively, you can run the bump-versions npm script. This workflow will create a branch named bump-versions-<version> from the target branch (or, if running the script, your current head) with the needed changes. Open a PR and merge that new branch into the branch on which you want to change versions.
This workflow has the following inputs:
newVersion: enter the version you want to bump to (e.g. 0.3.0-alpha.0). Future changes will apply to this new version instead of to the version.newMarketingVersion: a human-readable "marketing-level" version to call this version. It is best to set this only on the specific commit you intend to release so there is no confusion over which version is running. E.g. β1newMarketingVersionMoniker: a human-readable "marketing-level" version moniker to call this version. It is best to set this only on the specific commit you intend to release so there is no confusion over which version is running. E.g. Developer Preview
For example, to bump branch my-branch to version 0.2.0 with optional marketing version β1 and optional marketing version moniker Developer Preview, run the following:
git checkout my-branch
npm run bump-versions -- 0.2.0 --marketing-version β1 --marketing-version-moniker "Developer Preview"Then create a PR and merge the bump-versions-0.2.0 branch into my-branch. my-branch is now ready for release.
Following are some problems you may encounter while publishing and steps to solve them.
If you see the following error in the GitHub Actions workflow logs while packaging:
Module build failed (from ./node_modules/swc-loader/src/index.js):
Error: Failed to load native binding
Please see "Failed to load native binding" in the Troubleshooting guide for how to solve this problem.
The following tests run automatically on each GitHub PR (see test.yml).
To run C# unit tests:
cd c-sharp-tests
dotnet testTo run C# unit tests watching for file changes:
cd c-sharp-tests
dotnet watch testTo run all TS unit tests:
npm testTo run an individual TS unit test watching for file changes:
npm test -- <path/to/test-file.test.ts> --watchYou can also use the recommended VS Code extensions to run tests there.
To run playwright-based UI tests that also run on GitHub PRs, you can run:
npm run test:e2e:smokeAll test:e2e:* scripts are there for running variations of the playwright end-to-end tests.
test:e2e:smokeruns a single instance of the application, and all tests share that instancetest:e2e:smoke-wslruns the same smoke tests using a "hidden UI" on Linux instead of a visible UI. Its script also takes--wrap <command>to run any other e2e command the same way, e.g.e2e-tests/run-e2e-wsl.sh --wrap npm run test:e2e:isolated multi-windowtest:e2e:isolatedruns tests that need their own application instance rather than sharing one. These tests are organized into feature subsets undere2e-tests/tests/isolated/; runnpm run test:e2e:isolated <subset>for one feature, or pass no arguments to list the available subsets (no tests are run in that case).npm run test:e2e:isolated allruns every subset: specs that attach to an app you started live ine2e-tests/tests/attached/instead, so nothing undertests/isolated/needs a running apptest:e2e-cdpruns tests that require the application UI to be open alreadytest:e2e:allruns every project ine2e-tests/playwright.config.ts. It cannot currently pass:enhanced-resourcesattaches to a running app, which that config's global setup refuses to start alongside
To run Storybook locally:
npm run storybookTo build Storybook:
npm run storybook:buildTo run Storybook as a web app, after it was built successfully:
npx http-server ./storybook-staticOn Windows, you can install WSL (Windows Subsystem for Linux) so you can test cross-platform compatibility on Linux (as well as Windows). You'll need to use a Linux distribution with WSL2 (rather than WSL1) so the X-Server windows can be opened for Electron.
- Here is how to install Linux on Windows with WSL.
- You'll want to follow that by setting up to use VS Code, Git and NodeJS with WSL. See the various tutorials.
- In the WSL distribution, add system libraries needed for Electron, see Linux Development Pre-requisites above.
- In the WSL distribution, clone the repo as described above under Developer Install.
You'll be running a copy of the repo in both Windows and WSL so make sure they are both up-to-date.
You can use VS Code from your host to access code in your WSL repo clone using the Microsoft Remote Development VS Code extension.
Extensions highly recommended for this repo are already displayed in VS Code through the Extensions Recommendations settings. These are optional extensions that our developers enjoy using:
Formatting happens automatically when you commit. If you use VS Code with this repo's recommended extensions it will format when you save.
To check TypeScript for readability, maintainability, and functionality errors, and to check a few other files for proper formatting, run the following from the repo root (or just use VS Code with this repo's recommended extensions)
npm run format
npm run lintTo check C# for readability, maintainability, and functionality errors, run the following from the repo root (or just use VS Code with this repo's recommended extensions)
cd c-sharp
dotnet tool restore
dotnet csharpier .VSCode renders JSDoc comments in the UI to make it easier for developers to use functions and properties as intended. However, those comments do not always propagate from modules to the d.ts type definition file when those modules are re-exported. To help with this problem in papi.d.ts that we export for extensions to reference, we added some custom functionality.
If you want comments to be copied from one location in papi.d.ts to another, do the following:
- In the JSDoc comments that you want copied elsewhere, add "JSDOC SOURCE myServiceName" (must have a blank line after) in the JSDoc comments like this:
/**
* JSDOC SOURCE myService
*
* myService is amazing. Here are more details about it.
* ...
*/
const myService = {
...
}- In the location where you want the docs copied, add "JSDOC DESTINATION myServiceName" like this:
const papi = {
...
/** JSDOC DESTINATION myService */
myService,THIRD-PARTY-NOTICES.md and its sidecar THIRD-PARTY-NOTICES.lock.json are generated, never edited
by hand. They describe what the packaged application redistributes: the npm packages webpack
compiled into dist/, the NuGet closure of the .NET data provider, and the components that belong
to neither graph. The document ships inside every installer.
This section is the procedure. For what the document covers and deliberately does not, which policy
entry answers a blocked build, and how to change the generator, see
.erb/scripts/third-party-notices/README.md.
You do not need to regenerate them as part of ordinary work. Building never touches them and
never complains, whatever your webpack cache holds. Regenerate deliberately, when a production
dependency changes - an npm package that reaches the bundle, or a NuGet version in
c-sharp/ParanextDataProvider.csproj. If you forget, CI's Linux leg fails: it verifies, never
regenerates, so the committed copy has to be brought up to date here and committed.
The generator reads what webpack actually compiled, and a warm webpack filesystem cache can under-report modules that were served from cache instead of rebuilt - so it refuses to write from one. Start from a cold cache:
rm -rf node_modules/.cache/webpack-* .notices
npm run build
npm run build:extensions:production
dotnet restore c-sharp/ParanextDataProvider.csproj
npm run build:third-party-noticesnpm run build leaves its extensions leg in development mode, and every job that packages an
installer follows it with build:extensions:production — so the second command is what makes the
document describe the graph the installers actually carry. Each manifest records the webpack mode it
came from and the generator refuses a mixed set, so skipping it fails rather than producing the
wrong document.
Then read the diff and commit both files together; they are written as a pair.
If a package cannot be cleared, the run stops and prints the package, both signals it read, and the
exact policy entry to add to .erb/scripts/third-party-notices/notices-policy.json. Nothing is
written from an incomplete set.
If dependencies were genuinely removed and the npm set drops by more than 10%, the run refuses that too - the drop is otherwise indistinguishable from a broken tree. Acknowledge a real one explicitly:
NOTICES_ACCEPT_SHRINK=1 npm run build:third-party-notices| Command | Answers | Needs |
|---|---|---|
npm run verify:third-party-notices |
Does the committed pair match what this tree derives, verdicts and license texts included? | Linux, Ruby, dotnet restore |
npm run verify:third-party-notices:shipping-set |
Is the committed document the one its lock was written beside, and does this platform ship the same npm packages the lock records? | Nothing beyond a build |
npm run verify:third-party-notices:document |
Is the committed document the one its lock was written beside? That half on its own. | Nothing at all |
The shipping-set check runs on every platform, and the release workflows (publish.yml,
package-main.yml) run it immediately after their production extension build. Its two halves answer
under different conditions. The document check compares two committed files — the lock records a
sha256 of the document it was written beside — so nothing can stop it running; it is what keeps a
hand-edited THIRD-PARTY-NOTICES.md out of an installer on the paths that cannot afford the full
check. The npm shipping-set check reads what webpack compiled, so it can only answer straight after
a build whose cache was cold: on a warm cache it says so and skips that half rather than reporting a
difference it cannot trust. In CI it only ever runs in that cold position, so a warm stamp there is
a real anomaly and fails the build.
npm run package runs the document check rather than the shipping-set one, and has to: it
rebuilds from a tree that has already been built, so its webpack caches are warm by construction and
the shipping-set half would refuse to answer on every platform. The document is what
electron-builder packs into each installer, so package verifies it before packaging it.
Some important decisions in this project were inspired by the work done in Visual Studio Code. Thanks VS Code developers for some great ideas!
This repository contains code under two licenses:
- The core Platform.Bible application — the Electron client, extension host, .NET data provider, the bundled extensions, and the build- and lint-time packages
lib/papi-dts,lib/eslint-plugin-paranext, andlib/browserslist-config-detect-electron— is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). - The two developer libraries an extension links against at runtime —
platform-bible-reactandplatform-bible-utils, both underlib/— remain under the MIT License, so an extension takes those two under MIT rather than the AGPL. Each carries its ownLICENSEfile. The boundary keys on runtime linkage, not on thelib/directory: three of the five packages there are AGPL. Extensions are covered by the Platform.Bible Extension License Exception, an additional permission under AGPL section 7: an extension that talks to Platform.Bible only through the published Extension Interface may be conveyed under terms of its author's choosing. It frees an extension author to choose, and takes no position on what they should choose — see LICENSING.md, "What a third-party extension links against".
See LICENSING.md for the authoritative path-by-path map and copyright attributions.
Copyright © 2017-2026 SIL Global and United Bible Societies

