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
17 changes: 8 additions & 9 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,20 @@ jobs:
- name: Inject version
shell: bash
run: |
# Version priority:
# 1. Manual input from workflow_dispatch
# 2. Git tag (tagged push)
# 3. Branch name (non-main branches = test build)
# 4. Fallback
# Version precedence:
# 1. Explicit workflow/build version (workflow_dispatch input)
# 2. Exact Git tag (tagged push)
# 3. Authoritative source version from main.py + "-test" marker
# Ordinary branch builds must never regress the embedded version to
# an older Git tag or 0.0.0-dev.

if [[ -n "${{ github.event.inputs.version_override }}" ]]; then
VERSION="${{ github.event.inputs.version_override }}"
elif [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
VERSION="${GITHUB_REF_NAME#v}"
elif [[ "${GITHUB_REF_NAME}" != "main" ]]; then
BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
VERSION="${BRANCH//\//-}-test"
else
VERSION="0.0.0-dev"
SOURCE_VERSION="$(python scripts/inject_version.py --source-version)"
VERSION="${SOURCE_VERSION}-test"
fi
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
python scripts/inject_version.py "$VERSION"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ OpenReader-Test-Cert.cer
.env
.env.*
!.env.example
test-builds/
*.log
*.pem
*.key
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This repository is a local-first desktop PDF utility. Keep maintenance changes b

## Product Boundaries

**Current version:** v1.2.5 (2026-07-03) — Icon pipeline, branding docs, site deployment
**Current version:** v1.2.7 (2026-08-02) — Store-aware updates, default-reader UX, Windows file associations

- Do not add new user-facing PDF features unless the task explicitly asks for them.
- Do not change the local-first privacy philosophy.
Expand Down
37 changes: 29 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Changelog

## v1.2.5 — File Association & Branding — 2026-08-02

- **Version:** Bumped `__version__` to `1.2.5`, MSIX version to `1.2.5.0`; aligned the MCP package, packaging manifests, and release configuration to a single version source. `scripts/inject_version.py` now propagates the version to `main.py`, `packages/mcp-server/pyproject.toml`, and both MSIX manifests.
- **Added:** `.pdf` file-type association registered in the MSIX manifest (`windows.fileTypeAssociation`) — a Store/MSIX install now appears under **Open with** and **Default apps** with the OpenReader name and icon. The legacy installer sets `ChangesAssociations=yes` so Windows refreshes its association cache.
- **Branding:** Open Product Family alignment — OPEN/Reader header lockup, dark/light themed header icon, OpenPalette canonical spec, 155 icon assets regenerated from the source PNG via Lanczos with RGBA transparency fix, and reader.kovina.org deployed to Cloudflare Pages.
- **Fixed:** Recent-files were persisted under an accidental `***` settings key — migrated to `recentFiles`, with automatic upgrade for existing users.
- **Verification:** Packaging validation test asserts the MSIX `.pdf` association, frozen identity, and version alignment across manifest/appinstaller/MCP package.

## v1.2.7 — Store-Aware Updates — 2026-08-02

- **Added:** Install-source detection (`pdfreader_lib/install_source.py`) — source, MSIX/Store, Setup.exe, or portable ZIP. Packaged execution is detected via the Windows package API (`GetCurrentPackageFamilyName`), with the `WindowsApps` path as a fallback.
- **Changed:** Store/MSIX installs no longer query GitHub — updates are shown as managed by the Microsoft Store with an **Open Microsoft Store** button. GitHub installs keep release detection with current + latest versions shown.
- **Added:** Proper **Software Updates** dialog (replaces the inline message box) with channel-aware content: Store-managed info, release notes, and a clean offline/error state with **Try Again**.
- **Fixed:** "Skip This Version" now persists (`updateSkipVersion`) so the same release isn't re-announced; interactive checks record `updateLastChecked`.
- **Fixed:** Build scripts no longer regress the embedded version to an older Git tag — precedence is explicit build version → exact Git tag → authoritative source `__version__` (`scripts/build_windows.ps1`, `scripts/build_macos.sh`, `.github/workflows/build-windows.yml`).
- **Verification:** Channel detection, skip gating, and semantic-version safety (including `1.2.7-test` vs `1.2.4`/`1.2.7`/`1.2.8`); full suite green.

## v1.2.6 — Default Reader Experience — 2026-08-02

- **Added:** Detect whether OpenReader is the current default PDF handler by reading Windows' `UserChoice` ProgID (`pdfreader_lib/win_default_apps.py`) — ownership resolved via the AppUserModelId for MSIX installs and the `OpenReaderPDF` ProgID for the legacy installer.
- **Added:** Settings dialog (**File → Settings…**) with a Files / Default Apps section — shows the current PDF default, a "Set OpenReader as Default PDF Reader" button that opens the Windows Default Apps page (Windows keeps the final confirmation; OpenReader never writes the default), and a recovery message when the association isn't fully registered.
- **Added:** First-launch prompt to set OpenReader as the default — Set as default / Maybe later / Do not ask again, persisted in QSettings.
- **Fixed:** Default-handler detection now resolves ProgIDs across both `HKCU` and `HKLM` — an admin Inno install registers `OpenReaderPDF` under `HKLM\Software\Classes`, so the Settings recovery warning no longer appears when OpenReader is already the default. Ownership is verified via the registered open command and the AppUserModelId, not a single hardcoded ProgID. When OpenReader is the default, Settings shows a confirmation and the action becomes the neutral **Open Default Apps**.
- **Verification:** 15 unit tests covering Inno (HKCU and HKLM), MSIX (AUMID in either hive), shell-command resolution, another app, unknown/missing, unresolved entries, friendly-name lookup, and non-Windows neutrality.

## v1.2.4 — Toolbar Icon Redesign — 2026-06-21

- **Version:** Bumped `__version__` to `1.2.4`, MSIX version to `1.2.4.0`.
Expand All @@ -10,14 +35,6 @@

## v1.2.3 — Reader UX Polish — 2026-06-20

- **Version:** Bumped `__version__` to `1.2.3`, MSIX version to `1.2.3.0`.
- **Added:** Default Fit Page on open — all PDFs (normal open, recent files, session restore, new tab) now start in Fit view so the first page fits cleanly inside the document viewport. Uses both width and height constraints for true Fit Page behavior.
- **Added:** Ctrl+Mouse Wheel zoom — scroll up to zoom in, scroll down to zoom out. Works when the PDF viewer has focus. Page scrolling is suppressed while Ctrl is held.
- **Changed:** Zoom buttons redesigned from plain text characters to bold QPainter-drawn vector icons — clear bar, cross, and fit-arrow symbols that render crisply at any size or resolution.
- **Changed:** Annotation buttons (`Copy`, `HL`, `UL`, `ST`, `📝`) replaced with recognizable vector icons — overlapping document pages, highlighter pen, underlined U, strikethrough S, and notepad with pin. The 📝 emoji (reported as "looking like a glue stick") is no longer used.
- **Fixed:** Zoom buttons now have proper hover/press/checked visual feedback via stylesheet. Annotation buttons get consistent hover/press states.
- **Fixed:** Missing `QPen` and `QPointF` imports added for QPainter icon drawing.

- **Version:** Bumped `__version__` to `1.2.3`, MSIX version to `1.2.3.0`.
- **Added:** Default Fit Page on open — all PDFs (normal open, recent files, session restore, new tab) now start in Fit view so the first page fits cleanly inside the document viewport. Uses both width and height constraints for true Fit Page behavior.
- **Added:** Ctrl+Mouse Wheel zoom — scroll up to zoom in, scroll down to zoom out. Works when the PDF viewer has focus. Page scrolling is suppressed while Ctrl is held.
Expand Down Expand Up @@ -169,6 +186,8 @@
- **Windows installer publisher docs** — README now explains "Unknown Publisher" warning and that code-signing requires an EV certificate.
- **9 new regression tests** — 28 total, all passing. Covers: re-entrant open guard, blank tab, session checkbox, compress guard, post-update verification, unsigned publisher docs, clean cancel message.

## v1.1.0 — MCP Server Release — 2026-06-16

- **Version:** Bumped `__version__` to `1.1.0-dev`.
- **MCP server** (`pdfreader_lib/mcp_server.py`) — new Model Context Protocol server exposing 14 PDF operations as tools for AI agents:
- **Reading:** `extract_text`, `get_page_text`, `get_metadata`, `get_page_count`
Expand All @@ -183,6 +202,8 @@
- **Roadmap:** v1.1.0 marked as current. v1.1.0-plan.md updated to note MCP server shipped; visual polish items deferred to v1.2.0.
- All operations are local. No cloud dependencies, no code changes to the existing GUI.

## v1.0.6 — Windows Installed-App Verification — 2026-06-15

- **Version:** Bumped `__version__` to `1.0.6-dev`.
- **Branch:** `windows-installed-app-verification-v1.0.6`

Expand Down
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Overview

**Latest update:** v1.2.5Icon pipeline, branding, site deployment (2026-07-03)
**Latest update:** v1.2.7Store-aware updates, default-reader UX, Windows file associations (2026-08-02)

Privacy-first, local-only desktop PDF utility for Windows.
macOS experimental; Linux unsupported.
Expand All @@ -11,8 +11,11 @@ macOS experimental; Linux unsupported.

| Version | Date | Summary |
|---------|------|---------|
| v1.2.5 | 2026-07-02 | Open Product Family branding alignment — header lockup, docs/BRANDING.md, icon generation pipeline, dark/light theme icon |
| v1.2.7 | 2026-08-02 | Store-aware updates — install-source detection, channel-aware Software Updates dialog, persisted skip + last-checked |
| v1.2.6 | 2026-08-02 | Default Reader Experience — Windows UserChoice detection, Settings dialog, first-launch default prompt |
| v1.2.5 | 2026-08-02 | Windows File Association Fix — MSIX `.pdf` association, ChangesAssociations, single-source version injector, changelog cleanup |
| v1.2.5 | 2026-07-03 | OpenPalette canonical spec, 155 assets from PNG via Lanczos, RGBA transparency fix, dual-image CSS dark/light transition, reader.kovina.org deployed to Cloudflare Pages |
| v1.2.5 | 2026-07-02 | Open Product Family branding alignment — header lockup, docs/BRANDING.md, icon generation pipeline, dark/light theme icon |
| v1.2.4 | 2026-06-28 | (previous release) |

## Architecture Constraints
Expand Down
38 changes: 29 additions & 9 deletions docs/RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,22 @@ OpenReader uses semantic version tags to publish packaged builds.
## Version Source of Truth

- `__version__` in `main.py` is the canonical source. Set it to the next release version.
- `scripts/inject_version.py` propagates the version to every canonical source so
they stay in lockstep: `main.py`, `packages/mcp-server/pyproject.toml`,
`packages/mcp-server/src/openreader_mcp.egg-info/PKG-INFO`, and both MSIX
manifests (`AppxManifest.xml` / `AppInstaller.xml`, mapped to a 4-part
version). Run `python scripts/inject_version.py X.Y.Z` after bumping `main.py`.
- MSIX manifests are only rewritten for strict semver versions; dev/test builds
(`-dev`, `-test`) bump the code/MCP sources but leave the manifests to CI.
- Tags must use the format `vMAJOR.MINOR.PATCH`, for example `v1.2.2`.
- The injected runtime version removes the leading `v`, so `v1.2.2` becomes `__version__ = "1.2.2"` in packaged builds.
- CI injects the tag version for release builds via `scripts/inject_version.py`.
- **Build version precedence** (local `scripts/build_windows.ps1`,
`scripts/build_macos.sh`, and the CI `build-windows.yml` workflow):
1. explicit workflow/build version (`version_override` / `BUILD_VERSION`)
2. exact Git tag on HEAD (`git describe --exact-match`)
3. authoritative source version from `main.py` `__version__`
Ordinary branch builds never regress the embedded version to an older Git
tag or `0.0.0-dev`; CI test builds append `-test` to the source version.

## Release Architecture

Expand Down Expand Up @@ -54,16 +67,23 @@ no separate code-signing certificate is needed.

## Update Detection

The app's Help → Check for Updates:

1. Calls `https://api.github.com/repos/sparshsam/openreader/releases/latest`
2. Reads `tag_name` and compares against `__version__`
3. If a newer version exists, shows a dialog with release notes
4. User clicks "Open Releases Page" → browser opens GitHub Releases
5. User downloads the MSIX (or Setup.exe) and installs manually
Update behaviour is channel-aware (v1.2.7). `pdfreader_lib/install_source.py`
detects how OpenReader was installed:

- **MSIX / Microsoft Store** — the app never calls GitHub. Help → Check for
Updates shows the Software Updates dialog ("Updates are managed by the
Microsoft Store") with an *Open Microsoft Store* button.
- **Setup.exe / portable ZIP** — the app queries GitHub:
1. Calls `https://api.github.com/repos/sparshsam/openreader/releases/latest`
2. Reads `tag_name` and compares against `__version__`
3. If newer, shows the Software Updates dialog with current → latest, release
notes, and a last-checked timestamp
4. *Skip This Version* persists (`updateSkipVersion`) so the same release
isn't re-announced; *Open Releases Page* opens GitHub Releases
- **Source** — no auto-update; developers `git pull` and rebuild.

Source builds usually run with a `-dev` version and are not the primary update
target. Developers should update source builds with `git pull` and rebuild locally.
target.

## Validation Checklist

Expand Down
15 changes: 11 additions & 4 deletions docs/VERSIONING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ OpenReader follows [Semantic Versioning 2.0](https://semver.org/):

## Current Version

The current version is tracked in the `__version__` variable in `main.py`.

- **Source builds** — update `__version__` in `main.py` to match the target release.
- **Packaged releases** — the version can be overridden from the Git tag during the CI release workflow (see `scripts/inject_version.py`).
`__version__` in `main.py` is the canonical source of truth. All other version
sources are derived from it by `scripts/inject_version.py`, which rewrites
`main.py`, `packages/mcp-server/pyproject.toml`, the MCP `egg-info/PKG-INFO`,
and both MSIX manifests (`AppxManifest.xml` / `AppInstaller.xml`, 4-part form)
in one pass.

- **Source builds** — set `__version__` in `main.py` to the target release,
then run `python scripts/inject_version.py X.Y.Z`.
- **Packaged releases** — CI injects the tag version via
`scripts/inject_version.py`; MSIX manifests are only rewritten for strict
semver versions (dev/test builds bump code/MCP sources only).

## Tag Format

Expand Down
60 changes: 60 additions & 0 deletions docs/store-acceptance-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Post-Release Store Acceptance Checklist

The MSIX package cannot be produced or signed locally: production signing uses
the Microsoft Store identity (no self-procured code-signing certificate), and
the Windows SDK `MakeAppx.exe`/Store signing pipeline runs in Partner Center.
This checklist covers the Store-specific validation that must happen **after**
the `v1.2.7` MSIX is submitted and signed by the Store.

Frozen identity (do not alter):
| Field | Value |
|-------|-------|
| Identity Name | `SparshSam.OpenReader` |
| Publisher | `CN=E6186421-BF8A-47E0-A89C-0F513DFF91C0` |
| Package Family Name | `SparshSam.OpenReader_yh0byntbzd2qw` |
| Store ID | `9MXDVW2645LL` |

## On a clean Windows machine, after Store install

- [ ] Install from the Microsoft Store; app launches and reports **v1.2.7** (Help → About).
- [ ] `Get-AppxPackage` shows the package with the frozen identity and PFN.
- [ ] The process runs with a package identity — install-source detection
reports the Store channel (no GitHub request on launch; Help → Check for
Updates shows the Store-managed dialog with **Open Microsoft Store**).

### File association (v1.2.5)
- [ ] Right-click a `.pdf` → **Open with** lists **OpenReader** with the correct icon.
- [ ] **Settings → Apps → Default apps** lists OpenReader once, named `OpenReader`,
file-type description "PDF Document".
- [ ] Double-clicking a `.pdf` opens it in OpenReader.
- [ ] With OpenReader running, opening a second `.pdf` routes to the existing
instance as a new tab.
- [ ] Paths with spaces / Unicode / parentheses / long names open correctly.

### Default reader (v1.2.6)
- [ ] **File → Settings → Files / Default Apps** reads the current handler from
`UserChoice` and shows "OpenReader is the default PDF reader" — **no**
recovery warning.
- [ ] Setting another app as default then reopening Settings shows that app's
name, and the recovery message appears only if OpenReader's association
is genuinely missing.
- [ ] First-launch prompt: **Set as default** opens Default Apps; **Do not ask
again** persists across launches.

### Updates (v1.2.7)
- [ ] Store install never queries `api.github.com` (silent launch check and
Help → Check for Updates both use the Store channel).
- [ ] **Open Microsoft Store** button opens the Store listing.

## Sideloaded MSIX (developer mode, for pre-submission testing)

- [ ] `Add-AppxPackage` the unsigned MSIX from a GitHub test build succeeds in
Developer Mode.
- [ ] Because sideloaded and Store packages both run with an AppX identity,
install-source detection reports the same Store channel — expected.

## Out of scope

- Do not weaken or replace the frozen identity.
- Do not publish a GitHub Release or submit to Partner Center without explicit
instruction.
Loading