Metadata-Version: 2.4
Name: ipynb-local-viewer
Version: 0.1.1
Summary: A smooth local reader for saved Jupyter notebooks with lazy media output streaming.
Author-email: Masoud KA <pypi@masoudka.com>
Maintainer-email: masoudka <pypi@masoudka.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/msdkhairi/ipynb-viewer
Project-URL: Repository, https://github.com/msdkhairi/ipynb-viewer
Project-URL: Issues, https://github.com/msdkhairi/ipynb-viewer/issues
Project-URL: Changelog, https://github.com/msdkhairi/ipynb-viewer/blob/main/CHANGELOG.md
Keywords: jupyter,notebook,viewer,flask,ipynb
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Framework :: Flask
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Visualization
Classifier: Topic :: Text Processing :: Markup :: HTML
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: flask>=3.0
Requires-Dist: Markdown>=3.6
Requires-Dist: bleach[css]>=6.1
Requires-Dist: Pillow>=10
Requires-Dist: Pygments>=2.17
Provides-Extra: demo
Requires-Dist: nbclient>=0.10; extra == "demo"
Requires-Dist: nbformat>=5.10; extra == "demo"
Requires-Dist: ipykernel>=6.29; extra == "demo"
Requires-Dist: IPython>=8; extra == "demo"
Requires-Dist: numpy>=1.24; extra == "demo"
Requires-Dist: scipy>=1.10; extra == "demo"
Requires-Dist: pandas>=2.0; extra == "demo"
Requires-Dist: matplotlib>=3.7; extra == "demo"
Requires-Dist: Pillow>=10; extra == "demo"
Requires-Dist: seaborn>=0.13; extra == "demo"
Requires-Dist: plotly>=5.20; extra == "demo"
Dynamic: license-file

# ipynb-local-viewer

[![PyPI](https://img.shields.io/pypi/v/ipynb-local-viewer.svg)](https://pypi.org/project/ipynb-local-viewer/)
[![Python](https://img.shields.io/pypi/pyversions/ipynb-local-viewer.svg)](https://pypi.org/project/ipynb-local-viewer/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![CI](https://github.com/msdkhairi/ipynb-viewer/actions/workflows/ci.yml/badge.svg)](https://github.com/msdkhairi/ipynb-viewer/actions/workflows/ci.yml)

A smooth, local-first reader for saved Jupyter notebooks.

`ipynb-local-viewer` turns an `.ipynb` file into a fast reading surface: markdown
headings become an outline, outputs are shown first, code is collapsed behind
syntax-highlighted expandable blocks, and large images or videos are loaded
through lazy streamed asset URLs instead of being embedded into the main page
payload.

In normal viewing mode it reads saved notebooks only and does not execute code.
Packaged demo notebooks can be executed explicitly with the optional `[demo]`
extra.

## Features

- Markdown-derived outline with smooth section navigation.
- Lazy image and video output loading for media-heavy notebooks.
- Collapsed code cells with Pygments syntax highlighting.
- Safe sanitized HTML output rendering with Bleach.
- Light, dark, and device theme modes.
- Local Flask server with no frontend build step.
- Root-restricted notebook access for safer local browsing.
- Small decoded media cache for faster repeat reads.
- Optional live demo notebooks with scientific and visualization outputs.

## Install

```bash
python -m pip install ipynb-local-viewer
```

Install the optional demo stack when you want to run the packaged examples:

```bash
python -m pip install "ipynb-local-viewer[demo]"
```

For local development from a checkout:

```bash
python -m pip install -e ".[demo]"
```

## Quick Start

Open a notebook on `http://localhost:8770`:

```bash
notebook-viewer --notebook analysis.ipynb --port 8770
```

Restrict the viewer to a specific project directory:

```bash
notebook-viewer --root ~/projects/my-analysis --notebook notebooks/report.ipynb --port 8770
```

Start without opening a browser:

```bash
notebook-viewer --root . --notebook analysis.ipynb --port 8770 --no-browser
```

Run a packaged live demo:

```bash
notebook-viewer --demo quickstart --port 8770
notebook-viewer --demo.signal_lab --port 8770
```

Stop a foreground server with `Ctrl-C`.

## CLI

```bash
notebook-viewer [--root ROOT] [--notebook NOTEBOOK] [--demo DEMO] [--list-demos] [--host HOST] [--port PORT] [--no-browser]
```

Options:

- `--root`: directory that readable notebooks must stay inside, default `.`
- `--notebook`: notebook path relative to `--root`
- `--demo`: copy and run a packaged demo notebook
- `--demo.NAME`: shorthand for `--demo NAME`, for example `--demo.motion_demo`
- `--list-demos`: list packaged demos and exit
- `--host`: bind host, default `0.0.0.0`
- `--port`: bind port, default `8770`
- `--no-browser`: do not open a browser automatically

Packaged demos:

- `quickstart`: markdown, stdout, a plot, a table, and collapsed code.
- `signal_lab`: NumPy/SciPy signal synthesis, filtering, FFT, and spectrograms.
- `visual_story`: generated images, color maps, and SVG output.
- `interactive_charts`: pandas, seaborn, Plotly-style HTML, and sanitized rich output.
- `motion_demo`: a small generated animation for lazy media loading.

## How It Works

The viewer parses saved notebook JSON with the Python standard library and
caches parsed notebooks by path, mtime, and size. Markdown headings define the
outline. Selecting or scrolling to a section loads the cells for that section,
while image and video outputs are represented as asset descriptors and streamed
separately from `/api/asset/<asset_id>`.

Supported output behavior:

- PNG, JPEG, GIF, SVG, and common video MIME outputs become lazy asset URLs.
- HTML video data URLs are extracted into streamed video assets.
- HTML tables and rich HTML outputs are sanitized before rendering.
- stdout, stderr, tracebacks, and unsupported widgets use readable fallback
  blocks.
- Code cells include raw source and highlighted HTML in the section API.

## API

The local server exposes a small JSON/asset API:

- `GET /api/notebooks`: list `.ipynb` files under `--root`
- `GET /api/notebook?path=...`: return outline and section metadata
- `GET /api/section?path=...&section=...`: return cells for one section
- `GET /api/asset/<asset_id>`: stream decoded media assets
- `GET /api/demo-status?run_id=...`: return live demo execution status

Notebook and section JSON responses avoid embedding large base64 media payloads.

## Security Notes

`ipynb-local-viewer` is a local reader, not a notebook execution environment.

- It never runs user-selected notebook code.
- Demo mode only runs packaged demo notebooks and must be started explicitly.
- It restricts notebook access to the configured `--root`.
- It sanitizes markdown-rendered HTML and notebook HTML outputs.
- It serves decoded media assets from `.notebook_viewer_cache/`.
- Demo run copies are written under `.notebook_viewer_cache/demo_runs/`.

As with any local web server, bind it only where you intend to expose it. The
default host is `0.0.0.0`; use `--host 127.0.0.1` for loopback-only access.

## Development

Install development tools and run the test suite:

```bash
python -m pip install -e .
python -m pip install build twine trove-classifiers tomli
python -m unittest discover -s tests
python -m compileall src
```

Build and validate distribution artifacts:

```bash
python -m build
python -m twine check dist/*
python scripts/check_classifiers.py
```

## Publishing

Releases are intended to use PyPI Trusted Publishing from GitHub Actions.

One-time PyPI setup:

1. Log into the PyPI account `masoudka`.
2. Create or select the PyPI project `ipynb-local-viewer`.
3. Add a pending trusted publisher for:
   - Owner: `msdkhairi`
   - Repository: `ipynb-viewer`
   - Workflow: `release.yml`
   - Environment: `pypi`

`Owner` is the GitHub repository owner, not the PyPI username. The GitHub
repository can remain `ipynb-viewer` even though the PyPI distribution name is
`ipynb-local-viewer`.

Release flow:

```bash
git tag v0.1.0
git push origin v0.1.0
```

Then create a GitHub release for that tag. The release workflow builds the
artifacts, validates them, and publishes with PyPI Trusted Publishing.

Manual fallback:

```bash
python -m build
python -m twine check dist/*
python -m twine upload dist/*
```

## License

`ipynb-local-viewer` is distributed under the MIT License. See [LICENSE](LICENSE).
