Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ jobs:
cache-dependency-path: service/package-lock.json
- name: Install locked service dependencies
run: npm ci --prefix service
- name: Verify shipped runtime matches source
run: |
npm run build --prefix service
git diff --exit-code -- service/dist/
- name: Check service JavaScript
run: npm run check --prefix service
- name: Run source or release tests
Expand Down
77 changes: 77 additions & 0 deletions BackendRuntime.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import QtQuick
import Quickshell
import Quickshell.Io

// One shell-owned backend for all monitors. Its stdin closes on shell exit;
// stopping this component also terminates it on disable or plugin reload.
Scope {
id: root
readonly property string host: Quickshell.env("LIGHT_HOST") || "127.0.0.1"
readonly property string baseUrl: "http://" + (host === "::1" ? "[::1]" : host)
+ ":" + (Quickshell.env("LIGHT_PORT") || "8788")
property bool ready: false
property bool nativeAudioAvailable: false
property url nativeAudioUrl: ""
property string error: ""
property int attempts: 0
readonly property string runtimePath: decodeURIComponent(Qt.resolvedUrl("service/start.sh").toString().slice(7))

function start() {
ready = false
error = ""
attempts++
backend.running = true
startupTimeout.restart()
}
Component.onCompleted: start()
Component.onDestruction: {
restartTimer.stop()
backend.running = false
}
Process {
id: backend
command: ["bash", root.runtimePath]
stdinEnabled: true
stdout: SplitParser {
onRead: function(line) {
if (line === "LIGHT_RESTARTING") root.ready = false
if (line.indexOf("LIGHT_READY ") === 0) {
var state = JSON.parse(line.slice(12))
root.nativeAudioAvailable = Boolean(state.nativeAudioAvailable)
root.nativeAudioUrl = state.nativeAudioUrl || ""
root.ready = true
root.error = ""
stableTimer.restart()
startupTimeout.stop()
}
}
}
stderr: SplitParser {
onRead: function(line) {
if (line.indexOf("ERROR:") === 0 || line.indexOf("EADDRINUSE") >= 0)
root.error = line
}
}
// Quickshell omits QProcess::ExitStatus from its tooling metadata.
// qmllint disable signal-handler-parameters
onExited: function(exitCode) {
root.ready = false
stableTimer.stop()
startupTimeout.stop()
if (!root.error) root.error = "Light's background process stopped."
if (root.attempts < 3) restartTimer.restart()
}
// qmllint enable signal-handler-parameters
}
Timer {
id: startupTimeout
interval: 10000
onTriggered: {
root.error = "Light could not start. Check that Node.js 22.13 or newer is available."
if (backend.running) backend.running = false
else if (root.attempts < 3) restartTimer.restart()
}
}
Timer { id: stableTimer; interval: 30000; onTriggered: root.attempts = 0 }
Timer { id: restartTimer; interval: 1500; onTriggered: root.start() }
}
53 changes: 20 additions & 33 deletions INSTALL.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,40 @@
# Install Light

Install the prerequisites listed below, then copy this complete command into your terminal:
On current Omarchy Quattro, copy this command into your terminal:

```bash
(omarchy plugin add https://github.com/whiteOvis/Light.git --yes && cd "$HOME/.config/omarchy/plugins/light.bible-reader" && npm ci --prefix service && ./install.sh)
omarchy plugin add https://github.com/whiteOvis/Light.git --enable
```

For a new installation on Omarchy. Already have Light? Use the [update command](#update-light).
Open Light from the top bar, choose Sign in, and use your YouVersion account.
The backend and required JavaScript dependencies are ready to run. No npm
installation, native build, service installation, or extra setup command is
required on a standard current Omarchy installation.

## Before you install
Omarchy provides Node.js (22.13+ required), Quickshell, Qt Multimedia (6.10+),
`curl`, `wl-clipboard`, and `xdg-utils`. Bible reading requires a YouVersion
account and access to the YouVersion Platform API.

Requires Omarchy/Quickshell, Node.js 22.5+, npm, Qt 6 Multimedia 6.8+, `base-devel`, `pkgconf`, `curl`, `jq`, `wl-clipboard`, and `xdg-utils`.

Bible reading requires a YouVersion account and access to the YouVersion Platform API. Sign in after installation.

The command installs the current GitHub version. You can [review the installer](install.sh) before running it. `--yes` accepts Omarchy's repository confirmation; `&&` stops setup if a step fails.

## What setup does

- Adds Light without enabling it prematurely.
- Installs the service dependencies and builds the native Qt audio module.
- Configures and starts the local Light user service.
- Registers the OAuth callback handler while retaining the previous handler.
- Preserves existing shortcuts and enables Light in the top bar after setup succeeds.

Keep the checkout at `~/.config/omarchy/plugins/light.bible-reader`: the service runs from it. Light stores its data separately in `~/.config/omarchy/light-public/`.

## Start reading

1. Open Light from the top bar.
2. Sign in with YouVersion.
3. Choose a Bible version and start reading.
The command installs the current upstream branch. Marketplace verification
applies to an exact recorded commit; the standard install button will become
available only after the marketplace approves this installation path.

## Update Light

```bash
(omarchy plugin update light.bible-reader && cd "$HOME/.config/omarchy/plugins/light.bible-reader" && npm ci --prefix service && ./install.sh)
omarchy plugin update light.bible-reader
```

If you originally installed from another checkout, update that checkout and run `npm ci --prefix service` followed by `./install.sh` there instead.

## Remove Light

For an installation made using the command above:

```bash
(cd "$HOME/.config/omarchy/plugins/light.bible-reader" && ./uninstall.sh)
omarchy plugin remove light.bible-reader
```

Your saved user data remains on the device.
Your notes, bookmarks, permitted downloads and account data remain on the device.

## Upgrading from Light 0.6

[Back to Light](README.md)
New installations require no service setup. If you previously ran Light 0.6's
installer, see the [legacy upgrade notes](docs/UPGRADE.md) to retire its old
service while preserving your data and existing shortcuts.
22 changes: 15 additions & 7 deletions LightSession.qml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ QtObject {
property var widgets: []
property var panel: null
property var panelFactory: null
property var backend: null
property var backendFactory: null

function registerWidget(widget) {
if (widgets.indexOf(widget) >= 0) return
widgets = widgets.concat([widget])
if (!backendFactory) backendFactory = Qt.createComponent(Qt.resolvedUrl("BackendRuntime.qml"))
if (!backend) backend = backendFactory.createObject(root)
if (!panelFactory) panelFactory = Qt.createComponent(Qt.resolvedUrl("Panel.qml"))
if (!panel) panel = panelFactory.createObject(root)
if (!panel) {
Expand All @@ -34,18 +38,22 @@ QtObject {

function unregisterWidget(widget) {
widgets = widgets.filter(function(item) { return item && item !== widget })
if (!panel) return
if (widgets.length === 0) {
if (backend) backend.destroy()
backend = null
backendFactory = null
// A singleton must never keep an input surface alive after plugin unload.
var oldPanel = panel
panel = null
panelFactory = null
oldPanel.close()
oldPanel.hostWidget = null
oldPanel.anchorItem = null
oldPanel.bar = null
oldPanel.destroy()
} else if (panel.hostWidget === widget) {
if (oldPanel) {
oldPanel.close()
oldPanel.hostWidget = null
oldPanel.anchorItem = null
oldPanel.bar = null
oldPanel.destroy()
}
} else if (panel && panel.hostWidget === widget) {
attach(widgets[0])
}
}
Expand Down
2 changes: 1 addition & 1 deletion Panel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ Panel {

readonly property var barIdentity: hostWidget || root
readonly property string serviceBaseUrl: String(
setting("serviceBaseUrl", "http://127.0.0.1:8788")
setting("serviceBaseUrl", LightSession.backend ? LightSession.backend.baseUrl : "http://127.0.0.1:8788")
)

function normalizedAppScale(value) {
Expand Down
125 changes: 76 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,69 +4,96 @@ A Bible reader and Christian radio player for the Omarchy top bar.

## [Install Light →](INSTALL.md)

**[Open the installation page for the complete copy-and-paste terminal command.](INSTALL.md)**

## Get started

Requires Omarchy/Quickshell, Node.js 22.5+, npm, Qt 6 Multimedia 6.8+, `base-devel`, `pkgconf`, `curl`, `jq`, `wl-clipboard`, and `xdg-utils`.

Bible reading requires a YouVersion account and access to the YouVersion Platform API.

1. In the Light checkout, run `npm ci --prefix service`, then `./install.sh`.
2. Open Light from the top bar and sign in with YouVersion.
3. Choose a Bible version and start reading.

The installer enables the widget and starts `omarchy-light-public.service` on loopback port 8788. Every HTTP endpoint requires a random local client credential, rotated on daemon startup and stored in the owner-only `~/.config/omarchy/light-public/client-auth-header` file (0600 inside a 0700 directory). The widget reads this file directly; other local users cannot access the service using the daemon owner’s YouVersion session. If you override `LIGHT_CONFIG_DIR`, set the same value for the widget process. Light stores its data in `~/.config/omarchy/light-public/` and preserves existing LHT services and shortcuts. If a suggested shortcut is occupied, choose another in Settings.

### Install from the Omarchy plugin page

After installing the prerequisites above, copy this complete command into your terminal:
On current Omarchy Quattro, install and enable Light with:

```bash
(omarchy plugin add https://github.com/whiteOvis/Light.git --yes && cd "$HOME/.config/omarchy/plugins/light.bible-reader" && npm ci --prefix service && ./install.sh)
omarchy plugin add https://github.com/whiteOvis/Light.git --enable
```

This adds the repository without enabling it, installs the service dependencies, builds the native audio module, configures the local service, and enables Light only after setup succeeds. `--yes` accepts Omarchy's repository confirmation; each `&&` stops setup if the previous step fails.

The marketplace currently marks Light as **Manual setup** and suppresses its standard copy-install button because `omarchy plugin add ... --enable` alone cannot complete the required build and service setup. Use the complete command above instead.

To update an existing installation:
Open Light and sign in with YouVersion to start reading. Bible reading requires
a YouVersion account and access permitted by YouVersion’s API requirements.
No Bible text or concordance data is bundled.

Light 0.7 starts its local backend automatically while its widget is loaded.
It ships the JavaScript dependencies needed at runtime. A new installation
requires no `npm ci`, native compilation, systemd service, or setup script.
Omarchy supplies Node.js (22.13+ required), Quickshell, Qt Multimedia (6.10+),
`curl`, `wl-clipboard`, and `xdg-utils`.

## Reading and radio

Read and study with tabs, highlights, notes, bookmarks, history, and permitted
offline downloads. Optional Christian radio uses standard Qt Multimedia and
retains the seven player layouts, station management, playback and volume controls.

Decoded audio meters, waveforms, record scratching, and enhanced ICY stream titles
use the optional native audio module. These enhancements are not part of the
standard installation; Qt-provided track metadata still works without it.
Advanced users can build the enhancement with
`bash components/LightAudio/build.sh` after installing `base-devel`, `pkgconf`,
and matching Qt 6 Multimedia development libraries, then reload the plugin.
A native module that cannot load does not prevent ordinary radio playback.

## Keyboard shortcuts

Global toggle defaults to **Super+B** and Verse of the Day to **Super+Alt+V**.
Light registers these for the current desktop session and removes its own
bindings when disabled. It restores them after a compositor configuration
reload without editing Hyprland configuration files. Existing bindings take
priority: if another app uses a key, choose a free key in Light's Settings.
Existing user-configured Light bindings remain untouched. All in-app shortcuts
remain available and customizable.

## Account and local data

The backend listens on loopback port 8788. Every HTTP endpoint requires a random
local client credential, rotated on backend startup and stored in the owner-only
`~/.config/omarchy/light-public/client-auth-header` file (0600 in a 0700 directory).
The widget reads the header file directly; the secret is not placed in process
arguments. The backend stops when the last widget unloads or the shell exits.
Interrupted downloads can be resumed on the next launch.

Choosing Sign in registers the `omarchy://` callback handler automatically and
preserves the previous handler for other applications. A small callback bridge
stays in the user data directory so it can forward unrelated callbacks even
after Light is removed. Light data lives in
`~/.config/omarchy/light-public/`. If you override `LIGHT_CONFIG_DIR`, use the same
value for the widget and backend. Disabling or removing the plugin preserves
saved data.

## Update and remove

```bash
(omarchy plugin update light.bible-reader && cd "$HOME/.config/omarchy/plugins/light.bible-reader" && npm ci --prefix service && ./install.sh)
omarchy plugin update light.bible-reader
```

Keep the checkout: the service runs from it.

Type a reference or phrase to search. Click the faint book, chapter, or version inside the search box to browse.

Remove Light with `./uninstall.sh`. Your saved data remains on the device.

## Features
```bash
omarchy plugin remove light.bible-reader
```

- Highlights, notes, bookmarks, and tabs.
- Offline reading where permitted by the selected version.
- Adjustable text, night mode, and optional Christian radio.
- Markdown exports for notes and bookmarks.
Keep the checkout at `~/.config/omarchy/plugins/light.bible-reader`; Light runs
from that directory. The marketplace verifies exact commits, while Omarchy's
Git commands follow the upstream branch. Marketplace availability of the
standard install button requires a separate maintainer approval.

See **Settings → Shortcuts** for keyboard controls.
For installations made using Light 0.6's setup script, see the
[upgrade notes](INSTALL.md#upgrading-from-light-06).

## Screenshots

[View all 14 screenshots](docs/SCREENSHOTS.md): reader, Verse of the Day, settings, downloads, shortcuts, and all seven radio layouts.

### Bible reader

![Light Bible reader showing John 3](preview.png)

### Christian radio

![Light Christian radio with station playlist](docs/media/radio.png)
[Open the screenshot gallery](docs/SCREENSHOTS.md).

## Data and terms
## Development

Settings, notes, bookmarks, and encrypted sign-in data stay on your device. Bible requests and optional highlight sync use YouVersion.
Install development dependencies with `npm ci --prefix service`, then build the
shipped JavaScript runtime with `npm run build --prefix service`. Commit the
resulting `service/dist/` files with their source changes. CI rebuilds and checks
that they match. Run `node --test test/*.test.mjs service/test/*.test.js` and,
on Omarchy, `node scripts/validate.mjs`. The native audio build is optional:
pass `--native-audio` to validate that enhancement separately.

Light ships without Bible text. Content availability and use follow [YouVersion’s API requirements](https://developers.youversion.com/api-usage) and publisher terms. Light is independent and is not endorsed by YouVersion.
## License and third-party terms

[Privacy](https://www.bible.com/privacy) · [Terms](https://platform.youversion.com/?tos=1) · [MIT license](LICENSE) · [Third-party notices](THIRD_PARTY_NOTICES.md)
Light is MIT licensed. See [LICENSE](LICENSE),
[third-party notices](THIRD_PARTY_NOTICES.md), and [security notes](SECURITY.md).
Bible text, online services and radio content have their own terms.
8 changes: 7 additions & 1 deletion THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ the Bible publisher's terms.
## Software

Light uses YouVersion's `@youversion/platform-core` under Apache-2.0 and Zod
under MIT. Qt, Quickshell, Node.js, and Omarchy are system dependencies. Their
under MIT. These dependencies are included in the readable JavaScript runtime
in `service/dist/`. Their license texts are included in
[`service/licenses/`](service/licenses/). The runtime is rebuilt from the exact
versions recorded in `service/package-lock.json`; esbuild is a development-only
bundler and is not required to install or run Light.

Qt, Quickshell, Node.js, and Omarchy are system dependencies. Their
names and marks belong to their owners.

## Radio
Expand Down
Loading
Loading