Skip to content
Closed
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
201 changes: 129 additions & 72 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,76 +8,133 @@ permissions:
contents: write
jobs:
build:
runs-on: windows-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
artifact_name: paperback-windows
artifact_paths: |
target/release/paperback_windows.zip
target/release/paperback_setup.exe
- os: ubuntu-latest
artifact_name: paperback-linux
artifact_paths: target/release/paperback_linux.zip
- os: macos-latest
artifact_name: paperback-macos
artifact_paths: target/release/paperback_mac.zip
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup MSVC
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: rustfmt, clippy
- name: Install CMake, Ninja, and Pandoc
run: |
choco install cmake ninja pandoc -y
- name: Install Gettext via MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: >-
gettext
mingw-w64-ucrt-x86_64-gettext-tools
- name: Build
run: |
$env:PATH = "$env:PATH;C:\msys64\ucrt64\bin;C:\msys64\usr\bin"
cargo release
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: paperback-build
path: |
target/release/paperback.zip
target/release/paperback_setup.exe
retention-days: 30
- name: Get latest tag reachable from HEAD
id: get_tag
shell: bash
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Generate release notes
id: release_notes
shell: bash
run: |
PREV_TAG="${{ steps.get_tag.outputs.tag }}"
if [ -z "$PREV_TAG" ]; then
COMMITS=$(git log -1 --pretty=format:"- %h: %s" HEAD)
else
COMMITS=$(git log --pretty=format:"- %h: %s" "$PREV_TAG"..HEAD)
fi
COMMITS="${COMMITS//'%'/'%25'}"
echo "commits<<EOF" >> $GITHUB_OUTPUT
echo "$COMMITS" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create or update latest release
uses: softprops/action-gh-release@v1
with:
tag_name: latest
name: Development Build
body: |
## Commits since last release
${{ steps.release_notes.outputs.commits }}
files: |
target/release/paperback.zip
target/release/paperback_setup.exe
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0

- name: Setup MSVC
if: matrix.os == 'windows-latest'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: rustfmt, clippy

- name: Install Windows build dependencies
if: matrix.os == 'windows-latest'
run: |
choco install cmake ninja pandoc -y

- name: Install Gettext via MSYS2
if: matrix.os == 'windows-latest'
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: >-
gettext
mingw-w64-ucrt-x86_64-gettext-tools

- name: Install Linux build dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build pandoc gettext pkg-config libgtk-3-dev libexpat1-dev libtiff-dev
sudo apt-get install -y libwebkit2gtk-4.1-dev || sudo apt-get install -y libwebkit2gtk-4.0-dev

- name: Install macOS build dependencies
if: matrix.os == 'macos-latest'
run: |
brew install cmake ninja pandoc gettext
echo "$(brew --prefix gettext)/bin" >> "$GITHUB_PATH"

- name: Build (Windows)
if: matrix.os == 'windows-latest'
run: |
$env:PATH = "$env:PATH;C:\msys64\ucrt64\bin;C:\msys64\usr\bin"
cargo release

- name: Build (Linux/macOS)
if: matrix.os != 'windows-latest'
run: cargo release

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_paths }}
retention-days: 30

release:
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: paperback-*
path: dist
merge-multiple: true

- name: Get latest tag reachable from HEAD
id: get_tag
shell: bash
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
echo "tag=$TAG" >> $GITHUB_OUTPUT

- name: Generate release notes
id: release_notes
shell: bash
run: |
PREV_TAG="${{ steps.get_tag.outputs.tag }}"
if [ -z "$PREV_TAG" ]; then
COMMITS=$(git log -1 --pretty=format:"- %h: %s" HEAD)
else
COMMITS=$(git log --pretty=format:"- %h: %s" "$PREV_TAG"..HEAD)
fi
COMMITS="${COMMITS//'%'/'%25'}"
echo "commits<<EOF" >> $GITHUB_OUTPUT
echo "$COMMITS" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Create or update latest release
uses: softprops/action-gh-release@v1
with:
tag_name: latest
name: Development Build
body: |
## Commits since last release
${{ steps.release_notes.outputs.commits }}
files: dist/**
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,24 @@

To build, you'll need cargo, as well as CMake and Ninja for building wxDragon.

```batch
On Linux, you will also need clang/libclang (for bindgen), GTK3, WebKitGTK, Expat, and TIFF development packages (for example `clang`, `libclang-dev`, `libgtk-3-dev`, `libwebkit2gtk-4.1-dev`, `libexpat1-dev`, and `libtiff-dev` on Debian/Ubuntu).

```bash
cargo build --release
```

to generate the binary in the release folder, and

```batch
```bash
cargo release
```

`cargo release` produces platform-specific archives:

* Windows: `paperback_windows.zip` and `paperback_setup.exe`
* Linux: `paperback_linux.zip`
* macOS: `paperback_mac.zip`

### Optional tools:

The following tools aren't required to build a functioning Paperback on a basic level, but will help you make a complete release build.
Expand Down
22 changes: 20 additions & 2 deletions po/paperback.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: paperback 0.8.0\n"
"Report-Msgid-Bugs-To: https://github.com/trypsynth/paperback/issues\n"
"POT-Creation-Date: 2026-02-07 11:18-0700\n"
"POT-Creation-Date: 2026-02-11 12:18-0600\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down Expand Up @@ -297,6 +297,9 @@ msgstr ""
msgid "Ready"
msgstr ""

msgid "File menu"
msgstr ""

msgid "&Password:"
msgstr ""

Expand Down Expand Up @@ -428,6 +431,21 @@ msgstr ""
msgid "Update failed"
msgstr ""

msgid ""
"Update downloaded to:\n"
"{}\n"
"Open it to finish installing."
msgstr ""

msgid ""
"Update downloaded to:\n"
"{}\n"
"Please open it manually to finish installing."
msgstr ""

msgid "Update Downloaded"
msgstr ""

msgid "Unsupported format selected."
msgstr ""

Expand Down Expand Up @@ -991,7 +1009,7 @@ msgid "Wrapping to end. "
msgstr ""

#, c-format
msgid "%s Heading level %d"
msgid "Heading level %d: %s"
msgstr ""

#, c-format
Expand Down
45 changes: 38 additions & 7 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,15 +877,46 @@ pub fn get_sorted_document_list(config: &ConfigManager, open_paths: &[String], f

fn get_config_path() -> String {
let exe_dir = get_exe_directory();
let is_installed = (0..10).any(|i| exe_dir.join(format!("unins{i:03}.exe")).exists());
if is_installed {
if let Some(appdata) = env::var_os("APPDATA") {
let config_dir = PathBuf::from(appdata).join("Paperback");
let _ = fs::create_dir_all(&config_dir);
return config_dir.join("Paperback.ini").to_string_lossy().to_string();
let exe_config = exe_dir.join("Paperback.ini");
if exe_config.exists() {
return exe_config.to_string_lossy().to_string();
}
#[cfg(target_os = "windows")]
{
let is_installed = (0..10).any(|i| exe_dir.join(format!("unins{i:03}.exe")).exists());
if is_installed {
if let Some(appdata) = env::var_os("APPDATA") {
let config_dir = PathBuf::from(appdata).join("Paperback");
if fs::create_dir_all(&config_dir).is_ok() {
return config_dir.join("Paperback.ini").to_string_lossy().to_string();
}
}
}
return exe_config.to_string_lossy().to_string();
}
#[cfg(target_os = "macos")]
{
if let Some(home) = env::var_os("HOME") {
let config_dir = PathBuf::from(home).join("Library").join("Application Support").join("Paperback");
if fs::create_dir_all(&config_dir).is_ok() {
return config_dir.join("Paperback.ini").to_string_lossy().to_string();
}
}
return exe_config.to_string_lossy().to_string();
}
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
{
let config_root = env::var_os("XDG_CONFIG_HOME")
.map(PathBuf::from)
.or_else(|| env::var_os("HOME").map(|home| PathBuf::from(home).join(".config")));
if let Some(root) = config_root {
let config_dir = root.join("paperback");
if fs::create_dir_all(&config_dir).is_ok() {
return config_dir.join("Paperback.ini").to_string_lossy().to_string();
}
}
exe_config.to_string_lossy().to_string()
}
exe_dir.join("Paperback.ini").to_string_lossy().to_string()
}

fn get_exe_directory() -> PathBuf {
Expand Down
5 changes: 4 additions & 1 deletion src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,11 @@ mod tests {

#[test]
fn normalize_cli_path_handles_absolute_and_relative() {
#[cfg(windows)]
let abs = Path::new("C:\\nonexistent_abs_path");
assert_eq!(normalize_cli_path(abs), PathBuf::from("C:\\nonexistent_abs_path"));
#[cfg(not(windows))]
let abs = Path::new("/nonexistent_abs_path");
assert_eq!(normalize_cli_path(abs), PathBuf::from(abs));
let rel = Path::new("nonexistent_rel_path");
let expected = env::current_dir().unwrap().join(rel);
assert_eq!(normalize_cli_path(rel), expected);
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg_attr(not(test), windows_subsystem = "windows")]
#![cfg_attr(all(not(test), target_os = "windows"), windows_subsystem = "windows")]
#![warn(clippy::all, clippy::nursery, clippy::pedantic)]

mod config;
Expand Down
13 changes: 12 additions & 1 deletion src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,21 @@ mod tests {
fn extracts_title_from_path() {
assert_eq!(extract_title_from_path("foo.txt"), "foo");
assert_eq!(extract_title_from_path("/home/quin/books/worm.epub"), "worm");
assert_eq!(extract_title_from_path("C:\\Users\\Quin\\Desktop\\file.log"), "file");
assert_eq!(extract_title_from_path("/path/with/trailing/slash/"), "Untitled");
assert_eq!(extract_title_from_path("C:\\path\\with\\trailing\\slash\\"), "Untitled");
assert_eq!(extract_title_from_path(" spaced.txt "), "spaced");
assert_eq!(extract_title_from_path(""), "Untitled");
}

#[cfg(windows)]
#[test]
fn extracts_title_from_windows_path() {
assert_eq!(extract_title_from_path("C:\\Users\\Quin\\Desktop\\file.log"), "file");
}

#[cfg(not(windows))]
#[test]
fn extracts_title_from_windows_like_path_non_windows() {
assert_eq!(extract_title_from_path("C:\\Users\\Quin\\Desktop\\file.log"), "C:\\Users\\Quin\\Desktop\\file");
}
}
1 change: 1 addition & 0 deletions src/ui.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod accessibility;
mod app;
mod dialogs;
mod document_manager;
Expand Down
Loading