feat: EUPL license, man page, config path support
- Add `LICENCE.txt` with European Union Public Licence v1.2
- Add `docs/bibel.1` man page documenting all options, configuration,
and algorithms
- Add `assets/logo.gif` and `assets/screenshot.svg` for README branding
- Rewrite `README.md` with clearer structure, usage examples, and build
instructions
- Improve configuration loading (`internal/config.go`)
- Support custom config file path via `--config` flag
- Fix variable shadowing (`configPath` → `xdgConfigPath`) and path
handling
- Add fallback to XDG data directory for Bible JSON files
- Refactor output mode logic in `cmd/bibel.go`
- Consolidate plain, formatted, and TUI modes into single conditional
block
- Respect `--numbered` and `--paragraphs` flags without requiring
`--formatted`
- Automatically use formatted mode when numbered or paragraphs are
requested
- Update `SaveConfig` to use correct XDG config directory variable
Maxwell Jensen 85795372+maxwelljens@users.noreply.github.com
Sat, 18 Apr 2026 18:05:52 +0200
8 files changed,
861 insertions(+),
236 deletions(-)
M
CHANGELOG.md
→
CHANGELOG.md
@@ -8,6 +8,32 @@ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] +## [1.0.0] - 2026-04-18 + +### Added + +- **Comprehensive `man` page**: `bibel.1` with 15 sections that should cover +all aspects of the program + +### Changed + +- **Revised documentation**: `README.md` updated to a more concise page (80 +lines, was ~245) + +### Fixed + +- **Output mode distinction**: Plain (`-p/--plain`) and formatted +(`-f/--formatted`) modes now behave correctly: + - Plain mode forces `useColours = false` for true plain text output + - Formatted mode respects `config.Formatter.UseColours` setting +- **Formatter logic**: Restructured to handle all flag combinations (`-p +--numbered`, `-f --numbered`, etc.) +- `-p/--plain` and `-f/--formatted` previously operated identically (both with +ANSI colours) +- `--config` flag existed but didn't actually load custom config files +- Fixed part where `--numbered` flag alone didn't trigger formatted output mode +- Edge cases with flag combinations (`-p --numbered`, `-f --paragraphs`) + ## [0.11.0] - 2026-04-18 ### Added
A
LICENCE.txt
@@ -0,0 +1,287 @@
+ EUROPEAN UNION PUBLIC LICENCE v. 1.2 + EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined +below) which is provided under the terms of this Licence. Any use of the Work, +other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). + +The Work is provided under the terms of this Licence when the Licensor (as +defined below) has placed the following notice immediately following the +copyright notice for the Work: + + Licensed under the EUPL + +or has expressed by any other means his willingness to license under the EUPL. + +1. Definitions + +In this Licence, the following terms have the following meaning: + +- ‘The Licence’: this Licence. + +- ‘The Original Work’: the work or software distributed or communicated by the + Licensor under this Licence, available as Source Code and also as Executable + Code as the case may be. + +- ‘Derivative Works’: the works or software that could be created by the + Licensee, based upon the Original Work or modifications thereof. This Licence + does not define the extent of modification or dependence on the Original Work + required in order to classify a work as a Derivative Work; this extent is + determined by copyright law applicable in the country mentioned in Article 15. + +- ‘The Work’: the Original Work or its Derivative Works. + +- ‘The Source Code’: the human-readable form of the Work which is the most + convenient for people to study and modify. + +- ‘The Executable Code’: any code which has generally been compiled and which is + meant to be interpreted by a computer as a program. + +- ‘The Licensor’: the natural or legal person that distributes or communicates + the Work under the Licence. + +- ‘Contributor(s)’: any natural or legal person who modifies the Work under the + Licence, or otherwise contributes to the creation of a Derivative Work. + +- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of + the Work under the terms of the Licence. + +- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, + renting, distributing, communicating, transmitting, or otherwise making + available, online or offline, copies of the Work or providing access to its + essential functionalities at the disposal of any other natural or legal + person. + +2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +sublicensable licence to do the following, for the duration of copyright vested +in the Original Work: + +- use the Work in any circumstance and for all usage, +- reproduce the Work, +- modify the Work, and make Derivative Works based upon the Work, +- communicate to the public, including the right to make available or display + the Work or copies thereof to the public and perform publicly, as the case may + be, the Work, +- distribute the Work or copies thereof, +- lend and rent the Work or copies thereof, +- sublicense rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether now +known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make effective +the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to +any patents held by the Licensor, to the extent necessary to make use of the +rights granted on the Work under this Licence. + +3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as +Executable Code. If the Work is provided as Executable Code, the Licensor +provides in addition a machine-readable copy of the Source Code of the Work +along with each copy of the Work that the Licensor distributes or indicates, in +a notice following the copyright notice attached to the Work, a repository where +the Source Code is easily and freely accessible for as long as the Licensor +continues to distribute or communicate the Work. + +4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits from +any exception or limitation to the exclusive rights of the rights owners in the +Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the +disclaimer of warranties. The Licensee must include a copy of such notices and a +copy of the Licence with every copy of the Work he/she distributes or +communicates. The Licensee must cause any Derivative Work to carry prominent +notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the +Original Works or Derivative Works, this Distribution or Communication will be +done under the terms of this Licence or of a later version of this Licence +unless the Original Work is expressly distributed only under this version of the +Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee +(becoming Licensor) cannot offer or impose any additional terms or conditions on +the Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative +Works or copies thereof based upon both the Work and another work licensed under +a Compatible Licence, this Distribution or Communication can be done under the +terms of this Compatible Licence. For the sake of this clause, ‘Compatible +Licence’ refers to the licences listed in the appendix attached to this Licence. +Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible +Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, +the Licensee will provide a machine-readable copy of the Source Code or indicate +a repository where this Source will be easily and freely available for as long +as the Licensee continues to distribute or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade names, +trademarks, service marks, or names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings +to the Work are owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent +Contributors grant You a licence to their contributions to the Work, under the +terms of this Licence. + +7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +Contributors. It is not a finished work and may therefore contain defects or +‘bugs’ inherent to this type of development. + +For the above reason, the Work is provided under the Licence on an ‘as is’ basis +and without warranties of any kind concerning the Work, including without +limitation merchantability, fitness for a particular purpose, absence of defects +or errors, accuracy, non-infringement of intellectual property rights other than +copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition +for the grant of any rights to the Work. + +8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the use +of the Work, including without limitation, damages for loss of goodwill, work +stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such damage. +However, the Licensor will be liable under statutory product liability laws as +far such laws apply to the Work. + +9. Additional agreements + +While distributing the Work, You may choose to conclude an additional agreement, +defining obligations or services consistent with this Licence. However, if +accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, +and only if You agree to indemnify, defend, and hold each Contributor harmless +for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ +placed under the bottom of a window displaying the text of this Licence or by +affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and +conditions by exercising any rights granted to You by Article 2 of this Licence, +such as the use of the Work, the creation by You of a Derivative Work or the +Distribution or Communication by You of the Work or copies thereof. + +11. Information to the public + +In case of any Distribution or Communication of the Work by means of electronic +communication by You (for example, by offering to download the Work from a +remote location) the distribution channel or media (for example, a website) must +at least provide to the public the information requested by the applicable law +regarding the Licensor, the Licence and the way it may be accessible, concluded, +stored and reproduced by the Licensee. + +12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon +any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has +received the Work from the Licensee under the Licence, provided such persons +remain in full compliance with the Licence. + +13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as a +whole. Such provision will be construed or reformed so as necessary to make it +valid and enforceable. + +The European Commission may publish other linguistic versions or new versions of +this Licence or updated versions of the Appendix, so far this is required and +reasonable, without reducing the scope of the rights granted by the Licence. New +versions of the Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version of +their choice. + +14. Jurisdiction + +Without prejudice to specific agreement between parties, + +- any litigation resulting from the interpretation of this License, arising + between the European Union institutions, bodies, offices or agencies, as a + Licensor, and any Licensee, will be subject to the jurisdiction of the Court + of Justice of the European Union, as laid down in article 272 of the Treaty on + the Functioning of the European Union, + +- any litigation arising between other parties and resulting from the + interpretation of this License, will be subject to the exclusive jurisdiction + of the competent court where the Licensor resides or conducts its primary + business. + +15. Applicable Law + +Without prejudice to specific agreement between parties, + +- this Licence shall be governed by the law of the European Union Member State + where the Licensor has his seat, resides or has his registered office, + +- this licence shall be governed by Belgian law if the Licensor has no seat, + residence or registered office inside a European Union Member State. + +Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: + +- GNU General Public License (GPL) v. 2, v. 3 +- GNU Affero General Public License (AGPL) v. 3 +- Open Software License (OSL) v. 2.1, v. 3.0 +- Eclipse Public License (EPL) v. 1.0 +- CeCILL v. 2.0, v. 2.1 +- Mozilla Public Licence (MPL) v. 2 +- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for + works other than software +- European Union Public Licence (EUPL) v. 1.1, v. 1.2 +- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong + Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above +licences without producing a new version of the EUPL, as long as they provide +the rights granted in Article 2 of this Licence and protect the covered Source +Code from exclusive appropriation. + +All other changes or additions to this Appendix require the production of a new +EUPL version.
M
README.md
→
README.md
@@ -1,238 +1,109 @@
-# `bibel` - Bible Verse CLI/TUI Utility + -A Go utility for displaying Bible verses with interactive terminal interface. -This tool shows Bible verses based on the current date, displaying 12 verses -per day through the four Gospels. + -## Features +--- -- **Interactive TUI**: Terminal User Interface using `bubbletea` with styled -boxes -- **Date-Based Progression**: Automatically calculates position based on day of -year (1 January = Matthew 1:1-12) -- **Smart Sizing**: Default snippet size is 12 verses, extends to end of -chapter if less than 12 verses remain -- **ANSI Colours: Uses ANSI terminal colours set by terminal emulator -- **Interactive Controls**: Press `q` to quit (MOTD-like behaviour) -- **Yearly Cycle**: Progresses through all four Gospels each year, restarting -on 1 January -- **Multiple Output Modes**: Interactive TUI, formatted ANSI, or plain text -- **Verse Numbering**: Option to print each verse on a numbered line with verse -numbers -- **Paragraph Handling**: Option to render pilcrows (¶) in source JSON Bible -file as paragraph breaks with blank lines -- **Easter Progress Bar**: Fancy progress bar showing time until Easter with -visual progress indication (default: Eastern Orthodox; Catholic with `-l` flag) +## What is bibel? -## Installation +`bibel` (bee•bell) isn’t another all‑purpose Bible app. It’s a small CLI/TUI +utility with one main job: showing today’s reading from the Gospels when you +open your terminal. Think of it as a MOTD for Scripture: just 12 verses a day, +based on the calendar, without menus, search, or bloat. -```bash -go build ./cmd/bibel.go -``` +That is just the default configuration, most of which can be changed to suit +one's preferences. -## Usage +## How do I use bibel? -```bash -./bibel -``` + bibel [OPTIONS]... -By default, the verses the program picks are done by: -1. Calculating today's date and day of year -2. Determining Bible position: day of year * 12 verses -3. Find corresponding verses in the Gospels +By default, `bibel` shows an interactive TUI with today's reading. The verses +are selected by date: day of year * 12 verses, progressing through all four +Gospels each day. -### Usage Examples +### Options -```bash -# Default TUI mode with interactive display (shows Orthodox Easter progress) -./bibel + -r, --reading Reading mode: "evangelion" (four Gospels), "new_testament", + "old_testament", "bible" (default: evangelion) + -p, --plain Output plain text without formatting or TUI + -f, --formatted Output formatted text with ANSI colours (no TUI) + -n, --numbered Print each verse on a numbered line with verse number + -g, --paragraphs Render pilcrows (¶) as blank lines instead of ignoring them + -l, --latin Show time until Roman Catholic Easter instead of Orthodox + --generate-config Generate a default configuration file and exit + -v, --verbose Print additional runtime information to STDOUT -# Roman Catholic Easter progress bar instead of Orthodox -./bibel --latin +### Examples -# Plain text output -./bibel --plain + # Default TUI mode with interactive display (shows Orthodox Easter progress) + bibel -# Formatted output with ANSI colours -./bibel --formatted + # Roman Catholic Easter progress bar instead of Orthodox + bibel --latin -# Numbered verses (each verse on its own line with verse number) -./bibel --numbered + # Plain text output + bibel --plain -# Paragraph mode (pilcrows render as blank lines) -./bibel --paragraphs + # Formatted output with ANSI colours + bibel --formatted -# Combine numbered and paragraph modes -./bibel --numbered --paragraphs + # Numbered verses (each verse on its own line with verse number) + bibel --numbered -# Plain output with numbered verses -./bibel --plain --numbered + # Paragraph mode (pilcrows render as blank lines) + bibel --paragraphs -# Generate configuration file -./bibel --generate-config -``` + # Generate configuration file + bibel --generate-config -This is default behaviour. Alternative configurations are available (see -below). +## How does `bibel` work? -## Configuration +`bibel` reads Bible data from a JSON file (provided example: +`books/pol_nbg.json`). It calculates today's reading position by: -`bibel` supports configuration via a TOML file located at -`$XDG_CONFIG_HOME/bibel/config.toml` (default: `~/.config/bibel/config.toml`). -Default macOS and Windows paths are also supported. +1. Getting the current day of year (1-366) +2. Multiplying by 12 verses per day: `offset = (day_of_year - 1) * 12` +3. Applying modulo with total Gospel verses (3779) to cycle yearly +4. Finding the corresponding verses in the Gospels -### Configuration Options +The program extends to the end of a chapter if less than 12 verses remain for +the next day. Books 40-43 correspond to the four Gospels: Matthew, Mark, Luke, +and John. -- **reading_mode**: Reading mode: "evangelion" (four Gospels; *default*), -"new_testament", "old_testament", "bible" (default: "evangelion") -- **output_mode**: Output mode: "tui" (interactive terminal UI), "formatted" -(ANSI-coloured text), or "plain" (plain text) -- **bible_path**: Path to Bible data file (default: "books/pol_nbg.json") +The TUI shows a progress bar counting down to the next Easter, with Orthodox +Easter as default (calculated via Meeus Julian algorithm). Use `-l/--latin` for +Roman Catholic Easter (Delambre and Butcher's algorithm). -#### TUI Settings (`[tui]` section) -- **show_quit_message**: Whether to show "Press q to quit..." message (default: -true) -- **border_style**: Box border style: "rounded", "double", "single", or -"hidden" (default: "rounded") +## Configuration -#### Formatter Settings (`[formatter]` section) -- **use_colours**: Whether to use ANSI colours in formatted output (default: -true) -- **header_format**: Header format template with variables: {book}, {chapter}, -{first_verse}, {second_verse} -- **numbered**: Whether to print each verse on a numbered line corresponding to -the verse number (default: false) -- **paragraphs**: Whether to render pilcrows (¶) as blank lines instead of -ignoring them (default: false) +`bibel` supports configuration via a TOML file at +`$XDG_CONFIG_HOME/bibel/config.toml` (default: `~/.config/bibel/config.toml`). +macOS and Windows are also supported automatically (see +[here](https://github.com/adrg/xdg) for more details). Settings include output +mode, reading mode, border style, colours, and Easter type. -#### Date Progression Settings (`[date_progression]` section) -- **verses_per_day**: Number of verses to read per day (default: 12) -- **start_date**: Start date for yearly progression (format: "1 January", empty -for current year) - -#### Easter Settings -- **easter_type**: Easter calculation type: "orthodox" (default) or "latin" - -### Example Configuration - -See `configs/config.example.toml` in the project directory for a complete example. - -### Generating Configuration - -Generate a default configuration file: - -```bash -./bibel --generate-config -``` - -### Command Line Arguments - -Command line arguments override configuration file settings: - -- `-r, --reading`: "evangelion" (four Gospels; *default*), "new_testament", -"old_testament", "bible" (default: "evangelion") -- `-p, --plain`: Output plain text without formatting or TUI -- `-f, --formatted`: Output formatted text with ANSI colours (no TUI) -- `-g, --generate-config`: Generate a default configuration file and exit -- `-c, --config`: Path to configuration file (not yet implemented) -- `-n, --numbered`: Print each verse on a numbered line corresponding to the verse number -- `-g, --paragraphs`: Render pilcrows (¶) as blank lines instead of ignoring them -- `-l, --latin`: Use Roman Catholic Easter instead of Eastern Orthodox (shows in TUI progress bar) - -## Data Format - -The Bible data is in JSON format (see `books/pol_nbg.json`) with the following -structure: -```json -{ - "metadata": { ... }, - "verses": [ - { - "book_name": "Mateusza", - "book": 40, - "chapter": 1, - "verse": 1, - "text": "¶ KSIĘGA początku Jezusa Chrystusa..." - }, - ... - ] -} -``` - -Books 40-43 correspond to the four Gospels: -- 40: Matthew (Mateusza) -- 41: Mark (Marek) -- 42: Luke (Łukasz) -- 43: John (Jana) - -## Date-Based Algorithm - -The program calculates reading position as follows: - -1. **Day of Year**: Get current day number (1-366) -2. **Verse Offset**: Multiply by 12 verses per day: `offset = (day_of_year - 1) - × 12` -3. **Modulo Wrap**: Apply modulo with total Gospel verses (3779) to cycle - yearly -4. **Position Mapping**: Walk through Gospels to find corresponding verses -5. **Lookahead Rule**: Extend to chapter end if less than 12 verses remain +Generate a default configuration: -## Easter Progress Bar + bibel --generate-config -The TUI features a progress bar showing time until the next Easter: +## How do I build bibel? -### Easter Type Selection -- **Default**: Eastern Orthodox Easter (calculated via Meeus Julian algorithm) -- **Catholic**: Roman Catholic Easter via `-l/--latin` flag (Delambre and Butcher's algorithm) +`bibel` is written in Go. External dependencies are `bubbletea` (TUI), +`lipgloss` (styling), `go-arg` (argument parsing), `viper` (configuration), and +`xdg` (XDG Base Directory support). -### Progress Bar Features -- **Visual Progress**: Filled segments (█) showing annual cycle percentage between consecutive Easters -- **Time Display**: Shows exact days, hours, and minutes until next Easter -- **Styling: Uses `lipgloss` with ANSI-coloured segments -- **Configuration**: Easter type configurable via `easter_type` field in TOML configuration +Build with: -### Easter Calculations -- **Orthodox Easter**: Uses Meeus Julian algorithm (accurate for years > 325) -- **Catholic Easter**: Uses Delambre and Butcher's algorithm (Gregorian calendar) -- **Annual Cycle Progress**: Calculated as time elapsed between consecutive Easters -- **Real-time Updates**: Progress updates continuously while TUI is running + go build ./cmd/bibel.go -## Project Structure +## Documentation -``` -. -├── cmd/bibel.go # Main CLI entry point -├── configs/ # Default configuration files -│ └── config.example.toml # The default configuration file -├── internal/ -│ ├── config.go # Reading and writing to config -│ ├── dateprogression.go # Date-based position calculation -│ ├── easterprogression.go # Easter date and progress calculation -│ ├── verse.go # Data structures -│ ├── loader.go # JSON loading and indexing -│ ├── formatter.go # Output formatting -│ └── tui/ # Terminal User Interface -│ └── model.go # bubbletea TUI model and styling -├── pkg/ -│ └── eastertime.go # Easter date calculation algorithms (Orthodox/Catholic) -├── books/pol_nbg.json # Bible data -├── go.mod # Go module dependencies -└── go.sum # Go dependency checksums -``` +For more specific information, refer to the `man` page located at +`docs/bibel.1`. It is recommended to put it in `$MANPATH` for easy access via +`man bibel` on your Linux system. -## Examples +## Licence -- **1 January**: Matthew 1:1-12 -- **2 January**: Matthew 1:13-25 (extends to end of chapter) -- **16 April**: Mark 5:41-43 -- **31 December**: Matthew 18:7-18 (year wraps around) - -## Development - -```bash -# Build -go build ./cmd/bibel.go - -# Run -./bibel -``` +This project is licensed under [European Union Public Licence +1.2](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12).
A
assets/screenshot.svg
@@ -0,0 +1,131 @@
+<svg height="567.2" width="803.2" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(32,16)"> +<filter filterUnits="userSpaceOnUse" height="575.2" id="shadow" width="811.2" x="-32" y="-24"> +<feGaussianBlur stdDeviation="12"/> +</filter> +<rect fill="#000000c0" filter="url(#shadow)" height="503.2" rx="10" ry="10" width="739.2" x="4" y="12"/> +<rect fill="#282c34" height="503.2" rx="10" ry="10" width="739.2"/> +<clipPath id="header"> +<rect height="28" width="739.2"/> +</clipPath> +<rect clip-path="url(#header)" fill="#383837" height="56" rx="10" ry="10" width="739.2"/> +<line stroke="#000000c0" stroke-width="0.5" x1="0" x2="739.2" y1="28" y2="28"/><text dominant-baseline="central" fill="#b3b3b3" font-family="system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif" font-size="14" font-weight="bold" text-anchor="middle" x="369.6" y="14">bibel</text> +<g> +<circle cx="14" cy="14" fill="#ff5f56" r="6" stroke="#ff5f56" stroke-width="0.5"/> +<circle cx="34" cy="14" fill="#ffbd2e" r="6" stroke="#ffbd2e" stroke-width="0.5"/> +<circle cx="54" cy="14" fill="#28c941" r="6" stroke="#28c941" stroke-width="0.5"/> +</g> +<svg class="terminal" font-family="JetBrains Mono, Fira Code, Source Code Pro, Cascadia Code, Consolas, Menlo, Monaco, DejaVu Sans Mono, monospace, Symbols Nerd Font Mono, Cascadia Mono" font-size="12" height="475.2" width="739.2" y="28"> +<svg fill="#dcdfe4" x="9.6" y="7.2"> +<g> +<svg height="460.8" viewBox="0 0 60 38.4" width="720"> +<g stroke-width="0.025"> +<path d="M3,34.8 H3.6 V36 H3 Z" fill="#98c379" stroke="#98c379"/> +<path d="M3.6,34.8 H51 V36 H3.6 Z" fill="#3e4451" stroke="#3e4451"/> +</g> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="14.4"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">╭────────────────────────────────────────────────────────────────────────────────────────╮</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="28.8"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="43.2"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#98c379" font-weight="bold">Marka </tspan><tspan fill="#98c379" font-weight="bold">6</tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="57.6"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#98c379" font-weight="bold">w. </tspan><tspan fill="#98c379" font-weight="bold">22-33</tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="72"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="86.4"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="100.8"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">i </tspan><tspan fill="#dcdfe4">gdy </tspan><tspan fill="#dcdfe4">weszła </tspan><tspan fill="#dcdfe4">córka </tspan><tspan fill="#dcdfe4">Herodiady, </tspan><tspan fill="#dcdfe4">oraz </tspan><tspan fill="#dcdfe4">gdy </tspan><tspan fill="#dcdfe4">zatańczyła </tspan><tspan fill="#dcdfe4">a </tspan><tspan fill="#dcdfe4">spodobała </tspan><tspan fill="#dcdfe4">się </tspan><tspan fill="#dcdfe4">Herodowi </tspan><tspan fill="#dcdfe4">i </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="115.2"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">współleżącym </tspan><tspan fill="#dcdfe4">król </tspan><tspan fill="#dcdfe4">powiedział </tspan><tspan fill="#dcdfe4">dzieweczce: </tspan><tspan fill="#dcdfe4">Poproś </tspan><tspan fill="#dcdfe4">mnie, </tspan><tspan fill="#dcdfe4">jeśli </tspan><tspan fill="#dcdfe4">coś </tspan><tspan fill="#dcdfe4">chcesz, </tspan><tspan fill="#dcdfe4">a </tspan><tspan fill="#dcdfe4">ci </tspan><tspan fill="#dcdfe4">dam. </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="129.6"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">Także </tspan><tspan fill="#dcdfe4">jej </tspan><tspan fill="#dcdfe4">przysiągł: </tspan><tspan fill="#dcdfe4">O </tspan><tspan fill="#dcdfe4">cokolwiek </tspan><tspan fill="#dcdfe4">byś </tspan><tspan fill="#dcdfe4">mnie </tspan><tspan fill="#dcdfe4">prosiła </tspan><tspan fill="#dcdfe4">dam </tspan><tspan fill="#dcdfe4">ci, </tspan><tspan fill="#dcdfe4">aż </tspan><tspan fill="#dcdfe4">do </tspan><tspan fill="#dcdfe4">połowy </tspan><tspan fill="#dcdfe4">mojego </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="144"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">królestwa. </tspan><tspan fill="#dcdfe4">Zaś </tspan><tspan fill="#dcdfe4">ona </tspan><tspan fill="#dcdfe4">wyszła </tspan><tspan fill="#dcdfe4">i </tspan><tspan fill="#dcdfe4">powiedziała </tspan><tspan fill="#dcdfe4">swojej </tspan><tspan fill="#dcdfe4">matce: </tspan><tspan fill="#dcdfe4">O </tspan><tspan fill="#dcdfe4">co </tspan><tspan fill="#dcdfe4">mam </tspan><tspan fill="#dcdfe4">prosić? </tspan><tspan fill="#dcdfe4">A </tspan><tspan fill="#dcdfe4">ona </tspan><tspan fill="#dcdfe4">rzekła: </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="158.4"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">O </tspan><tspan fill="#dcdfe4">głowę </tspan><tspan fill="#dcdfe4">Jana </tspan><tspan fill="#dcdfe4">Chrzciciela. </tspan><tspan fill="#dcdfe4">Więc </tspan><tspan fill="#dcdfe4">przyszła </tspan><tspan fill="#dcdfe4">do </tspan><tspan fill="#dcdfe4">króla </tspan><tspan fill="#dcdfe4">i </tspan><tspan fill="#dcdfe4">poprosiła </tspan><tspan fill="#dcdfe4">mówiąc: </tspan><tspan fill="#dcdfe4">Chcę, </tspan><tspan fill="#dcdfe4">abyś </tspan><tspan fill="#dcdfe4">mi </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="172.8"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">natychmiast </tspan><tspan fill="#dcdfe4">dał </tspan><tspan fill="#dcdfe4">na </tspan><tspan fill="#dcdfe4">półmisku </tspan><tspan fill="#dcdfe4">głowę </tspan><tspan fill="#dcdfe4">Jana </tspan><tspan fill="#dcdfe4">Chrzciciela. </tspan><tspan fill="#dcdfe4">Zatem </tspan><tspan fill="#dcdfe4">król </tspan><tspan fill="#dcdfe4">stał </tspan><tspan fill="#dcdfe4">się </tspan><tspan fill="#dcdfe4">zasmuconym, </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="187.2"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">lecz </tspan><tspan fill="#dcdfe4">z </tspan><tspan fill="#dcdfe4">powodu </tspan><tspan fill="#dcdfe4">przysiąg </tspan><tspan fill="#dcdfe4">i </tspan><tspan fill="#dcdfe4">współleżących, </tspan><tspan fill="#dcdfe4">nie </tspan><tspan fill="#dcdfe4">zechciał </tspan><tspan fill="#dcdfe4">jej </tspan><tspan fill="#dcdfe4">odmówić. </tspan><tspan fill="#dcdfe4">I </tspan><tspan fill="#dcdfe4">zaraz </tspan><tspan fill="#dcdfe4">posłał </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="201.6"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">kata </tspan><tspan fill="#dcdfe4">oraz </tspan><tspan fill="#dcdfe4">nakazał </tspan><tspan fill="#dcdfe4">przynieść </tspan><tspan fill="#dcdfe4">tą </tspan><tspan fill="#dcdfe4">głowę. </tspan><tspan fill="#dcdfe4">Zaś </tspan><tspan fill="#dcdfe4">on </tspan><tspan fill="#dcdfe4">poszedł, </tspan><tspan fill="#dcdfe4">ściął </tspan><tspan fill="#dcdfe4">go </tspan><tspan fill="#dcdfe4">w </tspan><tspan fill="#dcdfe4">więzieniu, </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="216"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">przyniósł </tspan><tspan fill="#dcdfe4">na </tspan><tspan fill="#dcdfe4">półmisku </tspan><tspan fill="#dcdfe4">jego </tspan><tspan fill="#dcdfe4">głowę </tspan><tspan fill="#dcdfe4">i </tspan><tspan fill="#dcdfe4">dał </tspan><tspan fill="#dcdfe4">ją </tspan><tspan fill="#dcdfe4">dzieweczce, </tspan><tspan fill="#dcdfe4">a </tspan><tspan fill="#dcdfe4">dzieweczka </tspan><tspan fill="#dcdfe4">dała </tspan><tspan fill="#dcdfe4">ją </tspan><tspan fill="#dcdfe4">swojej </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="230.4"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">matce. </tspan><tspan fill="#dcdfe4">Więc </tspan><tspan fill="#dcdfe4">kiedy </tspan><tspan fill="#dcdfe4">to </tspan><tspan fill="#dcdfe4">usłyszeli </tspan><tspan fill="#dcdfe4">jego </tspan><tspan fill="#dcdfe4">uczniowie, </tspan><tspan fill="#dcdfe4">przyszli, </tspan><tspan fill="#dcdfe4">wzięli </tspan><tspan fill="#dcdfe4">jego </tspan><tspan fill="#dcdfe4">zwłoki </tspan><tspan fill="#dcdfe4">oraz </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="244.8"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">złożyli </tspan><tspan fill="#dcdfe4">je </tspan><tspan fill="#dcdfe4">w </tspan><tspan fill="#dcdfe4">grobie. </tspan><tspan fill="#dcdfe4">A </tspan><tspan fill="#dcdfe4">apostołowie </tspan><tspan fill="#dcdfe4">zeszli </tspan><tspan fill="#dcdfe4">się </tspan><tspan fill="#dcdfe4">przy </tspan><tspan fill="#dcdfe4">Jezusie </tspan><tspan fill="#dcdfe4">oraz </tspan><tspan fill="#dcdfe4">wszystko </tspan><tspan fill="#dcdfe4">mu </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="259.2"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">oznajmili; </tspan><tspan fill="#dcdfe4">i </tspan><tspan fill="#dcdfe4">jak </tspan><tspan fill="#dcdfe4">uczynili, </tspan><tspan fill="#dcdfe4">i </tspan><tspan fill="#dcdfe4">jak </tspan><tspan fill="#dcdfe4">nauczali. </tspan><tspan fill="#dcdfe4">Zatem </tspan><tspan fill="#dcdfe4">im </tspan><tspan fill="#dcdfe4">powiedział: </tspan><tspan fill="#dcdfe4">Idźcie </tspan><tspan fill="#dcdfe4">wy </tspan><tspan fill="#dcdfe4">sami, </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="273.6"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">osobno, </tspan><tspan fill="#dcdfe4">na </tspan><tspan fill="#dcdfe4">puste </tspan><tspan fill="#dcdfe4">miejsce </tspan><tspan fill="#dcdfe4">i </tspan><tspan fill="#dcdfe4">trochę </tspan><tspan fill="#dcdfe4">odpocznijcie. </tspan><tspan fill="#dcdfe4">Bo </tspan><tspan fill="#dcdfe4">byli </tspan><tspan fill="#dcdfe4">liczni </tspan><tspan fill="#dcdfe4">co </tspan><tspan fill="#dcdfe4">przychodzili </tspan><tspan fill="#dcdfe4">i </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="288"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">odchodzili, </tspan><tspan fill="#dcdfe4">więc </tspan><tspan fill="#dcdfe4">nie </tspan><tspan fill="#dcdfe4">mieli </tspan><tspan fill="#dcdfe4">nawet </tspan><tspan fill="#dcdfe4">sposobności </tspan><tspan fill="#dcdfe4">by </tspan><tspan fill="#dcdfe4">zjeść. </tspan><tspan fill="#dcdfe4">Zatem </tspan><tspan fill="#dcdfe4">w </tspan><tspan fill="#dcdfe4">łodzi </tspan><tspan fill="#dcdfe4">odpłynęli </tspan><tspan fill="#dcdfe4">do </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="302.4"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">samotni, </tspan><tspan fill="#dcdfe4">na </tspan><tspan fill="#dcdfe4">puste </tspan><tspan fill="#dcdfe4">miejsce. </tspan><tspan fill="#dcdfe4">A </tspan><tspan fill="#dcdfe4">tłum </tspan><tspan fill="#dcdfe4">zobaczył </tspan><tspan fill="#dcdfe4">ich </tspan><tspan fill="#dcdfe4">odchodzących </tspan><tspan fill="#dcdfe4">i </tspan><tspan fill="#dcdfe4">wielu </tspan><tspan fill="#dcdfe4">go </tspan><tspan fill="#dcdfe4">poznało, </tspan><tspan fill="#dcdfe4">więc </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="316.8"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">zbiegli </tspan><tspan fill="#dcdfe4">się </tspan><tspan fill="#dcdfe4">tam </tspan><tspan fill="#dcdfe4">pieszo </tspan><tspan fill="#dcdfe4">ze </tspan><tspan fill="#dcdfe4">wszystkich </tspan><tspan fill="#dcdfe4">miast, </tspan><tspan fill="#dcdfe4">wyprzedzili </tspan><tspan fill="#dcdfe4">ich, </tspan><tspan fill="#dcdfe4">oraz </tspan><tspan fill="#dcdfe4">się </tspan><tspan fill="#dcdfe4">przy </tspan><tspan fill="#dcdfe4">nim </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="331.2"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#dcdfe4">zgromadzili. </tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="345.6"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">│</tspan><tspan> </tspan><tspan fill="#545d6d">│</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="360"> +<text xml:space="preserve" y="0.96em"><tspan fill="#545d6d">╰────────────────────────────────────────────────────────────────────────────────────────╯</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="403.2"> +<text xml:space="preserve" y="0.96em"><tspan> </tspan><tspan fill="#dcdfe4">Easter </tspan><tspan fill="#dcdfe4">in </tspan><tspan fill="#dcdfe4">378 </tspan><tspan fill="#dcdfe4">days, </tspan><tspan fill="#dcdfe4">6 </tspan><tspan fill="#dcdfe4">hours, </tspan><tspan fill="#dcdfe4">2 </tspan><tspan fill="#dcdfe4">minutes</tspan><tspan> </tspan><tspan fill="#545d6d" font-weight="bold">1.8%</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="417.6"> +<text xml:space="preserve" y="0.96em"><tspan> </tspan><tspan fill="#f7f9fc">█</tspan><tspan fill="#545d6d">░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░</tspan></text> +</svg> +<svg height="14.4" overflow="hidden" width="720" y="446.4"> +<text xml:space="preserve" y="0.96em"><tspan> </tspan><tspan fill="#545d6d" font-style="italic">Press </tspan><tspan fill="#545d6d" font-style="italic">q </tspan><tspan fill="#545d6d" font-style="italic">to </tspan><tspan fill="#545d6d" font-style="italic">quit...</tspan></text> +</svg> +</g> +</svg> +</svg> +<rect fill="none" height="503.2" rx="10" ry="10" stroke="#000000" stroke-width="0.5" width="739.2"/> +<rect fill="none" height="501.2" rx="9" ry="9" stroke="#f0f0f080" stroke-width="0.5" width="737.2" x="1" y="1"/> +</g> +<style> +@font-face { + font-family: JetBrains Mono; + font-weight: 100 800; + font-style: italic; + src: url(https://raw.githubusercontent.com/pamburus/fonts/refs/heads/main/JetBrainsMono/fonts/variable/JetBrainsMono-Italic%5Bwght%5D.woff2) format('woff2'); +} +@font-face { + font-family: JetBrains Mono; + font-weight: 100 800; + font-style: normal; + src: url(https://raw.githubusercontent.com/pamburus/fonts/refs/heads/main/JetBrainsMono/fonts/variable/JetBrainsMono%5Bwght%5D.woff2) format('woff2'); +} +</style> +</svg>
M
cmd/bibel.go
→
cmd/bibel.go
@@ -47,7 +47,7 @@ return
} // Load configuration - config, err := bible.LoadConfig(args.Verbose) + config, err := bible.LoadConfig(args.ConfigPath, args.Verbose) if err != nil { fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err) os.Exit(1)@@ -82,29 +82,26 @@ fmt.Fprintf(os.Stderr, "Error calculating today's reading position: %v\n", err)
os.Exit(1) } - // Handle plain output mode - if args.Plain { - formatter := bible.NewFormatter(bibleData) - header := formatter.FormatHeader(bookmark) - content := formatter.ExtractAndFormat(bibleData, bookmark) - fmt.Println(header) - fmt.Println(content) - return + // Determine output mode: plain, formatted, or TUI + // Plain mode forces no colours, formatted respects config + usePlain := args.Plain + useFormatted := args.Formatted + + // If no explicit mode but numbered/paragraphs flags are set, use formatted mode + if !usePlain && !useFormatted && (args.Numbered || args.Paragraphs) { + useFormatted = true } - // Handle formatted output mode - if args.Formatted { - formatter := bible.NewFormatterWithConfig(bibleData, config.Formatter.UseColours, config.Formatter.HeaderFormat) - header := formatter.FormatHeader(bookmark) - content := formatter.ExtractAndFormat(bibleData, bookmark) - fmt.Println(header) - fmt.Println(content) - return - } + // If we're outputting text (plain or formatted) + if usePlain || useFormatted { + // Determine colours: plain mode = false, formatted mode = from config + useColours := false + if useFormatted { + useColours = config.Formatter.UseColours + } - // Handle numbered/paragraph options - if args.Numbered || args.Paragraphs { - formatter := bible.NewFormatterWithFullConfig(bibleData, config.Formatter.UseColours, config.Formatter.HeaderFormat, args.Numbered, args.Paragraphs) + // Create formatter with appropriate settings + formatter := bible.NewFormatterWithFullConfig(bibleData, useColours, config.Formatter.HeaderFormat, args.Numbered, args.Paragraphs) header := formatter.FormatHeader(bookmark) content := formatter.ExtractAndFormat(bibleData, bookmark) fmt.Println(header)@@ -112,7 +109,7 @@ fmt.Println(content)
return } - // Default: TUI mode + // Default: TUI mode (no plain or formatted flags) // Check if we're in a terminal if !term.IsTerminal(int(os.Stdin.Fd())) { fmt.Fprintln(os.Stderr, "Error: Standard input is not a terminal. Use --plain or --formatted for non-interactive output.")
A
docs/bibel.1
@@ -0,0 +1,299 @@
+.\" bibel(1) - Bible verse CLI/TUI utility +.\" +.\" bibel - Display daily Bible readings with date-based progression +.\" +.\" European Union Public Licence 1.2 +.\" +.TH BIBEL 1 "April 2026" "bibel 1.0" "User Commands" +.SH NAME +bibel \- display today's Bible reading based on date progression +.SH SYNOPSIS +.B bibel +.RI [ options ] +.SH DESCRIPTION +.B bibel +is a command line interface (CLI) and terminal user interface (TUI) utility for +displaying Bible verses. Its primary purpose is as a MOTD-like program to be shown +on system boot, displaying today's Bible reading based on the current date. +.PP +The program reads 12 verses per day from the four Gospels (Matthew, Mark, Luke, +John), with a yearly progression that restarts on 1 January. It supports +multiple output modes including an interactive TUI with progress bars, +formatted ANSI-coloured text, and plain text output. +.PP +The program calculates Easter dates using either Eastern Orthodox (Meeus Julian +algorithm) or Roman Catholic (Delambre and Butcher's algorithm) methods. +.SH OPTIONS +.TP +.BI \-r " reading" , " \-\-reading=" reading +Reading mode. Must be one of: +.RS +.IP \fIevangelion\fR 15 +Four Gospels (Matthew, Mark, Luke, John). Books 40-43. This is the default. +.IP \fInew_testament\fR +Entire New Testament. Books 40-66 (Matthew through Revelation). +.IP \fIold_testament\fR +Entire Old Testament. Books 1-39 (Genesis through Malachi). +.IP \fIbible\fR +Entire Bible. Books 1-66. +.RE +.TP +.BR \-p , " \-\-plain" +Output plain text without formatting or TUI. Prints header followed by verse text. +.TP +.BR \-f , " \-\-formatted" +Output formatted text with ANSI colours. Uses colours set by terminal emulator. +.TP +.BI \-c " path" , " \-\-config=" path +Path to configuration file. Defaults to +.IR $XDG_CONFIG_HOME/bibel/config.toml +(~/.config/bibel/config.toml). +.TP +.BR \-n , " \-\-numbered" +Print each verse on a numbered line corresponding to the verse number. +.TP +.BR \-g , " \-\-paragraphs" +Render pilcrows (¶) found in source JSON Bible file as paragraph breaks with +blank lines instead of ignoring them. +.TP +.BR \-l , " \-\-latin" +Use Roman Catholic Easter calculation instead of Eastern Orthodox for the TUI +progress bar. +.TP +.B \-\-generate\-config +Generate a default configuration file and exit. File is created at the default +configuration path. +.TP +.BR \-v , " \-\-verbose" +Print additional runtime information to STDOUT, including configuration paths, +Bible file loading status, and position calculations. +.TP +.BR \-h , " \-\-help" +Display help message and exit. +.SH CONFIGURATION +.B bibel +reads configuration from a TOML file located at +.IR $XDG_CONFIG_HOME/bibel/config.toml . +Command line arguments override configuration file settings. +.PP +The configuration file has the following structure: +.PP +.nf +.RS +# Output mode: "tui" (interactive terminal UI), "formatted" (ANSI-coloured text), +# or "plain" (plain text without formatting) +output_mode = "tui" + +# Path to Bible data file (relative to executable or absolute path). If empty, +# automatically uses first JSON file found in $XDG_DATA_HOME/bibel +bible_path = "" + +# Reading mode: "evangelion" (four Gospels), "new_testament", "old_testament", "bible" +reading_mode = "evangelion" + +[tui] + # Whether to show the quit message "Press q to quit..." + show_quit_message = true + + # Box border style: "rounded", "double", "single", or "hidden" + border_style = "rounded" + +[formatter] + # Whether to use ANSI colours in formatted output mode + use_colours = true + + # Header format template + # Available variables: {book}, {chapter}, {first_verse}, {second_verse} + header_format = "{book} {chapter}\\nw. {first_verse}-{second_verse}" + + # Whether to print each verse on a numbered line with verse number + numbered = false + + # Whether to render pilcrows (¶) as blank lines instead of ignoring them + paragraphs = false + +[date_progression] + # Number of verses to read per day + verses_per_day = 12 + + # Start date for yearly progression (format: "1 January") + # Empty means 1 January of current year + start_date = "" + +# Easter type: "orthodox" (default) or "latin" (i.e., roman catholic) +easter_type = "orthodox" +.RE +.fi +.PP +To generate a default configuration file, run: +.PP +.RS +.B bibel \-\-generate\-config +.RE +.SH ALGORITHM +.B bibel +uses a date-based algorithm to determine daily readings: +.IP 1. 4 +Calculate day of year (1-366 for non-leap years, 1-367 for leap years) +.IP 2. 4 +Compute verse offset: \fIoffset = (day_of_year - 1) × verses_per_day\fR +(default verses_per_day = 12) +.IP 3. 4 +Apply modulo with total verses in selected reading mode to cycle yearly. +For evangelion mode: total verses = 3779 +.IP 4. 4 +Walk through books in selected range to find corresponding verses +.IP 5. 4 +Apply lookahead rule: if less than \fIverses_per_day\fR verses remain in chapter +after current snippet, extend to end of chapter +.PP +Book ranges for reading modes: +.TS +tab(;) allbox; +l l l. +Reading Mode;Start Book;End Book +evangelion;40 (Matthew);43 (John) +new_testament;40 (Matthew);66 (Revelation) +old_testament;1 (Genesis);39 (Malachi) +bible;1 (Genesis);66 (Revelation) +.TE +.SH JSON SCHEMA +.PP +A JSON file is used to read a Bible. The Bible has to follow this structure: +.PP +.nf +.RS +{ + "metadata": { ... }, + "verses": [ + { + "book_name": "Mateusza", + "book": 40, + "chapter": 1, + "verse": 1, + "text": "¶ KSIĘGA początku Jezusa Chrystusa..." + }, + ... + ] +} +.RE +.fi +.SH EASTER CALCULATIONS +The TUI displays a progress bar showing time until the next Easter: +.IP \(bu 3 +\fBOrthodox Easter\fR (default): Calculated using Meeus Julian algorithm, +accurate for years after 325 AD. +.IP \(bu 3 +\fBRoman Catholic Easter\fR (with \fB\-l\fR flag): Calculated using Delambre +and Butcher's algorithm for Gregorian calendar. +.PP +The progress bar shows: +.IP \(bu 3 +Visual segments (█) representing percentage of annual cycle between consecutive +Easters +.IP \(bu 3 +Exact days, hours, and minutes until next Easter +.IP \(bu 3 +Real-time updates while TUI is running +.SH EXAMPLES +.TP +.B bibel +Default TUI mode with interactive display showing Orthodox Easter progress. +.TP +.B bibel \-\-latin +Roman Catholic Easter progress bar instead of Orthodox. +.TP +.B bibel \-\-plain +Plain text output without formatting or TUI. +.TP +.B bibel \-\-formatted +Formatted output with ANSI colours. +.TP +.B bibel \-\-numbered +Numbered verses (each verse on its own line with verse number). +.TP +.B bibel \-\-paragraphs +Paragraph mode (pilcrows render as blank lines). +.TP +.B bibel \-\-numbered \-\-paragraphs +Combine numbered and paragraph modes. +.TP +.B bibel \-\-plain \-\-numbered +Plain output with numbered verses. +.TP +.B bibel \-\-generate\-config +Generate a default configuration file. +.TP +.B bibel \-\-reading new_testament \-\-formatted +Formatted output from New Testament. +.TP +.B bibel \-\-reading bible \-\-plain +Plain text output from entire Bible. +.SH FILES +.TP +.B $XDG_CONFIG_HOME/bibel/config.toml +Configuration file (default: ~/.config/bibel/config.toml). +.TP +.B $XDG_DATA_HOME/bibel/ +Directory for Bible JSON files (default: ~/.local/share/bibel/). +.TP +.B ./books/pol_nbg.json +Default Bible data file (Polish New Bible translation). +.TP +.B ./configs/config.example.toml +Example configuration file. +.SH EXIT STATUS +.PP +.B bibel +exits with: +.TP +.B 0 +on successful operation. +.TP +.B 1 +on configuration error, Bible data loading failure, or calculation error. +.SH ENVIRONMENT +.TP +.B XDG_CONFIG_HOME +Base directory for user-specific configuration files. Defaults to ~/.config. +.TP +.B XDG_DATA_HOME +Base directory for user-specific data files. Defaults to ~/.local/share. +.TP +.B TERM +Terminal type. Used for determining TUI capabilities and colour support. +.SH BUGS +Report issues at the project repository. +.SH SEE ALSO +.PP +.UR https://github.com/charmbracelet/bubbletea +bubbletea(1) +.UE , +.UR https://github.com/charmbracelet/lipgloss +lipgloss(1) +.UE , +.UR https://github.com/spf13/viper +viper(1) +.UE , +.UR https://github.com/alexflint/go-arg +go\-arg(1) +.UE , +.UR https://github.com/adrg/xdg +xdg(1) +.UE +.PP +.UR https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +European Union Public Licence 1.2 +.UE +.PP +.SH AUTHORS +Maxwell Jensen <maxwelljensen@posteo.net> +.PP +Project repository: +.UR https://codeberg.org/maxwelljens/bibel +codeberg.org/maxwelljens/bibel +.UE +.SH COPYRIGHT +Copyright \(co 2026 Maxwell Jensen. +.br +This program is licensed under the European Union Public Licence 1.2.
M
internal/config.go
→
internal/config.go
@@ -106,19 +106,27 @@ return cfg
} // LoadConfig loads configuration from file and environment -func LoadConfig(verbose bool) (*Config, error) { +// If configPath is empty, uses default XDG config location +// LoadConfig loads configuration from file and environment +// If configPath is empty, uses default XDG config location +func LoadConfig(configPath string, verbose bool) (*Config, error) { logger := NewVerboseLogger(verbose) logger.Section("Loading Configuration") // Set up viper viper.SetConfigName("config") // Name of config file (without extension) viper.SetConfigType("toml") // REQUIRED if the config file does not have the extension in the name - // Add XDG config paths - configPath := filepath.Join(xdg.ConfigHome, "bibel") - viper.AddConfigPath(configPath) - - // Also check current directory for local config - viper.AddConfigPath(".") + // If a specific config file path was provided, use it + if configPath != "" { + // Use the provided config file directly + viper.SetConfigFile(configPath) + } else { + // Use default XDG config path + xdgConfigPath := filepath.Join(xdg.ConfigHome, "bibel") + viper.AddConfigPath(xdgConfigPath) + // Also check current directory for local config + viper.AddConfigPath(".") + } // Set defaults defaultCfg := DefaultConfig()@@ -139,7 +147,12 @@ // Read in config file (if it exists)
if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { // Config file not found; we'll use defaults - logger.Info("No configuration file found at %s/config.toml", configPath) + if configPath != "" { + logger.Info("No configuration file found at %s", configPath) + } else { + xdgConfigPath := filepath.Join(xdg.ConfigHome, "bibel") + logger.Info("No configuration file found at %s/config.toml", xdgConfigPath) + } logger.Info("Using default configuration. Run 'bibel --generate-config' to create a default config file.") } else { // Config file was found but another error was produced@@ -198,6 +211,7 @@ return &cfg, nil
} // SaveConfig saves the configuration to file +// SaveConfig saves the configuration to file func SaveConfig(cfg *Config) error { // Set up viper with current config viper.Set("reading_mode", cfg.ReadingMode)@@ -214,13 +228,13 @@ viper.Set("date_progression.verses_per_day", cfg.DateProgression.VersesPerDay)
viper.Set("date_progression.start_date", cfg.DateProgression.StartDate) // Ensure config directory exists - configPath := filepath.Join(xdg.ConfigHome, "bibel") - if err := os.MkdirAll(configPath, 0755); err != nil { + xdgConfigPath := filepath.Join(xdg.ConfigHome, "bibel") + if err := os.MkdirAll(xdgConfigPath, 0755); err != nil { return fmt.Errorf("error creating config directory: %w", err) } // Write config file - configFile := filepath.Join(configPath, "config.toml") + configFile := filepath.Join(xdgConfigPath, "config.toml") if err := viper.WriteConfigAs(configFile); err != nil { return fmt.Errorf("error writing config file: %w", err) }