Skip to content

Building from Source

samaBR edited this page Aug 3, 2026 · 5 revisions

Building from Source

Electron 43, React 19, TypeScript 7, Vite 7, Tailwind 4.

git clone https://github.com/samaBR85/Valendo-TeleprompterSuite.git
cd Valendo-TeleprompterSuite
npm install
npm run dev

If npm install does not fetch the Electron binary, run node node_modules/electron/install.js.

On Windows, Abrir Valendo.bat does the install and the build on the first double-click, for people who do not have a terminal open.

Commands

Command What it does
npm run dev Development, with hot reload
npm run build Bundles main, preload and renderer into out/
npm test The test suite
npm run typecheck tsc --noEmit
npm run start:debug Runs the built app with remote debugging on port 9222
npm run verify Checks the acceptance criteria against the running app (needs start:debug)
npm run dist:win The Windows installer (NSIS) into dist/
npm run dist:mac The macOS disk images, arm64 and x64, into dist/

dist:mac only works on macOS — Apple's signing tools do not exist on other systems. There is no way around this; it is why the release workflow builds each platform on its own runner.

Releases

.github/workflows/release.yml builds both platforms whenever a v* tag is pushed, and attaches the installers to the release. macos-14 runners are Apple Silicon, which is where the arm64 disk image has to be built.

Running the workflow by hand (Actions → Release → Run workflow) builds the same artifacts without creating a release, and leaves them as downloadable job artifacts — useful for testing a build before deciding it is worth a tag.

The macOS build is signed ad-hoc, never notarised: the project has no paid Apple developer account. Ad-hoc signing is not optional — Apple Silicon refuses to launch a binary with no signature at all — but it does not satisfy Gatekeeper for a downloaded app, which is why Getting Started documents the unblock.

Layout

src/main/       windows, monitors, authoritative state, persistence
src/preload/    the IPC bridge exposed to the renderer
src/shared/     pure, testable logic: anchor, lines, pacing, history, commands
src/renderer/   prompter (shared), operator interface, broadcast window
scripts/        end-to-end verification over the Chromium protocol
docs/           screenshots used in the README and this wiki

The shape of the thing

There is one authoritative state, in the main process, in src/main/state.ts. Renderers never hold their own copy of anything that matters; they dispatch actions and mirror what comes back.

src/shared is deliberately free of Electron and of React. It is where the anchor, the line composition, the pacing arithmetic, the undo history and the command registry live, and it is where almost all the tests are — the logic can be proved without launching a window.

Tests

npm test

The suite covers the pure logic and the parts of the main process that can be reached with a mocked userData folder: the anchor after a reflow, line composition, pacing in the three modes, what does and does not travel inside a .valendo, migration of projects written by older versions, and the i18n dictionaries.

The i18n test is worth knowing about: it enforces that all six dictionaries carry exactly the same set of keys, that none is empty, and that {interpolation} marks survive translation. Forgetting one language breaks the build rather than shipping a blank label.

End-to-end checks

scripts/verify.mjs drives the running app over the Chromium DevTools Protocol: it launches with an isolated user-data folder, dispatches real input, and asserts against the real DOM. That is how the acceptance criteria are checked — including the one that matters most, that the word under the reading line does not move when the text above it changes.

npm run start:debug     # in one terminal
npm run verify          # in another

Versioning

The semantic version is a human decision and sits at 1.0.0 in package.json. The build number rises on its own with every npm run build, through scripts/bump-build.mjs, and shows in the app header and credits as v1.0.0 - build N.

So a bug report that says "v1.0.0 - build 199" names an exact bundle, without anyone having to remember to bump a number before cutting a release.

Redistributed ffmpeg

The installer carries an ffmpeg, through ffmpeg-static, pinned in package-lock.json. It is a GPL build of ffmpeg 6.1.1 compiled by gyan.dev with --enable-gpl --enable-version3 --enable-libx264, compatible with this project's licence. The corresponding source is at ffmpeg.org/download.html and in the official repository, tag n6.1.1.

Because ffmpeg is a real executable, it is unpacked beside app.asar rather than inside it — Windows cannot run a binary that lives inside an archive.

Contributing

Issues and pull requests are welcome. Two things to know before opening one:

  • The comments in this codebase explain why, not what. A comment that restates the line below it will be asked to say something else.
  • If a change touches the reading position, it needs a test. That is the one promise the whole app is built on.

Clone this wiki locally