Metadata-Version: 2.4
Name: reflex-pdf
Version: 0.0.2
Summary: Reflex PDF viewer component and app.
License-Expression: Apache-2.0
Project-URL: homepage, https://GitHub.Com/ArtificialHumanoid/reflex-pdf
Keywords: reflex,reflex-components
Classifier: Development Status :: 4 - Beta
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: reflex>=0.8.10
Requires-Dist: websocket-client>=1.8
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"

# Reflex PDF Viewer + App

This repository includes the source (under `/src`) for a Python package `reflex_pdf` that provides both

- a reusable Reflex custom component package `reflex_pdf.components` and
- a standalone Reflex application `reflex_pdf_app.apps`.

## Prerequisites
- Python, including `pip`.

## `reflex-pdf.components`

A PDF viewer component for [Reflex](https://reflex.dev), built on top of `react-pdf`.

### Install

```bash
pip install reflex-pdf
```

### Use in your Reflex app

```python
import reflex as rx
from reflex_pdf import Document, Page


class State(rx.State):
    current_page: int = 1
    n_pages: int = 1

    @rx.event
    def load_success(self, info: dict):
        self.n_pages = info.get("numPages", 1)


def index():
    return rx.vstack(
        rx.heading("Reflex PDF preview", size="8"),
        Document.create(
            Page.create(page_number=State.current_page),
            file="https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf",
            on_load_success=State.load_success,
        ),
    )


app = rx.App()
app.add_page(index)
```

Add this to your `rxconfig.py`:

```python
import reflex as rx

config = rx.Config(
    app_name="your_app",
    frontend_packages=["react-pdf@9.1.1"],
)
```

## Standalone app: `reflex_pdf_app`

Run from the repository root:

```bash
python -m reflex_pdf.apps.run_app
```

This script will:

1. Prepare runtime directories for Reflex state and build artifacts
2. Ensure bun is available for Reflex frontend builds
3. Ensure Node.js is available (>= 20.19.0 in dev mode, >= 22.14.0 in prod single-port mode)
4. Start the app in `src/reflex_pdf/apps/` using the current Python environment

Run modes:
- local default: `dev` mode on frontend `3000` and backend `8000`
- Codespaces default: `prod single-port` mode on `3000`

Environment overrides:
- `REFLEX_RUN_MODE`:
  - `dev`
  - `prod-single-port`
- `REFLEX_TRANSPORT`:
  - `websocket`
  - `polling`
- `REFLEX_FRONTEND_PORT` and `REFLEX_BACKEND_PORT` (dev mode)
- `REFLEX_SINGLE_PORT` (prod single-port mode)

For Codespaces, single-port mode avoids separate backend tunnel auth for `/_event`.
The launcher defaults `REFLEX_TRANSPORT=polling` in Codespaces for compatibility with forwarded-port browser sessions.


## Backend event endpoint

This app already uses Reflex's built-in backend event endpoint (`/_event`) for all state updates (for example `State.set_pdf_url`).

Because `/_event` is Reflex's internal event transport rather than a FastAPI route, it is not exposed through Swagger/OpenAPI by default.

For MFE-style integration, the host can set the PDF URL from the same browser session in either of these ways:

1. Initial URL in iframe source query string:
   - `https://<viewer-host>/?pdf=<encoded_pdf_url>`
2. Runtime updates via `postMessage` to the iframe:

```js
iframe.contentWindow.postMessage(
  { type: "reflex-pdf:set-url", url: "https://example.com/file.pdf" },
  "*",
);
```

Both approaches update only that user session (no separate backend request needed).


> Query-string rule: only one parameter is accepted (`pdf`). If the URL contains more than one parameter, the app shows: "At most one query parameter is supported (?pdf=<url_encoded_pdf_url>). If your PDF URL contains query arguments, URL encode the entire value."

Example that must be encoded:
- raw (invalid): `/?pdf=https://example.com/file.pdf?token=abc&download=1`
- encoded (valid): `/?pdf=https%3A%2F%2Fexample.com%2Ffile.pdf%3Ftoken%3Dabc%26download%3D1`


The viewer uses a single URL state (`pdf_url`) for both initialization and runtime updates:
- `?pdf=...` initializes `pdf_url` when the page loads.
- `postMessage` with `{ type: "reflex-pdf:set-url", url }` updates that same `pdf_url` in-session.

For automation/integration testing outside the browser session, you can post the same event with `post_pdf_url_event`:

```python
from reflex_pdf.apps.event_test_client import post_pdf_url_event

post_pdf_url_event("http://127.0.0.1:3000", "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")
```

## Development

```bash
pip install -e .
```
